From: Daniel Borkmann <daniel@iogearbox.net> Date: 2018-10-17 22:39:42
This set first adds smp_* barrier variants to tools infrastructure
and in a second step updates perf and libbpf to make use of them.
For details, please see individual patches, thanks!
Arnaldo, if there are no objections, could this be routed via bpf-next
with Acked-by's due to later dependencies in libbpf? Alternatively,
I could also get the 2nd patch out during merge window, but perhaps
it's okay to do in one go as there shouldn't be much conflict in perf.
Thanks!
Daniel Borkmann (3):
tools: add smp_* barrier variants to include infrastructure
tools, perf: use smp_{rmb,mb} barriers instead of {rmb,mb}
bpf, libbpf: use proper barriers in perf ring buffer walk
tools/arch/arm64/include/asm/barrier.h | 10 ++++++++++
tools/arch/x86/include/asm/barrier.h | 9 ++++++---
tools/include/asm/barrier.h | 11 +++++++++++
tools/lib/bpf/libbpf.c | 25 +++++++++++++++++++------
tools/perf/util/mmap.h | 5 +++--
5 files changed, 49 insertions(+), 11 deletions(-)
--
2.9.5
From: Daniel Borkmann <daniel@iogearbox.net> Date: 2018-10-17 22:39:42
Add the definition for smp_rmb(), smp_wmb(), and smp_mb() to the
tools include infrastructure. This patch adds the implementation
for x86-64 and arm64, and have it fall back for other archs which
do not have it implemented at this point such that others can be
added successively for those who have access to test machines. The
x86-64 one uses lock + add combination for smp_mb() with address
below red zone.
Signed-off-by: Daniel Borkmann <daniel@iogearbox.net>
Cc: Peter Zijlstra <peterz@infradead.org>
Cc: "Paul E. McKenney" <redacted>
Cc: Will Deacon <redacted>
Cc: Arnaldo Carvalho de Melo <redacted>
---
tools/arch/arm64/include/asm/barrier.h | 10 ++++++++++
tools/arch/x86/include/asm/barrier.h | 9 ++++++---
tools/include/asm/barrier.h | 11 +++++++++++
3 files changed, 27 insertions(+), 3 deletions(-)
From: Daniel Borkmann <daniel@iogearbox.net> Date: 2018-10-17 22:39:42
Switch both rmb()/mb() barriers to more lightweight smp_rmb()/smp_mb()
ones. When walking the perf ring buffer they pair the following way,
quoting kernel/events/ring_buffer.c:
Since the mmap() consumer (userspace) can run on a different CPU:
kernel user
if (LOAD ->data_tail) { LOAD ->data_head
(A) smp_rmb() (C)
STORE $data LOAD $data
smp_wmb() (B) smp_mb() (D)
STORE ->data_head STORE ->data_tail
}
Where A pairs with D, and B pairs with C.
In our case (A) is a control dependency that separates the load
of the ->data_tail and the stores of $data. In case ->data_tail
indicates there is no room in the buffer to store $data we do not.
D needs to be a full barrier since it separates the data READ from
the tail WRITE.
For B a WMB is sufficient since it separates two WRITEs, and for C
an RMB is sufficient since it separates two READs.
Currently, on x86-64, perf uses LFENCE and MFENCE which is overkill
as we can do more lightweight in particular given this is fast-path.
According to Peter rmb()/mb() were added back then via a94d342b9cb0
("tools/perf: Add required memory barriers") at a time where kernel
still supported chips that needed it, but nowadays support for these
has been ditched completely, therefore we can fix them up as well.
Signed-off-by: Daniel Borkmann <daniel@iogearbox.net>
Cc: Peter Zijlstra <peterz@infradead.org>
Cc: "Paul E. McKenney" <redacted>
Cc: Will Deacon <redacted>
Cc: Arnaldo Carvalho de Melo <redacted>
---
tools/perf/util/mmap.h | 5 +++--
1 file changed, 3 insertions(+), 2 deletions(-)
From: Daniel Borkmann <daniel@iogearbox.net> Date: 2018-10-17 22:39:43
Add bpf_perf_read_head() and bpf_perf_write_tail() helpers to make it
more clear in what context barriers are used here, and use smp_rmb()
as well as smp_mb() barriers. Given libbpf is not restricted to x86-64
only, the compiler barrier needs to be replaced with smp_rmb(). Also
the __sync_synchronize() emits mfence whereas faster lock + add can
be used on x86-64 via smp_mb().
Fixes: d0cabbb021be ("tools: bpf: move the event reading loop to libbpf")
Fixes: 39111695b1b8 ("samples: bpf: add bpf_perf_event_output example")
Signed-off-by: Daniel Borkmann <daniel@iogearbox.net>
---
tools/lib/bpf/libbpf.c | 25 +++++++++++++++++++------
1 file changed, 19 insertions(+), 6 deletions(-)
@@ -2413,18 +2414,32 @@ int bpf_prog_load_xattr(const struct bpf_prog_load_attr *attr,return0;}+static__u64bpf_perf_read_head(structperf_event_mmap_page*header)+{+__u64data_head=READ_ONCE(header->data_head);++smp_rmb();+returndata_head;+}++staticvoidbpf_perf_write_tail(structperf_event_mmap_page*header,+__u64data_tail)+{+smp_mb();+header->data_tail=data_tail;+}+enumbpf_perf_event_retbpf_perf_event_read_simple(void*mem,unsignedlongsize,unsignedlongpage_size,void**buf,size_t*buf_len,bpf_perf_event_print_tfn,void*priv){-volatilestructperf_event_mmap_page*header=mem;+structperf_event_mmap_page*header=mem;+__u64data_head=bpf_perf_read_head(header);__u64data_tail=header->data_tail;-__u64data_head=header->data_head;intret=LIBBPF_PERF_EVENT_ERROR;void*base,*begin,*end;-asmvolatile("":::"memory");/* in real code it should be smp_rmb() */if(data_head==data_tail)returnLIBBPF_PERF_EVENT_CONT;
From: Arnaldo Carvalho de Melo <hidden> Date: 2018-10-17 22:59:10
Em Wed, Oct 17, 2018 at 04:41:53PM +0200, Daniel Borkmann escreveu:
This set first adds smp_* barrier variants to tools infrastructure
and in a second step updates perf and libbpf to make use of them.
For details, please see individual patches, thanks!
Arnaldo, if there are no objections, could this be routed via bpf-next
with Acked-by's due to later dependencies in libbpf? Alternatively,
I could also get the 2nd patch out during merge window, but perhaps
it's okay to do in one go as there shouldn't be much conflict in perf.
Right, when updating kernel/events/ring_buffer.c the corresponding
code in tools/ should've been changed :-)
Acked-by: Arnaldo Carvalho de Melo <redacted>
- Arnaldo
Thanks!
Daniel Borkmann (3):
tools: add smp_* barrier variants to include infrastructure
tools, perf: use smp_{rmb,mb} barriers instead of {rmb,mb}
bpf, libbpf: use proper barriers in perf ring buffer walk
tools/arch/arm64/include/asm/barrier.h | 10 ++++++++++
tools/arch/x86/include/asm/barrier.h | 9 ++++++---
tools/include/asm/barrier.h | 11 +++++++++++
tools/lib/bpf/libbpf.c | 25 +++++++++++++++++++------
tools/perf/util/mmap.h | 5 +++--
5 files changed, 49 insertions(+), 11 deletions(-)
--
2.9.5
@@ -84,7 +85,7 @@ static inline void perf_mmap__write_tail(struct perf_mmap *md, u64 tail) /* * ensure all reads are done before we write the tail out. */- mb();+ smp_mb(); pc->data_tail = tail;
Ideally that would be a WRITE_ONCE() to avoid store tearing.
Alternatively, I think we can use smp_store_release() here, all we care
about is that the prior loads stay prior.
Similarly, I suppose, we could use smp_load_acquire() for the data_head
load above.
@@ -84,7 +85,7 @@ static inline void perf_mmap__write_tail(struct perf_mmap *md, u64 tail) /* * ensure all reads are done before we write the tail out. */- mb();+ smp_mb(); pc->data_tail = tail;
Ideally that would be a WRITE_ONCE() to avoid store tearing.
Right, agree.
Alternatively, I think we can use smp_store_release() here, all we care
about is that the prior loads stay prior.
Similarly, I suppose, we could use smp_load_acquire() for the data_head
load above.
Wouldn't this then also allow the kernel side to use smp_store_release()
when it updates the head? We'd be pretty much at the model as described
in Documentation/core-api/circular-buffers.rst.
Meaning, rough pseudo-code diff would look as:
From: Peter Zijlstra <peterz@infradead.org> Date: 2018-10-18 16:14:45
On Thu, Oct 18, 2018 at 01:10:15AM +0200, Daniel Borkmann wrote:
quoted hunk
Wouldn't this then also allow the kernel side to use smp_store_release()
when it updates the head? We'd be pretty much at the model as described
in Documentation/core-api/circular-buffers.rst.
Meaning, rough pseudo-code diff would look as:
@@ -84,8 +84,9 @@ static void perf_output_put_handle(struct perf_output_handle *handle)**Seeperf_output_begin().*/-smp_wmb();/* B, matches C */-rb->user_page->data_head=head;++/* B, matches C */+smp_store_release(&rb->user_page->data_head,head);
Yes, this would be correct.
The reason we didn't do this is because smp_store_release() ends up
being smp_mb() + WRITE_ONCE() for a fair number of platforms, even if
they have a cheaper smp_wmb(). Most notably ARM.
(ARM64 OTOH would like to have smp_store_release() there I imagine;
while x86 doesn't care either way around).
A similar concern exists for the smp_load_acquire() I proposed for the
userspace side, ARM would have to resort to smp_mb() in that situation,
instead of the cheaper smp_rmb().
The smp_store_release() on the userspace side will actually be of equal
cost or cheaper, since it already has an smp_mb(). Most notably, x86 can
avoid barrier entirely, because TSO doesn't allow the LOAD-STORE reorder
(it only allows the STORE-LOAD reorder). And PowerPC can use LWSYNC
instead of SYNC.
From: Daniel Borkmann <daniel@iogearbox.net> Date: 2018-10-18 23:07:41
On 10/18/2018 10:14 AM, Peter Zijlstra wrote:
On Thu, Oct 18, 2018 at 01:10:15AM +0200, Daniel Borkmann wrote:
quoted
Wouldn't this then also allow the kernel side to use smp_store_release()
when it updates the head? We'd be pretty much at the model as described
in Documentation/core-api/circular-buffers.rst.
Meaning, rough pseudo-code diff would look as:
@@ -84,8 +84,9 @@ static void perf_output_put_handle(struct perf_output_handle *handle)**Seeperf_output_begin().*/-smp_wmb();/* B, matches C */-rb->user_page->data_head=head;++/* B, matches C */+smp_store_release(&rb->user_page->data_head,head);
Yes, this would be correct.
The reason we didn't do this is because smp_store_release() ends up
being smp_mb() + WRITE_ONCE() for a fair number of platforms, even if
they have a cheaper smp_wmb(). Most notably ARM.
Yep agree, that would be worse..
(ARM64 OTOH would like to have smp_store_release() there I imagine;
while x86 doesn't care either way around).
A similar concern exists for the smp_load_acquire() I proposed for the
userspace side, ARM would have to resort to smp_mb() in that situation,
instead of the cheaper smp_rmb().
The smp_store_release() on the userspace side will actually be of equal
cost or cheaper, since it already has an smp_mb(). Most notably, x86 can
avoid barrier entirely, because TSO doesn't allow the LOAD-STORE reorder
(it only allows the STORE-LOAD reorder). And PowerPC can use LWSYNC
instead of SYNC.
Ok, thanks a lot for your feedback, Peter! I've changed the user space
side now to the following diff (also moving to a small helper so it can
be reused by libbpf in the subsequent fix I had in the series):
tools/arch/arm64/include/asm/barrier.h | 70 +++++++++++++++++++++++++++++++
tools/arch/ia64/include/asm/barrier.h | 13 ++++++
tools/arch/powerpc/include/asm/barrier.h | 16 +++++++
tools/arch/s390/include/asm/barrier.h | 13 ++++++
tools/arch/sparc/include/asm/barrier_64.h | 13 ++++++
tools/arch/x86/include/asm/barrier.h | 14 +++++++
tools/include/asm/barrier.h | 35 ++++++++++++++++
tools/include/linux/ring_buffer.h | 69 ++++++++++++++++++++++++++++++
tools/perf/util/mmap.h | 15 ++-----
9 files changed, 246 insertions(+), 12 deletions(-)
create mode 100644 tools/include/linux/ring_buffer.h
I don't like this proliferation of asm.
Why do we think that we can do better job than compiler?
can we please use gcc builtins instead?
https://gcc.gnu.org/onlinedocs/gcc/_005f_005fatomic-Builtins.html
__atomic_load_n(ptr, __ATOMIC_ACQUIRE);
__atomic_store_n(ptr, val, __ATOMIC_RELEASE);
are done specifically for this use case if I'm not mistaken.
I think it pays to learn what compiler provides.
I don't like this proliferation of asm.
Why do we think that we can do better job than compiler?
can we please use gcc builtins instead?
https://gcc.gnu.org/onlinedocs/gcc/_005f_005fatomic-Builtins.html
__atomic_load_n(ptr, __ATOMIC_ACQUIRE);
__atomic_store_n(ptr, val, __ATOMIC_RELEASE);
are done specifically for this use case if I'm not mistaken.
I think it pays to learn what compiler provides.
But are you sure the C11 memory model matches exact same model as kernel?
Seems like last time Will looked into it [0] it wasn't the case ...
The above was pulled in and slightly adapted from kernel side of arch
asm barriers. Hm, it would probably be safest if an arch decides to adapt
C11 barriers first from kernel side and user space could then use the
exact same matching builtin functions for scenarios like these as well.
[0] https://lore.kernel.org/lkml/20170308174300.GL20400@arm.com/
I don't like this proliferation of asm.
Why do we think that we can do better job than compiler?
can we please use gcc builtins instead?
https://gcc.gnu.org/onlinedocs/gcc/_005f_005fatomic-Builtins.html
__atomic_load_n(ptr, __ATOMIC_ACQUIRE);
__atomic_store_n(ptr, val, __ATOMIC_RELEASE);
are done specifically for this use case if I'm not mistaken.
I think it pays to learn what compiler provides.
But are you sure the C11 memory model matches exact same model as kernel?
Seems like last time Will looked into it [0] it wasn't the case ...
I'm only suggesting equivalence of __atomic_load_n(ptr, __ATOMIC_ACQUIRE)
with kernel's smp_load_acquire().
I've seen a bunch of user space ring buffer implementations implemented
with __atomic_load_n() primitives.
But let's ask experts who live in both worlds.
Paul,
what would you recommend?
Should we copy paste smp_store_release() from kernel to be used
in user space library/tools
or use __atomic_load_n() builtins instead?
The above was pulled in and slightly adapted from kernel side of arch
asm barriers. Hm, it would probably be safest if an arch decides to adapt
C11 barriers first from kernel side and user space could then use the
exact same matching builtin functions for scenarios like these as well.
[0] https://lore.kernel.org/lkml/20170308174300.GL20400@arm.com/
I don't like this proliferation of asm.
Why do we think that we can do better job than compiler?
can we please use gcc builtins instead?
https://gcc.gnu.org/onlinedocs/gcc/_005f_005fatomic-Builtins.html
__atomic_load_n(ptr, __ATOMIC_ACQUIRE);
__atomic_store_n(ptr, val, __ATOMIC_RELEASE);
are done specifically for this use case if I'm not mistaken.
I think it pays to learn what compiler provides.
My problem with using the C11 stuff for this is that we're then limited
to compilers that actually support that. The kernel has a minimum of
gcc-4.6 (and thus perf does too I think) and gcc-4.6 does not have C11.
What Daniel writes is also true; the kernel and C11 memory models don't
align; but you're right in that for this purpose the C11 load-acquire
and store-release would indeed suffice.
I don't like this proliferation of asm.
Why do we think that we can do better job than compiler?
can we please use gcc builtins instead?
https://gcc.gnu.org/onlinedocs/gcc/_005f_005fatomic-Builtins.html
__atomic_load_n(ptr, __ATOMIC_ACQUIRE);
__atomic_store_n(ptr, val, __ATOMIC_RELEASE);
are done specifically for this use case if I'm not mistaken.
I think it pays to learn what compiler provides.
But are you sure the C11 memory model matches exact same model as kernel?
Seems like last time Will looked into it [0] it wasn't the case ...
I'm only suggesting equivalence of __atomic_load_n(ptr, __ATOMIC_ACQUIRE)
with kernel's smp_load_acquire().
I've seen a bunch of user space ring buffer implementations implemented
with __atomic_load_n() primitives.
But let's ask experts who live in both worlds.
One thing to be wary of is if there is an implementation choice between
how to implement load-acquire and store-release for a given architecture.
In these situations, it's often important that concurrent software agrees
on the "mapping", so we'd need to be sure that (a) All userspace compilers
that we care about have compatible mappings and (b) These mappings are
compatible with the kernel code.
Will
I don't like this proliferation of asm.
Why do we think that we can do better job than compiler?
can we please use gcc builtins instead?
https://gcc.gnu.org/onlinedocs/gcc/_005f_005fatomic-Builtins.html
__atomic_load_n(ptr, __ATOMIC_ACQUIRE);
__atomic_store_n(ptr, val, __ATOMIC_RELEASE);
are done specifically for this use case if I'm not mistaken.
I think it pays to learn what compiler provides.
But are you sure the C11 memory model matches exact same model as kernel?
Seems like last time Will looked into it [0] it wasn't the case ...
I'm only suggesting equivalence of __atomic_load_n(ptr, __ATOMIC_ACQUIRE)
with kernel's smp_load_acquire().
I've seen a bunch of user space ring buffer implementations implemented
with __atomic_load_n() primitives.
But let's ask experts who live in both worlds.
One thing to be wary of is if there is an implementation choice between
how to implement load-acquire and store-release for a given architecture.
In these situations, it's often important that concurrent software agrees
on the "mapping", so we'd need to be sure that (a) All userspace compilers
that we care about have compatible mappings and (b) These mappings are
compatible with the kernel code.
Agreed! Mixing and matching can be done, but it does require quite a
bit of care.
Thanx, Paul