* [patch 1/3] netpoll: set poll_owner to -1 before unlocking in netpoll_poll_unlock
2005-06-23 1:26 [patch 0/3] netpoll: support multiple netpoll clients per net_device Jeff Moyer
@ 2005-06-23 1:27 ` Jeff Moyer
2005-06-23 1:30 ` [patch 2/3] netpoll: Introduce a netpoll_info struct Jeff Moyer
` (2 subsequent siblings)
3 siblings, 0 replies; 5+ messages in thread
From: Jeff Moyer @ 2005-06-23 1:27 UTC (permalink / raw)
To: mpm, netdev, linux-kernel, akpm
Hi,
This trivial patch moves the assignment of poll_owner to -1 inside of the
lock. This fixes a potential SMP race in the code.
-Jeff
Signed-off-by: Jeff Moyer <jmoyer@redhat.com>
---
--- linux-2.6.12/include/linux/netpoll.h.orig 2005-06-22 18:47:12.917261688 -0400
+++ linux-2.6.12/include/linux/netpoll.h 2005-06-22 18:47:15.799783018 -0400
@@ -53,8 +53,8 @@ static inline void netpoll_poll_lock(str
static inline void netpoll_poll_unlock(struct net_device *dev)
{
if (dev->np) {
- spin_unlock(&dev->np->poll_lock);
dev->np->poll_owner = -1;
+ spin_unlock(&dev->np->poll_lock);
}
}
^ permalink raw reply [flat|nested] 5+ messages in thread* [patch 2/3] netpoll: Introduce a netpoll_info struct
2005-06-23 1:26 [patch 0/3] netpoll: support multiple netpoll clients per net_device Jeff Moyer
2005-06-23 1:27 ` [patch 1/3] netpoll: set poll_owner to -1 before unlocking in netpoll_poll_unlock Jeff Moyer
@ 2005-06-23 1:30 ` Jeff Moyer
2005-06-23 1:30 ` [patch 3/3] netpoll: allow multiple netpoll_clients to register against one interface Jeff Moyer
2005-06-23 5:06 ` [patch 0/3] netpoll: support multiple netpoll clients per net_device David S. Miller
3 siblings, 0 replies; 5+ messages in thread
From: Jeff Moyer @ 2005-06-23 1:30 UTC (permalink / raw)
To: mpm, netdev, linux-kernel, akpm
Hi,
This patch introduces a netpoll_info structure, which the struct net_device
will now point to instead of pointing to a struct netpoll. The reason for
this is two-fold: 1) fields such as the rx_flags, poll_owner, and poll_lock
should be maintained per net_device, not per netpoll; and 2) this is a first
step in providing support for multiple netpoll clients to register against the
same net_device.
The struct netpoll is now pointed to by the netpoll_info structure. As
such, the previous behaviour of the code is preserved.
-Jeff
Signed-off-by: Jeff Moyer <jmoyer@redhat.com>
---
--- linux-2.6.12/net/core/netpoll.c.orig 2005-06-22 20:11:06.673701253 -0400
+++ linux-2.6.12/net/core/netpoll.c 2005-06-22 20:11:45.323283581 -0400
@@ -130,19 +130,20 @@ static int checksum_udp(struct sk_buff *
*/
static void poll_napi(struct netpoll *np)
{
+ struct netpoll_info *npinfo = np->dev->npinfo;
int budget = 16;
if (test_bit(__LINK_STATE_RX_SCHED, &np->dev->state) &&
- np->poll_owner != smp_processor_id() &&
- spin_trylock(&np->poll_lock)) {
- np->rx_flags |= NETPOLL_RX_DROP;
+ npinfo->poll_owner != smp_processor_id() &&
+ spin_trylock(&npinfo->poll_lock)) {
+ npinfo->rx_flags |= NETPOLL_RX_DROP;
atomic_inc(&trapped);
np->dev->poll(np->dev, &budget);
atomic_dec(&trapped);
- np->rx_flags &= ~NETPOLL_RX_DROP;
- spin_unlock(&np->poll_lock);
+ npinfo->rx_flags &= ~NETPOLL_RX_DROP;
+ spin_unlock(&npinfo->poll_lock);
}
}
@@ -245,6 +246,7 @@ repeat:
static void netpoll_send_skb(struct netpoll *np, struct sk_buff *skb)
{
int status;
+ struct netpoll_info *npinfo;
repeat:
if(!np || !np->dev || !netif_running(np->dev)) {
@@ -253,8 +255,9 @@ repeat:
}
/* avoid recursion */
- if(np->poll_owner == smp_processor_id() ||
- np->dev->xmit_lock_owner == smp_processor_id()) {
+ npinfo = np->dev->npinfo;
+ if (npinfo->poll_owner == smp_processor_id() ||
+ np->dev->xmit_lock_owner == smp_processor_id()) {
if (np->drop)
np->drop(skb);
else
@@ -341,14 +344,18 @@ void netpoll_send_udp(struct netpoll *np
static void arp_reply(struct sk_buff *skb)
{
+ struct netpoll_info *npinfo = skb->dev->npinfo;
struct arphdr *arp;
unsigned char *arp_ptr;
int size, type = ARPOP_REPLY, ptype = ETH_P_ARP;
u32 sip, tip;
struct sk_buff *send_skb;
- struct netpoll *np = skb->dev->np;
+ struct netpoll *np = NULL;
- if (!np) return;
+ if (npinfo)
+ np = npinfo->np;
+ if (!np)
+ return;
/* No arp on this interface */
if (skb->dev->flags & IFF_NOARP)
@@ -429,7 +436,7 @@ int __netpoll_rx(struct sk_buff *skb)
int proto, len, ulen;
struct iphdr *iph;
struct udphdr *uh;
- struct netpoll *np = skb->dev->np;
+ struct netpoll *np = skb->dev->npinfo->np;
if (!np->rx_hook)
goto out;
@@ -611,9 +618,7 @@ int netpoll_setup(struct netpoll *np)
{
struct net_device *ndev = NULL;
struct in_device *in_dev;
-
- np->poll_lock = SPIN_LOCK_UNLOCKED;
- np->poll_owner = -1;
+ struct netpoll_info *npinfo;
if (np->dev_name)
ndev = dev_get_by_name(np->dev_name);
@@ -624,7 +629,16 @@ int netpoll_setup(struct netpoll *np)
}
np->dev = ndev;
- ndev->np = np;
+ if (!ndev->npinfo) {
+ npinfo = kmalloc(sizeof(*npinfo), GFP_KERNEL);
+ if (!npinfo)
+ goto release;
+
+ npinfo->np = NULL;
+ npinfo->poll_lock = SPIN_LOCK_UNLOCKED;
+ npinfo->poll_owner = -1;
+ } else
+ npinfo = ndev->npinfo;
if (!ndev->poll_controller) {
printk(KERN_ERR "%s: %s doesn't support polling, aborting.\n",
@@ -693,12 +707,15 @@ int netpoll_setup(struct netpoll *np)
}
if(np->rx_hook)
- np->rx_flags = NETPOLL_RX_ENABLED;
+ npinfo->rx_flags = NETPOLL_RX_ENABLED;
+ npinfo->np = np;
+ ndev->npinfo = npinfo;
return 0;
release:
- ndev->np = NULL;
+ if (!ndev->npinfo)
+ kfree(npinfo);
np->dev = NULL;
dev_put(ndev);
return -1;
@@ -706,9 +723,11 @@ int netpoll_setup(struct netpoll *np)
void netpoll_cleanup(struct netpoll *np)
{
- if (np->dev)
- np->dev->np = NULL;
- dev_put(np->dev);
+ if (np->dev) {
+ if (np->dev->npinfo)
+ np->dev->npinfo->np = NULL;
+ dev_put(np->dev);
+ }
np->dev = NULL;
}
--- linux-2.6.12/include/linux/netpoll.h.orig 2005-06-22 20:11:15.326264524 -0400
+++ linux-2.6.12/include/linux/netpoll.h 2005-06-22 20:11:59.119992646 -0400
@@ -16,14 +16,18 @@ struct netpoll;
struct netpoll {
struct net_device *dev;
char dev_name[16], *name;
- int rx_flags;
void (*rx_hook)(struct netpoll *, int, char *, int);
void (*drop)(struct sk_buff *skb);
u32 local_ip, remote_ip;
u16 local_port, remote_port;
unsigned char local_mac[6], remote_mac[6];
+};
+
+struct netpoll_info {
spinlock_t poll_lock;
int poll_owner;
+ int rx_flags;
+ struct netpoll *np;
};
void netpoll_poll(struct netpoll *np);
@@ -39,22 +43,27 @@ void netpoll_queue(struct sk_buff *skb);
#ifdef CONFIG_NETPOLL
static inline int netpoll_rx(struct sk_buff *skb)
{
- return skb->dev->np && skb->dev->np->rx_flags && __netpoll_rx(skb);
+ struct netpoll_info *npinfo = skb->dev->npinfo;
+
+ if (!npinfo || !npinfo->rx_flags)
+ return 0;
+
+ return npinfo->np && __netpoll_rx(skb);
}
static inline void netpoll_poll_lock(struct net_device *dev)
{
- if (dev->np) {
- spin_lock(&dev->np->poll_lock);
- dev->np->poll_owner = smp_processor_id();
+ if (dev->npinfo) {
+ spin_lock(&dev->npinfo->poll_lock);
+ dev->npinfo->poll_owner = smp_processor_id();
}
}
static inline void netpoll_poll_unlock(struct net_device *dev)
{
- if (dev->np) {
- dev->np->poll_owner = -1;
- spin_unlock(&dev->np->poll_lock);
+ if (dev->npinfo) {
+ dev->npinfo->poll_owner = -1;
+ spin_unlock(&dev->npinfo->poll_lock);
}
}
--- linux-2.6.12/include/linux/netdevice.h.orig 2005-06-22 20:11:24.277778150 -0400
+++ linux-2.6.12/include/linux/netdevice.h 2005-06-22 20:11:45.325283249 -0400
@@ -41,7 +41,7 @@
struct divert_blk;
struct vlan_group;
struct ethtool_ops;
-struct netpoll;
+struct netpoll_info;
/* source back-compat hooks */
#define SET_ETHTOOL_OPS(netdev,ops) \
( (netdev)->ethtool_ops = (ops) )
@@ -468,7 +468,7 @@ struct net_device
unsigned char *haddr);
int (*neigh_setup)(struct net_device *dev, struct neigh_parms *);
#ifdef CONFIG_NETPOLL
- struct netpoll *np;
+ struct netpoll_info *npinfo;
#endif
#ifdef CONFIG_NET_POLL_CONTROLLER
void (*poll_controller)(struct net_device *dev);
^ permalink raw reply [flat|nested] 5+ messages in thread* [patch 3/3] netpoll: allow multiple netpoll_clients to register against one interface
2005-06-23 1:26 [patch 0/3] netpoll: support multiple netpoll clients per net_device Jeff Moyer
2005-06-23 1:27 ` [patch 1/3] netpoll: set poll_owner to -1 before unlocking in netpoll_poll_unlock Jeff Moyer
2005-06-23 1:30 ` [patch 2/3] netpoll: Introduce a netpoll_info struct Jeff Moyer
@ 2005-06-23 1:30 ` Jeff Moyer
2005-06-23 5:06 ` [patch 0/3] netpoll: support multiple netpoll clients per net_device David S. Miller
3 siblings, 0 replies; 5+ messages in thread
From: Jeff Moyer @ 2005-06-23 1:30 UTC (permalink / raw)
To: mpm, netdev, linux-kernel, akpm
Hi,
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.
-Jeff
Signed-off-by: Jeff Moyer <jmoyer@redhat.com>
---
--- linux-2.6.12/net/core/netpoll.c.orig 2005-06-22 20:13:21.823259533 -0400
+++ linux-2.6.12/net/core/netpoll.c 2005-06-22 20:13:33.304353028 -0400
@@ -349,11 +349,15 @@ static void arp_reply(struct sk_buff *sk
unsigned char *arp_ptr;
int size, type = ARPOP_REPLY, ptype = ETH_P_ARP;
u32 sip, tip;
+ unsigned long flags;
struct sk_buff *send_skb;
struct netpoll *np = NULL;
- if (npinfo)
- np = npinfo->np;
+ spin_lock_irqsave(&npinfo->rx_lock, flags);
+ if (npinfo->rx_np && npinfo->rx_np->dev == skb->dev)
+ np = npinfo->rx_np;
+ spin_unlock_irqrestore(&npinfo->rx_lock, flags);
+
if (!np)
return;
@@ -436,9 +440,9 @@ int __netpoll_rx(struct sk_buff *skb)
int proto, len, ulen;
struct iphdr *iph;
struct udphdr *uh;
- struct netpoll *np = skb->dev->npinfo->np;
+ struct netpoll *np = skb->dev->npinfo->rx_np;
- if (!np->rx_hook)
+ if (!np)
goto out;
if (skb->dev->type != ARPHRD_ETHER)
goto out;
@@ -619,6 +623,7 @@ int netpoll_setup(struct netpoll *np)
struct net_device *ndev = NULL;
struct in_device *in_dev;
struct netpoll_info *npinfo;
+ unsigned long flags;
if (np->dev_name)
ndev = dev_get_by_name(np->dev_name);
@@ -634,9 +639,10 @@ int netpoll_setup(struct netpoll *np)
if (!npinfo)
goto release;
- npinfo->np = NULL;
+ npinfo->rx_np = NULL;
npinfo->poll_lock = SPIN_LOCK_UNLOCKED;
npinfo->poll_owner = -1;
+ npinfo->rx_lock = SPIN_LOCK_UNLOCKED;
} else
npinfo = ndev->npinfo;
@@ -706,9 +712,13 @@ int netpoll_setup(struct netpoll *np)
np->name, HIPQUAD(np->local_ip));
}
- if(np->rx_hook)
- npinfo->rx_flags = NETPOLL_RX_ENABLED;
- npinfo->np = np;
+ if (np->rx_hook) {
+ spin_lock_irqsave(&npinfo->rx_lock, flags);
+ npinfo->rx_flags |= NETPOLL_RX_ENABLED;
+ npinfo->rx_np = np;
+ spin_unlock_irqrestore(&npinfo->rx_lock, flags);
+ }
+ /* last thing to do is link it to the net device structure */
ndev->npinfo = npinfo;
return 0;
@@ -723,11 +733,20 @@ int netpoll_setup(struct netpoll *np)
void netpoll_cleanup(struct netpoll *np)
{
+ struct netpoll_info *npinfo;
+ unsigned long flags;
+
if (np->dev) {
- if (np->dev->npinfo)
- np->dev->npinfo->np = NULL;
+ npinfo = np->dev->npinfo;
+ if (npinfo && npinfo->rx_np == np) {
+ spin_lock_irqsave(&npinfo->rx_lock, flags);
+ npinfo->rx_np = NULL;
+ npinfo->rx_flags &= ~NETPOLL_RX_ENABLED;
+ spin_unlock_irqrestore(&npinfo->rx_lock, flags);
+ }
dev_put(np->dev);
}
+
np->dev = NULL;
}
--- linux-2.6.12/include/linux/netpoll.h.orig 2005-06-22 20:11:59.119992646 -0400
+++ linux-2.6.12/include/linux/netpoll.h 2005-06-22 20:13:33.306352696 -0400
@@ -27,7 +27,8 @@ struct netpoll_info {
spinlock_t poll_lock;
int poll_owner;
int rx_flags;
- struct netpoll *np;
+ spinlock_t rx_lock;
+ struct netpoll *rx_np; /* netpoll that registered an rx_hook */
};
void netpoll_poll(struct netpoll *np);
@@ -44,11 +45,19 @@ void netpoll_queue(struct sk_buff *skb);
static inline int netpoll_rx(struct sk_buff *skb)
{
struct netpoll_info *npinfo = skb->dev->npinfo;
+ unsigned long flags;
+ int ret = 0;
- if (!npinfo || !npinfo->rx_flags)
+ if (!npinfo || (!npinfo->rx_np && !npinfo->rx_flags))
return 0;
- return npinfo->np && __netpoll_rx(skb);
+ spin_lock_irqsave(&npinfo->rx_lock, flags);
+ /* check rx_flags again with the lock held */
+ if (npinfo->rx_flags && __netpoll_rx(skb))
+ ret = 1;
+ spin_unlock_irqrestore(&npinfo->rx_lock, flags);
+
+ return ret;
}
static inline void netpoll_poll_lock(struct net_device *dev)
^ permalink raw reply [flat|nested] 5+ messages in thread* Re: [patch 0/3] netpoll: support multiple netpoll clients per net_device
2005-06-23 1:26 [patch 0/3] netpoll: support multiple netpoll clients per net_device Jeff Moyer
` (2 preceding siblings ...)
2005-06-23 1:30 ` [patch 3/3] netpoll: allow multiple netpoll_clients to register against one interface Jeff Moyer
@ 2005-06-23 5:06 ` David S. Miller
3 siblings, 0 replies; 5+ messages in thread
From: David S. Miller @ 2005-06-23 5:06 UTC (permalink / raw)
To: jmoyer; +Cc: mpm, netdev, linux-kernel, akpm
From: Jeff Moyer <jmoyer@redhat.com>
Date: Wed, 22 Jun 2005 21:26:29 -0400
> This patch series restores the ability to register multiple netpoll clients
> against the same network interface. To this end, I created a new structure:
...
> I have tested this by registering two netpoll clients, and verifying that
> they both function properly. The clients were netconsole, and a quick
> module I hacked together to send console messages to syslog. I issued
> sysrq-h, sysrq-m, and sysrq-t's both by echo'ing to /proc/sysrq-trigger and
> by hitting the key combination on the keyboard. This verifies that the
> modules work both inside and out of interrupt context.
This all looks great. I've applied all 3 patches.
Thanks for taking care of this Jeff.
^ permalink raw reply [flat|nested] 5+ messages in thread