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

Configure Feed

Select the types of activity you want to include in your feed.

iov_iter: Kunit tests for copying to/from an iterator

Add some kunit tests for page extraction for ITER_BVEC, ITER_KVEC and
ITER_XARRAY type iterators. ITER_UBUF and ITER_IOVEC aren't dealt with
as they require userspace VM interaction. ITER_DISCARD isn't dealt with
either as that does nothing.

Signed-off-by: David Howells <dhowells@redhat.com>
Cc: Christoph Hellwig <hch@lst.de>
Cc: Christian Brauner <brauner@kernel.org>
Cc: Jens Axboe <axboe@kernel.dk>
Cc: Al Viro <viro@zeniv.linux.org.uk>
Cc: David Hildenbrand <david@redhat.com>
Cc: John Hubbard <jhubbard@nvidia.com>
Signed-off-by: Linus Torvalds <torvalds@linux-foundation.org>

authored by

David Howells and committed by
Linus Torvalds
2d71340f f741bd71

+549
+11
lib/Kconfig.debug
··· 2237 2237 2238 2238 If unsure, say N. 2239 2239 2240 + config TEST_IOV_ITER 2241 + tristate "Test iov_iter operation" if !KUNIT_ALL_TESTS 2242 + depends on KUNIT 2243 + default KUNIT_ALL_TESTS 2244 + help 2245 + Enable this to turn on testing of the operation of the I/O iterator 2246 + (iov_iter). This test is executed only once during system boot (so 2247 + affects only boot time), or at module load time. 2248 + 2249 + If unsure, say N. 2250 + 2240 2251 config KPROBES_SANITY_TEST 2241 2252 tristate "Kprobes sanity tests" if !KUNIT_ALL_TESTS 2242 2253 depends on DEBUG_KERNEL
+1
lib/Makefile
··· 64 64 CFLAGS_test_bitops.o += -Werror 65 65 obj-$(CONFIG_CPUMASK_KUNIT_TEST) += cpumask_kunit.o 66 66 obj-$(CONFIG_TEST_SYSCTL) += test_sysctl.o 67 + obj-$(CONFIG_TEST_IOV_ITER) += kunit_iov_iter.o 67 68 obj-$(CONFIG_HASH_KUNIT_TEST) += test_hash.o 68 69 obj-$(CONFIG_TEST_IDA) += test_ida.o 69 70 obj-$(CONFIG_TEST_UBSAN) += test_ubsan.o
+537
lib/kunit_iov_iter.c
··· 1 + // SPDX-License-Identifier: GPL-2.0-only 2 + /* I/O iterator tests. This can only test kernel-backed iterator types. 3 + * 4 + * Copyright (C) 2023 Red Hat, Inc. All Rights Reserved. 5 + * Written by David Howells (dhowells@redhat.com) 6 + */ 7 + 8 + #define pr_fmt(fmt) KBUILD_MODNAME ": " fmt 9 + 10 + #include <linux/module.h> 11 + #include <linux/vmalloc.h> 12 + #include <linux/mm.h> 13 + #include <linux/uio.h> 14 + #include <linux/bvec.h> 15 + #include <kunit/test.h> 16 + 17 + MODULE_DESCRIPTION("iov_iter testing"); 18 + MODULE_AUTHOR("David Howells <dhowells@redhat.com>"); 19 + MODULE_LICENSE("GPL"); 20 + 21 + struct kvec_test_range { 22 + int from, to; 23 + }; 24 + 25 + static const struct kvec_test_range kvec_test_ranges[] = { 26 + { 0x00002, 0x00002 }, 27 + { 0x00027, 0x03000 }, 28 + { 0x05193, 0x18794 }, 29 + { 0x20000, 0x20000 }, 30 + { 0x20000, 0x24000 }, 31 + { 0x24000, 0x27001 }, 32 + { 0x29000, 0xffffb }, 33 + { 0xffffd, 0xffffe }, 34 + { -1 } 35 + }; 36 + 37 + static inline u8 pattern(unsigned long x) 38 + { 39 + return x & 0xff; 40 + } 41 + 42 + static void iov_kunit_unmap(void *data) 43 + { 44 + vunmap(data); 45 + } 46 + 47 + static void *__init iov_kunit_create_buffer(struct kunit *test, 48 + struct page ***ppages, 49 + size_t npages) 50 + { 51 + struct page **pages; 52 + unsigned long got; 53 + void *buffer; 54 + 55 + pages = kunit_kcalloc(test, npages, sizeof(struct page *), GFP_KERNEL); 56 + KUNIT_ASSERT_NOT_ERR_OR_NULL(test, pages); 57 + *ppages = pages; 58 + 59 + got = alloc_pages_bulk_array(GFP_KERNEL, npages, pages); 60 + if (got != npages) { 61 + release_pages(pages, got); 62 + KUNIT_ASSERT_EQ(test, got, npages); 63 + } 64 + 65 + buffer = vmap(pages, npages, VM_MAP | VM_MAP_PUT_PAGES, PAGE_KERNEL); 66 + KUNIT_ASSERT_NOT_ERR_OR_NULL(test, buffer); 67 + 68 + kunit_add_action_or_reset(test, iov_kunit_unmap, buffer); 69 + return buffer; 70 + } 71 + 72 + static void __init iov_kunit_load_kvec(struct kunit *test, 73 + struct iov_iter *iter, int dir, 74 + struct kvec *kvec, unsigned int kvmax, 75 + void *buffer, size_t bufsize, 76 + const struct kvec_test_range *pr) 77 + { 78 + size_t size = 0; 79 + int i; 80 + 81 + for (i = 0; i < kvmax; i++, pr++) { 82 + if (pr->from < 0) 83 + break; 84 + KUNIT_ASSERT_GE(test, pr->to, pr->from); 85 + KUNIT_ASSERT_LE(test, pr->to, bufsize); 86 + kvec[i].iov_base = buffer + pr->from; 87 + kvec[i].iov_len = pr->to - pr->from; 88 + size += pr->to - pr->from; 89 + } 90 + KUNIT_ASSERT_LE(test, size, bufsize); 91 + 92 + iov_iter_kvec(iter, dir, kvec, i, size); 93 + } 94 + 95 + /* 96 + * Test copying to a ITER_KVEC-type iterator. 97 + */ 98 + static void __init iov_kunit_copy_to_kvec(struct kunit *test) 99 + { 100 + const struct kvec_test_range *pr; 101 + struct iov_iter iter; 102 + struct page **spages, **bpages; 103 + struct kvec kvec[8]; 104 + u8 *scratch, *buffer; 105 + size_t bufsize, npages, size, copied; 106 + int i, patt; 107 + 108 + bufsize = 0x100000; 109 + npages = bufsize / PAGE_SIZE; 110 + 111 + scratch = iov_kunit_create_buffer(test, &spages, npages); 112 + for (i = 0; i < bufsize; i++) 113 + scratch[i] = pattern(i); 114 + 115 + buffer = iov_kunit_create_buffer(test, &bpages, npages); 116 + memset(buffer, 0, bufsize); 117 + 118 + iov_kunit_load_kvec(test, &iter, READ, kvec, ARRAY_SIZE(kvec), 119 + buffer, bufsize, kvec_test_ranges); 120 + size = iter.count; 121 + 122 + copied = copy_to_iter(scratch, size, &iter); 123 + 124 + KUNIT_EXPECT_EQ(test, copied, size); 125 + KUNIT_EXPECT_EQ(test, iter.count, 0); 126 + KUNIT_EXPECT_EQ(test, iter.nr_segs, 0); 127 + 128 + /* Build the expected image in the scratch buffer. */ 129 + patt = 0; 130 + memset(scratch, 0, bufsize); 131 + for (pr = kvec_test_ranges; pr->from >= 0; pr++) 132 + for (i = pr->from; i < pr->to; i++) 133 + scratch[i] = pattern(patt++); 134 + 135 + /* Compare the images */ 136 + for (i = 0; i < bufsize; i++) { 137 + KUNIT_EXPECT_EQ_MSG(test, buffer[i], scratch[i], "at i=%x", i); 138 + if (buffer[i] != scratch[i]) 139 + return; 140 + } 141 + 142 + KUNIT_SUCCEED(); 143 + } 144 + 145 + /* 146 + * Test copying from a ITER_KVEC-type iterator. 147 + */ 148 + static void __init iov_kunit_copy_from_kvec(struct kunit *test) 149 + { 150 + const struct kvec_test_range *pr; 151 + struct iov_iter iter; 152 + struct page **spages, **bpages; 153 + struct kvec kvec[8]; 154 + u8 *scratch, *buffer; 155 + size_t bufsize, npages, size, copied; 156 + int i, j; 157 + 158 + bufsize = 0x100000; 159 + npages = bufsize / PAGE_SIZE; 160 + 161 + buffer = iov_kunit_create_buffer(test, &bpages, npages); 162 + for (i = 0; i < bufsize; i++) 163 + buffer[i] = pattern(i); 164 + 165 + scratch = iov_kunit_create_buffer(test, &spages, npages); 166 + memset(scratch, 0, bufsize); 167 + 168 + iov_kunit_load_kvec(test, &iter, WRITE, kvec, ARRAY_SIZE(kvec), 169 + buffer, bufsize, kvec_test_ranges); 170 + size = min(iter.count, bufsize); 171 + 172 + copied = copy_from_iter(scratch, size, &iter); 173 + 174 + KUNIT_EXPECT_EQ(test, copied, size); 175 + KUNIT_EXPECT_EQ(test, iter.count, 0); 176 + KUNIT_EXPECT_EQ(test, iter.nr_segs, 0); 177 + 178 + /* Build the expected image in the main buffer. */ 179 + i = 0; 180 + memset(buffer, 0, bufsize); 181 + for (pr = kvec_test_ranges; pr->from >= 0; pr++) { 182 + for (j = pr->from; j < pr->to; j++) { 183 + buffer[i++] = pattern(j); 184 + if (i >= bufsize) 185 + goto stop; 186 + } 187 + } 188 + stop: 189 + 190 + /* Compare the images */ 191 + for (i = 0; i < bufsize; i++) { 192 + KUNIT_EXPECT_EQ_MSG(test, scratch[i], buffer[i], "at i=%x", i); 193 + if (scratch[i] != buffer[i]) 194 + return; 195 + } 196 + 197 + KUNIT_SUCCEED(); 198 + } 199 + 200 + struct bvec_test_range { 201 + int page, from, to; 202 + }; 203 + 204 + static const struct bvec_test_range bvec_test_ranges[] = { 205 + { 0, 0x0002, 0x0002 }, 206 + { 1, 0x0027, 0x0893 }, 207 + { 2, 0x0193, 0x0794 }, 208 + { 3, 0x0000, 0x1000 }, 209 + { 4, 0x0000, 0x1000 }, 210 + { 5, 0x0000, 0x1000 }, 211 + { 6, 0x0000, 0x0ffb }, 212 + { 6, 0x0ffd, 0x0ffe }, 213 + { -1, -1, -1 } 214 + }; 215 + 216 + static void __init iov_kunit_load_bvec(struct kunit *test, 217 + struct iov_iter *iter, int dir, 218 + struct bio_vec *bvec, unsigned int bvmax, 219 + struct page **pages, size_t npages, 220 + size_t bufsize, 221 + const struct bvec_test_range *pr) 222 + { 223 + struct page *can_merge = NULL, *page; 224 + size_t size = 0; 225 + int i; 226 + 227 + for (i = 0; i < bvmax; i++, pr++) { 228 + if (pr->from < 0) 229 + break; 230 + KUNIT_ASSERT_LT(test, pr->page, npages); 231 + KUNIT_ASSERT_LT(test, pr->page * PAGE_SIZE, bufsize); 232 + KUNIT_ASSERT_GE(test, pr->from, 0); 233 + KUNIT_ASSERT_GE(test, pr->to, pr->from); 234 + KUNIT_ASSERT_LE(test, pr->to, PAGE_SIZE); 235 + 236 + page = pages[pr->page]; 237 + if (pr->from == 0 && pr->from != pr->to && page == can_merge) { 238 + i--; 239 + bvec[i].bv_len += pr->to; 240 + } else { 241 + bvec_set_page(&bvec[i], page, pr->to - pr->from, pr->from); 242 + } 243 + 244 + size += pr->to - pr->from; 245 + if ((pr->to & ~PAGE_MASK) == 0) 246 + can_merge = page + pr->to / PAGE_SIZE; 247 + else 248 + can_merge = NULL; 249 + } 250 + 251 + iov_iter_bvec(iter, dir, bvec, i, size); 252 + } 253 + 254 + /* 255 + * Test copying to a ITER_BVEC-type iterator. 256 + */ 257 + static void __init iov_kunit_copy_to_bvec(struct kunit *test) 258 + { 259 + const struct bvec_test_range *pr; 260 + struct iov_iter iter; 261 + struct bio_vec bvec[8]; 262 + struct page **spages, **bpages; 263 + u8 *scratch, *buffer; 264 + size_t bufsize, npages, size, copied; 265 + int i, b, patt; 266 + 267 + bufsize = 0x100000; 268 + npages = bufsize / PAGE_SIZE; 269 + 270 + scratch = iov_kunit_create_buffer(test, &spages, npages); 271 + for (i = 0; i < bufsize; i++) 272 + scratch[i] = pattern(i); 273 + 274 + buffer = iov_kunit_create_buffer(test, &bpages, npages); 275 + memset(buffer, 0, bufsize); 276 + 277 + iov_kunit_load_bvec(test, &iter, READ, bvec, ARRAY_SIZE(bvec), 278 + bpages, npages, bufsize, bvec_test_ranges); 279 + size = iter.count; 280 + 281 + copied = copy_to_iter(scratch, size, &iter); 282 + 283 + KUNIT_EXPECT_EQ(test, copied, size); 284 + KUNIT_EXPECT_EQ(test, iter.count, 0); 285 + KUNIT_EXPECT_EQ(test, iter.nr_segs, 0); 286 + 287 + /* Build the expected image in the scratch buffer. */ 288 + b = 0; 289 + patt = 0; 290 + memset(scratch, 0, bufsize); 291 + for (pr = bvec_test_ranges; pr->from >= 0; pr++, b++) { 292 + u8 *p = scratch + pr->page * PAGE_SIZE; 293 + 294 + for (i = pr->from; i < pr->to; i++) 295 + p[i] = pattern(patt++); 296 + } 297 + 298 + /* Compare the images */ 299 + for (i = 0; i < bufsize; i++) { 300 + KUNIT_EXPECT_EQ_MSG(test, buffer[i], scratch[i], "at i=%x", i); 301 + if (buffer[i] != scratch[i]) 302 + return; 303 + } 304 + 305 + KUNIT_SUCCEED(); 306 + } 307 + 308 + /* 309 + * Test copying from a ITER_BVEC-type iterator. 310 + */ 311 + static void __init iov_kunit_copy_from_bvec(struct kunit *test) 312 + { 313 + const struct bvec_test_range *pr; 314 + struct iov_iter iter; 315 + struct bio_vec bvec[8]; 316 + struct page **spages, **bpages; 317 + u8 *scratch, *buffer; 318 + size_t bufsize, npages, size, copied; 319 + int i, j; 320 + 321 + bufsize = 0x100000; 322 + npages = bufsize / PAGE_SIZE; 323 + 324 + buffer = iov_kunit_create_buffer(test, &bpages, npages); 325 + for (i = 0; i < bufsize; i++) 326 + buffer[i] = pattern(i); 327 + 328 + scratch = iov_kunit_create_buffer(test, &spages, npages); 329 + memset(scratch, 0, bufsize); 330 + 331 + iov_kunit_load_bvec(test, &iter, WRITE, bvec, ARRAY_SIZE(bvec), 332 + bpages, npages, bufsize, bvec_test_ranges); 333 + size = iter.count; 334 + 335 + copied = copy_from_iter(scratch, size, &iter); 336 + 337 + KUNIT_EXPECT_EQ(test, copied, size); 338 + KUNIT_EXPECT_EQ(test, iter.count, 0); 339 + KUNIT_EXPECT_EQ(test, iter.nr_segs, 0); 340 + 341 + /* Build the expected image in the main buffer. */ 342 + i = 0; 343 + memset(buffer, 0, bufsize); 344 + for (pr = bvec_test_ranges; pr->from >= 0; pr++) { 345 + size_t patt = pr->page * PAGE_SIZE; 346 + 347 + for (j = pr->from; j < pr->to; j++) { 348 + buffer[i++] = pattern(patt + j); 349 + if (i >= bufsize) 350 + goto stop; 351 + } 352 + } 353 + stop: 354 + 355 + /* Compare the images */ 356 + for (i = 0; i < bufsize; i++) { 357 + KUNIT_EXPECT_EQ_MSG(test, scratch[i], buffer[i], "at i=%x", i); 358 + if (scratch[i] != buffer[i]) 359 + return; 360 + } 361 + 362 + KUNIT_SUCCEED(); 363 + } 364 + 365 + static void iov_kunit_destroy_xarray(void *data) 366 + { 367 + struct xarray *xarray = data; 368 + 369 + xa_destroy(xarray); 370 + kfree(xarray); 371 + } 372 + 373 + static void __init iov_kunit_load_xarray(struct kunit *test, 374 + struct iov_iter *iter, int dir, 375 + struct xarray *xarray, 376 + struct page **pages, size_t npages) 377 + { 378 + size_t size = 0; 379 + int i; 380 + 381 + for (i = 0; i < npages; i++) { 382 + void *x = xa_store(xarray, i, pages[i], GFP_KERNEL); 383 + 384 + KUNIT_ASSERT_FALSE(test, xa_is_err(x)); 385 + size += PAGE_SIZE; 386 + } 387 + iov_iter_xarray(iter, dir, xarray, 0, size); 388 + } 389 + 390 + static struct xarray *iov_kunit_create_xarray(struct kunit *test) 391 + { 392 + struct xarray *xarray; 393 + 394 + xarray = kzalloc(sizeof(struct xarray), GFP_KERNEL); 395 + xa_init(xarray); 396 + KUNIT_ASSERT_NOT_ERR_OR_NULL(test, xarray); 397 + kunit_add_action_or_reset(test, iov_kunit_destroy_xarray, xarray); 398 + return xarray; 399 + } 400 + 401 + /* 402 + * Test copying to a ITER_XARRAY-type iterator. 403 + */ 404 + static void __init iov_kunit_copy_to_xarray(struct kunit *test) 405 + { 406 + const struct kvec_test_range *pr; 407 + struct iov_iter iter; 408 + struct xarray *xarray; 409 + struct page **spages, **bpages; 410 + u8 *scratch, *buffer; 411 + size_t bufsize, npages, size, copied; 412 + int i, patt; 413 + 414 + bufsize = 0x100000; 415 + npages = bufsize / PAGE_SIZE; 416 + 417 + xarray = iov_kunit_create_xarray(test); 418 + 419 + scratch = iov_kunit_create_buffer(test, &spages, npages); 420 + for (i = 0; i < bufsize; i++) 421 + scratch[i] = pattern(i); 422 + 423 + buffer = iov_kunit_create_buffer(test, &bpages, npages); 424 + memset(buffer, 0, bufsize); 425 + 426 + iov_kunit_load_xarray(test, &iter, READ, xarray, bpages, npages); 427 + 428 + i = 0; 429 + for (pr = kvec_test_ranges; pr->from >= 0; pr++) { 430 + size = pr->to - pr->from; 431 + KUNIT_ASSERT_LE(test, pr->to, bufsize); 432 + 433 + iov_iter_xarray(&iter, READ, xarray, pr->from, size); 434 + copied = copy_to_iter(scratch + i, size, &iter); 435 + 436 + KUNIT_EXPECT_EQ(test, copied, size); 437 + KUNIT_EXPECT_EQ(test, iter.count, 0); 438 + KUNIT_EXPECT_EQ(test, iter.iov_offset, size); 439 + i += size; 440 + } 441 + 442 + /* Build the expected image in the scratch buffer. */ 443 + patt = 0; 444 + memset(scratch, 0, bufsize); 445 + for (pr = kvec_test_ranges; pr->from >= 0; pr++) 446 + for (i = pr->from; i < pr->to; i++) 447 + scratch[i] = pattern(patt++); 448 + 449 + /* Compare the images */ 450 + for (i = 0; i < bufsize; i++) { 451 + KUNIT_EXPECT_EQ_MSG(test, buffer[i], scratch[i], "at i=%x", i); 452 + if (buffer[i] != scratch[i]) 453 + return; 454 + } 455 + 456 + KUNIT_SUCCEED(); 457 + } 458 + 459 + /* 460 + * Test copying from a ITER_XARRAY-type iterator. 461 + */ 462 + static void __init iov_kunit_copy_from_xarray(struct kunit *test) 463 + { 464 + const struct kvec_test_range *pr; 465 + struct iov_iter iter; 466 + struct xarray *xarray; 467 + struct page **spages, **bpages; 468 + u8 *scratch, *buffer; 469 + size_t bufsize, npages, size, copied; 470 + int i, j; 471 + 472 + bufsize = 0x100000; 473 + npages = bufsize / PAGE_SIZE; 474 + 475 + xarray = iov_kunit_create_xarray(test); 476 + 477 + buffer = iov_kunit_create_buffer(test, &bpages, npages); 478 + for (i = 0; i < bufsize; i++) 479 + buffer[i] = pattern(i); 480 + 481 + scratch = iov_kunit_create_buffer(test, &spages, npages); 482 + memset(scratch, 0, bufsize); 483 + 484 + iov_kunit_load_xarray(test, &iter, READ, xarray, bpages, npages); 485 + 486 + i = 0; 487 + for (pr = kvec_test_ranges; pr->from >= 0; pr++) { 488 + size = pr->to - pr->from; 489 + KUNIT_ASSERT_LE(test, pr->to, bufsize); 490 + 491 + iov_iter_xarray(&iter, WRITE, xarray, pr->from, size); 492 + copied = copy_from_iter(scratch + i, size, &iter); 493 + 494 + KUNIT_EXPECT_EQ(test, copied, size); 495 + KUNIT_EXPECT_EQ(test, iter.count, 0); 496 + KUNIT_EXPECT_EQ(test, iter.iov_offset, size); 497 + i += size; 498 + } 499 + 500 + /* Build the expected image in the main buffer. */ 501 + i = 0; 502 + memset(buffer, 0, bufsize); 503 + for (pr = kvec_test_ranges; pr->from >= 0; pr++) { 504 + for (j = pr->from; j < pr->to; j++) { 505 + buffer[i++] = pattern(j); 506 + if (i >= bufsize) 507 + goto stop; 508 + } 509 + } 510 + stop: 511 + 512 + /* Compare the images */ 513 + for (i = 0; i < bufsize; i++) { 514 + KUNIT_EXPECT_EQ_MSG(test, scratch[i], buffer[i], "at i=%x", i); 515 + if (scratch[i] != buffer[i]) 516 + return; 517 + } 518 + 519 + KUNIT_SUCCEED(); 520 + } 521 + 522 + static struct kunit_case __refdata iov_kunit_cases[] = { 523 + KUNIT_CASE(iov_kunit_copy_to_kvec), 524 + KUNIT_CASE(iov_kunit_copy_from_kvec), 525 + KUNIT_CASE(iov_kunit_copy_to_bvec), 526 + KUNIT_CASE(iov_kunit_copy_from_bvec), 527 + KUNIT_CASE(iov_kunit_copy_to_xarray), 528 + KUNIT_CASE(iov_kunit_copy_from_xarray), 529 + {} 530 + }; 531 + 532 + static struct kunit_suite iov_kunit_suite = { 533 + .name = "iov_iter", 534 + .test_cases = iov_kunit_cases, 535 + }; 536 + 537 + kunit_test_suites(&iov_kunit_suite);