From mboxrd@z Thu Jan 1 00:00:00 1970 Return-Path: Received: from mail.openvz.org (unknown [69.168.225.77]) by lore.virtuozzo.com (Postfix) with ESMTPS id BC0BD80726 for ; Tue, 13 Oct 2026 11:18:04 +0000 (UTC) Received: from mail.openvz.org (localhost [127.0.0.1]) by mail.openvz.org (8.14.4/8.14.4) with ESMTP id 69DBGoAv012582; Tue, 13 Oct 2026 14:16:51 +0300 DKIM-Filter: OpenDKIM Filter v2.11.0 mail.openvz.org 69DBGoAv012582 Authentication-Results: mail.openvz.org; dkim=fail reason="signature verification failed" (2048-bit key) header.d=virtuozzo.com header.i=@virtuozzo.com header.b="bdsDAxP/" Received: from mail-wm1-f70.google.com (mail-wm1-f70.google.com [209.85.128.70]) by mail.openvz.org (8.14.4/8.14.4) with ESMTP id 69DBGedO012537 (version=TLSv1/SSLv3 cipher=AES128-GCM-SHA256 bits=128 verify=FAIL) for ; Tue, 13 Oct 2026 14:16:40 +0300 DKIM-Filter: OpenDKIM Filter v2.11.0 mail.openvz.org 69DBGedO012537 Received: by mail-wm1-f70.google.com with SMTP id 5b1f17b1804b1-4a01d1fb07bso33425325e9.2 for ; Tue, 13 Oct 2026 04:16:40 -0700 (PDT) X-Gm-Message-State: AFq9FYKjxaeREEQ6wnTiGIHofSA7FMTjd38vY+WHA+u2v50p0RoCABQP fE9InPlmz14A9O54kH3mxaYdoY63Tke1VlPFI4hGjaHnGX4B7LQXLX2ijEd2FpwFuM+8J+2vk1x 1QlPVuLCWOueutWxda1BwiQUEFy5sqQzBjgSeO1+pKqPHTlyZnMhK/A== X-Gm-Gg: AYBFou1rbq/mTjc/GzsKVBy6RLRv8m/dziqs9vbVSWtA8ztnUaKv7rZB0IJOM0YNhtK awvMgL4iF0d8Lp1a/WHV64xYGg1Rgy4CHy/yG8HAPyYccTALcux1gL8sj+ZL3RCjCrya/ozAkuP glTY3vQ1v3Wtyh5KEWB2+wvKyXyWOc/H85OCpZNXHad1677X9sLwsQy6srnzw3ViDXzw9+PYxl+ MxLZGt7OV3gczlFG/LFrHbtfyVK46oojLuRGEQGfmg7gXEKm03+YkXt6e4uDaCxFqDozu/IoNxp mhfSFvZJMxaV7XLnq8wH3Aloh1n5rHSiOAAsHdfplNdqMzGDbvFkw/UWlV3A2LeHIEbUEuFiBM/ Lkze7TOov+OX/MbpGQEgISeUiMSU= X-Received: by 2002:a05:600c:6289:b0:4a1:a475:d885 with SMTP id 5b1f17b1804b1-4a1a475d8acmr72409205e9.34.1791890199839; Tue, 13 Oct 2026 04:16:39 -0700 (PDT) X-Received: by 2002:a05:600c:6289:b0:4a1:a475:d885 with SMTP id 5b1f17b1804b1-4a1a475d8acmr72408275e9.34.1791890198985; Tue, 13 Oct 2026 04:16:38 -0700 (PDT) ARC-Seal: i=1; a=rsa-sha256; t=1791890198; cv=none; d=google.com; s=arc-20260327; b=XWHTH3OWuE0gjVR4VdetHfuLtJ8krKxiXMf+vv7qPc7jpnNhE73W3Eah73oNPF5UTn E+7QLsn5AKjYg9ff0vRSk19BIyJSEFi9+UtWrEghQtBT9wkzVxwt7806vn/D8avnZX5C sG6R4bWTfN3j5vWIshGm/Ol+J5aOvv11LEBN44T7SzrGz7ocNNlgzeASxN9IvrF4+3ht l75T8pIo/C3H6iWGDpkrrekDjKaKUtk2OxnjZXiX3lXcWWY0c4iLwCswUgOK7pkbKWgR Wfw4WI2xZf4+noCAT71Pe4QFjjw6v92iIuY+vMpEm558GW1RzHxgDJWsewTkzlDjj5LE wmkg== ARC-Message-Signature: i=1; a=rsa-sha256; c=relaxed/relaxed; d=google.com; s=arc-20260327; h=content-transfer-encoding:mime-version:references:in-reply-to :message-id:date:subject:cc:to:from:dkim-signature; bh=RiFbqOVwgnLMo4YjfTv9Z8Jpyw3jcIDGHxZIlzaMlVU=; fh=M+TyAUhAp4+t7uzAVeqNdLHW5eNz2FQXSv4TjrRhpCk=; b=j9/UdylHZTRtMSD8yywSC4z+BqxbytfFOr4YHn+Qm6oVYZ6UPj3i1oJVip7kp7b4yv BuHnLPgYSo8+kuPIq/mbyubNg2bK+90Aa6yp47V3CoXyFuZsKcyVf6iMypJ5lKpxM8cR jc5fGK41ua0ARr0egnK1Xaa81AEP4JQcnJ23ACMRJQfb1SxwUoaioMZjeHjRqs8d5AZm n+ExxyafstvBzMFiHgbjPf9vftJw5xFks9vSO/oZUwRb8jWdufpDmhjwrzgvOeJ9P/zu NcmsQI6GtnVz965fajGi48QKmlsPLWgpE7eJ37Z1GVG62PyCjUFVtlTPCV0jh4Sagxed s9rQ==; dara=google.com ARC-Authentication-Results: i=1; mx.google.com; dkim=pass header.i=@virtuozzo.com header.s=relay header.b="bdsDAxP/"; spf=pass (google.com: domain of alexander.ivanov@virtuozzo.com designates 130.117.225.111 as permitted sender) smtp.mailfrom=alexander.ivanov@virtuozzo.com; dmarc=pass (p=QUARANTINE sp=QUARANTINE dis=NONE) header.from=virtuozzo.com Received: from relay.virtuozzo.com (relay.virtuozzo.com. [130.117.225.111]) by mx.google.com with ESMTPS id 5b1f17b1804b1-4a195e31a8esi283295855e9.5.2026.10.13.04.16.38 (version=TLS1_3 cipher=TLS_AES_256_GCM_SHA384 bits=256/256); Tue, 13 Oct 2026 04:16:38 -0700 (PDT) Received-SPF: pass (google.com: domain of alexander.ivanov@virtuozzo.com designates 130.117.225.111 as permitted sender) client-ip=130.117.225.111; Authentication-Results: mx.google.com; dkim=pass header.i=@virtuozzo.com header.s=relay header.b="bdsDAxP/"; spf=pass (google.com: domain of alexander.ivanov@virtuozzo.com designates 130.117.225.111 as permitted sender) smtp.mailfrom=alexander.ivanov@virtuozzo.com; dmarc=pass (p=QUARANTINE sp=QUARANTINE dis=NONE) header.from=virtuozzo.com DKIM-Signature: v=1; a=rsa-sha256; q=dns/txt; c=relaxed/relaxed; d=virtuozzo.com; s=relay; h=MIME-Version:Message-ID:Date:Subject:From: Content-Type; bh=RiFbqOVwgnLMo4YjfTv9Z8Jpyw3jcIDGHxZIlzaMlVU=; b=bdsDAxP/ATlz Z+XPskYoI/c9n5Z+hurGgtQqRFWiTN8cAAER91RHnoE62jAsQG4+KgeP1TXlPHo2aHrQpoUUPRSSF HpSHxKj6YkCmuYdVugX60dehnB8FSJ1Q+2QxkB8stPGAoTsQwBSXaoe0pw6T5e9O6C1bt1kMudnKh vAmUKxO0R8r3jq7G/TeaQNkhb1r2HvCnwfGgNSy02EgOlcAHyc3CvWKSMncNqfqQded8ccdBDZ/Bv v8wUCI6OKu0R1YtblbyIpmC/f1h3iuzbEUyCK5Q9rxkvdAm/RAGuJ6KZ+FH7ppCDZRABK3/hrDk05 mrxaI8fDbBliugdtZfc1hQ==; Received: from ch-vpn.virtuozzo.com ([130.117.225.6] helo=sky) by relay.virtuozzo.com with esmtps (TLS1.3) tls TLS_AES_256_GCM_SHA384 (Exim 4.96) (envelope-from ) id 1xGaQz-008pMQ-0z; Tue, 13 Oct 2026 13:16:02 +0200 From: Alexander Ivanov To: den@openvz.org Date: Tue, 13 Oct 2026 13:16:03 +0200 Message-ID: <20261013111604.142989-12-alexander.ivanov@virtuozzo.com> X-Mailer: git-send-email 2.53.0 In-Reply-To: <20261013111604.142989-1-alexander.ivanov@virtuozzo.com> References: <20261013111604.142989-1-alexander.ivanov@virtuozzo.com> MIME-Version: 1.0 X-OZ-Fwd: true Cc: devel@openvz.org Subject: [Devel] [PATCH hci-8.1 v6 11/12] dm-ploop: reload metadata on resume X-BeenThere: devel@openvz.org X-Mailman-Version: 2.1.12 Precedence: list List-Id: OpenVZ development List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , Content-Type: text/plain; charset="us-ascii" Content-Transfer-Encoding: 7bit Sender: devel-bounces@openvz.org Errors-To: devel-bounces@openvz.org On "dmsetup reload" the new ploop reads image metadata in ploop_ctr(), but IO keeps going through the old ploop until suspend, and userspace may modify images of a suspended device (merge zeroes BAT entries of the base delta, snapshot creation trims the top delta). So after resume the new ploop may work with a stale BAT and file sizes, e.g. allocate clusters already in use. Reload all metadata in ploop_preresume(): the top delta header, BAT of every delta and delta file sizes, and rebuild the BAT tree and holes_bitmap. On error the old metadata is kept and resume fails. DM has already swapped the new table in by then, so the device stays suspended and the next resume retries. Userspace must check the result and must not resume the previous instance on the same images. Skip the in-use signature check on reload, it is done in ploop_ctr(). Take the signature from disk: libploop marks the image closed on the suspended device before umount, and an in-memory IN_USE written over it makes umount trim the image and lose CBT. After flip_upper_deltas keep the in-memory signature, as userspace relies on it being written to the new top delta, otherwise the next load fails with -ENOLCK after online merge. If metadata is dirty after suspend, keep it, as md pages may still be referenced. Data bios complete before BAT writeback, so wait for inflight_md in postsuspend. Request preallocation in ploop_preresume() after the reload instead of in ploop_ctr(), so the new ploop doesn't truncate the top delta back to a stale size before resume. Wait for pending preallocation in postsuspend, killably, as the allocator may hang on a dead file. Don't wait in standby, so that dm_replace() of libploop can suspend the device to recover it. Skip the reload and prealloc on resume while preallocation is pending, as the allocator changes the top delta size under bat_lock only. Skip the reload in standby too, as the images may be inaccessible. Track standby per ploop instead of the queue bit. The queue is shared by all tables, and ploop_ctr() of a new table clears the bit before the old table is suspended, so the old ploop would wait for its stuck allocator in postsuspend. ploop_llseek_hole() needs no locking against the reload: dm_llseek_hole() calls it under md->io_barrier and returns -EAGAIN on a suspended device. https://virtuozzo.atlassian.net/browse/VSTOR-148171 Feature: dm-ploop: ploop target driver Signed-off-by: Alexander Ivanov --- drivers/md/dm-ploop-bat.c | 146 ++++++++++++++++++++++++++++++++++- drivers/md/dm-ploop-cmd.c | 1 + drivers/md/dm-ploop-map.c | 9 ++- drivers/md/dm-ploop-target.c | 88 +++++++++++++++------ drivers/md/dm-ploop.h | 13 +++- 5 files changed, 229 insertions(+), 28 deletions(-) diff --git a/drivers/md/dm-ploop-bat.c b/drivers/md/dm-ploop-bat.c index 5a89771a2e02..c060afdd8d3e 100644 --- a/drivers/md/dm-ploop-bat.c +++ b/drivers/md/dm-ploop-bat.c @@ -185,7 +185,8 @@ static int ploop_setup_holes_bitmap(struct ploop *ploop, u32 bat_clusters) } ALLOW_ERROR_INJECTION(ploop_setup_holes_bitmap, ERRNO); -int ploop_setup_metadata(struct ploop *ploop, struct page *page) +int ploop_setup_metadata(struct ploop *ploop, struct page *page, + bool check_in_use) { struct ploop_pvd_header *m_hdr = NULL; u32 bat_clusters, offset_clusters; @@ -200,7 +201,8 @@ int ploop_setup_metadata(struct ploop *ploop, struct page *page) goto out; ret = -ENOLCK; - if (m_hdr->m_DiskInUse != cpu_to_le32(SIGNATURE_DISK_IN_USE) && + if (check_in_use && + m_hdr->m_DiskInUse != cpu_to_le32(SIGNATURE_DISK_IN_USE) && !ploop_is_ro(ploop) && !ignore_signature_disk_in_use) goto out; @@ -567,6 +569,146 @@ int ploop_add_delta(struct ploop *ploop, u32 level, struct file *file, bool is_r } ALLOW_ERROR_INJECTION(ploop_add_delta, ERRNO); +static bool ploop_md_pages_are_clean(struct ploop *ploop) +{ + struct rb_node *node; + struct md_page *md; + + if (!llist_empty(&ploop->wb_batch_llist) || + atomic_read(&ploop->inflight_md)) + return false; + + ploop_for_each_md_page(ploop, md, node) { + if (test_bit(MD_DIRTY, &md->status) || + test_bit(MD_WRITEBACK, &md->status) || + test_bit(MD_UPDATING, &md->status) || + md->piwb || !llist_empty(&md->wait_llist)) + return false; + } + + return true; +} + +/* + * Build in-memory metadata from scratch: re-read header of top delta, + * BAT of every delta and sizes of delta files. This is called on resume + * of suspended device, since after "dmsetup reload" the metadata of the + * new ploop may be outdated: the images could be changed by the old + * ploop after the new one was constructed. On error the old metadata + * is kept. + */ +int ploop_reload_metadata(struct ploop *ploop) +{ + struct rb_root old_bat_entries = ploop->bat_entries; + void *old_holes_bitmap = ploop->holes_bitmap; + u32 old_nr_bat_entries = ploop->nr_bat_entries; + u32 old_hb_nr = ploop->hb_nr; + u32 old_m_Type = ploop->m_Type; + u32 old_m_Sectors = ploop->m_Sectors; + struct ploop_pvd_header *hdr, *old_hdr; + u8 old_m_Sig[sizeof(ploop->m_Sig)]; + struct ploop_delta *delta, *old_deltas; + struct page *page; + loff_t file_size; + u32 nr_be; + int level, ret; + + /* + * Metadata of the device that has already been running is up to + * date, so we only need to reload clean metadata. Dirty metadata + * must not appear after suspend, as all IO and BAT writeback are + * completed (see ploop_postsuspend()), but we can't free md pages, + * which are still in use, in any case. + */ + if (WARN_ON_ONCE(!ploop_md_pages_are_clean(ploop))) + return 0; + + memcpy(old_m_Sig, ploop->m_Sig, sizeof(old_m_Sig)); + + old_deltas = kmemdup(ploop->deltas, + sizeof(*old_deltas) * ploop->nr_deltas, + GFP_KERNEL); + page = alloc_page(GFP_KERNEL); + ret = -ENOMEM; + if (!old_deltas || !page) + goto out; + + ret = ploop_rw_page_sync(READ, ploop_top_delta(ploop)->file, 0, page); + if (ret) + goto out; + + ploop->bat_entries = RB_ROOT; + ploop->holes_bitmap = NULL; + + /* Image is already checked for being in use in ploop_ctr() */ + ret = ploop_setup_metadata(ploop, page, false); + if (ret) + goto restore; + + ret = ploop_prealloc_md_pages(&ploop->bat_entries, 0, + ploop->nr_bat_entries); + if (ret) + goto restore; + + /* + * Same order as in ploop_add_deltas_stack(): from top to base. + * ploop_read_delta() relies on it, see the comment there. nr_be of + * the delta is updated right away, as the lower delta is checked + * against it on the next iteration. + */ + for (level = ploop_top_level(ploop); level >= 0; level--) { + delta = &ploop->deltas[level]; + + ret = ploop_read_delta(ploop, level, delta->file, + &file_size, &nr_be); + if (ret) + goto restore; + + delta->file_size = file_size; + delta->file_preallocated_area_start = file_size; + delta->nr_be = nr_be; + } + + /* + * Header of top delta is written with BAT page 0. Userspace doesn't + * mark the delta, which becomes top after flip_upper_deltas, as in + * use, and relies that the in-use signature of the old top delta + * will be written to it. So keep the signature we had. In other + * cases take it from disk: userspace may have changed it on the + * suspended device (e.g. marked the image closed on umount). + */ + if (ploop->top_flipped) { + old_hdr = ploop_md_first_entry(&old_bat_entries)->kmpage; + hdr = ploop_md_first_entry(&ploop->bat_entries)->kmpage; + hdr->m_DiskInUse = old_hdr->m_DiskInUse; + ploop->top_flipped = false; + } + + ploop_free_md_pages_tree(&old_bat_entries); + kvfree(old_holes_bitmap); + goto out; +restore: + ploop_free_md_pages_tree(&ploop->bat_entries); + kvfree(ploop->holes_bitmap); + ploop->bat_entries = old_bat_entries; + ploop->holes_bitmap = old_holes_bitmap; + ploop->nr_bat_entries = old_nr_bat_entries; + ploop->hb_nr = old_hb_nr; + memcpy(ploop->m_Sig, old_m_Sig, sizeof(ploop->m_Sig)); + ploop->m_Type = old_m_Type; + ploop->m_Sectors = old_m_Sectors; + memcpy(ploop->deltas, old_deltas, + sizeof(*old_deltas) * ploop->nr_deltas); +out: + if (page) + put_page(page); + kfree(old_deltas); + if (ret) + PL_ERR("Failed to reload metadata: %d", ret); + return ret; +} +ALLOW_ERROR_INJECTION(ploop_reload_metadata, ERRNO); + void ploop_put_delta(struct ploop *ploop, struct ploop_delta *delta) { int i; diff --git a/drivers/md/dm-ploop-cmd.c b/drivers/md/dm-ploop-cmd.c index 977b6920a5b4..1590af23360f 100644 --- a/drivers/md/dm-ploop-cmd.c +++ b/drivers/md/dm-ploop-cmd.c @@ -1049,6 +1049,7 @@ static int process_flip_upper_deltas(struct ploop *ploop) /* FIXME */ swap(ploop->deltas[level], ploop->deltas[level+1]); + ploop->top_flipped = true; return 0; } diff --git a/drivers/md/dm-ploop-map.c b/drivers/md/dm-ploop-map.c index e2b70cb3c142..5733b7d28196 100644 --- a/drivers/md/dm-ploop-map.c +++ b/drivers/md/dm-ploop-map.c @@ -1247,7 +1247,7 @@ void ploop_should_prealloc(struct ploop *ploop) spin_unlock_irqrestore(&ploop->bat_lock, flags); } -static int ploop_no_pending_prealloc(struct ploop *ploop) +int ploop_no_pending_prealloc(struct ploop *ploop) { int ret; unsigned long flags; @@ -1259,6 +1259,12 @@ static int ploop_no_pending_prealloc(struct ploop *ploop) return ret; } +void ploop_wait_prealloc(struct ploop *ploop) +{ + wait_event_killable(ploop->dispatcher_wq_prealloc, + ploop_no_pending_prealloc(ploop)); +} + static int ploop_allocate_cluster(struct ploop *ploop, u32 *dst_clu, struct file *file) { struct ploop_delta *top = ploop_top_delta(ploop); @@ -1446,6 +1452,7 @@ static void ploop_check_standby_mode(struct ploop *ploop, long res) spin_lock_irqsave(&q->queue_lock, flags); prev = test_and_set_bit(QUEUE_FLAG_STANDBY, &q->queue_flags); spin_unlock_irqrestore(&q->queue_lock, flags); + WRITE_ONCE(ploop->standby, true); if (!prev) PL_INFO("was switched into the standby mode"); diff --git a/drivers/md/dm-ploop-target.c b/drivers/md/dm-ploop-target.c index abb60aa6a1e9..fea00210d166 100644 --- a/drivers/md/dm-ploop-target.c +++ b/drivers/md/dm-ploop-target.c @@ -21,6 +21,7 @@ #include #include #include +#include #include "dm-ploop.h" #define DM_MSG_PREFIX "ploop" @@ -295,7 +296,7 @@ static int ploop_check_top_delta(struct ploop *ploop, struct file *file) /* flush dirty pages before changing direct IO */ vfs_fsync(file, 1); - ret = ploop_setup_metadata(ploop, page); + ret = ploop_setup_metadata(ploop, page, true); if (ret) goto out; @@ -426,8 +427,7 @@ static struct ploop_worker *ploop_worker_create(struct ploop *ploop, * suspended and resumed. This behavior is intentional to allow for atomic * updates to the mapping table without disrupting ongoing I/O operations. * - * But dm-ploop is not fully compatible with this design. Ploop does not do a - * "metadata" reload on resume. So there can be situations like: + * So there can be situations like: * * 1. dmsetup reload is called, and new ploop is created with ploop_ctr for * the same delta image file as an old ploop and ploop_add_deltas_stack reads @@ -438,23 +438,19 @@ static struct ploop_worker *ploop_worker_create(struct ploop *ploop, * the new one, new ploop has outdated metadata and thus may corrupt the * images by relying on it. * - * For instance we thought to truncate the preallocations to the delta image - * file on ploop_dtr, but that can't be done as ploop_dtr is called on the old - * ploop after the new one is resumed, so we can't possibly modify the image - * from the old ploop context at this point, as new ploop may be already doing - * its own IO or preallocation. + * To handle this, ploop_preresume reloads all the metadata from the images + * (see ploop_reload_metadata), so that the new ploop "sees" all previous + * updates from the old one. For the same reason the new ploop must not + * modify the images before resume, so preallocation is started only after + * the metadata reload, and the old ploop waits for its preallocation to + * complete on postsuspend. * - * The better way to do this truncation is on ploop_*suspend, and then on resume - * to reload the metadata in the new ploop so that the new ploop "sees" all - * previous updates from the old one. - * - * For now we can leave with it, as we chose not to do this truncation on - * destroy and we rely that userspace always does dmsetup reload on already - * suspended device. - * - * FIXME: We must implement metadata reload on resume. After that we can - * implement preallocation truncation on suspend (see dm-qcow2 as it already - * implements that in qcow2_parse_metadata and qcow2_truncate_preallocations). + * Still, we can't truncate the preallocations to the delta image file on + * ploop_dtr, as ploop_dtr is called on the old ploop after the new one is + * resumed, so we can't possibly modify the image from the old ploop context + * at this point, as new ploop may be already doing its own IO or + * preallocation. This truncation should be done on ploop_*suspend (see + * dm-qcow2 as it already implements that in qcow2_truncate_preallocations). */ static int ploop_ctr(struct dm_target *ti, unsigned int argc, char **argv) { @@ -493,6 +489,8 @@ static int ploop_ctr(struct dm_target *ti, unsigned int argc, char **argv) spin_lock_init(&ploop->deferred_lock); init_llist_head(&ploop->llsuspended_pios); + /* A new table stays suspended until the first successful resume */ + ploop->suspended = true; for (i = 0; i < PLOOP_LIST_COUNT; i++) init_llist_head(&ploop->pios[i]); @@ -644,9 +642,6 @@ static int ploop_ctr(struct dm_target *ti, unsigned int argc, char **argv) ti->num_discard_bios = 1; ti->discards_supported = true; - if (!ploop_is_ro(ploop) && ploop->nr_deltas > 0) - ploop_should_prealloc(ploop); - return 0; err: @@ -726,6 +721,11 @@ static void ploop_set_suspended(struct dm_target *ti, bool suspended) up_write(&ploop->ctl_rwsem); } +static bool ploop_is_standby(struct ploop *ploop) +{ + return READ_ONCE(ploop->standby); +} + static void ploop_presuspend(struct dm_target *ti) { struct ploop *ploop = ti->private; @@ -750,16 +750,49 @@ static void ploop_presuspend_undo(struct dm_target *ti) } static void ploop_postsuspend(struct dm_target *ti) { + struct ploop *ploop = ti->private; + + /* + * Let the next resumed ploop (maybe of reloaded table) + * see final size of the top delta. Don't wait in standby + * mode: the allocator may be stuck on the dead file, while + * dm_replace() of libploop must be able to suspend the device + * to recover it. The wait is killable for the same reason. + * If the wait is skipped or killed, a ploop of a reloaded + * table may see a stale size, and a late truncate of our + * allocator may shrink the file under it. + */ + if (!ploop_is_standby(ploop)) + ploop_wait_prealloc(ploop); + /* + * Data bios are completed before BAT writeback is finished + * (see ploop_put_piwb()), so wait for it explicitly to make + * md pages clean for ploop_reload_metadata(). + */ + wait_event(ploop->inflight_md_wq, !atomic_read(&ploop->inflight_md)); ploop_set_suspended(ti, true); } static int ploop_preresume(struct dm_target *ti) { struct ploop *ploop = ti->private; + unsigned int noio_flags; + bool reload = false; int ret = 0; down_write(&ploop->ctl_rwsem); - if (ploop->noresume) + if (ploop->noresume) { ret = -EAGAIN; + } else if (!ploop_is_standby(ploop) && + ploop_no_pending_prealloc(ploop)) { + reload = true; + /* + * The device is suspended, so reclaim must not wait + * for IO to it. Otherwise we deadlock with ourselves. + */ + noio_flags = memalloc_noio_save(); + ret = ploop_reload_metadata(ploop); + memalloc_noio_restore(noio_flags); + } /* * We are singleton target. There will be no more reasons to break * resume. Clear it under the same lock as the noresume check, so @@ -769,8 +802,15 @@ static int ploop_preresume(struct dm_target *ti) ploop->suspended = false; up_write(&ploop->ctl_rwsem); - if (ret == 0) + if (ret == 0) { + /* + * Preallocate only after the metadata is reloaded, + * as prealloc relies on the actual top delta size. + */ + if (reload && !ploop_is_ro(ploop)) + ploop_should_prealloc(ploop); ploop_set_wants_suspend(ti, false); + } return ret; } ALLOW_ERROR_INJECTION(ploop_preresume, ERRNO); diff --git a/drivers/md/dm-ploop.h b/drivers/md/dm-ploop.h index 5041fc8f1e13..5750eafc8a1b 100644 --- a/drivers/md/dm-ploop.h +++ b/drivers/md/dm-ploop.h @@ -245,6 +245,13 @@ struct ploop { /* Resume is prohibited */ bool noresume; + /* Top delta was swapped by flip_upper_deltas */ + bool top_flipped; + /* + * This ploop switched the queue into standby. Unlike the queue + * bit, it is not cleared by ploop_ctr() of a new table. + */ + bool standby; /* Device is suspended */ bool suspended; /* Device wants suspend */ @@ -633,7 +640,9 @@ extern struct pio *ploop_alloc_pio_with_pages(struct ploop *ploop); extern void ploop_free_pio_with_pages(struct ploop *ploop, struct pio *pio); extern void ploop_pio_prepare_offsets(struct ploop *ploop, struct pio *pio, u32 clu); -extern int ploop_setup_metadata(struct ploop *ploop, struct page *page); +extern int ploop_setup_metadata(struct ploop *ploop, struct page *page, + bool check_in_use); +extern int ploop_reload_metadata(struct ploop *ploop); extern int ploop_read_delta_metadata(struct ploop *ploop, struct file *file, struct rb_root *md_root, u32 *delta_nr_be); extern void ploop_index_wb_init(struct ploop_index_wb *piwb, @@ -651,6 +660,8 @@ extern void ploop_disable_writeback_delay(struct ploop *ploop); extern void ploop_enable_writeback_delay(struct ploop *ploop); extern void ploop_should_prealloc(struct ploop *ploop); +extern void ploop_wait_prealloc(struct ploop *ploop); +extern int ploop_no_pending_prealloc(struct ploop *ploop); extern void ploop_resubmit_enospc_pios(struct ploop *ploop); #endif /* __DM_PLOOP_H */ -- 2.53.0 _______________________________________________ Devel mailing list Devel@openvz.org https://lists.openvz.org/mailman/listinfo/devel