Skip to content

Commit

Permalink
Browse files Browse the repository at this point in the history
call_rcu: stop using mb_set/mb_read
Use a store-release when enqueuing a new call_rcu, and a load-acquire
when dequeuing; and read the tail after checking that node->next is
consistent, which is the standard message passing pattern and it is
clearer than mb_read/mb_set.

Reviewed-by: Richard Henderson <richard.henderson@linaro.org>
Signed-off-by: Paolo Bonzini <pbonzini@redhat.com>
  • Loading branch information
bonzini committed May 8, 2023
1 parent 355635c commit 8f593ba
Showing 1 changed file with 32 additions and 13 deletions.
45 changes: 32 additions & 13 deletions util/rcu.c
Expand Up @@ -189,35 +189,54 @@ static void enqueue(struct rcu_head *node)
struct rcu_head **old_tail;

node->next = NULL;

/*
* Make this node the tail of the list. The node will be
* used by further enqueue operations, but it will not
* be dequeued yet...
*/
old_tail = qatomic_xchg(&tail, &node->next);
qatomic_mb_set(old_tail, node);

/*
* ... until it is pointed to from another item in the list.
* In the meantime, try_dequeue() will find a NULL next pointer
* and loop.
*
* Synchronizes with qatomic_load_acquire() in try_dequeue().
*/
qatomic_store_release(old_tail, node);
}

static struct rcu_head *try_dequeue(void)
{
struct rcu_head *node, *next;

retry:
/* Test for an empty list, which we do not expect. Note that for
/* Head is only written by this thread, so no need for barriers. */
node = head;

/*
* If the head node has NULL in its next pointer, the value is
* wrong and we need to wait until its enqueuer finishes the update.
*/
next = qatomic_load_acquire(&node->next);
if (!next) {
return NULL;
}

/*
* Test for an empty list, which we do not expect. Note that for
* the consumer head and tail are always consistent. The head
* is consistent because only the consumer reads/writes it.
* The tail, because it is the first step in the enqueuing.
* It is only the next pointers that might be inconsistent.
*/
if (head == &dummy && qatomic_mb_read(&tail) == &dummy.next) {
if (head == &dummy && qatomic_read(&tail) == &dummy.next) {
abort();
}

/* If the head node has NULL in its next pointer, the value is
* wrong and we need to wait until its enqueuer finishes the update.
*/
node = head;
next = qatomic_mb_read(&head->next);
if (!next) {
return NULL;
}

/* Since we are the sole consumer, and we excluded the empty case
/*
* Since we are the sole consumer, and we excluded the empty case
* above, the queue will always have at least two nodes: the
* dummy node, and the one being removed. So we do not need to update
* the tail pointer.
Expand Down

0 comments on commit 8f593ba

Please sign in to comment.