Ring Buffer Page Counter Invariants:
In kernel/trace/ring_buffer.c, struct ring_buffer_per_cpu maintains several counters:
pages_touched: Incremented in rb_tail_page_update() whenever a writer completely fills a sub-buffer and moves tail_page forward to next_page.
pages_lost: Incremented when events or sub-buffers are discarded (e.g., when overwriting old pages in rb_handle_head_page()).
pages_read: Incremented in __rb_get_reader_page() whenever a reader swaps cpu_buffer->reader_page with head_page.
When a freshly initialized ring buffer receives events, the writer writes to the initial page without advancing tail_page, leaving pages_touched == 0. When a reader then reads that sub-buffer, __rb_get_reader_page() swaps the page into reader_page and increments pages_read to 1. In ring_buffer_nr_dirty_pages(), cnt = pages_touched - pages_lost is 0, and read = pages_read is 1. Because read <= cnt + 1 holds, the check:
/* The reader can read an empty page, but not more than that */
if (cnt < read) {
WARN_ON_ONCE(read > cnt + 1);
return 0;
}
does not trigger a warning. Under normal lifecycle conditions, a reader cannot swap another page until the writer writes enough events to advance tail_page away from reader_page, which increments pages_touched, preserving the invariant pages_read <= (pages_touched - pages_lost) + 1.
Incomplete Reinitialization in ring_buffer_subbuf_order_set():
When userspace reconfigures the sub-buffer size via buffer_subbuf_size_kb (calling ring_buffer_subbuf_order_set()), all existing sub-buffers and data are discarded because their sub-buffer size/order is incompatible with the new configuration.
In ring_buffer_subbuf_order_set():
list_add(&old_pages, cpu_buffer->pages);
list_add(&cpu_buffer->reader_page->list, &old_pages);
cpu_buffer->reader_page = list_entry(cpu_buffer->new_pages.next,
struct buffer_page, list);
list_del_init(&cpu_buffer->reader_page->list);
cpu_buffer->pages = cpu_buffer->new_pages.next;
list_del_init(&cpu_buffer->new_pages);
cpu_buffer->cnt++;
cpu_buffer->head_page
= list_entry(cpu_buffer->pages, struct buffer_page, list);
cpu_buffer->tail_page = cpu_buffer->commit_page = cpu_buffer->head_page;
...
/* Free old sub buffers */
list_for_each_entry_safe(bpage, tmp, &old_pages, list) {
list_del_init(&bpage->list);
free_buffer_page(bpage);
}
All old pages are freed, and head_page, tail_page, commit_page, and reader_page are re-pointed to freshly allocated, empty pages. However, ring_buffer_subbuf_order_set() never resets cpu_buffer->pages_read, pages_touched, pages_lost, entries, or read, nor does it invoke rb_reset_cpu().