[net-next,v1,14/16] net: add SO_DEVMEM_DONTNEED setsockopt to release RX frags
Commit Message
Add an interface for the user to notify the kernel that it is done
reading the devmem dmabuf frags returned as cmsg. The kernel will
drop the reference on the frags to make them available for re-use.
Signed-off-by: Willem de Bruijn <willemb@google.com>
Signed-off-by: Kaiyuan Zhang <kaiyuanz@google.com>
Signed-off-by: Mina Almasry <almasrymina@google.com>
---
Changes in v1:
- devmemtoken -> dmabuf_token (David).
- Use napi_pp_put_page() for refcounting (Yunsheng).
---
include/uapi/asm-generic/socket.h | 1 +
include/uapi/linux/uio.h | 4 ++++
net/core/sock.c | 38 +++++++++++++++++++++++++++++++
3 files changed, 43 insertions(+)
Comments
On Thu, Dec 07, 2023 at 04:52:45PM -0800, Mina Almasry wrote:
> Add an interface for the user to notify the kernel that it is done
> reading the devmem dmabuf frags returned as cmsg. The kernel will
> drop the reference on the frags to make them available for re-use.
>
> Signed-off-by: Willem de Bruijn <willemb@google.com>
> Signed-off-by: Kaiyuan Zhang <kaiyuanz@google.com>
> Signed-off-by: Mina Almasry <almasrymina@google.com>
...
> diff --git a/net/core/sock.c b/net/core/sock.c
> index fef349dd72fa..521bdc4ff260 100644
> --- a/net/core/sock.c
> +++ b/net/core/sock.c
> @@ -1051,6 +1051,41 @@ static int sock_reserve_memory(struct sock *sk, int bytes)
> return 0;
> }
>
> +static noinline_for_stack int
> +sock_devmem_dontneed(struct sock *sk, sockptr_t optval, unsigned int optlen)
> +{
> + struct dmabuf_token tokens[128];
Hi Mina,
I am guessing it is mostly due to the line above,
but on x86 32bit builds I see:
warning: the frame size of 1048 bytes is larger than 1024 bytes [-Wframe-larger-than
> + unsigned int num_tokens, i, j;
> + int ret;
> +
> + if (sk->sk_type != SOCK_STREAM || sk->sk_protocol != IPPROTO_TCP)
> + return -EBADF;
> +
> + if (optlen % sizeof(struct dmabuf_token) || optlen > sizeof(tokens))
> + return -EINVAL;
> +
> + num_tokens = optlen / sizeof(struct dmabuf_token);
> + if (copy_from_sockptr(tokens, optval, optlen))
> + return -EFAULT;
> +
> + ret = 0;
> + for (i = 0; i < num_tokens; i++) {
> + for (j = 0; j < tokens[i].token_count; j++) {
> + struct page *page = xa_erase(&sk->sk_user_pages,
> + tokens[i].token_start + j);
> +
> + if (page) {
> + if (WARN_ON_ONCE(!napi_pp_put_page(page,
> + false)))
> + page_pool_page_put_many(page, 1);
> + ret++;
> + }
> + }
> + }
> +
> + return ret;
> +}
> +
> void sockopt_lock_sock(struct sock *sk)
> {
> /* When current->bpf_ctx is set, the setsockopt is called from
...
@@ -135,6 +135,7 @@
#define SO_PASSPIDFD 76
#define SO_PEERPIDFD 77
+#define SO_DEVMEM_DONTNEED 97
#define SO_DEVMEM_LINEAR 98
#define SCM_DEVMEM_LINEAR SO_DEVMEM_LINEAR
#define SO_DEVMEM_DMABUF 99
@@ -30,6 +30,10 @@ struct dmabuf_cmsg {
__u32 dmabuf_id; /* dmabuf id this frag belongs to. */
};
+struct dmabuf_token {
+ __u32 token_start;
+ __u32 token_count;
+};
/*
* UIO_MAXIOV shall be at least 16 1003.1g (5.4.1.1)
*/
@@ -1051,6 +1051,41 @@ static int sock_reserve_memory(struct sock *sk, int bytes)
return 0;
}
+static noinline_for_stack int
+sock_devmem_dontneed(struct sock *sk, sockptr_t optval, unsigned int optlen)
+{
+ struct dmabuf_token tokens[128];
+ unsigned int num_tokens, i, j;
+ int ret;
+
+ if (sk->sk_type != SOCK_STREAM || sk->sk_protocol != IPPROTO_TCP)
+ return -EBADF;
+
+ if (optlen % sizeof(struct dmabuf_token) || optlen > sizeof(tokens))
+ return -EINVAL;
+
+ num_tokens = optlen / sizeof(struct dmabuf_token);
+ if (copy_from_sockptr(tokens, optval, optlen))
+ return -EFAULT;
+
+ ret = 0;
+ for (i = 0; i < num_tokens; i++) {
+ for (j = 0; j < tokens[i].token_count; j++) {
+ struct page *page = xa_erase(&sk->sk_user_pages,
+ tokens[i].token_start + j);
+
+ if (page) {
+ if (WARN_ON_ONCE(!napi_pp_put_page(page,
+ false)))
+ page_pool_page_put_many(page, 1);
+ ret++;
+ }
+ }
+ }
+
+ return ret;
+}
+
void sockopt_lock_sock(struct sock *sk)
{
/* When current->bpf_ctx is set, the setsockopt is called from
@@ -1538,6 +1573,9 @@ int sk_setsockopt(struct sock *sk, int level, int optname,
break;
}
+ case SO_DEVMEM_DONTNEED:
+ ret = sock_devmem_dontneed(sk, optval, optlen);
+ break;
default:
ret = -ENOPROTOOPT;
break;