diff options
| author | Linus Torvalds <torvalds@linux-foundation.org> | 2014-05-02 14:14:02 -0700 | 
|---|---|---|
| committer | Linus Torvalds <torvalds@linux-foundation.org> | 2014-05-02 14:14:02 -0700 | 
| commit | 54366a7fd68c955795ee4fa87a69faf5938572ea (patch) | |
| tree | f824c5058497dee164da00f2ccdb63168725be46 | |
| parent | 0845e11c2a40acab29606ebfe3aa2d23e188f0f5 (diff) | |
| parent | 131cd131a9ff63d4b84f3fe15073a2984ac30066 (diff) | |
Merge tag 'dm-3.15-fixes' of git://git.kernel.org/pub/scm/linux/kernel/git/device-mapper/linux-dm
Pull device mapper fixes from Mike Snitzer:
 "A few dm-thinp fixes for changes merged in 3.15-rc1.
  A dm-verity fix for an immutable biovec regression that affects 3.14+.
  A dm-cache fix to properly quiesce when using writethrough mode (3.14+)"
* tag 'dm-3.15-fixes' of git://git.kernel.org/pub/scm/linux/kernel/git/device-mapper/linux-dm:
  dm cache: fix writethrough mode quiescing in cache_map
  dm thin: use INIT_WORK_ONSTACK in noflush_work to avoid ODEBUG warning
  dm verity: fix biovecs hash calculation regression
  dm thin: fix rcu_read_lock being held in code that can sleep
  dm thin: irqsave must always be used with the pool->lock spinlock
| -rw-r--r-- | drivers/md/dm-cache-target.c | 1 | ||||
| -rw-r--r-- | drivers/md/dm-thin.c | 77 | ||||
| -rw-r--r-- | drivers/md/dm-verity.c | 15 | 
3 files changed, 81 insertions, 12 deletions
| diff --git a/drivers/md/dm-cache-target.c b/drivers/md/dm-cache-target.c index 1bf4a71919ec..9380be7b1895 100644 --- a/drivers/md/dm-cache-target.c +++ b/drivers/md/dm-cache-target.c @@ -2488,6 +2488,7 @@ static int cache_map(struct dm_target *ti, struct bio *bio)  		} else {  			inc_hit_counter(cache, bio); +			pb->all_io_entry = dm_deferred_entry_inc(cache->all_io_ds);  			if (bio_data_dir(bio) == WRITE && writethrough_mode(&cache->features) &&  			    !is_dirty(cache, lookup_result.cblock)) diff --git a/drivers/md/dm-thin.c b/drivers/md/dm-thin.c index 53728be84dee..13abade76ad9 100644 --- a/drivers/md/dm-thin.c +++ b/drivers/md/dm-thin.c @@ -232,6 +232,13 @@ struct thin_c {  	struct bio_list deferred_bio_list;  	struct bio_list retry_on_resume_list;  	struct rb_root sort_bio_list; /* sorted list of deferred bios */ + +	/* +	 * Ensures the thin is not destroyed until the worker has finished +	 * iterating the active_thins list. +	 */ +	atomic_t refcount; +	struct completion can_destroy;  };  /*----------------------------------------------------------------*/ @@ -1486,6 +1493,45 @@ static void process_thin_deferred_bios(struct thin_c *tc)  	blk_finish_plug(&plug);  } +static void thin_get(struct thin_c *tc); +static void thin_put(struct thin_c *tc); + +/* + * We can't hold rcu_read_lock() around code that can block.  So we + * find a thin with the rcu lock held; bump a refcount; then drop + * the lock. + */ +static struct thin_c *get_first_thin(struct pool *pool) +{ +	struct thin_c *tc = NULL; + +	rcu_read_lock(); +	if (!list_empty(&pool->active_thins)) { +		tc = list_entry_rcu(pool->active_thins.next, struct thin_c, list); +		thin_get(tc); +	} +	rcu_read_unlock(); + +	return tc; +} + +static struct thin_c *get_next_thin(struct pool *pool, struct thin_c *tc) +{ +	struct thin_c *old_tc = tc; + +	rcu_read_lock(); +	list_for_each_entry_continue_rcu(tc, &pool->active_thins, list) { +		thin_get(tc); +		thin_put(old_tc); +		rcu_read_unlock(); +		return tc; +	} +	thin_put(old_tc); +	rcu_read_unlock(); + +	return NULL; +} +  static void process_deferred_bios(struct pool *pool)  {  	unsigned long flags; @@ -1493,10 +1539,11 @@ static void process_deferred_bios(struct pool *pool)  	struct bio_list bios;  	struct thin_c *tc; -	rcu_read_lock(); -	list_for_each_entry_rcu(tc, &pool->active_thins, list) +	tc = get_first_thin(pool); +	while (tc) {  		process_thin_deferred_bios(tc); -	rcu_read_unlock(); +		tc = get_next_thin(pool, tc); +	}  	/*  	 * If there are any deferred flush bios, we must commit @@ -1578,7 +1625,7 @@ static void noflush_work(struct thin_c *tc, void (*fn)(struct work_struct *))  {  	struct noflush_work w; -	INIT_WORK(&w.worker, fn); +	INIT_WORK_ONSTACK(&w.worker, fn);  	w.tc = tc;  	atomic_set(&w.complete, 0);  	init_waitqueue_head(&w.wait); @@ -3061,11 +3108,25 @@ static struct target_type pool_target = {  /*----------------------------------------------------------------   * Thin target methods   *--------------------------------------------------------------*/ +static void thin_get(struct thin_c *tc) +{ +	atomic_inc(&tc->refcount); +} + +static void thin_put(struct thin_c *tc) +{ +	if (atomic_dec_and_test(&tc->refcount)) +		complete(&tc->can_destroy); +} +  static void thin_dtr(struct dm_target *ti)  {  	struct thin_c *tc = ti->private;  	unsigned long flags; +	thin_put(tc); +	wait_for_completion(&tc->can_destroy); +  	spin_lock_irqsave(&tc->pool->lock, flags);  	list_del_rcu(&tc->list);  	spin_unlock_irqrestore(&tc->pool->lock, flags); @@ -3101,6 +3162,7 @@ static int thin_ctr(struct dm_target *ti, unsigned argc, char **argv)  	struct thin_c *tc;  	struct dm_dev *pool_dev, *origin_dev;  	struct mapped_device *pool_md; +	unsigned long flags;  	mutex_lock(&dm_thin_pool_table.mutex); @@ -3191,9 +3253,12 @@ static int thin_ctr(struct dm_target *ti, unsigned argc, char **argv)  	mutex_unlock(&dm_thin_pool_table.mutex); -	spin_lock(&tc->pool->lock); +	atomic_set(&tc->refcount, 1); +	init_completion(&tc->can_destroy); + +	spin_lock_irqsave(&tc->pool->lock, flags);  	list_add_tail_rcu(&tc->list, &tc->pool->active_thins); -	spin_unlock(&tc->pool->lock); +	spin_unlock_irqrestore(&tc->pool->lock, flags);  	/*  	 * This synchronize_rcu() call is needed here otherwise we risk a  	 * wake_worker() call finding no bios to process (because the newly diff --git a/drivers/md/dm-verity.c b/drivers/md/dm-verity.c index 796007a5e0e1..7a7bab8947ae 100644 --- a/drivers/md/dm-verity.c +++ b/drivers/md/dm-verity.c @@ -330,15 +330,17 @@ test_block_hash:  				return r;  			}  		} -  		todo = 1 << v->data_dev_block_bits; -		while (io->iter.bi_size) { +		do {  			u8 *page; +			unsigned len;  			struct bio_vec bv = bio_iter_iovec(bio, io->iter);  			page = kmap_atomic(bv.bv_page); -			r = crypto_shash_update(desc, page + bv.bv_offset, -						bv.bv_len); +			len = bv.bv_len; +			if (likely(len >= todo)) +				len = todo; +			r = crypto_shash_update(desc, page + bv.bv_offset, len);  			kunmap_atomic(page);  			if (r < 0) { @@ -346,8 +348,9 @@ test_block_hash:  				return r;  			} -			bio_advance_iter(bio, &io->iter, bv.bv_len); -		} +			bio_advance_iter(bio, &io->iter, len); +			todo -= len; +		} while (todo);  		if (!v->version) {  			r = crypto_shash_update(desc, v->salt, v->salt_size); | 
