Add BPF_SYNCHRONIZE_MAPS bpf(2) command
Subsystems (5)
Quoted 18 lines · Daniel Colascione
BPF_SYNCHRONIZE_MAPSwaits for the release of any references to a BPF map made by a BPF program that is running at the time theBPF_SYNCHRONIZE_MAPScommand is issued. The purpose of this command is to provide a means for userspace to replace a BPF map with another, newer version, then ensure that no component is still using the "old" map before manipulating the "old" map in some way.Signed-off-by Daniel Colascione <[email protected]>--- include/uapi/linux/bpf.h | 9 +++++++++ kernel/bpf/syscall.c | 13 +++++++++++++ 2 files changed, 22 insertions(+)include/uapi/linux/bpf.h +0
-0
include/uapi/linux/bpf.h struct bpf_lpm_trie_key { · line 80
Quoted 6 lines
include/uapi/linux/bpf.h +3
-0@@ -75,6 +75,14 @@ struct bpf_lpm_trie_key {__u8 data[0 ];/* Arbitrary size */ }; +/* BPF_SYNCHRONIZE_MAPS waits for the release of any references to a +* BPF map made by a BPF program that is running at the time the +* BPF_SYNCHRONIZE_MAPS command is issued. The purpose of this command
that doesn't sound right to me.
such command won't wait for the release of the references. in case of map-in-map the program does not hold the references to inner map (only to outer map).
include/uapi/linux/bpf.h struct bpf_lpm_trie_key { · line 84
Quoted 4 lines
include/uapi/linux/bpf.h +4
-0+* is to provide a means for userspace to replace a BPF map with +* another, newer version, then ensure that no component is still +* using the "old" map before manipulating the "old" map in some way. +*/
that's correct, but the whole paragraph still reads too 'generic' which will lead to confusion, whereas the use case is map-in-map only.
I think bpf_sync_inner_map or bpf_sync_map_in_map would be better
choices for the name.
btw i don't have reliable internet access at the moment, so pls excuse the delays.
Quoted 23 lines · Alexei Starovoitov
Quoted 21 lines · Daniel Colascione
BPF_SYNCHRONIZE_MAPSwaits for the release of any references to a BPF map made by a BPF program that is running at the time theBPF_SYNCHRONIZE_MAPScommand is issued. The purpose of this command is to provide a means for userspace to replace a BPF map with another, newer version, then ensure that no component is still using the "old" map before manipulating the "old" map in some way.Signed-off-by Daniel Colascione <[email protected]>--- include/uapi/linux/bpf.h | 9 +++++++++ kernel/bpf/syscall.c | 13 +++++++++++++ 2 files changed, 22 insertions(+)include/uapi/linux/bpf.h +0
-0@@ -75,6 +75,14 @@ struct bpf_lpm_trie_key {__u8 data[0 ];/* Arbitrary size */ };
Quoted 9 lines
include/uapi/linux/bpf.h struct bpf_lpm_trie_key { · line 80
Quoted 3 lines
include/uapi/linux/bpf.h +3
-0+/* BPF_SYNCHRONIZE_MAPS waits for the release of any references to a +* BPF map made by a BPF program that is running at the time the +* BPF_SYNCHRONIZE_MAPS command is issued. The purpose of this command that doesn't sound right to me.
such command won't wait for the release of the references. in case of map-in-map the program does not hold the references to inner map (only to outer map).
Well, today that's the guarantee it provides. :-) I figured it'd be simpler to explain the guarantee this way.
How about "waits for the release of any reference to a map obtained from another map"?
Quoted 6 lines
include/uapi/linux/bpf.h struct bpf_lpm_trie_key { · line 84
Quoted 5 lines
include/uapi/linux/bpf.h +4
-0+* is to provide a means for userspace to replace a BPF map with +* another, newer version, then ensure that no component is still +* using the "old" map before manipulating the "old" map in some way. +*/
Quoted 6 lines
that's correct, but the whole paragraph still reads too 'generic' which will lead to confusion, whereas the use case is map-in-map only.
I thinkbpf_sync_inner_maporbpf_sync_map_in_mapwould be better
choices for the name.
I worry that a name like that would be too specific.
PATCH v3 1/1 Add BPF_SYNCHRONIZE_MAP_TO_MAP_REFERENCES bpf(2) command
BPF_SYNCHRONIZE_MAP_TO_MAP_REFERENCES waits for the release of all references to maps active at the instant the BPF_SYNCHRONIZE_MAP_TO_MAP_REFERENCES is
issued. BPF_SYNCHRONIZE_MAP_TO_MAP_REFERENCES waits only for the expiration of map references obtained by BPF programs from other maps.
The purpose of this command is to provide a means for userspace to replace a BPF map with another, newer version, then ensure that no component is still using the "old" map before manipulating the "old" map in some way.
--- include/uapi/linux/bpf.h | 14 ++++++++++++++ kernel/bpf/syscall.c | 13 +++++++++++++ 2 files changed, 27 insertions(+)
include/uapi/linux/bpf.h +14 -0
@@ -75,6 +75,19 @@ struct bpf_lpm_trie_key {__u8 data[0 ];/* Arbitrary size */ }; +/* BPF_SYNCHRONIZE_MAP_TO_MAP_REFERENCES waits for the release of all +* references to maps active at the instant the +* BPF_SYNCHRONIZE_MAP_TO_MAP_REFERENCES is +* issued. BPF_SYNCHRONIZE_MAP_TO_MAP_REFERENCES waits only for the +* expiration of map references obtained by BPF programs from +* other maps. +* +* The purpose of this command is to provide a means for userspace to +* replace a BPF map with another, newer version, then ensure that no +* component is still using the "old" map before manipulating the +* "old" map in some way. +*/ +/* BPF syscall commands, see bpf(2) man-page for details. */ enum bpf_cmd {BPF_MAP_CREATE ,
@@ -98,6 +111,7 @@ enum bpf_cmd {BPF_BTF_LOAD ,BPF_BTF_GET_FD_BY_ID ,BPF_TASK_FD_QUERY , + BPF_SYNCHRONIZE_MAP_TO_MAP_REFERENCES, }; enum bpf_map_type {
kernel/bpf/syscall.c +13 -0
@@ -2274,6 +2274,19 @@ SYSCALL_DEFINE3(bpf, int, cmd, union bpf_attr __user *, uattr, unsigned int, sizif (sysctl_unprivileged_bpf_disabled && !capable (CAP_SYS_ADMIN ))return -EPERM ; +if (cmd ==BPF_SYNCHRONIZE_MAP_TO_MAP_REFERENCES ) { +if (uattr !=NULL || size !=0 ) +return -EINVAL ; + err =security_bpf (cmd,NULL ,0 ); +if (err <0 ) +return err; +/* BPF programs always enter a critical section while +* they have a map reference outstanding. +*/ +synchronize_rcu (); +return 0 ; + } + err =bpf_check_uarg_tail_zero (uattr,sizeof (attr), size);if (err)return err;
Signature · 1 line
2.18.0.345.g5c9ce644c3-goog
Daniel Borkmann
maintainerRe: [PATCH v3] Add BPF_SYNCHRONIZE_MAP_TO_MAP_REFERENCES bpf(2) command
Quoted 64 lines · Daniel Colascione
BPF_SYNCHRONIZE_MAP_TO_MAP_REFERENCESwaits for the release of all references to maps active at the instant theBPF_SYNCHRONIZE_MAP_TO_MAP_REFERENCESis
issued.BPF_SYNCHRONIZE_MAP_TO_MAP_REFERENCESwaits only for the expiration of map references obtained by BPF programs from other maps.The purpose of this command is to provide a means for userspace to replace a BPF map with another, newer version, then ensure that no component is still using the "old" map before manipulating the "old" map in some way.
Signed-off-by Daniel Colascione <[email protected]>--- include/uapi/linux/bpf.h | 14 ++++++++++++++ kernel/bpf/syscall.c | 13 +++++++++++++ 2 files changed, 27 insertions(+)include/uapi/linux/bpf.h +14
-0@@ -75,6 +75,19 @@ struct bpf_lpm_trie_key {__u8 data[0 ];/* Arbitrary size */ }; +/* BPF_SYNCHRONIZE_MAP_TO_MAP_REFERENCES waits for the release of all +* references to maps active at the instant the +* BPF_SYNCHRONIZE_MAP_TO_MAP_REFERENCES is +* issued. BPF_SYNCHRONIZE_MAP_TO_MAP_REFERENCES waits only for the +* expiration of map references obtained by BPF programs from +* other maps. +* +* The purpose of this command is to provide a means for userspace to +* replace a BPF map with another, newer version, then ensure that no +* component is still using the "old" map before manipulating the +* "old" map in some way. +*/ +/* BPF syscall commands, see bpf(2) man-page for details. */ enum bpf_cmd {BPF_MAP_CREATE ,@@ -98,6 +111,7 @@ enum bpf_cmd {BPF_BTF_LOAD ,BPF_BTF_GET_FD_BY_ID ,BPF_TASK_FD_QUERY , + BPF_SYNCHRONIZE_MAP_TO_MAP_REFERENCES, }; enum bpf_map_type {kernel/bpf/syscall.c +6
-0@@ -2274,6 +2274,19 @@ SYSCALL_DEFINE3(bpf, int, cmd, union bpf_attr __user *, uattr, unsigned int, sizif (sysctl_unprivileged_bpf_disabled && !capable (CAP_SYS_ADMIN ))return -EPERM ; +if (cmd ==BPF_SYNCHRONIZE_MAP_TO_MAP_REFERENCES ) { +if (uattr !=NULL || size !=0 ) +return -EINVAL ; + err =security_bpf (cmd,NULL ,0 ); +if (err <0 ) +return err;
kernel/bpf/syscall.c SYSCALL_DEFINE3(bpf, int, cmd, union bpf_attr __user *, uattr, unsigned int, siz · line 2292
Quoted 10 lines
kernel/bpf/syscall.c +7
-0+/* BPF programs always enter a critical section while +* they have a map reference outstanding. +*/ +synchronize_rcu (); +return 0 ; + } + err =bpf_check_uarg_tail_zero (uattr,sizeof (attr), size);if (err)return err;
Hmm, I don't think such UAPI as above is future-proof. In case we would want a similar mechanism in future for other maps, we would need a whole new bpf command or reuse BPF_SYNCHRONIZE_MAP_TO_MAP_REFERENCES as a workaround though the underlying map may not even be a map-to-map. Additionally, we don't have any map object at hand in the above, so we couldn't make any finer grained decisions either. Something like below would be more suitable and leaves room for extending this further in future.
Thanks,
Daniel
Quoted 1 line
From
8dfea71b73fa0d402633b76f78c106e82a7a5007Mon Sep 17 00:00:00 2001
From: Daniel Borkmann <[email protected]>
Date: Mon, 30 Jul 2018 11:47:37 +0200
Subject: [PATCH] sync map refs
--- include/linux/bpf.h | 1 + include/uapi/linux/bpf.h | 1 + kernel/bpf/arraymap.c | 1 + kernel/bpf/hashtab.c | 1 + kernel/bpf/map_in_map.c | 6 ++++++ kernel/bpf/map_in_map.h | 1 + kernel/bpf/syscall.c | 24 ++++++++++++++++++++++++ 7 files changed, 35 insertions(+)
include/linux/bpf.h +1 -0
@@ -34,6 +34,7 @@ struct bpf_map_ops {void (*map_free)(struct bpf_map *map);int (*map_get_next_key)(struct bpf_map *map,void *key,void *next_key);void (*map_release_uref)(struct bpf_map *map); +int (*map_sync_refs)(struct bpf_map *map);/* funcs callable from userspace and from eBPF programs */ void *(*map_lookup_elem)(struct bpf_map *map,void *key);
include/uapi/linux/bpf.h +1 -0
@@ -98,6 +98,7 @@ enum bpf_cmd {BPF_BTF_LOAD ,BPF_BTF_GET_FD_BY_ID ,BPF_TASK_FD_QUERY , + BPF_MAP_SYNC_REFS, }; enum bpf_map_type {
kernel/bpf/arraymap.c +1 -0
@@ -748,5 +748,6 @@ const struct bpf_map_ops array_of_maps_map_ops = { .map_fd_get_ptr = bpf_map_fd_get_ptr, .map_fd_put_ptr = bpf_map_fd_put_ptr, .map_fd_sys_lookup_elem = bpf_map_fd_sys_lookup_elem, + .map_sync_refs = bpf_map_sync_refs, .map_gen_lookup = array_of_map_gen_lookup, };
kernel/bpf/hashtab.c +1 -0
@@ -1407,5 +1407,6 @@ const struct bpf_map_ops htab_of_maps_map_ops = { .map_fd_get_ptr = bpf_map_fd_get_ptr, .map_fd_put_ptr = bpf_map_fd_put_ptr, .map_fd_sys_lookup_elem = bpf_map_fd_sys_lookup_elem, + .map_sync_refs = bpf_map_sync_refs, .map_gen_lookup = htab_of_map_gen_lookup, };
kernel/bpf/map_in_map.c +6 -0
@@ -96,6 +96,12 @@ void bpf_map_fd_put_ptr(void *ptr)bpf_map_put (ptr); } +int bpf_map_sync_refs (struct bpf_map *map) +{ +synchronize_rcu (); +return 0 ; +} +u32 bpf_map_fd_sys_lookup_elem (void *ptr) {return ((struct bpf_map *)ptr)->id;
kernel/bpf/map_in_map.h +1 -0
@@ -19,6 +19,7 @@ bool bpf_map_meta_equal(const struct bpf_map *meta0,void *bpf_map_fd_get_ptr (struct bpf_map *map,struct file *map_file,int ufd);void bpf_map_fd_put_ptr (void *ptr); +int bpf_map_sync_refs (struct bpf_map *map);u32 bpf_map_fd_sys_lookup_elem (void *ptr);#endif
kernel/bpf/syscall.c +24 -0
@@ -896,6 +896,27 @@ static int map_get_next_key(union bpf_attr *attr)return err; } +#define BPF_MAP_SYNC_REFS_LAST_FIELD map_fd + +static int map_sync_refs (union bpf_attr *attr) +{ +int err = -ENOTSUPP , ufd = attr->map_fd; +struct bpf_map *map; +struct fd f; + +if (CHECK_ATTR (BPF_MAP_SYNC_REFS )) +return -EINVAL ; + + f =fdget (ufd); + map =__bpf_map_get (f); +if (IS_ERR (map)) +return PTR_ERR (map); +if (map->ops->map_sync_refs) + err = map->ops->map_sync_refs (map); +fdput (f); +return err; +} +static const struct bpf_prog_ops *const bpf_prog_types[] = { #define BPF_PROG_TYPE(_id, _name) \ [_id] = & _name ## _prog_ops,
@@ -2303,6 +2324,9 @@ SYSCALL_DEFINE3(bpf, int, cmd, union bpf_attr __user *, uattr, unsigned int, sizcase BPF_MAP_GET_NEXT_KEY : err =map_get_next_key (&attr);break ; +case BPF_MAP_SYNC_REFS : + err =map_sync_refs (&attr); +break ;case BPF_PROG_LOAD : err =bpf_prog_load (&attr);break ;
Signature · 1 line
2.9.5
Re: [PATCH v3] Add BPF_SYNCHRONIZE_MAP_TO_MAP_REFERENCES bpf(2) command
Quoted 7 lines · Daniel Borkmann
Hmm, I don't think such UAPI as above is future-proof. In case we would want a similar mechanism in future for other maps, we would need a whole new bpf command or reuse
BPF_SYNCHRONIZE_MAP_TO_MAP_REFERENCESas a workaround though the underlying map may not even be a map-to-map. Additionally, we don't have any map object at hand in the above, so we couldn't make any finer grained decisions either. Something like below would be more suitable and leaves room for extending this further in future.
YAGNI. Your proposed mechanism doesn't add anything under the current implementation. It's also not clear how a map-specific synchronization command is supposed to work in cases where we swap multiple map references. Do we synchronize_rcu multiple times? Why would we impose that inefficiency just for the sake of some non-specific future extensibility? Add some kind of batching layer? The current approach works for the anticipated use cases.
While my preference is not to talk about map-to-maps at all in the user API and instead spec the thing as talking about map references in general, I'd rather have something that talks about references-to-maps-acquired-from-maps than this interface.
Re: [PATCH v3] Add BPF_SYNCHRONIZE_MAP_TO_MAP_REFERENCES bpf(2) command
Quoted 11 lines · Daniel Colascione
Quoted 7 lines · Daniel Borkmann
Hmm, I don't think such UAPI as above is future-proof. In case we would want a similar mechanism in future for other maps, we would need a whole new bpf command or reuse
BPF_SYNCHRONIZE_MAP_TO_MAP_REFERENCESas a workaround though the underlying map may not even be a map-to-map. Additionally, we don't have any map object at hand in the above, so we couldn't make any finer grained decisions either. Something like below would be more suitable and leaves room for extending this further in future.YAGNI. Your proposed mechanism doesn't add anything under the current implementation.
FWIW in case of HW offload targeting a particular map may allow users to avoid a potentially slow sync with all the devices on the system.
Quoted 11 lines
It's also not clear how a map-specific synchronization command is supposed to work in cases where we swap multiple map references. Do we
synchronize_rcumultiple times? Why would we impose that inefficiency just for the sake of some non-specific future extensibility? Add some kind of batching layer? The current approach works for the anticipated use cases.While my preference is not to talk about map-to-maps at all in the user API and instead spec the thing as talking about map references in general, I'd rather have something that talks about references-to-maps-acquired-from-maps than this interface.
Re: [PATCH v3] Add BPF_SYNCHRONIZE_MAP_TO_MAP_REFERENCES bpf(2) command
Quoted 11 lines · Jakub Kicinski
Quoted 9 lines · Daniel Colascione
Quoted 7 lines · Daniel Borkmann
Hmm, I don't think such UAPI as above is future-proof. In case we would want a similar mechanism in future for other maps, we would need a whole new bpf command or reuse
BPF_SYNCHRONIZE_MAP_TO_MAP_REFERENCESas a workaround though the underlying map may not even be a map-to-map. Additionally, we don't have any map object at hand in the above, so we couldn't make any finer grained decisions either. Something like below would be more suitable and leaves room for extending this further in future.
Quoted 5 lines
Quoted 2 lines
YAGNI. Your proposed mechanism doesn't add anything under the current implementation.
FWIW in case of HW offload targeting a particular map may allow users to avoid a potentially slow sync with all the devices on the system.
Sure. But such a thing doesn't exist right now (right?), and we can add that more-efficient-in-that-one-case BPF interface when it lands. I'd rather keep things simple for now.
Re: [PATCH v3] Add BPF_SYNCHRONIZE_MAP_TO_MAP_REFERENCES bpf(2) command
Quoted 14 lines · Daniel Colascione
Quoted 13 lines · Jakub Kicinski
Quoted 11 lines · Daniel Colascione
Quoted 7 lines · Daniel Borkmann
Hmm, I don't think such UAPI as above is future-proof. In case we would want a similar mechanism in future for other maps, we would need a whole new bpf command or reuse
BPF_SYNCHRONIZE_MAP_TO_MAP_REFERENCESas a workaround though the underlying map may not even be a map-to-map. Additionally, we don't have any map object at hand in the above, so we couldn't make any finer grained decisions either. Something like below would be more suitable and leaves room for extending this further in future.YAGNI. Your proposed mechanism doesn't add anything under the current implementation.
Quoted 6 lines
Quoted 2 lines
FWIW in case of HW offload targeting a particular map may allow users to avoid a potentially slow sync with all the devices on the system.
Sure. But such a thing doesn't exist right now (right?), and we can add that more-efficient-in-that-one-case BPF interface when it lands. I'd rather keep things simple for now.
You mean map-in-map offload doesn't exist today? True, but it's on the roadmap for Netronome.
Tangentially it would be really useful for us to have a "the map has actually been freed" notification/barrier. We have complaints of users creating huge maps and then trying to free and recreate them quickly, and the creation part failing with -ENOMEM, because the free from a workqueue didn't run, yet :( It'd probably require a different API to solve than what's discussed here, but since we're talking about syncing things I thought I'd put it out there...
Re: [PATCH v3] Add BPF_SYNCHRONIZE_MAP_TO_MAP_REFERENCES bpf(2) command
Quoted 18 lines · Jakub Kicinski
Quoted 17 lines · Daniel Colascione
Quoted 15 lines · Jakub Kicinski
Quoted 11 lines · Daniel Colascione
Quoted 7 lines · Daniel Borkmann
Hmm, I don't think such UAPI as above is future-proof. In case we would want a similar mechanism in future for other maps, we would need a whole new bpf command or reuse
BPF_SYNCHRONIZE_MAP_TO_MAP_REFERENCESas a workaround though the underlying map may not even be a map-to-map. Additionally, we don't have any map object at hand in the above, so we couldn't make any finer grained decisions either. Something like below would be more suitable and leaves room for extending this further in future.YAGNI. Your proposed mechanism doesn't add anything under the current implementation.
FWIW in case of HW offload targeting a particular map may allow users to avoid a potentially slow sync with all the devices on the system.
Quoted 6 lines
Quoted 3 lines
Sure. But such a thing doesn't exist right now (right?), and we can add that more-efficient-in-that-one-case BPF interface when it lands. I'd rather keep things simple for now.
You mean map-in-map offload doesn't exist today? True, but it's on the roadmap for Netronome.
Sounds cool. I'd still wait until map-in-map offload lands until adding the map-specific API though.
Quoted 7 lines
Tangentially it would be really useful for us to have a "the map has actually been freed" notification/barrier. We have complaints of users creating huge maps and then trying to free and recreate them quickly, and the creation part failing with
-ENOMEM, because the free from a workqueue didn't run, yet :( It'd probably require a different API to solve than what's discussed here, but since we're talking about syncing things I thought I'd put it out there...
Yeah. What you're talking about is what I meant upthread when I briefly mentioned a "refcount draining approach" --- you'd block until all references except your own to a map disappeared.
Re: [PATCH v3] Add BPF_SYNCHRONIZE_MAP_TO_MAP_REFERENCES bpf(2) command
Quoted 11 lines · Daniel Colascione
Quoted 7 lines
Tangentially it would be really useful for us to have a "the map has actually been freed" notification/barrier. We have complaints of users creating huge maps and then trying to free and recreate them quickly, and the creation part failing with
-ENOMEM, because the free from a workqueue didn't run, yet :( It'd probably require a different API to solve than what's discussed here, but since we're talking about syncing things I thought I'd put it out there...Yeah. What you're talking about is what I meant upthread when I briefly mentioned a "refcount draining approach" --- you'd block until all references except your own to a map disappeared.
I don't think so. The ref count goes to 0, work gets scheduled to call free, work runs, free gets called, device memory gets freed. Now the memory can be reused. As long as there are any refs we can't free memory, unless we want to make the semantics really ugly.
But as I said, only superficially related to this patch. Perhaps solution will naturally fall out of an API to query the device capabilities and resources, if we ever get there.
Daniel Borkmann
maintainerRe: [PATCH v3] Add BPF_SYNCHRONIZE_MAP_TO_MAP_REFERENCES bpf(2) command
In reply to Daniel ColascioneQuoted 15 lines · Daniel Colascione
Quoted 13 lines · Jakub Kicinski
Quoted 11 lines · Daniel Colascione
Quoted 7 lines · Daniel Borkmann
Hmm, I don't think such UAPI as above is future-proof. In case we would want a similar mechanism in future for other maps, we would need a whole new bpf command or reuse
BPF_SYNCHRONIZE_MAP_TO_MAP_REFERENCESas a workaround though the underlying map may not even be a map-to-map. Additionally, we don't have any map object at hand in the above, so we couldn't make any finer grained decisions either. Something like below would be more suitable and leaves room for extending this further in future.YAGNI. Your proposed mechanism doesn't add anything under the current implementation.
Quoted 6 lines
Quoted 2 lines
FWIW in case of HW offload targeting a particular map may allow users to avoid a potentially slow sync with all the devices on the system.
Sure. But such a thing doesn't exist right now (right?), and we can add that more-efficient-in-that-one-case BPF interface when it lands. I'd rather keep things simple for now.
I don't see a reason why that is even more complicated. An API command name such as BPF_SYNCHRONIZE_MAP_TO_MAP_REFERENCES is simply non-generic, and exposes specific map details (here: map-in-map) into the UAPI whereas it should reside within a specific implementation instead similar to other ops we have for maps. If in future other maps would be added that would have similar mechanisms of inner objects they return to the BPF program, we'll be adding yet another command just for this. Also, union bpf_attr is extensible, e.g. additional members could be added in future whenever needed for this subcommand instead of forcing it to NULL as done here. E.g. having a high-level one like bpf(BPF_MAP_WAIT, { .map_fd = fd }) could later on also cover the use-case from Jakub by adding an 'event' member into union bpf_attr where we could add other map specific wakeups for user space while the current default of 0 would be BPF_MAP_WAIT_PENDING_REFS (or the like). All I'm saying is to keep it generic so it can be extended later.
Re: [PATCH v3] Add BPF_SYNCHRONIZE_MAP_TO_MAP_REFERENCES bpf(2) command
Quoted 16 lines · Daniel Borkmann
Quoted 15 lines · Daniel Colascione
Quoted 13 lines · Jakub Kicinski
Quoted 11 lines · Daniel Colascione
Quoted 7 lines · Daniel Borkmann
Hmm, I don't think such UAPI as above is future-proof. In case we would want a similar mechanism in future for other maps, we would need a whole new bpf command or reuse
BPF_SYNCHRONIZE_MAP_TO_MAP_REFERENCESas a workaround though the underlying map may not even be a map-to-map. Additionally, we don't have any map object at hand in the above, so we couldn't make any finer grained decisions either. Something like below would be more suitable and leaves room for extending this further in future.YAGNI. Your proposed mechanism doesn't add anything under the current implementation.
Quoted 8 lines
Quoted 6 lines
Quoted 2 lines
FWIW in case of HW offload targeting a particular map may allow users to avoid a potentially slow sync with all the devices on the system.
Sure. But such a thing doesn't exist right now (right?), and we can add that more-efficient-in-that-one-case BPF interface when it lands. I'd rather keep things simple for now.
I don't see a reason why that is even more complicated.
Both the API and the implementation are much more complicated in the per-map ops version: just look at the patch size. The size argument isn't necessarily a dealbreaker, but I still don't see what the extra code size and complexity is buying.
Quoted 5 lines
An API command name
such asBPF_SYNCHRONIZE_MAP_TO_MAP_REFERENCESis simply non-generic, and exposes specific map details (here: map-in-map) into the UAPI whereas it should reside within a specific implementation instead similar to other ops we have for maps.
But synchronize isn't conceptually a command that applies to a specific map. It waits on all references. Did you address my point about your proposed map-specific interface requiring redundant synchronize_rcu calls in the case where we swap multiple maps and want to wait for all the references to drain? Under my proposal, you'd just BPF_SYNCHRONIZE_WHATEVER and call schedule_rcu once. Under your proposal, we'd make it a per-map operation, so we'd issue one synchronize_rcu per map.
Quoted 3 lines
If in future other maps would be added that would have similar mechanisms of inner objects they return to the BPF program, we'll be adding yet another command just for this.
And that's why my personal preference is to just calling this thing BPF_SYNCHRONIZE, which I'd define to wait for all such "inner objects". Alexei is the one who asked for the very specific naming, I believe.
Anyway, we have a very simple patch that we could apply today. It addresses a real need, and it doesn't preclude adding something more specific later, when we know we need it. Besides, it's not as if adding a BPF command is particularly expensive.
Quoted 3 lines
Also, union
bpf_attris extensible,
e.g. additional members could be added in future whenever needed for this subcommand instead of forcing it to NULL as done here.
We fail with EINVAL when attr != NULL now, which means that we can safely accept a non-NULL attr-based subcommand later without breaking anyone. The interface is already extensible.
Quoted 2 lines
All I'm saying is to
keep it generic so it can be extended later.
Sure, but no more extensible than it has to be. Prematurely-added extension points tend to cause trouble later.
Re: [PATCH v3] Add BPF_SYNCHRONIZE_MAP_TO_MAP_REFERENCES bpf(2) command
Quoted 7 lines · Daniel Colascione
Quoted 6 lines
An API command name
such asBPF_SYNCHRONIZE_MAP_TO_MAP_REFERENCESis simply non-generic, and exposes specific map details (here: map-in-map) into the UAPI whereas it should reside within a specific implementation instead similar to other ops we have for maps.
Quoted 8 lines
But synchronize isn't conceptually a command that applies to a specific map. It waits on all references. Did you address my point about your proposed map-specific interface requiring redundant
synchronize_rcucalls in the case where we swap multiple maps and want to wait for all the references to drain? Under my proposal, you'd justBPF_SYNCHRONIZE_WHATEVERand callschedule_rcuonce. Under your proposal, we'd make it a per-map operation, so we'd issue onesynchronize_rcuper map.
optimizing for multi-map sync sounds like premature optimization.
In general I'd prefer DanielB proposal to make the sync logic map and fd specific, but before we argue about implementation further let's agree on
the problem we're trying to solve.
I believe the only issue being discussed is user space doesn't know when it's ok to start draining the inner map when it was replaced
by bpf_map_update syscall command with another map, right?
If we agree on that, should bpf_map_update handle it then?
Wouldn't it be much easier to understand and use from user pov?
No new commands to learn. map_update syscall replaced the map
and old map is no longer accessed by the program via this given map-in-map. But if the replaced map is used directly or it sits in some other map-in-map slot the progs can still access it.
My issue with DanielC SYNC cmd that it exposes implementation details and introduces complex 'synchronization' semantics. To majority of the users it won't be obvious what is being synchronized.
My issue with DanielB WAIT_REF map_fd cmd that it needs to wait for all refs to this map to be dropped. I think combination of usercnt and refcnt can answer that, but feels dangerous to sleep potentially forever in a syscall until all prog->map references are gone, though such cmd is useful beyond map-in-map use case.
Re: [PATCH v3] Add BPF_SYNCHRONIZE_MAP_TO_MAP_REFERENCES bpf(2) command
Quoted 17 lines · Alexei Starovoitov
Quoted 7 lines · Daniel Colascione
Quoted 6 lines
An API command name
such asBPF_SYNCHRONIZE_MAP_TO_MAP_REFERENCESis simply non-generic, and exposes specific map details (here: map-in-map) into the UAPI whereas it should reside within a specific implementation instead similar to other ops we have for maps.Quoted 8 lines
But synchronize isn't conceptually a command that applies to a specific map. It waits on all references. Did you address my point about your proposed map-specific interface requiring redundant
synchronize_rcucalls in the case where we swap multiple maps and want to wait for all the references to drain? Under my proposal, you'd justBPF_SYNCHRONIZE_WHATEVERand callschedule_rcuonce. Under your proposal, we'd make it a per-map operation, so we'd issue onesynchronize_rcuper map.
Quoted 1 line
optimizing for multi-map sync sounds like premature optimization.
Maybe, but the per-map proposal is less efficient *and* more complicated! I don't want to spend more code just to go slower.
Quoted 3 lines
I believe the only issue being discussed is user space doesn't know when it's ok to start draining the inner map when it was replaced by
bpf_map_updatesyscall command with another map, right?
Yes.
Quoted 4 lines
If we agree on that, should
bpf_map_updatehandle it then? Wouldn't it be much easier to understand and use from user pov? No new commands to learn.map_updatesyscall replaced the map and old map is no longer accessed by the program via this given map-in-map.
Maybe with a new BPF_SYNCHRONIZE flag for BPF_MAP_UPDATE_ELEM and BPF_MAP_DELETE_ELEM. Otherwise, it seems wrong to make every user of these commands pay for synchronization that only a few will need.
Quoted 4 lines
But if the replaced map is used directly or it sits in some other map-in-map slot the progs can still access it.
My issue with DanielC SYNC cmd that it exposes implementation details
What implementation details? The command semantics are defined entirely in terms of existing user-visible primitives.
Quoted 8 lines
and introduces complex 'synchronization' semantics. To majority of the users it won't be obvious what is being synchronized.
My issue with DanielB
WAIT_REFmap_fdcmd that it needs to wait for all refs to this map to be dropped. I think combination of usercnt and refcnt can answer that, but feels dangerous to sleep potentially forever in a syscall until allprog->mapreferences are gone, though such cmd is useful beyond map-in-map use case.
In what scenarios?
In any case, can we submit _something_?
Re: [PATCH v3] Add BPF_SYNCHRONIZE_MAP_TO_MAP_REFERENCES bpf(2) command
Quoted 9 lines · Daniel Colascione
Quoted 5 lines
If we agree on that, should
bpf_map_updatehandle it then? Wouldn't it be much easier to understand and use from user pov? No new commands to learn.map_updatesyscall replaced the map and old map is no longer accessed by the program via this given map-in-map.Maybe with a new
BPF_SYNCHRONIZEflag forBPF_MAP_UPDATE_ELEMandBPF_MAP_DELETE_ELEM. Otherwise, it seems wrong to make every user of these commands pay for synchronization that only a few will need.
I don't think extra flag is needed. Extra sync_rcu() for map-in-map is useful for all users. I would consider it a bugfix, since users that examine deleted map have this race today and removing the race is always a good thing especially since the cost is small.
PATCH v4 1/1 Wait for running BPF programs when updating map-in-map
The map-in-map frequently serves as a mechanism for atomic snapshotting of state that a BPF program might record. The current implementation is dangerous to use in this way, however, since userspace has no way of knowing when all programs that might have retrieved the "old" value of the map may have completed.
This change ensures that map update operations on map-in-map map types always wait for all references to the old map to drop before returning to userspace.
--- kernel/bpf/syscall.c | 14 ++++++++++++++ 1 file changed, 14 insertions(+)
kernel/bpf/syscall.c +14 -0
@@ -741,6 +741,18 @@ static int map_lookup_elem(union bpf_attr *attr)return err; } +static void maybe_wait_bpf_programs (struct bpf_map *map) +{ +/* Wait for any running BPF programs to complete so that +* userspace, when we return to it, knows that all programs +* that could be running use the new map value. +*/ +if (map->map_type ==BPF_MAP_TYPE_HASH_OF_MAPS || + map->map_type ==BPF_MAP_TYPE_ARRAY_OF_MAPS ) { +synchronize_rcu (); + } +} +#define BPF_MAP_UPDATE_ELEM_LAST_FIELD flagsstatic int map_update_elem (union bpf_attr *attr)
@@ -831,6 +843,7 @@ static int map_update_elem(union bpf_attr *attr) }__this_cpu_dec (bpf_prog_active);preempt_enable (); +maybe_wait_bpf_programs (map); out: free_value:kfree (value);
@@ -883,6 +896,7 @@ static int map_delete_elem(union bpf_attr *attr)rcu_read_unlock ();__this_cpu_dec (bpf_prog_active);preempt_enable (); +maybe_wait_bpf_programs (map); out:kfree (key); err_put:
Signature · 1 line
2.19.0.605.g01d371f741-goog
Re: [PATCH v4] Wait for running BPF programs when updating map-in-map
Quoted 47 lines · Daniel Colascione
The map-in-map frequently serves as a mechanism for atomic snapshotting of state that a BPF program might record. The current implementation is dangerous to use in this way, however, since userspace has no way of knowing when all programs that might have retrieved the "old" value of the map may have completed.
This change ensures that map update operations on map-in-map map types always wait for all references to the old map to drop before returning to userspace.
Signed-off-by Daniel Colascione <[email protected]>--- kernel/bpf/syscall.c | 14 ++++++++++++++ 1 file changed, 14 insertions(+)kernel/bpf/syscall.c +13
-0@@ -741,6 +741,18 @@ static int map_lookup_elem(union bpf_attr *attr)return err; } +static void maybe_wait_bpf_programs (struct bpf_map *map) +{ +/* Wait for any running BPF programs to complete so that +* userspace, when we return to it, knows that all programs +* that could be running use the new map value. +*/ +if (map->map_type ==BPF_MAP_TYPE_HASH_OF_MAPS || + map->map_type ==BPF_MAP_TYPE_ARRAY_OF_MAPS ) { +synchronize_rcu (); + } +} +#define BPF_MAP_UPDATE_ELEM_LAST_FIELD flagsstatic int map_update_elem (union bpf_attr *attr)@@ -831,6 +843,7 @@ static int map_update_elem(union bpf_attr *attr) }__this_cpu_dec (bpf_prog_active);preempt_enable (); +maybe_wait_bpf_programs (map); out: free_value:kfree (value);
kernel/bpf/syscall.c static int map_delete_elem(union bpf_attr *attr) · line 899
Quoted 4 lines
kernel/bpf/syscall.c +1
-0@@ -883,6 +896,7 @@ static int map_delete_elem(union bpf_attr *attr)rcu_read_unlock ();__this_cpu_dec (bpf_prog_active);preempt_enable (); +maybe_wait_bpf_programs (map);
Looks good to me,
Also I believe that those rcu_read_lock() and rcu_read_unlock() calls in the existing code are useless. preempt_disable()d code is already an RCU read-side section, and synchronize_rcu and friends work on those type of read-side sections as well (as of recent kernel releases) however removing it may make lockdep unhappy, unless we also replace all rcu_dereference() usages with rcu_dereference_sched(), so lets leave that alone for now I guess.
thanks,
- Joel
Re: [PATCH v4] Wait for running BPF programs when updating map-in-map
In reply to Daniel ColascioneQuoted 23 lines · Daniel Colascione
The map-in-map frequently serves as a mechanism for atomic snapshotting of state that a BPF program might record. The current implementation is dangerous to use in this way, however, since userspace has no way of knowing when all programs that might have retrieved the "old" value of the map may have completed.
This change ensures that map update operations on map-in-map map types always wait for all references to the old map to drop before returning to userspace.
Signed-off-by Daniel Colascione <[email protected]>--- kernel/bpf/syscall.c | 14 ++++++++++++++ 1 file changed, 14 insertions(+)kernel/bpf/syscall.c +0
-0@@ -741,6 +741,18 @@ static int map_lookup_elem(union bpf_attr *attr)return err; }
kernel/bpf/syscall.c static int map_lookup_elem(union bpf_attr *attr) · line 753
Quoted 10 lines
kernel/bpf/syscall.c +10
-0+static void maybe_wait_bpf_programs (struct bpf_map *map) +{ +/* Wait for any running BPF programs to complete so that +* userspace, when we return to it, knows that all programs +* that could be running use the new map value. +*/ +if (map->map_type ==BPF_MAP_TYPE_HASH_OF_MAPS || + map->map_type ==BPF_MAP_TYPE_ARRAY_OF_MAPS ) { +synchronize_rcu (); + }
extra {} were not necessary. I removed them while applying to bpf-next. Please run checkpatch.pl next time.
Thanks
Re: [PATCH v4] Wait for running BPF programs when updating map-in-map
Quoted 24 lines · Alexei Starovoitov
Quoted 23 lines · Daniel Colascione
The map-in-map frequently serves as a mechanism for atomic snapshotting of state that a BPF program might record. The current implementation is dangerous to use in this way, however, since userspace has no way of knowing when all programs that might have retrieved the "old" value of the map may have completed.
This change ensures that map update operations on map-in-map map types always wait for all references to the old map to drop before returning to userspace.
Signed-off-by Daniel Colascione <[email protected]>--- kernel/bpf/syscall.c | 14 ++++++++++++++ 1 file changed, 14 insertions(+)kernel/bpf/syscall.c +0
-0@@ -741,6 +741,18 @@ static int map_lookup_elem(union bpf_attr *attr)return err; }
Quoted 14 lines
kernel/bpf/syscall.c static int map_lookup_elem(union bpf_attr *attr) · line 753
Quoted 10 lines
kernel/bpf/syscall.c +10
-0+static void maybe_wait_bpf_programs (struct bpf_map *map) +{ +/* Wait for any running BPF programs to complete so that +* userspace, when we return to it, knows that all programs +* that could be running use the new map value. +*/ +if (map->map_type ==BPF_MAP_TYPE_HASH_OF_MAPS || + map->map_type ==BPF_MAP_TYPE_ARRAY_OF_MAPS ) { +synchronize_rcu (); + }extra {} were not necessary. I removed them while applying to bpf-next. Please run checkpatch.pl next time.
Thanks
Thanks Alexei for taking it. Me and Lorenzo were discussing that not having this causes incorrect behavior for apps using map-in-map for this. So I CC'd stable as well.
-Joel
Re: [PATCH v4] Wait for running BPF programs when updating map-in-map
Quoted 41 lines · Joel Fernandes
Quoted 24 lines · Alexei Starovoitov
Quoted 23 lines · Daniel Colascione
The map-in-map frequently serves as a mechanism for atomic snapshotting of state that a BPF program might record. The current implementation is dangerous to use in this way, however, since userspace has no way of knowing when all programs that might have retrieved the "old" value of the map may have completed.
This change ensures that map update operations on map-in-map map types always wait for all references to the old map to drop before returning to userspace.
Signed-off-by Daniel Colascione <[email protected]>--- kernel/bpf/syscall.c | 14 ++++++++++++++ 1 file changed, 14 insertions(+)kernel/bpf/syscall.c +0
-0@@ -741,6 +741,18 @@ static int map_lookup_elem(union bpf_attr *attr)return err; }Quoted 14 lines
kernel/bpf/syscall.c static int map_lookup_elem(union bpf_attr *attr) · line 753
Quoted 10 lines
kernel/bpf/syscall.c +10
-0+static void maybe_wait_bpf_programs (struct bpf_map *map) +{ +/* Wait for any running BPF programs to complete so that +* userspace, when we return to it, knows that all programs +* that could be running use the new map value. +*/ +if (map->map_type ==BPF_MAP_TYPE_HASH_OF_MAPS || + map->map_type ==BPF_MAP_TYPE_ARRAY_OF_MAPS ) { +synchronize_rcu (); + }extra {} were not necessary. I removed them while applying to bpf-next. Please run checkpatch.pl next time.
Thanks
Quoted 3 lines
Thanks Alexei for taking it. Me and Lorenzo were discussing that not having this causes incorrect behavior for apps using map-in-map for this. So I CC'd stable as well.
It is too late in the release cycle.
We can submit it to stable releases after the merge window.
Re: [PATCH v4] Wait for running BPF programs when updating map-in-map
Quoted 42 lines · Alexei Starovoitov
Quoted 41 lines · Joel Fernandes
Quoted 38 lines · Alexei Starovoitov
Quoted 23 lines · Daniel Colascione
The map-in-map frequently serves as a mechanism for atomic snapshotting of state that a BPF program might record. The current implementation is dangerous to use in this way, however, since userspace has no way of knowing when all programs that might have retrieved the "old" value of the map may have completed.
This change ensures that map update operations on map-in-map map types always wait for all references to the old map to drop before returning to userspace.
Signed-off-by Daniel Colascione <[email protected]>--- kernel/bpf/syscall.c | 14 ++++++++++++++ 1 file changed, 14 insertions(+)kernel/bpf/syscall.c +0
-0@@ -741,6 +741,18 @@ static int map_lookup_elem(union bpf_attr *attr)return err; }kernel/bpf/syscall.c static int map_lookup_elem(union bpf_attr *attr) · line 753
Quoted 10 lines
kernel/bpf/syscall.c +10
-0+static void maybe_wait_bpf_programs (struct bpf_map *map) +{ +/* Wait for any running BPF programs to complete so that +* userspace, when we return to it, knows that all programs +* that could be running use the new map value. +*/ +if (map->map_type ==BPF_MAP_TYPE_HASH_OF_MAPS || + map->map_type ==BPF_MAP_TYPE_ARRAY_OF_MAPS ) { +synchronize_rcu (); + }extra {} were not necessary. I removed them while applying to bpf-next. Please run checkpatch.pl next time.
Thanks
Quoted 7 lines
Quoted 3 lines
Thanks Alexei for taking it. Me and Lorenzo were discussing that not having this causes incorrect behavior for apps using map-in-map for this. So I CC'd stable as well.
It is too late in the release cycle.
We can submit it to stable releases after the merge window.
Sounds good, thanks.
- Joel
Re: [PATCH v4] Wait for running BPF programs when updating map-in-map
Hi netdev,
Could we queue up this patch to stable 4.14 and stable 4.19? I can provide a backport patch if needed. I checked it is a clean cherry-pick for 4.19 but have some minor conflict for 4.14.
Thanks
Chenbo Feng
Quoted 56 lines · Joel Fernandes
Quoted 42 lines · Alexei Starovoitov
Quoted 41 lines · Joel Fernandes
Quoted 38 lines · Alexei Starovoitov
Quoted 23 lines · Daniel Colascione
The map-in-map frequently serves as a mechanism for atomic snapshotting of state that a BPF program might record. The current implementation is dangerous to use in this way, however, since userspace has no way of knowing when all programs that might have retrieved the "old" value of the map may have completed.
This change ensures that map update operations on map-in-map map types always wait for all references to the old map to drop before returning to userspace.
Signed-off-by Daniel Colascione <[email protected]>--- kernel/bpf/syscall.c | 14 ++++++++++++++ 1 file changed, 14 insertions(+)kernel/bpf/syscall.c +0
-0@@ -741,6 +741,18 @@ static int map_lookup_elem(union bpf_attr *attr)return err; }kernel/bpf/syscall.c static int map_lookup_elem(union bpf_attr *attr) · line 753
Quoted 10 lines
kernel/bpf/syscall.c +10
-0+static void maybe_wait_bpf_programs (struct bpf_map *map) +{ +/* Wait for any running BPF programs to complete so that +* userspace, when we return to it, knows that all programs +* that could be running use the new map value. +*/ +if (map->map_type ==BPF_MAP_TYPE_HASH_OF_MAPS || + map->map_type ==BPF_MAP_TYPE_ARRAY_OF_MAPS ) { +synchronize_rcu (); + }extra {} were not necessary. I removed them while applying to bpf-next. Please run checkpatch.pl next time.
ThanksQuoted 7 lines
Quoted 3 lines
Thanks Alexei for taking it. Me and Lorenzo were discussing that not having this causes incorrect behavior for apps using map-in-map for this. So I CC'd stable as well.
It is too late in the release cycle.
We can submit it to stable releases after the merge window.Sounds good, thanks.
- Joel
Greg KH
maintainerRe: [PATCH v4] Wait for running BPF programs when updating map-in-map
Quoted 5 lines · Chenbo Feng
Hi netdev,
Could we queue up this patch to stable 4.14 and stable 4.19? I can provide a backport patch if needed. I checked it is a clean cherry-pick for 4.19 but have some minor conflict for 4.14.
What is the git commit id of the patch in Linus's tree?
thanks
greg k-h
Re: [PATCH v4] Wait for running BPF programs when updating map-in-map
The Linus's tree commit SHA is 1ae80cf31938c8f77c37a29bbe29e7f1cd492be8.
I can send the patch to stable directly if needed.
Thanks
Chenbo Feng
Quoted 13 lines · Greg KH
Quoted 5 lines · Chenbo Feng
Hi netdev,
Could we queue up this patch to stable 4.14 and stable 4.19? I can provide a backport patch if needed. I checked it is a clean cherry-pick for 4.19 but have some minor conflict for 4.14.
What is the git commit id of the patch in Linus's tree?
thanks
greg k-h
A message that is not in the archive (sent off-list or not yet received): CAKOZues6SE_c=ix7ap6QaJHqd1TmYpWWMJiu3=TtuqgKuqOUCA@mail.gmail.com
Re: [PATCH v3] Add BPF_SYNCHRONIZE_MAP_TO_MAP_REFERENCES bpf(2) command
Quoted 1 line · Daniel Colascione
Ping.
sorry for delay. have been off grid. let's continue in the other thread for full context
Quoted 22 lines · Alexei Starovoitov
Quoted 21 lines · Daniel Colascione
BPF_SYNCHRONIZE_MAPSwaits for the release of any references to a BPF map made by a BPF program that is running at the time theBPF_SYNCHRONIZE_MAPScommand is issued. The purpose of this command is to provide a means for userspace to replace a BPF map with another, newer version, then ensure that no component is still using the "old" map before manipulating the "old" map in some way.Signed-off-by Daniel Colascione <[email protected]>--- include/uapi/linux/bpf.h | 9 +++++++++ kernel/bpf/syscall.c | 13 +++++++++++++ 2 files changed, 22 insertions(+)include/uapi/linux/bpf.h +0
-0@@ -75,6 +75,14 @@ struct bpf_lpm_trie_key {__u8 data[0 ];/* Arbitrary size */ };
Quoted 8 lines
include/uapi/linux/bpf.h struct bpf_lpm_trie_key { · line 80
Quoted 3 lines
include/uapi/linux/bpf.h +3
-0+/* BPF_SYNCHRONIZE_MAPS waits for the release of any references to a +* BPF map made by a BPF program that is running at the time the +* BPF_SYNCHRONIZE_MAPS command is issued. The purpose of this command that doesn't sound right to me.
such command won't wait for the release of the references. in case of map-in-map the program does not hold the references to inner map (only to outer map).
I didn't follow this completely.
The userspace program is using the inner map per your description of the algorithm for using map-in-map to solve the race conditions that this patch is trying to address:
If you don't mind, I copy-pasted it below from your netdev post:
if you use map-in-map you don't need extra boolean map.
0. bpf prog can do
inner_map = lookup(map_in_map, key=0);
lookup(inner_map, your_real_key);
1. user space writes into map_in_map[0] <- FD of new map
2. some cpus are using old inner map and some a new
3. user space does sys_membarrier(CMD_GLOBAL) which will do synchronize_sched() which in CONFIG_PREEMPT_NONE=y servers is the same as synchronize_rcu() which will guarantee that progs finished.
4. scan old inner map
In step 2, as you mentioned there are CPUs using different inner maps. So could you clarify how the synchronize_rcu mechanism will even work if you're now saying "program does not hold references to the inner maps"?
Thanks!
- Joel
Quoted 32 lines · Joel Fernandes
Quoted 22 lines · Alexei Starovoitov
Quoted 21 lines · Daniel Colascione
BPF_SYNCHRONIZE_MAPSwaits for the release of any references to a BPF map made by a BPF program that is running at the time theBPF_SYNCHRONIZE_MAPScommand is issued. The purpose of this command is to provide a means for userspace to replace a BPF map with another, newer version, then ensure that no component is still using the "old" map before manipulating the "old" map in some way.Signed-off-by Daniel Colascione <[email protected]>--- include/uapi/linux/bpf.h | 9 +++++++++ kernel/bpf/syscall.c | 13 +++++++++++++ 2 files changed, 22 insertions(+)include/uapi/linux/bpf.h +0
-0@@ -75,6 +75,14 @@ struct bpf_lpm_trie_key {__u8 data[0 ];/* Arbitrary size */ };Quoted 8 lines
include/uapi/linux/bpf.h struct bpf_lpm_trie_key { · line 80
Quoted 3 lines
include/uapi/linux/bpf.h +3
-0+/* BPF_SYNCHRONIZE_MAPS waits for the release of any references to a +* BPF map made by a BPF program that is running at the time the +* BPF_SYNCHRONIZE_MAPS command is issued. The purpose of this command that doesn't sound right to me.
such command won't wait for the release of the references. in case of map-in-map the program does not hold the references to inner map (only to outer map).
Quoted 3 lines
I didn't follow this completely.
The userspace program is using the inner map per your description of the
Sorry just to correct myself, here I meant "The kernel eBPF program is using the inner map on multiple CPUs" instead of "userspace".
thanks,
- Joel
Quoted 24 lines
algorithm for using map-in-map to solve the race conditions that this patch is trying to address:
If you don't mind, I copy-pasted it below from your netdev post:
if you use map-in-map you don't need extra boolean map.
0. bpf prog can doinner_map = lookup(map_in_map, key=0); lookup(inner_map, your_real_key);1. user space writes into map_in_map[0] <- FD of new map
2. some cpus are using old inner map and some a new
3. user space doessys_membarrier(CMD_GLOBAL)which will dosynchronize_sched()which inCONFIG_PREEMPT_NONE=yservers is the same assynchronize_rcu()which will guarantee that progs finished.
4. scan old inner mapIn step 2, as you mentioned there are CPUs using different inner maps. So could you clarify how the
synchronize_rcumechanism will even work if you're now saying "program does not hold references to the inner maps"?Thanks!
- Joel
Quoted 35 lines · Joel Fernandes
Quoted 34 lines · Joel Fernandes
Quoted 22 lines · Alexei Starovoitov
Quoted 21 lines · Daniel Colascione
BPF_SYNCHRONIZE_MAPSwaits for the release of any references to a BPF map made by a BPF program that is running at the time theBPF_SYNCHRONIZE_MAPScommand is issued. The purpose of this command is to provide a means for userspace to replace a BPF map with another, newer version, then ensure that no component is still using the "old" map before manipulating the "old" map in some way.Signed-off-by Daniel Colascione <[email protected]>--- include/uapi/linux/bpf.h | 9 +++++++++ kernel/bpf/syscall.c | 13 +++++++++++++ 2 files changed, 22 insertions(+)include/uapi/linux/bpf.h +0
-0@@ -75,6 +75,14 @@ struct bpf_lpm_trie_key {__u8 data[0 ];/* Arbitrary size */ };Quoted 8 lines
include/uapi/linux/bpf.h struct bpf_lpm_trie_key { · line 80
Quoted 3 lines
include/uapi/linux/bpf.h +3
-0+/* BPF_SYNCHRONIZE_MAPS waits for the release of any references to a +* BPF map made by a BPF program that is running at the time the +* BPF_SYNCHRONIZE_MAPS command is issued. The purpose of this command that doesn't sound right to me.
such command won't wait for the release of the references. in case of map-in-map the program does not hold the references to inner map (only to outer map).I didn't follow this completely.
Quoted 32 lines
Quoted 1 line
The userspace program is using the inner map per your description of the
Sorry just to correct myself, here I meant "The kernel eBPF program is using the inner map on multiple CPUs" instead of "userspace".
thanks,
- Joel
Quoted 19 lines
algorithm for using map-in-map to solve the race conditions that this patch is trying to address:
If you don't mind, I copy-pasted it below from your netdev post:
if you use map-in-map you don't need extra boolean map.
0. bpf prog can doinner_map = lookup(map_in_map, key=0); lookup(inner_map, your_real_key);1. user space writes into map_in_map[0] <- FD of new map
2. some cpus are using old inner map and some a new
3. user space doessys_membarrier(CMD_GLOBAL)which will dosynchronize_sched()which inCONFIG_PREEMPT_NONE=yservers is the same assynchronize_rcu()which will guarantee that progs finished.
4. scan old inner mapIn step 2, as you mentioned there are CPUs using different inner maps. So could you clarify how the
synchronize_rcumechanism will even work if you're now saying "program does not hold references to the inner maps"?
The program only held references to the outer maps, and the outer map held references to the inner maps. The user space program can add/remove the inner map for a particular outer map while the prog <-> outer-map relationship is not changed.
Quoted 5 lines
Quoted 5 lines
Thanks!
- Joel
Quoted 53 lines · Y Song
Quoted 52 lines · Joel Fernandes
Quoted 51 lines · Joel Fernandes
Quoted 22 lines · Alexei Starovoitov
Quoted 21 lines · Daniel Colascione
BPF_SYNCHRONIZE_MAPSwaits for the release of any references to a BPF map made by a BPF program that is running at the time theBPF_SYNCHRONIZE_MAPScommand is issued. The purpose of this command is to provide a means for userspace to replace a BPF map with another, newer version, then ensure that no component is still using the "old" map before manipulating the "old" map in some way.Signed-off-by Daniel Colascione <[email protected]>--- include/uapi/linux/bpf.h | 9 +++++++++ kernel/bpf/syscall.c | 13 +++++++++++++ 2 files changed, 22 insertions(+)include/uapi/linux/bpf.h +0
-0@@ -75,6 +75,14 @@ struct bpf_lpm_trie_key {__u8 data[0 ];/* Arbitrary size */ };Quoted 8 lines
include/uapi/linux/bpf.h struct bpf_lpm_trie_key { · line 80
Quoted 3 lines
include/uapi/linux/bpf.h +3
-0+/* BPF_SYNCHRONIZE_MAPS waits for the release of any references to a +* BPF map made by a BPF program that is running at the time the +* BPF_SYNCHRONIZE_MAPS command is issued. The purpose of this command that doesn't sound right to me.
such command won't wait for the release of the references. in case of map-in-map the program does not hold the references to inner map (only to outer map).I didn't follow this completely.
The userspace program is using the inner map per your description of the algorithm for using map-in-map to solve the race conditions that this patch is trying to address:
If you don't mind, I copy-pasted it below from your netdev post:
if you use map-in-map you don't need extra boolean map.
0. bpf prog can doinner_map = lookup(map_in_map, key=0); lookup(inner_map, your_real_key);1. user space writes into map_in_map[0] <- FD of new map
2. some cpus are using old inner map and some a new
3. user space doessys_membarrier(CMD_GLOBAL)which will dosynchronize_sched()which inCONFIG_PREEMPT_NONE=yservers is the same assynchronize_rcu()which will guarantee that progs finished.
4. scan old inner map
Quoted 8 lines
Quoted 3 lines
Quoted 3 lines
In step 2, as you mentioned there are CPUs using different inner maps. So could you clarify how the
synchronize_rcumechanism will even work if you're now saying "program does not hold references to the inner maps"?The program only held references to the outer maps, and the outer map held references to the inner maps. The user space program can add/remove the inner map for a particular outer map while the prog <-> outer-map relationship is not changed.
My definition of "reference" in this context is protection by rcu_read_lock.
So I was concerned the above map-in-map access isn't protected as such when Alexei said "program doesn't have reference on inner map" in the above steps. Maybe I misunderstood what is the meaning of reference here.
To make the map-in-map thing to work for Chenbo/Lorenzo's usecase, both the access of outer map at key=0 and the inner map have to protect by rcu_read_lock so that the membarrier call will work.
So basically step 0 in the steps above should be rcu_read_lock protected to satisfy Chenbo/Lorenzo's usecase.
I know today the entire program is run as preempt disabled (unless something changed) so this shouldn't be a problem, but in the future if the verifier is doing similar things at a finer grainer level, then the above has to be taken into consideration.
Does that make sense or am I missing something?
thanks,
- Joel
Daniel Borkmann
maintainerQuoted 76 lines · Joel Fernandes
Quoted 53 lines · Y Song
Quoted 52 lines · Joel Fernandes
Quoted 51 lines · Joel Fernandes
Quoted 22 lines · Alexei Starovoitov
Quoted 21 lines · Daniel Colascione
BPF_SYNCHRONIZE_MAPSwaits for the release of any references to a BPF map made by a BPF program that is running at the time theBPF_SYNCHRONIZE_MAPScommand is issued. The purpose of this command is to provide a means for userspace to replace a BPF map with another, newer version, then ensure that no component is still using the "old" map before manipulating the "old" map in some way.Signed-off-by Daniel Colascione <[email protected]>--- include/uapi/linux/bpf.h | 9 +++++++++ kernel/bpf/syscall.c | 13 +++++++++++++ 2 files changed, 22 insertions(+)include/uapi/linux/bpf.h +0
-0@@ -75,6 +75,14 @@ struct bpf_lpm_trie_key {__u8 data[0 ];/* Arbitrary size */ };Quoted 8 lines
include/uapi/linux/bpf.h struct bpf_lpm_trie_key { · line 80
Quoted 3 lines
include/uapi/linux/bpf.h +3
-0+/* BPF_SYNCHRONIZE_MAPS waits for the release of any references to a +* BPF map made by a BPF program that is running at the time the +* BPF_SYNCHRONIZE_MAPS command is issued. The purpose of this command that doesn't sound right to me.
such command won't wait for the release of the references. in case of map-in-map the program does not hold the references to inner map (only to outer map).I didn't follow this completely.
The userspace program is using the inner map per your description of the algorithm for using map-in-map to solve the race conditions that this patch is trying to address:
If you don't mind, I copy-pasted it below from your netdev post:
if you use map-in-map you don't need extra boolean map.
0. bpf prog can doinner_map = lookup(map_in_map, key=0); lookup(inner_map, your_real_key);1. user space writes into map_in_map[0] <- FD of new map
2. some cpus are using old inner map and some a new
3. user space doessys_membarrier(CMD_GLOBAL)which will dosynchronize_sched()which inCONFIG_PREEMPT_NONE=yservers is the same assynchronize_rcu()which will guarantee that progs finished.
4. scan old inner mapQuoted 8 lines
Quoted 3 lines
Quoted 3 lines
In step 2, as you mentioned there are CPUs using different inner maps. So could you clarify how the
synchronize_rcumechanism will even work if you're now saying "program does not hold references to the inner maps"?The program only held references to the outer maps, and the outer map held references to the inner maps. The user space program can add/remove the inner map for a particular outer map while the prog <-> outer-map relationship is not changed.
My definition of "reference" in this context is protection by
rcu_read_lock.So I was concerned the above map-in-map access isn't protected as such when Alexei said "program doesn't have reference on inner map" in the above steps. Maybe I misunderstood what is the meaning of reference here.
To make the map-in-map thing to work for Chenbo/Lorenzo's usecase, both the access of outer map at key=0 and the inner map have to protect by
rcu_read_lockso that the membarrier call will work.So basically step 0 in the steps above should be
rcu_read_lockprotected to satisfy Chenbo/Lorenzo's usecase.
Quoted 6 lines
I know today the entire program is run as preempt disabled (unless something changed) so this shouldn't be a problem, but in the future if the verifier is doing similar things at a finer grainer level, then the above has to be taken into consideration.
Does that make sense or am I missing something?
All BPF programs are required to run under rcu_read_lock today, so that assumption holds. Should this ever change in future, then this constraint of course needs to be taken into consideration.
Thanks,
Daniel