diff options
author | WANG Cong <amwang@redhat.com> | 2010-05-06 03:48:24 -0400 |
---|---|---|
committer | David S. Miller <davem@davemloft.net> | 2010-05-06 03:48:24 -0400 |
commit | c06ee961d3c0e51009cbd0e123b61fbb97f37d0b (patch) | |
tree | 5d5899374a6f32f5ab77a78e6baeeb4a122a4721 /net/bridge | |
parent | 0e34e93177fb1f642cab080e0bde664c06c7183a (diff) |
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 <davem@davemloft.net>
Cc: Neil Horman <nhorman@tuxdriver.com>
Cc: Stephen Hemminger <shemminger@linux-foundation.org>
Cc: Matt Mackall <mpm@selenic.com>
Signed-off-by: WANG Cong <amwang@redhat.com>
Signed-off-by: David S. Miller <davem@davemloft.net>
Diffstat (limited to 'net/bridge')
-rw-r--r-- | net/bridge/br_device.c | 59 | ||||
-rw-r--r-- | net/bridge/br_forward.c | 23 | ||||
-rw-r--r-- | net/bridge/br_if.c | 25 | ||||
-rw-r--r-- | net/bridge/br_private.h | 2 |
4 files changed, 108 insertions, 1 deletions
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 @@ | |||
13 | 13 | ||
14 | #include <linux/kernel.h> | 14 | #include <linux/kernel.h> |
15 | #include <linux/netdevice.h> | 15 | #include <linux/netdevice.h> |
16 | #include <linux/netpoll.h> | ||
16 | #include <linux/etherdevice.h> | 17 | #include <linux/etherdevice.h> |
17 | #include <linux/ethtool.h> | 18 | #include <linux/ethtool.h> |
19 | #include <linux/list.h> | ||
18 | 20 | ||
19 | #include <asm/uaccess.h> | 21 | #include <asm/uaccess.h> |
20 | #include "br_private.h" | 22 | #include "br_private.h" |
@@ -188,6 +190,59 @@ static int br_set_tx_csum(struct net_device *dev, u32 data) | |||
188 | return 0; | 190 | return 0; |
189 | } | 191 | } |
190 | 192 | ||
193 | #ifdef CONFIG_NET_POLL_CONTROLLER | ||
194 | bool br_devices_support_netpoll(struct net_bridge *br) | ||
195 | { | ||
196 | struct net_bridge_port *p; | ||
197 | bool ret = true; | ||
198 | int count = 0; | ||
199 | unsigned long flags; | ||
200 | |||
201 | spin_lock_irqsave(&br->lock, flags); | ||
202 | list_for_each_entry(p, &br->port_list, list) { | ||
203 | count++; | ||
204 | if ((p->dev->priv_flags & IFF_DISABLE_NETPOLL) || | ||
205 | !p->dev->netdev_ops->ndo_poll_controller) | ||
206 | ret = false; | ||
207 | } | ||
208 | spin_unlock_irqrestore(&br->lock, flags); | ||
209 | return count != 0 && ret; | ||
210 | } | ||
211 | |||
212 | static void br_poll_controller(struct net_device *br_dev) | ||
213 | { | ||
214 | struct netpoll *np = br_dev->npinfo->netpoll; | ||
215 | |||
216 | if (np->real_dev != br_dev) | ||
217 | netpoll_poll_dev(np->real_dev); | ||
218 | } | ||
219 | |||
220 | void br_netpoll_cleanup(struct net_device *br_dev) | ||
221 | { | ||
222 | struct net_bridge *br = netdev_priv(br_dev); | ||
223 | struct net_bridge_port *p, *n; | ||
224 | const struct net_device_ops *ops; | ||
225 | |||
226 | br->dev->npinfo = NULL; | ||
227 | list_for_each_entry_safe(p, n, &br->port_list, list) { | ||
228 | if (p->dev) { | ||
229 | ops = p->dev->netdev_ops; | ||
230 | if (ops->ndo_netpoll_cleanup) | ||
231 | ops->ndo_netpoll_cleanup(p->dev); | ||
232 | else | ||
233 | p->dev->npinfo = NULL; | ||
234 | } | ||
235 | } | ||
236 | } | ||
237 | |||
238 | #else | ||
239 | |||
240 | void br_netpoll_cleanup(struct net_device *br_dev) | ||
241 | { | ||
242 | } | ||
243 | |||
244 | #endif | ||
245 | |||
191 | static const struct ethtool_ops br_ethtool_ops = { | 246 | static const struct ethtool_ops br_ethtool_ops = { |
192 | .get_drvinfo = br_getinfo, | 247 | .get_drvinfo = br_getinfo, |
193 | .get_link = ethtool_op_get_link, | 248 | .get_link = ethtool_op_get_link, |
@@ -211,6 +266,10 @@ static const struct net_device_ops br_netdev_ops = { | |||
211 | .ndo_set_multicast_list = br_dev_set_multicast_list, | 266 | .ndo_set_multicast_list = br_dev_set_multicast_list, |
212 | .ndo_change_mtu = br_change_mtu, | 267 | .ndo_change_mtu = br_change_mtu, |
213 | .ndo_do_ioctl = br_dev_ioctl, | 268 | .ndo_do_ioctl = br_dev_ioctl, |
269 | #ifdef CONFIG_NET_POLL_CONTROLLER | ||
270 | .ndo_netpoll_cleanup = br_netpoll_cleanup, | ||
271 | .ndo_poll_controller = br_poll_controller, | ||
272 | #endif | ||
214 | }; | 273 | }; |
215 | 274 | ||
216 | static void br_dev_free(struct net_device *dev) | 275 | 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 @@ | |||
15 | #include <linux/slab.h> | 15 | #include <linux/slab.h> |
16 | #include <linux/kernel.h> | 16 | #include <linux/kernel.h> |
17 | #include <linux/netdevice.h> | 17 | #include <linux/netdevice.h> |
18 | #include <linux/netpoll.h> | ||
18 | #include <linux/skbuff.h> | 19 | #include <linux/skbuff.h> |
19 | #include <linux/if_vlan.h> | 20 | #include <linux/if_vlan.h> |
20 | #include <linux/netfilter_bridge.h> | 21 | #include <linux/netfilter_bridge.h> |
@@ -50,7 +51,13 @@ int br_dev_queue_push_xmit(struct sk_buff *skb) | |||
50 | else { | 51 | else { |
51 | skb_push(skb, ETH_HLEN); | 52 | skb_push(skb, ETH_HLEN); |
52 | 53 | ||
53 | dev_queue_xmit(skb); | 54 | #ifdef CONFIG_NET_POLL_CONTROLLER |
55 | if (unlikely(skb->dev->priv_flags & IFF_IN_NETPOLL)) { | ||
56 | netpoll_send_skb(skb->dev->npinfo->netpoll, skb); | ||
57 | skb->dev->priv_flags &= ~IFF_IN_NETPOLL; | ||
58 | } else | ||
59 | #endif | ||
60 | dev_queue_xmit(skb); | ||
54 | } | 61 | } |
55 | } | 62 | } |
56 | 63 | ||
@@ -66,9 +73,23 @@ int br_forward_finish(struct sk_buff *skb) | |||
66 | 73 | ||
67 | static void __br_deliver(const struct net_bridge_port *to, struct sk_buff *skb) | 74 | static void __br_deliver(const struct net_bridge_port *to, struct sk_buff *skb) |
68 | { | 75 | { |
76 | #ifdef CONFIG_NET_POLL_CONTROLLER | ||
77 | struct net_bridge *br = to->br; | ||
78 | if (unlikely(br->dev->priv_flags & IFF_IN_NETPOLL)) { | ||
79 | struct netpoll *np; | ||
80 | to->dev->npinfo = skb->dev->npinfo; | ||
81 | np = skb->dev->npinfo->netpoll; | ||
82 | np->real_dev = np->dev = to->dev; | ||
83 | to->dev->priv_flags |= IFF_IN_NETPOLL; | ||
84 | } | ||
85 | #endif | ||
69 | skb->dev = to->dev; | 86 | skb->dev = to->dev; |
70 | NF_HOOK(PF_BRIDGE, NF_BR_LOCAL_OUT, skb, NULL, skb->dev, | 87 | NF_HOOK(PF_BRIDGE, NF_BR_LOCAL_OUT, skb, NULL, skb->dev, |
71 | br_forward_finish); | 88 | br_forward_finish); |
89 | #ifdef CONFIG_NET_POLL_CONTROLLER | ||
90 | if (skb->dev->npinfo) | ||
91 | skb->dev->npinfo->netpoll->dev = br->dev; | ||
92 | #endif | ||
72 | } | 93 | } |
73 | 94 | ||
74 | static void __br_forward(const struct net_bridge_port *to, struct sk_buff *skb) | 95 | 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 @@ | |||
13 | 13 | ||
14 | #include <linux/kernel.h> | 14 | #include <linux/kernel.h> |
15 | #include <linux/netdevice.h> | 15 | #include <linux/netdevice.h> |
16 | #include <linux/netpoll.h> | ||
16 | #include <linux/ethtool.h> | 17 | #include <linux/ethtool.h> |
17 | #include <linux/if_arp.h> | 18 | #include <linux/if_arp.h> |
18 | #include <linux/module.h> | 19 | #include <linux/module.h> |
@@ -153,6 +154,14 @@ static void del_nbp(struct net_bridge_port *p) | |||
153 | kobject_uevent(&p->kobj, KOBJ_REMOVE); | 154 | kobject_uevent(&p->kobj, KOBJ_REMOVE); |
154 | kobject_del(&p->kobj); | 155 | kobject_del(&p->kobj); |
155 | 156 | ||
157 | #ifdef CONFIG_NET_POLL_CONTROLLER | ||
158 | if (br_devices_support_netpoll(br)) | ||
159 | br->dev->priv_flags &= ~IFF_DISABLE_NETPOLL; | ||
160 | if (dev->netdev_ops->ndo_netpoll_cleanup) | ||
161 | dev->netdev_ops->ndo_netpoll_cleanup(dev); | ||
162 | else | ||
163 | dev->npinfo = NULL; | ||
164 | #endif | ||
156 | call_rcu(&p->rcu, destroy_nbp_rcu); | 165 | call_rcu(&p->rcu, destroy_nbp_rcu); |
157 | } | 166 | } |
158 | 167 | ||
@@ -165,6 +174,8 @@ static void del_br(struct net_bridge *br, struct list_head *head) | |||
165 | del_nbp(p); | 174 | del_nbp(p); |
166 | } | 175 | } |
167 | 176 | ||
177 | br_netpoll_cleanup(br->dev); | ||
178 | |||
168 | del_timer_sync(&br->gc_timer); | 179 | del_timer_sync(&br->gc_timer); |
169 | 180 | ||
170 | br_sysfs_delbr(br->dev); | 181 | br_sysfs_delbr(br->dev); |
@@ -444,6 +455,20 @@ int br_add_if(struct net_bridge *br, struct net_device *dev) | |||
444 | 455 | ||
445 | kobject_uevent(&p->kobj, KOBJ_ADD); | 456 | kobject_uevent(&p->kobj, KOBJ_ADD); |
446 | 457 | ||
458 | #ifdef CONFIG_NET_POLL_CONTROLLER | ||
459 | if (br_devices_support_netpoll(br)) { | ||
460 | br->dev->priv_flags &= ~IFF_DISABLE_NETPOLL; | ||
461 | if (br->dev->npinfo) | ||
462 | dev->npinfo = br->dev->npinfo; | ||
463 | } else if (!(br->dev->priv_flags & IFF_DISABLE_NETPOLL)) { | ||
464 | br->dev->priv_flags |= IFF_DISABLE_NETPOLL; | ||
465 | printk(KERN_INFO "New device %s does not support netpoll\n", | ||
466 | dev->name); | ||
467 | printk(KERN_INFO "Disabling netpoll for %s\n", | ||
468 | br->dev->name); | ||
469 | } | ||
470 | #endif | ||
471 | |||
447 | return 0; | 472 | return 0; |
448 | err2: | 473 | err2: |
449 | br_fdb_delete_by_port(br, p, 1); | 474 | 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) | |||
253 | extern void br_dev_setup(struct net_device *dev); | 253 | extern void br_dev_setup(struct net_device *dev); |
254 | extern netdev_tx_t br_dev_xmit(struct sk_buff *skb, | 254 | extern netdev_tx_t br_dev_xmit(struct sk_buff *skb, |
255 | struct net_device *dev); | 255 | struct net_device *dev); |
256 | extern bool br_devices_support_netpoll(struct net_bridge *br); | ||
257 | extern void br_netpoll_cleanup(struct net_device *br_dev); | ||
256 | 258 | ||
257 | /* br_fdb.c */ | 259 | /* br_fdb.c */ |
258 | extern int br_fdb_init(void); | 260 | extern int br_fdb_init(void); |