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(egc, "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(egc, "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/device/vbd/%d/eject",
101 libxl__xs_get_dompath(gc, domid),
102 devid);
103 if (!path) { rc = ERROR_NOMEM; goto out; }
104
105 const char *libxl_path = GCSPRINTF("%s/device/vbd/%d",
106 libxl__xs_libxl_path(gc, domid),
107 devid);
108 evg->be_ptr_path = libxl__sprintf(NOGC, "%s/backend", libxl_path);
109
110 const char *configured_vdev;
111 rc = libxl__xs_read_checked(gc, XBT_NULL,
112 GCSPRINTF("%s/dev", libxl_path), &configured_vdev);
113 if (rc) goto out;
114
115 evg->vdev = libxl__strdup(NOGC, configured_vdev);
116
117 rc = libxl__ev_xswatch_register(gc, &evg->watch,
118 disk_eject_xswatch_callback, path);
119 if (rc) goto out;
120
121 *evgen_out = evg;
122 CTX_UNLOCK;
123 GC_FREE;
124 return 0;
125
126 out:
127 if (evg)
128 libxl__evdisable_disk_eject(gc, evg);
129 CTX_UNLOCK;
130 GC_FREE;
131 return rc;
132 }
133
libxl__evdisable_disk_eject(libxl__gc * gc,libxl_evgen_disk_eject * evg)134 void libxl__evdisable_disk_eject(libxl__gc *gc, libxl_evgen_disk_eject *evg) {
135 CTX_LOCK;
136
137 LIBXL_LIST_REMOVE(evg, entry);
138
139 if (libxl__ev_xswatch_isregistered(&evg->watch))
140 libxl__ev_xswatch_deregister(gc, &evg->watch);
141
142 free(evg->vdev);
143 free(evg->be_ptr_path);
144 free(evg);
145
146 CTX_UNLOCK;
147 }
148
libxl_evdisable_disk_eject(libxl_ctx * ctx,libxl_evgen_disk_eject * evg)149 void libxl_evdisable_disk_eject(libxl_ctx *ctx, libxl_evgen_disk_eject *evg) {
150 GC_INIT(ctx);
151 libxl__evdisable_disk_eject(gc, evg);
152 GC_FREE;
153 }
154
libxl__device_disk_setdefault(libxl__gc * gc,uint32_t domid,libxl_device_disk * disk,bool hotplug)155 static int libxl__device_disk_setdefault(libxl__gc *gc, uint32_t domid,
156 libxl_device_disk *disk, bool hotplug)
157 {
158 int rc;
159
160 libxl_defbool_setdefault(&disk->discard_enable, !!disk->readwrite);
161 libxl_defbool_setdefault(&disk->colo_enable, false);
162 libxl_defbool_setdefault(&disk->colo_restore_enable, false);
163
164 rc = libxl__resolve_domid(gc, disk->backend_domname, &disk->backend_domid);
165 if (rc < 0) return rc;
166
167 /* Force Qdisk backend for CDROM devices of guests with a device model. */
168 if (disk->is_cdrom != 0 &&
169 libxl__domain_type(gc, domid) == LIBXL_DOMAIN_TYPE_HVM) {
170 if (!(disk->backend == LIBXL_DISK_BACKEND_QDISK ||
171 disk->backend == LIBXL_DISK_BACKEND_UNKNOWN)) {
172 LOGD(ERROR, domid, "Backend for CD devices on HVM guests must be Qdisk");
173 return ERROR_FAIL;
174 }
175 disk->backend = LIBXL_DISK_BACKEND_QDISK;
176 }
177
178 rc = libxl__device_disk_set_backend(gc, disk);
179 return rc;
180 }
181
libxl__device_from_disk(libxl__gc * gc,uint32_t domid,const libxl_device_disk * disk,libxl__device * device)182 static int libxl__device_from_disk(libxl__gc *gc, uint32_t domid,
183 const libxl_device_disk *disk,
184 libxl__device *device)
185 {
186 int devid;
187
188 devid = libxl__device_disk_dev_number(disk->vdev, NULL, NULL);
189 if (devid==-1) {
190 LOGD(ERROR, domid, "Invalid or unsupported"" virtual disk identifier %s",
191 disk->vdev);
192 return ERROR_INVAL;
193 }
194
195 device->backend_domid = disk->backend_domid;
196 device->backend_devid = devid;
197
198 switch (disk->backend) {
199 case LIBXL_DISK_BACKEND_PHY:
200 device->backend_kind = LIBXL__DEVICE_KIND_VBD;
201 break;
202 case LIBXL_DISK_BACKEND_TAP:
203 device->backend_kind = LIBXL__DEVICE_KIND_VBD;
204 break;
205 case LIBXL_DISK_BACKEND_QDISK:
206 device->backend_kind = LIBXL__DEVICE_KIND_QDISK;
207 break;
208 default:
209 LOGD(ERROR, domid, "Unrecognized disk backend type: %d",
210 disk->backend);
211 return ERROR_INVAL;
212 }
213
214 device->domid = domid;
215 device->devid = devid;
216 device->kind = LIBXL__DEVICE_KIND_VBD;
217
218 return 0;
219 }
220
221 /* Specific function called directly only by local disk attach,
222 * all other users should instead use the regular
223 * libxl__device_disk_add wrapper
224 *
225 * The (optionally) passed function get_vdev will be used to
226 * set the vdev the disk should be attached to. When it is set the caller
227 * must also pass get_vdev_user, which will be passed to get_vdev.
228 *
229 * The passed get_vdev function is also in charge of printing
230 * the corresponding error message when appropiate.
231 */
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)232 static void device_disk_add(libxl__egc *egc, uint32_t domid,
233 libxl_device_disk *disk,
234 libxl__ao_device *aodev,
235 char *get_vdev(libxl__gc *, void *,
236 xs_transaction_t),
237 void *get_vdev_user)
238 {
239 STATE_AO_GC(aodev->ao);
240 flexarray_t *front = NULL;
241 flexarray_t *back = NULL;
242 char *dev = NULL, *script;
243 libxl__device *device;
244 int rc;
245 libxl_ctx *ctx = gc->owner;
246 xs_transaction_t t = XBT_NULL;
247 libxl_domain_config d_config;
248 libxl_device_disk disk_saved;
249 libxl__domain_userdata_lock *lock = NULL;
250
251 libxl_domain_config_init(&d_config);
252 libxl_device_disk_init(&disk_saved);
253 libxl_device_disk_copy(ctx, &disk_saved, disk);
254
255 libxl_domain_type type = libxl__domain_type(gc, domid);
256 if (type == LIBXL_DOMAIN_TYPE_INVALID) {
257 rc = ERROR_FAIL;
258 goto out;
259 }
260
261 /*
262 * get_vdev != NULL -> local attach
263 * get_vdev == NULL -> block attach
264 *
265 * We don't care about local attach state because it's only
266 * intermediate state.
267 */
268 if (!get_vdev && aodev->update_json) {
269 lock = libxl__lock_domain_userdata(gc, domid);
270 if (!lock) {
271 rc = ERROR_LOCK_FAIL;
272 goto out;
273 }
274
275 rc = libxl__get_domain_configuration(gc, domid, &d_config);
276 if (rc) goto out;
277
278 device_add_domain_config(gc, &d_config, &libxl__disk_devtype,
279 &disk_saved);
280
281 rc = libxl__dm_check_start(gc, &d_config, domid);
282 if (rc) goto out;
283 }
284
285 for (;;) {
286 rc = libxl__xs_transaction_start(gc, &t);
287 if (rc) goto out;
288
289 if (get_vdev) {
290 assert(get_vdev_user);
291 disk->vdev = get_vdev(gc, get_vdev_user, t);
292 if (disk->vdev == NULL) {
293 rc = ERROR_FAIL;
294 goto out;
295 }
296 }
297
298 rc = libxl__device_disk_setdefault(gc, domid, disk, aodev->update_json);
299 if (rc) goto out;
300
301 front = flexarray_make(gc, 16, 1);
302 back = flexarray_make(gc, 16, 1);
303
304 GCNEW(device);
305 rc = libxl__device_from_disk(gc, domid, disk, device);
306 if (rc != 0) {
307 LOGD(ERROR, domid, "Invalid or unsupported"" virtual disk identifier %s",
308 disk->vdev);
309 goto out;
310 }
311
312 rc = libxl__device_exists(gc, t, device);
313 if (rc < 0) goto out;
314 if (rc == 1) { /* already exists in xenstore */
315 LOGD(ERROR, domid, "device already exists in xenstore");
316 aodev->action = LIBXL__DEVICE_ACTION_ADD; /* for error message */
317 rc = ERROR_DEVICE_EXISTS;
318 goto out;
319 }
320
321 switch (disk->backend) {
322 case LIBXL_DISK_BACKEND_PHY:
323 dev = disk->pdev_path;
324
325 do_backend_phy:
326 flexarray_append(back, "params");
327 flexarray_append(back, dev);
328
329 script = libxl__abs_path(gc, disk->script?: "block",
330 libxl__xen_script_dir_path());
331 flexarray_append_pair(back, "script", script);
332
333 assert(device->backend_kind == LIBXL__DEVICE_KIND_VBD);
334 break;
335
336 case LIBXL_DISK_BACKEND_TAP:
337 if (dev == NULL) {
338 dev = libxl__blktap_devpath(gc, disk->pdev_path,
339 disk->format);
340 if (!dev) {
341 LOGD(ERROR, domid, "Failed to get blktap devpath for %p",
342 disk->pdev_path);
343 rc = ERROR_FAIL;
344 goto out;
345 }
346 }
347 flexarray_append(back, "tapdisk-params");
348 flexarray_append(back, GCSPRINTF("%s:%s",
349 libxl__device_disk_string_of_format(disk->format),
350 disk->pdev_path));
351
352 /* tap backends with scripts are rejected by
353 * libxl__device_disk_set_backend */
354 assert(!disk->script);
355
356 /* now create a phy device to export the device to the guest */
357 goto do_backend_phy;
358 case LIBXL_DISK_BACKEND_QDISK:
359 flexarray_append(back, "params");
360 flexarray_append(back, GCSPRINTF("%s:%s",
361 libxl__device_disk_string_of_format(disk->format),
362 disk->pdev_path ? : ""));
363 if (libxl_defbool_val(disk->colo_enable)) {
364 flexarray_append(back, "colo-host");
365 flexarray_append(back, libxl__sprintf(gc, "%s", disk->colo_host));
366 flexarray_append(back, "colo-port");
367 flexarray_append(back, libxl__sprintf(gc, "%d", disk->colo_port));
368 flexarray_append(back, "colo-export");
369 flexarray_append(back, libxl__sprintf(gc, "%s", disk->colo_export));
370 flexarray_append(back, "active-disk");
371 flexarray_append(back, libxl__sprintf(gc, "%s", disk->active_disk));
372 flexarray_append(back, "hidden-disk");
373 flexarray_append(back, libxl__sprintf(gc, "%s", disk->hidden_disk));
374 }
375 assert(device->backend_kind == LIBXL__DEVICE_KIND_QDISK);
376 break;
377 default:
378 LOGD(ERROR, domid, "Unrecognized disk backend type: %d",
379 disk->backend);
380 rc = ERROR_INVAL;
381 goto out;
382 }
383
384 flexarray_append(back, "frontend-id");
385 flexarray_append(back, GCSPRINTF("%d", domid));
386 flexarray_append(back, "online");
387 flexarray_append(back, "1");
388 flexarray_append(back, "removable");
389 flexarray_append(back, GCSPRINTF("%d", (disk->removable) ? 1 : 0));
390 flexarray_append(back, "bootable");
391 flexarray_append(back, GCSPRINTF("%d", 1));
392 flexarray_append(back, "state");
393 flexarray_append(back, GCSPRINTF("%d", XenbusStateInitialising));
394 flexarray_append(back, "dev");
395 flexarray_append(back, disk->vdev);
396 flexarray_append(back, "type");
397 flexarray_append(back, libxl__device_disk_string_of_backend(disk->backend));
398 flexarray_append(back, "mode");
399 flexarray_append(back, disk->readwrite ? "w" : "r");
400 flexarray_append(back, "device-type");
401 flexarray_append(back, disk->is_cdrom ? "cdrom" : "disk");
402 if (disk->direct_io_safe) {
403 flexarray_append(back, "direct-io-safe");
404 flexarray_append(back, "1");
405 }
406 flexarray_append_pair(back, "discard-enable",
407 libxl_defbool_val(disk->discard_enable) ?
408 "1" : "0");
409
410 flexarray_append(front, "backend-id");
411 flexarray_append(front, GCSPRINTF("%d", disk->backend_domid));
412 flexarray_append(front, "state");
413 flexarray_append(front, GCSPRINTF("%d", XenbusStateInitialising));
414 flexarray_append(front, "virtual-device");
415 flexarray_append(front, GCSPRINTF("%d", device->devid));
416 flexarray_append(front, "device-type");
417 flexarray_append(front, disk->is_cdrom ? "cdrom" : "disk");
418
419 /*
420 * Old PV kernel disk frontends before 2.6.26 rely on tool stack to
421 * write disk native protocol to frontend node. Xend does this, port
422 * this behaviour to xl.
423 *
424 * New kernels write this node themselves. In that case it just
425 * overwrites an existing node which is OK.
426 */
427 if (type == LIBXL_DOMAIN_TYPE_PV) {
428 const char *protocol =
429 xc_domain_get_native_protocol(ctx->xch, domid);
430 if (protocol) {
431 flexarray_append(front, "protocol");
432 flexarray_append(front, libxl__strdup(gc, protocol));
433 }
434 }
435
436 if (!get_vdev && aodev->update_json) {
437 rc = libxl__set_domain_configuration(gc, domid, &d_config);
438 if (rc) goto out;
439 }
440
441 libxl__device_generic_add(gc, t, device,
442 libxl__xs_kvs_of_flexarray(gc, back),
443 libxl__xs_kvs_of_flexarray(gc, front),
444 NULL);
445
446 rc = libxl__xs_transaction_commit(gc, &t);
447 if (!rc) break;
448 if (rc < 0) goto out;
449 }
450
451 aodev->dev = device;
452 aodev->action = LIBXL__DEVICE_ACTION_ADD;
453 libxl__wait_device_connection(egc, aodev);
454
455 rc = 0;
456
457 out:
458 libxl__xs_transaction_abort(gc, &t);
459 if (lock) libxl__unlock_domain_userdata(lock);
460 libxl_device_disk_dispose(&disk_saved);
461 libxl_domain_config_dispose(&d_config);
462 aodev->rc = rc;
463 if (rc) aodev->callback(egc, aodev);
464 return;
465 }
466
libxl__device_disk_add(libxl__egc * egc,uint32_t domid,libxl_device_disk * disk,libxl__ao_device * aodev)467 static void libxl__device_disk_add(libxl__egc *egc, uint32_t domid,
468 libxl_device_disk *disk,
469 libxl__ao_device *aodev)
470 {
471 device_disk_add(egc, domid, disk, aodev, NULL, NULL);
472 }
473
libxl__disk_from_xenstore(libxl__gc * gc,const char * libxl_path,libxl_devid devid,libxl_device_disk * disk)474 static int libxl__disk_from_xenstore(libxl__gc *gc, const char *libxl_path,
475 libxl_devid devid,
476 libxl_device_disk *disk)
477 {
478 libxl_ctx *ctx = libxl__gc_owner(gc);
479 unsigned int len;
480 char *tmp;
481 int rc;
482
483 const char *backend_path;
484 rc = libxl__xs_read_checked(gc, XBT_NULL,
485 GCSPRINTF("%s/backend", libxl_path),
486 &backend_path);
487 if (rc) goto out;
488
489 if (!backend_path) {
490 LOG(ERROR, "disk %s does not exist (no backend path", libxl_path);
491 rc = ERROR_FAIL;
492 goto out;
493 }
494
495 rc = libxl__backendpath_parse_domid(gc, backend_path, &disk->backend_domid);
496 if (rc) {
497 LOG(ERROR, "Unable to fetch device backend domid from %s", backend_path);
498 goto out;
499 }
500
501 /*
502 * "params" may not be present; but everything else must be.
503 * colo releated entries(colo-host, colo-port, colo-export,
504 * active-disk and hidden-disk) are present only if colo is
505 * enabled.
506 */
507 tmp = xs_read(ctx->xsh, XBT_NULL,
508 GCSPRINTF("%s/params", libxl_path), &len);
509 if (tmp && strchr(tmp, ':')) {
510 disk->pdev_path = strdup(strchr(tmp, ':') + 1);
511 free(tmp);
512 } else {
513 disk->pdev_path = tmp;
514 }
515
516 tmp = xs_read(ctx->xsh, XBT_NULL,
517 GCSPRINTF("%s/colo-host", libxl_path), &len);
518 if (tmp) {
519 libxl_defbool_set(&disk->colo_enable, true);
520 disk->colo_host = tmp;
521
522 tmp = xs_read(ctx->xsh, XBT_NULL,
523 GCSPRINTF("%s/colo-port", libxl_path), &len);
524 if (!tmp) {
525 LOG(ERROR, "Missing xenstore node %s/colo-port", libxl_path);
526 goto cleanup;
527 }
528 disk->colo_port = atoi(tmp);
529
530 #define XS_READ_COLO(param, item) do { \
531 tmp = xs_read(ctx->xsh, XBT_NULL, \
532 GCSPRINTF("%s/"#param"", libxl_path), &len); \
533 if (!tmp) { \
534 LOG(ERROR, "Missing xenstore node %s/"#param"", libxl_path); \
535 goto cleanup; \
536 } \
537 disk->item = tmp; \
538 } while (0)
539 XS_READ_COLO(colo-export, colo_export);
540 XS_READ_COLO(active-disk, active_disk);
541 XS_READ_COLO(hidden-disk, hidden_disk);
542 #undef XS_READ_COLO
543 } else {
544 libxl_defbool_set(&disk->colo_enable, false);
545 }
546
547 tmp = libxl__xs_read(gc, XBT_NULL,
548 GCSPRINTF("%s/type", libxl_path));
549 if (!tmp) {
550 LOG(ERROR, "Missing xenstore node %s/type", libxl_path);
551 goto cleanup;
552 }
553 libxl_string_to_backend(ctx, tmp, &(disk->backend));
554
555 disk->vdev = xs_read(ctx->xsh, XBT_NULL,
556 GCSPRINTF("%s/dev", libxl_path), &len);
557 if (!disk->vdev) {
558 LOG(ERROR, "Missing xenstore node %s/dev", libxl_path);
559 goto cleanup;
560 }
561
562 tmp = libxl__xs_read(gc, XBT_NULL, libxl__sprintf
563 (gc, "%s/removable", libxl_path));
564 if (!tmp) {
565 LOG(ERROR, "Missing xenstore node %s/removable", libxl_path);
566 goto cleanup;
567 }
568 disk->removable = atoi(tmp);
569
570 tmp = libxl__xs_read(gc, XBT_NULL, GCSPRINTF("%s/mode", libxl_path));
571 if (!tmp) {
572 LOG(ERROR, "Missing xenstore node %s/mode", libxl_path);
573 goto cleanup;
574 }
575 if (!strcmp(tmp, "w"))
576 disk->readwrite = 1;
577 else
578 disk->readwrite = 0;
579
580 tmp = libxl__xs_read(gc, XBT_NULL,
581 GCSPRINTF("%s/device-type", libxl_path));
582 if (!tmp) {
583 LOG(ERROR, "Missing xenstore node %s/device-type", libxl_path);
584 goto cleanup;
585 }
586 disk->is_cdrom = !strcmp(tmp, "cdrom");
587
588 disk->format = LIBXL_DISK_FORMAT_UNKNOWN;
589
590 return 0;
591 cleanup:
592 rc = ERROR_FAIL;
593 out:
594 libxl_device_disk_dispose(disk);
595 return rc;
596 }
597
libxl_vdev_to_device_disk(libxl_ctx * ctx,uint32_t domid,const char * vdev,libxl_device_disk * disk)598 int libxl_vdev_to_device_disk(libxl_ctx *ctx, uint32_t domid,
599 const char *vdev, libxl_device_disk *disk)
600 {
601 GC_INIT(ctx);
602 char *dom_xl_path, *libxl_path;
603 int devid = libxl__device_disk_dev_number(vdev, NULL, NULL);
604 int rc = ERROR_FAIL;
605
606 if (devid < 0)
607 return ERROR_INVAL;
608
609 libxl_device_disk_init(disk);
610
611 dom_xl_path = libxl__xs_libxl_path(gc, domid);
612 if (!dom_xl_path) {
613 goto out;
614 }
615 libxl_path = GCSPRINTF("%s/device/vbd/%d", dom_xl_path, devid);
616
617 rc = libxl__disk_from_xenstore(gc, libxl_path, devid, disk);
618 out:
619 GC_FREE;
620 return rc;
621 }
622
libxl_device_disk_getinfo(libxl_ctx * ctx,uint32_t domid,libxl_device_disk * disk,libxl_diskinfo * diskinfo)623 int libxl_device_disk_getinfo(libxl_ctx *ctx, uint32_t domid,
624 libxl_device_disk *disk, libxl_diskinfo *diskinfo)
625 {
626 GC_INIT(ctx);
627 char *dompath, *fe_path, *libxl_path;
628 char *val;
629 int rc;
630
631 diskinfo->backend = NULL;
632
633 dompath = libxl__xs_get_dompath(gc, domid);
634 diskinfo->devid = libxl__device_disk_dev_number(disk->vdev, NULL, NULL);
635
636 /* tap devices entries in xenstore are written as vbd devices. */
637 fe_path = GCSPRINTF("%s/device/vbd/%d", dompath, diskinfo->devid);
638 libxl_path = GCSPRINTF("%s/device/vbd/%d",
639 libxl__xs_libxl_path(gc, domid), diskinfo->devid);
640 diskinfo->backend = xs_read(ctx->xsh, XBT_NULL,
641 GCSPRINTF("%s/backend", libxl_path), NULL);
642 if (!diskinfo->backend) {
643 GC_FREE;
644 return ERROR_FAIL;
645 }
646 rc = libxl__backendpath_parse_domid(gc, diskinfo->backend,
647 &diskinfo->backend_id);
648 if (rc) goto out;
649
650 val = libxl__xs_read(gc, XBT_NULL, GCSPRINTF("%s/state", fe_path));
651 diskinfo->state = val ? strtoul(val, NULL, 10) : -1;
652 val = libxl__xs_read(gc, XBT_NULL, GCSPRINTF("%s/event-channel", fe_path));
653 diskinfo->evtch = val ? strtoul(val, NULL, 10) : -1;
654 val = libxl__xs_read(gc, XBT_NULL, GCSPRINTF("%s/ring-ref", fe_path));
655 diskinfo->rref = val ? strtoul(val, NULL, 10) : -1;
656 diskinfo->frontend = xs_read(ctx->xsh, XBT_NULL,
657 GCSPRINTF("%s/frontend", libxl_path), NULL);
658 diskinfo->frontend_id = domid;
659
660 GC_FREE;
661 return 0;
662
663 out:
664 free(diskinfo->backend);
665 return rc;
666 }
667
libxl_cdrom_insert(libxl_ctx * ctx,uint32_t domid,libxl_device_disk * disk,const libxl_asyncop_how * ao_how)668 int libxl_cdrom_insert(libxl_ctx *ctx, uint32_t domid, libxl_device_disk *disk,
669 const libxl_asyncop_how *ao_how)
670 {
671 AO_CREATE(ctx, domid, ao_how);
672 int num = 0, i;
673 libxl_device_disk *disks = NULL, disk_saved, disk_empty;
674 libxl_domain_config d_config;
675 int rc, dm_ver;
676 libxl__device device;
677 const char *be_path, *libxl_path;
678 char * tmp;
679 libxl__domain_userdata_lock *lock = NULL;
680 xs_transaction_t t = XBT_NULL;
681 flexarray_t *insert = NULL, *empty = NULL;
682
683 libxl_domain_config_init(&d_config);
684 libxl_device_disk_init(&disk_empty);
685 libxl_device_disk_init(&disk_saved);
686 libxl_device_disk_copy(ctx, &disk_saved, disk);
687
688 disk_empty.format = LIBXL_DISK_FORMAT_EMPTY;
689 disk_empty.vdev = libxl__strdup(NOGC, disk->vdev);
690 disk_empty.pdev_path = libxl__strdup(NOGC, "");
691 disk_empty.is_cdrom = 1;
692 libxl__device_disk_setdefault(gc, domid, &disk_empty, false);
693
694 libxl_domain_type type = libxl__domain_type(gc, domid);
695 if (type == LIBXL_DOMAIN_TYPE_INVALID) {
696 rc = ERROR_FAIL;
697 goto out;
698 }
699 if (type != LIBXL_DOMAIN_TYPE_HVM) {
700 LOGD(ERROR, domid, "cdrom-insert requires an HVM domain");
701 rc = ERROR_INVAL;
702 goto out;
703 }
704
705 if (libxl_get_stubdom_id(ctx, domid) != 0) {
706 LOGD(ERROR, domid, "cdrom-insert doesn't work for stub domains");
707 rc = ERROR_INVAL;
708 goto out;
709 }
710
711 dm_ver = libxl__device_model_version_running(gc, domid);
712 if (dm_ver == -1) {
713 LOGD(ERROR, domid, "Cannot determine device model version");
714 rc = ERROR_FAIL;
715 goto out;
716 }
717
718 disks = libxl__device_list(gc, &libxl__disk_devtype, domid, &num);
719 for (i = 0; i < num; i++) {
720 if (disks[i].is_cdrom && !strcmp(disk->vdev, disks[i].vdev))
721 {
722 /* Found. Set backend type appropriately. */
723 disk->backend=disks[i].backend;
724 break;
725 }
726 }
727 if (i == num) {
728 LOGD(ERROR, domid, "Virtual device not found");
729 rc = ERROR_FAIL;
730 goto out;
731 }
732
733 rc = libxl__device_disk_setdefault(gc, domid, disk, false);
734 if (rc) goto out;
735
736 if (!disk->pdev_path) {
737 disk->pdev_path = libxl__strdup(NOGC, "");
738 disk->format = LIBXL_DISK_FORMAT_EMPTY;
739 }
740
741 rc = libxl__device_from_disk(gc, domid, disk, &device);
742 if (rc) goto out;
743
744 be_path = libxl__device_backend_path(gc, &device);
745 libxl_path = libxl__device_libxl_path(gc, &device);
746
747 insert = flexarray_make(gc, 4, 1);
748
749 flexarray_append_pair(insert, "type",
750 libxl__device_disk_string_of_backend(disk->backend));
751 if (disk->format != LIBXL_DISK_FORMAT_EMPTY)
752 flexarray_append_pair(insert, "params",
753 GCSPRINTF("%s:%s",
754 libxl__device_disk_string_of_format(disk->format),
755 disk->pdev_path));
756 else
757 flexarray_append_pair(insert, "params", "");
758
759 empty = flexarray_make(gc, 4, 1);
760 flexarray_append_pair(empty, "type",
761 libxl__device_disk_string_of_backend(disk->backend));
762 flexarray_append_pair(empty, "params", "");
763
764 /* Note: CTX lock is already held at this point so lock hierarchy
765 * is maintained.
766 */
767 lock = libxl__lock_domain_userdata(gc, domid);
768 if (!lock) {
769 rc = ERROR_LOCK_FAIL;
770 goto out;
771 }
772
773 /* We need to eject the original image first. This is implemented
774 * by inserting empty media. JSON is not updated.
775 */
776 if (dm_ver == LIBXL_DEVICE_MODEL_VERSION_QEMU_XEN) {
777 rc = libxl__qmp_insert_cdrom(gc, domid, &disk_empty);
778 if (rc) goto out;
779 }
780
781 for (;;) {
782 rc = libxl__xs_transaction_start(gc, &t);
783 if (rc) goto out;
784 /* Sanity check: make sure the device exists before writing here */
785 tmp = libxl__xs_read(gc, t, GCSPRINTF("%s/frontend", libxl_path));
786 if (!tmp)
787 {
788 LOGD(ERROR, domid, "Internal error: %s does not exist",
789 GCSPRINTF("%s/frontend", libxl_path));
790 rc = ERROR_FAIL;
791 goto out;
792 }
793
794 char **kvs = libxl__xs_kvs_of_flexarray(gc, empty);
795
796 rc = libxl__xs_writev(gc, t, be_path, kvs);
797 if (rc) goto out;
798
799 rc = libxl__xs_writev(gc, t, libxl_path, kvs);
800 if (rc) goto out;
801
802 rc = libxl__xs_transaction_commit(gc, &t);
803 if (!rc) break;
804 if (rc < 0) goto out;
805 }
806
807 rc = libxl__get_domain_configuration(gc, domid, &d_config);
808 if (rc) goto out;
809
810 device_add_domain_config(gc, &d_config, &libxl__disk_devtype, &disk_saved);
811
812 rc = libxl__dm_check_start(gc, &d_config, domid);
813 if (rc) goto out;
814
815 if (dm_ver == LIBXL_DEVICE_MODEL_VERSION_QEMU_XEN) {
816 rc = libxl__qmp_insert_cdrom(gc, domid, disk);
817 if (rc) goto out;
818 }
819
820 for (;;) {
821 rc = libxl__xs_transaction_start(gc, &t);
822 if (rc) goto out;
823 /* Sanity check: make sure the device exists before writing here */
824 tmp = libxl__xs_read(gc, t, GCSPRINTF("%s/frontend", libxl_path));
825 if (!tmp)
826 {
827 LOGD(ERROR, domid, "Internal error: %s does not exist",
828 GCSPRINTF("%s/frontend", libxl_path));
829 rc = ERROR_FAIL;
830 goto out;
831 }
832
833 rc = libxl__set_domain_configuration(gc, domid, &d_config);
834 if (rc) goto out;
835
836 char **kvs = libxl__xs_kvs_of_flexarray(gc, insert);
837
838 rc = libxl__xs_writev(gc, t, be_path, kvs);
839 if (rc) goto out;
840
841 rc = libxl__xs_writev(gc, t, libxl_path, kvs);
842 if (rc) goto out;
843
844 rc = libxl__xs_transaction_commit(gc, &t);
845 if (!rc) break;
846 if (rc < 0) goto out;
847 }
848
849 /* success, no actual async */
850 libxl__ao_complete(egc, ao, 0);
851
852 rc = 0;
853
854 out:
855 libxl__xs_transaction_abort(gc, &t);
856 libxl__device_list_free(&libxl__disk_devtype, disks, num);
857 libxl_device_disk_dispose(&disk_empty);
858 libxl_device_disk_dispose(&disk_saved);
859 libxl_domain_config_dispose(&d_config);
860
861 if (lock) libxl__unlock_domain_userdata(lock);
862
863 if (rc) return AO_CREATE_FAIL(rc);
864 return AO_INPROGRESS;
865 }
866
867 /* libxl__alloc_vdev only works on the local domain, that is the domain
868 * where the toolstack is running */
libxl__alloc_vdev(libxl__gc * gc,void * get_vdev_user,xs_transaction_t t)869 static char * libxl__alloc_vdev(libxl__gc *gc, void *get_vdev_user,
870 xs_transaction_t t)
871 {
872 const char *blkdev_start = (const char *) get_vdev_user;
873 int devid = 0, disk = 0, part = 0;
874 char *libxl_dom_path = libxl__xs_libxl_path(gc, LIBXL_TOOLSTACK_DOMID);
875
876 libxl__device_disk_dev_number(blkdev_start, &disk, &part);
877 if (part != 0) {
878 LOG(ERROR, "blkdev_start is invalid");
879 return NULL;
880 }
881
882 do {
883 devid = libxl__device_disk_dev_number(GCSPRINTF("d%dp0", disk),
884 NULL, NULL);
885 if (devid < 0)
886 return NULL;
887 if (libxl__xs_read(gc, t,
888 GCSPRINTF("%s/device/vbd/%d/backend",
889 libxl_dom_path, devid)) == NULL) {
890 if (errno == ENOENT)
891 return libxl__devid_to_vdev(gc, devid);
892 else
893 return NULL;
894 }
895 disk++;
896 } while (1);
897 return NULL;
898 }
899
900 /* Callbacks */
901
libxl__device_disk_find_local_path(libxl__gc * gc,libxl_domid guest_domid,const libxl_device_disk * disk,bool qdisk_direct)902 char *libxl__device_disk_find_local_path(libxl__gc *gc,
903 libxl_domid guest_domid,
904 const libxl_device_disk *disk,
905 bool qdisk_direct)
906 {
907 char *path = NULL;
908
909 /* No local paths for driver domains */
910 if (disk->backend_domname != NULL) {
911 LOG(DEBUG, "Non-local backend, can't access locally.\n");
912 goto out;
913 }
914
915 /*
916 * If this is in raw format, and we're not using a script or a
917 * driver domain, we can access the target path directly.
918 */
919 if (disk->format == LIBXL_DISK_FORMAT_RAW
920 && disk->script == NULL) {
921 path = libxl__strdup(gc, disk->pdev_path);
922 LOG(DEBUG, "Directly accessing local RAW disk %s", path);
923 goto out;
924 }
925
926 /*
927 * If we're being called for a qemu path, we can pass the target
928 * string directly as well
929 */
930 if (qdisk_direct && disk->backend == LIBXL_DISK_BACKEND_QDISK) {
931 path = libxl__strdup(gc, disk->pdev_path);
932 LOG(DEBUG, "Directly accessing local QDISK target %s", path);
933 goto out;
934 }
935
936 /*
937 * If the format isn't raw and / or we're using a script, then see
938 * if the script has written a path to the "cooked" node
939 */
940 if (disk->script && guest_domid != INVALID_DOMID) {
941 libxl__device device;
942 char *be_path, *pdpath;
943 int rc;
944
945 LOGD(DEBUG, guest_domid,
946 "Run from a script; checking for physical-device-path (vdev %s)",
947 disk->vdev);
948
949 rc = libxl__device_from_disk(gc, guest_domid, disk, &device);
950 if (rc < 0)
951 goto out;
952
953 be_path = libxl__device_backend_path(gc, &device);
954
955 pdpath = libxl__sprintf(gc, "%s/physical-device-path", be_path);
956
957 LOGD(DEBUG, guest_domid, "Attempting to read node %s", pdpath);
958 path = libxl__xs_read(gc, XBT_NULL, pdpath);
959
960 if (path)
961 LOGD(DEBUG, guest_domid, "Accessing cooked block device %s", path);
962 else
963 LOGD(DEBUG, guest_domid, "No physical-device-path, can't access locally.");
964
965 goto out;
966 }
967
968 out:
969 return path;
970 }
971
972 static void local_device_attach_cb(libxl__egc *egc, libxl__ao_device *aodev);
973
libxl__device_disk_local_initiate_attach(libxl__egc * egc,libxl__disk_local_state * dls)974 void libxl__device_disk_local_initiate_attach(libxl__egc *egc,
975 libxl__disk_local_state *dls)
976 {
977 STATE_AO_GC(dls->ao);
978 int rc;
979 const libxl_device_disk *in_disk = dls->in_disk;
980 libxl_device_disk *disk = &dls->disk;
981 const char *blkdev_start = dls->blkdev_start;
982
983 assert(in_disk->pdev_path);
984
985 disk->vdev = NULL;
986
987 if (dls->diskpath)
988 LOG(DEBUG, "Strange, dls->diskpath already set: %s", dls->diskpath);
989
990 LOG(DEBUG, "Trying to find local path");
991
992 dls->diskpath = libxl__device_disk_find_local_path(gc, INVALID_DOMID,
993 in_disk, false);
994 if (dls->diskpath) {
995 LOG(DEBUG, "Local path found, executing callback.");
996 dls->callback(egc, dls, 0);
997 } else {
998 LOG(DEBUG, "Local path not found, initiating attach.");
999
1000 memcpy(disk, in_disk, sizeof(libxl_device_disk));
1001 disk->pdev_path = libxl__strdup(gc, in_disk->pdev_path);
1002 if (in_disk->script != NULL)
1003 disk->script = libxl__strdup(gc, in_disk->script);
1004 disk->vdev = NULL;
1005
1006 rc = libxl__device_disk_setdefault(gc, LIBXL_TOOLSTACK_DOMID, disk,
1007 false);
1008 if (rc) goto out;
1009
1010 libxl__prepare_ao_device(ao, &dls->aodev);
1011 dls->aodev.callback = local_device_attach_cb;
1012 device_disk_add(egc, LIBXL_TOOLSTACK_DOMID, disk, &dls->aodev,
1013 libxl__alloc_vdev, (void *) blkdev_start);
1014 }
1015
1016 return;
1017
1018 out:
1019 assert(rc);
1020 dls->rc = rc;
1021 libxl__device_disk_local_initiate_detach(egc, dls);
1022 dls->callback(egc, dls, rc);
1023 }
1024
local_device_attach_cb(libxl__egc * egc,libxl__ao_device * aodev)1025 static void local_device_attach_cb(libxl__egc *egc, libxl__ao_device *aodev)
1026 {
1027 STATE_AO_GC(aodev->ao);
1028 libxl__disk_local_state *dls = CONTAINER_OF(aodev, *dls, aodev);
1029 char *be_path = NULL;
1030 int rc;
1031 libxl__device device;
1032 libxl_device_disk *disk = &dls->disk;
1033
1034 rc = aodev->rc;
1035 if (rc) {
1036 LOGE(ERROR, "unable locally attach device: %s", disk->pdev_path);
1037 goto out;
1038 }
1039
1040 rc = libxl__device_from_disk(gc, LIBXL_TOOLSTACK_DOMID, disk, &device);
1041 if (rc < 0)
1042 goto out;
1043 be_path = libxl__device_backend_path(gc, &device);
1044 rc = libxl__wait_for_backend(gc, be_path, GCSPRINTF("%d", XenbusStateConnected));
1045 if (rc < 0)
1046 goto out;
1047
1048 dls->diskpath = GCSPRINTF("/dev/%s",
1049 libxl__devid_to_localdev(gc, device.devid));
1050 LOG(DEBUG, "locally attached disk %s", dls->diskpath);
1051
1052 dls->callback(egc, dls, 0);
1053 return;
1054
1055 out:
1056 assert(rc);
1057 dls->rc = rc;
1058 libxl__device_disk_local_initiate_detach(egc, dls);
1059 return;
1060 }
1061
1062 /* Callbacks for local detach */
1063
1064 static void local_device_detach_cb(libxl__egc *egc, libxl__ao_device *aodev);
1065
libxl__device_disk_local_initiate_detach(libxl__egc * egc,libxl__disk_local_state * dls)1066 void libxl__device_disk_local_initiate_detach(libxl__egc *egc,
1067 libxl__disk_local_state *dls)
1068 {
1069 STATE_AO_GC(dls->ao);
1070 int rc = 0;
1071 libxl_device_disk *disk = &dls->disk;
1072 libxl__device *device;
1073 libxl__ao_device *aodev = &dls->aodev;
1074 libxl__prepare_ao_device(ao, aodev);
1075
1076 if (!dls->diskpath) goto out;
1077
1078 if (disk->vdev != NULL) {
1079 GCNEW(device);
1080 rc = libxl__device_from_disk(gc, LIBXL_TOOLSTACK_DOMID,
1081 disk, device);
1082 if (rc != 0) goto out;
1083
1084 aodev->action = LIBXL__DEVICE_ACTION_REMOVE;
1085 aodev->dev = device;
1086 aodev->callback = local_device_detach_cb;
1087 aodev->force = 0;
1088 libxl__initiate_device_generic_remove(egc, aodev);
1089 return;
1090 }
1091
1092 out:
1093 aodev->rc = rc;
1094 local_device_detach_cb(egc, aodev);
1095 return;
1096 }
1097
local_device_detach_cb(libxl__egc * egc,libxl__ao_device * aodev)1098 static void local_device_detach_cb(libxl__egc *egc, libxl__ao_device *aodev)
1099 {
1100 STATE_AO_GC(aodev->ao);
1101 libxl__disk_local_state *dls = CONTAINER_OF(aodev, *dls, aodev);
1102 int rc;
1103
1104 if (aodev->rc) {
1105 LOGED(ERROR, aodev->dev->domid, "Unable to %s %s with id %u",
1106 libxl__device_action_to_string(aodev->action),
1107 libxl__device_kind_to_string(aodev->dev->kind),
1108 aodev->dev->devid);
1109 goto out;
1110 }
1111
1112 out:
1113 /*
1114 * If there was an error in dls->rc, it means we have been called from
1115 * a failed execution of libxl__device_disk_local_initiate_attach,
1116 * so return the original error.
1117 */
1118 rc = dls->rc ? dls->rc : aodev->rc;
1119 dls->callback(egc, dls, rc);
1120 return;
1121 }
1122
1123 /* The following functions are defined:
1124 * libxl_device_disk_add
1125 * libxl__add_disks
1126 * libxl_device_disk_remove
1127 * libxl_device_disk_destroy
1128 */
1129 LIBXL_DEFINE_DEVICE_ADD(disk)
LIBXL_DEFINE_DEVICES_ADD(disk)1130 LIBXL_DEFINE_DEVICES_ADD(disk)
1131 LIBXL_DEFINE_DEVICE_REMOVE(disk)
1132
1133 static int libxl_device_disk_compare(libxl_device_disk *d1,
1134 libxl_device_disk *d2)
1135 {
1136 return COMPARE_DISK(d1, d2);
1137 }
1138
1139 /* Take care of removable device. We maintain invariant in the
1140 * insert / remove operation so that:
1141 * 1. if xenstore is "empty" while JSON is not, the result
1142 * is "empty"
1143 * 2. if xenstore has a different media than JSON, use the
1144 * one in JSON
1145 * 3. if xenstore and JSON have the same media, well, you
1146 * know the answer :-)
1147 *
1148 * Currently there is only one removable device -- CDROM.
1149 * Look for libxl_cdrom_insert for reference.
1150 */
libxl_device_disk_merge(libxl_ctx * ctx,void * d1,void * d2)1151 static void libxl_device_disk_merge(libxl_ctx *ctx, void *d1, void *d2)
1152 {
1153 GC_INIT(ctx);
1154 libxl_device_disk *src = d1;
1155 libxl_device_disk *dst = d2;
1156
1157 if (src->removable) {
1158 if (!src->pdev_path || *src->pdev_path == '\0') {
1159 /* 1, no media in drive */
1160 free(dst->pdev_path);
1161 dst->pdev_path = libxl__strdup(NOGC, "");
1162 dst->format = LIBXL_DISK_FORMAT_EMPTY;
1163 } else {
1164 /* 2 and 3, use JSON, no need to touch anything */
1165 ;
1166 }
1167 }
1168 }
1169
libxl_device_disk_dm_needed(void * e,unsigned domid)1170 static int libxl_device_disk_dm_needed(void *e, unsigned domid)
1171 {
1172 libxl_device_disk *elem = e;
1173
1174 return elem->backend == LIBXL_DISK_BACKEND_QDISK &&
1175 elem->backend_domid == domid;
1176 }
1177
1178 LIBXL_DEFINE_DEVICE_LIST(disk)
1179
1180 #define libxl__device_disk_update_devid NULL
1181
1182 DEFINE_DEVICE_TYPE_STRUCT_X(disk, disk, vbd,
1183 .merge = libxl_device_disk_merge,
1184 .dm_needed = libxl_device_disk_dm_needed,
1185 .from_xenstore = (device_from_xenstore_fn_t)libxl__disk_from_xenstore,
1186 .skip_attach = 1,
1187 );
1188
1189 /*
1190 * Local variables:
1191 * mode: C
1192 * c-basic-offset: 4
1193 * indent-tabs-mode: nil
1194 * End:
1195 */
1196