xref: /openbmc/qemu/hw/block/xen-block.c (revision 500eb6db)
1 /*
2  * Copyright (c) 2018  Citrix Systems Inc.
3  *
4  * This work is licensed under the terms of the GNU GPL, version 2 or later.
5  * See the COPYING file in the top-level directory.
6  */
7 
8 #include "qemu/osdep.h"
9 #include "qemu/cutils.h"
10 #include "qemu/module.h"
11 #include "qemu/option.h"
12 #include "qapi/error.h"
13 #include "qapi/qapi-commands-block-core.h"
14 #include "qapi/qapi-commands-misc.h"
15 #include "qapi/qapi-visit-block-core.h"
16 #include "qapi/qobject-input-visitor.h"
17 #include "qapi/visitor.h"
18 #include "qapi/qmp/qdict.h"
19 #include "qapi/qmp/qstring.h"
20 #include "hw/hw.h"
21 #include "hw/xen/xen_common.h"
22 #include "hw/block/xen_blkif.h"
23 #include "hw/xen/xen-block.h"
24 #include "hw/xen/xen-backend.h"
25 #include "sysemu/blockdev.h"
26 #include "sysemu/block-backend.h"
27 #include "sysemu/iothread.h"
28 #include "dataplane/xen-block.h"
29 #include "trace.h"
30 
31 static char *xen_block_get_name(XenDevice *xendev, Error **errp)
32 {
33     XenBlockDevice *blockdev = XEN_BLOCK_DEVICE(xendev);
34     XenBlockVdev *vdev = &blockdev->props.vdev;
35 
36     return g_strdup_printf("%lu", vdev->number);
37 }
38 
39 static void xen_block_disconnect(XenDevice *xendev, Error **errp)
40 {
41     XenBlockDevice *blockdev = XEN_BLOCK_DEVICE(xendev);
42     const char *type = object_get_typename(OBJECT(blockdev));
43     XenBlockVdev *vdev = &blockdev->props.vdev;
44 
45     trace_xen_block_disconnect(type, vdev->disk, vdev->partition);
46 
47     xen_block_dataplane_stop(blockdev->dataplane);
48 }
49 
50 static void xen_block_connect(XenDevice *xendev, Error **errp)
51 {
52     XenBlockDevice *blockdev = XEN_BLOCK_DEVICE(xendev);
53     const char *type = object_get_typename(OBJECT(blockdev));
54     XenBlockVdev *vdev = &blockdev->props.vdev;
55     unsigned int order, nr_ring_ref, *ring_ref, event_channel, protocol;
56     char *str;
57 
58     trace_xen_block_connect(type, vdev->disk, vdev->partition);
59 
60     if (xen_device_frontend_scanf(xendev, "ring-page-order", "%u",
61                                   &order) != 1) {
62         nr_ring_ref = 1;
63         ring_ref = g_new(unsigned int, nr_ring_ref);
64 
65         if (xen_device_frontend_scanf(xendev, "ring-ref", "%u",
66                                       &ring_ref[0]) != 1) {
67             error_setg(errp, "failed to read ring-ref");
68             g_free(ring_ref);
69             return;
70         }
71     } else if (order <= blockdev->props.max_ring_page_order) {
72         unsigned int i;
73 
74         nr_ring_ref = 1 << order;
75         ring_ref = g_new(unsigned int, nr_ring_ref);
76 
77         for (i = 0; i < nr_ring_ref; i++) {
78             const char *key = g_strdup_printf("ring-ref%u", i);
79 
80             if (xen_device_frontend_scanf(xendev, key, "%u",
81                                           &ring_ref[i]) != 1) {
82                 error_setg(errp, "failed to read %s", key);
83                 g_free((gpointer)key);
84                 g_free(ring_ref);
85                 return;
86             }
87 
88             g_free((gpointer)key);
89         }
90     } else {
91         error_setg(errp, "invalid ring-page-order (%d)", order);
92         return;
93     }
94 
95     if (xen_device_frontend_scanf(xendev, "event-channel", "%u",
96                                   &event_channel) != 1) {
97         error_setg(errp, "failed to read event-channel");
98         g_free(ring_ref);
99         return;
100     }
101 
102     if (xen_device_frontend_scanf(xendev, "protocol", "%ms",
103                                   &str) != 1) {
104         protocol = BLKIF_PROTOCOL_NATIVE;
105     } else {
106         if (strcmp(str, XEN_IO_PROTO_ABI_X86_32) == 0) {
107             protocol = BLKIF_PROTOCOL_X86_32;
108         } else if (strcmp(str, XEN_IO_PROTO_ABI_X86_64) == 0) {
109             protocol = BLKIF_PROTOCOL_X86_64;
110         } else {
111             protocol = BLKIF_PROTOCOL_NATIVE;
112         }
113 
114         free(str);
115     }
116 
117     xen_block_dataplane_start(blockdev->dataplane, ring_ref, nr_ring_ref,
118                               event_channel, protocol, errp);
119 
120     g_free(ring_ref);
121 }
122 
123 static void xen_block_unrealize(XenDevice *xendev, Error **errp)
124 {
125     XenBlockDevice *blockdev = XEN_BLOCK_DEVICE(xendev);
126     XenBlockDeviceClass *blockdev_class =
127         XEN_BLOCK_DEVICE_GET_CLASS(xendev);
128     const char *type = object_get_typename(OBJECT(blockdev));
129     XenBlockVdev *vdev = &blockdev->props.vdev;
130 
131     if (vdev->type == XEN_BLOCK_VDEV_TYPE_INVALID) {
132         return;
133     }
134 
135     trace_xen_block_unrealize(type, vdev->disk, vdev->partition);
136 
137     /* Disconnect from the frontend in case this has not already happened */
138     xen_block_disconnect(xendev, NULL);
139 
140     xen_block_dataplane_destroy(blockdev->dataplane);
141     blockdev->dataplane = NULL;
142 
143     if (blockdev_class->unrealize) {
144         blockdev_class->unrealize(blockdev, errp);
145     }
146 }
147 
148 static void xen_block_set_size(XenBlockDevice *blockdev)
149 {
150     const char *type = object_get_typename(OBJECT(blockdev));
151     XenBlockVdev *vdev = &blockdev->props.vdev;
152     BlockConf *conf = &blockdev->props.conf;
153     int64_t sectors = blk_getlength(conf->blk) / XEN_BLKIF_SECTOR_SIZE;
154     XenDevice *xendev = XEN_DEVICE(blockdev);
155 
156     trace_xen_block_size(type, vdev->disk, vdev->partition, sectors);
157 
158     xen_device_backend_printf(xendev, "sectors", "%"PRIi64, sectors);
159 }
160 
161 static void xen_block_resize_cb(void *opaque)
162 {
163     XenBlockDevice *blockdev = opaque;
164     XenDevice *xendev = XEN_DEVICE(blockdev);
165     enum xenbus_state state = xen_device_backend_get_state(xendev);
166 
167     xen_block_set_size(blockdev);
168 
169     /*
170      * Mimic the behaviour of Linux xen-blkback and re-write the state
171      * to trigger the frontend watch.
172      */
173     xen_device_backend_printf(xendev, "state", "%u", state);
174 }
175 
176 static const BlockDevOps xen_block_dev_ops = {
177     .resize_cb = xen_block_resize_cb,
178 };
179 
180 static void xen_block_realize(XenDevice *xendev, Error **errp)
181 {
182     XenBlockDevice *blockdev = XEN_BLOCK_DEVICE(xendev);
183     XenBlockDeviceClass *blockdev_class =
184         XEN_BLOCK_DEVICE_GET_CLASS(xendev);
185     const char *type = object_get_typename(OBJECT(blockdev));
186     XenBlockVdev *vdev = &blockdev->props.vdev;
187     BlockConf *conf = &blockdev->props.conf;
188     Error *local_err = NULL;
189 
190     if (vdev->type == XEN_BLOCK_VDEV_TYPE_INVALID) {
191         error_setg(errp, "vdev property not set");
192         return;
193     }
194 
195     trace_xen_block_realize(type, vdev->disk, vdev->partition);
196 
197     if (blockdev_class->realize) {
198         blockdev_class->realize(blockdev, &local_err);
199         if (local_err) {
200             error_propagate(errp, local_err);
201             return;
202         }
203     }
204 
205     /*
206      * The blkif protocol does not deal with removable media, so it must
207      * always be present, even for CDRom devices.
208      */
209     assert(conf->blk);
210     if (!blk_is_inserted(conf->blk)) {
211         error_setg(errp, "device needs media, but drive is empty");
212         return;
213     }
214 
215     if (!blkconf_apply_backend_options(conf, blockdev->info & VDISK_READONLY,
216                                        true, errp)) {
217         return;
218     }
219 
220     if (!(blockdev->info & VDISK_CDROM) &&
221         !blkconf_geometry(conf, NULL, 65535, 255, 255, errp)) {
222         return;
223     }
224 
225     blkconf_blocksizes(conf);
226 
227     if (conf->logical_block_size != XEN_BLKIF_SECTOR_SIZE) {
228         error_setg(errp, "logical_block_size != %u not supported",
229                    XEN_BLKIF_SECTOR_SIZE);
230         return;
231     }
232 
233     if (conf->logical_block_size > conf->physical_block_size) {
234         error_setg(
235             errp, "logical_block_size > physical_block_size not supported");
236         return;
237     }
238 
239     blk_set_dev_ops(conf->blk, &xen_block_dev_ops, blockdev);
240     blk_set_guest_block_size(conf->blk, conf->logical_block_size);
241 
242     if (conf->discard_granularity == -1) {
243         conf->discard_granularity = conf->physical_block_size;
244     }
245 
246     if (blk_get_flags(conf->blk) & BDRV_O_UNMAP) {
247         xen_device_backend_printf(xendev, "feature-discard", "%u", 1);
248         xen_device_backend_printf(xendev, "discard-granularity", "%u",
249                                   conf->discard_granularity);
250     }
251 
252     xen_device_backend_printf(xendev, "feature-flush-cache", "%u", 1);
253     xen_device_backend_printf(xendev, "max-ring-page-order", "%u",
254                               blockdev->props.max_ring_page_order);
255     xen_device_backend_printf(xendev, "info", "%u", blockdev->info);
256 
257     xen_device_frontend_printf(xendev, "virtual-device", "%lu",
258                                vdev->number);
259     xen_device_frontend_printf(xendev, "device-type", "%s",
260                                blockdev->device_type);
261 
262     xen_device_backend_printf(xendev, "sector-size", "%u",
263                               XEN_BLKIF_SECTOR_SIZE);
264 
265     xen_block_set_size(blockdev);
266 
267     blockdev->dataplane =
268         xen_block_dataplane_create(xendev, conf, blockdev->props.iothread);
269 }
270 
271 static void xen_block_frontend_changed(XenDevice *xendev,
272                                        enum xenbus_state frontend_state,
273                                        Error **errp)
274 {
275     enum xenbus_state backend_state = xen_device_backend_get_state(xendev);
276     Error *local_err = NULL;
277 
278     switch (frontend_state) {
279     case XenbusStateInitialised:
280     case XenbusStateConnected:
281         if (backend_state == XenbusStateConnected) {
282             break;
283         }
284 
285         xen_block_disconnect(xendev, &local_err);
286         if (local_err) {
287             error_propagate(errp, local_err);
288             break;
289         }
290 
291         xen_block_connect(xendev, &local_err);
292         if (local_err) {
293             error_propagate(errp, local_err);
294             break;
295         }
296 
297         xen_device_backend_set_state(xendev, XenbusStateConnected);
298         break;
299 
300     case XenbusStateClosing:
301         xen_device_backend_set_state(xendev, XenbusStateClosing);
302         break;
303 
304     case XenbusStateClosed:
305         xen_block_disconnect(xendev, &local_err);
306         if (local_err) {
307             error_propagate(errp, local_err);
308             break;
309         }
310 
311         xen_device_backend_set_state(xendev, XenbusStateClosed);
312         break;
313 
314     default:
315         break;
316     }
317 }
318 
319 static char *disk_to_vbd_name(unsigned int disk)
320 {
321     char *name, *prefix = (disk >= 26) ?
322         disk_to_vbd_name((disk / 26) - 1) : g_strdup("");
323 
324     name = g_strdup_printf("%s%c", prefix, 'a' + disk % 26);
325     g_free(prefix);
326 
327     return name;
328 }
329 
330 static void xen_block_get_vdev(Object *obj, Visitor *v, const char *name,
331                                void *opaque, Error **errp)
332 {
333     DeviceState *dev = DEVICE(obj);
334     Property *prop = opaque;
335     XenBlockVdev *vdev = qdev_get_prop_ptr(dev, prop);
336     char *str;
337 
338     switch (vdev->type) {
339     case XEN_BLOCK_VDEV_TYPE_DP:
340         str = g_strdup_printf("d%lup%lu", vdev->disk, vdev->partition);
341         break;
342 
343     case XEN_BLOCK_VDEV_TYPE_XVD:
344     case XEN_BLOCK_VDEV_TYPE_HD:
345     case XEN_BLOCK_VDEV_TYPE_SD: {
346         char *name = disk_to_vbd_name(vdev->disk);
347 
348         str = g_strdup_printf("%s%s%lu",
349                               (vdev->type == XEN_BLOCK_VDEV_TYPE_XVD) ?
350                               "xvd" :
351                               (vdev->type == XEN_BLOCK_VDEV_TYPE_HD) ?
352                               "hd" :
353                               "sd",
354                               name, vdev->partition);
355         g_free(name);
356         break;
357     }
358     default:
359         error_setg(errp, "invalid vdev type");
360         return;
361     }
362 
363     visit_type_str(v, name, &str, errp);
364     g_free(str);
365 }
366 
367 static int vbd_name_to_disk(const char *name, const char **endp,
368                             unsigned long *disk)
369 {
370     unsigned int n = 0;
371 
372     while (*name != '\0') {
373         if (!g_ascii_isalpha(*name) || !g_ascii_islower(*name)) {
374             break;
375         }
376 
377         n *= 26;
378         n += *name++ - 'a' + 1;
379     }
380     *endp = name;
381 
382     if (!n) {
383         return -1;
384     }
385 
386     *disk = n - 1;
387 
388     return 0;
389 }
390 
391 static void xen_block_set_vdev(Object *obj, Visitor *v, const char *name,
392                                void *opaque, Error **errp)
393 {
394     DeviceState *dev = DEVICE(obj);
395     Property *prop = opaque;
396     XenBlockVdev *vdev = qdev_get_prop_ptr(dev, prop);
397     Error *local_err = NULL;
398     char *str, *p;
399     const char *end;
400 
401     if (dev->realized) {
402         qdev_prop_set_after_realize(dev, name, errp);
403         return;
404     }
405 
406     visit_type_str(v, name, &str, &local_err);
407     if (local_err) {
408         error_propagate(errp, local_err);
409         return;
410     }
411 
412     p = strchr(str, 'd');
413     if (!p) {
414         goto invalid;
415     }
416 
417     *p++ = '\0';
418     if (*str == '\0') {
419         vdev->type = XEN_BLOCK_VDEV_TYPE_DP;
420     } else if (strcmp(str, "xv") == 0) {
421         vdev->type = XEN_BLOCK_VDEV_TYPE_XVD;
422     } else if (strcmp(str, "h") == 0) {
423         vdev->type = XEN_BLOCK_VDEV_TYPE_HD;
424     } else if (strcmp(str, "s") == 0) {
425         vdev->type = XEN_BLOCK_VDEV_TYPE_SD;
426     } else {
427         goto invalid;
428     }
429 
430     if (vdev->type == XEN_BLOCK_VDEV_TYPE_DP) {
431         if (qemu_strtoul(p, &end, 10, &vdev->disk)) {
432             goto invalid;
433         }
434 
435         if (*end == 'p') {
436             if (*(++end) == '\0') {
437                 goto invalid;
438             }
439         }
440     } else {
441         if (vbd_name_to_disk(p, &end, &vdev->disk)) {
442             goto invalid;
443         }
444     }
445 
446     if (*end != '\0') {
447         p = (char *)end;
448 
449         if (qemu_strtoul(p, &end, 10, &vdev->partition)) {
450             goto invalid;
451         }
452 
453         if (*end != '\0') {
454             goto invalid;
455         }
456     } else {
457         vdev->partition = 0;
458     }
459 
460     switch (vdev->type) {
461     case XEN_BLOCK_VDEV_TYPE_DP:
462     case XEN_BLOCK_VDEV_TYPE_XVD:
463         if (vdev->disk < (1 << 4) && vdev->partition < (1 << 4)) {
464             vdev->number = (202 << 8) | (vdev->disk << 4) |
465                 vdev->partition;
466         } else if (vdev->disk < (1 << 20) && vdev->partition < (1 << 8)) {
467             vdev->number = (1 << 28) | (vdev->disk << 8) |
468                 vdev->partition;
469         } else {
470             goto invalid;
471         }
472         break;
473 
474     case XEN_BLOCK_VDEV_TYPE_HD:
475         if ((vdev->disk == 0 || vdev->disk == 1) &&
476             vdev->partition < (1 << 6)) {
477             vdev->number = (3 << 8) | (vdev->disk << 6) | vdev->partition;
478         } else if ((vdev->disk == 2 || vdev->disk == 3) &&
479                    vdev->partition < (1 << 6)) {
480             vdev->number = (22 << 8) | ((vdev->disk - 2) << 6) |
481                 vdev->partition;
482         } else {
483             goto invalid;
484         }
485         break;
486 
487     case XEN_BLOCK_VDEV_TYPE_SD:
488         if (vdev->disk < (1 << 4) && vdev->partition < (1 << 4)) {
489             vdev->number = (8 << 8) | (vdev->disk << 4) | vdev->partition;
490         } else {
491             goto invalid;
492         }
493         break;
494 
495     default:
496         goto invalid;
497     }
498 
499     g_free(str);
500     return;
501 
502 invalid:
503     error_setg(errp, "invalid virtual disk specifier");
504 
505     vdev->type = XEN_BLOCK_VDEV_TYPE_INVALID;
506     g_free(str);
507 }
508 
509 /*
510  * This property deals with 'vdev' names adhering to the Xen VBD naming
511  * scheme described in:
512  *
513  * https://xenbits.xen.org/docs/unstable/man/xen-vbd-interface.7.html
514  */
515 const PropertyInfo xen_block_prop_vdev = {
516     .name  = "str",
517     .description = "Virtual Disk specifier: d*p*/xvd*/hd*/sd*",
518     .get = xen_block_get_vdev,
519     .set = xen_block_set_vdev,
520 };
521 
522 static Property xen_block_props[] = {
523     DEFINE_PROP("vdev", XenBlockDevice, props.vdev,
524                 xen_block_prop_vdev, XenBlockVdev),
525     DEFINE_BLOCK_PROPERTIES(XenBlockDevice, props.conf),
526     DEFINE_PROP_UINT32("max-ring-page-order", XenBlockDevice,
527                        props.max_ring_page_order, 4),
528     DEFINE_PROP_LINK("iothread", XenBlockDevice, props.iothread,
529                      TYPE_IOTHREAD, IOThread *),
530     DEFINE_PROP_END_OF_LIST()
531 };
532 
533 static void xen_block_class_init(ObjectClass *class, void *data)
534 {
535     DeviceClass *dev_class = DEVICE_CLASS(class);
536     XenDeviceClass *xendev_class = XEN_DEVICE_CLASS(class);
537 
538     xendev_class->backend = "qdisk";
539     xendev_class->device = "vbd";
540     xendev_class->get_name = xen_block_get_name;
541     xendev_class->realize = xen_block_realize;
542     xendev_class->frontend_changed = xen_block_frontend_changed;
543     xendev_class->unrealize = xen_block_unrealize;
544 
545     dev_class->props = xen_block_props;
546 }
547 
548 static const TypeInfo xen_block_type_info = {
549     .name = TYPE_XEN_BLOCK_DEVICE,
550     .parent = TYPE_XEN_DEVICE,
551     .instance_size = sizeof(XenBlockDevice),
552     .abstract = true,
553     .class_size = sizeof(XenBlockDeviceClass),
554     .class_init = xen_block_class_init,
555 };
556 
557 static void xen_disk_unrealize(XenBlockDevice *blockdev, Error **errp)
558 {
559     trace_xen_disk_unrealize();
560 }
561 
562 static void xen_disk_realize(XenBlockDevice *blockdev, Error **errp)
563 {
564     BlockConf *conf = &blockdev->props.conf;
565 
566     trace_xen_disk_realize();
567 
568     blockdev->device_type = "disk";
569 
570     if (!conf->blk) {
571         error_setg(errp, "drive property not set");
572         return;
573     }
574 
575     blockdev->info = blk_is_read_only(conf->blk) ? VDISK_READONLY : 0;
576 }
577 
578 static void xen_disk_class_init(ObjectClass *class, void *data)
579 {
580     DeviceClass *dev_class = DEVICE_CLASS(class);
581     XenBlockDeviceClass *blockdev_class = XEN_BLOCK_DEVICE_CLASS(class);
582 
583     blockdev_class->realize = xen_disk_realize;
584     blockdev_class->unrealize = xen_disk_unrealize;
585 
586     dev_class->desc = "Xen Disk Device";
587 }
588 
589 static const TypeInfo xen_disk_type_info = {
590     .name = TYPE_XEN_DISK_DEVICE,
591     .parent = TYPE_XEN_BLOCK_DEVICE,
592     .instance_size = sizeof(XenDiskDevice),
593     .class_init = xen_disk_class_init,
594 };
595 
596 static void xen_cdrom_unrealize(XenBlockDevice *blockdev, Error **errp)
597 {
598     trace_xen_cdrom_unrealize();
599 }
600 
601 static void xen_cdrom_realize(XenBlockDevice *blockdev, Error **errp)
602 {
603     BlockConf *conf = &blockdev->props.conf;
604 
605     trace_xen_cdrom_realize();
606 
607     blockdev->device_type = "cdrom";
608 
609     if (!conf->blk) {
610         int rc;
611 
612         /* Set up an empty drive */
613         conf->blk = blk_new(qemu_get_aio_context(), 0, BLK_PERM_ALL);
614 
615         rc = blk_attach_dev(conf->blk, DEVICE(blockdev));
616         if (!rc) {
617             error_setg_errno(errp, -rc, "failed to create drive");
618             return;
619         }
620     }
621 
622     blockdev->info = VDISK_READONLY | VDISK_CDROM;
623 }
624 
625 static void xen_cdrom_class_init(ObjectClass *class, void *data)
626 {
627     DeviceClass *dev_class = DEVICE_CLASS(class);
628     XenBlockDeviceClass *blockdev_class = XEN_BLOCK_DEVICE_CLASS(class);
629 
630     blockdev_class->realize = xen_cdrom_realize;
631     blockdev_class->unrealize = xen_cdrom_unrealize;
632 
633     dev_class->desc = "Xen CD-ROM Device";
634 }
635 
636 static const TypeInfo xen_cdrom_type_info = {
637     .name = TYPE_XEN_CDROM_DEVICE,
638     .parent = TYPE_XEN_BLOCK_DEVICE,
639     .instance_size = sizeof(XenCDRomDevice),
640     .class_init = xen_cdrom_class_init,
641 };
642 
643 static void xen_block_register_types(void)
644 {
645     type_register_static(&xen_block_type_info);
646     type_register_static(&xen_disk_type_info);
647     type_register_static(&xen_cdrom_type_info);
648 }
649 
650 type_init(xen_block_register_types)
651 
652 static void xen_block_blockdev_del(const char *node_name, Error **errp)
653 {
654     trace_xen_block_blockdev_del(node_name);
655 
656     qmp_blockdev_del(node_name, errp);
657 }
658 
659 static char *xen_block_blockdev_add(const char *id, QDict *qdict,
660                                     Error **errp)
661 {
662     const char *driver = qdict_get_try_str(qdict, "driver");
663     BlockdevOptions *options = NULL;
664     Error *local_err = NULL;
665     char *node_name;
666     Visitor *v;
667 
668     if (!driver) {
669         error_setg(errp, "no 'driver' parameter");
670         return NULL;
671     }
672 
673     node_name = g_strdup_printf("%s-%s", id, driver);
674     qdict_put_str(qdict, "node-name", node_name);
675 
676     trace_xen_block_blockdev_add(node_name);
677 
678     v = qobject_input_visitor_new(QOBJECT(qdict));
679     visit_type_BlockdevOptions(v, NULL, &options, &local_err);
680     visit_free(v);
681 
682     if (local_err) {
683         error_propagate(errp, local_err);
684         goto fail;
685     }
686 
687     qmp_blockdev_add(options, &local_err);
688 
689     if (local_err) {
690         error_propagate(errp, local_err);
691         goto fail;
692     }
693 
694     qapi_free_BlockdevOptions(options);
695 
696     return node_name;
697 
698 fail:
699     if (options) {
700         qapi_free_BlockdevOptions(options);
701     }
702     g_free(node_name);
703 
704     return NULL;
705 }
706 
707 static void xen_block_drive_destroy(XenBlockDrive *drive, Error **errp)
708 {
709     char *node_name = drive->node_name;
710 
711     if (node_name) {
712         Error *local_err = NULL;
713 
714         xen_block_blockdev_del(node_name, &local_err);
715         if (local_err) {
716             error_propagate(errp, local_err);
717             return;
718         }
719         g_free(node_name);
720         drive->node_name = NULL;
721     }
722     g_free(drive->id);
723     g_free(drive);
724 }
725 
726 static XenBlockDrive *xen_block_drive_create(const char *id,
727                                              const char *device_type,
728                                              QDict *opts, Error **errp)
729 {
730     const char *params = qdict_get_try_str(opts, "params");
731     const char *mode = qdict_get_try_str(opts, "mode");
732     const char *direct_io_safe = qdict_get_try_str(opts, "direct-io-safe");
733     const char *discard_enable = qdict_get_try_str(opts, "discard-enable");
734     char *driver = NULL;
735     char *filename = NULL;
736     XenBlockDrive *drive = NULL;
737     Error *local_err = NULL;
738     QDict *file_layer;
739     QDict *driver_layer;
740 
741     if (params) {
742         char **v = g_strsplit(params, ":", 2);
743 
744         if (v[1] == NULL) {
745             filename = g_strdup(v[0]);
746             driver = g_strdup("raw");
747         } else {
748             if (strcmp(v[0], "aio") == 0) {
749                 driver = g_strdup("raw");
750             } else if (strcmp(v[0], "vhd") == 0) {
751                 driver = g_strdup("vpc");
752             } else {
753                 driver = g_strdup(v[0]);
754             }
755             filename = g_strdup(v[1]);
756         }
757 
758         g_strfreev(v);
759     } else {
760         error_setg(errp, "no params");
761         goto done;
762     }
763 
764     assert(filename);
765     assert(driver);
766 
767     drive = g_new0(XenBlockDrive, 1);
768     drive->id = g_strdup(id);
769 
770     file_layer = qdict_new();
771     driver_layer = qdict_new();
772 
773     qdict_put_str(file_layer, "driver", "file");
774     qdict_put_str(file_layer, "filename", filename);
775     g_free(filename);
776 
777     if (mode && *mode != 'w') {
778         qdict_put_bool(file_layer, "read-only", true);
779     }
780 
781     if (direct_io_safe) {
782         unsigned long value;
783 
784         if (!qemu_strtoul(direct_io_safe, NULL, 2, &value) && !!value) {
785             QDict *cache_qdict = qdict_new();
786 
787             qdict_put_bool(cache_qdict, "direct", true);
788             qdict_put(file_layer, "cache", cache_qdict);
789 
790             qdict_put_str(file_layer, "aio", "native");
791         }
792     }
793 
794     if (discard_enable) {
795         unsigned long value;
796 
797         if (!qemu_strtoul(discard_enable, NULL, 2, &value) && !!value) {
798             qdict_put_str(file_layer, "discard", "unmap");
799             qdict_put_str(driver_layer, "discard", "unmap");
800         }
801     }
802 
803     /*
804      * It is necessary to turn file locking off as an emulated device
805      * may have already opened the same image file.
806      */
807     qdict_put_str(file_layer, "locking", "off");
808 
809     qdict_put_str(driver_layer, "driver", driver);
810     g_free(driver);
811 
812     qdict_put(driver_layer, "file", file_layer);
813 
814     g_assert(!drive->node_name);
815     drive->node_name = xen_block_blockdev_add(drive->id, driver_layer,
816                                               &local_err);
817 
818     qobject_unref(driver_layer);
819 
820 done:
821     if (local_err) {
822         error_propagate(errp, local_err);
823         xen_block_drive_destroy(drive, NULL);
824         return NULL;
825     }
826 
827     return drive;
828 }
829 
830 static const char *xen_block_drive_get_node_name(XenBlockDrive *drive)
831 {
832     return drive->node_name ? drive->node_name : "";
833 }
834 
835 static void xen_block_iothread_destroy(XenBlockIOThread *iothread,
836                                        Error **errp)
837 {
838     qmp_object_del(iothread->id, errp);
839 
840     g_free(iothread->id);
841     g_free(iothread);
842 }
843 
844 static XenBlockIOThread *xen_block_iothread_create(const char *id,
845                                                    Error **errp)
846 {
847     XenBlockIOThread *iothread = g_new(XenBlockIOThread, 1);
848     Error *local_err = NULL;
849 
850     iothread->id = g_strdup(id);
851 
852     qmp_object_add(TYPE_IOTHREAD, id, false, NULL, &local_err);
853     if (local_err) {
854         error_propagate(errp, local_err);
855 
856         g_free(iothread->id);
857         g_free(iothread);
858         return NULL;
859     }
860 
861     return iothread;
862 }
863 
864 static void xen_block_device_create(XenBackendInstance *backend,
865                                     QDict *opts, Error **errp)
866 {
867     XenBus *xenbus = xen_backend_get_bus(backend);
868     const char *name = xen_backend_get_name(backend);
869     unsigned long number;
870     const char *vdev, *device_type;
871     XenBlockDrive *drive = NULL;
872     XenBlockIOThread *iothread = NULL;
873     XenDevice *xendev = NULL;
874     Error *local_err = NULL;
875     const char *type;
876     XenBlockDevice *blockdev;
877 
878     if (qemu_strtoul(name, NULL, 10, &number)) {
879         error_setg(errp, "failed to parse name '%s'", name);
880         goto fail;
881     }
882 
883     trace_xen_block_device_create(number);
884 
885     vdev = qdict_get_try_str(opts, "dev");
886     if (!vdev) {
887         error_setg(errp, "no dev parameter");
888         goto fail;
889     }
890 
891     device_type = qdict_get_try_str(opts, "device-type");
892     if (!device_type) {
893         error_setg(errp, "no device-type parameter");
894         goto fail;
895     }
896 
897     if (!strcmp(device_type, "disk")) {
898         type = TYPE_XEN_DISK_DEVICE;
899     } else if (!strcmp(device_type, "cdrom")) {
900         type = TYPE_XEN_CDROM_DEVICE;
901     } else {
902         error_setg(errp, "invalid device-type parameter '%s'", device_type);
903         goto fail;
904     }
905 
906     drive = xen_block_drive_create(vdev, device_type, opts, &local_err);
907     if (!drive) {
908         error_propagate_prepend(errp, local_err, "failed to create drive: ");
909         goto fail;
910     }
911 
912     iothread = xen_block_iothread_create(vdev, &local_err);
913     if (local_err) {
914         error_propagate_prepend(errp, local_err,
915                                 "failed to create iothread: ");
916         goto fail;
917     }
918 
919     xendev = XEN_DEVICE(qdev_create(BUS(xenbus), type));
920     blockdev = XEN_BLOCK_DEVICE(xendev);
921 
922     object_property_set_str(OBJECT(xendev), vdev, "vdev", &local_err);
923     if (local_err) {
924         error_propagate_prepend(errp, local_err, "failed to set 'vdev': ");
925         goto fail;
926     }
927 
928     object_property_set_str(OBJECT(xendev),
929                             xen_block_drive_get_node_name(drive), "drive",
930                             &local_err);
931     if (local_err) {
932         error_propagate_prepend(errp, local_err, "failed to set 'drive': ");
933         goto fail;
934     }
935 
936     object_property_set_str(OBJECT(xendev), iothread->id, "iothread",
937                             &local_err);
938     if (local_err) {
939         error_propagate_prepend(errp, local_err,
940                                 "failed to set 'iothread': ");
941         goto fail;
942     }
943 
944     blockdev->iothread = iothread;
945     blockdev->drive = drive;
946 
947     object_property_set_bool(OBJECT(xendev), true, "realized", &local_err);
948     if (local_err) {
949         error_propagate_prepend(errp, local_err,
950                                 "realization of device %s failed: ",
951                                 type);
952         goto fail;
953     }
954 
955     xen_backend_set_device(backend, xendev);
956     return;
957 
958 fail:
959     if (xendev) {
960         object_unparent(OBJECT(xendev));
961     }
962 
963     if (iothread) {
964         xen_block_iothread_destroy(iothread, NULL);
965     }
966 
967     if (drive) {
968         xen_block_drive_destroy(drive, NULL);
969     }
970 }
971 
972 static void xen_block_device_destroy(XenBackendInstance *backend,
973                                      Error **errp)
974 {
975     XenDevice *xendev = xen_backend_get_device(backend);
976     XenBlockDevice *blockdev = XEN_BLOCK_DEVICE(xendev);
977     XenBlockVdev *vdev = &blockdev->props.vdev;
978     XenBlockDrive *drive = blockdev->drive;
979     XenBlockIOThread *iothread = blockdev->iothread;
980 
981     trace_xen_block_device_destroy(vdev->number);
982 
983     object_unparent(OBJECT(xendev));
984 
985     if (iothread) {
986         Error *local_err = NULL;
987 
988         xen_block_iothread_destroy(iothread, &local_err);
989         if (local_err) {
990             error_propagate_prepend(errp, local_err,
991                                 "failed to destroy iothread: ");
992             return;
993         }
994     }
995 
996     if (drive) {
997         Error *local_err = NULL;
998 
999         xen_block_drive_destroy(drive, &local_err);
1000         if (local_err) {
1001             error_propagate_prepend(errp, local_err,
1002                                 "failed to destroy drive: ");
1003         }
1004     }
1005 }
1006 
1007 static const XenBackendInfo xen_block_backend_info = {
1008     .type = "qdisk",
1009     .create = xen_block_device_create,
1010     .destroy = xen_block_device_destroy,
1011 };
1012 
1013 static void xen_block_register_backend(void)
1014 {
1015     xen_backend_register(&xen_block_backend_info);
1016 }
1017 
1018 xen_backend_init(xen_block_register_backend);
1019