Add BPF_SYNCHRONIZE bpf(2) command
Subsystems (4)
RFC 1/1 Add BPF_SYNCHRONIZE bpf(2) command
BPF_SYNCHRONIZE waits for any BPF programs active at the time of BPF_SYNCHRONIZE to complete, allowing userspace to ensure atomicity of RCU data structure operations with respect to active programs. For example, userspace can update a map->map entry to point to a new map, use BPF_SYNCHRONIZE to wait for any BPF programs using the old map to complete, and then drain the old map without fear that BPF programs may still be updating it.
--- include/uapi/linux/bpf.h | 1 + kernel/bpf/syscall.c | 14 ++++++++++++++ 2 files changed, 15 insertions(+)
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_SYNCHRONIZE, }; enum bpf_map_type {
kernel/bpf/syscall.c +14 -0
@@ -2272,6 +2272,20 @@ 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 ) { +if (uattr !=NULL || size !=0 ) +return -EINVAL ; + err =security_bpf (cmd,NULL ,0 ); +if (err <0 ) +return err; +/* BPF programs are run with preempt disabled, so +* synchronize_sched is sufficient even with +* RCU_PREEMPT. +*/ +synchronize_sched (); +return 0 ; + } + err =bpf_check_uarg_tail_zero (uattr,sizeof (attr), size);if (err)return err;
Signature · 1 line
2.18.0.203.gfac676dfb9-goog
Quoted 36 lines · Daniel Colascione
BPF_SYNCHRONIZEwaits for any BPF programs active at the time ofBPF_SYNCHRONIZEto complete, allowing userspace to ensure atomicity of RCU data structure operations with respect to active programs. For example, userspace can update amap->mapentry to point to a new map, useBPF_SYNCHRONIZEto wait for any BPF programs using the old map to complete, and then drain the old map without fear that BPF programs may still be updating it.Signed-off-by Daniel Colascione <[email protected]>--- include/uapi/linux/bpf.h | 1 + kernel/bpf/syscall.c | 14 ++++++++++++++ 2 files changed, 15 insertions(+)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_SYNCHRONIZE, }; enum bpf_map_type {kernel/bpf/syscall.c +2
-0@@ -2272,6 +2272,20 @@ 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 ) { +if (uattr !=NULL || size !=0 )
kernel/bpf/syscall.c SYSCALL_DEFINE3(bpf, int, cmd, union bpf_attr __user *, uattr, unsigned int, siz · line 2286
Quoted 10 lines
kernel/bpf/syscall.c +10
-0+return -EINVAL ; + err =security_bpf (cmd,NULL ,0 ); +if (err <0 ) +return err; +/* BPF programs are run with preempt disabled, so +* synchronize_sched is sufficient even with +* RCU_PREEMPT. +*/ +synchronize_sched (); +return 0 ;
I don't think it's necessary. sys_membarrier() can do this already and some folks use it exactly for this use case.
Quoted 35 lines · Alexei Starovoitov
Quoted 34 lines · Daniel Colascione
BPF_SYNCHRONIZEwaits for any BPF programs active at the time ofBPF_SYNCHRONIZEto complete, allowing userspace to ensure atomicity of RCU data structure operations with respect to active programs. For example, userspace can update amap->mapentry to point to a new map, useBPF_SYNCHRONIZEto wait for any BPF programs using the old map to complete, and then drain the old map without fear that BPF programs may still be updating it.Signed-off-by Daniel Colascione <[email protected]>--- include/uapi/linux/bpf.h | 1 + kernel/bpf/syscall.c | 14 ++++++++++++++ 2 files changed, 15 insertions(+)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_SYNCHRONIZE, }; enum bpf_map_type {kernel/bpf/syscall.c +0
-0@@ -2272,6 +2272,20 @@ SYSCALL_DEFINE3(bpf, int, cmd, union bpf_attr __user *, uattr, unsigned int, sizif (sysctl_unprivileged_bpf_disabled && !capable (CAP_SYS_ADMIN ))return -EPERM ;
Quoted 15 lines
kernel/bpf/syscall.c SYSCALL_DEFINE3(bpf, int, cmd, union bpf_attr __user *, uattr, unsigned int, siz · line 2286
Quoted 12 lines
kernel/bpf/syscall.c +12
-0+if (cmd ==BPF_SYNCHRONIZE ) { +if (uattr !=NULL || size !=0 ) +return -EINVAL ; + err =security_bpf (cmd,NULL ,0 ); +if (err <0 ) +return err; +/* BPF programs are run with preempt disabled, so +* synchronize_sched is sufficient even with +* RCU_PREEMPT. +*/ +synchronize_sched (); +return 0 ;I don't think it's necessary.
sys_membarrier()can do this already and some folks use it exactly for this use case.
True --- implementation-wise, today. The point of the patch is to sort out contractual guarantees. membarrier isn't documented to have the BPF_SYNCHRONIZE effect right now, although it does. If we want to extend membarrier's contract, great, but that might make it more expensive than necessary for callers who, well, just want a memory barrier. It's worthwhile to have a dedicated BPF command for this task, especially considering the simplicity of the implementation.
Quoted 35 lines · Alexei Starovoitov
Quoted 34 lines · Daniel Colascione
BPF_SYNCHRONIZEwaits for any BPF programs active at the time ofBPF_SYNCHRONIZEto complete, allowing userspace to ensure atomicity of RCU data structure operations with respect to active programs. For example, userspace can update amap->mapentry to point to a new map, useBPF_SYNCHRONIZEto wait for any BPF programs using the old map to complete, and then drain the old map without fear that BPF programs may still be updating it.Signed-off-by Daniel Colascione <[email protected]>--- include/uapi/linux/bpf.h | 1 + kernel/bpf/syscall.c | 14 ++++++++++++++ 2 files changed, 15 insertions(+)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_SYNCHRONIZE, }; enum bpf_map_type {kernel/bpf/syscall.c +0
-0@@ -2272,6 +2272,20 @@ SYSCALL_DEFINE3(bpf, int, cmd, union bpf_attr __user *, uattr, unsigned int, sizif (sysctl_unprivileged_bpf_disabled && !capable (CAP_SYS_ADMIN ))return -EPERM ;
Quoted 15 lines
kernel/bpf/syscall.c SYSCALL_DEFINE3(bpf, int, cmd, union bpf_attr __user *, uattr, unsigned int, siz · line 2286
Quoted 12 lines
kernel/bpf/syscall.c +12
-0+if (cmd ==BPF_SYNCHRONIZE ) { +if (uattr !=NULL || size !=0 ) +return -EINVAL ; + err =security_bpf (cmd,NULL ,0 ); +if (err <0 ) +return err; +/* BPF programs are run with preempt disabled, so +* synchronize_sched is sufficient even with +* RCU_PREEMPT. +*/ +synchronize_sched (); +return 0 ;I don't think it's necessary.
sys_membarrier()can do this already and some folks use it exactly for this use case.
Alexei, the use of sys_membarrier for this purpose seems kind of weird to me though. No where does the manpage say membarrier should be implemented this way so what happens if the implementation changes?
Further, membarrier manpage says that a memory barrier should be matched with a matching barrier. In this use case there is no matching barrier, so it makes it weirder.
Lastly, sys_membarrier seems will not work on nohz-full systems, so its a bit fragile to depend on it for this?
case MEMBARRIER_CMD_GLOBAL:
/* MEMBARRIER_CMD_GLOBAL is not compatible with nohz_full. */
if (tick_nohz_full_enabled())
return -EINVAL;
if (num_online_cpus() > 1)
synchronize_sched();
return 0;Adding Mathieu as well who I believe is author/maintainer of membarrier.
thanks!
- Joel
Quoted 73 lines · ----- On Jul 7, 2018, at 4:33 PM, Joel Fernandes [email protected] wrote:
Quoted 36 lines · Alexei Starovoitov
Quoted 35 lines · Daniel Colascione
BPF_SYNCHRONIZEwaits for any BPF programs active at the time ofBPF_SYNCHRONIZEto complete, allowing userspace to ensure atomicity of RCU data structure operations with respect to active programs. For example, userspace can update amap->mapentry to point to a new map, useBPF_SYNCHRONIZEto wait for any BPF programs using the old map to complete, and then drain the old map without fear that BPF programs may still be updating it.Signed-off-by Daniel Colascione <[email protected]>--- include/uapi/linux/bpf.h | 1 + kernel/bpf/syscall.c | 14 ++++++++++++++ 2 files changed, 15 insertions(+)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_SYNCHRONIZE, }; enum bpf_map_type {kernel/bpf/syscall.c +0
-0@@ -2272,6 +2272,20 @@ SYSCALL_DEFINE3(bpf, int, cmd, union bpf_attr __user *,uattr, unsigned int, siz
kernel/bpf/syscall.c +0
-0if (sysctl_unprivileged_bpf_disabled && !capable (CAP_SYS_ADMIN ))return -EPERM ;Quoted 15 lines
kernel/bpf/syscall.c SYSCALL_DEFINE3(bpf, int, cmd, union bpf_attr __user *, uattr, unsigned int, siz · line 2286
Quoted 12 lines
kernel/bpf/syscall.c +12
-0+if (cmd ==BPF_SYNCHRONIZE ) { +if (uattr !=NULL || size !=0 ) +return -EINVAL ; + err =security_bpf (cmd,NULL ,0 ); +if (err <0 ) +return err; +/* BPF programs are run with preempt disabled, so +* synchronize_sched is sufficient even with +* RCU_PREEMPT. +*/ +synchronize_sched (); +return 0 ;I don't think it's necessary.
sys_membarrier()can do this already and some folks use it exactly for this use case.Alexei, the use of
sys_membarrierfor this purpose seems kind of weird to me though. No where does the manpage say membarrier should be implemented this way so what happens if the implementation changes?Further, membarrier manpage says that a memory barrier should be matched with a matching barrier. In this use case there is no matching barrier, so it makes it weirder.
Lastly,
sys_membarrierseems will not work on nohz-full systems, so its a bit fragile to depend on it for this?case MEMBARRIER_CMD_GLOBAL: /* MEMBARRIER_CMD_GLOBAL is not compatible with nohz_full. */ if (tick_nohz_full_enabled()) return -EINVAL; if (num_online_cpus() > 1) synchronize_sched(); return 0;
Quoted 1 line
Adding Mathieu as well who I believe is author/maintainer of membarrier.
See commit 907565337
"Fix: Disable sys_membarrier when nohz_full is enabled"
"Userspace applications should be allowed to expect the membarrier system call with MEMBARRIER_CMD_SHARED command to issue memory barriers on nohz_full CPUs, but synchronize_sched() does not take those into account."
So AFAIU you'd want to re-use membarrier to issue synchronize_sched, and you only care about kernel preempt off critical sections.
Clearly bpf code does not run in user-space, so it would "work".
But the guarantees provided by membarrier are not to synchronize against preempt off per se. It's just that the current implementation happens to do that. The point of membarrier is to turn user-space memory barriers into compiler barriers.
If what you need is to wait for a RCU grace period for whatever RCU flavor ebpf is using, I would against using membarrier for this. I would rather recommend adding a dedicated BPF_SYNCHRONIZE so you won't leak implementation details to user-space, *and* you can eventually change you RCU implementation for e.g. SRCU in the future if needed.
Thanks,
Mathieu
Quoted 4 lines
thanks!
- Joel
Signature · 3 lines
Mathieu Desnoyers EfficiOS Inc. http://www.efficios.com
Quoted 95 lines · Mathieu Desnoyers
Quoted 73 lines · ----- On Jul 7, 2018, at 4:33 PM, Joel Fernandes [email protected] wrote:
Quoted 36 lines · Alexei Starovoitov
Quoted 35 lines · Daniel Colascione
BPF_SYNCHRONIZEwaits for any BPF programs active at the time ofBPF_SYNCHRONIZEto complete, allowing userspace to ensure atomicity of RCU data structure operations with respect to active programs. For example, userspace can update amap->mapentry to point to a new map, useBPF_SYNCHRONIZEto wait for any BPF programs using the old map to complete, and then drain the old map without fear that BPF programs may still be updating it.Signed-off-by Daniel Colascione <[email protected]>--- include/uapi/linux/bpf.h | 1 + kernel/bpf/syscall.c | 14 ++++++++++++++ 2 files changed, 15 insertions(+)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_SYNCHRONIZE, }; enum bpf_map_type {kernel/bpf/syscall.c +0
-0@@ -2272,6 +2272,20 @@ SYSCALL_DEFINE3(bpf, int, cmd, union bpf_attr __user *,uattr, unsigned int, siz
kernel/bpf/syscall.c +0
-0if (sysctl_unprivileged_bpf_disabled && !capable (CAP_SYS_ADMIN ))return -EPERM ;Quoted 15 lines
kernel/bpf/syscall.c SYSCALL_DEFINE3(bpf, int, cmd, union bpf_attr __user *, uattr, unsigned int, siz · line 2286
Quoted 12 lines
kernel/bpf/syscall.c +12
-0+if (cmd ==BPF_SYNCHRONIZE ) { +if (uattr !=NULL || size !=0 ) +return -EINVAL ; + err =security_bpf (cmd,NULL ,0 ); +if (err <0 ) +return err; +/* BPF programs are run with preempt disabled, so +* synchronize_sched is sufficient even with +* RCU_PREEMPT. +*/ +synchronize_sched (); +return 0 ;I don't think it's necessary.
sys_membarrier()can do this already and some folks use it exactly for this use case.Alexei, the use of
sys_membarrierfor this purpose seems kind of weird to me though. No where does the manpage say membarrier should be implemented this way so what happens if the implementation changes?Further, membarrier manpage says that a memory barrier should be matched with a matching barrier. In this use case there is no matching barrier, so it makes it weirder.
Lastly,
sys_membarrierseems will not work on nohz-full systems, so its a bit fragile to depend on it for this?case MEMBARRIER_CMD_GLOBAL: /* MEMBARRIER_CMD_GLOBAL is not compatible with nohz_full. */ if (tick_nohz_full_enabled()) return -EINVAL; if (num_online_cpus() > 1) synchronize_sched(); return 0;Quoted 1 line
Adding Mathieu as well who I believe is author/maintainer of membarrier.
See commit 907565337
"Fix: Disablesys_membarrierwhennohz_fullis enabled""Userspace applications should be allowed to expect the membarrier system call with
MEMBARRIER_CMD_SHAREDcommand to issue memory barriers onnohz_fullCPUs, butsynchronize_sched()does not take those into account."So AFAIU you'd want to re-use membarrier to issue
synchronize_sched, and you only care about kernel preempt off critical sections.Clearly bpf code does not run in user-space, so it would "work".
But the guarantees provided by membarrier are not to synchronize against preempt off per se. It's just that the current implementation happens to do that. The point of membarrier is to turn user-space memory barriers into compiler barriers.
Quoted 5 lines
If what you need is to wait for a RCU grace period for whatever RCU flavor ebpf is using, I would against using membarrier for this. I would rather recommend adding a dedicated
BPF_SYNCHRONIZEso you won't leak implementation details to user-space, *and* you can eventually change you RCU implementation for e.g. SRCU in the future if needed.
The point about future changes to underlying bpf mechanisms is valid. There is work already on the way to reduce the scope of preempt_off+rcu_lock that currently lasts the whole prog. We will have new prog types that won't have such wrappers and will do rcu_lock/unlock and preempt on/off only when necessary.
So something like BPF_SYNCHRONIZE will break soon, since the kernel cannot have guarantees on when programs finish. Calling this command BPF_SYNCHRONIZE_PROG also won't make sense for the same reason.
What we can do it instead is to define synchronization barrier for programs accessing maps. May be call it something like:BPF_SYNC_MAP_ACCESS ?uapi/bpf.h would need to have extensive comment what this barrier is doing. Implementation should probably call synchronize_rcu() and not play games with synchronize_sched(), since that's going too much into implementation. Also should such sys_bpf command be root only?
I'm not sure whether dos attack can be made by spamming synchronize_rcu() and synchronize_sched() for that matter.
Quoted 111 lines · ----- On Jul 9, 2018, at 5:09 PM, Alexei Starovoitov…
Quoted 95 lines · Mathieu Desnoyers
Quoted 73 lines · ----- On Jul 7, 2018, at 4:33 PM, Joel Fernandes [email protected] wrote:
Quoted 36 lines · Alexei Starovoitov
Quoted 35 lines · Daniel Colascione
BPF_SYNCHRONIZEwaits for any BPF programs active at the time ofBPF_SYNCHRONIZEto complete, allowing userspace to ensure atomicity of RCU data structure operations with respect to active programs. For example, userspace can update amap->mapentry to point to a new map, useBPF_SYNCHRONIZEto wait for any BPF programs using the old map to complete, and then drain the old map without fear that BPF programs may still be updating it.Signed-off-by Daniel Colascione <[email protected]>--- include/uapi/linux/bpf.h | 1 + kernel/bpf/syscall.c | 14 ++++++++++++++ 2 files changed, 15 insertions(+)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_SYNCHRONIZE, }; enum bpf_map_type {kernel/bpf/syscall.c +0
-0@@ -2272,6 +2272,20 @@ SYSCALL_DEFINE3(bpf, int, cmd, union bpf_attr __user *,uattr, unsigned int, siz
kernel/bpf/syscall.c +0
-0if (sysctl_unprivileged_bpf_disabled && !capable (CAP_SYS_ADMIN ))return -EPERM ;Quoted 15 lines
kernel/bpf/syscall.c SYSCALL_DEFINE3(bpf, int, cmd, union bpf_attr __user *, uattr, unsigned int, siz · line 2286
Quoted 12 lines
kernel/bpf/syscall.c +12
-0+if (cmd ==BPF_SYNCHRONIZE ) { +if (uattr !=NULL || size !=0 ) +return -EINVAL ; + err =security_bpf (cmd,NULL ,0 ); +if (err <0 ) +return err; +/* BPF programs are run with preempt disabled, so +* synchronize_sched is sufficient even with +* RCU_PREEMPT. +*/ +synchronize_sched (); +return 0 ;I don't think it's necessary.
sys_membarrier()can do this already and some folks use it exactly for this use case.Alexei, the use of
sys_membarrierfor this purpose seems kind of weird to me though. No where does the manpage say membarrier should be implemented this way so what happens if the implementation changes?Further, membarrier manpage says that a memory barrier should be matched with a matching barrier. In this use case there is no matching barrier, so it makes it weirder.
Lastly,
sys_membarrierseems will not work on nohz-full systems, so its a bit fragile to depend on it for this?case MEMBARRIER_CMD_GLOBAL: /* MEMBARRIER_CMD_GLOBAL is not compatible with nohz_full. */ if (tick_nohz_full_enabled()) return -EINVAL; if (num_online_cpus() > 1) synchronize_sched(); return 0;Quoted 1 line
Adding Mathieu as well who I believe is author/maintainer of membarrier.
See commit 907565337
"Fix: Disablesys_membarrierwhennohz_fullis enabled""Userspace applications should be allowed to expect the membarrier system call with
MEMBARRIER_CMD_SHAREDcommand to issue memory barriers onnohz_fullCPUs, butsynchronize_sched()does not take those into account."So AFAIU you'd want to re-use membarrier to issue
synchronize_sched, and you only care about kernel preempt off critical sections.Clearly bpf code does not run in user-space, so it would "work".
But the guarantees provided by membarrier are not to synchronize against preempt off per se. It's just that the current implementation happens to do that. The point of membarrier is to turn user-space memory barriers into compiler barriers.
Quoted 5 lines
If what you need is to wait for a RCU grace period for whatever RCU flavor ebpf is using, I would against using membarrier for this. I would rather recommend adding a dedicated
BPF_SYNCHRONIZEso you won't leak implementation details to user-space, *and* you can eventually change you RCU implementation for e.g. SRCU in the future if needed.The point about future changes to underlying bpf mechanisms is valid. There is work already on the way to reduce the scope of preempt_off+rcu_lock that currently lasts the whole prog. We will have new prog types that won't have such wrappers and will do rcu_lock/unlock and preempt on/off only when necessary.
So something likeBPF_SYNCHRONIZEwill break soon, since the kernel cannot have guarantees on when programs finish. Calling this commandBPF_SYNCHRONIZE_PROGalso won't make sense for the same reason.
What we can do it instead is to define synchronization barrier for
Quoted 8 lines
programs accessing maps. May be call it something like:
BPF_SYNC_MAP_ACCESS?uapi/bpf.hwould need to have extensive comment what this barrier is doing. Implementation should probably callsynchronize_rcu()and not play games withsynchronize_sched(), since that's going too much into implementation. Also should suchsys_bpfcommand be root only?
I'm not sure whether dos attack can be made by spammingsynchronize_rcu()andsynchronize_sched()for that matter.
Adding Paul E. McKenney in CC. He may want to share his thoughts on the matter.
Thanks,
Mathieu
Signature · 3 lines
Mathieu Desnoyers EfficiOS Inc. http://www.efficios.com
Quoted 124 lines · Mathieu Desnoyers
Quoted 111 lines · ----- On Jul 9, 2018, at 5:09 PM, Alexei Starovoitov…
Quoted 95 lines · Mathieu Desnoyers
Quoted 73 lines · ----- On Jul 7, 2018, at 4:33 PM, Joel Fernandes [email protected] wrote:
Quoted 36 lines · Alexei Starovoitov
Quoted 35 lines · Daniel Colascione
BPF_SYNCHRONIZEwaits for any BPF programs active at the time ofBPF_SYNCHRONIZEto complete, allowing userspace to ensure atomicity of RCU data structure operations with respect to active programs. For example, userspace can update amap->mapentry to point to a new map, useBPF_SYNCHRONIZEto wait for any BPF programs using the old map to complete, and then drain the old map without fear that BPF programs may still be updating it.Signed-off-by Daniel Colascione <[email protected]>--- include/uapi/linux/bpf.h | 1 + kernel/bpf/syscall.c | 14 ++++++++++++++ 2 files changed, 15 insertions(+)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_SYNCHRONIZE, }; enum bpf_map_type {kernel/bpf/syscall.c +0
-0@@ -2272,6 +2272,20 @@ SYSCALL_DEFINE3(bpf, int, cmd, union bpf_attr __user *,uattr, unsigned int, siz
kernel/bpf/syscall.c +0
-0if (sysctl_unprivileged_bpf_disabled && !capable (CAP_SYS_ADMIN ))return -EPERM ;Quoted 15 lines
kernel/bpf/syscall.c SYSCALL_DEFINE3(bpf, int, cmd, union bpf_attr __user *, uattr, unsigned int, siz · line 2286
Quoted 12 lines
kernel/bpf/syscall.c +12
-0+if (cmd ==BPF_SYNCHRONIZE ) { +if (uattr !=NULL || size !=0 ) +return -EINVAL ; + err =security_bpf (cmd,NULL ,0 ); +if (err <0 ) +return err; +/* BPF programs are run with preempt disabled, so +* synchronize_sched is sufficient even with +* RCU_PREEMPT. +*/ +synchronize_sched (); +return 0 ;I don't think it's necessary.
sys_membarrier()can do this already and some folks use it exactly for this use case.Alexei, the use of
sys_membarrierfor this purpose seems kind of weird to me though. No where does the manpage say membarrier should be implemented this way so what happens if the implementation changes?Further, membarrier manpage says that a memory barrier should be matched with a matching barrier. In this use case there is no matching barrier, so it makes it weirder.
Lastly,
sys_membarrierseems will not work on nohz-full systems, so its a bit fragile to depend on it for this?case MEMBARRIER_CMD_GLOBAL: /* MEMBARRIER_CMD_GLOBAL is not compatible with nohz_full. */ if (tick_nohz_full_enabled()) return -EINVAL; if (num_online_cpus() > 1) synchronize_sched(); return 0;Quoted 1 line
Adding Mathieu as well who I believe is author/maintainer of membarrier.
See commit 907565337
"Fix: Disablesys_membarrierwhennohz_fullis enabled""Userspace applications should be allowed to expect the membarrier system call with
MEMBARRIER_CMD_SHAREDcommand to issue memory barriers onnohz_fullCPUs, butsynchronize_sched()does not take those into account."So AFAIU you'd want to re-use membarrier to issue
synchronize_sched, and you only care about kernel preempt off critical sections.Clearly bpf code does not run in user-space, so it would "work".
But the guarantees provided by membarrier are not to synchronize against preempt off per se. It's just that the current implementation happens to do that. The point of membarrier is to turn user-space memory barriers into compiler barriers.
Quoted 5 lines
If what you need is to wait for a RCU grace period for whatever RCU flavor ebpf is using, I would against using membarrier for this. I would rather recommend adding a dedicated
BPF_SYNCHRONIZEso you won't leak implementation details to user-space, *and* you can eventually change you RCU implementation for e.g. SRCU in the future if needed.The point about future changes to underlying bpf mechanisms is valid. There is work already on the way to reduce the scope of preempt_off+rcu_lock that currently lasts the whole prog. We will have new prog types that won't have such wrappers and will do rcu_lock/unlock and preempt on/off only when necessary.
So something likeBPF_SYNCHRONIZEwill break soon, since the kernel cannot have guarantees on when programs finish. Calling this commandBPF_SYNCHRONIZE_PROGalso won't make sense for the same reason.
What we can do it instead is to define synchronization barrier forQuoted 8 lines
programs accessing maps. May be call it something like:
BPF_SYNC_MAP_ACCESS?uapi/bpf.hwould need to have extensive comment what this barrier is doing. Implementation should probably callsynchronize_rcu()and not play games withsynchronize_sched(), since that's going too much into implementation. Also should suchsys_bpfcommand be root only?
I'm not sure whether dos attack can be made by spammingsynchronize_rcu()andsynchronize_sched()for that matter.
Quoted 1 line
Adding Paul E. McKenney in CC. He may want to share his thoughts on the matter.
Let's see...
Spamming synchronize_rcu() and synchronize_sched() should be a non-event, at least aside from the CPUs doing the spamming. The reason for this is that a given task can only fire off a single synchronize_sched or synchronize_rcu() per few milliseconds, so you need a -lot- of tasks to have much effect, at which point the sheer number of tasks is much more a problem than the large number of outstanding synchronize_rcu() or synchronize_sched() invocations.
I very strongly agree that usermode should have a operation that synchronizes with whatever eBPF uses, rather than something that forces a specific type of RCU grace period.
Finally, in a few releases, synchronize_sched() will be retiring in favor of synchronize_rcu(), which will wait on preemption-disabled regions of code in addition to waiting on RCU read-side critical sections. Not a big deal, as I expect to enlist Coccinelle's aid in this.
Did I manage to hit all the high points?
Thanx, Paul
Quoted 141 lines · Paul E. McKenney
Quoted 124 lines · Mathieu Desnoyers
Quoted 111 lines · ----- On Jul 9, 2018, at 5:09 PM, Alexei Starovoitov…
Quoted 95 lines · Mathieu Desnoyers
Quoted 73 lines · ----- On Jul 7, 2018, at 4:33 PM, Joel Fernandes [email protected] wrote:
Quoted 36 lines · Alexei Starovoitov
Quoted 35 lines · Daniel Colascione
BPF_SYNCHRONIZEwaits for any BPF programs active at the time ofBPF_SYNCHRONIZEto complete, allowing userspace to ensure atomicity of RCU data structure operations with respect to active programs. For example, userspace can update amap->mapentry to point to a new map, useBPF_SYNCHRONIZEto wait for any BPF programs using the old map to complete, and then drain the old map without fear that BPF programs may still be updating it.Signed-off-by Daniel Colascione <[email protected]>--- include/uapi/linux/bpf.h | 1 + kernel/bpf/syscall.c | 14 ++++++++++++++ 2 files changed, 15 insertions(+)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_SYNCHRONIZE, }; enum bpf_map_type {kernel/bpf/syscall.c +0
-0@@ -2272,6 +2272,20 @@ SYSCALL_DEFINE3(bpf, int, cmd, union bpf_attr __user *,uattr, unsigned int, siz
kernel/bpf/syscall.c +0
-0if (sysctl_unprivileged_bpf_disabled && !capable (CAP_SYS_ADMIN ))return -EPERM ;Quoted 15 lines
kernel/bpf/syscall.c
Quoted 12 lines
kernel/bpf/syscall.c +12
-0+if (cmd ==BPF_SYNCHRONIZE ) { +if (uattr !=NULL || size !=0 ) +return -EINVAL ; + err =security_bpf (cmd,NULL ,0 ); +if (err <0 ) +return err; +/* BPF programs are run with preempt disabled, so +* synchronize_sched is sufficient even with +* RCU_PREEMPT. +*/ +synchronize_sched (); +return 0 ;I don't think it's necessary.
sys_membarrier()can do this already and some folks use it exactly for this use case.Alexei, the use of
sys_membarrierfor this purpose seems kind of weird to me though. No where does the manpage say membarrier should be implemented this way so what happens if the implementation changes?Further, membarrier manpage says that a memory barrier should be matched with a matching barrier. In this use case there is no matching barrier, so it makes it weirder.
Lastly,
sys_membarrierseems will not work on nohz-full systems, so its a bit fragile to depend on it for this?case MEMBARRIER_CMD_GLOBAL: /* MEMBARRIER_CMD_GLOBAL is not compatible with nohz_full. */ if (tick_nohz_full_enabled()) return -EINVAL; if (num_online_cpus() > 1) synchronize_sched(); return 0;Quoted 1 line
Adding Mathieu as well who I believe is author/maintainer of membarrier.
See commit 907565337
"Fix: Disablesys_membarrierwhennohz_fullis enabled""Userspace applications should be allowed to expect the membarrier system call with
MEMBARRIER_CMD_SHAREDcommand to issue memory barriers onnohz_fullCPUs, butsynchronize_sched()does not take those into account."So AFAIU you'd want to re-use membarrier to issue
synchronize_sched, and you only care about kernel preempt off critical sections.Clearly bpf code does not run in user-space, so it would "work".
But the guarantees provided by membarrier are not to synchronize against preempt off per se. It's just that the current implementation happens to do that. The point of membarrier is to turn user-space memory barriers into compiler barriers.
Quoted 5 lines
If what you need is to wait for a RCU grace period for whatever RCU flavor ebpf is using, I would against using membarrier for this. I would rather recommend adding a dedicated
BPF_SYNCHRONIZEso you won't leak implementation details to user-space, *and* you can eventually change you RCU implementation for e.g. SRCU in the future if needed.The point about future changes to underlying bpf mechanisms is valid. There is work already on the way to reduce the scope of preempt_off+rcu_lock that currently lasts the whole prog. We will have new prog types that won't have such wrappers and will do rcu_lock/unlock and preempt on/off only when necessary.
So something likeBPF_SYNCHRONIZEwill break soon, since the kernel cannot have guarantees on when programs finish. Calling this commandBPF_SYNCHRONIZE_PROGalso won't make sense for the same reason.
What we can do it instead is to define synchronization barrier forQuoted 8 lines
programs accessing maps. May be call it something like:
BPF_SYNC_MAP_ACCESS?uapi/bpf.hwould need to have extensive comment what this barrier is doing. Implementation should probably callsynchronize_rcu()and not play games withsynchronize_sched(), since that's going too much into implementation. Also should suchsys_bpfcommand be root only?
I'm not sure whether dos attack can be made by spammingsynchronize_rcu()andsynchronize_sched()for that matter.Quoted 1 line
Adding Paul E. McKenney in CC. He may want to share his thoughts on the matter.
Let's see...
Spamming
synchronize_rcu()andsynchronize_sched()should be a non-event, at least aside from the CPUs doing the spamming. The reason for this is that a given task can only fire off a singlesynchronize_schedorsynchronize_rcu()per few milliseconds, so you need a -lot- of tasks to have much effect, at which point the sheer number of tasks is much more a problem than the large number of outstandingsynchronize_rcu()orsynchronize_sched()invocations.I very strongly agree that usermode should have a operation that synchronizes with whatever eBPF uses, rather than something that forces a specific type of RCU grace period.
Quoted 6 lines
Finally, in a few releases,
synchronize_sched()will be retiring in favor ofsynchronize_rcu(), which will wait on preemption-disabled regions of code in addition to waiting on RCU read-side critical sections. Not a big deal, as I expect to enlist Coccinelle's aid in this.Did I manage to hit all the high points?
Thanks. It's ok for new cmd being unpriv then.
Quoted 142 lines · Alexei Starovoitov
Quoted 141 lines · Paul E. McKenney
Quoted 124 lines · Mathieu Desnoyers
Quoted 111 lines · ----- On Jul 9, 2018, at 5:09 PM, Alexei Starovoitov…
Quoted 95 lines · Mathieu Desnoyers
Quoted 73 lines · ----- On Jul 7, 2018, at 4:33 PM, Joel Fernandes [email protected] wrote:
Quoted 36 lines · Alexei Starovoitov
Quoted 35 lines · Daniel Colascione
BPF_SYNCHRONIZEwaits for any BPF programs active at the time ofBPF_SYNCHRONIZEto complete, allowing userspace to ensure atomicity of RCU data structure operations with respect to active programs. For example, userspace can update amap->mapentry to point to a new map, useBPF_SYNCHRONIZEto wait for any BPF programs using the old map to complete, and then drain the old map without fear that BPF programs may still be updating it.Signed-off-by Daniel Colascione <[email protected]>--- include/uapi/linux/bpf.h | 1 + kernel/bpf/syscall.c | 14 ++++++++++++++ 2 files changed, 15 insertions(+)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_SYNCHRONIZE, }; enum bpf_map_type {kernel/bpf/syscall.c +0
-0@@ -2272,6 +2272,20 @@ SYSCALL_DEFINE3(bpf, int, cmd, union bpf_attr __user *,uattr, unsigned int, siz
kernel/bpf/syscall.c +0
-0if (sysctl_unprivileged_bpf_disabled && !capable (CAP_SYS_ADMIN ))return -EPERM ;Quoted 15 lines
kernel/bpf/syscall.c
Quoted 12 lines
kernel/bpf/syscall.c +12
-0+if (cmd ==BPF_SYNCHRONIZE ) { +if (uattr !=NULL || size !=0 ) +return -EINVAL ; + err =security_bpf (cmd,NULL ,0 ); +if (err <0 ) +return err; +/* BPF programs are run with preempt disabled, so +* synchronize_sched is sufficient even with +* RCU_PREEMPT. +*/ +synchronize_sched (); +return 0 ;I don't think it's necessary.
sys_membarrier()can do this already and some folks use it exactly for this use case.Alexei, the use of
sys_membarrierfor this purpose seems kind of weird to me though. No where does the manpage say membarrier should be implemented this way so what happens if the implementation changes?Further, membarrier manpage says that a memory barrier should be matched with a matching barrier. In this use case there is no matching barrier, so it makes it weirder.
Lastly,
sys_membarrierseems will not work on nohz-full systems, so its a bit fragile to depend on it for this?case MEMBARRIER_CMD_GLOBAL: /* MEMBARRIER_CMD_GLOBAL is not compatible with nohz_full. */ if (tick_nohz_full_enabled()) return -EINVAL; if (num_online_cpus() > 1) synchronize_sched(); return 0;Quoted 1 line
Adding Mathieu as well who I believe is author/maintainer of membarrier.
See commit 907565337
"Fix: Disablesys_membarrierwhennohz_fullis enabled""Userspace applications should be allowed to expect the membarrier system call with
MEMBARRIER_CMD_SHAREDcommand to issue memory barriers onnohz_fullCPUs, butsynchronize_sched()does not take those into account."So AFAIU you'd want to re-use membarrier to issue
synchronize_sched, and you only care about kernel preempt off critical sections.Clearly bpf code does not run in user-space, so it would "work".
But the guarantees provided by membarrier are not to synchronize against preempt off per se. It's just that the current implementation happens to do that. The point of membarrier is to turn user-space memory barriers into compiler barriers.
Quoted 5 lines
If what you need is to wait for a RCU grace period for whatever RCU flavor ebpf is using, I would against using membarrier for this. I would rather recommend adding a dedicated
BPF_SYNCHRONIZEso you won't leak implementation details to user-space, *and* you can eventually change you RCU implementation for e.g. SRCU in the future if needed.The point about future changes to underlying bpf mechanisms is valid. There is work already on the way to reduce the scope of preempt_off+rcu_lock that currently lasts the whole prog. We will have new prog types that won't have such wrappers and will do rcu_lock/unlock and preempt on/off only when necessary.
So something likeBPF_SYNCHRONIZEwill break soon, since the kernel cannot have guarantees on when programs finish. Calling this commandBPF_SYNCHRONIZE_PROGalso won't make sense for the same reason.
What we can do it instead is to define synchronization barrier forQuoted 8 lines
programs accessing maps. May be call it something like:
BPF_SYNC_MAP_ACCESS?uapi/bpf.hwould need to have extensive comment what this barrier is doing. Implementation should probably callsynchronize_rcu()and not play games withsynchronize_sched(), since that's going too much into implementation. Also should suchsys_bpfcommand be root only?
I'm not sure whether dos attack can be made by spammingsynchronize_rcu()andsynchronize_sched()for that matter.Quoted 1 line
Adding Paul E. McKenney in CC. He may want to share his thoughts on the matter.
Let's see...
Spamming
synchronize_rcu()andsynchronize_sched()should be a non-event, at least aside from the CPUs doing the spamming. The reason for this is that a given task can only fire off a singlesynchronize_schedorsynchronize_rcu()per few milliseconds, so you need a -lot- of tasks to have much effect, at which point the sheer number of tasks is much more a problem than the large number of outstandingsynchronize_rcu()orsynchronize_sched()invocations.I very strongly agree that usermode should have a operation that synchronizes with whatever eBPF uses, rather than something that forces a specific type of RCU grace period.
Quoted 8 lines
Quoted 6 lines
Finally, in a few releases,
synchronize_sched()will be retiring in favor ofsynchronize_rcu(), which will wait on preemption-disabled regions of code in addition to waiting on RCU read-side critical sections. Not a big deal, as I expect to enlist Coccinelle's aid in this.Did I manage to hit all the high points?
Thanks. It's ok for new cmd being unpriv then.
Yes. After all, users can cause RCU far more pain by doing close(open()) in a tight loop. ;-)
Thanx, Paul
Quoted 102 lines · Alexei Starovoitov
Quoted 95 lines · Mathieu Desnoyers
Quoted 73 lines · ----- On Jul 7, 2018, at 4:33 PM, Joel Fernandes [email protected] wrote:
Quoted 36 lines · Alexei Starovoitov
Quoted 35 lines · Daniel Colascione
BPF_SYNCHRONIZEwaits for any BPF programs active at the time ofBPF_SYNCHRONIZEto complete, allowing userspace to ensure atomicity of RCU data structure operations with respect to active programs. For example, userspace can update amap->mapentry to point to a new map, useBPF_SYNCHRONIZEto wait for any BPF programs using the old map to complete, and then drain the old map without fear that BPF programs may still be updating it.Signed-off-by Daniel Colascione <[email protected]>--- include/uapi/linux/bpf.h | 1 + kernel/bpf/syscall.c | 14 ++++++++++++++ 2 files changed, 15 insertions(+)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_SYNCHRONIZE, }; enum bpf_map_type {kernel/bpf/syscall.c +0
-0@@ -2272,6 +2272,20 @@ SYSCALL_DEFINE3(bpf, int, cmd, union bpf_attr __user *,uattr, unsigned int, siz
if (sysctl_unprivileged_bpf_disabled && !capable(CAP_SYS_ADMIN)) return -EPERM;Quoted 15 lines
kernel/bpf/syscall.c SYSCALL_DEFINE3(bpf, int, cmd, union bpf_attr __user *, uattr, unsigned int, siz · line 2286
Quoted 12 lines
kernel/bpf/syscall.c +12
-0+if (cmd ==BPF_SYNCHRONIZE ) { +if (uattr !=NULL || size !=0 ) +return -EINVAL ; + err =security_bpf (cmd,NULL ,0 ); +if (err <0 ) +return err; +/* BPF programs are run with preempt disabled, so +* synchronize_sched is sufficient even with +* RCU_PREEMPT. +*/ +synchronize_sched (); +return 0 ;I don't think it's necessary.
sys_membarrier()can do this already and some folks use it exactly for this use case.Alexei, the use of
sys_membarrierfor this purpose seems kind of weird to me though. No where does the manpage say membarrier should be implemented this way so what happens if the implementation changes?Further, membarrier manpage says that a memory barrier should be matched with a matching barrier. In this use case there is no matching barrier, so it makes it weirder.
Lastly,
sys_membarrierseems will not work on nohz-full systems, so its a bit fragile to depend on it for this?case MEMBARRIER_CMD_GLOBAL: /* MEMBARRIER_CMD_GLOBAL is not compatible with nohz_full. */ if (tick_nohz_full_enabled()) return -EINVAL; if (num_online_cpus() > 1) synchronize_sched(); return 0;Quoted 1 line
Adding Mathieu as well who I believe is author/maintainer of membarrier.
See commit 907565337
"Fix: Disablesys_membarrierwhennohz_fullis enabled""Userspace applications should be allowed to expect the membarrier system call with
MEMBARRIER_CMD_SHAREDcommand to issue memory barriers onnohz_fullCPUs, butsynchronize_sched()does not take those into account."So AFAIU you'd want to re-use membarrier to issue
synchronize_sched, and you only care about kernel preempt off critical sections.Clearly bpf code does not run in user-space, so it would "work".
But the guarantees provided by membarrier are not to synchronize against preempt off per se. It's just that the current implementation happens to do that. The point of membarrier is to turn user-space memory barriers into compiler barriers.
Quoted 5 lines
If what you need is to wait for a RCU grace period for whatever RCU flavor ebpf is using, I would against using membarrier for this. I would rather recommend adding a dedicated
BPF_SYNCHRONIZEso you won't leak implementation details to user-space, *and* you can eventually change you RCU implementation for e.g. SRCU in the future if needed.
Quoted 11 lines
The point about future changes to underlying bpf mechanisms is valid. There is work already on the way to reduce the scope of preempt_off+rcu_lock that currently lasts the whole prog. We will have new prog types that won't have such wrappers and will do rcu_lock/unlock and preempt on/off only when necessary.
So something likeBPF_SYNCHRONIZEwill break soon, since the kernel cannot have guarantees on when programs finish. Calling this commandBPF_SYNCHRONIZE_PROGalso won't make sense for the same reason.
What we can do it instead is to define synchronization barrier for programs accessing maps. May be call it something like:BPF_SYNC_MAP_ACCESS?
I'm not sure what you're proposing. In the case the commit message describes, a user-space program that wants to "drain" a map needs to be confident that the map won't change under it, even across multiple bpf system calls on that map. One way of doing that is to ensure that nothing that could possibly hold a reference to that map is still running. Are you proposing some kind of refcount-draining approach? Simple locking won't work, since BPF programs can't block, and I don't see right now how a simple barrier would help.
Quoted 3 lines
Also should such
sys_bpfcommand be root only?
I'm not sure whether dos attack can be made by spammingsynchronize_rcu()andsynchronize_sched()for that matter.
membarrier being globally available seems not to have caused any kind of DoS catastrophe.
Quoted 116 lines · Daniel Colascione
Quoted 102 lines · Alexei Starovoitov
Quoted 95 lines · Mathieu Desnoyers
Quoted 73 lines · ----- On Jul 7, 2018, at 4:33 PM, Joel Fernandes [email protected] wrote:
Quoted 36 lines · Alexei Starovoitov
Quoted 35 lines · Daniel Colascione
BPF_SYNCHRONIZEwaits for any BPF programs active at the time ofBPF_SYNCHRONIZEto complete, allowing userspace to ensure atomicity of RCU data structure operations with respect to active programs. For example, userspace can update amap->mapentry to point to a new map, useBPF_SYNCHRONIZEto wait for any BPF programs using the old map to complete, and then drain the old map without fear that BPF programs may still be updating it.Signed-off-by Daniel Colascione <[email protected]>--- include/uapi/linux/bpf.h | 1 + kernel/bpf/syscall.c | 14 ++++++++++++++ 2 files changed, 15 insertions(+)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_SYNCHRONIZE, }; enum bpf_map_type {kernel/bpf/syscall.c +0
-0@@ -2272,6 +2272,20 @@ SYSCALL_DEFINE3(bpf, int, cmd, union bpf_attr __user *,uattr, unsigned int, siz
if (sysctl_unprivileged_bpf_disabled && !capable(CAP_SYS_ADMIN)) return -EPERM;Quoted 15 lines
kernel/bpf/syscall.c SYSCALL_DEFINE3(bpf, int, cmd, union bpf_attr __user *, uattr, unsigned int, siz · line 2286
Quoted 12 lines
kernel/bpf/syscall.c +12
-0+if (cmd ==BPF_SYNCHRONIZE ) { +if (uattr !=NULL || size !=0 ) +return -EINVAL ; + err =security_bpf (cmd,NULL ,0 ); +if (err <0 ) +return err; +/* BPF programs are run with preempt disabled, so +* synchronize_sched is sufficient even with +* RCU_PREEMPT. +*/ +synchronize_sched (); +return 0 ;I don't think it's necessary.
sys_membarrier()can do this already and some folks use it exactly for this use case.Alexei, the use of
sys_membarrierfor this purpose seems kind of weird to me though. No where does the manpage say membarrier should be implemented this way so what happens if the implementation changes?Further, membarrier manpage says that a memory barrier should be matched with a matching barrier. In this use case there is no matching barrier, so it makes it weirder.
Lastly,
sys_membarrierseems will not work on nohz-full systems, so its a bit fragile to depend on it for this?case MEMBARRIER_CMD_GLOBAL: /* MEMBARRIER_CMD_GLOBAL is not compatible with nohz_full. */ if (tick_nohz_full_enabled()) return -EINVAL; if (num_online_cpus() > 1) synchronize_sched(); return 0;Quoted 1 line
Adding Mathieu as well who I believe is author/maintainer of membarrier.
See commit 907565337
"Fix: Disablesys_membarrierwhennohz_fullis enabled""Userspace applications should be allowed to expect the membarrier system call with
MEMBARRIER_CMD_SHAREDcommand to issue memory barriers onnohz_fullCPUs, butsynchronize_sched()does not take those into account."So AFAIU you'd want to re-use membarrier to issue
synchronize_sched, and you only care about kernel preempt off critical sections.Clearly bpf code does not run in user-space, so it would "work".
But the guarantees provided by membarrier are not to synchronize against preempt off per se. It's just that the current implementation happens to do that. The point of membarrier is to turn user-space memory barriers into compiler barriers.
Quoted 5 lines
If what you need is to wait for a RCU grace period for whatever RCU flavor ebpf is using, I would against using membarrier for this. I would rather recommend adding a dedicated
BPF_SYNCHRONIZEso you won't leak implementation details to user-space, *and* you can eventually change you RCU implementation for e.g. SRCU in the future if needed.Quoted 11 lines
The point about future changes to underlying bpf mechanisms is valid. There is work already on the way to reduce the scope of preempt_off+rcu_lock that currently lasts the whole prog. We will have new prog types that won't have such wrappers and will do rcu_lock/unlock and preempt on/off only when necessary.
So something likeBPF_SYNCHRONIZEwill break soon, since the kernel cannot have guarantees on when programs finish. Calling this commandBPF_SYNCHRONIZE_PROGalso won't make sense for the same reason.
What we can do it instead is to define synchronization barrier for programs accessing maps. May be call it something like:BPF_SYNC_MAP_ACCESS?
Quoted 8 lines
I'm not sure what you're proposing. In the case the commit message describes, a user-space program that wants to "drain" a map needs to be confident that the map won't change under it, even across multiple bpf system calls on that map. One way of doing that is to ensure that nothing that could possibly hold a reference to that map is still running. Are you proposing some kind of refcount-draining approach? Simple locking won't work, since BPF programs can't block, and I don't see right now how a simple barrier would help.
I'm proposing few changes for your patch:
s/BPF_SYNCHRONIZE/BPF_SYNC_MAP_ACCESS/
and s/synchronize_sched/synchronize_rcu/
with detailed comment in uapi/bpf.h that has an example why folks would want to use this new cmd.
I think the bpf maps will be rcu protected for foreseeable future even when rcu_read_lock/unlock will be done by the programs instead of kernel wrappers.
Quoted 126 lines · Alexei Starovoitov
Quoted 116 lines · Daniel Colascione
Quoted 102 lines · Alexei Starovoitov
Quoted 95 lines · Mathieu Desnoyers
Quoted 73 lines · ----- On Jul 7, 2018, at 4:33 PM, Joel Fernandes [email protected] wrote:
Quoted 36 lines · Alexei Starovoitov
Quoted 35 lines · Daniel Colascione
BPF_SYNCHRONIZEwaits for any BPF programs active at the time ofBPF_SYNCHRONIZEto complete, allowing userspace to ensure atomicity of RCU data structure operations with respect to active programs. For example, userspace can update amap->mapentry to point to a new map, useBPF_SYNCHRONIZEto wait for any BPF programs using the old map to complete, and then drain the old map without fear that BPF programs may still be updating it.Signed-off-by Daniel Colascione <[email protected]>--- include/uapi/linux/bpf.h | 1 + kernel/bpf/syscall.c | 14 ++++++++++++++ 2 files changed, 15 insertions(+)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_SYNCHRONIZE, }; enum bpf_map_type {kernel/bpf/syscall.c +0
-0@@ -2272,6 +2272,20 @@ SYSCALL_DEFINE3(bpf, int, cmd, union bpf_attr __user *,uattr, unsigned int, siz
if (sysctl_unprivileged_bpf_disabled && !capable(CAP_SYS_ADMIN)) return -EPERM;Quoted 15 lines
kernel/bpf/syscall.c
Quoted 12 lines
kernel/bpf/syscall.c +12
-0+if (cmd ==BPF_SYNCHRONIZE ) { +if (uattr !=NULL || size !=0 ) +return -EINVAL ; + err =security_bpf (cmd,NULL ,0 ); +if (err <0 ) +return err; +/* BPF programs are run with preempt disabled, so +* synchronize_sched is sufficient even with +* RCU_PREEMPT. +*/ +synchronize_sched (); +return 0 ;I don't think it's necessary.
sys_membarrier()can do this already and some folks use it exactly for this use case.Alexei, the use of
sys_membarrierfor this purpose seems kind of weird to me though. No where does the manpage say membarrier should be implemented this way so what happens if the implementation changes?Further, membarrier manpage says that a memory barrier should be matched with a matching barrier. In this use case there is no matching barrier, so it makes it weirder.
Lastly,
sys_membarrierseems will not work on nohz-full systems, so its a bit fragile to depend on it for this?case MEMBARRIER_CMD_GLOBAL: /* MEMBARRIER_CMD_GLOBAL is not compatible with nohz_full. */ if (tick_nohz_full_enabled()) return -EINVAL; if (num_online_cpus() > 1) synchronize_sched(); return 0;Quoted 1 line
Adding Mathieu as well who I believe is author/maintainer of membarrier.
See commit 907565337
"Fix: Disablesys_membarrierwhennohz_fullis enabled""Userspace applications should be allowed to expect the membarrier system call with
MEMBARRIER_CMD_SHAREDcommand to issue memory barriers onnohz_fullCPUs, butsynchronize_sched()does not take those into account."So AFAIU you'd want to re-use membarrier to issue
synchronize_sched, and you only care about kernel preempt off critical sections.Clearly bpf code does not run in user-space, so it would "work".
But the guarantees provided by membarrier are not to synchronize against preempt off per se. It's just that the current implementation happens to do that. The point of membarrier is to turn user-space memory barriers into compiler barriers.
Quoted 5 lines
If what you need is to wait for a RCU grace period for whatever RCU flavor ebpf is using, I would against using membarrier for this. I would rather recommend adding a dedicated
BPF_SYNCHRONIZEso you won't leak implementation details to user-space, *and* you can eventually change you RCU implementation for e.g. SRCU in the future if needed.Quoted 11 lines
The point about future changes to underlying bpf mechanisms is valid. There is work already on the way to reduce the scope of preempt_off+rcu_lock that currently lasts the whole prog. We will have new prog types that won't have such wrappers and will do rcu_lock/unlock and preempt on/off only when necessary.
So something likeBPF_SYNCHRONIZEwill break soon, since the kernel cannot have guarantees on when programs finish. Calling this commandBPF_SYNCHRONIZE_PROGalso won't make sense for the same reason.
What we can do it instead is to define synchronization barrier for programs accessing maps. May be call it something like:BPF_SYNC_MAP_ACCESS?Quoted 8 lines
I'm not sure what you're proposing. In the case the commit message describes, a user-space program that wants to "drain" a map needs to be confident that the map won't change under it, even across multiple bpf system calls on that map. One way of doing that is to ensure that nothing that could possibly hold a reference to that map is still running. Are you proposing some kind of refcount-draining approach? Simple locking won't work, since BPF programs can't block, and I don't see right now how a simple barrier would help.
Quoted 5 lines
I'm proposing few changes for your patch:
s/BPF_SYNCHRONIZE/BPF_SYNC_MAP_ACCESS/
and s/synchronize_sched/synchronize_rcu/
with detailed comment inuapi/bpf.hthat has an example why folks would want to use this new cmd.
Thanks for clarifying.
Quoted 3 lines
I think the bpf maps will be rcu protected for foreseeable future even when rcu_read_lock/unlock will be done by the programs instead of kernel wrappers.
Can we guarantee that we always obtain a map reference and dispose of that reference inside the same critical section? If so, can BPF programs then disable preemption for as long as they'd like?
Quoted 134 lines · Daniel Colascione
Quoted 126 lines · Alexei Starovoitov
Quoted 116 lines · Daniel Colascione
Quoted 102 lines · Alexei Starovoitov
Quoted 95 lines · Mathieu Desnoyers
Quoted 73 lines · ----- On Jul 7, 2018, at 4:33 PM, Joel Fernandes [email protected] wrote:
Quoted 36 lines · Alexei Starovoitov
Quoted 35 lines · Daniel Colascione
BPF_SYNCHRONIZEwaits for any BPF programs active at the time ofBPF_SYNCHRONIZEto complete, allowing userspace to ensure atomicity of RCU data structure operations with respect to active programs. For example, userspace can update amap->mapentry to point to a new map, useBPF_SYNCHRONIZEto wait for any BPF programs using the old map to complete, and then drain the old map without fear that BPF programs may still be updating it.Signed-off-by Daniel Colascione <[email protected]>--- include/uapi/linux/bpf.h | 1 + kernel/bpf/syscall.c | 14 ++++++++++++++ 2 files changed, 15 insertions(+)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_SYNCHRONIZE, }; enum bpf_map_type {kernel/bpf/syscall.c +0
-0@@ -2272,6 +2272,20 @@ SYSCALL_DEFINE3(bpf, int, cmd, union bpf_attr __user *,uattr, unsigned int, siz
if (sysctl_unprivileged_bpf_disabled && !capable(CAP_SYS_ADMIN)) return -EPERM;Quoted 15 lines
kernel/bpf/syscall.c
Quoted 12 lines
kernel/bpf/syscall.c +12
-0+if (cmd ==BPF_SYNCHRONIZE ) { +if (uattr !=NULL || size !=0 ) +return -EINVAL ; + err =security_bpf (cmd,NULL ,0 ); +if (err <0 ) +return err; +/* BPF programs are run with preempt disabled, so +* synchronize_sched is sufficient even with +* RCU_PREEMPT. +*/ +synchronize_sched (); +return 0 ;I don't think it's necessary.
sys_membarrier()can do this already and some folks use it exactly for this use case.Alexei, the use of
sys_membarrierfor this purpose seems kind of weird to me though. No where does the manpage say membarrier should be implemented this way so what happens if the implementation changes?Further, membarrier manpage says that a memory barrier should be matched with a matching barrier. In this use case there is no matching barrier, so it makes it weirder.
Lastly,
sys_membarrierseems will not work on nohz-full systems, so its a bit fragile to depend on it for this?case MEMBARRIER_CMD_GLOBAL: /* MEMBARRIER_CMD_GLOBAL is not compatible with nohz_full. */ if (tick_nohz_full_enabled()) return -EINVAL; if (num_online_cpus() > 1) synchronize_sched(); return 0;Quoted 1 line
Adding Mathieu as well who I believe is author/maintainer of membarrier.
See commit 907565337
"Fix: Disablesys_membarrierwhennohz_fullis enabled""Userspace applications should be allowed to expect the membarrier system call with
MEMBARRIER_CMD_SHAREDcommand to issue memory barriers onnohz_fullCPUs, butsynchronize_sched()does not take those into account."So AFAIU you'd want to re-use membarrier to issue
synchronize_sched, and you only care about kernel preempt off critical sections.Clearly bpf code does not run in user-space, so it would "work".
But the guarantees provided by membarrier are not to synchronize against preempt off per se. It's just that the current implementation happens to do that. The point of membarrier is to turn user-space memory barriers into compiler barriers.
Quoted 5 lines
If what you need is to wait for a RCU grace period for whatever RCU flavor ebpf is using, I would against using membarrier for this. I would rather recommend adding a dedicated
BPF_SYNCHRONIZEso you won't leak implementation details to user-space, *and* you can eventually change you RCU implementation for e.g. SRCU in the future if needed.Quoted 11 lines
The point about future changes to underlying bpf mechanisms is valid. There is work already on the way to reduce the scope of preempt_off+rcu_lock that currently lasts the whole prog. We will have new prog types that won't have such wrappers and will do rcu_lock/unlock and preempt on/off only when necessary.
So something likeBPF_SYNCHRONIZEwill break soon, since the kernel cannot have guarantees on when programs finish. Calling this commandBPF_SYNCHRONIZE_PROGalso won't make sense for the same reason.
What we can do it instead is to define synchronization barrier for programs accessing maps. May be call it something like:BPF_SYNC_MAP_ACCESS?Quoted 8 lines
I'm not sure what you're proposing. In the case the commit message describes, a user-space program that wants to "drain" a map needs to be confident that the map won't change under it, even across multiple bpf system calls on that map. One way of doing that is to ensure that nothing that could possibly hold a reference to that map is still running. Are you proposing some kind of refcount-draining approach? Simple locking won't work, since BPF programs can't block, and I don't see right now how a simple barrier would help.
Quoted 5 lines
I'm proposing few changes for your patch:
s/BPF_SYNCHRONIZE/BPF_SYNC_MAP_ACCESS/
and s/synchronize_sched/synchronize_rcu/
with detailed comment inuapi/bpf.hthat has an example why folks would want to use this new cmd.
Quoted 8 lines
Thanks for clarifying.
Quoted 3 lines
I think the bpf maps will be rcu protected for foreseeable future even when rcu_read_lock/unlock will be done by the programs instead of kernel wrappers.
Can we guarantee that we always obtain a map reference and dispose of that reference inside the same critical section?
yep. the verifier will guarantee that.
Quoted 2 lines
If so, can BPF
programs then disable preemption for as long as they'd like?
you mean after the finish? no. only while running.
The verifier will match things like lookup/release, lock/unlock, preempt on/off and will make sure there is no dangling preempt disable after program returns.
Quoted 145 lines · Alexei Starovoitov
Quoted 134 lines · Daniel Colascione
Quoted 126 lines · Alexei Starovoitov
Quoted 116 lines · Daniel Colascione
Quoted 102 lines · Alexei Starovoitov
Quoted 95 lines · Mathieu Desnoyers
Quoted 73 lines · ----- On Jul 7, 2018, at 4:33 PM, Joel Fernandes [email protected] wrote:
Quoted 36 lines · Alexei Starovoitov
Quoted 35 lines · Daniel Colascione
BPF_SYNCHRONIZEwaits for any BPF programs active at the time ofBPF_SYNCHRONIZEto complete, allowing userspace to ensure atomicity of RCU data structure operations with respect to active programs. For example, userspace can update amap->mapentry to point to a new map, useBPF_SYNCHRONIZEto wait for any BPF programs using the old map to complete, and then drain the old map without fear that BPF programs may still be updating it.Signed-off-by Daniel Colascione <[email protected]>--- include/uapi/linux/bpf.h | 1 + kernel/bpf/syscall.c | 14 ++++++++++++++ 2 files changed, 15 insertions(+)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_SYNCHRONIZE, }; enum bpf_map_type {kernel/bpf/syscall.c +0
-0@@ -2272,6 +2272,20 @@ SYSCALL_DEFINE3(bpf, int, cmd, union bpf_attr __user *,uattr, unsigned int, siz
if (sysctl_unprivileged_bpf_disabled && !capable(CAP_SYS_ADMIN)) return -EPERM;Quoted 15 lines
kernel/bpf/syscall.c
Quoted 12 lines
kernel/bpf/syscall.c +12
-0+if (cmd ==BPF_SYNCHRONIZE ) { +if (uattr !=NULL || size !=0 ) +return -EINVAL ; + err =security_bpf (cmd,NULL ,0 ); +if (err <0 ) +return err; +/* BPF programs are run with preempt disabled, so +* synchronize_sched is sufficient even with +* RCU_PREEMPT. +*/ +synchronize_sched (); +return 0 ;I don't think it's necessary.
sys_membarrier()can do this already and some folks use it exactly for this use case.Alexei, the use of
sys_membarrierfor this purpose seems kind of weird to me though. No where does the manpage say membarrier should be implemented this way so what happens if the implementation changes?Further, membarrier manpage says that a memory barrier should be matched with a matching barrier. In this use case there is no matching barrier, so it makes it weirder.
Lastly,
sys_membarrierseems will not work on nohz-full systems, so its a bit fragile to depend on it for this?case MEMBARRIER_CMD_GLOBAL: /* MEMBARRIER_CMD_GLOBAL is not compatible with nohz_full. */ if (tick_nohz_full_enabled()) return -EINVAL; if (num_online_cpus() > 1) synchronize_sched(); return 0;Quoted 1 line
Adding Mathieu as well who I believe is author/maintainer of membarrier.
See commit 907565337
"Fix: Disablesys_membarrierwhennohz_fullis enabled""Userspace applications should be allowed to expect the membarrier system call with
MEMBARRIER_CMD_SHAREDcommand to issue memory barriers onnohz_fullCPUs, butsynchronize_sched()does not take those into account."So AFAIU you'd want to re-use membarrier to issue
synchronize_sched, and you only care about kernel preempt off critical sections.Clearly bpf code does not run in user-space, so it would "work".
But the guarantees provided by membarrier are not to synchronize against preempt off per se. It's just that the current implementation happens to do that. The point of membarrier is to turn user-space memory barriers into compiler barriers.
Quoted 5 lines
If what you need is to wait for a RCU grace period for whatever RCU flavor ebpf is using, I would against using membarrier for this. I would rather recommend adding a dedicated
BPF_SYNCHRONIZEso you won't leak implementation details to user-space, *and* you can eventually change you RCU implementation for e.g. SRCU in the future if needed.Quoted 11 lines
The point about future changes to underlying bpf mechanisms is valid. There is work already on the way to reduce the scope of preempt_off+rcu_lock that currently lasts the whole prog. We will have new prog types that won't have such wrappers and will do rcu_lock/unlock and preempt on/off only when necessary.
So something likeBPF_SYNCHRONIZEwill break soon, since the kernel cannot have guarantees on when programs finish. Calling this commandBPF_SYNCHRONIZE_PROGalso won't make sense for the same reason.
What we can do it instead is to define synchronization barrier for programs accessing maps. May be call it something like:BPF_SYNC_MAP_ACCESS?Quoted 8 lines
I'm not sure what you're proposing. In the case the commit message describes, a user-space program that wants to "drain" a map needs to be confident that the map won't change under it, even across multiple bpf system calls on that map. One way of doing that is to ensure that nothing that could possibly hold a reference to that map is still running. Are you proposing some kind of refcount-draining approach? Simple locking won't work, since BPF programs can't block, and I don't see right now how a simple barrier would help.
Quoted 5 lines
I'm proposing few changes for your patch:
s/BPF_SYNCHRONIZE/BPF_SYNC_MAP_ACCESS/
and s/synchronize_sched/synchronize_rcu/
with detailed comment inuapi/bpf.hthat has an example why folks would want to use this new cmd.Quoted 8 lines
Thanks for clarifying.
Quoted 3 lines
I think the bpf maps will be rcu protected for foreseeable future even when rcu_read_lock/unlock will be done by the programs instead of kernel wrappers.
Can we guarantee that we always obtain a map reference and dispose of that reference inside the same critical section?
Quoted 9 lines
yep. the verifier will guarantee that.
Quoted 2 lines
If so, can BPF
programs then disable preemption for as long as they'd like?you mean after the finish? no. only while running.
The verifier will match things like lookup/release, lock/unlock, preempt on/off and will make sure there is no dangling preempt disable after program returns.
It might be a silly question but does this new bpf program type provide a way to customize where to hold the rcu_lock/unlock and enable/disable preemption when creating the program? Since the BPF_SYNC_MAP_ACCESS cmd sounds not very useful if it only protect each single map access instead of the whole program. For example, sometimes we have to read the same map multiple times when running a eBPF program and if the userspace changed the map value between two reads, it may cause troubles. It would be great if we can have a RCU lock on a block of eBPF program and in that way I think BPF_SYNC_MAP_ACCESS cmd should be enough in handling userspace kernel racing problems.
Thanks
Chenbo Feng
Quoted 156 lines · Chenbo Feng
Quoted 145 lines · Alexei Starovoitov
Quoted 134 lines · Daniel Colascione
Quoted 126 lines · Alexei Starovoitov
Quoted 116 lines · Daniel Colascione
Quoted 102 lines · Alexei Starovoitov
Quoted 95 lines · Mathieu Desnoyers
Quoted 73 lines · ----- On Jul 7, 2018, at 4:33 PM, Joel Fernandes [email protected] wrote:
Quoted 36 lines · Alexei Starovoitov
Quoted 35 lines · Daniel Colascione
BPF_SYNCHRONIZEwaits for any BPF programs active at the time ofBPF_SYNCHRONIZEto complete, allowing userspace to ensure atomicity of RCU data structure operations with respect to active programs. For example, userspace can update amap->mapentry to point to a new map, useBPF_SYNCHRONIZEto wait for any BPF programs using the old map to complete, and then drain the old map without fear that BPF programs may still be updating it.Signed-off-by Daniel Colascione <[email protected]>--- include/uapi/linux/bpf.h | 1 + kernel/bpf/syscall.c | 14 ++++++++++++++ 2 files changed, 15 insertions(+)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_SYNCHRONIZE, }; enum bpf_map_type {kernel/bpf/syscall.c +0
-0@@ -2272,6 +2272,20 @@ SYSCALL_DEFINE3(bpf, int, cmd, union bpf_attr __user *,uattr, unsigned int, siz
if (sysctl_unprivileged_bpf_disabled && !capable(CAP_SYS_ADMIN)) return -EPERM;Quoted 15 lines
kernel/bpf/syscall.c
Quoted 12 lines
kernel/bpf/syscall.c +12
-0+if (cmd ==BPF_SYNCHRONIZE ) { +if (uattr !=NULL || size !=0 ) +return -EINVAL ; + err =security_bpf (cmd,NULL ,0 ); +if (err <0 ) +return err; +/* BPF programs are run with preempt disabled, so +* synchronize_sched is sufficient even with +* RCU_PREEMPT. +*/ +synchronize_sched (); +return 0 ;I don't think it's necessary.
sys_membarrier()can do this already and some folks use it exactly for this use case.Alexei, the use of
sys_membarrierfor this purpose seems kind of weird to me though. No where does the manpage say membarrier should be implemented this way so what happens if the implementation changes?Further, membarrier manpage says that a memory barrier should be matched with a matching barrier. In this use case there is no matching barrier, so it makes it weirder.
Lastly,
sys_membarrierseems will not work on nohz-full systems, so its a bit fragile to depend on it for this?case MEMBARRIER_CMD_GLOBAL: /* MEMBARRIER_CMD_GLOBAL is not compatible with nohz_full. */ if (tick_nohz_full_enabled()) return -EINVAL; if (num_online_cpus() > 1) synchronize_sched(); return 0;Quoted 1 line
Adding Mathieu as well who I believe is author/maintainer of membarrier.
See commit 907565337
"Fix: Disablesys_membarrierwhennohz_fullis enabled""Userspace applications should be allowed to expect the membarrier system call with
MEMBARRIER_CMD_SHAREDcommand to issue memory barriers onnohz_fullCPUs, butsynchronize_sched()does not take those into account."So AFAIU you'd want to re-use membarrier to issue
synchronize_sched, and you only care about kernel preempt off critical sections.Clearly bpf code does not run in user-space, so it would "work".
But the guarantees provided by membarrier are not to synchronize against preempt off per se. It's just that the current implementation happens to do that. The point of membarrier is to turn user-space memory barriers into compiler barriers.
Quoted 5 lines
If what you need is to wait for a RCU grace period for whatever RCU flavor ebpf is using, I would against using membarrier for this. I would rather recommend adding a dedicated
BPF_SYNCHRONIZEso you won't leak implementation details to user-space, *and* you can eventually change you RCU implementation for e.g. SRCU in the future if needed.Quoted 11 lines
The point about future changes to underlying bpf mechanisms is valid. There is work already on the way to reduce the scope of preempt_off+rcu_lock that currently lasts the whole prog. We will have new prog types that won't have such wrappers and will do rcu_lock/unlock and preempt on/off only when necessary.
So something likeBPF_SYNCHRONIZEwill break soon, since the kernel cannot have guarantees on when programs finish. Calling this commandBPF_SYNCHRONIZE_PROGalso won't make sense for the same reason.
What we can do it instead is to define synchronization barrier for programs accessing maps. May be call it something like:BPF_SYNC_MAP_ACCESS?Quoted 8 lines
I'm not sure what you're proposing. In the case the commit message describes, a user-space program that wants to "drain" a map needs to be confident that the map won't change under it, even across multiple bpf system calls on that map. One way of doing that is to ensure that nothing that could possibly hold a reference to that map is still running. Are you proposing some kind of refcount-draining approach? Simple locking won't work, since BPF programs can't block, and I don't see right now how a simple barrier would help.
Quoted 5 lines
I'm proposing few changes for your patch:
s/BPF_SYNCHRONIZE/BPF_SYNC_MAP_ACCESS/
and s/synchronize_sched/synchronize_rcu/
with detailed comment inuapi/bpf.hthat has an example why folks would want to use this new cmd.Quoted 8 lines
Thanks for clarifying.
Quoted 3 lines
I think the bpf maps will be rcu protected for foreseeable future even when rcu_read_lock/unlock will be done by the programs instead of kernel wrappers.
Can we guarantee that we always obtain a map reference and dispose of that reference inside the same critical section?
Quoted 9 lines
yep. the verifier will guarantee that.
Quoted 2 lines
If so, can BPF
programs then disable preemption for as long as they'd like?you mean after the finish? no. only while running.
The verifier will match things like lookup/release, lock/unlock, preempt on/off and will make sure there is no dangling preempt disable after program returns.
Quoted 10 lines
It might be a silly question but does this new bpf program type provide a way to customize where to hold the rcu_lock/unlock and enable/disable preemption when creating the program? Since the
BPF_SYNC_MAP_ACCESScmd sounds not very useful if it only protect each single map access instead of the whole program. For example, sometimes we have to read the same map multiple times when running a eBPF program and if the userspace changed the map value between two reads, it may cause troubles. It would be great if we can have a RCU lock on a block of eBPF program and in that way I thinkBPF_SYNC_MAP_ACCESScmd should be enough in handling userspace kernel racing problems.
we need to make sure we have detailed description of BPF_SYNC_MAP_ACCESS in uapi/bpf.h, since I feel the confusion regarding its usage is starting already. This new cmd will only make sense for map-in-map type of maps. Expecting that BPF_SYNC_MAP_ACCESS is somehow implies the end of
the program or doing some other map synchronization is not correct. Commit log of this patch got it right:
"""
For example, userspace can update a map->map entry to point to a new map, use BPF_SYNCHRONIZE to wait for any BPF programs using the old map to complete, and then drain the old map without fear that BPF programs may still be updating it.
"""
Now consider two cases:
1. single program does two lookups (inner and outer map) and the program is called twice (as trigger on two events or it was tail-called or through prog_array)
2. future single program without rcu wrappers does:
rcu_lock + outer lookup + inner lookup + rcu_unlock + .. some other bpf stuff .. + rcu_lock + outer lookup + inner lookup + rcu_unlock + .. yet another bpf stuff .. All of the above as part of single program that is called once.
These two cases are the same from the point of view of BPF_SYNC_MAP_ACCESS and completion of this cmd does not guarantee that program finished.
In both cases the user space will be correct to start draining the inner map it replaced after BPF_SYNC_MAP_ACCESS is done
(assuming that implementation does synchronize_rcu I mentioned earlier)
Preemption on/off as done today by prog wrappers or in the future explicitly by the prog code is orthogonal to this new cmd and map-in-map draining.
Quoted 13 lines · Alexei Starovoitov
we need to make sure we have detailed description of
BPF_SYNC_MAP_ACCESSinuapi/bpf.h, since I feel the confusion regarding its usage is starting already. This new cmd will only make sense for map-in-map type of maps. Expecting thatBPF_SYNC_MAP_ACCESSis somehow implies the end of
the program or doing some other map synchronization is not correct. Commit log of this patch got it right:
"""
For example, userspace can update amap->mapentry to point to a new map, useBPF_SYNCHRONIZEto wait for any BPF programs using the old map to complete, and then drain the old map without fear that BPF programs may still be updating it.
"""
+1 for detailed documentation. For example, consider what happens if we have two map fds, one active and one standby, and a map-in-map with one element that contains a pointer to the currently-active map fd. The kernel program might do:
=====
const int current_map_key = 1;
void *current_map = bpf_map_lookup_elem(outer_map, ¤t_map_key);
int stats_key = 42;uint64_t *stats_value = bpf_map_lookup_elem(current_map, &stats_key); __sync_fetch_and_add(&stats_value, 1);
=====
If a userspace does:
1. Write new fd to outer_map[1].
2. Call BPF_SYNC_MAP_ACCESS.
3. Start deleting everything in the old map.
How can we guarantee that the __sync_fetch_and_add will not add to the old map? If it does, we'll lose data. Will the verifier automatically hold the RCU lock for as long as a pointer to an inner map is valid?
Quoted 16 lines · Lorenzo Colitti
Quoted 13 lines · Alexei Starovoitov
we need to make sure we have detailed description of
BPF_SYNC_MAP_ACCESSinuapi/bpf.h, since I feel the confusion regarding its usage is starting already. This new cmd will only make sense for map-in-map type of maps. Expecting thatBPF_SYNC_MAP_ACCESSis somehow implies the end of
the program or doing some other map synchronization is not correct. Commit log of this patch got it right:
"""
For example, userspace can update amap->mapentry to point to a new map, useBPF_SYNCHRONIZEto wait for any BPF programs using the old map to complete, and then drain the old map without fear that BPF programs may still be updating it.
"""
Quoted 3 lines
+1 for detailed documentation. For example, consider what happens if we have two map fds, one active and one standby, and a map-in-map with one element that contains a pointer to the currently-active map fd.
yes. that's exactly the use case that folks use.
Quoted 11 lines
The kernel program might do:
=====
const intcurrent_map_key= 1;
void*current_map=bpf_map_lookup_elem(outer_map, ¤t_map_key);int
stats_key= 42;uint64_t*stats_value=bpf_map_lookup_elem(current_map, &stats_key);__sync_fetch_and_add(&stats_value, 1);
=====
Quoted 8 lines
If a userspace does:
1. Write new fd to outer_map[1].
2. CallBPF_SYNC_MAP_ACCESS.
3. Start deleting everything in the old map.How can we guarantee that the
__sync_fetch_and_addwill not add to the old map?
without any changes to the kernel sys_membarrier will work. And that's what folks use already.BPF_SYNC_MAP_ACCESS implemented via synchronize_rcu() will work as well whether in the current implementation where rcu_lock/unlock is done outside of the program and in the future when rcu_lock/unlock are called by the program itself.
Quoted 2 lines
Will the verifier automatically
hold the RCU lock for as long as a pointer to an inner map is valid?
the verifier will guarantee the equivalency of future explicit lock/unlock by the program vs current situation of implicit lock/unlock by the kernel.
The verifier will track that bpf_map_lookup_elem() is done after rcu_lock and that the value returned by this helper is not accessed after rcu_unlock. Baby steps of dataflow analysis.
On Tue, Jul 10, 2018 at 08:40:19PM -0700, Alexei Starovoitov wrote:
[..]
Quoted 20 lines
Quoted 11 lines
The kernel program might do:
=====
const intcurrent_map_key= 1;
void*current_map=bpf_map_lookup_elem(outer_map, ¤t_map_key);int
stats_key= 42;uint64_t*stats_value=bpf_map_lookup_elem(current_map, &stats_key);__sync_fetch_and_add(&stats_value, 1);
=====Quoted 8 lines
If a userspace does:
1. Write new fd to outer_map[1].
2. CallBPF_SYNC_MAP_ACCESS.
3. Start deleting everything in the old map.How can we guarantee that the
__sync_fetch_and_addwill not add to the old map?
Quoted 6 lines
without any changes to the kernel
sys_membarrierwill work. And that's what folks use already.BPF_SYNC_MAP_ACCESSimplemented viasynchronize_rcu()will work as well whether in the current implementation where rcu_lock/unlock is done outside of the program and in the future when rcu_lock/unlock are called by the program itself.
Cool Alexei and Lorenzo, sounds great to me. Daniel want to send a follow up patch with BPF_SYNC_MAP_ACCESS changes then?
Quoted 9 lines
Quoted 2 lines
Will the verifier automatically
hold the RCU lock for as long as a pointer to an inner map is valid?the verifier will guarantee the equivalency of future explicit lock/unlock by the program vs current situation of implicit lock/unlock by the kernel.
The verifier will track thatbpf_map_lookup_elem()is done afterrcu_lockand that the value returned by this helper is not accessed afterrcu_unlock. Baby steps of dataflow analysis.
Nice!
By the way just curious I was briefly going through kernel/bpf/arraymap.c. How are you protecting against load-store tearing of values of array map updates/lookups?
For example, if userspace reads an array map at a particular index, while another CPU is updating it, then userspace can read partial values / half-updated values right? Since rcu_read_lock is in use, I was hoping to find something like rcu_assign_pointer there to protect readers against concurrent updates. Thanks for any clarification.
Regards,
- Joel
Quoted 29 lines · Joel Fernandes
On Tue, Jul 10, 2018 at 08:40:19PM -0700, Alexei Starovoitov wrote:
[..]Quoted 20 lines
Quoted 11 lines
The kernel program might do:
=====
const intcurrent_map_key= 1;
void*current_map=bpf_map_lookup_elem(outer_map, ¤t_map_key);int
stats_key= 42;uint64_t*stats_value=bpf_map_lookup_elem(current_map, &stats_key);__sync_fetch_and_add(&stats_value, 1);
=====Quoted 8 lines
If a userspace does:
1. Write new fd to outer_map[1].
2. CallBPF_SYNC_MAP_ACCESS.
3. Start deleting everything in the old map.How can we guarantee that the
__sync_fetch_and_addwill not add to the old map?Quoted 6 lines
without any changes to the kernel
sys_membarrierwill work. And that's what folks use already.BPF_SYNC_MAP_ACCESSimplemented viasynchronize_rcu()will work as well whether in the current implementation where rcu_lock/unlock is done outside of the program and in the future when rcu_lock/unlock are called by the program itself.
Quoted 2 lines
Cool Alexei and Lorenzo, sounds great to me. Daniel want to send a follow up patch with
BPF_SYNC_MAP_ACCESSchanges then?
Will do. Mind if I just mine this thread for the doc comment?
Quoted 30 lines · Daniel Colascione
Quoted 29 lines · Joel Fernandes
On Tue, Jul 10, 2018 at 08:40:19PM -0700, Alexei Starovoitov wrote:
[..]Quoted 26 lines
Quoted 11 lines
The kernel program might do:
=====
const intcurrent_map_key= 1;
void*current_map=bpf_map_lookup_elem(outer_map, ¤t_map_key);int
stats_key= 42;uint64_t*stats_value=bpf_map_lookup_elem(current_map, &stats_key);__sync_fetch_and_add(&stats_value, 1);
=====Quoted 8 lines
If a userspace does:
1. Write new fd to outer_map[1].
2. CallBPF_SYNC_MAP_ACCESS.
3. Start deleting everything in the old map.How can we guarantee that the
__sync_fetch_and_addwill not add to the old map?without any changes to the kernel
sys_membarrierwill work. And that's what folks use already.BPF_SYNC_MAP_ACCESSimplemented viasynchronize_rcu()will work as well whether in the current implementation where rcu_lock/unlock is done outside of the program and in the future when rcu_lock/unlock are called by the program itself.
Quoted 4 lines
Quoted 2 lines
Cool Alexei and Lorenzo, sounds great to me. Daniel want to send a follow up patch with
BPF_SYNC_MAP_ACCESSchanges then?Will do. Mind if I just mine this thread for the doc comment?
Do you mean the changelog? Yes I believe you could use the discussion in this thread for the rationale as Alexei described.
thanks,
- Joel
PATCH v2 1/1 Add BPF_SYNCHRONIZE_MAPS bpf(2) command
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 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 | 9 +++++++++ kernel/bpf/syscall.c | 13 +++++++++++++ 2 files changed, 22 insertions(+)
include/uapi/linux/bpf.h +9 -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 +* 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 +106,7 @@ enum bpf_cmd {BPF_BTF_LOAD ,BPF_BTF_GET_FD_BY_ID ,BPF_TASK_FD_QUERY , + BPF_SYNCHRONIZE_MAPS, }; 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_MAPS ) { +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.233.g985f88cf7e-goog
Quoted 9 lines · Joel Fernandes
By the way just curious I was briefly going through
kernel/bpf/arraymap.c. How are you protecting against load-store tearing of values of array map updates/lookups?For example, if userspace reads an array map at a particular index, while another CPU is updating it, then userspace can read partial values / half-updated values right? Since
rcu_read_lockis in use, I was hoping to find something likercu_assign_pointerthere to protect readers against concurrent updates. Thanks for any clarification.
I'm also curious about the answer to this question.
Quoted 80 lines · Mathieu Desnoyers
Quoted 73 lines · ----- On Jul 7, 2018, at 4:33 PM, Joel Fernandes [email protected] wrote:
Quoted 36 lines · Alexei Starovoitov
Quoted 35 lines · Daniel Colascione
BPF_SYNCHRONIZEwaits for any BPF programs active at the time ofBPF_SYNCHRONIZEto complete, allowing userspace to ensure atomicity of RCU data structure operations with respect to active programs. For example, userspace can update amap->mapentry to point to a new map, useBPF_SYNCHRONIZEto wait for any BPF programs using the old map to complete, and then drain the old map without fear that BPF programs may still be updating it.Signed-off-by Daniel Colascione <[email protected]>--- include/uapi/linux/bpf.h | 1 + kernel/bpf/syscall.c | 14 ++++++++++++++ 2 files changed, 15 insertions(+)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_SYNCHRONIZE, }; enum bpf_map_type {kernel/bpf/syscall.c +0
-0@@ -2272,6 +2272,20 @@ SYSCALL_DEFINE3(bpf, int, cmd, union bpf_attr __user *,uattr, unsigned int, siz
kernel/bpf/syscall.c +0
-0if (sysctl_unprivileged_bpf_disabled && !capable (CAP_SYS_ADMIN ))return -EPERM ;Quoted 15 lines
kernel/bpf/syscall.c SYSCALL_DEFINE3(bpf, int, cmd, union bpf_attr __user *, uattr, unsigned int, siz · line 2286
Quoted 12 lines
kernel/bpf/syscall.c +12
-0+if (cmd ==BPF_SYNCHRONIZE ) { +if (uattr !=NULL || size !=0 ) +return -EINVAL ; + err =security_bpf (cmd,NULL ,0 ); +if (err <0 ) +return err; +/* BPF programs are run with preempt disabled, so +* synchronize_sched is sufficient even with +* RCU_PREEMPT. +*/ +synchronize_sched (); +return 0 ;I don't think it's necessary.
sys_membarrier()can do this already and some folks use it exactly for this use case.Alexei, the use of
sys_membarrierfor this purpose seems kind of weird to me though. No where does the manpage say membarrier should be implemented this way so what happens if the implementation changes?Further, membarrier manpage says that a memory barrier should be matched with a matching barrier. In this use case there is no matching barrier, so it makes it weirder.
Lastly,
sys_membarrierseems will not work on nohz-full systems, so its a bit fragile to depend on it for this?case MEMBARRIER_CMD_GLOBAL: /* MEMBARRIER_CMD_GLOBAL is not compatible with nohz_full. */ if (tick_nohz_full_enabled()) return -EINVAL; if (num_online_cpus() > 1) synchronize_sched(); return 0;Quoted 1 line
Adding Mathieu as well who I believe is author/maintainer of membarrier.
See commit 907565337
"Fix: Disablesys_membarrierwhennohz_fullis enabled"
Quoted 7 lines
"Userspace applications should be allowed to expect the membarrier system call with
MEMBARRIER_CMD_SHAREDcommand to issue memory barriers onnohz_fullCPUs, butsynchronize_sched()does not take those into account."So AFAIU you'd want to re-use membarrier to issue
synchronize_sched, and you only care about kernel preempt off critical sections.
Mathieu, Thanks a lot for your reply. I understand what you said and agree with you. Slight OT, but I tried to go back to first principles and understand how membarrier() uses synchronize_sched() for the "slow path" and it didn't make immediate sense to me. Let me clarify my dillema..
My understanding is membarrier's MEMBARRIER_CMD_GLOBAL will employ synchronize_sched to make sure all other CPUs aren't executing anymore in an section of usercode that happen to be accessing memory that was written to before the membarrier call was made. To do this, the system call will use synchronize_sched to try to guarantee that all user-mode execution that started before the membarrier call would be completed when the membarrier call returns. This guarantees that without using a real memory barrier on the "fast path", things work just fine and everyone wins.
But, going through RCU code, I see that a "RCU-sched quiecent state" on a CPU may be reached when the CPU receives a timer tick while executing in user mode:
void rcu_check_callbacks(int user)
{
trace_rcu_utilization(TPS("Start scheduler-tick"));
increment_cpu_stall_ticks();
if (user || rcu_is_cpu_rrupt_from_idle()) {
[...]
rcu_sched_qs();
rcu_bh_qs();The problem I see is the CPU could be executing usermode code at the time of the RCU sched-QS. This IMO is enough reason for synchronize_sched() to return, because the CPU in question just reported a QS (assuming all other CPUs also happen to do so if they needed to).
Then I am wondering how does the membarrier call even work, the tick could very well have interrupted the CPU while it was executing usermode code in the middle of a set of instructions performing memory accesses. Reporting a quiescent state at such an inopportune time would cause the membarrier call to prematurely return, no? Sorry if I missed something.
The other question I have is about the whole "nohz-full doesn't work" thing. I didn't fully understand why. RCU is already tracking the state of nohz-full CPUs because the rcu dynticks code in (kernel/rcu/tree.c) monitors transitions to and from usermode even if the timer tick is turned off. So why would it not work?
thanks a lot!
- Joel
Quoted 116 lines · Joel Fernandes
Quoted 80 lines · Mathieu Desnoyers
Quoted 73 lines · ----- On Jul 7, 2018, at 4:33 PM, Joel Fernandes [email protected] wrote:
Quoted 36 lines · Alexei Starovoitov
Quoted 35 lines · Daniel Colascione
BPF_SYNCHRONIZEwaits for any BPF programs active at the time ofBPF_SYNCHRONIZEto complete, allowing userspace to ensure atomicity of RCU data structure operations with respect to active programs. For example, userspace can update amap->mapentry to point to a new map, useBPF_SYNCHRONIZEto wait for any BPF programs using the old map to complete, and then drain the old map without fear that BPF programs may still be updating it.Signed-off-by Daniel Colascione <[email protected]>--- include/uapi/linux/bpf.h | 1 + kernel/bpf/syscall.c | 14 ++++++++++++++ 2 files changed, 15 insertions(+)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_SYNCHRONIZE, }; enum bpf_map_type {kernel/bpf/syscall.c +0
-0@@ -2272,6 +2272,20 @@ SYSCALL_DEFINE3(bpf, int, cmd, union bpf_attr __user *,uattr, unsigned int, siz
kernel/bpf/syscall.c +0
-0if (sysctl_unprivileged_bpf_disabled && !capable (CAP_SYS_ADMIN ))return -EPERM ;Quoted 15 lines
kernel/bpf/syscall.c SYSCALL_DEFINE3(bpf, int, cmd, union bpf_attr __user *, uattr, unsigned int, siz · line 2286
Quoted 12 lines
kernel/bpf/syscall.c +12
-0+if (cmd ==BPF_SYNCHRONIZE ) { +if (uattr !=NULL || size !=0 ) +return -EINVAL ; + err =security_bpf (cmd,NULL ,0 ); +if (err <0 ) +return err; +/* BPF programs are run with preempt disabled, so +* synchronize_sched is sufficient even with +* RCU_PREEMPT. +*/ +synchronize_sched (); +return 0 ;I don't think it's necessary.
sys_membarrier()can do this already and some folks use it exactly for this use case.Alexei, the use of
sys_membarrierfor this purpose seems kind of weird to me though. No where does the manpage say membarrier should be implemented this way so what happens if the implementation changes?Further, membarrier manpage says that a memory barrier should be matched with a matching barrier. In this use case there is no matching barrier, so it makes it weirder.
Lastly,
sys_membarrierseems will not work on nohz-full systems, so its a bit fragile to depend on it for this?case MEMBARRIER_CMD_GLOBAL: /* MEMBARRIER_CMD_GLOBAL is not compatible with nohz_full. */ if (tick_nohz_full_enabled()) return -EINVAL; if (num_online_cpus() > 1) synchronize_sched(); return 0;Quoted 1 line
Adding Mathieu as well who I believe is author/maintainer of membarrier.
See commit 907565337
"Fix: Disablesys_membarrierwhennohz_fullis enabled"Quoted 7 lines
"Userspace applications should be allowed to expect the membarrier system call with
MEMBARRIER_CMD_SHAREDcommand to issue memory barriers onnohz_fullCPUs, butsynchronize_sched()does not take those into account."So AFAIU you'd want to re-use membarrier to issue
synchronize_sched, and you only care about kernel preempt off critical sections.Mathieu, Thanks a lot for your reply. I understand what you said and agree with you. Slight OT, but I tried to go back to first principles and understand how
membarrier()usessynchronize_sched()for the "slow path" and it didn't make immediate sense to me. Let me clarify my dillema..My understanding is membarrier's
MEMBARRIER_CMD_GLOBALwill employsynchronize_schedto make sure all other CPUs aren't executing anymore in an section of usercode that happen to be accessing memory that was written to before the membarrier call was made. To do this, the system call will usesynchronize_schedto try to guarantee that all user-mode execution that started before the membarrier call would be completed when the membarrier call returns. This guarantees that without using a real memory barrier on the "fast path", things work just fine and everyone wins.But, going through RCU code, I see that a "RCU-sched quiecent state" on a CPU may be reached when the CPU receives a timer tick while executing in user mode:
void
rcu_check_callbacks(int user){ trace_rcu_utilization(TPS("Start scheduler-tick")); increment_cpu_stall_ticks(); if (user || rcu_is_cpu_rrupt_from_idle()) { [...] rcu_sched_qs(); rcu_bh_qs();
Quoted 4 lines
The problem I see is the CPU could be executing usermode code at the time of the RCU sched-QS. This IMO is enough reason for
synchronize_sched()to return, because the CPU in question just reported a QS (assuming all other CPUs also happen to do so if they needed to).
This scenario will have inserted the needed smp_mb() into the userspace instruction execution stream, as is required by the sys_membarrier use cases.
Quoted 5 lines
Then I am wondering how does the membarrier call even work, the tick could very well have interrupted the CPU while it was executing usermode code in the middle of a set of instructions performing memory accesses. Reporting a quiescent state at such an inopportune time would cause the membarrier call to prematurely return, no? Sorry if I missed something.
One way to think of sys_membarrier() is as something that promotes a barrier() to an smp_mb(). This barrier then separates the target CPU's accesses that the caller saw before the sys_membarrier() from that same CPU's accesses that the caller will see after the sys_membarrier().
Quoted 5 lines
The other question I have is about the whole "nohz-full doesn't work" thing. I didn't fully understand why. RCU is already tracking the state of nohz-full CPUs because the rcu dynticks code in (
kernel/rcu/tree.c) monitors transitions to and from usermode even if the timer tick is turned off. So why would it not work?
In the nohz_full case, there is no need for sys_membarrier()'s call to synchronize_sched() to interact directly with the nohz_full CPU. It can instead look at the target CPU's dyntick-idle state, and that state would potentially have been set in the dim distant past, thus having no effect on the target CPU's current execution.
Thanx, Paul
Quoted 117 lines · Paul E. McKenney
Quoted 116 lines · Joel Fernandes
Quoted 80 lines · Mathieu Desnoyers
Quoted 73 lines · ----- On Jul 7, 2018, at 4:33 PM, Joel Fernandes [email protected] wrote:
Quoted 36 lines · Alexei Starovoitov
Quoted 35 lines · Daniel Colascione
BPF_SYNCHRONIZEwaits for any BPF programs active at the time ofBPF_SYNCHRONIZEto complete, allowing userspace to ensure atomicity of RCU data structure operations with respect to active programs. For example, userspace can update amap->mapentry to point to a new map, useBPF_SYNCHRONIZEto wait for any BPF programs using the old map to complete, and then drain the old map without fear that BPF programs may still be updating it.Signed-off-by Daniel Colascione <[email protected]>--- include/uapi/linux/bpf.h | 1 + kernel/bpf/syscall.c | 14 ++++++++++++++ 2 files changed, 15 insertions(+)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_SYNCHRONIZE, }; enum bpf_map_type {kernel/bpf/syscall.c +0
-0@@ -2272,6 +2272,20 @@ SYSCALL_DEFINE3(bpf, int, cmd, union bpf_attr __user *,uattr, unsigned int, siz
kernel/bpf/syscall.c +0
-0if (sysctl_unprivileged_bpf_disabled && !capable (CAP_SYS_ADMIN ))return -EPERM ;Quoted 15 lines
kernel/bpf/syscall.c SYSCALL_DEFINE3(bpf, int, cmd, union bpf_attr __user *, uattr, unsigned int, siz · line 2286
Quoted 12 lines
kernel/bpf/syscall.c +12
-0+if (cmd ==BPF_SYNCHRONIZE ) { +if (uattr !=NULL || size !=0 ) +return -EINVAL ; + err =security_bpf (cmd,NULL ,0 ); +if (err <0 ) +return err; +/* BPF programs are run with preempt disabled, so +* synchronize_sched is sufficient even with +* RCU_PREEMPT. +*/ +synchronize_sched (); +return 0 ;I don't think it's necessary.
sys_membarrier()can do this already and some folks use it exactly for this use case.Alexei, the use of
sys_membarrierfor this purpose seems kind of weird to me though. No where does the manpage say membarrier should be implemented this way so what happens if the implementation changes?Further, membarrier manpage says that a memory barrier should be matched with a matching barrier. In this use case there is no matching barrier, so it makes it weirder.
Lastly,
sys_membarrierseems will not work on nohz-full systems, so its a bit fragile to depend on it for this?case MEMBARRIER_CMD_GLOBAL: /* MEMBARRIER_CMD_GLOBAL is not compatible with nohz_full. */ if (tick_nohz_full_enabled()) return -EINVAL; if (num_online_cpus() > 1) synchronize_sched(); return 0;Quoted 1 line
Adding Mathieu as well who I believe is author/maintainer of membarrier.
See commit 907565337
"Fix: Disablesys_membarrierwhennohz_fullis enabled"Quoted 7 lines
"Userspace applications should be allowed to expect the membarrier system call with
MEMBARRIER_CMD_SHAREDcommand to issue memory barriers onnohz_fullCPUs, butsynchronize_sched()does not take those into account."So AFAIU you'd want to re-use membarrier to issue
synchronize_sched, and you only care about kernel preempt off critical sections.Mathieu, Thanks a lot for your reply. I understand what you said and agree with you. Slight OT, but I tried to go back to first principles and understand how
membarrier()usessynchronize_sched()for the "slow path" and it didn't make immediate sense to me. Let me clarify my dillema..My understanding is membarrier's
MEMBARRIER_CMD_GLOBALwill employsynchronize_schedto make sure all other CPUs aren't executing anymore in an section of usercode that happen to be accessing memory that was written to before the membarrier call was made. To do this, the system call will usesynchronize_schedto try to guarantee that all user-mode execution that started before the membarrier call would be completed when the membarrier call returns. This guarantees that without using a real memory barrier on the "fast path", things work just fine and everyone wins.But, going through RCU code, I see that a "RCU-sched quiecent state" on a CPU may be reached when the CPU receives a timer tick while executing in user mode:
void
rcu_check_callbacks(int user){ trace_rcu_utilization(TPS("Start scheduler-tick")); increment_cpu_stall_ticks(); if (user || rcu_is_cpu_rrupt_from_idle()) { [...] rcu_sched_qs(); rcu_bh_qs();
Quoted 8 lines
Quoted 4 lines
The problem I see is the CPU could be executing usermode code at the time of the RCU sched-QS. This IMO is enough reason for
synchronize_sched()to return, because the CPU in question just reported a QS (assuming all other CPUs also happen to do so if they needed to).This scenario will have inserted the needed
smp_mb()into the userspace instruction execution stream, as is required by thesys_membarrieruse cases.
Oh ok, that makes sense!
Quoted 10 lines
Quoted 5 lines
Then I am wondering how does the membarrier call even work, the tick could very well have interrupted the CPU while it was executing usermode code in the middle of a set of instructions performing memory accesses. Reporting a quiescent state at such an inopportune time would cause the membarrier call to prematurely return, no? Sorry if I missed something.
One way to think of
sys_membarrier()is as something that promotes abarrier()to ansmp_mb(). This barrier then separates the target CPU's accesses that the caller saw before thesys_membarrier()from that same CPU's accesses that the caller will see after thesys_membarrier().
Got it!
Quoted 11 lines
Quoted 5 lines
The other question I have is about the whole "nohz-full doesn't work" thing. I didn't fully understand why. RCU is already tracking the state of nohz-full CPUs because the rcu dynticks code in (
kernel/rcu/tree.c) monitors transitions to and from usermode even if the timer tick is turned off. So why would it not work?In the
nohz_fullcase, there is no need for sys_membarrier()'s call tosynchronize_sched()to interact directly with thenohz_fullCPU. It can instead look at the target CPU's dyntick-idle state, and that state would potentially have been set in the dim distant past, thus having no effect on the target CPU's current execution.
In nohz-idle case though, there's nothing to promote the barrier() to smp_mb() if you were to purely look at the dynticks-idle state on the nohz-full CPU executing in user mode?
So then it makes sense to me now that nohz-full needs something to IPI that CPU inorder to enforce the needed memory barrier and pure synchronize_sched() wouldn't work. So then makes me think the expedited versions of synchronize_sched should be able to do the job but I could off on a different track..
Thanks a lot,
-Joel
Quoted 158 lines · Joel Fernandes
Quoted 117 lines · Paul E. McKenney
Quoted 116 lines · Joel Fernandes
Quoted 80 lines · Mathieu Desnoyers
Quoted 73 lines · ----- On Jul 7, 2018, at 4:33 PM, Joel Fernandes [email protected] wrote:
Quoted 36 lines · Alexei Starovoitov
Quoted 35 lines · Daniel Colascione
BPF_SYNCHRONIZEwaits for any BPF programs active at the time ofBPF_SYNCHRONIZEto complete, allowing userspace to ensure atomicity of RCU data structure operations with respect to active programs. For example, userspace can update amap->mapentry to point to a new map, useBPF_SYNCHRONIZEto wait for any BPF programs using the old map to complete, and then drain the old map without fear that BPF programs may still be updating it.Signed-off-by Daniel Colascione <[email protected]>--- include/uapi/linux/bpf.h | 1 + kernel/bpf/syscall.c | 14 ++++++++++++++ 2 files changed, 15 insertions(+)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_SYNCHRONIZE, }; enum bpf_map_type {kernel/bpf/syscall.c +0
-0@@ -2272,6 +2272,20 @@ SYSCALL_DEFINE3(bpf, int, cmd, union bpf_attr __user *,uattr, unsigned int, siz
kernel/bpf/syscall.c +0
-0if (sysctl_unprivileged_bpf_disabled && !capable (CAP_SYS_ADMIN ))return -EPERM ;Quoted 15 lines
kernel/bpf/syscall.c
Quoted 12 lines
kernel/bpf/syscall.c +12
-0+if (cmd ==BPF_SYNCHRONIZE ) { +if (uattr !=NULL || size !=0 ) +return -EINVAL ; + err =security_bpf (cmd,NULL ,0 ); +if (err <0 ) +return err; +/* BPF programs are run with preempt disabled, so +* synchronize_sched is sufficient even with +* RCU_PREEMPT. +*/ +synchronize_sched (); +return 0 ;I don't think it's necessary.
sys_membarrier()can do this already and some folks use it exactly for this use case.Alexei, the use of
sys_membarrierfor this purpose seems kind of weird to me though. No where does the manpage say membarrier should be implemented this way so what happens if the implementation changes?Further, membarrier manpage says that a memory barrier should be matched with a matching barrier. In this use case there is no matching barrier, so it makes it weirder.
Lastly,
sys_membarrierseems will not work on nohz-full systems, so its a bit fragile to depend on it for this?case MEMBARRIER_CMD_GLOBAL: /* MEMBARRIER_CMD_GLOBAL is not compatible with nohz_full. */ if (tick_nohz_full_enabled()) return -EINVAL; if (num_online_cpus() > 1) synchronize_sched(); return 0;Quoted 1 line
Adding Mathieu as well who I believe is author/maintainer of membarrier.
See commit 907565337
"Fix: Disablesys_membarrierwhennohz_fullis enabled"Quoted 7 lines
"Userspace applications should be allowed to expect the membarrier system call with
MEMBARRIER_CMD_SHAREDcommand to issue memory barriers onnohz_fullCPUs, butsynchronize_sched()does not take those into account."So AFAIU you'd want to re-use membarrier to issue
synchronize_sched, and you only care about kernel preempt off critical sections.Mathieu, Thanks a lot for your reply. I understand what you said and agree with you. Slight OT, but I tried to go back to first principles and understand how
membarrier()usessynchronize_sched()for the "slow path" and it didn't make immediate sense to me. Let me clarify my dillema..My understanding is membarrier's
MEMBARRIER_CMD_GLOBALwill employsynchronize_schedto make sure all other CPUs aren't executing anymore in an section of usercode that happen to be accessing memory that was written to before the membarrier call was made. To do this, the system call will usesynchronize_schedto try to guarantee that all user-mode execution that started before the membarrier call would be completed when the membarrier call returns. This guarantees that without using a real memory barrier on the "fast path", things work just fine and everyone wins.But, going through RCU code, I see that a "RCU-sched quiecent state" on a CPU may be reached when the CPU receives a timer tick while executing in user mode:
void
rcu_check_callbacks(int user){ trace_rcu_utilization(TPS("Start scheduler-tick")); increment_cpu_stall_ticks(); if (user || rcu_is_cpu_rrupt_from_idle()) { [...] rcu_sched_qs(); rcu_bh_qs();Quoted 8 lines
Quoted 4 lines
The problem I see is the CPU could be executing usermode code at the time of the RCU sched-QS. This IMO is enough reason for
synchronize_sched()to return, because the CPU in question just reported a QS (assuming all other CPUs also happen to do so if they needed to).This scenario will have inserted the needed
smp_mb()into the userspace instruction execution stream, as is required by thesys_membarrieruse cases.Oh ok, that makes sense!
Quoted 10 lines
Quoted 5 lines
Then I am wondering how does the membarrier call even work, the tick could very well have interrupted the CPU while it was executing usermode code in the middle of a set of instructions performing memory accesses. Reporting a quiescent state at such an inopportune time would cause the membarrier call to prematurely return, no? Sorry if I missed something.
One way to think of
sys_membarrier()is as something that promotes abarrier()to ansmp_mb(). This barrier then separates the target CPU's accesses that the caller saw before thesys_membarrier()from that same CPU's accesses that the caller will see after thesys_membarrier().Got it!
Quoted 11 lines
Quoted 5 lines
The other question I have is about the whole "nohz-full doesn't work" thing. I didn't fully understand why. RCU is already tracking the state of nohz-full CPUs because the rcu dynticks code in (
kernel/rcu/tree.c) monitors transitions to and from usermode even if the timer tick is turned off. So why would it not work?In the
nohz_fullcase, there is no need for sys_membarrier()'s call tosynchronize_sched()to interact directly with thenohz_fullCPU. It can instead look at the target CPU's dyntick-idle state, and that state would potentially have been set in the dim distant past, thus having no effect on the target CPU's current execution.In nohz-idle case though, there's nothing to promote the
barrier()tosmp_mb()if you were to purely look at the dynticks-idle state on the nohz-full CPU executing in user mode?
Quoted 5 lines
So then it makes sense to me now that nohz-full needs something to IPI that CPU inorder to enforce the needed memory barrier and pure
synchronize_sched()wouldn't work. So then makes me think the expedited versions ofsynchronize_schedshould be able to do the job but I could off on a different track..
The problem is that the expedited versions also check the dyntick-idle state and don't touch idle (or nohz_full usermode) CPUs. This is by design for the battery-powered embedded use case. ;-)
Thanx, Paul
Quoted 5 lines
Thanks a lot,
-Joel
On Tue, Jul 10, 2018 at 10:12:29AM -0700, Paul E. McKenney wrote:
[..]
Quoted 22 lines
Quoted 16 lines
Quoted 11 lines
Quoted 5 lines
The other question I have is about the whole "nohz-full doesn't work" thing. I didn't fully understand why. RCU is already tracking the state of nohz-full CPUs because the rcu dynticks code in (
kernel/rcu/tree.c) monitors transitions to and from usermode even if the timer tick is turned off. So why would it not work?In the
nohz_fullcase, there is no need for sys_membarrier()'s call tosynchronize_sched()to interact directly with thenohz_fullCPU. It can instead look at the target CPU's dyntick-idle state, and that state would potentially have been set in the dim distant past, thus having no effect on the target CPU's current execution.In nohz-idle case though, there's nothing to promote the
barrier()tosmp_mb()if you were to purely look at the dynticks-idle state on the nohz-full CPU executing in user mode?Quoted 5 lines
So then it makes sense to me now that nohz-full needs something to IPI that CPU inorder to enforce the needed memory barrier and pure
synchronize_sched()wouldn't work. So then makes me think the expedited versions ofsynchronize_schedshould be able to do the job but I could off on a different track..
Quoted 3 lines
The problem is that the expedited versions also check the dyntick-idle state and don't touch idle (or
nohz_fullusermode) CPUs. This is by design for the battery-powered embedded use case. ;-)
Oh ok! ;)
I guess there's also a MEMBARRIER_CMD_GLOBAL_EXPEDITED which seems to IPI CPUs (I'm guessing regardless of dynticks state) and execute smp_mb within the IPI so userspace can fallback to using that incase MEMBARRIER_CMD_GLOBAL returns -EINVAL.
thanks!
-Joel
Quoted 28 lines · Joel Fernandes
On Tue, Jul 10, 2018 at 10:12:29AM -0700, Paul E. McKenney wrote:
[..]Quoted 22 lines
Quoted 16 lines
Quoted 11 lines
Quoted 5 lines
The other question I have is about the whole "nohz-full doesn't work" thing. I didn't fully understand why. RCU is already tracking the state of nohz-full CPUs because the rcu dynticks code in (
kernel/rcu/tree.c) monitors transitions to and from usermode even if the timer tick is turned off. So why would it not work?In the
nohz_fullcase, there is no need for sys_membarrier()'s call tosynchronize_sched()to interact directly with thenohz_fullCPU. It can instead look at the target CPU's dyntick-idle state, and that state would potentially have been set in the dim distant past, thus having no effect on the target CPU's current execution.In nohz-idle case though, there's nothing to promote the
barrier()tosmp_mb()if you were to purely look at the dynticks-idle state on the nohz-full CPU executing in user mode?Quoted 5 lines
So then it makes sense to me now that nohz-full needs something to IPI that CPU inorder to enforce the needed memory barrier and pure
synchronize_sched()wouldn't work. So then makes me think the expedited versions ofsynchronize_schedshould be able to do the job but I could off on a different track..Quoted 3 lines
The problem is that the expedited versions also check the dyntick-idle state and don't touch idle (or
nohz_fullusermode) CPUs. This is by design for the battery-powered embedded use case. ;-)
Quoted 6 lines
Oh ok! ;)
I guess there's also a
MEMBARRIER_CMD_GLOBAL_EXPEDITEDwhich seems to IPI CPUs (I'm guessing regardless of dynticks state) and executesmp_mbwithin the IPI so userspace can fallback to using that incaseMEMBARRIER_CMD_GLOBALreturns-EINVAL.
Yes, and this avoids IPIing idle CPUs via the ->mm checks. But it will IPI nohz_full CPUs in that same process, as it must for correctness.
Thanx, Paul