From: Kevin Corry Some testing of DM multipath has turned up a problem with the DEVICE_WAIT command. In the tests, while performing a DEVICE_WAIT on a multipath device, the command sometimes returns immediately, even though the event-number is correct and no path-failure has occurred to trigger an event. The problem was tracked down to the call to schedule() in dev_wait(), which would return even though it was not woken up by a DM table event. This patch moves the responsibility for waiting from the ioctl interface into the core driver, and uses wait_event_interruptible() instead of relying on wait-queues and schedule(). Signed-off-by: Andrew Morton --- 25-akpm/drivers/md/dm-ioctl.c | 9 +++------ 25-akpm/drivers/md/dm.c | 37 +++++++------------------------------ 25-akpm/drivers/md/dm.h | 4 +--- 3 files changed, 11 insertions(+), 39 deletions(-) diff -puN drivers/md/dm.c~dm-ioctl-replace-dm__wait_queue-with-dm_wait_event drivers/md/dm.c --- 25/drivers/md/dm.c~dm-ioctl-replace-dm__wait_queue-with-dm_wait_event Wed May 26 15:42:42 2004 +++ 25-akpm/drivers/md/dm.c Wed May 26 15:42:42 2004 @@ -80,7 +80,7 @@ struct mapped_device { /* * Event handling. */ - uint32_t event_nr; + atomic_t event_nr; wait_queue_head_t eventq; /* @@ -700,6 +700,7 @@ static struct mapped_device *alloc_dev(u init_rwsem(&md->lock); rwlock_init(&md->map_lock); atomic_set(&md->holders, 1); + atomic_set(&md->event_nr, 0); md->queue = blk_alloc_queue(GFP_KERNEL); if (!md->queue) @@ -770,10 +771,8 @@ static void event_callback(void *context { struct mapped_device *md = (struct mapped_device *) context; - down_write(&md->lock); - md->event_nr++; + atomic_inc(&md->event_nr);; wake_up(&md->eventq); - up_write(&md->lock); } static void __set_size(struct gendisk *disk, sector_t size) @@ -1071,35 +1070,13 @@ int dm_resume(struct mapped_device *md) *---------------------------------------------------------------*/ uint32_t dm_get_event_nr(struct mapped_device *md) { - uint32_t r; - - down_read(&md->lock); - r = md->event_nr; - up_read(&md->lock); - - return r; -} - -int dm_add_wait_queue(struct mapped_device *md, wait_queue_t *wq, - uint32_t event_nr) -{ - down_write(&md->lock); - if (event_nr != md->event_nr) { - up_write(&md->lock); - return 1; - } - - add_wait_queue(&md->eventq, wq); - up_write(&md->lock); - - return 0; + return atomic_read(&md->event_nr); } -void dm_remove_wait_queue(struct mapped_device *md, wait_queue_t *wq) +int dm_wait_event(struct mapped_device *md, int event_nr) { - down_write(&md->lock); - remove_wait_queue(&md->eventq, wq); - up_write(&md->lock); + return wait_event_interruptible(md->eventq, + (event_nr != atomic_read(&md->event_nr))); } /* diff -puN drivers/md/dm.h~dm-ioctl-replace-dm__wait_queue-with-dm_wait_event drivers/md/dm.h --- 25/drivers/md/dm.h~dm-ioctl-replace-dm__wait_queue-with-dm_wait_event Wed May 26 15:42:42 2004 +++ 25-akpm/drivers/md/dm.h Wed May 26 15:42:42 2004 @@ -81,9 +81,7 @@ struct dm_table *dm_get_table(struct map * Event functions. */ uint32_t dm_get_event_nr(struct mapped_device *md); -int dm_add_wait_queue(struct mapped_device *md, wait_queue_t *wq, - uint32_t event_nr); -void dm_remove_wait_queue(struct mapped_device *md, wait_queue_t *wq); +int dm_wait_event(struct mapped_device *md, int event_nr); /* * Info functions. diff -puN drivers/md/dm-ioctl.c~dm-ioctl-replace-dm__wait_queue-with-dm_wait_event drivers/md/dm-ioctl.c --- 25/drivers/md/dm-ioctl.c~dm-ioctl-replace-dm__wait_queue-with-dm_wait_event Wed May 26 15:42:42 2004 +++ 25-akpm/drivers/md/dm-ioctl.c Wed May 26 15:42:42 2004 @@ -850,7 +850,6 @@ static int dev_wait(struct dm_ioctl *par int r; struct mapped_device *md; struct dm_table *table; - DECLARE_WAITQUEUE(wq, current); md = find_device(param); if (!md) @@ -859,12 +858,10 @@ static int dev_wait(struct dm_ioctl *par /* * Wait for a notification event */ - set_current_state(TASK_INTERRUPTIBLE); - if (!dm_add_wait_queue(md, &wq, param->event_nr)) { - schedule(); - dm_remove_wait_queue(md, &wq); + if (dm_wait_event(md, param->event_nr)) { + r = -ERESTARTSYS; + goto out; } - set_current_state(TASK_RUNNING); /* * The userland program is going to want to know what _