Browse Source

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

Pull device mapper fixes from Mike Snitzer:
 "Two fixes for 4.4-rc1's DM ioctl changes that introduced the potential
  for infinite recursion on ioctl (with DM multipath).

  And four stable fixes:

   - A DM thin-provisioning fix to restore 'error_if_no_space' setting
     when a thin-pool is made writable again (after having been out of
     space).

   - A DM thin-provisioning fix to properly advertise discard support
     for thin volumes that are stacked on a thin-pool whose underlying
     data device doesn't support discards.

   - A DM ioctl fix to allow ctrl-c to break out of an ioctl retry loop
     when DM multipath is configured to 'queue_if_no_path'.

   - A DM crypt fix for a possible hang on dm-crypt device removal"

* tag 'dm-4.4-fixes' of git://git.kernel.org/pub/scm/linux/kernel/git/device-mapper/linux-dm:
  dm thin: fix regression in advertised discard limits
  dm crypt: fix a possible hang due to race condition on exit
  dm mpath: fix infinite recursion in ioctl when no paths and !queue_if_no_path
  dm: do not reuse dm_blk_ioctl block_device input as local variable
  dm: fix ioctl retry termination with signal
  dm thin: restore requested 'error_if_no_space' setting on OODS to WRITE transition
Linus Torvalds 9 years ago
parent
commit
6ffeba9607
4 changed files with 36 additions and 29 deletions
  1. 13 9
      drivers/md/dm-crypt.c
  2. 16 14
      drivers/md/dm-mpath.c
  3. 3 3
      drivers/md/dm-thin.c
  4. 4 3
      drivers/md/dm.c

+ 13 - 9
drivers/md/dm-crypt.c

@@ -112,7 +112,8 @@ struct iv_tcw_private {
  * and encrypts / decrypts at the same time.
  * and encrypts / decrypts at the same time.
  */
  */
 enum flags { DM_CRYPT_SUSPENDED, DM_CRYPT_KEY_VALID,
 enum flags { DM_CRYPT_SUSPENDED, DM_CRYPT_KEY_VALID,
-	     DM_CRYPT_SAME_CPU, DM_CRYPT_NO_OFFLOAD };
+	     DM_CRYPT_SAME_CPU, DM_CRYPT_NO_OFFLOAD,
+	     DM_CRYPT_EXIT_THREAD};
 
 
 /*
 /*
  * The fields in here must be read only after initialization.
  * The fields in here must be read only after initialization.
@@ -1203,20 +1204,18 @@ continue_locked:
 		if (!RB_EMPTY_ROOT(&cc->write_tree))
 		if (!RB_EMPTY_ROOT(&cc->write_tree))
 			goto pop_from_list;
 			goto pop_from_list;
 
 
+		if (unlikely(test_bit(DM_CRYPT_EXIT_THREAD, &cc->flags))) {
+			spin_unlock_irq(&cc->write_thread_wait.lock);
+			break;
+		}
+
 		__set_current_state(TASK_INTERRUPTIBLE);
 		__set_current_state(TASK_INTERRUPTIBLE);
 		__add_wait_queue(&cc->write_thread_wait, &wait);
 		__add_wait_queue(&cc->write_thread_wait, &wait);
 
 
 		spin_unlock_irq(&cc->write_thread_wait.lock);
 		spin_unlock_irq(&cc->write_thread_wait.lock);
 
 
-		if (unlikely(kthread_should_stop())) {
-			set_task_state(current, TASK_RUNNING);
-			remove_wait_queue(&cc->write_thread_wait, &wait);
-			break;
-		}
-
 		schedule();
 		schedule();
 
 
-		set_task_state(current, TASK_RUNNING);
 		spin_lock_irq(&cc->write_thread_wait.lock);
 		spin_lock_irq(&cc->write_thread_wait.lock);
 		__remove_wait_queue(&cc->write_thread_wait, &wait);
 		__remove_wait_queue(&cc->write_thread_wait, &wait);
 		goto continue_locked;
 		goto continue_locked;
@@ -1531,8 +1530,13 @@ static void crypt_dtr(struct dm_target *ti)
 	if (!cc)
 	if (!cc)
 		return;
 		return;
 
 
-	if (cc->write_thread)
+	if (cc->write_thread) {
+		spin_lock_irq(&cc->write_thread_wait.lock);
+		set_bit(DM_CRYPT_EXIT_THREAD, &cc->flags);
+		wake_up_locked(&cc->write_thread_wait);
+		spin_unlock_irq(&cc->write_thread_wait.lock);
 		kthread_stop(cc->write_thread);
 		kthread_stop(cc->write_thread);
+	}
 
 
 	if (cc->io_queue)
 	if (cc->io_queue)
 		destroy_workqueue(cc->io_queue);
 		destroy_workqueue(cc->io_queue);

+ 16 - 14
drivers/md/dm-mpath.c

@@ -1537,32 +1537,34 @@ static int multipath_prepare_ioctl(struct dm_target *ti,
 		struct block_device **bdev, fmode_t *mode)
 		struct block_device **bdev, fmode_t *mode)
 {
 {
 	struct multipath *m = ti->private;
 	struct multipath *m = ti->private;
-	struct pgpath *pgpath;
 	unsigned long flags;
 	unsigned long flags;
 	int r;
 	int r;
 
 
-	r = 0;
-
 	spin_lock_irqsave(&m->lock, flags);
 	spin_lock_irqsave(&m->lock, flags);
 
 
 	if (!m->current_pgpath)
 	if (!m->current_pgpath)
 		__choose_pgpath(m, 0);
 		__choose_pgpath(m, 0);
 
 
-	pgpath = m->current_pgpath;
-
-	if (pgpath) {
-		*bdev = pgpath->path.dev->bdev;
-		*mode = pgpath->path.dev->mode;
+	if (m->current_pgpath) {
+		if (!m->queue_io) {
+			*bdev = m->current_pgpath->path.dev->bdev;
+			*mode = m->current_pgpath->path.dev->mode;
+			r = 0;
+		} else {
+			/* pg_init has not started or completed */
+			r = -ENOTCONN;
+		}
+	} else {
+		/* No path is available */
+		if (m->queue_if_no_path)
+			r = -ENOTCONN;
+		else
+			r = -EIO;
 	}
 	}
 
 
-	if ((pgpath && m->queue_io) || (!pgpath && m->queue_if_no_path))
-		r = -ENOTCONN;
-	else if (!*bdev)
-		r = -EIO;
-
 	spin_unlock_irqrestore(&m->lock, flags);
 	spin_unlock_irqrestore(&m->lock, flags);
 
 
-	if (r == -ENOTCONN && !fatal_signal_pending(current)) {
+	if (r == -ENOTCONN) {
 		spin_lock_irqsave(&m->lock, flags);
 		spin_lock_irqsave(&m->lock, flags);
 		if (!m->current_pg) {
 		if (!m->current_pg) {
 			/* Path status changed, redo selection */
 			/* Path status changed, redo selection */

+ 3 - 3
drivers/md/dm-thin.c

@@ -2432,6 +2432,7 @@ static void set_pool_mode(struct pool *pool, enum pool_mode new_mode)
 	case PM_WRITE:
 	case PM_WRITE:
 		if (old_mode != new_mode)
 		if (old_mode != new_mode)
 			notify_of_pool_mode_change(pool, "write");
 			notify_of_pool_mode_change(pool, "write");
+		pool->pf.error_if_no_space = pt->requested_pf.error_if_no_space;
 		dm_pool_metadata_read_write(pool->pmd);
 		dm_pool_metadata_read_write(pool->pmd);
 		pool->process_bio = process_bio;
 		pool->process_bio = process_bio;
 		pool->process_discard = process_discard_bio;
 		pool->process_discard = process_discard_bio;
@@ -4249,10 +4250,9 @@ static void thin_io_hints(struct dm_target *ti, struct queue_limits *limits)
 {
 {
 	struct thin_c *tc = ti->private;
 	struct thin_c *tc = ti->private;
 	struct pool *pool = tc->pool;
 	struct pool *pool = tc->pool;
-	struct queue_limits *pool_limits = dm_get_queue_limits(pool->pool_md);
 
 
-	if (!pool_limits->discard_granularity)
-		return; /* pool's discard support is disabled */
+	if (!pool->pf.discard_enabled)
+		return;
 
 
 	limits->discard_granularity = pool->sectors_per_block << SECTOR_SHIFT;
 	limits->discard_granularity = pool->sectors_per_block << SECTOR_SHIFT;
 	limits->max_discard_sectors = 2048 * 1024 * 16; /* 16G */
 	limits->max_discard_sectors = 2048 * 1024 * 16; /* 16G */

+ 4 - 3
drivers/md/dm.c

@@ -591,7 +591,7 @@ retry:
 
 
 out:
 out:
 	dm_put_live_table(md, *srcu_idx);
 	dm_put_live_table(md, *srcu_idx);
-	if (r == -ENOTCONN) {
+	if (r == -ENOTCONN && !fatal_signal_pending(current)) {
 		msleep(10);
 		msleep(10);
 		goto retry;
 		goto retry;
 	}
 	}
@@ -603,9 +603,10 @@ static int dm_blk_ioctl(struct block_device *bdev, fmode_t mode,
 {
 {
 	struct mapped_device *md = bdev->bd_disk->private_data;
 	struct mapped_device *md = bdev->bd_disk->private_data;
 	struct dm_target *tgt;
 	struct dm_target *tgt;
+	struct block_device *tgt_bdev = NULL;
 	int srcu_idx, r;
 	int srcu_idx, r;
 
 
-	r = dm_get_live_table_for_ioctl(md, &tgt, &bdev, &mode, &srcu_idx);
+	r = dm_get_live_table_for_ioctl(md, &tgt, &tgt_bdev, &mode, &srcu_idx);
 	if (r < 0)
 	if (r < 0)
 		return r;
 		return r;
 
 
@@ -620,7 +621,7 @@ static int dm_blk_ioctl(struct block_device *bdev, fmode_t mode,
 			goto out;
 			goto out;
 	}
 	}
 
 
-	r =  __blkdev_driver_ioctl(bdev, mode, cmd, arg);
+	r =  __blkdev_driver_ioctl(tgt_bdev, mode, cmd, arg);
 out:
 out:
 	dm_put_live_table(md, srcu_idx);
 	dm_put_live_table(md, srcu_idx);
 	return r;
 	return r;