diff options
Diffstat (limited to 'kernel/trace/trace.c')
| -rw-r--r-- | kernel/trace/trace.c | 184 | 
1 files changed, 135 insertions, 49 deletions
diff --git a/kernel/trace/trace.c b/kernel/trace/trace.c index ed7b5d1e12f..1ab8e35d069 100644 --- a/kernel/trace/trace.c +++ b/kernel/trace/trace.c @@ -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;  |