github.com/kubeshark/ebpf@v0.9.2/ringbuf/ring.go (about) 1 package ringbuf 2 3 import ( 4 "fmt" 5 "io" 6 "os" 7 "runtime" 8 "sync/atomic" 9 "unsafe" 10 11 "github.com/kubeshark/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 // These point into mmap'ed memory and must be accessed atomically. 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 // cap is always a power of two 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 // clamp delta to 'end' if 'start+delta' is beyond 'end' 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 }