mirror of
https://github.com/Motorhead1991/qemu.git
synced 2025-08-04 00:03:54 -06:00
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>
This commit is contained in:
parent
355635c018
commit
8f593ba9c5
1 changed files with 32 additions and 13 deletions
45
util/rcu.c
45
util/rcu.c
|
@ -189,8 +189,22 @@ static void enqueue(struct rcu_head *node)
|
||||||
struct rcu_head **old_tail;
|
struct rcu_head **old_tail;
|
||||||
|
|
||||||
node->next = NULL;
|
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);
|
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)
|
static struct rcu_head *try_dequeue(void)
|
||||||
|
@ -198,26 +212,31 @@ static struct rcu_head *try_dequeue(void)
|
||||||
struct rcu_head *node, *next;
|
struct rcu_head *node, *next;
|
||||||
|
|
||||||
retry:
|
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
|
* the consumer head and tail are always consistent. The head
|
||||||
* is consistent because only the consumer reads/writes it.
|
* is consistent because only the consumer reads/writes it.
|
||||||
* The tail, because it is the first step in the enqueuing.
|
* The tail, because it is the first step in the enqueuing.
|
||||||
* It is only the next pointers that might be inconsistent.
|
* 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();
|
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.
|
* Since we are the sole consumer, and we excluded the empty case
|
||||||
*/
|
|
||||||
node = head;
|
|
||||||
next = qatomic_mb_read(&head->next);
|
|
||||||
if (!next) {
|
|
||||||
return NULL;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Since we are the sole consumer, and we excluded the empty case
|
|
||||||
* above, the queue will always have at least two nodes: the
|
* 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
|
* dummy node, and the one being removed. So we do not need to update
|
||||||
* the tail pointer.
|
* the tail pointer.
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue