Re: [PATCH 3.18 53/67] dm snapshot: suspend merging snapshot when doing exception handover

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

 



Hello,

It seems that this commit is applied two times. Furthermore, as
indicated in the commit message, dm_internal_suspend_fast and
dm_internal_resume_fast need to be replaced by dm_internal_suspend and
dm_internal_resume instead in kernel 3.18.
Doing this trival replacements solves the compiles error.

Sincerely yours,

François Valenduc

Le 16/04/15 19:10, Sasha Levin a écrit :
> From: Mikulas Patocka <mpatocka@xxxxxxxxxx>
> 
> [ Upstream commit 09ee96b21456883e108c3b00597bb37ec512151b
> b735fede8d957d9d255e9c5cf3964cfa59799637 ]
> 
> The "dm snapshot: suspend origin when doing exception handover" commit
> fixed a exception store handover bug associated with pending exceptions
> to the "snapshot-origin" target.
> 
> However, a similar problem exists in snapshot merging.  When snapshot
> merging is in progress, we use the target "snapshot-merge" instead of
> "snapshot-origin".  Consequently, during exception store handover, we
> must find the snapshot-merge target and suspend its associated
> mapped_device.
> 
> To avoid lockdep warnings, the target must be suspended and resumed
> without holding _origins_lock.
> 
> Introduce a dm_hold() function that grabs a reference on a
> mapped_device, but unlike dm_get(), it doesn't crash if the device has
> the DMF_FREEING flag set, it returns an error in this case.
> 
> In snapshot_resume() we grab the reference to the origin device using
> dm_hold() while holding _origins_lock (_origins_lock guarantees that the
> device won't disappear).  Then we release _origins_lock, suspend the
> device and grab _origins_lock again.
> 
> NOTE to stable@ people:
> When backporting to kernels 3.18 and older, use dm_internal_suspend and
> dm_internal_resume instead of dm_internal_suspend_fast and
> dm_internal_resume_fast.
> 
> Signed-off-by: Mikulas Patocka <mpatocka@xxxxxxxxxx>
> Signed-off-by: Mike Snitzer <snitzer@xxxxxxxxxx>
> Cc: stable@xxxxxxxxxxxxxxx
> 
> dm snapshot: suspend origin when doing exception handover
> 
> [ Upstream commit 09ee96b21456883e108c3b00597bb37ec512151b
> b735fede8d957d9d255e9c5cf3964cfa59799637 ]
> 
> In the function snapshot_resume we perform exception store handover.  If
> there is another active snapshot target, the exception store is moved
> from this target to the target that is being resumed.
> 
> The problem is that if there is some pending exception, it will point to
> an incorrect exception store after that handover, causing a crash due to
> dm-snap-persistent.c:get_exception()'s BUG_ON.
> 
> This bug can be triggered by repeatedly changing snapshot permissions
> with "lvchange -p r" and "lvchange -p rw" while there are writes on the
> associated origin device.
> 
> To fix this bug, we must suspend the origin device when doing the
> exception store handover to make sure that there are no pending
> exceptions:
> - introduce _origin_hash that keeps track of dm_origin structures.
> - introduce functions __lookup_dm_origin, __insert_dm_origin and
>   __remove_dm_origin that manipulate the origin hash.
> - modify snapshot_resume so that it calls dm_internal_suspend_fast() and
>   dm_internal_resume_fast() on the origin device.
> 
> NOTE to stable@ people:
> 
> When backporting to kernels 3.12-3.18, use dm_internal_suspend and
> dm_internal_resume instead of dm_internal_suspend_fast and
> dm_internal_resume_fast.
> 
> When backporting to kernels older than 3.12, you need to pick functions
> dm_internal_suspend and dm_internal_resume from the commit
> fd2ed4d252701d3bbed4cd3e3d267ad469bb832a.
> 
> Signed-off-by: Mikulas Patocka <mpatocka@xxxxxxxxxx>
> Signed-off-by: Mike Snitzer <snitzer@xxxxxxxxxx>
> Cc: stable@xxxxxxxxxxxxxxx
> Signed-off-by: Sasha Levin <sasha.levin@xxxxxxxxxx>
> ---
>  drivers/md/dm-snap.c | 93 +++++++++++++++++++++++++++++++++++++++++++++++-----
>  drivers/md/dm.c      |  2 ++
>  2 files changed, 86 insertions(+), 9 deletions(-)
> 
> diff --git a/drivers/md/dm-snap.c b/drivers/md/dm-snap.c
> index 8b204ae2..c2bf822 100644
> --- a/drivers/md/dm-snap.c
> +++ b/drivers/md/dm-snap.c
> @@ -20,6 +20,8 @@
>  #include <linux/log2.h>
>  #include <linux/dm-kcopyd.h>
>  
> +#include "dm.h"
> +
>  #include "dm-exception-store.h"
>  
>  #define DM_MSG_PREFIX "snapshots"
> @@ -291,12 +293,23 @@ struct origin {
>  };
>  
>  /*
> + * This structure is allocated for each origin target
> + */
> +struct dm_origin {
> +	struct dm_dev *dev;
> +	struct dm_target *ti;
> +	unsigned split_boundary;
> +	struct list_head hash_list;
> +};
> +
> +/*
>   * Size of the hash table for origin volumes. If we make this
>   * the size of the minors list then it should be nearly perfect
>   */
>  #define ORIGIN_HASH_SIZE 256
>  #define ORIGIN_MASK      0xFF
>  static struct list_head *_origins;
> +static struct list_head *_dm_origins;
>  static struct rw_semaphore _origins_lock;
>  
>  static DECLARE_WAIT_QUEUE_HEAD(_pending_exceptions_done);
> @@ -310,12 +323,22 @@ static int init_origin_hash(void)
>  	_origins = kmalloc(ORIGIN_HASH_SIZE * sizeof(struct list_head),
>  			   GFP_KERNEL);
>  	if (!_origins) {
> -		DMERR("unable to allocate memory");
> +		DMERR("unable to allocate memory for _origins");
>  		return -ENOMEM;
>  	}
> -
>  	for (i = 0; i < ORIGIN_HASH_SIZE; i++)
>  		INIT_LIST_HEAD(_origins + i);
> +
> +	_dm_origins = kmalloc(ORIGIN_HASH_SIZE * sizeof(struct list_head),
> +			      GFP_KERNEL);
> +	if (!_dm_origins) {
> +		DMERR("unable to allocate memory for _dm_origins");
> +		kfree(_origins);
> +		return -ENOMEM;
> +	}
> +	for (i = 0; i < ORIGIN_HASH_SIZE; i++)
> +		INIT_LIST_HEAD(_dm_origins + i);
> +
>  	init_rwsem(&_origins_lock);
>  
>  	return 0;
> @@ -324,6 +347,7 @@ static int init_origin_hash(void)
>  static void exit_origin_hash(void)
>  {
>  	kfree(_origins);
> +	kfree(_dm_origins);
>  }
>  
>  static unsigned origin_hash(struct block_device *bdev)
> @@ -350,6 +374,30 @@ static void __insert_origin(struct origin *o)
>  	list_add_tail(&o->hash_list, sl);
>  }
>  
> +static struct dm_origin *__lookup_dm_origin(struct block_device *origin)
> +{
> +	struct list_head *ol;
> +	struct dm_origin *o;
> +
> +	ol = &_dm_origins[origin_hash(origin)];
> +	list_for_each_entry (o, ol, hash_list)
> +		if (bdev_equal(o->dev->bdev, origin))
> +			return o;
> +
> +	return NULL;
> +}
> +
> +static void __insert_dm_origin(struct dm_origin *o)
> +{
> +	struct list_head *sl = &_dm_origins[origin_hash(o->dev->bdev)];
> +	list_add_tail(&o->hash_list, sl);
> +}
> +
> +static void __remove_dm_origin(struct dm_origin *o)
> +{
> +	list_del(&o->hash_list);
> +}
> +
>  /*
>   * _origins_lock must be held when calling this function.
>   * Returns number of snapshots registered using the supplied cow device, plus:
> @@ -1841,8 +1889,20 @@ static void snapshot_resume(struct dm_target *ti)
>  {
>  	struct dm_snapshot *s = ti->private;
>  	struct dm_snapshot *snap_src = NULL, *snap_dest = NULL;
> +	struct dm_origin *o;
> +	struct mapped_device *origin_md = NULL;
>  
>  	down_read(&_origins_lock);
> +
> +	o = __lookup_dm_origin(s->origin->bdev);
> +	if (o)
> +		origin_md = dm_table_get_md(o->ti->table);
> +	if (origin_md == dm_table_get_md(ti->table))
> +		origin_md = NULL;
> +
> +	if (origin_md)
> +		dm_internal_suspend_fast(origin_md);
> +
>  	(void) __find_snapshots_sharing_cow(s, &snap_src, &snap_dest, NULL);
>  	if (snap_src && snap_dest) {
>  		down_write(&snap_src->lock);
> @@ -1851,6 +1911,10 @@ static void snapshot_resume(struct dm_target *ti)
>  		up_write(&snap_dest->lock);
>  		up_write(&snap_src->lock);
>  	}
> +
> +	if (origin_md)
> +		dm_internal_resume_fast(origin_md);
> +
>  	up_read(&_origins_lock);
>  
>  	/* Now we have correct chunk size, reregister */
> @@ -2133,11 +2197,6 @@ static int origin_write_extent(struct dm_snapshot *merging_snap,
>   * Origin: maps a linear range of a device, with hooks for snapshotting.
>   */
>  
> -struct dm_origin {
> -	struct dm_dev *dev;
> -	unsigned split_boundary;
> -};
> -
>  /*
>   * Construct an origin mapping: <dev_path>
>   * The context for an origin is merely a 'struct dm_dev *'
> @@ -2166,6 +2225,7 @@ static int origin_ctr(struct dm_target *ti, unsigned int argc, char **argv)
>  		goto bad_open;
>  	}
>  
> +	o->ti = ti;
>  	ti->private = o;
>  	ti->num_flush_bios = 1;
>  
> @@ -2180,6 +2240,7 @@ bad_alloc:
>  static void origin_dtr(struct dm_target *ti)
>  {
>  	struct dm_origin *o = ti->private;
> +
>  	dm_put_device(ti, o->dev);
>  	kfree(o);
>  }
> @@ -2216,6 +2277,19 @@ static void origin_resume(struct dm_target *ti)
>  	struct dm_origin *o = ti->private;
>  
>  	o->split_boundary = get_origin_minimum_chunksize(o->dev->bdev);
> +
> +	down_write(&_origins_lock);
> +	__insert_dm_origin(o);
> +	up_write(&_origins_lock);
> +}
> +
> +static void origin_postsuspend(struct dm_target *ti)
> +{
> +	struct dm_origin *o = ti->private;
> +
> +	down_write(&_origins_lock);
> +	__remove_dm_origin(o);
> +	up_write(&_origins_lock);
>  }
>  
>  static void origin_status(struct dm_target *ti, status_type_t type,
> @@ -2258,12 +2332,13 @@ static int origin_iterate_devices(struct dm_target *ti,
>  
>  static struct target_type origin_target = {
>  	.name    = "snapshot-origin",
> -	.version = {1, 8, 1},
> +	.version = {1, 9, 0},
>  	.module  = THIS_MODULE,
>  	.ctr     = origin_ctr,
>  	.dtr     = origin_dtr,
>  	.map     = origin_map,
>  	.resume  = origin_resume,
> +	.postsuspend = origin_postsuspend,
>  	.status  = origin_status,
>  	.merge	 = origin_merge,
>  	.iterate_devices = origin_iterate_devices,
> @@ -2271,7 +2346,7 @@ static struct target_type origin_target = {
>  
>  static struct target_type snapshot_target = {
>  	.name    = "snapshot",
> -	.version = {1, 12, 0},
> +	.version = {1, 13, 0},
>  	.module  = THIS_MODULE,
>  	.ctr     = snapshot_ctr,
>  	.dtr     = snapshot_dtr,
> diff --git a/drivers/md/dm.c b/drivers/md/dm.c
> index f743f47..83aa36f 100644
> --- a/drivers/md/dm.c
> +++ b/drivers/md/dm.c
> @@ -2887,6 +2887,7 @@ void dm_internal_suspend(struct mapped_device *md)
>  	flush_workqueue(md->wq);
>  	dm_wait_for_completion(md, TASK_UNINTERRUPTIBLE);
>  }
> +EXPORT_SYMBOL_GPL(dm_internal_suspend_fast);
>  
>  void dm_internal_resume(struct mapped_device *md)
>  {
> @@ -2898,6 +2899,7 @@ void dm_internal_resume(struct mapped_device *md)
>  done:
>  	mutex_unlock(&md->suspend_lock);
>  }
> +EXPORT_SYMBOL_GPL(dm_internal_resume_fast);
>  
>  /*-----------------------------------------------------------------
>   * Event notification.
> 

--
To unsubscribe from this list: send the line "unsubscribe stable" in
the body of a message to majordomo@xxxxxxxxxxxxxxx
More majordomo info at  http://vger.kernel.org/majordomo-info.html




[Index of Archives]     [Linux Kernel]     [Kernel Development Newbies]     [Linux USB Devel]     [Video for Linux]     [Linux Audio Users]     [Yosemite Hiking]     [Linux Kernel]     [Linux SCSI]