A discard, which frees a cluster, has to know when data writes submitted to the cluster earlier are completed. Nothing tracks them now: a write goes from perform_rw_mapped() straight to the image file and is not linked to anything a discard could wait for.
Count every data write submitted from perform_rw_mapped() in the L2 page mapping its cluster, from submission till completion. The counter is split in two generations and a write is counted in the current one. This allows the next patch to switch the generation and to wait for the previous one to drain without being starved by new writes. The counting is done from the main work only, so the generation is stable there and no lock is needed. No functional change: nobody switches the generation or waits for the counters yet. https://virtuozzo.atlassian.net/browse/VSTOR-154659 Feature: dm-qcow2: block device over QCOW2 files driver Signed-off-by: Andrey Zhadchenko <[email protected]> --- drivers/md/dm-qcow2-map.c | 22 ++++++++++++++++++++++ drivers/md/dm-qcow2-target.c | 3 +++ drivers/md/dm-qcow2.h | 8 ++++++++ 3 files changed, 33 insertions(+) diff --git a/drivers/md/dm-qcow2-map.c b/drivers/md/dm-qcow2-map.c index 34d9d9810f601..2c0e7bdb251dc 100644 --- a/drivers/md/dm-qcow2-map.c +++ b/drivers/md/dm-qcow2-map.c @@ -368,10 +368,12 @@ void qcow2_init_qio(struct qio *qio, unsigned int bi_op, struct qcow2 *qcow2) qio->queue_list_id = QLIST_DEFERRED; qio->ext = NULL; qio->data = NULL; + qio->writer_md = NULL; qio->bi_op = bi_op; qio->bi_io_vec = NULL; qio->flags = 0; qio->ref_index = REF_INDEX_INVALID; + qio->writer_gen = 0; atomic_set(&qio->remaining, 1); /* @@ -613,6 +615,21 @@ static bool delay_if_has_wpc_readers(struct qcow2 *qcow2, struct md_page *md, return ret; } +static void inc_writers(struct md_page *md, struct qio *qio) +{ + qio->writer_md = md; + qio->writer_gen = md->writer_gen; + atomic_inc(&md->active_writers[qio->writer_gen]); +} + +static void dec_writers(struct qio *qio) +{ + struct md_page *md = qio->writer_md; + + qio->writer_md = NULL; + atomic_dec(&md->active_writers[qio->writer_gen]); +} + static u32 calc_cow_mask(struct qcow2 *qcow2, u64 ext_l2, struct qio *qio, bool wants_backing, bool wants_data, bool wants_zeroes) @@ -2863,6 +2880,9 @@ static void data_rw_complete(struct qio *qio) bi_status = errno_to_blk_status(qio->ret); } + if (qio->writer_md) + dec_writers(qio); + wbd = qio->data; if (wbd) { WARN_ON_ONCE(!write); @@ -2919,6 +2939,8 @@ static void perform_rw_mapped(struct qcow2_map *map, struct qio *qio) qio->data = md->wbd; spin_unlock_irqrestore(&qcow2->md_pages_lock, flags); } + if (rw == WRITE) + inc_writers(md, qio); map_and_submit_rw(qcow2, map->data_clu_pos, qio); } diff --git a/drivers/md/dm-qcow2-target.c b/drivers/md/dm-qcow2-target.c index 8337efc5c6238..07ca928002e0a 100644 --- a/drivers/md/dm-qcow2-target.c +++ b/drivers/md/dm-qcow2-target.c @@ -399,6 +399,9 @@ int qcow2_alloc_and_insert_md_page(struct qcow2 *qcow2, u64 index, struct md_pag (*md)->lockd = NULL; atomic_set(&(*md)->wpc_readers, 0); (*md)->wpc_noread_count = 0; + (*md)->writer_gen = 0; + atomic_set(&(*md)->active_writers[0], 0); + atomic_set(&(*md)->active_writers[1], 0); INIT_LIST_HEAD(&(*md)->wait_list); INIT_LIST_HEAD(&(*md)->wpc_readers_wait_list); INIT_LIST_HEAD(&(*md)->wb_link); diff --git a/drivers/md/dm-qcow2.h b/drivers/md/dm-qcow2.h index ad5286b16883e..5755788e0e5ae 100644 --- a/drivers/md/dm-qcow2.h +++ b/drivers/md/dm-qcow2.h @@ -135,6 +135,7 @@ struct md_page { #define MD_WRITEBACK (1U << 2) /* Writeback was submitted */ #define MD_WRITEBACK_ERROR (1U << 3) /* Last writeback failed with error */ unsigned int status; + u8 writer_gen; struct page *page; struct list_head wait_list; /* To link in qcow2::{,slow}wb_batch_list and qcow2::QLIST_COMPLETED_WB */ @@ -148,6 +149,11 @@ struct md_page { atomic_t wpc_readers; int wpc_noread_count; /* Read is prohibited, if positive */ struct list_head wpc_readers_wait_list; + /* + * Data writes in flight to clusters mapped by this L2 page, split + * in two generations. New writes are counted in writer_gen one. + */ + atomic_t active_writers[2]; }; enum qcow2_backward_merge_state { @@ -357,6 +363,7 @@ struct qio { * bio processing should resume. */ u8 queue_list_id:4; + u8 writer_gen:1; atomic_t remaining; @@ -365,6 +372,7 @@ struct qio { int ret; /* iocb result */ void (*complete)(struct qio *me); void *data; + struct md_page *writer_md; /* Some operations (COW) require special destruction or requeue */ struct qio_ext *ext; struct list_head link; -- 2.43.5 _______________________________________________ Devel mailing list [email protected] https://lists.openvz.org/mailman/listinfo/devel
