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