Linux kernel mirror (for testing) git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
kernel os linux
1
fork

Configure Feed

Select the types of activity you want to include in your feed.

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 <ast@kernel.org>

+146 -9
+1 -1
include/linux/netdevice.h
··· 3768 3768 3769 3769 typedef int (*bpf_op_t)(struct net_device *dev, struct netdev_bpf *bpf); 3770 3770 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); 3772 3772 u32 __dev_xdp_query(struct net_device *dev, bpf_op_t xdp_op, 3773 3773 enum bpf_netdev_command cmd); 3774 3774 int xdp_umem_query(struct net_device *dev, u16 queue_id);
+3 -1
include/uapi/linux/if_link.h
··· 972 972 #define XDP_FLAGS_SKB_MODE (1U << 1) 973 973 #define XDP_FLAGS_DRV_MODE (1U << 2) 974 974 #define XDP_FLAGS_HW_MODE (1U << 3) 975 + #define XDP_FLAGS_REPLACE (1U << 4) 975 976 #define XDP_FLAGS_MODES (XDP_FLAGS_SKB_MODE | \ 976 977 XDP_FLAGS_DRV_MODE | \ 977 978 XDP_FLAGS_HW_MODE) 978 979 #define XDP_FLAGS_MASK (XDP_FLAGS_UPDATE_IF_NOEXIST | \ 979 - XDP_FLAGS_MODES) 980 + XDP_FLAGS_MODES | XDP_FLAGS_REPLACE) 980 981 981 982 /* These are stored into IFLA_XDP_ATTACHED on dump. */ 982 983 enum { ··· 997 996 IFLA_XDP_DRV_PROG_ID, 998 997 IFLA_XDP_SKB_PROG_ID, 999 998 IFLA_XDP_HW_PROG_ID, 999 + IFLA_XDP_EXPECTED_FD, 1000 1000 __IFLA_XDP_MAX, 1001 1001 }; 1002 1002
+21 -5
net/core/dev.c
··· 8655 8655 * @dev: device 8656 8656 * @extack: netlink extended ack 8657 8657 * @fd: new program fd or negative value to clear 8658 + * @expected_fd: old program fd that userspace expects to replace or clear 8658 8659 * @flags: xdp-related flags 8659 8660 * 8660 8661 * Set or clear a bpf program for a device 8661 8662 */ 8662 8663 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) 8664 8665 { 8665 8666 const struct net_device_ops *ops = dev->netdev_ops; 8666 8667 enum bpf_netdev_command query; 8668 + u32 prog_id, expected_id = 0; 8667 8669 struct bpf_prog *prog = NULL; 8668 8670 bpf_op_t bpf_op, bpf_chk; 8669 8671 bool offload; ··· 8686 8684 if (bpf_op == bpf_chk) 8687 8685 bpf_chk = generic_xdp_install; 8688 8686 8689 - if (fd >= 0) { 8690 - u32 prog_id; 8687 + prog_id = __dev_xdp_query(dev, bpf_op, query); 8688 + if (flags & XDP_FLAGS_REPLACE) { 8689 + if (expected_fd >= 0) { 8690 + prog = bpf_prog_get_type_dev(expected_fd, 8691 + BPF_PROG_TYPE_XDP, 8692 + bpf_op == ops->ndo_bpf); 8693 + if (IS_ERR(prog)) 8694 + return PTR_ERR(prog); 8695 + expected_id = prog->aux->id; 8696 + bpf_prog_put(prog); 8697 + } 8691 8698 8699 + if (prog_id != expected_id) { 8700 + NL_SET_ERR_MSG(extack, "Active program does not match expected"); 8701 + return -EEXIST; 8702 + } 8703 + } 8704 + if (fd >= 0) { 8692 8705 if (!offload && __dev_xdp_query(dev, bpf_chk, XDP_QUERY_PROG)) { 8693 8706 NL_SET_ERR_MSG(extack, "native and generic XDP can't be active at the same time"); 8694 8707 return -EEXIST; 8695 8708 } 8696 8709 8697 - prog_id = __dev_xdp_query(dev, bpf_op, query); 8698 8710 if ((flags & XDP_FLAGS_UPDATE_IF_NOEXIST) && prog_id) { 8699 8711 NL_SET_ERR_MSG(extack, "XDP program already attached"); 8700 8712 return -EBUSY; ··· 8731 8715 return 0; 8732 8716 } 8733 8717 } else { 8734 - if (!__dev_xdp_query(dev, bpf_op, query)) 8718 + if (!prog_id) 8735 8719 return 0; 8736 8720 } 8737 8721
+14
net/core/rtnetlink.c
··· 1872 1872 }; 1873 1873 1874 1874 static const struct nla_policy ifla_xdp_policy[IFLA_XDP_MAX + 1] = { 1875 + [IFLA_XDP_UNSPEC] = { .strict_start_type = IFLA_XDP_EXPECTED_FD }, 1875 1876 [IFLA_XDP_FD] = { .type = NLA_S32 }, 1877 + [IFLA_XDP_EXPECTED_FD] = { .type = NLA_S32 }, 1876 1878 [IFLA_XDP_ATTACHED] = { .type = NLA_U8 }, 1877 1879 [IFLA_XDP_FLAGS] = { .type = NLA_U32 }, 1878 1880 [IFLA_XDP_PROG_ID] = { .type = NLA_U32 }, ··· 2801 2799 } 2802 2800 2803 2801 if (xdp[IFLA_XDP_FD]) { 2802 + int expected_fd = -1; 2803 + 2804 + if (xdp_flags & XDP_FLAGS_REPLACE) { 2805 + if (!xdp[IFLA_XDP_EXPECTED_FD]) { 2806 + err = -EINVAL; 2807 + goto errout; 2808 + } 2809 + expected_fd = 2810 + nla_get_s32(xdp[IFLA_XDP_EXPECTED_FD]); 2811 + } 2812 + 2804 2813 err = dev_change_xdp_fd(dev, extack, 2805 2814 nla_get_s32(xdp[IFLA_XDP_FD]), 2815 + expected_fd, 2806 2816 xdp_flags); 2807 2817 if (err) 2808 2818 goto errout;
+3 -1
tools/include/uapi/linux/if_link.h
··· 960 960 #define XDP_FLAGS_SKB_MODE (1U << 1) 961 961 #define XDP_FLAGS_DRV_MODE (1U << 2) 962 962 #define XDP_FLAGS_HW_MODE (1U << 3) 963 + #define XDP_FLAGS_REPLACE (1U << 4) 963 964 #define XDP_FLAGS_MODES (XDP_FLAGS_SKB_MODE | \ 964 965 XDP_FLAGS_DRV_MODE | \ 965 966 XDP_FLAGS_HW_MODE) 966 967 #define XDP_FLAGS_MASK (XDP_FLAGS_UPDATE_IF_NOEXIST | \ 967 - XDP_FLAGS_MODES) 968 + XDP_FLAGS_MODES | XDP_FLAGS_REPLACE) 968 969 969 970 /* These are stored into IFLA_XDP_ATTACHED on dump. */ 970 971 enum { ··· 985 984 IFLA_XDP_DRV_PROG_ID, 986 985 IFLA_XDP_SKB_PROG_ID, 987 986 IFLA_XDP_HW_PROG_ID, 987 + IFLA_XDP_EXPECTED_FD, 988 988 __IFLA_XDP_MAX, 989 989 }; 990 990
+8
tools/lib/bpf/libbpf.h
··· 444 444 __u8 attach_mode; 445 445 }; 446 446 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 + 447 453 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); 448 456 LIBBPF_API int bpf_get_link_xdp_id(int ifindex, __u32 *prog_id, __u32 flags); 449 457 LIBBPF_API int bpf_get_link_xdp_info(int ifindex, struct xdp_link_info *info, 450 458 size_t info_size, __u32 flags);
+1
tools/lib/bpf/libbpf.map
··· 244 244 bpf_link__pin_path; 245 245 bpf_link__unpin; 246 246 bpf_program__set_attach_target; 247 + bpf_set_link_xdp_fd_opts; 247 248 } LIBBPF_0.0.7;
+33 -1
tools/lib/bpf/netlink.c
··· 132 132 return ret; 133 133 } 134 134 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) 136 137 { 137 138 int sock, seq = 0, ret; 138 139 struct nlattr *nla, *nla_xdp; ··· 179 178 nla->nla_len += nla_xdp->nla_len; 180 179 } 181 180 181 + if (flags & XDP_FLAGS_REPLACE) { 182 + nla_xdp = (struct nlattr *)((char *)nla + nla->nla_len); 183 + nla_xdp->nla_type = IFLA_XDP_EXPECTED_FD; 184 + nla_xdp->nla_len = NLA_HDRLEN + sizeof(old_fd); 185 + memcpy((char *)nla_xdp + NLA_HDRLEN, &old_fd, sizeof(old_fd)); 186 + nla->nla_len += nla_xdp->nla_len; 187 + } 188 + 182 189 req.nh.nlmsg_len += NLA_ALIGN(nla->nla_len); 183 190 184 191 if (send(sock, &req, req.nh.nlmsg_len, 0) < 0) { ··· 198 189 cleanup: 199 190 close(sock); 200 191 return ret; 192 + } 193 + 194 + int bpf_set_link_xdp_fd_opts(int ifindex, int fd, __u32 flags, 195 + const struct bpf_xdp_set_link_opts *opts) 196 + { 197 + int old_fd = -1; 198 + 199 + if (!OPTS_VALID(opts, bpf_xdp_set_link_opts)) 200 + return -EINVAL; 201 + 202 + if (OPTS_HAS(opts, old_fd)) { 203 + old_fd = OPTS_GET(opts, old_fd, -1); 204 + flags |= XDP_FLAGS_REPLACE; 205 + } 206 + 207 + return __bpf_set_link_xdp_fd_replace(ifindex, fd, 208 + old_fd, 209 + flags); 210 + } 211 + 212 + int bpf_set_link_xdp_fd(int ifindex, int fd, __u32 flags) 213 + { 214 + return __bpf_set_link_xdp_fd_replace(ifindex, fd, 0, flags); 201 215 } 202 216 203 217 static int __dump_link_nlmsg(struct nlmsghdr *nlh,
+62
tools/testing/selftests/bpf/prog_tests/xdp_attach.c
··· 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 + }