dm delay: fix a possible deadlock due to shared workqueue
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. Cc: stable@vger.kernel.org # 2.6.22+ Signed-off-by: Mikulas Patocka <mpatocka@redhat.com> Signed-off-by: Mike Snitzer <snitzer@redhat.com>
This commit is contained in:
parent
7b6b2bc98c
commit
718822c1c1
@ -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 *ti)
|
||||
{
|
||||
struct delay_c *dc = ti->private;
|
||||
|
||||
flush_workqueue(kdelayd_wq);
|
||||
destroy_workqueue(dc->kdelayd_wq);
|
||||
|
||||
dm_put_device(ti, dc->dev_read);
|
||||
|
||||
@ -350,12 +358,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.");
|
||||
@ -373,8 +375,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;
|
||||
}
|
||||
|
||||
@ -382,7 +382,6 @@ static void __exit dm_delay_exit(void)
|
||||
{
|
||||
dm_unregister_target(&delay_target);
|
||||
kmem_cache_destroy(delayed_cache);
|
||||
destroy_workqueue(kdelayd_wq);
|
||||
}
|
||||
|
||||
/* Module hooks */
|
||||
|
Loading…
Reference in New Issue
Block a user