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
create_memfd_with_seals(off64_t size,bool hpage)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
create_udmabuf_list(int devfd,int memfd,off64_t memfd_size)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
write_to_memfd(void * addr,off64_t size,char chr)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
mmap_fd(int fd,off64_t size)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
compare_chunks(void * addr1,void * addr2,off64_t memfd_size)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
main(int argc,char * argv[])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