Additional patches for 10.3 lvm2

Resolves: RHEL-188186 RHEL-164001
This commit is contained in:
Marian Csontos 2026-07-02 15:57:33 +02:00
parent 80e131be0b
commit 41a5f705f1
10 changed files with 1242 additions and 0 deletions

View File

@ -0,0 +1,47 @@
From b185eefe423ad4292edac96456c45fe3da3402a1 Mon Sep 17 00:00:00 2001
From: David Teigland <teigland@redhat.com>
Date: Thu, 4 Jun 2026 15:36:29 -0500
Subject: [PATCH 06/16] fix lvmpersist WEAR/EAAR reserve race when multiple
nodes start simultaneously
When multiple nodes run "vgchange --persist start" in parallel, the
check-then-reserve sequence for WEAR/EAAR reservations can race: a node
checks that no reservation exists, but by the time it calls resv-acquire,
another node has already created the reservation. The acquire fails and
lvmpersist treats it as fatal, undoing its registration and failing the
start.
After a failed acquire, re-check whether the expected reservation type
now exists. If another registrant completed the reservation, the node
is already protected under the all-registrants scope, so treat it as
success.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
(cherry picked from commit a01615872e5ba97fecb16c3c67d14186c1ef91ca)
---
scripts/lvmpersist.sh | 9 +++++++++
1 file changed, 9 insertions(+)
diff --git a/scripts/lvmpersist.sh b/scripts/lvmpersist.sh
index 4cc0ba300..02ab245f0 100755
--- a/scripts/lvmpersist.sh
+++ b/scripts/lvmpersist.sh
@@ -766,6 +766,15 @@ do_start() {
fi
if [[ "$?" -ne 0 ]]; then
+ # For WEAR/EAAR, another host may have acquired the
+ # reservation between our check and our acquire attempt.
+ # Re-check: if the reservation now exists, that's fine.
+ if [[ "$type_str" == "WEAR" || "$type_str" == "EAAR" ]]; then
+ get_dev_reservation "$dev"
+ if [[ "$DEV_PRDESC" == "$type_str" ]]; then
+ continue
+ fi
+ fi
logmsg "start $GROUP failed to reserve $dev."
undo_register
exit 1
--
2.54.0

View File

@ -0,0 +1,34 @@
From a186787a056bf0f4f424cfa1ce9dec4ee22492f2 Mon Sep 17 00:00:00 2001
From: David Teigland <teigland@redhat.com>
Date: Wed, 24 Jun 2026 13:17:07 -0500
Subject: [PATCH 07/16] man lvmlockd: fix invalid vgchange --setpersist
--setlockargs
(cherry picked from commit c19a1561e031e3633ffc83eeb9daf10a9d08e3aa)
---
man/lvmlockd.8_main | 8 ++++++--
1 file changed, 6 insertions(+), 2 deletions(-)
diff --git a/man/lvmlockd.8_main b/man/lvmlockd.8_main
index dc8865a4c..a7a17c4df 100644
--- a/man/lvmlockd.8_main
+++ b/man/lvmlockd.8_main
@@ -382,9 +382,13 @@ For more information on using PR with LVM in general, see
.P
The vgcreate options to enable PR-based lock recovery in a new VG are shown above.
.P
-To enable PR-based lock recovery in an existing VG:
+To enable PR-based lock recovery in an existing VG, first enable PR (if
+not already), then change the lock args:
.br
-.B $ vgchange --setpersist y --setlockargs persist,notimeout
+.B $ vgchange --setpersist y
+.I VG
+.br
+.B $ vgchange --setlockargs persist,notimeout
.I VG
.P
Changing the PR or lockargs settings requires the VG to be stopped on all
--
2.54.0

View File

@ -0,0 +1,46 @@
From 559cb88b10214befbb786ba1a51f892a9a4c47bd Mon Sep 17 00:00:00 2001
From: David Teigland <teigland@redhat.com>
Date: Tue, 23 Jun 2026 14:01:48 -0500
Subject: [PATCH 08/16] lvmpersist: settle udev between register and
preempt-abort
sg_persist REGISTER triggers udev to re-probe the device (blkid,
scsi_id), issuing ~50 reads over ~14ms. do_takeover() follows
REGISTER with PREEMPT AND ABORT only ~3ms later, while probing
reads are still in-flight on the same iSCSI connection.
On the LIO target, core_tmr_drain_state_list() finds those reads
as in-flight commands and blocks, forming a deadlock with the TX
thread's iscsit_close_connection().
Add udevadm settle between the register and preempt-abort loops
so udev probing completes before the preempt-abort is issued.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
(cherry picked from commit 0080e310bf82f91d737f4ec4e4be8454bd7361af)
---
scripts/lvmpersist.sh | 8 ++++++++
1 file changed, 8 insertions(+)
diff --git a/scripts/lvmpersist.sh b/scripts/lvmpersist.sh
index 02ab245f0..b91f456f3 100755
--- a/scripts/lvmpersist.sh
+++ b/scripts/lvmpersist.sh
@@ -682,6 +682,14 @@ do_takeover() {
exit 1
fi
+ # The register above triggers udev to re-probe the device (blkid,
+ # scsi_id). Those probing reads share the iSCSI connection with
+ # the preempt-abort below. If probing is still in-flight when the
+ # preempt-abort arrives at the LIO target, the target deadlocks in
+ # core_tmr_drain_state_list (waiting for in-flight reads) vs
+ # iscsit_close_connection (waiting for RX thread exit).
+ udevadm settle
+
# Reserve the device
for dev in "${DEVICES[@]}"; do
--
2.54.0

View File

@ -0,0 +1,49 @@
From a8871780844b22491314ab49b60cc10d802d9aa2 Mon Sep 17 00:00:00 2001
From: David Teigland <teigland@redhat.com>
Date: Thu, 25 Jun 2026 11:50:38 -0500
Subject: [PATCH 09/16] man lvmlockd: improve setlockargs description
(cherry picked from commit 41cbab67e9e21ac736cfe4f25ecf8d72db508358)
---
man/lvmlockd.8_main | 19 +++++++++++++++----
1 file changed, 15 insertions(+), 4 deletions(-)
diff --git a/man/lvmlockd.8_main b/man/lvmlockd.8_main
index a7a17c4df..c86a0a130 100644
--- a/man/lvmlockd.8_main
+++ b/man/lvmlockd.8_main
@@ -383,16 +383,27 @@ For more information on using PR with LVM in general, see
The vgcreate options to enable PR-based lock recovery in a new VG are shown above.
.P
To enable PR-based lock recovery in an existing VG, first enable PR (if
-not already), then change the lock args:
+not already), then change the lock args. Changing lock args can involve
+stopping/starting PR and locking at different stages. Changing the PR or
+lock args settings requires the VG to be stopped on all other nodes.
+.P
+.I Example
+.br
+.B $ vgcreate --shared
+.I VG
+.I devices
.br
.B $ vgchange --setpersist y
.I VG
.br
+.B $ vgchange --persist start
+.I VG
+.br
.B $ vgchange --setlockargs persist,notimeout
.I VG
-.P
-Changing the PR or lockargs settings requires the VG to be stopped on all
-other nodes.
+.br
+.B $ vgchange --persist start --lockstart
+.I VG
.P
Display the VG attributes configured by setpersist and setlockargs:
.br
--
2.54.0

View File

@ -0,0 +1,374 @@
From fc77a2a50faa1d1e6b10310f84384d2ad18dec47 Mon Sep 17 00:00:00 2001
From: Zdenek Kabelac <zkabelac@redhat.com>
Date: Sun, 26 Apr 2026 11:35:09 +0200
Subject: [PATCH 10/16] vdo: add pre-format pool geometry computation
Add vdo_format.c with vdo_pool_info() that replicates the vdoformat
pool layout calculation: given a pool's physical size and VDO
parameters, compute UDS index size, slab count, per-slab data blocks,
and usable data blocks (accounting for block map forest overhead).
Return results in struct vdo_pool_info so callers can both set
the correct virtual size before kernel formatting and report pool
geometry (slab count, addressable capacity, growth limits).
(cherry picked from commit d5298389476a869a7e650f0188d6830202986ac6)
---
lib/Makefile.in | 1 +
lib/metadata/metadata-exported.h | 10 +
lib/metadata/vdo_format.c | 310 +++++++++++++++++++++++++++++++
3 files changed, 321 insertions(+)
create mode 100644 lib/metadata/vdo_format.c
diff --git a/lib/Makefile.in b/lib/Makefile.in
index bffe5dddc..667534967 100644
--- a/lib/Makefile.in
+++ b/lib/Makefile.in
@@ -94,6 +94,7 @@ SOURCES =\
metadata/segtype.c \
metadata/snapshot_manip.c \
metadata/thin_manip.c \
+ metadata/vdo_format.c \
metadata/vdo_manip.c \
metadata/vg.c \
mirror/mirrored.c \
diff --git a/lib/metadata/metadata-exported.h b/lib/metadata/metadata-exported.h
index 4318477f4..1f78c7aac 100644
--- a/lib/metadata/metadata-exported.h
+++ b/lib/metadata/metadata-exported.h
@@ -1388,6 +1388,16 @@ const char *get_vdo_index_state_name(enum dm_vdo_index_state state);
const char *get_vdo_operating_mode_name(enum dm_vdo_operating_mode mode);
const char *get_vdo_write_policy_name(enum dm_vdo_write_policy policy);
uint64_t get_vdo_pool_virtual_size(const struct lv_segment *vdo_pool_seg);
+struct vdo_pool_info {
+ uint64_t data_blocks;
+ uint64_t uds_blocks;
+ unsigned slab_count;
+ unsigned data_per_slab;
+};
+
+int vdo_pool_info(uint64_t pool_size_sectors,
+ const struct dm_vdo_target_params *vtp,
+ struct vdo_pool_info *info);
int update_vdo_pool_virtual_size(struct lv_segment *vdo_pool_seg);
uint32_t get_vdo_pool_max_extents(const struct dm_vdo_target_params *vtp,
uint32_t extent_size);
diff --git a/lib/metadata/vdo_format.c b/lib/metadata/vdo_format.c
new file mode 100644
index 000000000..48b0e6a2c
--- /dev/null
+++ b/lib/metadata/vdo_format.c
@@ -0,0 +1,310 @@
+/*
+ * Copyright (C) 2026 Red Hat, Inc. All rights reserved.
+ *
+ * This file is part of LVM2.
+ *
+ * This copyrighted material is made available to anyone wishing to use,
+ * modify, copy, or redistribute it subject to the terms and conditions
+ * of the GNU Lesser General Public License v.2.1.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this program; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
+ */
+
+#include "lib/misc/lib.h"
+#include "lib/metadata/metadata.h"
+
+#include <math.h>
+
+/*
+ * Compute VDO data blocks available for storing compressed/deduplicated content.
+ *
+ * Replicates the vdoformat calculation: given a pool's physical size and
+ * VDO parameters, compute the usable data blocks at 1:1 mapping
+ * (accounting for UDS index, slab overhead, and block map forest).
+ */
+
+/* UDS index geometry constants */
+#define UDS_BLOCK_SIZE 4096U
+#define UDS_BYTES_PER_RECORD 32U
+#define UDS_BYTES_PER_PAGE 32768U
+#define UDS_RECORDS_PER_PAGE (UDS_BYTES_PER_PAGE / UDS_BYTES_PER_RECORD) /* 1024 */
+#define UDS_DEFAULT_CHAPTERS 1024U
+#define UDS_SMALL_RPC 64U
+#define UDS_DEFAULT_RPC 256U
+#define UDS_DELTA_LIST_SIZE 256U
+#define UDS_MAX_ZONES 16U
+#define UDS_MAX_SAVES 2U
+#define UDS_HEADER_SIZE 19U /* immutable delta index header */
+#define UDS_CHAPTER_MEAN_DELTA_BITS 16U /* chapter mean_delta = 1<<16 */
+#define UDS_VOLUME_INDEX_MEAN_DELTA 4096U
+#define UDS_SPARSE_SAMPLE_RATE 32U
+
+/* UDS on-disk structure sizes */
+#define UDS_SIZEOF_DI_HEADER 40U
+#define UDS_SIZEOF_DL_SAVE_INFO 8U
+#define UDS_SIZEOF_DP_HEADER 20U
+#define UDS_SIZEOF_SUB_INDEX_DATA 40U
+#define UDS_SIZEOF_VI_DATA 12U /* magic[8] + u32 sparse_sample_rate */
+#define UDS_PAGE_MAP_MAGIC_LEN 8U
+#define UDS_OPEN_CHAPTER_MAGIC_LEN (sizeof("ALBOC") - 1)
+#define UDS_OPEN_CHAPTER_VER_LEN (sizeof("02.00") - 1)
+
+/* VDO layout constants */
+#define VDO_BLOCK_MAP_TREE_HEIGHT 5U
+#define VDO_BLOCK_MAP_ROOT_COUNT 60U
+#define VDO_BLOCK_MAP_ENTRIES_PER_PAGE 812U
+#define VDO_SLAB_JOURNAL_SIZE 224U /* blocks per slab for journal */
+#define VDO_SLAB_SUMMARY_BLOCKS 64U
+#define VDO_RECOVERY_JOURNAL_SIZE 32768U
+#define VDO_COUNTS_PER_BLOCK 4032U /* reference counts per block */
+
+static unsigned _bits_per(unsigned n)
+{
+ if (!n)
+ return 0;
+
+ return 32 - clz(n);
+}
+
+static unsigned _delta_min_bits(unsigned mean_delta)
+{
+ unsigned incr_keys = (unsigned)round(log(2.0) * mean_delta);
+
+ return _bits_per(incr_keys + 1);
+}
+
+/* Total bits for one chapter's delta index */
+static uint64_t _delta_index_size(uint64_t entry_count, unsigned mean_delta,
+ unsigned payload_bits)
+{
+ unsigned min_bits = _delta_min_bits(mean_delta);
+
+ return entry_count * (payload_bits + min_bits + 1) + entry_count / 2;
+}
+
+/* Number of delta index pages needed for one chapter */
+static unsigned _delta_index_pages(uint64_t entry_count, unsigned list_count,
+ unsigned mean_delta, unsigned payload_bits)
+{
+ uint64_t total_bits = _delta_index_size(entry_count, mean_delta, payload_bits);
+ unsigned bits_per_list = (unsigned)(total_bits / list_count);
+ unsigned bits_per_page;
+
+ total_bits += (uint64_t)list_count * UDS_HEADER_SIZE;
+ bits_per_page = (UDS_BYTES_PER_PAGE - UDS_SIZEOF_DP_HEADER) * 8 -
+ UDS_HEADER_SIZE - bits_per_list;
+
+ return (unsigned)dm_div_up(total_bits, bits_per_page);
+}
+
+/* Save bytes for one volume sub-index */
+static uint64_t _sub_index_save_bytes(unsigned records_per_chapter,
+ unsigned chapters, unsigned vi_mean)
+{
+ uint64_t delta_list_records = (uint64_t)records_per_chapter * chapters;
+ unsigned list_count = (unsigned)(delta_list_records / UDS_DELTA_LIST_SIZE);
+ unsigned addr_bits, chapter_bits, invalid_chapters;
+ unsigned chapters_in_vi;
+ uint64_t entries_in_vi, addr_span;
+ unsigned mean_delta;
+ uint64_t chapter_size_bits, index_size_bytes, memory_size;
+ uint64_t zone_memory, di_save;
+
+ if (list_count < UDS_MAX_ZONES * UDS_MAX_ZONES)
+ list_count = UDS_MAX_ZONES * UDS_MAX_ZONES;
+
+ addr_bits = _bits_per(vi_mean * UDS_DELTA_LIST_SIZE - 1);
+ chapter_bits = _bits_per(chapters - 1);
+ invalid_chapters = chapters / 256;
+ if (invalid_chapters < 2)
+ invalid_chapters = 2;
+ chapters_in_vi = chapters + invalid_chapters;
+ entries_in_vi = (uint64_t)records_per_chapter * chapters_in_vi;
+ addr_span = (uint64_t)list_count << addr_bits;
+ mean_delta = (unsigned)(addr_span / entries_in_vi);
+
+ chapter_size_bits = _delta_index_size(records_per_chapter, mean_delta,
+ chapter_bits);
+ index_size_bytes = (chapter_size_bits * chapters_in_vi) / 8;
+ /* add 6% headroom to reduce delta list rebalancing when index is full */
+ memory_size = index_size_bytes * 106 / 100;
+
+ /* zone_mem(1, memory_size): round up to 64K */
+ zone_memory = dm_div_up(memory_size, 65536U) * 65536U;
+
+ /* delta_index save = header + list_count*(save_info+1) + zone_memory */
+ di_save = UDS_SIZEOF_DI_HEADER + zone_memory +
+ (uint64_t)list_count * (UDS_SIZEOF_DL_SAVE_INFO + 1);
+
+ /* sub_index save = sub_index_data + list_count*8 + di_save */
+ return UDS_SIZEOF_SUB_INDEX_DATA + (uint64_t)list_count * 8 + di_save;
+}
+
+/*
+ * Compute total UDS index size in 4K blocks.
+ *
+ * index_memory_mb: UDS memory config (256, 512, 768, or N*1024)
+ * use_sparse: nonzero for sparse index
+ */
+static uint64_t _uds_index_blocks(unsigned index_memory_mb, int use_sparse)
+{
+ unsigned base_chapters, rpc, chapters, sparse_chapters = 0;
+ unsigned records_per_chapter, chapter_payload_bits;
+ unsigned chapter_dl_bits, delta_lists_per_chapter;
+ unsigned ippc, pages_per_chapter;
+ uint64_t pages_per_volume, bytes_per_volume, volume_blocks;
+ uint64_t vi_save_bytes, vi_blocks;
+ unsigned page_map_blocks, open_chapter_blocks;
+ unsigned save_blocks;
+
+ /* Map memory config to chapters and records-per-page */
+ if (index_memory_mb <= 256) {
+ base_chapters = UDS_DEFAULT_CHAPTERS;
+ rpc = UDS_SMALL_RPC;
+ } else if (index_memory_mb <= 512) {
+ base_chapters = UDS_DEFAULT_CHAPTERS;
+ rpc = 2 * UDS_SMALL_RPC;
+ } else if (index_memory_mb <= 768) {
+ base_chapters = UDS_DEFAULT_CHAPTERS;
+ rpc = 3 * UDS_SMALL_RPC;
+ } else {
+ base_chapters = (index_memory_mb / 1024) * UDS_DEFAULT_CHAPTERS;
+ rpc = UDS_DEFAULT_RPC;
+ }
+
+ /* sparse index: 10x total chapters, 95% sparse (19/2 sparse per base chapter) */
+ if (use_sparse) {
+ sparse_chapters = (19 * base_chapters) / 2;
+ chapters = base_chapters * 10;
+ } else {
+ chapters = base_chapters;
+ }
+
+ /* Geometry */
+ records_per_chapter = UDS_RECORDS_PER_PAGE * rpc;
+ chapter_payload_bits = _bits_per(rpc - 1);
+ chapter_dl_bits = _bits_per((records_per_chapter - 1) | 077) - 6;
+ delta_lists_per_chapter = 1U << chapter_dl_bits;
+ ippc = _delta_index_pages(records_per_chapter, delta_lists_per_chapter,
+ 1U << UDS_CHAPTER_MEAN_DELTA_BITS,
+ chapter_payload_bits);
+ pages_per_chapter = ippc + rpc;
+ pages_per_volume = (uint64_t)pages_per_chapter * chapters;
+ bytes_per_volume = UDS_BYTES_PER_PAGE * (pages_per_volume + 1);
+ volume_blocks = bytes_per_volume / UDS_BLOCK_SIZE;
+
+ /* Volume index save blocks */
+ if (!use_sparse) {
+ vi_save_bytes = _sub_index_save_bytes(records_per_chapter,
+ chapters,
+ UDS_VOLUME_INDEX_MEAN_DELTA);
+ } else {
+ unsigned hook_rec = records_per_chapter / UDS_SPARSE_SAMPLE_RATE;
+ unsigned non_hook_rec = records_per_chapter - hook_rec;
+ unsigned dense_chapters = chapters - sparse_chapters;
+ uint64_t hook_bytes, non_hook_bytes;
+
+ hook_bytes = _sub_index_save_bytes(hook_rec, chapters,
+ UDS_VOLUME_INDEX_MEAN_DELTA);
+ non_hook_bytes = _sub_index_save_bytes(non_hook_rec,
+ dense_chapters,
+ UDS_VOLUME_INDEX_MEAN_DELTA);
+ vi_save_bytes = UDS_SIZEOF_VI_DATA + hook_bytes + non_hook_bytes;
+ }
+ vi_blocks = dm_div_up(vi_save_bytes + UDS_SIZEOF_DL_SAVE_INFO,
+ UDS_BLOCK_SIZE) + UDS_MAX_ZONES;
+
+ /* Page map and open chapter save blocks */
+ page_map_blocks =
+ (unsigned)dm_div_up(UDS_PAGE_MAP_MAGIC_LEN + 8 + (uint64_t)chapters * (ippc - 1) * 2,
+ UDS_BLOCK_SIZE);
+ open_chapter_blocks =
+ (unsigned)dm_div_up(UDS_OPEN_CHAPTER_MAGIC_LEN + UDS_OPEN_CHAPTER_VER_LEN + 4 +
+ (uint64_t)records_per_chapter * UDS_BYTES_PER_RECORD,
+ UDS_BLOCK_SIZE);
+
+ save_blocks = 1 + vi_blocks + page_map_blocks + open_chapter_blocks;
+
+ /* total = header(3) + volume + MAX_SAVES * save */
+ return 3 + volume_blocks + UDS_MAX_SAVES * save_blocks;
+}
+
+/*
+ * Compute block map forest overhead: the number of blocks consumed
+ * by the 5-level block map tree when mapping data_blocks entries.
+ * Tree roots (level 3) and super roots (level 4) are not allocated
+ * from slabs, so they are excluded.
+ */
+static uint64_t _forest_overhead(uint64_t data_blocks)
+{
+ uint64_t leaf_pages, level_size, non_leaves, leaves;
+ unsigned levels[VDO_BLOCK_MAP_TREE_HEIGHT];
+ unsigned h;
+
+ leaf_pages = dm_div_up(data_blocks, VDO_BLOCK_MAP_ENTRIES_PER_PAGE);
+ level_size = dm_div_up(leaf_pages, VDO_BLOCK_MAP_ROOT_COUNT);
+
+ for (h = 0; h < VDO_BLOCK_MAP_TREE_HEIGHT; h++) {
+ level_size = dm_div_up(level_size, VDO_BLOCK_MAP_ENTRIES_PER_PAGE);
+ levels[h] = (unsigned)level_size;
+ }
+
+ non_leaves = 0;
+ for (h = 0; h < VDO_BLOCK_MAP_TREE_HEIGHT; h++)
+ non_leaves += (uint64_t)levels[h] * VDO_BLOCK_MAP_ROOT_COUNT;
+
+ /* Exclude tree roots and super roots (not from slabs) */
+ non_leaves -= (levels[VDO_BLOCK_MAP_TREE_HEIGHT - 2] +
+ levels[VDO_BLOCK_MAP_TREE_HEIGHT - 1]) *
+ VDO_BLOCK_MAP_ROOT_COUNT;
+
+ leaves = dm_div_up(data_blocks - non_leaves,
+ VDO_BLOCK_MAP_ENTRIES_PER_PAGE);
+
+ return non_leaves + leaves;
+}
+
+int vdo_pool_info(uint64_t pool_size_sectors,
+ const struct dm_vdo_target_params *vtp,
+ struct vdo_pool_info *info)
+{
+ uint64_t pool_4k = pool_size_sectors / DM_VDO_BLOCK_SIZE;
+ unsigned slab_4k = (unsigned)((uint64_t)vtp->slab_size_mb * (1048576 / UDS_BLOCK_SIZE));
+ uint64_t offset, depot_blocks;
+ unsigned ref_blocks;
+ uint64_t total_data, forest;
+
+ memset(info, 0, sizeof(*info));
+
+ info->uds_blocks = _uds_index_blocks(vtp->index_memory_size_mb,
+ vtp->use_sparse_index);
+
+ /* Layout: geometry(1) + UDS + super(1) + roots + slab_summary + recovery_journal */
+ offset = info->uds_blocks + 2;
+ if (pool_4k <= offset + VDO_BLOCK_MAP_ROOT_COUNT +
+ VDO_SLAB_SUMMARY_BLOCKS + VDO_RECOVERY_JOURNAL_SIZE)
+ return 0;
+
+ depot_blocks = pool_4k - offset - VDO_BLOCK_MAP_ROOT_COUNT -
+ VDO_SLAB_SUMMARY_BLOCKS - VDO_RECOVERY_JOURNAL_SIZE;
+
+ info->slab_count = (unsigned)(depot_blocks / slab_4k);
+ if (!info->slab_count)
+ return 0;
+
+ /* Slab layout: journal(224) + ref_blocks + data_blocks = slab_4k */
+ ref_blocks = (unsigned)dm_div_up(slab_4k - VDO_SLAB_JOURNAL_SIZE,
+ VDO_COUNTS_PER_BLOCK + 1);
+ info->data_per_slab = slab_4k - VDO_SLAB_JOURNAL_SIZE - ref_blocks;
+ total_data = (uint64_t)info->data_per_slab * info->slab_count;
+
+ forest = _forest_overhead(total_data);
+ if (forest >= total_data)
+ return 0;
+
+ info->data_blocks = total_data - forest;
+
+ return 1;
+}
--
2.54.0

View File

@ -0,0 +1,239 @@
From efdcd4b2f8c2f25e9db8e3b217de85d8cba54aab Mon Sep 17 00:00:00 2001
From: Bruce Johnston <bjohnsto@redhat.com>
Date: Wed, 17 Dec 2025 12:40:44 -0500
Subject: [PATCH 11/16] vdo: add support for VDO kernel direct formatting
VDO has added a feature (dm-vdo 9.2.0+) to format devices directly
in its kernel code vs using the vdoformat userspace tool. Add support
for this via a new config parameter vdo_use_kernel_format.
When vdo_use_kernel_format=1, LVM zeroes the first 4KiB of the pool
data LV and passes slabSize, indexMemory, and indexSparse parameters
on the device-mapper table line so the kernel formats on activation.
The use_kernel_format flag is persisted in VDO pool metadata so it
survives reactivation. Old metadata without the flag defaults to 0
(userspace vdoformat), ensuring backward compatibility.
Signed-off-by: Bruce Johnston <bjohnsto@redhat.com>
Made-with: Cursor
(cherry picked from commit 7642c5b4ab0c87ed6dc31bcb56e2de40a45045ce)
---
lib/config/config_settings.h | 10 ++++++++--
lib/config/defaults.h | 1 +
lib/metadata/segtype.h | 1 +
lib/metadata/vdo_manip.c | 32 +++++++++++++++++++++++++++++---
lib/vdo/vdo.c | 6 ++++++
libdm/libdevmapper.h | 2 ++
libdm/libdm-deptree.c | 20 ++++++++++++++++++++
tools/toollib.c | 1 +
8 files changed, 68 insertions(+), 5 deletions(-)
diff --git a/lib/config/config_settings.h b/lib/config/config_settings.h
index 8a1872b62..92d5f9e40 100644
--- a/lib/config/config_settings.h
+++ b/lib/config/config_settings.h
@@ -782,6 +782,12 @@ cfg(allocation_vdo_check_point_frequency_CFG, "vdo_check_point_frequency", alloc
cfg(allocation_vdo_use_sparse_index_CFG, "vdo_use_sparse_index", allocation_CFG_SECTION, CFG_PROFILABLE | CFG_PROFILABLE_METADATA | CFG_DEFAULT_COMMENTED, CFG_TYPE_BOOL, DEFAULT_VDO_USE_SPARSE_INDEX, VDO_1ST_VSN, NULL, 0, NULL,
"Enables sparse indexing for VDO volume.\n")
+/* vdo format */
+cfg(allocation_vdo_use_kernel_format_CFG, "vdo_use_kernel_format", allocation_CFG_SECTION, CFG_PROFILABLE | CFG_PROFILABLE_METADATA | CFG_DEFAULT_COMMENTED, CFG_TYPE_BOOL, DEFAULT_VDO_USE_KERNEL_FORMAT, vsn(2, 3, 41), NULL, 0, NULL,
+ "Use kernel direct formatting for VDO volume.\n"
+ "When enabled (1), uses kernel direct formatting (requires dm-vdo 9.2.0+).\n"
+ "When disabled (0), uses traditional userspace vdoformat tool.\n")
+
/* vdo format */
cfg(allocation_vdo_index_memory_size_mb_CFG, "vdo_index_memory_size_mb", allocation_CFG_SECTION, CFG_PROFILABLE | CFG_PROFILABLE_METADATA | CFG_DEFAULT_COMMENTED, CFG_TYPE_INT, DEFAULT_VDO_INDEX_MEMORY_SIZE_MB, VDO_1ST_VSN, NULL, 0, NULL,
"Specifies the amount of index memory in MiB for VDO volume.\n"
@@ -1336,10 +1342,10 @@ cfg_array(global_vdo_format_options_CFG, "vdo_format_options", global_CFG_SECTIO
cfg_array(global_vdo_disabled_features_CFG, "vdo_disabled_features", global_CFG_SECTION, CFG_ALLOW_EMPTY | CFG_DEFAULT_UNDEFINED, CFG_TYPE_STRING, NULL, vsn(2, 3, 11), NULL, 0, NULL,
"Features to not use in the vdo driver.\n"
"This can be helpful for testing, or to avoid using a feature that is\n"
- "causing problems. Features include: online_rename, version4\n"
+ "causing problems. Features include: online_rename, version4, kernel_format\n"
"#\n"
"Example\n"
- "vdo_disabled_features = [ \"online_rename\", \"version4\" ]\n"
+ "vdo_disabled_features = [ \"online_rename\", \"version4\", \"kernel_format\" ]\n"
"#\n")
cfg(global_fsadm_executable_CFG, "fsadm_executable", global_CFG_SECTION, CFG_DEFAULT_COMMENTED, CFG_TYPE_STRING, DEFAULT_FSADM_PATH, vsn(2, 2, 170), "@FSADM_PATH@", 0, NULL,
diff --git a/lib/config/defaults.h b/lib/config/defaults.h
index c9c19dcef..f0cbddf72 100644
--- a/lib/config/defaults.h
+++ b/lib/config/defaults.h
@@ -169,6 +169,7 @@
#define DEFAULT_VDO_BLOCK_MAP_CACHE_SIZE_MB (DM_VDO_BLOCK_MAP_CACHE_SIZE_MINIMUM_MB)
#define DEFAULT_VDO_BLOCK_MAP_ERA_LENGTH (DM_VDO_BLOCK_MAP_ERA_LENGTH_MAXIMUM)
#define DEFAULT_VDO_USE_SPARSE_INDEX 0
+#define DEFAULT_VDO_USE_KERNEL_FORMAT 1
#define DEFAULT_VDO_CHECK_POINT_FREQUENCY (0)
#define DEFAULT_VDO_INDEX_MEMORY_SIZE_MB (DM_VDO_INDEX_MEMORY_SIZE_MINIMUM_MB)
#define DEFAULT_VDO_SLAB_SIZE_MB (2 * 1024) /* 2GiB ... 19 slabbits */
diff --git a/lib/metadata/segtype.h b/lib/metadata/segtype.h
index b53859d9d..64650579b 100644
--- a/lib/metadata/segtype.h
+++ b/lib/metadata/segtype.h
@@ -356,6 +356,7 @@ int init_vdo_segtypes(struct cmd_context *cmd, struct segtype_library *seglib);
#define VDO_FEATURE_ONLINE_RENAME (1U << 0) /* version 6.2.3 */
#define VDO_FEATURE_VERSION4 (1U << 1) /* version 8.2.0 */
+#define VDO_FEATURE_KERNEL_FORMAT (1U << 2) /* version 9.2.0 */
int init_writecache_segtypes(struct cmd_context *cmd, struct segtype_library *seglib);
diff --git a/lib/metadata/vdo_manip.c b/lib/metadata/vdo_manip.c
index 9d1c7305e..0b4f2d3fe 100644
--- a/lib/metadata/vdo_manip.c
+++ b/lib/metadata/vdo_manip.c
@@ -390,6 +390,8 @@ int convert_vdo_pool_lv(struct logical_volume *data_lv,
struct lv_segment *vdo_pool_seg;
uint64_t vdo_logical_size = 0;
uint64_t adjust;
+ unsigned attrs = 0;
+ int use_kernel = format && vtp->use_kernel_format;
if (!(vdo_pool_segtype = get_segtype_from_string(cmd, SEG_TYPE_NAME_VDO_POOL)))
return_0;
@@ -412,9 +414,30 @@ int convert_vdo_pool_lv(struct logical_volume *data_lv,
if (format) {
if (test_mode()) {
log_verbose("Test mode: Skipping formatting of VDO pool volume.");
- } else if (!_format_vdo_pool_data_lv(data_lv, vtp, &vdo_logical_size)) {
- log_error("Cannot format VDO pool volume %s.", display_lvname(data_lv));
- return 0;
+ } else if (use_kernel) {
+ if (use_kernel &&
+ (!vdo_pool_segtype->ops->target_present ||
+ !vdo_pool_segtype->ops->target_present(cmd, NULL, &attrs) ||
+ !(attrs & VDO_FEATURE_KERNEL_FORMAT))) {
+ log_error("Kernel direct formatting (vdo_use_kernel_format=1) requires VDO target version 9.2.0 or newer.");
+ return 0;
+ }
+
+ /* Kernel decides to format if the first 4k are zeroes */
+ if (!activate_and_wipe_lv(data_lv, WIPE_MODE_DO_ZERO, 0, 0)) {
+ log_error("Aborting. Failed to wipe first 4 KiB of VDO pool volume %s.",
+ display_lvname(data_lv));
+ return 0;
+ }
+
+ if (!*virtual_extents)
+ vdo_logical_size = data_lv->size;
+ } else {
+ /* Traditional userspace vdoformat */
+ if (!_format_vdo_pool_data_lv(data_lv, vtp, &vdo_logical_size)) {
+ log_error("Cannot format VDO pool volume %s.", display_lvname(data_lv));
+ return 0;
+ }
}
} else {
log_verbose("Skipping VDO formatting %s.", display_lvname(data_lv));
@@ -455,6 +478,7 @@ int convert_vdo_pool_lv(struct logical_volume *data_lv,
vdo_pool_seg = first_seg(vdo_pool_lv);
vdo_pool_seg->segtype = vdo_pool_segtype;
vdo_pool_seg->vdo_params = *vtp;
+ vdo_pool_seg->vdo_params.use_kernel_format = use_kernel;
vdo_pool_seg->vdo_pool_header_size = vdo_pool_header_size;
vdo_pool_seg->vdo_pool_virtual_extents = *virtual_extents;
@@ -621,6 +645,8 @@ int fill_vdo_target_params(struct cmd_context *cmd,
find_config_tree_int(cmd, allocation_vdo_block_map_era_length_CFG, profile);
vtp->use_sparse_index =
find_config_tree_bool(cmd, allocation_vdo_use_sparse_index_CFG, profile);
+ vtp->use_kernel_format =
+ find_config_tree_bool(cmd, allocation_vdo_use_kernel_format_CFG, profile);
vtp->index_memory_size_mb =
find_config_tree_int64(cmd, allocation_vdo_index_memory_size_mb_CFG, profile);
vtp->slab_size_mb =
diff --git a/lib/vdo/vdo.c b/lib/vdo/vdo.c
index c3cb2b2a5..1e9d377b5 100644
--- a/lib/vdo/vdo.c
+++ b/lib/vdo/vdo.c
@@ -254,6 +254,9 @@ static int _vdo_pool_text_import(struct lv_segment *seg,
if (!_import_bool(n, "use_sparse_index", &vtp->use_sparse_index))
return_0;
+ if (!_import_bool(n, "use_kernel_format", &vtp->use_kernel_format))
+ return_0;
+
if (!dm_config_get_uint32(n, "index_memory_size_mb", &vtp->index_memory_size_mb))
return _bad_field("index_memory_size_mb");
@@ -327,6 +330,8 @@ static int _vdo_pool_text_export(const struct lv_segment *seg, struct formatter
if (vtp->use_sparse_index)
outf(f, "use_sparse_index = 1");
+ if (vtp->use_kernel_format)
+ outf(f, "use_kernel_format = 1");
/* TODO - conditionally */
outsize(f, vtp->index_memory_size_mb * UINT64_C(2 * 1024),
"index_memory_size_mb = %u", vtp->index_memory_size_mb);
@@ -430,6 +435,7 @@ static int _vdo_target_present(struct cmd_context *cmd,
} _features[] = {
{ 6, 2, 3, VDO_FEATURE_ONLINE_RENAME, "online_rename" },
{ 8, 2, 0, VDO_FEATURE_VERSION4, "version4" },
+ { 9, 2, 0, VDO_FEATURE_KERNEL_FORMAT, "kernel_format" },
};
static const char _lvmconf[] = "global/vdo_disabled_features";
static int _vdo_checked = 0;
diff --git a/libdm/libdevmapper.h b/libdm/libdevmapper.h
index c393c217e..d5066fbda 100644
--- a/libdm/libdevmapper.h
+++ b/libdm/libdevmapper.h
@@ -2167,6 +2167,8 @@ struct dm_vdo_target_params {
/* write policy */
enum dm_vdo_write_policy write_policy;
+
+ int use_kernel_format; /* kernel direct format (added last for ABI compat) */
};
int dm_vdo_validate_target_params(const struct dm_vdo_target_params *vtp,
diff --git a/libdm/libdm-deptree.c b/libdm/libdm-deptree.c
index 0a246f272..02b730bb8 100644
--- a/libdm/libdm-deptree.c
+++ b/libdm/libdm-deptree.c
@@ -3026,6 +3026,26 @@ static int _vdo_emit_segment_line(struct dm_task *dmt, uint32_t major, uint32_t
seg->vdo_params.logical_threads,
seg->vdo_params.physical_threads);
+ /* Add format parameters for kernel direct formatting */
+ if (seg->vdo_params.use_kernel_format) {
+ /* Convert slab size from MB to 4KiB blocks */
+ EMIT_PARAMS(pos, " slabSize " FMTu64,
+ seg->vdo_params.slab_size_mb * UINT64_C(256)); // 1MiB -> 256 * 4KiB blocks
+
+ /* Index memory size: format as GiB if >= 1024MB, otherwise as fraction */
+ if (seg->vdo_params.index_memory_size_mb >= 1024)
+ EMIT_PARAMS(pos, " indexMemory %u", seg->vdo_params.index_memory_size_mb / 1024);
+ else if (seg->vdo_params.index_memory_size_mb >= 768)
+ EMIT_PARAMS(pos, " indexMemory 0.75");
+ else if (seg->vdo_params.index_memory_size_mb >= 512)
+ EMIT_PARAMS(pos, " indexMemory 0.50");
+ else
+ EMIT_PARAMS(pos, " indexMemory 0.25");
+
+ /* Sparse index setting */
+ EMIT_PARAMS(pos, " indexSparse %s", seg->vdo_params.use_sparse_index ? "on" : "off");
+ }
+
return 1;
}
diff --git a/tools/toollib.c b/tools/toollib.c
index 0e6ce5d3a..f91fae701 100644
--- a/tools/toollib.c
+++ b/tools/toollib.c
@@ -1369,6 +1369,7 @@ int get_vdo_settings(struct cmd_context *cmd,
DO_OFFLINE(slab_size_mb);
DO_OFFLINE(use_metadata_hints);
DO_OFFLINE(use_sparse_index);
+ DO_OFFLINE(use_kernel_format);
option = "write_policy";
if (_compare_vdo_option(cn->key, option)) {
--
2.54.0

View File

@ -0,0 +1,355 @@
From cc3b0438480b7bdc140416010e077943543a305a Mon Sep 17 00:00:00 2001
From: Zdenek Kabelac <zkabelac@redhat.com>
Date: Thu, 25 Jun 2026 17:17:32 +0200
Subject: [PATCH 12/16] vdo: add kernel formatting path with LV_VDOFORMAT
metadata flag
Introduce LV_VDOFORMAT status flag persisted in VG metadata so the
VDO pool activation can pass use_kernel_format=1 to the dm-vdo target.
The flag is set before activation and cleared after formatting completes.
Add _format_vdo_pool_kernel() which activates the VDO pool with the
flag set, waits for the VDO index to come online, deactivates, clears
LV_VDOFORMAT and commits metadata.
In convert_vdo_pool_lv(), gracefully fall back to userspace vdoformat
when the VDO target is older than 9.2.0 instead of failing with an
error. For the kernel path, compute pool geometry via vdo_pool_info()
to determine virtual size and report slab layout after formatting.
On failure, revert status flags and remove the data layer.
Correct versioning for dm_tree_node_add_vdo_target as DM_1_02_216 after
adding use_kernel_format to dm_vdo_target_params.
Add ABI compatibility wrapper for 1.02.213 callers whose struct lacks
the new field -- the wrapper zero-initialises use_kernel_format so old
binaries default to userspace vdoformat.
Use a local copy of vdo_params in _vdo_pool_add_target_line() to
avoid mutating in-memory metadata when setting use_kernel_format
(cherry picked from commit 50355f35e5eaad7d44f82c7d2a4c3d39f23527e1)
---
lib/format_text/flags.c | 1 +
lib/metadata/metadata-exported.h | 1 +
lib/metadata/vdo_manip.c | 138 ++++++++++++++++++++++++----
lib/vdo/vdo.c | 7 +-
libdm/.exported_symbols.DM_1_02_216 | 1 +
libdm/libdm-deptree.c | 53 +++++++++--
6 files changed, 176 insertions(+), 25 deletions(-)
create mode 100644 libdm/.exported_symbols.DM_1_02_216
diff --git a/lib/format_text/flags.c b/lib/format_text/flags.c
index 1f81d063a..dde5aabc8 100644
--- a/lib/format_text/flags.c
+++ b/lib/format_text/flags.c
@@ -77,6 +77,7 @@ static const struct flag _lv_flags[] = {
{LV_CROP_METADATA, "CROP_METADATA", SEGTYPE_FLAG},
{LV_CACHE_VOL, "CACHE_VOL", COMPATIBLE_FLAG},
{LV_CACHE_USES_CACHEVOL, "CACHE_USES_CACHEVOL", SEGTYPE_FLAG},
+ {LV_VDOFORMAT, "VDOFORMAT", SEGTYPE_FLAG},
{LV_NOSCAN, NULL, 0},
{LV_TEMPORARY, NULL, 0},
{POOL_METADATA_SPARE, NULL, 0},
diff --git a/lib/metadata/metadata-exported.h b/lib/metadata/metadata-exported.h
index 1f78c7aac..70a00d0bd 100644
--- a/lib/metadata/metadata-exported.h
+++ b/lib/metadata/metadata-exported.h
@@ -156,6 +156,7 @@
#define LV_VDO UINT64_C(0x0000000020000000) /* LV - Internal use only */
#define LV_VDO_POOL UINT64_C(0x0000000040000000) /* LV - Internal use only */
#define LV_VDO_POOL_DATA UINT64_C(0x8000000000000000) /* LV - Internal use only */
+#define LV_VDOFORMAT UINT64_C(0x0000000000000800) /* LV - VDO pool passes format args */
#define LV_CACHE_VOL UINT64_C(0x0010000000000000) /* LV - also a PV flag */
#define LV_CACHE_USES_CACHEVOL UINT64_C(0x4000000000000000) /* LV - also a PV flag */
diff --git a/lib/metadata/vdo_manip.c b/lib/metadata/vdo_manip.c
index 0b4f2d3fe..ca1c34331 100644
--- a/lib/metadata/vdo_manip.c
+++ b/lib/metadata/vdo_manip.c
@@ -16,6 +16,7 @@
#include "lib/metadata/metadata.h"
#include "lib/locking/locking.h"
#include "lib/misc/lvm-string.h"
+#include "lib/misc/lvm-signal.h"
#include "lib/commands/toolcontext.h"
#include "lib/display/display.h"
#include "lib/metadata/segtype.h"
@@ -367,6 +368,73 @@ static int _format_vdo_pool_data_lv(struct logical_volume *data_lv,
return 1;
}
+static int _format_vdo_pool_kernel(struct logical_volume *vdo_pool_lv)
+{
+ struct cmd_context *cmd = vdo_pool_lv->vg->cmd;
+ struct lv_status_vdo *vdo_status;
+ enum dm_vdo_index_state index_state;
+ int r = 0;
+
+ if (!vg_write(vdo_pool_lv->vg) || !vg_commit(vdo_pool_lv->vg))
+ return_0;
+
+ if (!activate_lv(cmd, vdo_pool_lv)) {
+ log_error("Failed to activate VDO pool %s for kernel formatting.",
+ display_lvname(vdo_pool_lv));
+ goto out;
+ }
+
+ sync_local_dev_names(cmd);
+
+ /* Wait for the UDS index to finish initializing */
+ for (;;) {
+ if (!lv_vdo_pool_status(vdo_pool_lv, 0, &vdo_status)) {
+ stack;
+ break;
+ }
+
+ index_state = vdo_status->vdo->index_state;
+ dm_pool_destroy(vdo_status->mem);
+
+ if (index_state == DM_VDO_INDEX_ERROR) {
+ log_error("Failed to format VDO pool %s, index error.",
+ display_lvname(vdo_pool_lv));
+ break;
+ }
+
+ log_verbose("VDO pool %s index state: %s.",
+ display_lvname(vdo_pool_lv),
+ get_vdo_index_state_name(index_state));
+
+ if (index_state != DM_VDO_INDEX_OPENING) {
+ r = 1; /* Initialized */
+ break;
+ }
+
+ /* 50ms: formatting is typically fast, avoid unnecessary delay */
+ if (!sigint_usleep(50000)) {
+ log_warn("WARNING: Interrupted waiting for VDO index on %s.",
+ display_lvname(vdo_pool_lv));
+ break;
+ }
+ }
+
+ if (!deactivate_lv(cmd, vdo_pool_lv)) {
+ log_error("Failed to deactivate VDO pool %s after kernel formatting.",
+ display_lvname(vdo_pool_lv));
+ r = 0;
+ }
+out:
+ vdo_pool_lv->status &= ~LV_VDOFORMAT;
+
+ if (!vg_write(vdo_pool_lv->vg) || !vg_commit(vdo_pool_lv->vg))
+ return_0;
+
+ sync_local_dev_names(cmd);
+
+ return r;
+}
+
/*
* convert_vdo_pool_lv
* @data_lv
@@ -388,6 +456,7 @@ int convert_vdo_pool_lv(struct logical_volume *data_lv,
struct logical_volume *vdo_pool_lv = data_lv;
const struct segment_type *vdo_pool_segtype;
struct lv_segment *vdo_pool_seg;
+ struct vdo_pool_info pinfo = { 0 };
uint64_t vdo_logical_size = 0;
uint64_t adjust;
unsigned attrs = 0;
@@ -414,29 +483,37 @@ int convert_vdo_pool_lv(struct logical_volume *data_lv,
if (format) {
if (test_mode()) {
log_verbose("Test mode: Skipping formatting of VDO pool volume.");
- } else if (use_kernel) {
+ } else {
if (use_kernel &&
(!vdo_pool_segtype->ops->target_present ||
!vdo_pool_segtype->ops->target_present(cmd, NULL, &attrs) ||
!(attrs & VDO_FEATURE_KERNEL_FORMAT))) {
- log_error("Kernel direct formatting (vdo_use_kernel_format=1) requires VDO target version 9.2.0 or newer.");
- return 0;
+ log_verbose("Kernel formatting requires VDO target version 9.2.0 or newer, using vdoformat.");
+ use_kernel = 0;
}
- /* Kernel decides to format if the first 4k are zeroes */
- if (!activate_and_wipe_lv(data_lv, WIPE_MODE_DO_ZERO, 0, 0)) {
- log_error("Aborting. Failed to wipe first 4 KiB of VDO pool volume %s.",
- display_lvname(data_lv));
- return 0;
- }
-
- if (!*virtual_extents)
- vdo_logical_size = data_lv->size;
- } else {
- /* Traditional userspace vdoformat */
- if (!_format_vdo_pool_data_lv(data_lv, vtp, &vdo_logical_size)) {
- log_error("Cannot format VDO pool volume %s.", display_lvname(data_lv));
- return 0;
+ if (use_kernel) {
+ /* Kernel decides to format if the first 4k are zeroes */
+ if (!activate_and_wipe_lv(data_lv, WIPE_MODE_DO_ZERO, 0, 0)) {
+ log_error("Aborting. Failed to wipe first 4 KiB of VDO pool volume %s.",
+ display_lvname(data_lv));
+ return 0;
+ }
+
+ if (!vdo_pool_info(data_lv->size, vtp, &pinfo)) {
+ log_error("Pool %s is too small for the given VDO configuration.",
+ display_lvname(data_lv));
+ return 0;
+ }
+
+ if (!*virtual_extents)
+ vdo_logical_size = pinfo.data_blocks * DM_VDO_BLOCK_SIZE;
+ } else {
+ /* Traditional userspace vdoformat */
+ if (!_format_vdo_pool_data_lv(data_lv, vtp, &vdo_logical_size)) {
+ log_error("Cannot format VDO pool volume %s.", display_lvname(data_lv));
+ return 0;
+ }
}
}
} else {
@@ -485,6 +562,33 @@ int convert_vdo_pool_lv(struct logical_volume *data_lv,
vdo_pool_lv->status |= LV_VDO_POOL;
data_lv->status |= LV_VDO_POOL_DATA;
+ if (use_kernel) {
+ vdo_pool_lv->status |= LV_VDOFORMAT;
+
+ if (!_format_vdo_pool_kernel(vdo_pool_lv)) {
+ log_error("Failed to kernel format VDO pool %s, reverting.",
+ display_lvname(vdo_pool_lv));
+ vdo_pool_lv->status &= ~LV_VDO_POOL;
+ data_lv->status &= ~LV_VDO_POOL_DATA;
+ if (!remove_layer_from_lv(vdo_pool_lv, data_lv))
+ return_0;
+ if (!lv_remove(data_lv))
+ return_0;
+ if (!vg_write(vdo_pool_lv->vg) || !vg_commit(vdo_pool_lv->vg))
+ return_0;
+ return 0;
+ }
+
+ log_print_unless_silent(" The VDO volume can address %s in %u data slabs, each %s.",
+ display_size(cmd, (uint64_t)pinfo.data_per_slab * pinfo.slab_count * DM_VDO_BLOCK_SIZE),
+ pinfo.slab_count,
+ display_mb_size(cmd, vtp->slab_size_mb));
+ log_print_unless_silent(" It can grow to address at most %s of physical storage in %u slabs.",
+ display_mb_size(cmd, (uint64_t)DM_VDO_SLABS_MAXIMUM * vtp->slab_size_mb),
+ (unsigned)DM_VDO_SLABS_MAXIMUM);
+ log_print_unless_silent(" If a larger maximum size might be needed, use bigger slabs.");
+ }
+
return 1;
}
diff --git a/lib/vdo/vdo.c b/lib/vdo/vdo.c
index 1e9d377b5..e0a0c3079 100644
--- a/lib/vdo/vdo.c
+++ b/lib/vdo/vdo.c
@@ -391,6 +391,7 @@ static int _vdo_pool_add_target_line(struct dev_manager *dm,
struct dm_tree_node *node, uint64_t len,
uint32_t *pvmove_mirror_count __attribute__((unused)))
{
+ struct dm_vdo_target_params vdo_params;
char *vdo_pool_name, *data_uuid;
unsigned attrs = 0;
@@ -411,11 +412,15 @@ static int _vdo_pool_add_target_line(struct dev_manager *dm,
if (!(data_uuid = build_dm_uuid(mem, seg_lv(seg, 0), lv_layer(seg_lv(seg, 0)))))
return_0;
+ /* Use local copy to avoid mutating in-memory metadata */
+ vdo_params = seg->vdo_params;
+ vdo_params.use_kernel_format = (seg->lv->status & LV_VDOFORMAT) ? 1 : 0;
+
/* VDO uses virtual size instead of its physical size */
if (!dm_tree_node_add_vdo_target(node, get_vdo_pool_virtual_size(seg),
!(attrs & VDO_FEATURE_VERSION4) ? 2 : 4,
vdo_pool_name, data_uuid, seg_lv(seg, 0)->size,
- &seg->vdo_params))
+ &vdo_params))
return_0;
return 1;
diff --git a/libdm/.exported_symbols.DM_1_02_216 b/libdm/.exported_symbols.DM_1_02_216
new file mode 100644
index 000000000..f88fb03a7
--- /dev/null
+++ b/libdm/.exported_symbols.DM_1_02_216
@@ -0,0 +1 @@
+dm_tree_node_add_vdo_target
diff --git a/libdm/libdm-deptree.c b/libdm/libdm-deptree.c
index 02b730bb8..4c420eeba 100644
--- a/libdm/libdm-deptree.c
+++ b/libdm/libdm-deptree.c
@@ -4609,13 +4609,14 @@ void dm_tree_node_set_callback(struct dm_tree_node *dnode,
dnode->callback_data = cb_data;
}
-int dm_tree_node_add_vdo_target(struct dm_tree_node *node,
- uint64_t size,
- uint32_t vdo_version,
- const char *vdo_pool_name,
- const char *data_uuid,
- uint64_t data_size,
- const struct dm_vdo_target_params *vtp)
+DM_EXPORT_NEW_SYMBOL(int, dm_tree_node_add_vdo_target, 1_02_216)
+ (struct dm_tree_node *node,
+ uint64_t size,
+ uint32_t vdo_version,
+ const char *vdo_pool_name,
+ const char *data_uuid,
+ uint64_t data_size,
+ const struct dm_vdo_target_params *vtp)
{
struct load_segment *seg;
@@ -4699,4 +4700,42 @@ int dm_tree_node_add_cache_target_base(struct dm_tree_node *node,
metadata_uuid, data_uuid, origin_uuid,
policy_name, policy_settings, data_block_size);
}
+
+/*
+ * Retain ABI compatibility after adding use_kernel_format field
+ * to dm_vdo_target_params in version 1.02.216.
+ *
+ * Old binaries compiled against 1.02.213 have struct dm_vdo_target_params
+ * without the use_kernel_format field at the end. The wrapper copies only
+ * the old fields and leaves use_kernel_format=0 (old apps use userspace
+ * vdoformat).
+ *
+ * dm_vdo_validate_target_params does not need versioning as it never
+ * accesses the use_kernel_format field.
+ */
+
+DM_EXPORT_SYMBOL(dm_tree_node_add_vdo_target, 1_02_213)
+int dm_tree_node_add_vdo_target_v1_02_213(struct dm_tree_node *node,
+ uint64_t size,
+ uint32_t vdo_version,
+ const char *vdo_pool_name,
+ const char *data_uuid,
+ uint64_t data_size,
+ const struct dm_vdo_target_params *vtp);
+int dm_tree_node_add_vdo_target_v1_02_213(struct dm_tree_node *node,
+ uint64_t size,
+ uint32_t vdo_version,
+ const char *vdo_pool_name,
+ const char *data_uuid,
+ uint64_t data_size,
+ const struct dm_vdo_target_params *vtp)
+{
+ struct dm_vdo_target_params vtp_compat = { 0 };
+
+ /* Old callers pass struct without use_kernel_format at the end */
+ memcpy(&vtp_compat, vtp, offsetof(struct dm_vdo_target_params, use_kernel_format));
+
+ return dm_tree_node_add_vdo_target(node, size, vdo_version, vdo_pool_name,
+ data_uuid, data_size, &vtp_compat);
+}
#endif
--
2.54.0

44
0013-make-generate.patch Normal file
View File

@ -0,0 +1,44 @@
From 60d971e910d8e1ffac7c4e5dafa9933ab0e3a767 Mon Sep 17 00:00:00 2001
From: Zdenek Kabelac <zkabelac@redhat.com>
Date: Sun, 26 Apr 2026 16:29:42 +0200
Subject: [PATCH 13/16] make: generate
(cherry picked from commit 9ec3d17d61a0a7bb5063129bbbf4b3144234ccc4)
---
conf/example.conf.in | 11 +++++++++--
1 file changed, 9 insertions(+), 2 deletions(-)
diff --git a/conf/example.conf.in b/conf/example.conf.in
index 9a74feda5..ef8ca256f 100644
--- a/conf/example.conf.in
+++ b/conf/example.conf.in
@@ -703,6 +703,13 @@ allocation {
# This configuration option has an automatic default value.
# vdo_use_sparse_index = 0
+ # Configuration option allocation/vdo_use_kernel_format.
+ # Use kernel direct formatting for VDO volume.
+ # When enabled (1), uses kernel direct formatting (requires dm-vdo 9.2.0+).
+ # When disabled (0), uses traditional userspace vdoformat tool.
+ # This configuration option has an automatic default value.
+ # vdo_use_kernel_format = 1
+
# Configuration option allocation/vdo_index_memory_size_mb.
# Specifies the amount of index memory in MiB for VDO volume.
# The value must be at least 256MiB and at most 1TiB.
@@ -1383,10 +1390,10 @@ global {
# Configuration option global/vdo_disabled_features.
# Features to not use in the vdo driver.
# This can be helpful for testing, or to avoid using a feature that is
- # causing problems. Features include: online_rename, version4
+ # causing problems. Features include: online_rename, version4, kernel_format
#
# Example
- # vdo_disabled_features = [ "online_rename", "version4" ]
+ # vdo_disabled_features = [ "online_rename", "version4", "kernel_format" ]
#
# This configuration option does not have a default value defined.
--
2.54.0

View File

@ -0,0 +1,38 @@
From d3bc31dc85f8890ccb14101703b8da4484040a5b Mon Sep 17 00:00:00 2001
From: Zdenek Kabelac <zkabelac@redhat.com>
Date: Sun, 26 Apr 2026 11:36:45 +0200
Subject: [PATCH 14/16] WHATS_NEW: update
(cherry picked from commit 4785084a64bbe6426dbc4adc43a4e181280713ab)
---
WHATS_NEW | 2 ++
WHATS_NEW_DM | 4 ++++
2 files changed, 6 insertions(+)
diff --git a/WHATS_NEW b/WHATS_NEW
index e53280e10..56c565d9c 100644
--- a/WHATS_NEW
+++ b/WHATS_NEW
@@ -1,5 +1,7 @@
Version 2.03.42 -
==================
+ Support formatting vdo volumes with kernel vdo target (version >= 9.2).
+ Add vdo_format.c with pre-format logical size computation for kernel format.
Fix autoactivation on top of MD and loop devices if udev db records incomplete.
Make MD device ready state check in udev to be in sync with MD udev rules.
diff --git a/WHATS_NEW_DM b/WHATS_NEW_DM
index b567591db..e58e4f86e 100644
--- a/WHATS_NEW_DM
+++ b/WHATS_NEW_DM
@@ -1,3 +1,7 @@
+Version 1.02.216 -
+===================
+ Add use_kernel_format to dm_vdo_target_params.
+
Version 1.02.215 - 15th May 2026
================================
Fix ttree insert/lookup depth mismatch in libdm.
--
2.54.0

View File

@ -49,6 +49,18 @@ Patch2: 0002-lvmlockd-fix-adopt-failure-when-gl-is-not-found.patch
Patch3: 0003-udev-check-MD-array_state-value-instead-of-just-file.patch
Patch4: 0004-udev-MD-and-loop-fallback-handling-for-synthetic-ADD.patch
Patch5: 0005-WHATS_NEW-update.patch
# RHEL-188186:
Patch6: 0006-fix-lvmpersist-WEAR-EAAR-reserve-race-when-multiple-.patch
# Minor fixes
Patch7: 0007-man-lvmlockd-fix-invalid-vgchange-setpersist-setlock.patch
Patch8: 0008-lvmpersist-settle-udev-between-register-and-preempt-.patch
Patch9: 0009-man-lvmlockd-improve-setlockargs-description.patch
# RHEL-164001:
Patch10: 0010-vdo-add-pre-format-pool-geometry-computation.patch
Patch11: 0011-vdo-add-support-for-VDO-kernel-direct-formatting.patch
Patch12: 0012-vdo-add-kernel-formatting-path-with-LV_VDOFORMAT-met.patch
Patch13: 0013-make-generate.patch
Patch14: 0014-WHATS_NEW-update.patch
BuildRequires: make
BuildRequires: gcc
@ -660,6 +672,10 @@ An extensive functional testsuite for LVM2.
%endif
%changelog
* Thu Jul 02 2026 Marian Csontos <mcsontos@redhat.com> - 2.03.41-3
- Fix concurrent PR start.
- Use kernel vdoformat if present.
* Tue Jun 02 2026 Marian Csontos <mcsontos@redhat.com> - 2.03.41-2
- Fix MD handling in udev rules.