rcu: Implement drain_call_rcu

This will allow is to preserve the semantics of hmp_device_del,
that the device is deleted immediatly which was changed by previos
patch that delayed this to RCU callback

Signed-off-by: Maxim Levitsky <mlevitsk@redhat.com>
Suggested-by: Stefan Hajnoczi <stefanha@gmail.com>
Reviewed-by: Stefan Hajnoczi <stefanha@redhat.com>
Message-Id: <20200915121318.247-2-luoyonggang@gmail.com>
Signed-off-by: Thomas Huth <thuth@redhat.com>
This commit is contained in:
Maxim Levitsky 2020-09-15 20:12:53 +08:00 committed by Thomas Huth
parent 37d98abdc7
commit d816614ca4
2 changed files with 56 additions and 0 deletions

View File

@ -133,6 +133,7 @@ struct rcu_head {
};
extern void call_rcu1(struct rcu_head *head, RCUCBFunc *func);
extern void drain_call_rcu(void);
/* The operands of the minus operator must have the same type,
* which must be the one that we specify in the cast.

View File

@ -293,6 +293,61 @@ void call_rcu1(struct rcu_head *node, void (*func)(struct rcu_head *node))
qemu_event_set(&rcu_call_ready_event);
}
struct rcu_drain {
struct rcu_head rcu;
QemuEvent drain_complete_event;
};
static void drain_rcu_callback(struct rcu_head *node)
{
struct rcu_drain *event = (struct rcu_drain *)node;
qemu_event_set(&event->drain_complete_event);
}
/*
* This function ensures that all pending RCU callbacks
* on the current thread are done executing
* drops big qemu lock during the wait to allow RCU thread
* to process the callbacks
*
*/
void drain_call_rcu(void)
{
struct rcu_drain rcu_drain;
bool locked = qemu_mutex_iothread_locked();
memset(&rcu_drain, 0, sizeof(struct rcu_drain));
qemu_event_init(&rcu_drain.drain_complete_event, false);
if (locked) {
qemu_mutex_unlock_iothread();
}
/*
* RCU callbacks are invoked in the same order as in which they
* are registered, thus we can be sure that when 'drain_rcu_callback'
* is called, all RCU callbacks that were registered on this thread
* prior to calling this function are completed.
*
* Note that since we have only one global queue of the RCU callbacks,
* we also end up waiting for most of RCU callbacks that were registered
* on the other threads, but this is a side effect that shoudn't be
* assumed.
*/
call_rcu1(&rcu_drain.rcu, drain_rcu_callback);
qemu_event_wait(&rcu_drain.drain_complete_event);
if (locked) {
qemu_mutex_lock_iothread();
}
}
void rcu_register_thread(void)
{
assert(rcu_reader.ctr == 0);