The dm-qcow2 module parameter kernel_sets_dirty_bit makes the kernel set the dirty bit on resume and clear it on postsuspend. It is off by default and Virtuozzo does not set it: ploop manages the dirty bit from userspace itself. So this is a hardening patch for the non-default mode.
With kernel_sets_dirty_bit, postsuspend clears the dirty bit unconditionally. An image that is dirty at open, e.g. after a crash, has unrepaired refcounts, which may be below real usage, yet it is marked clean on disk after the first suspend. A userspace check before the next open then has no reason to repair it. The same happens after an error that sets wants_check, e.g. md_writeback_error: it is kept only in the target, so resume is refused, but after a table reload or a host restart the image is reopened with a clean header. Clear the bit only if the image was clean before the kernel set it and wants_check is not set. The parsed header can't tell the former: it is reread on every preresume and may carry the kernel's own bit, e.g. on the lower image of a backward merge cancelled by suspend in waiting_completion or of a merge with depth > 0. So track two flags per image: kernel_dirty_bit_set is set only while the last header write was a successful set and the reread header still has the bit, opened_dirty is set when the header has the bit and kernel_dirty_bit_set is not set. Clearing the bit of an image opened dirty is left to userspace check. After a failed write the bit found on disk is taken as opened dirty, which costs an extra check, rather than as the kernel's, which could clear a bit set by userspace while the device was suspended. https://virtuozzo.atlassian.net/browse/VSTOR-148171 Feature: dm-qcow2: block device over QCOW2 files driver Signed-off-by: Alexander Ivanov <[email protected]> --- drivers/md/dm-qcow2-target.c | 14 ++++++++++++-- drivers/md/dm-qcow2.h | 2 ++ 2 files changed, 14 insertions(+), 2 deletions(-) diff --git a/drivers/md/dm-qcow2-target.c b/drivers/md/dm-qcow2-target.c index bb322254cea9..6824d95193ae 100644 --- a/drivers/md/dm-qcow2-target.c +++ b/drivers/md/dm-qcow2-target.c @@ -666,6 +666,7 @@ int qcow2_set_image_file_features(struct qcow2 *qcow2, bool dirty) u64 dirty_mask = cpu_to_be64(INCOMPATIBLE_FEATURES_DIRTY_BIT); struct QCowHeader *raw_hdr; struct md_page *md; + int ret; if (qcow2->hdr.version == 2) return 0; @@ -682,12 +683,15 @@ int qcow2_set_image_file_features(struct qcow2 *qcow2, bool dirty) if (kernel_sets_dirty_bit) { if (dirty) raw_hdr->incompatible_features |= dirty_mask; - else + else if (!qcow2->opened_dirty && !qcow2_wants_check(qcow2->tgt)) raw_hdr->incompatible_features &= ~dirty_mask; } kunmap(md->page); - return qcow2_rw_page_sync(WRITE, qcow2, md->id, md->page); + ret = qcow2_rw_page_sync(WRITE, qcow2, md->id, md->page); + if (kernel_sets_dirty_bit) + qcow2->kernel_dirty_bit_set = dirty && !ret; + return ret; } ALLOW_ERROR_INJECTION(qcow2_set_image_file_features, ERRNO); @@ -911,6 +915,12 @@ static int qcow2_parse_header(struct dm_target *ti, struct qcow2 *qcow2, goto out; qcow2_calc_cached_parameters(qcow2, hdr); + if (!(hdr->incompatible_features & INCOMPATIBLE_FEATURES_DIRTY_BIT)) { + qcow2->opened_dirty = false; + qcow2->kernel_dirty_bit_set = false; + } else if (!qcow2->kernel_dirty_bit_set) { + qcow2->opened_dirty = true; + } ret = -EOPNOTSUPP; if (qcow2->clu_size < PAGE_SIZE || (qcow2->ext_l2 && qcow2->clu_size < PAGE_SIZE * 32)) diff --git a/drivers/md/dm-qcow2.h b/drivers/md/dm-qcow2.h index 5abc7f5ad239..2a6203c86e3a 100644 --- a/drivers/md/dm-qcow2.h +++ b/drivers/md/dm-qcow2.h @@ -259,6 +259,8 @@ struct qcow2 { u32 refblock_bits; u32 refblock_entries; bool ext_l2; + bool opened_dirty; + bool kernel_dirty_bit_set; bool pause_submitting_qios; /* This is used only on top qcow2 image */ bool backward_merge_in_process; -- 2.53.0 _______________________________________________ Devel mailing list [email protected] https://lists.openvz.org/mailman/listinfo/devel
