The new tests added in this patch try to mimic the situation that
happens when a hole is punched in the memfd associated with Guest
RAM that is managed by a VMM such as Qemu.

The main goal of these tests is to ensure that the udmabuf driver
updates its list of pages when newer Guest writes overlap with
the region of the mapping where a hole had been punched previously.

Based-on-patch-by: Mike Kravetz <[email protected]>
Cc: Shuah Khan <[email protected]>
Cc: David Hildenbrand <[email protected]>
Cc: Mike Kravetz <[email protected]>
Cc: Hugh Dickins <[email protected]>
Cc: Peter Xu <[email protected]>
Cc: Jason Gunthorpe <[email protected]>
Cc: Gerd Hoffmann <[email protected]>
Cc: Dongwon Kim <[email protected]>
Cc: Junxiao Chang <[email protected]>
Signed-off-by: Vivek Kasireddy <[email protected]>
---
 .../selftests/drivers/dma-buf/udmabuf.c       | 165 +++++++++++++++++-
 1 file changed, 161 insertions(+), 4 deletions(-)

diff --git a/tools/testing/selftests/drivers/dma-buf/udmabuf.c 
b/tools/testing/selftests/drivers/dma-buf/udmabuf.c
index c812080e304e..bee966444e9e 100644
--- a/tools/testing/selftests/drivers/dma-buf/udmabuf.c
+++ b/tools/testing/selftests/drivers/dma-buf/udmabuf.c
@@ -9,26 +9,144 @@
 #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>
 
 #define TEST_PREFIX    "drivers/dma-buf/udmabuf"
 #define NUM_PAGES       4
+#define NUM_ENTRIES     4
+#define MEMFD_SIZE      1024 /* in pages */
 
-static int memfd_create(const char *name, unsigned int flags)
+static unsigned int page_size;
+
+static int create_memfd_with_seals(off64_t size, bool hpage)
 {
-       return syscall(__NR_memfd_create, name, flags);
+       int memfd, ret;
+       unsigned int flags = MFD_ALLOW_SEALING;
+
+       if (hpage)
+               flags |= MFD_HUGETLB;
+
+       memfd = memfd_create("udmabuf-test", flags);
+       if (memfd < 0) {
+               printf("%s: [skip,no-memfd]\n", TEST_PREFIX);
+               exit(77);
+       }
+
+       ret = fcntl(memfd, F_ADD_SEALS, F_SEAL_SHRINK);
+       if (ret < 0) {
+               printf("%s: [skip,fcntl-add-seals]\n", TEST_PREFIX);
+               exit(77);
+       }
+
+       ret = ftruncate(memfd, size);
+       if (ret == -1) {
+               printf("%s: [FAIL,memfd-truncate]\n", TEST_PREFIX);
+               exit(1);
+       }
+
+       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) {
+               printf("%s: [FAIL, udmabuf-malloc]\n", TEST_PREFIX);
+               exit(1);
+       }
+
+       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   = getpagesize() * NUM_PAGES;
+       }
+
+       list->count = NUM_ENTRIES;
+       list->flags = UDMABUF_FLAGS_CLOEXEC;
+       ubuf_fd = ioctl(devfd, UDMABUF_CREATE_LIST, list);
+       free(list);
+       if (ubuf_fd < 0) {
+               printf("%s: [FAIL, udmabuf-create]\n", TEST_PREFIX);
+               exit(1);
+       }
+
+       return ubuf_fd;
+}
+
+static void write_to_memfd(void *addr, off64_t size, char chr)
+{
+       int i;
+
+       for (i = 0; i < size / page_size; i++) {
+               *((char *)addr + (i * page_size)) = chr;
+       }
+}
+
+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) {
+               printf("%s: ubuf_fd mmap fail\n", TEST_PREFIX);
+               exit(1);
+       }
+
+       return addr;
+}
+
+static void punch_hole(int memfd, int num_pages)
+{
+       int ret;
+
+       ret = fallocate(memfd, FALLOC_FL_PUNCH_HOLE | FALLOC_FL_KEEP_SIZE,
+                       0, page_size * num_pages);
+       if (ret) {
+               printf("%s: memfd punch hole failed\n", TEST_PREFIX);
+               exit(1);
+       }
+}
+
+static int compare_chunks(void *addr1, void *addr2, off64_t memfd_size)
+{
+       off64_t off;
+       int i = 0, j, k = 0, ret = 0;
+       char char1, char2;
+
+       while (i < NUM_ENTRIES) {
+               off = i * (memfd_size / NUM_ENTRIES);
+               for (j = 0; j < NUM_PAGES; j++, k++) {
+                       char1 = *((char *)addr1 + off + (j * getpagesize()));
+                       char2 = *((char *)addr2 + (k * getpagesize()));
+                       if (char1 != char2) {
+                               ret = -1;
+                               goto err;
+                       }
+               }
+               i++;
+       }
+err:
+       munmap(addr1, memfd_size);
+       munmap(addr2, NUM_ENTRIES * NUM_PAGES * getpagesize());
+       return ret;
 }
 
 int main(int argc, char *argv[])
 {
        struct udmabuf_create create;
        int devfd, memfd, buf, ret;
-       off_t size;
-       void *mem;
+       off64_t size;
+       void *addr1, *addr2;
 
        devfd = open("/dev/udmabuf", O_RDWR);
        if (devfd < 0) {
@@ -90,6 +208,9 @@ int main(int argc, char *argv[])
        }
 
        /* should work */
+       page_size = getpagesize();
+       addr1 = mmap_fd(memfd, size);
+       write_to_memfd(addr1, size, 'a');
        create.memfd  = memfd;
        create.offset = 0;
        create.size   = size;
@@ -98,6 +219,42 @@ int main(int argc, char *argv[])
                printf("%s: [FAIL,test-4]\n", TEST_PREFIX);
                exit(1);
        }
+       munmap(addr1, size);
+       close(buf);
+       close(memfd);
+
+       /* should work (punch hole)*/
+       size = MEMFD_SIZE * page_size;
+       memfd = create_memfd_with_seals(size, false);
+       addr1 = mmap_fd(memfd, size);
+       write_to_memfd(addr1, size, 'a');
+       buf = create_udmabuf_list(devfd, memfd, size);
+       addr2 = mmap_fd(buf, NUM_PAGES * NUM_ENTRIES * getpagesize());
+       punch_hole(memfd, MEMFD_SIZE / 3);
+       write_to_memfd(addr1, size, 'b');
+       ret = compare_chunks(addr1, addr2, size);
+       if (ret < 0) {
+               printf("%s: [FAIL,test-5]\n", TEST_PREFIX);
+               exit(1);
+       }
+       close(buf);
+       close(memfd);
+
+       /* should work (huge pages + punch hole)*/
+       page_size = getpagesize() * 512; /* 2 MB */
+       size = MEMFD_SIZE * page_size;
+       memfd = create_memfd_with_seals(size, true);
+       addr1 = mmap_fd(memfd, size);
+       write_to_memfd(addr1, size, 'a');
+       buf = create_udmabuf_list(devfd, memfd, size);
+       addr2 = mmap_fd(buf, NUM_PAGES * NUM_ENTRIES * getpagesize());
+       punch_hole(memfd, MEMFD_SIZE / 3);
+       write_to_memfd(addr1, size, 'b');
+       ret = compare_chunks(addr1, addr2, size);
+       if (ret < 0) {
+               printf("%s: [FAIL,test-6]\n", TEST_PREFIX);
+               exit(1);
+       }
 
        fprintf(stderr, "%s: ok\n", TEST_PREFIX);
        close(buf);
-- 
2.39.2

Reply via email to