...
1 package ringbuf
2
3 import (
4 "fmt"
5 "io"
6 "os"
7 "runtime"
8 "sync/atomic"
9 "unsafe"
10
11 "github.com/cilium/ebpf/internal/unix"
12 )
13
14 type ringbufEventRing struct {
15 prod []byte
16 cons []byte
17 *ringReader
18 }
19
20 func newRingBufEventRing(mapFD, size int) (*ringbufEventRing, error) {
21 cons, err := unix.Mmap(mapFD, 0, os.Getpagesize(), unix.PROT_READ|unix.PROT_WRITE, unix.MAP_SHARED)
22 if err != nil {
23 return nil, fmt.Errorf("can't mmap consumer page: %w", err)
24 }
25
26 prod, err := unix.Mmap(mapFD, (int64)(os.Getpagesize()), os.Getpagesize()+2*size, unix.PROT_READ, unix.MAP_SHARED)
27 if err != nil {
28 _ = unix.Munmap(cons)
29 return nil, fmt.Errorf("can't mmap data pages: %w", err)
30 }
31
32 cons_pos := (*uint64)(unsafe.Pointer(&cons[0]))
33 prod_pos := (*uint64)(unsafe.Pointer(&prod[0]))
34
35 ring := &ringbufEventRing{
36 prod: prod,
37 cons: cons,
38 ringReader: newRingReader(cons_pos, prod_pos, prod[os.Getpagesize():]),
39 }
40 runtime.SetFinalizer(ring, (*ringbufEventRing).Close)
41
42 return ring, nil
43 }
44
45 func (ring *ringbufEventRing) Close() {
46 runtime.SetFinalizer(ring, nil)
47
48 _ = unix.Munmap(ring.prod)
49 _ = unix.Munmap(ring.cons)
50
51 ring.prod = nil
52 ring.cons = nil
53 }
54
55 type ringReader struct {
56
57 prod_pos, cons_pos *uint64
58 cons uint64
59 mask uint64
60 ring []byte
61 }
62
63 func newRingReader(cons_ptr, prod_ptr *uint64, ring []byte) *ringReader {
64 return &ringReader{
65 prod_pos: prod_ptr,
66 cons_pos: cons_ptr,
67 cons: atomic.LoadUint64(cons_ptr),
68
69 mask: uint64(cap(ring)/2 - 1),
70 ring: ring,
71 }
72 }
73
74 func (rr *ringReader) loadConsumer() {
75 rr.cons = atomic.LoadUint64(rr.cons_pos)
76 }
77
78 func (rr *ringReader) storeConsumer() {
79 atomic.StoreUint64(rr.cons_pos, rr.cons)
80 }
81
82
83 func clamp(start, end, delta uint64) uint64 {
84 if remainder := end - start; delta > remainder {
85 return remainder
86 }
87 return delta
88 }
89
90 func (rr *ringReader) skipRead(skipBytes uint64) {
91 rr.cons += clamp(rr.cons, atomic.LoadUint64(rr.prod_pos), skipBytes)
92 }
93
94 func (rr *ringReader) Read(p []byte) (int, error) {
95 prod := atomic.LoadUint64(rr.prod_pos)
96
97 n := clamp(rr.cons, prod, uint64(len(p)))
98
99 start := rr.cons & rr.mask
100
101 copy(p, rr.ring[start:start+n])
102 rr.cons += n
103
104 if prod == rr.cons {
105 return int(n), io.EOF
106 }
107
108 return int(n), nil
109 }
110
View as plain text