Commit ae661dec authored by Alexei Starovoitov's avatar Alexei Starovoitov
Browse files

Merge branch 'ifla_xdp_expected_fd'



Toke Høiland-Jørgensen says:

====================
This series adds support for atomically replacing the XDP program loaded on an
interface. This is achieved by means of a new netlink attribute that can specify
the expected previous program to replace on the interface. If set, the kernel
will compare this "expected fd" attribute with the program currently loaded on
the interface, and reject the operation if it does not match.

With this primitive, userspace applications can avoid stepping on each other's
toes when simultaneously updating the loaded XDP program.

Changelog:

v4:
- Switch back to passing FD instead of ID (Andrii)
- Rename flag to XDP_FLAGS_REPLACE (for consistency with other similar uses)

v3:
- Pass existing ID instead of FD (Jakub)
- Use opts struct for new libbpf function (Andrii)

v2:
- Fix checkpatch nits and add .strict_start_type to netlink policy (Jakub)
====================

Signed-off-by: default avatarAlexei Starovoitov <ast@kernel.org>
parents e9ff9d52 87854a0b
Loading
Loading
Loading
Loading
+1 −1
Original line number Diff line number Diff line
@@ -3768,7 +3768,7 @@ struct sk_buff *dev_hard_start_xmit(struct sk_buff *skb, struct net_device *dev,

typedef int (*bpf_op_t)(struct net_device *dev, struct netdev_bpf *bpf);
int dev_change_xdp_fd(struct net_device *dev, struct netlink_ext_ack *extack,
		      int fd, u32 flags);
		      int fd, int expected_fd, u32 flags);
u32 __dev_xdp_query(struct net_device *dev, bpf_op_t xdp_op,
		    enum bpf_netdev_command cmd);
int xdp_umem_query(struct net_device *dev, u16 queue_id);
+3 −1
Original line number Diff line number Diff line
@@ -972,11 +972,12 @@ enum {
#define XDP_FLAGS_SKB_MODE		(1U << 1)
#define XDP_FLAGS_DRV_MODE		(1U << 2)
#define XDP_FLAGS_HW_MODE		(1U << 3)
#define XDP_FLAGS_REPLACE		(1U << 4)
#define XDP_FLAGS_MODES			(XDP_FLAGS_SKB_MODE | \
					 XDP_FLAGS_DRV_MODE | \
					 XDP_FLAGS_HW_MODE)
#define XDP_FLAGS_MASK			(XDP_FLAGS_UPDATE_IF_NOEXIST | \
					 XDP_FLAGS_MODES)
					 XDP_FLAGS_MODES | XDP_FLAGS_REPLACE)

/* These are stored into IFLA_XDP_ATTACHED on dump. */
enum {
@@ -996,6 +997,7 @@ enum {
	IFLA_XDP_DRV_PROG_ID,
	IFLA_XDP_SKB_PROG_ID,
	IFLA_XDP_HW_PROG_ID,
	IFLA_XDP_EXPECTED_FD,
	__IFLA_XDP_MAX,
};

+21 −5
Original line number Diff line number Diff line
@@ -8655,15 +8655,17 @@ static void dev_xdp_uninstall(struct net_device *dev)
 *	@dev: device
 *	@extack: netlink extended ack
 *	@fd: new program fd or negative value to clear
 *	@expected_fd: old program fd that userspace expects to replace or clear
 *	@flags: xdp-related flags
 *
 *	Set or clear a bpf program for a device
 */
int dev_change_xdp_fd(struct net_device *dev, struct netlink_ext_ack *extack,
		      int fd, u32 flags)
		      int fd, int expected_fd, u32 flags)
{
	const struct net_device_ops *ops = dev->netdev_ops;
	enum bpf_netdev_command query;
	u32 prog_id, expected_id = 0;
	struct bpf_prog *prog = NULL;
	bpf_op_t bpf_op, bpf_chk;
	bool offload;
@@ -8684,15 +8686,29 @@ int dev_change_xdp_fd(struct net_device *dev, struct netlink_ext_ack *extack,
	if (bpf_op == bpf_chk)
		bpf_chk = generic_xdp_install;

	if (fd >= 0) {
		u32 prog_id;
	prog_id = __dev_xdp_query(dev, bpf_op, query);
	if (flags & XDP_FLAGS_REPLACE) {
		if (expected_fd >= 0) {
			prog = bpf_prog_get_type_dev(expected_fd,
						     BPF_PROG_TYPE_XDP,
						     bpf_op == ops->ndo_bpf);
			if (IS_ERR(prog))
				return PTR_ERR(prog);
			expected_id = prog->aux->id;
			bpf_prog_put(prog);
		}

		if (prog_id != expected_id) {
			NL_SET_ERR_MSG(extack, "Active program does not match expected");
			return -EEXIST;
		}
	}
	if (fd >= 0) {
		if (!offload && __dev_xdp_query(dev, bpf_chk, XDP_QUERY_PROG)) {
			NL_SET_ERR_MSG(extack, "native and generic XDP can't be active at the same time");
			return -EEXIST;
		}

		prog_id = __dev_xdp_query(dev, bpf_op, query);
		if ((flags & XDP_FLAGS_UPDATE_IF_NOEXIST) && prog_id) {
			NL_SET_ERR_MSG(extack, "XDP program already attached");
			return -EBUSY;
@@ -8715,7 +8731,7 @@ int dev_change_xdp_fd(struct net_device *dev, struct netlink_ext_ack *extack,
			return 0;
		}
	} else {
		if (!__dev_xdp_query(dev, bpf_op, query))
		if (!prog_id)
			return 0;
	}

+14 −0
Original line number Diff line number Diff line
@@ -1872,7 +1872,9 @@ static const struct nla_policy ifla_port_policy[IFLA_PORT_MAX+1] = {
};

static const struct nla_policy ifla_xdp_policy[IFLA_XDP_MAX + 1] = {
	[IFLA_XDP_UNSPEC]	= { .strict_start_type = IFLA_XDP_EXPECTED_FD },
	[IFLA_XDP_FD]		= { .type = NLA_S32 },
	[IFLA_XDP_EXPECTED_FD]	= { .type = NLA_S32 },
	[IFLA_XDP_ATTACHED]	= { .type = NLA_U8 },
	[IFLA_XDP_FLAGS]	= { .type = NLA_U32 },
	[IFLA_XDP_PROG_ID]	= { .type = NLA_U32 },
@@ -2799,8 +2801,20 @@ static int do_setlink(const struct sk_buff *skb,
		}

		if (xdp[IFLA_XDP_FD]) {
			int expected_fd = -1;

			if (xdp_flags & XDP_FLAGS_REPLACE) {
				if (!xdp[IFLA_XDP_EXPECTED_FD]) {
					err = -EINVAL;
					goto errout;
				}
				expected_fd =
					nla_get_s32(xdp[IFLA_XDP_EXPECTED_FD]);
			}

			err = dev_change_xdp_fd(dev, extack,
						nla_get_s32(xdp[IFLA_XDP_FD]),
						expected_fd,
						xdp_flags);
			if (err)
				goto errout;
+3 −1
Original line number Diff line number Diff line
@@ -960,11 +960,12 @@ enum {
#define XDP_FLAGS_SKB_MODE		(1U << 1)
#define XDP_FLAGS_DRV_MODE		(1U << 2)
#define XDP_FLAGS_HW_MODE		(1U << 3)
#define XDP_FLAGS_REPLACE		(1U << 4)
#define XDP_FLAGS_MODES			(XDP_FLAGS_SKB_MODE | \
					 XDP_FLAGS_DRV_MODE | \
					 XDP_FLAGS_HW_MODE)
#define XDP_FLAGS_MASK			(XDP_FLAGS_UPDATE_IF_NOEXIST | \
					 XDP_FLAGS_MODES)
					 XDP_FLAGS_MODES | XDP_FLAGS_REPLACE)

/* These are stored into IFLA_XDP_ATTACHED on dump. */
enum {
@@ -984,6 +985,7 @@ enum {
	IFLA_XDP_DRV_PROG_ID,
	IFLA_XDP_SKB_PROG_ID,
	IFLA_XDP_HW_PROG_ID,
	IFLA_XDP_EXPECTED_FD,
	__IFLA_XDP_MAX,
};

Loading