diff options
Diffstat (limited to 'kernel/trace/ring_buffer.c')
| -rw-r--r-- | kernel/trace/ring_buffer.c | 104 | 
1 files changed, 98 insertions, 6 deletions
diff --git a/kernel/trace/ring_buffer.c b/kernel/trace/ring_buffer.c index d59b6a328b7f..9712083832f4 100644 --- a/kernel/trace/ring_buffer.c +++ b/kernel/trace/ring_buffer.c @@ -413,6 +413,7 @@ struct rb_irq_work {  	struct irq_work			work;  	wait_queue_head_t		waiters;  	wait_queue_head_t		full_waiters; +	long				wait_index;  	bool				waiters_pending;  	bool				full_waiters_pending;  	bool				wakeup_full; @@ -884,7 +885,7 @@ size_t ring_buffer_nr_pages(struct trace_buffer *buffer, int cpu)  }  /** - * ring_buffer_nr_pages_dirty - get the number of used pages in the ring buffer + * ring_buffer_nr_dirty_pages - get the number of used pages in the ring buffer   * @buffer: The ring_buffer to get the number of pages from   * @cpu: The cpu of the ring_buffer to get the number of pages from   * @@ -917,13 +918,56 @@ static void rb_wake_up_waiters(struct irq_work *work)  	struct rb_irq_work *rbwork = container_of(work, struct rb_irq_work, work);  	wake_up_all(&rbwork->waiters); -	if (rbwork->wakeup_full) { +	if (rbwork->full_waiters_pending || rbwork->wakeup_full) {  		rbwork->wakeup_full = false; +		rbwork->full_waiters_pending = false;  		wake_up_all(&rbwork->full_waiters);  	}  }  /** + * ring_buffer_wake_waiters - wake up any waiters on this ring buffer + * @buffer: The ring buffer to wake waiters on + * + * In the case of a file that represents a ring buffer is closing, + * it is prudent to wake up any waiters that are on this. + */ +void ring_buffer_wake_waiters(struct trace_buffer *buffer, int cpu) +{ +	struct ring_buffer_per_cpu *cpu_buffer; +	struct rb_irq_work *rbwork; + +	if (!buffer) +		return; + +	if (cpu == RING_BUFFER_ALL_CPUS) { + +		/* Wake up individual ones too. One level recursion */ +		for_each_buffer_cpu(buffer, cpu) +			ring_buffer_wake_waiters(buffer, cpu); + +		rbwork = &buffer->irq_work; +	} else { +		if (WARN_ON_ONCE(!buffer->buffers)) +			return; +		if (WARN_ON_ONCE(cpu >= nr_cpu_ids)) +			return; + +		cpu_buffer = buffer->buffers[cpu]; +		/* The CPU buffer may not have been initialized yet */ +		if (!cpu_buffer) +			return; +		rbwork = &cpu_buffer->irq_work; +	} + +	rbwork->wait_index++; +	/* make sure the waiters see the new index */ +	smp_wmb(); + +	rb_wake_up_waiters(&rbwork->work); +} + +/**   * ring_buffer_wait - wait for input to the ring buffer   * @buffer: buffer to wait on   * @cpu: the cpu buffer to wait on @@ -938,6 +982,7 @@ int ring_buffer_wait(struct trace_buffer *buffer, int cpu, int full)  	struct ring_buffer_per_cpu *cpu_buffer;  	DEFINE_WAIT(wait);  	struct rb_irq_work *work; +	long wait_index;  	int ret = 0;  	/* @@ -956,6 +1001,7 @@ int ring_buffer_wait(struct trace_buffer *buffer, int cpu, int full)  		work = &cpu_buffer->irq_work;  	} +	wait_index = READ_ONCE(work->wait_index);  	while (true) {  		if (full) @@ -1011,7 +1057,7 @@ int ring_buffer_wait(struct trace_buffer *buffer, int cpu, int full)  			nr_pages = cpu_buffer->nr_pages;  			dirty = ring_buffer_nr_dirty_pages(buffer, cpu);  			if (!cpu_buffer->shortest_full || -			    cpu_buffer->shortest_full < full) +			    cpu_buffer->shortest_full > full)  				cpu_buffer->shortest_full = full;  			raw_spin_unlock_irqrestore(&cpu_buffer->reader_lock, flags);  			if (!pagebusy && @@ -1020,6 +1066,11 @@ int ring_buffer_wait(struct trace_buffer *buffer, int cpu, int full)  		}  		schedule(); + +		/* Make sure to see the new wait index */ +		smp_rmb(); +		if (wait_index != work->wait_index) +			break;  	}  	if (full) @@ -2608,6 +2659,9 @@ rb_reset_tail(struct ring_buffer_per_cpu *cpu_buffer,  		/* Mark the rest of the page with padding */  		rb_event_set_padding(event); +		/* Make sure the padding is visible before the write update */ +		smp_wmb(); +  		/* Set the write back to the previous setting */  		local_sub(length, &tail_page->write);  		return; @@ -2619,6 +2673,9 @@ rb_reset_tail(struct ring_buffer_per_cpu *cpu_buffer,  	/* time delta must be non zero */  	event->time_delta = 1; +	/* Make sure the padding is visible before the tail_page->write update */ +	smp_wmb(); +  	/* Set write to end of buffer */  	length = (tail + length) - BUF_PAGE_SIZE;  	local_sub(length, &tail_page->write); @@ -4587,6 +4644,33 @@ rb_get_reader_page(struct ring_buffer_per_cpu *cpu_buffer)  	arch_spin_unlock(&cpu_buffer->lock);  	local_irq_restore(flags); +	/* +	 * The writer has preempt disable, wait for it. But not forever +	 * Although, 1 second is pretty much "forever" +	 */ +#define USECS_WAIT	1000000 +        for (nr_loops = 0; nr_loops < USECS_WAIT; nr_loops++) { +		/* If the write is past the end of page, a writer is still updating it */ +		if (likely(!reader || rb_page_write(reader) <= BUF_PAGE_SIZE)) +			break; + +		udelay(1); + +		/* Get the latest version of the reader write value */ +		smp_rmb(); +	} + +	/* The writer is not moving forward? Something is wrong */ +	if (RB_WARN_ON(cpu_buffer, nr_loops == USECS_WAIT)) +		reader = NULL; + +	/* +	 * Make sure we see any padding after the write update +	 * (see rb_reset_tail()) +	 */ +	smp_rmb(); + +  	return reader;  } @@ -5232,7 +5316,7 @@ void ring_buffer_reset_cpu(struct trace_buffer *buffer, int cpu)  EXPORT_SYMBOL_GPL(ring_buffer_reset_cpu);  /** - * ring_buffer_reset_cpu - reset a ring buffer per CPU buffer + * ring_buffer_reset_online_cpus - reset a ring buffer per CPU buffer   * @buffer: The ring buffer to reset a per cpu buffer of   * @cpu: The CPU buffer to be reset   */ @@ -5302,7 +5386,7 @@ void ring_buffer_reset(struct trace_buffer *buffer)  EXPORT_SYMBOL_GPL(ring_buffer_reset);  /** - * rind_buffer_empty - is the ring buffer empty? + * ring_buffer_empty - is the ring buffer empty?   * @buffer: The ring buffer to test   */  bool ring_buffer_empty(struct trace_buffer *buffer) @@ -5616,7 +5700,15 @@ int ring_buffer_read_page(struct trace_buffer *buffer,  		unsigned int pos = 0;  		unsigned int size; -		if (full) +		/* +		 * If a full page is expected, this can still be returned +		 * if there's been a previous partial read and the +		 * rest of the page can be read and the commit page is off +		 * the reader page. +		 */ +		if (full && +		    (!read || (len < (commit - read)) || +		     cpu_buffer->reader_page == cpu_buffer->commit_page))  			goto out_unlock;  		if (len > (commit - read))  |