* Re: Behavior of wakeup_events being 0 in perf_event_open(2)
[not found] <CAN8n623GU-VbFJF1O7nDWhty-dUeTdo7HwPe31m1xDbU=hQVjA@mail.gmail.com>
@ 2026-11-09 9:16 ` Alejandro Colomar
0 siblings, 0 replies; only message in thread
From: Alejandro Colomar @ 2026-11-09 9:16 UTC (permalink / raw)
To: Nick Krichevsky; +Cc: linux-man, linux-perf-users, Vince Weaver
[-- Attachment #1: Type: text/plain, Size: 5108 bytes --]
[CC += linux-perf-users@]
Hi Nick,
> Date: 2026-11-08 21:08:44-0500
> From: Nick Krichevsky <nick@ollien.com>
>
> Hi,
>
> I was experimenting with perf_event_open(2) and caught this sentence in the
> man page.
>
> Prior to Linux 3.0, setting wakeup_events to 0 resulted in no overflow
> notifications; more recent kernels treat 0 the same as 1.
>
> I wrote a small test program (below) to test this behavior. On
> kernel 7.1.13-200.fc44.x86_64, my experience seems to contradict the man page;
> it seems that when wakeup_events is 0, it actually notifies the file descriptor
> when the ring buffer is half filled (4096 byte size buffer here, (4096 bytes /
> 2) / (8 bytes per event) = 256 events). Setting this to 1 sends a notification
> after every event.
>
> My (rough) reading of the source code supports this understanding:
> https://git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git/tree/kernel/events/ring_buffer.c?id=7475ea8b58616b33c04afb976cf0fbaad9dd63fb#n322
>
> Is this a stale entry in the man page, or is my understanding of this facility
> incorrect? If it is indeed stale, perhaps this could read
I don't know; it could be stale, or it could be a misunderstanding of
the author of the text, or it could be that you're misunderstanding it. :)
The commit that added that text was:
21977c9d2ac8 (2015-01-21; "perf_event_open.2: Clarify description of overflow events")
by Vincent. I've added him to CC. Let's see if he or some maintainer
can help clarify what happened.
> Prior to Linux 3.0, setting wakeup_events to 0 resulted in no overflow
> notifications; more recent kernels generate notifications based on a default
> watermark of half the ring buffer size.
If this is true, we should clarify the exact version in which the
behavior changed.
> Test run of program:
> $ gcc main.c
> $ ./a.out 0
> wakeup_events = 0
> wakeup 1: 257 samples of 8 bytes
> wakeup 2: 256 samples of 8 bytes
> wakeup 3: 256 samples of 8 bytes
> wakeup 4: 256 samples of 8 bytes
> $ ./a.out 1
> wakeup_events = 1
> wakeup 1: 1 samples of 8 bytes
> wakeup 2: 1 samples of 8 bytes
> wakeup 3: 1 samples of 8 bytes
> wakeup 4: 1 samples of 8 bytes
>
> Program:
> #define _GNU_SOURCE
> #include <linux/perf_event.h>
> #include <poll.h>
> #include <pthread.h>
> #include <stdint.h>
> #include <stdio.h>
> #include <stdlib.h>
> #include <sys/ioctl.h>
> #include <sys/mman.h>
> #include <sys/syscall.h>
> #include <unistd.h>
>
> #define PAGE_SIZE 4096
>
> static void *workload(void *unused) {
> for (;;) {
> }
>
> return unused;
> }
>
> int main(int argc, char **argv) {
> unsigned wakeup_events = argc > 1 ? atoi(argv[1]) : 0;
>
> pthread_t workload_thread;
> pthread_create(&workload_thread, NULL, workload, NULL);
> // Needs glibc >= 2.42
> pid_t workload_tid = pthread_gettid_np(workload_thread);
>
> struct perf_event_attr attrs = {.size = sizeof(attrs)};
> attrs.type = PERF_TYPE_SOFTWARE;
> attrs.config = PERF_COUNT_SW_CPU_CLOCK;
> attrs.freq = 1;
> attrs.sample_freq = 49;
> attrs.disabled = 1;
> attrs.exclude_kernel = 1;
> attrs.wakeup_events = wakeup_events;
>
> int fd = syscall(SYS_perf_event_open, &attrs, workload_tid, -1, -1, 0);
> if (fd < 0) {
> perror("perf_event_open");
> return 1;
> }
>
> void *buffer =
> mmap(NULL, PAGE_SIZE * 2, PROT_READ | PROT_WRITE, MAP_SHARED, fd, 0);
> if (buffer == MAP_FAILED) {
> perror("mmap");
> return 1;
> }
>
> struct perf_event_mmap_page *meta = buffer;
> char *data = (char *)buffer + meta->data_offset;
>
> ioctl(fd, PERF_EVENT_IOC_ENABLE, 0);
> printf("wakeup_events = %u\n", wakeup_events);
>
> for (int i = 1; i <= 4; i++) {
> struct pollfd poll_fd = {.fd = fd, .events = POLLIN};
> poll(&poll_fd, 1, -1);
>
> uint64_t head = __atomic_load_n(&meta->data_head, __ATOMIC_ACQUIRE);
> uint64_t tail = meta->data_tail;
> int sample_count = 0;
> int sample_size = 0;
>
> while (tail < head) {
> struct perf_event_header *header =
> (struct perf_event_header *)(data + tail % meta->data_size);
>
> if (header->type == PERF_RECORD_SAMPLE) {
> sample_count++;
> sample_size = header->size;
> }
>
> tail += header->size;
> }
> __atomic_store_n(&meta->data_tail, tail, __ATOMIC_RELEASE);
>
> printf(
> "wakeup %d: %d samples of %d bytes\n",
> i, sample_count, sample_size
> );
> }
>
> return 0;
> }
Thanks a lot for the program!
Have a lovely day!
Alex
>
>
>
>
> -Nick Krichevsky
--
<https://www.alejandro-colomar.es>
[-- Attachment #2: signature.asc --]
[-- Type: application/pgp-signature, Size: 833 bytes --]
^ permalink raw reply [flat|nested] only message in thread