1 /*
2 * Copyright (C) 2015 Citrix Ltd.
3 *
4 * This program is free software; you can redistribute it and/or modify
5 * it under the terms of the GNU Lesser General Public License as published
6 * by the Free Software Foundation; version 2.1 only. with the special
7 * exception on linking described in file LICENSE.
8 *
9 * This program is distributed in the hope that it will be useful,
10 * but WITHOUT ANY WARRANTY; without even the implied warranty of
11 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 * GNU Lesser General Public License for more details.
13 */
14
15 #include "libxl_osdeps.h" /* must come before any other headers */
16
17 #include "libxl_internal.h"
18
19 /*
20 * Infrastructure for writing a domain to a libxl migration v2 stream.
21 *
22 * Entry points from outside:
23 * - libxl__stream_write_start()
24 * - Start writing a stream from the start.
25 * - libxl__stream_write_start_checkpoint()
26 * - Write the records which form a checkpoint into a stream.
27 *
28 * In normal operation, there are two tasks running at once; this
29 * stream processing, and the libxl-save-helper. check_all_finished()
30 * is used to join all the tasks in both success and error cases.
31 *
32 * Nomenclature for event callbacks:
33 * - $FOO_done(): Completion callback for $FOO
34 * - write_$FOO(): Set up the datacopier to write a $FOO
35 * - $BAR_header(): A $BAR record header only
36 * - $BAR_record(): A complete $BAR record with header and content
37 *
38 * The main loop for a plain VM writes:
39 * - Stream header
40 * - Libxc record
41 * - (optional) Emulator xenstore record
42 * - if (hvm)
43 * - Emulator context record
44 * - End record
45 *
46 * For checkpointed stream, there is a second loop which is triggered by a
47 * save-helper checkpoint callback. It writes:
48 * - (optional) Emulator xenstore record
49 * - if (hvm)
50 * - Emulator context record
51 * - Checkpoint end record
52 *
53 * For back channel stream:
54 * - libxl__stream_write_start()
55 * - Set up the stream to running state
56 *
57 * - Use libxl__stream_write_checkpoint_state to write the record. When the
58 * record is written out, call stream->checkpoint_callback() to return.
59 */
60
61 /* Success/error/cleanup handling. */
62 static void stream_success(libxl__egc *egc,
63 libxl__stream_write_state *stream);
64 static void stream_complete(libxl__egc *egc,
65 libxl__stream_write_state *stream, int rc);
66 static void stream_done(libxl__egc *egc,
67 libxl__stream_write_state *stream, int rc);
68 static void checkpoint_done(libxl__egc *egc,
69 libxl__stream_write_state *stream,
70 int rc);
71 static void check_all_finished(libxl__egc *egc,
72 libxl__stream_write_state *stream, int rc);
73
74 /* Event chain for a plain VM. */
75 static void stream_header_done(libxl__egc *egc,
76 libxl__datacopier_state *dc,
77 int rc, int onwrite, int errnoval);
78 static void libxc_header_done(libxl__egc *egc,
79 libxl__stream_write_state *stream);
80 /* libxl__xc_domain_save_done() lives here, event-order wise. */
81 static void write_emulator_xenstore_record(libxl__egc *egc,
82 libxl__stream_write_state *stream);
83 static void emulator_xenstore_record_done(libxl__egc *egc,
84 libxl__stream_write_state *stream);
85 static void write_emulator_context_record(libxl__egc *egc,
86 libxl__stream_write_state *stream);
87 static void emulator_context_read_done(libxl__egc *egc,
88 libxl__datacopier_state *dc,
89 int rc, int onwrite, int errnoval);
90 static void emulator_context_record_done(libxl__egc *egc,
91 libxl__stream_write_state *stream);
92 static void write_end_record(libxl__egc *egc,
93 libxl__stream_write_state *stream);
94
95 /* Event chain unique to checkpointed streams. */
96 static void write_checkpoint_end_record(libxl__egc *egc,
97 libxl__stream_write_state *stream);
98 static void checkpoint_end_record_done(libxl__egc *egc,
99 libxl__stream_write_state *stream);
100
101 /* checkpoint state */
102 static void write_checkpoint_state_done(libxl__egc *egc,
103 libxl__stream_write_state *stream);
104 static void checkpoint_state_done(libxl__egc *egc,
105 libxl__stream_write_state *stream, int rc);
106
107 /*----- Helpers -----*/
108
109 static void write_done(libxl__egc *egc,
110 libxl__datacopier_state *dc,
111 int rc, int onwrite, int errnoval);
112
113 /* Generic helper to set up writing some data to the stream. */
setup_generic_write(libxl__egc * egc,libxl__stream_write_state * stream,const char * what,libxl__sr_rec_hdr * hdr,libxl__sr_emulator_hdr * emu_hdr,void * body,sws_record_done_cb cb)114 static void setup_generic_write(libxl__egc *egc,
115 libxl__stream_write_state *stream,
116 const char *what,
117 libxl__sr_rec_hdr *hdr,
118 libxl__sr_emulator_hdr *emu_hdr,
119 void *body,
120 sws_record_done_cb cb)
121 {
122 static const uint8_t zero_padding[1U << REC_ALIGN_ORDER] = { 0 };
123
124 libxl__datacopier_state *dc = &stream->dc;
125 int rc;
126
127 assert(stream->record_done_callback == NULL);
128
129 dc->writewhat = what;
130 dc->used = 0;
131 dc->callback = write_done;
132 rc = libxl__datacopier_start(dc);
133
134 if (rc) {
135 stream_complete(egc, stream, rc);
136 return;
137 }
138
139 size_t padsz = ROUNDUP(hdr->length, REC_ALIGN_ORDER) - hdr->length;
140 uint32_t length = hdr->length;
141
142 /* Insert header */
143 libxl__datacopier_prefixdata(egc, dc, hdr, sizeof(*hdr));
144
145 /* Optional emulator sub-header */
146 if (emu_hdr) {
147 assert(length >= sizeof(*emu_hdr));
148 libxl__datacopier_prefixdata(egc, dc, emu_hdr, sizeof(*emu_hdr));
149 length -= sizeof(*emu_hdr);
150 }
151
152 /* Optional body */
153 if (body)
154 libxl__datacopier_prefixdata(egc, dc, body, length);
155
156 /* Any required padding */
157 if (padsz > 0)
158 libxl__datacopier_prefixdata(egc, dc,
159 zero_padding, padsz);
160 stream->record_done_callback = cb;
161 }
162
163 /* Helper to set up writing a regular record to the stream. */
setup_write(libxl__egc * egc,libxl__stream_write_state * stream,const char * what,libxl__sr_rec_hdr * hdr,void * body,sws_record_done_cb cb)164 static void setup_write(libxl__egc *egc,
165 libxl__stream_write_state *stream,
166 const char *what,
167 libxl__sr_rec_hdr *hdr,
168 void *body,
169 sws_record_done_cb cb)
170 {
171 setup_generic_write(egc, stream, what, hdr, NULL, body, cb);
172 }
173
174 /* Helper to set up writing a record with an emulator prefix to the stream. */
setup_emulator_write(libxl__egc * egc,libxl__stream_write_state * stream,const char * what,libxl__sr_rec_hdr * hdr,libxl__sr_emulator_hdr * emu_hdr,void * body,sws_record_done_cb cb)175 static void setup_emulator_write(libxl__egc *egc,
176 libxl__stream_write_state *stream,
177 const char *what,
178 libxl__sr_rec_hdr *hdr,
179 libxl__sr_emulator_hdr *emu_hdr,
180 void *body,
181 sws_record_done_cb cb)
182 {
183 assert(stream->emu_sub_hdr.id != EMULATOR_UNKNOWN);
184 setup_generic_write(egc, stream, what, hdr, emu_hdr, body, cb);
185 }
186
187
write_done(libxl__egc * egc,libxl__datacopier_state * dc,int rc,int onwrite,int errnoval)188 static void write_done(libxl__egc *egc,
189 libxl__datacopier_state *dc,
190 int rc, int onwrite, int errnoval)
191 {
192 libxl__stream_write_state *stream = CONTAINER_OF(dc, *stream, dc);
193 STATE_AO_GC(stream->ao);
194 sws_record_done_cb cb = stream->record_done_callback;
195
196 stream->record_done_callback = NULL;
197
198 if (onwrite || errnoval)
199 stream_complete(egc, stream, rc ?: ERROR_FAIL);
200 else
201 cb(egc, stream);
202 }
203
204 /*----- Entrypoints -----*/
205
libxl__stream_write_init(libxl__stream_write_state * stream)206 void libxl__stream_write_init(libxl__stream_write_state *stream)
207 {
208 assert(stream->ao);
209
210 stream->shs.ao = stream->ao;
211 libxl__save_helper_init(&stream->shs);
212
213 stream->rc = 0;
214 stream->running = false;
215 stream->in_checkpoint = false;
216 stream->sync_teardown = false;
217 FILLZERO(stream->dc);
218 stream->record_done_callback = NULL;
219 FILLZERO(stream->emu_dc);
220 stream->emu_carefd = NULL;
221 FILLZERO(stream->emu_rec_hdr);
222 FILLZERO(stream->emu_sub_hdr);
223 stream->emu_body = NULL;
224 stream->device_model_version = LIBXL_DEVICE_MODEL_VERSION_UNKNOWN;
225 }
226
libxl__stream_write_start(libxl__egc * egc,libxl__stream_write_state * stream)227 void libxl__stream_write_start(libxl__egc *egc,
228 libxl__stream_write_state *stream)
229 {
230 libxl__datacopier_state *dc = &stream->dc;
231 libxl__domain_save_state *dss = stream->dss;
232 STATE_AO_GC(stream->ao);
233 struct libxl__sr_hdr hdr;
234 int rc = 0;
235
236 libxl__stream_write_init(stream);
237
238 stream->running = true;
239
240 dc->ao = ao;
241 dc->readfd = -1;
242 dc->writewhat = "stream header";
243 dc->copywhat = "save v2 stream";
244 dc->writefd = stream->fd;
245 dc->maxsz = -1;
246 dc->callback = stream_header_done;
247
248 if (stream->back_channel)
249 return;
250
251 if (dss->type == LIBXL_DOMAIN_TYPE_HVM) {
252 stream->device_model_version =
253 libxl__device_model_version_running(gc, dss->domid);
254 switch (stream->device_model_version) {
255 case LIBXL_DEVICE_MODEL_VERSION_QEMU_XEN_TRADITIONAL:
256 stream->emu_sub_hdr.id = EMULATOR_QEMU_TRADITIONAL;
257 break;
258
259 case LIBXL_DEVICE_MODEL_VERSION_QEMU_XEN:
260 stream->emu_sub_hdr.id = EMULATOR_QEMU_UPSTREAM;
261 break;
262
263 default:
264 rc = ERROR_FAIL;
265 LOGD(ERROR, dss->domid, "Unknown emulator for HVM domain");
266 goto err;
267 }
268 stream->emu_sub_hdr.index = 0;
269 }
270
271 rc = libxl__datacopier_start(dc);
272 if (rc)
273 goto err;
274
275 FILLZERO(hdr);
276 hdr.ident = htobe64(RESTORE_STREAM_IDENT);
277 hdr.version = htobe32(RESTORE_STREAM_VERSION);
278 hdr.options = htobe32(0);
279
280 libxl__datacopier_prefixdata(egc, dc, &hdr, sizeof(hdr));
281 return;
282
283 err:
284 assert(rc);
285 stream_complete(egc, stream, rc);
286 }
287
libxl__stream_write_start_checkpoint(libxl__egc * egc,libxl__stream_write_state * stream)288 void libxl__stream_write_start_checkpoint(libxl__egc *egc,
289 libxl__stream_write_state *stream)
290 {
291 assert(stream->running);
292 assert(!stream->in_checkpoint);
293 assert(!stream->back_channel);
294 stream->in_checkpoint = true;
295
296 write_emulator_xenstore_record(egc, stream);
297 }
298
libxl__stream_write_abort(libxl__egc * egc,libxl__stream_write_state * stream,int rc)299 void libxl__stream_write_abort(libxl__egc *egc,
300 libxl__stream_write_state *stream, int rc)
301 {
302 assert(rc);
303
304 if (stream->running)
305 stream_complete(egc, stream, rc);
306 }
307
308 /*----- Event logic -----*/
309
stream_header_done(libxl__egc * egc,libxl__datacopier_state * dc,int rc,int onwrite,int errnoval)310 static void stream_header_done(libxl__egc *egc,
311 libxl__datacopier_state *dc,
312 int rc, int onwrite, int errnoval)
313 {
314 libxl__stream_write_state *stream = CONTAINER_OF(dc, *stream, dc);
315 STATE_AO_GC(stream->ao);
316 struct libxl__sr_rec_hdr rec;
317
318 if (rc || errnoval) {
319 stream_complete(egc, stream, rc ?: ERROR_FAIL);
320 return;
321 }
322
323 FILLZERO(rec);
324 rec.type = REC_TYPE_LIBXC_CONTEXT;
325
326 setup_write(egc, stream, "libxc header",
327 &rec, NULL, libxc_header_done);
328 }
329
libxc_header_done(libxl__egc * egc,libxl__stream_write_state * stream)330 static void libxc_header_done(libxl__egc *egc,
331 libxl__stream_write_state *stream)
332 {
333 libxl__xc_domain_save(egc, stream->dss, &stream->shs);
334 }
335
libxl__xc_domain_save_done(libxl__egc * egc,void * dss_void,int rc,int retval,int errnoval)336 void libxl__xc_domain_save_done(libxl__egc *egc, void *dss_void,
337 int rc, int retval, int errnoval)
338 {
339 libxl__domain_save_state *dss = dss_void;
340 libxl__stream_write_state *stream = &dss->sws;
341 STATE_AO_GC(dss->ao);
342
343 if (rc)
344 goto err;
345
346 if (retval) {
347 LOGEVD(ERROR, errnoval, dss->domid, "saving domain: %s",
348 dss->dsps.guest_responded ?
349 "domain responded to suspend request" :
350 "domain did not respond to suspend request");
351 if (!dss->dsps.guest_responded)
352 rc = ERROR_GUEST_TIMEDOUT;
353 else if (dss->rc)
354 rc = dss->rc;
355 else
356 rc = ERROR_FAIL;
357 goto err;
358 }
359
360 err:
361 check_all_finished(egc, stream, rc);
362
363 /*
364 * This function is the callback associated with the save helper
365 * task, not the stream task. We do not know whether the stream is
366 * alive, and check_all_finished() may have torn it down around us.
367 * If the stream is not still alive, we must not continue any work.
368 */
369 if (libxl__stream_write_inuse(stream)) {
370 if (dss->checkpointed_stream != LIBXL_CHECKPOINTED_STREAM_NONE)
371 /*
372 * For remus, if libxl__xc_domain_save_done() completes,
373 * there was an error sending data to the secondary.
374 * Resume the primary ASAP. The caller doesn't care of the
375 * return value (Please refer to libxl__remus_teardown())
376 */
377 stream_complete(egc, stream, 0);
378 else
379 write_emulator_xenstore_record(egc, stream);
380 }
381 }
382
write_emulator_xenstore_record(libxl__egc * egc,libxl__stream_write_state * stream)383 static void write_emulator_xenstore_record(libxl__egc *egc,
384 libxl__stream_write_state *stream)
385 {
386 libxl__domain_save_state *dss = stream->dss;
387 STATE_AO_GC(stream->ao);
388 struct libxl__sr_rec_hdr rec;
389 int rc;
390 char *buf = NULL;
391 uint32_t len = 0;
392
393 if (dss->type != LIBXL_DOMAIN_TYPE_HVM) {
394 emulator_xenstore_record_done(egc, stream);
395 return;
396 }
397
398 rc = libxl__save_emulator_xenstore_data(dss, &buf, &len);
399 if (rc)
400 goto err;
401
402 /* No record? - All done. */
403 if (len == 0) {
404 emulator_xenstore_record_done(egc, stream);
405 return;
406 }
407
408 FILLZERO(rec);
409 rec.type = REC_TYPE_EMULATOR_XENSTORE_DATA;
410 rec.length = len + sizeof(stream->emu_sub_hdr);
411
412 setup_emulator_write(egc, stream, "emulator xenstore record",
413 &rec, &stream->emu_sub_hdr, buf,
414 emulator_xenstore_record_done);
415 return;
416
417 err:
418 assert(rc);
419 stream_complete(egc, stream, rc);
420 }
421
emulator_xenstore_record_done(libxl__egc * egc,libxl__stream_write_state * stream)422 static void emulator_xenstore_record_done(libxl__egc *egc,
423 libxl__stream_write_state *stream)
424 {
425 libxl__domain_save_state *dss = stream->dss;
426
427 if (dss->type == LIBXL_DOMAIN_TYPE_HVM)
428 write_emulator_context_record(egc, stream);
429 else {
430 if (stream->in_checkpoint)
431 write_checkpoint_end_record(egc, stream);
432 else
433 write_end_record(egc, stream);
434 }
435 }
436
write_emulator_context_record(libxl__egc * egc,libxl__stream_write_state * stream)437 static void write_emulator_context_record(libxl__egc *egc,
438 libxl__stream_write_state *stream)
439 {
440 libxl__domain_save_state *dss = stream->dss;
441 libxl__datacopier_state *dc = &stream->emu_dc;
442 STATE_AO_GC(stream->ao);
443 struct libxl__sr_rec_hdr *rec = &stream->emu_rec_hdr;
444 struct stat st;
445 int rc;
446
447 if (dss->type != LIBXL_DOMAIN_TYPE_HVM) {
448 emulator_context_record_done(egc, stream);
449 return;
450 }
451
452 /* Convenience aliases */
453 const char *const filename = dss->dsps.dm_savefile;
454
455 libxl__carefd_begin();
456 int readfd = open(filename, O_RDONLY);
457 stream->emu_carefd = libxl__carefd_opened(CTX, readfd);
458 if (readfd == -1) {
459 rc = ERROR_FAIL;
460 LOGED(ERROR, dss->domid, "unable to open %s", filename);
461 goto err;
462 }
463
464 if (fstat(readfd, &st)) {
465 rc = ERROR_FAIL;
466 LOGED(ERROR, dss->domid, "unable to fstat %s", filename);
467 goto err;
468 }
469
470 if (!S_ISREG(st.st_mode)) {
471 rc = ERROR_FAIL;
472 LOGD(ERROR, dss->domid, "%s is not a plain file!", filename);
473 goto err;
474 }
475
476 rec->type = REC_TYPE_EMULATOR_CONTEXT;
477 rec->length = st.st_size + sizeof(stream->emu_sub_hdr);
478 stream->emu_body = libxl__malloc(NOGC, st.st_size);
479
480 FILLZERO(*dc);
481 dc->ao = stream->ao;
482 dc->readwhat = "qemu save file";
483 dc->copywhat = "save v2 stream";
484 dc->readfd = readfd;
485 dc->writefd = -1;
486 dc->maxsz = -1;
487 dc->readbuf = stream->emu_body;
488 dc->bytes_to_read = st.st_size;
489 dc->callback = emulator_context_read_done;
490
491 rc = libxl__datacopier_start(dc);
492 if (rc)
493 goto err;
494
495 return;
496
497 err:
498 assert(rc);
499 stream_complete(egc, stream, rc);
500 }
501
emulator_context_read_done(libxl__egc * egc,libxl__datacopier_state * dc,int rc,int onwrite,int errnoval)502 static void emulator_context_read_done(libxl__egc *egc,
503 libxl__datacopier_state *dc,
504 int rc, int onwrite, int errnoval)
505 {
506 libxl__stream_write_state *stream = CONTAINER_OF(dc, *stream, emu_dc);
507 STATE_AO_GC(stream->ao);
508
509 if (rc || onwrite || errnoval) {
510 stream_complete(egc, stream, rc ?: ERROR_FAIL);
511 return;
512 }
513
514 libxl__carefd_close(stream->emu_carefd);
515 stream->emu_carefd = NULL;
516
517 setup_emulator_write(egc, stream, "emulator record",
518 &stream->emu_rec_hdr,
519 &stream->emu_sub_hdr,
520 stream->emu_body,
521 emulator_context_record_done);
522 }
523
emulator_context_record_done(libxl__egc * egc,libxl__stream_write_state * stream)524 static void emulator_context_record_done(libxl__egc *egc,
525 libxl__stream_write_state *stream)
526 {
527 free(stream->emu_body);
528 stream->emu_body = NULL;
529
530 if (stream->in_checkpoint)
531 write_checkpoint_end_record(egc, stream);
532 else
533 write_end_record(egc, stream);
534 }
535
write_end_record(libxl__egc * egc,libxl__stream_write_state * stream)536 static void write_end_record(libxl__egc *egc,
537 libxl__stream_write_state *stream)
538 {
539 struct libxl__sr_rec_hdr rec;
540
541 FILLZERO(rec);
542 rec.type = REC_TYPE_END;
543
544 setup_write(egc, stream, "end record",
545 &rec, NULL, stream_success);
546 }
547
write_checkpoint_end_record(libxl__egc * egc,libxl__stream_write_state * stream)548 static void write_checkpoint_end_record(libxl__egc *egc,
549 libxl__stream_write_state *stream)
550 {
551 struct libxl__sr_rec_hdr rec;
552
553 FILLZERO(rec);
554 rec.type = REC_TYPE_CHECKPOINT_END;
555
556 setup_write(egc, stream, "checkpoint end record",
557 &rec, NULL, checkpoint_end_record_done);
558 }
559
checkpoint_end_record_done(libxl__egc * egc,libxl__stream_write_state * stream)560 static void checkpoint_end_record_done(libxl__egc *egc,
561 libxl__stream_write_state *stream)
562 {
563 checkpoint_done(egc, stream, 0);
564 }
565
566 /*----- Success/error/cleanup handling. -----*/
567
stream_success(libxl__egc * egc,libxl__stream_write_state * stream)568 static void stream_success(libxl__egc *egc, libxl__stream_write_state *stream)
569 {
570 stream_complete(egc, stream, 0);
571 }
572
stream_complete(libxl__egc * egc,libxl__stream_write_state * stream,int rc)573 static void stream_complete(libxl__egc *egc,
574 libxl__stream_write_state *stream, int rc)
575 {
576 assert(stream->running);
577
578 if (stream->in_checkpoint) {
579 assert(rc);
580
581 /*
582 * If an error is encountered while in a checkpoint, pass it
583 * back to libxc. The failure will come back around to us via
584 * libxl__xc_domain_save_done()
585 */
586 checkpoint_done(egc, stream, rc);
587 return;
588 }
589
590 if (stream->in_checkpoint_state) {
591 assert(rc);
592
593 /*
594 * If an error is encountered while in a checkpoint, pass it
595 * back to libxc. The failure will come back around to us via
596 * 1. normal stream
597 * libxl__xc_domain_save_done()
598 * 2. back_channel stream
599 * libxl__stream_write_abort()
600 */
601 checkpoint_state_done(egc, stream, rc);
602 return;
603 }
604
605 stream_done(egc, stream, rc);
606 }
607
stream_done(libxl__egc * egc,libxl__stream_write_state * stream,int rc)608 static void stream_done(libxl__egc *egc,
609 libxl__stream_write_state *stream, int rc)
610 {
611 assert(stream->running);
612 assert(!stream->in_checkpoint_state);
613 stream->running = false;
614
615 if (stream->emu_carefd)
616 libxl__carefd_close(stream->emu_carefd);
617 free(stream->emu_body);
618
619 if (!stream->back_channel) {
620 /*
621 * 1. In stream_done(), stream->running is set to false, so
622 * the stream itself is not in use.
623 * 2. Write stream is a back channel stream, this means it
624 * is only used by secondary(restore side) to send records
625 * back, so it doesn't have save helper.
626 * So we don't need invoke check_all_finished here
627 */
628 check_all_finished(egc, stream, rc);
629 }
630 }
631
checkpoint_done(libxl__egc * egc,libxl__stream_write_state * stream,int rc)632 static void checkpoint_done(libxl__egc *egc,
633 libxl__stream_write_state *stream,
634 int rc)
635 {
636 assert(stream->in_checkpoint);
637
638 stream->in_checkpoint = false;
639 stream->checkpoint_callback(egc, stream, rc);
640 }
641
check_all_finished(libxl__egc * egc,libxl__stream_write_state * stream,int rc)642 static void check_all_finished(libxl__egc *egc,
643 libxl__stream_write_state *stream,
644 int rc)
645 {
646 STATE_AO_GC(stream->ao);
647
648 /*
649 * In the case of a failure, the _abort()'s below might cancel
650 * synchronously on top of us, or asynchronously at a later point.
651 *
652 * We must avoid the situation where all _abort() cancel
653 * synchronously and the completion_callback() gets called twice;
654 * once by the first error and once by the final stacked abort(),
655 * both of whom will find that all of the tasks have stopped.
656 *
657 * To avoid this problem, any stacked re-entry into this function is
658 * ineligible to fire the completion callback. The outermost
659 * instance will take care of completing, once the stack has
660 * unwound.
661 */
662 if (stream->sync_teardown)
663 return;
664
665 if (!stream->rc && rc) {
666 /* First reported failure. Tear everything down. */
667 stream->rc = rc;
668 stream->sync_teardown = true;
669
670 libxl__stream_write_abort(egc, stream, rc);
671 libxl__save_helper_abort(egc, &stream->shs);
672
673 stream->sync_teardown = false;
674 }
675
676 /* Don't fire the callback until all our parallel tasks have stopped. */
677 if (libxl__stream_write_inuse(stream) ||
678 libxl__save_helper_inuse(&stream->shs))
679 return;
680
681 if (stream->completion_callback)
682 /* back channel stream doesn't have completion_callback() */
683 stream->completion_callback(egc, stream, stream->rc);
684 }
685
686 /*----- checkpoint state -----*/
687
libxl__stream_write_checkpoint_state(libxl__egc * egc,libxl__stream_write_state * stream,libxl_sr_checkpoint_state * srcs)688 void libxl__stream_write_checkpoint_state(libxl__egc *egc,
689 libxl__stream_write_state *stream,
690 libxl_sr_checkpoint_state *srcs)
691 {
692 struct libxl__sr_rec_hdr rec;
693
694 assert(stream->running);
695 assert(!stream->in_checkpoint);
696 assert(!stream->in_checkpoint_state);
697 stream->in_checkpoint_state = true;
698
699 FILLZERO(rec);
700 rec.type = REC_TYPE_CHECKPOINT_STATE;
701 rec.length = sizeof(*srcs);
702
703 setup_write(egc, stream, "checkpoint state", &rec,
704 srcs, write_checkpoint_state_done);
705 }
706
write_checkpoint_state_done(libxl__egc * egc,libxl__stream_write_state * stream)707 static void write_checkpoint_state_done(libxl__egc *egc,
708 libxl__stream_write_state *stream)
709 {
710 checkpoint_state_done(egc, stream, 0);
711 }
712
checkpoint_state_done(libxl__egc * egc,libxl__stream_write_state * stream,int rc)713 static void checkpoint_state_done(libxl__egc *egc,
714 libxl__stream_write_state *stream, int rc)
715 {
716 assert(stream->in_checkpoint_state);
717 stream->in_checkpoint_state = false;
718 stream->checkpoint_callback(egc, stream, rc);
719 }
720
721 /*
722 * Local variables:
723 * mode: C
724 * c-basic-offset: 4
725 * indent-tabs-mode: nil
726 * End:
727 */
728