|
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 |
|
@@ -135,6 +136,8 @@ TEST_F(iommufd, cmd_length)
|
135 | 136 | TEST_LENGTH(iommu_ioas_map_file, IOMMU_IOAS_MAP_FILE, iova);
|
136 | 137 | TEST_LENGTH(iommu_viommu_alloc, IOMMU_VIOMMU_ALLOC, out_viommu_id);
|
137 | 138 | TEST_LENGTH(iommu_vdevice_alloc, IOMMU_VDEVICE_ALLOC, virt_id);
|
| 139 | + TEST_LENGTH(iommu_ioas_change_process, IOMMU_IOAS_CHANGE_PROCESS, |
| 140 | + __reserved); |
138 | 141 | #undef TEST_LENGTH
|
139 | 142 | }
|
140 | 143 |
|
@@ -193,6 +196,144 @@ TEST_F(iommufd, global_options)
|
193 | 196 | EXPECT_ERRNO(ENOENT, ioctl(self->fd, IOMMU_OPTION, &cmd));
|
194 | 197 | }
|
195 | 198 |
|
| 199 | +static void drop_cap_ipc_lock(struct __test_metadata *_metadata) |
| 200 | +{ |
| 201 | + cap_t caps; |
| 202 | + cap_value_t cap_list[1] = { CAP_IPC_LOCK }; |
| 203 | + |
| 204 | + caps = cap_get_proc(); |
| 205 | + ASSERT_NE(caps, NULL); |
| 206 | + ASSERT_NE(-1, |
| 207 | + cap_set_flag(caps, CAP_EFFECTIVE, 1, cap_list, CAP_CLEAR)); |
| 208 | + ASSERT_NE(-1, cap_set_proc(caps)); |
| 209 | + cap_free(caps); |
| 210 | +} |
| 211 | + |
| 212 | +static long get_proc_status_value(pid_t pid, const char *var) |
| 213 | +{ |
| 214 | + FILE *fp; |
| 215 | + char buf[80], tag[80]; |
| 216 | + long val = -1; |
| 217 | + |
| 218 | + snprintf(buf, sizeof(buf), "/proc/%d/status", pid); |
| 219 | + fp = fopen(buf, "r"); |
| 220 | + if (!fp) |
| 221 | + return val; |
| 222 | + |
| 223 | + while (fgets(buf, sizeof(buf), fp)) |
| 224 | + if (fscanf(fp, "%s %ld\n", tag, &val) == 2 && !strcmp(tag, var)) |
| 225 | + break; |
| 226 | + |
| 227 | + fclose(fp); |
| 228 | + return val; |
| 229 | +} |
| 230 | + |
| 231 | +static long get_vm_pinned(pid_t pid) |
| 232 | +{ |
| 233 | + return get_proc_status_value(pid, "VmPin:"); |
| 234 | +} |
| 235 | + |
| 236 | +static long get_vm_locked(pid_t pid) |
| 237 | +{ |
| 238 | + return get_proc_status_value(pid, "VmLck:"); |
| 239 | +} |
| 240 | + |
| 241 | +FIXTURE(change_process) |
| 242 | +{ |
| 243 | + int fd; |
| 244 | + uint32_t ioas_id; |
| 245 | +}; |
| 246 | + |
| 247 | +FIXTURE_VARIANT(change_process) |
| 248 | +{ |
| 249 | + int accounting; |
| 250 | +}; |
| 251 | + |
| 252 | +FIXTURE_SETUP(change_process) |
| 253 | +{ |
| 254 | + self->fd = open("/dev/iommu", O_RDWR); |
| 255 | + ASSERT_NE(-1, self->fd); |
| 256 | + |
| 257 | + drop_cap_ipc_lock(_metadata); |
| 258 | + if (variant->accounting != IOPT_PAGES_ACCOUNT_NONE) { |
| 259 | + struct iommu_option set_limit_cmd = { |
| 260 | + .size = sizeof(set_limit_cmd), |
| 261 | + .option_id = IOMMU_OPTION_RLIMIT_MODE, |
| 262 | + .op = IOMMU_OPTION_OP_SET, |
| 263 | + .val64 = (variant->accounting == IOPT_PAGES_ACCOUNT_MM), |
| 264 | + }; |
| 265 | + ASSERT_EQ(0, ioctl(self->fd, IOMMU_OPTION, &set_limit_cmd)); |
| 266 | + } |
| 267 | + |
| 268 | + test_ioctl_ioas_alloc(&self->ioas_id); |
| 269 | + test_cmd_mock_domain(self->ioas_id, NULL, NULL, NULL); |
| 270 | +} |
| 271 | + |
| 272 | +FIXTURE_TEARDOWN(change_process) |
| 273 | +{ |
| 274 | + teardown_iommufd(self->fd, _metadata); |
| 275 | +} |
| 276 | + |
| 277 | +FIXTURE_VARIANT_ADD(change_process, account_none) |
| 278 | +{ |
| 279 | + .accounting = IOPT_PAGES_ACCOUNT_NONE, |
| 280 | +}; |
| 281 | + |
| 282 | +FIXTURE_VARIANT_ADD(change_process, account_user) |
| 283 | +{ |
| 284 | + .accounting = IOPT_PAGES_ACCOUNT_USER, |
| 285 | +}; |
| 286 | + |
| 287 | +FIXTURE_VARIANT_ADD(change_process, account_mm) |
| 288 | +{ |
| 289 | + .accounting = IOPT_PAGES_ACCOUNT_MM, |
| 290 | +}; |
| 291 | + |
| 292 | +TEST_F(change_process, basic) |
| 293 | +{ |
| 294 | + pid_t parent = getpid(); |
| 295 | + pid_t child; |
| 296 | + __u64 iova; |
| 297 | + struct iommu_ioas_change_process cmd = { |
| 298 | + .size = sizeof(cmd), |
| 299 | + }; |
| 300 | + |
| 301 | + /* Expect failure if non-file maps exist */ |
| 302 | + test_ioctl_ioas_map(buffer, PAGE_SIZE, &iova); |
| 303 | + EXPECT_ERRNO(EINVAL, ioctl(self->fd, IOMMU_IOAS_CHANGE_PROCESS, &cmd)); |
| 304 | + test_ioctl_ioas_unmap(iova, PAGE_SIZE); |
| 305 | + |
| 306 | + /* Change process works in current process. */ |
| 307 | + test_ioctl_ioas_map_file(mfd, 0, PAGE_SIZE, &iova); |
| 308 | + ASSERT_EQ(0, ioctl(self->fd, IOMMU_IOAS_CHANGE_PROCESS, &cmd)); |
| 309 | + |
| 310 | + /* Change process works in another process */ |
| 311 | + child = fork(); |
| 312 | + if (!child) { |
| 313 | + int nlock = PAGE_SIZE / 1024; |
| 314 | + |
| 315 | + /* Parent accounts for locked memory before */ |
| 316 | + ASSERT_EQ(nlock, get_vm_pinned(parent)); |
| 317 | + if (variant->accounting == IOPT_PAGES_ACCOUNT_MM) |
| 318 | + ASSERT_EQ(nlock, get_vm_locked(parent)); |
| 319 | + ASSERT_EQ(0, get_vm_pinned(getpid())); |
| 320 | + ASSERT_EQ(0, get_vm_locked(getpid())); |
| 321 | + |
| 322 | + ASSERT_EQ(0, ioctl(self->fd, IOMMU_IOAS_CHANGE_PROCESS, &cmd)); |
| 323 | + |
| 324 | + /* Child accounts for locked memory after */ |
| 325 | + ASSERT_EQ(0, get_vm_pinned(parent)); |
| 326 | + ASSERT_EQ(0, get_vm_locked(parent)); |
| 327 | + ASSERT_EQ(nlock, get_vm_pinned(getpid())); |
| 328 | + if (variant->accounting == IOPT_PAGES_ACCOUNT_MM) |
| 329 | + ASSERT_EQ(nlock, get_vm_locked(getpid())); |
| 330 | + |
| 331 | + exit(0); |
| 332 | + } |
| 333 | + ASSERT_NE(-1, child); |
| 334 | + ASSERT_EQ(child, waitpid(child, NULL, 0)); |
| 335 | +} |
| 336 | + |
196 | 337 | FIXTURE(iommufd_ioas)
|
197 | 338 | {
|
198 | 339 | int fd;
|
|
0 commit comments