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

selftests/udmabuf: add tests to verify data after page migration

Since the memfd pages associated with a udmabuf may be migrated as part of
udmabuf create, we need to verify the data coherency after successful
migration. The new tests added in this patch try to do just that using 4k
sized pages and also 2 MB sized huge pages for the memfd.

Successful completion of the tests would mean that there is no disconnect
between the memfd pages and the ones associated with a udmabuf. And,
these tests can also be augmented in the future to test newer udmabuf
features (such as handling memfd hole punch).

The idea for these tests comes from a patch by Mike Kravetz here:
https://lists.freedesktop.org/archives/dri-devel/2023-June/410623.html

v1->v2: (suggestions from Shuah)
- Use ksft_* functions to print and capture results of tests
- Use appropriate KSFT_* status codes for exit()
- Add Mike Kravetz's suggested-by tag

Link: https://lkml.kernel.org/r/20240624063952.1572359-10-vivek.kasireddy@intel.com
Signed-off-by: Vivek Kasireddy <vivek.kasireddy@intel.com>
Suggested-by: Mike Kravetz <mike.kravetz@oracle.com>
Acked-by: Dave Airlie <airlied@redhat.com>
Acked-by: Gerd Hoffmann <kraxel@redhat.com>
Cc: Shuah Khan <shuah@kernel.org>
Cc: David Hildenbrand <david@redhat.com>
Cc: Daniel Vetter <daniel.vetter@ffwll.ch>
Cc: Hugh Dickins <hughd@google.com>
Cc: Peter Xu <peterx@redhat.com>
Cc: Jason Gunthorpe <jgg@nvidia.com>
Cc: Dongwon Kim <dongwon.kim@intel.com>
Cc: Junxiao Chang <junxiao.chang@intel.com>
Cc: Arnd Bergmann <arnd@arndb.de>
Cc: Christoph Hellwig <hch@infradead.org>
Cc: Christoph Hellwig <hch@lst.de>
Cc: Matthew Wilcox (Oracle) <willy@infradead.org>
Cc: Oscar Salvador <osalvador@suse.de>
Signed-off-by: Andrew Morton <akpm@linux-foundation.org>

authored by

Vivek Kasireddy and committed by
Andrew Morton
8d42e2a9 c6a3194c

+183 -31
+183 -31
tools/testing/selftests/drivers/dma-buf/udmabuf.c
··· 9 9 #include <errno.h> 10 10 #include <fcntl.h> 11 11 #include <malloc.h> 12 + #include <stdbool.h> 12 13 13 14 #include <sys/ioctl.h> 14 15 #include <sys/syscall.h> 16 + #include <sys/mman.h> 15 17 #include <linux/memfd.h> 16 18 #include <linux/udmabuf.h> 19 + #include "../../kselftest.h" 17 20 18 21 #define TEST_PREFIX "drivers/dma-buf/udmabuf" 19 22 #define NUM_PAGES 4 23 + #define NUM_ENTRIES 4 24 + #define MEMFD_SIZE 1024 /* in pages */ 20 25 21 - static int memfd_create(const char *name, unsigned int flags) 26 + static unsigned int page_size; 27 + 28 + static int create_memfd_with_seals(off64_t size, bool hpage) 22 29 { 23 - return syscall(__NR_memfd_create, name, flags); 30 + int memfd, ret; 31 + unsigned int flags = MFD_ALLOW_SEALING; 32 + 33 + if (hpage) 34 + flags |= MFD_HUGETLB; 35 + 36 + memfd = memfd_create("udmabuf-test", flags); 37 + if (memfd < 0) { 38 + ksft_print_msg("%s: [skip,no-memfd]\n", TEST_PREFIX); 39 + exit(KSFT_SKIP); 40 + } 41 + 42 + ret = fcntl(memfd, F_ADD_SEALS, F_SEAL_SHRINK); 43 + if (ret < 0) { 44 + ksft_print_msg("%s: [skip,fcntl-add-seals]\n", TEST_PREFIX); 45 + exit(KSFT_SKIP); 46 + } 47 + 48 + ret = ftruncate(memfd, size); 49 + if (ret == -1) { 50 + ksft_print_msg("%s: [FAIL,memfd-truncate]\n", TEST_PREFIX); 51 + exit(KSFT_FAIL); 52 + } 53 + 54 + return memfd; 55 + } 56 + 57 + static int create_udmabuf_list(int devfd, int memfd, off64_t memfd_size) 58 + { 59 + struct udmabuf_create_list *list; 60 + int ubuf_fd, i; 61 + 62 + list = malloc(sizeof(struct udmabuf_create_list) + 63 + sizeof(struct udmabuf_create_item) * NUM_ENTRIES); 64 + if (!list) { 65 + ksft_print_msg("%s: [FAIL, udmabuf-malloc]\n", TEST_PREFIX); 66 + exit(KSFT_FAIL); 67 + } 68 + 69 + for (i = 0; i < NUM_ENTRIES; i++) { 70 + list->list[i].memfd = memfd; 71 + list->list[i].offset = i * (memfd_size / NUM_ENTRIES); 72 + list->list[i].size = getpagesize() * NUM_PAGES; 73 + } 74 + 75 + list->count = NUM_ENTRIES; 76 + list->flags = UDMABUF_FLAGS_CLOEXEC; 77 + ubuf_fd = ioctl(devfd, UDMABUF_CREATE_LIST, list); 78 + free(list); 79 + if (ubuf_fd < 0) { 80 + ksft_print_msg("%s: [FAIL, udmabuf-create]\n", TEST_PREFIX); 81 + exit(KSFT_FAIL); 82 + } 83 + 84 + return ubuf_fd; 85 + } 86 + 87 + static void write_to_memfd(void *addr, off64_t size, char chr) 88 + { 89 + int i; 90 + 91 + for (i = 0; i < size / page_size; i++) { 92 + *((char *)addr + (i * page_size)) = chr; 93 + } 94 + } 95 + 96 + static void *mmap_fd(int fd, off64_t size) 97 + { 98 + void *addr; 99 + 100 + addr = mmap(NULL, size, PROT_READ|PROT_WRITE, MAP_SHARED, fd, 0); 101 + if (addr == MAP_FAILED) { 102 + ksft_print_msg("%s: ubuf_fd mmap fail\n", TEST_PREFIX); 103 + exit(KSFT_FAIL); 104 + } 105 + 106 + return addr; 107 + } 108 + 109 + static int compare_chunks(void *addr1, void *addr2, off64_t memfd_size) 110 + { 111 + off64_t off; 112 + int i = 0, j, k = 0, ret = 0; 113 + char char1, char2; 114 + 115 + while (i < NUM_ENTRIES) { 116 + off = i * (memfd_size / NUM_ENTRIES); 117 + for (j = 0; j < NUM_PAGES; j++, k++) { 118 + char1 = *((char *)addr1 + off + (j * getpagesize())); 119 + char2 = *((char *)addr2 + (k * getpagesize())); 120 + if (char1 != char2) { 121 + ret = -1; 122 + goto err; 123 + } 124 + } 125 + i++; 126 + } 127 + err: 128 + munmap(addr1, memfd_size); 129 + munmap(addr2, NUM_ENTRIES * NUM_PAGES * getpagesize()); 130 + return ret; 24 131 } 25 132 26 133 int main(int argc, char *argv[]) 27 134 { 28 135 struct udmabuf_create create; 29 136 int devfd, memfd, buf, ret; 30 - off_t size; 31 - void *mem; 137 + off64_t size; 138 + void *addr1, *addr2; 139 + 140 + ksft_print_header(); 141 + ksft_set_plan(6); 32 142 33 143 devfd = open("/dev/udmabuf", O_RDWR); 34 144 if (devfd < 0) { 35 - printf("%s: [skip,no-udmabuf: Unable to access DMA buffer device file]\n", 36 - TEST_PREFIX); 37 - exit(77); 145 + ksft_print_msg( 146 + "%s: [skip,no-udmabuf: Unable to access DMA buffer device file]\n", 147 + TEST_PREFIX); 148 + exit(KSFT_SKIP); 38 149 } 39 150 40 151 memfd = memfd_create("udmabuf-test", MFD_ALLOW_SEALING); 41 152 if (memfd < 0) { 42 - printf("%s: [skip,no-memfd]\n", TEST_PREFIX); 43 - exit(77); 153 + ksft_print_msg("%s: [skip,no-memfd]\n", TEST_PREFIX); 154 + exit(KSFT_SKIP); 44 155 } 45 156 46 157 ret = fcntl(memfd, F_ADD_SEALS, F_SEAL_SHRINK); 47 158 if (ret < 0) { 48 - printf("%s: [skip,fcntl-add-seals]\n", TEST_PREFIX); 49 - exit(77); 159 + ksft_print_msg("%s: [skip,fcntl-add-seals]\n", TEST_PREFIX); 160 + exit(KSFT_SKIP); 50 161 } 51 - 52 162 53 163 size = getpagesize() * NUM_PAGES; 54 164 ret = ftruncate(memfd, size); 55 165 if (ret == -1) { 56 - printf("%s: [FAIL,memfd-truncate]\n", TEST_PREFIX); 57 - exit(1); 166 + ksft_print_msg("%s: [FAIL,memfd-truncate]\n", TEST_PREFIX); 167 + exit(KSFT_FAIL); 58 168 } 59 169 60 170 memset(&create, 0, sizeof(create)); ··· 174 64 create.offset = getpagesize()/2; 175 65 create.size = getpagesize(); 176 66 buf = ioctl(devfd, UDMABUF_CREATE, &create); 177 - if (buf >= 0) { 178 - printf("%s: [FAIL,test-1]\n", TEST_PREFIX); 179 - exit(1); 180 - } 67 + if (buf >= 0) 68 + ksft_test_result_fail("%s: [FAIL,test-1]\n", TEST_PREFIX); 69 + else 70 + ksft_test_result_pass("%s: [PASS,test-1]\n", TEST_PREFIX); 181 71 182 72 /* should fail (size not multiple of page) */ 183 73 create.memfd = memfd; 184 74 create.offset = 0; 185 75 create.size = getpagesize()/2; 186 76 buf = ioctl(devfd, UDMABUF_CREATE, &create); 187 - if (buf >= 0) { 188 - printf("%s: [FAIL,test-2]\n", TEST_PREFIX); 189 - exit(1); 190 - } 77 + if (buf >= 0) 78 + ksft_test_result_fail("%s: [FAIL,test-2]\n", TEST_PREFIX); 79 + else 80 + ksft_test_result_pass("%s: [PASS,test-2]\n", TEST_PREFIX); 191 81 192 82 /* should fail (not memfd) */ 193 83 create.memfd = 0; /* stdin */ 194 84 create.offset = 0; 195 85 create.size = size; 196 86 buf = ioctl(devfd, UDMABUF_CREATE, &create); 197 - if (buf >= 0) { 198 - printf("%s: [FAIL,test-3]\n", TEST_PREFIX); 199 - exit(1); 200 - } 87 + if (buf >= 0) 88 + ksft_test_result_fail("%s: [FAIL,test-3]\n", TEST_PREFIX); 89 + else 90 + ksft_test_result_pass("%s: [PASS,test-3]\n", TEST_PREFIX); 201 91 202 92 /* should work */ 93 + page_size = getpagesize(); 94 + addr1 = mmap_fd(memfd, size); 95 + write_to_memfd(addr1, size, 'a'); 203 96 create.memfd = memfd; 204 97 create.offset = 0; 205 98 create.size = size; 206 99 buf = ioctl(devfd, UDMABUF_CREATE, &create); 207 - if (buf < 0) { 208 - printf("%s: [FAIL,test-4]\n", TEST_PREFIX); 209 - exit(1); 210 - } 100 + if (buf < 0) 101 + ksft_test_result_fail("%s: [FAIL,test-4]\n", TEST_PREFIX); 102 + else 103 + ksft_test_result_pass("%s: [PASS,test-4]\n", TEST_PREFIX); 211 104 212 - fprintf(stderr, "%s: ok\n", TEST_PREFIX); 105 + munmap(addr1, size); 106 + close(buf); 107 + close(memfd); 108 + 109 + /* should work (migration of 4k size pages)*/ 110 + size = MEMFD_SIZE * page_size; 111 + memfd = create_memfd_with_seals(size, false); 112 + addr1 = mmap_fd(memfd, size); 113 + write_to_memfd(addr1, size, 'a'); 114 + buf = create_udmabuf_list(devfd, memfd, size); 115 + addr2 = mmap_fd(buf, NUM_PAGES * NUM_ENTRIES * getpagesize()); 116 + write_to_memfd(addr1, size, 'b'); 117 + ret = compare_chunks(addr1, addr2, size); 118 + if (ret < 0) 119 + ksft_test_result_fail("%s: [FAIL,test-5]\n", TEST_PREFIX); 120 + else 121 + ksft_test_result_pass("%s: [PASS,test-5]\n", TEST_PREFIX); 122 + 123 + close(buf); 124 + close(memfd); 125 + 126 + /* should work (migration of 2MB size huge pages)*/ 127 + page_size = getpagesize() * 512; /* 2 MB */ 128 + size = MEMFD_SIZE * page_size; 129 + memfd = create_memfd_with_seals(size, true); 130 + addr1 = mmap_fd(memfd, size); 131 + write_to_memfd(addr1, size, 'a'); 132 + buf = create_udmabuf_list(devfd, memfd, size); 133 + addr2 = mmap_fd(buf, NUM_PAGES * NUM_ENTRIES * getpagesize()); 134 + write_to_memfd(addr1, size, 'b'); 135 + ret = compare_chunks(addr1, addr2, size); 136 + if (ret < 0) 137 + ksft_test_result_fail("%s: [FAIL,test-6]\n", TEST_PREFIX); 138 + else 139 + ksft_test_result_pass("%s: [PASS,test-6]\n", TEST_PREFIX); 140 + 213 141 close(buf); 214 142 close(memfd); 215 143 close(devfd); 144 + 145 + ksft_print_msg("%s: ok\n", TEST_PREFIX); 146 + ksft_print_cnts(); 147 + 216 148 return 0; 217 149 }