diff options
author | Ingo Molnar <mingo@elte.hu> | 2006-03-26 01:36:54 -0800 |
---|---|---|
committer | Linus Torvalds <torvalds@g5.osdl.org> | 2006-03-26 08:56:53 -0800 |
commit | 6dc659d813fdd1789e605d69d0b8762d284e8c60 (patch) | |
tree | 3eda05a63e1ed271d8382ca4de3315d1e1c19a24 /drivers/block/floppy.c | |
parent | 36ddf5bbdea7ba4582abc62f106f0f0e9f0b6b91 (diff) |
[PATCH] drivers/block/floppy.c: dont free_irq() from irq context
free_irq() should not be executed from softirq context.
Found by the lock validator. The fix is to push fd_free_irq() into
keventd. The code validates fine with this patch applied.
(akpm: this is revolting, but so is floppy.c)
[akpm@osdl.org: added flush_scheduled_work()]
Signed-off-by: Ingo Molnar <mingo@elte.hu>
Signed-off-by: Andrew Morton <akpm@osdl.org>
Signed-off-by: Linus Torvalds <torvalds@osdl.org>
Diffstat (limited to 'drivers/block/floppy.c')
-rw-r--r-- | drivers/block/floppy.c | 23 |
1 files changed, 22 insertions, 1 deletions
diff --git a/drivers/block/floppy.c b/drivers/block/floppy.c index 840919bba76c..d3ad9081697e 100644 --- a/drivers/block/floppy.c +++ b/drivers/block/floppy.c @@ -250,6 +250,18 @@ static int irqdma_allocated; #include <linux/cdrom.h> /* for the compatibility eject ioctl */ #include <linux/completion.h> +/* + * Interrupt freeing also means /proc VFS work - dont do it + * from interrupt context. We push this work into keventd: + */ +static void fd_free_irq_fn(void *data) +{ + fd_free_irq(); +} + +static DECLARE_WORK(fd_free_irq_work, fd_free_irq_fn, NULL); + + static struct request *current_req; static struct request_queue *floppy_queue; static void do_fd_request(request_queue_t * q); @@ -4433,6 +4445,13 @@ static int floppy_grab_irq_and_dma(void) return 0; } spin_unlock_irqrestore(&floppy_usage_lock, flags); + + /* + * We might have scheduled a free_irq(), wait it to + * drain first: + */ + flush_scheduled_work(); + if (fd_request_irq()) { DPRINT("Unable to grab IRQ%d for the floppy driver\n", FLOPPY_IRQ); @@ -4522,7 +4541,7 @@ static void floppy_release_irq_and_dma(void) if (irqdma_allocated) { fd_disable_dma(); fd_free_dma(); - fd_free_irq(); + schedule_work(&fd_free_irq_work); irqdma_allocated = 0; } set_dor(0, ~0, 8); @@ -4633,6 +4652,8 @@ void cleanup_module(void) /* eject disk, if any */ fd_eject(0); + flush_scheduled_work(); /* fd_free_irq() might be pending */ + wait_for_completion(&device_release); } |