Only one discard at a time switches the generation of an L2 page and waits for the previous one to drain. Others queue behind it and then do the same one by one, so N discards of clusters mapped by one L2 page take N drain cycles under write load. This is the common case: discards updating the same L2 page complete its writeback together and come to process_indexes_end() in one batch.
A discard, which finds a switch in progress while no write is counted in the new generation yet, has exactly the same writes to wait for as the discard which switched. Let it join: wait for the same previous generation instead of queueing for its own switch. If that generation is already empty, nothing is in flight and the discard goes on immediately. Count the discards waiting for the previous generation instead of keeping a flag, and allow the next switch when the last of them is released. 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 | 29 ++++++++++++++++++++++------- drivers/md/dm-qcow2-target.c | 2 +- drivers/md/dm-qcow2.h | 2 +- 3 files changed, 24 insertions(+), 9 deletions(-) diff --git a/drivers/md/dm-qcow2-map.c b/drivers/md/dm-qcow2-map.c index b07747f80030f..21f2d34ca371f 100644 --- a/drivers/md/dm-qcow2-map.c +++ b/drivers/md/dm-qcow2-map.c @@ -671,20 +671,34 @@ static bool delay_discard_if_writers(struct qcow2 *qcow2, struct md_page *md, if (atomic_read(&md->active_writers[qio->writer_gen])) goto delay; qio->flags &= ~QIO_DISCARD_WAIT_FL; - md->gen_switched = false; - discard_waiters_to_list(qcow2, md, &wake_list); + if (!--md->discard_waiters) + discard_waiters_to_list(qcow2, md, &wake_list); goto unlock; } - /* another discard already waits: we can't switch without breaking it */ - if (md->gen_switched) - goto delay; - /* * md->active_writers checks are valid only if increases are * from main kwork (as we are) or under md_pages_lock. */ gen = md->writer_gen; + if (md->discard_waiters) { + /* + * New writes since previous switch and another discard + * already waits: do not break it and delay + */ + if (atomic_read(&md->active_writers[gen])) + goto delay; + + gen = !gen; + /* + * No new writes and previous writes just completed + * but waiters haven't been processed yet: proceed + */ + if (!atomic_read(&md->active_writers[gen])) + goto unlock; + goto wait; + } + if (!atomic_read(&md->active_writers[gen])) goto unlock; @@ -693,7 +707,8 @@ static bool delay_discard_if_writers(struct qcow2 *qcow2, struct md_page *md, if (!atomic_read(&md->active_writers[gen])) goto unlock; - md->gen_switched = true; +wait: + md->discard_waiters++; qio->writer_gen = gen; qio->flags |= QIO_DISCARD_WAIT_FL; delay: diff --git a/drivers/md/dm-qcow2-target.c b/drivers/md/dm-qcow2-target.c index 24a42c5a66759..3cdd1ec2aab4b 100644 --- a/drivers/md/dm-qcow2-target.c +++ b/drivers/md/dm-qcow2-target.c @@ -401,7 +401,7 @@ int qcow2_alloc_and_insert_md_page(struct qcow2 *qcow2, u64 index, struct md_pag atomic_set(&(*md)->wpc_readers, 0); (*md)->wpc_noread_count = 0; (*md)->writer_gen = 0; - (*md)->gen_switched = false; + (*md)->discard_waiters = 0; atomic_set(&(*md)->active_writers[0], 0); atomic_set(&(*md)->active_writers[1], 0); INIT_LIST_HEAD(&(*md)->wait_list); diff --git a/drivers/md/dm-qcow2.h b/drivers/md/dm-qcow2.h index 8eb0c49f1e4eb..1e1eb9e4daef0 100644 --- a/drivers/md/dm-qcow2.h +++ b/drivers/md/dm-qcow2.h @@ -136,7 +136,7 @@ struct md_page { #define MD_WRITEBACK_ERROR (1U << 3) /* Last writeback failed with error */ unsigned int status; u8 writer_gen; - bool gen_switched; + u16 discard_waiters; struct page *page; struct list_head wait_list; /* To link in qcow2::{,slow}wb_batch_list and qcow2::QLIST_COMPLETED_WB */ -- 2.43.5 _______________________________________________ Devel mailing list [email protected] https://lists.openvz.org/mailman/listinfo/devel
