diff options
Diffstat (limited to 'kernel/trace/ring_buffer.c')
| -rw-r--r-- | kernel/trace/ring_buffer.c | 79 | 
1 files changed, 55 insertions, 24 deletions
| diff --git a/kernel/trace/ring_buffer.c b/kernel/trace/ring_buffer.c index 91874a95060d..5af2842dea96 100644 --- a/kernel/trace/ring_buffer.c +++ b/kernel/trace/ring_buffer.c @@ -280,6 +280,8 @@ EXPORT_SYMBOL_GPL(ring_buffer_event_data);  /* Missed count stored at end */  #define RB_MISSED_STORED	(1 << 30) +#define RB_MISSED_FLAGS		(RB_MISSED_EVENTS|RB_MISSED_STORED) +  struct buffer_data_page {  	u64		 time_stamp;	/* page time stamp */  	local_t		 commit;	/* write committed index */ @@ -331,7 +333,9 @@ static void rb_init_page(struct buffer_data_page *bpage)   */  size_t ring_buffer_page_len(void *page)  { -	return local_read(&((struct buffer_data_page *)page)->commit) +	struct buffer_data_page *bpage = page; + +	return (local_read(&bpage->commit) & ~RB_MISSED_FLAGS)  		+ BUF_PAGE_HDR_SIZE;  } @@ -1799,12 +1803,6 @@ void ring_buffer_change_overwrite(struct ring_buffer *buffer, int val)  }  EXPORT_SYMBOL_GPL(ring_buffer_change_overwrite); -static __always_inline void * -__rb_data_page_index(struct buffer_data_page *bpage, unsigned index) -{ -	return bpage->data + index; -} -  static __always_inline void *__rb_page_index(struct buffer_page *bpage, unsigned index)  {  	return bpage->page->data + index; @@ -2536,29 +2534,58 @@ rb_wakeups(struct ring_buffer *buffer, struct ring_buffer_per_cpu *cpu_buffer)   * The lock and unlock are done within a preempt disable section.   * The current_context per_cpu variable can only be modified   * by the current task between lock and unlock. But it can - * be modified more than once via an interrupt. There are four - * different contexts that we need to consider. + * be modified more than once via an interrupt. To pass this + * information from the lock to the unlock without having to + * access the 'in_interrupt()' functions again (which do show + * a bit of overhead in something as critical as function tracing, + * we use a bitmask trick. + * + *  bit 0 =  NMI context + *  bit 1 =  IRQ context + *  bit 2 =  SoftIRQ context + *  bit 3 =  normal context. + * + * This works because this is the order of contexts that can + * preempt other contexts. A SoftIRQ never preempts an IRQ + * context. + * + * When the context is determined, the corresponding bit is + * checked and set (if it was set, then a recursion of that context + * happened). + * + * On unlock, we need to clear this bit. To do so, just subtract + * 1 from the current_context and AND it to itself.   * - *  Normal context. - *  SoftIRQ context - *  IRQ context - *  NMI context + * (binary) + *  101 - 1 = 100 + *  101 & 100 = 100 (clearing bit zero)   * - * If for some reason the ring buffer starts to recurse, we - * only allow that to happen at most 4 times (one for each - * context). If it happens 5 times, then we consider this a - * recusive loop and do not let it go further. + *  1010 - 1 = 1001 + *  1010 & 1001 = 1000 (clearing bit 1) + * + * The least significant bit can be cleared this way, and it + * just so happens that it is the same bit corresponding to + * the current context.   */  static __always_inline int  trace_recursive_lock(struct ring_buffer_per_cpu *cpu_buffer)  { -	if (cpu_buffer->current_context >= 4) +	unsigned int val = cpu_buffer->current_context; +	unsigned long pc = preempt_count(); +	int bit; + +	if (!(pc & (NMI_MASK | HARDIRQ_MASK | SOFTIRQ_OFFSET))) +		bit = RB_CTX_NORMAL; +	else +		bit = pc & NMI_MASK ? RB_CTX_NMI : +			pc & HARDIRQ_MASK ? RB_CTX_IRQ : RB_CTX_SOFTIRQ; + +	if (unlikely(val & (1 << bit)))  		return 1; -	cpu_buffer->current_context++; -	/* Interrupts must see this update */ -	barrier(); +	val |= (1 << bit); +	cpu_buffer->current_context = val;  	return 0;  } @@ -2566,9 +2593,7 @@ trace_recursive_lock(struct ring_buffer_per_cpu *cpu_buffer)  static __always_inline void  trace_recursive_unlock(struct ring_buffer_per_cpu *cpu_buffer)  { -	/* Don't let the dec leak out */ -	barrier(); -	cpu_buffer->current_context--; +	cpu_buffer->current_context &= cpu_buffer->current_context - 1;  }  /** @@ -4406,8 +4431,13 @@ void ring_buffer_free_read_page(struct ring_buffer *buffer, int cpu, void *data)  {  	struct ring_buffer_per_cpu *cpu_buffer = buffer->buffers[cpu];  	struct buffer_data_page *bpage = data; +	struct page *page = virt_to_page(bpage);  	unsigned long flags; +	/* If the page is still in use someplace else, we can't reuse it */ +	if (page_ref_count(page) > 1) +		goto out; +  	local_irq_save(flags);  	arch_spin_lock(&cpu_buffer->lock); @@ -4419,6 +4449,7 @@ void ring_buffer_free_read_page(struct ring_buffer *buffer, int cpu, void *data)  	arch_spin_unlock(&cpu_buffer->lock);  	local_irq_restore(flags); + out:  	free_page((unsigned long)bpage);  }  EXPORT_SYMBOL_GPL(ring_buffer_free_read_page); |