Loading...
Note: File does not exist in v3.15.
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
25static 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
39static 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
47static 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
58static 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
95static 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
125static 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 = 0; flag; flag <<= 1) {
140 fd = __vm_create_guest_memfd(vm, page_size, flag);
141 TEST_ASSERT(fd == -1 && errno == EINVAL,
142 "guest_memfd() with flag '0x%lx' should fail with EINVAL",
143 flag);
144 }
145}
146
147static void test_create_guest_memfd_multiple(struct kvm_vm *vm)
148{
149 int fd1, fd2, ret;
150 struct stat st1, st2;
151
152 fd1 = __vm_create_guest_memfd(vm, 4096, 0);
153 TEST_ASSERT(fd1 != -1, "memfd creation should succeed");
154
155 ret = fstat(fd1, &st1);
156 TEST_ASSERT(ret != -1, "memfd fstat should succeed");
157 TEST_ASSERT(st1.st_size == 4096, "memfd st_size should match requested size");
158
159 fd2 = __vm_create_guest_memfd(vm, 8192, 0);
160 TEST_ASSERT(fd2 != -1, "memfd creation should succeed");
161
162 ret = fstat(fd2, &st2);
163 TEST_ASSERT(ret != -1, "memfd fstat should succeed");
164 TEST_ASSERT(st2.st_size == 8192, "second memfd st_size should match requested size");
165
166 ret = fstat(fd1, &st1);
167 TEST_ASSERT(ret != -1, "memfd fstat should succeed");
168 TEST_ASSERT(st1.st_size == 4096, "first memfd st_size should still match requested size");
169 TEST_ASSERT(st1.st_ino != st2.st_ino, "different memfd should have different inode numbers");
170
171 close(fd2);
172 close(fd1);
173}
174
175int main(int argc, char *argv[])
176{
177 size_t page_size;
178 size_t total_size;
179 int fd;
180 struct kvm_vm *vm;
181
182 TEST_REQUIRE(kvm_has_cap(KVM_CAP_GUEST_MEMFD));
183
184 page_size = getpagesize();
185 total_size = page_size * 4;
186
187 vm = vm_create_barebones();
188
189 test_create_guest_memfd_invalid(vm);
190 test_create_guest_memfd_multiple(vm);
191
192 fd = vm_create_guest_memfd(vm, total_size, 0);
193
194 test_file_read_write(fd);
195 test_mmap(fd, page_size);
196 test_file_size(fd, page_size, total_size);
197 test_fallocate(fd, page_size, total_size);
198 test_invalid_punch_hole(fd, page_size, total_size);
199
200 close(fd);
201}