Skip to content

Commit

Permalink
replay: push replay_mutex_lock up the call tree
Browse files Browse the repository at this point in the history
Now instead of using the replay_lock to guard the output of the log we
now use it to protect the whole execution section. This replaces what
the BQL used to do when it was held during TCG execution.

We also introduce some rules for locking order - mainly that you
cannot take the replay_mutex while holding the BQL. This leads to some
slight sophistry during start-up and extending the
replay_mutex_destroy function to unlock the mutex without checking
for the BQL condition so it can be cleanly dropped in the non-replay
case.

Signed-off-by: Alex Bennée <alex.bennee@linaro.org>
Signed-off-by: Pavel Dovgalyuk <pavel.dovgaluk@ispras.ru>
Tested-by: Pavel Dovgalyuk <pavel.dovgaluk@ispras.ru>
Message-Id: <20180227095248.1060.40374.stgit@pasha-VirtualBox>
Signed-off-by: Paolo Bonzini <pbonzini@redhat.com>
Signed-off-by: Alex Bennée <alex.bennee@linaro.org>
  • Loading branch information
stsquad authored and bonzini committed Mar 12, 2018
1 parent 1a42389 commit d759c95
Show file tree
Hide file tree
Showing 10 changed files with 109 additions and 76 deletions.
24 changes: 22 additions & 2 deletions cpus.c
Original file line number Diff line number Diff line change
Expand Up @@ -1317,6 +1317,8 @@ static void prepare_icount_for_run(CPUState *cpu)
insns_left = MIN(0xffff, cpu->icount_budget);
cpu->icount_decr.u16.low = insns_left;
cpu->icount_extra = cpu->icount_budget - insns_left;

replay_mutex_lock();
}
}

Expand All @@ -1332,6 +1334,8 @@ static void process_icount_data(CPUState *cpu)
cpu->icount_budget = 0;

replay_account_executed_instructions();

replay_mutex_unlock();
}
}

Expand All @@ -1346,11 +1350,9 @@ static int tcg_cpu_exec(CPUState *cpu)
#ifdef CONFIG_PROFILER
ti = profile_getclock();
#endif
qemu_mutex_unlock_iothread();
cpu_exec_start(cpu);
ret = cpu_exec(cpu);
cpu_exec_end(cpu);
qemu_mutex_lock_iothread();
#ifdef CONFIG_PROFILER
tcg_time += profile_getclock() - ti;
#endif
Expand Down Expand Up @@ -1417,6 +1419,9 @@ static void *qemu_tcg_rr_cpu_thread_fn(void *arg)
cpu->exit_request = 1;

while (1) {
qemu_mutex_unlock_iothread();
replay_mutex_lock();
qemu_mutex_lock_iothread();
/* Account partial waits to QEMU_CLOCK_VIRTUAL. */
qemu_account_warp_timer();

Expand All @@ -1425,6 +1430,8 @@ static void *qemu_tcg_rr_cpu_thread_fn(void *arg)
*/
handle_icount_deadline();

replay_mutex_unlock();

if (!cpu) {
cpu = first_cpu;
}
Expand All @@ -1440,11 +1447,13 @@ static void *qemu_tcg_rr_cpu_thread_fn(void *arg)
if (cpu_can_run(cpu)) {
int r;

qemu_mutex_unlock_iothread();
prepare_icount_for_run(cpu);

r = tcg_cpu_exec(cpu);

process_icount_data(cpu);
qemu_mutex_lock_iothread();

if (r == EXCP_DEBUG) {
cpu_handle_guest_debug(cpu);
Expand Down Expand Up @@ -1634,7 +1643,9 @@ static void *qemu_tcg_cpu_thread_fn(void *arg)
while (1) {
if (cpu_can_run(cpu)) {
int r;
qemu_mutex_unlock_iothread();
r = tcg_cpu_exec(cpu);
qemu_mutex_lock_iothread();
switch (r) {
case EXCP_DEBUG:
cpu_handle_guest_debug(cpu);
Expand Down Expand Up @@ -1781,12 +1792,21 @@ void pause_all_vcpus(void)
}
}

/* We need to drop the replay_lock so any vCPU threads woken up
* can finish their replay tasks
*/
replay_mutex_unlock();

while (!all_vcpus_paused()) {
qemu_cond_wait(&qemu_pause_cond, &qemu_global_mutex);
CPU_FOREACH(cpu) {
qemu_cpu_kick(cpu);
}
}

qemu_mutex_unlock_iothread();
replay_mutex_lock();
qemu_mutex_lock_iothread();
}

void cpu_resume(CPUState *cpu)
Expand Down
22 changes: 22 additions & 0 deletions docs/replay.txt
Original file line number Diff line number Diff line change
Expand Up @@ -49,6 +49,28 @@ Modifications of qemu include:
* recording/replaying user input (mouse and keyboard)
* adding internal checkpoints for cpu and io synchronization

Locking and thread synchronisation
----------------------------------

Previously the synchronisation of the main thread and the vCPU thread
was ensured by the holding of the BQL. However the trend has been to
reduce the time the BQL was held across the system including under TCG
system emulation. As it is important that batches of events are kept
in sequence (e.g. expiring timers and checkpoints in the main thread
while instruction checkpoints are written by the vCPU thread) we need
another lock to keep things in lock-step. This role is now handled by
the replay_mutex_lock. It used to be held only for each event being
written but now it is held for a whole execution period. This results
in a deterministic ping-pong between the two main threads.

As the BQL is now a finer grained lock than the replay_lock it is almost
certainly a bug, and a source of deadlocks, to take the
replay_mutex_lock while the BQL is held. This is enforced by an assert.
While the unlocks are usually in the reverse order, this is not
necessary; you can drop the replay_lock while holding the BQL, without
doing a more complicated unlock_iothread/replay_unlock/lock_iothread
sequence.

Non-deterministic events
------------------------

Expand Down
14 changes: 4 additions & 10 deletions replay/replay-audio.c
Original file line number Diff line number Diff line change
Expand Up @@ -19,20 +19,17 @@
void replay_audio_out(int *played)
{
if (replay_mode == REPLAY_MODE_RECORD) {
g_assert(replay_mutex_locked());
replay_save_instructions();
replay_mutex_lock();
replay_put_event(EVENT_AUDIO_OUT);
replay_put_dword(*played);
replay_mutex_unlock();
} else if (replay_mode == REPLAY_MODE_PLAY) {
g_assert(replay_mutex_locked());
replay_account_executed_instructions();
replay_mutex_lock();
if (replay_next_event_is(EVENT_AUDIO_OUT)) {
*played = replay_get_dword();
replay_finish_event();
replay_mutex_unlock();
} else {
replay_mutex_unlock();
error_report("Missing audio out event in the replay log");
abort();
}
Expand All @@ -44,8 +41,8 @@ void replay_audio_in(int *recorded, void *samples, int *wpos, int size)
int pos;
uint64_t left, right;
if (replay_mode == REPLAY_MODE_RECORD) {
g_assert(replay_mutex_locked());
replay_save_instructions();
replay_mutex_lock();
replay_put_event(EVENT_AUDIO_IN);
replay_put_dword(*recorded);
replay_put_dword(*wpos);
Expand All @@ -55,10 +52,9 @@ void replay_audio_in(int *recorded, void *samples, int *wpos, int size)
replay_put_qword(left);
replay_put_qword(right);
}
replay_mutex_unlock();
} else if (replay_mode == REPLAY_MODE_PLAY) {
g_assert(replay_mutex_locked());
replay_account_executed_instructions();
replay_mutex_lock();
if (replay_next_event_is(EVENT_AUDIO_IN)) {
*recorded = replay_get_dword();
*wpos = replay_get_dword();
Expand All @@ -69,9 +65,7 @@ void replay_audio_in(int *recorded, void *samples, int *wpos, int size)
audio_sample_from_uint64(samples, pos, left, right);
}
replay_finish_event();
replay_mutex_unlock();
} else {
replay_mutex_unlock();
error_report("Missing audio in event in the replay log");
abort();
}
Expand Down
21 changes: 8 additions & 13 deletions replay/replay-char.c
Original file line number Diff line number Diff line change
Expand Up @@ -96,69 +96,64 @@ void *replay_event_char_read_load(void)

void replay_char_write_event_save(int res, int offset)
{
g_assert(replay_mutex_locked());

replay_save_instructions();
replay_mutex_lock();
replay_put_event(EVENT_CHAR_WRITE);
replay_put_dword(res);
replay_put_dword(offset);
replay_mutex_unlock();
}

void replay_char_write_event_load(int *res, int *offset)
{
g_assert(replay_mutex_locked());

replay_account_executed_instructions();
replay_mutex_lock();
if (replay_next_event_is(EVENT_CHAR_WRITE)) {
*res = replay_get_dword();
*offset = replay_get_dword();
replay_finish_event();
replay_mutex_unlock();
} else {
replay_mutex_unlock();
error_report("Missing character write event in the replay log");
exit(1);
}
}

int replay_char_read_all_load(uint8_t *buf)
{
replay_mutex_lock();
g_assert(replay_mutex_locked());

if (replay_next_event_is(EVENT_CHAR_READ_ALL)) {
size_t size;
int res;
replay_get_array(buf, &size);
replay_finish_event();
replay_mutex_unlock();
res = (int)size;
assert(res >= 0);
return res;
} else if (replay_next_event_is(EVENT_CHAR_READ_ALL_ERROR)) {
int res = replay_get_dword();
replay_finish_event();
replay_mutex_unlock();
return res;
} else {
replay_mutex_unlock();
error_report("Missing character read all event in the replay log");
exit(1);
}
}

void replay_char_read_all_save_error(int res)
{
g_assert(replay_mutex_locked());
assert(res < 0);
replay_save_instructions();
replay_mutex_lock();
replay_put_event(EVENT_CHAR_READ_ALL_ERROR);
replay_put_dword(res);
replay_mutex_unlock();
}

void replay_char_read_all_save_buf(uint8_t *buf, int offset)
{
g_assert(replay_mutex_locked());
replay_save_instructions();
replay_mutex_lock();
replay_put_event(EVENT_CHAR_READ_ALL);
replay_put_array(buf, offset);
replay_mutex_unlock();
}
20 changes: 7 additions & 13 deletions replay/replay-events.c
Original file line number Diff line number Diff line change
Expand Up @@ -79,16 +79,14 @@ bool replay_has_events(void)

void replay_flush_events(void)
{
replay_mutex_lock();
g_assert(replay_mutex_locked());

while (!QTAILQ_EMPTY(&events_list)) {
Event *event = QTAILQ_FIRST(&events_list);
replay_mutex_unlock();
replay_run_event(event);
replay_mutex_lock();
QTAILQ_REMOVE(&events_list, event, events);
g_free(event);
}
replay_mutex_unlock();
}

void replay_disable_events(void)
Expand All @@ -102,14 +100,14 @@ void replay_disable_events(void)

void replay_clear_events(void)
{
replay_mutex_lock();
g_assert(replay_mutex_locked());

while (!QTAILQ_EMPTY(&events_list)) {
Event *event = QTAILQ_FIRST(&events_list);
QTAILQ_REMOVE(&events_list, event, events);

g_free(event);
}
replay_mutex_unlock();
}

/*! Adds specified async event to the queue */
Expand All @@ -136,9 +134,8 @@ void replay_add_event(ReplayAsyncEventKind event_kind,
event->opaque2 = opaque2;
event->id = id;

replay_mutex_lock();
g_assert(replay_mutex_locked());
QTAILQ_INSERT_TAIL(&events_list, event, events);
replay_mutex_unlock();
}

void replay_bh_schedule_event(QEMUBH *bh)
Expand Down Expand Up @@ -207,13 +204,11 @@ static void replay_save_event(Event *event, int checkpoint)
/* Called with replay mutex locked */
void replay_save_events(int checkpoint)
{
g_assert(replay_mutex_locked());
while (!QTAILQ_EMPTY(&events_list)) {
Event *event = QTAILQ_FIRST(&events_list);
replay_save_event(event, checkpoint);

replay_mutex_unlock();
replay_run_event(event);
replay_mutex_lock();
QTAILQ_REMOVE(&events_list, event, events);
g_free(event);
}
Expand Down Expand Up @@ -292,16 +287,15 @@ static Event *replay_read_event(int checkpoint)
/* Called with replay mutex locked */
void replay_read_events(int checkpoint)
{
g_assert(replay_mutex_locked());
while (replay_state.data_kind == EVENT_ASYNC) {
Event *event = replay_read_event(checkpoint);
if (!event) {
break;
}
replay_finish_event();
read_event_kind = -1;
replay_mutex_unlock();
replay_run_event(event);
replay_mutex_lock();

g_free(event);
}
Expand Down
24 changes: 16 additions & 8 deletions replay/replay-internal.c
Original file line number Diff line number Diff line change
Expand Up @@ -174,32 +174,41 @@ static __thread bool replay_locked;
void replay_mutex_init(void)
{
qemu_mutex_init(&lock);
/* Hold the mutex while we start-up */
qemu_mutex_lock(&lock);
replay_locked = true;
}

bool replay_mutex_locked(void)
{
return replay_locked;
}

/* Ordering constraints, replay_lock must be taken before BQL */
void replay_mutex_lock(void)
{
g_assert(!replay_mutex_locked());
qemu_mutex_lock(&lock);
replay_locked = true;
if (replay_mode != REPLAY_MODE_NONE) {
g_assert(!qemu_mutex_iothread_locked());
g_assert(!replay_mutex_locked());
qemu_mutex_lock(&lock);
replay_locked = true;
}
}

void replay_mutex_unlock(void)
{
g_assert(replay_mutex_locked());
replay_locked = false;
qemu_mutex_unlock(&lock);
if (replay_mode != REPLAY_MODE_NONE) {
g_assert(replay_mutex_locked());
replay_locked = false;
qemu_mutex_unlock(&lock);
}
}

/*! Saves cached instructions. */
void replay_save_instructions(void)
{
if (replay_file && replay_mode == REPLAY_MODE_RECORD) {
replay_mutex_lock();
g_assert(replay_mutex_locked());
int diff = (int)(replay_get_current_step() - replay_state.current_step);

/* Time can only go forward */
Expand All @@ -210,6 +219,5 @@ void replay_save_instructions(void)
replay_put_dword(diff);
replay_state.current_step += diff;
}
replay_mutex_unlock();
}
}
Loading

0 comments on commit d759c95

Please sign in to comment.