forked from mirrors/linux
		
	xsk: add support for bind for Rx
Here, the bind syscall is added. Binding an AF_XDP socket, means
associating the socket to an umem, a netdev and a queue index. This
can be done in two ways.
The first way, creating a "socket from scratch". Create the umem using
the XDP_UMEM_REG setsockopt and an associated fill queue with
XDP_UMEM_FILL_QUEUE. Create the Rx queue using the XDP_RX_QUEUE
setsockopt. Call bind passing ifindex and queue index ("channel" in
ethtool speak).
The second way to bind a socket, is simply skipping the
umem/netdev/queue index, and passing another already setup AF_XDP
socket. The new socket will then have the same umem/netdev/queue index
as the parent so it will share the same umem. You must also set the
flags field in the socket address to XDP_SHARED_UMEM.
v2: Use PTR_ERR instead of passing error variable explicitly.
Signed-off-by: Magnus Karlsson <magnus.karlsson@intel.com>
Signed-off-by: Alexei Starovoitov <ast@kernel.org>
			
			
This commit is contained in:
		
							parent
							
								
									b9b6b68e8a
								
							
						
					
					
						commit
						965a990984
					
				
					 7 changed files with 150 additions and 1 deletions
				
			
		| 
						 | 
					@ -28,6 +28,7 @@ struct xdp_sock {
 | 
				
			||||||
	struct xsk_queue *rx;
 | 
						struct xsk_queue *rx;
 | 
				
			||||||
	struct net_device *dev;
 | 
						struct net_device *dev;
 | 
				
			||||||
	struct xdp_umem *umem;
 | 
						struct xdp_umem *umem;
 | 
				
			||||||
 | 
						u16 queue_id;
 | 
				
			||||||
	/* Protects multiple processes in the control path */
 | 
						/* Protects multiple processes in the control path */
 | 
				
			||||||
	struct mutex mutex;
 | 
						struct mutex mutex;
 | 
				
			||||||
};
 | 
					};
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -21,6 +21,17 @@
 | 
				
			||||||
 | 
					
 | 
				
			||||||
#include <linux/types.h>
 | 
					#include <linux/types.h>
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					/* Options for the sxdp_flags field */
 | 
				
			||||||
 | 
					#define XDP_SHARED_UMEM 1
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					struct sockaddr_xdp {
 | 
				
			||||||
 | 
						__u16 sxdp_family;
 | 
				
			||||||
 | 
						__u32 sxdp_ifindex;
 | 
				
			||||||
 | 
						__u32 sxdp_queue_id;
 | 
				
			||||||
 | 
						__u32 sxdp_shared_umem_fd;
 | 
				
			||||||
 | 
						__u16 sxdp_flags;
 | 
				
			||||||
 | 
					};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
/* XDP socket options */
 | 
					/* XDP socket options */
 | 
				
			||||||
#define XDP_RX_RING			1
 | 
					#define XDP_RX_RING			1
 | 
				
			||||||
#define XDP_UMEM_REG			3
 | 
					#define XDP_UMEM_REG			3
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -248,3 +248,8 @@ int xdp_umem_reg(struct xdp_umem *umem, struct xdp_umem_reg *mr)
 | 
				
			||||||
	put_pid(umem->pid);
 | 
						put_pid(umem->pid);
 | 
				
			||||||
	return err;
 | 
						return err;
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					bool xdp_umem_validate_queues(struct xdp_umem *umem)
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
						return umem->fq;
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -39,6 +39,7 @@ struct xdp_umem {
 | 
				
			||||||
	struct work_struct work;
 | 
						struct work_struct work;
 | 
				
			||||||
};
 | 
					};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					bool xdp_umem_validate_queues(struct xdp_umem *umem);
 | 
				
			||||||
int xdp_umem_reg(struct xdp_umem *umem, struct xdp_umem_reg *mr);
 | 
					int xdp_umem_reg(struct xdp_umem *umem, struct xdp_umem_reg *mr);
 | 
				
			||||||
void xdp_get_umem(struct xdp_umem *umem);
 | 
					void xdp_get_umem(struct xdp_umem *umem);
 | 
				
			||||||
void xdp_put_umem(struct xdp_umem *umem);
 | 
					void xdp_put_umem(struct xdp_umem *umem);
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
							
								
								
									
										124
									
								
								net/xdp/xsk.c
									
									
									
									
									
								
							
							
						
						
									
										124
									
								
								net/xdp/xsk.c
									
									
									
									
									
								
							| 
						 | 
					@ -57,9 +57,18 @@ static int xsk_init_queue(u32 entries, struct xsk_queue **queue,
 | 
				
			||||||
	return 0;
 | 
						return 0;
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					static void __xsk_release(struct xdp_sock *xs)
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
						/* Wait for driver to stop using the xdp socket. */
 | 
				
			||||||
 | 
						synchronize_net();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						dev_put(xs->dev);
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
static int xsk_release(struct socket *sock)
 | 
					static int xsk_release(struct socket *sock)
 | 
				
			||||||
{
 | 
					{
 | 
				
			||||||
	struct sock *sk = sock->sk;
 | 
						struct sock *sk = sock->sk;
 | 
				
			||||||
 | 
						struct xdp_sock *xs = xdp_sk(sk);
 | 
				
			||||||
	struct net *net;
 | 
						struct net *net;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	if (!sk)
 | 
						if (!sk)
 | 
				
			||||||
| 
						 | 
					@ -71,6 +80,11 @@ static int xsk_release(struct socket *sock)
 | 
				
			||||||
	sock_prot_inuse_add(net, sk->sk_prot, -1);
 | 
						sock_prot_inuse_add(net, sk->sk_prot, -1);
 | 
				
			||||||
	local_bh_enable();
 | 
						local_bh_enable();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						if (xs->dev) {
 | 
				
			||||||
 | 
							__xsk_release(xs);
 | 
				
			||||||
 | 
							xs->dev = NULL;
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	sock_orphan(sk);
 | 
						sock_orphan(sk);
 | 
				
			||||||
	sock->sk = NULL;
 | 
						sock->sk = NULL;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
| 
						 | 
					@ -80,6 +94,114 @@ static int xsk_release(struct socket *sock)
 | 
				
			||||||
	return 0;
 | 
						return 0;
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					static struct socket *xsk_lookup_xsk_from_fd(int fd)
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
						struct socket *sock;
 | 
				
			||||||
 | 
						int err;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						sock = sockfd_lookup(fd, &err);
 | 
				
			||||||
 | 
						if (!sock)
 | 
				
			||||||
 | 
							return ERR_PTR(-ENOTSOCK);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						if (sock->sk->sk_family != PF_XDP) {
 | 
				
			||||||
 | 
							sockfd_put(sock);
 | 
				
			||||||
 | 
							return ERR_PTR(-ENOPROTOOPT);
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						return sock;
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					static int xsk_bind(struct socket *sock, struct sockaddr *addr, int addr_len)
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
						struct sockaddr_xdp *sxdp = (struct sockaddr_xdp *)addr;
 | 
				
			||||||
 | 
						struct sock *sk = sock->sk;
 | 
				
			||||||
 | 
						struct net_device *dev, *dev_curr;
 | 
				
			||||||
 | 
						struct xdp_sock *xs = xdp_sk(sk);
 | 
				
			||||||
 | 
						struct xdp_umem *old_umem = NULL;
 | 
				
			||||||
 | 
						int err = 0;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						if (addr_len < sizeof(struct sockaddr_xdp))
 | 
				
			||||||
 | 
							return -EINVAL;
 | 
				
			||||||
 | 
						if (sxdp->sxdp_family != AF_XDP)
 | 
				
			||||||
 | 
							return -EINVAL;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						mutex_lock(&xs->mutex);
 | 
				
			||||||
 | 
						dev_curr = xs->dev;
 | 
				
			||||||
 | 
						dev = dev_get_by_index(sock_net(sk), sxdp->sxdp_ifindex);
 | 
				
			||||||
 | 
						if (!dev) {
 | 
				
			||||||
 | 
							err = -ENODEV;
 | 
				
			||||||
 | 
							goto out_release;
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						if (!xs->rx) {
 | 
				
			||||||
 | 
							err = -EINVAL;
 | 
				
			||||||
 | 
							goto out_unlock;
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						if (sxdp->sxdp_queue_id >= dev->num_rx_queues) {
 | 
				
			||||||
 | 
							err = -EINVAL;
 | 
				
			||||||
 | 
							goto out_unlock;
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						if (sxdp->sxdp_flags & XDP_SHARED_UMEM) {
 | 
				
			||||||
 | 
							struct xdp_sock *umem_xs;
 | 
				
			||||||
 | 
							struct socket *sock;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
							if (xs->umem) {
 | 
				
			||||||
 | 
								/* We have already our own. */
 | 
				
			||||||
 | 
								err = -EINVAL;
 | 
				
			||||||
 | 
								goto out_unlock;
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
							sock = xsk_lookup_xsk_from_fd(sxdp->sxdp_shared_umem_fd);
 | 
				
			||||||
 | 
							if (IS_ERR(sock)) {
 | 
				
			||||||
 | 
								err = PTR_ERR(sock);
 | 
				
			||||||
 | 
								goto out_unlock;
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
							umem_xs = xdp_sk(sock->sk);
 | 
				
			||||||
 | 
							if (!umem_xs->umem) {
 | 
				
			||||||
 | 
								/* No umem to inherit. */
 | 
				
			||||||
 | 
								err = -EBADF;
 | 
				
			||||||
 | 
								sockfd_put(sock);
 | 
				
			||||||
 | 
								goto out_unlock;
 | 
				
			||||||
 | 
							} else if (umem_xs->dev != dev ||
 | 
				
			||||||
 | 
								   umem_xs->queue_id != sxdp->sxdp_queue_id) {
 | 
				
			||||||
 | 
								err = -EINVAL;
 | 
				
			||||||
 | 
								sockfd_put(sock);
 | 
				
			||||||
 | 
								goto out_unlock;
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
							xdp_get_umem(umem_xs->umem);
 | 
				
			||||||
 | 
							old_umem = xs->umem;
 | 
				
			||||||
 | 
							xs->umem = umem_xs->umem;
 | 
				
			||||||
 | 
							sockfd_put(sock);
 | 
				
			||||||
 | 
						} else if (!xs->umem || !xdp_umem_validate_queues(xs->umem)) {
 | 
				
			||||||
 | 
							err = -EINVAL;
 | 
				
			||||||
 | 
							goto out_unlock;
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						/* Rebind? */
 | 
				
			||||||
 | 
						if (dev_curr && (dev_curr != dev ||
 | 
				
			||||||
 | 
								 xs->queue_id != sxdp->sxdp_queue_id)) {
 | 
				
			||||||
 | 
							__xsk_release(xs);
 | 
				
			||||||
 | 
							if (old_umem)
 | 
				
			||||||
 | 
								xdp_put_umem(old_umem);
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						xs->dev = dev;
 | 
				
			||||||
 | 
						xs->queue_id = sxdp->sxdp_queue_id;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						xskq_set_umem(xs->rx, &xs->umem->props);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					out_unlock:
 | 
				
			||||||
 | 
						if (err)
 | 
				
			||||||
 | 
							dev_put(dev);
 | 
				
			||||||
 | 
					out_release:
 | 
				
			||||||
 | 
						mutex_unlock(&xs->mutex);
 | 
				
			||||||
 | 
						return err;
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
static int xsk_setsockopt(struct socket *sock, int level, int optname,
 | 
					static int xsk_setsockopt(struct socket *sock, int level, int optname,
 | 
				
			||||||
			  char __user *optval, unsigned int optlen)
 | 
								  char __user *optval, unsigned int optlen)
 | 
				
			||||||
{
 | 
					{
 | 
				
			||||||
| 
						 | 
					@ -203,7 +325,7 @@ static const struct proto_ops xsk_proto_ops = {
 | 
				
			||||||
	.family =	PF_XDP,
 | 
						.family =	PF_XDP,
 | 
				
			||||||
	.owner =	THIS_MODULE,
 | 
						.owner =	THIS_MODULE,
 | 
				
			||||||
	.release =	xsk_release,
 | 
						.release =	xsk_release,
 | 
				
			||||||
	.bind =		sock_no_bind,
 | 
						.bind =		xsk_bind,
 | 
				
			||||||
	.connect =	sock_no_connect,
 | 
						.connect =	sock_no_connect,
 | 
				
			||||||
	.socketpair =	sock_no_socketpair,
 | 
						.socketpair =	sock_no_socketpair,
 | 
				
			||||||
	.accept =	sock_no_accept,
 | 
						.accept =	sock_no_accept,
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -16,6 +16,14 @@
 | 
				
			||||||
 | 
					
 | 
				
			||||||
#include "xsk_queue.h"
 | 
					#include "xsk_queue.h"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					void xskq_set_umem(struct xsk_queue *q, struct xdp_umem_props *umem_props)
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
						if (!q)
 | 
				
			||||||
 | 
							return;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						q->umem_props = *umem_props;
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
static u32 xskq_umem_get_ring_size(struct xsk_queue *q)
 | 
					static u32 xskq_umem_get_ring_size(struct xsk_queue *q)
 | 
				
			||||||
{
 | 
					{
 | 
				
			||||||
	return sizeof(struct xdp_umem_ring) + q->nentries * sizeof(u32);
 | 
						return sizeof(struct xdp_umem_ring) + q->nentries * sizeof(u32);
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -32,6 +32,7 @@ struct xsk_queue {
 | 
				
			||||||
	u64 invalid_descs;
 | 
						u64 invalid_descs;
 | 
				
			||||||
};
 | 
					};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					void xskq_set_umem(struct xsk_queue *q, struct xdp_umem_props *umem_props);
 | 
				
			||||||
struct xsk_queue *xskq_create(u32 nentries, bool umem_queue);
 | 
					struct xsk_queue *xskq_create(u32 nentries, bool umem_queue);
 | 
				
			||||||
void xskq_destroy(struct xsk_queue *q);
 | 
					void xskq_destroy(struct xsk_queue *q);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
		Loading…
	
		Reference in a new issue