diff options
Diffstat (limited to 'net/bridge/br_device.c')
-rw-r--r-- | net/bridge/br_device.c | 108 |
1 files changed, 61 insertions, 47 deletions
diff --git a/net/bridge/br_device.c b/net/bridge/br_device.c index f8cb908db81f..6f3a9279be30 100644 --- a/net/bridge/br_device.c +++ b/net/bridge/br_device.c | |||
@@ -47,6 +47,10 @@ netdev_tx_t br_dev_xmit(struct sk_buff *skb, struct net_device *dev) | |||
47 | skb_pull(skb, ETH_HLEN); | 47 | skb_pull(skb, ETH_HLEN); |
48 | 48 | ||
49 | if (is_multicast_ether_addr(dest)) { | 49 | if (is_multicast_ether_addr(dest)) { |
50 | if (unlikely(netpoll_tx_running(dev))) { | ||
51 | br_flood_deliver(br, skb); | ||
52 | goto out; | ||
53 | } | ||
50 | if (br_multicast_rcv(br, NULL, skb)) | 54 | if (br_multicast_rcv(br, NULL, skb)) |
51 | goto out; | 55 | goto out; |
52 | 56 | ||
@@ -199,72 +203,81 @@ static int br_set_tx_csum(struct net_device *dev, u32 data) | |||
199 | } | 203 | } |
200 | 204 | ||
201 | #ifdef CONFIG_NET_POLL_CONTROLLER | 205 | #ifdef CONFIG_NET_POLL_CONTROLLER |
202 | static bool br_devices_support_netpoll(struct net_bridge *br) | 206 | static void br_poll_controller(struct net_device *br_dev) |
203 | { | 207 | { |
204 | struct net_bridge_port *p; | ||
205 | bool ret = true; | ||
206 | int count = 0; | ||
207 | unsigned long flags; | ||
208 | |||
209 | spin_lock_irqsave(&br->lock, flags); | ||
210 | list_for_each_entry(p, &br->port_list, list) { | ||
211 | count++; | ||
212 | if ((p->dev->priv_flags & IFF_DISABLE_NETPOLL) || | ||
213 | !p->dev->netdev_ops->ndo_poll_controller) | ||
214 | ret = false; | ||
215 | } | ||
216 | spin_unlock_irqrestore(&br->lock, flags); | ||
217 | return count != 0 && ret; | ||
218 | } | 208 | } |
219 | 209 | ||
220 | static void br_poll_controller(struct net_device *br_dev) | 210 | static void br_netpoll_cleanup(struct net_device *dev) |
221 | { | 211 | { |
222 | struct netpoll *np = br_dev->npinfo->netpoll; | 212 | struct net_bridge *br = netdev_priv(dev); |
213 | struct net_bridge_port *p, *n; | ||
223 | 214 | ||
224 | if (np->real_dev != br_dev) | 215 | list_for_each_entry_safe(p, n, &br->port_list, list) { |
225 | netpoll_poll_dev(np->real_dev); | 216 | br_netpoll_disable(p); |
217 | } | ||
226 | } | 218 | } |
227 | 219 | ||
228 | void br_netpoll_cleanup(struct net_device *dev) | 220 | static int br_netpoll_setup(struct net_device *dev, struct netpoll_info *ni) |
229 | { | 221 | { |
230 | struct net_bridge *br = netdev_priv(dev); | 222 | struct net_bridge *br = netdev_priv(dev); |
231 | struct net_bridge_port *p, *n; | 223 | struct net_bridge_port *p, *n; |
232 | const struct net_device_ops *ops; | 224 | int err = 0; |
233 | 225 | ||
234 | list_for_each_entry_safe(p, n, &br->port_list, list) { | 226 | list_for_each_entry_safe(p, n, &br->port_list, list) { |
235 | if (p->dev) { | 227 | if (!p->dev) |
236 | ops = p->dev->netdev_ops; | 228 | continue; |
237 | if (ops->ndo_netpoll_cleanup) | 229 | |
238 | ops->ndo_netpoll_cleanup(p->dev); | 230 | err = br_netpoll_enable(p); |
239 | else | 231 | if (err) |
240 | p->dev->npinfo = NULL; | 232 | goto fail; |
241 | } | ||
242 | } | 233 | } |
234 | |||
235 | out: | ||
236 | return err; | ||
237 | |||
238 | fail: | ||
239 | br_netpoll_cleanup(dev); | ||
240 | goto out; | ||
243 | } | 241 | } |
244 | 242 | ||
245 | void br_netpoll_disable(struct net_bridge *br, | 243 | int br_netpoll_enable(struct net_bridge_port *p) |
246 | struct net_device *dev) | ||
247 | { | 244 | { |
248 | if (br_devices_support_netpoll(br)) | 245 | struct netpoll *np; |
249 | br->dev->priv_flags &= ~IFF_DISABLE_NETPOLL; | 246 | int err = 0; |
250 | if (dev->netdev_ops->ndo_netpoll_cleanup) | 247 | |
251 | dev->netdev_ops->ndo_netpoll_cleanup(dev); | 248 | np = kzalloc(sizeof(*p->np), GFP_KERNEL); |
252 | else | 249 | err = -ENOMEM; |
253 | dev->npinfo = NULL; | 250 | if (!np) |
251 | goto out; | ||
252 | |||
253 | np->dev = p->dev; | ||
254 | |||
255 | err = __netpoll_setup(np); | ||
256 | if (err) { | ||
257 | kfree(np); | ||
258 | goto out; | ||
259 | } | ||
260 | |||
261 | p->np = np; | ||
262 | |||
263 | out: | ||
264 | return err; | ||
254 | } | 265 | } |
255 | 266 | ||
256 | void br_netpoll_enable(struct net_bridge *br, | 267 | void br_netpoll_disable(struct net_bridge_port *p) |
257 | struct net_device *dev) | ||
258 | { | 268 | { |
259 | if (br_devices_support_netpoll(br)) { | 269 | struct netpoll *np = p->np; |
260 | br->dev->priv_flags &= ~IFF_DISABLE_NETPOLL; | 270 | |
261 | if (br->dev->npinfo) | 271 | if (!np) |
262 | dev->npinfo = br->dev->npinfo; | 272 | return; |
263 | } else if (!(br->dev->priv_flags & IFF_DISABLE_NETPOLL)) { | 273 | |
264 | br->dev->priv_flags |= IFF_DISABLE_NETPOLL; | 274 | p->np = NULL; |
265 | br_info(br,"new device %s does not support netpoll (disabling)", | 275 | |
266 | dev->name); | 276 | /* Wait for transmitting packets to finish before freeing. */ |
267 | } | 277 | synchronize_rcu_bh(); |
278 | |||
279 | __netpoll_cleanup(np); | ||
280 | kfree(np); | ||
268 | } | 281 | } |
269 | 282 | ||
270 | #endif | 283 | #endif |
@@ -293,6 +306,7 @@ static const struct net_device_ops br_netdev_ops = { | |||
293 | .ndo_change_mtu = br_change_mtu, | 306 | .ndo_change_mtu = br_change_mtu, |
294 | .ndo_do_ioctl = br_dev_ioctl, | 307 | .ndo_do_ioctl = br_dev_ioctl, |
295 | #ifdef CONFIG_NET_POLL_CONTROLLER | 308 | #ifdef CONFIG_NET_POLL_CONTROLLER |
309 | .ndo_netpoll_setup = br_netpoll_setup, | ||
296 | .ndo_netpoll_cleanup = br_netpoll_cleanup, | 310 | .ndo_netpoll_cleanup = br_netpoll_cleanup, |
297 | .ndo_poll_controller = br_poll_controller, | 311 | .ndo_poll_controller = br_poll_controller, |
298 | #endif | 312 | #endif |