This patch add a test ioctl in udmabuf to show vmap can work. by compare pfn vmap and pages vmap.
But this skip HVO folio compare due to can't use pages vmap.
Signed-off-by: Huan Yang link@vivo.com --- drivers/dma-buf/udmabuf.c | 71 ++++++++ include/uapi/linux/udmabuf.h | 5 + .../selftests/drivers/dma-buf/Makefile | 1 + .../selftests/drivers/dma-buf/udmabuf_vmap.c | 166 ++++++++++++++++++ 4 files changed, 243 insertions(+) create mode 100644 tools/testing/selftests/drivers/dma-buf/udmabuf_vmap.c
diff --git a/drivers/dma-buf/udmabuf.c b/drivers/dma-buf/udmabuf.c index 2dfe639230dc..fbe4b59b4c97 100644 --- a/drivers/dma-buf/udmabuf.c +++ b/drivers/dma-buf/udmabuf.c @@ -557,6 +557,74 @@ static long udmabuf_ioctl_create_list(struct file *filp, unsigned long arg) return ret; }
+static long udmabuf_vmap_test(struct file *filp, unsigned long arg) +{ + struct udmabuf_vmap uv; + struct dma_buf *dmabuf; + bool can_page = true; + struct iosys_map map; + struct udmabuf *ubuf; + struct page **pages; + void *vaddr, *pvaddr; + struct file *file; + int ret = 0, i; + + if (copy_from_user(&uv, (void __user *)arg, sizeof(uv))) + return -EFAULT; + file = fget(uv.dma_buf_fd); + if (!file) + return -EINVAL; + + dmabuf = file->private_data; + ret = dma_buf_vmap(dmabuf, &map); + if (ret) + goto out; + vaddr = map.vaddr; + + ubuf = dmabuf->priv; + for (i = 0; i < ubuf->pagecount; ++i) { + struct folio *folio = ubuf->folios[i]; + + if (folio_test_hugetlb_vmemmap_optimized(folio)) { + can_page = false; + break; + } + } + + if (!can_page) + goto out_vaddr; + + pages = kvmalloc_array(ubuf->pagecount, sizeof(*pages), GFP_KERNEL); + if (WARN_ON(!pages)) { + ret = -ENOMEM; + goto out_vaddr; + } + + for (i = 0; i < ubuf->pagecount; ++i) + pages[i] = folio_page(ubuf->folios[i], + ubuf->offsets[i] >> PAGE_SHIFT); + + pvaddr = vmap(pages, ubuf->pagecount, 0, PAGE_KERNEL); + if (WARN_ON(!pvaddr)) { + ret = -ENOMEM; + goto out_pages; + } + + // compare if pages and pfns is same? + if (WARN_ON(memcmp(vaddr, pvaddr, ubuf->pagecount * PAGE_SIZE) != 0)) + ret = -EINVAL; + + vunmap(pvaddr); +out_pages: + kvfree(pages); +out_vaddr: + dma_buf_vunmap(dmabuf, &map); +out: + fput(file); + + return ret; +} + static long udmabuf_ioctl(struct file *filp, unsigned int ioctl, unsigned long arg) { @@ -569,6 +637,9 @@ static long udmabuf_ioctl(struct file *filp, unsigned int ioctl, case UDMABUF_CREATE_LIST: ret = udmabuf_ioctl_create_list(filp, arg); break; + case UDMABUF_VMAP: + ret = udmabuf_vmap_test(filp, arg); + break; default: ret = -ENOTTY; break; diff --git a/include/uapi/linux/udmabuf.h b/include/uapi/linux/udmabuf.h index 46b6532ed855..88f5e5516286 100644 --- a/include/uapi/linux/udmabuf.h +++ b/include/uapi/linux/udmabuf.h @@ -27,7 +27,12 @@ struct udmabuf_create_list { struct udmabuf_create_item list[]; };
+struct udmabuf_vmap { + int dma_buf_fd; +}; + #define UDMABUF_CREATE _IOW('u', 0x42, struct udmabuf_create) #define UDMABUF_CREATE_LIST _IOW('u', 0x43, struct udmabuf_create_list) +#define UDMABUF_VMAP _IOW('u', 0x44, struct udmabuf_vmap)
#endif /* _UAPI_LINUX_UDMABUF_H */ diff --git a/tools/testing/selftests/drivers/dma-buf/Makefile b/tools/testing/selftests/drivers/dma-buf/Makefile index 441407bb0e80..e5b131dcc2c3 100644 --- a/tools/testing/selftests/drivers/dma-buf/Makefile +++ b/tools/testing/selftests/drivers/dma-buf/Makefile @@ -2,6 +2,7 @@ CFLAGS += $(KHDR_INCLUDES)
TEST_GEN_PROGS := udmabuf +TEST_GEN_PROGS := udmabuf_vmap
top_srcdir ?=../../../../..
diff --git a/tools/testing/selftests/drivers/dma-buf/udmabuf_vmap.c b/tools/testing/selftests/drivers/dma-buf/udmabuf_vmap.c new file mode 100644 index 000000000000..7bd46c909bdf --- /dev/null +++ b/tools/testing/selftests/drivers/dma-buf/udmabuf_vmap.c @@ -0,0 +1,166 @@ +// SPDX-License-Identifier: GPL-2.0 +#define _GNU_SOURCE +#define __EXPORTED_HEADERS__ + +#include <stdio.h> +#include <stdlib.h> +#include <unistd.h> +#include <string.h> +#include <errno.h> +#include <fcntl.h> +#include <malloc.h> +#include <stdbool.h> + +#include <sys/ioctl.h> +#include <sys/syscall.h> +#include <sys/mman.h> +#include <linux/memfd.h> +#include <linux/udmabuf.h> +#include "../../kselftest.h" + +#define TEST_PREFIX "drivers/dma-buf/udmabuf" +#define NUM_PAGES 4 +#define NUM_ENTRIES 4 +#define MEMFD_SIZE 1024 /* in pages */ + +static unsigned int page_size; + +static int create_memfd_with_seals(off64_t size, bool hpage) +{ + int memfd, ret; + unsigned int flags = MFD_ALLOW_SEALING; + + if (hpage) + flags |= MFD_HUGETLB; + + memfd = memfd_create("udmabuf-test", flags); + if (memfd < 0) { + ksft_print_msg("%s: [skip,no-memfd]\n", TEST_PREFIX); + exit(KSFT_SKIP); + } + + ret = fcntl(memfd, F_ADD_SEALS, F_SEAL_SHRINK); + if (ret < 0) { + ksft_print_msg("%s: [skip,fcntl-add-seals]\n", TEST_PREFIX); + exit(KSFT_SKIP); + } + + ret = ftruncate(memfd, size); + if (ret == -1) { + ksft_print_msg("%s: [FAIL,memfd-truncate]\n", TEST_PREFIX); + exit(KSFT_FAIL); + } + + return memfd; +} + +static int create_udmabuf_list(int devfd, int memfd, off64_t memfd_size) +{ + struct udmabuf_create_list *list; + int ubuf_fd, i; + + list = malloc(sizeof(struct udmabuf_create_list) + + sizeof(struct udmabuf_create_item) * NUM_ENTRIES); + if (!list) { + ksft_print_msg("%s: [FAIL, udmabuf-malloc]\n", TEST_PREFIX); + exit(KSFT_FAIL); + } + + for (i = 0; i < NUM_ENTRIES; i++) { + list->list[i].memfd = memfd; + list->list[i].offset = i * (memfd_size / NUM_ENTRIES); + list->list[i].size = memfd_size / NUM_ENTRIES; + } + + list->count = NUM_ENTRIES; + list->flags = UDMABUF_FLAGS_CLOEXEC; + ubuf_fd = ioctl(devfd, UDMABUF_CREATE_LIST, list); + free(list); + if (ubuf_fd < 0) { + ksft_print_msg("%s: [FAIL, udmabuf-create]\n", TEST_PREFIX); + exit(KSFT_FAIL); + } + + return ubuf_fd; +} + +static void *mmap_fd(int fd, off64_t size) +{ + void *addr; + + addr = mmap(NULL, size, PROT_READ | PROT_WRITE, MAP_SHARED, fd, 0); + if (addr == MAP_FAILED) { + ksft_print_msg("%s: ubuf_fd mmap fail\n", TEST_PREFIX); + exit(KSFT_FAIL); + } + + return addr; +} + +int main(int argc, char *argv[]) +{ + struct udmabuf_create create; + int devfd, memfd, buf, ret; + struct udmabuf_vmap vm; + unsigned long *vaddr; + off64_t size; + int i; + + ksft_print_header(); + ksft_set_plan(2); + + devfd = open("/dev/udmabuf", O_RDWR); + if (devfd < 0) { + ksft_print_msg( + "%s: [skip,no-udmabuf: Unable to access DMA buffer device file]\n", + TEST_PREFIX); + exit(KSFT_SKIP); + } + + /** + * Normal test + */ + size = getpagesize() * 512 + getpagesize() * 256; + memfd = create_memfd_with_seals(size, false); + buf = create_udmabuf_list(devfd, memfd, size); + vaddr = (unsigned long *)mmap_fd(buf, size); + for (i = 0; i < size / sizeof(unsigned long); i++) + vaddr[i] = random(); + + vm.dma_buf_fd = buf; + + ret = ioctl(devfd, UDMABUF_VMAP, &vm); + if (ret < 0) + ksft_test_result_fail("%s: [FAIL, normal test]\n", TEST_PREFIX); + else + ksft_test_result_pass("%s: [PASS, normal test]\n", TEST_PREFIX); + munmap(vaddr, size); + close(buf); + close(memfd); + + /** + * Hugetlb test, 2MB + */ + size = getpagesize() * 512; + memfd = create_memfd_with_seals(size, true); + buf = create_udmabuf_list(devfd, memfd, size); + vaddr = (unsigned long *)mmap_fd(buf, size); + for (i = 0; i < size / sizeof(unsigned long); i++) + vaddr[i] = random(); + + vm.dma_buf_fd = buf; + + ret = ioctl(devfd, UDMABUF_VMAP, &vm); + if (ret < 0) + ksft_test_result_fail("%s: [FAIL, huge test]\n", TEST_PREFIX); + else + ksft_test_result_pass("%s: [PASS, huge test]\n", TEST_PREFIX); + munmap(vaddr, size); + close(buf); + close(memfd); + + ksft_print_msg("%s: ok\n", TEST_PREFIX); + ksft_print_cnts(); + + return 0; +}