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-common.h" 13 #include "qemu/error-report.h" 14 #include "sysemu/replay.h" 15 #include "replay-internal.h" 16 #include "block/aio.h" 17 18 typedef struct Event { 19 ReplayAsyncEventKind event_kind; 20 void *opaque; 21 void *opaque2; 22 uint64_t id; 23 24 QTAILQ_ENTRY(Event) events; 25 } Event; 26 27 static QTAILQ_HEAD(, Event) events_list = QTAILQ_HEAD_INITIALIZER(events_list); 28 static unsigned int read_event_kind = -1; 29 static uint64_t read_id = -1; 30 static int read_checkpoint = -1; 31 32 static bool events_enabled; 33 34 /* Functions */ 35 36 static void replay_run_event(Event *event) 37 { 38 switch (event->event_kind) { 39 case REPLAY_ASYNC_EVENT_BH: 40 aio_bh_call(event->opaque); 41 break; 42 default: 43 error_report("Replay: invalid async event ID (%d) in the queue", 44 event->event_kind); 45 exit(1); 46 break; 47 } 48 } 49 50 void replay_enable_events(void) 51 { 52 events_enabled = true; 53 } 54 55 bool replay_has_events(void) 56 { 57 return !QTAILQ_EMPTY(&events_list); 58 } 59 60 void replay_flush_events(void) 61 { 62 replay_mutex_lock(); 63 while (!QTAILQ_EMPTY(&events_list)) { 64 Event *event = QTAILQ_FIRST(&events_list); 65 replay_mutex_unlock(); 66 replay_run_event(event); 67 replay_mutex_lock(); 68 QTAILQ_REMOVE(&events_list, event, events); 69 g_free(event); 70 } 71 replay_mutex_unlock(); 72 } 73 74 void replay_disable_events(void) 75 { 76 if (replay_mode != REPLAY_MODE_NONE) { 77 events_enabled = false; 78 /* Flush events queue before waiting of completion */ 79 replay_flush_events(); 80 } 81 } 82 83 void replay_clear_events(void) 84 { 85 replay_mutex_lock(); 86 while (!QTAILQ_EMPTY(&events_list)) { 87 Event *event = QTAILQ_FIRST(&events_list); 88 QTAILQ_REMOVE(&events_list, event, events); 89 90 g_free(event); 91 } 92 replay_mutex_unlock(); 93 } 94 95 /*! Adds specified async event to the queue */ 96 static void replay_add_event(ReplayAsyncEventKind event_kind, 97 void *opaque, 98 void *opaque2, uint64_t id) 99 { 100 assert(event_kind < REPLAY_ASYNC_COUNT); 101 102 if (!replay_file || replay_mode == REPLAY_MODE_NONE 103 || !events_enabled) { 104 Event e; 105 e.event_kind = event_kind; 106 e.opaque = opaque; 107 e.opaque2 = opaque2; 108 e.id = id; 109 replay_run_event(&e); 110 return; 111 } 112 113 Event *event = g_malloc0(sizeof(Event)); 114 event->event_kind = event_kind; 115 event->opaque = opaque; 116 event->opaque2 = opaque2; 117 event->id = id; 118 119 replay_mutex_lock(); 120 QTAILQ_INSERT_TAIL(&events_list, event, events); 121 replay_mutex_unlock(); 122 } 123 124 void replay_bh_schedule_event(QEMUBH *bh) 125 { 126 if (replay_mode != REPLAY_MODE_NONE) { 127 uint64_t id = replay_get_current_step(); 128 replay_add_event(REPLAY_ASYNC_EVENT_BH, bh, NULL, id); 129 } else { 130 qemu_bh_schedule(bh); 131 } 132 } 133 134 static void replay_save_event(Event *event, int checkpoint) 135 { 136 if (replay_mode != REPLAY_MODE_PLAY) { 137 /* put the event into the file */ 138 replay_put_event(EVENT_ASYNC); 139 replay_put_byte(checkpoint); 140 replay_put_byte(event->event_kind); 141 142 /* save event-specific data */ 143 switch (event->event_kind) { 144 case REPLAY_ASYNC_EVENT_BH: 145 replay_put_qword(event->id); 146 break; 147 default: 148 error_report("Unknown ID %d of replay event", read_event_kind); 149 exit(1); 150 } 151 } 152 } 153 154 /* Called with replay mutex locked */ 155 void replay_save_events(int checkpoint) 156 { 157 while (!QTAILQ_EMPTY(&events_list)) { 158 Event *event = QTAILQ_FIRST(&events_list); 159 replay_save_event(event, checkpoint); 160 161 replay_mutex_unlock(); 162 replay_run_event(event); 163 replay_mutex_lock(); 164 QTAILQ_REMOVE(&events_list, event, events); 165 g_free(event); 166 } 167 } 168 169 static Event *replay_read_event(int checkpoint) 170 { 171 Event *event; 172 if (read_event_kind == -1) { 173 read_checkpoint = replay_get_byte(); 174 read_event_kind = replay_get_byte(); 175 read_id = -1; 176 replay_check_error(); 177 } 178 179 if (checkpoint != read_checkpoint) { 180 return NULL; 181 } 182 183 /* Events that has not to be in the queue */ 184 switch (read_event_kind) { 185 case REPLAY_ASYNC_EVENT_BH: 186 if (read_id == -1) { 187 read_id = replay_get_qword(); 188 } 189 break; 190 default: 191 error_report("Unknown ID %d of replay event", read_event_kind); 192 exit(1); 193 break; 194 } 195 196 QTAILQ_FOREACH(event, &events_list, events) { 197 if (event->event_kind == read_event_kind 198 && (read_id == -1 || read_id == event->id)) { 199 break; 200 } 201 } 202 203 if (event) { 204 QTAILQ_REMOVE(&events_list, event, events); 205 } else { 206 return NULL; 207 } 208 209 /* Read event-specific data */ 210 211 return event; 212 } 213 214 /* Called with replay mutex locked */ 215 void replay_read_events(int checkpoint) 216 { 217 while (replay_data_kind == EVENT_ASYNC) { 218 Event *event = replay_read_event(checkpoint); 219 if (!event) { 220 break; 221 } 222 replay_mutex_unlock(); 223 replay_run_event(event); 224 replay_mutex_lock(); 225 226 g_free(event); 227 replay_finish_event(); 228 read_event_kind = -1; 229 } 230 } 231 232 void replay_init_events(void) 233 { 234 read_event_kind = -1; 235 } 236 237 void replay_finish_events(void) 238 { 239 events_enabled = false; 240 replay_clear_events(); 241 } 242 243 bool replay_events_enabled(void) 244 { 245 return events_enabled; 246 } 247