Linux kernel mirror (for testing) git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
kernel os linux

iommufd: IOMMU_IOAS_CHANGE_PROCESS selftest

Add selftest cases for IOMMU_IOAS_CHANGE_PROCESS.

Link: https://patch.msgid.link/r/1731527497-16091-5-git-send-email-steven.sistare@oracle.com
Signed-off-by: Steve Sistare <steven.sistare@oracle.com>
Reviewed-by: Jason Gunthorpe <jgg@nvidia.com>
Signed-off-by: Jason Gunthorpe <jgg@nvidia.com>

authored by

Steve Sistare and committed by
Jason Gunthorpe
c0dec4b8 829ed626

+148
+1
tools/testing/selftests/iommu/Makefile
··· 1 1 # SPDX-License-Identifier: GPL-2.0-only 2 2 CFLAGS += -Wall -O2 -Wno-unused-function 3 3 CFLAGS += $(KHDR_INCLUDES) 4 + LDLIBS += -lcap 4 5 5 6 TEST_GEN_PROGS := 6 7 TEST_GEN_PROGS += iommufd
+141
tools/testing/selftests/iommu/iommufd.c
··· 2 2 /* Copyright (c) 2021-2022, NVIDIA CORPORATION & AFFILIATES */ 3 3 #include <asm/unistd.h> 4 4 #include <stdlib.h> 5 + #include <sys/capability.h> 5 6 #include <sys/mman.h> 6 7 #include <sys/eventfd.h> 7 8 ··· 136 135 TEST_LENGTH(iommu_ioas_map_file, IOMMU_IOAS_MAP_FILE, iova); 137 136 TEST_LENGTH(iommu_viommu_alloc, IOMMU_VIOMMU_ALLOC, out_viommu_id); 138 137 TEST_LENGTH(iommu_vdevice_alloc, IOMMU_VDEVICE_ALLOC, virt_id); 138 + TEST_LENGTH(iommu_ioas_change_process, IOMMU_IOAS_CHANGE_PROCESS, 139 + __reserved); 139 140 #undef TEST_LENGTH 140 141 } 141 142 ··· 194 191 EXPECT_ERRNO(ENOENT, ioctl(self->fd, IOMMU_OPTION, &cmd)); 195 192 cmd.op = IOMMU_OPTION_OP_SET; 196 193 EXPECT_ERRNO(ENOENT, ioctl(self->fd, IOMMU_OPTION, &cmd)); 194 + } 195 + 196 + static void drop_cap_ipc_lock(struct __test_metadata *_metadata) 197 + { 198 + cap_t caps; 199 + cap_value_t cap_list[1] = { CAP_IPC_LOCK }; 200 + 201 + caps = cap_get_proc(); 202 + ASSERT_NE(caps, NULL); 203 + ASSERT_NE(-1, 204 + cap_set_flag(caps, CAP_EFFECTIVE, 1, cap_list, CAP_CLEAR)); 205 + ASSERT_NE(-1, cap_set_proc(caps)); 206 + cap_free(caps); 207 + } 208 + 209 + static long get_proc_status_value(pid_t pid, const char *var) 210 + { 211 + FILE *fp; 212 + char buf[80], tag[80]; 213 + long val = -1; 214 + 215 + snprintf(buf, sizeof(buf), "/proc/%d/status", pid); 216 + fp = fopen(buf, "r"); 217 + if (!fp) 218 + return val; 219 + 220 + while (fgets(buf, sizeof(buf), fp)) 221 + if (fscanf(fp, "%s %ld\n", tag, &val) == 2 && !strcmp(tag, var)) 222 + break; 223 + 224 + fclose(fp); 225 + return val; 226 + } 227 + 228 + static long get_vm_pinned(pid_t pid) 229 + { 230 + return get_proc_status_value(pid, "VmPin:"); 231 + } 232 + 233 + static long get_vm_locked(pid_t pid) 234 + { 235 + return get_proc_status_value(pid, "VmLck:"); 236 + } 237 + 238 + FIXTURE(change_process) 239 + { 240 + int fd; 241 + uint32_t ioas_id; 242 + }; 243 + 244 + FIXTURE_VARIANT(change_process) 245 + { 246 + int accounting; 247 + }; 248 + 249 + FIXTURE_SETUP(change_process) 250 + { 251 + self->fd = open("/dev/iommu", O_RDWR); 252 + ASSERT_NE(-1, self->fd); 253 + 254 + drop_cap_ipc_lock(_metadata); 255 + if (variant->accounting != IOPT_PAGES_ACCOUNT_NONE) { 256 + struct iommu_option set_limit_cmd = { 257 + .size = sizeof(set_limit_cmd), 258 + .option_id = IOMMU_OPTION_RLIMIT_MODE, 259 + .op = IOMMU_OPTION_OP_SET, 260 + .val64 = (variant->accounting == IOPT_PAGES_ACCOUNT_MM), 261 + }; 262 + ASSERT_EQ(0, ioctl(self->fd, IOMMU_OPTION, &set_limit_cmd)); 263 + } 264 + 265 + test_ioctl_ioas_alloc(&self->ioas_id); 266 + test_cmd_mock_domain(self->ioas_id, NULL, NULL, NULL); 267 + } 268 + 269 + FIXTURE_TEARDOWN(change_process) 270 + { 271 + teardown_iommufd(self->fd, _metadata); 272 + } 273 + 274 + FIXTURE_VARIANT_ADD(change_process, account_none) 275 + { 276 + .accounting = IOPT_PAGES_ACCOUNT_NONE, 277 + }; 278 + 279 + FIXTURE_VARIANT_ADD(change_process, account_user) 280 + { 281 + .accounting = IOPT_PAGES_ACCOUNT_USER, 282 + }; 283 + 284 + FIXTURE_VARIANT_ADD(change_process, account_mm) 285 + { 286 + .accounting = IOPT_PAGES_ACCOUNT_MM, 287 + }; 288 + 289 + TEST_F(change_process, basic) 290 + { 291 + pid_t parent = getpid(); 292 + pid_t child; 293 + __u64 iova; 294 + struct iommu_ioas_change_process cmd = { 295 + .size = sizeof(cmd), 296 + }; 297 + 298 + /* Expect failure if non-file maps exist */ 299 + test_ioctl_ioas_map(buffer, PAGE_SIZE, &iova); 300 + EXPECT_ERRNO(EINVAL, ioctl(self->fd, IOMMU_IOAS_CHANGE_PROCESS, &cmd)); 301 + test_ioctl_ioas_unmap(iova, PAGE_SIZE); 302 + 303 + /* Change process works in current process. */ 304 + test_ioctl_ioas_map_file(mfd, 0, PAGE_SIZE, &iova); 305 + ASSERT_EQ(0, ioctl(self->fd, IOMMU_IOAS_CHANGE_PROCESS, &cmd)); 306 + 307 + /* Change process works in another process */ 308 + child = fork(); 309 + if (!child) { 310 + int nlock = PAGE_SIZE / 1024; 311 + 312 + /* Parent accounts for locked memory before */ 313 + ASSERT_EQ(nlock, get_vm_pinned(parent)); 314 + if (variant->accounting == IOPT_PAGES_ACCOUNT_MM) 315 + ASSERT_EQ(nlock, get_vm_locked(parent)); 316 + ASSERT_EQ(0, get_vm_pinned(getpid())); 317 + ASSERT_EQ(0, get_vm_locked(getpid())); 318 + 319 + ASSERT_EQ(0, ioctl(self->fd, IOMMU_IOAS_CHANGE_PROCESS, &cmd)); 320 + 321 + /* Child accounts for locked memory after */ 322 + ASSERT_EQ(0, get_vm_pinned(parent)); 323 + ASSERT_EQ(0, get_vm_locked(parent)); 324 + ASSERT_EQ(nlock, get_vm_pinned(getpid())); 325 + if (variant->accounting == IOPT_PAGES_ACCOUNT_MM) 326 + ASSERT_EQ(nlock, get_vm_locked(getpid())); 327 + 328 + exit(0); 329 + } 330 + ASSERT_NE(-1, child); 331 + ASSERT_EQ(child, waitpid(child, NULL, 0)); 197 332 } 198 333 199 334 FIXTURE(iommufd_ioas)
+6
tools/testing/selftests/iommu/iommufd_utils.h
··· 22 22 #define BIT_MASK(nr) (1UL << ((nr) % __BITS_PER_LONG)) 23 23 #define BIT_WORD(nr) ((nr) / __BITS_PER_LONG) 24 24 25 + enum { 26 + IOPT_PAGES_ACCOUNT_NONE = 0, 27 + IOPT_PAGES_ACCOUNT_USER = 1, 28 + IOPT_PAGES_ACCOUNT_MM = 2, 29 + }; 30 + 25 31 #define DIV_ROUND_UP(n, d) (((n) + (d) - 1) / (d)) 26 32 27 33 static inline void set_bit(unsigned int nr, unsigned long *addr)