|
@@ -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,
|