[NETPOLL]: allow multiple netpoll_clients to register against one interface

This patch provides support for registering multiple netpoll clients to the
same network device. Only one of these clients may register an rx_hook,
however. In practice, this restriction has not been problematic. It is
worth mentioning, though, that the current design can be easily extended to
allow for the registration of multiple rx_hooks.

The basic idea of the patch is that the rx_np pointer in the netpoll_info
structure points to the struct netpoll that has rx_hook filled in. Aside
from this one case, there is no need for a pointer from the struct
net_device to an individual struct netpoll.

A lock is introduced to protect the setting and clearing of the np_rx
pointer. The pointer will only be cleared upon netpoll client module
removal, and the lock should be uncontested.

Signed-off-by: Jeff Moyer <jmoyer@redhat.com>
Signed-off-by: David S. Miller <davem@davemloft.net>

authored by Jeff Moyer and committed by David S. Miller fbeec2e1 115c1d6e

+41 -13
+12 -3
include/linux/netpoll.h
··· 27 27 spinlock_t poll_lock; 28 28 int poll_owner; 29 29 int rx_flags; 30 - struct netpoll *np; 30 + spinlock_t rx_lock; 31 + struct netpoll *rx_np; /* netpoll that registered an rx_hook */ 31 32 }; 32 33 33 34 void netpoll_poll(struct netpoll *np); ··· 45 44 static inline int netpoll_rx(struct sk_buff *skb) 46 45 { 47 46 struct netpoll_info *npinfo = skb->dev->npinfo; 47 + unsigned long flags; 48 + int ret = 0; 48 49 49 - if (!npinfo || !npinfo->rx_flags) 50 + if (!npinfo || (!npinfo->rx_np && !npinfo->rx_flags)) 50 51 return 0; 51 52 52 - return npinfo->np && __netpoll_rx(skb); 53 + spin_lock_irqsave(&npinfo->rx_lock, flags); 54 + /* check rx_flags again with the lock held */ 55 + if (npinfo->rx_flags && __netpoll_rx(skb)) 56 + ret = 1; 57 + spin_unlock_irqrestore(&npinfo->rx_lock, flags); 58 + 59 + return ret; 53 60 } 54 61 55 62 static inline void netpoll_poll_lock(struct net_device *dev)
+29 -10
net/core/netpoll.c
··· 349 349 unsigned char *arp_ptr; 350 350 int size, type = ARPOP_REPLY, ptype = ETH_P_ARP; 351 351 u32 sip, tip; 352 + unsigned long flags; 352 353 struct sk_buff *send_skb; 353 354 struct netpoll *np = NULL; 354 355 355 - if (npinfo) 356 - np = npinfo->np; 356 + spin_lock_irqsave(&npinfo->rx_lock, flags); 357 + if (npinfo->rx_np && npinfo->rx_np->dev == skb->dev) 358 + np = npinfo->rx_np; 359 + spin_unlock_irqrestore(&npinfo->rx_lock, flags); 360 + 357 361 if (!np) 358 362 return; 359 363 ··· 440 436 int proto, len, ulen; 441 437 struct iphdr *iph; 442 438 struct udphdr *uh; 443 - struct netpoll *np = skb->dev->npinfo->np; 439 + struct netpoll *np = skb->dev->npinfo->rx_np; 444 440 445 - if (!np->rx_hook) 441 + if (!np) 446 442 goto out; 447 443 if (skb->dev->type != ARPHRD_ETHER) 448 444 goto out; ··· 623 619 struct net_device *ndev = NULL; 624 620 struct in_device *in_dev; 625 621 struct netpoll_info *npinfo; 622 + unsigned long flags; 626 623 627 624 if (np->dev_name) 628 625 ndev = dev_get_by_name(np->dev_name); ··· 639 634 if (!npinfo) 640 635 goto release; 641 636 642 - npinfo->np = NULL; 637 + npinfo->rx_np = NULL; 643 638 npinfo->poll_lock = SPIN_LOCK_UNLOCKED; 644 639 npinfo->poll_owner = -1; 640 + npinfo->rx_lock = SPIN_LOCK_UNLOCKED; 645 641 } else 646 642 npinfo = ndev->npinfo; 647 643 ··· 712 706 np->name, HIPQUAD(np->local_ip)); 713 707 } 714 708 715 - if(np->rx_hook) 716 - npinfo->rx_flags = NETPOLL_RX_ENABLED; 717 - npinfo->np = np; 709 + if (np->rx_hook) { 710 + spin_lock_irqsave(&npinfo->rx_lock, flags); 711 + npinfo->rx_flags |= NETPOLL_RX_ENABLED; 712 + npinfo->rx_np = np; 713 + spin_unlock_irqrestore(&npinfo->rx_lock, flags); 714 + } 715 + /* last thing to do is link it to the net device structure */ 718 716 ndev->npinfo = npinfo; 719 717 720 718 return 0; ··· 733 723 734 724 void netpoll_cleanup(struct netpoll *np) 735 725 { 726 + struct netpoll_info *npinfo; 727 + unsigned long flags; 728 + 736 729 if (np->dev) { 737 - if (np->dev->npinfo) 738 - np->dev->npinfo->np = NULL; 730 + npinfo = np->dev->npinfo; 731 + if (npinfo && npinfo->rx_np == np) { 732 + spin_lock_irqsave(&npinfo->rx_lock, flags); 733 + npinfo->rx_np = NULL; 734 + npinfo->rx_flags &= ~NETPOLL_RX_ENABLED; 735 + spin_unlock_irqrestore(&npinfo->rx_lock, flags); 736 + } 739 737 dev_put(np->dev); 740 738 } 739 + 741 740 np->dev = NULL; 742 741 } 743 742