Commit 67b9d76f authored by Linus Torvalds's avatar Linus Torvalds

Merge tag 'dm-3.16-fixes' of git://git.kernel.org/pub/scm/linux/kernel/git/device-mapper/linux-dm

Pull device mapper fixes from Mike Snitzer:

 - Fix DM multipath IO hang regression from 3.15 due to logic bug in
   multipath_busy.  This impacted cable-pull testing and also the
   ability to boot with IPR SCSI on a POWER8 box.

 - Fix possible deadlock with deferred device removal by using a new
   dedicated workqueue rather than using the system workqueue.

 - Fix NULL pointer crash due to race condition in dm-io's wake up code
   for sync_io by using a completion.

 - Update dm-crypt and dm-zero author name following legal name change;
   this is important to Jana so I didn't see any reason to hold it back.

* tag 'dm-3.16-fixes' of git://git.kernel.org/pub/scm/linux/kernel/git/device-mapper/linux-dm:
  dm mpath: fix IO hang due to logic bug in multipath_busy
  dm io: fix a race condition in the wake up code for sync_io
  dm crypt, dm zero: update author name following legal name change
  dm: allocate a special workqueue for deferred device removal
parents c7c3ae25 7a7a3b45
/* /*
* Copyright (C) 2003 Christophe Saout <christophe@saout.de> * Copyright (C) 2003 Jana Saout <jana@saout.de>
* Copyright (C) 2004 Clemens Fruhwirth <clemens@endorphin.org> * Copyright (C) 2004 Clemens Fruhwirth <clemens@endorphin.org>
* Copyright (C) 2006-2009 Red Hat, Inc. All rights reserved. * Copyright (C) 2006-2009 Red Hat, Inc. All rights reserved.
* Copyright (C) 2013 Milan Broz <gmazyland@gmail.com> * Copyright (C) 2013 Milan Broz <gmazyland@gmail.com>
...@@ -1996,6 +1996,6 @@ static void __exit dm_crypt_exit(void) ...@@ -1996,6 +1996,6 @@ static void __exit dm_crypt_exit(void)
module_init(dm_crypt_init); module_init(dm_crypt_init);
module_exit(dm_crypt_exit); module_exit(dm_crypt_exit);
MODULE_AUTHOR("Christophe Saout <christophe@saout.de>"); MODULE_AUTHOR("Jana Saout <jana@saout.de>");
MODULE_DESCRIPTION(DM_NAME " target for transparent encryption / decryption"); MODULE_DESCRIPTION(DM_NAME " target for transparent encryption / decryption");
MODULE_LICENSE("GPL"); MODULE_LICENSE("GPL");
...@@ -10,6 +10,7 @@ ...@@ -10,6 +10,7 @@
#include <linux/device-mapper.h> #include <linux/device-mapper.h>
#include <linux/bio.h> #include <linux/bio.h>
#include <linux/completion.h>
#include <linux/mempool.h> #include <linux/mempool.h>
#include <linux/module.h> #include <linux/module.h>
#include <linux/sched.h> #include <linux/sched.h>
...@@ -32,7 +33,7 @@ struct dm_io_client { ...@@ -32,7 +33,7 @@ struct dm_io_client {
struct io { struct io {
unsigned long error_bits; unsigned long error_bits;
atomic_t count; atomic_t count;
struct task_struct *sleeper; struct completion *wait;
struct dm_io_client *client; struct dm_io_client *client;
io_notify_fn callback; io_notify_fn callback;
void *context; void *context;
...@@ -121,8 +122,8 @@ static void dec_count(struct io *io, unsigned int region, int error) ...@@ -121,8 +122,8 @@ static void dec_count(struct io *io, unsigned int region, int error)
invalidate_kernel_vmap_range(io->vma_invalidate_address, invalidate_kernel_vmap_range(io->vma_invalidate_address,
io->vma_invalidate_size); io->vma_invalidate_size);
if (io->sleeper) if (io->wait)
wake_up_process(io->sleeper); complete(io->wait);
else { else {
unsigned long r = io->error_bits; unsigned long r = io->error_bits;
...@@ -387,6 +388,7 @@ static int sync_io(struct dm_io_client *client, unsigned int num_regions, ...@@ -387,6 +388,7 @@ static int sync_io(struct dm_io_client *client, unsigned int num_regions,
*/ */
volatile char io_[sizeof(struct io) + __alignof__(struct io) - 1]; volatile char io_[sizeof(struct io) + __alignof__(struct io) - 1];
struct io *io = (struct io *)PTR_ALIGN(&io_, __alignof__(struct io)); struct io *io = (struct io *)PTR_ALIGN(&io_, __alignof__(struct io));
DECLARE_COMPLETION_ONSTACK(wait);
if (num_regions > 1 && (rw & RW_MASK) != WRITE) { if (num_regions > 1 && (rw & RW_MASK) != WRITE) {
WARN_ON(1); WARN_ON(1);
...@@ -395,7 +397,7 @@ static int sync_io(struct dm_io_client *client, unsigned int num_regions, ...@@ -395,7 +397,7 @@ static int sync_io(struct dm_io_client *client, unsigned int num_regions,
io->error_bits = 0; io->error_bits = 0;
atomic_set(&io->count, 1); /* see dispatch_io() */ atomic_set(&io->count, 1); /* see dispatch_io() */
io->sleeper = current; io->wait = &wait;
io->client = client; io->client = client;
io->vma_invalidate_address = dp->vma_invalidate_address; io->vma_invalidate_address = dp->vma_invalidate_address;
...@@ -403,15 +405,7 @@ static int sync_io(struct dm_io_client *client, unsigned int num_regions, ...@@ -403,15 +405,7 @@ static int sync_io(struct dm_io_client *client, unsigned int num_regions,
dispatch_io(rw, num_regions, where, dp, io, 1); dispatch_io(rw, num_regions, where, dp, io, 1);
while (1) { wait_for_completion_io(&wait);
set_current_state(TASK_UNINTERRUPTIBLE);
if (!atomic_read(&io->count))
break;
io_schedule();
}
set_current_state(TASK_RUNNING);
if (error_bits) if (error_bits)
*error_bits = io->error_bits; *error_bits = io->error_bits;
...@@ -434,7 +428,7 @@ static int async_io(struct dm_io_client *client, unsigned int num_regions, ...@@ -434,7 +428,7 @@ static int async_io(struct dm_io_client *client, unsigned int num_regions,
io = mempool_alloc(client->pool, GFP_NOIO); io = mempool_alloc(client->pool, GFP_NOIO);
io->error_bits = 0; io->error_bits = 0;
atomic_set(&io->count, 1); /* see dispatch_io() */ atomic_set(&io->count, 1); /* see dispatch_io() */
io->sleeper = NULL; io->wait = NULL;
io->client = client; io->client = client;
io->callback = fn; io->callback = fn;
io->context = context; io->context = context;
......
...@@ -1611,8 +1611,9 @@ static int multipath_busy(struct dm_target *ti) ...@@ -1611,8 +1611,9 @@ static int multipath_busy(struct dm_target *ti)
spin_lock_irqsave(&m->lock, flags); spin_lock_irqsave(&m->lock, flags);
/* pg_init in progress, requeue until done */ /* pg_init in progress or no paths available */
if (!pg_ready(m)) { if (m->pg_init_in_progress ||
(!m->nr_valid_paths && m->queue_if_no_path)) {
busy = 1; busy = 1;
goto out; goto out;
} }
......
/* /*
* Copyright (C) 2003 Christophe Saout <christophe@saout.de> * Copyright (C) 2003 Jana Saout <jana@saout.de>
* *
* This file is released under the GPL. * This file is released under the GPL.
*/ */
...@@ -79,6 +79,6 @@ static void __exit dm_zero_exit(void) ...@@ -79,6 +79,6 @@ static void __exit dm_zero_exit(void)
module_init(dm_zero_init) module_init(dm_zero_init)
module_exit(dm_zero_exit) module_exit(dm_zero_exit)
MODULE_AUTHOR("Christophe Saout <christophe@saout.de>"); MODULE_AUTHOR("Jana Saout <jana@saout.de>");
MODULE_DESCRIPTION(DM_NAME " dummy target returning zeros"); MODULE_DESCRIPTION(DM_NAME " dummy target returning zeros");
MODULE_LICENSE("GPL"); MODULE_LICENSE("GPL");
...@@ -54,6 +54,8 @@ static void do_deferred_remove(struct work_struct *w); ...@@ -54,6 +54,8 @@ static void do_deferred_remove(struct work_struct *w);
static DECLARE_WORK(deferred_remove_work, do_deferred_remove); static DECLARE_WORK(deferred_remove_work, do_deferred_remove);
static struct workqueue_struct *deferred_remove_workqueue;
/* /*
* For bio-based dm. * For bio-based dm.
* One of these is allocated per bio. * One of these is allocated per bio.
...@@ -276,16 +278,24 @@ static int __init local_init(void) ...@@ -276,16 +278,24 @@ static int __init local_init(void)
if (r) if (r)
goto out_free_rq_tio_cache; goto out_free_rq_tio_cache;
deferred_remove_workqueue = alloc_workqueue("kdmremove", WQ_UNBOUND, 1);
if (!deferred_remove_workqueue) {
r = -ENOMEM;
goto out_uevent_exit;
}
_major = major; _major = major;
r = register_blkdev(_major, _name); r = register_blkdev(_major, _name);
if (r < 0) if (r < 0)
goto out_uevent_exit; goto out_free_workqueue;
if (!_major) if (!_major)
_major = r; _major = r;
return 0; return 0;
out_free_workqueue:
destroy_workqueue(deferred_remove_workqueue);
out_uevent_exit: out_uevent_exit:
dm_uevent_exit(); dm_uevent_exit();
out_free_rq_tio_cache: out_free_rq_tio_cache:
...@@ -299,6 +309,7 @@ static int __init local_init(void) ...@@ -299,6 +309,7 @@ static int __init local_init(void)
static void local_exit(void) static void local_exit(void)
{ {
flush_scheduled_work(); flush_scheduled_work();
destroy_workqueue(deferred_remove_workqueue);
kmem_cache_destroy(_rq_tio_cache); kmem_cache_destroy(_rq_tio_cache);
kmem_cache_destroy(_io_cache); kmem_cache_destroy(_io_cache);
...@@ -407,7 +418,7 @@ static void dm_blk_close(struct gendisk *disk, fmode_t mode) ...@@ -407,7 +418,7 @@ static void dm_blk_close(struct gendisk *disk, fmode_t mode)
if (atomic_dec_and_test(&md->open_count) && if (atomic_dec_and_test(&md->open_count) &&
(test_bit(DMF_DEFERRED_REMOVE, &md->flags))) (test_bit(DMF_DEFERRED_REMOVE, &md->flags)))
schedule_work(&deferred_remove_work); queue_work(deferred_remove_workqueue, &deferred_remove_work);
dm_put(md); dm_put(md);
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment