diff options
| -rw-r--r-- | kernel/events/ring_buffer.c | 24 | 
1 files changed, 20 insertions, 4 deletions
| diff --git a/kernel/events/ring_buffer.c b/kernel/events/ring_buffer.c index 674b35383491..009467a60578 100644 --- a/kernel/events/ring_buffer.c +++ b/kernel/events/ring_buffer.c @@ -51,11 +51,18 @@ again:  	head = local_read(&rb->head);  	/* -	 * IRQ/NMI can happen here, which means we can miss a head update. +	 * IRQ/NMI can happen here and advance @rb->head, causing our +	 * load above to be stale.  	 */ -	if (!local_dec_and_test(&rb->nest)) +	/* +	 * If this isn't the outermost nesting, we don't have to update +	 * @rb->user_page->data_head. +	 */ +	if (local_read(&rb->nest) > 1) { +		local_dec(&rb->nest);  		goto out; +	}  	/*  	 * Since the mmap() consumer (userspace) can run on a different CPU: @@ -87,9 +94,18 @@ again:  	rb->user_page->data_head = head;  	/* -	 * Now check if we missed an update -- rely on previous implied -	 * compiler barriers to force a re-read. +	 * We must publish the head before decrementing the nest count, +	 * otherwise an IRQ/NMI can publish a more recent head value and our +	 * write will (temporarily) publish a stale value. +	 */ +	barrier(); +	local_set(&rb->nest, 0); + +	/* +	 * Ensure we decrement @rb->nest before we validate the @rb->head. +	 * Otherwise we cannot be sure we caught the 'last' nested update.  	 */ +	barrier();  	if (unlikely(head != local_read(&rb->head))) {  		local_inc(&rb->nest);  		goto again; |