| From 718822c1c112dc99e0c72c8968ee1db9d9d910f0 Mon Sep 17 00:00:00 2001 |
| From: Mikulas Patocka <mpatocka@redhat.com> |
| Date: Fri, 15 Nov 2013 16:12:20 -0500 |
| Subject: dm delay: fix a possible deadlock due to shared workqueue |
| |
| From: Mikulas Patocka <mpatocka@redhat.com> |
| |
| commit 718822c1c112dc99e0c72c8968ee1db9d9d910f0 upstream. |
| |
| The dm-delay target uses a shared workqueue for multiple instances. This |
| can cause deadlock if two or more dm-delay targets are stacked on the top |
| of each other. |
| |
| This patch changes dm-delay to use a per-instance workqueue. |
| |
| Signed-off-by: Mikulas Patocka <mpatocka@redhat.com> |
| Signed-off-by: Mike Snitzer <snitzer@redhat.com> |
| Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org> |
| |
| --- |
| drivers/md/dm-delay.c | 23 +++++++++++------------ |
| 1 file changed, 11 insertions(+), 12 deletions(-) |
| |
| --- a/drivers/md/dm-delay.c |
| +++ b/drivers/md/dm-delay.c |
| @@ -20,6 +20,7 @@ |
| struct delay_c { |
| struct timer_list delay_timer; |
| struct mutex timer_lock; |
| + struct workqueue_struct *kdelayd_wq; |
| struct work_struct flush_expired_bios; |
| struct list_head delayed_bios; |
| atomic_t may_delay; |
| @@ -45,14 +46,13 @@ struct dm_delay_info { |
| |
| static DEFINE_MUTEX(delayed_bios_lock); |
| |
| -static struct workqueue_struct *kdelayd_wq; |
| static struct kmem_cache *delayed_cache; |
| |
| static void handle_delayed_timer(unsigned long data) |
| { |
| struct delay_c *dc = (struct delay_c *)data; |
| |
| - queue_work(kdelayd_wq, &dc->flush_expired_bios); |
| + queue_work(dc->kdelayd_wq, &dc->flush_expired_bios); |
| } |
| |
| static void queue_timeout(struct delay_c *dc, unsigned long expires) |
| @@ -191,6 +191,12 @@ out: |
| goto bad_dev_write; |
| } |
| |
| + dc->kdelayd_wq = alloc_workqueue("kdelayd", WQ_MEM_RECLAIM, 0); |
| + if (!dc->kdelayd_wq) { |
| + DMERR("Couldn't start kdelayd"); |
| + goto bad_queue; |
| + } |
| + |
| setup_timer(&dc->delay_timer, handle_delayed_timer, (unsigned long)dc); |
| |
| INIT_WORK(&dc->flush_expired_bios, flush_expired_bios); |
| @@ -203,6 +209,8 @@ out: |
| ti->private = dc; |
| return 0; |
| |
| +bad_queue: |
| + mempool_destroy(dc->delayed_pool); |
| bad_dev_write: |
| if (dc->dev_write) |
| dm_put_device(ti, dc->dev_write); |
| @@ -217,7 +225,7 @@ static void delay_dtr(struct dm_target * |
| { |
| struct delay_c *dc = ti->private; |
| |
| - flush_workqueue(kdelayd_wq); |
| + destroy_workqueue(dc->kdelayd_wq); |
| |
| dm_put_device(ti, dc->dev_read); |
| |
| @@ -351,12 +359,6 @@ static int __init dm_delay_init(void) |
| { |
| int r = -ENOMEM; |
| |
| - kdelayd_wq = alloc_workqueue("kdelayd", WQ_MEM_RECLAIM, 0); |
| - if (!kdelayd_wq) { |
| - DMERR("Couldn't start kdelayd"); |
| - goto bad_queue; |
| - } |
| - |
| delayed_cache = KMEM_CACHE(dm_delay_info, 0); |
| if (!delayed_cache) { |
| DMERR("Couldn't create delayed bio cache."); |
| @@ -374,8 +376,6 @@ static int __init dm_delay_init(void) |
| bad_register: |
| kmem_cache_destroy(delayed_cache); |
| bad_memcache: |
| - destroy_workqueue(kdelayd_wq); |
| -bad_queue: |
| return r; |
| } |
| |
| @@ -383,7 +383,6 @@ static void __exit dm_delay_exit(void) |
| { |
| dm_unregister_target(&delay_target); |
| kmem_cache_destroy(delayed_cache); |
| - destroy_workqueue(kdelayd_wq); |
| } |
| |
| /* Module hooks */ |