1 /*
2  * Copyright (C) 2014 FUJITSU LIMITED
3  * Author: Yang Hongyang <yanghy@cn.fujitsu.com>
4  *
5  * This program is free software; you can redistribute it and/or modify
6  * it under the terms of the GNU Lesser General Public License as published
7  * by the Free Software Foundation; version 2.1 only. with the special
8  * exception on linking described in file LICENSE.
9  *
10  * This program is distributed in the hope that it will be useful,
11  * but WITHOUT ANY WARRANTY; without even the implied warranty of
12  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
13  * GNU Lesser General Public License for more details.
14  */
15 
16 #include "libxl_osdeps.h" /* must come before any other headers */
17 
18 #include "libxl_internal.h"
19 
20 /*----- setup() and teardown() -----*/
21 
22 /* callbacks */
23 
24 static void all_devices_setup_cb(libxl__egc *egc,
25                                  libxl__multidev *multidev,
26                                  int rc);
27 static void device_setup_iterate(libxl__egc *egc,
28                                  libxl__ao_device *aodev);
29 static void devices_teardown_cb(libxl__egc *egc,
30                                 libxl__multidev *multidev,
31                                 int rc);
32 
33 /* checkpoint device setup and teardown */
34 
checkpoint_device_init(libxl__egc * egc,libxl__checkpoint_devices_state * cds,libxl__device_kind kind,void * libxl_dev)35 static libxl__checkpoint_device* checkpoint_device_init(libxl__egc *egc,
36                                         libxl__checkpoint_devices_state *cds,
37                                         libxl__device_kind kind,
38                                         void *libxl_dev)
39 {
40     libxl__checkpoint_device *dev = NULL;
41 
42     STATE_AO_GC(cds->ao);
43     GCNEW(dev);
44     dev->backend_dev = libxl_dev;
45     dev->kind = kind;
46     dev->cds = cds;
47 
48     return dev;
49 }
50 
51 static void checkpoint_devices_setup(libxl__egc *egc,
52                                      libxl__checkpoint_devices_state *cds);
53 
libxl__checkpoint_devices_setup(libxl__egc * egc,libxl__checkpoint_devices_state * cds)54 void libxl__checkpoint_devices_setup(libxl__egc *egc,
55                                      libxl__checkpoint_devices_state *cds)
56 {
57     int i;
58 
59     STATE_AO_GC(cds->ao);
60 
61     cds->num_devices = 0;
62     cds->num_nics = 0;
63     cds->num_disks = 0;
64 
65     if (cds->device_kind_flags & (1 << LIBXL__DEVICE_KIND_VIF))
66         cds->nics = libxl__device_list(gc, &libxl__nic_devtype, cds->domid,
67                                        &cds->num_nics);
68 
69     if (cds->device_kind_flags & (1 << LIBXL__DEVICE_KIND_VBD))
70         cds->disks = libxl__device_list(gc, &libxl__disk_devtype, cds->domid,
71                                         &cds->num_disks);
72 
73     if (cds->num_nics == 0 && cds->num_disks == 0)
74         goto out;
75 
76     GCNEW_ARRAY(cds->devs, cds->num_nics + cds->num_disks);
77 
78     for (i = 0; i < cds->num_nics; i++) {
79         cds->devs[cds->num_devices++] = checkpoint_device_init(egc, cds,
80                                                 LIBXL__DEVICE_KIND_VIF,
81                                                 &cds->nics[i]);
82     }
83 
84     for (i = 0; i < cds->num_disks; i++) {
85         cds->devs[cds->num_devices++] = checkpoint_device_init(egc, cds,
86                                                 LIBXL__DEVICE_KIND_VBD,
87                                                 &cds->disks[i]);
88     }
89 
90     checkpoint_devices_setup(egc, cds);
91 
92     return;
93 
94 out:
95     cds->callback(egc, cds, 0);
96 }
97 
checkpoint_devices_setup(libxl__egc * egc,libxl__checkpoint_devices_state * cds)98 static void checkpoint_devices_setup(libxl__egc *egc,
99                                      libxl__checkpoint_devices_state *cds)
100 {
101     int i, rc;
102 
103     STATE_AO_GC(cds->ao);
104 
105     libxl__multidev_begin(ao, &cds->multidev);
106     cds->multidev.callback = all_devices_setup_cb;
107     for (i = 0; i < cds->num_devices; i++) {
108         libxl__checkpoint_device *dev = cds->devs[i];
109         dev->ops_index = -1;
110         libxl__multidev_prepare_with_aodev(&cds->multidev, &dev->aodev);
111 
112         dev->aodev.rc = ERROR_CHECKPOINT_DEVICE_NOT_SUPPORTED;
113         dev->aodev.callback = device_setup_iterate;
114         device_setup_iterate(egc,&dev->aodev);
115     }
116 
117     rc = 0;
118     libxl__multidev_prepared(egc, &cds->multidev, rc);
119 }
120 
121 
device_setup_iterate(libxl__egc * egc,libxl__ao_device * aodev)122 static void device_setup_iterate(libxl__egc *egc, libxl__ao_device *aodev)
123 {
124     libxl__checkpoint_device *dev = CONTAINER_OF(aodev, *dev, aodev);
125     EGC_GC;
126 
127     if (aodev->rc != ERROR_CHECKPOINT_DEVICE_NOT_SUPPORTED &&
128         aodev->rc != ERROR_CHECKPOINT_DEVOPS_DOES_NOT_MATCH)
129         /* might be success or disaster */
130         goto out;
131 
132     do {
133         dev->ops = dev->cds->ops[++dev->ops_index];
134         if (!dev->ops) {
135             libxl_device_nic * nic = NULL;
136             libxl_device_disk * disk = NULL;
137             uint32_t domid = INVALID_DOMID;
138             int devid;
139             if (dev->kind == LIBXL__DEVICE_KIND_VIF) {
140                 nic = (libxl_device_nic *)dev->backend_dev;
141                 domid = nic->backend_domid;
142                 devid = nic->devid;
143             } else if (dev->kind == LIBXL__DEVICE_KIND_VBD) {
144                 disk = (libxl_device_disk *)dev->backend_dev;
145                 domid = disk->backend_domid;
146                 devid = libxl__device_disk_dev_number(disk->vdev, NULL, NULL);
147             } else {
148                 LOGD(ERROR, domid, "device kind not handled by checkpoint: %s",
149                      libxl__device_kind_to_string(dev->kind));
150                 aodev->rc = ERROR_FAIL;
151                 goto out;
152             }
153             LOGD(ERROR, domid, "device not handled by checkpoint"
154                  " (device=%s:%"PRId32"/%"PRId32")",
155                  libxl__device_kind_to_string(dev->kind),
156                  domid, devid);
157             aodev->rc = ERROR_CHECKPOINT_DEVICE_NOT_SUPPORTED;
158             goto out;
159         }
160     } while (dev->ops->kind != dev->kind);
161 
162     /* found the next ops_index to try */
163     assert(dev->aodev.callback == device_setup_iterate);
164     dev->ops->setup(egc,dev);
165     return;
166 
167  out:
168     libxl__multidev_one_callback(egc,aodev);
169 }
170 
all_devices_setup_cb(libxl__egc * egc,libxl__multidev * multidev,int rc)171 static void all_devices_setup_cb(libxl__egc *egc,
172                                  libxl__multidev *multidev,
173                                  int rc)
174 {
175     STATE_AO_GC(multidev->ao);
176 
177     /* Convenience aliases */
178     libxl__checkpoint_devices_state *const cds =
179                             CONTAINER_OF(multidev, *cds, multidev);
180 
181     cds->callback(egc, cds, rc);
182 }
183 
libxl__checkpoint_devices_teardown(libxl__egc * egc,libxl__checkpoint_devices_state * cds)184 void libxl__checkpoint_devices_teardown(libxl__egc *egc,
185                                    libxl__checkpoint_devices_state *cds)
186 {
187     int i;
188     libxl__checkpoint_device *dev;
189 
190     STATE_AO_GC(cds->ao);
191 
192     libxl__multidev_begin(ao, &cds->multidev);
193     cds->multidev.callback = devices_teardown_cb;
194     for (i = 0; i < cds->num_devices; i++) {
195         dev = cds->devs[i];
196         if (!dev->ops || !dev->matched)
197             continue;
198 
199         libxl__multidev_prepare_with_aodev(&cds->multidev, &dev->aodev);
200         dev->ops->teardown(egc,dev);
201     }
202 
203     libxl__multidev_prepared(egc, &cds->multidev, 0);
204 }
205 
devices_teardown_cb(libxl__egc * egc,libxl__multidev * multidev,int rc)206 static void devices_teardown_cb(libxl__egc *egc,
207                                 libxl__multidev *multidev,
208                                 int rc)
209 {
210     STATE_AO_GC(multidev->ao);
211 
212     /* Convenience aliases */
213     libxl__checkpoint_devices_state *const cds =
214                             CONTAINER_OF(multidev, *cds, multidev);
215 
216     /* clean nic */
217     libxl__device_list_free(&libxl__nic_devtype, cds->nics, cds->num_nics);
218     cds->nics = NULL;
219     cds->num_nics = 0;
220 
221     /* clean disk */
222     libxl__device_list_free(&libxl__disk_devtype, cds->disks, cds->num_disks);
223     cds->disks = NULL;
224     cds->num_disks = 0;
225 
226     cds->callback(egc, cds, rc);
227 }
228 
229 /*----- checkpointing APIs -----*/
230 
231 /* callbacks */
232 
233 static void devices_checkpoint_cb(libxl__egc *egc,
234                                   libxl__multidev *multidev,
235                                   int rc);
236 
237 /* API implementations */
238 
239 #define define_checkpoint_api(api)                                      \
240 void libxl__checkpoint_devices_##api(libxl__egc *egc,                   \
241                                 libxl__checkpoint_devices_state *cds)   \
242 {                                                                       \
243     int i;                                                              \
244     libxl__checkpoint_device *dev;                                      \
245                                                                         \
246     STATE_AO_GC(cds->ao);                                               \
247                                                                         \
248     libxl__multidev_begin(ao, &cds->multidev);                          \
249     cds->multidev.callback = devices_checkpoint_cb;                     \
250     for (i = 0; i < cds->num_devices; i++) {                            \
251         dev = cds->devs[i];                                             \
252         if (!dev->matched || !dev->ops->api)                            \
253             continue;                                                   \
254         libxl__multidev_prepare_with_aodev(&cds->multidev, &dev->aodev);\
255         dev->ops->api(egc,dev);                                         \
256     }                                                                   \
257                                                                         \
258     libxl__multidev_prepared(egc, &cds->multidev, 0);                   \
259 }
260 
261 define_checkpoint_api(postsuspend);
262 
263 define_checkpoint_api(preresume);
264 
265 define_checkpoint_api(commit);
266 
devices_checkpoint_cb(libxl__egc * egc,libxl__multidev * multidev,int rc)267 static void devices_checkpoint_cb(libxl__egc *egc,
268                                   libxl__multidev *multidev,
269                                   int rc)
270 {
271     STATE_AO_GC(multidev->ao);
272 
273     /* Convenience aliases */
274     libxl__checkpoint_devices_state *const cds =
275                             CONTAINER_OF(multidev, *cds, multidev);
276 
277     cds->callback(egc, cds, rc);
278 }
279