Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit 965a9909 authored by Magnus Karlsson's avatar Magnus Karlsson Committed by Alexei Starovoitov
Browse files

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: default avatarMagnus Karlsson <magnus.karlsson@intel.com>
Signed-off-by: default avatarAlexei Starovoitov <ast@kernel.org>
parent b9b6b68e
Loading
Loading
Loading
Loading
+1 −0
Original line number Diff line number Diff line
@@ -28,6 +28,7 @@ struct xdp_sock {
	struct xsk_queue *rx;
	struct net_device *dev;
	struct xdp_umem *umem;
	u16 queue_id;
	/* Protects multiple processes in the control path */
	struct mutex mutex;
};
+11 −0
Original line number Diff line number Diff line
@@ -21,6 +21,17 @@

#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 */
#define XDP_RX_RING			1
#define XDP_UMEM_REG			3
+5 −0
Original line number Diff line number Diff line
@@ -248,3 +248,8 @@ int xdp_umem_reg(struct xdp_umem *umem, struct xdp_umem_reg *mr)
	put_pid(umem->pid);
	return err;
}

bool xdp_umem_validate_queues(struct xdp_umem *umem)
{
	return umem->fq;
}
+1 −0
Original line number Diff line number Diff line
@@ -39,6 +39,7 @@ struct xdp_umem {
	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);
void xdp_get_umem(struct xdp_umem *umem);
void xdp_put_umem(struct xdp_umem *umem);
+123 −1
Original line number Diff line number Diff line
@@ -57,9 +57,18 @@ static int xsk_init_queue(u32 entries, struct xsk_queue **queue,
	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)
{
	struct sock *sk = sock->sk;
	struct xdp_sock *xs = xdp_sk(sk);
	struct net *net;

	if (!sk)
@@ -71,6 +80,11 @@ static int xsk_release(struct socket *sock)
	sock_prot_inuse_add(net, sk->sk_prot, -1);
	local_bh_enable();

	if (xs->dev) {
		__xsk_release(xs);
		xs->dev = NULL;
	}

	sock_orphan(sk);
	sock->sk = NULL;

@@ -80,6 +94,114 @@ static int xsk_release(struct socket *sock)
	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,
			  char __user *optval, unsigned int optlen)
{
@@ -203,7 +325,7 @@ static const struct proto_ops xsk_proto_ops = {
	.family =	PF_XDP,
	.owner =	THIS_MODULE,
	.release =	xsk_release,
	.bind =		sock_no_bind,
	.bind =		xsk_bind,
	.connect =	sock_no_connect,
	.socketpair =	sock_no_socketpair,
	.accept =	sock_no_accept,
Loading