linux-man.vger.kernel.org archive mirror
 help / color / mirror / Atom feed
* Behavior of wakeup_events being 0 in perf_event_open(2)
@ 2026-11-09  2:08 Nick Krichevsky
  2026-11-09  9:16 ` Alejandro Colomar
  0 siblings, 1 reply; 2+ messages in thread
From: Nick Krichevsky @ 2026-11-09  2:08 UTC (permalink / raw)
  To: alx+linux-man; +Cc: linux-man

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

  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.

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;
    }




-Nick Krichevsky

^ permalink raw reply	[flat|nested] 2+ messages in thread

* Re: Behavior of wakeup_events being 0 in perf_event_open(2)
  2026-11-09  2:08 Behavior of wakeup_events being 0 in perf_event_open(2) Nick Krichevsky
@ 2026-11-09  9:16 ` Alejandro Colomar
  0 siblings, 0 replies; 2+ messages 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] 2+ messages in thread

end of thread, other threads:[~2026-11-09  9:16 UTC | newest]

Thread overview: 2+ messages (download: mbox.gz follow: Atom feed
-- links below jump to the message on this page --
2026-11-09  2:08 Behavior of wakeup_events being 0 in perf_event_open(2) Nick Krichevsky
2026-11-09  9:16 ` Alejandro Colomar

This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for NNTP newsgroup(s).