Re: [RFC 3/9] selftests: block_seek_hole: add loop block driver tests

Eric Blake
2024-03-29T12:38:28Z

On Thu, Mar 28, 2024 at 04:39:04PM -0400, Stefan Hajnoczi wrote:
> Run the tests with:
>
> $ make TARGETS=block_seek_hole -C tools/selftests run_tests
>
> Signed-off-by: Stefan Hajnoczi <stefanha@redhat.com>
> ---
> tools/testing/selftests/Makefile | 1 +
> .../selftests/block_seek_hole/Makefile | 17 +++
> .../testing/selftests/block_seek_hole/config | 1 +
> .../selftests/block_seek_hole/map_holes.py | 37 +++++++
> .../testing/selftests/block_seek_hole/test.py | 103 ++++++++++++++++++
> 5 files changed, 159 insertions(+)
> create mode 100644 tools/testing/selftests/block_seek_hole/Makefile
> create mode 100644 tools/testing/selftests/block_seek_hole/config
> create mode 100755 tools/testing/selftests/block_seek_hole/map_holes.py
> create mode 100755 tools/testing/selftests/block_seek_hole/test.py
>

> +
> +def map_holes(fd):
> + end = os.lseek(fd, 0, os.SEEK_END)
> + offset = 0
> +
> + print('TYPE START END SIZE')
> +
> + while offset < end:
> + contents = 'DATA'
> + new_offset = os.lseek(fd, offset, os.SEEK_HOLE)
> + if new_offset == offset:
> + contents = 'HOLE'
> + try:
> + new_offset = os.lseek(fd, offset, os.SEEK_DATA)
> + except OSError as err:
> + if err.errno == errno.ENXIO:
> + new_offset = end
> + else:
> + raise err
> + assert new_offset != offset
> + print(f'{contents} {offset} {new_offset} {new_offset - offset}')
> + offset = new_offset

Over the years, I've seen various SEEK_HOLE implementation bugs where
things work great on the initial boundary, but fail when requested on
an offset not aligned to the start of the extent boundary. It would
probably be worth enhancing the test to prove that:

if lseek(fd, offset, SEEK_HOLE) == offset:
new_offset = lseek(fd, offset, SEEK_DATA)
assert new_offset > offset
assert lseek(fd, new_offset - 1, SEEK_HOLE) == new_offset - 1
else:
assert lseek(fd, offset, SEEK_DATA) == offset
new_offset = lseek(fd, offset, SEEK_HOLE)
assert new_offset > offset
assert lseek(fd, new_offset - 1, SEEK_DATA) == new_offset - 1

Among other things, this would prove that even though block devices
generally operate on a minimum granularity of a sector, lseek() still
gives byte-accurate results for a random offset that falls in the
middle of a sector, and doesn't accidentally round down reporting an
offset less than the value passed in to the request.

--
Eric Blake, Principal Software Engineer
Red Hat, Inc.
Virtualization: qemu.org | libguestfs.org