dm-qcow2 heavily relies on main kwork do_qcow2_work() being sequential. All metadata change happens there and cannot run in parallel. Unfortunately that caps dm-qcow2 performance. But if the request does not require metadata changes we should be able to immediately dispatch it. process_one_qio() and handle_metadata() already know what to do: just add avoid_metadata_changes flag and return codes. While at it, just return in handle_metadata() instead of setting ret to 0 and jumping to if (ret < 0) label.
Also pass the flag to parse_metadata() and return to the slow path before it looks up R1/R2 pages. They are needed only by COW writes and cluster unmapping discards, which go to the slow path anyway. Besides the useless lookup, R1 is unstable outside main kwork: an R1 page read in parallel with relocate_refcount_table() may stay cached under a stale page id. Also exclude compressed clusters as there are some complications with wpc_readers. https://virtuozzo.atlassian.net/browse/VSTOR-95494 Feature: dm-qcow2: block device over QCOW2 files driver Signed-off-by: Andrey Zhadchenko <[email protected]> --- v2: return to the slow path before R1/R2 lookup in parse_metadata(); QIO_NEEDS_MAIN_KWORK instead of magic 2; fixed the "avoid_metadata_changes() flag" typo drivers/md/dm-qcow2-map.c | 55 ++++++++++++++++++++++++++++++--------- 1 file changed, 42 insertions(+), 13 deletions(-) diff --git a/drivers/md/dm-qcow2-map.c b/drivers/md/dm-qcow2-map.c index 49dc3e8119e76..c495834a4d29f 100644 --- a/drivers/md/dm-qcow2-map.c +++ b/drivers/md/dm-qcow2-map.c @@ -2002,6 +2002,12 @@ static int qcow2_handle_r1r2_maps(struct qcow2 *qcow2, loff_t pos, struct qio ** return ret; } +/* + * Returned by parse_metadata() and handle_metadata() with + * @avoid_metadata_changes, when @qio must be processed by main kwork. + */ +#define QIO_NEEDS_MAIN_KWORK 2 + /* * This caches pages of allocated on disk md levels, which are * required for submission @qio, and checks they are stable. @@ -2009,9 +2015,12 @@ static int qcow2_handle_r1r2_maps(struct qcow2 *qcow2, loff_t pos, struct qio ** * Returns: negative in case of error, or 0 if success. * Special case: return 0 with zeroed @qio means @qio was deferred * till some event: reading of md page, end of writeback, etc. + * With @avoid_metadata_changes, return QIO_NEEDS_MAIN_KWORK if @qio + * needs R1/R2: outside main kwork R1 is unstable, see + * __handle_r1r2_maps(). */ static int parse_metadata(struct qcow2 *qcow2, struct qio **qio, - struct qcow2_map *map) + struct qcow2_map *map, bool avoid_metadata_changes) { bool write = op_is_write((*qio)->bi_op); struct md_page *md; @@ -2075,6 +2084,9 @@ static int parse_metadata(struct qcow2 *qcow2, struct qio **qio, if (!qio_may_modify_image(qcow2, *qio)) return 0; + if (avoid_metadata_changes) + return QIO_NEEDS_MAIN_KWORK; + /* Now refcounters table/block */ ret = qcow2_handle_r1r2_maps(qcow2, pos, qio, &map->r1, &map->r2, map->compressed); @@ -3548,24 +3560,31 @@ static int prepare_cluster_discard(struct qcow2 *qcow2, struct qio **qio, } static int handle_metadata(struct qcow2 *qcow2, struct qio **qio, - struct qcow2_map *map) + struct qcow2_map *map, bool avoid_metadata_changes) { bool write = op_is_write((*qio)->bi_op); int ret; - ret = parse_metadata(qcow2, qio, map); + ret = parse_metadata(qcow2, qio, map, avoid_metadata_changes); if (ret < 0 || !*qio) /* Error or postponed */ goto check_err; + if (ret == QIO_NEEDS_MAIN_KWORK) + return QIO_NEEDS_MAIN_KWORK; + + if (avoid_metadata_changes && map->compressed) + return QIO_NEEDS_MAIN_KWORK; ret = 1; if (unlikely(qcow2->backward_merge_in_process)) { + if (avoid_metadata_changes) + return QIO_NEEDS_MAIN_KWORK; /* Keep in mind the below may replace *qio */ ret = prepare_backward_merge(qcow2, qio, map, write); } else if (unlikely(fake_l1cow_qio(*qio)) && (!map->clu_is_cow || (map->level & L2_LEVEL))) { /* Nothing to COW or L1 is mapped exactly once */ qio_endio(*qio); - ret = 0; + return 0; } else if (unlikely(op_is_discard((*qio)->bi_op))) { /* * Discard carries no data and is advisory. If it does not @@ -3573,13 +3592,17 @@ static int handle_metadata(struct qcow2 *qcow2, struct qio **qio, * is not present, end early. */ if (qio_discard_updates_metadata(qcow2, *qio, map)) { + if (avoid_metadata_changes) + return QIO_NEEDS_MAIN_KWORK; ret = prepare_cluster_discard(qcow2, qio, map); } else if (!map->data_clu_alloced || map->clu_is_cow) { qio_endio(*qio); - ret = 0; + return 0; } } else if (write && (!qio_is_fully_alloced(qcow2, *qio, map) || map->clu_is_cow)) { + if (avoid_metadata_changes) + return QIO_NEEDS_MAIN_KWORK; if (map->clu_is_cow) { /* COW to compressed or shared with snapshot cluster */ ret = prepare_l1l2_replace(qcow2, *qio, map); @@ -3691,13 +3714,17 @@ static void process_read_qio(struct qcow2 *qcow2, struct qio *qio, qio_endio(qio); } -static void process_one_qio(struct qcow2 *qcow2, struct qio *qio) +static int process_one_qio(struct qcow2 *qcow2, struct qio *qio, bool avoid_metadata_changes) { struct qcow2_map map = { .qcow2 = qcow2, }; bool write; + int ret; - if (!handle_metadata(qcow2, &qio, &map)) - return; + ret = handle_metadata(qcow2, &qio, &map, avoid_metadata_changes); + if (!ret) + return 0; + else if (ret == QIO_NEEDS_MAIN_KWORK) + return 1; /* * Merge machinery makes out of order merges for present @@ -3709,7 +3736,7 @@ static void process_one_qio(struct qcow2 *qcow2, struct qio *qio) if (unlikely(qcow2->backward_merge_in_process) && (fake_merge_qio(qio) || qcow2_file_is_writable(qcow2))) { submit_top_delta_read(&map, qio); - return; + return 0; } write = op_is_write(qio->bi_op); @@ -3717,13 +3744,13 @@ static void process_one_qio(struct qcow2 *qcow2, struct qio *qio) /* Process discard early. Don't do COW just to discard */ if (unlikely(op_is_discard(qio->bi_op))) { issue_discard(&map, qio); - return; + return 0; } if (unlikely(map.compressed)) { /* Compressed qio never uses sub-clus */ submit_read_compressed(&map, qio, write); - return; + return 0; } if (!write) { @@ -3736,6 +3763,8 @@ static void process_one_qio(struct qcow2 *qcow2, struct qio *qio) else perform_rw_mapped(&map, qio); } + + return 0; } static struct bio_vec *create_bvec_from_rq(struct request *rq) @@ -3838,7 +3867,7 @@ static void process_deferred_qios(struct qcow2 *qcow2, struct list_head *qios) continue; } - process_one_qio(qcow2, qio); + process_one_qio(qcow2, qio, false); } } @@ -4774,7 +4803,7 @@ static int qcow2_llseek_hole_qio(struct qio *qio, int whence, loff_t *result) map.qcow2 = qio->qcow2; qptr = qio; - ret = parse_metadata(qio->qcow2, &qptr, &map); + ret = parse_metadata(qio->qcow2, &qptr, &map, false); if (ret < 0) break; if (qptr == NULL) { -- 2.43.5 _______________________________________________ Devel mailing list [email protected] https://lists.openvz.org/mailman/listinfo/devel
