Skip to content

Commit 5784d8c

Browse files
committed
Merge tag 'trace-ring-buffer-v6.14-rc2' of git://git.kernel.org/pub/scm/linux/kernel/git/trace/linux-trace
Pull trace ring buffer fixes from Steven Rostedt: - Enable resize on mmap() error When a process mmaps a ring buffer, its size is locked and resizing is disabled. But if the user passes in a wrong parameter, the mmap() can fail after the resize was disabled and the mmap() exits with error without reenabling the ring buffer resize. This prevents the ring buffer from ever being resized after that. Reenable resizing of the ring buffer on mmap() error. - Have resizing return proper error and not always -ENOMEM If the ring buffer is mmapped by one task and another task tries to resize the buffer it will error with -ENOMEM. This is confusing to the user as there may be plenty of memory available. Have it return the error that actually happens (in this case -EBUSY) where the user can understand why the resize failed. - Test the sub-buffer array to validate persistent memory buffer On boot up, the initialization of the persistent memory buffer will do a validation check to see if the content of the data is valid, and if so, it will use the memory as is, otherwise it re-initializes it. There's meta data in this persistent memory that keeps track of which sub-buffer is the reader page and an array that states the order of the sub-buffers. The values in this array are indexes into the sub-buffers. The validator checks to make sure that all the entries in the array are within the sub-buffer list index, but it does not check for duplications. While working on this code, the array got corrupted and had duplicates, where not all the sub-buffers were accounted for. This passed the validator as all entries were valid, but the link list was incorrect and could have caused a crash. The corruption only produced incorrect data, but it could have been more severe. To fix this, create a bitmask that covers all the sub-buffer indexes and set it to all zeros. While iterating the array checking the values of the array content, have it set a bit corresponding to the index in the array. If the bit was already set, then it is a duplicate and mark the buffer as invalid and reset it. - Prevent mmap()ing persistent ring buffer The persistent ring buffer uses vmap() to map the persistent memory. Currently, the mmap() logic only uses virt_to_page() to get the page from the ring buffer memory and use that to map to user space. This works because a normal ring buffer uses alloc_page() to allocate its memory. But because the persistent ring buffer use vmap() it causes a kernel crash. Fixing this to work with vmap() is not hard, but since mmap() on persistent memory buffers never worked, just have the mmap() return -ENODEV (what was returned before mmap() for persistent memory ring buffers, as they never supported mmap. Normal buffers will still allow mmap(). Implementing mmap() for persistent memory ring buffers can wait till the next merge window. - Fix polling on persistent ring buffers There's a "buffer_percent" option (default set to 50), that is used to have reads of the ring buffer binary data block until the buffer fills to that percentage. The field "pages_touched" is incremented every time a new sub-buffer has content added to it. This field is used in the calculations to determine the amount of content is in the buffer and if it exceeds the "buffer_percent" then it will wake the task polling on the buffer. As persistent ring buffers can be created by the content from a previous boot, the "pages_touched" field was not updated. This means that if a task were to poll on the persistent buffer, it would block even if the buffer was completely full. It would block even if the "buffer_percent" was zero, because with "pages_touched" as zero, it would be calculated as the buffer having no content. Update pages_touched when initializing the persistent ring buffer from a previous boot. * tag 'trace-ring-buffer-v6.14-rc2' of git://git.kernel.org/pub/scm/linux/kernel/git/trace/linux-trace: ring-buffer: Update pages_touched to reflect persistent buffer content tracing: Do not allow mmap() of persistent ring buffer ring-buffer: Validate the persistent meta data subbuf array tracing: Have the error of __tracing_resize_ring_buffer() passed to user ring-buffer: Unlock resize on mmap error
2 parents 4966590 + 9793783 commit 5784d8c

File tree

2 files changed

+31
-9
lines changed

2 files changed

+31
-9
lines changed

kernel/trace/ring_buffer.c

Lines changed: 26 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1672,14 +1672,18 @@ static void *rb_range_buffer(struct ring_buffer_per_cpu *cpu_buffer, int idx)
16721672
* must be the same.
16731673
*/
16741674
static bool rb_meta_valid(struct ring_buffer_meta *meta, int cpu,
1675-
struct trace_buffer *buffer, int nr_pages)
1675+
struct trace_buffer *buffer, int nr_pages,
1676+
unsigned long *subbuf_mask)
16761677
{
16771678
int subbuf_size = PAGE_SIZE;
16781679
struct buffer_data_page *subbuf;
16791680
unsigned long buffers_start;
16801681
unsigned long buffers_end;
16811682
int i;
16821683

1684+
if (!subbuf_mask)
1685+
return false;
1686+
16831687
/* Check the meta magic and meta struct size */
16841688
if (meta->magic != RING_BUFFER_META_MAGIC ||
16851689
meta->struct_size != sizeof(*meta)) {
@@ -1712,6 +1716,8 @@ static bool rb_meta_valid(struct ring_buffer_meta *meta, int cpu,
17121716

17131717
subbuf = rb_subbufs_from_meta(meta);
17141718

1719+
bitmap_clear(subbuf_mask, 0, meta->nr_subbufs);
1720+
17151721
/* Is the meta buffers and the subbufs themselves have correct data? */
17161722
for (i = 0; i < meta->nr_subbufs; i++) {
17171723
if (meta->buffers[i] < 0 ||
@@ -1725,6 +1731,12 @@ static bool rb_meta_valid(struct ring_buffer_meta *meta, int cpu,
17251731
return false;
17261732
}
17271733

1734+
if (test_bit(meta->buffers[i], subbuf_mask)) {
1735+
pr_info("Ring buffer boot meta [%d] array has duplicates\n", cpu);
1736+
return false;
1737+
}
1738+
1739+
set_bit(meta->buffers[i], subbuf_mask);
17281740
subbuf = (void *)subbuf + subbuf_size;
17291741
}
17301742

@@ -1838,6 +1850,11 @@ static void rb_meta_validate_events(struct ring_buffer_per_cpu *cpu_buffer)
18381850
cpu_buffer->cpu);
18391851
goto invalid;
18401852
}
1853+
1854+
/* If the buffer has content, update pages_touched */
1855+
if (ret)
1856+
local_inc(&cpu_buffer->pages_touched);
1857+
18411858
entries += ret;
18421859
entry_bytes += local_read(&head_page->page->commit);
18431860
local_set(&cpu_buffer->head_page->entries, ret);
@@ -1889,17 +1906,22 @@ static void rb_meta_init_text_addr(struct ring_buffer_meta *meta)
18891906
static void rb_range_meta_init(struct trace_buffer *buffer, int nr_pages)
18901907
{
18911908
struct ring_buffer_meta *meta;
1909+
unsigned long *subbuf_mask;
18921910
unsigned long delta;
18931911
void *subbuf;
18941912
int cpu;
18951913
int i;
18961914

1915+
/* Create a mask to test the subbuf array */
1916+
subbuf_mask = bitmap_alloc(nr_pages + 1, GFP_KERNEL);
1917+
/* If subbuf_mask fails to allocate, then rb_meta_valid() will return false */
1918+
18971919
for (cpu = 0; cpu < nr_cpu_ids; cpu++) {
18981920
void *next_meta;
18991921

19001922
meta = rb_range_meta(buffer, nr_pages, cpu);
19011923

1902-
if (rb_meta_valid(meta, cpu, buffer, nr_pages)) {
1924+
if (rb_meta_valid(meta, cpu, buffer, nr_pages, subbuf_mask)) {
19031925
/* Make the mappings match the current address */
19041926
subbuf = rb_subbufs_from_meta(meta);
19051927
delta = (unsigned long)subbuf - meta->first_buffer;
@@ -1943,6 +1965,7 @@ static void rb_range_meta_init(struct trace_buffer *buffer, int nr_pages)
19431965
subbuf += meta->subbuf_size;
19441966
}
19451967
}
1968+
bitmap_free(subbuf_mask);
19461969
}
19471970

19481971
static void *rbm_start(struct seq_file *m, loff_t *pos)
@@ -7126,6 +7149,7 @@ int ring_buffer_map(struct trace_buffer *buffer, int cpu,
71267149
kfree(cpu_buffer->subbuf_ids);
71277150
cpu_buffer->subbuf_ids = NULL;
71287151
rb_free_meta_page(cpu_buffer);
7152+
atomic_dec(&cpu_buffer->resize_disabled);
71297153
}
71307154

71317155
unlock:

kernel/trace/trace.c

Lines changed: 5 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -5977,8 +5977,6 @@ static int __tracing_resize_ring_buffer(struct trace_array *tr,
59775977
ssize_t tracing_resize_ring_buffer(struct trace_array *tr,
59785978
unsigned long size, int cpu_id)
59795979
{
5980-
int ret;
5981-
59825980
guard(mutex)(&trace_types_lock);
59835981

59845982
if (cpu_id != RING_BUFFER_ALL_CPUS) {
@@ -5987,11 +5985,7 @@ ssize_t tracing_resize_ring_buffer(struct trace_array *tr,
59875985
return -EINVAL;
59885986
}
59895987

5990-
ret = __tracing_resize_ring_buffer(tr, size, cpu_id);
5991-
if (ret < 0)
5992-
ret = -ENOMEM;
5993-
5994-
return ret;
5988+
return __tracing_resize_ring_buffer(tr, size, cpu_id);
59955989
}
59965990

59975991
static void update_last_data(struct trace_array *tr)
@@ -8285,6 +8279,10 @@ static int tracing_buffers_mmap(struct file *filp, struct vm_area_struct *vma)
82858279
struct trace_iterator *iter = &info->iter;
82868280
int ret = 0;
82878281

8282+
/* Currently the boot mapped buffer is not supported for mmap */
8283+
if (iter->tr->flags & TRACE_ARRAY_FL_BOOT)
8284+
return -ENODEV;
8285+
82888286
ret = get_snapshot_map(iter->tr);
82898287
if (ret)
82908288
return ret;

0 commit comments

Comments
 (0)