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

Reply via email to