|
@@ -1498,25 +1498,119 @@ static void __trace_userstack(struct trace_array *tr, unsigned long flags)
|
|
|
|
|
|
#endif /* CONFIG_STACKTRACE */
|
|
|
|
|
|
+/* created for use with alloc_percpu */
|
|
|
+struct trace_buffer_struct {
|
|
|
+ char buffer[TRACE_BUF_SIZE];
|
|
|
+};
|
|
|
+
|
|
|
+static struct trace_buffer_struct *trace_percpu_buffer;
|
|
|
+static struct trace_buffer_struct *trace_percpu_sirq_buffer;
|
|
|
+static struct trace_buffer_struct *trace_percpu_irq_buffer;
|
|
|
+static struct trace_buffer_struct *trace_percpu_nmi_buffer;
|
|
|
+
|
|
|
+/*
|
|
|
+ * The buffer used is dependent on the context. There is a per cpu
|
|
|
+ * buffer for normal context, softirq contex, hard irq context and
|
|
|
+ * for NMI context. Thise allows for lockless recording.
|
|
|
+ *
|
|
|
+ * Note, if the buffers failed to be allocated, then this returns NULL
|
|
|
+ */
|
|
|
+static char *get_trace_buf(void)
|
|
|
+{
|
|
|
+ struct trace_buffer_struct *percpu_buffer;
|
|
|
+ struct trace_buffer_struct *buffer;
|
|
|
+
|
|
|
+ /*
|
|
|
+ * If we have allocated per cpu buffers, then we do not
|
|
|
+ * need to do any locking.
|
|
|
+ */
|
|
|
+ if (in_nmi())
|
|
|
+ percpu_buffer = trace_percpu_nmi_buffer;
|
|
|
+ else if (in_irq())
|
|
|
+ percpu_buffer = trace_percpu_irq_buffer;
|
|
|
+ else if (in_softirq())
|
|
|
+ percpu_buffer = trace_percpu_sirq_buffer;
|
|
|
+ else
|
|
|
+ percpu_buffer = trace_percpu_buffer;
|
|
|
+
|
|
|
+ if (!percpu_buffer)
|
|
|
+ return NULL;
|
|
|
+
|
|
|
+ buffer = per_cpu_ptr(percpu_buffer, smp_processor_id());
|
|
|
+
|
|
|
+ return buffer->buffer;
|
|
|
+}
|
|
|
+
|
|
|
+static int alloc_percpu_trace_buffer(void)
|
|
|
+{
|
|
|
+ struct trace_buffer_struct *buffers;
|
|
|
+ struct trace_buffer_struct *sirq_buffers;
|
|
|
+ struct trace_buffer_struct *irq_buffers;
|
|
|
+ struct trace_buffer_struct *nmi_buffers;
|
|
|
+
|
|
|
+ buffers = alloc_percpu(struct trace_buffer_struct);
|
|
|
+ if (!buffers)
|
|
|
+ goto err_warn;
|
|
|
+
|
|
|
+ sirq_buffers = alloc_percpu(struct trace_buffer_struct);
|
|
|
+ if (!sirq_buffers)
|
|
|
+ goto err_sirq;
|
|
|
+
|
|
|
+ irq_buffers = alloc_percpu(struct trace_buffer_struct);
|
|
|
+ if (!irq_buffers)
|
|
|
+ goto err_irq;
|
|
|
+
|
|
|
+ nmi_buffers = alloc_percpu(struct trace_buffer_struct);
|
|
|
+ if (!nmi_buffers)
|
|
|
+ goto err_nmi;
|
|
|
+
|
|
|
+ trace_percpu_buffer = buffers;
|
|
|
+ trace_percpu_sirq_buffer = sirq_buffers;
|
|
|
+ trace_percpu_irq_buffer = irq_buffers;
|
|
|
+ trace_percpu_nmi_buffer = nmi_buffers;
|
|
|
+
|
|
|
+ return 0;
|
|
|
+
|
|
|
+ err_nmi:
|
|
|
+ free_percpu(irq_buffers);
|
|
|
+ err_irq:
|
|
|
+ free_percpu(sirq_buffers);
|
|
|
+ err_sirq:
|
|
|
+ free_percpu(buffers);
|
|
|
+ err_warn:
|
|
|
+ WARN(1, "Could not allocate percpu trace_printk buffer");
|
|
|
+ return -ENOMEM;
|
|
|
+}
|
|
|
+
|
|
|
+void trace_printk_init_buffers(void)
|
|
|
+{
|
|
|
+ static int buffers_allocated;
|
|
|
+
|
|
|
+ if (buffers_allocated)
|
|
|
+ return;
|
|
|
+
|
|
|
+ if (alloc_percpu_trace_buffer())
|
|
|
+ return;
|
|
|
+
|
|
|
+ pr_info("ftrace: Allocated trace_printk buffers\n");
|
|
|
+
|
|
|
+ buffers_allocated = 1;
|
|
|
+}
|
|
|
+
|
|
|
/**
|
|
|
* trace_vbprintk - write binary msg to tracing buffer
|
|
|
*
|
|
|
*/
|
|
|
int trace_vbprintk(unsigned long ip, const char *fmt, va_list args)
|
|
|
{
|
|
|
- static arch_spinlock_t trace_buf_lock =
|
|
|
- (arch_spinlock_t)__ARCH_SPIN_LOCK_UNLOCKED;
|
|
|
- static u32 trace_buf[TRACE_BUF_SIZE];
|
|
|
-
|
|
|
struct ftrace_event_call *call = &event_bprint;
|
|
|
struct ring_buffer_event *event;
|
|
|
struct ring_buffer *buffer;
|
|
|
struct trace_array *tr = &global_trace;
|
|
|
- struct trace_array_cpu *data;
|
|
|
struct bprint_entry *entry;
|
|
|
unsigned long flags;
|
|
|
- int disable;
|
|
|
- int cpu, len = 0, size, pc;
|
|
|
+ char *tbuffer;
|
|
|
+ int len = 0, size, pc;
|
|
|
|
|
|
if (unlikely(tracing_selftest_running || tracing_disabled))
|
|
|
return 0;
|
|
@@ -1526,43 +1620,36 @@ int trace_vbprintk(unsigned long ip, const char *fmt, va_list args)
|
|
|
|
|
|
pc = preempt_count();
|
|
|
preempt_disable_notrace();
|
|
|
- cpu = raw_smp_processor_id();
|
|
|
- data = tr->data[cpu];
|
|
|
|
|
|
- disable = atomic_inc_return(&data->disabled);
|
|
|
- if (unlikely(disable != 1))
|
|
|
+ tbuffer = get_trace_buf();
|
|
|
+ if (!tbuffer) {
|
|
|
+ len = 0;
|
|
|
goto out;
|
|
|
+ }
|
|
|
|
|
|
- /* Lockdep uses trace_printk for lock tracing */
|
|
|
- local_irq_save(flags);
|
|
|
- arch_spin_lock(&trace_buf_lock);
|
|
|
- len = vbin_printf(trace_buf, TRACE_BUF_SIZE, fmt, args);
|
|
|
+ len = vbin_printf((u32 *)tbuffer, TRACE_BUF_SIZE/sizeof(int), fmt, args);
|
|
|
|
|
|
- if (len > TRACE_BUF_SIZE || len < 0)
|
|
|
- goto out_unlock;
|
|
|
+ if (len > TRACE_BUF_SIZE/sizeof(int) || len < 0)
|
|
|
+ goto out;
|
|
|
|
|
|
+ local_save_flags(flags);
|
|
|
size = sizeof(*entry) + sizeof(u32) * len;
|
|
|
buffer = tr->buffer;
|
|
|
event = trace_buffer_lock_reserve(buffer, TRACE_BPRINT, size,
|
|
|
flags, pc);
|
|
|
if (!event)
|
|
|
- goto out_unlock;
|
|
|
+ goto out;
|
|
|
entry = ring_buffer_event_data(event);
|
|
|
entry->ip = ip;
|
|
|
entry->fmt = fmt;
|
|
|
|
|
|
- memcpy(entry->buf, trace_buf, sizeof(u32) * len);
|
|
|
+ memcpy(entry->buf, tbuffer, sizeof(u32) * len);
|
|
|
if (!filter_check_discard(call, entry, buffer, event)) {
|
|
|
ring_buffer_unlock_commit(buffer, event);
|
|
|
ftrace_trace_stack(buffer, flags, 6, pc);
|
|
|
}
|
|
|
|
|
|
-out_unlock:
|
|
|
- arch_spin_unlock(&trace_buf_lock);
|
|
|
- local_irq_restore(flags);
|
|
|
-
|
|
|
out:
|
|
|
- atomic_dec_return(&data->disabled);
|
|
|
preempt_enable_notrace();
|
|
|
unpause_graph_tracing();
|
|
|
|
|
@@ -1588,58 +1675,53 @@ int trace_array_printk(struct trace_array *tr,
|
|
|
int trace_array_vprintk(struct trace_array *tr,
|
|
|
unsigned long ip, const char *fmt, va_list args)
|
|
|
{
|
|
|
- static arch_spinlock_t trace_buf_lock = __ARCH_SPIN_LOCK_UNLOCKED;
|
|
|
- static char trace_buf[TRACE_BUF_SIZE];
|
|
|
-
|
|
|
struct ftrace_event_call *call = &event_print;
|
|
|
struct ring_buffer_event *event;
|
|
|
struct ring_buffer *buffer;
|
|
|
- struct trace_array_cpu *data;
|
|
|
- int cpu, len = 0, size, pc;
|
|
|
+ int len = 0, size, pc;
|
|
|
struct print_entry *entry;
|
|
|
- unsigned long irq_flags;
|
|
|
- int disable;
|
|
|
+ unsigned long flags;
|
|
|
+ char *tbuffer;
|
|
|
|
|
|
if (tracing_disabled || tracing_selftest_running)
|
|
|
return 0;
|
|
|
|
|
|
+ /* Don't pollute graph traces with trace_vprintk internals */
|
|
|
+ pause_graph_tracing();
|
|
|
+
|
|
|
pc = preempt_count();
|
|
|
preempt_disable_notrace();
|
|
|
- cpu = raw_smp_processor_id();
|
|
|
- data = tr->data[cpu];
|
|
|
|
|
|
- disable = atomic_inc_return(&data->disabled);
|
|
|
- if (unlikely(disable != 1))
|
|
|
+
|
|
|
+ tbuffer = get_trace_buf();
|
|
|
+ if (!tbuffer) {
|
|
|
+ len = 0;
|
|
|
goto out;
|
|
|
+ }
|
|
|
|
|
|
- pause_graph_tracing();
|
|
|
- raw_local_irq_save(irq_flags);
|
|
|
- arch_spin_lock(&trace_buf_lock);
|
|
|
- len = vsnprintf(trace_buf, TRACE_BUF_SIZE, fmt, args);
|
|
|
+ len = vsnprintf(tbuffer, TRACE_BUF_SIZE, fmt, args);
|
|
|
+ if (len > TRACE_BUF_SIZE)
|
|
|
+ goto out;
|
|
|
|
|
|
+ local_save_flags(flags);
|
|
|
size = sizeof(*entry) + len + 1;
|
|
|
buffer = tr->buffer;
|
|
|
event = trace_buffer_lock_reserve(buffer, TRACE_PRINT, size,
|
|
|
- irq_flags, pc);
|
|
|
+ flags, pc);
|
|
|
if (!event)
|
|
|
- goto out_unlock;
|
|
|
+ goto out;
|
|
|
entry = ring_buffer_event_data(event);
|
|
|
entry->ip = ip;
|
|
|
|
|
|
- memcpy(&entry->buf, trace_buf, len);
|
|
|
+ memcpy(&entry->buf, tbuffer, len);
|
|
|
entry->buf[len] = '\0';
|
|
|
if (!filter_check_discard(call, entry, buffer, event)) {
|
|
|
ring_buffer_unlock_commit(buffer, event);
|
|
|
- ftrace_trace_stack(buffer, irq_flags, 6, pc);
|
|
|
+ ftrace_trace_stack(buffer, flags, 6, pc);
|
|
|
}
|
|
|
-
|
|
|
- out_unlock:
|
|
|
- arch_spin_unlock(&trace_buf_lock);
|
|
|
- raw_local_irq_restore(irq_flags);
|
|
|
- unpause_graph_tracing();
|
|
|
out:
|
|
|
- atomic_dec_return(&data->disabled);
|
|
|
preempt_enable_notrace();
|
|
|
+ unpause_graph_tracing();
|
|
|
|
|
|
return len;
|
|
|
}
|
|
@@ -4955,6 +5037,10 @@ __init static int tracer_alloc_buffers(void)
|
|
|
if (!alloc_cpumask_var(&tracing_cpumask, GFP_KERNEL))
|
|
|
goto out_free_buffer_mask;
|
|
|
|
|
|
+ /* Only allocate trace_printk buffers if a trace_printk exists */
|
|
|
+ if (__stop___trace_bprintk_fmt != __start___trace_bprintk_fmt)
|
|
|
+ trace_printk_init_buffers();
|
|
|
+
|
|
|
/* To save memory, keep the ring buffer size to its minimum */
|
|
|
if (ring_buffer_expanded)
|
|
|
ring_buf_size = trace_buf_size;
|