replay-events.c (8910B)
1/* 2 * replay-events.c 3 * 4 * Copyright (c) 2010-2015 Institute for System Programming 5 * of the Russian Academy of Sciences. 6 * 7 * This work is licensed under the terms of the GNU GPL, version 2 or later. 8 * See the COPYING file in the top-level directory. 9 * 10 */ 11 12#include "qemu/osdep.h" 13#include "qemu/error-report.h" 14#include "sysemu/replay.h" 15#include "replay-internal.h" 16#include "block/aio.h" 17#include "ui/input.h" 18#include "hw/core/cpu.h" 19 20typedef struct Event { 21 ReplayAsyncEventKind event_kind; 22 void *opaque; 23 void *opaque2; 24 uint64_t id; 25 26 QTAILQ_ENTRY(Event) events; 27} Event; 28 29static QTAILQ_HEAD(, Event) events_list = QTAILQ_HEAD_INITIALIZER(events_list); 30static bool events_enabled; 31 32/* Functions */ 33 34static void replay_run_event(Event *event) 35{ 36 switch (event->event_kind) { 37 case REPLAY_ASYNC_EVENT_BH: 38 aio_bh_call(event->opaque); 39 break; 40 case REPLAY_ASYNC_EVENT_BH_ONESHOT: 41 ((QEMUBHFunc *)event->opaque)(event->opaque2); 42 break; 43 case REPLAY_ASYNC_EVENT_INPUT: 44 qemu_input_event_send_impl(NULL, (InputEvent *)event->opaque); 45 qapi_free_InputEvent((InputEvent *)event->opaque); 46 break; 47 case REPLAY_ASYNC_EVENT_INPUT_SYNC: 48 qemu_input_event_sync_impl(); 49 break; 50 case REPLAY_ASYNC_EVENT_CHAR_READ: 51 replay_event_char_read_run(event->opaque); 52 break; 53 case REPLAY_ASYNC_EVENT_BLOCK: 54 aio_bh_call(event->opaque); 55 break; 56 case REPLAY_ASYNC_EVENT_NET: 57 replay_event_net_run(event->opaque); 58 break; 59 default: 60 error_report("Replay: invalid async event ID (%d) in the queue", 61 event->event_kind); 62 exit(1); 63 break; 64 } 65} 66 67void replay_enable_events(void) 68{ 69 if (replay_mode != REPLAY_MODE_NONE) { 70 events_enabled = true; 71 } 72} 73 74bool replay_has_events(void) 75{ 76 return !QTAILQ_EMPTY(&events_list); 77} 78 79void replay_flush_events(void) 80{ 81 if (replay_mode == REPLAY_MODE_NONE) { 82 return; 83 } 84 85 g_assert(replay_mutex_locked()); 86 87 while (!QTAILQ_EMPTY(&events_list)) { 88 Event *event = QTAILQ_FIRST(&events_list); 89 replay_run_event(event); 90 QTAILQ_REMOVE(&events_list, event, events); 91 g_free(event); 92 } 93} 94 95void replay_disable_events(void) 96{ 97 if (replay_mode != REPLAY_MODE_NONE) { 98 events_enabled = false; 99 /* Flush events queue before waiting of completion */ 100 replay_flush_events(); 101 } 102} 103 104/*! Adds specified async event to the queue */ 105void replay_add_event(ReplayAsyncEventKind event_kind, 106 void *opaque, 107 void *opaque2, uint64_t id) 108{ 109 assert(event_kind < REPLAY_ASYNC_COUNT); 110 111 if (!replay_file || replay_mode == REPLAY_MODE_NONE 112 || !events_enabled) { 113 Event e; 114 e.event_kind = event_kind; 115 e.opaque = opaque; 116 e.opaque2 = opaque2; 117 e.id = id; 118 replay_run_event(&e); 119 return; 120 } 121 122 Event *event = g_malloc0(sizeof(Event)); 123 event->event_kind = event_kind; 124 event->opaque = opaque; 125 event->opaque2 = opaque2; 126 event->id = id; 127 128 g_assert(replay_mutex_locked()); 129 QTAILQ_INSERT_TAIL(&events_list, event, events); 130 qemu_cpu_kick(first_cpu); 131} 132 133void replay_bh_schedule_event(QEMUBH *bh) 134{ 135 if (events_enabled) { 136 uint64_t id = replay_get_current_icount(); 137 replay_add_event(REPLAY_ASYNC_EVENT_BH, bh, NULL, id); 138 } else { 139 qemu_bh_schedule(bh); 140 } 141} 142 143void replay_bh_schedule_oneshot_event(AioContext *ctx, 144 QEMUBHFunc *cb, void *opaque) 145{ 146 if (events_enabled) { 147 uint64_t id = replay_get_current_icount(); 148 replay_add_event(REPLAY_ASYNC_EVENT_BH_ONESHOT, cb, opaque, id); 149 } else { 150 aio_bh_schedule_oneshot(ctx, cb, opaque); 151 } 152} 153 154void replay_add_input_event(struct InputEvent *event) 155{ 156 replay_add_event(REPLAY_ASYNC_EVENT_INPUT, event, NULL, 0); 157} 158 159void replay_add_input_sync_event(void) 160{ 161 replay_add_event(REPLAY_ASYNC_EVENT_INPUT_SYNC, NULL, NULL, 0); 162} 163 164void replay_block_event(QEMUBH *bh, uint64_t id) 165{ 166 if (events_enabled) { 167 replay_add_event(REPLAY_ASYNC_EVENT_BLOCK, bh, NULL, id); 168 } else { 169 qemu_bh_schedule(bh); 170 } 171} 172 173static void replay_save_event(Event *event, int checkpoint) 174{ 175 if (replay_mode != REPLAY_MODE_PLAY) { 176 /* put the event into the file */ 177 replay_put_event(EVENT_ASYNC); 178 replay_put_byte(checkpoint); 179 replay_put_byte(event->event_kind); 180 181 /* save event-specific data */ 182 switch (event->event_kind) { 183 case REPLAY_ASYNC_EVENT_BH: 184 case REPLAY_ASYNC_EVENT_BH_ONESHOT: 185 replay_put_qword(event->id); 186 break; 187 case REPLAY_ASYNC_EVENT_INPUT: 188 replay_save_input_event(event->opaque); 189 break; 190 case REPLAY_ASYNC_EVENT_INPUT_SYNC: 191 break; 192 case REPLAY_ASYNC_EVENT_CHAR_READ: 193 replay_event_char_read_save(event->opaque); 194 break; 195 case REPLAY_ASYNC_EVENT_BLOCK: 196 replay_put_qword(event->id); 197 break; 198 case REPLAY_ASYNC_EVENT_NET: 199 replay_event_net_save(event->opaque); 200 break; 201 default: 202 error_report("Unknown ID %" PRId64 " of replay event", event->id); 203 exit(1); 204 } 205 } 206} 207 208/* Called with replay mutex locked */ 209void replay_save_events(int checkpoint) 210{ 211 g_assert(replay_mutex_locked()); 212 g_assert(checkpoint != CHECKPOINT_CLOCK_WARP_START); 213 g_assert(checkpoint != CHECKPOINT_CLOCK_VIRTUAL); 214 while (!QTAILQ_EMPTY(&events_list)) { 215 Event *event = QTAILQ_FIRST(&events_list); 216 replay_save_event(event, checkpoint); 217 replay_run_event(event); 218 QTAILQ_REMOVE(&events_list, event, events); 219 g_free(event); 220 } 221} 222 223static Event *replay_read_event(int checkpoint) 224{ 225 Event *event; 226 if (replay_state.read_event_kind == -1) { 227 replay_state.read_event_checkpoint = replay_get_byte(); 228 replay_state.read_event_kind = replay_get_byte(); 229 replay_state.read_event_id = -1; 230 replay_check_error(); 231 } 232 233 if (checkpoint != replay_state.read_event_checkpoint) { 234 return NULL; 235 } 236 237 /* Events that has not to be in the queue */ 238 switch (replay_state.read_event_kind) { 239 case REPLAY_ASYNC_EVENT_BH: 240 case REPLAY_ASYNC_EVENT_BH_ONESHOT: 241 if (replay_state.read_event_id == -1) { 242 replay_state.read_event_id = replay_get_qword(); 243 } 244 break; 245 case REPLAY_ASYNC_EVENT_INPUT: 246 event = g_malloc0(sizeof(Event)); 247 event->event_kind = replay_state.read_event_kind; 248 event->opaque = replay_read_input_event(); 249 return event; 250 case REPLAY_ASYNC_EVENT_INPUT_SYNC: 251 event = g_malloc0(sizeof(Event)); 252 event->event_kind = replay_state.read_event_kind; 253 event->opaque = 0; 254 return event; 255 case REPLAY_ASYNC_EVENT_CHAR_READ: 256 event = g_malloc0(sizeof(Event)); 257 event->event_kind = replay_state.read_event_kind; 258 event->opaque = replay_event_char_read_load(); 259 return event; 260 case REPLAY_ASYNC_EVENT_BLOCK: 261 if (replay_state.read_event_id == -1) { 262 replay_state.read_event_id = replay_get_qword(); 263 } 264 break; 265 case REPLAY_ASYNC_EVENT_NET: 266 event = g_malloc0(sizeof(Event)); 267 event->event_kind = replay_state.read_event_kind; 268 event->opaque = replay_event_net_load(); 269 return event; 270 default: 271 error_report("Unknown ID %d of replay event", 272 replay_state.read_event_kind); 273 exit(1); 274 break; 275 } 276 277 QTAILQ_FOREACH(event, &events_list, events) { 278 if (event->event_kind == replay_state.read_event_kind 279 && (replay_state.read_event_id == -1 280 || replay_state.read_event_id == event->id)) { 281 break; 282 } 283 } 284 285 if (event) { 286 QTAILQ_REMOVE(&events_list, event, events); 287 } else { 288 return NULL; 289 } 290 291 /* Read event-specific data */ 292 293 return event; 294} 295 296/* Called with replay mutex locked */ 297void replay_read_events(int checkpoint) 298{ 299 g_assert(replay_mutex_locked()); 300 while (replay_state.data_kind == EVENT_ASYNC) { 301 Event *event = replay_read_event(checkpoint); 302 if (!event) { 303 break; 304 } 305 replay_finish_event(); 306 replay_state.read_event_kind = -1; 307 replay_run_event(event); 308 309 g_free(event); 310 } 311} 312 313void replay_init_events(void) 314{ 315 replay_state.read_event_kind = -1; 316} 317 318void replay_finish_events(void) 319{ 320 events_enabled = false; 321 replay_flush_events(); 322} 323 324bool replay_events_enabled(void) 325{ 326 return events_enabled; 327} 328 329uint64_t blkreplay_next_id(void) 330{ 331 if (replay_events_enabled()) { 332 return replay_state.block_request_id++; 333 } 334 return 0; 335}