diff options
| author | Filipe Manana <fdmanana@suse.com> | 2025-07-08 16:01:19 +0100 | 
|---|---|---|
| committer | David Sterba <dsterba@suse.com> | 2025-07-22 00:09:22 +0200 | 
| commit | 6599716de2d68ab7b3c0429221deca306dbda878 (patch) | |
| tree | 0f89748a48fcaa967613e80a2fa93c9d18f12f4c /rust/helpers/processor.c | |
| parent | e8d2e254dc073f9a1ea2c00b53ba44f329828701 (diff) | |
btrfs: fix -ENOSPC mmap write failure on NOCOW files/extents
If we attempt a mmap write into a NOCOW file or a prealloc extent when
there is no more available data space (or unallocated space to allocate a
new data block group) and we can do a NOCOW write (there are no reflinks
for the target extent or snapshots), we always fail due to -ENOSPC, unlike
for the regular buffered write and direct IO paths where we check that we
can do a NOCOW write in case we can't reserve data space.
Simple reproducer:
  $ cat test.sh
  #!/bin/bash
  DEV=/dev/sdi
  MNT=/mnt/sdi
  umount $DEV &> /dev/null
  mkfs.btrfs -f -b $((512 * 1024 * 1024)) $DEV
  mount $DEV $MNT
  touch $MNT/foobar
  # Make it a NOCOW file.
  chattr +C $MNT/foobar
  # Add initial data to file.
  xfs_io -c "pwrite -S 0xab 0 1M" $MNT/foobar
  # Fill all the remaining data space and unallocated space with data.
  dd if=/dev/zero of=$MNT/filler bs=4K &> /dev/null
  # Overwrite the file with a mmap write. Should succeed.
  xfs_io -c "mmap -w 0 1M"        \
         -c "mwrite -S 0xcd 0 1M" \
         -c "munmap"              \
         $MNT/foobar
  # Unmount, mount again and verify the new data was persisted.
  umount $MNT
  mount $DEV $MNT
  od -A d -t x1 $MNT/foobar
  umount $MNT
Running this:
  $ ./test.sh
  (...)
  wrote 1048576/1048576 bytes at offset 0
  1 MiB, 256 ops; 0.0008 sec (1.188 GiB/sec and 311435.5231 ops/sec)
  ./test.sh: line 24: 234865 Bus error               xfs_io -c "mmap -w 0 1M" -c "mwrite -S 0xcd 0 1M" -c "munmap" $MNT/foobar
  0000000 ab ab ab ab ab ab ab ab ab ab ab ab ab ab ab ab
  *
  1048576
Fix this by not failing in case we can't allocate data space and we can
NOCOW into the target extent - reserving only metadata space in this case.
After this change the test passes:
  $ ./test.sh
  (...)
  wrote 1048576/1048576 bytes at offset 0
  1 MiB, 256 ops; 0.0007 sec (1.262 GiB/sec and 330749.3540 ops/sec)
  0000000 cd cd cd cd cd cd cd cd cd cd cd cd cd cd cd cd
  *
  1048576
A test case for fstests will be added soon.
Reviewed-by: Qu Wenruo <wqu@suse.com>
Signed-off-by: Filipe Manana <fdmanana@suse.com>
Signed-off-by: David Sterba <dsterba@suse.com>
Diffstat (limited to 'rust/helpers/processor.c')
0 files changed, 0 insertions, 0 deletions
