1 // SPDX-License-Identifier: GPL-2.0 2 #define _GNU_SOURCE 3 #define __EXPORTED_HEADERS__ 4 5 #include <stdio.h> 6 #include <stdlib.h> 7 #include <unistd.h> 8 #include <string.h> 9 #include <errno.h> 10 #include <fcntl.h> 11 #include <malloc.h> 12 #include <stdbool.h> 13 14 #include <sys/ioctl.h> 15 #include <sys/syscall.h> 16 #include <sys/mman.h> 17 #include <linux/memfd.h> 18 #include <linux/udmabuf.h> 19 #include "../../kselftest.h" 20 21 #define TEST_PREFIX "drivers/dma-buf/udmabuf" 22 #define NUM_PAGES 4 23 #define NUM_ENTRIES 4 24 #define MEMFD_SIZE 1024 /* in pages */ 25 26 static unsigned int page_size; 27 28 static int create_memfd_with_seals(off64_t size, bool hpage) 29 { 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; 131 } 132 133 int main(int argc, char *argv[]) 134 { 135 struct udmabuf_create create; 136 int devfd, memfd, buf, ret; 137 off64_t size; 138 void *addr1, *addr2; 139 140 ksft_print_header(); 141 ksft_set_plan(6); 142 143 devfd = open("/dev/udmabuf", O_RDWR); 144 if (devfd < 0) { 145 ksft_print_msg( 146 "%s: [skip,no-udmabuf: Unable to access DMA buffer device file]\n", 147 TEST_PREFIX); 148 exit(KSFT_SKIP); 149 } 150 151 memfd = memfd_create("udmabuf-test", MFD_ALLOW_SEALING); 152 if (memfd < 0) { 153 ksft_print_msg("%s: [skip,no-memfd]\n", TEST_PREFIX); 154 exit(KSFT_SKIP); 155 } 156 157 ret = fcntl(memfd, F_ADD_SEALS, F_SEAL_SHRINK); 158 if (ret < 0) { 159 ksft_print_msg("%s: [skip,fcntl-add-seals]\n", TEST_PREFIX); 160 exit(KSFT_SKIP); 161 } 162 163 size = getpagesize() * NUM_PAGES; 164 ret = ftruncate(memfd, size); 165 if (ret == -1) { 166 ksft_print_msg("%s: [FAIL,memfd-truncate]\n", TEST_PREFIX); 167 exit(KSFT_FAIL); 168 } 169 170 memset(&create, 0, sizeof(create)); 171 172 /* should fail (offset not page aligned) */ 173 create.memfd = memfd; 174 create.offset = getpagesize()/2; 175 create.size = getpagesize(); 176 buf = ioctl(devfd, UDMABUF_CREATE, &create); 177 if (buf >= 0) 178 ksft_test_result_fail("%s: [FAIL,test-1]\n", TEST_PREFIX); 179 else 180 ksft_test_result_pass("%s: [PASS,test-1]\n", TEST_PREFIX); 181 182 /* should fail (size not multiple of page) */ 183 create.memfd = memfd; 184 create.offset = 0; 185 create.size = getpagesize()/2; 186 buf = ioctl(devfd, UDMABUF_CREATE, &create); 187 if (buf >= 0) 188 ksft_test_result_fail("%s: [FAIL,test-2]\n", TEST_PREFIX); 189 else 190 ksft_test_result_pass("%s: [PASS,test-2]\n", TEST_PREFIX); 191 192 /* should fail (not memfd) */ 193 create.memfd = 0; /* stdin */ 194 create.offset = 0; 195 create.size = size; 196 buf = ioctl(devfd, UDMABUF_CREATE, &create); 197 if (buf >= 0) 198 ksft_test_result_fail("%s: [FAIL,test-3]\n", TEST_PREFIX); 199 else 200 ksft_test_result_pass("%s: [PASS,test-3]\n", TEST_PREFIX); 201 202 /* should work */ 203 page_size = getpagesize(); 204 addr1 = mmap_fd(memfd, size); 205 write_to_memfd(addr1, size, 'a'); 206 create.memfd = memfd; 207 create.offset = 0; 208 create.size = size; 209 buf = ioctl(devfd, UDMABUF_CREATE, &create); 210 if (buf < 0) 211 ksft_test_result_fail("%s: [FAIL,test-4]\n", TEST_PREFIX); 212 else 213 ksft_test_result_pass("%s: [PASS,test-4]\n", TEST_PREFIX); 214 215 munmap(addr1, size); 216 close(buf); 217 close(memfd); 218 219 /* should work (migration of 4k size pages)*/ 220 size = MEMFD_SIZE * page_size; 221 memfd = create_memfd_with_seals(size, false); 222 addr1 = mmap_fd(memfd, size); 223 write_to_memfd(addr1, size, 'a'); 224 buf = create_udmabuf_list(devfd, memfd, size); 225 addr2 = mmap_fd(buf, NUM_PAGES * NUM_ENTRIES * getpagesize()); 226 write_to_memfd(addr1, size, 'b'); 227 ret = compare_chunks(addr1, addr2, size); 228 if (ret < 0) 229 ksft_test_result_fail("%s: [FAIL,test-5]\n", TEST_PREFIX); 230 else 231 ksft_test_result_pass("%s: [PASS,test-5]\n", TEST_PREFIX); 232 233 close(buf); 234 close(memfd); 235 236 /* should work (migration of 2MB size huge pages)*/ 237 page_size = getpagesize() * 512; /* 2 MB */ 238 size = MEMFD_SIZE * page_size; 239 memfd = create_memfd_with_seals(size, true); 240 addr1 = mmap_fd(memfd, size); 241 write_to_memfd(addr1, size, 'a'); 242 buf = create_udmabuf_list(devfd, memfd, size); 243 addr2 = mmap_fd(buf, NUM_PAGES * NUM_ENTRIES * getpagesize()); 244 write_to_memfd(addr1, size, 'b'); 245 ret = compare_chunks(addr1, addr2, size); 246 if (ret < 0) 247 ksft_test_result_fail("%s: [FAIL,test-6]\n", TEST_PREFIX); 248 else 249 ksft_test_result_pass("%s: [PASS,test-6]\n", TEST_PREFIX); 250 251 close(buf); 252 close(memfd); 253 close(devfd); 254 255 ksft_print_msg("%s: ok\n", TEST_PREFIX); 256 ksft_print_cnts(); 257 258 return 0; 259 } 260