[Devel] [PATCH v3 VZ9 5/5] dm-qcow2: add merge_backward progress command
Pavel Tikhomirov
ptikhomirov at virtuozzo.com
Mon Mar 3 10:24:36 MSK 2025
This allows to see progress of backward merge. It shows the stage we are
at and for iterative stages it provides progress in form of how many
iteratious are done and how many iterations there are in total.
Locking:
The progress data consistency is protected by tgt->ctl_mutex, we always
update stage and error consistently under lock. Inside iterative stages for
progress updating we have xchg instead of lock so that changes to progress
are atomic and imply memory barrier (this way we would not see progress
greater than max_progress in progress reporting), but at the same time
there is less contention on tgt->ctl_mutex.
https://virtuozzo.atlassian.net/browse/VSTOR-100466
Signed-off-by: Pavel Tikhomirov <ptikhomirov at virtuozzo.com>
--
v3: Adress Kostya's review comments: move progress printing out of lock,
remove excess updates of max_progress, make progress updates without
lock.
---
drivers/md/dm-qcow2-cmd.c | 83 +++++++++++++++++++++++++++++++++++++++
drivers/md/dm-qcow2.h | 14 +++++++
2 files changed, 97 insertions(+)
diff --git a/drivers/md/dm-qcow2-cmd.c b/drivers/md/dm-qcow2-cmd.c
index 7f9c582778d5f..b9d37e78b7577 100644
--- a/drivers/md/dm-qcow2-cmd.c
+++ b/drivers/md/dm-qcow2-cmd.c
@@ -54,6 +54,10 @@ static void service_qio_endio(struct qcow2_target *tgt, struct qio *qio,
wake_up(&tgt->service_wq);
}
+static void backward_merge_update_progress(struct qcow2_target *tgt,
+ long long progress);
+static void backward_merge_update_max_progress(struct qcow2_target *tgt,
+ long long max_progress);
static bool qcow2_backward_merge_should_stop(struct qcow2_target *tgt);
static int qcow2_service_iter(struct qcow2_target *tgt, struct qcow2 *qcow2,
@@ -66,7 +70,10 @@ static int qcow2_service_iter(struct qcow2_target *tgt, struct qcow2 *qcow2,
WRITE_ONCE(service_status, BLK_STS_OK);
+ backward_merge_update_max_progress(tgt, end);
for (pos = 0; pos < end; pos += step) {
+ backward_merge_update_progress(tgt, pos);
+
if (qcow2_backward_merge_should_stop(tgt)) {
ret = -EINTR;
break;
@@ -165,6 +172,66 @@ static void set_backward_merge_in_process(struct qcow2_target *tgt,
qcow2_submit_embedded_qios(tgt, &list);
}
+static void __backward_merge_update_stage(struct qcow2_target *tgt,
+ enum qcow2_backward_merge_stage stage)
+{
+ tgt->backward_merge.stage = stage;
+ tgt->backward_merge.progress = 0;
+ tgt->backward_merge.max_progress = 0;
+}
+
+static void backward_merge_update_stage(struct qcow2_target *tgt,
+ enum qcow2_backward_merge_stage stage)
+{
+ mutex_lock(&tgt->ctl_mutex);
+ __backward_merge_update_stage(tgt, stage);
+ mutex_unlock(&tgt->ctl_mutex);
+}
+
+static void backward_merge_update_max_progress(struct qcow2_target *tgt,
+ long long max_progress)
+{
+ xchg(&tgt->backward_merge.max_progress, max_progress);
+}
+
+static void backward_merge_update_progress(struct qcow2_target *tgt,
+ long long progress)
+{
+ xchg(&tgt->backward_merge.progress, progress);
+}
+
+char *backward_merge_stage_names[] = {
+ "none",
+ "break_l1cow",
+ "set_dirty",
+ "running",
+ "waiting_completion",
+ "completing",
+ "fail",
+};
+
+static int qcow2_merge_backward_progress(struct qcow2_target *tgt,
+ char *result, unsigned int maxlen)
+{
+ struct qcow2_backward_merge backward_merge;
+ unsigned int sz = 0;
+ int ret;
+
+ BUILD_BUG_ON(ARRAY_SIZE(backward_merge_stage_names) != BACKWARD_MERGE_STAGE_MAX);
+
+ mutex_lock(&tgt->ctl_mutex);
+ backward_merge = tgt->backward_merge;
+ mutex_unlock(&tgt->ctl_mutex);
+
+ ret = DMEMIT("stage=%s\nprogress=%lld\nmax_progress=%lld\nerror=%d\n",
+ backward_merge_stage_names[backward_merge.stage],
+ backward_merge.progress,
+ backward_merge.max_progress,
+ backward_merge.error);
+
+ return ret ? 1 : 0;
+}
+
static int qcow2_merge_backward_start(struct qcow2_target *tgt)
{
struct qcow2 *qcow2 = tgt->top, *lower = qcow2->lower;
@@ -205,6 +272,7 @@ void qcow2_merge_backward_work(struct work_struct *work)
return;
}
tgt->backward_merge.state = BACKWARD_MERGE_RUN;
+ __backward_merge_update_stage(tgt, BACKWARD_MERGE_STAGE_BREAK_L1COW);
mutex_unlock(&tgt->ctl_mutex);
qcow2 = tgt->top;
@@ -222,6 +290,7 @@ void qcow2_merge_backward_work(struct work_struct *work)
goto out_err;
}
+ backward_merge_update_stage(tgt, BACKWARD_MERGE_STAGE_SET_DIRTY);
ret = qcow2_set_image_file_features(lower, true);
if (ret) {
QC_ERR(tgt->ti, "Can't set dirty bit");
@@ -230,6 +299,7 @@ void qcow2_merge_backward_work(struct work_struct *work)
set_backward_merge_in_process(tgt, qcow2, true);
/* Start merge */
+ backward_merge_update_stage(tgt, BACKWARD_MERGE_STAGE_RUNNING);
ret = qcow2_merge_common(tgt);
if (ret) {
set_backward_merge_in_process(tgt, qcow2, false);
@@ -244,14 +314,17 @@ void qcow2_merge_backward_work(struct work_struct *work)
/* Error */
tgt->backward_merge.state = BACKWARD_MERGE_STOPPED;
tgt->backward_merge.error = ret;
+ __backward_merge_update_stage(tgt, BACKWARD_MERGE_STAGE_FAIL);
} else if (tgt->backward_merge.state == BACKWARD_MERGE_STOP) {
/* Merge is canceled */
set_backward_merge_in_process(tgt, qcow2, false);
tgt->backward_merge.state = BACKWARD_MERGE_STOPPED;
tgt->backward_merge.error = -EINTR;
+ __backward_merge_update_stage(tgt, BACKWARD_MERGE_STAGE_FAIL);
} else {
/* Finish merge */
tgt->backward_merge.state = BACKWARD_MERGE_WAIT_COMPLETION;
+ __backward_merge_update_stage(tgt, BACKWARD_MERGE_STAGE_WAITING_COMPLETION);
}
if (tgt->backward_merge.eventfd_ctx)
eventfd_signal(tgt->backward_merge.eventfd_ctx, 1);
@@ -267,6 +340,7 @@ static int qcow2_merge_backward_complete(struct qcow2_target *tgt)
if (tgt->backward_merge.state != BACKWARD_MERGE_WAIT_COMPLETION)
return -EBUSY;
+ __backward_merge_update_stage(tgt, BACKWARD_MERGE_STAGE_COMPLETING);
tgt->top = lower;
smp_wmb(); /* Pairs with qcow2_ref_inc() */
@@ -280,6 +354,7 @@ static int qcow2_merge_backward_complete(struct qcow2_target *tgt)
qcow2_destroy(qcow2);
tgt->backward_merge.state = BACKWARD_MERGE_STOPPED;
+ __backward_merge_update_stage(tgt, BACKWARD_MERGE_STAGE_NONE);
return 0;
}
@@ -306,6 +381,7 @@ void qcow2_merge_backward_cancel(struct qcow2_target *tgt)
} else if (tgt->backward_merge.state == BACKWARD_MERGE_WAIT_COMPLETION) {
set_backward_merge_in_process(tgt, tgt->top, false);
tgt->backward_merge.state = BACKWARD_MERGE_STOPPED;
+ __backward_merge_update_stage(tgt, BACKWARD_MERGE_STAGE_NONE);
}
mutex_unlock(&tgt->ctl_mutex);
@@ -515,6 +591,13 @@ int qcow2_message(struct dm_target *ti, unsigned int argc, char **argv,
}
ret = qcow2_merge_backward_set_eventfd(tgt, efd);
goto out;
+ } else if (!strcmp(argv[1], "progress")) {
+ if (argc != 2) {
+ ret = -EINVAL;
+ goto out;
+ }
+ ret = qcow2_merge_backward_progress(tgt, result, maxlen);
+ goto out;
}
}
diff --git a/drivers/md/dm-qcow2.h b/drivers/md/dm-qcow2.h
index c4956e3fd0eb7..ed7cf79348052 100644
--- a/drivers/md/dm-qcow2.h
+++ b/drivers/md/dm-qcow2.h
@@ -158,11 +158,25 @@ enum qcow2_backward_merge_state {
BACKWARD_MERGE_STOP,
};
+enum qcow2_backward_merge_stage {
+ BACKWARD_MERGE_STAGE_NONE = 0,
+ BACKWARD_MERGE_STAGE_BREAK_L1COW,
+ BACKWARD_MERGE_STAGE_SET_DIRTY,
+ BACKWARD_MERGE_STAGE_RUNNING,
+ BACKWARD_MERGE_STAGE_WAITING_COMPLETION,
+ BACKWARD_MERGE_STAGE_COMPLETING,
+ BACKWARD_MERGE_STAGE_FAIL,
+ BACKWARD_MERGE_STAGE_MAX,
+};
+
struct qcow2_backward_merge {
struct work_struct work;
enum qcow2_backward_merge_state state;
int error;
struct eventfd_ctx *eventfd_ctx;
+ enum qcow2_backward_merge_stage stage;
+ long long progress;
+ long long max_progress;
};
struct qcow2_target {
--
2.48.1
More information about the Devel
mailing list