All of lore.kernel.org
 help / color / mirror / Atom feed
* [PATCH] label: fix writes when the logical block size is not 512 or 4096
@ 2026-10-29 12:53 Itai Handler
  2026-10-29 13:12 ` Zdenek Kabelac
  0 siblings, 1 reply; 3+ messages in thread
From: Itai Handler @ 2026-10-29 12:53 UTC (permalink / raw)
  To: lvm-devel; +Cc: David Teigland, Zdenek Kabelac

Since lvm2 2.03.06, LVM cannot be used on a device whose logical block
size, the smallest unit it can read or write, is neither 512 nor 4096
bytes.  pvcreate, which prepares a device for LVM, fails:

  # truncate -s 64M disk.img
  # losetup --sector-size 2048 --show -f disk.img
  /dev/loop8
  # pvcreate /dev/loop8
    WARNING: threads io engine write device offset 0 len 4608: Invalid argument.
    Error writing device /dev/loop8 at 4096 length 512.
    Failed to write mda header to /dev/loop8.
    Failed to write physical volume "/dev/loop8".

LVM writes with direct I/O, bypassing the kernel's cache, and such
writes must cover whole logical blocks.  LVM keeps the data it reads in
memory, in 128KiB pieces.  To save a change, it writes a piece back from
its start up to the end of the area being updated, rounded up to a
whole block.  But for this rounding it only knows block sizes of 512
and 4096 bytes, and uses 512 for any other size.  So above, to update
the 512-byte metadata header at offset 4096, LVM writes the first 4608
bytes, 2.25 blocks of 2048 bytes, and the kernel rejects the write.

Fix this by rounding up to the logical block size of the device when
it is neither 512 nor 4096.  Writes then cover a little more of the
device, but the extra bytes are rewritten with the data already there,
just as on devices with 4096-byte blocks.  Devices with 512- or
4096-byte blocks, which are almost all disks, are not affected.

Devices with other block sizes are uncommon, but easy to create: loop
devices with --sector-size 1024 or 2048, and dm-crypt (disk encryption)
devices with 1024- or 2048-byte sectors.  On kernels configured with
pages larger than 4KiB, both can also use 8KiB up to the page size.
For dm-crypt this needs target version 1.30.0, added by Linux commit
1acc65c30f85 ("dm-crypt: allow encryption sector size up to PAGE_SIZE").
With blocks that large, the fix also lets lvcreate clear the first 4KiB
of a new logical volume, which inherits the block size of the device it
is on.

Fixes: 7f347698e3d0 ("Fix rounding writes up to sector size")
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
---
Tested on main a2decfaf4:

- Linux 6.8 x86_64 host: the new test passes with 1KiB and 2KiB loop
  devices and fails without the fix (larger sizes are skipped, loop
  accepts at most the page size there).  allow-mixed-block-sizes,
  large-physical-sector-size and topology-support still pass.
  pvcreate with two metadata areas, vgcreate, lvcreate, lvextend and
  reactivation work on dm-crypt with sector_size 2048.

- QEMU, Linux 7.4 merge window kernel (65ba8f34ec88), arm64 with 64KiB
  pages: the new test passes with all six sizes, 1KiB to 64KiB, and
  fails without the fix.  The same sequence works on dm-crypt with
  these sector sizes; a new LV on reused extents has its first 4KiB
  zeroed and the rest of its first logical block unchanged.  Debian's
  lvm2 2.03.31 fails pvcreate on each of these devices.

 WHATS_NEW                        |  1 +
 lib/label/label.c                | 11 ++++--
 test/shell/logical-block-size.sh | 65 ++++++++++++++++++++++++++++++++
 3 files changed, 74 insertions(+), 3 deletions(-)
 create mode 100644 test/shell/logical-block-size.sh

diff --git a/WHATS_NEW b/WHATS_NEW
index 1dad9be52..8f2cdd5a1 100644
--- a/WHATS_NEW
+++ b/WHATS_NEW
@@ -1,5 +1,6 @@
 Version 2.03.45 - 
 ==================
+  Fix writes to devices with logical block size other than 512 or 4096.
   Explain RAID extend failures when images share a PV or PVs differ.
 
 Version 2.03.44 - 27th October 2026
diff --git a/lib/label/label.c b/lib/label/label.c
index b9c0e7ca9..adf0540a0 100644
--- a/lib/label/label.c
+++ b/lib/label/label.c
@@ -2082,9 +2082,14 @@ void dev_set_last_byte(struct device *dev, uint64_t offset)
 			  physical_block_size, logical_block_size);
 		bs = 4096;
 	} else {
-		log_debug("Set last byte mixed block sizes physical %u logical %u using 512",
-			  physical_block_size, logical_block_size);
-		bs = 512;
+		/*
+		 * Neither size is 512 or 4096, e.g. a loop device or dm-crypt
+		 * with 2048-byte sectors.  Direct I/O must still cover whole
+		 * logical blocks.
+		 */
+		bs = logical_block_size ? logical_block_size : 512;
+		log_debug("Set last byte block sizes physical %u logical %u using %u",
+			  physical_block_size, logical_block_size, bs);
 	}
 
 	blkio_set_last_byte(scan_blkio, dev->blkio_di, offset, bs);
diff --git a/test/shell/logical-block-size.sh b/test/shell/logical-block-size.sh
new file mode 100644
index 000000000..2091a65ff
--- /dev/null
+++ b/test/shell/logical-block-size.sh
@@ -0,0 +1,65 @@
+#!/usr/bin/env bash
+
+# Copyright (C) 2026 Itai Handler. All rights reserved.
+#
+# 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 General Public License v.2.
+#
+# You should have received a copy of the GNU 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
+
+# Use a PV with a logical block size other than 512 and 4096: a loop
+# device with --sector-size 1024 or 2048, or larger up to the page size.
+# dm-crypt with such a sector_size behaves the same.  Direct I/O fails
+# unless writes cover whole logical blocks, on the PV and on LVs that
+# inherit its size.
+
+
+. lib/inittest --skip-with-lvmpolld --skip-with-lvmlockd
+
+losetup -h | grep sector-size || skip
+
+# A multiple of every tested block size but not of the 128KiB I/O block,
+# so the metadata area at the end of the device ends in a partial block.
+dd if=/dev/zero of=loopa bs=64k count=0 seek=513 2>/dev/null
+LOOP=$(losetup -f --show loopa)
+
+# prepare devX mapping so it works for real & fake dev dir
+m=${LOOP##*loop}
+dev1="$DM_DEV_DIR/loop$m"
+test -e "$dev1" || mknod "$dev1" b 7 "$m"
+
+aux extend_filter "a|$dev1|"
+aux extend_devices "$dev1"
+
+tested=
+for bs in 1024 2048 8192 16384 32768 65536 ; do
+	# The kernel rejects sizes above its limit (page size or 64KiB).
+	for i in {1..5} ; do
+		losetup --sector-size "$bs" "$LOOP" && break
+		sleep .1
+	done
+	test "$(blockdev --getss "$LOOP")" -eq "$bs" || continue
+
+	pvcreate --pvmetadatacopies 2 "$dev1"
+	vgcreate $vg "$dev1"
+	# Zeroes the first 4KiB of the LV.
+	lvcreate -l2 -n $lv1 $vg
+	check sysfs "$DM_DEV_DIR/$vg/$lv1" queue/logical_block_size "$bs"
+	lvextend -l+1 $vg/$lv1
+	vgchange -an $vg
+	vgchange -ay $vg
+	check lv_field $vg/$lv1 seg_size_pe "3"
+	vgremove -ff $vg
+	pvremove "$dev1"
+
+	tested="$tested $bs"
+done
+
+losetup -d "$LOOP"
+rm loopa
+
+test -n "$tested" || skip "No logical block size other than 512 or 4096"
+echo "Tested logical block sizes:$tested"

base-commit: a2decfaf4ce6f132137da61ba9132578b840e333
-- 
2.34.1


^ permalink raw reply related	[flat|nested] 3+ messages in thread

end of thread, other threads:[~2026-10-29 13:54 UTC | newest]

Thread overview: 3+ messages (download: mbox.gz follow: Atom feed
-- links below jump to the message on this page --
2026-10-29 12:53 [PATCH] label: fix writes when the logical block size is not 512 or 4096 Itai Handler
2026-10-29 13:12 ` Zdenek Kabelac
2026-10-29 13:53   ` Itai Handler

This is an external index of several public inboxes,
see mirroring instructions on how to clone and mirror
all data and code used by this external index.