* [RFC][PATCH] Add primitives for manipulating bitfields both in host- and fixed-endian.
From: Al Viro @ 2017-12-15 2:33 UTC (permalink / raw)
To: Jakub Kicinski; +Cc: Linus Torvalds, netdev, linux-kernel
In-Reply-To: <20171213174554.GE21978@ZenIV.linux.org.uk>
The following primitives are defined in linux/bitfield.h:
* u32 le32_get_bits(__le32 val, u32 field) extracts the contents of the
bitfield specified by @field in little-endian 32bit value @val and
converts it to host-endian.
* void le32p_replace_bits(__le32 *p, u32 v, u32 field) replaces
the contents of the bitfield specified by @field in little-endian
32bit object pointet to by *p with the value of @v. New value is
given in host-endian and stored as little-endian.
* __le32 le32_replace_bits(__le32 old, u32 v, u32 field) is equivalent to
({__le32 tmp = old; le32p_replace_bits(&old, v, field); tmp;})
In other words, instead of modifying an object in memory, it takes
the initial value and returns the modified one.
Such set of helpers is defined for each of little-, big- and host-endian
types; e.g. u64_get_bits(val, field) will return the contents of the bitfield
specified by @field in host-endian 64bit value @val, etc. Of course, for
host-endian no conversion is involved.
Fields to access are specified as GENMASK() values - an N-bit field
starting at bit #M is encoded as GENMASK(M + N - 1, N). Note that
bit numbers refer to endianness of the object we are working with -
e.g. GENMASK(11, 0) in __be16 refers to the second byte and the lower
4 bits of the first byte. In __le16 it would refer to the first byte
and the lower 4 bits of the second byte, etc.
Field specification must be a constant; __builtin_constant_p() doesn't
have to be true for it, but compiler must be able to evaluate it at
build time. If it cannot or if the value does not encode any bitfield,
the build will fail.
If the value being stored in ..._replace_bits() is a constant that does
not fit into bitfield, a warning will be generated at compile time.
Signed-off-by: Al Viro <viro@zeniv.linux.org.uk>
diff --git a/include/linux/bitfield.h b/include/linux/bitfield.h
index 1030651f8309..4b4f0531a79c 100644
--- a/include/linux/bitfield.h
+++ b/include/linux/bitfield.h
@@ -16,6 +16,7 @@
#define _LINUX_BITFIELD_H
#include <linux/build_bug.h>
+#include <asm/byteorder.h>
/*
* Bitfield access macros
@@ -103,4 +104,50 @@
(typeof(_mask))(((_reg) & (_mask)) >> __bf_shf(_mask)); \
})
+extern void __compiletime_warning("value doesn't fit into mask")
+__field_overflow(void);
+extern void __compiletime_error("bad bitfield mask")
+__bad_mask(void);
+static __always_inline u64 mask_to_multiplier(u64 mask)
+{
+ if ((mask | (mask - 1)) & ((mask | (mask - 1)) + 1))
+ __bad_mask();
+ return mask & -mask;
+}
+
+#define ____MAKE_OP(type,base,to,from) \
+static __always_inline __##type type##_replace_bits(__##type old, \
+ base val, base field) \
+{ \
+ __##type m = to(field); \
+ if (__builtin_constant_p(val) && \
+ (val & ~(field/mask_to_multiplier(field)))) \
+ __field_overflow(); \
+ return (old & ~m) | \
+ (to(val * mask_to_multiplier(field)) & m); \
+} \
+static __always_inline void type##p_replace_bits(__##type *p, \
+ base val, base field) \
+{ \
+ __##type m = to(field); \
+ if (__builtin_constant_p(val) && \
+ (val & ~(field/mask_to_multiplier(field)))) \
+ __field_overflow(); \
+ *p = (*p & ~m) | \
+ (to(val * mask_to_multiplier(field)) & m); \
+} \
+static __always_inline base type##_get_bits(__##type v, base field) \
+{ \
+ return (from(v) & field)/mask_to_multiplier(field); \
+}
+#define __MAKE_OP(size) \
+ ____MAKE_OP(le##size,u##size,cpu_to_le##size,le##size##_to_cpu) \
+ ____MAKE_OP(be##size,u##size,cpu_to_be##size,be##size##_to_cpu) \
+ ____MAKE_OP(u##size,u##size,,)
+__MAKE_OP(16)
+__MAKE_OP(32)
+__MAKE_OP(64)
+#undef __MAKE_OP
+#undef ____MAKE_OP
+
#endif
^ permalink raw reply related
* [PATCH] ip_gre: fix wrong return value of erspan_rcv
From: Haishuang Yan @ 2017-12-15 2:46 UTC (permalink / raw)
To: David S. Miller, Alexey Kuznetsov, Hideaki YOSHIFUJI
Cc: netdev, linux-kernel, Haishuang Yan, William Tu
If pskb_may_pull return failed, return PACKET_REJECT instead of -ENOMEM.
Fixes: 84e54fe0a5ea ("gre: introduce native tunnel support for ERSPAN")
Cc: William Tu <u9012063@gmail.com>
Signed-off-by: Haishuang Yan <yanhaishuang@cmss.chinamobile.com>
---
net/ipv4/ip_gre.c | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/net/ipv4/ip_gre.c b/net/ipv4/ip_gre.c
index 61ee014..d747d06 100644
--- a/net/ipv4/ip_gre.c
+++ b/net/ipv4/ip_gre.c
@@ -267,7 +267,7 @@ static int erspan_rcv(struct sk_buff *skb, struct tnl_ptk_info *tpi,
len = gre_hdr_len + sizeof(*ershdr);
if (unlikely(!pskb_may_pull(skb, len)))
- return -ENOMEM;
+ return PACKET_REJECT;
iph = ip_hdr(skb);
ershdr = (struct erspanhdr *)(skb->data + gre_hdr_len);
--
1.8.3.1
^ permalink raw reply related
* [PATCH] ip6_gre: fix a pontential issue in ip6erspan_rcv
From: Haishuang Yan @ 2017-12-15 2:46 UTC (permalink / raw)
To: David S. Miller, Alexey Kuznetsov, Hideaki YOSHIFUJI
Cc: netdev, linux-kernel, Haishuang Yan, William Tu
pskb_may_pull() can change skb->data, so we need to load ipv6h/ershdr at
the right place.
Fixes: 5a963eb61b7c ("ip6_gre: Add ERSPAN native tunnel support")
Cc: William Tu <u9012063@gmail.com>
Signed-off-by: Haishuang Yan <yanhaishuang@cmss.chinamobile.com>
---
net/ipv6/ip6_gre.c | 6 +++---
1 file changed, 3 insertions(+), 3 deletions(-)
diff --git a/net/ipv6/ip6_gre.c b/net/ipv6/ip6_gre.c
index 68e7eef..eab4b56 100644
--- a/net/ipv6/ip6_gre.c
+++ b/net/ipv6/ip6_gre.c
@@ -506,12 +506,12 @@ static int ip6erspan_rcv(struct sk_buff *skb, int gre_hdr_len,
struct ip6_tnl *tunnel;
__be32 index;
- ipv6h = ipv6_hdr(skb);
- ershdr = (struct erspanhdr *)skb->data;
-
if (unlikely(!pskb_may_pull(skb, sizeof(*ershdr))))
return PACKET_REJECT;
+ ipv6h = ipv6_hdr(skb);
+ ershdr = (struct erspanhdr *)skb->data;
+
tpi->key = cpu_to_be32(ntohs(ershdr->session_id) & ID_MASK);
index = ershdr->md.index;
--
1.8.3.1
^ permalink raw reply related
* [PATCH v3 net-next 0/3] add VLAN support to DSA MT7530
From: sean.wang @ 2017-12-15 4:46 UTC (permalink / raw)
To: davem, andrew, f.fainelli, vivien.didelot
Cc: netdev, linux-kernel, linux-mediatek, Sean Wang
From: Sean Wang <sean.wang@mediatek.com>
Changes sicne v2:
update to the latest code base from net-next and fix up all building
errors with -Werror.
Changes since v1:
- fix up the typo
- prefer ordering declarations longest to shortest
- update that vlan_prepare callback should not change any state
- use lower case letter for function naming
The patchset extends DSA MT7530 to VLAN support through filling required
callbacks in patch 1 and merging the special tag with VLAN tag in patch 2
for allowing that the hardware can handle these packets with VID from the
CPU port.
Sean Wang (3):
net: dsa: mediatek: add VLAN support for MT7530
net: dsa: mediatek: combine MediaTek tag with VLAN tag
net: dsa: mediatek: update MAINTAINERS entry with MediaTek switch
driver
MAINTAINERS | 7 ++
drivers/net/dsa/mt7530.c | 288 ++++++++++++++++++++++++++++++++++++++++++++++-
drivers/net/dsa/mt7530.h | 83 +++++++++++++-
net/dsa/tag_mtk.c | 38 +++++--
4 files changed, 400 insertions(+), 16 deletions(-)
--
2.7.4
^ permalink raw reply
* [PATCH v3 net-next 1/3] net: dsa: mediatek: add VLAN support for MT7530
From: sean.wang-NuS5LvNUpcJWk0Htik3J/w @ 2017-12-15 4:47 UTC (permalink / raw)
To: davem-fT/PcQaiUtIeIZ0/mPfg9Q, andrew-g2DYL2Zd6BY,
f.fainelli-Re5JQEeQqe8AvxtiuMwx3w,
vivien.didelot-4ysUXcep3aM1wj+D4I0NRVaTQe2KTcn/
Cc: netdev-u79uwXL29TY76Z2rM5mHXA, Sean Wang,
linux-mediatek-IAPFreCvJWM7uuMidbF8XUB+6BGkLq7r,
linux-kernel-u79uwXL29TY76Z2rM5mHXA
In-Reply-To: <cover.1513312600.git.sean.wang-NuS5LvNUpcJWk0Htik3J/w@public.gmane.org>
From: Sean Wang <sean.wang-NuS5LvNUpcJWk0Htik3J/w@public.gmane.org>
MT7530 can treat each port as either VLAN-unaware port or VLAN-aware port
through the implementation of port matrix mode or port security mode on
the ingress port, respectively. On one hand, Each port has been acting as
the VLAN-unaware one whenever the device is created in the initial or
certain port joins or leaves into/from the bridge at the runtime. On the
other hand, the patch just filling the required callbacks for VLAN
operations is achieved via extending the port to be into port security
mode when the port is configured as VLAN-aware port. Which mode can make
the port be able to recognize VID from incoming packets and look up VLAN
table to validate and judge which port it should be going to. And the
range for VID from 1 to 4094 is valid for the hardware.
Signed-off-by: Sean Wang <sean.wang-NuS5LvNUpcJWk0Htik3J/w@public.gmane.org>
Reviewed-by: Andrew Lunn <andrew-g2DYL2Zd6BY@public.gmane.org>
---
drivers/net/dsa/mt7530.c | 288 ++++++++++++++++++++++++++++++++++++++++++++++-
drivers/net/dsa/mt7530.h | 83 +++++++++++++-
2 files changed, 364 insertions(+), 7 deletions(-)
diff --git a/drivers/net/dsa/mt7530.c b/drivers/net/dsa/mt7530.c
index 2820d69..8a0bb00 100644
--- a/drivers/net/dsa/mt7530.c
+++ b/drivers/net/dsa/mt7530.c
@@ -805,6 +805,69 @@ mt7530_port_bridge_join(struct dsa_switch *ds, int port,
}
static void
+mt7530_port_set_vlan_unaware(struct dsa_switch *ds, int port)
+{
+ struct mt7530_priv *priv = ds->priv;
+ bool all_user_ports_removed = true;
+ int i;
+
+ /* When a port is removed from the bridge, the port would be set up
+ * back to the default as is at initial boot which is a VLAN-unaware
+ * port.
+ */
+ mt7530_rmw(priv, MT7530_PCR_P(port), PCR_PORT_VLAN_MASK,
+ MT7530_PORT_MATRIX_MODE);
+ mt7530_rmw(priv, MT7530_PVC_P(port), VLAN_ATTR_MASK,
+ VLAN_ATTR(MT7530_VLAN_TRANSPARENT));
+
+ priv->ports[port].vlan_filtering = false;
+
+ for (i = 0; i < MT7530_NUM_PORTS; i++) {
+ if (dsa_is_user_port(ds, i) &&
+ priv->ports[i].vlan_filtering) {
+ all_user_ports_removed = false;
+ break;
+ }
+ }
+
+ /* CPU port also does the same thing until all user ports belonging to
+ * the CPU port get out of VLAN filtering mode.
+ */
+ if (all_user_ports_removed) {
+ mt7530_write(priv, MT7530_PCR_P(MT7530_CPU_PORT),
+ PCR_MATRIX(dsa_user_ports(priv->ds)));
+ mt7530_write(priv, MT7530_PVC_P(MT7530_CPU_PORT),
+ PORT_SPEC_TAG);
+ }
+}
+
+static void
+mt7530_port_set_vlan_aware(struct dsa_switch *ds, int port)
+{
+ struct mt7530_priv *priv = ds->priv;
+
+ /* The real fabric path would be decided on the membership in the
+ * entry of VLAN table. PCR_MATRIX set up here with ALL_MEMBERS
+ * means potential VLAN can be consisting of certain subset of all
+ * ports.
+ */
+ mt7530_rmw(priv, MT7530_PCR_P(port),
+ PCR_MATRIX_MASK, PCR_MATRIX(MT7530_ALL_MEMBERS));
+
+ /* Trapped into security mode allows packet forwarding through VLAN
+ * table lookup.
+ */
+ mt7530_rmw(priv, MT7530_PCR_P(port), PCR_PORT_VLAN_MASK,
+ MT7530_PORT_SECURITY_MODE);
+
+ /* Set the port as a user port which is to be able to recognize VID
+ * from incoming packets before fetching entry within the VLAN table.
+ */
+ mt7530_rmw(priv, MT7530_PVC_P(port), VLAN_ATTR_MASK,
+ VLAN_ATTR(MT7530_VLAN_USER));
+}
+
+static void
mt7530_port_bridge_leave(struct dsa_switch *ds, int port,
struct net_device *bridge)
{
@@ -817,8 +880,11 @@ mt7530_port_bridge_leave(struct dsa_switch *ds, int port,
/* Remove this port from the port matrix of the other ports
* in the same bridge. If the port is disabled, port matrix
* is kept and not being setup until the port becomes enabled.
+ * And the other port's port matrix cannot be broken when the
+ * other port is still a VLAN-aware port.
*/
- if (dsa_is_user_port(ds, i) && i != port) {
+ if (!priv->ports[i].vlan_filtering &&
+ dsa_is_user_port(ds, i) && i != port) {
if (dsa_to_port(ds, i)->bridge_dev != bridge)
continue;
if (priv->ports[i].enable)
@@ -836,6 +902,8 @@ mt7530_port_bridge_leave(struct dsa_switch *ds, int port,
PCR_MATRIX(BIT(MT7530_CPU_PORT)));
priv->ports[port].pm = PCR_MATRIX(BIT(MT7530_CPU_PORT));
+ mt7530_port_set_vlan_unaware(ds, port);
+
mutex_unlock(&priv->reg_mutex);
}
@@ -906,6 +974,220 @@ mt7530_port_fdb_dump(struct dsa_switch *ds, int port,
return 0;
}
+static int
+mt7530_vlan_cmd(struct mt7530_priv *priv, enum mt7530_vlan_cmd cmd, u16 vid)
+{
+ struct mt7530_dummy_poll p;
+ u32 val;
+ int ret;
+
+ val = VTCR_BUSY | VTCR_FUNC(cmd) | vid;
+ mt7530_write(priv, MT7530_VTCR, val);
+
+ INIT_MT7530_DUMMY_POLL(&p, priv, MT7530_VTCR);
+ ret = readx_poll_timeout(_mt7530_read, &p, val,
+ !(val & VTCR_BUSY), 20, 20000);
+ if (ret < 0) {
+ dev_err(priv->dev, "poll timeout\n");
+ return ret;
+ }
+
+ val = mt7530_read(priv, MT7530_VTCR);
+ if (val & VTCR_INVALID) {
+ dev_err(priv->dev, "read VTCR invalid\n");
+ return -EINVAL;
+ }
+
+ return 0;
+}
+
+static int
+mt7530_port_vlan_filtering(struct dsa_switch *ds, int port,
+ bool vlan_filtering)
+{
+ struct mt7530_priv *priv = ds->priv;
+
+ priv->ports[port].vlan_filtering = vlan_filtering;
+
+ if (vlan_filtering) {
+ /* The port is being kept as VLAN-unaware port when bridge is
+ * set up with vlan_filtering not being set, Otherwise, the
+ * port and the corresponding CPU port is required the setup
+ * for becoming a VLAN-aware port.
+ */
+ mt7530_port_set_vlan_aware(ds, port);
+ mt7530_port_set_vlan_aware(ds, MT7530_CPU_PORT);
+ }
+
+ return 0;
+}
+
+static int
+mt7530_port_vlan_prepare(struct dsa_switch *ds, int port,
+ const struct switchdev_obj_port_vlan *vlan)
+{
+ /* nothing needed */
+
+ return 0;
+}
+
+static void
+mt7530_hw_vlan_add(struct mt7530_priv *priv,
+ struct mt7530_hw_vlan_entry *entry)
+{
+ u8 new_members;
+ u32 val;
+
+ new_members = entry->old_members | BIT(entry->port) |
+ BIT(MT7530_CPU_PORT);
+
+ /* Validate the entry with independent learning, create egress tag per
+ * VLAN and joining the port as one of the port members.
+ */
+ val = IVL_MAC | VTAG_EN | PORT_MEM(new_members) | VLAN_VALID;
+ mt7530_write(priv, MT7530_VAWD1, val);
+
+ /* Decide whether adding tag or not for those outgoing packets from the
+ * port inside the VLAN.
+ */
+ val = entry->untagged ? MT7530_VLAN_EGRESS_UNTAG :
+ MT7530_VLAN_EGRESS_TAG;
+ mt7530_rmw(priv, MT7530_VAWD2,
+ ETAG_CTRL_P_MASK(entry->port),
+ ETAG_CTRL_P(entry->port, val));
+
+ /* CPU port is always taken as a tagged port for serving more than one
+ * VLANs across and also being applied with egress type stack mode for
+ * that VLAN tags would be appended after hardware special tag used as
+ * DSA tag.
+ */
+ mt7530_rmw(priv, MT7530_VAWD2,
+ ETAG_CTRL_P_MASK(MT7530_CPU_PORT),
+ ETAG_CTRL_P(MT7530_CPU_PORT,
+ MT7530_VLAN_EGRESS_STACK));
+}
+
+static void
+mt7530_hw_vlan_del(struct mt7530_priv *priv,
+ struct mt7530_hw_vlan_entry *entry)
+{
+ u8 new_members;
+ u32 val;
+
+ new_members = entry->old_members & ~BIT(entry->port);
+
+ val = mt7530_read(priv, MT7530_VAWD1);
+ if (!(val & VLAN_VALID)) {
+ dev_err(priv->dev,
+ "Cannot be deleted due to invalid entry\n");
+ return;
+ }
+
+ /* If certain member apart from CPU port is still alive in the VLAN,
+ * the entry would be kept valid. Otherwise, the entry is got to be
+ * disabled.
+ */
+ if (new_members && new_members != BIT(MT7530_CPU_PORT)) {
+ val = IVL_MAC | VTAG_EN | PORT_MEM(new_members) |
+ VLAN_VALID;
+ mt7530_write(priv, MT7530_VAWD1, val);
+ } else {
+ mt7530_write(priv, MT7530_VAWD1, 0);
+ mt7530_write(priv, MT7530_VAWD2, 0);
+ }
+}
+
+static void
+mt7530_hw_vlan_update(struct mt7530_priv *priv, u16 vid,
+ struct mt7530_hw_vlan_entry *entry,
+ mt7530_vlan_op vlan_op)
+{
+ u32 val;
+
+ /* Fetch entry */
+ mt7530_vlan_cmd(priv, MT7530_VTCR_RD_VID, vid);
+
+ val = mt7530_read(priv, MT7530_VAWD1);
+
+ entry->old_members = (val >> PORT_MEM_SHFT) & PORT_MEM_MASK;
+
+ /* Manipulate entry */
+ vlan_op(priv, entry);
+
+ /* Flush result to hardware */
+ mt7530_vlan_cmd(priv, MT7530_VTCR_WR_VID, vid);
+}
+
+static void
+mt7530_port_vlan_add(struct dsa_switch *ds, int port,
+ const struct switchdev_obj_port_vlan *vlan)
+{
+ bool untagged = vlan->flags & BRIDGE_VLAN_INFO_UNTAGGED;
+ bool pvid = vlan->flags & BRIDGE_VLAN_INFO_PVID;
+ struct mt7530_hw_vlan_entry new_entry;
+ struct mt7530_priv *priv = ds->priv;
+ u16 vid;
+
+ /* The port is kept as VLAN-unaware if bridge with vlan_filtering not
+ * being set.
+ */
+ if (!priv->ports[port].vlan_filtering)
+ return;
+
+ mutex_lock(&priv->reg_mutex);
+
+ for (vid = vlan->vid_begin; vid <= vlan->vid_end; ++vid) {
+ mt7530_hw_vlan_entry_init(&new_entry, port, untagged);
+ mt7530_hw_vlan_update(priv, vid, &new_entry,
+ mt7530_hw_vlan_add);
+ }
+
+ if (pvid) {
+ mt7530_rmw(priv, MT7530_PPBV1_P(port), G0_PORT_VID_MASK,
+ G0_PORT_VID(vlan->vid_end));
+ priv->ports[port].pvid = vlan->vid_end;
+ }
+
+ mutex_unlock(&priv->reg_mutex);
+}
+
+static int
+mt7530_port_vlan_del(struct dsa_switch *ds, int port,
+ const struct switchdev_obj_port_vlan *vlan)
+{
+ struct mt7530_hw_vlan_entry target_entry;
+ struct mt7530_priv *priv = ds->priv;
+ u16 vid, pvid;
+
+ /* The port is kept as VLAN-unaware if bridge with vlan_filtering not
+ * being set.
+ */
+ if (!priv->ports[port].vlan_filtering)
+ return 0;
+
+ mutex_lock(&priv->reg_mutex);
+
+ pvid = priv->ports[port].pvid;
+ for (vid = vlan->vid_begin; vid <= vlan->vid_end; ++vid) {
+ mt7530_hw_vlan_entry_init(&target_entry, port, 0);
+ mt7530_hw_vlan_update(priv, vid, &target_entry,
+ mt7530_hw_vlan_del);
+
+ /* PVID is being restored to the default whenever the PVID port
+ * is being removed from the VLAN.
+ */
+ if (pvid == vid)
+ pvid = G0_PORT_VID_DEF;
+ }
+
+ mt7530_rmw(priv, MT7530_PPBV1_P(port), G0_PORT_VID_MASK, pvid);
+ priv->ports[port].pvid = pvid;
+
+ mutex_unlock(&priv->reg_mutex);
+
+ return 0;
+}
+
static enum dsa_tag_protocol
mtk_get_tag_protocol(struct dsa_switch *ds, int port)
{
@@ -1035,6 +1317,10 @@ static const struct dsa_switch_ops mt7530_switch_ops = {
.port_fdb_add = mt7530_port_fdb_add,
.port_fdb_del = mt7530_port_fdb_del,
.port_fdb_dump = mt7530_port_fdb_dump,
+ .port_vlan_filtering = mt7530_port_vlan_filtering,
+ .port_vlan_prepare = mt7530_port_vlan_prepare,
+ .port_vlan_add = mt7530_port_vlan_add,
+ .port_vlan_del = mt7530_port_vlan_del,
};
static int
diff --git a/drivers/net/dsa/mt7530.h b/drivers/net/dsa/mt7530.h
index 74db982..d9b407a 100644
--- a/drivers/net/dsa/mt7530.h
+++ b/drivers/net/dsa/mt7530.h
@@ -17,6 +17,7 @@
#define MT7530_NUM_PORTS 7
#define MT7530_CPU_PORT 6
#define MT7530_NUM_FDB_RECORDS 2048
+#define MT7530_ALL_MEMBERS 0xff
#define NUM_TRGMII_CTRL 5
@@ -88,21 +89,42 @@ enum mt7530_fdb_cmd {
/* Register for vlan table control */
#define MT7530_VTCR 0x90
#define VTCR_BUSY BIT(31)
-#define VTCR_FUNC (((x) & 0xf) << 12)
-#define VTCR_FUNC_RD_VID 0x1
-#define VTCR_FUNC_WR_VID 0x2
-#define VTCR_FUNC_INV_VID 0x3
-#define VTCR_FUNC_VAL_VID 0x4
+#define VTCR_INVALID BIT(16)
+#define VTCR_FUNC(x) (((x) & 0xf) << 12)
#define VTCR_VID ((x) & 0xfff)
+enum mt7530_vlan_cmd {
+ /* Read/Write the specified VID entry from VAWD register based
+ * on VID.
+ */
+ MT7530_VTCR_RD_VID = 0,
+ MT7530_VTCR_WR_VID = 1,
+};
+
/* Register for setup vlan and acl write data */
#define MT7530_VAWD1 0x94
#define PORT_STAG BIT(31)
+/* Independent VLAN Learning */
#define IVL_MAC BIT(30)
+/* Per VLAN Egress Tag Control */
+#define VTAG_EN BIT(28)
+/* VLAN Member Control */
#define PORT_MEM(x) (((x) & 0xff) << 16)
-#define VALID BIT(1)
+/* VLAN Entry Valid */
+#define VLAN_VALID BIT(0)
+#define PORT_MEM_SHFT 16
+#define PORT_MEM_MASK 0xff
#define MT7530_VAWD2 0x98
+/* Egress Tag Control */
+#define ETAG_CTRL_P(p, x) (((x) & 0x3) << ((p) << 1))
+#define ETAG_CTRL_P_MASK(p) ETAG_CTRL_P(p, 3)
+
+enum mt7530_vlan_egress_attr {
+ MT7530_VLAN_EGRESS_UNTAG = 0,
+ MT7530_VLAN_EGRESS_TAG = 2,
+ MT7530_VLAN_EGRESS_STACK = 3,
+};
/* Register for port STP state control */
#define MT7530_SSP_P(x) (0x2000 + ((x) * 0x100))
@@ -120,11 +142,23 @@ enum mt7530_stp_state {
/* Register for port control */
#define MT7530_PCR_P(x) (0x2004 + ((x) * 0x100))
#define PORT_VLAN(x) ((x) & 0x3)
+
+enum mt7530_port_mode {
+ /* Port Matrix Mode: Frames are forwarded by the PCR_MATRIX members. */
+ MT7530_PORT_MATRIX_MODE = PORT_VLAN(0),
+
+ /* Security Mode: Discard any frame due to ingress membership
+ * violation or VID missed on the VLAN table.
+ */
+ MT7530_PORT_SECURITY_MODE = PORT_VLAN(3),
+};
+
#define PCR_MATRIX(x) (((x) & 0xff) << 16)
#define PORT_PRI(x) (((x) & 0x7) << 24)
#define EG_TAG(x) (((x) & 0x3) << 28)
#define PCR_MATRIX_MASK PCR_MATRIX(0xff)
#define PCR_MATRIX_CLR PCR_MATRIX(0)
+#define PCR_PORT_VLAN_MASK PORT_VLAN(3)
/* Register for port security control */
#define MT7530_PSC_P(x) (0x200c + ((x) * 0x100))
@@ -134,10 +168,20 @@ enum mt7530_stp_state {
#define MT7530_PVC_P(x) (0x2010 + ((x) * 0x100))
#define PORT_SPEC_TAG BIT(5)
#define VLAN_ATTR(x) (((x) & 0x3) << 6)
+#define VLAN_ATTR_MASK VLAN_ATTR(3)
+
+enum mt7530_vlan_port_attr {
+ MT7530_VLAN_USER = 0,
+ MT7530_VLAN_TRANSPARENT = 3,
+};
+
#define STAG_VPID (((x) & 0xffff) << 16)
/* Register for port port-and-protocol based vlan 1 control */
#define MT7530_PPBV1_P(x) (0x2014 + ((x) * 0x100))
+#define G0_PORT_VID(x) (((x) & 0xfff) << 0)
+#define G0_PORT_VID_MASK G0_PORT_VID(0xfff)
+#define G0_PORT_VID_DEF G0_PORT_VID(1)
/* Register for port MAC control register */
#define MT7530_PMCR_P(x) (0x3000 + ((x) * 0x100))
@@ -345,9 +389,20 @@ struct mt7530_fdb {
bool noarp;
};
+/* struct mt7530_port - This is the main data structure for holding the state
+ * of the port.
+ * @enable: The status used for show port is enabled or not.
+ * @pm: The matrix used to show all connections with the port.
+ * @pvid: The VLAN specified is to be considered a PVID at ingress. Any
+ * untagged frames will be assigned to the related VLAN.
+ * @vlan_filtering: The flags indicating whether the port that can recognize
+ * VLAN-tagged frames.
+ */
struct mt7530_port {
bool enable;
u32 pm;
+ u16 pvid;
+ bool vlan_filtering;
};
/* struct mt7530_priv - This is the main data structure for holding the state
@@ -382,6 +437,22 @@ struct mt7530_priv {
struct mutex reg_mutex;
};
+struct mt7530_hw_vlan_entry {
+ int port;
+ u8 old_members;
+ bool untagged;
+};
+
+static inline void mt7530_hw_vlan_entry_init(struct mt7530_hw_vlan_entry *e,
+ int port, bool untagged)
+{
+ e->port = port;
+ e->untagged = untagged;
+}
+
+typedef void (*mt7530_vlan_op)(struct mt7530_priv *,
+ struct mt7530_hw_vlan_entry *);
+
struct mt7530_hw_stats {
const char *string;
u16 reg;
--
2.7.4
^ permalink raw reply related
* [PATCH v3 net-next 2/3] net: dsa: mediatek: combine MediaTek tag with VLAN tag
From: sean.wang @ 2017-12-15 4:47 UTC (permalink / raw)
To: davem, andrew, f.fainelli, vivien.didelot
Cc: netdev, linux-kernel, linux-mediatek, Sean Wang
In-Reply-To: <cover.1513312600.git.sean.wang@mediatek.com>
From: Sean Wang <sean.wang@mediatek.com>
In order to let MT7530 switch can recognize well those egress packets
having both special tag and VLAN tag, the information about the special
tag should be carried on the existing VLAN tag. On the other hand, it's
unnecessary for extra handling for ingress packets when VLAN tag is
present since it is able to put the VLAN tag after the special tag and
then follow the existing way to parse.
Signed-off-by: Sean Wang <sean.wang@mediatek.com>
Reviewed-by: Andrew Lunn <andrew@lunn.ch>
---
net/dsa/tag_mtk.c | 38 +++++++++++++++++++++++++++++---------
1 file changed, 29 insertions(+), 9 deletions(-)
diff --git a/net/dsa/tag_mtk.c b/net/dsa/tag_mtk.c
index 8475434..11535bc 100644
--- a/net/dsa/tag_mtk.c
+++ b/net/dsa/tag_mtk.c
@@ -13,10 +13,13 @@
*/
#include <linux/etherdevice.h>
+#include <linux/if_vlan.h>
#include "dsa_priv.h"
#define MTK_HDR_LEN 4
+#define MTK_HDR_XMIT_UNTAGGED 0
+#define MTK_HDR_XMIT_TAGGED_TPID_8100 1
#define MTK_HDR_RECV_SOURCE_PORT_MASK GENMASK(2, 0)
#define MTK_HDR_XMIT_DP_BIT_MASK GENMASK(5, 0)
@@ -25,20 +28,37 @@ static struct sk_buff *mtk_tag_xmit(struct sk_buff *skb,
{
struct dsa_port *dp = dsa_slave_to_port(dev);
u8 *mtk_tag;
+ bool is_vlan_skb = true;
- if (skb_cow_head(skb, MTK_HDR_LEN) < 0)
- return NULL;
-
- skb_push(skb, MTK_HDR_LEN);
+ /* Build the special tag after the MAC Source Address. If VLAN header
+ * is present, it's required that VLAN header and special tag is
+ * being combined. Only in this way we can allow the switch can parse
+ * the both special and VLAN tag at the same time and then look up VLAN
+ * table with VID.
+ */
+ if (!skb_vlan_tagged(skb)) {
+ if (skb_cow_head(skb, MTK_HDR_LEN) < 0)
+ return NULL;
- memmove(skb->data, skb->data + MTK_HDR_LEN, 2 * ETH_ALEN);
+ skb_push(skb, MTK_HDR_LEN);
+ memmove(skb->data, skb->data + MTK_HDR_LEN, 2 * ETH_ALEN);
+ is_vlan_skb = false;
+ }
- /* Build the tag after the MAC Source Address */
mtk_tag = skb->data + 2 * ETH_ALEN;
- mtk_tag[0] = 0;
+
+ /* Mark tag attribute on special tag insertion to notify hardware
+ * whether that's a combined special tag with 802.1Q header.
+ */
+ mtk_tag[0] = is_vlan_skb ? MTK_HDR_XMIT_TAGGED_TPID_8100 :
+ MTK_HDR_XMIT_UNTAGGED;
mtk_tag[1] = (1 << dp->index) & MTK_HDR_XMIT_DP_BIT_MASK;
- mtk_tag[2] = 0;
- mtk_tag[3] = 0;
+
+ /* Tag control information is kept for 802.1Q */
+ if (!is_vlan_skb) {
+ mtk_tag[2] = 0;
+ mtk_tag[3] = 0;
+ }
return skb;
}
--
2.7.4
^ permalink raw reply related
* [PATCH v3 net-next 3/3] net: dsa: mediatek: update MAINTAINERS entry with MediaTek switch driver
From: sean.wang @ 2017-12-15 4:47 UTC (permalink / raw)
To: davem, andrew, f.fainelli, vivien.didelot
Cc: netdev, linux-kernel, linux-mediatek, Sean Wang
In-Reply-To: <cover.1513312600.git.sean.wang@mediatek.com>
From: Sean Wang <sean.wang@mediatek.com>
I work for MediaTek and maintain SoC targeting to home gateway and
also will keep extending and testing the function from MediaTek
switch.
Signed-off-by: Sean Wang <sean.wang@mediatek.com>
Reviewed-by: Andrew Lunn <andrew@lunn.ch>
---
MAINTAINERS | 7 +++++++
1 file changed, 7 insertions(+)
diff --git a/MAINTAINERS b/MAINTAINERS
index c86781b..90ca7f8 100644
--- a/MAINTAINERS
+++ b/MAINTAINERS
@@ -8728,6 +8728,13 @@ L: netdev@vger.kernel.org
S: Maintained
F: drivers/net/ethernet/mediatek/
+MEDIATEK SWITCH DRIVER
+M: Sean Wang <sean.wang@mediatek.com>
+L: netdev@vger.kernel.org
+S: Maintained
+F: drivers/net/dsa/mt7530.*
+F: net/dsa/tag_mtk.c
+
MEDIATEK JPEG DRIVER
M: Rick Chang <rick.chang@mediatek.com>
M: Bin Liu <bin.liu@mediatek.com>
--
2.7.4
^ permalink raw reply related
* Re: [RFC][PATCH] Add primitives for manipulating bitfields both in host- and fixed-endian.
From: Jakub Kicinski @ 2017-12-15 5:07 UTC (permalink / raw)
To: Al Viro; +Cc: Linus Torvalds, netdev, linux-kernel
In-Reply-To: <20171215023343.GG21978@ZenIV.linux.org.uk>
Looks great to me!
On Fri, 15 Dec 2017 02:33:43 +0000, Al Viro wrote:
> The following primitives are defined in linux/bitfield.h:
>
> * u32 le32_get_bits(__le32 val, u32 field) extracts the contents of the
> bitfield specified by @field in little-endian 32bit value @val and
> converts it to host-endian.
>
> * void le32p_replace_bits(__le32 *p, u32 v, u32 field) replaces
> the contents of the bitfield specified by @field in little-endian
> 32bit object pointet to by *p with the value of @v. New value is
> given in host-endian and stored as little-endian.
>
> * __le32 le32_replace_bits(__le32 old, u32 v, u32 field) is equivalent to
> ({__le32 tmp = old; le32p_replace_bits(&old, v, field); tmp;})
> In other words, instead of modifying an object in memory, it takes
> the initial value and returns the modified one.
the current macros take filed/mask as first param, not sure if it's
worth maintaining the order
^ permalink raw reply
* Re: [PATCH v2 iproute2 1/1] ss: add missing path MTU parameter
From: Stephen Hemminger @ 2017-12-15 5:15 UTC (permalink / raw)
To: Roman Mashak; +Cc: netdev, jhs
In-Reply-To: <1513281551-31687-1-git-send-email-mrv@mojatatu.com>
On Thu, 14 Dec 2017 14:59:11 -0500
Roman Mashak <mrv@mojatatu.com> wrote:
> v2:
> Print the path MTU immediately after the MSS, as it is easier to parse
> for humans (suggested by Neal Cardwell).
>
> Signed-off-by: Roman Mashak <mrv@mojatatu.com>
Thanks for the patch, it looks like a good field to show.
Unfortunately, it does not apply cleanly to current iproute2 master.
Please rebase and resubmit.
^ permalink raw reply
* [PATCH net-next] net/ncsi: Don't take any action on HNCDSC AEN
From: Samuel Mendoza-Jonas @ 2017-12-15 5:16 UTC (permalink / raw)
To: netdev; +Cc: Samuel Mendoza-Jonas, David S . Miller, linux-kernel, openbmc
The current HNCDSC handler takes the status flag from the AEN packet and
will update or change the current channel based on this flag and the
current channel status.
However the flag from the HNCDSC packet merely represents the host link
state. While the state of the host interface is potentially interesting
information it should not affect the state of the NCSI link. Indeed the
NCSI specification makes no mention of any recommended action related to
the host network controller driver state.
Update the HNCDSC handler to record the host network driver status but
take no other action.
Signed-off-by: Samuel Mendoza-Jonas <sam@mendozajonas.com>
---
net/ncsi/ncsi-aen.c | 35 +++--------------------------------
1 file changed, 3 insertions(+), 32 deletions(-)
diff --git a/net/ncsi/ncsi-aen.c b/net/ncsi/ncsi-aen.c
index 67e708e98ccf..e7b05de1e6d1 100644
--- a/net/ncsi/ncsi-aen.c
+++ b/net/ncsi/ncsi-aen.c
@@ -143,43 +143,14 @@ static int ncsi_aen_handler_hncdsc(struct ncsi_dev_priv *ndp,
if (!nc)
return -ENODEV;
- /* If the channel is active one, we need reconfigure it */
spin_lock_irqsave(&nc->lock, flags);
ncm = &nc->modes[NCSI_MODE_LINK];
hncdsc = (struct ncsi_aen_hncdsc_pkt *)h;
ncm->data[3] = ntohl(hncdsc->status);
- netdev_info(ndp->ndev.dev, "NCSI: HNCDSC AEN - channel %u state %s\n",
- nc->id, ncm->data[3] & 0x3 ? "up" : "down");
- if (!list_empty(&nc->link) ||
- nc->state != NCSI_CHANNEL_ACTIVE) {
- spin_unlock_irqrestore(&nc->lock, flags);
- return 0;
- }
-
- spin_unlock_irqrestore(&nc->lock, flags);
- if (!(ndp->flags & NCSI_DEV_HWA) && !(ncm->data[3] & 0x1))
- ndp->flags |= NCSI_DEV_RESHUFFLE;
-
- /* If this channel is the active one and the link doesn't
- * work, we have to choose another channel to be active one.
- * The logic here is exactly similar to what we do when link
- * is down on the active channel.
- *
- * On the other hand, we need configure it when host driver
- * state on the active channel becomes ready.
- */
- ncsi_stop_channel_monitor(nc);
-
- spin_lock_irqsave(&nc->lock, flags);
- nc->state = (ncm->data[3] & 0x1) ? NCSI_CHANNEL_INACTIVE :
- NCSI_CHANNEL_ACTIVE;
spin_unlock_irqrestore(&nc->lock, flags);
-
- spin_lock_irqsave(&ndp->lock, flags);
- list_add_tail_rcu(&nc->link, &ndp->channel_queue);
- spin_unlock_irqrestore(&ndp->lock, flags);
-
- ncsi_process_next_channel(ndp);
+ netdev_printk(KERN_DEBUG, ndp->ndev.dev,
+ "NCSI: host driver %srunning on channel %u\n",
+ ncm->data[3] & 0x1 ? "" : "not ", nc->id);
return 0;
}
--
2.15.1
^ permalink raw reply related
* Re: [patch iproute2] tc: fix command "tc actions del" hang issue
From: Stephen Hemminger @ 2017-12-15 5:16 UTC (permalink / raw)
To: Chris Mi; +Cc: netdev, jiri
In-Reply-To: <20171214090900.14336-1-chrism@mellanox.com>
On Thu, 14 Dec 2017 18:09:00 +0900
Chris Mi <chrism@mellanox.com> wrote:
> If command is RTM_DELACTION, a non-NULL pointer is passed to rtnl_talk().
> Then flag NLM_F_ACK is not set on n->nlmsg_flags and netlink_ack() will
> not be called. Command tc will wait for the reply for ever.
>
> Fixes: 86bf43c7c2fd ("lib/libnetlink: update rtnl_talk to support malloc
> buff at run time")
> Signed-off-by: Chris Mi <chrism@mellanox.com>
> Reviewed-by: Jiri Pirko <jiri@mellanox.com>
Thanks for fixing this.
Applied, but please don't linewrap the fixes tag.
^ permalink raw reply
* Re: [PATCH v4 iproute2 net-next] gre6: add collect metadata support
From: Stephen Hemminger @ 2017-12-15 5:20 UTC (permalink / raw)
To: William Tu; +Cc: netdev, Daniel Borkmann
In-Reply-To: <1513131772-87005-1-git-send-email-u9012063@gmail.com>
On Tue, 12 Dec 2017 18:22:52 -0800
William Tu <u9012063@gmail.com> wrote:
> The patch adds 'external' option to support collect metadata
> gre6 tunnel. The 'external' keyword is already used to set the
> device into collect metadata mode such as vxlan, geneve, ipip,
> etc. This patch extends support for ipv6 gre and gretap.
> Example of L3 and L2 gre device:
> bash:~# ip link add dev ip6gre123 type ip6gre external
> bash:~# ip link add dev ip6gretap123 type ip6gretap external
>
> Signed-off-by: William Tu <u9012063@gmail.com>
> Cc: Daniel Borkmann <daniel@iogearbox.net>
Applied to net-next
^ permalink raw reply
* [PATCH bpf-next 0/5] nfp: bpf: adjust head support
From: Jakub Kicinski @ 2017-12-15 5:29 UTC (permalink / raw)
To: netdev; +Cc: oss-drivers, daniel, alexei.starovoitov, Jakub Kicinski
Hi!
This small set adds support for bpf_xdp_adjust_head() to the offload.
Since we have access to unmodified BPF bytecode translating calls is
pretty trivial. First part of the series adds handling of BPF
capabilities included in the FW in TLV format. The last two patches
add adjust head support in the nfp verifier and jit, and a small
optimization in case we can guarantee the constant adjustment
will always meet adjustment constaints.
Jakub Kicinski (5):
nfp: add nfp_cpp_area_size() accessor
nfp: bpf: prepare for parsing BPF FW capabilities
nfp: bpf: prepare for call support
nfp: bpf: add basic support for adjust head call
nfp: bpf: optimize the adjust_head calls in trivial cases
drivers/net/ethernet/netronome/nfp/bpf/fw.h | 54 ++++++++++
drivers/net/ethernet/netronome/nfp/bpf/jit.c | 107 +++++++++++++++++++
drivers/net/ethernet/netronome/nfp/bpf/main.c | 115 +++++++++++++++++++++
drivers/net/ethernet/netronome/nfp/bpf/main.h | 30 ++++++
drivers/net/ethernet/netronome/nfp/bpf/offload.c | 2 +
drivers/net/ethernet/netronome/nfp/bpf/verifier.c | 70 +++++++++++++
drivers/net/ethernet/netronome/nfp/nfp_asm.h | 2 +
.../net/ethernet/netronome/nfp/nfpcore/nfp_cpp.h | 1 +
.../ethernet/netronome/nfp/nfpcore/nfp_cppcore.c | 11 ++
9 files changed, 392 insertions(+)
create mode 100644 drivers/net/ethernet/netronome/nfp/bpf/fw.h
--
2.15.1
^ permalink raw reply
* [PATCH bpf-next 1/5] nfp: add nfp_cpp_area_size() accessor
From: Jakub Kicinski @ 2017-12-15 5:29 UTC (permalink / raw)
To: netdev; +Cc: oss-drivers, daniel, alexei.starovoitov, Jakub Kicinski
In-Reply-To: <20171215052919.18343-1-jakub.kicinski@netronome.com>
Allow users outside of core reading area sizes. This was not needed
previously because whatever entity created the area would usually know
what size it asked for. The nfp_rtsym_map() helper, however, will
allocate the area based on the size of an RT-symbol with given name.
Signed-off-by: Jakub Kicinski <jakub.kicinski@netronome.com>
---
drivers/net/ethernet/netronome/nfp/nfpcore/nfp_cpp.h | 1 +
drivers/net/ethernet/netronome/nfp/nfpcore/nfp_cppcore.c | 11 +++++++++++
2 files changed, 12 insertions(+)
diff --git a/drivers/net/ethernet/netronome/nfp/nfpcore/nfp_cpp.h b/drivers/net/ethernet/netronome/nfp/nfpcore/nfp_cpp.h
index 5798adc57cbc..c8f2c064cce3 100644
--- a/drivers/net/ethernet/netronome/nfp/nfpcore/nfp_cpp.h
+++ b/drivers/net/ethernet/netronome/nfp/nfpcore/nfp_cpp.h
@@ -242,6 +242,7 @@ int nfp_cpp_area_read(struct nfp_cpp_area *area, unsigned long offset,
void *buffer, size_t length);
int nfp_cpp_area_write(struct nfp_cpp_area *area, unsigned long offset,
const void *buffer, size_t length);
+size_t nfp_cpp_area_size(struct nfp_cpp_area *area);
const char *nfp_cpp_area_name(struct nfp_cpp_area *cpp_area);
void *nfp_cpp_area_priv(struct nfp_cpp_area *cpp_area);
struct nfp_cpp *nfp_cpp_area_cpp(struct nfp_cpp_area *cpp_area);
diff --git a/drivers/net/ethernet/netronome/nfp/nfpcore/nfp_cppcore.c b/drivers/net/ethernet/netronome/nfp/nfpcore/nfp_cppcore.c
index 3fcb522d2e85..28262470dabf 100644
--- a/drivers/net/ethernet/netronome/nfp/nfpcore/nfp_cppcore.c
+++ b/drivers/net/ethernet/netronome/nfp/nfpcore/nfp_cppcore.c
@@ -567,6 +567,17 @@ int nfp_cpp_area_write(struct nfp_cpp_area *area,
return area->cpp->op->area_write(area, kernel_vaddr, offset, length);
}
+/**
+ * nfp_cpp_area_size() - return size of a CPP area
+ * @cpp_area: CPP area handle
+ *
+ * Return: Size of the area
+ */
+size_t nfp_cpp_area_size(struct nfp_cpp_area *cpp_area)
+{
+ return cpp_area->size;
+}
+
/**
* nfp_cpp_area_name() - return name of a CPP area
* @cpp_area: CPP area handle
--
2.15.1
^ permalink raw reply related
* [PATCH bpf-next 2/5] nfp: bpf: prepare for parsing BPF FW capabilities
From: Jakub Kicinski @ 2017-12-15 5:29 UTC (permalink / raw)
To: netdev; +Cc: oss-drivers, daniel, alexei.starovoitov, Jakub Kicinski
In-Reply-To: <20171215052919.18343-1-jakub.kicinski@netronome.com>
BPF FW creates a run time symbol called bpf_capabilities which
contains TLV-formatted capability information. Allocate app
private structure to store parsed capabilities and add a skeleton
of parsing logic.
Signed-off-by: Jakub Kicinski <jakub.kicinski@netronome.com>
---
drivers/net/ethernet/netronome/nfp/bpf/main.c | 75 ++++++++++++++++++++++++
drivers/net/ethernet/netronome/nfp/bpf/main.h | 11 ++++
drivers/net/ethernet/netronome/nfp/bpf/offload.c | 2 +
3 files changed, 88 insertions(+)
diff --git a/drivers/net/ethernet/netronome/nfp/bpf/main.c b/drivers/net/ethernet/netronome/nfp/bpf/main.c
index 54bfd7846f6d..f76bb40c20bc 100644
--- a/drivers/net/ethernet/netronome/nfp/bpf/main.c
+++ b/drivers/net/ethernet/netronome/nfp/bpf/main.c
@@ -34,6 +34,7 @@
#include <net/pkt_cls.h>
#include "../nfpcore/nfp_cpp.h"
+#include "../nfpcore/nfp_nffw.h"
#include "../nfp_app.h"
#include "../nfp_main.h"
#include "../nfp_net.h"
@@ -155,10 +156,84 @@ static bool nfp_bpf_tc_busy(struct nfp_app *app, struct nfp_net *nn)
return nn->dp.ctrl & NFP_NET_CFG_CTRL_BPF;
}
+static int nfp_bpf_parse_capabilities(struct nfp_app *app)
+{
+ struct nfp_cpp *cpp = app->pf->cpp;
+ struct nfp_cpp_area *area;
+ u8 __iomem *mem, *start;
+
+ mem = nfp_rtsym_map(app->pf->rtbl, "_abi_bpf_capabilities", "bpf.cap",
+ 8, &area);
+ if (IS_ERR(mem))
+ return PTR_ERR(mem) == -ENOENT ? 0 : PTR_ERR(mem);
+
+ start = mem;
+ while (mem - start + 8 < nfp_cpp_area_size(area)) {
+ u32 type, length;
+
+ type = readl(mem);
+ length = readl(mem + 4);
+
+ mem += 8 + length;
+ if (mem - start > nfp_cpp_area_size(area))
+ goto err_release_free;
+
+ switch (type) {
+ default:
+ nfp_dbg(cpp, "unknown BPF capability: %d\n", type);
+ break;
+ }
+ }
+ if (mem - start != nfp_cpp_area_size(area)) {
+ nfp_err(cpp, "BPF capabilities left after parsing, parsed:%lu total length:%lu\n",
+ mem - start, nfp_cpp_area_size(area));
+ goto err_release_free;
+ }
+
+ nfp_cpp_area_release_free(area);
+
+ return 0;
+
+err_release_free:
+ nfp_err(cpp, "invalid BPF capabilities at offset:%ld\n", mem - start);
+ nfp_cpp_area_release_free(area);
+ return -EINVAL;
+}
+
+static int nfp_bpf_init(struct nfp_app *app)
+{
+ struct nfp_app_bpf *bpf;
+ int err;
+
+ bpf = kzalloc(sizeof(*bpf), GFP_KERNEL);
+ if (!bpf)
+ return -ENOMEM;
+ bpf->app = app;
+ app->priv = bpf;
+
+ err = nfp_bpf_parse_capabilities(app);
+ if (err)
+ goto err_free_bpf;
+
+ return 0;
+
+err_free_bpf:
+ kfree(bpf);
+ return err;
+}
+
+static void nfp_bpf_clean(struct nfp_app *app)
+{
+ kfree(app->priv);
+}
+
const struct nfp_app_type app_bpf = {
.id = NFP_APP_BPF_NIC,
.name = "ebpf",
+ .init = nfp_bpf_init,
+ .clean = nfp_bpf_clean,
+
.extra_cap = nfp_bpf_extra_cap,
.vnic_alloc = nfp_app_nic_vnic_alloc,
diff --git a/drivers/net/ethernet/netronome/nfp/bpf/main.h b/drivers/net/ethernet/netronome/nfp/bpf/main.h
index 5884291ddba5..a1a3f96353df 100644
--- a/drivers/net/ethernet/netronome/nfp/bpf/main.h
+++ b/drivers/net/ethernet/netronome/nfp/bpf/main.h
@@ -78,6 +78,14 @@ enum pkt_vec {
#define NFP_BPF_ABI_FLAGS reg_imm(0)
#define NFP_BPF_ABI_FLAG_MARK 1
+/**
+ * struct nfp_app_bpf - bpf app priv structure
+ * @app: backpointer to the app
+ */
+struct nfp_app_bpf {
+ struct nfp_app *app;
+};
+
struct nfp_prog;
struct nfp_insn_meta;
typedef int (*instr_cb_t)(struct nfp_prog *, struct nfp_insn_meta *);
@@ -160,6 +168,7 @@ static inline bool is_mbpf_store(const struct nfp_insn_meta *meta)
/**
* struct nfp_prog - nfp BPF program
+ * @bpf: backpointer to the bpf app priv structure
* @prog: machine code
* @prog_len: number of valid instructions in @prog array
* @__prog_alloc_len: alloc size of @prog array
@@ -176,6 +185,8 @@ static inline bool is_mbpf_store(const struct nfp_insn_meta *meta)
* @insns: list of BPF instruction wrappers (struct nfp_insn_meta)
*/
struct nfp_prog {
+ struct nfp_app_bpf *bpf;
+
u64 *prog;
unsigned int prog_len;
unsigned int __prog_alloc_len;
diff --git a/drivers/net/ethernet/netronome/nfp/bpf/offload.c b/drivers/net/ethernet/netronome/nfp/bpf/offload.c
index 377976ce92dd..fa2905e67b07 100644
--- a/drivers/net/ethernet/netronome/nfp/bpf/offload.c
+++ b/drivers/net/ethernet/netronome/nfp/bpf/offload.c
@@ -48,6 +48,7 @@
#include <net/tc_act/tc_mirred.h>
#include "main.h"
+#include "../nfp_app.h"
#include "../nfp_net_ctrl.h"
#include "../nfp_net.h"
@@ -115,6 +116,7 @@ int nfp_bpf_verifier_prep(struct nfp_app *app, struct nfp_net *nn,
INIT_LIST_HEAD(&nfp_prog->insns);
nfp_prog->type = prog->type;
+ nfp_prog->bpf = app->priv;
ret = nfp_prog_prepare(nfp_prog, prog->insnsi, prog->len);
if (ret)
--
2.15.1
^ permalink raw reply related
* [PATCH bpf-next 3/5] nfp: bpf: prepare for call support
From: Jakub Kicinski @ 2017-12-15 5:29 UTC (permalink / raw)
To: netdev; +Cc: oss-drivers, daniel, alexei.starovoitov, Jakub Kicinski
In-Reply-To: <20171215052919.18343-1-jakub.kicinski@netronome.com>
Add skeleton of verifier checks and translation handler
for call instructions. Make sure jump target resolution
will not treat them as jumps.
Signed-off-by: Jakub Kicinski <jakub.kicinski@netronome.com>
---
drivers/net/ethernet/netronome/nfp/bpf/jit.c | 12 ++++++++++++
drivers/net/ethernet/netronome/nfp/bpf/verifier.c | 16 ++++++++++++++++
2 files changed, 28 insertions(+)
diff --git a/drivers/net/ethernet/netronome/nfp/bpf/jit.c b/drivers/net/ethernet/netronome/nfp/bpf/jit.c
index 3419ad495962..7afdc6d8e5ac 100644
--- a/drivers/net/ethernet/netronome/nfp/bpf/jit.c
+++ b/drivers/net/ethernet/netronome/nfp/bpf/jit.c
@@ -1930,6 +1930,15 @@ static int jne_reg(struct nfp_prog *nfp_prog, struct nfp_insn_meta *meta)
return wrp_test_reg(nfp_prog, meta, ALU_OP_XOR, BR_BNE);
}
+static int call(struct nfp_prog *nfp_prog, struct nfp_insn_meta *meta)
+{
+ switch (meta->insn.imm) {
+ default:
+ WARN_ONCE(1, "verifier allowed unsupported function\n");
+ return -EOPNOTSUPP;
+ }
+}
+
static int goto_out(struct nfp_prog *nfp_prog, struct nfp_insn_meta *meta)
{
wrp_br_special(nfp_prog, BR_UNC, OP_BR_GO_OUT);
@@ -2002,6 +2011,7 @@ static const instr_cb_t instr_cb[256] = {
[BPF_JMP | BPF_JLE | BPF_X] = jle_reg,
[BPF_JMP | BPF_JSET | BPF_X] = jset_reg,
[BPF_JMP | BPF_JNE | BPF_X] = jne_reg,
+ [BPF_JMP | BPF_CALL] = call,
[BPF_JMP | BPF_EXIT] = goto_out,
};
@@ -2026,6 +2036,8 @@ static int nfp_fixup_branches(struct nfp_prog *nfp_prog)
list_for_each_entry(meta, &nfp_prog->insns, l) {
if (meta->skip)
continue;
+ if (meta->insn.code == (BPF_JMP | BPF_CALL))
+ continue;
if (BPF_CLASS(meta->insn.code) != BPF_JMP)
continue;
diff --git a/drivers/net/ethernet/netronome/nfp/bpf/verifier.c b/drivers/net/ethernet/netronome/nfp/bpf/verifier.c
index d2bf29c90226..3b940b682ac3 100644
--- a/drivers/net/ethernet/netronome/nfp/bpf/verifier.c
+++ b/drivers/net/ethernet/netronome/nfp/bpf/verifier.c
@@ -68,6 +68,20 @@ nfp_bpf_goto_meta(struct nfp_prog *nfp_prog, struct nfp_insn_meta *meta,
return meta;
}
+static int
+nfp_bpf_check_call(struct nfp_prog *nfp_prog, struct nfp_insn_meta *meta)
+{
+ u32 func_id = meta->insn.imm;
+
+ switch (func_id) {
+ default:
+ pr_warn("unsupported function id: %d\n", func_id);
+ return -EOPNOTSUPP;
+ }
+
+ return 0;
+}
+
static int
nfp_bpf_check_exit(struct nfp_prog *nfp_prog,
struct bpf_verifier_env *env)
@@ -177,6 +191,8 @@ nfp_verify_insn(struct bpf_verifier_env *env, int insn_idx, int prev_insn_idx)
return -EINVAL;
}
+ if (meta->insn.code == (BPF_JMP | BPF_CALL))
+ return nfp_bpf_check_call(nfp_prog, meta);
if (meta->insn.code == (BPF_JMP | BPF_EXIT))
return nfp_bpf_check_exit(nfp_prog, env);
--
2.15.1
^ permalink raw reply related
* [PATCH bpf-next 4/5] nfp: bpf: add basic support for adjust head call
From: Jakub Kicinski @ 2017-12-15 5:29 UTC (permalink / raw)
To: netdev; +Cc: oss-drivers, daniel, alexei.starovoitov, Jakub Kicinski
In-Reply-To: <20171215052919.18343-1-jakub.kicinski@netronome.com>
Support bpf_xdp_adjust_head(). We need to check whether the
packet offset after adjustment is within datapath's limits.
We also check if the frame is at least ETH_HLEN long (similar
to the kernel implementation).
Signed-off-by: Jakub Kicinski <jakub.kicinski@netronome.com>
---
drivers/net/ethernet/netronome/nfp/bpf/fw.h | 54 +++++++++++++++++
drivers/net/ethernet/netronome/nfp/bpf/jit.c | 73 +++++++++++++++++++++++
drivers/net/ethernet/netronome/nfp/bpf/main.c | 38 ++++++++++++
drivers/net/ethernet/netronome/nfp/bpf/main.h | 11 ++++
drivers/net/ethernet/netronome/nfp/bpf/verifier.c | 12 ++++
drivers/net/ethernet/netronome/nfp/nfp_asm.h | 2 +
6 files changed, 190 insertions(+)
create mode 100644 drivers/net/ethernet/netronome/nfp/bpf/fw.h
diff --git a/drivers/net/ethernet/netronome/nfp/bpf/fw.h b/drivers/net/ethernet/netronome/nfp/bpf/fw.h
new file mode 100644
index 000000000000..7206aa1522db
--- /dev/null
+++ b/drivers/net/ethernet/netronome/nfp/bpf/fw.h
@@ -0,0 +1,54 @@
+/*
+ * Copyright (C) 2017 Netronome Systems, Inc.
+ *
+ * This software is dual licensed under the GNU General License Version 2,
+ * June 1991 as shown in the file COPYING in the top-level directory of this
+ * source tree or the BSD 2-Clause License provided below. You have the
+ * option to license this software under the complete terms of either license.
+ *
+ * The BSD 2-Clause License:
+ *
+ * Redistribution and use in source and binary forms, with or
+ * without modification, are permitted provided that the following
+ * conditions are met:
+ *
+ * 1. Redistributions of source code must retain the above
+ * copyright notice, this list of conditions and the following
+ * disclaimer.
+ *
+ * 2. Redistributions in binary form must reproduce the above
+ * copyright notice, this list of conditions and the following
+ * disclaimer in the documentation and/or other materials
+ * provided with the distribution.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+ * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+ * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+ * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
+ * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
+ * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
+ * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+ * SOFTWARE.
+ */
+
+#ifndef NFP_BPF_FW_H
+#define NFP_BPF_FW_H 1
+
+#include <linux/bitops.h>
+#include <linux/types.h>
+
+enum bpf_cap_tlv_type {
+ NFP_BPF_CAP_TYPE_ADJUST_HEAD = 2,
+};
+
+struct nfp_bpf_cap_tlv_adjust_head {
+ __le32 flags;
+ __le32 off_min;
+ __le32 off_max;
+ __le32 guaranteed_sub;
+ __le32 guaranteed_add;
+};
+
+#define NFP_BPF_ADJUST_HEAD_NO_META BIT(0)
+
+#endif
diff --git a/drivers/net/ethernet/netronome/nfp/bpf/jit.c b/drivers/net/ethernet/netronome/nfp/bpf/jit.c
index 7afdc6d8e5ac..4bfcb1f3def8 100644
--- a/drivers/net/ethernet/netronome/nfp/bpf/jit.c
+++ b/drivers/net/ethernet/netronome/nfp/bpf/jit.c
@@ -33,6 +33,7 @@
#define pr_fmt(fmt) "NFP net bpf: " fmt
+#include <linux/bug.h>
#include <linux/kernel.h>
#include <linux/bpf.h>
#include <linux/filter.h>
@@ -87,6 +88,18 @@ static unsigned int nfp_prog_current_offset(struct nfp_prog *nfp_prog)
return nfp_prog->start_off + nfp_prog->prog_len;
}
+static bool
+nfp_prog_confirm_current_offset(struct nfp_prog *nfp_prog, unsigned int off)
+{
+ /* If there is a recorded error we may have dropped instructions;
+ * that doesn't have to be due to translator bug, and the translation
+ * will fail anyway, so just return OK.
+ */
+ if (nfp_prog->error)
+ return true;
+ return !WARN_ON_ONCE(nfp_prog_current_offset(nfp_prog) != off);
+}
+
static unsigned int
nfp_prog_offset_to_index(struct nfp_prog *nfp_prog, unsigned int offset)
{
@@ -1196,6 +1209,64 @@ static void wrp_end32(struct nfp_prog *nfp_prog, swreg reg_in, u8 gpr_out)
SHF_SC_R_ROT, 16);
}
+static int adjust_head(struct nfp_prog *nfp_prog, struct nfp_insn_meta *meta)
+{
+ swreg tmp = imm_a(nfp_prog), tmp_len = imm_b(nfp_prog);
+ struct nfp_bpf_cap_adjust_head *adjust_head;
+ u32 ret_einval, end;
+
+ adjust_head = &nfp_prog->bpf->adjust_head;
+
+ ret_einval = nfp_prog_current_offset(nfp_prog) + 14;
+ end = ret_einval + 2;
+
+ /* We need to use a temp because offset is just a part of the pkt ptr */
+ emit_alu(nfp_prog, tmp,
+ reg_a(2 * 2), ALU_OP_ADD_2B, pptr_reg(nfp_prog));
+
+ /* Validate result will fit within FW datapath constraints */
+ emit_alu(nfp_prog, reg_none(),
+ tmp, ALU_OP_SUB, reg_imm(adjust_head->off_min));
+ emit_br(nfp_prog, BR_BLO, ret_einval, 0);
+ emit_alu(nfp_prog, reg_none(),
+ reg_imm(adjust_head->off_max), ALU_OP_SUB, tmp);
+ emit_br(nfp_prog, BR_BLO, ret_einval, 0);
+
+ /* Validate the length is at least ETH_HLEN */
+ emit_alu(nfp_prog, tmp_len,
+ plen_reg(nfp_prog), ALU_OP_SUB, reg_a(2 * 2));
+ emit_alu(nfp_prog, reg_none(),
+ tmp_len, ALU_OP_SUB, reg_imm(ETH_HLEN));
+ emit_br(nfp_prog, BR_BMI, ret_einval, 0);
+
+ /* Load the ret code */
+ wrp_immed(nfp_prog, reg_both(0), 0);
+ wrp_immed(nfp_prog, reg_both(1), 0);
+
+ /* Modify the packet metadata */
+ emit_ld_field(nfp_prog, pptr_reg(nfp_prog), 0x3, tmp, SHF_SC_NONE, 0);
+
+ /* Skip over the -EINVAL ret code (defer 2) */
+ emit_br_def(nfp_prog, end, 2);
+
+ emit_alu(nfp_prog, plen_reg(nfp_prog),
+ plen_reg(nfp_prog), ALU_OP_SUB, reg_a(2 * 2));
+ emit_alu(nfp_prog, pv_len(nfp_prog),
+ pv_len(nfp_prog), ALU_OP_SUB, reg_a(2 * 2));
+
+ /* return -EINVAL target */
+ if (!nfp_prog_confirm_current_offset(nfp_prog, ret_einval))
+ return -EINVAL;
+
+ wrp_immed(nfp_prog, reg_both(0), -22);
+ wrp_immed(nfp_prog, reg_both(1), ~0);
+
+ if (!nfp_prog_confirm_current_offset(nfp_prog, end))
+ return -EINVAL;
+
+ return 0;
+}
+
/* --- Callbacks --- */
static int mov_reg64(struct nfp_prog *nfp_prog, struct nfp_insn_meta *meta)
{
@@ -1933,6 +2004,8 @@ static int jne_reg(struct nfp_prog *nfp_prog, struct nfp_insn_meta *meta)
static int call(struct nfp_prog *nfp_prog, struct nfp_insn_meta *meta)
{
switch (meta->insn.imm) {
+ case BPF_FUNC_xdp_adjust_head:
+ return adjust_head(nfp_prog, meta);
default:
WARN_ONCE(1, "verifier allowed unsupported function\n");
return -EOPNOTSUPP;
diff --git a/drivers/net/ethernet/netronome/nfp/bpf/main.c b/drivers/net/ethernet/netronome/nfp/bpf/main.c
index f76bb40c20bc..bd4a1dcc58b3 100644
--- a/drivers/net/ethernet/netronome/nfp/bpf/main.c
+++ b/drivers/net/ethernet/netronome/nfp/bpf/main.c
@@ -39,6 +39,7 @@
#include "../nfp_main.h"
#include "../nfp_net.h"
#include "../nfp_port.h"
+#include "fw.h"
#include "main.h"
static bool nfp_net_ebpf_capable(struct nfp_net *nn)
@@ -156,6 +157,36 @@ static bool nfp_bpf_tc_busy(struct nfp_app *app, struct nfp_net *nn)
return nn->dp.ctrl & NFP_NET_CFG_CTRL_BPF;
}
+static int
+nfp_bpf_parse_cap_adjust_head(struct nfp_app_bpf *bpf, void __iomem *value,
+ u32 length)
+{
+ struct nfp_bpf_cap_tlv_adjust_head __iomem *cap = value;
+ struct nfp_cpp *cpp = bpf->app->pf->cpp;
+
+ if (length < sizeof(*cap)) {
+ nfp_err(cpp, "truncated adjust_head TLV: %d\n", length);
+ return -EINVAL;
+ }
+
+ bpf->adjust_head.flags = readl(&cap->flags);
+ bpf->adjust_head.off_min = readl(&cap->off_min);
+ bpf->adjust_head.off_max = readl(&cap->off_max);
+
+ if (bpf->adjust_head.off_min > bpf->adjust_head.off_max) {
+ nfp_err(cpp, "invalid adjust_head TLV: min > max\n");
+ return -EINVAL;
+ }
+ if (!FIELD_FIT(UR_REG_IMM_MAX, bpf->adjust_head.off_min) ||
+ !FIELD_FIT(UR_REG_IMM_MAX, bpf->adjust_head.off_max)) {
+ nfp_warn(cpp, "disabling adjust_head - driver expects min/max to fit in as immediates\n");
+ memset(&bpf->adjust_head, 0, sizeof(bpf->adjust_head));
+ return 0;
+ }
+
+ return 0;
+}
+
static int nfp_bpf_parse_capabilities(struct nfp_app *app)
{
struct nfp_cpp *cpp = app->pf->cpp;
@@ -169,16 +200,23 @@ static int nfp_bpf_parse_capabilities(struct nfp_app *app)
start = mem;
while (mem - start + 8 < nfp_cpp_area_size(area)) {
+ u8 __iomem *value;
u32 type, length;
type = readl(mem);
length = readl(mem + 4);
+ value = mem + 8;
mem += 8 + length;
if (mem - start > nfp_cpp_area_size(area))
goto err_release_free;
switch (type) {
+ case NFP_BPF_CAP_TYPE_ADJUST_HEAD:
+ if (nfp_bpf_parse_cap_adjust_head(app->priv, value,
+ length))
+ goto err_release_free;
+ break;
default:
nfp_dbg(cpp, "unknown BPF capability: %d\n", type);
break;
diff --git a/drivers/net/ethernet/netronome/nfp/bpf/main.h b/drivers/net/ethernet/netronome/nfp/bpf/main.h
index a1a3f96353df..00a46258fb6d 100644
--- a/drivers/net/ethernet/netronome/nfp/bpf/main.h
+++ b/drivers/net/ethernet/netronome/nfp/bpf/main.h
@@ -81,9 +81,20 @@ enum pkt_vec {
/**
* struct nfp_app_bpf - bpf app priv structure
* @app: backpointer to the app
+ *
+ * @adjust_head: adjust head capability
+ * @flags: extra flags for adjust head
+ * @off_min: minimal packet offset within buffer required
+ * @off_max: maximum packet offset within buffer required
*/
struct nfp_app_bpf {
struct nfp_app *app;
+
+ struct nfp_bpf_cap_adjust_head {
+ u32 flags;
+ int off_min;
+ int off_max;
+ } adjust_head;
};
struct nfp_prog;
diff --git a/drivers/net/ethernet/netronome/nfp/bpf/verifier.c b/drivers/net/ethernet/netronome/nfp/bpf/verifier.c
index 3b940b682ac3..0a457d98666c 100644
--- a/drivers/net/ethernet/netronome/nfp/bpf/verifier.c
+++ b/drivers/net/ethernet/netronome/nfp/bpf/verifier.c
@@ -38,6 +38,7 @@
#include <linux/kernel.h>
#include <linux/pkt_cls.h>
+#include "fw.h"
#include "main.h"
struct nfp_insn_meta *
@@ -71,9 +72,20 @@ nfp_bpf_goto_meta(struct nfp_prog *nfp_prog, struct nfp_insn_meta *meta,
static int
nfp_bpf_check_call(struct nfp_prog *nfp_prog, struct nfp_insn_meta *meta)
{
+ struct nfp_app_bpf *bpf = nfp_prog->bpf;
u32 func_id = meta->insn.imm;
switch (func_id) {
+ case BPF_FUNC_xdp_adjust_head:
+ if (!bpf->adjust_head.off_max) {
+ pr_warn("adjust_head not supported by FW\n");
+ return -EOPNOTSUPP;
+ }
+ if (!(bpf->adjust_head.flags & NFP_BPF_ADJUST_HEAD_NO_META)) {
+ pr_warn("adjust_head: FW requires shifting metadata, not supported by the driver\n");
+ return -EOPNOTSUPP;
+ }
+ break;
default:
pr_warn("unsupported function id: %d\n", func_id);
return -EOPNOTSUPP;
diff --git a/drivers/net/ethernet/netronome/nfp/nfp_asm.h b/drivers/net/ethernet/netronome/nfp/nfp_asm.h
index 3387e6926eb0..a24daeab1a77 100644
--- a/drivers/net/ethernet/netronome/nfp/nfp_asm.h
+++ b/drivers/net/ethernet/netronome/nfp/nfp_asm.h
@@ -77,6 +77,7 @@
enum br_mask {
BR_BEQ = 0x00,
BR_BNE = 0x01,
+ BR_BMI = 0x02,
BR_BHS = 0x04,
BR_BLO = 0x05,
BR_BGE = 0x08,
@@ -175,6 +176,7 @@ enum alu_op {
ALU_OP_NONE = 0x00,
ALU_OP_ADD = 0x01,
ALU_OP_NOT = 0x04,
+ ALU_OP_ADD_2B = 0x05,
ALU_OP_AND = 0x08,
ALU_OP_SUB_C = 0x0d,
ALU_OP_ADD_C = 0x11,
--
2.15.1
^ permalink raw reply related
* [PATCH bpf-next 5/5] nfp: bpf: optimize the adjust_head calls in trivial cases
From: Jakub Kicinski @ 2017-12-15 5:29 UTC (permalink / raw)
To: netdev; +Cc: oss-drivers, daniel, alexei.starovoitov, Jakub Kicinski
In-Reply-To: <20171215052919.18343-1-jakub.kicinski@netronome.com>
If the program is simple and has only one adjust head call
with constant parameters, we can check that the call will
always succeed at translation time. We need to track the
location of the call and make sure parameters are always
the same. We also have to check the parameters against
datapath constraints and ETH_HLEN.
Signed-off-by: Jakub Kicinski <jakub.kicinski@netronome.com>
---
drivers/net/ethernet/netronome/nfp/bpf/jit.c | 22 +++++++++++
drivers/net/ethernet/netronome/nfp/bpf/main.c | 2 +
drivers/net/ethernet/netronome/nfp/bpf/main.h | 8 ++++
drivers/net/ethernet/netronome/nfp/bpf/verifier.c | 46 ++++++++++++++++++++++-
4 files changed, 76 insertions(+), 2 deletions(-)
diff --git a/drivers/net/ethernet/netronome/nfp/bpf/jit.c b/drivers/net/ethernet/netronome/nfp/bpf/jit.c
index 4bfcb1f3def8..0de59f04da84 100644
--- a/drivers/net/ethernet/netronome/nfp/bpf/jit.c
+++ b/drivers/net/ethernet/netronome/nfp/bpf/jit.c
@@ -1217,6 +1217,28 @@ static int adjust_head(struct nfp_prog *nfp_prog, struct nfp_insn_meta *meta)
adjust_head = &nfp_prog->bpf->adjust_head;
+ /* Optimized version - 5 vs 14 cycles */
+ if (nfp_prog->adjust_head_location != UINT_MAX) {
+ if (WARN_ON_ONCE(nfp_prog->adjust_head_location != meta->n))
+ return -EINVAL;
+
+ emit_alu(nfp_prog, pptr_reg(nfp_prog),
+ reg_a(2 * 2), ALU_OP_ADD, pptr_reg(nfp_prog));
+ emit_alu(nfp_prog, plen_reg(nfp_prog),
+ plen_reg(nfp_prog), ALU_OP_SUB, reg_a(2 * 2));
+ emit_alu(nfp_prog, pv_len(nfp_prog),
+ pv_len(nfp_prog), ALU_OP_SUB, reg_a(2 * 2));
+
+ wrp_immed(nfp_prog, reg_both(0), 0);
+ wrp_immed(nfp_prog, reg_both(1), 0);
+
+ /* TODO: when adjust head is guaranteed to succeed we can
+ * also eliminate the following if (r0 == 0) branch.
+ */
+
+ return 0;
+ }
+
ret_einval = nfp_prog_current_offset(nfp_prog) + 14;
end = ret_einval + 2;
diff --git a/drivers/net/ethernet/netronome/nfp/bpf/main.c b/drivers/net/ethernet/netronome/nfp/bpf/main.c
index bd4a1dcc58b3..7678e687a2b1 100644
--- a/drivers/net/ethernet/netronome/nfp/bpf/main.c
+++ b/drivers/net/ethernet/netronome/nfp/bpf/main.c
@@ -172,6 +172,8 @@ nfp_bpf_parse_cap_adjust_head(struct nfp_app_bpf *bpf, void __iomem *value,
bpf->adjust_head.flags = readl(&cap->flags);
bpf->adjust_head.off_min = readl(&cap->off_min);
bpf->adjust_head.off_max = readl(&cap->off_max);
+ bpf->adjust_head.guaranteed_sub = readl(&cap->guaranteed_sub);
+ bpf->adjust_head.guaranteed_add = readl(&cap->guaranteed_add);
if (bpf->adjust_head.off_min > bpf->adjust_head.off_max) {
nfp_err(cpp, "invalid adjust_head TLV: min > max\n");
diff --git a/drivers/net/ethernet/netronome/nfp/bpf/main.h b/drivers/net/ethernet/netronome/nfp/bpf/main.h
index 00a46258fb6d..f49669bf6b44 100644
--- a/drivers/net/ethernet/netronome/nfp/bpf/main.h
+++ b/drivers/net/ethernet/netronome/nfp/bpf/main.h
@@ -86,6 +86,8 @@ enum pkt_vec {
* @flags: extra flags for adjust head
* @off_min: minimal packet offset within buffer required
* @off_max: maximum packet offset within buffer required
+ * @guaranteed_sub: amount of negative adjustment guaranteed possible
+ * @guaranteed_add: amount of positive adjustment guaranteed possible
*/
struct nfp_app_bpf {
struct nfp_app *app;
@@ -94,6 +96,8 @@ struct nfp_app_bpf {
u32 flags;
int off_min;
int off_max;
+ int guaranteed_sub;
+ int guaranteed_add;
} adjust_head;
};
@@ -116,6 +120,7 @@ typedef int (*instr_cb_t)(struct nfp_prog *, struct nfp_insn_meta *);
* @ptr: pointer type for memory operations
* @ldst_gather_len: memcpy length gathered from load/store sequence
* @paired_st: the paired store insn at the head of the sequence
+ * @arg2: arg2 for call instructions
* @ptr_not_const: pointer is not always constant
* @jmp_dst: destination info for jump instructions
* @off: index of first generated machine instruction (in nfp_prog.prog)
@@ -135,6 +140,7 @@ struct nfp_insn_meta {
bool ptr_not_const;
};
struct nfp_insn_meta *jmp_dst;
+ struct bpf_reg_state arg2;
};
unsigned int off;
unsigned short n;
@@ -193,6 +199,7 @@ static inline bool is_mbpf_store(const struct nfp_insn_meta *meta)
* @n_translated: number of successfully translated instructions (for errors)
* @error: error code if something went wrong
* @stack_depth: max stack depth from the verifier
+ * @adjust_head_location: if program has single adjust head call - the insn no.
* @insns: list of BPF instruction wrappers (struct nfp_insn_meta)
*/
struct nfp_prog {
@@ -216,6 +223,7 @@ struct nfp_prog {
int error;
unsigned int stack_depth;
+ unsigned int adjust_head_location;
struct list_head insns;
};
diff --git a/drivers/net/ethernet/netronome/nfp/bpf/verifier.c b/drivers/net/ethernet/netronome/nfp/bpf/verifier.c
index 0a457d98666c..9c2608445bd8 100644
--- a/drivers/net/ethernet/netronome/nfp/bpf/verifier.c
+++ b/drivers/net/ethernet/netronome/nfp/bpf/verifier.c
@@ -69,9 +69,47 @@ nfp_bpf_goto_meta(struct nfp_prog *nfp_prog, struct nfp_insn_meta *meta,
return meta;
}
+static void
+nfp_record_adjust_head(struct nfp_app_bpf *bpf, struct nfp_prog *nfp_prog,
+ struct nfp_insn_meta *meta,
+ const struct bpf_reg_state *reg2)
+{
+ unsigned int location = UINT_MAX;
+ int imm;
+
+ /* Datapath usually can give us guarantees on how much adjust head
+ * can be done without the need for any checks. Optimize the simple
+ * case where there is only one adjust head by a constant.
+ */
+ if (reg2->type != SCALAR_VALUE || !tnum_is_const(reg2->var_off))
+ goto exit_set_location;
+ imm = reg2->var_off.value;
+ /* Translator will skip all checks, we need to guarantee min pkt len */
+ if (imm > ETH_ZLEN - ETH_HLEN)
+ goto exit_set_location;
+ if (imm > (int)bpf->adjust_head.guaranteed_add ||
+ imm < -bpf->adjust_head.guaranteed_sub)
+ goto exit_set_location;
+
+ if (nfp_prog->adjust_head_location) {
+ /* Only one call per program allowed */
+ if (nfp_prog->adjust_head_location != meta->n)
+ goto exit_set_location;
+
+ if (meta->arg2.var_off.value != imm)
+ goto exit_set_location;
+ }
+
+ location = meta->n;
+exit_set_location:
+ nfp_prog->adjust_head_location = location;
+}
+
static int
-nfp_bpf_check_call(struct nfp_prog *nfp_prog, struct nfp_insn_meta *meta)
+nfp_bpf_check_call(struct nfp_prog *nfp_prog, struct bpf_verifier_env *env,
+ struct nfp_insn_meta *meta)
{
+ const struct bpf_reg_state *reg2 = cur_regs(env) + BPF_REG_2;
struct nfp_app_bpf *bpf = nfp_prog->bpf;
u32 func_id = meta->insn.imm;
@@ -85,12 +123,16 @@ nfp_bpf_check_call(struct nfp_prog *nfp_prog, struct nfp_insn_meta *meta)
pr_warn("adjust_head: FW requires shifting metadata, not supported by the driver\n");
return -EOPNOTSUPP;
}
+
+ nfp_record_adjust_head(bpf, nfp_prog, meta, reg2);
break;
default:
pr_warn("unsupported function id: %d\n", func_id);
return -EOPNOTSUPP;
}
+ meta->arg2 = *reg2;
+
return 0;
}
@@ -204,7 +246,7 @@ nfp_verify_insn(struct bpf_verifier_env *env, int insn_idx, int prev_insn_idx)
}
if (meta->insn.code == (BPF_JMP | BPF_CALL))
- return nfp_bpf_check_call(nfp_prog, meta);
+ return nfp_bpf_check_call(nfp_prog, env, meta);
if (meta->insn.code == (BPF_JMP | BPF_EXIT))
return nfp_bpf_check_exit(nfp_prog, env);
--
2.15.1
^ permalink raw reply related
* Re: [RFC][PATCH] Add primitives for manipulating bitfields both in host- and fixed-endian.
From: Al Viro @ 2017-12-15 5:34 UTC (permalink / raw)
To: Jakub Kicinski; +Cc: Linus Torvalds, netdev, linux-kernel
In-Reply-To: <20171214210713.6be03d43@cakuba.netronome.com>
On Thu, Dec 14, 2017 at 09:07:13PM -0800, Jakub Kicinski wrote:
> Looks great to me!
>
> On Fri, 15 Dec 2017 02:33:43 +0000, Al Viro wrote:
> > The following primitives are defined in linux/bitfield.h:
> >
> > * u32 le32_get_bits(__le32 val, u32 field) extracts the contents of the
> > bitfield specified by @field in little-endian 32bit value @val and
> > converts it to host-endian.
> >
> > * void le32p_replace_bits(__le32 *p, u32 v, u32 field) replaces
> > the contents of the bitfield specified by @field in little-endian
> > 32bit object pointet to by *p with the value of @v. New value is
> > given in host-endian and stored as little-endian.
> >
> > * __le32 le32_replace_bits(__le32 old, u32 v, u32 field) is equivalent to
> > ({__le32 tmp = old; le32p_replace_bits(&old, v, field); tmp;})
> > In other words, instead of modifying an object in memory, it takes
> > the initial value and returns the modified one.
>
> the current macros take filed/mask as first param, not sure if it's
> worth maintaining the order
Umm... For something like Haskell that would be more natural (as in
replace_foo = replace_field foo), but it's C - no partially applied
functions here...
While we are at it, to cover the FIELD_PREP users it might make sense to
add
__le32 le32_encode_bits(u32 v, u32 field)
{
if (__builtin_constant_p(v) &&
(v & ~(field/mask_to_multiplier(field))))
__field_overflow();
return cpu_to_le32((v * mask_to_multiplier(field)) & field);
}
turning the body of le32_replace_bits into
return (old & ~cpu_to_le32(field)) | le32_encode_bits(v, field);
^ permalink raw reply
* xfrm: Reinject transport-mode packets through tasklet
From: Herbert Xu @ 2017-12-15 5:40 UTC (permalink / raw)
To: Steffen Klassert, netdev
This is an old bugbear of mine:
https://www.mail-archive.com/netdev@vger.kernel.org/msg03894.html
By crafting special packets, it is possible to cause recursion
in our kernel when processing transport-mode packets at levels
that are only limited by packet size.
The easiest one is with DNAT, but an even worse one is where
UDP encapsulation is used in which case you just have to insert
an UDP encapsulation header in between each level of recursion.
This patch avoids this problem by reinjecting tranport-mode packets
through a tasklet.
Fixes: b05e106698d9 ("[IPV4/6]: Netfilter IPsec input hooks")
Signed-off-by: Herbert Xu <herbert@gondor.apana.org.au>
diff --git a/include/net/xfrm.h b/include/net/xfrm.h
index dc28a98..ae35991 100644
--- a/include/net/xfrm.h
+++ b/include/net/xfrm.h
@@ -1570,6 +1570,9 @@ struct xfrmk_spdinfo {
int xfrm_prepare_input(struct xfrm_state *x, struct sk_buff *skb);
int xfrm_input(struct sk_buff *skb, int nexthdr, __be32 spi, int encap_type);
int xfrm_input_resume(struct sk_buff *skb, int nexthdr);
+int xfrm_trans_queue(struct sk_buff *skb,
+ int (*finish)(struct net *, struct sock *,
+ struct sk_buff *));
int xfrm_output_resume(struct sk_buff *skb, int err);
int xfrm_output(struct sock *sk, struct sk_buff *skb);
int xfrm_inner_extract_output(struct xfrm_state *x, struct sk_buff *skb);
diff --git a/net/ipv4/xfrm4_input.c b/net/ipv4/xfrm4_input.c
index e50b7fe..bcfc00e 100644
--- a/net/ipv4/xfrm4_input.c
+++ b/net/ipv4/xfrm4_input.c
@@ -23,6 +23,12 @@ int xfrm4_extract_input(struct xfrm_state *x, struct sk_buff *skb)
return xfrm4_extract_header(skb);
}
+static int xfrm4_rcv_encap_finish2(struct net *net, struct sock *sk,
+ struct sk_buff *skb)
+{
+ return dst_input(skb);
+}
+
static inline int xfrm4_rcv_encap_finish(struct net *net, struct sock *sk,
struct sk_buff *skb)
{
@@ -33,7 +39,11 @@ static inline int xfrm4_rcv_encap_finish(struct net *net, struct sock *sk,
iph->tos, skb->dev))
goto drop;
}
- return dst_input(skb);
+
+ if (xfrm_trans_queue(skb, xfrm4_rcv_encap_finish2))
+ goto drop;
+
+ return 0;
drop:
kfree_skb(skb);
return NET_RX_DROP;
diff --git a/net/ipv6/xfrm6_input.c b/net/ipv6/xfrm6_input.c
index fe04e23..841f4a0 100644
--- a/net/ipv6/xfrm6_input.c
+++ b/net/ipv6/xfrm6_input.c
@@ -32,6 +32,14 @@ int xfrm6_rcv_spi(struct sk_buff *skb, int nexthdr, __be32 spi,
}
EXPORT_SYMBOL(xfrm6_rcv_spi);
+static int xfrm6_transport_finish2(struct net *net, struct sock *sk,
+ struct sk_buff *skb)
+{
+ if (xfrm_trans_queue(skb, ip6_rcv_finish))
+ __kfree_skb(skb);
+ return -1;
+}
+
int xfrm6_transport_finish(struct sk_buff *skb, int async)
{
struct xfrm_offload *xo = xfrm_offload(skb);
@@ -56,7 +64,7 @@ int xfrm6_transport_finish(struct sk_buff *skb, int async)
NF_HOOK(NFPROTO_IPV6, NF_INET_PRE_ROUTING,
dev_net(skb->dev), NULL, skb, skb->dev, NULL,
- ip6_rcv_finish);
+ xfrm6_transport_finish2);
return -1;
}
diff --git a/net/xfrm/xfrm_input.c b/net/xfrm/xfrm_input.c
index 347ab31..444fa37 100644
--- a/net/xfrm/xfrm_input.c
+++ b/net/xfrm/xfrm_input.c
@@ -8,15 +8,29 @@
*
*/
+#include <linux/bottom_half.h>
+#include <linux/interrupt.h>
#include <linux/slab.h>
#include <linux/module.h>
#include <linux/netdevice.h>
+#include <linux/percpu.h>
#include <net/dst.h>
#include <net/ip.h>
#include <net/xfrm.h>
#include <net/ip_tunnels.h>
#include <net/ip6_tunnel.h>
+struct xfrm_trans_tasklet {
+ struct tasklet_struct tasklet;
+ struct sk_buff_head queue;
+};
+
+struct xfrm_trans_cb {
+ int (*finish)(struct net *net, struct sock *sk, struct sk_buff *skb);
+};
+
+#define XFRM_TRANS_SKB_CB(__skb) ((struct xfrm_trans_cb *)&((__skb)->cb[0]))
+
static struct kmem_cache *secpath_cachep __read_mostly;
static DEFINE_SPINLOCK(xfrm_input_afinfo_lock);
@@ -25,6 +39,8 @@
static struct gro_cells gro_cells;
static struct net_device xfrm_napi_dev;
+static DEFINE_PER_CPU(struct xfrm_trans_tasklet, xfrm_trans_tasklet);
+
int xfrm_input_register_afinfo(const struct xfrm_input_afinfo *afinfo)
{
int err = 0;
@@ -467,9 +483,41 @@ int xfrm_input_resume(struct sk_buff *skb, int nexthdr)
}
EXPORT_SYMBOL(xfrm_input_resume);
+static void xfrm_trans_reinject(unsigned long data)
+{
+ struct xfrm_trans_tasklet *trans = (void *)data;
+ struct sk_buff_head queue;
+ struct sk_buff *skb;
+
+ __skb_queue_head_init(&queue);
+ skb_queue_splice_init(&trans->queue, &queue);
+
+ while ((skb = __skb_dequeue(&queue)))
+ XFRM_TRANS_SKB_CB(skb)->finish(dev_net(skb->dev), NULL, skb);
+}
+
+int xfrm_trans_queue(struct sk_buff *skb,
+ int (*finish)(struct net *, struct sock *,
+ struct sk_buff *))
+{
+ struct xfrm_trans_tasklet *trans;
+
+ trans = this_cpu_ptr(&xfrm_trans_tasklet);
+
+ if (skb_queue_len(&trans->queue) >= netdev_max_backlog)
+ return -ENOBUFS;
+
+ XFRM_TRANS_SKB_CB(skb)->finish = finish;
+ skb_queue_tail(&trans->queue, skb);
+ tasklet_schedule(&trans->tasklet);
+ return 0;
+}
+EXPORT_SYMBOL(xfrm_trans_queue);
+
void __init xfrm_input_init(void)
{
int err;
+ int i;
init_dummy_netdev(&xfrm_napi_dev);
err = gro_cells_init(&gro_cells, &xfrm_napi_dev);
@@ -480,4 +528,13 @@ void __init xfrm_input_init(void)
sizeof(struct sec_path),
0, SLAB_HWCACHE_ALIGN|SLAB_PANIC,
NULL);
+
+ for_each_possible_cpu(i) {
+ struct xfrm_trans_tasklet *trans;
+
+ trans = &per_cpu(xfrm_trans_tasklet, i);
+ __skb_queue_head_init(&trans->queue);
+ tasklet_init(&trans->tasklet, xfrm_trans_reinject,
+ (unsigned long)trans);
+ }
}
--
Email: Herbert Xu <herbert@gondor.apana.org.au>
Home Page: http://gondor.apana.org.au/~herbert/
PGP Key: http://gondor.apana.org.au/~herbert/pubkey.txt
^ permalink raw reply related
* Re: Linux 4.14 - regression: broken tun/tap / bridge network with virtio - bisected
From: Andreas Hartmann @ 2017-12-15 6:05 UTC (permalink / raw)
To: Willem de Bruijn
Cc: Michal Kubecek, Jason Wang, David Miller, Network Development
In-Reply-To: <CAF=yD-JnDSfH-2wMjN5BUVdXeezp+k4ievSuvVCvEjX-jncD7Q@mail.gmail.com>
On 12/14/2017 at 11:17 PM Willem de Bruijn wrote:
>>> Well, the patch does not fix hanging VMs, which have been shutdown and
>>> can't be killed any more.
>>> Because of the stack trace
>>>
>>> [<ffffffffc0d0e3c5>] vhost_net_ubuf_put_and_wait+0x35/0x60 [vhost_net]
>>> [<ffffffffc0d0f264>] vhost_net_ioctl+0x304/0x870 [vhost_net]
>>> [<ffffffff9b25460f>] do_vfs_ioctl+0x8f/0x5c0
>>> [<ffffffff9b254bb4>] SyS_ioctl+0x74/0x80
>>> [<ffffffff9b00365b>] do_syscall_64+0x5b/0x100
>>> [<ffffffff9b78e7ab>] entry_SYSCALL64_slow_path+0x25/0x25
>>> [<ffffffffffffffff>] 0xffffffffffffffff
>>>
>>> I was hoping, that the problems could be related - but that seems not to
>>> be true.
>>
>> However, it turned out, that reverting the complete patchset "Remove UDP
>> Fragmentation Offload support" prevent hanging qemu processes.
>
> That implies a combination of UFO and vhost zerocopy. Disabling
> experimental_zcopytx in vhost_net will probably work around the bug
> then.
I already tested it w/ options vhost_net experimental_zcopytx=0 - but
this didn't "resolve" anything. See
https://www.mail-archive.com/netdev@vger.kernel.org/msg203197.html
Therefore, I think your following thoughts are lapsed unfortunately,
aren't they?
> On the surface the two features are independent. Most of the relevant
> UFO code is reverted with the patch mentioned earlier. Missing from
> that is protocol stack support, but it is unlikely that your host OS is
> generating these UFO packets.
>
> They are coming from a guest over virtio_net, to which vhost_net then
> applies zerocopy. Then the packet(s) is/are either freed without calling
> uarg->callback() or queued somewhere for a very long time.
>
> Looking at the diff-of-diffs between my stable patch and your full revert,
> the majority of missing bits beside the procol layer is in device driver
> support. Removing that causes the UFO packets to be segmented at any
> dev_queue_xmit on their path. skb_segment ensures that when it segments
> a large zerocopy packet, all new segments also point to the zerocopy
> callback struct (ubuf_info), as the shared memory pages may not be
> released until all skbs pointing to them are freed.
>
> That may be wrong with vhost_zerocopy_callback, which does not use
> refcounting. I will look into that. It may be that before the msg_zerocopy
> patchsets large packets were copied before entering segmentation. It is
> safe to enter segmentation for msg_zerocopy skbs, but not legacy zerocopy
> skbs.
>
> I will also set up two VMs and try to send UFO packets and see whether
> they indeed are freed somewhere in the stack without notifying vhost_net.
Thanks,
Andreas
^ permalink raw reply
* Re: [PATCH net-next] net/ncsi: Don't take any action on HNCDSC AEN
From: Jeremy Kerr @ 2017-12-15 6:06 UTC (permalink / raw)
To: Samuel Mendoza-Jonas, netdev
Cc: openbmc, David S . Miller, linux-kernel, Brian J King
In-Reply-To: <20171215051640.10926-1-sam@mendozajonas.com>
Hi Sam,
> The current HNCDSC handler takes the status flag from the AEN packet and
> will update or change the current channel based on this flag and the
> current channel status.
>
> However the flag from the HNCDSC packet merely represents the host link
> state. While the state of the host interface is potentially interesting
> information it should not affect the state of the NCSI link. Indeed the
> NCSI specification makes no mention of any recommended action related to
> the host network controller driver state.
Yep, sounds good to me. If the link status does change, we should see an
separate AEN for that event.
Acked-by: Jeremy Kerr <jk@ozlabs.org>
However: we're looking at some behaviour of Broadcom NICs at the moment,
where the phy will be reset on link change events. We'd want to make
sure that we're not just seeing the HNCDSC events for that too. I'd
suggest we also get an Ack from Brian (CCed) to make sure we're not
breaking that case.
Cheers,
Jeremy
^ permalink raw reply
* [PATCH] net: phy: marvell: enable a errata for 88E1145
From: Zhao Qiang @ 2017-12-15 6:13 UTC (permalink / raw)
To: andrew, f.fainelli; +Cc: netdev, Zhao Qiang
The patch below
commit f2899788353c ("net: phy: marvell: Limit errata to 88m1101")
limit a errata's scope to 88E1101.
However, 88E1145 also need this errata, set config_aneg to
m88e1101_config_aneg for 88E1145
Signed-off-by: Zhao Qiang <qiang.zhao@nxp.com>
---
drivers/net/phy/marvell.c | 2 +-
1 files changed, 1 insertions(+), 1 deletions(-)
diff --git a/drivers/net/phy/marvell.c b/drivers/net/phy/marvell.c
index 4d02b27..a3f456b 100644
--- a/drivers/net/phy/marvell.c
+++ b/drivers/net/phy/marvell.c
@@ -2069,7 +2069,7 @@ static int m88e1510_probe(struct phy_device *phydev)
.flags = PHY_HAS_INTERRUPT,
.probe = marvell_probe,
.config_init = &m88e1145_config_init,
- .config_aneg = &marvell_config_aneg,
+ .config_aneg = &m88e1101_config_aneg,
.read_status = &genphy_read_status,
.ack_interrupt = &marvell_ack_interrupt,
.config_intr = &marvell_config_intr,
--
1.7.1
^ permalink raw reply related
* [net-next] phylib: Add device reset GPIO support causes DSA MT7530 acquires reset-gpios fails
From: Sean Wang @ 2017-12-15 6:55 UTC (permalink / raw)
To: sergei.shtylyov, andrew, f.fainelli, vivien.didelot
Cc: davem, netdev, linux-kernel, linux-mediatek, richard.leitner,
geert+renesas
Hi Sergei,
Recently I found the patch commit bafbdd527d56 (phylib: Add device reset
GPIO support) would have the impact on MT7530 driver. Which causes the
DSA MT7530 device (it's the child node under mdio bus) gets the
reset-gpios fails because the same GPIO seems already be held in the
earlier mdiobus_register_device call patched through the commit.
do you have any idea how the commits also considers DSA case ?
I guessed the DSA lan9303, mv88e8 switch should have the same issue
since they have the same GPIO name as mdiobus_register_device required.
drivers/net/dsa/lan9303-core.c:1303: chip->reset_gpio =
devm_gpiod_get_optional(chip->dev, "reset",
drivers/net/dsa/mv88e6xxx/chip.c:3936: chip->reset =
devm_gpiod_get_optional(dev, "reset", GPIOD_OUT_LOW);
drivers/net/dsa/mt7530.c:1476: priv->reset =
devm_gpiod_get_optional(&mdiodev->dev, "reset",
Below is the stack dump I added in of_get_named_gpiod_flags call. Which
apparently shows mdiobus_register_device would get gpio at first and
then DSA driver does.
[ 2.570607] [<c010d878>] (show_stack) from [<c073149c>] (dump_stack
+0x98/0xac)
[ 2.577779] [<c073149c>] (dump_stack) from [<c03ed3cc>]
(of_get_named_gpiod_flags+0xe8/0x128)
[ 2.586239] [<c03ed3cc>] (of_get_named_gpiod_flags) from [<c03ecbb4>]
(fwnode_get_named_gpiod+0x5c/0xd0)
[ 2.595648] [<c03ecbb4>] (fwnode_get_named_gpiod) from [<c04e3d00>]
(mdiobus_register_device+0x64/0xa8)
[ 2.604969] [<c04e3d00>] (mdiobus_register_device) from [<c04e4ca4>]
(mdio_device_register+0x34/0x8c)
[ 2.614119] [<c04e4ca4>] (mdio_device_register) from [<c05b9ff0>]
(of_mdiobus_register+0x150/0x2c8)
[ 2.623097] [<c05b9ff0>] (of_mdiobus_register) from [<c04ea680>]
(mtk_probe+0x6a4/0x820)
[ 2.631128] [<c04ea680>] (mtk_probe) from [<c04688e4>]
(platform_drv_probe+0x5c/0xc0)
[ 2.638898] [<c04688e4>] (platform_drv_probe) from [<c0466558>]
(driver_probe_device+0x2f4/0x4d8)
[ 2.647702] [<c0466558>] (driver_probe_device) from [<c0466848>]
(__driver_attach+0x10c/0x128)
[ 2.656248] [<c0466848>] (__driver_attach) from [<c046408c>]
(bus_for_each_dev+0x78/0xac)
[ 2.664364] [<c046408c>] (bus_for_each_dev) from [<c0465cc0>]
(driver_attach+0x2c/0x30)
[ 2.672307] [<c0465cc0>] (driver_attach) from [<c0465688>]
(bus_add_driver+0x1e0/0x278)
[ 2.680250] [<c0465688>] (bus_add_driver) from [<c0467574>]
(driver_register+0x88/0x108)would
[ 2.688277] [<c0467574>] (driver_register) from [<c0468834>]
(__platform_driver_register+0x50/0x58)
[ 2.697256] [<c0468834>] (__platform_driver_register) from
[<c0b34780>] (mtk_driver_init+0x24/0x28)
[ 2.706234] [<c0b34780>] (mtk_driver_init) from [<c0101b98>]
(do_one_initcall+0x50/0x17c)
[ 2.714351] [<c0101b98>] (do_one_initcall) from [<c0b00f70>]
(kernel_init_freeable+0x154/0x1f4)
[ 2.722984] [<c0b00f70>] (kernel_init_freeable) from [<c0746b1c>]
(kernel_init+0x18/0x124)
[ 2.731185] [<c0746b1c>] (kernel_init) from [<c0108e08>]
(ret_from_fork+0x14/0x2c)
[ 2.738719] of_get_named_gpiod_flags: parsed 'reset-gpios' property
of node '/ethernet@1b100000/mdio-bus/switch@0[0]' - status (0)
[ 2.750631] mt7530 mdio-bus:00: GPIO lookup for consumer reset
[ 2.756443] mt7530 mdio-bus:00: using device tree for GPIO lookup
[ 2.762494] CPU: 0 PID: 1 Comm: swapper/0 Not tainted
4.15.0-rc2-00819-g4d89425-dirty #6
[ 2.770514] Hardware name: Mediatek Cortex-A7 (Device Tree)
[ 2.776049] [<c0113390>] (unwind_backtrace) from [<c010d878>]
(show_stack+0x20/0x24)
[ 2.783733] [<c010d878>] (show_stack) from [<c073149c>] (dump_stack
+0x98/0xac)
[ 2.790901] [<c073149c>] (dump_stack) from [<c03ed3cc>]
(of_get_named_gpiod_flags+0xe8/0x128)
[ 2.799359] [<c03ed3cc>] (of_get_named_gpiod_flags) from [<c03ed4a8>]
(of_find_gpio+0x70/0xfc)
[ 2.807903] [<c03ed4a8>] (of_find_gpio) from [<c03ec7f8>]
(gpiod_get_index+0xac/0x2a0)
[ 2.815759] [<c03ec7f8>] (gpiod_get_index) from [<c03e6dd4>]
(devm_gpiod_get_index+0x5c/0x98)
[ 2.824220] [<c03e6dd4>] (devm_gpiod_get_index) from [<c03e6e80>]
(devm_gpiod_get_optional+0x20/0wouldx34)
[ 2.833370] [<c03e6e80>] (devm_gpiod_get_optional) from [<c04e6e04>]
(mt7530_probe+0x170/0x1a0)
[ 2.842003] [<c04e6e04>] (mt7530_probe) from [<c04e4b98>] (mdio_probe
+0x40/0x64)
[ 2.849342] [<c04e4b98>] (mdio_probe) from [<c0466558>]
(driver_probe_device+0x2f4/0x4d8)
[ 2.857455] [<c0466558>] (driver_probe_device) from [<c046692c>]
(__device_attach_driver+0xc8/0x144)
[ 2.866518] [<c046692c>] (__device_attach_driver) from [<c046415c>]
(bus_for_each_drv+0x70/0xa4)geert+renesas@glider.be
[ 2.875235] [<c046415c>] (bus_for_each_drv) from [<c04660b4>]
(__device_attach+0xc0/0x150)
[ 2.883434] [<c04660b4>] (__device_attach) from [<c0466a04>]
(device_initial_probe+0x1c/0x20)
[ 2.891893] [<c0466a04>] (device_initial_probe) from [<c0465358>]
(bus_probe_device+0x94/0x9c)
[ 2.900440] [<c0465358>] (bus_probe_device) from [<c0463078>]
(device_add+0x374/0x5c0)
[ 2.908297] [<c0463078>] (device_add) from [<c04e4cb4>]
(mdio_device_register+0x44/0x8c)
[ 2.916326] [<c04e4cb4>] (mdio_device_register) from [<c05b9ff0>]
(of_mdiobus_register+0x150/0x2c8)
[ 2.925302] [<c05b9ff0>] (of_mdiobus_register) from [<c04ea680>]
(mtk_probe+0x6a4/0x820)
and the related dts I used is as below
would
mdio: mdio-bus {
#address-cells = <1>;
#size-cells = <0>;
switch@0 {
compatible = "mediatek,mt7530";
#address-cells = <1>;
#size-cells = <0>;
reg = <0>;
reset-gpios = <&pio 33 0>;
core-supply = <&mt6323_vpa_reg>;
io-supply = <&mt6323_vemc3v3_reg>;
ports {
#address-cells = <1>;
#size-cells = <0>;
reg = <0>;
port@0 {
reg = <0>;
label = "wan";
};
Sean
^ permalink raw reply
* RE: [PATCH net-next v4 4/5] bnx2x: Use NETIF_F_GRO_HW.
From: Chopra, Manish @ 2017-12-15 7:07 UTC (permalink / raw)
To: Michael Chan
Cc: davem@davemloft.net, netdev@vger.kernel.org,
andrew.gospodarek@broadcom.com, Elior, Ariel,
Dept-Eng Everest Linux L2
In-Reply-To: <CACKFLi=nXxt6CzZOySUMqcwwJyS5zdruFA7V0tKqLDds=osiUw@mail.gmail.com>
> -----Original Message-----
> From: Michael Chan [mailto:michael.chan@broadcom.com]
> Sent: Thursday, December 14, 2017 2:16 AM
> To: Chopra, Manish <Manish.Chopra@cavium.com>
> Cc: davem@davemloft.net; netdev@vger.kernel.org;
> andrew.gospodarek@broadcom.com; Elior, Ariel <Ariel.Elior@cavium.com>;
> Dept-Eng Everest Linux L2 <Dept-EngEverestLinuxL2@cavium.com>
> Subject: Re: [PATCH net-next v4 4/5] bnx2x: Use NETIF_F_GRO_HW.
>
> On Wed, Dec 13, 2017 at 1:08 AM, Chopra, Manish
> <Manish.Chopra@cavium.com> wrote:
> >
> > Hi Michael, There seems a behavioral change here. This driver support
> > two HW aggregation modes [LRO and GRO] With the changes, Interfaces
> come with HW GRO enabled and LRO disabled by default as opposed to earlier
> where interfaces used to come with LRO enabled.
>
> Right. Before, you had both NETIF_F_GRO and NETIF_F_LRO set and the code
> looked at NETIF_F_LRO first and turned on LRO.
>
> Now, we set NETIF_F_GRO and NETIF_F_GRO_HW by default. NETIF_F_LRO is
> turned off since NETIF_F_GRO_HW is on.
>
> If you want, I can change it back to the old default.
>
Michael, I checked it on again, I tried to set LRO in dev->features and dev->hw_features.
Somehow, it gets disabled after register_netdevice(). Any idea why ? Although, I am not running any Bridge/bonding devices.
Looks like, without this series also devices seems to have LRO disabled by default.
Not sure why register_netdevice() disables LRO even driver populates this feature prior to register_netdevice().
^ permalink raw reply
page: next (older) | prev (newer) | latest
- recent:[subjects (threaded)|topics (new)|topics (active)]
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox