Skip to content

Commit 2b3486b

Browse files
fomichevMartin KaFai Lau
authored andcommitted
bpf: Introduce device-bound XDP programs
New flag BPF_F_XDP_DEV_BOUND_ONLY plus all the infra to have a way to associate a netdev with a BPF program at load time. netdevsim checks are dropped in favor of generic check in dev_xdp_attach. Cc: John Fastabend <[email protected]> Cc: David Ahern <[email protected]> Cc: Martin KaFai Lau <[email protected]> Cc: Jakub Kicinski <[email protected]> Cc: Willem de Bruijn <[email protected]> Cc: Jesper Dangaard Brouer <[email protected]> Cc: Anatoly Burakov <[email protected]> Cc: Alexander Lobakin <[email protected]> Cc: Magnus Karlsson <[email protected]> Cc: Maryam Tahhan <[email protected]> Cc: [email protected] Cc: [email protected] Signed-off-by: Stanislav Fomichev <[email protected]> Link: https://lore.kernel.org/r/[email protected] Signed-off-by: Martin KaFai Lau <[email protected]>
1 parent 89bbc53 commit 2b3486b

File tree

8 files changed

+113
-38
lines changed

8 files changed

+113
-38
lines changed

drivers/net/netdevsim/bpf.c

Lines changed: 0 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -315,10 +315,6 @@ nsim_setup_prog_hw_checks(struct netdevsim *ns, struct netdev_bpf *bpf)
315315
NSIM_EA(bpf->extack, "xdpoffload of non-bound program");
316316
return -EINVAL;
317317
}
318-
if (!bpf_offload_dev_match(bpf->prog, ns->netdev)) {
319-
NSIM_EA(bpf->extack, "program bound to different dev");
320-
return -EINVAL;
321-
}
322318

323319
state = bpf->prog->aux->offload->dev_priv;
324320
if (WARN_ON(strcmp(state->state, "xlated"))) {

include/linux/bpf.h

Lines changed: 20 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1261,7 +1261,8 @@ struct bpf_prog_aux {
12611261
enum bpf_prog_type saved_dst_prog_type;
12621262
enum bpf_attach_type saved_dst_attach_type;
12631263
bool verifier_zext; /* Zero extensions has been inserted by verifier. */
1264-
bool offload_requested;
1264+
bool dev_bound; /* Program is bound to the netdev. */
1265+
bool offload_requested; /* Program is bound and offloaded to the netdev. */
12651266
bool attach_btf_trace; /* true if attaching to BTF-enabled raw tp */
12661267
bool func_proto_unreliable;
12671268
bool sleepable;
@@ -2451,7 +2452,7 @@ void __bpf_free_used_maps(struct bpf_prog_aux *aux,
24512452
bool bpf_prog_get_ok(struct bpf_prog *, enum bpf_prog_type *, bool);
24522453

24532454
int bpf_prog_offload_compile(struct bpf_prog *prog);
2454-
void bpf_prog_offload_destroy(struct bpf_prog *prog);
2455+
void bpf_prog_dev_bound_destroy(struct bpf_prog *prog);
24552456
int bpf_prog_offload_info_fill(struct bpf_prog_info *info,
24562457
struct bpf_prog *prog);
24572458

@@ -2479,7 +2480,13 @@ bool bpf_offload_dev_match(struct bpf_prog *prog, struct net_device *netdev);
24792480
void unpriv_ebpf_notify(int new_state);
24802481

24812482
#if defined(CONFIG_NET) && defined(CONFIG_BPF_SYSCALL)
2482-
int bpf_prog_offload_init(struct bpf_prog *prog, union bpf_attr *attr);
2483+
int bpf_prog_dev_bound_init(struct bpf_prog *prog, union bpf_attr *attr);
2484+
void bpf_dev_bound_netdev_unregister(struct net_device *dev);
2485+
2486+
static inline bool bpf_prog_is_dev_bound(const struct bpf_prog_aux *aux)
2487+
{
2488+
return aux->dev_bound;
2489+
}
24832490

24842491
static inline bool bpf_prog_is_offloaded(const struct bpf_prog_aux *aux)
24852492
{
@@ -2507,12 +2514,21 @@ void sock_map_unhash(struct sock *sk);
25072514
void sock_map_destroy(struct sock *sk);
25082515
void sock_map_close(struct sock *sk, long timeout);
25092516
#else
2510-
static inline int bpf_prog_offload_init(struct bpf_prog *prog,
2517+
static inline int bpf_prog_dev_bound_init(struct bpf_prog *prog,
25112518
union bpf_attr *attr)
25122519
{
25132520
return -EOPNOTSUPP;
25142521
}
25152522

2523+
static inline void bpf_dev_bound_netdev_unregister(struct net_device *dev)
2524+
{
2525+
}
2526+
2527+
static inline bool bpf_prog_is_dev_bound(const struct bpf_prog_aux *aux)
2528+
{
2529+
return false;
2530+
}
2531+
25162532
static inline bool bpf_prog_is_offloaded(struct bpf_prog_aux *aux)
25172533
{
25182534
return false;

include/uapi/linux/bpf.h

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1156,6 +1156,11 @@ enum bpf_link_type {
11561156
*/
11571157
#define BPF_F_XDP_HAS_FRAGS (1U << 5)
11581158

1159+
/* If BPF_F_XDP_DEV_BOUND_ONLY is used in BPF_PROG_LOAD command, the loaded
1160+
* program becomes device-bound but can access XDP metadata.
1161+
*/
1162+
#define BPF_F_XDP_DEV_BOUND_ONLY (1U << 6)
1163+
11591164
/* link_create.kprobe_multi.flags used in LINK_CREATE command for
11601165
* BPF_TRACE_KPROBE_MULTI attach type to create return probe.
11611166
*/

kernel/bpf/core.c

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -2553,8 +2553,8 @@ static void bpf_prog_free_deferred(struct work_struct *work)
25532553
#endif
25542554
bpf_free_used_maps(aux);
25552555
bpf_free_used_btfs(aux);
2556-
if (bpf_prog_is_offloaded(aux))
2557-
bpf_prog_offload_destroy(aux->prog);
2556+
if (bpf_prog_is_dev_bound(aux))
2557+
bpf_prog_dev_bound_destroy(aux->prog);
25582558
#ifdef CONFIG_PERF_EVENTS
25592559
if (aux->prog->has_callchain_buf)
25602560
put_callchain_buffers();

kernel/bpf/offload.c

Lines changed: 71 additions & 24 deletions
Original file line numberDiff line numberDiff line change
@@ -41,7 +41,7 @@ struct bpf_offload_dev {
4141
struct bpf_offload_netdev {
4242
struct rhash_head l;
4343
struct net_device *netdev;
44-
struct bpf_offload_dev *offdev;
44+
struct bpf_offload_dev *offdev; /* NULL when bound-only */
4545
struct list_head progs;
4646
struct list_head maps;
4747
struct list_head offdev_netdevs;
@@ -89,19 +89,17 @@ static int __bpf_offload_dev_netdev_register(struct bpf_offload_dev *offdev,
8989
INIT_LIST_HEAD(&ondev->progs);
9090
INIT_LIST_HEAD(&ondev->maps);
9191

92-
down_write(&bpf_devs_lock);
9392
err = rhashtable_insert_fast(&offdevs, &ondev->l, offdevs_params);
9493
if (err) {
9594
netdev_warn(netdev, "failed to register for BPF offload\n");
96-
goto err_unlock_free;
95+
goto err_free;
9796
}
9897

99-
list_add(&ondev->offdev_netdevs, &offdev->netdevs);
100-
up_write(&bpf_devs_lock);
98+
if (offdev)
99+
list_add(&ondev->offdev_netdevs, &offdev->netdevs);
101100
return 0;
102101

103-
err_unlock_free:
104-
up_write(&bpf_devs_lock);
102+
err_free:
105103
kfree(ondev);
106104
return err;
107105
}
@@ -149,24 +147,26 @@ static void __bpf_map_offload_destroy(struct bpf_offloaded_map *offmap)
149147
static void __bpf_offload_dev_netdev_unregister(struct bpf_offload_dev *offdev,
150148
struct net_device *netdev)
151149
{
152-
struct bpf_offload_netdev *ondev, *altdev;
150+
struct bpf_offload_netdev *ondev, *altdev = NULL;
153151
struct bpf_offloaded_map *offmap, *mtmp;
154152
struct bpf_prog_offload *offload, *ptmp;
155153

156154
ASSERT_RTNL();
157155

158-
down_write(&bpf_devs_lock);
159156
ondev = rhashtable_lookup_fast(&offdevs, &netdev, offdevs_params);
160157
if (WARN_ON(!ondev))
161-
goto unlock;
158+
return;
162159

163160
WARN_ON(rhashtable_remove_fast(&offdevs, &ondev->l, offdevs_params));
164-
list_del(&ondev->offdev_netdevs);
165161

166162
/* Try to move the objects to another netdev of the device */
167-
altdev = list_first_entry_or_null(&offdev->netdevs,
168-
struct bpf_offload_netdev,
169-
offdev_netdevs);
163+
if (offdev) {
164+
list_del(&ondev->offdev_netdevs);
165+
altdev = list_first_entry_or_null(&offdev->netdevs,
166+
struct bpf_offload_netdev,
167+
offdev_netdevs);
168+
}
169+
170170
if (altdev) {
171171
list_for_each_entry(offload, &ondev->progs, offloads)
172172
offload->netdev = altdev->netdev;
@@ -185,11 +185,9 @@ static void __bpf_offload_dev_netdev_unregister(struct bpf_offload_dev *offdev,
185185
WARN_ON(!list_empty(&ondev->progs));
186186
WARN_ON(!list_empty(&ondev->maps));
187187
kfree(ondev);
188-
unlock:
189-
up_write(&bpf_devs_lock);
190188
}
191189

192-
int bpf_prog_offload_init(struct bpf_prog *prog, union bpf_attr *attr)
190+
int bpf_prog_dev_bound_init(struct bpf_prog *prog, union bpf_attr *attr)
193191
{
194192
struct bpf_offload_netdev *ondev;
195193
struct bpf_prog_offload *offload;
@@ -199,7 +197,11 @@ int bpf_prog_offload_init(struct bpf_prog *prog, union bpf_attr *attr)
199197
attr->prog_type != BPF_PROG_TYPE_XDP)
200198
return -EINVAL;
201199

202-
if (attr->prog_flags)
200+
if (attr->prog_flags & ~BPF_F_XDP_DEV_BOUND_ONLY)
201+
return -EINVAL;
202+
203+
if (attr->prog_type == BPF_PROG_TYPE_SCHED_CLS &&
204+
attr->prog_flags & BPF_F_XDP_DEV_BOUND_ONLY)
203205
return -EINVAL;
204206

205207
offload = kzalloc(sizeof(*offload), GFP_USER);
@@ -214,11 +216,23 @@ int bpf_prog_offload_init(struct bpf_prog *prog, union bpf_attr *attr)
214216
if (err)
215217
goto err_maybe_put;
216218

219+
prog->aux->offload_requested = !(attr->prog_flags & BPF_F_XDP_DEV_BOUND_ONLY);
220+
217221
down_write(&bpf_devs_lock);
218222
ondev = bpf_offload_find_netdev(offload->netdev);
219223
if (!ondev) {
220-
err = -EINVAL;
221-
goto err_unlock;
224+
if (bpf_prog_is_offloaded(prog->aux)) {
225+
err = -EINVAL;
226+
goto err_unlock;
227+
}
228+
229+
/* When only binding to the device, explicitly
230+
* create an entry in the hashtable.
231+
*/
232+
err = __bpf_offload_dev_netdev_register(NULL, offload->netdev);
233+
if (err)
234+
goto err_unlock;
235+
ondev = bpf_offload_find_netdev(offload->netdev);
222236
}
223237
offload->offdev = ondev->offdev;
224238
prog->aux->offload = offload;
@@ -321,12 +335,25 @@ bpf_prog_offload_remove_insns(struct bpf_verifier_env *env, u32 off, u32 cnt)
321335
up_read(&bpf_devs_lock);
322336
}
323337

324-
void bpf_prog_offload_destroy(struct bpf_prog *prog)
338+
void bpf_prog_dev_bound_destroy(struct bpf_prog *prog)
325339
{
340+
struct bpf_offload_netdev *ondev;
341+
struct net_device *netdev;
342+
343+
rtnl_lock();
326344
down_write(&bpf_devs_lock);
327-
if (prog->aux->offload)
345+
if (prog->aux->offload) {
346+
list_del_init(&prog->aux->offload->offloads);
347+
348+
netdev = prog->aux->offload->netdev;
328349
__bpf_prog_offload_destroy(prog);
350+
351+
ondev = bpf_offload_find_netdev(netdev);
352+
if (!ondev->offdev && list_empty(&ondev->progs))
353+
__bpf_offload_dev_netdev_unregister(NULL, netdev);
354+
}
329355
up_write(&bpf_devs_lock);
356+
rtnl_unlock();
330357
}
331358

332359
static int bpf_prog_offload_translate(struct bpf_prog *prog)
@@ -621,7 +648,7 @@ static bool __bpf_offload_dev_match(struct bpf_prog *prog,
621648
struct bpf_offload_netdev *ondev1, *ondev2;
622649
struct bpf_prog_offload *offload;
623650

624-
if (!bpf_prog_is_offloaded(prog->aux))
651+
if (!bpf_prog_is_dev_bound(prog->aux))
625652
return false;
626653

627654
offload = prog->aux->offload;
@@ -667,14 +694,21 @@ bool bpf_offload_prog_map_match(struct bpf_prog *prog, struct bpf_map *map)
667694
int bpf_offload_dev_netdev_register(struct bpf_offload_dev *offdev,
668695
struct net_device *netdev)
669696
{
670-
return __bpf_offload_dev_netdev_register(offdev, netdev);
697+
int err;
698+
699+
down_write(&bpf_devs_lock);
700+
err = __bpf_offload_dev_netdev_register(offdev, netdev);
701+
up_write(&bpf_devs_lock);
702+
return err;
671703
}
672704
EXPORT_SYMBOL_GPL(bpf_offload_dev_netdev_register);
673705

674706
void bpf_offload_dev_netdev_unregister(struct bpf_offload_dev *offdev,
675707
struct net_device *netdev)
676708
{
709+
down_write(&bpf_devs_lock);
677710
__bpf_offload_dev_netdev_unregister(offdev, netdev);
711+
up_write(&bpf_devs_lock);
678712
}
679713
EXPORT_SYMBOL_GPL(bpf_offload_dev_netdev_unregister);
680714

@@ -708,6 +742,19 @@ void *bpf_offload_dev_priv(struct bpf_offload_dev *offdev)
708742
}
709743
EXPORT_SYMBOL_GPL(bpf_offload_dev_priv);
710744

745+
void bpf_dev_bound_netdev_unregister(struct net_device *dev)
746+
{
747+
struct bpf_offload_netdev *ondev;
748+
749+
ASSERT_RTNL();
750+
751+
down_write(&bpf_devs_lock);
752+
ondev = bpf_offload_find_netdev(dev);
753+
if (ondev && !ondev->offdev)
754+
__bpf_offload_dev_netdev_unregister(NULL, ondev->netdev);
755+
up_write(&bpf_devs_lock);
756+
}
757+
711758
static int __init bpf_offload_init(void)
712759
{
713760
return rhashtable_init(&offdevs, &offdevs_params);

kernel/bpf/syscall.c

Lines changed: 5 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -2491,7 +2491,8 @@ static int bpf_prog_load(union bpf_attr *attr, bpfptr_t uattr)
24912491
BPF_F_TEST_STATE_FREQ |
24922492
BPF_F_SLEEPABLE |
24932493
BPF_F_TEST_RND_HI32 |
2494-
BPF_F_XDP_HAS_FRAGS))
2494+
BPF_F_XDP_HAS_FRAGS |
2495+
BPF_F_XDP_DEV_BOUND_ONLY))
24952496
return -EINVAL;
24962497

24972498
if (!IS_ENABLED(CONFIG_HAVE_EFFICIENT_UNALIGNED_ACCESS) &&
@@ -2575,7 +2576,7 @@ static int bpf_prog_load(union bpf_attr *attr, bpfptr_t uattr)
25752576
prog->aux->attach_btf = attach_btf;
25762577
prog->aux->attach_btf_id = attr->attach_btf_id;
25772578
prog->aux->dst_prog = dst_prog;
2578-
prog->aux->offload_requested = !!attr->prog_ifindex;
2579+
prog->aux->dev_bound = !!attr->prog_ifindex;
25792580
prog->aux->sleepable = attr->prog_flags & BPF_F_SLEEPABLE;
25802581
prog->aux->xdp_has_frags = attr->prog_flags & BPF_F_XDP_HAS_FRAGS;
25812582

@@ -2598,8 +2599,8 @@ static int bpf_prog_load(union bpf_attr *attr, bpfptr_t uattr)
25982599
atomic64_set(&prog->aux->refcnt, 1);
25992600
prog->gpl_compatible = is_gpl ? 1 : 0;
26002601

2601-
if (bpf_prog_is_offloaded(prog->aux)) {
2602-
err = bpf_prog_offload_init(prog, attr);
2602+
if (bpf_prog_is_dev_bound(prog->aux)) {
2603+
err = bpf_prog_dev_bound_init(prog, attr);
26032604
if (err)
26042605
goto free_prog_sec;
26052606
}

net/core/dev.c

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -9228,6 +9228,10 @@ static int dev_xdp_attach(struct net_device *dev, struct netlink_ext_ack *extack
92289228
NL_SET_ERR_MSG(extack, "Using offloaded program without HW_MODE flag is not supported");
92299229
return -EINVAL;
92309230
}
9231+
if (bpf_prog_is_dev_bound(new_prog->aux) && !bpf_offload_dev_match(new_prog, dev)) {
9232+
NL_SET_ERR_MSG(extack, "Program bound to different device");
9233+
return -EINVAL;
9234+
}
92319235
if (new_prog->expected_attach_type == BPF_XDP_DEVMAP) {
92329236
NL_SET_ERR_MSG(extack, "BPF_XDP_DEVMAP programs can not be attached to a device");
92339237
return -EINVAL;
@@ -10830,6 +10834,7 @@ void unregister_netdevice_many_notify(struct list_head *head,
1083010834
dev_shutdown(dev);
1083110835

1083210836
dev_xdp_uninstall(dev);
10837+
bpf_dev_bound_netdev_unregister(dev);
1083310838

1083410839
netdev_offload_xstats_disable_all(dev);
1083510840

tools/include/uapi/linux/bpf.h

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1156,6 +1156,11 @@ enum bpf_link_type {
11561156
*/
11571157
#define BPF_F_XDP_HAS_FRAGS (1U << 5)
11581158

1159+
/* If BPF_F_XDP_DEV_BOUND_ONLY is used in BPF_PROG_LOAD command, the loaded
1160+
* program becomes device-bound but can access XDP metadata.
1161+
*/
1162+
#define BPF_F_XDP_DEV_BOUND_ONLY (1U << 6)
1163+
11591164
/* link_create.kprobe_multi.flags used in LINK_CREATE command for
11601165
* BPF_TRACE_KPROBE_MULTI attach type to create return probe.
11611166
*/

0 commit comments

Comments
 (0)