Skip to content

Commit 8a89efd

Browse files
chao-pbonzini
authored andcommitted
KVM: selftests: Add basic selftest for guest_memfd()
Add a selftest to verify the basic functionality of guest_memfd(): + file descriptor created with the guest_memfd() ioctl does not allow read/write/mmap operations + file size and block size as returned from fstat are as expected + fallocate on the fd checks that offset/length on fallocate(FALLOC_FL_PUNCH_HOLE) should be page aligned + invalid inputs (misaligned size, invalid flags) are rejected + file size and inode are unique (the innocuous-sounding anon_inode_getfile() backs all files with a single inode...) Signed-off-by: Chao Peng <chao.p.peng@linux.intel.com> Co-developed-by: Ackerley Tng <ackerleytng@google.com> Signed-off-by: Ackerley Tng <ackerleytng@google.com> Co-developed-by: Paolo Bonzini <pbonzini@redhat.com> Signed-off-by: Paolo Bonzini <pbonzini@redhat.com> Co-developed-by: Sean Christopherson <seanjc@google.com> Signed-off-by: Sean Christopherson <seanjc@google.com> Message-Id: <20231027182217.3615211-35-seanjc@google.com> Reviewed-by: Fuad Tabba <tabba@google.com> Tested-by: Fuad Tabba <tabba@google.com> Signed-off-by: Paolo Bonzini <pbonzini@redhat.com>
1 parent 2feabb8 commit 8a89efd

File tree

2 files changed

+208
-0
lines changed

2 files changed

+208
-0
lines changed

tools/testing/selftests/kvm/Makefile

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -134,6 +134,7 @@ TEST_GEN_PROGS_x86_64 += access_tracking_perf_test
134134
TEST_GEN_PROGS_x86_64 += demand_paging_test
135135
TEST_GEN_PROGS_x86_64 += dirty_log_test
136136
TEST_GEN_PROGS_x86_64 += dirty_log_perf_test
137+
TEST_GEN_PROGS_x86_64 += guest_memfd_test
137138
TEST_GEN_PROGS_x86_64 += guest_print_test
138139
TEST_GEN_PROGS_x86_64 += hardware_disable_test
139140
TEST_GEN_PROGS_x86_64 += kvm_create_max_vcpus
Lines changed: 207 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,207 @@
1+
// SPDX-License-Identifier: GPL-2.0
2+
/*
3+
* Copyright Intel Corporation, 2023
4+
*
5+
* Author: Chao Peng <chao.p.peng@linux.intel.com>
6+
*/
7+
8+
#define _GNU_SOURCE
9+
#include <stdlib.h>
10+
#include <string.h>
11+
#include <unistd.h>
12+
#include <errno.h>
13+
#include <stdio.h>
14+
#include <fcntl.h>
15+
16+
#include <linux/bitmap.h>
17+
#include <linux/falloc.h>
18+
#include <sys/mman.h>
19+
#include <sys/types.h>
20+
#include <sys/stat.h>
21+
22+
#include "test_util.h"
23+
#include "kvm_util_base.h"
24+
25+
static void test_file_read_write(int fd)
26+
{
27+
char buf[64];
28+
29+
TEST_ASSERT(read(fd, buf, sizeof(buf)) < 0,
30+
"read on a guest_mem fd should fail");
31+
TEST_ASSERT(write(fd, buf, sizeof(buf)) < 0,
32+
"write on a guest_mem fd should fail");
33+
TEST_ASSERT(pread(fd, buf, sizeof(buf), 0) < 0,
34+
"pread on a guest_mem fd should fail");
35+
TEST_ASSERT(pwrite(fd, buf, sizeof(buf), 0) < 0,
36+
"pwrite on a guest_mem fd should fail");
37+
}
38+
39+
static void test_mmap(int fd, size_t page_size)
40+
{
41+
char *mem;
42+
43+
mem = mmap(NULL, page_size, PROT_READ | PROT_WRITE, MAP_SHARED, fd, 0);
44+
TEST_ASSERT_EQ(mem, MAP_FAILED);
45+
}
46+
47+
static void test_file_size(int fd, size_t page_size, size_t total_size)
48+
{
49+
struct stat sb;
50+
int ret;
51+
52+
ret = fstat(fd, &sb);
53+
TEST_ASSERT(!ret, "fstat should succeed");
54+
TEST_ASSERT_EQ(sb.st_size, total_size);
55+
TEST_ASSERT_EQ(sb.st_blksize, page_size);
56+
}
57+
58+
static void test_fallocate(int fd, size_t page_size, size_t total_size)
59+
{
60+
int ret;
61+
62+
ret = fallocate(fd, FALLOC_FL_KEEP_SIZE, 0, total_size);
63+
TEST_ASSERT(!ret, "fallocate with aligned offset and size should succeed");
64+
65+
ret = fallocate(fd, FALLOC_FL_KEEP_SIZE | FALLOC_FL_PUNCH_HOLE,
66+
page_size - 1, page_size);
67+
TEST_ASSERT(ret, "fallocate with unaligned offset should fail");
68+
69+
ret = fallocate(fd, FALLOC_FL_KEEP_SIZE, total_size, page_size);
70+
TEST_ASSERT(ret, "fallocate beginning at total_size should fail");
71+
72+
ret = fallocate(fd, FALLOC_FL_KEEP_SIZE, total_size + page_size, page_size);
73+
TEST_ASSERT(ret, "fallocate beginning after total_size should fail");
74+
75+
ret = fallocate(fd, FALLOC_FL_KEEP_SIZE | FALLOC_FL_PUNCH_HOLE,
76+
total_size, page_size);
77+
TEST_ASSERT(!ret, "fallocate(PUNCH_HOLE) at total_size should succeed");
78+
79+
ret = fallocate(fd, FALLOC_FL_KEEP_SIZE | FALLOC_FL_PUNCH_HOLE,
80+
total_size + page_size, page_size);
81+
TEST_ASSERT(!ret, "fallocate(PUNCH_HOLE) after total_size should succeed");
82+
83+
ret = fallocate(fd, FALLOC_FL_KEEP_SIZE | FALLOC_FL_PUNCH_HOLE,
84+
page_size, page_size - 1);
85+
TEST_ASSERT(ret, "fallocate with unaligned size should fail");
86+
87+
ret = fallocate(fd, FALLOC_FL_KEEP_SIZE | FALLOC_FL_PUNCH_HOLE,
88+
page_size, page_size);
89+
TEST_ASSERT(!ret, "fallocate(PUNCH_HOLE) with aligned offset and size should succeed");
90+
91+
ret = fallocate(fd, FALLOC_FL_KEEP_SIZE, page_size, page_size);
92+
TEST_ASSERT(!ret, "fallocate to restore punched hole should succeed");
93+
}
94+
95+
static void test_invalid_punch_hole(int fd, size_t page_size, size_t total_size)
96+
{
97+
struct {
98+
off_t offset;
99+
off_t len;
100+
} testcases[] = {
101+
{0, 1},
102+
{0, page_size - 1},
103+
{0, page_size + 1},
104+
105+
{1, 1},
106+
{1, page_size - 1},
107+
{1, page_size},
108+
{1, page_size + 1},
109+
110+
{page_size, 1},
111+
{page_size, page_size - 1},
112+
{page_size, page_size + 1},
113+
};
114+
int ret, i;
115+
116+
for (i = 0; i < ARRAY_SIZE(testcases); i++) {
117+
ret = fallocate(fd, FALLOC_FL_KEEP_SIZE | FALLOC_FL_PUNCH_HOLE,
118+
testcases[i].offset, testcases[i].len);
119+
TEST_ASSERT(ret == -1 && errno == EINVAL,
120+
"PUNCH_HOLE with !PAGE_SIZE offset (%lx) and/or length (%lx) should fail",
121+
testcases[i].offset, testcases[i].len);
122+
}
123+
}
124+
125+
static void test_create_guest_memfd_invalid(struct kvm_vm *vm)
126+
{
127+
size_t page_size = getpagesize();
128+
uint64_t flag;
129+
size_t size;
130+
int fd;
131+
132+
for (size = 1; size < page_size; size++) {
133+
fd = __vm_create_guest_memfd(vm, size, 0);
134+
TEST_ASSERT(fd == -1 && errno == EINVAL,
135+
"guest_memfd() with non-page-aligned page size '0x%lx' should fail with EINVAL",
136+
size);
137+
}
138+
139+
for (flag = 1; flag; flag <<= 1) {
140+
uint64_t bit;
141+
142+
fd = __vm_create_guest_memfd(vm, page_size, flag);
143+
TEST_ASSERT(fd == -1 && errno == EINVAL,
144+
"guest_memfd() with flag '0x%lx' should fail with EINVAL",
145+
flag);
146+
147+
for_each_set_bit(bit, &valid_flags, 64) {
148+
fd = __vm_create_guest_memfd(vm, page_size, flag | BIT_ULL(bit));
149+
TEST_ASSERT(fd == -1 && errno == EINVAL,
150+
"guest_memfd() with flags '0x%llx' should fail with EINVAL",
151+
flag | BIT_ULL(bit));
152+
}
153+
}
154+
}
155+
156+
static void test_create_guest_memfd_multiple(struct kvm_vm *vm)
157+
{
158+
int fd1, fd2, ret;
159+
struct stat st1, st2;
160+
161+
fd1 = __vm_create_guest_memfd(vm, 4096, 0);
162+
TEST_ASSERT(fd1 != -1, "memfd creation should succeed");
163+
164+
ret = fstat(fd1, &st1);
165+
TEST_ASSERT(ret != -1, "memfd fstat should succeed");
166+
TEST_ASSERT(st1.st_size == 4096, "memfd st_size should match requested size");
167+
168+
fd2 = __vm_create_guest_memfd(vm, 8192, 0);
169+
TEST_ASSERT(fd2 != -1, "memfd creation should succeed");
170+
171+
ret = fstat(fd2, &st2);
172+
TEST_ASSERT(ret != -1, "memfd fstat should succeed");
173+
TEST_ASSERT(st2.st_size == 8192, "second memfd st_size should match requested size");
174+
175+
ret = fstat(fd1, &st1);
176+
TEST_ASSERT(ret != -1, "memfd fstat should succeed");
177+
TEST_ASSERT(st1.st_size == 4096, "first memfd st_size should still match requested size");
178+
TEST_ASSERT(st1.st_ino != st2.st_ino, "different memfd should have different inode numbers");
179+
}
180+
181+
int main(int argc, char *argv[])
182+
{
183+
size_t page_size;
184+
size_t total_size;
185+
int fd;
186+
struct kvm_vm *vm;
187+
188+
TEST_REQUIRE(kvm_has_cap(KVM_CAP_GUEST_MEMFD));
189+
190+
page_size = getpagesize();
191+
total_size = page_size * 4;
192+
193+
vm = vm_create_barebones();
194+
195+
test_create_guest_memfd_invalid(vm);
196+
test_create_guest_memfd_multiple(vm);
197+
198+
fd = vm_create_guest_memfd(vm, total_size, 0);
199+
200+
test_file_read_write(fd);
201+
test_mmap(fd, page_size);
202+
test_file_size(fd, page_size, total_size);
203+
test_fallocate(fd, page_size, total_size);
204+
test_invalid_punch_hole(fd, page_size, total_size);
205+
206+
close(fd);
207+
}

0 commit comments

Comments
 (0)