* [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* Re: [PATCH] label: fix writes when the logical block size is not 512 or 4096
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
0 siblings, 1 reply; 3+ messages in thread
From: Zdenek Kabelac @ 2026-10-29 13:12 UTC (permalink / raw)
To: Itai Handler, lvm-devel
Dne 29. 10. 26 v 13:53 Itai Handler napsal(a):
> 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".
Hi
Hmm - this thread io engine is surely a new logic we are putting in.
I'd like to know more details what is the scenario we are trying to solve -
normally we expect devices to really have 512 or 4K writable blocks.
We have some set of patches to improve support on systems with 64K pagesize.
But I'm puzzled from where do we get 1K or 2K block sizes ??
> 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.
I think we want to fully hide this in blkio - label layer should be above.
So I will think more about this.
>
> Devices with other block sizes are uncommon, but easy to create: loop
Can you tell why do you actually need to use this blocksize - IMHO it must
bring some inefficiency ??
Regards
Zdenek
^ permalink raw reply [flat|nested] 3+ messages in thread
* Re: [PATCH] label: fix writes when the logical block size is not 512 or 4096
2026-10-29 13:12 ` Zdenek Kabelac
@ 2026-10-29 13:53 ` Itai Handler
0 siblings, 0 replies; 3+ messages in thread
From: Itai Handler @ 2026-10-29 13:53 UTC (permalink / raw)
To: Zdenek Kabelac; +Cc: lvm-devel, David Teigland
Hi Zdenek,
On Thu, Oct 29, 2026 at 02:12:34PM +0100, Zdenek Kabelac wrote:
> Hmm - this thread io engine is surely a new logic we are putting in.
This is not related to the threads engine; the warning just names the
engine in use. On main, pvcreate fails the same way with io_engine
set to sync or libaio, and also with releases from before the threads
engine, such as Ubuntu's 2.03.11 and Debian's 2.03.31. Built from the
release tags, 2.03.05 works on 1024- and 2048-byte loop devices
(pvcreate, vgcreate, lvcreate, vgremove), and 2.03.06 fails on the
2048-byte one from the patch:
# pvcreate /dev/loop8
Error writing device /dev/loop8 at 4096 length 512.
bcache_invalidate: block (3, 0) still dirty
Failed to write mda header to /dev/loop8 fd -1
Failed to write physical volume "/dev/loop8".
The cause is 7f347698e3d0 ("Fix rounding writes up to sector size"),
which went into 2.03.06. Before it, writes were rounded up to the
physical block size, which is never smaller than the logical one.
Since then only 512 and 4096 are used, and when neither block size is
one of them, it falls back to 512.
> I'd like to know more details what is the scenario we are trying to
> solve - normally we expect devices to really have 512 or 4K writable
> blocks.
>
> We have some set of patches to improve support on systems with 64K
> pagesize.
>
> But I'm puzzled from where do we get 1K or 2K block sizes ??
I ran into this while testing LVM on dm-crypt with sectors larger
than 4KiB. A dm-crypt device uses its encryption sector size as its
logical block size. dm-crypt has accepted sector sizes from 512 to
4096 since Linux 4.12, and my kernel change 1acc65c30f85 ("dm-crypt:
allow encryption sector size up to PAGE_SIZE", target version 1.30.0)
raises the limit to the page size, so on kernels configured with
64KiB pages it can also be 8KiB to 64KiB. pvcreate fails on all of
these, and while looking into why, I found that 1K and 2K fail the
same way on any kernel.
1K and 2K block sizes are not common, but they are valid today:
- cryptsetup has offered --sector-size for LUKS2 and plain devices
since 2.0.0, and accepts 1024 and 2048. For example, pvcreate fails
on a LUKS2 device made with "cryptsetup luksFormat --sector-size
2048", and LVM on LUKS is a common setup.
- s390 DASDs can be formatted with 1024- or 2048-byte blocks
(dasdfmt -b), although 4096 is the recommended size.
- loop devices with --sector-size, which is the easiest way to
reproduce it.
The 64K page size changes already in main, such as 3e91a5265
("striped: allow stripe size smaller than page size"), were in the
tree I tested, and do not change this. If you have more that are not
in main yet, I'd be glad to test them on my arm64 VM with 64KiB pages.
>> Fix this by rounding up to the logical block size of the device when
>> it is neither 512 nor 4096. [...]
>
> I think we want to fully hide this in blkio - label layer should be
> above.
Agreed, blkio does the direct I/O, so it should know the alignment it
needs. For example, it could get the logical block size of each fd
it is given (BLKSSZGET) and round shortened writes up to that, so the
label layer passes only the offset. For devices with 512- or
4096-byte logical blocks this rounds exactly as today, including
those with a huge physical block size, as in
large-physical-sector-size.sh.
> So I will think more about this.
Sure. I can send a v2 along those lines, keeping the test, or test
your version if you prefer to write it.
>> Devices with other block sizes are uncommon, but easy to create: loop
>
> Can you tell why do you actually need to use this blocksize - IMHO it
> must bring some inefficiency ??
I agree that blocks smaller than the disk's sector, such as 2KiB on an
SSD with 4KiB sectors, are inefficient, since a 2KiB write makes the
disk read and rewrite a whole 4KiB sector. I don't need those myself;
I mention them only because they hit the same bug.
What I want is the opposite: larger blocks, from 8KiB up to 64KiB,
for dm-crypt on kernels configured with 64KiB pages. These are whole
multiples of the disk's sector, so they do not have that problem.
Whichever size is used, the cost to LVM is small. Only LVM's own
writes, such as metadata updates, are rounded up to whole blocks,
which adds less than one block to each, and they happen only when LVM
changes something. LV data does not go through this code, and the
default 1MiB pe_start keeps extents aligned for any of these sizes.
Thanks,
Itai
^ permalink raw reply [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.