parse_metadata() is called not only from the main kwork: qcow2_llseek_hole() calls it in process context, in parallel with the kwork. When both miss the same md page, both drop md_pages_lock in submit_read_md_page() and try to insert a new md page. The one that loses the insert gets -EEXIST from qcow2_alloc_and_insert_md_page() and returns it as is: a guest I/O on the kwork side ends with EIO, or lseek(SEEK_DATA/SEEK_HOLE) fails with -EEXIST.
Teach __handle_md_page() to handle -EEXIST by repeating the lookup: the page inserted in parallel is found then, and the qio is either served from it or postponed until its read completes. Repeat it once only. The second lookup misses the page only if its read failed and erased it in between, and then the qio fails with -EIO as the qios waiting for that read did. https://virtuozzo.atlassian.net/browse/VSTOR-95494 Fixes: 412d5a4f7bf1 ("dm/dm-qcow2: add llseek_hole") Feature: dm-qcow2: block device over QCOW2 files driver Signed-off-by: Andrey Zhadchenko <[email protected]> --- v2: say in the message what fails without the patch: the race of qcow2_llseek_hole() with main kwork; added Fixes: tag v3: repeat the lookup once only, a second miss fails the qio with -EIO drivers/md/dm-qcow2-map.c | 23 ++++++++++++++++++----- 1 file changed, 18 insertions(+), 5 deletions(-) diff --git a/drivers/md/dm-qcow2-map.c b/drivers/md/dm-qcow2-map.c index 32373114580e9..58b95d7a955cf 100644 --- a/drivers/md/dm-qcow2-map.c +++ b/drivers/md/dm-qcow2-map.c @@ -1676,20 +1676,33 @@ static int submit_read_md_page(struct qcow2 *qcow2, struct qio **qio, /* * This may be called with @qio == NULL, in case of we are * interesting in searching cached in memory md only. - * This is aimed to be called not only from main kwork - * for L1/L2 pages, so all callers looking for L1/L2 - * must care about submit_read_md_page() may return EEXIST. + * + * submit_read_md_page() drops md_pages_lock, so the page may be + * inserted in parallel. Then it is looked up once more. It is not + * found again only if its read failed in the meantime, and then + * @qio fails too. */ static int __handle_md_page(struct qcow2 *qcow2, u64 page_id, struct qio **qio, struct md_page **ret_md) { + bool retried = false; struct md_page *md; + int ret; lockdep_assert_held(&qcow2->md_pages_lock); +again: md = qcow2_md_page_find_or_postpone(qcow2, page_id, qio); if (!md) { - if (qio && *qio) - return submit_read_md_page(qcow2, qio, page_id); + if (qio && *qio) { + ret = submit_read_md_page(qcow2, qio, page_id); + if (unlikely(ret == -EEXIST)) { + if (retried) + return -EIO; + retried = true; + goto again; + } + return ret; + } return 0; } -- 2.43.5 _______________________________________________ Devel mailing list [email protected] https://lists.openvz.org/mailman/listinfo/devel
