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