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
20 typedef 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
29 static QTAILQ_HEAD(, Event) events_list = QTAILQ_HEAD_INITIALIZER(events_list);
30 static bool events_enabled;
31
32 /* Functions */
33
replay_run_event(Event * event)34 static 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
replay_enable_events(void)67 void replay_enable_events(void)
68 {
69 if (replay_mode != REPLAY_MODE_NONE) {
70 events_enabled = true;
71 }
72 }
73
replay_has_events(void)74 bool replay_has_events(void)
75 {
76 return !QTAILQ_EMPTY(&events_list);
77 }
78
replay_flush_events(void)79 void 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
95 /*! Adds specified async event to the queue */
replay_add_event(ReplayAsyncEventKind event_kind,void * opaque,void * opaque2,uint64_t id)96 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_new0(Event, 1);
114 event->event_kind = event_kind;
115 event->opaque = opaque;
116 event->opaque2 = opaque2;
117 event->id = id;
118
119 g_assert(replay_mutex_locked());
120 QTAILQ_INSERT_TAIL(&events_list, event, events);
121 qemu_cpu_kick(first_cpu);
122 }
123
replay_bh_schedule_event(QEMUBH * bh)124 void replay_bh_schedule_event(QEMUBH *bh)
125 {
126 if (events_enabled) {
127 uint64_t id = replay_get_current_icount();
128 replay_add_event(REPLAY_ASYNC_EVENT_BH, bh, NULL, id);
129 } else {
130 qemu_bh_schedule(bh);
131 }
132 }
133
replay_bh_schedule_oneshot_event(AioContext * ctx,QEMUBHFunc * cb,void * opaque)134 void replay_bh_schedule_oneshot_event(AioContext *ctx,
135 QEMUBHFunc *cb, void *opaque)
136 {
137 if (events_enabled) {
138 uint64_t id = replay_get_current_icount();
139 replay_add_event(REPLAY_ASYNC_EVENT_BH_ONESHOT, cb, opaque, id);
140 } else {
141 aio_bh_schedule_oneshot(ctx, cb, opaque);
142 }
143 }
144
replay_add_input_event(struct InputEvent * event)145 void replay_add_input_event(struct InputEvent *event)
146 {
147 replay_add_event(REPLAY_ASYNC_EVENT_INPUT, event, NULL, 0);
148 }
149
replay_add_input_sync_event(void)150 void replay_add_input_sync_event(void)
151 {
152 replay_add_event(REPLAY_ASYNC_EVENT_INPUT_SYNC, NULL, NULL, 0);
153 }
154
replay_block_event(QEMUBH * bh,uint64_t id)155 void replay_block_event(QEMUBH *bh, uint64_t id)
156 {
157 if (events_enabled) {
158 replay_add_event(REPLAY_ASYNC_EVENT_BLOCK, bh, NULL, id);
159 } else {
160 qemu_bh_schedule(bh);
161 }
162 }
163
replay_save_event(Event * event)164 static void replay_save_event(Event *event)
165 {
166 if (replay_mode != REPLAY_MODE_PLAY) {
167 /* put the event into the file */
168 g_assert(event->event_kind < REPLAY_ASYNC_COUNT);
169 replay_put_event(EVENT_ASYNC + event->event_kind);
170
171 /* save event-specific data */
172 switch (event->event_kind) {
173 case REPLAY_ASYNC_EVENT_BH:
174 case REPLAY_ASYNC_EVENT_BH_ONESHOT:
175 replay_put_qword(event->id);
176 break;
177 case REPLAY_ASYNC_EVENT_INPUT:
178 replay_save_input_event(event->opaque);
179 break;
180 case REPLAY_ASYNC_EVENT_INPUT_SYNC:
181 break;
182 case REPLAY_ASYNC_EVENT_CHAR_READ:
183 replay_event_char_read_save(event->opaque);
184 break;
185 case REPLAY_ASYNC_EVENT_BLOCK:
186 replay_put_qword(event->id);
187 break;
188 case REPLAY_ASYNC_EVENT_NET:
189 replay_event_net_save(event->opaque);
190 break;
191 default:
192 error_report("Unknown ID %" PRId64 " of replay event", event->id);
193 exit(1);
194 }
195 }
196 }
197
198 /* Called with replay mutex locked */
replay_save_events(void)199 void replay_save_events(void)
200 {
201 g_assert(replay_mutex_locked());
202 while (!QTAILQ_EMPTY(&events_list)) {
203 Event *event = QTAILQ_FIRST(&events_list);
204 replay_save_event(event);
205 replay_run_event(event);
206 QTAILQ_REMOVE(&events_list, event, events);
207 g_free(event);
208 }
209 }
210
replay_read_event(void)211 static Event *replay_read_event(void)
212 {
213 Event *event;
214 ReplayAsyncEventKind event_kind = replay_state.data_kind - EVENT_ASYNC;
215
216 /* Events that has not to be in the queue */
217 switch (event_kind) {
218 case REPLAY_ASYNC_EVENT_BH:
219 case REPLAY_ASYNC_EVENT_BH_ONESHOT:
220 if (replay_state.read_event_id == -1) {
221 replay_state.read_event_id = replay_get_qword();
222 }
223 break;
224 case REPLAY_ASYNC_EVENT_INPUT:
225 event = g_new0(Event, 1);
226 event->event_kind = event_kind;
227 event->opaque = replay_read_input_event();
228 return event;
229 case REPLAY_ASYNC_EVENT_INPUT_SYNC:
230 event = g_new0(Event, 1);
231 event->event_kind = event_kind;
232 event->opaque = 0;
233 return event;
234 case REPLAY_ASYNC_EVENT_CHAR_READ:
235 event = g_new0(Event, 1);
236 event->event_kind = event_kind;
237 event->opaque = replay_event_char_read_load();
238 return event;
239 case REPLAY_ASYNC_EVENT_BLOCK:
240 if (replay_state.read_event_id == -1) {
241 replay_state.read_event_id = replay_get_qword();
242 }
243 break;
244 case REPLAY_ASYNC_EVENT_NET:
245 event = g_new0(Event, 1);
246 event->event_kind = event_kind;
247 event->opaque = replay_event_net_load();
248 return event;
249 default:
250 error_report("Unknown ID %d of replay event", event_kind);
251 exit(1);
252 break;
253 }
254
255 QTAILQ_FOREACH(event, &events_list, events) {
256 if (event->event_kind == event_kind
257 && (replay_state.read_event_id == -1
258 || replay_state.read_event_id == event->id)) {
259 break;
260 }
261 }
262
263 if (event) {
264 QTAILQ_REMOVE(&events_list, event, events);
265 }
266
267 return event;
268 }
269
270 /* Called with replay mutex locked */
replay_read_events(void)271 void replay_read_events(void)
272 {
273 g_assert(replay_mutex_locked());
274 while (replay_state.data_kind >= EVENT_ASYNC
275 && replay_state.data_kind <= EVENT_ASYNC_LAST) {
276 Event *event = replay_read_event();
277 if (!event) {
278 break;
279 }
280 replay_finish_event();
281 replay_state.read_event_id = -1;
282 replay_run_event(event);
283
284 g_free(event);
285 }
286 }
287
replay_init_events(void)288 void replay_init_events(void)
289 {
290 replay_state.read_event_id = -1;
291 }
292
replay_finish_events(void)293 void replay_finish_events(void)
294 {
295 events_enabled = false;
296 replay_flush_events();
297 }
298
replay_events_enabled(void)299 bool replay_events_enabled(void)
300 {
301 return events_enabled;
302 }
303
blkreplay_next_id(void)304 uint64_t blkreplay_next_id(void)
305 {
306 if (replay_events_enabled()) {
307 return replay_state.block_request_id++;
308 }
309 return 0;
310 }
311