Skip to content

Commit 9c1d099

Browse files
brycekahlelmb
authored andcommitted
add WakeupEvents support to perf Reader
Signed-off-by: Bryce Kahle <[email protected]>
1 parent 5a7f946 commit 9c1d099

File tree

4 files changed

+72
-18
lines changed

4 files changed

+72
-18
lines changed

perf/reader.go

Lines changed: 8 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -169,6 +169,10 @@ type Reader struct {
169169
// ReaderOptions control the behaviour of the user
170170
// space reader.
171171
type ReaderOptions struct {
172+
// The number of events required in any per CPU buffer before
173+
// Read will process data. This is mutually exclusive with Watermark.
174+
// The default is zero, which means Watermark will take precedence.
175+
WakeupEvents int
172176
// The number of written bytes required in any per CPU buffer before
173177
// Read will process data. Must be smaller than PerCPUBuffer.
174178
// The default is to start processing as soon as data is available.
@@ -192,6 +196,9 @@ func NewReaderWithOptions(array *ebpf.Map, perCPUBuffer int, opts ReaderOptions)
192196
if perCPUBuffer < 1 {
193197
return nil, errors.New("perCPUBuffer must be larger than 0")
194198
}
199+
if opts.WakeupEvents > 0 && opts.Watermark > 0 {
200+
return nil, errors.New("WakeupEvents and Watermark cannot both be non-zero")
201+
}
195202

196203
var (
197204
fds []int
@@ -224,7 +231,7 @@ func NewReaderWithOptions(array *ebpf.Map, perCPUBuffer int, opts ReaderOptions)
224231
// Hence we have to create a ring for each CPU.
225232
bufferSize := 0
226233
for i := 0; i < nCPU; i++ {
227-
ring, err := newPerfEventRing(i, perCPUBuffer, opts.Watermark, opts.Overwritable)
234+
ring, err := newPerfEventRing(i, perCPUBuffer, opts)
228235
if errors.Is(err, unix.ENODEV) {
229236
// The requested CPU is currently offline, skip it.
230237
rings = append(rings, nil)

perf/reader_test.go

Lines changed: 41 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -382,7 +382,7 @@ func TestPerfReaderClose(t *testing.T) {
382382
}
383383

384384
func TestCreatePerfEvent(t *testing.T) {
385-
fd, err := createPerfEvent(0, 1, false)
385+
fd, err := createPerfEvent(0, ReaderOptions{Watermark: 1, Overwritable: false})
386386
if err != nil {
387387
t.Fatal("Can't create perf event:", err)
388388
}
@@ -489,6 +489,46 @@ func TestPause(t *testing.T) {
489489
qt.Assert(t, qt.ErrorIs(err, ErrClosed), qt.Commentf("doesn't wrap ErrClosed"))
490490
}
491491

492+
func TestPerfReaderWakeupEvents(t *testing.T) {
493+
events := perfEventArray(t)
494+
495+
numEvents := 2
496+
rd, err := NewReaderWithOptions(events, 4096, ReaderOptions{WakeupEvents: numEvents})
497+
if err != nil {
498+
t.Fatal(err)
499+
}
500+
defer rd.Close()
501+
502+
// Write a sample. The reader should read it.
503+
prog := outputSamplesProg(t, events, 5)
504+
ret, _, err := prog.Test(internal.EmptyBPFContext)
505+
testutils.SkipIfNotSupported(t, err)
506+
if err != nil || ret != 0 {
507+
t.Fatal("Can't write sample")
508+
}
509+
510+
if errno := syscall.Errno(-int32(ret)); errno != 0 {
511+
t.Fatal("Expected 0 as return value, got", errno)
512+
}
513+
514+
rd.SetDeadline(time.Now().Add(10 * time.Millisecond))
515+
_, err = rd.Read()
516+
qt.Assert(t, qt.ErrorIs(err, os.ErrDeadlineExceeded), qt.Commentf("expected os.ErrDeadlineExceeded"))
517+
518+
// send followup events
519+
for i := 1; i < numEvents; i++ {
520+
_, _, err = prog.Test(internal.EmptyBPFContext)
521+
if err != nil {
522+
t.Fatal(err)
523+
}
524+
}
525+
526+
rd.SetDeadline(time.Time{})
527+
for i := 0; i < numEvents; i++ {
528+
checkRecord(t, rd)
529+
}
530+
}
531+
492532
func BenchmarkReader(b *testing.B) {
493533
events := perfEventArray(b)
494534
prog := outputSamplesProg(b, events, 80)

perf/ring.go

Lines changed: 18 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -22,12 +22,12 @@ type perfEventRing struct {
2222
ringReader
2323
}
2424

25-
func newPerfEventRing(cpu, perCPUBuffer, watermark int, overwritable bool) (*perfEventRing, error) {
26-
if watermark >= perCPUBuffer {
25+
func newPerfEventRing(cpu, perCPUBuffer int, opts ReaderOptions) (*perfEventRing, error) {
26+
if opts.Watermark >= perCPUBuffer {
2727
return nil, errors.New("watermark must be smaller than perCPUBuffer")
2828
}
2929

30-
fd, err := createPerfEvent(cpu, watermark, overwritable)
30+
fd, err := createPerfEvent(cpu, opts)
3131
if err != nil {
3232
return nil, err
3333
}
@@ -38,7 +38,7 @@ func newPerfEventRing(cpu, perCPUBuffer, watermark int, overwritable bool) (*per
3838
}
3939

4040
protections := unix.PROT_READ
41-
if !overwritable {
41+
if !opts.Overwritable {
4242
protections |= unix.PROT_WRITE
4343
}
4444

@@ -55,7 +55,7 @@ func newPerfEventRing(cpu, perCPUBuffer, watermark int, overwritable bool) (*per
5555
meta := (*unix.PerfEventMmapPage)(unsafe.Pointer(&mmap[0]))
5656

5757
var reader ringReader
58-
if overwritable {
58+
if opts.Overwritable {
5959
reader = newReverseReader(meta, mmap[meta.Data_offset:meta.Data_offset+meta.Data_size])
6060
} else {
6161
reader = newForwardReader(meta, mmap[meta.Data_offset:meta.Data_offset+meta.Data_size])
@@ -98,13 +98,20 @@ func (ring *perfEventRing) Close() {
9898
ring.mmap = nil
9999
}
100100

101-
func createPerfEvent(cpu, watermark int, overwritable bool) (int, error) {
102-
if watermark == 0 {
103-
watermark = 1
101+
func createPerfEvent(cpu int, opts ReaderOptions) (int, error) {
102+
wakeup := 0
103+
bits := 0
104+
if opts.WakeupEvents > 0 {
105+
wakeup = opts.WakeupEvents
106+
} else {
107+
wakeup = opts.Watermark
108+
if wakeup == 0 {
109+
wakeup = 1
110+
}
111+
bits |= unix.PerfBitWatermark
104112
}
105113

106-
bits := unix.PerfBitWatermark
107-
if overwritable {
114+
if opts.Overwritable {
108115
bits |= unix.PerfBitWriteBackward
109116
}
110117

@@ -113,7 +120,7 @@ func createPerfEvent(cpu, watermark int, overwritable bool) (int, error) {
113120
Config: unix.PERF_COUNT_SW_BPF_OUTPUT,
114121
Bits: uint64(bits),
115122
Sample_type: unix.PERF_SAMPLE_RAW,
116-
Wakeup: uint32(watermark),
123+
Wakeup: uint32(wakeup),
117124
}
118125

119126
attr.Size = uint32(unsafe.Sizeof(attr))

perf/ring_test.go

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -131,7 +131,7 @@ func makeForwardRing(size, offset int) *forwardReader {
131131

132132
func TestPerfEventRing(t *testing.T) {
133133
check := func(buffer, watermark int, overwritable bool) {
134-
ring, err := newPerfEventRing(0, buffer, watermark, overwritable)
134+
ring, err := newPerfEventRing(0, buffer, ReaderOptions{Watermark: watermark, Overwritable: overwritable})
135135
if err != nil {
136136
t.Fatal(err)
137137
}
@@ -154,21 +154,21 @@ func TestPerfEventRing(t *testing.T) {
154154
}
155155

156156
// watermark > buffer
157-
_, err := newPerfEventRing(0, 8192, 8193, false)
157+
_, err := newPerfEventRing(0, 8192, ReaderOptions{Watermark: 8193, Overwritable: false})
158158
if err == nil {
159159
t.Fatal("watermark > buffer allowed")
160160
}
161-
_, err = newPerfEventRing(0, 8192, 8193, true)
161+
_, err = newPerfEventRing(0, 8192, ReaderOptions{Watermark: 8193, Overwritable: true})
162162
if err == nil {
163163
t.Fatal("watermark > buffer allowed")
164164
}
165165

166166
// watermark == buffer
167-
_, err = newPerfEventRing(0, 8192, 8192, false)
167+
_, err = newPerfEventRing(0, 8192, ReaderOptions{Watermark: 8192, Overwritable: false})
168168
if err == nil {
169169
t.Fatal("watermark == buffer allowed")
170170
}
171-
_, err = newPerfEventRing(0, 8192, 8192, true)
171+
_, err = newPerfEventRing(0, 8192, ReaderOptions{Watermark: 8193, Overwritable: true})
172172
if err == nil {
173173
t.Fatal("watermark == buffer allowed")
174174
}

0 commit comments

Comments
 (0)