From 168d40ee3d147ae20860e7916bd79b636cbe8fd5 Mon Sep 17 00:00:00 2001 From: stephen hemminger Date: Tue, 27 Apr 2010 15:01:05 +0000 Subject: bridge: multicast flood Fix unsafe usage of RCU. Would never work on Alpha SMP because of lack of rcu_dereference() Signed-off-by: Stephen Hemminger Signed-off-by: David S. Miller --- net/bridge/br_forward.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) (limited to 'net/bridge/br_forward.c') diff --git a/net/bridge/br_forward.c b/net/bridge/br_forward.c index 7a241c396981..5b70fc012e40 100644 --- a/net/bridge/br_forward.c +++ b/net/bridge/br_forward.c @@ -216,7 +216,7 @@ static void br_multicast_flood(struct net_bridge_mdb_entry *mdst, prev = NULL; - rp = br->router_list.first; + rp = rcu_dereference(br->router_list.first); p = mdst ? mdst->ports : NULL; while (p || rp) { lport = p ? p->port : NULL; @@ -233,7 +233,7 @@ static void br_multicast_flood(struct net_bridge_mdb_entry *mdst, if ((unsigned long)lport >= (unsigned long)port) p = p->next; if ((unsigned long)rport >= (unsigned long)port) - rp = rp->next; + rp = rcu_dereference(rp->next); } if (!prev) -- cgit v1.2.3 From 83f6a740b4e52f88e312223df2fc94016a208618 Mon Sep 17 00:00:00 2001 From: stephen hemminger Date: Tue, 27 Apr 2010 15:01:06 +0000 Subject: bridge: multicast port group RCU fix The recently introduced bridge mulitcast port group list was only partially using RCU correctly. It was missing rcu_dereference() and missing the necessary barrier on deletion. The code should have used one of the standard list methods (list or hlist) instead of open coding a RCU based link list. Signed-off-by: Stephen Hemminger Signed-off-by: David S. Miller --- net/bridge/br_forward.c | 4 ++-- net/bridge/br_multicast.c | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) (limited to 'net/bridge/br_forward.c') diff --git a/net/bridge/br_forward.c b/net/bridge/br_forward.c index 5b70fc012e40..5f9988a3f06a 100644 --- a/net/bridge/br_forward.c +++ b/net/bridge/br_forward.c @@ -217,7 +217,7 @@ static void br_multicast_flood(struct net_bridge_mdb_entry *mdst, prev = NULL; rp = rcu_dereference(br->router_list.first); - p = mdst ? mdst->ports : NULL; + p = mdst ? rcu_dereference(mdst->ports) : NULL; while (p || rp) { lport = p ? p->port : NULL; rport = rp ? hlist_entry(rp, struct net_bridge_port, rlist) : @@ -231,7 +231,7 @@ static void br_multicast_flood(struct net_bridge_mdb_entry *mdst, goto out; if ((unsigned long)lport >= (unsigned long)port) - p = p->next; + p = rcu_dereference(p->next); if ((unsigned long)rport >= (unsigned long)port) rp = rcu_dereference(rp->next); } diff --git a/net/bridge/br_multicast.c b/net/bridge/br_multicast.c index d63868c9b2c0..7128abdce45f 100644 --- a/net/bridge/br_multicast.c +++ b/net/bridge/br_multicast.c @@ -259,7 +259,7 @@ static void br_multicast_del_pg(struct net_bridge *br, if (p != pg) continue; - *pp = p->next; + rcu_assign_pointer(*pp, p->next); hlist_del_init(&p->mglist); del_timer(&p->timer); del_timer(&p->query_timer); -- cgit v1.2.3 From afe0159d935ab731c682e811356914bb2be9470c Mon Sep 17 00:00:00 2001 From: stephen hemminger Date: Tue, 27 Apr 2010 15:01:07 +0000 Subject: bridge: multicast_flood cleanup Move some declarations around to make it clearer which variables are being used inside loop. Signed-off-by: Stephen Hemminger Signed-off-by: David S. Miller --- net/bridge/br_forward.c | 8 +++----- 1 file changed, 3 insertions(+), 5 deletions(-) (limited to 'net/bridge/br_forward.c') diff --git a/net/bridge/br_forward.c b/net/bridge/br_forward.c index 5f9988a3f06a..396f077216a3 100644 --- a/net/bridge/br_forward.c +++ b/net/bridge/br_forward.c @@ -208,17 +208,15 @@ static void br_multicast_flood(struct net_bridge_mdb_entry *mdst, { struct net_device *dev = BR_INPUT_SKB_CB(skb)->brdev; struct net_bridge *br = netdev_priv(dev); - struct net_bridge_port *port; - struct net_bridge_port *lport, *rport; - struct net_bridge_port *prev; + struct net_bridge_port *prev = NULL; struct net_bridge_port_group *p; struct hlist_node *rp; - prev = NULL; - rp = rcu_dereference(br->router_list.first); p = mdst ? rcu_dereference(mdst->ports) : NULL; while (p || rp) { + struct net_bridge_port *port, *lport, *rport; + lport = p ? p->port : NULL; rport = rp ? hlist_entry(rp, struct net_bridge_port, rlist) : NULL; -- cgit v1.2.3 From c06ee961d3c0e51009cbd0e123b61fbb97f37d0b Mon Sep 17 00:00:00 2001 From: WANG Cong Date: Thu, 6 May 2010 00:48:24 -0700 Subject: bridge: make bridge support netpoll Based on the previous patch, make bridge support netpoll by: 1) implement the 2 methods to support netpoll for bridge; 2) modify netpoll during forwarding packets via bridge; 3) disable netpoll support of bridge when a netpoll-unabled device is added to bridge; 4) enable netpoll support when all underlying devices support netpoll. Cc: David Miller Cc: Neil Horman Cc: Stephen Hemminger Cc: Matt Mackall Signed-off-by: WANG Cong Signed-off-by: David S. Miller --- net/bridge/br_device.c | 59 +++++++++++++++++++++++++++++++++++++++++++++++++ net/bridge/br_forward.c | 23 ++++++++++++++++++- net/bridge/br_if.c | 25 +++++++++++++++++++++ net/bridge/br_private.h | 2 ++ 4 files changed, 108 insertions(+), 1 deletion(-) (limited to 'net/bridge/br_forward.c') diff --git a/net/bridge/br_device.c b/net/bridge/br_device.c index 82599405dc15..074c59690fc5 100644 --- a/net/bridge/br_device.c +++ b/net/bridge/br_device.c @@ -13,8 +13,10 @@ #include #include +#include #include #include +#include #include #include "br_private.h" @@ -188,6 +190,59 @@ static int br_set_tx_csum(struct net_device *dev, u32 data) return 0; } +#ifdef CONFIG_NET_POLL_CONTROLLER +bool br_devices_support_netpoll(struct net_bridge *br) +{ + struct net_bridge_port *p; + bool ret = true; + int count = 0; + unsigned long flags; + + spin_lock_irqsave(&br->lock, flags); + list_for_each_entry(p, &br->port_list, list) { + count++; + if ((p->dev->priv_flags & IFF_DISABLE_NETPOLL) || + !p->dev->netdev_ops->ndo_poll_controller) + ret = false; + } + spin_unlock_irqrestore(&br->lock, flags); + return count != 0 && ret; +} + +static void br_poll_controller(struct net_device *br_dev) +{ + struct netpoll *np = br_dev->npinfo->netpoll; + + if (np->real_dev != br_dev) + netpoll_poll_dev(np->real_dev); +} + +void br_netpoll_cleanup(struct net_device *br_dev) +{ + struct net_bridge *br = netdev_priv(br_dev); + struct net_bridge_port *p, *n; + const struct net_device_ops *ops; + + br->dev->npinfo = NULL; + list_for_each_entry_safe(p, n, &br->port_list, list) { + if (p->dev) { + ops = p->dev->netdev_ops; + if (ops->ndo_netpoll_cleanup) + ops->ndo_netpoll_cleanup(p->dev); + else + p->dev->npinfo = NULL; + } + } +} + +#else + +void br_netpoll_cleanup(struct net_device *br_dev) +{ +} + +#endif + static const struct ethtool_ops br_ethtool_ops = { .get_drvinfo = br_getinfo, .get_link = ethtool_op_get_link, @@ -211,6 +266,10 @@ static const struct net_device_ops br_netdev_ops = { .ndo_set_multicast_list = br_dev_set_multicast_list, .ndo_change_mtu = br_change_mtu, .ndo_do_ioctl = br_dev_ioctl, +#ifdef CONFIG_NET_POLL_CONTROLLER + .ndo_netpoll_cleanup = br_netpoll_cleanup, + .ndo_poll_controller = br_poll_controller, +#endif }; static void br_dev_free(struct net_device *dev) diff --git a/net/bridge/br_forward.c b/net/bridge/br_forward.c index 396f077216a3..92ad9feb199d 100644 --- a/net/bridge/br_forward.c +++ b/net/bridge/br_forward.c @@ -15,6 +15,7 @@ #include #include #include +#include #include #include #include @@ -50,7 +51,13 @@ int br_dev_queue_push_xmit(struct sk_buff *skb) else { skb_push(skb, ETH_HLEN); - dev_queue_xmit(skb); +#ifdef CONFIG_NET_POLL_CONTROLLER + if (unlikely(skb->dev->priv_flags & IFF_IN_NETPOLL)) { + netpoll_send_skb(skb->dev->npinfo->netpoll, skb); + skb->dev->priv_flags &= ~IFF_IN_NETPOLL; + } else +#endif + dev_queue_xmit(skb); } } @@ -66,9 +73,23 @@ int br_forward_finish(struct sk_buff *skb) static void __br_deliver(const struct net_bridge_port *to, struct sk_buff *skb) { +#ifdef CONFIG_NET_POLL_CONTROLLER + struct net_bridge *br = to->br; + if (unlikely(br->dev->priv_flags & IFF_IN_NETPOLL)) { + struct netpoll *np; + to->dev->npinfo = skb->dev->npinfo; + np = skb->dev->npinfo->netpoll; + np->real_dev = np->dev = to->dev; + to->dev->priv_flags |= IFF_IN_NETPOLL; + } +#endif skb->dev = to->dev; NF_HOOK(PF_BRIDGE, NF_BR_LOCAL_OUT, skb, NULL, skb->dev, br_forward_finish); +#ifdef CONFIG_NET_POLL_CONTROLLER + if (skb->dev->npinfo) + skb->dev->npinfo->netpoll->dev = br->dev; +#endif } static void __br_forward(const struct net_bridge_port *to, struct sk_buff *skb) diff --git a/net/bridge/br_if.c b/net/bridge/br_if.c index 521439333316..537bdd60d9b9 100644 --- a/net/bridge/br_if.c +++ b/net/bridge/br_if.c @@ -13,6 +13,7 @@ #include #include +#include #include #include #include @@ -153,6 +154,14 @@ static void del_nbp(struct net_bridge_port *p) kobject_uevent(&p->kobj, KOBJ_REMOVE); kobject_del(&p->kobj); +#ifdef CONFIG_NET_POLL_CONTROLLER + if (br_devices_support_netpoll(br)) + br->dev->priv_flags &= ~IFF_DISABLE_NETPOLL; + if (dev->netdev_ops->ndo_netpoll_cleanup) + dev->netdev_ops->ndo_netpoll_cleanup(dev); + else + dev->npinfo = NULL; +#endif call_rcu(&p->rcu, destroy_nbp_rcu); } @@ -165,6 +174,8 @@ static void del_br(struct net_bridge *br, struct list_head *head) del_nbp(p); } + br_netpoll_cleanup(br->dev); + del_timer_sync(&br->gc_timer); br_sysfs_delbr(br->dev); @@ -444,6 +455,20 @@ int br_add_if(struct net_bridge *br, struct net_device *dev) kobject_uevent(&p->kobj, KOBJ_ADD); +#ifdef CONFIG_NET_POLL_CONTROLLER + if (br_devices_support_netpoll(br)) { + br->dev->priv_flags &= ~IFF_DISABLE_NETPOLL; + if (br->dev->npinfo) + dev->npinfo = br->dev->npinfo; + } else if (!(br->dev->priv_flags & IFF_DISABLE_NETPOLL)) { + br->dev->priv_flags |= IFF_DISABLE_NETPOLL; + printk(KERN_INFO "New device %s does not support netpoll\n", + dev->name); + printk(KERN_INFO "Disabling netpoll for %s\n", + br->dev->name); + } +#endif + return 0; err2: br_fdb_delete_by_port(br, p, 1); diff --git a/net/bridge/br_private.h b/net/bridge/br_private.h index 018499ebe19d..3d2d3fe0a97e 100644 --- a/net/bridge/br_private.h +++ b/net/bridge/br_private.h @@ -253,6 +253,8 @@ static inline int br_is_root_bridge(const struct net_bridge *br) extern void br_dev_setup(struct net_device *dev); extern netdev_tx_t br_dev_xmit(struct sk_buff *skb, struct net_device *dev); +extern bool br_devices_support_netpoll(struct net_bridge *br); +extern void br_netpoll_cleanup(struct net_device *br_dev); /* br_fdb.c */ extern int br_fdb_init(void); -- cgit v1.2.3