[RFC PATCH v1 10/10] KVM: selftests: Test that st_blocks is updated on allocation

From: Ackerley Tng

Date: Mon Feb 23 2026 - 02:11:40 EST


The st_blocks field reported by fstat should reflect the number of
allocated 512-byte blocks for the guest memfd file.

Extend the fallocate test to verify that st_blocks is correctly updated
when memory is allocated or deallocated via
fallocate(FALLOC_FL_PUNCH_HOLE).

Add checks after each fallocate call to ensure that st_blocks increases on
allocation, decreases when a hole is punched, and is restored when the hole
is re-allocated. Also verify that st_blocks remains unchanged for failing
fallocate calls.

Signed-off-by: Ackerley Tng <ackerleytng@xxxxxxxxxx>
---
tools/testing/selftests/kvm/guest_memfd_test.c | 17 +++++++++++++++++
1 file changed, 17 insertions(+)

diff --git a/tools/testing/selftests/kvm/guest_memfd_test.c b/tools/testing/selftests/kvm/guest_memfd_test.c
index 81387f06e770a..89228d73fa736 100644
--- a/tools/testing/selftests/kvm/guest_memfd_test.c
+++ b/tools/testing/selftests/kvm/guest_memfd_test.c
@@ -218,41 +218,58 @@ static void test_file_size(int fd, size_t total_size)
TEST_ASSERT_EQ(sb.st_blksize, page_size);
}

+static void assert_st_blocks_matches_size(int fd, size_t expected_size)
+{
+ struct stat sb;
+
+ kvm_fstat(fd, &sb);
+ TEST_ASSERT_EQ(sb.st_blocks, expected_size / 512);
+}
+
static void test_fallocate(int fd, size_t total_size)
{
int ret;

ret = fallocate(fd, FALLOC_FL_KEEP_SIZE, 0, total_size);
TEST_ASSERT(!ret, "fallocate with aligned offset and size should succeed");
+ assert_st_blocks_matches_size(fd, total_size);

ret = fallocate(fd, FALLOC_FL_KEEP_SIZE | FALLOC_FL_PUNCH_HOLE,
page_size - 1, page_size);
TEST_ASSERT(ret, "fallocate with unaligned offset should fail");
+ assert_st_blocks_matches_size(fd, total_size);

ret = fallocate(fd, FALLOC_FL_KEEP_SIZE, total_size, page_size);
TEST_ASSERT(ret, "fallocate beginning at total_size should fail");
+ assert_st_blocks_matches_size(fd, total_size);

ret = fallocate(fd, FALLOC_FL_KEEP_SIZE, total_size + page_size, page_size);
TEST_ASSERT(ret, "fallocate beginning after total_size should fail");
+ assert_st_blocks_matches_size(fd, total_size);

ret = fallocate(fd, FALLOC_FL_KEEP_SIZE | FALLOC_FL_PUNCH_HOLE,
total_size, page_size);
TEST_ASSERT(!ret, "fallocate(PUNCH_HOLE) at total_size should succeed");
+ assert_st_blocks_matches_size(fd, total_size);

ret = fallocate(fd, FALLOC_FL_KEEP_SIZE | FALLOC_FL_PUNCH_HOLE,
total_size + page_size, page_size);
TEST_ASSERT(!ret, "fallocate(PUNCH_HOLE) after total_size should succeed");
+ assert_st_blocks_matches_size(fd, total_size);

ret = fallocate(fd, FALLOC_FL_KEEP_SIZE | FALLOC_FL_PUNCH_HOLE,
page_size, page_size - 1);
TEST_ASSERT(ret, "fallocate with unaligned size should fail");
+ assert_st_blocks_matches_size(fd, total_size);

ret = fallocate(fd, FALLOC_FL_KEEP_SIZE | FALLOC_FL_PUNCH_HOLE,
page_size, page_size);
TEST_ASSERT(!ret, "fallocate(PUNCH_HOLE) with aligned offset and size should succeed");
+ assert_st_blocks_matches_size(fd, total_size - page_size);

ret = fallocate(fd, FALLOC_FL_KEEP_SIZE, page_size, page_size);
TEST_ASSERT(!ret, "fallocate to restore punched hole should succeed");
+ assert_st_blocks_matches_size(fd, total_size);
}

static void test_invalid_punch_hole(int fd, size_t total_size)
--
2.53.0.345.g96ddfc5eaa-goog