diff options
Diffstat (limited to 'drivers/md/dm-snap-persistent.c')
| -rw-r--r-- | drivers/md/dm-snap-persistent.c | 192 | 
1 files changed, 123 insertions, 69 deletions
diff --git a/drivers/md/dm-snap-persistent.c b/drivers/md/dm-snap-persistent.c index 2129cdb115d..d6e88178d22 100644 --- a/drivers/md/dm-snap-persistent.c +++ b/drivers/md/dm-snap-persistent.c @@ -10,12 +10,16 @@  #include <linux/mm.h>  #include <linux/pagemap.h>  #include <linux/vmalloc.h> +#include <linux/export.h>  #include <linux/slab.h>  #include <linux/dm-io.h> +#include "dm-bufio.h"  #define DM_MSG_PREFIX "persistent snapshot"  #define DM_CHUNK_SIZE_DEFAULT_SECTORS 32	/* 16KB */ +#define DM_PREFETCH_CHUNKS		12 +  /*-----------------------------------------------------------------   * Persistent snapshots, by persistent we mean that the snapshot   * will survive a reboot. @@ -58,25 +62,30 @@  #define NUM_SNAPSHOT_HDR_CHUNKS 1  struct disk_header { -	uint32_t magic; +	__le32 magic;  	/*  	 * Is this snapshot valid.  There is no way of recovering  	 * an invalid snapshot.  	 */ -	uint32_t valid; +	__le32 valid;  	/*  	 * Simple, incrementing version. no backward  	 * compatibility.  	 */ -	uint32_t version; +	__le32 version;  	/* In sectors */ -	uint32_t chunk_size; -}; +	__le32 chunk_size; +} __packed;  struct disk_exception { +	__le64 old_chunk; +	__le64 new_chunk; +} __packed; + +struct core_exception {  	uint64_t old_chunk;  	uint64_t new_chunk;  }; @@ -154,11 +163,6 @@ struct pstore {  	struct workqueue_struct *metadata_wq;  }; -static unsigned sectors_to_pages(unsigned sectors) -{ -	return DIV_ROUND_UP(sectors, PAGE_SIZE >> 9); -} -  static int alloc_area(struct pstore *ps)  {  	int r = -ENOMEM; @@ -174,10 +178,9 @@ static int alloc_area(struct pstore *ps)  	if (!ps->area)  		goto err_area; -	ps->zero_area = vmalloc(len); +	ps->zero_area = vzalloc(len);  	if (!ps->zero_area)  		goto err_zero_area; -	memset(ps->zero_area, 0, len);  	ps->header_area = vmalloc(len);  	if (!ps->header_area) @@ -257,6 +260,7 @@ static int chunk_io(struct pstore *ps, void *area, chunk_t chunk, int rw,  	INIT_WORK_ONSTACK(&req.work, do_metadata);  	queue_work(ps->metadata_wq, &req.work);  	flush_workqueue(ps->metadata_wq); +	destroy_work_on_stack(&req.work);  	return req.result;  } @@ -269,6 +273,14 @@ static chunk_t area_location(struct pstore *ps, chunk_t area)  	return NUM_SNAPSHOT_HDR_CHUNKS + ((ps->exceptions_per_area + 1) * area);  } +static void skip_metadata(struct pstore *ps) +{ +	uint32_t stride = ps->exceptions_per_area + 1; +	chunk_t next_free = ps->next_free; +	if (sector_div(next_free, stride) == NUM_SNAPSHOT_HDR_CHUNKS) +		ps->next_free++; +} +  /*   * Read or write a metadata area.  Remembering to skip the first   * chunk which holds the header. @@ -318,8 +330,7 @@ static int read_header(struct pstore *ps, int *new_snapshot)  		chunk_size_supplied = 0;  	} -	ps->io_client = dm_io_client_create(sectors_to_pages(ps->store-> -							     chunk_size)); +	ps->io_client = dm_io_client_create();  	if (IS_ERR(ps->io_client))  		return PTR_ERR(ps->io_client); @@ -368,11 +379,6 @@ static int read_header(struct pstore *ps, int *new_snapshot)  		return r;  	} -	r = dm_io_client_resize(sectors_to_pages(ps->store->chunk_size), -				ps->io_client); -	if (r) -		return r; -  	r = alloc_area(ps);  	return r; @@ -399,40 +405,41 @@ static int write_header(struct pstore *ps)  /*   * Access functions for the disk exceptions, these do the endian conversions.   */ -static struct disk_exception *get_exception(struct pstore *ps, uint32_t index) +static struct disk_exception *get_exception(struct pstore *ps, void *ps_area, +					    uint32_t index)  {  	BUG_ON(index >= ps->exceptions_per_area); -	return ((struct disk_exception *) ps->area) + index; +	return ((struct disk_exception *) ps_area) + index;  } -static void read_exception(struct pstore *ps, -			   uint32_t index, struct disk_exception *result) +static void read_exception(struct pstore *ps, void *ps_area, +			   uint32_t index, struct core_exception *result)  { -	struct disk_exception *e = get_exception(ps, index); +	struct disk_exception *de = get_exception(ps, ps_area, index);  	/* copy it */ -	result->old_chunk = le64_to_cpu(e->old_chunk); -	result->new_chunk = le64_to_cpu(e->new_chunk); +	result->old_chunk = le64_to_cpu(de->old_chunk); +	result->new_chunk = le64_to_cpu(de->new_chunk);  }  static void write_exception(struct pstore *ps, -			    uint32_t index, struct disk_exception *de) +			    uint32_t index, struct core_exception *e)  { -	struct disk_exception *e = get_exception(ps, index); +	struct disk_exception *de = get_exception(ps, ps->area, index);  	/* copy it */ -	e->old_chunk = cpu_to_le64(de->old_chunk); -	e->new_chunk = cpu_to_le64(de->new_chunk); +	de->old_chunk = cpu_to_le64(e->old_chunk); +	de->new_chunk = cpu_to_le64(e->new_chunk);  }  static void clear_exception(struct pstore *ps, uint32_t index)  { -	struct disk_exception *e = get_exception(ps, index); +	struct disk_exception *de = get_exception(ps, ps->area, index);  	/* clear it */ -	e->old_chunk = 0; -	e->new_chunk = 0; +	de->old_chunk = 0; +	de->new_chunk = 0;  }  /* @@ -440,7 +447,7 @@ static void clear_exception(struct pstore *ps, uint32_t index)   * 'full' is filled in to indicate if the area has been   * filled.   */ -static int insert_exceptions(struct pstore *ps, +static int insert_exceptions(struct pstore *ps, void *ps_area,  			     int (*callback)(void *callback_context,  					     chunk_t old, chunk_t new),  			     void *callback_context, @@ -448,13 +455,13 @@ static int insert_exceptions(struct pstore *ps,  {  	int r;  	unsigned int i; -	struct disk_exception de; +	struct core_exception e;  	/* presume the area is full */  	*full = 1;  	for (i = 0; i < ps->exceptions_per_area; i++) { -		read_exception(ps, i, &de); +		read_exception(ps, ps_area, i, &e);  		/*  		 * If the new_chunk is pointing at the start of @@ -462,7 +469,7 @@ static int insert_exceptions(struct pstore *ps,  		 * is we know that we've hit the end of the  		 * exceptions.  Therefore the area is not full.  		 */ -		if (de.new_chunk == 0LL) { +		if (e.new_chunk == 0LL) {  			ps->current_committed = i;  			*full = 0;  			break; @@ -471,13 +478,13 @@ static int insert_exceptions(struct pstore *ps,  		/*  		 * Keep track of the start of the free chunks.  		 */ -		if (ps->next_free <= de.new_chunk) -			ps->next_free = de.new_chunk + 1; +		if (ps->next_free <= e.new_chunk) +			ps->next_free = e.new_chunk + 1;  		/*  		 * Otherwise we add the exception to the snapshot.  		 */ -		r = callback(callback_context, de.old_chunk, de.new_chunk); +		r = callback(callback_context, e.old_chunk, e.new_chunk);  		if (r)  			return r;  	} @@ -491,24 +498,75 @@ static int read_exceptions(struct pstore *ps,  			   void *callback_context)  {  	int r, full = 1; +	struct dm_bufio_client *client; +	chunk_t prefetch_area = 0; + +	client = dm_bufio_client_create(dm_snap_cow(ps->store->snap)->bdev, +					ps->store->chunk_size << SECTOR_SHIFT, +					1, 0, NULL, NULL); + +	if (IS_ERR(client)) +		return PTR_ERR(client); + +	/* +	 * Setup for one current buffer + desired readahead buffers. +	 */ +	dm_bufio_set_minimum_buffers(client, 1 + DM_PREFETCH_CHUNKS);  	/*  	 * Keeping reading chunks and inserting exceptions until  	 * we find a partially full area.  	 */  	for (ps->current_area = 0; full; ps->current_area++) { -		r = area_io(ps, READ); -		if (r) -			return r; +		struct dm_buffer *bp; +		void *area; +		chunk_t chunk; + +		if (unlikely(prefetch_area < ps->current_area)) +			prefetch_area = ps->current_area; + +		if (DM_PREFETCH_CHUNKS) do { +			chunk_t pf_chunk = area_location(ps, prefetch_area); +			if (unlikely(pf_chunk >= dm_bufio_get_device_size(client))) +				break; +			dm_bufio_prefetch(client, pf_chunk, 1); +			prefetch_area++; +			if (unlikely(!prefetch_area)) +				break; +		} while (prefetch_area <= ps->current_area + DM_PREFETCH_CHUNKS); + +		chunk = area_location(ps, ps->current_area); + +		area = dm_bufio_read(client, chunk, &bp); +		if (unlikely(IS_ERR(area))) { +			r = PTR_ERR(area); +			goto ret_destroy_bufio; +		} -		r = insert_exceptions(ps, callback, callback_context, &full); -		if (r) -			return r; +		r = insert_exceptions(ps, area, callback, callback_context, +				      &full); + +		if (!full) +			memcpy(ps->area, area, ps->store->chunk_size << SECTOR_SHIFT); + +		dm_bufio_release(bp); + +		dm_bufio_forget(client, chunk); + +		if (unlikely(r)) +			goto ret_destroy_bufio;  	}  	ps->current_area--; -	return 0; +	skip_metadata(ps); + +	r = 0; + +ret_destroy_bufio: +	dm_bufio_client_destroy(client); + +	return r;  }  static struct pstore *get_info(struct dm_exception_store *store) @@ -574,7 +632,7 @@ static int persistent_read_metadata(struct dm_exception_store *store,  	ps->exceptions_per_area = (ps->store->chunk_size << SECTOR_SHIFT) /  				  sizeof(struct disk_exception);  	ps->callbacks = dm_vcalloc(ps->exceptions_per_area, -			sizeof(*ps->callbacks)); +				   sizeof(*ps->callbacks));  	if (!ps->callbacks)  		return -ENOMEM; @@ -622,8 +680,6 @@ static int persistent_prepare_exception(struct dm_exception_store *store,  					struct dm_exception *e)  {  	struct pstore *ps = get_info(store); -	uint32_t stride; -	chunk_t next_free;  	sector_t size = get_dev_size(dm_snap_cow(store->snap)->bdev);  	/* Is there enough room ? */ @@ -636,10 +692,8 @@ static int persistent_prepare_exception(struct dm_exception_store *store,  	 * Move onto the next free pending, making sure to take  	 * into account the location of the metadata chunks.  	 */ -	stride = (ps->exceptions_per_area + 1); -	next_free = ++ps->next_free; -	if (sector_div(next_free, stride) == 1) -		ps->next_free++; +	ps->next_free++; +	skip_metadata(ps);  	atomic_inc(&ps->pending_count);  	return 0; @@ -652,12 +706,12 @@ static void persistent_commit_exception(struct dm_exception_store *store,  {  	unsigned int i;  	struct pstore *ps = get_info(store); -	struct disk_exception de; +	struct core_exception ce;  	struct commit_callback *cb; -	de.old_chunk = e->old_chunk; -	de.new_chunk = e->new_chunk; -	write_exception(ps, ps->current_committed++, &de); +	ce.old_chunk = e->old_chunk; +	ce.new_chunk = e->new_chunk; +	write_exception(ps, ps->current_committed++, &ce);  	/*  	 * Add the callback to the back of the array.  This code @@ -681,7 +735,7 @@ static void persistent_commit_exception(struct dm_exception_store *store,  	 * If we completely filled the current area, then wipe the next one.  	 */  	if ((ps->current_committed == ps->exceptions_per_area) && -	     zero_disk_area(ps, ps->current_area + 1)) +	    zero_disk_area(ps, ps->current_area + 1))  		ps->valid = 0;  	/* @@ -712,7 +766,7 @@ static int persistent_prepare_merge(struct dm_exception_store *store,  				    chunk_t *last_new_chunk)  {  	struct pstore *ps = get_info(store); -	struct disk_exception de; +	struct core_exception ce;  	int nr_consecutive;  	int r; @@ -733,9 +787,9 @@ static int persistent_prepare_merge(struct dm_exception_store *store,  		ps->current_committed = ps->exceptions_per_area;  	} -	read_exception(ps, ps->current_committed - 1, &de); -	*last_old_chunk = de.old_chunk; -	*last_new_chunk = de.new_chunk; +	read_exception(ps, ps->area, ps->current_committed - 1, &ce); +	*last_old_chunk = ce.old_chunk; +	*last_new_chunk = ce.new_chunk;  	/*  	 * Find number of consecutive chunks within the current area, @@ -743,10 +797,10 @@ static int persistent_prepare_merge(struct dm_exception_store *store,  	 */  	for (nr_consecutive = 1; nr_consecutive < ps->current_committed;  	     nr_consecutive++) { -		read_exception(ps, ps->current_committed - 1 - nr_consecutive, -			       &de); -		if (de.old_chunk != *last_old_chunk - nr_consecutive || -		    de.new_chunk != *last_new_chunk - nr_consecutive) +		read_exception(ps, ps->area, +			       ps->current_committed - 1 - nr_consecutive, &ce); +		if (ce.old_chunk != *last_old_chunk - nr_consecutive || +		    ce.new_chunk != *last_new_chunk - nr_consecutive)  			break;  	} @@ -764,7 +818,7 @@ static int persistent_commit_merge(struct dm_exception_store *store,  	for (i = 0; i < nr_merged; i++)  		clear_exception(ps, ps->current_committed - 1 - i); -	r = area_io(ps, WRITE); +	r = area_io(ps, WRITE_FLUSH_FUA);  	if (r < 0)  		return r; @@ -818,7 +872,7 @@ static int persistent_ctr(struct dm_exception_store *store,  	atomic_set(&ps->pending_count, 0);  	ps->callbacks = NULL; -	ps->metadata_wq = create_singlethread_workqueue("ksnaphd"); +	ps->metadata_wq = alloc_workqueue("ksnaphd", WQ_MEM_RECLAIM, 0);  	if (!ps->metadata_wq) {  		kfree(ps);  		DMERR("couldn't start header metadata update thread");  | 
