devtx is a lightweight set of hooks before and after packet transmission. The hooks are implemented as a tracing program which has access to the XDP-metadata-like kfuncs. The initial set of kfuncs is implemented in the next patch, but the idea is similar to XDP metadata: the kfuncs have netdev-specific implementation, but common interface. Upon loading, the kfuncs are resolved to direct calls against per-netdev implementation. This can be achieved by marking devtx-tracing programs as dev-bound (largely reusing xdp-dev-bound program infrastructure). Signed-off-by: Stanislav Fomichev <sdf@xxxxxxxxxx> --- MAINTAINERS | 2 ++ include/net/devtx.h | 66 +++++++++++++++++++++++++++++++++++++ kernel/bpf/offload.c | 15 +++++++++ net/core/Makefile | 1 + net/core/dev.c | 1 + net/core/devtx.c | 78 ++++++++++++++++++++++++++++++++++++++++++++ 6 files changed, 163 insertions(+) create mode 100644 include/net/devtx.h create mode 100644 net/core/devtx.c diff --git a/MAINTAINERS b/MAINTAINERS index acbe54087d1c..de6a2430d49a 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -23063,11 +23063,13 @@ L: bpf@xxxxxxxxxxxxxxx S: Supported F: drivers/net/ethernet/*/*/*/*/*xdp* F: drivers/net/ethernet/*/*/*xdp* +F: include/net/devtx.h F: include/net/xdp.h F: include/net/xdp_priv.h F: include/trace/events/xdp.h F: kernel/bpf/cpumap.c F: kernel/bpf/devmap.c +F: net/core/devtx.c F: net/core/xdp.c F: samples/bpf/xdp* F: tools/testing/selftests/bpf/*/*xdp* diff --git a/include/net/devtx.h b/include/net/devtx.h new file mode 100644 index 000000000000..88127ca87b9a --- /dev/null +++ b/include/net/devtx.h @@ -0,0 +1,66 @@ +/* SPDX-License-Identifier: GPL-2.0-only */ +#ifndef __LINUX_NET_DEVTX_H__ +#define __LINUX_NET_DEVTX_H__ + +#include <linux/jump_label.h> +#include <linux/skbuff.h> +#include <linux/netdevice.h> +#include <linux/btf_ids.h> +#include <net/xdp.h> + +struct devtx_ctx { + struct net_device *netdev; + struct skb_shared_info *sinfo; /* for frags */ +}; + +#define DECLARE_DEVTX_HOOKS(PREFIX) \ +void PREFIX ## _devtx_submit_skb(struct devtx_ctx *ctx, struct sk_buff *skb); \ +void PREFIX ## _devtx_complete_skb(struct devtx_ctx *ctx, struct sk_buff *skb); \ +void PREFIX ## _devtx_submit_xdp(struct devtx_ctx *ctx, struct xdp_frame *xdpf); \ +void PREFIX ## _devtx_complete_xdp(struct devtx_ctx *ctx, struct xdp_frame *xdpf) + +#define DEFINE_DEVTX_HOOKS(PREFIX) \ +__weak noinline void PREFIX ## _devtx_submit_skb(struct devtx_ctx *ctx, \ + struct sk_buff *skb) {} \ +__weak noinline void PREFIX ## _devtx_complete_skb(struct devtx_ctx *ctx, \ + struct sk_buff *skb) {} \ +__weak noinline void PREFIX ## _devtx_submit_xdp(struct devtx_ctx *ctx, \ + struct xdp_frame *xdpf) {} \ +__weak noinline void PREFIX ## _devtx_complete_xdp(struct devtx_ctx *ctx, \ + struct xdp_frame *xdpf) {} \ +\ +BTF_SET8_START(PREFIX ## _devtx_hook_ids) \ +BTF_ID_FLAGS(func, PREFIX ## _devtx_submit_skb) \ +BTF_ID_FLAGS(func, PREFIX ## _devtx_complete_skb) \ +BTF_ID_FLAGS(func, PREFIX ## _devtx_submit_xdp) \ +BTF_ID_FLAGS(func, PREFIX ## _devtx_complete_xdp) \ +BTF_SET8_END(PREFIX ## _devtx_hook_ids) + +#ifdef CONFIG_NET +void devtx_hooks_enable(void); +void devtx_hooks_disable(void); +bool devtx_hooks_match(u32 attach_btf_id, const struct xdp_metadata_ops *xmo); +int devtx_hooks_register(struct btf_id_set8 *set, const struct xdp_metadata_ops *xmo); +void devtx_hooks_unregister(struct btf_id_set8 *set); + +DECLARE_STATIC_KEY_FALSE(devtx_enabled_key); + +static inline bool devtx_enabled(void) +{ + return static_branch_unlikely(&devtx_enabled_key); +} +#else +static inline void devtx_hooks_enable(void) {} +static inline void devtx_hooks_disable(void) {} +static inline bool devtx_hooks_match(u32 attach_btf_id, const struct xdp_metadata_ops *xmo) {} +static inline int devtx_hooks_register(struct btf_id_set8 *set, + const struct xdp_metadata_ops *xmo) {} +static inline void devtx_hooks_unregister(struct btf_id_set8 *set) {} + +static inline bool devtx_enabled(void) +{ + return false; +} +#endif + +#endif /* __LINUX_NET_DEVTX_H__ */ diff --git a/kernel/bpf/offload.c b/kernel/bpf/offload.c index cec63c76dce5..a4423803b3dd 100644 --- a/kernel/bpf/offload.c +++ b/kernel/bpf/offload.c @@ -25,6 +25,7 @@ #include <linux/rhashtable.h> #include <linux/rtnetlink.h> #include <linux/rwsem.h> +#include <net/devtx.h> /* Protects offdevs, members of bpf_offload_netdev and offload members * of all progs. @@ -228,6 +229,7 @@ int bpf_prog_dev_bound_init(struct bpf_prog *prog, union bpf_attr *attr) int err; if (attr->prog_type != BPF_PROG_TYPE_SCHED_CLS && + attr->prog_type != BPF_PROG_TYPE_TRACING && attr->prog_type != BPF_PROG_TYPE_XDP) return -EINVAL; @@ -242,6 +244,15 @@ int bpf_prog_dev_bound_init(struct bpf_prog *prog, union bpf_attr *attr) if (!netdev) return -EINVAL; + /* Make sure device-bound tracing programs are being attached + * to the appropriate netdev. + */ + if (attr->prog_type == BPF_PROG_TYPE_TRACING && + !devtx_hooks_match(prog->aux->attach_btf_id, netdev->xdp_metadata_ops)) { + err = -EINVAL; + goto out; + } + err = bpf_dev_offload_check(netdev); if (err) goto out; @@ -252,6 +263,9 @@ int bpf_prog_dev_bound_init(struct bpf_prog *prog, union bpf_attr *attr) err = __bpf_prog_dev_bound_init(prog, netdev); up_write(&bpf_devs_lock); + if (!err) + devtx_hooks_enable(); + out: dev_put(netdev); return err; @@ -384,6 +398,7 @@ void bpf_prog_dev_bound_destroy(struct bpf_prog *prog) ondev = bpf_offload_find_netdev(netdev); if (!ondev->offdev && list_empty(&ondev->progs)) __bpf_offload_dev_netdev_unregister(NULL, netdev); + devtx_hooks_disable(); } up_write(&bpf_devs_lock); rtnl_unlock(); diff --git a/net/core/Makefile b/net/core/Makefile index 731db2eaa610..97b4d6703a77 100644 --- a/net/core/Makefile +++ b/net/core/Makefile @@ -39,4 +39,5 @@ obj-$(CONFIG_FAILOVER) += failover.o obj-$(CONFIG_NET_SOCK_MSG) += skmsg.o obj-$(CONFIG_BPF_SYSCALL) += sock_map.o obj-$(CONFIG_BPF_SYSCALL) += bpf_sk_storage.o +obj-$(CONFIG_BPF_SYSCALL) += devtx.o obj-$(CONFIG_OF) += of_net.o diff --git a/net/core/dev.c b/net/core/dev.c index 69a3e544676c..b9500a722591 100644 --- a/net/core/dev.c +++ b/net/core/dev.c @@ -150,6 +150,7 @@ #include <linux/pm_runtime.h> #include <linux/prandom.h> #include <linux/once_lite.h> +#include <net/devtx.h> #include "dev.h" #include "net-sysfs.h" diff --git a/net/core/devtx.c b/net/core/devtx.c new file mode 100644 index 000000000000..6ae1aecce2c5 --- /dev/null +++ b/net/core/devtx.c @@ -0,0 +1,78 @@ +// SPDX-License-Identifier: GPL-2.0-only + +#include <net/devtx.h> +#include <linux/filter.h> + +DEFINE_STATIC_KEY_FALSE(devtx_enabled_key); +EXPORT_SYMBOL_GPL(devtx_enabled_key); + +struct devtx_hook_entry { + struct list_head devtx_hooks; + struct btf_id_set8 *set; + const struct xdp_metadata_ops *xmo; +}; + +static LIST_HEAD(devtx_hooks); +static DEFINE_MUTEX(devtx_hooks_lock); + +void devtx_hooks_enable(void) +{ + static_branch_inc(&devtx_enabled_key); +} + +void devtx_hooks_disable(void) +{ + static_branch_dec(&devtx_enabled_key); +} + +bool devtx_hooks_match(u32 attach_btf_id, const struct xdp_metadata_ops *xmo) +{ + struct devtx_hook_entry *entry, *tmp; + bool match = false; + + mutex_lock(&devtx_hooks_lock); + list_for_each_entry_safe(entry, tmp, &devtx_hooks, devtx_hooks) { + if (btf_id_set8_contains(entry->set, attach_btf_id)) { + match = entry->xmo == xmo; + break; + } + } + mutex_unlock(&devtx_hooks_lock); + + return match; +} + +int devtx_hooks_register(struct btf_id_set8 *set, const struct xdp_metadata_ops *xmo) +{ + struct devtx_hook_entry *entry; + + entry = kzalloc(sizeof(*entry), GFP_KERNEL); + if (!entry) + return -ENOMEM; + + entry->set = set; + entry->xmo = xmo; + + mutex_lock(&devtx_hooks_lock); + list_add(&entry->devtx_hooks, &devtx_hooks); + mutex_unlock(&devtx_hooks_lock); + + return 0; +} +EXPORT_SYMBOL_GPL(devtx_hooks_register); + +void devtx_hooks_unregister(struct btf_id_set8 *set) +{ + struct devtx_hook_entry *entry, *tmp; + + mutex_lock(&devtx_hooks_lock); + list_for_each_entry_safe(entry, tmp, &devtx_hooks, devtx_hooks) { + if (entry->set == set) { + list_del(&entry->devtx_hooks); + kfree(entry); + break; + } + } + mutex_unlock(&devtx_hooks_lock); +} +EXPORT_SYMBOL_GPL(devtx_hooks_unregister); -- 2.41.0.255.g8b1d071c50-goog