* Re: [PATCH net-next] net: bridge: add per-port group_fwd_mask with less restrictions
From: David Miller @ 2017-09-29 5:04 UTC (permalink / raw)
To: nikolay; +Cc: netdev, roopa, bridge, stephen
In-Reply-To: <1506517964-17479-1-git-send-email-nikolay@cumulusnetworks.com>
From: Nikolay Aleksandrov <nikolay@cumulusnetworks.com>
Date: Wed, 27 Sep 2017 16:12:44 +0300
> We need to be able to transparently forward most link-local frames via
> tunnels (e.g. vxlan, qinq). Currently the bridge's group_fwd_mask has a
> mask which restricts the forwarding of STP and LACP, but we need to be able
> to forward these over tunnels and control that forwarding on a per-port
> basis thus add a new per-port group_fwd_mask option which only disallows
> mac pause frames to be forwarded (they're always dropped anyway).
> The patch does not change the current default situation - all of the others
> are still restricted unless configured for forwarding.
> We have successfully tested this patch with LACP and STP forwarding over
> VxLAN and qinq tunnels.
>
> Signed-off-by: Nikolay Aleksandrov <nikolay@cumulusnetworks.com>
Applied, thanks.
^ permalink raw reply
* Re: [patch net-next 1/7] skbuff: Add the offload_mr_fwd_mark field
From: Davide Caratti @ 2017-09-29 11:14 UTC (permalink / raw)
To: Jiri Pirko, netdev
Cc: davem, yotamg, idosch, mlxsw, nikolay, andrew, dsa, edumazet,
willemb, johannes.berg, pabeni, daniel, f.fainelli, fw,
gfree.wind
In-Reply-To: <20170928173415.15551-2-jiri@resnulli.us>
hello Jiri and Yotam,
On Thu, 2017-09-28 at 19:34 +0200, Jiri Pirko wrote:
> From: Yotam Gigi <yotamg@mellanox.com>
>
> diff --git a/include/linux/skbuff.h b/include/linux/skbuff.h
> index 19e64bf..ada8214 100644
> --- a/include/linux/skbuff.h
> +++ b/include/linux/skbuff.h
> @@ -772,6 +772,7 @@ struct sk_buff {
> __u8 remcsum_offload:1;
> #ifdef CONFIG_NET_SWITCHDEV
> __u8 offload_fwd_mark:1;
> + __u8 offload_mr_fwd_mark:1;
I had a look at the pahole output:
$ make allyesconfig
$ make net/core/skbuff.o
$ pahole net/core/skbuff.o | grep -C7 tc_from_ingress
__u8 ipvs_property:1; /* 147: 7 1 */
__u8 inner_protocol_type:1; /* 147: 6 1 */
__u8 remcsum_offload:1; /* 147: 5 1 */
__u8 offload_fwd_mark:1; /* 147: 4 1 */
__u8 tc_skip_classify:1; /* 147: 3 1 */
__u8 tc_at_ingress:1; /* 147: 2 1 */
__u8 tc_redirected:1; /* 147: 1 1 */
__u8 tc_from_ingress:1; /* 147: 0 1 */
__u16 tc_index; /* 148 2 */
/* XXX 2 bytes hole, try to pack */
union {
__wsum csum; /* 4 */
struct {
apparently there are no more spare bits to use at that offset: therefore,
adding 'offload_mr_fwd_mark' before 'tc_skip_classify' will make
'tc_from_ingress' slip at offset 148, and tc_index at offset 150.
I think you can use that 2-bytes hole below tc_index, and also move the
offload_fwd_mark bit there, as we use both when CONFIG_NET_SWITCHDEV is
enabled. This way we will also gain one spare bit, without changing the
struct size or worsening the cacheline alignments.
what do you think?
regards,
--
davide
^ permalink raw reply
* Re: [PATCH net-next] net: ipv4: remove fib_info arg to fib_check_nh
From: David Miller @ 2017-09-29 5:20 UTC (permalink / raw)
To: dsahern; +Cc: netdev
In-Reply-To: <1506570119-17088-1-git-send-email-dsahern@gmail.com>
From: David Ahern <dsahern@gmail.com>
Date: Wed, 27 Sep 2017 20:41:59 -0700
> fib_check_nh does not use the fib_info arg; remove t.
>
> Signed-off-by: David Ahern <dsahern@gmail.com>
Applied.
^ permalink raw reply
* Re: [PATCH net-next] net: ipv4: remove fib_weight
From: David Miller @ 2017-09-29 5:20 UTC (permalink / raw)
To: dsahern; +Cc: netdev
In-Reply-To: <1506564480-20374-1-git-send-email-dsahern@gmail.com>
From: David Ahern <dsahern@gmail.com>
Date: Wed, 27 Sep 2017 19:08:00 -0700
> fib_weight in fib_info is set but not used. Remove it and the
> helpers for setting it.
>
> Signed-off-by: David Ahern <dsahern@gmail.com>
Applied.
^ permalink raw reply
* [PATCH net-next] net: core: decouple ifalias get/set from rtnl lock
From: Florian Westphal @ 2017-09-29 11:21 UTC (permalink / raw)
To: netdev; +Cc: edumazet, Florian Westphal
Device alias can be set by either rtnetlink (rtnl is held) or sysfs.
rtnetlink hold the rtnl mutex, sysfs acquires it for this purpose.
Add an extra mutex for it plus a seqcount to get a consistent snapshot
of the alias buffer.
This allows the sysfs path to not take rtnl and would later allow
to not hold it when dumping ifalias.
Signed-off-by: Florian Westphal <fw@strlen.de>
---
include/linux/netdevice.h | 3 +-
net/core/dev.c | 70 +++++++++++++++++++++++++++++++++++++++--------
net/core/net-sysfs.c | 14 ++++------
net/core/rtnetlink.c | 13 +++++++--
4 files changed, 77 insertions(+), 23 deletions(-)
diff --git a/include/linux/netdevice.h b/include/linux/netdevice.h
index f535779d9dc1..0bcedb498f6f 100644
--- a/include/linux/netdevice.h
+++ b/include/linux/netdevice.h
@@ -1632,7 +1632,7 @@ enum netdev_priv_flags {
struct net_device {
char name[IFNAMSIZ];
struct hlist_node name_hlist;
- char *ifalias;
+ char __rcu *ifalias;
/*
* I/O specific fields
* FIXME: Merge these and struct ifmap into one
@@ -3275,6 +3275,7 @@ void __dev_notify_flags(struct net_device *, unsigned int old_flags,
unsigned int gchanges);
int dev_change_name(struct net_device *, const char *);
int dev_set_alias(struct net_device *, const char *, size_t);
+int dev_get_alias(const struct net_device *, char *, size_t);
int dev_change_net_namespace(struct net_device *, struct net *, const char *);
int __dev_set_mtu(struct net_device *, int);
int dev_set_mtu(struct net_device *, int);
diff --git a/net/core/dev.c b/net/core/dev.c
index e350c768d4b5..a7ac2902d702 100644
--- a/net/core/dev.c
+++ b/net/core/dev.c
@@ -188,6 +188,9 @@ static struct napi_struct *napi_by_id(unsigned int napi_id);
DEFINE_RWLOCK(dev_base_lock);
EXPORT_SYMBOL(dev_base_lock);
+static DEFINE_MUTEX(ifalias_mutex);
+static seqcount_t ifalias_rename_seq;
+
/* protects napi_hash addition/deletion and napi_gen_id */
static DEFINE_SPINLOCK(napi_hash_lock);
@@ -1265,29 +1268,72 @@ int dev_change_name(struct net_device *dev, const char *newname)
*/
int dev_set_alias(struct net_device *dev, const char *alias, size_t len)
{
- char *new_ifalias;
-
- ASSERT_RTNL();
+ char *new_ifalias, *old_ifalias;
if (len >= IFALIASZ)
return -EINVAL;
+ mutex_lock(&ifalias_mutex);
+
+ old_ifalias = rcu_dereference_protected(dev->ifalias,
+ mutex_is_locked(&ifalias_mutex));
if (!len) {
- kfree(dev->ifalias);
- dev->ifalias = NULL;
- return 0;
+ RCU_INIT_POINTER(dev->ifalias, NULL);
+ goto out;
}
- new_ifalias = krealloc(dev->ifalias, len + 1, GFP_KERNEL);
- if (!new_ifalias)
+ new_ifalias = __krealloc(old_ifalias, len + 1, GFP_KERNEL);
+ if (!new_ifalias) {
+ mutex_unlock(&ifalias_mutex);
return -ENOMEM;
- dev->ifalias = new_ifalias;
- memcpy(dev->ifalias, alias, len);
- dev->ifalias[len] = 0;
+ }
+ if (new_ifalias == old_ifalias) {
+ write_seqcount_begin(&ifalias_rename_seq);
+ memcpy(new_ifalias, alias, len);
+ new_ifalias[len] = 0;
+ write_seqcount_end(&ifalias_rename_seq);
+ mutex_unlock(&ifalias_mutex);
+ return len;
+ }
+
+ memcpy(new_ifalias, alias, len);
+ new_ifalias[len] = 0;
+
+ rcu_assign_pointer(dev->ifalias, new_ifalias);
+out:
+ mutex_unlock(&ifalias_mutex);
+ if (old_ifalias) {
+ synchronize_net();
+ kfree(old_ifalias);
+ }
return len;
}
+int dev_get_alias(const struct net_device *dev, char *alias, size_t len)
+{
+ unsigned int seq;
+ int ret;
+
+ for (;;) {
+ const char *name;
+
+ ret = 0;
+ rcu_read_lock();
+ name = rcu_dereference(dev->ifalias);
+ seq = raw_seqcount_begin(&ifalias_rename_seq);
+ if (name)
+ ret = snprintf(alias, len, "%s", name);
+ rcu_read_unlock();
+
+ if (!read_seqcount_retry(&ifalias_rename_seq, seq))
+ break;
+
+ cond_resched();
+ }
+
+ return ret;
+}
/**
* netdev_features_change - device changes features
@@ -8770,6 +8816,8 @@ static int __init net_dev_init(void)
NULL, dev_cpu_dead);
WARN_ON(rc < 0);
rc = 0;
+
+ seqcount_init(&ifalias_rename_seq);
out:
return rc;
}
diff --git a/net/core/net-sysfs.c b/net/core/net-sysfs.c
index 927a6dcbad96..3d85119d3104 100644
--- a/net/core/net-sysfs.c
+++ b/net/core/net-sysfs.c
@@ -391,10 +391,7 @@ static ssize_t ifalias_store(struct device *dev, struct device_attribute *attr,
if (len > 0 && buf[len - 1] == '\n')
--count;
- if (!rtnl_trylock())
- return restart_syscall();
ret = dev_set_alias(netdev, buf, count);
- rtnl_unlock();
return ret < 0 ? ret : len;
}
@@ -403,13 +400,12 @@ static ssize_t ifalias_show(struct device *dev,
struct device_attribute *attr, char *buf)
{
const struct net_device *netdev = to_net_dev(dev);
+ char tmp[IFALIASZ];
ssize_t ret = 0;
- if (!rtnl_trylock())
- return restart_syscall();
- if (netdev->ifalias)
- ret = sprintf(buf, "%s\n", netdev->ifalias);
- rtnl_unlock();
+ ret = dev_get_alias(netdev, tmp, sizeof(tmp));
+ if (ret > 0)
+ ret = sprintf(buf, "%s\n", tmp);
return ret;
}
static DEVICE_ATTR_RW(ifalias);
@@ -1488,7 +1484,7 @@ static void netdev_release(struct device *d)
BUG_ON(dev->reg_state != NETREG_RELEASED);
- kfree(dev->ifalias);
+ kfree(rcu_access_pointer(dev->ifalias));
netdev_freemem(dev);
}
diff --git a/net/core/rtnetlink.c b/net/core/rtnetlink.c
index e6955da0d58d..3961f87cdc76 100644
--- a/net/core/rtnetlink.c
+++ b/net/core/rtnetlink.c
@@ -1366,6 +1366,16 @@ static int nla_put_iflink(struct sk_buff *skb, const struct net_device *dev)
return nla_put_u32(skb, IFLA_LINK, ifindex);
}
+static noinline_for_stack int nla_put_ifalias(struct sk_buff *skb,
+ struct net_device *dev)
+{
+ char buf[IFALIASZ];
+ int ret;
+
+ ret = dev_get_alias(dev, buf, sizeof(buf));
+ return ret > 0 ? nla_put_string(skb, IFLA_IFALIAS, buf) : 0;
+}
+
static int rtnl_fill_link_netnsid(struct sk_buff *skb,
const struct net_device *dev)
{
@@ -1425,8 +1435,7 @@ static int rtnl_fill_ifinfo(struct sk_buff *skb, struct net_device *dev,
nla_put_u8(skb, IFLA_CARRIER, netif_carrier_ok(dev)) ||
(dev->qdisc &&
nla_put_string(skb, IFLA_QDISC, dev->qdisc->ops->id)) ||
- (dev->ifalias &&
- nla_put_string(skb, IFLA_IFALIAS, dev->ifalias)) ||
+ nla_put_ifalias(skb, dev) ||
nla_put_u32(skb, IFLA_CARRIER_CHANGES,
atomic_read(&dev->carrier_changes)) ||
nla_put_u8(skb, IFLA_PROTO_DOWN, dev->proto_down))
--
2.13.5
^ permalink raw reply related
* Re: [PATCH v3] ebtables: fix race condition in frame_filter_net_init()
From: Pablo Neira Ayuso @ 2017-09-29 11:29 UTC (permalink / raw)
To: Artem Savkov; +Cc: Florian Westphal, netdev, linux-kernel, netfilter-devel
In-Reply-To: <20170926163545.3668-1-asavkov@redhat.com>
On Tue, Sep 26, 2017 at 06:35:45PM +0200, Artem Savkov wrote:
> It is possible for ebt_in_hook to be triggered before ebt_table is assigned
> resulting in a NULL-pointer dereference. Make sure hooks are
> registered as the last step.
Applied, thanks.
^ permalink raw reply
* RE: [patch net-next 1/7] skbuff: Add the offload_mr_fwd_mark field
From: Yuval Mintz @ 2017-09-29 11:36 UTC (permalink / raw)
To: Davide Caratti, Jiri Pirko, netdev@vger.kernel.org
Cc: davem@davemloft.net, Yotam Gigi, Ido Schimmel, mlxsw,
nikolay@cumulusnetworks.com, andrew@lunn.ch,
dsa@cumulusnetworks.com, edumazet@google.com, willemb@google.com,
johannes.berg@intel.com, pabeni@redhat.com, daniel@iogearbox.net,
f.fainelli@gmail.com, fw@strlen.de, gfree.wind@vip.163.com
In-Reply-To: <1506683686.2980.44.camel@redhat.com>
> hello Jiri and Yotam,
>
> On Thu, 2017-09-28 at 19:34 +0200, Jiri Pirko wrote:
> > From: Yotam Gigi <yotamg@mellanox.com>
> >
> > diff --git a/include/linux/skbuff.h b/include/linux/skbuff.h
> > index 19e64bf..ada8214 100644
> > --- a/include/linux/skbuff.h
> > +++ b/include/linux/skbuff.h
> > @@ -772,6 +772,7 @@ struct sk_buff {
> > __u8 remcsum_offload:1;
> > #ifdef CONFIG_NET_SWITCHDEV
> > __u8 offload_fwd_mark:1;
> > + __u8 offload_mr_fwd_mark:1;
>
> I had a look at the pahole output:
>
> $ make allyesconfig
> $ make net/core/skbuff.o
> $ pahole net/core/skbuff.o | grep -C7 tc_from_ingress
>
>
> __u8 ipvs_property:1; /* 147: 7 1 */
> __u8 inner_protocol_type:1; /* 147: 6 1 */
> __u8 remcsum_offload:1; /* 147: 5 1 */
> __u8 offload_fwd_mark:1; /* 147: 4 1 */
> __u8 tc_skip_classify:1; /* 147: 3 1 */
> __u8 tc_at_ingress:1; /* 147: 2 1 */
> __u8 tc_redirected:1; /* 147: 1 1 */
> __u8 tc_from_ingress:1; /* 147: 0 1 */
> __u16 tc_index; /* 148 2 */
>
> /* XXX 2 bytes hole, try to pack */
>
> union {
> __wsum csum; /* 4 */
> struct {
>
> apparently there are no more spare bits to use at that offset: therefore,
> adding 'offload_mr_fwd_mark' before 'tc_skip_classify' will make
> 'tc_from_ingress' slip at offset 148, and tc_index at offset 150.
> I think you can use that 2-bytes hole below tc_index, and also move the
> offload_fwd_mark bit there, as we use both when
> CONFIG_NET_SWITCHDEV is
> enabled. This way we will also gain one spare bit, without changing the
> struct size or worsening the cacheline alignments.
>
> what do you think?
Your pahole output still shows a 2B hole until the following union
which is 4B-aligned.
While it's true tc_index moves to offset 150, the union will not move
[I.e., stay at offset 152] so the layout doesn't really change [greatly]
nor the size of the struct. And we have the benefit of all the bits
remaining consecutive.
^ permalink raw reply
* [PATCH iproute2] ip xfrm: use correct key length for netlink message
From: Michal Kubecek @ 2017-09-29 11:41 UTC (permalink / raw)
To: Stephen Hemminger; +Cc: netdev
When SA is added manually using "ip xfrm state add", xfrm_state_modify()
uses alg_key_len field of struct xfrm_algo for the length of key passed to
kernel in the netlink message. However alg_key_len is bit length of the key
while we need byte length here. This is usually harmless as kernel ignores
the excess data but when the bit length of the key exceeds 512
(XFRM_ALGO_KEY_BUF_SIZE), it can result in buffer overflow.
We can simply divide by 8 here as the only place setting alg_key_len is in
xfrm_algo_parse() where it is always set to a multiple of 8 (and there are
already multiple places using "algo->alg_key_len / 8").
Signed-off-by: Michal Kubecek <mkubecek@suse.cz>
---
ip/xfrm_state.c | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/ip/xfrm_state.c b/ip/xfrm_state.c
index 4483fb8f71d2..99fdec2325ec 100644
--- a/ip/xfrm_state.c
+++ b/ip/xfrm_state.c
@@ -539,7 +539,7 @@ static int xfrm_state_modify(int cmd, unsigned int flags, int argc, char **argv)
xfrm_algo_parse((void *)&alg, type, name, key,
buf, sizeof(alg.buf));
- len += alg.u.alg.alg_key_len;
+ len += alg.u.alg.alg_key_len / 8;
addattr_l(&req.n, sizeof(req.buf), type,
(void *)&alg, len);
--
2.14.2
^ permalink raw reply related
* Re: [PATCH] net: ethernet: sfc: There is a typo, modify curent to current.
From: Hao Zhang @ 2017-09-29 11:51 UTC (permalink / raw)
To: davem, bkenward, ecree, netdev, linux-net-drivers
In-Reply-To: <20170929101937.GA8963@arx-kt>
Resend to maintainer.
Regards :)
2017-09-29 18:19 GMT+08:00 Hao Zhang <hao5781286@gmail.com>:
> There is a typo, fix it by modify curent to current.
>
> Signed-off-by: Hao Zhang <hao5781286@gmail.com>
> ---
> drivers/net/ethernet/sfc/mcdi_pcol.h | 2 +-
> 1 file changed, 1 insertion(+), 1 deletion(-)
>
> diff --git a/drivers/net/ethernet/sfc/mcdi_pcol.h b/drivers/net/ethernet/sfc/mcdi_pcol.h
> index 91fb54f..8bfc8d4 100644
> --- a/drivers/net/ethernet/sfc/mcdi_pcol.h
> +++ b/drivers/net/ethernet/sfc/mcdi_pcol.h
> @@ -11505,7 +11505,7 @@
>
> /***********************************/
> /* MC_CMD_GET_SNAPSHOT_LENGTH
> - * Obtain the curent range of allowable values for the SNAPSHOT_LENGTH
> + * Obtain the current range of allowable values for the SNAPSHOT_LENGTH
> * parameter to MC_CMD_INIT_RXQ.
> */
> #define MC_CMD_GET_SNAPSHOT_LENGTH 0x101
> --
> 2.7.4
>
^ permalink raw reply
* Fwd: [PATCH] net: ethernet: sfc: There is a typo, modify curent to current.
From: Hao Zhang @ 2017-09-29 11:53 UTC (permalink / raw)
To: netdev, davem
In-Reply-To: <20170929101937.GA8963@arx-kt>
Resend to maintainer.
Regards :)
---------- Forwarded message ----------
From: Hao Zhang <hao5781286@gmail.com>
Date: 2017-09-29 18:19 GMT+08:00
Subject: [PATCH] net: ethernet: sfc: There is a typo, modify curent to current.
To: linux-net-drivers@solarflare.com, ecree@solarflare.com,
bkenward@solarflare.com
抄送: linux-kernel@vger.kernel.org, hao5781286@gmail.com
There is a typo, fix it by modify curent to current.
Signed-off-by: Hao Zhang <hao5781286@gmail.com>
---
drivers/net/ethernet/sfc/mcdi_pcol.h | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/drivers/net/ethernet/sfc/mcdi_pcol.h
b/drivers/net/ethernet/sfc/mcdi_pcol.h
index 91fb54f..8bfc8d4 100644
--- a/drivers/net/ethernet/sfc/mcdi_pcol.h
+++ b/drivers/net/ethernet/sfc/mcdi_pcol.h
@@ -11505,7 +11505,7 @@
/***********************************/
/* MC_CMD_GET_SNAPSHOT_LENGTH
- * Obtain the curent range of allowable values for the SNAPSHOT_LENGTH
+ * Obtain the current range of allowable values for the SNAPSHOT_LENGTH
* parameter to MC_CMD_INIT_RXQ.
*/
#define MC_CMD_GET_SNAPSHOT_LENGTH 0x101
^ permalink raw reply related
* Re: [PATCH RFC 3/5] Add KSZ8795 switch driver
From: Andrew Lunn @ 2017-09-29 12:12 UTC (permalink / raw)
To: David Laight
Cc: Tristram.Ha@microchip.com, muvarov@gmail.com, pavel@ucw.cz,
nathan.leigh.conrad@gmail.com,
vivien.didelot@savoirfairelinux.com, f.fainelli@gmail.com,
netdev@vger.kernel.org, linux-kernel@vger.kernel.org,
Woojung.Huh@microchip.com
In-Reply-To: <063D6719AE5E284EB5DD2968C1650D6DD0084EDC@AcuExch.aculab.com>
On Fri, Sep 29, 2017 at 09:14:26AM +0000, David Laight wrote:
> From: Andrew Lunn
> > Sent: 28 September 2017 20:34
> ...
> > > There are 34 counters. In normal case using generic bus I/O or PCI to read them
> > > is very quick, but the switch is mostly accessed using SPI, or even I2C. As the SPI
> > > access is very slow.
> >
> > How slow is it? The Marvell switches all use MDIO. It is probably a
> > bit faster than I2C, but it is a lot slower than MMIO or PCI.
> >
> > ethtool -S lan0 takes about 25ms.
>
> Is the SPI access software bit-banged?
That will depend on the board design. I've used mdio bit banging, and
that was painfully slow for stats.
But we should primarily think about average hardware. It is going to
have hardware SPI or I2C. If statistics reading with hardware I2C is
reasonable, i would avoid caching, and just ensure other accesses are
permitted between individual statistic reads.
It also requires Microchip also post new code. They have been very
silent for quite a while....
Andrew
^ permalink raw reply
* [PATCH v2 net] net: mvpp2: Fix clock resource by adding an optional bus clock
From: Gregory CLEMENT @ 2017-09-29 12:27 UTC (permalink / raw)
To: David S. Miller, linux-kernel, netdev
Cc: Jason Cooper, Andrew Lunn, Sebastian Hesselbarth, Gregory CLEMENT,
Thomas Petazzoni, linux-arm-kernel, Antoine Tenart,
Miquèl Raynal, Nadav Haklai, Shadi Ammouri, Yehuda Yitschak,
Omri Itach, Hanna Hawa, Igal Liberman, Marcin Wojtas
On Armada 7K/8K we need to explicitly enable the bus clock. The bus clock
is optional because not all the SoCs need them but at least for Armada
7K/8K it is actually mandatory.
The binding documentation is updating accordingly.
Signed-off-by: Gregory CLEMENT <gregory.clement@free-electrons.com>
---
Changelog:
v1 -> v2:
- manage the -EPROBE_DEFER case
- fix typos in documentation
- remove useless test before clk_disable_unprepare()
Documentation/devicetree/bindings/net/marvell-pp2.txt | 10 ++++++----
drivers/net/ethernet/marvell/mvpp2.c | 15 +++++++++++++++
2 files changed, 21 insertions(+), 4 deletions(-)
diff --git a/Documentation/devicetree/bindings/net/marvell-pp2.txt b/Documentation/devicetree/bindings/net/marvell-pp2.txt
index 7e2dad08a12e..1814fa13f6ab 100644
--- a/Documentation/devicetree/bindings/net/marvell-pp2.txt
+++ b/Documentation/devicetree/bindings/net/marvell-pp2.txt
@@ -21,8 +21,9 @@ Required properties:
- main controller clock (for both armada-375-pp2 and armada-7k-pp2)
- GOP clock (for both armada-375-pp2 and armada-7k-pp2)
- MG clock (only for armada-7k-pp2)
-- clock-names: names of used clocks, must be "pp_clk", "gop_clk" and
- "mg_clk" (the latter only for armada-7k-pp2).
+ - AXI clock (only for armada-7k-pp2)
+- clock-names: names of used clocks, must be "pp_clk", "gop_clk", "mg_clk"
+ and "axi_clk" (the 2 latter only for armada-7k-pp2).
The ethernet ports are represented by subnodes. At least one port is
required.
@@ -78,8 +79,9 @@ Example for marvell,armada-7k-pp2:
cpm_ethernet: ethernet@0 {
compatible = "marvell,armada-7k-pp22";
reg = <0x0 0x100000>, <0x129000 0xb000>;
- clocks = <&cpm_syscon0 1 3>, <&cpm_syscon0 1 9>, <&cpm_syscon0 1 5>;
- clock-names = "pp_clk", "gop_clk", "gp_clk";
+ clocks = <&cpm_syscon0 1 3>, <&cpm_syscon0 1 9>,
+ <&cpm_syscon0 1 5>, <&cpm_syscon0 1 18>;
+ clock-names = "pp_clk", "gop_clk", "gp_clk", "axi_clk";
eth0: eth0 {
interrupts = <ICU_GRP_NSR 39 IRQ_TYPE_LEVEL_HIGH>,
diff --git a/drivers/net/ethernet/marvell/mvpp2.c b/drivers/net/ethernet/marvell/mvpp2.c
index dd0ee2691c86..f2656112986b 100644
--- a/drivers/net/ethernet/marvell/mvpp2.c
+++ b/drivers/net/ethernet/marvell/mvpp2.c
@@ -792,6 +792,7 @@ struct mvpp2 {
struct clk *pp_clk;
struct clk *gop_clk;
struct clk *mg_clk;
+ struct clk *axi_clk;
/* List of pointers to port structures */
struct mvpp2_port **port_list;
@@ -7963,6 +7964,18 @@ static int mvpp2_probe(struct platform_device *pdev)
err = clk_prepare_enable(priv->mg_clk);
if (err < 0)
goto err_gop_clk;
+
+ priv->axi_clk = devm_clk_get(&pdev->dev, "axi_clk");
+ if (IS_ERR(priv->axi_clk)) {
+ err = PTR_ERR(priv->axi_clk);
+ if (err == -EPROBE_DEFER)
+ goto err_gop_clk;
+ priv->axi_clk = NULL;
+ } else {
+ err = clk_prepare_enable(priv->axi_clk);
+ if (err < 0)
+ goto err_gop_clk;
+ }
}
/* Get system's tclk rate */
@@ -8015,6 +8028,7 @@ static int mvpp2_probe(struct platform_device *pdev)
return 0;
err_mg_clk:
+ clk_disable_unprepare(priv->axi_clk);
if (priv->hw_version == MVPP22)
clk_disable_unprepare(priv->mg_clk);
err_gop_clk:
@@ -8052,6 +8066,7 @@ static int mvpp2_remove(struct platform_device *pdev)
aggr_txq->descs_dma);
}
+ clk_disable_unprepare(priv->axi_clk);
clk_disable_unprepare(priv->mg_clk);
clk_disable_unprepare(priv->pp_clk);
clk_disable_unprepare(priv->gop_clk);
--
2.14.1
^ permalink raw reply related
* Re: [PATCHv4 iproute2 1/2] lib/libnetlink: re malloc buff if size is not enough
From: Michal Kubecek @ 2017-09-29 12:55 UTC (permalink / raw)
To: Hangbin Liu; +Cc: netdev, Stephen Hemminger, Phil Sutter, Hangbin Liu
In-Reply-To: <1506605626-1744-2-git-send-email-haliu@redhat.com>
On Thu, Sep 28, 2017 at 09:33:45PM +0800, Hangbin Liu wrote:
> From: Hangbin Liu <liuhangbin@gmail.com>
>
> With commit 72b365e8e0fd ("libnetlink: Double the dump buffer size")
> we doubled the buffer size to support more VFs. But the VFs number is
> increasing all the time. Some customers even use more than 200 VFs now.
>
> We could not double it everytime when the buffer is not enough. Let's just
> not hard code the buffer size and malloc the correct number when running.
>
> Introduce function rtnl_recvmsg() to always return a newly allocated buffer.
> The caller need to free it after using.
>
> Signed-off-by: Hangbin Liu <liuhangbin@gmail.com>
> Signed-off-by: Phil Sutter <phil@nwl.cc>
> ---
> lib/libnetlink.c | 114 ++++++++++++++++++++++++++++++++++++++-----------------
> 1 file changed, 80 insertions(+), 34 deletions(-)
>
Reviewed-by: Michal Kubecek <mkubecek@suse.cz>
^ permalink raw reply
* Re: [PATCH net-next 03/10] sctp: factor out stream->in allocation
From: 'Marcelo Ricardo Leitner' @ 2017-09-29 13:05 UTC (permalink / raw)
To: David Laight
Cc: netdev@vger.kernel.org, linux-sctp@vger.kernel.org, Neil Horman,
Vlad Yasevich, Xin Long
In-Reply-To: <063D6719AE5E284EB5DD2968C1650D6DD0084F8E@AcuExch.aculab.com>
On Fri, Sep 29, 2017 at 10:04:15AM +0000, David Laight wrote:
> From: Marcelo Ricardo Leitner
> > Sent: 28 September 2017 21:25
> > Same idea as previous patch.
>
> That needs a proper description.
Alright.
Marcelo
^ permalink raw reply
* Re: [net-next PATCH 3/5] bpf: cpumap xdp_buff to skb conversion and allocation
From: Jesper Dangaard Brouer @ 2017-09-29 13:05 UTC (permalink / raw)
To: Jason Wang
Cc: netdev, jakub.kicinski, Michael S. Tsirkin, mchan, John Fastabend,
peter.waskiewicz.jr, Daniel Borkmann, Alexei Starovoitov,
Andy Gospodarek, brouer
In-Reply-To: <1c37f945-0e2f-1eec-fe88-a740815026d3@redhat.com>
On Fri, 29 Sep 2017 17:49:23 +0800
Jason Wang <jasowang@redhat.com> wrote:
> On 2017年09月28日 20:57, Jesper Dangaard Brouer wrote:
> > +};
> > +
> > +/* Convert xdp_buff to xdp_pkt */
> > +static struct xdp_pkt *convert_to_xdp_pkt(struct xdp_buff *xdp)
> > +{
> > + struct xdp_pkt *xdp_pkt;
> > + int headroom;
> > +
> > + /* Assure headroom is available for storing info */
> > + headroom = xdp->data - xdp->data_hard_start;
> > + if (headroom < sizeof(*xdp_pkt))
> > + return NULL;
>
> Hi Jesper:
>
> Do you consider this as a trick or a long term solution? Is it better to
> store XDP in a circular buffer? (I'm asking since I meet similar issue
> when doing xdp_xmit for tun).
(The way you ask the question is slightly ambiguous, but I hope I understand.)
IMHO the best solution to allow queueing of XDP packets is to create a
meta-data structure, with the needed info. For performance reasons, we
don't want to allocate a new memory area for this. Thus, we simply use
the available headroom in the page that the packet is stored into.
Notice that DPDK also use the first cache-line of the packet data, for
its packet meta-data structure. (This is not a performance problem.
I've done several PoC benchmarks, before choosing to do this)
For now, this "trick" is local to the cpumap, and thus not exposed as
any API. Thus we can evolve and change the contents easily. But I
would in time, like to see this generalized. When/if more places need
to queue XDP packets, this header meta-data format should be
standardized.
Pipe-dreaming: Taking this to the extreme... if I could get away with
it, I would actually like to store the (232 bytes) SKB meta-data header
inside headroom too. That would eliminate any real SKB memory alloc.
> > +
> > + /* Store info in top of packet */
> > + xdp_pkt = xdp->data_hard_start;
> > +
> > + xdp_pkt->data = xdp->data;
> > + xdp_pkt->len = xdp->data_end - xdp->data;
> > + xdp_pkt->headroom = headroom - sizeof(*xdp_pkt);
> > +
>
> Is wmb() needed here?
No. This xdp_pkt is queued into a into a ptr_ring, which have a
spin_lock on enqueue, and any atomic operation works as a full memory
barrirer mb().
--
Best regards,
Jesper Dangaard Brouer
MSc.CS, Principal Kernel Engineer at Red Hat
LinkedIn: http://www.linkedin.com/in/brouer
^ permalink raw reply
* Re: [PATCHv4 iproute2 2/2] lib/libnetlink: update rtnl_talk to support malloc buff at run time
From: Michal Kubecek @ 2017-09-29 13:06 UTC (permalink / raw)
To: Hangbin Liu; +Cc: netdev, Stephen Hemminger, Phil Sutter, Hangbin Liu
In-Reply-To: <1506605626-1744-3-git-send-email-haliu@redhat.com>
On Thu, Sep 28, 2017 at 09:33:46PM +0800, Hangbin Liu wrote:
> From: Hangbin Liu <liuhangbin@gmail.com>
>
> This is an update for 460c03f3f3cc ("iplink: double the buffer size also in
> iplink_get()"). After update, we will not need to double the buffer size
> every time when VFs number increased.
>
> With call like rtnl_talk(&rth, &req.n, NULL, 0), we can simply remove the
> length parameter.
>
> With call like rtnl_talk(&rth, nlh, nlh, sizeof(req), I add a new variable
> answer to avoid overwrite data in nlh, because it may has more info after
> nlh. also this will avoid nlh buffer not enough issue.
>
> We need to free answer after using.
>
> Signed-off-by: Hangbin Liu <liuhangbin@gmail.com>
> Signed-off-by: Phil Sutter <phil@nwl.cc>
> ---
Reviewed-by: Michal Kubecek <mkubecek@suse.cz>
> diff --git a/ip/link_gre.c b/ip/link_gre.c
> index 9ea2970..35782ca 100644
> --- a/ip/link_gre.c
> +++ b/ip/link_gre.c
> @@ -68,7 +68,6 @@ static int gre_parse_opt(struct link_util *lu, int argc, char **argv,
> struct {
> struct nlmsghdr n;
> struct ifinfomsg i;
> - char buf[16384];
> } req = {
> .n.nlmsg_len = NLMSG_LENGTH(sizeof(*ifi)),
> .n.nlmsg_flags = NLM_F_REQUEST,
> @@ -76,6 +75,7 @@ static int gre_parse_opt(struct link_util *lu, int argc, char **argv,
> .i.ifi_family = preferred_family,
> .i.ifi_index = ifi->ifi_index,
> };
> + struct nlmsghdr *answer = NULL;
> struct rtattr *tb[IFLA_MAX + 1];
> struct rtattr *linkinfo[IFLA_INFO_MAX+1];
> struct rtattr *greinfo[IFLA_GRE_MAX + 1];
> @@ -100,19 +100,20 @@ static int gre_parse_opt(struct link_util *lu, int argc, char **argv,
> __u32 erspan_idx = 0;
>
> if (!(n->nlmsg_flags & NLM_F_CREATE)) {
> - if (rtnl_talk(&rth, &req.n, &req.n, sizeof(req)) < 0) {
> + if (rtnl_talk(&rth, &req.n, &answer) < 0) {
> get_failed:
> fprintf(stderr,
> "Failed to get existing tunnel info.\n");
> + free(answer);
> return -1;
> }
Took me a moment to realize answer is still NULL if we get here via
failed rtnl_talk() but non-NULL if we get here via "goto get_failed"
later. Nice trick. :-)
Michal Kubecek
^ permalink raw reply
* [PATCH][net-next] net: ipmr: make function ipmr_notifier_init static
From: Colin King @ 2017-09-29 13:34 UTC (permalink / raw)
To: David S . Miller, Alexey Kuznetsov, Hideaki YOSHIFUJI, netdev,
linux-kernel
Cc: kernel-janitors
From: Colin Ian King <colin.king@canonical.com>
The function ipmr_notifier_init is local to the source and does
not need to be in global scope, so make it static.
Cleans up sparse warning:
warning: symbol 'ipmr_notifier_init' was not declared. Should it be static?
Signed-off-by: Colin Ian King <colin.king@canonical.com>
---
net/ipv4/ipmr.c | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/net/ipv4/ipmr.c b/net/ipv4/ipmr.c
index 292a8e80bdfa..a844738b38bd 100644
--- a/net/ipv4/ipmr.c
+++ b/net/ipv4/ipmr.c
@@ -3224,7 +3224,7 @@ static const struct fib_notifier_ops ipmr_notifier_ops_template = {
.owner = THIS_MODULE,
};
-int __net_init ipmr_notifier_init(struct net *net)
+static int __net_init ipmr_notifier_init(struct net *net)
{
struct fib_notifier_ops *ops;
--
2.14.1
^ permalink raw reply related
* RE: [RESEND PATCH 2/6] staging: fsl-dpaa2/ethsw: Add Freescale DPAA2 Ethernet Switch driver
From: Bogdan Purcareata @ 2017-09-29 13:36 UTC (permalink / raw)
To: Razvan Stefanescu, gregkh@linuxfoundation.org
Cc: devel@driverdev.osuosl.org, arnd@arndb.de, netdev@vger.kernel.org,
Alexandru Marginean, linux-kernel@vger.kernel.org, agraf@suse.de,
stuyoder@gmail.com, Laurentiu Tudor
In-Reply-To: <1505825158-8192-3-git-send-email-razvan.stefanescu@nxp.com>
> Introduce the DPAA2 Ethernet Switch driver, which manages Datapath Switch
> (DPSW) objects discovered on the MC bus.
>
> Suggested-by: Alexandru Marginean <alexandru.marginean@nxp.com>
> Signed-off-by: Razvan Stefanescu <razvan.stefanescu@nxp.com>
> ---
> drivers/staging/fsl-dpaa2/ethsw/Makefile | 2 +-
> drivers/staging/fsl-dpaa2/ethsw/ethsw.c | 1523 ++++++++++++++++++++++++++++++
> drivers/staging/fsl-dpaa2/ethsw/ethsw.h | 88 ++
> 3 files changed, 1612 insertions(+), 1 deletion(-)
> create mode 100644 drivers/staging/fsl-dpaa2/ethsw/ethsw.c
> create mode 100644 drivers/staging/fsl-dpaa2/ethsw/ethsw.h
>
> diff --git a/drivers/staging/fsl-dpaa2/ethsw/Makefile b/drivers/staging/fsl-
> dpaa2/ethsw/Makefile
> index db137f7..a6d72d1 100644
> --- a/drivers/staging/fsl-dpaa2/ethsw/Makefile
> +++ b/drivers/staging/fsl-dpaa2/ethsw/Makefile
> @@ -4,4 +4,4 @@
>
> obj-$(CONFIG_FSL_DPAA2_ETHSW) += dpaa2-ethsw.o
>
> -dpaa2-ethsw-objs := dpsw.o
> +dpaa2-ethsw-objs := ethsw.o dpsw.o
> diff --git a/drivers/staging/fsl-dpaa2/ethsw/ethsw.c b/drivers/staging/fsl-
> dpaa2/ethsw/ethsw.c
> new file mode 100644
> index 0000000..ae86078
> --- /dev/null
> +++ b/drivers/staging/fsl-dpaa2/ethsw/ethsw.c
> @@ -0,0 +1,1523 @@
> +/* Copyright 2014-2016 Freescale Semiconductor Inc.
> + * Copyright 2017 NXP
> + *
> + * Redistribution and use in source and binary forms, with or without
> + * modification, are permitted provided that the following conditions are met:
> + * * Redistributions of source code must retain the above copyright
> + * notice, this list of conditions and the following disclaimer.
> + * * 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.
> + * * Neither the name of the above-listed copyright holders nor the
> + * names of any contributors may be used to endorse or promote products
> + * derived from this software without specific prior written permission.
> + *
> + *
> + * ALTERNATIVELY, this software may be distributed under the terms of the
> + * GNU General Public License ("GPL") as published by the Free Software
> + * Foundation, either version 2 of that License or (at your option) any
> + * later version.
> + *
> + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
> + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
> + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
> + * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDERS OR CONTRIBUTORS BE
> + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
> + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
> + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
> + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
> + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
> + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
> + * POSSIBILITY OF SUCH DAMAGE.
> + */
> +
> +#include <linux/module.h>
> +
> +#include <linux/interrupt.h>
> +#include <linux/msi.h>
> +#include <linux/kthread.h>
> +#include <linux/workqueue.h>
> +
> +#include "../../fsl-mc/include/mc.h"
> +
> +#include "ethsw.h"
> +
> +static struct workqueue_struct *ethsw_owq;
> +
> +/* Minimal supported DPSW version */
> +#define DPSW_MIN_VER_MAJOR 8
> +#define DPSW_MIN_VER_MINOR 0
> +
> +#define DEFAULT_VLAN_ID 1
> +
> +static int ethsw_add_vlan(struct ethsw_core *ethsw, u16 vid)
> +{
> + int err;
> +
> + struct dpsw_vlan_cfg vcfg = {
> + .fdb_id = 0,
> + };
> +
> + if (ethsw->vlans[vid]) {
> + dev_err(ethsw->dev, "VLAN already configured\n");
> + return -EEXIST;
> + }
> +
> + err = dpsw_vlan_add(ethsw->mc_io, 0,
> + ethsw->dpsw_handle, vid, &vcfg);
> + if (err) {
> + dev_err(ethsw->dev, "dpsw_vlan_add err %d\n", err);
> + return err;
> + }
> + ethsw->vlans[vid] = ETHSW_VLAN_MEMBER;/
> +
> + return 0;
> +}
> +
> +static int ethsw_port_add_vlan(struct ethsw_port_priv *port_priv,
> + u16 vid, u16 flags)
> +{
> + struct ethsw_core *ethsw = port_priv->ethsw_data;
> + struct net_device *netdev = port_priv->netdev;
> + struct dpsw_vlan_if_cfg vcfg;
> + bool is_oper;
> + int err, err2;
Mild suggestion - s/err2/ret/, just because it sounds better, at least to me (same for similar situations in the rest of the file).
> +
> + if (port_priv->vlans[vid]) {
> + netdev_warn(netdev, "VLAN %d already configured\n", vid);
> + return -EEXIST;
> + }
> +
> + vcfg.num_ifs = 1;
> + vcfg.if_id[0] = port_priv->idx;
> + err = dpsw_vlan_add_if(ethsw->mc_io, 0, ethsw->dpsw_handle, vid, &vcfg);
> + if (err) {
> + netdev_err(netdev, "dpsw_vlan_add_if err %d\n", err);
> + return err;
> + }
> +
> + port_priv->vlans[vid] = ETHSW_VLAN_MEMBER;
> +
> + if (flags & BRIDGE_VLAN_INFO_UNTAGGED) {
> + err = dpsw_vlan_add_if_untagged(ethsw->mc_io, 0,
> + ethsw->dpsw_handle,
> + vid, &vcfg);
> + if (err) {
> + netdev_err(netdev,
> + "dpsw_vlan_add_if_untagged err %d\n", err);
> + return err;
> + }
> + port_priv->vlans[vid] |= ETHSW_VLAN_UNTAGGED;
> + }
> +
> + if (flags & BRIDGE_VLAN_INFO_PVID) {
> + struct dpsw_tci_cfg tci_cfg = {
> + .pcp = 0,
> + .dei = 0,
> + .vlan_id = vid,
> + };
> +
> + /* Interface needs to be down to change PVID */
> + is_oper = netif_oper_up(netdev);
> + if (is_oper) {
> + err = dpsw_if_disable(ethsw->mc_io, 0,
> + ethsw->dpsw_handle,
> + port_priv->idx);
> + if (err) {
> + netdev_err(netdev,
> + "dpsw_if_disable err %d\n", err);
> + return err;
> + }
> + }
> +
> + err = dpsw_if_set_tci(ethsw->mc_io, 0, ethsw->dpsw_handle,
> + port_priv->idx, &tci_cfg);
> + if (!err) {
> + /* Delete previous PVID info and mark the new one */
> + if (port_priv->pvid)
> + port_priv->vlans[port_priv->pvid]
> + ^= ETHSW_VLAN_PVID;
Can it be " &= ~ETHSW_VLAN_PVID" ? Are there other implications?
> +
> + port_priv->vlans[vid] |= ETHSW_VLAN_PVID;
> + port_priv->pvid = vid;
> + } else {
> + netdev_err(netdev, "dpsw_if_set_tci err %d\n", err);
> + }
> +
> + if (is_oper) {
> + err2 = dpsw_if_enable(ethsw->mc_io, 0,
> + ethsw->dpsw_handle,
> + port_priv->idx);
> + if (err2) {
> + netdev_err(netdev,
> + "dpsw_if_enable err %d\n", err2);
> + return err2;
> + }
> + }
> + }
> +
> + return err;
> +}
> +
> +static int ethsw_set_learning(struct ethsw_core *ethsw, u8 flag)
> +{
> + enum dpsw_fdb_learning_mode learn_mode;
> + int err;
> +
> + if (flag)
> + learn_mode = DPSW_FDB_LEARNING_MODE_HW;
> + else
> + learn_mode = DPSW_FDB_LEARNING_MODE_DIS;
> +
> + err = dpsw_fdb_set_learning_mode(ethsw->mc_io, 0, ethsw->dpsw_handle, 0,
> + learn_mode);
> + if (err) {
> + dev_err(ethsw->dev, "dpsw_fdb_set_learning_mode err %d\n", err);
> + return err;
> + }
> + ethsw->learning = !!flag;
> +
> + return 0;
> +}
> +
> +static int ethsw_port_set_flood(struct ethsw_port_priv *port_priv, u8 flag)
> +{
> + int err;
> +
> + err = dpsw_if_set_flooding(port_priv->ethsw_data->mc_io, 0,
> + port_priv->ethsw_data->dpsw_handle,
> + port_priv->idx, (int)flag);
Why is this cast necessary? Can't the API be reworked to use u8 (or, better yet, bool) instead of int?
> + if (err) {
> + netdev_err(port_priv->netdev,
> + "dpsw_fdb_set_learning_mode err %d\n", err);
> + return err;
> + }
> + port_priv->flood = !!flag;
> +
> + return 0;
> +}
> +
> +static int ethsw_port_set_stp_state(struct ethsw_port_priv *port_priv, u8
> state)
> +{
> + struct dpsw_stp_cfg stp_cfg = {
> + .vlan_id = DEFAULT_VLAN_ID,
> + .state = state,
> + };
> + int err;
> +
> + if (!netif_oper_up(port_priv->netdev) || state == port_priv->stp_state)
> + return 0; /* Nothing to do */
> +
> + err = dpsw_if_set_stp(port_priv->ethsw_data->mc_io, 0,
> + port_priv->ethsw_data->dpsw_handle,
> + port_priv->idx, &stp_cfg);
> + if (err) {
> + netdev_err(port_priv->netdev,
> + "dpsw_if_set_stp err %d\n", err);
> + return err;
> + }
> +
> + port_priv->stp_state = state;
> +
> + return 0;
> +}
> +
> +static int ethsw_dellink_switch(struct ethsw_core *ethsw, u16 vid)
> +{
> + struct ethsw_port_priv *ppriv_local = NULL;
> + int i, err;
> +
> + if (!ethsw->vlans[vid])
> + return -ENOENT;
> +
> + err = dpsw_vlan_remove(ethsw->mc_io, 0, ethsw->dpsw_handle, vid);
> + if (err) {
> + dev_err(ethsw->dev, "dpsw_vlan_remove err %d\n", err);
> + return err;
> + }
> + ethsw->vlans[vid] = 0;
> +
> + for (i = 0; i < ethsw->sw_attr.num_ifs; i++) {
> + ppriv_local = ethsw->ports[i];
> + ppriv_local->vlans[vid] = 0;
> + }
> +
> + return 0;
> +}
> +
> +static int ethsw_port_fdb_add_uc(struct ethsw_port_priv *port_priv,
> + const unsigned char *addr)
> +{
> + struct dpsw_fdb_unicast_cfg entry = {0};
> + int err;
> +
> + entry.if_egress = port_priv->idx;
> + entry.type = DPSW_FDB_ENTRY_STATIC;
> + ether_addr_copy(entry.mac_addr, addr);
> +
> + err = dpsw_fdb_add_unicast(port_priv->ethsw_data->mc_io, 0,
> + port_priv->ethsw_data->dpsw_handle,
> + 0, &entry);
> + if (err)
> + netdev_err(port_priv->netdev,
> + "dpsw_fdb_add_unicast err %d\n", err);
> + return err;
> +}
> +
> +static int ethsw_port_fdb_del_uc(struct ethsw_port_priv *port_priv,
> + const unsigned char *addr)
> +{
> + struct dpsw_fdb_unicast_cfg entry = {0};
> + int err;
> +
> + entry.if_egress = port_priv->idx;
> + entry.type = DPSW_FDB_ENTRY_STATIC;
> + ether_addr_copy(entry.mac_addr, addr);
> +
> + err = dpsw_fdb_remove_unicast(port_priv->ethsw_data->mc_io, 0,
> + port_priv->ethsw_data->dpsw_handle,
> + 0, &entry);
> + if (err)
> + netdev_err(port_priv->netdev,
> + "dpsw_fdb_remove_unicast err %d\n", err);
> + return err;
> +}
> +
> +static int ethsw_port_fdb_add_mc(struct ethsw_port_priv *port_priv,
> + const unsigned char *addr)
> +{
> + struct dpsw_fdb_multicast_cfg entry = {0};
> + int err;
> +
> + ether_addr_copy(entry.mac_addr, addr);
> + entry.type = DPSW_FDB_ENTRY_STATIC;
> + entry.num_ifs = 1;
> + entry.if_id[0] = port_priv->idx;
> +
> + err = dpsw_fdb_add_multicast(port_priv->ethsw_data->mc_io, 0,
> + port_priv->ethsw_data->dpsw_handle,
> + 0, &entry);
> + if (err)
> + netdev_err(port_priv->netdev, "dpsw_fdb_add_multicast err %d\n",
> + err);
> + return err;
> +}
> +
> +static int ethsw_port_fdb_del_mc(struct ethsw_port_priv *port_priv,
> + const unsigned char *addr)
> +{
> + struct dpsw_fdb_multicast_cfg entry = {0};
> + int err;
> +
> + ether_addr_copy(entry.mac_addr, addr);
> + entry.type = DPSW_FDB_ENTRY_STATIC;
> + entry.num_ifs = 1;
> + entry.if_id[0] = port_priv->idx;
> +
> + err = dpsw_fdb_remove_multicast(port_priv->ethsw_data->mc_io, 0,
> + port_priv->ethsw_data->dpsw_handle,
> + 0, &entry);
> + if (err)
> + netdev_err(port_priv->netdev,
> + "dpsw_fdb_remove_multicast err %d\n", err);
> + return err;
> +}
> +
> +static void port_get_stats(struct net_device *netdev,
> + struct rtnl_link_stats64 *stats)
> +{
> + struct ethsw_port_priv *port_priv = netdev_priv(netdev);
> + u64 tmp;
> + int err;
> +
> + err = dpsw_if_get_counter(port_priv->ethsw_data->mc_io, 0,
> + port_priv->ethsw_data->dpsw_handle,
> + port_priv->idx,
> + DPSW_CNT_ING_FRAME, &stats->rx_packets);
> + if (err)
> + goto error;
> +
> + err = dpsw_if_get_counter(port_priv->ethsw_data->mc_io, 0,
> + port_priv->ethsw_data->dpsw_handle,
> + port_priv->idx,
> + DPSW_CNT_EGR_FRAME, &stats->tx_packets);
> + if (err)
> + goto error;
> +
> + err = dpsw_if_get_counter(port_priv->ethsw_data->mc_io, 0,
> + port_priv->ethsw_data->dpsw_handle,
> + port_priv->idx,
> + DPSW_CNT_ING_BYTE, &stats->rx_bytes);
> + if (err)
> + goto error;
> +
> + err = dpsw_if_get_counter(port_priv->ethsw_data->mc_io, 0,
> + port_priv->ethsw_data->dpsw_handle,
> + port_priv->idx,
> + DPSW_CNT_EGR_BYTE, &stats->tx_bytes);
> + if (err)
> + goto error;
> +
> + err = dpsw_if_get_counter(port_priv->ethsw_data->mc_io, 0,
> + port_priv->ethsw_data->dpsw_handle,
> + port_priv->idx,
> + DPSW_CNT_ING_FRAME_DISCARD,
> + &stats->rx_dropped);
> + if (err)
> + goto error;
> +
> + err = dpsw_if_get_counter(port_priv->ethsw_data->mc_io, 0,
> + port_priv->ethsw_data->dpsw_handle,
> + port_priv->idx,
> + DPSW_CNT_ING_FLTR_FRAME,
> + &tmp);
> + if (err)
> + goto error;
> + stats->rx_dropped += tmp;
> +
> + err = dpsw_if_get_counter(port_priv->ethsw_data->mc_io, 0,
> + port_priv->ethsw_data->dpsw_handle,
> + port_priv->idx,
> + DPSW_CNT_EGR_FRAME_DISCARD,
> + &stats->tx_dropped);
> + if (err)
> + goto error;
> +
> + return;
> +
> +error:
> + netdev_err(netdev, "dpsw_if_get_counter err %d\n", err);
> +}
> +
> +static bool port_has_offload_stats(const struct net_device *netdev,
> + int attr_id)
> +{
> + return (attr_id == IFLA_OFFLOAD_XSTATS_CPU_HIT);
> +}
> +
> +static int port_get_offload_stats(int attr_id,
> + const struct net_device *netdev,
> + void *sp)
> +{
> + switch (attr_id) {
> + case IFLA_OFFLOAD_XSTATS_CPU_HIT:
> + port_get_stats((struct net_device *)netdev, sp);
> + return 0;
> + }
> +
> + return -EINVAL;
> +}
> +
> +static int port_change_mtu(struct net_device *netdev, int mtu)
> +{
> + struct ethsw_port_priv *port_priv = netdev_priv(netdev);
> + int err;
> +
> + err = dpsw_if_set_max_frame_length(port_priv->ethsw_data->mc_io,
> + 0,
> + port_priv->ethsw_data->dpsw_handle,
> + port_priv->idx,
> + (u16)ETHSW_L2_MAX_FRM(mtu));
> + if (err) {
> + netdev_err(netdev,
> + "dpsw_if_set_max_frame_length() err %d\n", err);
> + return err;
> + }
> +
> + netdev->mtu = mtu;
> + return 0;
> +}
> +
> +static int port_carrier_state_sync(struct net_device *netdev)
> +{
> + struct ethsw_port_priv *port_priv = netdev_priv(netdev);
> + struct dpsw_link_state state;
> + int err;
> +
> + err = dpsw_if_get_link_state(port_priv->ethsw_data->mc_io, 0,
> + port_priv->ethsw_data->dpsw_handle,
> + port_priv->idx, &state);
> + if (err) {
> + netdev_err(netdev, "dpsw_if_get_link_state() err %d\n", err);
> + return err;
> + }
> +
> + WARN_ONCE(state.up > 1, "Garbage read into link_state");
> +
> + if (state.up != port_priv->link_state) {
> + if (state.up)
> + netif_carrier_on(netdev);
> + else
> + netif_carrier_off(netdev);
> + port_priv->link_state = state.up;
> + }
> + return 0;
> +}
> +
> +static int port_open(struct net_device *netdev)
> +{
> + struct ethsw_port_priv *port_priv = netdev_priv(netdev);
> + int err;
> +
> + /* No need to allow Tx as control interface is disabled */
> + netif_tx_stop_all_queues(netdev);
> +
> + err = dpsw_if_enable(port_priv->ethsw_data->mc_io, 0,
> + port_priv->ethsw_data->dpsw_handle,
> + port_priv->idx);
> + if (err) {
> + netdev_err(netdev, "dpsw_if_enable err %d\n", err);
> + return err;
> + }
> +
> + /* sync carrier state */
> + err = port_carrier_state_sync(netdev);
> + if (err) {
> + netdev_err(netdev,
> + "port_carrier_state_sync err %d\n", err);
> + goto err_carrier_sync;
> + }
> +
> + return 0;
> +
> +err_carrier_sync:
> + dpsw_if_disable(port_priv->ethsw_data->mc_io, 0,
> + port_priv->ethsw_data->dpsw_handle,
> + port_priv->idx);
> + return err;
> +}
> +
> +static int port_stop(struct net_device *netdev)
> +{
> + struct ethsw_port_priv *port_priv = netdev_priv(netdev);
> + int err;
> +
> + err = dpsw_if_disable(port_priv->ethsw_data->mc_io, 0,
> + port_priv->ethsw_data->dpsw_handle,
> + port_priv->idx);
> + if (err) {
> + netdev_err(netdev, "dpsw_if_disable err %d\n", err);
> + return err;
> + }
> +
> + return 0;
> +}
> +
> +static netdev_tx_t port_dropframe(struct sk_buff *skb,
> + struct net_device *netdev)
> +{
> + /* we don't support I/O for now, drop the frame */
> + dev_kfree_skb_any(skb);
> +
> + return NETDEV_TX_OK;
> +}
> +
> +static const struct net_device_ops ethsw_port_ops = {
> + .ndo_open = port_open,
> + .ndo_stop = port_stop,
> +
> + .ndo_set_mac_address = eth_mac_addr,
> + .ndo_change_mtu = port_change_mtu,
> + .ndo_has_offload_stats = port_has_offload_stats,
> + .ndo_get_offload_stats = port_get_offload_stats,
> +
> + .ndo_start_xmit = port_dropframe,
> +};
> +
> +static void ethsw_links_state_update(struct ethsw_core *ethsw)
> +{
> + int i;
> +
> + for (i = 0; i < ethsw->sw_attr.num_ifs; i++)
> + port_carrier_state_sync(ethsw->ports[i]->netdev);
> +}
> +
> +static irqreturn_t ethsw_irq0_handler(int irq_num, void *arg)
> +{
> + return IRQ_WAKE_THREAD;
> +}
> +
> +static irqreturn_t ethsw_irq0_handler_thread(int irq_num, void *arg)
> +{
> + struct device *dev = (struct device *)arg;
> + struct ethsw_core *ethsw = dev_get_drvdata(dev);
> +
> + /* Mask the events and the if_id reserved bits to be cleared on read */
> + u32 status = DPSW_IRQ_EVENT_LINK_CHANGED | 0xFFFF0000;
> + int err;
> +
> + err = dpsw_get_irq_status(ethsw->mc_io, 0, ethsw->dpsw_handle,
> + DPSW_IRQ_INDEX_IF, &status);
> + if (err) {
> + dev_err(dev, "Can't get irq status (err %d)", err);
> +
> + err = dpsw_clear_irq_status(ethsw->mc_io, 0, ethsw->dpsw_handle,
> + DPSW_IRQ_INDEX_IF, 0xFFFFFFFF);
> + if (err)
> + dev_err(dev, "Can't clear irq status (err %d)", err);
> + goto out;
> + }
> +
> + if (status & DPSW_IRQ_EVENT_LINK_CHANGED)
> + ethsw_links_state_update(ethsw);
> +
> +out:
> + return IRQ_HANDLED;
> +}
> +
> +static int ethsw_setup_irqs(struct fsl_mc_device *sw_dev)
> +{
> + struct device *dev = &sw_dev->dev;
> + struct ethsw_core *ethsw = dev_get_drvdata(dev);
> + u32 mask = DPSW_IRQ_EVENT_LINK_CHANGED;
> + struct fsl_mc_device_irq *irq;
> + int err;
> +
> + err = fsl_mc_allocate_irqs(sw_dev);
> + if (err) {
> + dev_err(dev, "MC irqs allocation failed\n");
> + return err;
> + }
> +
> + if (WARN_ON(sw_dev->obj_desc.irq_count != DPSW_IRQ_NUM)) {
> + err = -EINVAL;
> + goto free_irq;
> + }
> +
> + err = dpsw_set_irq_enable(ethsw->mc_io, 0, ethsw->dpsw_handle,
> + DPSW_IRQ_INDEX_IF, 0);
> + if (err) {
> + dev_err(dev, "dpsw_set_irq_enable err %d\n", err);
> + goto free_irq;
> + }
> +
> + irq = sw_dev->irqs[DPSW_IRQ_INDEX_IF];
> +
> + err = devm_request_threaded_irq(dev, irq->msi_desc->irq,
> + ethsw_irq0_handler,
> + ethsw_irq0_handler_thread,
> + IRQF_NO_SUSPEND | IRQF_ONESHOT,
> + dev_name(dev), dev);
> + if (err) {
> + dev_err(dev, "devm_request_threaded_irq(): %d", err);
> + goto free_irq;
> + }
> +
> + err = dpsw_set_irq_mask(ethsw->mc_io, 0, ethsw->dpsw_handle,
> + DPSW_IRQ_INDEX_IF, mask);
> + if (err) {
> + dev_err(dev, "dpsw_set_irq_mask(): %d", err);
> + goto free_devm_irq;
> + }
> +
> + err = dpsw_set_irq_enable(ethsw->mc_io, 0, ethsw->dpsw_handle,
> + DPSW_IRQ_INDEX_IF, 1);
> + if (err) {
> + dev_err(dev, "dpsw_set_irq_enable(): %d", err);
> + goto free_devm_irq;
> + }
> +
> + return 0;
> +
> +free_devm_irq:
> + devm_free_irq(dev, irq->msi_desc->irq, dev);
> +free_irq:
> + fsl_mc_free_irqs(sw_dev);
> + return err;
> +}
> +
> +static void ethsw_teardown_irqs(struct fsl_mc_device *sw_dev)
> +{
> + struct device *dev = &sw_dev->dev;
> + struct ethsw_core *ethsw = dev_get_drvdata(dev);
> + struct fsl_mc_device_irq *irq;
> +
> + irq = sw_dev->irqs[DPSW_IRQ_INDEX_IF];
> + dpsw_set_irq_enable(ethsw->mc_io, 0, ethsw->dpsw_handle,
> + DPSW_IRQ_INDEX_IF, 0);
You can still print an error message here, in case something goes wrong.
> + fsl_mc_free_irqs(sw_dev);
> +}
> +
> +static int swdev_port_attr_get(struct net_device *netdev,
> + struct switchdev_attr *attr)
> +{
> + struct ethsw_port_priv *port_priv = netdev_priv(netdev);
> +
> + switch (attr->id) {
> + case SWITCHDEV_ATTR_ID_PORT_PARENT_ID:
> + attr->u.ppid.id_len = 1;
> + attr->u.ppid.id[0] = port_priv->ethsw_data->dev_id;
> + break;
> + case SWITCHDEV_ATTR_ID_PORT_BRIDGE_FLAGS:
> + attr->u.brport_flags =
> + (port_priv->ethsw_data->learning ? BR_LEARNING : 0) |
> + (port_priv->flood ? BR_FLOOD : 0);
> + break;
> + case SWITCHDEV_ATTR_ID_PORT_BRIDGE_FLAGS_SUPPORT:
> + attr->u.brport_flags_support = BR_LEARNING | BR_FLOOD;
> + break;
> + default:
> + return -EOPNOTSUPP;
> + }
> +
> + return 0;
> +}
> +
> +static int port_attr_stp_state_set(struct net_device *netdev,
> + struct switchdev_trans *trans,
> + u8 state)
> +{
> + struct ethsw_port_priv *port_priv = netdev_priv(netdev);
> +
> + if (switchdev_trans_ph_prepare(trans))
> + return 0;
> +
> + return ethsw_port_set_stp_state(port_priv, state);
> +}
> +
> +static int port_attr_br_flags_set(struct net_device *netdev,
> + struct switchdev_trans *trans,
> + unsigned long flags)
> +{
> + struct ethsw_port_priv *port_priv = netdev_priv(netdev);
> + int err = 0;
> +
> + if (switchdev_trans_ph_prepare(trans))
> + return 0;
> +
> + /* Learning is enabled per switch */
> + err = ethsw_set_learning(port_priv->ethsw_data, flags & BR_LEARNING);
> + if (err)
> + goto exit;
> +
> + err = ethsw_port_set_flood(port_priv, flags & BR_FLOOD);
> +
> +exit:
> + return err;
> +}
> +
> +static int swdev_port_attr_set(struct net_device *netdev,
> + const struct switchdev_attr *attr,
> + struct switchdev_trans *trans)
> +{
> + int err = 0;
> +
> + switch (attr->id) {
> + case SWITCHDEV_ATTR_ID_PORT_STP_STATE:
> + err = port_attr_stp_state_set(netdev, trans,
> + attr->u.stp_state);
> + break;
> + case SWITCHDEV_ATTR_ID_PORT_BRIDGE_FLAGS:
> + err = port_attr_br_flags_set(netdev, trans,
> + attr->u.brport_flags);
> + break;
> + case SWITCHDEV_ATTR_ID_BRIDGE_VLAN_FILTERING:
> + /* VLANs are supported by default */
> + break;
> + default:
> + err = -EOPNOTSUPP;
> + break;
> + }
> +
> + return err;
> +}
> +
> +static int port_vlans_add(struct net_device *netdev,
> + const struct switchdev_obj_port_vlan *vlan,
> + struct switchdev_trans *trans)
> +{
> + struct ethsw_port_priv *port_priv = netdev_priv(netdev);
> + int vid, err;
> +
> + if (switchdev_trans_ph_prepare(trans))
> + return 0;
> +
> + for (vid = vlan->vid_begin; vid <= vlan->vid_end; vid++) {
> + if (!port_priv->ethsw_data->vlans[vid]) {
> + /* this is a new VLAN */
> + err = ethsw_add_vlan(port_priv->ethsw_data, vid);
> + if (err)
> + return err;
> +
> + port_priv->ethsw_data->vlans[vid] |= ETHSW_VLAN_GLOBAL;
> + }
> + err = ethsw_port_add_vlan(port_priv, vid, vlan->flags);
> + if (err)
> + break;
> + }
> +
> + return err;
> +}
> +
> +static int port_lookup_address(struct net_device *netdev, int is_uc,
> + const unsigned char *addr)
> +{
> + struct netdev_hw_addr_list *list = (is_uc) ? &netdev->uc : &netdev->mc;
> + struct netdev_hw_addr *ha;
> +
> + netif_addr_lock_bh(netdev);
> + list_for_each_entry(ha, &list->list, list) {
> + if (ether_addr_equal(ha->addr, addr)) {
> + netif_addr_unlock_bh(netdev);
> + return 1;
> + }
> + }
> + netif_addr_unlock_bh(netdev);
> + return 0;
> +}
> +
> +static int port_mdb_add(struct net_device *netdev,
> + const struct switchdev_obj_port_mdb *mdb,
> + struct switchdev_trans *trans)
> +{
> + struct ethsw_port_priv *port_priv = netdev_priv(netdev);
> + int err;
> +
> + if (switchdev_trans_ph_prepare(trans))
> + return 0;
> +
> + /* Check if address is already set on this port */
> + if (port_lookup_address(netdev, 0, mdb->addr))
> + return -EEXIST;
> +
> + err = ethsw_port_fdb_add_mc(port_priv, mdb->addr);
> + if (err)
> + return err;
> +
> + err = dev_mc_add(netdev, mdb->addr);
> + if (err)
> + netdev_err(netdev, "dev_mc_add err %d\n", err);
In the error case, shouldn't there be a "ethsw_port_fdb_del_mc" ?
> +
> + return err;
> +}
> +
> +static int swdev_port_obj_add(struct net_device *netdev,
> + const struct switchdev_obj *obj,
> + struct switchdev_trans *trans)
> +{
> + int err;
> +
> + switch (obj->id) {
> + case SWITCHDEV_OBJ_ID_PORT_VLAN:
> + err = port_vlans_add(netdev,
> + SWITCHDEV_OBJ_PORT_VLAN(obj),
> + trans);
> + break;
> + case SWITCHDEV_OBJ_ID_PORT_MDB:
> + err = port_mdb_add(netdev,
> + SWITCHDEV_OBJ_PORT_MDB(obj),
> + trans);
> + break;
> + default:
> + err = -EOPNOTSUPP;
> + break;
> + }
> +
> + return err;
> +}
> +
> +static int ethsw_port_del_vlan(struct ethsw_port_priv *port_priv, u16 vid)
> +{
> + struct ethsw_core *ethsw = port_priv->ethsw_data;
> + struct net_device *netdev = port_priv->netdev;
> + struct dpsw_vlan_if_cfg vcfg;
> + int i, err, err2;
> + bool is_oper;
> +
> + if (!port_priv->vlans[vid])
> + return -ENOENT;
> +
> + if (port_priv->vlans[vid] & ETHSW_VLAN_PVID) {
> + struct dpsw_tci_cfg tci_cfg = { 0 };
> + /* Interface needs to be down to change PVID */
> + is_oper = netif_oper_up(netdev);
> +
> + if (is_oper) {
> + err = dpsw_if_disable(ethsw->mc_io, 0,
> + ethsw->dpsw_handle,
> + port_priv->idx);
> + if (err) {
> + netdev_err(netdev, "dpsw_if_disable err %d\n",
> + err);
> + goto exit_err;
> + }
> + }
> +
> + err = dpsw_if_set_tci(ethsw->mc_io, 0,
> + ethsw->dpsw_handle,
> + port_priv->idx, &tci_cfg);
> + if (!err) {
> + port_priv->vlans[vid] &= ~ETHSW_VLAN_PVID;
> + port_priv->pvid = 0;
> + } else {
> + netdev_err(netdev, "dpsw_if_set_tci err %d\n", err);
> + }
> +
> + if (is_oper) {
> + err2 = dpsw_if_enable(ethsw->mc_io, 0,
> + ethsw->dpsw_handle,
> + port_priv->idx);
> + if (err2) {
> + netdev_err(netdev, "dpsw_if_enable err %d\n",
> + err2);
> + return err2;
> + }
> + }
> +
> + if (err)
> + goto exit_err;
> + }
> +
> + vcfg.num_ifs = 1;
> + vcfg.if_id[0] = port_priv->idx;
> + if (port_priv->vlans[vid] & ETHSW_VLAN_UNTAGGED) {
> + err = dpsw_vlan_remove_if_untagged(ethsw->mc_io, 0,
> + ethsw->dpsw_handle,
> + vid, &vcfg);
> + if (err) {
> + netdev_err(netdev,
> + "dpsw_vlan_remove_if_untagged err %d\n",
> + err);
> + }
> + port_priv->vlans[vid] &= ~ETHSW_VLAN_UNTAGGED;
> + }
> +
> + if (port_priv->vlans[vid] & ETHSW_VLAN_MEMBER) {
> + err = dpsw_vlan_remove_if(ethsw->mc_io, 0, ethsw->dpsw_handle,
> + vid, &vcfg);
> + if (err) {
> + netdev_err(netdev,
> + "dpsw_vlan_remove_if err %d\n", err);
> + return err;
> + }
> + port_priv->vlans[vid] &= ~ETHSW_VLAN_MEMBER;
> +
> + /* Delete VLAN from switch if it is no longer configured on
> + * any port
> + */
> + for (i = 0; i < ethsw->sw_attr.num_ifs; i++)
> + if (ethsw->ports[i]->vlans[vid] & ETHSW_VLAN_MEMBER)
> + return 0; /* Found a port member in VID */
> +
> + ethsw->vlans[vid] &= ~ETHSW_VLAN_GLOBAL;
> +
> + err = ethsw_dellink_switch(ethsw, vid);
> + if (err)
> + goto exit_err;
> + }
> +
> + return 0;
> +exit_err:
> + return err;
> +}
> +
> +static int port_vlans_del(struct net_device *netdev,
> + const struct switchdev_obj_port_vlan *vlan)
> +{
> + struct ethsw_port_priv *port_priv = netdev_priv(netdev);
> + int vid, err;
> +
> + for (vid = vlan->vid_begin; vid <= vlan->vid_end; vid++) {
> + err = ethsw_port_del_vlan(port_priv, vid);
> + if (err)
> + break;
> + }
> +
> + return err;
> +}
> +
> +static int port_mdb_del(struct net_device *netdev,
> + const struct switchdev_obj_port_mdb *mdb)
> +{
> + struct ethsw_port_priv *port_priv = netdev_priv(netdev);
> + int err;
> +
> + if (!port_lookup_address(netdev, 0, mdb->addr))
> + return -ENOENT;
> +
> + err = ethsw_port_fdb_del_mc(port_priv, mdb->addr);
> + if (err)
> + return err;
> +
> + err = dev_mc_del(netdev, mdb->addr);
> + if (err) {
> + netdev_err(netdev, "dev_mc_del err %d\n", err);
> + return err;
> + }
> +
> + return err;
> +}
> +
> +static int swdev_port_obj_del(struct net_device *netdev,
> + const struct switchdev_obj *obj)
> +{
> + int err;
> +
> + switch (obj->id) {
> + case SWITCHDEV_OBJ_ID_PORT_VLAN:
> + err = port_vlans_del(netdev, SWITCHDEV_OBJ_PORT_VLAN(obj));
> + break;
> + case SWITCHDEV_OBJ_ID_PORT_MDB:
> + err = port_mdb_del(netdev, SWITCHDEV_OBJ_PORT_MDB(obj));
> + break;
> + default:
> + err = -EOPNOTSUPP;
> + break;
> + }
> + return err;
> +}
> +
> +static const struct switchdev_ops ethsw_port_switchdev_ops = {
> + .switchdev_port_attr_get = swdev_port_attr_get,
> + .switchdev_port_attr_set = swdev_port_attr_set,
> + .switchdev_port_obj_add = swdev_port_obj_add,
> + .switchdev_port_obj_del = swdev_port_obj_del,
> +};
> +
> +/* For the moment, only flood setting needs to be updated */
> +static int port_bridge_join(struct net_device *netdev)
> +{
> + struct ethsw_port_priv *port_priv = netdev_priv(netdev);
> +
> + /* Enable flooding */
> + return ethsw_port_set_flood(port_priv, 1);
> +}
> +
> +static int port_bridge_leave(struct net_device *netdev)
> +{
> + struct ethsw_port_priv *port_priv = netdev_priv(netdev);
> +
> + /* Disable flooding */
> + return ethsw_port_set_flood(port_priv, 0);
> +}
> +
> +static int port_netdevice_event(struct notifier_block *unused,
> + unsigned long event, void *ptr)
> +{
> + struct net_device *netdev = netdev_notifier_info_to_dev(ptr);
> + struct netdev_notifier_changeupper_info *info = ptr;
> + struct net_device *upper_dev;
> + int err = 0;
> +
> + if (netdev->netdev_ops != ðsw_port_ops)
> + return NOTIFY_DONE;
> +
> + /* Handle just upper dev link/unlink for the moment */
> + if (event == NETDEV_CHANGEUPPER) {
> + upper_dev = info->upper_dev;
> + if (netif_is_bridge_master(upper_dev)) {
> + if (info->linking)
> + err = port_bridge_join(netdev);
> + else
> + err = port_bridge_leave(netdev);
> + }
> + }
> +
> + return notifier_from_errno(err);
> +}
> +
> +static struct notifier_block port_nb __read_mostly = {
> + .notifier_call = port_netdevice_event,
> +};
> +
> +struct ethsw_switchdev_event_work {
> + struct work_struct work;
> + struct switchdev_notifier_fdb_info fdb_info;
> + struct net_device *dev;
> + unsigned long event;
> +};
> +
> +static void ethsw_switchdev_event_work(struct work_struct *work)
> +{
> + struct ethsw_switchdev_event_work *switchdev_work =
> + container_of(work, struct ethsw_switchdev_event_work, work);
> + struct net_device *dev = switchdev_work->dev;
> + struct switchdev_notifier_fdb_info *fdb_info;
> + struct ethsw_port_priv *port_priv;
> +
> + rtnl_lock();
> + port_priv = netdev_priv(dev);
> + fdb_info = &switchdev_work->fdb_info;
> +
> + switch (switchdev_work->event) {
> + case SWITCHDEV_FDB_ADD_TO_DEVICE:
> + ethsw_port_fdb_add_uc(netdev_priv(dev), fdb_info->addr);
> + break;
> + case SWITCHDEV_FDB_DEL_TO_DEVICE:
> + ethsw_port_fdb_del_uc(netdev_priv(dev), fdb_info->addr);
> + break;
> + }
> +
> + rtnl_unlock();
> + kfree(switchdev_work->fdb_info.addr);
> + kfree(switchdev_work);
> + dev_put(dev);
> +}
> +
> +/* Called under rcu_read_lock() */
> +static int port_switchdev_event(struct notifier_block *unused,
> + unsigned long event, void *ptr)
> +{
> + struct net_device *dev = switchdev_notifier_info_to_dev(ptr);
> + struct ethsw_switchdev_event_work *switchdev_work;
> + struct switchdev_notifier_fdb_info *fdb_info = ptr;
> +
> + switchdev_work = kzalloc(sizeof(*switchdev_work), GFP_ATOMIC);
> + if (!switchdev_work)
> + return NOTIFY_BAD;
> +
> + INIT_WORK(&switchdev_work->work, ethsw_switchdev_event_work);
> + switchdev_work->dev = dev;
> + switchdev_work->event = event;
> +
> + switch (event) {
> + case SWITCHDEV_FDB_ADD_TO_DEVICE:
> + case SWITCHDEV_FDB_DEL_TO_DEVICE:
> + memcpy(&switchdev_work->fdb_info, ptr,
> + sizeof(switchdev_work->fdb_info));
> + switchdev_work->fdb_info.addr = kzalloc(ETH_ALEN, GFP_ATOMIC);
> + if (!switchdev_work->fdb_info.addr)
> + goto err_addr_alloc;
> +
> + ether_addr_copy((u8 *)switchdev_work->fdb_info.addr,
> + fdb_info->addr);
> +
> + /* Take a reference on the device to avoid being freed. */
> + dev_hold(dev);
> + break;
> + default:
> + return NOTIFY_DONE;
> + }
> +
> + queue_work(ethsw_owq, &switchdev_work->work);
> +
> + return NOTIFY_DONE;
> +
> +err_addr_alloc:
> + kfree(switchdev_work);
> + return NOTIFY_BAD;
> +}
> +
> +static struct notifier_block port_switchdev_nb = {
> + .notifier_call = port_switchdev_event,
> +};
> +
> +static int ethsw_register_notifier(struct device *dev)
> +{
> + int err;
> +
> + err = register_netdevice_notifier(&port_nb);
> + if (err) {
> + dev_err(dev, "Failed to register netdev notifier\n");
> + return err;
> + }
> +
> + err = register_switchdev_notifier(&port_switchdev_nb);
> + if (err) {
> + dev_err(dev, "Failed to register switchdev notifier\n");
> + goto err_switchdev_nb;
> + }
> +
> + return 0;
> +
> +err_switchdev_nb:
> + unregister_netdevice_notifier(&port_nb);
> + return err;
> +}
> +
> +static int ethsw_open(struct ethsw_core *ethsw)
Minor formatting error, tab in function signature - see following function as well.
> +{
> + struct ethsw_port_priv *port_priv = NULL;
> + int i, err;
> +
> + err = dpsw_enable(ethsw->mc_io, 0, ethsw->dpsw_handle);
> + if (err) {
> + dev_err(ethsw->dev, "dpsw_enable err %d\n", err);
> + return err;
> + }
> +
> + for (i = 0; i < ethsw->sw_attr.num_ifs; i++) {
> + port_priv = ethsw->ports[i];
> + err = dev_open(port_priv->netdev);
> + if (err) {
> + netdev_err(port_priv->netdev, "dev_open err %d\n", err);
> + return err;
> + }
> + }
> +
> + return 0;
> +}
> +
> +static int ethsw_stop(struct ethsw_core *ethsw)
> +{
> + struct ethsw_port_priv *port_priv = NULL;
> + int i, err;
> +
> + destroy_workqueue(ethsw_owq);
If workqueue is destroyed here, shouldn't it be alloc'd in ethsw_open?
> +
> + for (i = 0; i < ethsw->sw_attr.num_ifs; i++) {
> + port_priv = ethsw->ports[i];
> + dev_close(port_priv->netdev);
> + }
> +
> + err = dpsw_disable(ethsw->mc_io, 0, ethsw->dpsw_handle);
> + if (err) {
> + dev_err(ethsw->dev, "dpsw_disable err %d\n", err);
> + return err;
> + }
> +
> + return 0;
> +}
> +
> +static int ethsw_init(struct fsl_mc_device *sw_dev)
> +{
> + struct device *dev = &sw_dev->dev;
> + struct ethsw_core *ethsw = dev_get_drvdata(dev);
> + u16 version_major, version_minor, i;
> + struct dpsw_stp_cfg stp_cfg;
> + int err;
> +
> + ethsw->dev_id = sw_dev->obj_desc.id;
> +
> + err = dpsw_open(ethsw->mc_io, 0, ethsw->dev_id, ðsw->dpsw_handle);
> + if (err) {
> + dev_err(dev, "dpsw_open err %d\n", err);
> + return err;
> + }
> +
> + err = dpsw_get_attributes(ethsw->mc_io, 0, ethsw->dpsw_handle,
> + ðsw->sw_attr);
> + if (err) {
> + dev_err(dev, "dpsw_get_attributes err %d\n", err);
> + goto err_close;
> + }
> +
> + err = dpsw_get_api_version(ethsw->mc_io, 0,
> + &version_major,
> + &version_minor);
> + if (err) {
> + dev_err(dev, "dpsw_get_api_version err %d\n", err);
> + goto err_close;
> + }
> +
> + /* Minimum supported DPSW version check */
> + if (version_major < DPSW_MIN_VER_MAJOR ||
> + (version_major == DPSW_MIN_VER_MAJOR &&
> + version_minor < DPSW_MIN_VER_MINOR)) {
> + dev_err(dev, "DPSW version %d:%d not supported. Use %d.%d or
> greater.\n",
> + version_major,
> + version_minor,
> + DPSW_MIN_VER_MAJOR, DPSW_MIN_VER_MINOR);
> + err = -ENOTSUPP;
> + goto err_close;
> + }
> +
> + err = dpsw_reset(ethsw->mc_io, 0, ethsw->dpsw_handle);
> + if (err) {
> + dev_err(dev, "dpsw_reset err %d\n", err);
> + goto err_close;
> + }
> +
> + err = dpsw_fdb_set_learning_mode(ethsw->mc_io, 0, ethsw->dpsw_handle, 0,
> + DPSW_FDB_LEARNING_MODE_HW);
> + if (err) {
> + dev_err(dev, "dpsw_fdb_set_learning_mode err %d\n", err);
> + goto err_close;
> + }
> +
> + stp_cfg.vlan_id = DEFAULT_VLAN_ID;
> + stp_cfg.state = DPSW_STP_STATE_FORWARDING;
> +
> + for (i = 0; i < ethsw->sw_attr.num_ifs; i++) {
> + err = dpsw_if_set_stp(ethsw->mc_io, 0, ethsw->dpsw_handle, i,
> + &stp_cfg);
> + if (err) {
> + dev_err(dev, "dpsw_if_set_stp err %d for port %d\n",
> + err, i);
> + goto err_close;
> + }
> +
> + err = dpsw_if_set_broadcast(ethsw->mc_io, 0,
> + ethsw->dpsw_handle, i, 1);
> + if (err) {
> + dev_err(dev,
> + "dpsw_if_set_broadcast err %d for port %d\n",
> + err, i);
> + goto err_close;
> + }
> + }
> +
> + ethsw_owq = alloc_ordered_workqueue("%s_ordered", WQ_MEM_RECLAIM,
> + "ethsw");
> + if (!ethsw_owq) {
> + err = -ENOMEM;
> + goto err_close;
> + }
> +
> + err = ethsw_register_notifier(dev);
> + if (err)
> + goto err_destroy_ordered_workqueue;
> +
> + return 0;
> +
> +err_destroy_ordered_workqueue:
> + destroy_workqueue(ethsw_owq);
> +
> +err_close:
> + dpsw_close(ethsw->mc_io, 0, ethsw->dpsw_handle);
> + return err;
> +}
> +
> +static int ethsw_port_init(struct ethsw_port_priv *port_priv, u16 port)
> +{
> + const char def_mcast[ETH_ALEN] = {0x01, 0x00, 0x5e, 0x00, 0x00, 0x01};
> + struct net_device *netdev = port_priv->netdev;
> + struct ethsw_core *ethsw = port_priv->ethsw_data;
> + struct dpsw_tci_cfg tci_cfg = {0};
> + struct dpsw_vlan_if_cfg vcfg;
> + int err;
> +
> + /* Switch starts with all ports configured to VLAN 1. Need to
> + * remove this setting to allow configuration at bridge join
> + */
> + vcfg.num_ifs = 1;
> + vcfg.if_id[0] = port_priv->idx;
> +
> + err = dpsw_vlan_remove_if_untagged(ethsw->mc_io, 0, ethsw->dpsw_handle,
> + DEFAULT_VLAN_ID, &vcfg);
> + if (err) {
> + netdev_err(netdev, "dpsw_vlan_remove_if_untagged err %d\n",
> + err);
> + return err;
> + }
> +
> + err = dpsw_if_set_tci(ethsw->mc_io, 0, ethsw->dpsw_handle,
> + port_priv->idx, &tci_cfg);
> + if (err) {
> + netdev_err(netdev, "dpsw_if_set_tci err %d\n", err);
> + return err;
> + }
> +
> + err = dpsw_vlan_remove_if(ethsw->mc_io, 0, ethsw->dpsw_handle,
> + DEFAULT_VLAN_ID, &vcfg);
> + if (err) {
> + netdev_err(netdev, "dpsw_vlan_remove_if err %d\n", err);
> + return err;
> + }
> +
> + err = ethsw_port_fdb_add_mc(port_priv, def_mcast);
> +
> + return err;
> +}
> +
> +static void ethsw_takedown(struct fsl_mc_device *sw_dev)
> +{
> + struct device *dev = &sw_dev->dev;
> + struct ethsw_core *ethsw = dev_get_drvdata(dev);
> + int err;
> +
> + err = unregister_switchdev_notifier(&port_switchdev_nb);
> + if (err)
> + dev_err(dev,
> + "Failed to unregister switchdev notifier (%d)\n", err);
> +
> + err = unregister_netdevice_notifier(&port_nb);
> + if (err)
> + dev_err(dev,
> + "Failed to unregister netdev notifier (%d)\n", err);
Above 2 can be grouped into ethsw_unregister_notifier.
> +
> + err = dpsw_close(ethsw->mc_io, 0, ethsw->dpsw_handle);
> + if (err)
> + dev_warn(dev, "dpsw_close err %d\n", err);
> +}
> +
> +static int ethsw_remove(struct fsl_mc_device *sw_dev)
> +{
> + struct ethsw_port_priv *port_priv;
> + struct ethsw_core *ethsw;
> + struct device *dev;
> + int i;
> +
> + dev = &sw_dev->dev;
> + ethsw = dev_get_drvdata(dev);
> +
> + ethsw_teardown_irqs(sw_dev);
> +
> + rtnl_lock();
> + ethsw_stop(ethsw);
> + rtnl_unlock();
> +
> + for (i = 0; i < ethsw->sw_attr.num_ifs; i++) {
> + port_priv = ethsw->ports[i];
> + unregister_netdev(port_priv->netdev);
> + free_netdev(port_priv->netdev);
> + }
> + kfree(ethsw->ports);
> +
> + ethsw_takedown(sw_dev);
> + fsl_mc_portal_free(ethsw->mc_io);
> +
> + kfree(ethsw);
> +
> + dev_set_drvdata(dev, NULL);
> +
> + return 0;
> +}
> +
> +static int ethsw_probe_port(struct ethsw_core *ethsw, u16 port_idx)
> +{
> + struct ethsw_port_priv *port_priv;
> + struct device *dev = ethsw->dev;
> + struct net_device *port_netdev;
> + int err;
> +
> + port_netdev = alloc_etherdev(sizeof(struct ethsw_port_priv));
> + if (!port_netdev) {
> + dev_err(dev, "alloc_etherdev error\n");
> + return -ENOMEM;
> + }
> +
> + port_priv = netdev_priv(port_netdev);
> + port_priv->netdev = port_netdev;
> + port_priv->ethsw_data = ethsw;
> +
> + port_priv->idx = port_idx;
> + port_priv->stp_state = BR_STATE_FORWARDING;
> +
> + /* Flooding is implicitly enabled */
> + port_priv->flood = true;
> +
> + SET_NETDEV_DEV(port_netdev, dev);
> + port_netdev->netdev_ops = ðsw_port_ops;
> + port_netdev->switchdev_ops = ðsw_port_switchdev_ops;
> +
> + /* Set MTU limits */
> + port_netdev->min_mtu = ETH_MIN_MTU;
> + port_netdev->max_mtu = ETHSW_MAX_FRAME_LENGTH;
> +
> + err = register_netdev(port_netdev);
> + if (err < 0) {
> + dev_err(dev, "register_netdev error %d\n", err);
> + free_netdev(port_netdev);
> + return err;
> + }
> +
> + ethsw->ports[port_idx] = port_priv;
> +
> + return ethsw_port_init(port_priv, port_idx);
> +}
> +
> +static int ethsw_probe(struct fsl_mc_device *sw_dev)
> +{
> + struct device *dev = &sw_dev->dev;
> + struct ethsw_core *ethsw;
> + int err;
> + u16 i, j;
> +
> + /* Allocate switch core*/
> + ethsw = kzalloc(sizeof(*ethsw), GFP_KERNEL);
> +
> + if (!ethsw)
> + return -ENOMEM;
> +
> + ethsw->dev = dev;
> + dev_set_drvdata(dev, ethsw);
> +
> + err = fsl_mc_portal_allocate(sw_dev, 0, ðsw->mc_io);
> + if (err) {
> + dev_err(dev, "fsl_mc_portal_allocate err %d\n", err);
> + goto err_free_drvdata;
> + }
> +
> + err = ethsw_init(sw_dev);
> + if (err)
> + goto err_free_cmdport;
> +
> + /* DEFAULT_VLAN_ID is implicitly configured on the switch */
> + ethsw->vlans[DEFAULT_VLAN_ID] = ETHSW_VLAN_MEMBER;
> +
> + /* Learning is implicitly enabled */
> + ethsw->learning = true;
> +
> + ethsw->ports = kcalloc(ethsw->sw_attr.num_ifs, sizeof(*ethsw->ports),
> + GFP_KERNEL);
> + if (!(ethsw->ports)) {
> + err = -ENOMEM;
> + goto err_takedown;
> + }
> +
> + for (i = 0; i < ethsw->sw_attr.num_ifs; i++) {
> + err = ethsw_probe_port(ethsw, i);
> + if (err) {
> + /* Cleanup previous ports only */
> + for (j = 0; j < i; j++) {
I think you can go with
for (i--; i >= 0; i--)
or better yet:
goto err_free_ports
and refactor err_free_ports to look like:
for (i--; i >= 0; i--) {
...
}
Best regards,
Bogdan P.
> + unregister_netdev(ethsw->ports[j]->netdev);
> + free_netdev(ethsw->ports[j]->netdev);
> + }
> + goto err_takedown;
> + }
> + }
> +
> + /* Switch starts up enabled */
> + rtnl_lock();
> + err = ethsw_open(ethsw);
> + rtnl_unlock();
> + if (err)
> + goto err_free_ports;
> +
> + /* Setup IRQs */
> + err = ethsw_setup_irqs(sw_dev);
> + if (err)
> + goto err_stop;
> +
> + dev_info(dev, "probed %d port switch\n", ethsw->sw_attr.num_ifs);
> + return 0;
> +
> +err_stop:
> + rtnl_lock();
> + ethsw_stop(ethsw);
> + rtnl_unlock();
> +
> +err_free_ports:
> + for (i = 0; i < ethsw->sw_attr.num_ifs; i++) {
> + unregister_netdev(ethsw->ports[i]->netdev);
> + free_netdev(ethsw->ports[i]->netdev);
> + }
> + kfree(ethsw->ports);
> +
> +err_takedown:
> + ethsw_takedown(sw_dev);
> +
> +err_free_cmdport:
> + fsl_mc_portal_free(ethsw->mc_io);
> +
> +err_free_drvdata:
> + kfree(ethsw);
> + dev_set_drvdata(dev, NULL);
> +
> + return err;
> +}
> +
> +static const struct fsl_mc_device_id ethsw_match_id_table[] = {
> + {
> + .vendor = FSL_MC_VENDOR_FREESCALE,
> + .obj_type = "dpsw",
> + },
> + { .vendor = 0x0 }
> +};
> +MODULE_DEVICE_TABLE(fslmc, ethsw_match_id_table);
> +
> +static struct fsl_mc_driver eth_sw_drv = {
> + .driver = {
> + .name = KBUILD_MODNAME,
> + .owner = THIS_MODULE,
> + },
> + .probe = ethsw_probe,
> + .remove = ethsw_remove,
> + .match_id_table = ethsw_match_id_table
> +};
> +
> +module_fsl_mc_driver(eth_sw_drv);
> +
> +MODULE_LICENSE("Dual BSD/GPL");
> +MODULE_DESCRIPTION("DPAA2 Ethernet Switch Driver");
> diff --git a/drivers/staging/fsl-dpaa2/ethsw/ethsw.h b/drivers/staging/fsl-
> dpaa2/ethsw/ethsw.h
> new file mode 100644
> index 0000000..8c1d645
> --- /dev/null
> +++ b/drivers/staging/fsl-dpaa2/ethsw/ethsw.h
> @@ -0,0 +1,88 @@
> +/* Copyright 2014-2017 Freescale Semiconductor Inc.
> + * Copyright 2017 NXP
> + *
> + * Redistribution and use in source and binary forms, with or without
> + * modification, are permitted provided that the following conditions are met:
> + * * Redistributions of source code must retain the above copyright
> + * notice, this list of conditions and the following disclaimer.
> + * * 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.
> + * * Neither the name of the above-listed copyright holders nor the
> + * names of any contributors may be used to endorse or promote products
> + * derived from this software without specific prior written permission.
> + *
> + *
> + * ALTERNATIVELY, this software may be distributed under the terms of the
> + * GNU General Public License ("GPL") as published by the Free Software
> + * Foundation, either version 2 of that License or (at your option) any
> + * later version.
> + *
> + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
> + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
> + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
> + * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDERS OR CONTRIBUTORS BE
> + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
> + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
> + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
> + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
> + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
> + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
> + * POSSIBILITY OF SUCH DAMAGE.
> + */
> +
> +#ifndef __ETHSW_H
> +#define __ETHSW_H
> +
> +#include <linux/netdevice.h>
> +#include <linux/etherdevice.h>
> +#include <linux/rtnetlink.h>
> +#include <linux/if_vlan.h>
> +#include <uapi/linux/if_bridge.h>
> +#include <net/switchdev.h>
> +#include <linux/if_bridge.h>
> +
> +#include "dpsw.h"
> +
> +/* Number of IRQs supported */
> +#define DPSW_IRQ_NUM 2
> +
> +#define ETHSW_VLAN_MEMBER 1
> +#define ETHSW_VLAN_UNTAGGED 2
> +#define ETHSW_VLAN_PVID 4
> +#define ETHSW_VLAN_GLOBAL 8
> +
> +/* Maximum Frame Length supported by HW (currently 10k) */
> +#define DPAA2_MFL (10 * 1024)
> +#define ETHSW_MAX_FRAME_LENGTH (DPAA2_MFL - VLAN_ETH_HLEN - ETH_FCS_LEN)
> +#define ETHSW_L2_MAX_FRM(mtu) ((mtu) + VLAN_ETH_HLEN + ETH_FCS_LEN)
> +
> +struct ethsw_core;
> +
> +/* Per port private data */
> +struct ethsw_port_priv {
> + struct net_device *netdev;
> + u16 idx;
> + struct ethsw_core *ethsw_data;
> + u8 link_state;
> + u8 stp_state;
> + bool flood;
> +
> + u8 vlans[VLAN_VID_MASK + 1];
> + u16 pvid;
> +};
> +
> +/* Switch data */
> +struct ethsw_core {
> + struct device *dev;
> + struct fsl_mc_io *mc_io;
> + u16 dpsw_handle;
> + struct dpsw_attr sw_attr;
> + int dev_id;
> + struct ethsw_port_priv **ports;
> +
> + u8 vlans[VLAN_VID_MASK + 1];
> + bool learning;
> +};
> +
> +#endif /* __ETHSW_H */
> --
> 1.9.1
^ permalink raw reply
* (unknown),
From: marketing @ 2017-09-29 13:49 UTC (permalink / raw)
To: netdev
[-- Attachment #1: 761981.zip --]
[-- Type: application/zip, Size: 7219 bytes --]
^ permalink raw reply
* RE: [RESEND PATCH 2/6] staging: fsl-dpaa2/ethsw: Add Freescale DPAA2 Ethernet Switch driver
From: Razvan Stefanescu @ 2017-09-29 13:59 UTC (permalink / raw)
To: Bogdan Purcareata, gregkh@linuxfoundation.org
Cc: devel@driverdev.osuosl.org, linux-kernel@vger.kernel.org,
netdev@vger.kernel.org, agraf@suse.de, arnd@arndb.de,
Alexandru Marginean, Ruxandra Ioana Radulescu, Laurentiu Tudor,
stuyoder@gmail.com
In-Reply-To: <DB5PR04MB12400025DF190790B4792B50EA7E0@DB5PR04MB1240.eurprd04.prod.outlook.com>
> -----Original Message-----
> From: Bogdan Purcareata
> Sent: Friday, September 29, 2017 16:36
> To: Razvan Stefanescu <razvan.stefanescu@nxp.com>;
> gregkh@linuxfoundation.org
> Cc: devel@driverdev.osuosl.org; linux-kernel@vger.kernel.org;
> netdev@vger.kernel.org; agraf@suse.de; arnd@arndb.de; Alexandru Marginean
> <alexandru.marginean@nxp.com>; Ruxandra Ioana Radulescu
> <ruxandra.radulescu@nxp.com>; Laurentiu Tudor <laurentiu.tudor@nxp.com>;
> stuyoder@gmail.com
> Subject: RE: [RESEND PATCH 2/6] staging: fsl-dpaa2/ethsw: Add Freescale DPAA2
> Ethernet Switch driver
>
> > Introduce the DPAA2 Ethernet Switch driver, which manages Datapath Switch
> > (DPSW) objects discovered on the MC bus.
> >
> > Suggested-by: Alexandru Marginean <alexandru.marginean@nxp.com>
> > Signed-off-by: Razvan Stefanescu <razvan.stefanescu@nxp.com>
> > ---
> > drivers/staging/fsl-dpaa2/ethsw/Makefile | 2 +-
> > drivers/staging/fsl-dpaa2/ethsw/ethsw.c | 1523
> ++++++++++++++++++++++++++++++
> > drivers/staging/fsl-dpaa2/ethsw/ethsw.h | 88 ++
> > 3 files changed, 1612 insertions(+), 1 deletion(-)
> > create mode 100644 drivers/staging/fsl-dpaa2/ethsw/ethsw.c
> > create mode 100644 drivers/staging/fsl-dpaa2/ethsw/ethsw.h
> >
> > diff --git a/drivers/staging/fsl-dpaa2/ethsw/Makefile b/drivers/staging/fsl-
> > dpaa2/ethsw/Makefile
> > index db137f7..a6d72d1 100644
> > --- a/drivers/staging/fsl-dpaa2/ethsw/Makefile
> > +++ b/drivers/staging/fsl-dpaa2/ethsw/Makefile
> > @@ -4,4 +4,4 @@
> >
> > obj-$(CONFIG_FSL_DPAA2_ETHSW) += dpaa2-ethsw.o
> >
> > -dpaa2-ethsw-objs := dpsw.o
> > +dpaa2-ethsw-objs := ethsw.o dpsw.o
> > diff --git a/drivers/staging/fsl-dpaa2/ethsw/ethsw.c b/drivers/staging/fsl-
> > dpaa2/ethsw/ethsw.c
> > new file mode 100644
> > index 0000000..ae86078
> > --- /dev/null
> > +++ b/drivers/staging/fsl-dpaa2/ethsw/ethsw.c
> > @@ -0,0 +1,1523 @@
> > +/* Copyright 2014-2016 Freescale Semiconductor Inc.
> > + * Copyright 2017 NXP
> > + *
> > + * Redistribution and use in source and binary forms, with or without
> > + * modification, are permitted provided that the following conditions are met:
> > + * * Redistributions of source code must retain the above copyright
> > + * notice, this list of conditions and the following disclaimer.
> > + * * 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.
> > + * * Neither the name of the above-listed copyright holders nor the
> > + * names of any contributors may be used to endorse or promote products
> > + * derived from this software without specific prior written permission.
> > + *
> > + *
> > + * ALTERNATIVELY, this software may be distributed under the terms of the
> > + * GNU General Public License ("GPL") as published by the Free Software
> > + * Foundation, either version 2 of that License or (at your option) any
> > + * later version.
> > + *
> > + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND
> CONTRIBUTORS "AS IS"
> > + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
> LIMITED TO, THE
> > + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A
> PARTICULAR PURPOSE
> > + * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDERS OR
> CONTRIBUTORS BE
> > + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
> > + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
> PROCUREMENT OF
> > + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR
> BUSINESS
> > + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
> WHETHER IN
> > + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
> OTHERWISE)
> > + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
> ADVISED OF THE
> > + * POSSIBILITY OF SUCH DAMAGE.
> > + */
> > +
> > +#include <linux/module.h>
> > +
> > +#include <linux/interrupt.h>
> > +#include <linux/msi.h>
> > +#include <linux/kthread.h>
> > +#include <linux/workqueue.h>
> > +
> > +#include "../../fsl-mc/include/mc.h"
> > +
> > +#include "ethsw.h"
> > +
> > +static struct workqueue_struct *ethsw_owq;
> > +
> > +/* Minimal supported DPSW version */
> > +#define DPSW_MIN_VER_MAJOR 8
> > +#define DPSW_MIN_VER_MINOR 0
> > +
> > +#define DEFAULT_VLAN_ID 1
> > +
> > +static int ethsw_add_vlan(struct ethsw_core *ethsw, u16 vid)
> > +{
> > + int err;
> > +
> > + struct dpsw_vlan_cfg vcfg = {
> > + .fdb_id = 0,
> > + };
> > +
> > + if (ethsw->vlans[vid]) {
> > + dev_err(ethsw->dev, "VLAN already configured\n");
> > + return -EEXIST;
> > + }
> > +
> > + err = dpsw_vlan_add(ethsw->mc_io, 0,
> > + ethsw->dpsw_handle, vid, &vcfg);
> > + if (err) {
> > + dev_err(ethsw->dev, "dpsw_vlan_add err %d\n", err);
> > + return err;
> > + }
> > + ethsw->vlans[vid] = ETHSW_VLAN_MEMBER;/
> > +
> > + return 0;
> > +}
> > +
> > +static int ethsw_port_add_vlan(struct ethsw_port_priv *port_priv,
> > + u16 vid, u16 flags)
> > +{
> > + struct ethsw_core *ethsw = port_priv->ethsw_data;
> > + struct net_device *netdev = port_priv->netdev;
> > + struct dpsw_vlan_if_cfg vcfg;
> > + bool is_oper;
> > + int err, err2;
>
> Mild suggestion - s/err2/ret/, just because it sounds better, at least to me (same
> for similar situations in the rest of the file).
>
Thank you for your suggestion. I will make the change and send v2.
> > +
> > + if (port_priv->vlans[vid]) {
> > + netdev_warn(netdev, "VLAN %d already configured\n", vid);
> > + return -EEXIST;
> > + }
> > +
> > + vcfg.num_ifs = 1;
> > + vcfg.if_id[0] = port_priv->idx;
> > + err = dpsw_vlan_add_if(ethsw->mc_io, 0, ethsw->dpsw_handle, vid,
> &vcfg);
> > + if (err) {
> > + netdev_err(netdev, "dpsw_vlan_add_if err %d\n", err);
> > + return err;
> > + }
> > +
> > + port_priv->vlans[vid] = ETHSW_VLAN_MEMBER;
> > +
> > + if (flags & BRIDGE_VLAN_INFO_UNTAGGED) {
> > + err = dpsw_vlan_add_if_untagged(ethsw->mc_io, 0,
> > + ethsw->dpsw_handle,
> > + vid, &vcfg);
> > + if (err) {
> > + netdev_err(netdev,
> > + "dpsw_vlan_add_if_untagged err %d\n", err);
> > + return err;
> > + }
> > + port_priv->vlans[vid] |= ETHSW_VLAN_UNTAGGED;
> > + }
> > +
> > + if (flags & BRIDGE_VLAN_INFO_PVID) {
> > + struct dpsw_tci_cfg tci_cfg = {
> > + .pcp = 0,
> > + .dei = 0,
> > + .vlan_id = vid,
> > + };
> > +
> > + /* Interface needs to be down to change PVID */
> > + is_oper = netif_oper_up(netdev);
> > + if (is_oper) {
> > + err = dpsw_if_disable(ethsw->mc_io, 0,
> > + ethsw->dpsw_handle,
> > + port_priv->idx);
> > + if (err) {
> > + netdev_err(netdev,
> > + "dpsw_if_disable err %d\n", err);
> > + return err;
> > + }
> > + }
> > +
> > + err = dpsw_if_set_tci(ethsw->mc_io, 0, ethsw->dpsw_handle,
> > + port_priv->idx, &tci_cfg);
> > + if (!err) {
> > + /* Delete previous PVID info and mark the new one */
> > + if (port_priv->pvid)
> > + port_priv->vlans[port_priv->pvid]
> > + ^= ETHSW_VLAN_PVID;
>
> Can it be " &= ~ETHSW_VLAN_PVID" ? Are there other implications?
>
You are right. Flag must be un-set. Will update in v2, along with the other observations.
Thank you,
Razvan S.
> > +
> > + port_priv->vlans[vid] |= ETHSW_VLAN_PVID;
> > + port_priv->pvid = vid;
> > + } else {
> > + netdev_err(netdev, "dpsw_if_set_tci err %d\n", err);
> > + }
> > +
> > + if (is_oper) {
> > + err2 = dpsw_if_enable(ethsw->mc_io, 0,
> > + ethsw->dpsw_handle,
> > + port_priv->idx);
> > + if (err2) {
> > + netdev_err(netdev,
> > + "dpsw_if_enable err %d\n", err2);
> > + return err2;
> > + }
> > + }
> > + }
> > +
> > + return err;
> > +}
> > +
> > +static int ethsw_set_learning(struct ethsw_core *ethsw, u8 flag)
> > +{
> > + enum dpsw_fdb_learning_mode learn_mode;
> > + int err;
> > +
> > + if (flag)
> > + learn_mode = DPSW_FDB_LEARNING_MODE_HW;
> > + else
> > + learn_mode = DPSW_FDB_LEARNING_MODE_DIS;
> > +
> > + err = dpsw_fdb_set_learning_mode(ethsw->mc_io, 0, ethsw-
> >dpsw_handle, 0,
> > + learn_mode);
> > + if (err) {
> > + dev_err(ethsw->dev, "dpsw_fdb_set_learning_mode err %d\n",
> err);
> > + return err;
> > + }
> > + ethsw->learning = !!flag;
> > +
> > + return 0;
> > +}
> > +
> > +static int ethsw_port_set_flood(struct ethsw_port_priv *port_priv, u8 flag)
> > +{
> > + int err;
> > +
> > + err = dpsw_if_set_flooding(port_priv->ethsw_data->mc_io, 0,
> > + port_priv->ethsw_data->dpsw_handle,
> > + port_priv->idx, (int)flag);
>
> Why is this cast necessary? Can't the API be reworked to use u8 (or, better yet,
> bool) instead of int?
>
> > + if (err) {
> > + netdev_err(port_priv->netdev,
> > + "dpsw_fdb_set_learning_mode err %d\n", err);
> > + return err;
> > + }
> > + port_priv->flood = !!flag;
> > +
> > + return 0;
> > +}
> > +
> > +static int ethsw_port_set_stp_state(struct ethsw_port_priv *port_priv, u8
> > state)
> > +{
> > + struct dpsw_stp_cfg stp_cfg = {
> > + .vlan_id = DEFAULT_VLAN_ID,
> > + .state = state,
> > + };
> > + int err;
> > +
> > + if (!netif_oper_up(port_priv->netdev) || state == port_priv->stp_state)
> > + return 0; /* Nothing to do */
> > +
> > + err = dpsw_if_set_stp(port_priv->ethsw_data->mc_io, 0,
> > + port_priv->ethsw_data->dpsw_handle,
> > + port_priv->idx, &stp_cfg);
> > + if (err) {
> > + netdev_err(port_priv->netdev,
> > + "dpsw_if_set_stp err %d\n", err);
> > + return err;
> > + }
> > +
> > + port_priv->stp_state = state;
> > +
> > + return 0;
> > +}
> > +
> > +static int ethsw_dellink_switch(struct ethsw_core *ethsw, u16 vid)
> > +{
> > + struct ethsw_port_priv *ppriv_local = NULL;
> > + int i, err;
> > +
> > + if (!ethsw->vlans[vid])
> > + return -ENOENT;
> > +
> > + err = dpsw_vlan_remove(ethsw->mc_io, 0, ethsw->dpsw_handle, vid);
> > + if (err) {
> > + dev_err(ethsw->dev, "dpsw_vlan_remove err %d\n", err);
> > + return err;
> > + }
> > + ethsw->vlans[vid] = 0;
> > +
> > + for (i = 0; i < ethsw->sw_attr.num_ifs; i++) {
> > + ppriv_local = ethsw->ports[i];
> > + ppriv_local->vlans[vid] = 0;
> > + }
> > +
> > + return 0;
> > +}
> > +
> > +static int ethsw_port_fdb_add_uc(struct ethsw_port_priv *port_priv,
> > + const unsigned char *addr)
> > +{
> > + struct dpsw_fdb_unicast_cfg entry = {0};
> > + int err;
> > +
> > + entry.if_egress = port_priv->idx;
> > + entry.type = DPSW_FDB_ENTRY_STATIC;
> > + ether_addr_copy(entry.mac_addr, addr);
> > +
> > + err = dpsw_fdb_add_unicast(port_priv->ethsw_data->mc_io, 0,
> > + port_priv->ethsw_data->dpsw_handle,
> > + 0, &entry);
> > + if (err)
> > + netdev_err(port_priv->netdev,
> > + "dpsw_fdb_add_unicast err %d\n", err);
> > + return err;
> > +}
> > +
> > +static int ethsw_port_fdb_del_uc(struct ethsw_port_priv *port_priv,
> > + const unsigned char *addr)
> > +{
> > + struct dpsw_fdb_unicast_cfg entry = {0};
> > + int err;
> > +
> > + entry.if_egress = port_priv->idx;
> > + entry.type = DPSW_FDB_ENTRY_STATIC;
> > + ether_addr_copy(entry.mac_addr, addr);
> > +
> > + err = dpsw_fdb_remove_unicast(port_priv->ethsw_data->mc_io, 0,
> > + port_priv->ethsw_data->dpsw_handle,
> > + 0, &entry);
> > + if (err)
> > + netdev_err(port_priv->netdev,
> > + "dpsw_fdb_remove_unicast err %d\n", err);
> > + return err;
> > +}
> > +
> > +static int ethsw_port_fdb_add_mc(struct ethsw_port_priv *port_priv,
> > + const unsigned char *addr)
> > +{
> > + struct dpsw_fdb_multicast_cfg entry = {0};
> > + int err;
> > +
> > + ether_addr_copy(entry.mac_addr, addr);
> > + entry.type = DPSW_FDB_ENTRY_STATIC;
> > + entry.num_ifs = 1;
> > + entry.if_id[0] = port_priv->idx;
> > +
> > + err = dpsw_fdb_add_multicast(port_priv->ethsw_data->mc_io, 0,
> > + port_priv->ethsw_data->dpsw_handle,
> > + 0, &entry);
> > + if (err)
> > + netdev_err(port_priv->netdev, "dpsw_fdb_add_multicast err
> %d\n",
> > + err);
> > + return err;
> > +}
> > +
> > +static int ethsw_port_fdb_del_mc(struct ethsw_port_priv *port_priv,
> > + const unsigned char *addr)
> > +{
> > + struct dpsw_fdb_multicast_cfg entry = {0};
> > + int err;
> > +
> > + ether_addr_copy(entry.mac_addr, addr);
> > + entry.type = DPSW_FDB_ENTRY_STATIC;
> > + entry.num_ifs = 1;
> > + entry.if_id[0] = port_priv->idx;
> > +
> > + err = dpsw_fdb_remove_multicast(port_priv->ethsw_data->mc_io, 0,
> > + port_priv->ethsw_data->dpsw_handle,
> > + 0, &entry);
> > + if (err)
> > + netdev_err(port_priv->netdev,
> > + "dpsw_fdb_remove_multicast err %d\n", err);
> > + return err;
> > +}
> > +
> > +static void port_get_stats(struct net_device *netdev,
> > + struct rtnl_link_stats64 *stats)
> > +{
> > + struct ethsw_port_priv *port_priv = netdev_priv(netdev);
> > + u64 tmp;
> > + int err;
> > +
> > + err = dpsw_if_get_counter(port_priv->ethsw_data->mc_io, 0,
> > + port_priv->ethsw_data->dpsw_handle,
> > + port_priv->idx,
> > + DPSW_CNT_ING_FRAME, &stats->rx_packets);
> > + if (err)
> > + goto error;
> > +
> > + err = dpsw_if_get_counter(port_priv->ethsw_data->mc_io, 0,
> > + port_priv->ethsw_data->dpsw_handle,
> > + port_priv->idx,
> > + DPSW_CNT_EGR_FRAME, &stats->tx_packets);
> > + if (err)
> > + goto error;
> > +
> > + err = dpsw_if_get_counter(port_priv->ethsw_data->mc_io, 0,
> > + port_priv->ethsw_data->dpsw_handle,
> > + port_priv->idx,
> > + DPSW_CNT_ING_BYTE, &stats->rx_bytes);
> > + if (err)
> > + goto error;
> > +
> > + err = dpsw_if_get_counter(port_priv->ethsw_data->mc_io, 0,
> > + port_priv->ethsw_data->dpsw_handle,
> > + port_priv->idx,
> > + DPSW_CNT_EGR_BYTE, &stats->tx_bytes);
> > + if (err)
> > + goto error;
> > +
> > + err = dpsw_if_get_counter(port_priv->ethsw_data->mc_io, 0,
> > + port_priv->ethsw_data->dpsw_handle,
> > + port_priv->idx,
> > + DPSW_CNT_ING_FRAME_DISCARD,
> > + &stats->rx_dropped);
> > + if (err)
> > + goto error;
> > +
> > + err = dpsw_if_get_counter(port_priv->ethsw_data->mc_io, 0,
> > + port_priv->ethsw_data->dpsw_handle,
> > + port_priv->idx,
> > + DPSW_CNT_ING_FLTR_FRAME,
> > + &tmp);
> > + if (err)
> > + goto error;
> > + stats->rx_dropped += tmp;
> > +
> > + err = dpsw_if_get_counter(port_priv->ethsw_data->mc_io, 0,
> > + port_priv->ethsw_data->dpsw_handle,
> > + port_priv->idx,
> > + DPSW_CNT_EGR_FRAME_DISCARD,
> > + &stats->tx_dropped);
> > + if (err)
> > + goto error;
> > +
> > + return;
> > +
> > +error:
> > + netdev_err(netdev, "dpsw_if_get_counter err %d\n", err);
> > +}
> > +
> > +static bool port_has_offload_stats(const struct net_device *netdev,
> > + int attr_id)
> > +{
> > + return (attr_id == IFLA_OFFLOAD_XSTATS_CPU_HIT);
> > +}
> > +
> > +static int port_get_offload_stats(int attr_id,
> > + const struct net_device *netdev,
> > + void *sp)
> > +{
> > + switch (attr_id) {
> > + case IFLA_OFFLOAD_XSTATS_CPU_HIT:
> > + port_get_stats((struct net_device *)netdev, sp);
> > + return 0;
> > + }
> > +
> > + return -EINVAL;
> > +}
> > +
> > +static int port_change_mtu(struct net_device *netdev, int mtu)
> > +{
> > + struct ethsw_port_priv *port_priv = netdev_priv(netdev);
> > + int err;
> > +
> > + err = dpsw_if_set_max_frame_length(port_priv->ethsw_data->mc_io,
> > + 0,
> > + port_priv->ethsw_data->dpsw_handle,
> > + port_priv->idx,
> > + (u16)ETHSW_L2_MAX_FRM(mtu));
> > + if (err) {
> > + netdev_err(netdev,
> > + "dpsw_if_set_max_frame_length() err %d\n", err);
> > + return err;
> > + }
> > +
> > + netdev->mtu = mtu;
> > + return 0;
> > +}
> > +
> > +static int port_carrier_state_sync(struct net_device *netdev)
> > +{
> > + struct ethsw_port_priv *port_priv = netdev_priv(netdev);
> > + struct dpsw_link_state state;
> > + int err;
> > +
> > + err = dpsw_if_get_link_state(port_priv->ethsw_data->mc_io, 0,
> > + port_priv->ethsw_data->dpsw_handle,
> > + port_priv->idx, &state);
> > + if (err) {
> > + netdev_err(netdev, "dpsw_if_get_link_state() err %d\n", err);
> > + return err;
> > + }
> > +
> > + WARN_ONCE(state.up > 1, "Garbage read into link_state");
> > +
> > + if (state.up != port_priv->link_state) {
> > + if (state.up)
> > + netif_carrier_on(netdev);
> > + else
> > + netif_carrier_off(netdev);
> > + port_priv->link_state = state.up;
> > + }
> > + return 0;
> > +}
> > +
> > +static int port_open(struct net_device *netdev)
> > +{
> > + struct ethsw_port_priv *port_priv = netdev_priv(netdev);
> > + int err;
> > +
> > + /* No need to allow Tx as control interface is disabled */
> > + netif_tx_stop_all_queues(netdev);
> > +
> > + err = dpsw_if_enable(port_priv->ethsw_data->mc_io, 0,
> > + port_priv->ethsw_data->dpsw_handle,
> > + port_priv->idx);
> > + if (err) {
> > + netdev_err(netdev, "dpsw_if_enable err %d\n", err);
> > + return err;
> > + }
> > +
> > + /* sync carrier state */
> > + err = port_carrier_state_sync(netdev);
> > + if (err) {
> > + netdev_err(netdev,
> > + "port_carrier_state_sync err %d\n", err);
> > + goto err_carrier_sync;
> > + }
> > +
> > + return 0;
> > +
> > +err_carrier_sync:
> > + dpsw_if_disable(port_priv->ethsw_data->mc_io, 0,
> > + port_priv->ethsw_data->dpsw_handle,
> > + port_priv->idx);
> > + return err;
> > +}
> > +
> > +static int port_stop(struct net_device *netdev)
> > +{
> > + struct ethsw_port_priv *port_priv = netdev_priv(netdev);
> > + int err;
> > +
> > + err = dpsw_if_disable(port_priv->ethsw_data->mc_io, 0,
> > + port_priv->ethsw_data->dpsw_handle,
> > + port_priv->idx);
> > + if (err) {
> > + netdev_err(netdev, "dpsw_if_disable err %d\n", err);
> > + return err;
> > + }
> > +
> > + return 0;
> > +}
> > +
> > +static netdev_tx_t port_dropframe(struct sk_buff *skb,
> > + struct net_device *netdev)
> > +{
> > + /* we don't support I/O for now, drop the frame */
> > + dev_kfree_skb_any(skb);
> > +
> > + return NETDEV_TX_OK;
> > +}
> > +
> > +static const struct net_device_ops ethsw_port_ops = {
> > + .ndo_open = port_open,
> > + .ndo_stop = port_stop,
> > +
> > + .ndo_set_mac_address = eth_mac_addr,
> > + .ndo_change_mtu = port_change_mtu,
> > + .ndo_has_offload_stats = port_has_offload_stats,
> > + .ndo_get_offload_stats = port_get_offload_stats,
> > +
> > + .ndo_start_xmit = port_dropframe,
> > +};
> > +
> > +static void ethsw_links_state_update(struct ethsw_core *ethsw)
> > +{
> > + int i;
> > +
> > + for (i = 0; i < ethsw->sw_attr.num_ifs; i++)
> > + port_carrier_state_sync(ethsw->ports[i]->netdev);
> > +}
> > +
> > +static irqreturn_t ethsw_irq0_handler(int irq_num, void *arg)
> > +{
> > + return IRQ_WAKE_THREAD;
> > +}
> > +
> > +static irqreturn_t ethsw_irq0_handler_thread(int irq_num, void *arg)
> > +{
> > + struct device *dev = (struct device *)arg;
> > + struct ethsw_core *ethsw = dev_get_drvdata(dev);
> > +
> > + /* Mask the events and the if_id reserved bits to be cleared on read */
> > + u32 status = DPSW_IRQ_EVENT_LINK_CHANGED | 0xFFFF0000;
> > + int err;
> > +
> > + err = dpsw_get_irq_status(ethsw->mc_io, 0, ethsw->dpsw_handle,
> > + DPSW_IRQ_INDEX_IF, &status);
> > + if (err) {
> > + dev_err(dev, "Can't get irq status (err %d)", err);
> > +
> > + err = dpsw_clear_irq_status(ethsw->mc_io, 0, ethsw-
> >dpsw_handle,
> > + DPSW_IRQ_INDEX_IF, 0xFFFFFFFF);
> > + if (err)
> > + dev_err(dev, "Can't clear irq status (err %d)", err);
> > + goto out;
> > + }
> > +
> > + if (status & DPSW_IRQ_EVENT_LINK_CHANGED)
> > + ethsw_links_state_update(ethsw);
> > +
> > +out:
> > + return IRQ_HANDLED;
> > +}
> > +
> > +static int ethsw_setup_irqs(struct fsl_mc_device *sw_dev)
> > +{
> > + struct device *dev = &sw_dev->dev;
> > + struct ethsw_core *ethsw = dev_get_drvdata(dev);
> > + u32 mask = DPSW_IRQ_EVENT_LINK_CHANGED;
> > + struct fsl_mc_device_irq *irq;
> > + int err;
> > +
> > + err = fsl_mc_allocate_irqs(sw_dev);
> > + if (err) {
> > + dev_err(dev, "MC irqs allocation failed\n");
> > + return err;
> > + }
> > +
> > + if (WARN_ON(sw_dev->obj_desc.irq_count != DPSW_IRQ_NUM)) {
> > + err = -EINVAL;
> > + goto free_irq;
> > + }
> > +
> > + err = dpsw_set_irq_enable(ethsw->mc_io, 0, ethsw->dpsw_handle,
> > + DPSW_IRQ_INDEX_IF, 0);
> > + if (err) {
> > + dev_err(dev, "dpsw_set_irq_enable err %d\n", err);
> > + goto free_irq;
> > + }
> > +
> > + irq = sw_dev->irqs[DPSW_IRQ_INDEX_IF];
> > +
> > + err = devm_request_threaded_irq(dev, irq->msi_desc->irq,
> > + ethsw_irq0_handler,
> > + ethsw_irq0_handler_thread,
> > + IRQF_NO_SUSPEND | IRQF_ONESHOT,
> > + dev_name(dev), dev);
> > + if (err) {
> > + dev_err(dev, "devm_request_threaded_irq(): %d", err);
> > + goto free_irq;
> > + }
> > +
> > + err = dpsw_set_irq_mask(ethsw->mc_io, 0, ethsw->dpsw_handle,
> > + DPSW_IRQ_INDEX_IF, mask);
> > + if (err) {
> > + dev_err(dev, "dpsw_set_irq_mask(): %d", err);
> > + goto free_devm_irq;
> > + }
> > +
> > + err = dpsw_set_irq_enable(ethsw->mc_io, 0, ethsw->dpsw_handle,
> > + DPSW_IRQ_INDEX_IF, 1);
> > + if (err) {
> > + dev_err(dev, "dpsw_set_irq_enable(): %d", err);
> > + goto free_devm_irq;
> > + }
> > +
> > + return 0;
> > +
> > +free_devm_irq:
> > + devm_free_irq(dev, irq->msi_desc->irq, dev);
> > +free_irq:
> > + fsl_mc_free_irqs(sw_dev);
> > + return err;
> > +}
> > +
> > +static void ethsw_teardown_irqs(struct fsl_mc_device *sw_dev)
> > +{
> > + struct device *dev = &sw_dev->dev;
> > + struct ethsw_core *ethsw = dev_get_drvdata(dev);
> > + struct fsl_mc_device_irq *irq;
> > +
> > + irq = sw_dev->irqs[DPSW_IRQ_INDEX_IF];
> > + dpsw_set_irq_enable(ethsw->mc_io, 0, ethsw->dpsw_handle,
> > + DPSW_IRQ_INDEX_IF, 0);
>
> You can still print an error message here, in case something goes wrong.
>
> > + fsl_mc_free_irqs(sw_dev);
> > +}
> > +
> > +static int swdev_port_attr_get(struct net_device *netdev,
> > + struct switchdev_attr *attr)
> > +{
> > + struct ethsw_port_priv *port_priv = netdev_priv(netdev);
> > +
> > + switch (attr->id) {
> > + case SWITCHDEV_ATTR_ID_PORT_PARENT_ID:
> > + attr->u.ppid.id_len = 1;
> > + attr->u.ppid.id[0] = port_priv->ethsw_data->dev_id;
> > + break;
> > + case SWITCHDEV_ATTR_ID_PORT_BRIDGE_FLAGS:
> > + attr->u.brport_flags =
> > + (port_priv->ethsw_data->learning ? BR_LEARNING : 0) |
> > + (port_priv->flood ? BR_FLOOD : 0);
> > + break;
> > + case SWITCHDEV_ATTR_ID_PORT_BRIDGE_FLAGS_SUPPORT:
> > + attr->u.brport_flags_support = BR_LEARNING | BR_FLOOD;
> > + break;
> > + default:
> > + return -EOPNOTSUPP;
> > + }
> > +
> > + return 0;
> > +}
> > +
> > +static int port_attr_stp_state_set(struct net_device *netdev,
> > + struct switchdev_trans *trans,
> > + u8 state)
> > +{
> > + struct ethsw_port_priv *port_priv = netdev_priv(netdev);
> > +
> > + if (switchdev_trans_ph_prepare(trans))
> > + return 0;
> > +
> > + return ethsw_port_set_stp_state(port_priv, state);
> > +}
> > +
> > +static int port_attr_br_flags_set(struct net_device *netdev,
> > + struct switchdev_trans *trans,
> > + unsigned long flags)
> > +{
> > + struct ethsw_port_priv *port_priv = netdev_priv(netdev);
> > + int err = 0;
> > +
> > + if (switchdev_trans_ph_prepare(trans))
> > + return 0;
> > +
> > + /* Learning is enabled per switch */
> > + err = ethsw_set_learning(port_priv->ethsw_data, flags &
> BR_LEARNING);
> > + if (err)
> > + goto exit;
> > +
> > + err = ethsw_port_set_flood(port_priv, flags & BR_FLOOD);
> > +
> > +exit:
> > + return err;
> > +}
> > +
> > +static int swdev_port_attr_set(struct net_device *netdev,
> > + const struct switchdev_attr *attr,
> > + struct switchdev_trans *trans)
> > +{
> > + int err = 0;
> > +
> > + switch (attr->id) {
> > + case SWITCHDEV_ATTR_ID_PORT_STP_STATE:
> > + err = port_attr_stp_state_set(netdev, trans,
> > + attr->u.stp_state);
> > + break;
> > + case SWITCHDEV_ATTR_ID_PORT_BRIDGE_FLAGS:
> > + err = port_attr_br_flags_set(netdev, trans,
> > + attr->u.brport_flags);
> > + break;
> > + case SWITCHDEV_ATTR_ID_BRIDGE_VLAN_FILTERING:
> > + /* VLANs are supported by default */
> > + break;
> > + default:
> > + err = -EOPNOTSUPP;
> > + break;
> > + }
> > +
> > + return err;
> > +}
> > +
> > +static int port_vlans_add(struct net_device *netdev,
> > + const struct switchdev_obj_port_vlan *vlan,
> > + struct switchdev_trans *trans)
> > +{
> > + struct ethsw_port_priv *port_priv = netdev_priv(netdev);
> > + int vid, err;
> > +
> > + if (switchdev_trans_ph_prepare(trans))
> > + return 0;
> > +
> > + for (vid = vlan->vid_begin; vid <= vlan->vid_end; vid++) {
> > + if (!port_priv->ethsw_data->vlans[vid]) {
> > + /* this is a new VLAN */
> > + err = ethsw_add_vlan(port_priv->ethsw_data, vid);
> > + if (err)
> > + return err;
> > +
> > + port_priv->ethsw_data->vlans[vid] |=
> ETHSW_VLAN_GLOBAL;
> > + }
> > + err = ethsw_port_add_vlan(port_priv, vid, vlan->flags);
> > + if (err)
> > + break;
> > + }
> > +
> > + return err;
> > +}
> > +
> > +static int port_lookup_address(struct net_device *netdev, int is_uc,
> > + const unsigned char *addr)
> > +{
> > + struct netdev_hw_addr_list *list = (is_uc) ? &netdev->uc : &netdev->mc;
> > + struct netdev_hw_addr *ha;
> > +
> > + netif_addr_lock_bh(netdev);
> > + list_for_each_entry(ha, &list->list, list) {
> > + if (ether_addr_equal(ha->addr, addr)) {
> > + netif_addr_unlock_bh(netdev);
> > + return 1;
> > + }
> > + }
> > + netif_addr_unlock_bh(netdev);
> > + return 0;
> > +}
> > +
> > +static int port_mdb_add(struct net_device *netdev,
> > + const struct switchdev_obj_port_mdb *mdb,
> > + struct switchdev_trans *trans)
> > +{
> > + struct ethsw_port_priv *port_priv = netdev_priv(netdev);
> > + int err;
> > +
> > + if (switchdev_trans_ph_prepare(trans))
> > + return 0;
> > +
> > + /* Check if address is already set on this port */
> > + if (port_lookup_address(netdev, 0, mdb->addr))
> > + return -EEXIST;
> > +
> > + err = ethsw_port_fdb_add_mc(port_priv, mdb->addr);
> > + if (err)
> > + return err;
> > +
> > + err = dev_mc_add(netdev, mdb->addr);
> > + if (err)
> > + netdev_err(netdev, "dev_mc_add err %d\n", err);
>
> In the error case, shouldn't there be a "ethsw_port_fdb_del_mc" ?
>
> > +
> > + return err;
> > +}
> > +
> > +static int swdev_port_obj_add(struct net_device *netdev,
> > + const struct switchdev_obj *obj,
> > + struct switchdev_trans *trans)
> > +{
> > + int err;
> > +
> > + switch (obj->id) {
> > + case SWITCHDEV_OBJ_ID_PORT_VLAN:
> > + err = port_vlans_add(netdev,
> > + SWITCHDEV_OBJ_PORT_VLAN(obj),
> > + trans);
> > + break;
> > + case SWITCHDEV_OBJ_ID_PORT_MDB:
> > + err = port_mdb_add(netdev,
> > + SWITCHDEV_OBJ_PORT_MDB(obj),
> > + trans);
> > + break;
> > + default:
> > + err = -EOPNOTSUPP;
> > + break;
> > + }
> > +
> > + return err;
> > +}
> > +
> > +static int ethsw_port_del_vlan(struct ethsw_port_priv *port_priv, u16 vid)
> > +{
> > + struct ethsw_core *ethsw = port_priv->ethsw_data;
> > + struct net_device *netdev = port_priv->netdev;
> > + struct dpsw_vlan_if_cfg vcfg;
> > + int i, err, err2;
> > + bool is_oper;
> > +
> > + if (!port_priv->vlans[vid])
> > + return -ENOENT;
> > +
> > + if (port_priv->vlans[vid] & ETHSW_VLAN_PVID) {
> > + struct dpsw_tci_cfg tci_cfg = { 0 };
> > + /* Interface needs to be down to change PVID */
> > + is_oper = netif_oper_up(netdev);
> > +
> > + if (is_oper) {
> > + err = dpsw_if_disable(ethsw->mc_io, 0,
> > + ethsw->dpsw_handle,
> > + port_priv->idx);
> > + if (err) {
> > + netdev_err(netdev, "dpsw_if_disable err %d\n",
> > + err);
> > + goto exit_err;
> > + }
> > + }
> > +
> > + err = dpsw_if_set_tci(ethsw->mc_io, 0,
> > + ethsw->dpsw_handle,
> > + port_priv->idx, &tci_cfg);
> > + if (!err) {
> > + port_priv->vlans[vid] &= ~ETHSW_VLAN_PVID;
> > + port_priv->pvid = 0;
> > + } else {
> > + netdev_err(netdev, "dpsw_if_set_tci err %d\n", err);
> > + }
> > +
> > + if (is_oper) {
> > + err2 = dpsw_if_enable(ethsw->mc_io, 0,
> > + ethsw->dpsw_handle,
> > + port_priv->idx);
> > + if (err2) {
> > + netdev_err(netdev, "dpsw_if_enable err %d\n",
> > + err2);
> > + return err2;
> > + }
> > + }
> > +
> > + if (err)
> > + goto exit_err;
> > + }
> > +
> > + vcfg.num_ifs = 1;
> > + vcfg.if_id[0] = port_priv->idx;
> > + if (port_priv->vlans[vid] & ETHSW_VLAN_UNTAGGED) {
> > + err = dpsw_vlan_remove_if_untagged(ethsw->mc_io, 0,
> > + ethsw->dpsw_handle,
> > + vid, &vcfg);
> > + if (err) {
> > + netdev_err(netdev,
> > + "dpsw_vlan_remove_if_untagged err %d\n",
> > + err);
> > + }
> > + port_priv->vlans[vid] &= ~ETHSW_VLAN_UNTAGGED;
> > + }
> > +
> > + if (port_priv->vlans[vid] & ETHSW_VLAN_MEMBER) {
> > + err = dpsw_vlan_remove_if(ethsw->mc_io, 0, ethsw-
> >dpsw_handle,
> > + vid, &vcfg);
> > + if (err) {
> > + netdev_err(netdev,
> > + "dpsw_vlan_remove_if err %d\n", err);
> > + return err;
> > + }
> > + port_priv->vlans[vid] &= ~ETHSW_VLAN_MEMBER;
> > +
> > + /* Delete VLAN from switch if it is no longer configured on
> > + * any port
> > + */
> > + for (i = 0; i < ethsw->sw_attr.num_ifs; i++)
> > + if (ethsw->ports[i]->vlans[vid] &
> ETHSW_VLAN_MEMBER)
> > + return 0; /* Found a port member in VID */
> > +
> > + ethsw->vlans[vid] &= ~ETHSW_VLAN_GLOBAL;
> > +
> > + err = ethsw_dellink_switch(ethsw, vid);
> > + if (err)
> > + goto exit_err;
> > + }
> > +
> > + return 0;
> > +exit_err:
> > + return err;
> > +}
> > +
> > +static int port_vlans_del(struct net_device *netdev,
> > + const struct switchdev_obj_port_vlan *vlan)
> > +{
> > + struct ethsw_port_priv *port_priv = netdev_priv(netdev);
> > + int vid, err;
> > +
> > + for (vid = vlan->vid_begin; vid <= vlan->vid_end; vid++) {
> > + err = ethsw_port_del_vlan(port_priv, vid);
> > + if (err)
> > + break;
> > + }
> > +
> > + return err;
> > +}
> > +
> > +static int port_mdb_del(struct net_device *netdev,
> > + const struct switchdev_obj_port_mdb *mdb)
> > +{
> > + struct ethsw_port_priv *port_priv = netdev_priv(netdev);
> > + int err;
> > +
> > + if (!port_lookup_address(netdev, 0, mdb->addr))
> > + return -ENOENT;
> > +
> > + err = ethsw_port_fdb_del_mc(port_priv, mdb->addr);
> > + if (err)
> > + return err;
> > +
> > + err = dev_mc_del(netdev, mdb->addr);
> > + if (err) {
> > + netdev_err(netdev, "dev_mc_del err %d\n", err);
> > + return err;
> > + }
> > +
> > + return err;
> > +}
> > +
> > +static int swdev_port_obj_del(struct net_device *netdev,
> > + const struct switchdev_obj *obj)
> > +{
> > + int err;
> > +
> > + switch (obj->id) {
> > + case SWITCHDEV_OBJ_ID_PORT_VLAN:
> > + err = port_vlans_del(netdev,
> SWITCHDEV_OBJ_PORT_VLAN(obj));
> > + break;
> > + case SWITCHDEV_OBJ_ID_PORT_MDB:
> > + err = port_mdb_del(netdev, SWITCHDEV_OBJ_PORT_MDB(obj));
> > + break;
> > + default:
> > + err = -EOPNOTSUPP;
> > + break;
> > + }
> > + return err;
> > +}
> > +
> > +static const struct switchdev_ops ethsw_port_switchdev_ops = {
> > + .switchdev_port_attr_get = swdev_port_attr_get,
> > + .switchdev_port_attr_set = swdev_port_attr_set,
> > + .switchdev_port_obj_add = swdev_port_obj_add,
> > + .switchdev_port_obj_del = swdev_port_obj_del,
> > +};
> > +
> > +/* For the moment, only flood setting needs to be updated */
> > +static int port_bridge_join(struct net_device *netdev)
> > +{
> > + struct ethsw_port_priv *port_priv = netdev_priv(netdev);
> > +
> > + /* Enable flooding */
> > + return ethsw_port_set_flood(port_priv, 1);
> > +}
> > +
> > +static int port_bridge_leave(struct net_device *netdev)
> > +{
> > + struct ethsw_port_priv *port_priv = netdev_priv(netdev);
> > +
> > + /* Disable flooding */
> > + return ethsw_port_set_flood(port_priv, 0);
> > +}
> > +
> > +static int port_netdevice_event(struct notifier_block *unused,
> > + unsigned long event, void *ptr)
> > +{
> > + struct net_device *netdev = netdev_notifier_info_to_dev(ptr);
> > + struct netdev_notifier_changeupper_info *info = ptr;
> > + struct net_device *upper_dev;
> > + int err = 0;
> > +
> > + if (netdev->netdev_ops != ðsw_port_ops)
> > + return NOTIFY_DONE;
> > +
> > + /* Handle just upper dev link/unlink for the moment */
> > + if (event == NETDEV_CHANGEUPPER) {
> > + upper_dev = info->upper_dev;
> > + if (netif_is_bridge_master(upper_dev)) {
> > + if (info->linking)
> > + err = port_bridge_join(netdev);
> > + else
> > + err = port_bridge_leave(netdev);
> > + }
> > + }
> > +
> > + return notifier_from_errno(err);
> > +}
> > +
> > +static struct notifier_block port_nb __read_mostly = {
> > + .notifier_call = port_netdevice_event,
> > +};
> > +
> > +struct ethsw_switchdev_event_work {
> > + struct work_struct work;
> > + struct switchdev_notifier_fdb_info fdb_info;
> > + struct net_device *dev;
> > + unsigned long event;
> > +};
> > +
> > +static void ethsw_switchdev_event_work(struct work_struct *work)
> > +{
> > + struct ethsw_switchdev_event_work *switchdev_work =
> > + container_of(work, struct ethsw_switchdev_event_work, work);
> > + struct net_device *dev = switchdev_work->dev;
> > + struct switchdev_notifier_fdb_info *fdb_info;
> > + struct ethsw_port_priv *port_priv;
> > +
> > + rtnl_lock();
> > + port_priv = netdev_priv(dev);
> > + fdb_info = &switchdev_work->fdb_info;
> > +
> > + switch (switchdev_work->event) {
> > + case SWITCHDEV_FDB_ADD_TO_DEVICE:
> > + ethsw_port_fdb_add_uc(netdev_priv(dev), fdb_info->addr);
> > + break;
> > + case SWITCHDEV_FDB_DEL_TO_DEVICE:
> > + ethsw_port_fdb_del_uc(netdev_priv(dev), fdb_info->addr);
> > + break;
> > + }
> > +
> > + rtnl_unlock();
> > + kfree(switchdev_work->fdb_info.addr);
> > + kfree(switchdev_work);
> > + dev_put(dev);
> > +}
> > +
> > +/* Called under rcu_read_lock() */
> > +static int port_switchdev_event(struct notifier_block *unused,
> > + unsigned long event, void *ptr)
> > +{
> > + struct net_device *dev = switchdev_notifier_info_to_dev(ptr);
> > + struct ethsw_switchdev_event_work *switchdev_work;
> > + struct switchdev_notifier_fdb_info *fdb_info = ptr;
> > +
> > + switchdev_work = kzalloc(sizeof(*switchdev_work), GFP_ATOMIC);
> > + if (!switchdev_work)
> > + return NOTIFY_BAD;
> > +
> > + INIT_WORK(&switchdev_work->work, ethsw_switchdev_event_work);
> > + switchdev_work->dev = dev;
> > + switchdev_work->event = event;
> > +
> > + switch (event) {
> > + case SWITCHDEV_FDB_ADD_TO_DEVICE:
> > + case SWITCHDEV_FDB_DEL_TO_DEVICE:
> > + memcpy(&switchdev_work->fdb_info, ptr,
> > + sizeof(switchdev_work->fdb_info));
> > + switchdev_work->fdb_info.addr = kzalloc(ETH_ALEN,
> GFP_ATOMIC);
> > + if (!switchdev_work->fdb_info.addr)
> > + goto err_addr_alloc;
> > +
> > + ether_addr_copy((u8 *)switchdev_work->fdb_info.addr,
> > + fdb_info->addr);
> > +
> > + /* Take a reference on the device to avoid being freed. */
> > + dev_hold(dev);
> > + break;
> > + default:
> > + return NOTIFY_DONE;
> > + }
> > +
> > + queue_work(ethsw_owq, &switchdev_work->work);
> > +
> > + return NOTIFY_DONE;
> > +
> > +err_addr_alloc:
> > + kfree(switchdev_work);
> > + return NOTIFY_BAD;
> > +}
> > +
> > +static struct notifier_block port_switchdev_nb = {
> > + .notifier_call = port_switchdev_event,
> > +};
> > +
> > +static int ethsw_register_notifier(struct device *dev)
> > +{
> > + int err;
> > +
> > + err = register_netdevice_notifier(&port_nb);
> > + if (err) {
> > + dev_err(dev, "Failed to register netdev notifier\n");
> > + return err;
> > + }
> > +
> > + err = register_switchdev_notifier(&port_switchdev_nb);
> > + if (err) {
> > + dev_err(dev, "Failed to register switchdev notifier\n");
> > + goto err_switchdev_nb;
> > + }
> > +
> > + return 0;
> > +
> > +err_switchdev_nb:
> > + unregister_netdevice_notifier(&port_nb);
> > + return err;
> > +}
> > +
> > +static int ethsw_open(struct ethsw_core *ethsw)
>
> Minor formatting error, tab in function signature - see following function as well.
>
> > +{
> > + struct ethsw_port_priv *port_priv = NULL;
> > + int i, err;
> > +
> > + err = dpsw_enable(ethsw->mc_io, 0, ethsw->dpsw_handle);
> > + if (err) {
> > + dev_err(ethsw->dev, "dpsw_enable err %d\n", err);
> > + return err;
> > + }
> > +
> > + for (i = 0; i < ethsw->sw_attr.num_ifs; i++) {
> > + port_priv = ethsw->ports[i];
> > + err = dev_open(port_priv->netdev);
> > + if (err) {
> > + netdev_err(port_priv->netdev, "dev_open err %d\n",
> err);
> > + return err;
> > + }
> > + }
> > +
> > + return 0;
> > +}
> > +
> > +static int ethsw_stop(struct ethsw_core *ethsw)
> > +{
> > + struct ethsw_port_priv *port_priv = NULL;
> > + int i, err;
> > +
> > + destroy_workqueue(ethsw_owq);
>
> If workqueue is destroyed here, shouldn't it be alloc'd in ethsw_open?
>
> > +
> > + for (i = 0; i < ethsw->sw_attr.num_ifs; i++) {
> > + port_priv = ethsw->ports[i];
> > + dev_close(port_priv->netdev);
> > + }
> > +
> > + err = dpsw_disable(ethsw->mc_io, 0, ethsw->dpsw_handle);
> > + if (err) {
> > + dev_err(ethsw->dev, "dpsw_disable err %d\n", err);
> > + return err;
> > + }
> > +
> > + return 0;
> > +}
> > +
> > +static int ethsw_init(struct fsl_mc_device *sw_dev)
> > +{
> > + struct device *dev = &sw_dev->dev;
> > + struct ethsw_core *ethsw = dev_get_drvdata(dev);
> > + u16 version_major, version_minor, i;
> > + struct dpsw_stp_cfg stp_cfg;
> > + int err;
> > +
> > + ethsw->dev_id = sw_dev->obj_desc.id;
> > +
> > + err = dpsw_open(ethsw->mc_io, 0, ethsw->dev_id, ðsw-
> >dpsw_handle);
> > + if (err) {
> > + dev_err(dev, "dpsw_open err %d\n", err);
> > + return err;
> > + }
> > +
> > + err = dpsw_get_attributes(ethsw->mc_io, 0, ethsw->dpsw_handle,
> > + ðsw->sw_attr);
> > + if (err) {
> > + dev_err(dev, "dpsw_get_attributes err %d\n", err);
> > + goto err_close;
> > + }
> > +
> > + err = dpsw_get_api_version(ethsw->mc_io, 0,
> > + &version_major,
> > + &version_minor);
> > + if (err) {
> > + dev_err(dev, "dpsw_get_api_version err %d\n", err);
> > + goto err_close;
> > + }
> > +
> > + /* Minimum supported DPSW version check */
> > + if (version_major < DPSW_MIN_VER_MAJOR ||
> > + (version_major == DPSW_MIN_VER_MAJOR &&
> > + version_minor < DPSW_MIN_VER_MINOR)) {
> > + dev_err(dev, "DPSW version %d:%d not supported. Use %d.%d
> or
> > greater.\n",
> > + version_major,
> > + version_minor,
> > + DPSW_MIN_VER_MAJOR, DPSW_MIN_VER_MINOR);
> > + err = -ENOTSUPP;
> > + goto err_close;
> > + }
> > +
> > + err = dpsw_reset(ethsw->mc_io, 0, ethsw->dpsw_handle);
> > + if (err) {
> > + dev_err(dev, "dpsw_reset err %d\n", err);
> > + goto err_close;
> > + }
> > +
> > + err = dpsw_fdb_set_learning_mode(ethsw->mc_io, 0, ethsw-
> >dpsw_handle, 0,
> > + DPSW_FDB_LEARNING_MODE_HW);
> > + if (err) {
> > + dev_err(dev, "dpsw_fdb_set_learning_mode err %d\n", err);
> > + goto err_close;
> > + }
> > +
> > + stp_cfg.vlan_id = DEFAULT_VLAN_ID;
> > + stp_cfg.state = DPSW_STP_STATE_FORWARDING;
> > +
> > + for (i = 0; i < ethsw->sw_attr.num_ifs; i++) {
> > + err = dpsw_if_set_stp(ethsw->mc_io, 0, ethsw->dpsw_handle, i,
> > + &stp_cfg);
> > + if (err) {
> > + dev_err(dev, "dpsw_if_set_stp err %d for port %d\n",
> > + err, i);
> > + goto err_close;
> > + }
> > +
> > + err = dpsw_if_set_broadcast(ethsw->mc_io, 0,
> > + ethsw->dpsw_handle, i, 1);
> > + if (err) {
> > + dev_err(dev,
> > + "dpsw_if_set_broadcast err %d for port %d\n",
> > + err, i);
> > + goto err_close;
> > + }
> > + }
> > +
> > + ethsw_owq = alloc_ordered_workqueue("%s_ordered",
> WQ_MEM_RECLAIM,
> > + "ethsw");
> > + if (!ethsw_owq) {
> > + err = -ENOMEM;
> > + goto err_close;
> > + }
> > +
> > + err = ethsw_register_notifier(dev);
> > + if (err)
> > + goto err_destroy_ordered_workqueue;
> > +
> > + return 0;
> > +
> > +err_destroy_ordered_workqueue:
> > + destroy_workqueue(ethsw_owq);
> > +
> > +err_close:
> > + dpsw_close(ethsw->mc_io, 0, ethsw->dpsw_handle);
> > + return err;
> > +}
> > +
> > +static int ethsw_port_init(struct ethsw_port_priv *port_priv, u16 port)
> > +{
> > + const char def_mcast[ETH_ALEN] = {0x01, 0x00, 0x5e, 0x00, 0x00, 0x01};
> > + struct net_device *netdev = port_priv->netdev;
> > + struct ethsw_core *ethsw = port_priv->ethsw_data;
> > + struct dpsw_tci_cfg tci_cfg = {0};
> > + struct dpsw_vlan_if_cfg vcfg;
> > + int err;
> > +
> > + /* Switch starts with all ports configured to VLAN 1. Need to
> > + * remove this setting to allow configuration at bridge join
> > + */
> > + vcfg.num_ifs = 1;
> > + vcfg.if_id[0] = port_priv->idx;
> > +
> > + err = dpsw_vlan_remove_if_untagged(ethsw->mc_io, 0, ethsw-
> >dpsw_handle,
> > + DEFAULT_VLAN_ID, &vcfg);
> > + if (err) {
> > + netdev_err(netdev, "dpsw_vlan_remove_if_untagged err
> %d\n",
> > + err);
> > + return err;
> > + }
> > +
> > + err = dpsw_if_set_tci(ethsw->mc_io, 0, ethsw->dpsw_handle,
> > + port_priv->idx, &tci_cfg);
> > + if (err) {
> > + netdev_err(netdev, "dpsw_if_set_tci err %d\n", err);
> > + return err;
> > + }
> > +
> > + err = dpsw_vlan_remove_if(ethsw->mc_io, 0, ethsw->dpsw_handle,
> > + DEFAULT_VLAN_ID, &vcfg);
> > + if (err) {
> > + netdev_err(netdev, "dpsw_vlan_remove_if err %d\n", err);
> > + return err;
> > + }
> > +
> > + err = ethsw_port_fdb_add_mc(port_priv, def_mcast);
> > +
> > + return err;
> > +}
> > +
> > +static void ethsw_takedown(struct fsl_mc_device *sw_dev)
> > +{
> > + struct device *dev = &sw_dev->dev;
> > + struct ethsw_core *ethsw = dev_get_drvdata(dev);
> > + int err;
> > +
> > + err = unregister_switchdev_notifier(&port_switchdev_nb);
> > + if (err)
> > + dev_err(dev,
> > + "Failed to unregister switchdev notifier (%d)\n", err);
> > +
> > + err = unregister_netdevice_notifier(&port_nb);
> > + if (err)
> > + dev_err(dev,
> > + "Failed to unregister netdev notifier (%d)\n", err);
>
> Above 2 can be grouped into ethsw_unregister_notifier.
>
> > +
> > + err = dpsw_close(ethsw->mc_io, 0, ethsw->dpsw_handle);
> > + if (err)
> > + dev_warn(dev, "dpsw_close err %d\n", err);
> > +}
> > +
> > +static int ethsw_remove(struct fsl_mc_device *sw_dev)
> > +{
> > + struct ethsw_port_priv *port_priv;
> > + struct ethsw_core *ethsw;
> > + struct device *dev;
> > + int i;
> > +
> > + dev = &sw_dev->dev;
> > + ethsw = dev_get_drvdata(dev);
> > +
> > + ethsw_teardown_irqs(sw_dev);
> > +
> > + rtnl_lock();
> > + ethsw_stop(ethsw);
> > + rtnl_unlock();
> > +
> > + for (i = 0; i < ethsw->sw_attr.num_ifs; i++) {
> > + port_priv = ethsw->ports[i];
> > + unregister_netdev(port_priv->netdev);
> > + free_netdev(port_priv->netdev);
> > + }
> > + kfree(ethsw->ports);
> > +
> > + ethsw_takedown(sw_dev);
> > + fsl_mc_portal_free(ethsw->mc_io);
> > +
> > + kfree(ethsw);
> > +
> > + dev_set_drvdata(dev, NULL);
> > +
> > + return 0;
> > +}
> > +
> > +static int ethsw_probe_port(struct ethsw_core *ethsw, u16 port_idx)
> > +{
> > + struct ethsw_port_priv *port_priv;
> > + struct device *dev = ethsw->dev;
> > + struct net_device *port_netdev;
> > + int err;
> > +
> > + port_netdev = alloc_etherdev(sizeof(struct ethsw_port_priv));
> > + if (!port_netdev) {
> > + dev_err(dev, "alloc_etherdev error\n");
> > + return -ENOMEM;
> > + }
> > +
> > + port_priv = netdev_priv(port_netdev);
> > + port_priv->netdev = port_netdev;
> > + port_priv->ethsw_data = ethsw;
> > +
> > + port_priv->idx = port_idx;
> > + port_priv->stp_state = BR_STATE_FORWARDING;
> > +
> > + /* Flooding is implicitly enabled */
> > + port_priv->flood = true;
> > +
> > + SET_NETDEV_DEV(port_netdev, dev);
> > + port_netdev->netdev_ops = ðsw_port_ops;
> > + port_netdev->switchdev_ops = ðsw_port_switchdev_ops;
> > +
> > + /* Set MTU limits */
> > + port_netdev->min_mtu = ETH_MIN_MTU;
> > + port_netdev->max_mtu = ETHSW_MAX_FRAME_LENGTH;
> > +
> > + err = register_netdev(port_netdev);
> > + if (err < 0) {
> > + dev_err(dev, "register_netdev error %d\n", err);
> > + free_netdev(port_netdev);
> > + return err;
> > + }
> > +
> > + ethsw->ports[port_idx] = port_priv;
> > +
> > + return ethsw_port_init(port_priv, port_idx);
> > +}
> > +
> > +static int ethsw_probe(struct fsl_mc_device *sw_dev)
> > +{
> > + struct device *dev = &sw_dev->dev;
> > + struct ethsw_core *ethsw;
> > + int err;
> > + u16 i, j;
> > +
> > + /* Allocate switch core*/
> > + ethsw = kzalloc(sizeof(*ethsw), GFP_KERNEL);
> > +
> > + if (!ethsw)
> > + return -ENOMEM;
> > +
> > + ethsw->dev = dev;
> > + dev_set_drvdata(dev, ethsw);
> > +
> > + err = fsl_mc_portal_allocate(sw_dev, 0, ðsw->mc_io);
> > + if (err) {
> > + dev_err(dev, "fsl_mc_portal_allocate err %d\n", err);
> > + goto err_free_drvdata;
> > + }
> > +
> > + err = ethsw_init(sw_dev);
> > + if (err)
> > + goto err_free_cmdport;
> > +
> > + /* DEFAULT_VLAN_ID is implicitly configured on the switch */
> > + ethsw->vlans[DEFAULT_VLAN_ID] = ETHSW_VLAN_MEMBER;
> > +
> > + /* Learning is implicitly enabled */
> > + ethsw->learning = true;
> > +
> > + ethsw->ports = kcalloc(ethsw->sw_attr.num_ifs, sizeof(*ethsw->ports),
> > + GFP_KERNEL);
> > + if (!(ethsw->ports)) {
> > + err = -ENOMEM;
> > + goto err_takedown;
> > + }
> > +
> > + for (i = 0; i < ethsw->sw_attr.num_ifs; i++) {
> > + err = ethsw_probe_port(ethsw, i);
> > + if (err) {
> > + /* Cleanup previous ports only */
> > + for (j = 0; j < i; j++) {
>
> I think you can go with
> for (i--; i >= 0; i--)
>
> or better yet:
> goto err_free_ports
> and refactor err_free_ports to look like:
> for (i--; i >= 0; i--) {
> ...
> }
>
> Best regards,
> Bogdan P.
>
> > + unregister_netdev(ethsw->ports[j]->netdev);
> > + free_netdev(ethsw->ports[j]->netdev);
> > + }
> > + goto err_takedown;
> > + }
> > + }
> > +
> > + /* Switch starts up enabled */
> > + rtnl_lock();
> > + err = ethsw_open(ethsw);
> > + rtnl_unlock();
> > + if (err)
> > + goto err_free_ports;
> > +
> > + /* Setup IRQs */
> > + err = ethsw_setup_irqs(sw_dev);
> > + if (err)
> > + goto err_stop;
> > +
> > + dev_info(dev, "probed %d port switch\n", ethsw->sw_attr.num_ifs);
> > + return 0;
> > +
> > +err_stop:
> > + rtnl_lock();
> > + ethsw_stop(ethsw);
> > + rtnl_unlock();
> > +
> > +err_free_ports:
> > + for (i = 0; i < ethsw->sw_attr.num_ifs; i++) {
> > + unregister_netdev(ethsw->ports[i]->netdev);
> > + free_netdev(ethsw->ports[i]->netdev);
> > + }
> > + kfree(ethsw->ports);
> > +
> > +err_takedown:
> > + ethsw_takedown(sw_dev);
> > +
> > +err_free_cmdport:
> > + fsl_mc_portal_free(ethsw->mc_io);
> > +
> > +err_free_drvdata:
> > + kfree(ethsw);
> > + dev_set_drvdata(dev, NULL);
> > +
> > + return err;
> > +}
> > +
> > +static const struct fsl_mc_device_id ethsw_match_id_table[] = {
> > + {
> > + .vendor = FSL_MC_VENDOR_FREESCALE,
> > + .obj_type = "dpsw",
> > + },
> > + { .vendor = 0x0 }
> > +};
> > +MODULE_DEVICE_TABLE(fslmc, ethsw_match_id_table);
> > +
> > +static struct fsl_mc_driver eth_sw_drv = {
> > + .driver = {
> > + .name = KBUILD_MODNAME,
> > + .owner = THIS_MODULE,
> > + },
> > + .probe = ethsw_probe,
> > + .remove = ethsw_remove,
> > + .match_id_table = ethsw_match_id_table
> > +};
> > +
> > +module_fsl_mc_driver(eth_sw_drv);
> > +
> > +MODULE_LICENSE("Dual BSD/GPL");
> > +MODULE_DESCRIPTION("DPAA2 Ethernet Switch Driver");
> > diff --git a/drivers/staging/fsl-dpaa2/ethsw/ethsw.h b/drivers/staging/fsl-
> > dpaa2/ethsw/ethsw.h
> > new file mode 100644
> > index 0000000..8c1d645
> > --- /dev/null
> > +++ b/drivers/staging/fsl-dpaa2/ethsw/ethsw.h
> > @@ -0,0 +1,88 @@
> > +/* Copyright 2014-2017 Freescale Semiconductor Inc.
> > + * Copyright 2017 NXP
> > + *
> > + * Redistribution and use in source and binary forms, with or without
> > + * modification, are permitted provided that the following conditions are met:
> > + * * Redistributions of source code must retain the above copyright
> > + * notice, this list of conditions and the following disclaimer.
> > + * * 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.
> > + * * Neither the name of the above-listed copyright holders nor the
> > + * names of any contributors may be used to endorse or promote products
> > + * derived from this software without specific prior written permission.
> > + *
> > + *
> > + * ALTERNATIVELY, this software may be distributed under the terms of the
> > + * GNU General Public License ("GPL") as published by the Free Software
> > + * Foundation, either version 2 of that License or (at your option) any
> > + * later version.
> > + *
> > + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND
> CONTRIBUTORS "AS IS"
> > + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
> LIMITED TO, THE
> > + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A
> PARTICULAR PURPOSE
> > + * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDERS OR
> CONTRIBUTORS BE
> > + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
> > + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
> PROCUREMENT OF
> > + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR
> BUSINESS
> > + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
> WHETHER IN
> > + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
> OTHERWISE)
> > + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
> ADVISED OF THE
> > + * POSSIBILITY OF SUCH DAMAGE.
> > + */
> > +
> > +#ifndef __ETHSW_H
> > +#define __ETHSW_H
> > +
> > +#include <linux/netdevice.h>
> > +#include <linux/etherdevice.h>
> > +#include <linux/rtnetlink.h>
> > +#include <linux/if_vlan.h>
> > +#include <uapi/linux/if_bridge.h>
> > +#include <net/switchdev.h>
> > +#include <linux/if_bridge.h>
> > +
> > +#include "dpsw.h"
> > +
> > +/* Number of IRQs supported */
> > +#define DPSW_IRQ_NUM 2
> > +
> > +#define ETHSW_VLAN_MEMBER 1
> > +#define ETHSW_VLAN_UNTAGGED 2
> > +#define ETHSW_VLAN_PVID 4
> > +#define ETHSW_VLAN_GLOBAL 8
> > +
> > +/* Maximum Frame Length supported by HW (currently 10k) */
> > +#define DPAA2_MFL (10 * 1024)
> > +#define ETHSW_MAX_FRAME_LENGTH (DPAA2_MFL - VLAN_ETH_HLEN -
> ETH_FCS_LEN)
> > +#define ETHSW_L2_MAX_FRM(mtu) ((mtu) + VLAN_ETH_HLEN +
> ETH_FCS_LEN)
> > +
> > +struct ethsw_core;
> > +
> > +/* Per port private data */
> > +struct ethsw_port_priv {
> > + struct net_device *netdev;
> > + u16 idx;
> > + struct ethsw_core *ethsw_data;
> > + u8 link_state;
> > + u8 stp_state;
> > + bool flood;
> > +
> > + u8 vlans[VLAN_VID_MASK + 1];
> > + u16 pvid;
> > +};
> > +
> > +/* Switch data */
> > +struct ethsw_core {
> > + struct device *dev;
> > + struct fsl_mc_io *mc_io;
> > + u16 dpsw_handle;
> > + struct dpsw_attr sw_attr;
> > + int dev_id;
> > + struct ethsw_port_priv **ports;
> > +
> > + u8 vlans[VLAN_VID_MASK + 1];
> > + bool learning;
> > +};
> > +
> > +#endif /* __ETHSW_H */
> > --
> > 1.9.1
^ permalink raw reply
* [PATCH][net-next] net_sched: remove redundant assignment to ret
From: Colin King @ 2017-09-29 14:01 UTC (permalink / raw)
To: Jamal Hadi Salim, Cong Wang, Jiri Pirko, David S . Miller, netdev
Cc: kernel-janitors, linux-kernel
From: Colin Ian King <colin.king@canonical.com>
The assignment of -EINVAL to variable ret is redundant as it
is being overwritten on the following error exit paths or
to the return value from the following call to basic_set_parms.
Fix this up by removing it. Cleans up clang warning message:
net/sched/cls_basic.c:185:2: warning: Value stored to 'err' is never read
Signed-off-by: Colin Ian King <colin.king@canonical.com>
---
net/sched/cls_basic.c | 1 -
1 file changed, 1 deletion(-)
diff --git a/net/sched/cls_basic.c b/net/sched/cls_basic.c
index cfeb6f158566..700b345b07f9 100644
--- a/net/sched/cls_basic.c
+++ b/net/sched/cls_basic.c
@@ -182,7 +182,6 @@ static int basic_change(struct net *net, struct sk_buff *in_skb,
if (err < 0)
goto errout;
- err = -EINVAL;
if (handle) {
fnew->handle = handle;
if (!fold) {
--
2.14.1
^ permalink raw reply related
* Re: [Intel-wired-lan] [PATCH net v2] i40e: Fix limit imprecise of the number of MAC/VLAN that can be added for VFs
From: Alexander Duyck @ 2017-09-29 15:04 UTC (permalink / raw)
To: wangyunjian
Cc: David Miller, Jeff Kirsher, Sergei Shtylyov, Netdev, caihe,
intel-wired-lan
In-Reply-To: <34EFBCA9F01B0748BEB6B629CE643AE60C64BFF6@dggemm513-mbx.china.huawei.com>
On Fri, Sep 29, 2017 at 2:13 AM, wangyunjian <wangyunjian@huawei.com> wrote:
>
>
>> -----Original Message-----
>> From: Alexander Duyck [mailto:alexander.duyck@gmail.com]
>> Sent: Thursday, September 28, 2017 11:44 PM
>> To: wangyunjian <wangyunjian@huawei.com>
>> Cc: David Miller <davem@davemloft.net>; Jeff Kirsher
>> <jeffrey.t.kirsher@intel.com>; Sergei Shtylyov
>> <sergei.shtylyov@cogentembedded.com>; Netdev
>> <netdev@vger.kernel.org>; caihe <caihe@huawei.com>; intel-wired-lan
>> <intel-wired-lan@lists.osuosl.org>
>> Subject: Re: [Intel-wired-lan] [PATCH net v2] i40e: Fix limit imprecise of the
>> number of MAC/VLAN that can be added for VFs
>>
>> On Wed, Sep 27, 2017 at 7:01 PM, w00273186 <wangyunjian@huawei.com>
>> wrote:
>> > From: Yunjian Wang <wangyunjian@huawei.com>
>> >
>> > Now it doesn't limit the number of MAC/VLAN strictly. When there is more
>> > elements in the virtchnl MAC/VLAN list, it can still add successfully.
>>
>> You could still add but should you. I'm not clear from this patch
>> description what this is supposed to be addressing. If you enable the
>> "trust" flag for a VF via the "ip link set dev <iface> vf <vfnum>
>> trust on" it can make use of any resources on the device, but without
>> that there is an upper limit that is supposed to be enforced to
>> prevent the VF from making use of an excessive amount of resources.
>> That is what is being enforced by the code you are moving out of the
>> way below.
>
> I don't enable the "trust" flag for a VF. But this script can successfully add
> MACs more than I40E_VC_MAX_MAC_ADDR_PER_VF(12) in VM. It has
> same problem with VLAN.
>
> Test script:
>
> for((i=10;i<50;i++))
> do
> ipmaddr add 01:00:5e:01:02:$i dev eth0
> done
>
> for ((i=1;i<40;i++))
> do
> ip link add link eth0 name eth0.$i type vlan id $i
> done
>
Okay, thanks for the info. I can see if we can address the issue in a
way that prevents us from adding the filters to the hardware before we
return the result indicating if we can support it or not.
- Alex
^ permalink raw reply
* Re: [PATCH net-next] net: bridge: add per-port group_fwd_mask with less restrictions
From: Stephen Hemminger @ 2017-09-29 15:14 UTC (permalink / raw)
To: Nikolay Aleksandrov; +Cc: netdev, roopa, bridge
In-Reply-To: <1506517964-17479-1-git-send-email-nikolay@cumulusnetworks.com>
On Wed, 27 Sep 2017 16:12:44 +0300
Nikolay Aleksandrov <nikolay@cumulusnetworks.com> wrote:
> We need to be able to transparently forward most link-local frames via
> tunnels (e.g. vxlan, qinq). Currently the bridge's group_fwd_mask has a
> mask which restricts the forwarding of STP and LACP, but we need to be able
> to forward these over tunnels and control that forwarding on a per-port
> basis thus add a new per-port group_fwd_mask option which only disallows
> mac pause frames to be forwarded (they're always dropped anyway).
> The patch does not change the current default situation - all of the others
> are still restricted unless configured for forwarding.
> We have successfully tested this patch with LACP and STP forwarding over
> VxLAN and qinq tunnels.
>
> Signed-off-by: Nikolay Aleksandrov <nikolay@cumulusnetworks.com>
LACP is fine, but STP must not be forwarded if STP in user or kernel
mode is enabled.
Please update this patch or revert it.
^ permalink raw reply
* RE: [PATCH net-next RFC 2/9] net: dsa: mv88e6xxx: expose switch time as a PTP hardware clock
From: Brandon Streiff @ 2017-09-29 15:17 UTC (permalink / raw)
To: Andrew Lunn
Cc: netdev@vger.kernel.org, linux-kernel@vger.kernel.org,
David S. Miller, Florian Fainelli, Vivien Didelot,
Richard Cochran, Erik Hons
In-Reply-To: <20170928170329.GC14940@lunn.ch>
> From: Andrew Lunn [mailto:andrew@lunn.ch]
> Sent: Thursday, September 28, 2017 12:03 PM
>
> > + bool timeout = time_is_before_jiffies(chip->last_overflow_check +
> > + MV88E6XXX_TAI_OVERFLOW_PERIOD);
> > +
> > + if (timeout) {
>
> Why do you need this timeout? Do you think the kernel will call this
> more often than required?
>
> Also, if it did call this function early, you skip the read, and
> reschedule. There is then a danger the next read is after the
> wraparound.....
That was, conceptually, a copy-paste from ixgbe_ptp.c as I was looking for how to implement the overflow accounting; that driver has a similar time_is_before_jiffies check in ixgbe_ptp_overflow_check.
Although now that I'm looking it over again, I'm also not certain of the need. Even if we're called more frequently than we expect, that doesn't seem to be harmful with regard to timekeeping. Hmm.
-- brandon
^ permalink raw reply
* Re: netlink backwards compatibility in userspace tools
From: Stephen Hemminger @ 2017-09-29 15:21 UTC (permalink / raw)
To: Jason A. Donenfeld; +Cc: Netdev, LKML, Daniel Kahn Gillmor
In-Reply-To: <CAHmME9oixZtPVdH24KJQ9NaTuf_ECAOoHwQhuA+Fy-BX+F_3dw@mail.gmail.com>
On Fri, 29 Sep 2017 12:22:42 +0200
"Jason A. Donenfeld" <Jason@zx2c4.com> wrote:
> Hi guys,
>
> One handy aspect of Netlink is that it's backwards compatible. This
> means that you can run old userspace utilities on new kernels, even if
> the new kernel supports new features and netlink attributes. The wire
> format is stable enough that the data marshaled can be extended
> without breaking compat. Neat.
>
> I was wondering, though, what you think the best stance is toward
> these old userspace utilities. What should they do if the kernel sends
> it netlink attributes that it does not recognize? At the moment, I'm
> doing something like this:
>
> static void warn_unrecognized(void)
> {
> static bool once = false;
> if (once)
> return;
> once = true;
> fprintf(stderr,
> "Warning: this program received from your kernel one or more\n"
> "attributes that it did not recognize. It is possible that\n"
> "this version of wg(8) is older than your kernel. You may\n"
> "want to update this program.\n");
> }
>
> This seems like a somewhat sensible warning, but then I wonder about
> distributions like Debian, which has a long stable life cycle, so it
> frequently has very old tools (ancient iproute2 for example). Then,
> VPS providers have these Debian images run on top of newer kernels.
> People in this situation would undoubtedly see the above warning a lot
> and not be able to do anything about it. Not horrible, but a bit
> annoying. Is this an okay annoyance? Or is it advised to just have no
> warning at all? One idea would be to put it behind an environment
> variable flag, but I don't like too many nobs.
>
> I'm generally wondering about attitudes toward this kind of userspace
> program behavior in response to newer kernels.
>
> Thanks,
> Jason
I can not see a reason that such a warning is required.
Old utilities should just work fine, they just won't show or allow
setting attributes they don't understand.
Any netlink attributes that the tools do not recognize should just
be ignored.
^ 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