]> git.ipfire.org Git - thirdparty/linux.git/commitdiff
tracing: Disable preemption when using the filter buffer
authorSteven Rostedt (VMware) <rostedt@goodmis.org>
Tue, 30 Nov 2021 02:39:47 +0000 (21:39 -0500)
committerSteven Rostedt (VMware) <rostedt@goodmis.org>
Mon, 6 Dec 2021 20:37:22 +0000 (15:37 -0500)
In case trace_event_buffer_lock_reserve() is called with preemption
enabled, the algorithm that defines the usage of the per cpu filter buffer
may fail if the task schedules to another CPU after determining which
buffer it will use.

Disable preemption when using the filter buffer. And because that same
buffer must be used throughout the call, keep preemption disabled until
the filter buffer is released.

This will also keep the semantics between the use case of when the filter
buffer is used, and when the ring buffer itself is used, as that case also
disables preemption until the ring buffer is released.

Link: https://lkml.kernel.org/r/20211130024318.880190623@goodmis.org
[ Fixed warning of assignment in if statement
Reported-by: kernel test robot <lkp@intel.com> ]
Signed-off-by: Steven Rostedt (VMware) <rostedt@goodmis.org>
kernel/trace/trace.c
kernel/trace/trace.h

index 2e87b7bf2ba7f5d0967f74595a0626eec7367d1f..e3b8c906b7b4dc0108fa3f8a0e38b3e880c9c231 100644 (file)
@@ -980,6 +980,8 @@ __buffer_unlock_commit(struct trace_buffer *buffer, struct ring_buffer_event *ev
                ring_buffer_write(buffer, event->array[0], &event->array[1]);
                /* Release the temp buffer */
                this_cpu_dec(trace_buffered_event_cnt);
+               /* ring_buffer_unlock_commit() enables preemption */
+               preempt_enable_notrace();
        } else
                ring_buffer_unlock_commit(buffer, event);
 }
@@ -2745,8 +2747,8 @@ trace_event_buffer_lock_reserve(struct trace_buffer **current_rb,
        *current_rb = tr->array_buffer.buffer;
 
        if (!tr->no_filter_buffering_ref &&
-           (trace_file->flags & (EVENT_FILE_FL_SOFT_DISABLED | EVENT_FILE_FL_FILTERED)) &&
-           (entry = __this_cpu_read(trace_buffered_event))) {
+           (trace_file->flags & (EVENT_FILE_FL_SOFT_DISABLED | EVENT_FILE_FL_FILTERED))) {
+               preempt_disable_notrace();
                /*
                 * Filtering is on, so try to use the per cpu buffer first.
                 * This buffer will simulate a ring_buffer_event,
@@ -2764,33 +2766,38 @@ trace_event_buffer_lock_reserve(struct trace_buffer **current_rb,
                 * is still quicker than no copy on match, but having
                 * to discard out of the ring buffer on a failed match.
                 */
-               int max_len = PAGE_SIZE - struct_size(entry, array, 1);
+               if ((entry = __this_cpu_read(trace_buffered_event))) {
+                       int max_len = PAGE_SIZE - struct_size(entry, array, 1);
 
-               val = this_cpu_inc_return(trace_buffered_event_cnt);
+                       val = this_cpu_inc_return(trace_buffered_event_cnt);
 
-               /*
-                * Preemption is disabled, but interrupts and NMIs
-                * can still come in now. If that happens after
-                * the above increment, then it will have to go
-                * back to the old method of allocating the event
-                * on the ring buffer, and if the filter fails, it
-                * will have to call ring_buffer_discard_commit()
-                * to remove it.
-                *
-                * Need to also check the unlikely case that the
-                * length is bigger than the temp buffer size.
-                * If that happens, then the reserve is pretty much
-                * guaranteed to fail, as the ring buffer currently
-                * only allows events less than a page. But that may
-                * change in the future, so let the ring buffer reserve
-                * handle the failure in that case.
-                */
-               if (val == 1 && likely(len <= max_len)) {
-                       trace_event_setup(entry, type, trace_ctx);
-                       entry->array[0] = len;
-                       return entry;
+                       /*
+                        * Preemption is disabled, but interrupts and NMIs
+                        * can still come in now. If that happens after
+                        * the above increment, then it will have to go
+                        * back to the old method of allocating the event
+                        * on the ring buffer, and if the filter fails, it
+                        * will have to call ring_buffer_discard_commit()
+                        * to remove it.
+                        *
+                        * Need to also check the unlikely case that the
+                        * length is bigger than the temp buffer size.
+                        * If that happens, then the reserve is pretty much
+                        * guaranteed to fail, as the ring buffer currently
+                        * only allows events less than a page. But that may
+                        * change in the future, so let the ring buffer reserve
+                        * handle the failure in that case.
+                        */
+                       if (val == 1 && likely(len <= max_len)) {
+                               trace_event_setup(entry, type, trace_ctx);
+                               entry->array[0] = len;
+                               /* Return with preemption disabled */
+                               return entry;
+                       }
+                       this_cpu_dec(trace_buffered_event_cnt);
                }
-               this_cpu_dec(trace_buffered_event_cnt);
+               /* __trace_buffer_lock_reserve() disables preemption */
+               preempt_enable_notrace();
        }
 
        entry = __trace_buffer_lock_reserve(*current_rb, type, len,
index 7162157b970bfdf2978b45fe45fd21b5f0bd42e3..8bd1a815ce90b70e0ae8423eb840212e4ff497a4 100644 (file)
@@ -1337,10 +1337,12 @@ __trace_event_discard_commit(struct trace_buffer *buffer,
                             struct ring_buffer_event *event)
 {
        if (this_cpu_read(trace_buffered_event) == event) {
-               /* Simply release the temp buffer */
+               /* Simply release the temp buffer and enable preemption */
                this_cpu_dec(trace_buffered_event_cnt);
+               preempt_enable_notrace();
                return;
        }
+       /* ring_buffer_discard_commit() enables preemption */
        ring_buffer_discard_commit(buffer, event);
 }