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