diff mbox series

[net-next,v2,02/12] selftests: ncdevmem: Separate out dmabuf provider

Message ID 20240930171753.2572922-3-sdf@fomichev.me (mailing list archive)
State Superseded
Delegated to: Netdev Maintainers
Headers show
Series selftests: ncdevmem: Add ncdevmem to ksft | expand

Checks

Context Check Description
netdev/series_format success Posting correctly formatted
netdev/tree_selection success Clearly marked for net-next
netdev/ynl success Generated files up to date; no warnings/errors; no diff in generated;
netdev/fixes_present success Fixes tag not required for -next series
netdev/header_inline success No static functions without inline keyword in header files
netdev/build_32bit success Errors and warnings before: 9 this patch: 9
netdev/build_tools success Errors and warnings before: 0 this patch: 0
netdev/cc_maintainers warning 2 maintainers not CCed: shuah@kernel.org linux-kselftest@vger.kernel.org
netdev/build_clang success Errors and warnings before: 9 this patch: 9
netdev/verify_signedoff success Signed-off-by tag matches author and committer
netdev/deprecated_api success None detected
netdev/check_selftest success No net selftest shell script
netdev/verify_fixes success No Fixes tag
netdev/build_allmodconfig_warn fail Errors and warnings before: 12 this patch: 12
netdev/checkpatch warning WARNING: line length of 85 exceeds 80 columns
netdev/build_clang_rust success No Rust files in patch. Skipping build
netdev/kdoc success Errors and warnings before: 0 this patch: 0
netdev/source_inline success Was 0 now: 0

Commit Message

Stanislav Fomichev Sept. 30, 2024, 5:17 p.m. UTC
So we can plug the other ones in the future if needed.

Cc: Mina Almasry <almasrymina@google.com>
Signed-off-by: Stanislav Fomichev <sdf@fomichev.me>
---
 tools/testing/selftests/net/ncdevmem.c | 215 +++++++++++++++----------
 1 file changed, 134 insertions(+), 81 deletions(-)

Comments

Mina Almasry Oct. 3, 2024, 5:57 p.m. UTC | #1
On Mon, Sep 30, 2024 at 10:18 AM Stanislav Fomichev <sdf@fomichev.me> wrote:
>
> So we can plug the other ones in the future if needed.
>
> Cc: Mina Almasry <almasrymina@google.com>
> Signed-off-by: Stanislav Fomichev <sdf@fomichev.me>

Feedback is minor, so with or without it:

Reviewed-by: Mina Almasry <almasrymina@google.com>

> ---
>  tools/testing/selftests/net/ncdevmem.c | 215 +++++++++++++++----------
>  1 file changed, 134 insertions(+), 81 deletions(-)
>
> diff --git a/tools/testing/selftests/net/ncdevmem.c b/tools/testing/selftests/net/ncdevmem.c
> index 9245d3f158dd..557175c3bf02 100644
> --- a/tools/testing/selftests/net/ncdevmem.c
> +++ b/tools/testing/selftests/net/ncdevmem.c
> @@ -71,17 +71,118 @@ static char *ifname = "eth1";
>  static unsigned int ifindex;
>  static unsigned int dmabuf_id;
>
> -void print_bytes(void *ptr, size_t size)
> +struct memory_buffer {
> +       int fd;
> +       size_t size;
> +
> +       int devfd;
> +       int memfd;
> +       char *buf_mem;
> +};
> +
> +struct memory_provider {
> +       struct memory_buffer *(*alloc)(size_t size);
> +       void (*free)(struct memory_buffer *ctx);
> +       void (*memcpy_to_device)(struct memory_buffer *dst, size_t off,
> +                                void *src, int n);

AFAICT all this code in this api and its implementation is unused. I'm
guessing this is for the future TX path testing?

If you get the chance to slice these changes out of this patch and
punting them for when the TX changes are ready, please do. Just to
minimize dead code until TX path is added, but since these are tests,
dead code is not a huge deal.

> +       void (*memcpy_from_device)(void *dst, struct memory_buffer *src,
> +                                  size_t off, int n);
> +};
> +
> +static struct memory_buffer *udmabuf_alloc(size_t size)
>  {
> -       unsigned char *p = ptr;
> -       int i;
> +       struct udmabuf_create create;
> +       struct memory_buffer *ctx;
> +       int ret;
>
> -       for (i = 0; i < size; i++)
> -               printf("%02hhX ", p[i]);
> -       printf("\n");
> +       ctx = malloc(sizeof(*ctx));
> +       if (!ctx)
> +               error(1, ENOMEM, "malloc failed");
> +
> +       ctx->size = size;
> +
> +       ctx->devfd = open("/dev/udmabuf", O_RDWR);
> +       if (ctx->devfd < 0)
> +               error(1, errno,
> +                     "%s: [skip,no-udmabuf: Unable to access DMA buffer device file]\n",
> +                     TEST_PREFIX);
> +
> +       ctx->memfd = memfd_create("udmabuf-test", MFD_ALLOW_SEALING);
> +       if (ctx->memfd < 0)
> +               error(1, errno, "%s: [skip,no-memfd]\n", TEST_PREFIX);
> +
> +       ret = fcntl(ctx->memfd, F_ADD_SEALS, F_SEAL_SHRINK);
> +       if (ret < 0)
> +               error(1, errno, "%s: [skip,fcntl-add-seals]\n", TEST_PREFIX);
> +
> +       ret = ftruncate(ctx->memfd, size);
> +       if (ret == -1)
> +               error(1, errno, "%s: [FAIL,memfd-truncate]\n", TEST_PREFIX);
> +
> +       memset(&create, 0, sizeof(create));
> +
> +       create.memfd = ctx->memfd;
> +       create.offset = 0;
> +       create.size = size;
> +       ctx->fd = ioctl(ctx->devfd, UDMABUF_CREATE, &create);
> +       if (ctx->fd < 0)
> +               error(1, errno, "%s: [FAIL, create udmabuf]\n", TEST_PREFIX);
> +
> +       ctx->buf_mem = mmap(NULL, size, PROT_READ | PROT_WRITE, MAP_SHARED,
> +                           ctx->fd, 0);
> +       if (ctx->buf_mem == MAP_FAILED)
> +               error(1, errno, "%s: [FAIL, map udmabuf]\n", TEST_PREFIX);
> +
> +       return ctx;
> +}
> +
> +static void udmabuf_free(struct memory_buffer *ctx)
> +{
> +       munmap(ctx->buf_mem, ctx->size);
> +       close(ctx->fd);
> +       close(ctx->memfd);
> +       close(ctx->devfd);
> +       free(ctx);
> +}
> +
> +static void udmabuf_memcpy_to_device(struct memory_buffer *dst, size_t off,
> +                                    void *src, int n)
> +{
> +       struct dma_buf_sync sync = {};
> +
> +       sync.flags = DMA_BUF_SYNC_START | DMA_BUF_SYNC_WRITE;
> +       ioctl(dst->fd, DMA_BUF_IOCTL_SYNC, &sync);
> +
> +       memcpy(dst->buf_mem + off, src, n);
> +
> +       sync.flags = DMA_BUF_SYNC_END | DMA_BUF_SYNC_WRITE;
> +       ioctl(dst->fd, DMA_BUF_IOCTL_SYNC, &sync);
> +}
> +
> +static void udmabuf_memcpy_from_device(void *dst, struct memory_buffer *src,
> +                                      size_t off, int n)
> +{
> +       struct dma_buf_sync sync = {};
> +
> +       sync.flags = DMA_BUF_SYNC_START;
> +       ioctl(src->fd, DMA_BUF_IOCTL_SYNC, &sync);
> +
> +       memcpy(dst, src->buf_mem + off, n);
> +
> +       sync.flags = DMA_BUF_SYNC_END;
> +       ioctl(src->fd, DMA_BUF_IOCTL_SYNC, &sync);
>  }
>
> -void print_nonzero_bytes(void *ptr, size_t size)
> +static struct memory_provider udmabuf_memory_provider = {
> +       .alloc = udmabuf_alloc,
> +       .free = udmabuf_free,
> +       .memcpy_to_device = udmabuf_memcpy_to_device,
> +       .memcpy_from_device = udmabuf_memcpy_from_device,
> +};
> +
> +static struct memory_provider *provider = &udmabuf_memory_provider;
> +
> +static void print_nonzero_bytes(void *ptr, size_t size)
>  {
>         unsigned char *p = ptr;
>         unsigned int i;
> @@ -201,42 +302,7 @@ static int bind_rx_queue(unsigned int ifindex, unsigned int dmabuf_fd,
>         return -1;
>  }
>
> -static void create_udmabuf(int *devfd, int *memfd, int *buf, size_t dmabuf_size)
> -{
> -       struct udmabuf_create create;
> -       int ret;
> -
> -       *devfd = open("/dev/udmabuf", O_RDWR);
> -       if (*devfd < 0) {
> -               error(70, 0,
> -                     "%s: [skip,no-udmabuf: Unable to access DMA buffer device file]\n",
> -                     TEST_PREFIX);
> -       }
> -
> -       *memfd = memfd_create("udmabuf-test", MFD_ALLOW_SEALING);
> -       if (*memfd < 0)
> -               error(70, 0, "%s: [skip,no-memfd]\n", TEST_PREFIX);
> -
> -       /* Required for udmabuf */
> -       ret = fcntl(*memfd, F_ADD_SEALS, F_SEAL_SHRINK);
> -       if (ret < 0)
> -               error(73, 0, "%s: [skip,fcntl-add-seals]\n", TEST_PREFIX);
> -
> -       ret = ftruncate(*memfd, dmabuf_size);
> -       if (ret == -1)
> -               error(74, 0, "%s: [FAIL,memfd-truncate]\n", TEST_PREFIX);
> -
> -       memset(&create, 0, sizeof(create));
> -
> -       create.memfd = *memfd;
> -       create.offset = 0;
> -       create.size = dmabuf_size;
> -       *buf = ioctl(*devfd, UDMABUF_CREATE, &create);
> -       if (*buf < 0)
> -               error(75, 0, "%s: [FAIL, create udmabuf]\n", TEST_PREFIX);
> -}
> -
> -int do_server(void)
> +int do_server(struct memory_buffer *mem)
>  {
>         char ctrl_data[sizeof(int) * 20000];
>         struct netdev_queue_id *queues;
> @@ -244,23 +310,18 @@ int do_server(void)
>         struct sockaddr_in client_addr;
>         struct sockaddr_in server_sin;
>         size_t page_aligned_frags = 0;
> -       int devfd, memfd, buf, ret;
>         size_t total_received = 0;
>         socklen_t client_addr_len;
>         bool is_devmem = false;
> -       char *buf_mem = NULL;
> +       char *tmp_mem = NULL;
>         struct ynl_sock *ys;
> -       size_t dmabuf_size;
>         char iobuf[819200];
>         char buffer[256];
>         int socket_fd;
>         int client_fd;
>         size_t i = 0;
>         int opt = 1;
> -
> -       dmabuf_size = getpagesize() * NUM_PAGES;
> -
> -       create_udmabuf(&devfd, &memfd, &buf, dmabuf_size);
> +       int ret;
>
>         if (reset_flow_steering())
>                 error(1, 0, "Failed to reset flow steering\n");
> @@ -284,13 +345,12 @@ int do_server(void)
>                 queues[i].id = start_queue + i;
>         }
>
> -       if (bind_rx_queue(ifindex, buf, queues, num_queues, &ys))
> +       if (bind_rx_queue(ifindex, mem->fd, queues, num_queues, &ys))
>                 error(1, 0, "Failed to bind\n");
>
> -       buf_mem = mmap(NULL, dmabuf_size, PROT_READ | PROT_WRITE, MAP_SHARED,
> -                      buf, 0);
> -       if (buf_mem == MAP_FAILED)
> -               error(1, 0, "mmap()");
> +       tmp_mem = malloc(mem->size);
> +       if (!tmp_mem)
> +               error(1, ENOMEM, "malloc failed");
>
>         server_sin.sin_family = AF_INET;
>         server_sin.sin_port = htons(atoi(port));
> @@ -341,7 +401,6 @@ int do_server(void)
>                 struct iovec iov = { .iov_base = iobuf,
>                                      .iov_len = sizeof(iobuf) };
>                 struct dmabuf_cmsg *dmabuf_cmsg = NULL;
> -               struct dma_buf_sync sync = { 0 };
>                 struct cmsghdr *cm = NULL;
>                 struct msghdr msg = { 0 };
>                 struct dmabuf_token token;
> @@ -410,22 +469,17 @@ int do_server(void)
>                         else
>                                 page_aligned_frags++;
>
> -                       sync.flags = DMA_BUF_SYNC_READ | DMA_BUF_SYNC_START;
> -                       ioctl(buf, DMA_BUF_IOCTL_SYNC, &sync);
> +                       provider->memcpy_from_device(tmp_mem, mem,
> +                                                    dmabuf_cmsg->frag_offset,
> +                                                    dmabuf_cmsg->frag_size);
>
>                         if (do_validation)
>                                 validate_buffer(
> -                                       ((unsigned char *)buf_mem) +
> +                                       ((unsigned char *)tmp_mem) +
>                                                 dmabuf_cmsg->frag_offset,
>                                         dmabuf_cmsg->frag_size);
>                         else
> -                               print_nonzero_bytes(
> -                                       ((unsigned char *)buf_mem) +
> -                                               dmabuf_cmsg->frag_offset,
> -                                       dmabuf_cmsg->frag_size);
> -
> -                       sync.flags = DMA_BUF_SYNC_READ | DMA_BUF_SYNC_END;
> -                       ioctl(buf, DMA_BUF_IOCTL_SYNC, &sync);
> +                               print_nonzero_bytes(tmp_mem, dmabuf_cmsg->frag_size);
>
>                         ret = setsockopt(client_fd, SOL_SOCKET,
>                                          SO_DEVMEM_DONTNEED, &token,
> @@ -450,12 +504,9 @@ int do_server(void)
>
>  cleanup:
>
> -       munmap(buf_mem, dmabuf_size);
> +       free(tmp_mem);
>         close(client_fd);
>         close(socket_fd);
> -       close(buf);
> -       close(memfd);
> -       close(devfd);
>         ynl_sock_destroy(ys);
>
>         return 0;
> @@ -464,14 +515,11 @@ int do_server(void)
>  void run_devmem_tests(void)
>  {
>         struct netdev_queue_id *queues;
> -       int devfd, memfd, buf;
> +       struct memory_buffer *mem;
>         struct ynl_sock *ys;
> -       size_t dmabuf_size;
>         size_t i = 0;
>
> -       dmabuf_size = getpagesize() * NUM_PAGES;
> -
> -       create_udmabuf(&devfd, &memfd, &buf, dmabuf_size);
> +       mem = provider->alloc(getpagesize() * NUM_PAGES);
>

There is some weirdness here where run_devmem_tests() allocates its
own provider, but do_server() uses a provider allocated in main(). Any
reason these are not symmetric? I would marginally prefer do_server()
to allocate its own provider just like run_devmem_tests(), or at least
make them both match, if possible.
Stanislav Fomichev Oct. 3, 2024, 10:08 p.m. UTC | #2
On 10/03, Mina Almasry wrote:
> On Mon, Sep 30, 2024 at 10:18 AM Stanislav Fomichev <sdf@fomichev.me> wrote:
> >
> > So we can plug the other ones in the future if needed.
> >
> > Cc: Mina Almasry <almasrymina@google.com>
> > Signed-off-by: Stanislav Fomichev <sdf@fomichev.me>
> 
> Feedback is minor, so with or without it:
> 
> Reviewed-by: Mina Almasry <almasrymina@google.com>
> 
> > ---
> >  tools/testing/selftests/net/ncdevmem.c | 215 +++++++++++++++----------
> >  1 file changed, 134 insertions(+), 81 deletions(-)
> >
> > diff --git a/tools/testing/selftests/net/ncdevmem.c b/tools/testing/selftests/net/ncdevmem.c
> > index 9245d3f158dd..557175c3bf02 100644
> > --- a/tools/testing/selftests/net/ncdevmem.c
> > +++ b/tools/testing/selftests/net/ncdevmem.c
> > @@ -71,17 +71,118 @@ static char *ifname = "eth1";
> >  static unsigned int ifindex;
> >  static unsigned int dmabuf_id;
> >
> > -void print_bytes(void *ptr, size_t size)
> > +struct memory_buffer {
> > +       int fd;
> > +       size_t size;
> > +
> > +       int devfd;
> > +       int memfd;
> > +       char *buf_mem;
> > +};
> > +
> > +struct memory_provider {
> > +       struct memory_buffer *(*alloc)(size_t size);
> > +       void (*free)(struct memory_buffer *ctx);
> > +       void (*memcpy_to_device)(struct memory_buffer *dst, size_t off,
> > +                                void *src, int n);
> 
> AFAICT all this code in this api and its implementation is unused. I'm
> guessing this is for the future TX path testing?
> 
> If you get the chance to slice these changes out of this patch and
> punting them for when the TX changes are ready, please do. Just to
> minimize dead code until TX path is added, but since these are tests,
> dead code is not a huge deal.

Ah, yes, I'll remove it for now.

> > +       void (*memcpy_from_device)(void *dst, struct memory_buffer *src,
> > +                                  size_t off, int n);
> > +};
> > +
> > +static struct memory_buffer *udmabuf_alloc(size_t size)
> >  {
> > -       unsigned char *p = ptr;
> > -       int i;
> > +       struct udmabuf_create create;
> > +       struct memory_buffer *ctx;
> > +       int ret;
> >
> > -       for (i = 0; i < size; i++)
> > -               printf("%02hhX ", p[i]);
> > -       printf("\n");
> > +       ctx = malloc(sizeof(*ctx));
> > +       if (!ctx)
> > +               error(1, ENOMEM, "malloc failed");
> > +
> > +       ctx->size = size;
> > +
> > +       ctx->devfd = open("/dev/udmabuf", O_RDWR);
> > +       if (ctx->devfd < 0)
> > +               error(1, errno,
> > +                     "%s: [skip,no-udmabuf: Unable to access DMA buffer device file]\n",
> > +                     TEST_PREFIX);
> > +
> > +       ctx->memfd = memfd_create("udmabuf-test", MFD_ALLOW_SEALING);
> > +       if (ctx->memfd < 0)
> > +               error(1, errno, "%s: [skip,no-memfd]\n", TEST_PREFIX);
> > +
> > +       ret = fcntl(ctx->memfd, F_ADD_SEALS, F_SEAL_SHRINK);
> > +       if (ret < 0)
> > +               error(1, errno, "%s: [skip,fcntl-add-seals]\n", TEST_PREFIX);
> > +
> > +       ret = ftruncate(ctx->memfd, size);
> > +       if (ret == -1)
> > +               error(1, errno, "%s: [FAIL,memfd-truncate]\n", TEST_PREFIX);
> > +
> > +       memset(&create, 0, sizeof(create));
> > +
> > +       create.memfd = ctx->memfd;
> > +       create.offset = 0;
> > +       create.size = size;
> > +       ctx->fd = ioctl(ctx->devfd, UDMABUF_CREATE, &create);
> > +       if (ctx->fd < 0)
> > +               error(1, errno, "%s: [FAIL, create udmabuf]\n", TEST_PREFIX);
> > +
> > +       ctx->buf_mem = mmap(NULL, size, PROT_READ | PROT_WRITE, MAP_SHARED,
> > +                           ctx->fd, 0);
> > +       if (ctx->buf_mem == MAP_FAILED)
> > +               error(1, errno, "%s: [FAIL, map udmabuf]\n", TEST_PREFIX);
> > +
> > +       return ctx;
> > +}
> > +
> > +static void udmabuf_free(struct memory_buffer *ctx)
> > +{
> > +       munmap(ctx->buf_mem, ctx->size);
> > +       close(ctx->fd);
> > +       close(ctx->memfd);
> > +       close(ctx->devfd);
> > +       free(ctx);
> > +}
> > +
> > +static void udmabuf_memcpy_to_device(struct memory_buffer *dst, size_t off,
> > +                                    void *src, int n)
> > +{
> > +       struct dma_buf_sync sync = {};
> > +
> > +       sync.flags = DMA_BUF_SYNC_START | DMA_BUF_SYNC_WRITE;
> > +       ioctl(dst->fd, DMA_BUF_IOCTL_SYNC, &sync);
> > +
> > +       memcpy(dst->buf_mem + off, src, n);
> > +
> > +       sync.flags = DMA_BUF_SYNC_END | DMA_BUF_SYNC_WRITE;
> > +       ioctl(dst->fd, DMA_BUF_IOCTL_SYNC, &sync);
> > +}
> > +
> > +static void udmabuf_memcpy_from_device(void *dst, struct memory_buffer *src,
> > +                                      size_t off, int n)
> > +{
> > +       struct dma_buf_sync sync = {};
> > +
> > +       sync.flags = DMA_BUF_SYNC_START;
> > +       ioctl(src->fd, DMA_BUF_IOCTL_SYNC, &sync);
> > +
> > +       memcpy(dst, src->buf_mem + off, n);
> > +
> > +       sync.flags = DMA_BUF_SYNC_END;
> > +       ioctl(src->fd, DMA_BUF_IOCTL_SYNC, &sync);
> >  }
> >
> > -void print_nonzero_bytes(void *ptr, size_t size)
> > +static struct memory_provider udmabuf_memory_provider = {
> > +       .alloc = udmabuf_alloc,
> > +       .free = udmabuf_free,
> > +       .memcpy_to_device = udmabuf_memcpy_to_device,
> > +       .memcpy_from_device = udmabuf_memcpy_from_device,
> > +};
> > +
> > +static struct memory_provider *provider = &udmabuf_memory_provider;
> > +
> > +static void print_nonzero_bytes(void *ptr, size_t size)
> >  {
> >         unsigned char *p = ptr;
> >         unsigned int i;
> > @@ -201,42 +302,7 @@ static int bind_rx_queue(unsigned int ifindex, unsigned int dmabuf_fd,
> >         return -1;
> >  }
> >
> > -static void create_udmabuf(int *devfd, int *memfd, int *buf, size_t dmabuf_size)
> > -{
> > -       struct udmabuf_create create;
> > -       int ret;
> > -
> > -       *devfd = open("/dev/udmabuf", O_RDWR);
> > -       if (*devfd < 0) {
> > -               error(70, 0,
> > -                     "%s: [skip,no-udmabuf: Unable to access DMA buffer device file]\n",
> > -                     TEST_PREFIX);
> > -       }
> > -
> > -       *memfd = memfd_create("udmabuf-test", MFD_ALLOW_SEALING);
> > -       if (*memfd < 0)
> > -               error(70, 0, "%s: [skip,no-memfd]\n", TEST_PREFIX);
> > -
> > -       /* Required for udmabuf */
> > -       ret = fcntl(*memfd, F_ADD_SEALS, F_SEAL_SHRINK);
> > -       if (ret < 0)
> > -               error(73, 0, "%s: [skip,fcntl-add-seals]\n", TEST_PREFIX);
> > -
> > -       ret = ftruncate(*memfd, dmabuf_size);
> > -       if (ret == -1)
> > -               error(74, 0, "%s: [FAIL,memfd-truncate]\n", TEST_PREFIX);
> > -
> > -       memset(&create, 0, sizeof(create));
> > -
> > -       create.memfd = *memfd;
> > -       create.offset = 0;
> > -       create.size = dmabuf_size;
> > -       *buf = ioctl(*devfd, UDMABUF_CREATE, &create);
> > -       if (*buf < 0)
> > -               error(75, 0, "%s: [FAIL, create udmabuf]\n", TEST_PREFIX);
> > -}
> > -
> > -int do_server(void)
> > +int do_server(struct memory_buffer *mem)
> >  {
> >         char ctrl_data[sizeof(int) * 20000];
> >         struct netdev_queue_id *queues;
> > @@ -244,23 +310,18 @@ int do_server(void)
> >         struct sockaddr_in client_addr;
> >         struct sockaddr_in server_sin;
> >         size_t page_aligned_frags = 0;
> > -       int devfd, memfd, buf, ret;
> >         size_t total_received = 0;
> >         socklen_t client_addr_len;
> >         bool is_devmem = false;
> > -       char *buf_mem = NULL;
> > +       char *tmp_mem = NULL;
> >         struct ynl_sock *ys;
> > -       size_t dmabuf_size;
> >         char iobuf[819200];
> >         char buffer[256];
> >         int socket_fd;
> >         int client_fd;
> >         size_t i = 0;
> >         int opt = 1;
> > -
> > -       dmabuf_size = getpagesize() * NUM_PAGES;
> > -
> > -       create_udmabuf(&devfd, &memfd, &buf, dmabuf_size);
> > +       int ret;
> >
> >         if (reset_flow_steering())
> >                 error(1, 0, "Failed to reset flow steering\n");
> > @@ -284,13 +345,12 @@ int do_server(void)
> >                 queues[i].id = start_queue + i;
> >         }
> >
> > -       if (bind_rx_queue(ifindex, buf, queues, num_queues, &ys))
> > +       if (bind_rx_queue(ifindex, mem->fd, queues, num_queues, &ys))
> >                 error(1, 0, "Failed to bind\n");
> >
> > -       buf_mem = mmap(NULL, dmabuf_size, PROT_READ | PROT_WRITE, MAP_SHARED,
> > -                      buf, 0);
> > -       if (buf_mem == MAP_FAILED)
> > -               error(1, 0, "mmap()");
> > +       tmp_mem = malloc(mem->size);
> > +       if (!tmp_mem)
> > +               error(1, ENOMEM, "malloc failed");
> >
> >         server_sin.sin_family = AF_INET;
> >         server_sin.sin_port = htons(atoi(port));
> > @@ -341,7 +401,6 @@ int do_server(void)
> >                 struct iovec iov = { .iov_base = iobuf,
> >                                      .iov_len = sizeof(iobuf) };
> >                 struct dmabuf_cmsg *dmabuf_cmsg = NULL;
> > -               struct dma_buf_sync sync = { 0 };
> >                 struct cmsghdr *cm = NULL;
> >                 struct msghdr msg = { 0 };
> >                 struct dmabuf_token token;
> > @@ -410,22 +469,17 @@ int do_server(void)
> >                         else
> >                                 page_aligned_frags++;
> >
> > -                       sync.flags = DMA_BUF_SYNC_READ | DMA_BUF_SYNC_START;
> > -                       ioctl(buf, DMA_BUF_IOCTL_SYNC, &sync);
> > +                       provider->memcpy_from_device(tmp_mem, mem,
> > +                                                    dmabuf_cmsg->frag_offset,
> > +                                                    dmabuf_cmsg->frag_size);
> >
> >                         if (do_validation)
> >                                 validate_buffer(
> > -                                       ((unsigned char *)buf_mem) +
> > +                                       ((unsigned char *)tmp_mem) +
> >                                                 dmabuf_cmsg->frag_offset,
> >                                         dmabuf_cmsg->frag_size);
> >                         else
> > -                               print_nonzero_bytes(
> > -                                       ((unsigned char *)buf_mem) +
> > -                                               dmabuf_cmsg->frag_offset,
> > -                                       dmabuf_cmsg->frag_size);
> > -
> > -                       sync.flags = DMA_BUF_SYNC_READ | DMA_BUF_SYNC_END;
> > -                       ioctl(buf, DMA_BUF_IOCTL_SYNC, &sync);
> > +                               print_nonzero_bytes(tmp_mem, dmabuf_cmsg->frag_size);
> >
> >                         ret = setsockopt(client_fd, SOL_SOCKET,
> >                                          SO_DEVMEM_DONTNEED, &token,
> > @@ -450,12 +504,9 @@ int do_server(void)
> >
> >  cleanup:
> >
> > -       munmap(buf_mem, dmabuf_size);
> > +       free(tmp_mem);
> >         close(client_fd);
> >         close(socket_fd);
> > -       close(buf);
> > -       close(memfd);
> > -       close(devfd);
> >         ynl_sock_destroy(ys);
> >
> >         return 0;
> > @@ -464,14 +515,11 @@ int do_server(void)
> >  void run_devmem_tests(void)
> >  {
> >         struct netdev_queue_id *queues;
> > -       int devfd, memfd, buf;
> > +       struct memory_buffer *mem;
> >         struct ynl_sock *ys;
> > -       size_t dmabuf_size;
> >         size_t i = 0;
> >
> > -       dmabuf_size = getpagesize() * NUM_PAGES;
> > -
> > -       create_udmabuf(&devfd, &memfd, &buf, dmabuf_size);
> > +       mem = provider->alloc(getpagesize() * NUM_PAGES);
> >
> 
> There is some weirdness here where run_devmem_tests() allocates its
> own provider, but do_server() uses a provider allocated in main(). Any
> reason these are not symmetric? I would marginally prefer do_server()
> to allocate its own provider just like run_devmem_tests(), or at least
> make them both match, if possible.

I wanted to keep them separate in case we end up adding more to
the selftest part. For example, not sure what would happen now if we pass
a udmabuf with just one page? Do we need some test for the drivers
to make sure they handle this case?
Mina Almasry Oct. 4, 2024, 1:42 a.m. UTC | #3
On Thu, Oct 3, 2024 at 3:08 PM Stanislav Fomichev <stfomichev@gmail.com> wrote:
>
> > > @@ -464,14 +515,11 @@ int do_server(void)
> > >  void run_devmem_tests(void)
> > >  {
> > >         struct netdev_queue_id *queues;
> > > -       int devfd, memfd, buf;
> > > +       struct memory_buffer *mem;
> > >         struct ynl_sock *ys;
> > > -       size_t dmabuf_size;
> > >         size_t i = 0;
> > >
> > > -       dmabuf_size = getpagesize() * NUM_PAGES;
> > > -
> > > -       create_udmabuf(&devfd, &memfd, &buf, dmabuf_size);
> > > +       mem = provider->alloc(getpagesize() * NUM_PAGES);
> > >
> >
> > There is some weirdness here where run_devmem_tests() allocates its
> > own provider, but do_server() uses a provider allocated in main(). Any
> > reason these are not symmetric? I would marginally prefer do_server()
> > to allocate its own provider just like run_devmem_tests(), or at least
> > make them both match, if possible.
>
> I wanted to keep them separate in case we end up adding more to
> the selftest part. For example, not sure what would happen now if we pass
> a udmabuf with just one page? Do we need some test for the drivers
> to make sure they handle this case?

The size of the udmabuf (or more generically, any dmabuf) is the
address space for the page pool; if the dmabuf is too small, like 1
page, the rest of the pp allocations will return -ENOMEM.
diff mbox series

Patch

diff --git a/tools/testing/selftests/net/ncdevmem.c b/tools/testing/selftests/net/ncdevmem.c
index 9245d3f158dd..557175c3bf02 100644
--- a/tools/testing/selftests/net/ncdevmem.c
+++ b/tools/testing/selftests/net/ncdevmem.c
@@ -71,17 +71,118 @@  static char *ifname = "eth1";
 static unsigned int ifindex;
 static unsigned int dmabuf_id;
 
-void print_bytes(void *ptr, size_t size)
+struct memory_buffer {
+	int fd;
+	size_t size;
+
+	int devfd;
+	int memfd;
+	char *buf_mem;
+};
+
+struct memory_provider {
+	struct memory_buffer *(*alloc)(size_t size);
+	void (*free)(struct memory_buffer *ctx);
+	void (*memcpy_to_device)(struct memory_buffer *dst, size_t off,
+				 void *src, int n);
+	void (*memcpy_from_device)(void *dst, struct memory_buffer *src,
+				   size_t off, int n);
+};
+
+static struct memory_buffer *udmabuf_alloc(size_t size)
 {
-	unsigned char *p = ptr;
-	int i;
+	struct udmabuf_create create;
+	struct memory_buffer *ctx;
+	int ret;
 
-	for (i = 0; i < size; i++)
-		printf("%02hhX ", p[i]);
-	printf("\n");
+	ctx = malloc(sizeof(*ctx));
+	if (!ctx)
+		error(1, ENOMEM, "malloc failed");
+
+	ctx->size = size;
+
+	ctx->devfd = open("/dev/udmabuf", O_RDWR);
+	if (ctx->devfd < 0)
+		error(1, errno,
+		      "%s: [skip,no-udmabuf: Unable to access DMA buffer device file]\n",
+		      TEST_PREFIX);
+
+	ctx->memfd = memfd_create("udmabuf-test", MFD_ALLOW_SEALING);
+	if (ctx->memfd < 0)
+		error(1, errno, "%s: [skip,no-memfd]\n", TEST_PREFIX);
+
+	ret = fcntl(ctx->memfd, F_ADD_SEALS, F_SEAL_SHRINK);
+	if (ret < 0)
+		error(1, errno, "%s: [skip,fcntl-add-seals]\n", TEST_PREFIX);
+
+	ret = ftruncate(ctx->memfd, size);
+	if (ret == -1)
+		error(1, errno, "%s: [FAIL,memfd-truncate]\n", TEST_PREFIX);
+
+	memset(&create, 0, sizeof(create));
+
+	create.memfd = ctx->memfd;
+	create.offset = 0;
+	create.size = size;
+	ctx->fd = ioctl(ctx->devfd, UDMABUF_CREATE, &create);
+	if (ctx->fd < 0)
+		error(1, errno, "%s: [FAIL, create udmabuf]\n", TEST_PREFIX);
+
+	ctx->buf_mem = mmap(NULL, size, PROT_READ | PROT_WRITE, MAP_SHARED,
+			    ctx->fd, 0);
+	if (ctx->buf_mem == MAP_FAILED)
+		error(1, errno, "%s: [FAIL, map udmabuf]\n", TEST_PREFIX);
+
+	return ctx;
+}
+
+static void udmabuf_free(struct memory_buffer *ctx)
+{
+	munmap(ctx->buf_mem, ctx->size);
+	close(ctx->fd);
+	close(ctx->memfd);
+	close(ctx->devfd);
+	free(ctx);
+}
+
+static void udmabuf_memcpy_to_device(struct memory_buffer *dst, size_t off,
+				     void *src, int n)
+{
+	struct dma_buf_sync sync = {};
+
+	sync.flags = DMA_BUF_SYNC_START | DMA_BUF_SYNC_WRITE;
+	ioctl(dst->fd, DMA_BUF_IOCTL_SYNC, &sync);
+
+	memcpy(dst->buf_mem + off, src, n);
+
+	sync.flags = DMA_BUF_SYNC_END | DMA_BUF_SYNC_WRITE;
+	ioctl(dst->fd, DMA_BUF_IOCTL_SYNC, &sync);
+}
+
+static void udmabuf_memcpy_from_device(void *dst, struct memory_buffer *src,
+				       size_t off, int n)
+{
+	struct dma_buf_sync sync = {};
+
+	sync.flags = DMA_BUF_SYNC_START;
+	ioctl(src->fd, DMA_BUF_IOCTL_SYNC, &sync);
+
+	memcpy(dst, src->buf_mem + off, n);
+
+	sync.flags = DMA_BUF_SYNC_END;
+	ioctl(src->fd, DMA_BUF_IOCTL_SYNC, &sync);
 }
 
-void print_nonzero_bytes(void *ptr, size_t size)
+static struct memory_provider udmabuf_memory_provider = {
+	.alloc = udmabuf_alloc,
+	.free = udmabuf_free,
+	.memcpy_to_device = udmabuf_memcpy_to_device,
+	.memcpy_from_device = udmabuf_memcpy_from_device,
+};
+
+static struct memory_provider *provider = &udmabuf_memory_provider;
+
+static void print_nonzero_bytes(void *ptr, size_t size)
 {
 	unsigned char *p = ptr;
 	unsigned int i;
@@ -201,42 +302,7 @@  static int bind_rx_queue(unsigned int ifindex, unsigned int dmabuf_fd,
 	return -1;
 }
 
-static void create_udmabuf(int *devfd, int *memfd, int *buf, size_t dmabuf_size)
-{
-	struct udmabuf_create create;
-	int ret;
-
-	*devfd = open("/dev/udmabuf", O_RDWR);
-	if (*devfd < 0) {
-		error(70, 0,
-		      "%s: [skip,no-udmabuf: Unable to access DMA buffer device file]\n",
-		      TEST_PREFIX);
-	}
-
-	*memfd = memfd_create("udmabuf-test", MFD_ALLOW_SEALING);
-	if (*memfd < 0)
-		error(70, 0, "%s: [skip,no-memfd]\n", TEST_PREFIX);
-
-	/* Required for udmabuf */
-	ret = fcntl(*memfd, F_ADD_SEALS, F_SEAL_SHRINK);
-	if (ret < 0)
-		error(73, 0, "%s: [skip,fcntl-add-seals]\n", TEST_PREFIX);
-
-	ret = ftruncate(*memfd, dmabuf_size);
-	if (ret == -1)
-		error(74, 0, "%s: [FAIL,memfd-truncate]\n", TEST_PREFIX);
-
-	memset(&create, 0, sizeof(create));
-
-	create.memfd = *memfd;
-	create.offset = 0;
-	create.size = dmabuf_size;
-	*buf = ioctl(*devfd, UDMABUF_CREATE, &create);
-	if (*buf < 0)
-		error(75, 0, "%s: [FAIL, create udmabuf]\n", TEST_PREFIX);
-}
-
-int do_server(void)
+int do_server(struct memory_buffer *mem)
 {
 	char ctrl_data[sizeof(int) * 20000];
 	struct netdev_queue_id *queues;
@@ -244,23 +310,18 @@  int do_server(void)
 	struct sockaddr_in client_addr;
 	struct sockaddr_in server_sin;
 	size_t page_aligned_frags = 0;
-	int devfd, memfd, buf, ret;
 	size_t total_received = 0;
 	socklen_t client_addr_len;
 	bool is_devmem = false;
-	char *buf_mem = NULL;
+	char *tmp_mem = NULL;
 	struct ynl_sock *ys;
-	size_t dmabuf_size;
 	char iobuf[819200];
 	char buffer[256];
 	int socket_fd;
 	int client_fd;
 	size_t i = 0;
 	int opt = 1;
-
-	dmabuf_size = getpagesize() * NUM_PAGES;
-
-	create_udmabuf(&devfd, &memfd, &buf, dmabuf_size);
+	int ret;
 
 	if (reset_flow_steering())
 		error(1, 0, "Failed to reset flow steering\n");
@@ -284,13 +345,12 @@  int do_server(void)
 		queues[i].id = start_queue + i;
 	}
 
-	if (bind_rx_queue(ifindex, buf, queues, num_queues, &ys))
+	if (bind_rx_queue(ifindex, mem->fd, queues, num_queues, &ys))
 		error(1, 0, "Failed to bind\n");
 
-	buf_mem = mmap(NULL, dmabuf_size, PROT_READ | PROT_WRITE, MAP_SHARED,
-		       buf, 0);
-	if (buf_mem == MAP_FAILED)
-		error(1, 0, "mmap()");
+	tmp_mem = malloc(mem->size);
+	if (!tmp_mem)
+		error(1, ENOMEM, "malloc failed");
 
 	server_sin.sin_family = AF_INET;
 	server_sin.sin_port = htons(atoi(port));
@@ -341,7 +401,6 @@  int do_server(void)
 		struct iovec iov = { .iov_base = iobuf,
 				     .iov_len = sizeof(iobuf) };
 		struct dmabuf_cmsg *dmabuf_cmsg = NULL;
-		struct dma_buf_sync sync = { 0 };
 		struct cmsghdr *cm = NULL;
 		struct msghdr msg = { 0 };
 		struct dmabuf_token token;
@@ -410,22 +469,17 @@  int do_server(void)
 			else
 				page_aligned_frags++;
 
-			sync.flags = DMA_BUF_SYNC_READ | DMA_BUF_SYNC_START;
-			ioctl(buf, DMA_BUF_IOCTL_SYNC, &sync);
+			provider->memcpy_from_device(tmp_mem, mem,
+						     dmabuf_cmsg->frag_offset,
+						     dmabuf_cmsg->frag_size);
 
 			if (do_validation)
 				validate_buffer(
-					((unsigned char *)buf_mem) +
+					((unsigned char *)tmp_mem) +
 						dmabuf_cmsg->frag_offset,
 					dmabuf_cmsg->frag_size);
 			else
-				print_nonzero_bytes(
-					((unsigned char *)buf_mem) +
-						dmabuf_cmsg->frag_offset,
-					dmabuf_cmsg->frag_size);
-
-			sync.flags = DMA_BUF_SYNC_READ | DMA_BUF_SYNC_END;
-			ioctl(buf, DMA_BUF_IOCTL_SYNC, &sync);
+				print_nonzero_bytes(tmp_mem, dmabuf_cmsg->frag_size);
 
 			ret = setsockopt(client_fd, SOL_SOCKET,
 					 SO_DEVMEM_DONTNEED, &token,
@@ -450,12 +504,9 @@  int do_server(void)
 
 cleanup:
 
-	munmap(buf_mem, dmabuf_size);
+	free(tmp_mem);
 	close(client_fd);
 	close(socket_fd);
-	close(buf);
-	close(memfd);
-	close(devfd);
 	ynl_sock_destroy(ys);
 
 	return 0;
@@ -464,14 +515,11 @@  int do_server(void)
 void run_devmem_tests(void)
 {
 	struct netdev_queue_id *queues;
-	int devfd, memfd, buf;
+	struct memory_buffer *mem;
 	struct ynl_sock *ys;
-	size_t dmabuf_size;
 	size_t i = 0;
 
-	dmabuf_size = getpagesize() * NUM_PAGES;
-
-	create_udmabuf(&devfd, &memfd, &buf, dmabuf_size);
+	mem = provider->alloc(getpagesize() * NUM_PAGES);
 
 	/* Configure RSS to divert all traffic from our devmem queues */
 	if (configure_rss())
@@ -482,7 +530,7 @@  void run_devmem_tests(void)
 	if (configure_headersplit(1))
 		error(1, 0, "Failed to configure header split\n");
 
-	if (!bind_rx_queue(ifindex, buf, queues, num_queues, &ys))
+	if (!bind_rx_queue(ifindex, mem->fd, queues, num_queues, &ys))
 		error(1, 0, "Binding empty queues array should have failed\n");
 
 	for (i = 0; i < num_queues; i++) {
@@ -495,7 +543,7 @@  void run_devmem_tests(void)
 	if (configure_headersplit(0))
 		error(1, 0, "Failed to configure header split\n");
 
-	if (!bind_rx_queue(ifindex, buf, queues, num_queues, &ys))
+	if (!bind_rx_queue(ifindex, mem->fd, queues, num_queues, &ys))
 		error(1, 0, "Configure dmabuf with header split off should have failed\n");
 
 	if (configure_headersplit(1))
@@ -508,7 +556,7 @@  void run_devmem_tests(void)
 		queues[i].id = start_queue + i;
 	}
 
-	if (bind_rx_queue(ifindex, buf, queues, num_queues, &ys))
+	if (bind_rx_queue(ifindex, mem->fd, queues, num_queues, &ys))
 		error(1, 0, "Failed to bind\n");
 
 	/* Deactivating a bound queue should not be legal */
@@ -517,11 +565,15 @@  void run_devmem_tests(void)
 
 	/* Closing the netlink socket does an implicit unbind */
 	ynl_sock_destroy(ys);
+
+	provider->free(mem);
 }
 
 int main(int argc, char *argv[])
 {
+	struct memory_buffer *mem;
 	int is_server = 0, opt;
+	int ret;
 
 	while ((opt = getopt(argc, argv, "ls:c:p:v:q:t:f:")) != -1) {
 		switch (opt) {
@@ -562,8 +614,9 @@  int main(int argc, char *argv[])
 
 	run_devmem_tests();
 
-	if (is_server)
-		return do_server();
+	mem = provider->alloc(getpagesize() * NUM_PAGES);
+	ret = is_server ? do_server(mem) : 1;
+	provider->free(mem);
 
-	return 0;
+	return ret;
 }