1 /*
2  * Copyright (C) 2016 FUJITSU LIMITED
3  * Author: Wen Congyang <wency@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 enum {
21     primary,
22     secondary,
23 };
24 
25 /* ========== init() and cleanup() ========== */
26 
init_subkind_colo_nic(libxl__checkpoint_devices_state * cds)27 int init_subkind_colo_nic(libxl__checkpoint_devices_state *cds)
28 {
29     return 0;
30 }
31 
cleanup_subkind_colo_nic(libxl__checkpoint_devices_state * cds)32 void cleanup_subkind_colo_nic(libxl__checkpoint_devices_state *cds)
33 {
34 }
35 
36 /* ========== helper functions ========== */
37 
38 static void colo_save_setup_script_cb(libxl__egc *egc,
39                                      libxl__async_exec_state *aes,
40                                      int rc, int status);
41 static void colo_save_teardown_script_cb(libxl__egc *egc,
42                                          libxl__async_exec_state *aes,
43                                          int rc, int status);
44 
45 /*
46  * If the device has a vifname, then use that instead of
47  * the vifX.Y format.
48  * it must ONLY be used for remus because if driver domains
49  * were in use it would constitute a security vulnerability.
50  */
get_vifname(libxl__checkpoint_device * dev,const libxl_device_nic * nic)51 static const char *get_vifname(libxl__checkpoint_device *dev,
52                                const libxl_device_nic *nic)
53 {
54     const char *vifname = NULL;
55     const char *path;
56     int rc;
57 
58     STATE_AO_GC(dev->cds->ao);
59 
60     /* Convenience aliases */
61     const uint32_t domid = dev->cds->domid;
62 
63     path = GCSPRINTF("%s/vifname",
64                      libxl__domain_device_backend_path(gc, 0,
65                      domid, nic->devid, LIBXL__DEVICE_KIND_VIF));
66     rc = libxl__xs_read_checked(gc, XBT_NULL, path, &vifname);
67     if (!rc && !vifname) {
68         vifname = libxl__device_nic_devname(gc, domid,
69                                             nic->devid,
70                                             nic->nictype);
71     }
72 
73     return vifname;
74 }
75 
76 /*
77  * the script needs the following env & args
78  * $vifname
79  * $forwarddev
80  * $mode(primary/secondary)
81  * $index
82  * $bridge
83  * setup/teardown as command line arg.
84  */
setup_async_exec(libxl__checkpoint_device * dev,char * op,libxl__colo_proxy_state * cps,int side,char * colo_proxy_script)85 static void setup_async_exec(libxl__checkpoint_device *dev, char *op,
86                              libxl__colo_proxy_state *cps, int side,
87                              char *colo_proxy_script)
88 {
89     int arraysize, nr = 0;
90     char **env = NULL, **args = NULL;
91     libxl__colo_device_nic *colo_nic = dev->concrete_data;
92     libxl__checkpoint_devices_state *cds = dev->cds;
93     libxl__async_exec_state *aes = &dev->aodev.aes;
94     const libxl_device_nic *nic = dev->backend_dev;
95 
96     STATE_AO_GC(cds->ao);
97 
98     /* Convenience aliases */
99     const char *const vif = colo_nic->vif;
100 
101     arraysize = 11;
102     GCNEW_ARRAY(env, arraysize);
103     env[nr++] = "vifname";
104     env[nr++] = libxl__strdup(gc, vif);
105     env[nr++] = "forwarddev";
106     env[nr++] = libxl__strdup(gc, nic->coloft_forwarddev);
107     env[nr++] = "mode";
108     if (side == primary)
109         env[nr++] = "primary";
110     else
111         env[nr++] = "secondary";
112     env[nr++] = "index";
113     env[nr++] = GCSPRINTF("%d", cps->index);
114     env[nr++] = "bridge";
115     env[nr++] = libxl__strdup(gc, nic->bridge);
116     env[nr++] = NULL;
117     assert(nr == arraysize);
118 
119     arraysize = 3; nr = 0;
120     GCNEW_ARRAY(args, arraysize);
121     args[nr++] = colo_proxy_script;
122     args[nr++] = op;
123     args[nr++] = NULL;
124     assert(nr == arraysize);
125 
126     aes->ao = dev->cds->ao;
127     aes->what = GCSPRINTF("%s %s", args[0], args[1]);
128     aes->env = env;
129     aes->args = args;
130     aes->timeout_ms = LIBXL_HOTPLUG_TIMEOUT * 1000;
131     aes->stdfds[0] = -1;
132     aes->stdfds[1] = -1;
133     aes->stdfds[2] = -1;
134 
135     if (!strcmp(op, "teardown"))
136         aes->callback = colo_save_teardown_script_cb;
137     else
138         aes->callback = colo_save_setup_script_cb;
139 }
140 
141 /* ========== setup() and teardown() ========== */
142 
colo_nic_setup(libxl__egc * egc,libxl__checkpoint_device * dev,libxl__colo_proxy_state * cps,int side,char * colo_proxy_script)143 static void colo_nic_setup(libxl__egc *egc, libxl__checkpoint_device *dev,
144                            libxl__colo_proxy_state *cps, int side,
145                            char *colo_proxy_script)
146 {
147     int rc;
148     libxl__colo_device_nic *colo_nic;
149     const libxl_device_nic *nic = dev->backend_dev;
150 
151     STATE_AO_GC(dev->cds->ao);
152 
153     /*
154      * thers's no subkind of nic devices, so nic ops is always matched
155      * with nic devices, we begin to setup the nic device
156      */
157     dev->matched = 1;
158 
159     if (!nic->coloft_forwarddev) {
160         rc = ERROR_FAIL;
161         goto out;
162     }
163 
164     GCNEW(colo_nic);
165     dev->concrete_data = colo_nic;
166     colo_nic->devid = nic->devid;
167     colo_nic->vif = get_vifname(dev, nic);
168     if (!colo_nic->vif) {
169         rc = ERROR_FAIL;
170         goto out;
171     }
172 
173     setup_async_exec(dev, "setup", cps, side, colo_proxy_script);
174     rc = libxl__async_exec_start(&dev->aodev.aes);
175     if (rc)
176         goto out;
177 
178     return;
179 
180 out:
181     dev->aodev.rc = rc;
182     dev->aodev.callback(egc, &dev->aodev);
183 }
184 
colo_save_setup_script_cb(libxl__egc * egc,libxl__async_exec_state * aes,int rc,int status)185 static void colo_save_setup_script_cb(libxl__egc *egc,
186                                       libxl__async_exec_state *aes,
187                                       int rc, int status)
188 {
189     libxl__ao_device *aodev = CONTAINER_OF(aes, *aodev, aes);
190     libxl__checkpoint_device *dev = CONTAINER_OF(aodev, *dev, aodev);
191     libxl__colo_device_nic *colo_nic = dev->concrete_data;
192     libxl__checkpoint_devices_state *cds = dev->cds;
193     const char *out_path_base, *hotplug_error = NULL;
194 
195     EGC_GC;
196 
197     /* Convenience aliases */
198     const uint32_t domid = cds->domid;
199     const int devid = colo_nic->devid;
200     const char *const vif = colo_nic->vif;
201 
202     if (status && !rc)
203         rc = ERROR_FAIL;
204     if (rc)
205         goto out;
206 
207     out_path_base = GCSPRINTF("%s/colo_proxy/%d",
208                               libxl__xs_libxl_path(gc, domid), devid);
209 
210     rc = libxl__xs_read_checked(gc, XBT_NULL,
211                                 GCSPRINTF("%s/hotplug-error", out_path_base),
212                                 &hotplug_error);
213     if (rc)
214         goto out;
215 
216     if (hotplug_error) {
217         LOGD(ERROR, domid, "colo_proxy script %s setup failed for vif %s: %s",
218             aes->args[0], vif, hotplug_error);
219         rc = ERROR_FAIL;
220         goto out;
221     }
222 
223     rc = 0;
224 
225 out:
226     aodev->rc = rc;
227     aodev->callback(egc, aodev);
228 }
229 
colo_nic_teardown(libxl__egc * egc,libxl__checkpoint_device * dev,libxl__colo_proxy_state * cps,int side,char * colo_proxy_script)230 static void colo_nic_teardown(libxl__egc *egc, libxl__checkpoint_device *dev,
231                               libxl__colo_proxy_state *cps, int side,
232                               char *colo_proxy_script)
233 {
234     int rc;
235     libxl__colo_device_nic *colo_nic = dev->concrete_data;
236 
237     if (!colo_nic || !colo_nic->vif) {
238         /* colo nic has not yet been set up, just return */
239         rc = 0;
240         goto out;
241     }
242 
243     setup_async_exec(dev, "teardown", cps, side, colo_proxy_script);
244 
245     rc = libxl__async_exec_start(&dev->aodev.aes);
246     if (rc)
247         goto out;
248 
249     return;
250 
251 out:
252     dev->aodev.rc = rc;
253     dev->aodev.callback(egc, &dev->aodev);
254 }
255 
colo_save_teardown_script_cb(libxl__egc * egc,libxl__async_exec_state * aes,int rc,int status)256 static void colo_save_teardown_script_cb(libxl__egc *egc,
257                                          libxl__async_exec_state *aes,
258                                          int rc, int status)
259 {
260     libxl__ao_device *aodev = CONTAINER_OF(aes, *aodev, aes);
261 
262     if (status && !rc)
263         rc = ERROR_FAIL;
264     else
265         rc = 0;
266 
267     aodev->rc = rc;
268     aodev->callback(egc, aodev);
269 }
270 
271 /* ======== primary ======== */
272 
colo_nic_save_setup(libxl__egc * egc,libxl__checkpoint_device * dev)273 static void colo_nic_save_setup(libxl__egc *egc, libxl__checkpoint_device *dev)
274 {
275     libxl__colo_save_state *css = dev->cds->concrete_data;
276 
277     colo_nic_setup(egc, dev, &css->cps, primary, css->colo_proxy_script);
278 }
279 
colo_nic_save_teardown(libxl__egc * egc,libxl__checkpoint_device * dev)280 static void colo_nic_save_teardown(libxl__egc *egc,
281                                    libxl__checkpoint_device *dev)
282 {
283     libxl__colo_save_state *css = dev->cds->concrete_data;
284 
285     colo_nic_teardown(egc, dev, &css->cps, primary, css->colo_proxy_script);
286 }
287 
288 const libxl__checkpoint_device_instance_ops colo_save_device_nic = {
289     .kind = LIBXL__DEVICE_KIND_VIF,
290     .setup = colo_nic_save_setup,
291     .teardown = colo_nic_save_teardown,
292 };
293 
294 /* ======== secondary ======== */
295 
colo_nic_restore_setup(libxl__egc * egc,libxl__checkpoint_device * dev)296 static void colo_nic_restore_setup(libxl__egc *egc,
297                                    libxl__checkpoint_device *dev)
298 {
299     libxl__colo_restore_state *crs = dev->cds->concrete_data;
300 
301     colo_nic_setup(egc, dev, &crs->cps, secondary, crs->colo_proxy_script);
302 }
303 
colo_nic_restore_teardown(libxl__egc * egc,libxl__checkpoint_device * dev)304 static void colo_nic_restore_teardown(libxl__egc *egc,
305                                       libxl__checkpoint_device *dev)
306 {
307     libxl__colo_restore_state *crs = dev->cds->concrete_data;
308 
309     colo_nic_teardown(egc, dev, &crs->cps, secondary, crs->colo_proxy_script);
310 }
311 
312 const libxl__checkpoint_device_instance_ops colo_restore_device_nic = {
313     .kind = LIBXL__DEVICE_KIND_VIF,
314     .setup = colo_nic_restore_setup,
315     .teardown = colo_nic_restore_teardown,
316 };
317