1 /*
2 * Copyright 2009-2017 Citrix Ltd and other contributors
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"
16
17 #include "libxl_internal.h"
18
19 #define BACKEND_STRING_SIZE 5
20
disk_eject_xswatch_callback(libxl__egc * egc,libxl__ev_xswatch * w,const char * wpath,const char * epath)21 static void disk_eject_xswatch_callback(libxl__egc *egc, libxl__ev_xswatch *w,
22 const char *wpath, const char *epath) {
23 EGC_GC;
24 libxl_evgen_disk_eject *evg = (void*)w;
25 const char *backend;
26 char *value;
27 char backend_type[BACKEND_STRING_SIZE+1];
28 int rc;
29
30 value = libxl__xs_read(gc, XBT_NULL, wpath);
31
32 if (!value || strcmp(value, "eject"))
33 return;
34
35 if (libxl__xs_printf(gc, XBT_NULL, wpath, "")) {
36 LIBXL__EVENT_DISASTER(gc, "xs_write failed acknowledging eject",
37 errno, LIBXL_EVENT_TYPE_DISK_EJECT);
38 return;
39 }
40
41 libxl_event *ev = NEW_EVENT(egc, DISK_EJECT, evg->domid, evg->user);
42 libxl_device_disk *disk = &ev->u.disk_eject.disk;
43
44 rc = libxl__xs_read_checked(gc, XBT_NULL, evg->be_ptr_path, &backend);
45 if (rc) {
46 LIBXL__EVENT_DISASTER(gc, "xs_read failed reading be_ptr_path",
47 errno, LIBXL_EVENT_TYPE_DISK_EJECT);
48 return;
49 }
50 if (!backend) {
51 /* device has been removed, not simply ejected */
52 return;
53 }
54
55 sscanf(backend,
56 "/local/domain/%d/backend/%" TOSTRING(BACKEND_STRING_SIZE)
57 "[a-z]/%*d/%*d",
58 &disk->backend_domid, backend_type);
59 if (!strcmp(backend_type, "tap") || !strcmp(backend_type, "vbd")) {
60 disk->backend = LIBXL_DISK_BACKEND_TAP;
61 } else if (!strcmp(backend_type, "qdisk")) {
62 disk->backend = LIBXL_DISK_BACKEND_QDISK;
63 } else {
64 disk->backend = LIBXL_DISK_BACKEND_UNKNOWN;
65 }
66
67 disk->pdev_path = strdup(""); /* xxx fixme malloc failure */
68 disk->format = LIBXL_DISK_FORMAT_EMPTY;
69 /* this value is returned to the user: do not free right away */
70 disk->vdev = libxl__strdup(NOGC, evg->vdev);
71 disk->removable = 1;
72 disk->readwrite = 0;
73 disk->is_cdrom = 1;
74
75 libxl__event_occurred(egc, ev);
76 }
77
libxl_evenable_disk_eject(libxl_ctx * ctx,uint32_t guest_domid,const char * vdev,libxl_ev_user user,libxl_evgen_disk_eject ** evgen_out)78 int libxl_evenable_disk_eject(libxl_ctx *ctx, uint32_t guest_domid,
79 const char *vdev, libxl_ev_user user,
80 libxl_evgen_disk_eject **evgen_out) {
81 GC_INIT(ctx);
82 CTX_LOCK;
83 int rc;
84 char *path;
85 libxl_evgen_disk_eject *evg = NULL;
86
87 evg = malloc(sizeof(*evg)); if (!evg) { rc = ERROR_NOMEM; goto out; }
88 memset(evg, 0, sizeof(*evg));
89 evg->user = user;
90 evg->domid = guest_domid;
91 LIBXL_LIST_INSERT_HEAD(&CTX->disk_eject_evgens, evg, entry);
92
93 uint32_t domid = libxl_get_stubdom_id(ctx, guest_domid);
94
95 if (!domid)
96 domid = guest_domid;
97
98 int devid = libxl__device_disk_dev_number(vdev, NULL, NULL);
99
100 path = GCSPRINTF("%s/eject",
101 libxl__domain_device_frontend_path(gc, domid, devid,
102 LIBXL__DEVICE_KIND_VBD));
103 if (!path) { rc = ERROR_NOMEM; goto out; }
104
105 const char *libxl_path = libxl__domain_device_frontend_path(gc, domid, devid,
106 LIBXL__DEVICE_KIND_VBD);
107 evg->be_ptr_path = libxl__sprintf(NOGC, "%s/backend", libxl_path);
108
109 const char *configured_vdev;
110 rc = libxl__xs_read_checked(gc, XBT_NULL,
111 GCSPRINTF("%s/dev", libxl_path), &configured_vdev);
112 if (rc) goto out;
113
114 evg->vdev = libxl__strdup(NOGC, configured_vdev);
115
116 rc = libxl__ev_xswatch_register(gc, &evg->watch,
117 disk_eject_xswatch_callback, path);
118 if (rc) goto out;
119
120 *evgen_out = evg;
121 CTX_UNLOCK;
122 GC_FREE;
123 return 0;
124
125 out:
126 if (evg)
127 libxl__evdisable_disk_eject(gc, evg);
128 CTX_UNLOCK;
129 GC_FREE;
130 return rc;
131 }
132
libxl__evdisable_disk_eject(libxl__gc * gc,libxl_evgen_disk_eject * evg)133 void libxl__evdisable_disk_eject(libxl__gc *gc, libxl_evgen_disk_eject *evg) {
134 CTX_LOCK;
135
136 LIBXL_LIST_REMOVE(evg, entry);
137
138 if (libxl__ev_xswatch_isregistered(&evg->watch))
139 libxl__ev_xswatch_deregister(gc, &evg->watch);
140
141 free(evg->vdev);
142 free(evg->be_ptr_path);
143 free(evg);
144
145 CTX_UNLOCK;
146 }
147
libxl_evdisable_disk_eject(libxl_ctx * ctx,libxl_evgen_disk_eject * evg)148 void libxl_evdisable_disk_eject(libxl_ctx *ctx, libxl_evgen_disk_eject *evg) {
149 GC_INIT(ctx);
150 libxl__evdisable_disk_eject(gc, evg);
151 GC_FREE;
152 }
153
libxl__device_disk_setdefault(libxl__gc * gc,uint32_t domid,libxl_device_disk * disk,bool hotplug)154 static int libxl__device_disk_setdefault(libxl__gc *gc, uint32_t domid,
155 libxl_device_disk *disk, bool hotplug)
156 {
157 int rc;
158
159 libxl_defbool_setdefault(&disk->discard_enable, !!disk->readwrite);
160 libxl_defbool_setdefault(&disk->colo_enable, false);
161 libxl_defbool_setdefault(&disk->colo_restore_enable, false);
162
163 rc = libxl__resolve_domid(gc, disk->backend_domname, &disk->backend_domid);
164 if (rc < 0) return rc;
165
166 /* Force Qdisk backend for CDROM devices of guests with a device model. */
167 if (disk->is_cdrom != 0 &&
168 libxl__domain_type(gc, domid) == LIBXL_DOMAIN_TYPE_HVM) {
169 if (!(disk->backend == LIBXL_DISK_BACKEND_QDISK ||
170 disk->backend == LIBXL_DISK_BACKEND_UNKNOWN)) {
171 LOGD(ERROR, domid, "Backend for CD devices on HVM guests must be Qdisk");
172 return ERROR_FAIL;
173 }
174 disk->backend = LIBXL_DISK_BACKEND_QDISK;
175 }
176
177 rc = libxl__device_disk_set_backend(gc, disk);
178 return rc;
179 }
180
libxl__device_from_disk(libxl__gc * gc,uint32_t domid,const libxl_device_disk * disk,libxl__device * device)181 static int libxl__device_from_disk(libxl__gc *gc, uint32_t domid,
182 const libxl_device_disk *disk,
183 libxl__device *device)
184 {
185 int devid;
186
187 devid = libxl__device_disk_dev_number(disk->vdev, NULL, NULL);
188 if (devid==-1) {
189 LOGD(ERROR, domid, "Invalid or unsupported"" virtual disk identifier %s",
190 disk->vdev);
191 return ERROR_INVAL;
192 }
193
194 device->backend_domid = disk->backend_domid;
195 device->backend_devid = devid;
196
197 switch (disk->backend) {
198 case LIBXL_DISK_BACKEND_PHY:
199 device->backend_kind = LIBXL__DEVICE_KIND_VBD;
200 break;
201 case LIBXL_DISK_BACKEND_TAP:
202 device->backend_kind = LIBXL__DEVICE_KIND_VBD;
203 break;
204 case LIBXL_DISK_BACKEND_QDISK:
205 device->backend_kind = LIBXL__DEVICE_KIND_QDISK;
206 break;
207 default:
208 LOGD(ERROR, domid, "Unrecognized disk backend type: %d",
209 disk->backend);
210 return ERROR_INVAL;
211 }
212
213 device->domid = domid;
214 device->devid = devid;
215 device->kind = LIBXL__DEVICE_KIND_VBD;
216
217 return 0;
218 }
219
220 /* Specific function called directly only by local disk attach,
221 * all other users should instead use the regular
222 * libxl__device_disk_add wrapper
223 *
224 * The (optionally) passed function get_vdev will be used to
225 * set the vdev the disk should be attached to. When it is set the caller
226 * must also pass get_vdev_user, which will be passed to get_vdev.
227 *
228 * The passed get_vdev function is also in charge of printing
229 * the corresponding error message when appropiate.
230 */
device_disk_add(libxl__egc * egc,uint32_t domid,libxl_device_disk * disk,libxl__ao_device * aodev,char * get_vdev (libxl__gc *,void *,xs_transaction_t),void * get_vdev_user)231 static void device_disk_add(libxl__egc *egc, uint32_t domid,
232 libxl_device_disk *disk,
233 libxl__ao_device *aodev,
234 char *get_vdev(libxl__gc *, void *,
235 xs_transaction_t),
236 void *get_vdev_user)
237 {
238 STATE_AO_GC(aodev->ao);
239 flexarray_t *front = NULL;
240 flexarray_t *back = NULL;
241 char *dev = NULL, *script;
242 libxl__device *device;
243 int rc;
244 libxl_ctx *ctx = gc->owner;
245 xs_transaction_t t = XBT_NULL;
246 libxl_domain_config d_config;
247 libxl_device_disk disk_saved;
248 libxl__flock *lock = NULL;
249
250 libxl_domain_config_init(&d_config);
251 libxl_device_disk_init(&disk_saved);
252 libxl_device_disk_copy(ctx, &disk_saved, disk);
253
254 libxl_domain_type type = libxl__domain_type(gc, domid);
255 if (type == LIBXL_DOMAIN_TYPE_INVALID) {
256 rc = ERROR_FAIL;
257 goto out;
258 }
259
260 /*
261 * get_vdev != NULL -> local attach
262 * get_vdev == NULL -> block attach
263 *
264 * We don't care about local attach state because it's only
265 * intermediate state.
266 */
267 if (!get_vdev && aodev->update_json) {
268 lock = libxl__lock_domain_userdata(gc, domid);
269 if (!lock) {
270 rc = ERROR_LOCK_FAIL;
271 goto out;
272 }
273
274 rc = libxl__get_domain_configuration(gc, domid, &d_config);
275 if (rc) goto out;
276
277 device_add_domain_config(gc, &d_config, &libxl__disk_devtype,
278 &disk_saved);
279
280 rc = libxl__dm_check_start(gc, &d_config, domid);
281 if (rc) goto out;
282 }
283
284 for (;;) {
285 rc = libxl__xs_transaction_start(gc, &t);
286 if (rc) goto out;
287
288 if (get_vdev) {
289 assert(get_vdev_user);
290 disk->vdev = get_vdev(gc, get_vdev_user, t);
291 if (disk->vdev == NULL) {
292 rc = ERROR_FAIL;
293 goto out;
294 }
295 }
296
297 rc = libxl__device_disk_setdefault(gc, domid, disk, aodev->update_json);
298 if (rc) goto out;
299
300 front = flexarray_make(gc, 16, 1);
301 back = flexarray_make(gc, 16, 1);
302
303 GCNEW(device);
304 rc = libxl__device_from_disk(gc, domid, disk, device);
305 if (rc != 0) {
306 LOGD(ERROR, domid, "Invalid or unsupported"" virtual disk identifier %s",
307 disk->vdev);
308 goto out;
309 }
310
311 rc = libxl__device_exists(gc, t, device);
312 if (rc < 0) goto out;
313 if (rc == 1) { /* already exists in xenstore */
314 LOGD(ERROR, domid, "device already exists in xenstore");
315 aodev->action = LIBXL__DEVICE_ACTION_ADD; /* for error message */
316 rc = ERROR_DEVICE_EXISTS;
317 goto out;
318 }
319
320 switch (disk->backend) {
321 case LIBXL_DISK_BACKEND_PHY:
322 dev = disk->pdev_path;
323
324 flexarray_append(back, "params");
325 flexarray_append(back, dev);
326
327 script = libxl__abs_path(gc, disk->script?: "block",
328 libxl__xen_script_dir_path());
329 flexarray_append_pair(back, "script", script);
330
331 assert(device->backend_kind == LIBXL__DEVICE_KIND_VBD);
332 break;
333
334 case LIBXL_DISK_BACKEND_TAP:
335 LOG(ERROR, "blktap is not supported");
336 rc = ERROR_FAIL;
337 goto out;
338 case LIBXL_DISK_BACKEND_QDISK:
339 flexarray_append(back, "params");
340 flexarray_append(back, GCSPRINTF("%s:%s",
341 libxl__device_disk_string_of_format(disk->format),
342 disk->pdev_path ? : ""));
343 if (libxl_defbool_val(disk->colo_enable)) {
344 flexarray_append(back, "colo-host");
345 flexarray_append(back, libxl__sprintf(gc, "%s", disk->colo_host));
346 flexarray_append(back, "colo-port");
347 flexarray_append(back, libxl__sprintf(gc, "%d", disk->colo_port));
348 flexarray_append(back, "colo-export");
349 flexarray_append(back, libxl__sprintf(gc, "%s", disk->colo_export));
350 flexarray_append(back, "active-disk");
351 flexarray_append(back, libxl__sprintf(gc, "%s", disk->active_disk));
352 flexarray_append(back, "hidden-disk");
353 flexarray_append(back, libxl__sprintf(gc, "%s", disk->hidden_disk));
354 }
355 assert(device->backend_kind == LIBXL__DEVICE_KIND_QDISK);
356 break;
357 default:
358 LOGD(ERROR, domid, "Unrecognized disk backend type: %d",
359 disk->backend);
360 rc = ERROR_INVAL;
361 goto out;
362 }
363
364 flexarray_append(back, "frontend-id");
365 flexarray_append(back, GCSPRINTF("%d", domid));
366 flexarray_append(back, "online");
367 flexarray_append(back, "1");
368 flexarray_append(back, "removable");
369 flexarray_append(back, GCSPRINTF("%d", (disk->removable) ? 1 : 0));
370 flexarray_append(back, "bootable");
371 flexarray_append(back, GCSPRINTF("%d", 1));
372 flexarray_append(back, "state");
373 flexarray_append(back, GCSPRINTF("%d", XenbusStateInitialising));
374 flexarray_append(back, "dev");
375 flexarray_append(back, disk->vdev);
376 flexarray_append(back, "type");
377 flexarray_append(back, libxl__device_disk_string_of_backend(disk->backend));
378 flexarray_append(back, "mode");
379 flexarray_append(back, disk->readwrite ? "w" : "r");
380 flexarray_append(back, "device-type");
381 flexarray_append(back, disk->is_cdrom ? "cdrom" : "disk");
382 if (disk->direct_io_safe) {
383 flexarray_append(back, "direct-io-safe");
384 flexarray_append(back, "1");
385 }
386 flexarray_append_pair(back, "discard-enable",
387 libxl_defbool_val(disk->discard_enable) ?
388 "1" : "0");
389
390 flexarray_append(front, "backend-id");
391 flexarray_append(front, GCSPRINTF("%d", disk->backend_domid));
392 flexarray_append(front, "state");
393 flexarray_append(front, GCSPRINTF("%d", XenbusStateInitialising));
394 flexarray_append(front, "virtual-device");
395 flexarray_append(front, GCSPRINTF("%d", device->devid));
396 flexarray_append(front, "device-type");
397 flexarray_append(front, disk->is_cdrom ? "cdrom" : "disk");
398
399 /*
400 * Old PV kernel disk frontends before 2.6.26 rely on tool stack to
401 * write disk native protocol to frontend node. Xend does this, port
402 * this behaviour to xl.
403 *
404 * New kernels write this node themselves. In that case it just
405 * overwrites an existing node which is OK.
406 */
407 if (type == LIBXL_DOMAIN_TYPE_PV) {
408 const char *protocol =
409 xc_domain_get_native_protocol(ctx->xch, domid);
410 if (protocol) {
411 flexarray_append(front, "protocol");
412 flexarray_append(front, libxl__strdup(gc, protocol));
413 }
414 }
415
416 if (!get_vdev && aodev->update_json) {
417 rc = libxl__set_domain_configuration(gc, domid, &d_config);
418 if (rc) goto out;
419 }
420
421 libxl__device_generic_add(gc, t, device,
422 libxl__xs_kvs_of_flexarray(gc, back),
423 libxl__xs_kvs_of_flexarray(gc, front),
424 NULL);
425
426 rc = libxl__xs_transaction_commit(gc, &t);
427 if (!rc) break;
428 if (rc < 0) goto out;
429 }
430
431 aodev->dev = device;
432 aodev->action = LIBXL__DEVICE_ACTION_ADD;
433 libxl__wait_device_connection(egc, aodev);
434
435 rc = 0;
436
437 out:
438 libxl__xs_transaction_abort(gc, &t);
439 if (lock) libxl__unlock_file(lock);
440 libxl_device_disk_dispose(&disk_saved);
441 libxl_domain_config_dispose(&d_config);
442 aodev->rc = rc;
443 if (rc) aodev->callback(egc, aodev);
444 return;
445 }
446
libxl__device_disk_add(libxl__egc * egc,uint32_t domid,libxl_device_disk * disk,libxl__ao_device * aodev)447 static void libxl__device_disk_add(libxl__egc *egc, uint32_t domid,
448 libxl_device_disk *disk,
449 libxl__ao_device *aodev)
450 {
451 device_disk_add(egc, domid, disk, aodev, NULL, NULL);
452 }
453
libxl__disk_from_xenstore(libxl__gc * gc,const char * libxl_path,libxl_devid devid,libxl_device_disk * disk)454 static int libxl__disk_from_xenstore(libxl__gc *gc, const char *libxl_path,
455 libxl_devid devid,
456 libxl_device_disk *disk)
457 {
458 libxl_ctx *ctx = libxl__gc_owner(gc);
459 unsigned int len;
460 char *tmp;
461 int rc;
462
463 const char *backend_path;
464 rc = libxl__xs_read_checked(gc, XBT_NULL,
465 GCSPRINTF("%s/backend", libxl_path),
466 &backend_path);
467 if (rc) goto out;
468
469 if (!backend_path) {
470 LOG(ERROR, "disk %s does not exist (no backend path", libxl_path);
471 rc = ERROR_FAIL;
472 goto out;
473 }
474
475 rc = libxl__backendpath_parse_domid(gc, backend_path, &disk->backend_domid);
476 if (rc) {
477 LOG(ERROR, "Unable to fetch device backend domid from %s", backend_path);
478 goto out;
479 }
480
481 /*
482 * "params" may not be present; but everything else must be.
483 * colo releated entries(colo-host, colo-port, colo-export,
484 * active-disk and hidden-disk) are present only if colo is
485 * enabled.
486 */
487 tmp = xs_read(ctx->xsh, XBT_NULL,
488 GCSPRINTF("%s/params", libxl_path), &len);
489 if (tmp && strchr(tmp, ':')) {
490 disk->pdev_path = strdup(strchr(tmp, ':') + 1);
491 free(tmp);
492 } else {
493 disk->pdev_path = tmp;
494 }
495
496 tmp = xs_read(ctx->xsh, XBT_NULL,
497 GCSPRINTF("%s/colo-host", libxl_path), &len);
498 if (tmp) {
499 libxl_defbool_set(&disk->colo_enable, true);
500 disk->colo_host = tmp;
501
502 tmp = xs_read(ctx->xsh, XBT_NULL,
503 GCSPRINTF("%s/colo-port", libxl_path), &len);
504 if (!tmp) {
505 LOG(ERROR, "Missing xenstore node %s/colo-port", libxl_path);
506 goto cleanup;
507 }
508 disk->colo_port = atoi(tmp);
509
510 #define XS_READ_COLO(param, item) do { \
511 tmp = xs_read(ctx->xsh, XBT_NULL, \
512 GCSPRINTF("%s/"#param"", libxl_path), &len); \
513 if (!tmp) { \
514 LOG(ERROR, "Missing xenstore node %s/"#param"", libxl_path); \
515 goto cleanup; \
516 } \
517 disk->item = tmp; \
518 } while (0)
519 XS_READ_COLO(colo-export, colo_export);
520 XS_READ_COLO(active-disk, active_disk);
521 XS_READ_COLO(hidden-disk, hidden_disk);
522 #undef XS_READ_COLO
523 } else {
524 libxl_defbool_set(&disk->colo_enable, false);
525 }
526
527 tmp = libxl__xs_read(gc, XBT_NULL,
528 GCSPRINTF("%s/type", libxl_path));
529 if (!tmp) {
530 LOG(ERROR, "Missing xenstore node %s/type", libxl_path);
531 goto cleanup;
532 }
533 libxl_string_to_backend(ctx, tmp, &(disk->backend));
534
535 disk->vdev = xs_read(ctx->xsh, XBT_NULL,
536 GCSPRINTF("%s/dev", libxl_path), &len);
537 if (!disk->vdev) {
538 LOG(ERROR, "Missing xenstore node %s/dev", libxl_path);
539 goto cleanup;
540 }
541
542 tmp = libxl__xs_read(gc, XBT_NULL, libxl__sprintf
543 (gc, "%s/removable", libxl_path));
544 if (!tmp) {
545 LOG(ERROR, "Missing xenstore node %s/removable", libxl_path);
546 goto cleanup;
547 }
548 disk->removable = atoi(tmp);
549
550 tmp = libxl__xs_read(gc, XBT_NULL, GCSPRINTF("%s/mode", libxl_path));
551 if (!tmp) {
552 LOG(ERROR, "Missing xenstore node %s/mode", libxl_path);
553 goto cleanup;
554 }
555 if (!strcmp(tmp, "w"))
556 disk->readwrite = 1;
557 else
558 disk->readwrite = 0;
559
560 tmp = libxl__xs_read(gc, XBT_NULL,
561 GCSPRINTF("%s/device-type", libxl_path));
562 if (!tmp) {
563 LOG(ERROR, "Missing xenstore node %s/device-type", libxl_path);
564 goto cleanup;
565 }
566 disk->is_cdrom = !strcmp(tmp, "cdrom");
567
568 disk->format = LIBXL_DISK_FORMAT_UNKNOWN;
569
570 return 0;
571 cleanup:
572 rc = ERROR_FAIL;
573 out:
574 libxl_device_disk_dispose(disk);
575 return rc;
576 }
577
libxl_vdev_to_device_disk(libxl_ctx * ctx,uint32_t domid,const char * vdev,libxl_device_disk * disk)578 int libxl_vdev_to_device_disk(libxl_ctx *ctx, uint32_t domid,
579 const char *vdev, libxl_device_disk *disk)
580 {
581 GC_INIT(ctx);
582 char *libxl_path;
583 int devid = libxl__device_disk_dev_number(vdev, NULL, NULL);
584 int rc = ERROR_FAIL;
585
586 if (devid < 0)
587 return ERROR_INVAL;
588
589 libxl_device_disk_init(disk);
590
591 libxl_path = libxl__domain_device_libxl_path(gc, domid, devid,
592 LIBXL__DEVICE_KIND_VBD);
593
594 rc = libxl__disk_from_xenstore(gc, libxl_path, devid, disk);
595
596 GC_FREE;
597 return rc;
598 }
599
libxl_device_disk_getinfo(libxl_ctx * ctx,uint32_t domid,const libxl_device_disk * disk,libxl_diskinfo * diskinfo)600 int libxl_device_disk_getinfo(libxl_ctx *ctx, uint32_t domid,
601 const libxl_device_disk *disk,
602 libxl_diskinfo *diskinfo)
603 {
604 GC_INIT(ctx);
605 char *fe_path, *libxl_path;
606 char *val;
607 int rc;
608
609 diskinfo->backend = NULL;
610
611 diskinfo->devid = libxl__device_disk_dev_number(disk->vdev, NULL, NULL);
612
613 /* tap devices entries in xenstore are written as vbd devices. */
614 fe_path = libxl__domain_device_frontend_path(gc, domid, diskinfo->devid,
615 LIBXL__DEVICE_KIND_VBD);
616 libxl_path = libxl__domain_device_libxl_path(gc, domid, diskinfo->devid,
617 LIBXL__DEVICE_KIND_VBD);
618 diskinfo->backend = xs_read(ctx->xsh, XBT_NULL,
619 GCSPRINTF("%s/backend", libxl_path), NULL);
620 if (!diskinfo->backend) {
621 GC_FREE;
622 return ERROR_FAIL;
623 }
624 rc = libxl__backendpath_parse_domid(gc, diskinfo->backend,
625 &diskinfo->backend_id);
626 if (rc) goto out;
627
628 val = libxl__xs_read(gc, XBT_NULL, GCSPRINTF("%s/state", fe_path));
629 diskinfo->state = val ? strtoul(val, NULL, 10) : -1;
630 val = libxl__xs_read(gc, XBT_NULL, GCSPRINTF("%s/event-channel", fe_path));
631 diskinfo->evtch = val ? strtoul(val, NULL, 10) : -1;
632 val = libxl__xs_read(gc, XBT_NULL, GCSPRINTF("%s/ring-ref", fe_path));
633 diskinfo->rref = val ? strtoul(val, NULL, 10) : -1;
634 diskinfo->frontend = xs_read(ctx->xsh, XBT_NULL,
635 GCSPRINTF("%s/frontend", libxl_path), NULL);
636 diskinfo->frontend_id = domid;
637
638 GC_FREE;
639 return 0;
640
641 out:
642 free(diskinfo->backend);
643 return rc;
644 }
645
646 typedef struct {
647 libxl__ao *ao;
648 libxl_domid domid;
649 libxl_device_disk *disk;
650 libxl_device_disk disk_saved;
651 libxl__ev_slowlock qmp_lock;
652 int dm_ver;
653 libxl__ev_time time;
654 libxl__ev_qmp qmp;
655 } libxl__cdrom_insert_state;
656
657 static void cdrom_insert_lock_acquired(libxl__egc *, libxl__ev_slowlock *,
658 int rc);
659 static void cdrom_insert_ejected(libxl__egc *egc, libxl__ev_qmp *,
660 const libxl__json_object *, int rc);
661 static void cdrom_insert_addfd_cb(libxl__egc *egc, libxl__ev_qmp *,
662 const libxl__json_object *, int rc);
663 static void cdrom_insert_inserted(libxl__egc *egc, libxl__ev_qmp *,
664 const libxl__json_object *, int rc);
665 static void cdrom_insert_timout(libxl__egc *egc, libxl__ev_time *ev,
666 const struct timeval *requested_abs,
667 int rc);
668 static void cdrom_insert_done(libxl__egc *egc,
669 libxl__cdrom_insert_state *cis,
670 int rc);
671
libxl_cdrom_insert(libxl_ctx * ctx,uint32_t domid,libxl_device_disk * disk,const libxl_asyncop_how * ao_how)672 int libxl_cdrom_insert(libxl_ctx *ctx, uint32_t domid, libxl_device_disk *disk,
673 const libxl_asyncop_how *ao_how)
674 {
675 AO_CREATE(ctx, domid, ao_how);
676 int num = 0, i;
677 libxl_device_disk *disks = NULL;
678 int rc;
679 libxl__cdrom_insert_state *cis;
680
681 GCNEW(cis);
682 cis->ao = ao;
683 cis->domid = domid;
684 cis->disk = disk;
685 libxl_device_disk_init(&cis->disk_saved);
686 libxl_device_disk_copy(ctx, &cis->disk_saved, disk);
687 libxl__ev_devlock_init(&cis->qmp_lock);
688 cis->qmp_lock.ao = ao;
689 cis->qmp_lock.domid = domid;
690 libxl__ev_time_init(&cis->time);
691 libxl__ev_qmp_init(&cis->qmp);
692 cis->qmp.ao = ao;
693 cis->qmp.domid = domid;
694 cis->qmp.payload_fd = -1;
695
696 libxl_domain_type type = libxl__domain_type(gc, domid);
697 if (type == LIBXL_DOMAIN_TYPE_INVALID) {
698 rc = ERROR_FAIL;
699 goto out;
700 }
701 if (type != LIBXL_DOMAIN_TYPE_HVM) {
702 LOGD(ERROR, domid, "cdrom-insert requires an HVM domain");
703 rc = ERROR_INVAL;
704 goto out;
705 }
706
707 if (libxl_get_stubdom_id(ctx, domid) != 0) {
708 LOGD(ERROR, domid, "cdrom-insert doesn't work for stub domains");
709 rc = ERROR_INVAL;
710 goto out;
711 }
712
713 cis->dm_ver = libxl__device_model_version_running(gc, domid);
714 if (cis->dm_ver == -1) {
715 LOGD(ERROR, domid, "Cannot determine device model version");
716 rc = ERROR_FAIL;
717 goto out;
718 }
719
720 disks = libxl__device_list(gc, &libxl__disk_devtype, domid, &num);
721 for (i = 0; i < num; i++) {
722 if (disks[i].is_cdrom && !strcmp(disk->vdev, disks[i].vdev))
723 {
724 /* Found. Set backend type appropriately. */
725 disk->backend=disks[i].backend;
726 break;
727 }
728 }
729 if (i == num) {
730 LOGD(ERROR, domid, "Virtual device not found");
731 rc = ERROR_FAIL;
732 goto out;
733 }
734
735 rc = libxl__device_disk_setdefault(gc, domid, disk, false);
736 if (rc) goto out;
737
738 if (!disk->pdev_path) {
739 disk->pdev_path = libxl__strdup(NOGC, "");
740 disk->format = LIBXL_DISK_FORMAT_EMPTY;
741 }
742
743 out:
744 libxl__device_list_free(&libxl__disk_devtype, disks, num);
745 if (rc) {
746 cdrom_insert_done(egc, cis, rc); /* must be last */
747 } else {
748 cis->qmp_lock.callback = cdrom_insert_lock_acquired;
749 libxl__ev_slowlock_lock(egc, &cis->qmp_lock); /* must be last */
750 }
751 return AO_INPROGRESS;
752 }
753
cdrom_insert_lock_acquired(libxl__egc * egc,libxl__ev_slowlock * lock,int rc)754 static void cdrom_insert_lock_acquired(libxl__egc *egc,
755 libxl__ev_slowlock *lock,
756 int rc)
757 {
758 libxl__cdrom_insert_state *cis = CONTAINER_OF(lock, *cis, qmp_lock);
759 STATE_AO_GC(cis->ao);
760
761 if (rc) goto out;
762
763 rc = libxl__ev_time_register_rel(ao, &cis->time,
764 cdrom_insert_timout,
765 LIBXL_HOTPLUG_TIMEOUT * 1000);
766 if (rc) goto out;
767
768 /* We need to eject the original image first.
769 * JSON is not updated.
770 */
771
772 if (cis->dm_ver == LIBXL_DEVICE_MODEL_VERSION_QEMU_XEN) {
773 libxl__json_object *args = NULL;
774 int devid = libxl__device_disk_dev_number(cis->disk->vdev,
775 NULL, NULL);
776
777 QMP_PARAMETERS_SPRINTF(&args, "device", "ide-%i", devid);
778 cis->qmp.callback = cdrom_insert_ejected;
779 rc = libxl__ev_qmp_send(egc, &cis->qmp, "eject", args);
780 if (rc) goto out;
781 } else {
782 cdrom_insert_ejected(egc, &cis->qmp, NULL, 0); /* must be last */
783 }
784 return;
785
786 out:
787 cdrom_insert_done(egc, cis, rc); /* must be last */
788 }
789
cdrom_insert_ejected(libxl__egc * egc,libxl__ev_qmp * qmp,const libxl__json_object * response,int rc)790 static void cdrom_insert_ejected(libxl__egc *egc,
791 libxl__ev_qmp *qmp,
792 const libxl__json_object *response,
793 int rc)
794 {
795 EGC_GC;
796 libxl__cdrom_insert_state *cis = CONTAINER_OF(qmp, *cis, qmp);
797 libxl__flock *data_lock = NULL;
798 libxl__device device;
799 const char *be_path, *libxl_path;
800 flexarray_t *empty = NULL;
801 xs_transaction_t t = XBT_NULL;
802 char *tmp;
803 libxl_domain_config d_config;
804 bool has_callback = false;
805
806 /* convenience aliases */
807 libxl_domid domid = cis->domid;
808 libxl_device_disk *disk = cis->disk;
809
810 libxl_domain_config_init(&d_config);
811
812 if (rc) goto out;
813
814 rc = libxl__device_from_disk(gc, domid, disk, &device);
815 if (rc) goto out;
816 be_path = libxl__device_backend_path(gc, &device);
817 libxl_path = libxl__device_libxl_path(gc, &device);
818
819 data_lock = libxl__lock_domain_userdata(gc, domid);
820 if (!data_lock) {
821 rc = ERROR_LOCK_FAIL;
822 goto out;
823 }
824
825 empty = flexarray_make(gc, 4, 1);
826 flexarray_append_pair(empty, "type",
827 libxl__device_disk_string_of_backend(disk->backend));
828 flexarray_append_pair(empty, "params", "");
829
830 for (;;) {
831 rc = libxl__xs_transaction_start(gc, &t);
832 if (rc) goto out;
833 /* Sanity check: make sure the device exists before writing here */
834 tmp = libxl__xs_read(gc, t, GCSPRINTF("%s/frontend", libxl_path));
835 if (!tmp)
836 {
837 LOGD(ERROR, domid, "Internal error: %s does not exist",
838 GCSPRINTF("%s/frontend", libxl_path));
839 rc = ERROR_FAIL;
840 goto out;
841 }
842
843 char **kvs = libxl__xs_kvs_of_flexarray(gc, empty);
844
845 rc = libxl__xs_writev(gc, t, be_path, kvs);
846 if (rc) goto out;
847
848 rc = libxl__xs_writev(gc, t, libxl_path, kvs);
849 if (rc) goto out;
850
851 rc = libxl__xs_transaction_commit(gc, &t);
852 if (!rc) break;
853 if (rc < 0) goto out;
854 }
855
856 /*
857 * Now that the drive is empty, we can insert the new media.
858 */
859
860 rc = libxl__get_domain_configuration(gc, domid, &d_config);
861 if (rc) goto out;
862
863 device_add_domain_config(gc, &d_config, &libxl__disk_devtype,
864 &cis->disk_saved);
865
866 rc = libxl__dm_check_start(gc, &d_config, domid);
867 if (rc) goto out;
868
869 if (cis->dm_ver == LIBXL_DEVICE_MODEL_VERSION_QEMU_XEN &&
870 disk->format != LIBXL_DISK_FORMAT_EMPTY) {
871 libxl__json_object *args = NULL;
872
873 assert(qmp->payload_fd == -1);
874 qmp->payload_fd = open(disk->pdev_path, O_RDONLY);
875 if (qmp->payload_fd < 0) {
876 LOGED(ERROR, domid, "Failed to open cdrom file %s",
877 disk->pdev_path);
878 rc = ERROR_FAIL;
879 goto out;
880 }
881
882 /* This free form parameter is not use by QEMU or libxl. */
883 QMP_PARAMETERS_SPRINTF(&args, "opaque", "%s:%s",
884 libxl_disk_format_to_string(disk->format),
885 disk->pdev_path);
886 qmp->callback = cdrom_insert_addfd_cb;
887 rc = libxl__ev_qmp_send(egc, qmp, "add-fd", args);
888 if (rc) goto out;
889 has_callback = true;
890 } else {
891 has_callback = false;
892 }
893
894 rc = 0;
895
896 out:
897 libxl__xs_transaction_abort(gc, &t);
898 libxl_domain_config_dispose(&d_config);
899 if (data_lock) libxl__unlock_file(data_lock);
900 if (rc) {
901 cdrom_insert_done(egc, cis, rc); /* must be last */
902 } else if (!has_callback) {
903 /* Only called if no asynchronous callback are set. */
904 cdrom_insert_inserted(egc, qmp, NULL, 0); /* must be last */
905 }
906 }
907
cdrom_insert_addfd_cb(libxl__egc * egc,libxl__ev_qmp * qmp,const libxl__json_object * response,int rc)908 static void cdrom_insert_addfd_cb(libxl__egc *egc,
909 libxl__ev_qmp *qmp,
910 const libxl__json_object *response,
911 int rc)
912 {
913 EGC_GC;
914 libxl__cdrom_insert_state *cis = CONTAINER_OF(qmp, *cis, qmp);
915 libxl__json_object *args = NULL;
916 const libxl__json_object *o;
917 int devid;
918 int fdset;
919
920 /* convenience aliases */
921 libxl_device_disk *disk = cis->disk;
922
923 close(qmp->payload_fd);
924 qmp->payload_fd = -1;
925
926 if (rc) goto out;
927
928 o = libxl__json_map_get("fdset-id", response, JSON_INTEGER);
929 if (!o) {
930 rc = ERROR_FAIL;
931 goto out;
932 }
933 fdset = libxl__json_object_get_integer(o);
934
935 devid = libxl__device_disk_dev_number(disk->vdev, NULL, NULL);
936 QMP_PARAMETERS_SPRINTF(&args, "device", "ide-%i", devid);
937 QMP_PARAMETERS_SPRINTF(&args, "target", "/dev/fdset/%d", fdset);
938 libxl__qmp_param_add_string(gc, &args, "arg",
939 libxl__qemu_disk_format_string(disk->format));
940 qmp->callback = cdrom_insert_inserted;
941 rc = libxl__ev_qmp_send(egc, qmp, "change", args);
942 out:
943 if (rc)
944 cdrom_insert_done(egc, cis, rc); /* must be last */
945 }
946
cdrom_insert_inserted(libxl__egc * egc,libxl__ev_qmp * qmp,const libxl__json_object * response,int rc)947 static void cdrom_insert_inserted(libxl__egc *egc,
948 libxl__ev_qmp *qmp,
949 const libxl__json_object *response,
950 int rc)
951 {
952 EGC_GC;
953 libxl__cdrom_insert_state *cis = CONTAINER_OF(qmp, *cis, qmp);
954 libxl__flock *data_lock = NULL;
955 libxl_domain_config d_config;
956 flexarray_t *insert = NULL;
957 xs_transaction_t t = XBT_NULL;
958 libxl__device device;
959 const char *be_path, *libxl_path;
960 char *tmp;
961
962 /* convenience aliases */
963 libxl_domid domid = cis->domid;
964 libxl_device_disk *disk = cis->disk;
965
966 libxl_domain_config_init(&d_config);
967
968 if (rc) goto out;
969
970 rc = libxl__device_from_disk(gc, domid, disk, &device);
971 if (rc) goto out;
972 be_path = libxl__device_backend_path(gc, &device);
973 libxl_path = libxl__device_libxl_path(gc, &device);
974
975 data_lock = libxl__lock_domain_userdata(gc, domid);
976 if (!data_lock) {
977 rc = ERROR_LOCK_FAIL;
978 goto out;
979 }
980
981 rc = libxl__get_domain_configuration(gc, domid, &d_config);
982 if (rc) goto out;
983
984 device_add_domain_config(gc, &d_config, &libxl__disk_devtype,
985 &cis->disk_saved);
986
987 insert = flexarray_make(gc, 4, 1);
988 flexarray_append_pair(insert, "type",
989 libxl__device_disk_string_of_backend(disk->backend));
990 if (disk->format != LIBXL_DISK_FORMAT_EMPTY)
991 flexarray_append_pair(insert, "params",
992 GCSPRINTF("%s:%s",
993 libxl__device_disk_string_of_format(disk->format),
994 disk->pdev_path));
995 else
996 flexarray_append_pair(insert, "params", "");
997
998 for (;;) {
999 rc = libxl__xs_transaction_start(gc, &t);
1000 if (rc) goto out;
1001 /* Sanity check: make sure the device exists before writing here */
1002 tmp = libxl__xs_read(gc, t, GCSPRINTF("%s/frontend", libxl_path));
1003 if (!tmp)
1004 {
1005 LOGD(ERROR, domid, "Internal error: %s does not exist",
1006 GCSPRINTF("%s/frontend", libxl_path));
1007 rc = ERROR_FAIL;
1008 goto out;
1009 }
1010
1011 rc = libxl__set_domain_configuration(gc, domid, &d_config);
1012 if (rc) goto out;
1013
1014 char **kvs = libxl__xs_kvs_of_flexarray(gc, insert);
1015
1016 rc = libxl__xs_writev(gc, t, be_path, kvs);
1017 if (rc) goto out;
1018
1019 rc = libxl__xs_writev(gc, t, libxl_path, kvs);
1020 if (rc) goto out;
1021
1022 rc = libxl__xs_transaction_commit(gc, &t);
1023 if (!rc) break;
1024 if (rc < 0) goto out;
1025 }
1026
1027 rc = 0;
1028
1029 out:
1030 libxl__xs_transaction_abort(gc, &t);
1031 libxl_domain_config_dispose(&d_config);
1032 if (data_lock) libxl__unlock_file(data_lock);
1033 cdrom_insert_done(egc, cis, rc); /* must be last */
1034 }
1035
cdrom_insert_timout(libxl__egc * egc,libxl__ev_time * ev,const struct timeval * requested_abs,int rc)1036 static void cdrom_insert_timout(libxl__egc *egc, libxl__ev_time *ev,
1037 const struct timeval *requested_abs,
1038 int rc)
1039 {
1040 EGC_GC;
1041 libxl__cdrom_insert_state *cis = CONTAINER_OF(ev, *cis, time);
1042 LOGD(ERROR, cis->domid, "cdrom insertion timed out");
1043 cdrom_insert_done(egc, cis, rc);
1044 }
1045
cdrom_insert_done(libxl__egc * egc,libxl__cdrom_insert_state * cis,int rc)1046 static void cdrom_insert_done(libxl__egc *egc,
1047 libxl__cdrom_insert_state *cis,
1048 int rc)
1049 {
1050 EGC_GC;
1051
1052 libxl__ev_time_deregister(gc, &cis->time);
1053 libxl__ev_qmp_dispose(gc, &cis->qmp);
1054 if (cis->qmp.payload_fd >= 0) close(cis->qmp.payload_fd);
1055 libxl__ev_slowlock_unlock(gc, &cis->qmp_lock);
1056 libxl_device_disk_dispose(&cis->disk_saved);
1057 libxl__ao_complete(egc, cis->ao, rc);
1058 }
1059
1060 /* libxl__alloc_vdev only works on the local domain, that is the domain
1061 * where the toolstack is running */
libxl__alloc_vdev(libxl__gc * gc,void * get_vdev_user,xs_transaction_t t)1062 static char * libxl__alloc_vdev(libxl__gc *gc, void *get_vdev_user,
1063 xs_transaction_t t)
1064 {
1065 const char *blkdev_start = (const char *) get_vdev_user;
1066 int devid = 0, disk = 0, part = 0;
1067
1068 libxl__device_disk_dev_number(blkdev_start, &disk, &part);
1069 if (part != 0) {
1070 LOG(ERROR, "blkdev_start is invalid");
1071 return NULL;
1072 }
1073
1074 do {
1075 devid = libxl__device_disk_dev_number(GCSPRINTF("d%dp0", disk),
1076 NULL, NULL);
1077 if (devid < 0)
1078 return NULL;
1079 if (libxl__xs_read(gc, t, GCSPRINTF("%s/backend",
1080 libxl__domain_device_libxl_path(gc,
1081 LIBXL_TOOLSTACK_DOMID, devid,
1082 LIBXL__DEVICE_KIND_VBD))) == NULL) {
1083 if (errno == ENOENT)
1084 return libxl__devid_to_vdev(gc, devid);
1085 else
1086 return NULL;
1087 }
1088 disk++;
1089 } while (1);
1090 return NULL;
1091 }
1092
1093 /* Callbacks */
1094
libxl__device_disk_find_local_path(libxl__gc * gc,libxl_domid guest_domid,const libxl_device_disk * disk,bool qdisk_direct)1095 char *libxl__device_disk_find_local_path(libxl__gc *gc,
1096 libxl_domid guest_domid,
1097 const libxl_device_disk *disk,
1098 bool qdisk_direct)
1099 {
1100 char *path = NULL;
1101
1102 /* No local paths for driver domains */
1103 if (disk->backend_domname != NULL) {
1104 LOG(DEBUG, "Non-local backend, can't access locally.\n");
1105 goto out;
1106 }
1107
1108 /*
1109 * If this is in raw format, and we're not using a script or a
1110 * driver domain, we can access the target path directly.
1111 */
1112 if (disk->format == LIBXL_DISK_FORMAT_RAW
1113 && disk->script == NULL) {
1114 path = libxl__strdup(gc, disk->pdev_path);
1115 LOG(DEBUG, "Directly accessing local RAW disk %s", path);
1116 goto out;
1117 }
1118
1119 /*
1120 * If we're being called for a qemu path, we can pass the target
1121 * string directly as well
1122 */
1123 if (qdisk_direct && disk->backend == LIBXL_DISK_BACKEND_QDISK) {
1124 path = libxl__strdup(gc, disk->pdev_path);
1125 LOG(DEBUG, "Directly accessing local QDISK target %s", path);
1126 goto out;
1127 }
1128
1129 /*
1130 * If the format isn't raw and / or we're using a script, then see
1131 * if the script has written a path to the "cooked" node
1132 */
1133 if (disk->script && guest_domid != INVALID_DOMID) {
1134 libxl__device device;
1135 char *be_path, *pdpath;
1136 int rc;
1137
1138 LOGD(DEBUG, guest_domid,
1139 "Run from a script; checking for physical-device-path (vdev %s)",
1140 disk->vdev);
1141
1142 rc = libxl__device_from_disk(gc, guest_domid, disk, &device);
1143 if (rc < 0)
1144 goto out;
1145
1146 be_path = libxl__device_backend_path(gc, &device);
1147
1148 pdpath = libxl__sprintf(gc, "%s/physical-device-path", be_path);
1149
1150 LOGD(DEBUG, guest_domid, "Attempting to read node %s", pdpath);
1151 path = libxl__xs_read(gc, XBT_NULL, pdpath);
1152
1153 if (path)
1154 LOGD(DEBUG, guest_domid, "Accessing cooked block device %s", path);
1155 else
1156 LOGD(DEBUG, guest_domid, "No physical-device-path, can't access locally.");
1157
1158 goto out;
1159 }
1160
1161 out:
1162 return path;
1163 }
1164
1165 static void local_device_attach_cb(libxl__egc *egc, libxl__ao_device *aodev);
1166
libxl__device_disk_local_initiate_attach(libxl__egc * egc,libxl__disk_local_state * dls)1167 void libxl__device_disk_local_initiate_attach(libxl__egc *egc,
1168 libxl__disk_local_state *dls)
1169 {
1170 STATE_AO_GC(dls->ao);
1171 int rc;
1172 const libxl_device_disk *in_disk = dls->in_disk;
1173 libxl_device_disk *disk = &dls->disk;
1174 const char *blkdev_start = dls->blkdev_start;
1175
1176 assert(in_disk->pdev_path);
1177
1178 disk->vdev = NULL;
1179
1180 if (dls->diskpath)
1181 LOG(DEBUG, "Strange, dls->diskpath already set: %s", dls->diskpath);
1182
1183 LOG(DEBUG, "Trying to find local path");
1184
1185 dls->diskpath = libxl__device_disk_find_local_path(gc, INVALID_DOMID,
1186 in_disk, false);
1187 if (dls->diskpath) {
1188 LOG(DEBUG, "Local path found, executing callback.");
1189 dls->callback(egc, dls, 0);
1190 } else {
1191 LOG(DEBUG, "Local path not found, initiating attach.");
1192
1193 memcpy(disk, in_disk, sizeof(libxl_device_disk));
1194 disk->pdev_path = libxl__strdup(gc, in_disk->pdev_path);
1195 if (in_disk->script != NULL)
1196 disk->script = libxl__strdup(gc, in_disk->script);
1197 disk->vdev = NULL;
1198
1199 rc = libxl__device_disk_setdefault(gc, LIBXL_TOOLSTACK_DOMID, disk,
1200 false);
1201 if (rc) goto out;
1202
1203 libxl__prepare_ao_device(ao, &dls->aodev);
1204 dls->aodev.callback = local_device_attach_cb;
1205 device_disk_add(egc, LIBXL_TOOLSTACK_DOMID, disk, &dls->aodev,
1206 libxl__alloc_vdev, (void *) blkdev_start);
1207 }
1208
1209 return;
1210
1211 out:
1212 assert(rc);
1213 dls->rc = rc;
1214 libxl__device_disk_local_initiate_detach(egc, dls);
1215 dls->callback(egc, dls, rc);
1216 }
1217
local_device_attach_cb(libxl__egc * egc,libxl__ao_device * aodev)1218 static void local_device_attach_cb(libxl__egc *egc, libxl__ao_device *aodev)
1219 {
1220 STATE_AO_GC(aodev->ao);
1221 libxl__disk_local_state *dls = CONTAINER_OF(aodev, *dls, aodev);
1222 char *be_path = NULL;
1223 int rc;
1224 libxl__device device;
1225 libxl_device_disk *disk = &dls->disk;
1226
1227 rc = aodev->rc;
1228 if (rc) {
1229 LOGE(ERROR, "unable locally attach device: %s", disk->pdev_path);
1230 goto out;
1231 }
1232
1233 rc = libxl__device_from_disk(gc, LIBXL_TOOLSTACK_DOMID, disk, &device);
1234 if (rc < 0)
1235 goto out;
1236 be_path = libxl__device_backend_path(gc, &device);
1237 rc = libxl__wait_for_backend(gc, be_path, GCSPRINTF("%d", XenbusStateConnected));
1238 if (rc < 0)
1239 goto out;
1240
1241 dls->diskpath = GCSPRINTF("/dev/%s",
1242 libxl__devid_to_localdev(gc, device.devid));
1243 LOG(DEBUG, "locally attached disk %s", dls->diskpath);
1244
1245 dls->callback(egc, dls, 0);
1246 return;
1247
1248 out:
1249 assert(rc);
1250 dls->rc = rc;
1251 libxl__device_disk_local_initiate_detach(egc, dls);
1252 return;
1253 }
1254
1255 /* Callbacks for local detach */
1256
1257 static void local_device_detach_cb(libxl__egc *egc, libxl__ao_device *aodev);
1258
libxl__device_disk_local_initiate_detach(libxl__egc * egc,libxl__disk_local_state * dls)1259 void libxl__device_disk_local_initiate_detach(libxl__egc *egc,
1260 libxl__disk_local_state *dls)
1261 {
1262 STATE_AO_GC(dls->ao);
1263 int rc = 0;
1264 libxl_device_disk *disk = &dls->disk;
1265 libxl__device *device;
1266 libxl__ao_device *aodev = &dls->aodev;
1267 libxl__prepare_ao_device(ao, aodev);
1268
1269 if (!dls->diskpath) goto out;
1270
1271 if (disk->vdev != NULL) {
1272 GCNEW(device);
1273 rc = libxl__device_from_disk(gc, LIBXL_TOOLSTACK_DOMID,
1274 disk, device);
1275 if (rc != 0) goto out;
1276
1277 aodev->action = LIBXL__DEVICE_ACTION_REMOVE;
1278 aodev->dev = device;
1279 aodev->callback = local_device_detach_cb;
1280 aodev->force = 0;
1281 libxl__initiate_device_generic_remove(egc, aodev);
1282 return;
1283 }
1284
1285 out:
1286 aodev->rc = rc;
1287 local_device_detach_cb(egc, aodev);
1288 return;
1289 }
1290
local_device_detach_cb(libxl__egc * egc,libxl__ao_device * aodev)1291 static void local_device_detach_cb(libxl__egc *egc, libxl__ao_device *aodev)
1292 {
1293 STATE_AO_GC(aodev->ao);
1294 libxl__disk_local_state *dls = CONTAINER_OF(aodev, *dls, aodev);
1295 int rc;
1296
1297 if (aodev->rc) {
1298 LOGED(ERROR, aodev->dev->domid, "Unable to %s %s with id %u",
1299 libxl__device_action_to_string(aodev->action),
1300 libxl__device_kind_to_string(aodev->dev->kind),
1301 aodev->dev->devid);
1302 goto out;
1303 }
1304
1305 out:
1306 /*
1307 * If there was an error in dls->rc, it means we have been called from
1308 * a failed execution of libxl__device_disk_local_initiate_attach,
1309 * so return the original error.
1310 */
1311 rc = dls->rc ? dls->rc : aodev->rc;
1312 dls->callback(egc, dls, rc);
1313 return;
1314 }
1315
1316 /* The following functions are defined:
1317 * libxl_device_disk_add
1318 * libxl__add_disks
1319 * libxl_device_disk_remove
1320 * libxl_device_disk_destroy
1321 */
1322 LIBXL_DEFINE_DEVICE_ADD(disk)
LIBXL_DEFINE_DEVICES_ADD(disk)1323 LIBXL_DEFINE_DEVICES_ADD(disk)
1324 LIBXL_DEFINE_DEVICE_REMOVE(disk)
1325
1326 static int libxl_device_disk_compare(const libxl_device_disk *d1,
1327 const libxl_device_disk *d2)
1328 {
1329 return COMPARE_DISK(d1, d2);
1330 }
1331
1332 /* Take care of removable device. We maintain invariant in the
1333 * insert / remove operation so that:
1334 * 1. if xenstore is "empty" while JSON is not, the result
1335 * is "empty"
1336 * 2. if xenstore has a different media than JSON, use the
1337 * one in JSON
1338 * 3. if xenstore and JSON have the same media, well, you
1339 * know the answer :-)
1340 *
1341 * Currently there is only one removable device -- CDROM.
1342 * Look for libxl_cdrom_insert for reference.
1343 */
libxl_device_disk_merge(libxl_ctx * ctx,void * d1,void * d2)1344 static void libxl_device_disk_merge(libxl_ctx *ctx, void *d1, void *d2)
1345 {
1346 GC_INIT(ctx);
1347 libxl_device_disk *src = d1;
1348 libxl_device_disk *dst = d2;
1349
1350 if (src->removable) {
1351 if (!src->pdev_path || *src->pdev_path == '\0') {
1352 /* 1, no media in drive */
1353 free(dst->pdev_path);
1354 dst->pdev_path = libxl__strdup(NOGC, "");
1355 dst->format = LIBXL_DISK_FORMAT_EMPTY;
1356 } else {
1357 /* 2 and 3, use JSON, no need to touch anything */
1358 ;
1359 }
1360 }
1361 }
1362
libxl_device_disk_dm_needed(void * e,unsigned domid)1363 static int libxl_device_disk_dm_needed(void *e, unsigned domid)
1364 {
1365 libxl_device_disk *elem = e;
1366
1367 return elem->backend == LIBXL_DISK_BACKEND_QDISK &&
1368 elem->backend_domid == domid;
1369 }
1370
1371 LIBXL_DEFINE_DEVICE_LIST(disk)
1372
1373 #define libxl__device_disk_update_devid NULL
1374
1375 DEFINE_DEVICE_TYPE_STRUCT(disk, VBD,
1376 .merge = libxl_device_disk_merge,
1377 .dm_needed = libxl_device_disk_dm_needed,
1378 .from_xenstore = (device_from_xenstore_fn_t)libxl__disk_from_xenstore,
1379 .skip_attach = 1,
1380 );
1381
1382 /*
1383 * Local variables:
1384 * mode: C
1385 * c-basic-offset: 4
1386 * indent-tabs-mode: nil
1387 * End:
1388 */
1389