From 15e83ed78864d0625e87a85f09b297c0919a4797 Mon Sep 17 00:00:00 2001 From: Eric Dumazet Date: Wed, 19 May 2010 23:16:03 +0000 Subject: net: remove zap_completion_queue netpoll does an interesting work in zap_completion_queue(), but this was before we did skb orphaning before delivering packets to device. It now makes sense to add a test in dev_kfree_skb_irq() to not queue a skb if already orphaned, and to remove netpoll zap_completion_queue() as a bonus. Signed-off-by: Eric Dumazet Signed-off-by: David S. Miller --- net/core/netpoll.c | 31 ------------------------------- 1 file changed, 31 deletions(-) (limited to 'net/core/netpoll.c') diff --git a/net/core/netpoll.c b/net/core/netpoll.c index 94825b109551..e034342c819c 100644 --- a/net/core/netpoll.c +++ b/net/core/netpoll.c @@ -49,7 +49,6 @@ static atomic_t trapped; (MAX_UDP_CHUNK + sizeof(struct udphdr) + \ sizeof(struct iphdr) + sizeof(struct ethhdr)) -static void zap_completion_queue(void); static void arp_reply(struct sk_buff *skb); static unsigned int carrier_timeout = 4; @@ -197,7 +196,6 @@ void netpoll_poll_dev(struct net_device *dev) service_arp_queue(dev->npinfo); - zap_completion_queue(); } void netpoll_poll(struct netpoll *np) @@ -221,40 +219,11 @@ static void refill_skbs(void) spin_unlock_irqrestore(&skb_pool.lock, flags); } -static void zap_completion_queue(void) -{ - unsigned long flags; - struct softnet_data *sd = &get_cpu_var(softnet_data); - - if (sd->completion_queue) { - struct sk_buff *clist; - - local_irq_save(flags); - clist = sd->completion_queue; - sd->completion_queue = NULL; - local_irq_restore(flags); - - while (clist != NULL) { - struct sk_buff *skb = clist; - clist = clist->next; - if (skb->destructor) { - atomic_inc(&skb->users); - dev_kfree_skb_any(skb); /* put this one back */ - } else { - __kfree_skb(skb); - } - } - } - - put_cpu_var(softnet_data); -} - static struct sk_buff *find_skb(struct netpoll *np, int len, int reserve) { int count = 0; struct sk_buff *skb; - zap_completion_queue(); refill_skbs(); repeat: -- cgit v1.2.3 From c04ec8063d3a89e5ae933c7b4be04476fe65c6de Mon Sep 17 00:00:00 2001 From: Herbert Xu Date: Thu, 10 Jun 2010 16:12:42 +0000 Subject: netpoll: Set npinfo to NULL even with ndo_netpoll_cleanup Since we have to NULL npinfo regardless of whether there is a ndo_netpoll_cleanup, it makes sense to do this unconditionally in netpoll_cleanup rather than having every driver do it by themselves. Signed-off-by: Herbert Xu Signed-off-by: David S. Miller --- net/core/netpoll.c | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) (limited to 'net/core/netpoll.c') diff --git a/net/core/netpoll.c b/net/core/netpoll.c index e034342c819c..19ff66079f76 100644 --- a/net/core/netpoll.c +++ b/net/core/netpoll.c @@ -867,8 +867,7 @@ void netpoll_cleanup(struct netpoll *np) ops = np->dev->netdev_ops; if (ops->ndo_netpoll_cleanup) ops->ndo_netpoll_cleanup(np->dev); - else - np->dev->npinfo = NULL; + np->dev->npinfo = NULL; } } -- cgit v1.2.3 From de85d99eb7b595f6751550184b94c1e2f74a828b Mon Sep 17 00:00:00 2001 From: Herbert Xu Date: Thu, 10 Jun 2010 16:12:44 +0000 Subject: netpoll: Fix RCU usage The use of RCU in netpoll is incorrect in a number of places: 1) The initial setting is lacking a write barrier. 2) The synchronize_rcu is in the wrong place. 3) Read barriers are missing. 4) Some places are even missing rcu_read_lock. 5) npinfo is zeroed after freeing. This patch fixes those issues. As most users are in BH context, this also converts the RCU usage to the BH variant. Signed-off-by: Herbert Xu Acked-by: Paul E. McKenney Signed-off-by: David S. Miller --- net/core/netpoll.c | 20 ++++++++++++-------- 1 file changed, 12 insertions(+), 8 deletions(-) (limited to 'net/core/netpoll.c') diff --git a/net/core/netpoll.c b/net/core/netpoll.c index 19ff66079f76..e9ab4f0c454c 100644 --- a/net/core/netpoll.c +++ b/net/core/netpoll.c @@ -261,6 +261,7 @@ void netpoll_send_skb(struct netpoll *np, struct sk_buff *skb) unsigned long tries; struct net_device *dev = np->dev; const struct net_device_ops *ops = dev->netdev_ops; + /* It is up to the caller to keep npinfo alive. */ struct netpoll_info *npinfo = np->dev->npinfo; if (!npinfo || !netif_running(dev) || !netif_device_present(dev)) { @@ -810,10 +811,7 @@ int netpoll_setup(struct netpoll *np) refill_skbs(); /* last thing to do is link it to the net device structure */ - ndev->npinfo = npinfo; - - /* avoid racing with NAPI reading npinfo */ - synchronize_rcu(); + rcu_assign_pointer(ndev->npinfo, npinfo); return 0; @@ -857,6 +855,16 @@ void netpoll_cleanup(struct netpoll *np) if (atomic_dec_and_test(&npinfo->refcnt)) { const struct net_device_ops *ops; + + ops = np->dev->netdev_ops; + if (ops->ndo_netpoll_cleanup) + ops->ndo_netpoll_cleanup(np->dev); + + rcu_assign_pointer(np->dev->npinfo, NULL); + + /* avoid racing with NAPI reading npinfo */ + synchronize_rcu_bh(); + skb_queue_purge(&npinfo->arp_tx); skb_queue_purge(&npinfo->txq); cancel_rearming_delayed_work(&npinfo->tx_work); @@ -864,10 +872,6 @@ void netpoll_cleanup(struct netpoll *np) /* clean after last, unfinished work */ __skb_queue_purge(&npinfo->txq); kfree(npinfo); - ops = np->dev->netdev_ops; - if (ops->ndo_netpoll_cleanup) - ops->ndo_netpoll_cleanup(np->dev); - np->dev->npinfo = NULL; } } -- cgit v1.2.3 From dbaa154178341689faaa08fbf40b94ae5ca1d6c0 Mon Sep 17 00:00:00 2001 From: Herbert Xu Date: Thu, 10 Jun 2010 16:12:46 +0000 Subject: netpoll: Add locking for netpoll_setup/cleanup As it stands, netpoll_setup and netpoll_cleanup have no locking protection whatsoever. So chaos ensures if two entities try to perform them on the same device. This patch adds RTNL to the equation. The code has been rearranged so that bits that do not need RTNL protection are now moved to the top of netpoll_setup. Signed-off-by: Herbert Xu Signed-off-by: David S. Miller --- net/core/netpoll.c | 151 +++++++++++++++++++++++++++-------------------------- 1 file changed, 76 insertions(+), 75 deletions(-) (limited to 'net/core/netpoll.c') diff --git a/net/core/netpoll.c b/net/core/netpoll.c index e9ab4f0c454c..d10c249bcc8f 100644 --- a/net/core/netpoll.c +++ b/net/core/netpoll.c @@ -698,7 +698,6 @@ int netpoll_setup(struct netpoll *np) struct net_device *ndev = NULL; struct in_device *in_dev; struct netpoll_info *npinfo; - struct netpoll *npe, *tmp; unsigned long flags; int err; @@ -710,38 +709,6 @@ int netpoll_setup(struct netpoll *np) return -ENODEV; } - np->dev = ndev; - if (!ndev->npinfo) { - npinfo = kmalloc(sizeof(*npinfo), GFP_KERNEL); - if (!npinfo) { - err = -ENOMEM; - goto put; - } - - npinfo->rx_flags = 0; - INIT_LIST_HEAD(&npinfo->rx_np); - - spin_lock_init(&npinfo->rx_lock); - skb_queue_head_init(&npinfo->arp_tx); - skb_queue_head_init(&npinfo->txq); - INIT_DELAYED_WORK(&npinfo->tx_work, queue_process); - - atomic_set(&npinfo->refcnt, 1); - } else { - npinfo = ndev->npinfo; - atomic_inc(&npinfo->refcnt); - } - - npinfo->netpoll = np; - - if ((ndev->priv_flags & IFF_DISABLE_NETPOLL) || - !ndev->netdev_ops->ndo_poll_controller) { - printk(KERN_ERR "%s: %s doesn't support polling, aborting.\n", - np->name, np->dev_name); - err = -ENOTSUPP; - goto release; - } - if (!netif_running(ndev)) { unsigned long atmost, atleast; @@ -755,7 +722,7 @@ int netpoll_setup(struct netpoll *np) if (err) { printk(KERN_ERR "%s: failed to open %s\n", np->name, ndev->name); - goto release; + goto put; } atleast = jiffies + HZ/10; @@ -792,7 +759,7 @@ int netpoll_setup(struct netpoll *np) printk(KERN_ERR "%s: no IP address for %s, aborting\n", np->name, np->dev_name); err = -EDESTADDRREQ; - goto release; + goto put; } np->local_ip = in_dev->ifa_list->ifa_local; @@ -800,6 +767,43 @@ int netpoll_setup(struct netpoll *np) printk(KERN_INFO "%s: local IP %pI4\n", np->name, &np->local_ip); } + np->dev = ndev; + + /* fill up the skb queue */ + refill_skbs(); + + rtnl_lock(); + if ((ndev->priv_flags & IFF_DISABLE_NETPOLL) || + !ndev->netdev_ops->ndo_poll_controller) { + printk(KERN_ERR "%s: %s doesn't support polling, aborting.\n", + np->name, np->dev_name); + err = -ENOTSUPP; + goto unlock; + } + + if (!ndev->npinfo) { + npinfo = kmalloc(sizeof(*npinfo), GFP_KERNEL); + if (!npinfo) { + err = -ENOMEM; + goto unlock; + } + + npinfo->rx_flags = 0; + INIT_LIST_HEAD(&npinfo->rx_np); + + spin_lock_init(&npinfo->rx_lock); + skb_queue_head_init(&npinfo->arp_tx); + skb_queue_head_init(&npinfo->txq); + INIT_DELAYED_WORK(&npinfo->tx_work, queue_process); + + atomic_set(&npinfo->refcnt, 1); + } else { + npinfo = ndev->npinfo; + atomic_inc(&npinfo->refcnt); + } + + npinfo->netpoll = np; + if (np->rx_hook) { spin_lock_irqsave(&npinfo->rx_lock, flags); npinfo->rx_flags |= NETPOLL_RX_ENABLED; @@ -807,24 +811,14 @@ int netpoll_setup(struct netpoll *np) spin_unlock_irqrestore(&npinfo->rx_lock, flags); } - /* fill up the skb queue */ - refill_skbs(); - /* last thing to do is link it to the net device structure */ rcu_assign_pointer(ndev->npinfo, npinfo); + rtnl_unlock(); return 0; - release: - if (!ndev->npinfo) { - spin_lock_irqsave(&npinfo->rx_lock, flags); - list_for_each_entry_safe(npe, tmp, &npinfo->rx_np, rx) { - npe->dev = NULL; - } - spin_unlock_irqrestore(&npinfo->rx_lock, flags); - - kfree(npinfo); - } +unlock: + rtnl_unlock(); put: dev_put(ndev); return err; @@ -841,43 +835,50 @@ void netpoll_cleanup(struct netpoll *np) { struct netpoll_info *npinfo; unsigned long flags; + int free = 0; - if (np->dev) { - npinfo = np->dev->npinfo; - if (npinfo) { - if (!list_empty(&npinfo->rx_np)) { - spin_lock_irqsave(&npinfo->rx_lock, flags); - list_del(&np->rx); - if (list_empty(&npinfo->rx_np)) - npinfo->rx_flags &= ~NETPOLL_RX_ENABLED; - spin_unlock_irqrestore(&npinfo->rx_lock, flags); - } + if (!np->dev) + return; - if (atomic_dec_and_test(&npinfo->refcnt)) { - const struct net_device_ops *ops; + rtnl_lock(); + npinfo = np->dev->npinfo; + if (npinfo) { + if (!list_empty(&npinfo->rx_np)) { + spin_lock_irqsave(&npinfo->rx_lock, flags); + list_del(&np->rx); + if (list_empty(&npinfo->rx_np)) + npinfo->rx_flags &= ~NETPOLL_RX_ENABLED; + spin_unlock_irqrestore(&npinfo->rx_lock, flags); + } - ops = np->dev->netdev_ops; - if (ops->ndo_netpoll_cleanup) - ops->ndo_netpoll_cleanup(np->dev); + free = atomic_dec_and_test(&npinfo->refcnt); + if (free) { + const struct net_device_ops *ops; - rcu_assign_pointer(np->dev->npinfo, NULL); + ops = np->dev->netdev_ops; + if (ops->ndo_netpoll_cleanup) + ops->ndo_netpoll_cleanup(np->dev); - /* avoid racing with NAPI reading npinfo */ - synchronize_rcu_bh(); + rcu_assign_pointer(np->dev->npinfo, NULL); + } + } + rtnl_unlock(); - skb_queue_purge(&npinfo->arp_tx); - skb_queue_purge(&npinfo->txq); - cancel_rearming_delayed_work(&npinfo->tx_work); + if (free) { + /* avoid racing with NAPI reading npinfo */ + synchronize_rcu_bh(); - /* clean after last, unfinished work */ - __skb_queue_purge(&npinfo->txq); - kfree(npinfo); - } - } + skb_queue_purge(&npinfo->arp_tx); + skb_queue_purge(&npinfo->txq); + cancel_rearming_delayed_work(&npinfo->tx_work); - dev_put(np->dev); + /* clean after last, unfinished work */ + __skb_queue_purge(&npinfo->txq); + kfree(npinfo); } + dev_put(np->dev); + np->dev = NULL; } -- cgit v1.2.3 From 4247e161b12f8dffb7ee3ee07bc5e61f714ebe2d Mon Sep 17 00:00:00 2001 From: Herbert Xu Date: Thu, 10 Jun 2010 16:12:47 +0000 Subject: netpoll: Add ndo_netpoll_setup This patch adds ndo_netpoll_setup as the initialisation primitive to complement ndo_netpoll_cleanup. Signed-off-by: Herbert Xu Signed-off-by: David S. Miller --- net/core/netpoll.c | 10 ++++++++++ 1 file changed, 10 insertions(+) (limited to 'net/core/netpoll.c') diff --git a/net/core/netpoll.c b/net/core/netpoll.c index d10c249bcc8f..7de6dcad5d79 100644 --- a/net/core/netpoll.c +++ b/net/core/netpoll.c @@ -698,6 +698,7 @@ int netpoll_setup(struct netpoll *np) struct net_device *ndev = NULL; struct in_device *in_dev; struct netpoll_info *npinfo; + const struct net_device_ops *ops; unsigned long flags; int err; @@ -797,6 +798,13 @@ int netpoll_setup(struct netpoll *np) INIT_DELAYED_WORK(&npinfo->tx_work, queue_process); atomic_set(&npinfo->refcnt, 1); + + ops = np->dev->netdev_ops; + if (ops->ndo_netpoll_setup) { + err = ops->ndo_netpoll_setup(ndev, npinfo); + if (err) + goto free_npinfo; + } } else { npinfo = ndev->npinfo; atomic_inc(&npinfo->refcnt); @@ -817,6 +825,8 @@ int netpoll_setup(struct netpoll *np) return 0; +free_npinfo: + kfree(npinfo); unlock: rtnl_unlock(); put: -- cgit v1.2.3 From 8fdd95ec162a8fbac7f41d6f54f90402fe3e8cb1 Mon Sep 17 00:00:00 2001 From: Herbert Xu Date: Thu, 10 Jun 2010 16:12:48 +0000 Subject: netpoll: Allow netpoll_setup/cleanup recursion This patch adds the functions __netpoll_setup/__netpoll_cleanup which is designed to be called recursively through ndo_netpoll_seutp. They must be called with RTNL held, and the caller must initialise np->dev and ensure that it has a valid reference count. Signed-off-by: Herbert Xu Signed-off-by: David S. Miller --- net/core/netpoll.c | 176 +++++++++++++++++++++++++++++------------------------ 1 file changed, 97 insertions(+), 79 deletions(-) (limited to 'net/core/netpoll.c') diff --git a/net/core/netpoll.c b/net/core/netpoll.c index 7de6dcad5d79..560297ee55b4 100644 --- a/net/core/netpoll.c +++ b/net/core/netpoll.c @@ -693,15 +693,78 @@ int netpoll_parse_options(struct netpoll *np, char *opt) return -1; } -int netpoll_setup(struct netpoll *np) +int __netpoll_setup(struct netpoll *np) { - struct net_device *ndev = NULL; - struct in_device *in_dev; + struct net_device *ndev = np->dev; struct netpoll_info *npinfo; const struct net_device_ops *ops; unsigned long flags; int err; + if ((ndev->priv_flags & IFF_DISABLE_NETPOLL) || + !ndev->netdev_ops->ndo_poll_controller) { + printk(KERN_ERR "%s: %s doesn't support polling, aborting.\n", + np->name, np->dev_name); + err = -ENOTSUPP; + goto out; + } + + if (!ndev->npinfo) { + npinfo = kmalloc(sizeof(*npinfo), GFP_KERNEL); + if (!npinfo) { + err = -ENOMEM; + goto out; + } + + npinfo->rx_flags = 0; + INIT_LIST_HEAD(&npinfo->rx_np); + + spin_lock_init(&npinfo->rx_lock); + skb_queue_head_init(&npinfo->arp_tx); + skb_queue_head_init(&npinfo->txq); + INIT_DELAYED_WORK(&npinfo->tx_work, queue_process); + + atomic_set(&npinfo->refcnt, 1); + + ops = np->dev->netdev_ops; + if (ops->ndo_netpoll_setup) { + err = ops->ndo_netpoll_setup(ndev, npinfo); + if (err) + goto free_npinfo; + } + } else { + npinfo = ndev->npinfo; + atomic_inc(&npinfo->refcnt); + } + + npinfo->netpoll = np; + + if (np->rx_hook) { + spin_lock_irqsave(&npinfo->rx_lock, flags); + npinfo->rx_flags |= NETPOLL_RX_ENABLED; + list_add_tail(&np->rx, &npinfo->rx_np); + spin_unlock_irqrestore(&npinfo->rx_lock, flags); + } + + /* last thing to do is link it to the net device structure */ + rcu_assign_pointer(ndev->npinfo, npinfo); + rtnl_unlock(); + + return 0; + +free_npinfo: + kfree(npinfo); +out: + return err; +} +EXPORT_SYMBOL_GPL(__netpoll_setup); + +int netpoll_setup(struct netpoll *np) +{ + struct net_device *ndev = NULL; + struct in_device *in_dev; + int err; + if (np->dev_name) ndev = dev_get_by_name(&init_net, np->dev_name); if (!ndev) { @@ -774,61 +837,14 @@ int netpoll_setup(struct netpoll *np) refill_skbs(); rtnl_lock(); - if ((ndev->priv_flags & IFF_DISABLE_NETPOLL) || - !ndev->netdev_ops->ndo_poll_controller) { - printk(KERN_ERR "%s: %s doesn't support polling, aborting.\n", - np->name, np->dev_name); - err = -ENOTSUPP; - goto unlock; - } - - if (!ndev->npinfo) { - npinfo = kmalloc(sizeof(*npinfo), GFP_KERNEL); - if (!npinfo) { - err = -ENOMEM; - goto unlock; - } - - npinfo->rx_flags = 0; - INIT_LIST_HEAD(&npinfo->rx_np); - - spin_lock_init(&npinfo->rx_lock); - skb_queue_head_init(&npinfo->arp_tx); - skb_queue_head_init(&npinfo->txq); - INIT_DELAYED_WORK(&npinfo->tx_work, queue_process); - - atomic_set(&npinfo->refcnt, 1); - - ops = np->dev->netdev_ops; - if (ops->ndo_netpoll_setup) { - err = ops->ndo_netpoll_setup(ndev, npinfo); - if (err) - goto free_npinfo; - } - } else { - npinfo = ndev->npinfo; - atomic_inc(&npinfo->refcnt); - } - - npinfo->netpoll = np; - - if (np->rx_hook) { - spin_lock_irqsave(&npinfo->rx_lock, flags); - npinfo->rx_flags |= NETPOLL_RX_ENABLED; - list_add_tail(&np->rx, &npinfo->rx_np); - spin_unlock_irqrestore(&npinfo->rx_lock, flags); - } - - /* last thing to do is link it to the net device structure */ - rcu_assign_pointer(ndev->npinfo, npinfo); + err = __netpoll_setup(np); rtnl_unlock(); + if (err) + goto put; + return 0; -free_npinfo: - kfree(npinfo); -unlock: - rtnl_unlock(); put: dev_put(ndev); return err; @@ -841,40 +857,32 @@ static int __init netpoll_init(void) } core_initcall(netpoll_init); -void netpoll_cleanup(struct netpoll *np) +void __netpoll_cleanup(struct netpoll *np) { struct netpoll_info *npinfo; unsigned long flags; - int free = 0; - if (!np->dev) + npinfo = np->dev->npinfo; + if (!npinfo) return; - rtnl_lock(); - npinfo = np->dev->npinfo; - if (npinfo) { - if (!list_empty(&npinfo->rx_np)) { - spin_lock_irqsave(&npinfo->rx_lock, flags); - list_del(&np->rx); - if (list_empty(&npinfo->rx_np)) - npinfo->rx_flags &= ~NETPOLL_RX_ENABLED; - spin_unlock_irqrestore(&npinfo->rx_lock, flags); - } + if (!list_empty(&npinfo->rx_np)) { + spin_lock_irqsave(&npinfo->rx_lock, flags); + list_del(&np->rx); + if (list_empty(&npinfo->rx_np)) + npinfo->rx_flags &= ~NETPOLL_RX_ENABLED; + spin_unlock_irqrestore(&npinfo->rx_lock, flags); + } - free = atomic_dec_and_test(&npinfo->refcnt); - if (free) { - const struct net_device_ops *ops; + if (atomic_dec_and_test(&npinfo->refcnt)) { + const struct net_device_ops *ops; - ops = np->dev->netdev_ops; - if (ops->ndo_netpoll_cleanup) - ops->ndo_netpoll_cleanup(np->dev); + ops = np->dev->netdev_ops; + if (ops->ndo_netpoll_cleanup) + ops->ndo_netpoll_cleanup(np->dev); - rcu_assign_pointer(np->dev->npinfo, NULL); - } - } - rtnl_unlock(); + rcu_assign_pointer(np->dev->npinfo, NULL); - if (free) { /* avoid racing with NAPI reading npinfo */ synchronize_rcu_bh(); @@ -886,9 +894,19 @@ void netpoll_cleanup(struct netpoll *np) __skb_queue_purge(&npinfo->txq); kfree(npinfo); } +} +EXPORT_SYMBOL_GPL(__netpoll_cleanup); - dev_put(np->dev); +void netpoll_cleanup(struct netpoll *np) +{ + if (!np->dev) + return; + rtnl_lock(); + __netpoll_cleanup(np); + rtnl_unlock(); + + dev_put(np->dev); np->dev = NULL; } -- cgit v1.2.3 From deb0d7c740a008a4e26806317497549b0e8907eb Mon Sep 17 00:00:00 2001 From: Andrew Morton Date: Thu, 24 Jun 2010 20:33:04 -0700 Subject: net: fix "netpoll: Allow netpoll_setup/cleanup recursion" Remove rtnl_unlock() which had no corresponding rtnl_lock(). Signed-off-by: Andrew Morton Acked-by: Herbert Xu Signed-off-by: David S. Miller --- net/core/netpoll.c | 1 - 1 file changed, 1 deletion(-) (limited to 'net/core/netpoll.c') diff --git a/net/core/netpoll.c b/net/core/netpoll.c index 560297ee55b4..ca6dc31843ea 100644 --- a/net/core/netpoll.c +++ b/net/core/netpoll.c @@ -748,7 +748,6 @@ int __netpoll_setup(struct netpoll *np) /* last thing to do is link it to the net device structure */ rcu_assign_pointer(ndev->npinfo, npinfo); - rtnl_unlock(); return 0; -- cgit v1.2.3 From 9e34a5b51684bc90ac827ec4ba339f3892632eac Mon Sep 17 00:00:00 2001 From: Eric Dumazet Date: Fri, 9 Jul 2010 21:22:04 +0000 Subject: net/core: EXPORT_SYMBOL cleanups CodingStyle cleanups EXPORT_SYMBOL should immediately follow the symbol declaration. Signed-off-by: Eric Dumazet Signed-off-by: David S. Miller --- net/core/netpoll.c | 19 +++++++++---------- 1 file changed, 9 insertions(+), 10 deletions(-) (limited to 'net/core/netpoll.c') diff --git a/net/core/netpoll.c b/net/core/netpoll.c index ca6dc31843ea..c2b7a8bed8f6 100644 --- a/net/core/netpoll.c +++ b/net/core/netpoll.c @@ -197,11 +197,13 @@ void netpoll_poll_dev(struct net_device *dev) service_arp_queue(dev->npinfo); } +EXPORT_SYMBOL(netpoll_poll_dev); void netpoll_poll(struct netpoll *np) { netpoll_poll_dev(np->dev); } +EXPORT_SYMBOL(netpoll_poll); static void refill_skbs(void) { @@ -313,6 +315,7 @@ void netpoll_send_skb(struct netpoll *np, struct sk_buff *skb) schedule_delayed_work(&npinfo->tx_work,0); } } +EXPORT_SYMBOL(netpoll_send_skb); void netpoll_send_udp(struct netpoll *np, const char *msg, int len) { @@ -374,6 +377,7 @@ void netpoll_send_udp(struct netpoll *np, const char *msg, int len) netpoll_send_skb(np, skb); } +EXPORT_SYMBOL(netpoll_send_udp); static void arp_reply(struct sk_buff *skb) { @@ -600,6 +604,7 @@ void netpoll_print_options(struct netpoll *np) printk(KERN_INFO "%s: remote ethernet address %pM\n", np->name, np->remote_mac); } +EXPORT_SYMBOL(netpoll_print_options); int netpoll_parse_options(struct netpoll *np, char *opt) { @@ -692,6 +697,7 @@ int netpoll_parse_options(struct netpoll *np, char *opt) np->name, cur); return -1; } +EXPORT_SYMBOL(netpoll_parse_options); int __netpoll_setup(struct netpoll *np) { @@ -848,6 +854,7 @@ put: dev_put(ndev); return err; } +EXPORT_SYMBOL(netpoll_setup); static int __init netpoll_init(void) { @@ -908,11 +915,13 @@ void netpoll_cleanup(struct netpoll *np) dev_put(np->dev); np->dev = NULL; } +EXPORT_SYMBOL(netpoll_cleanup); int netpoll_trap(void) { return atomic_read(&trapped); } +EXPORT_SYMBOL(netpoll_trap); void netpoll_set_trap(int trap) { @@ -921,14 +930,4 @@ void netpoll_set_trap(int trap) else atomic_dec(&trapped); } - -EXPORT_SYMBOL(netpoll_send_skb); EXPORT_SYMBOL(netpoll_set_trap); -EXPORT_SYMBOL(netpoll_trap); -EXPORT_SYMBOL(netpoll_print_options); -EXPORT_SYMBOL(netpoll_parse_options); -EXPORT_SYMBOL(netpoll_setup); -EXPORT_SYMBOL(netpoll_cleanup); -EXPORT_SYMBOL(netpoll_send_udp); -EXPORT_SYMBOL(netpoll_poll_dev); -EXPORT_SYMBOL(netpoll_poll); -- cgit v1.2.3 From 3578b0c8abc7bdb4f02152ce5db7e09d484c6866 Mon Sep 17 00:00:00 2001 From: "David S. Miller" Date: Tue, 3 Aug 2010 00:24:04 -0700 Subject: Revert "net: remove zap_completion_queue" This reverts commit 15e83ed78864d0625e87a85f09b297c0919a4797. As explained by Johannes Berg, the optimization made here is invalid. Or, at best, incomplete. Not only destructor invocation, but conntract entry releasing must be executed outside of hw IRQ context. So just checking "skb->destructor" is insufficient. Signed-off-by: David S. Miller --- net/core/netpoll.c | 31 +++++++++++++++++++++++++++++++ 1 file changed, 31 insertions(+) (limited to 'net/core/netpoll.c') diff --git a/net/core/netpoll.c b/net/core/netpoll.c index c2b7a8bed8f6..537e01afd81b 100644 --- a/net/core/netpoll.c +++ b/net/core/netpoll.c @@ -49,6 +49,7 @@ static atomic_t trapped; (MAX_UDP_CHUNK + sizeof(struct udphdr) + \ sizeof(struct iphdr) + sizeof(struct ethhdr)) +static void zap_completion_queue(void); static void arp_reply(struct sk_buff *skb); static unsigned int carrier_timeout = 4; @@ -196,6 +197,7 @@ void netpoll_poll_dev(struct net_device *dev) service_arp_queue(dev->npinfo); + zap_completion_queue(); } EXPORT_SYMBOL(netpoll_poll_dev); @@ -221,11 +223,40 @@ static void refill_skbs(void) spin_unlock_irqrestore(&skb_pool.lock, flags); } +static void zap_completion_queue(void) +{ + unsigned long flags; + struct softnet_data *sd = &get_cpu_var(softnet_data); + + if (sd->completion_queue) { + struct sk_buff *clist; + + local_irq_save(flags); + clist = sd->completion_queue; + sd->completion_queue = NULL; + local_irq_restore(flags); + + while (clist != NULL) { + struct sk_buff *skb = clist; + clist = clist->next; + if (skb->destructor) { + atomic_inc(&skb->users); + dev_kfree_skb_any(skb); /* put this one back */ + } else { + __kfree_skb(skb); + } + } + } + + put_cpu_var(softnet_data); +} + static struct sk_buff *find_skb(struct netpoll *np, int len, int reserve) { int count = 0; struct sk_buff *skb; + zap_completion_queue(); refill_skbs(); repeat: -- cgit v1.2.3