Keyboard shortcuts

Press or to navigate between chapters

Press S or / to search in the book

Press ? to show this help

Press Esc to hide this help

Ring Buffers

Ring buffers send variable-sized records from BPF programs to userspace without per-event syscalls. Whistler provides both raw primitives and a convenience macro.

Primitives

ringbuf-reserve

Reserve space in a ring buffer map. Returns a pointer to the reserved region, or 0 if the buffer is full.

(ringbuf-reserve map-name size flags)

size should be a compile-time constant or (sizeof struct-name). flags is typically 0.

ringbuf-submit

Submit a previously reserved record to the ring buffer, making it visible to the userspace consumer.

(ringbuf-submit ptr flags)

ringbuf-discard

Discard a reserved record without submitting it.

(ringbuf-discard ptr flags)

ringbuf-output

Copy a stack-allocated struct directly into the ring buffer. This is a single helper call -- more compact than the reserve/submit pattern when you build the entire record before sending.

(ringbuf-output map-name data-ptr size flags)

data-ptr must point to a stack-allocated struct (e.g., from make-event). The BPF verifier requires the pointer to be fp-derived.

(let ((evt (make-conn-event)))
  (setf (conn-event-src-addr evt) src
        (conn-event-dst-addr evt) dst
        (conn-event-dst-port evt) port
        (conn-event-proto evt)    proto)
  (ringbuf-output events evt (sizeof conn-event) 0))

Use ringbuf-output when filling all fields before sending. Use with-ringbuf (below) when you need conditional field logic or want to avoid the stack copy.

with-ringbuf

The with-ringbuf macro handles reserve, body execution, and submit in one form. If ringbuf-reserve returns null, the body is skipped entirely.

(with-ringbuf (var map-name size [:flags 0])
  body...)

The variable is bound to the reserved pointer inside the body. On normal exit, ringbuf-submit is called automatically. The buffer is not zeroed -- set fields explicitly or use memset.

(defstruct conn-event
  (src-addr u32)
  (dst-addr u32)
  (dst-port u16)
  (proto    u8)
  (pad      u8))

(defmap events :type :ringbuf :max-entries 4096)

(with-ringbuf (event events (sizeof conn-event))
  (setf (conn-event-src-addr event) (ipv4-src-addr ip)
        (conn-event-dst-addr event) (ipv4-dst-addr ip)
        (conn-event-dst-port event) (tcp-dst-port tcp)
        (conn-event-proto event)    proto))

If the body executes (return ...), the reservation is not auto-submitted. Call (ringbuf-discard var 0) before returning if needed.

fill-process-info

A convenience macro for filling common process metadata fields in a struct. Each keyword names a struct field setter:

(fill-process-info event
  :pid-field       my-event-pid
  :uid-field       my-event-uid
  :timestamp-field my-event-timestamp
  :comm-field      my-event-comm-ptr
  :comm-size       16)

This expands to calls to get-current-pid-tgid, get-current-uid-gid, ktime-get-ns, and get-current-comm with the appropriate field setters.