Commit 678f6e28 authored by Mina Almasry's avatar Mina Almasry Committed by Jakub Kicinski

net: add SO_DEVMEM_DONTNEED setsockopt to release RX frags

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 reuse.
Signed-off-by: default avatarWillem de Bruijn <willemb@google.com>
Signed-off-by: default avatarKaiyuan Zhang <kaiyuanz@google.com>
Signed-off-by: default avatarMina Almasry <almasrymina@google.com>
Reviewed-by: default avatarPavel Begunkov <asml.silence@gmail.com>
Reviewed-by: default avatarEric Dumazet <edumazet@google.com>
Link: https://patch.msgid.link/20240910171458.219195-11-almasrymina@google.comSigned-off-by: default avatarJakub Kicinski <kuba@kernel.org>
parent 8f0b3cc9
...@@ -144,6 +144,7 @@ ...@@ -144,6 +144,7 @@
#define SCM_DEVMEM_LINEAR SO_DEVMEM_LINEAR #define SCM_DEVMEM_LINEAR SO_DEVMEM_LINEAR
#define SO_DEVMEM_DMABUF 79 #define SO_DEVMEM_DMABUF 79
#define SCM_DEVMEM_DMABUF SO_DEVMEM_DMABUF #define SCM_DEVMEM_DMABUF SO_DEVMEM_DMABUF
#define SO_DEVMEM_DONTNEED 80
#if !defined(__KERNEL__) #if !defined(__KERNEL__)
......
...@@ -155,6 +155,7 @@ ...@@ -155,6 +155,7 @@
#define SCM_DEVMEM_LINEAR SO_DEVMEM_LINEAR #define SCM_DEVMEM_LINEAR SO_DEVMEM_LINEAR
#define SO_DEVMEM_DMABUF 79 #define SO_DEVMEM_DMABUF 79
#define SCM_DEVMEM_DMABUF SO_DEVMEM_DMABUF #define SCM_DEVMEM_DMABUF SO_DEVMEM_DMABUF
#define SO_DEVMEM_DONTNEED 80
#if !defined(__KERNEL__) #if !defined(__KERNEL__)
......
...@@ -136,6 +136,7 @@ ...@@ -136,6 +136,7 @@
#define SCM_DEVMEM_LINEAR SO_DEVMEM_LINEAR #define SCM_DEVMEM_LINEAR SO_DEVMEM_LINEAR
#define SO_DEVMEM_DMABUF 79 #define SO_DEVMEM_DMABUF 79
#define SCM_DEVMEM_DMABUF SO_DEVMEM_DMABUF #define SCM_DEVMEM_DMABUF SO_DEVMEM_DMABUF
#define SO_DEVMEM_DONTNEED 80
#if !defined(__KERNEL__) #if !defined(__KERNEL__)
......
...@@ -137,6 +137,7 @@ ...@@ -137,6 +137,7 @@
#define SCM_DEVMEM_LINEAR SO_DEVMEM_LINEAR #define SCM_DEVMEM_LINEAR SO_DEVMEM_LINEAR
#define SO_DEVMEM_DMABUF 0x0058 #define SO_DEVMEM_DMABUF 0x0058
#define SCM_DEVMEM_DMABUF SO_DEVMEM_DMABUF #define SCM_DEVMEM_DMABUF SO_DEVMEM_DMABUF
#define SO_DEVMEM_DONTNEED 0x0059
#if !defined(__KERNEL__) #if !defined(__KERNEL__)
......
...@@ -139,6 +139,7 @@ ...@@ -139,6 +139,7 @@
#define SCM_DEVMEM_LINEAR SO_DEVMEM_LINEAR #define SCM_DEVMEM_LINEAR SO_DEVMEM_LINEAR
#define SO_DEVMEM_DMABUF 79 #define SO_DEVMEM_DMABUF 79
#define SCM_DEVMEM_DMABUF SO_DEVMEM_DMABUF #define SCM_DEVMEM_DMABUF SO_DEVMEM_DMABUF
#define SO_DEVMEM_DONTNEED 80
#if !defined(__KERNEL__) #if !defined(__KERNEL__)
......
...@@ -33,6 +33,11 @@ struct dmabuf_cmsg { ...@@ -33,6 +33,11 @@ struct dmabuf_cmsg {
*/ */
}; };
struct dmabuf_token {
__u32 token_start;
__u32 token_count;
};
/* /*
* UIO_MAXIOV shall be at least 16 1003.1g (5.4.1.1) * UIO_MAXIOV shall be at least 16 1003.1g (5.4.1.1)
*/ */
......
...@@ -124,6 +124,7 @@ ...@@ -124,6 +124,7 @@
#include <linux/netdevice.h> #include <linux/netdevice.h>
#include <net/protocol.h> #include <net/protocol.h>
#include <linux/skbuff.h> #include <linux/skbuff.h>
#include <linux/skbuff_ref.h>
#include <net/net_namespace.h> #include <net/net_namespace.h>
#include <net/request_sock.h> #include <net/request_sock.h>
#include <net/sock.h> #include <net/sock.h>
...@@ -1049,6 +1050,69 @@ static int sock_reserve_memory(struct sock *sk, int bytes) ...@@ -1049,6 +1050,69 @@ static int sock_reserve_memory(struct sock *sk, int bytes)
return 0; return 0;
} }
#ifdef CONFIG_PAGE_POOL
/* This is the number of tokens that the user can SO_DEVMEM_DONTNEED in
* 1 syscall. The limit exists to limit the amount of memory the kernel
* allocates to copy these tokens.
*/
#define MAX_DONTNEED_TOKENS 128
static noinline_for_stack int
sock_devmem_dontneed(struct sock *sk, sockptr_t optval, unsigned int optlen)
{
unsigned int num_tokens, i, j, k, netmem_num = 0;
struct dmabuf_token *tokens;
netmem_ref netmems[16];
int ret = 0;
if (!sk_is_tcp(sk))
return -EBADF;
if (optlen % sizeof(struct dmabuf_token) ||
optlen > sizeof(*tokens) * MAX_DONTNEED_TOKENS)
return -EINVAL;
tokens = kvmalloc_array(optlen, sizeof(*tokens), GFP_KERNEL);
if (!tokens)
return -ENOMEM;
num_tokens = optlen / sizeof(struct dmabuf_token);
if (copy_from_sockptr(tokens, optval, optlen)) {
kvfree(tokens);
return -EFAULT;
}
xa_lock_bh(&sk->sk_user_frags);
for (i = 0; i < num_tokens; i++) {
for (j = 0; j < tokens[i].token_count; j++) {
netmem_ref netmem = (__force netmem_ref)__xa_erase(
&sk->sk_user_frags, tokens[i].token_start + j);
if (netmem &&
!WARN_ON_ONCE(!netmem_is_net_iov(netmem))) {
netmems[netmem_num++] = netmem;
if (netmem_num == ARRAY_SIZE(netmems)) {
xa_unlock_bh(&sk->sk_user_frags);
for (k = 0; k < netmem_num; k++)
WARN_ON_ONCE(!napi_pp_put_page(netmems[k]));
netmem_num = 0;
xa_lock_bh(&sk->sk_user_frags);
}
ret++;
}
}
}
xa_unlock_bh(&sk->sk_user_frags);
for (k = 0; k < netmem_num; k++)
WARN_ON_ONCE(!napi_pp_put_page(netmems[k]));
kvfree(tokens);
return ret;
}
#endif
void sockopt_lock_sock(struct sock *sk) void sockopt_lock_sock(struct sock *sk)
{ {
/* When current->bpf_ctx is set, the setsockopt is called from /* When current->bpf_ctx is set, the setsockopt is called from
...@@ -1211,6 +1275,10 @@ int sk_setsockopt(struct sock *sk, int level, int optname, ...@@ -1211,6 +1275,10 @@ int sk_setsockopt(struct sock *sk, int level, int optname,
ret = -EOPNOTSUPP; ret = -EOPNOTSUPP;
return ret; return ret;
} }
#ifdef CONFIG_PAGE_POOL
case SO_DEVMEM_DONTNEED:
return sock_devmem_dontneed(sk, optval, optlen);
#endif
} }
sockopt_lock_sock(sk); sockopt_lock_sock(sk);
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment