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