diff options
Diffstat (limited to 'drivers/md/dm-snap.c')
| -rw-r--r-- | drivers/md/dm-snap.c | 71 | 
1 files changed, 64 insertions, 7 deletions
diff --git a/drivers/md/dm-snap.c b/drivers/md/dm-snap.c index aec57d76db5d..944690bafd93 100644 --- a/drivers/md/dm-snap.c +++ b/drivers/md/dm-snap.c @@ -66,6 +66,18 @@ struct dm_snapshot {  	atomic_t pending_exceptions_count; +	/* Protected by "lock" */ +	sector_t exception_start_sequence; + +	/* Protected by kcopyd single-threaded callback */ +	sector_t exception_complete_sequence; + +	/* +	 * A list of pending exceptions that completed out of order. +	 * Protected by kcopyd single-threaded callback. +	 */ +	struct list_head out_of_order_list; +  	mempool_t *pending_pool;  	struct dm_exception_table pending; @@ -173,6 +185,14 @@ struct dm_snap_pending_exception {  	 */  	int started; +	/* There was copying error. */ +	int copy_error; + +	/* A sequence number, it is used for in-order completion. */ +	sector_t exception_sequence; + +	struct list_head out_of_order_entry; +  	/*  	 * For writing a complete chunk, bypassing the copy.  	 */ @@ -1094,6 +1114,9 @@ static int snapshot_ctr(struct dm_target *ti, unsigned int argc, char **argv)  	s->valid = 1;  	s->active = 0;  	atomic_set(&s->pending_exceptions_count, 0); +	s->exception_start_sequence = 0; +	s->exception_complete_sequence = 0; +	INIT_LIST_HEAD(&s->out_of_order_list);  	init_rwsem(&s->lock);  	INIT_LIST_HEAD(&s->list);  	spin_lock_init(&s->pe_lock); @@ -1443,6 +1466,19 @@ static void commit_callback(void *context, int success)  	pending_complete(pe, success);  } +static void complete_exception(struct dm_snap_pending_exception *pe) +{ +	struct dm_snapshot *s = pe->snap; + +	if (unlikely(pe->copy_error)) +		pending_complete(pe, 0); + +	else +		/* Update the metadata if we are persistent */ +		s->store->type->commit_exception(s->store, &pe->e, +						 commit_callback, pe); +} +  /*   * Called when the copy I/O has finished.  kcopyd actually runs   * this code so don't block. @@ -1452,13 +1488,32 @@ static void copy_callback(int read_err, unsigned long write_err, void *context)  	struct dm_snap_pending_exception *pe = context;  	struct dm_snapshot *s = pe->snap; -	if (read_err || write_err) -		pending_complete(pe, 0); +	pe->copy_error = read_err || write_err; -	else -		/* Update the metadata if we are persistent */ -		s->store->type->commit_exception(s->store, &pe->e, -						 commit_callback, pe); +	if (pe->exception_sequence == s->exception_complete_sequence) { +		s->exception_complete_sequence++; +		complete_exception(pe); + +		while (!list_empty(&s->out_of_order_list)) { +			pe = list_entry(s->out_of_order_list.next, +					struct dm_snap_pending_exception, out_of_order_entry); +			if (pe->exception_sequence != s->exception_complete_sequence) +				break; +			s->exception_complete_sequence++; +			list_del(&pe->out_of_order_entry); +			complete_exception(pe); +		} +	} else { +		struct list_head *lh; +		struct dm_snap_pending_exception *pe2; + +		list_for_each_prev(lh, &s->out_of_order_list) { +			pe2 = list_entry(lh, struct dm_snap_pending_exception, out_of_order_entry); +			if (pe2->exception_sequence < pe->exception_sequence) +				break; +		} +		list_add(&pe->out_of_order_entry, lh); +	}  }  /* @@ -1553,6 +1608,8 @@ __find_pending_exception(struct dm_snapshot *s,  		return NULL;  	} +	pe->exception_sequence = s->exception_start_sequence++; +  	dm_insert_exception(&s->pending, &pe->e);  	return pe; @@ -2192,7 +2249,7 @@ static struct target_type origin_target = {  static struct target_type snapshot_target = {  	.name    = "snapshot", -	.version = {1, 11, 1}, +	.version = {1, 12, 0},  	.module  = THIS_MODULE,  	.ctr     = snapshot_ctr,  	.dtr     = snapshot_dtr,  | 
