diff options
author | Paolo 'Blaisorblade' Giarrusso <blaisorblade@yahoo.it> | 2006-01-18 20:42:56 -0500 |
---|---|---|
committer | Linus Torvalds <torvalds@g5.osdl.org> | 2006-01-18 22:20:21 -0500 |
commit | 71c8d4c3aad3132765d30b05dce98bb8a9508f02 (patch) | |
tree | d9b21742707b5de48511663f2fcd1627fb9a5a86 | |
parent | e56a78855a4f72fc658bfd21d08939dd6e09fa4c (diff) |
[PATCH] uml: fix spinlock recursion and sleep-inside-spinlock in error path
In this error path, when the interface has had a problem, we call dev_close(),
which is disallowed for two reasons:
*) takes again the UML internal spinlock, inside the ->stop method of this
device
*) can be called in process context only, while we're in interrupt context.
I've also thought that calling dev_close() may be a wrong policy to follow,
but it's not up to me to decide that.
However, we may end up with multiple dev_close() queued on the same device.
But the initial test for (dev->flags & IFF_UP) makes this harmless, though -
and dev_close() is supposed to care about races with itself. So there's no
harm in delaying the shutdown, IMHO.
Something to mark the interface as "going to shutdown" would be appreciated,
but dev_deactivate has the same problems as dev_close(), so we can't use it
either.
Signed-off-by: Paolo 'Blaisorblade' Giarrusso <blaisorblade@yahoo.it>
Cc: Jeff Dike <jdike@addtoit.com>
Signed-off-by: Andrew Morton <akpm@osdl.org>
Signed-off-by: Linus Torvalds <torvalds@osdl.org>
-rw-r--r-- | arch/um/drivers/net_kern.c | 15 |
1 files changed, 13 insertions, 2 deletions
diff --git a/arch/um/drivers/net_kern.c b/arch/um/drivers/net_kern.c index f3442ce29a21..8ebb2241ad42 100644 --- a/arch/um/drivers/net_kern.c +++ b/arch/um/drivers/net_kern.c | |||
@@ -68,6 +68,11 @@ static int uml_net_rx(struct net_device *dev) | |||
68 | return pkt_len; | 68 | return pkt_len; |
69 | } | 69 | } |
70 | 70 | ||
71 | static void uml_dev_close(void* dev) | ||
72 | { | ||
73 | dev_close( (struct net_device *) dev); | ||
74 | } | ||
75 | |||
71 | irqreturn_t uml_net_interrupt(int irq, void *dev_id, struct pt_regs *regs) | 76 | irqreturn_t uml_net_interrupt(int irq, void *dev_id, struct pt_regs *regs) |
72 | { | 77 | { |
73 | struct net_device *dev = dev_id; | 78 | struct net_device *dev = dev_id; |
@@ -80,15 +85,21 @@ irqreturn_t uml_net_interrupt(int irq, void *dev_id, struct pt_regs *regs) | |||
80 | spin_lock(&lp->lock); | 85 | spin_lock(&lp->lock); |
81 | while((err = uml_net_rx(dev)) > 0) ; | 86 | while((err = uml_net_rx(dev)) > 0) ; |
82 | if(err < 0) { | 87 | if(err < 0) { |
88 | DECLARE_WORK(close_work, uml_dev_close, dev); | ||
83 | printk(KERN_ERR | 89 | printk(KERN_ERR |
84 | "Device '%s' read returned %d, shutting it down\n", | 90 | "Device '%s' read returned %d, shutting it down\n", |
85 | dev->name, err); | 91 | dev->name, err); |
86 | dev_close(dev); | 92 | /* dev_close can't be called in interrupt context, and takes |
93 | * again lp->lock. | ||
94 | * And dev_close() can be safely called multiple times on the | ||
95 | * same device, since it tests for (dev->flags & IFF_UP). So | ||
96 | * there's no harm in delaying the device shutdown. */ | ||
97 | schedule_work(&close_work); | ||
87 | goto out; | 98 | goto out; |
88 | } | 99 | } |
89 | reactivate_fd(lp->fd, UM_ETH_IRQ); | 100 | reactivate_fd(lp->fd, UM_ETH_IRQ); |
90 | 101 | ||
91 | out: | 102 | out: |
92 | spin_unlock(&lp->lock); | 103 | spin_unlock(&lp->lock); |
93 | return(IRQ_HANDLED); | 104 | return(IRQ_HANDLED); |
94 | } | 105 | } |