Skip to content

Commit ae661de

Browse files
author
Alexei Starovoitov
committed
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: Alexei Starovoitov <[email protected]>
2 parents e9ff9d5 + 87854a0 commit ae661de

File tree

9 files changed

+146
-9
lines changed

9 files changed

+146
-9
lines changed

include/linux/netdevice.h

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3768,7 +3768,7 @@ struct sk_buff *dev_hard_start_xmit(struct sk_buff *skb, struct net_device *dev,
37683768

37693769
typedef int (*bpf_op_t)(struct net_device *dev, struct netdev_bpf *bpf);
37703770
int dev_change_xdp_fd(struct net_device *dev, struct netlink_ext_ack *extack,
3771-
int fd, u32 flags);
3771+
int fd, int expected_fd, u32 flags);
37723772
u32 __dev_xdp_query(struct net_device *dev, bpf_op_t xdp_op,
37733773
enum bpf_netdev_command cmd);
37743774
int xdp_umem_query(struct net_device *dev, u16 queue_id);

include/uapi/linux/if_link.h

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -972,11 +972,12 @@ enum {
972972
#define XDP_FLAGS_SKB_MODE (1U << 1)
973973
#define XDP_FLAGS_DRV_MODE (1U << 2)
974974
#define XDP_FLAGS_HW_MODE (1U << 3)
975+
#define XDP_FLAGS_REPLACE (1U << 4)
975976
#define XDP_FLAGS_MODES (XDP_FLAGS_SKB_MODE | \
976977
XDP_FLAGS_DRV_MODE | \
977978
XDP_FLAGS_HW_MODE)
978979
#define XDP_FLAGS_MASK (XDP_FLAGS_UPDATE_IF_NOEXIST | \
979-
XDP_FLAGS_MODES)
980+
XDP_FLAGS_MODES | XDP_FLAGS_REPLACE)
980981

981982
/* These are stored into IFLA_XDP_ATTACHED on dump. */
982983
enum {
@@ -996,6 +997,7 @@ enum {
996997
IFLA_XDP_DRV_PROG_ID,
997998
IFLA_XDP_SKB_PROG_ID,
998999
IFLA_XDP_HW_PROG_ID,
1000+
IFLA_XDP_EXPECTED_FD,
9991001
__IFLA_XDP_MAX,
10001002
};
10011003

net/core/dev.c

Lines changed: 21 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -8655,15 +8655,17 @@ static void dev_xdp_uninstall(struct net_device *dev)
86558655
* @dev: device
86568656
* @extack: netlink extended ack
86578657
* @fd: new program fd or negative value to clear
8658+
* @expected_fd: old program fd that userspace expects to replace or clear
86588659
* @flags: xdp-related flags
86598660
*
86608661
* Set or clear a bpf program for a device
86618662
*/
86628663
int dev_change_xdp_fd(struct net_device *dev, struct netlink_ext_ack *extack,
8663-
int fd, u32 flags)
8664+
int fd, int expected_fd, u32 flags)
86648665
{
86658666
const struct net_device_ops *ops = dev->netdev_ops;
86668667
enum bpf_netdev_command query;
8668+
u32 prog_id, expected_id = 0;
86678669
struct bpf_prog *prog = NULL;
86688670
bpf_op_t bpf_op, bpf_chk;
86698671
bool offload;
@@ -8684,15 +8686,29 @@ int dev_change_xdp_fd(struct net_device *dev, struct netlink_ext_ack *extack,
86848686
if (bpf_op == bpf_chk)
86858687
bpf_chk = generic_xdp_install;
86868688

8687-
if (fd >= 0) {
8688-
u32 prog_id;
8689+
prog_id = __dev_xdp_query(dev, bpf_op, query);
8690+
if (flags & XDP_FLAGS_REPLACE) {
8691+
if (expected_fd >= 0) {
8692+
prog = bpf_prog_get_type_dev(expected_fd,
8693+
BPF_PROG_TYPE_XDP,
8694+
bpf_op == ops->ndo_bpf);
8695+
if (IS_ERR(prog))
8696+
return PTR_ERR(prog);
8697+
expected_id = prog->aux->id;
8698+
bpf_prog_put(prog);
8699+
}
86898700

8701+
if (prog_id != expected_id) {
8702+
NL_SET_ERR_MSG(extack, "Active program does not match expected");
8703+
return -EEXIST;
8704+
}
8705+
}
8706+
if (fd >= 0) {
86908707
if (!offload && __dev_xdp_query(dev, bpf_chk, XDP_QUERY_PROG)) {
86918708
NL_SET_ERR_MSG(extack, "native and generic XDP can't be active at the same time");
86928709
return -EEXIST;
86938710
}
86948711

8695-
prog_id = __dev_xdp_query(dev, bpf_op, query);
86968712
if ((flags & XDP_FLAGS_UPDATE_IF_NOEXIST) && prog_id) {
86978713
NL_SET_ERR_MSG(extack, "XDP program already attached");
86988714
return -EBUSY;
@@ -8715,7 +8731,7 @@ int dev_change_xdp_fd(struct net_device *dev, struct netlink_ext_ack *extack,
87158731
return 0;
87168732
}
87178733
} else {
8718-
if (!__dev_xdp_query(dev, bpf_op, query))
8734+
if (!prog_id)
87198735
return 0;
87208736
}
87218737

net/core/rtnetlink.c

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1872,7 +1872,9 @@ static const struct nla_policy ifla_port_policy[IFLA_PORT_MAX+1] = {
18721872
};
18731873

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

28012803
if (xdp[IFLA_XDP_FD]) {
2804+
int expected_fd = -1;
2805+
2806+
if (xdp_flags & XDP_FLAGS_REPLACE) {
2807+
if (!xdp[IFLA_XDP_EXPECTED_FD]) {
2808+
err = -EINVAL;
2809+
goto errout;
2810+
}
2811+
expected_fd =
2812+
nla_get_s32(xdp[IFLA_XDP_EXPECTED_FD]);
2813+
}
2814+
28022815
err = dev_change_xdp_fd(dev, extack,
28032816
nla_get_s32(xdp[IFLA_XDP_FD]),
2817+
expected_fd,
28042818
xdp_flags);
28052819
if (err)
28062820
goto errout;

tools/include/uapi/linux/if_link.h

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -960,11 +960,12 @@ enum {
960960
#define XDP_FLAGS_SKB_MODE (1U << 1)
961961
#define XDP_FLAGS_DRV_MODE (1U << 2)
962962
#define XDP_FLAGS_HW_MODE (1U << 3)
963+
#define XDP_FLAGS_REPLACE (1U << 4)
963964
#define XDP_FLAGS_MODES (XDP_FLAGS_SKB_MODE | \
964965
XDP_FLAGS_DRV_MODE | \
965966
XDP_FLAGS_HW_MODE)
966967
#define XDP_FLAGS_MASK (XDP_FLAGS_UPDATE_IF_NOEXIST | \
967-
XDP_FLAGS_MODES)
968+
XDP_FLAGS_MODES | XDP_FLAGS_REPLACE)
968969

969970
/* These are stored into IFLA_XDP_ATTACHED on dump. */
970971
enum {
@@ -984,6 +985,7 @@ enum {
984985
IFLA_XDP_DRV_PROG_ID,
985986
IFLA_XDP_SKB_PROG_ID,
986987
IFLA_XDP_HW_PROG_ID,
988+
IFLA_XDP_EXPECTED_FD,
987989
__IFLA_XDP_MAX,
988990
};
989991

tools/lib/bpf/libbpf.h

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -444,7 +444,15 @@ struct xdp_link_info {
444444
__u8 attach_mode;
445445
};
446446

447+
struct bpf_xdp_set_link_opts {
448+
size_t sz;
449+
__u32 old_fd;
450+
};
451+
#define bpf_xdp_set_link_opts__last_field old_fd
452+
447453
LIBBPF_API int bpf_set_link_xdp_fd(int ifindex, int fd, __u32 flags);
454+
LIBBPF_API int bpf_set_link_xdp_fd_opts(int ifindex, int fd, __u32 flags,
455+
const struct bpf_xdp_set_link_opts *opts);
448456
LIBBPF_API int bpf_get_link_xdp_id(int ifindex, __u32 *prog_id, __u32 flags);
449457
LIBBPF_API int bpf_get_link_xdp_info(int ifindex, struct xdp_link_info *info,
450458
size_t info_size, __u32 flags);

tools/lib/bpf/libbpf.map

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -244,4 +244,5 @@ LIBBPF_0.0.8 {
244244
bpf_link__pin_path;
245245
bpf_link__unpin;
246246
bpf_program__set_attach_target;
247+
bpf_set_link_xdp_fd_opts;
247248
} LIBBPF_0.0.7;

tools/lib/bpf/netlink.c

Lines changed: 33 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -132,7 +132,8 @@ static int bpf_netlink_recv(int sock, __u32 nl_pid, int seq,
132132
return ret;
133133
}
134134

135-
int bpf_set_link_xdp_fd(int ifindex, int fd, __u32 flags)
135+
static int __bpf_set_link_xdp_fd_replace(int ifindex, int fd, int old_fd,
136+
__u32 flags)
136137
{
137138
int sock, seq = 0, ret;
138139
struct nlattr *nla, *nla_xdp;
@@ -178,6 +179,14 @@ int bpf_set_link_xdp_fd(int ifindex, int fd, __u32 flags)
178179
nla->nla_len += nla_xdp->nla_len;
179180
}
180181

182+
if (flags & XDP_FLAGS_REPLACE) {
183+
nla_xdp = (struct nlattr *)((char *)nla + nla->nla_len);
184+
nla_xdp->nla_type = IFLA_XDP_EXPECTED_FD;
185+
nla_xdp->nla_len = NLA_HDRLEN + sizeof(old_fd);
186+
memcpy((char *)nla_xdp + NLA_HDRLEN, &old_fd, sizeof(old_fd));
187+
nla->nla_len += nla_xdp->nla_len;
188+
}
189+
181190
req.nh.nlmsg_len += NLA_ALIGN(nla->nla_len);
182191

183192
if (send(sock, &req, req.nh.nlmsg_len, 0) < 0) {
@@ -191,6 +200,29 @@ int bpf_set_link_xdp_fd(int ifindex, int fd, __u32 flags)
191200
return ret;
192201
}
193202

203+
int bpf_set_link_xdp_fd_opts(int ifindex, int fd, __u32 flags,
204+
const struct bpf_xdp_set_link_opts *opts)
205+
{
206+
int old_fd = -1;
207+
208+
if (!OPTS_VALID(opts, bpf_xdp_set_link_opts))
209+
return -EINVAL;
210+
211+
if (OPTS_HAS(opts, old_fd)) {
212+
old_fd = OPTS_GET(opts, old_fd, -1);
213+
flags |= XDP_FLAGS_REPLACE;
214+
}
215+
216+
return __bpf_set_link_xdp_fd_replace(ifindex, fd,
217+
old_fd,
218+
flags);
219+
}
220+
221+
int bpf_set_link_xdp_fd(int ifindex, int fd, __u32 flags)
222+
{
223+
return __bpf_set_link_xdp_fd_replace(ifindex, fd, 0, flags);
224+
}
225+
194226
static int __dump_link_nlmsg(struct nlmsghdr *nlh,
195227
libbpf_dump_nlmsg_t dump_link_nlmsg, void *cookie)
196228
{
Lines changed: 62 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,62 @@
1+
// SPDX-License-Identifier: GPL-2.0
2+
#include <test_progs.h>
3+
4+
#define IFINDEX_LO 1
5+
#define XDP_FLAGS_REPLACE (1U << 4)
6+
7+
void test_xdp_attach(void)
8+
{
9+
struct bpf_object *obj1, *obj2, *obj3;
10+
const char *file = "./test_xdp.o";
11+
int err, fd1, fd2, fd3;
12+
__u32 duration = 0;
13+
DECLARE_LIBBPF_OPTS(bpf_xdp_set_link_opts, opts,
14+
.old_fd = -1);
15+
16+
err = bpf_prog_load(file, BPF_PROG_TYPE_XDP, &obj1, &fd1);
17+
if (CHECK_FAIL(err))
18+
return;
19+
err = bpf_prog_load(file, BPF_PROG_TYPE_XDP, &obj2, &fd2);
20+
if (CHECK_FAIL(err))
21+
goto out_1;
22+
err = bpf_prog_load(file, BPF_PROG_TYPE_XDP, &obj3, &fd3);
23+
if (CHECK_FAIL(err))
24+
goto out_2;
25+
26+
err = bpf_set_link_xdp_fd_opts(IFINDEX_LO, fd1, XDP_FLAGS_REPLACE,
27+
&opts);
28+
if (CHECK(err, "load_ok", "initial load failed"))
29+
goto out_close;
30+
31+
err = bpf_set_link_xdp_fd_opts(IFINDEX_LO, fd2, XDP_FLAGS_REPLACE,
32+
&opts);
33+
if (CHECK(!err, "load_fail", "load with expected id didn't fail"))
34+
goto out;
35+
36+
opts.old_fd = fd1;
37+
err = bpf_set_link_xdp_fd_opts(IFINDEX_LO, fd2, 0, &opts);
38+
if (CHECK(err, "replace_ok", "replace valid old_fd failed"))
39+
goto out;
40+
41+
err = bpf_set_link_xdp_fd_opts(IFINDEX_LO, fd3, 0, &opts);
42+
if (CHECK(!err, "replace_fail", "replace invalid old_fd didn't fail"))
43+
goto out;
44+
45+
err = bpf_set_link_xdp_fd_opts(IFINDEX_LO, -1, 0, &opts);
46+
if (CHECK(!err, "remove_fail", "remove invalid old_fd didn't fail"))
47+
goto out;
48+
49+
opts.old_fd = fd2;
50+
err = bpf_set_link_xdp_fd_opts(IFINDEX_LO, -1, 0, &opts);
51+
if (CHECK(err, "remove_ok", "remove valid old_fd failed"))
52+
goto out;
53+
54+
out:
55+
bpf_set_link_xdp_fd(IFINDEX_LO, -1, 0);
56+
out_close:
57+
bpf_object__close(obj3);
58+
out_2:
59+
bpf_object__close(obj2);
60+
out_1:
61+
bpf_object__close(obj1);
62+
}

0 commit comments

Comments
 (0)