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