qemu/block/qapi-sysemu.c
<<
>>
Prefs
   1/*
   2 * QMP command handlers specific to the system emulators
   3 *
   4 * Copyright (c) 2003-2008 Fabrice Bellard
   5 *
   6 * This work is licensed under the terms of the GNU GPL, version 2 or
   7 * later.  See the COPYING file in the top-level directory.
   8 *
   9 * This file incorporates work covered by the following copyright and
  10 * permission notice:
  11 *
  12 * Copyright (c) 2003-2008 Fabrice Bellard
  13 *
  14 * Permission is hereby granted, free of charge, to any person obtaining a copy
  15 * of this software and associated documentation files (the "Software"), to deal
  16 * in the Software without restriction, including without limitation the rights
  17 * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
  18 * copies of the Software, and to permit persons to whom the Software is
  19 * furnished to do so, subject to the following conditions:
  20 *
  21 * The above copyright notice and this permission notice shall be included in
  22 * all copies or substantial portions of the Software.
  23 *
  24 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
  25 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
  26 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
  27 * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
  28 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
  29 * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
  30 * THE SOFTWARE.
  31 */
  32
  33#include "qemu/osdep.h"
  34
  35#include "qapi/error.h"
  36#include "qapi/qapi-commands-block.h"
  37#include "qapi/qmp/qdict.h"
  38#include "sysemu/block-backend.h"
  39#include "sysemu/blockdev.h"
  40
  41static BlockBackend *qmp_get_blk(const char *blk_name, const char *qdev_id,
  42                                 Error **errp)
  43{
  44    BlockBackend *blk;
  45
  46    if (!blk_name == !qdev_id) {
  47        error_setg(errp, "Need exactly one of 'device' and 'id'");
  48        return NULL;
  49    }
  50
  51    if (qdev_id) {
  52        blk = blk_by_qdev_id(qdev_id, errp);
  53    } else {
  54        blk = blk_by_name(blk_name);
  55        if (blk == NULL) {
  56            error_set(errp, ERROR_CLASS_DEVICE_NOT_FOUND,
  57                      "Device '%s' not found", blk_name);
  58        }
  59    }
  60
  61    return blk;
  62}
  63
  64/*
  65 * Attempt to open the tray of @device.
  66 * If @force, ignore its tray lock.
  67 * Else, if the tray is locked, don't open it, but ask the guest to open it.
  68 * On error, store an error through @errp and return -errno.
  69 * If @device does not exist, return -ENODEV.
  70 * If it has no removable media, return -ENOTSUP.
  71 * If it has no tray, return -ENOSYS.
  72 * If the guest was asked to open the tray, return -EINPROGRESS.
  73 * Else, return 0.
  74 */
  75static int do_open_tray(const char *blk_name, const char *qdev_id,
  76                        bool force, Error **errp)
  77{
  78    BlockBackend *blk;
  79    const char *device = qdev_id ?: blk_name;
  80    bool locked;
  81
  82    blk = qmp_get_blk(blk_name, qdev_id, errp);
  83    if (!blk) {
  84        return -ENODEV;
  85    }
  86
  87    if (!blk_dev_has_removable_media(blk)) {
  88        error_setg(errp, "Device '%s' is not removable", device);
  89        return -ENOTSUP;
  90    }
  91
  92    if (!blk_dev_has_tray(blk)) {
  93        error_setg(errp, "Device '%s' does not have a tray", device);
  94        return -ENOSYS;
  95    }
  96
  97    if (blk_dev_is_tray_open(blk)) {
  98        return 0;
  99    }
 100
 101    locked = blk_dev_is_medium_locked(blk);
 102    if (locked) {
 103        blk_dev_eject_request(blk, force);
 104    }
 105
 106    if (!locked || force) {
 107        blk_dev_change_media_cb(blk, false, &error_abort);
 108    }
 109
 110    if (locked && !force) {
 111        error_setg(errp, "Device '%s' is locked and force was not specified, "
 112                   "wait for tray to open and try again", device);
 113        return -EINPROGRESS;
 114    }
 115
 116    return 0;
 117}
 118
 119void qmp_blockdev_open_tray(bool has_device, const char *device,
 120                            bool has_id, const char *id,
 121                            bool has_force, bool force,
 122                            Error **errp)
 123{
 124    Error *local_err = NULL;
 125    int rc;
 126
 127    if (!has_force) {
 128        force = false;
 129    }
 130    rc = do_open_tray(has_device ? device : NULL,
 131                      has_id ? id : NULL,
 132                      force, &local_err);
 133    if (rc && rc != -ENOSYS && rc != -EINPROGRESS) {
 134        error_propagate(errp, local_err);
 135        return;
 136    }
 137    error_free(local_err);
 138}
 139
 140void qmp_blockdev_close_tray(bool has_device, const char *device,
 141                             bool has_id, const char *id,
 142                             Error **errp)
 143{
 144    BlockBackend *blk;
 145    Error *local_err = NULL;
 146
 147    device = has_device ? device : NULL;
 148    id = has_id ? id : NULL;
 149
 150    blk = qmp_get_blk(device, id, errp);
 151    if (!blk) {
 152        return;
 153    }
 154
 155    if (!blk_dev_has_removable_media(blk)) {
 156        error_setg(errp, "Device '%s' is not removable", device ?: id);
 157        return;
 158    }
 159
 160    if (!blk_dev_has_tray(blk)) {
 161        /* Ignore this command on tray-less devices */
 162        return;
 163    }
 164
 165    if (!blk_dev_is_tray_open(blk)) {
 166        return;
 167    }
 168
 169    blk_dev_change_media_cb(blk, true, &local_err);
 170    if (local_err) {
 171        error_propagate(errp, local_err);
 172        return;
 173    }
 174}
 175
 176static void blockdev_remove_medium(bool has_device, const char *device,
 177                                   bool has_id, const char *id, Error **errp)
 178{
 179    BlockBackend *blk;
 180    BlockDriverState *bs;
 181    AioContext *aio_context;
 182    bool has_attached_device;
 183
 184    device = has_device ? device : NULL;
 185    id = has_id ? id : NULL;
 186
 187    blk = qmp_get_blk(device, id, errp);
 188    if (!blk) {
 189        return;
 190    }
 191
 192    /* For BBs without a device, we can exchange the BDS tree at will */
 193    has_attached_device = blk_get_attached_dev(blk);
 194
 195    if (has_attached_device && !blk_dev_has_removable_media(blk)) {
 196        error_setg(errp, "Device '%s' is not removable", device ?: id);
 197        return;
 198    }
 199
 200    if (has_attached_device && blk_dev_has_tray(blk) &&
 201        !blk_dev_is_tray_open(blk))
 202    {
 203        error_setg(errp, "Tray of device '%s' is not open", device ?: id);
 204        return;
 205    }
 206
 207    bs = blk_bs(blk);
 208    if (!bs) {
 209        return;
 210    }
 211
 212    aio_context = bdrv_get_aio_context(bs);
 213    aio_context_acquire(aio_context);
 214
 215    if (bdrv_op_is_blocked(bs, BLOCK_OP_TYPE_EJECT, errp)) {
 216        goto out;
 217    }
 218
 219    blk_remove_bs(blk);
 220
 221    if (!blk_dev_has_tray(blk)) {
 222        /* For tray-less devices, blockdev-open-tray is a no-op (or may not be
 223         * called at all); therefore, the medium needs to be ejected here.
 224         * Do it after blk_remove_bs() so blk_is_inserted(blk) returns the @load
 225         * value passed here (i.e. false). */
 226        blk_dev_change_media_cb(blk, false, &error_abort);
 227    }
 228
 229out:
 230    aio_context_release(aio_context);
 231}
 232
 233void qmp_blockdev_remove_medium(const char *id, Error **errp)
 234{
 235    blockdev_remove_medium(false, NULL, true, id, errp);
 236}
 237
 238static void qmp_blockdev_insert_anon_medium(BlockBackend *blk,
 239                                            BlockDriverState *bs, Error **errp)
 240{
 241    Error *local_err = NULL;
 242    bool has_device;
 243    int ret;
 244
 245    /* For BBs without a device, we can exchange the BDS tree at will */
 246    has_device = blk_get_attached_dev(blk);
 247
 248    if (has_device && !blk_dev_has_removable_media(blk)) {
 249        error_setg(errp, "Device is not removable");
 250        return;
 251    }
 252
 253    if (has_device && blk_dev_has_tray(blk) && !blk_dev_is_tray_open(blk)) {
 254        error_setg(errp, "Tray of the device is not open");
 255        return;
 256    }
 257
 258    if (blk_bs(blk)) {
 259        error_setg(errp, "There already is a medium in the device");
 260        return;
 261    }
 262
 263    ret = blk_insert_bs(blk, bs, errp);
 264    if (ret < 0) {
 265        return;
 266    }
 267
 268    if (!blk_dev_has_tray(blk)) {
 269        /* For tray-less devices, blockdev-close-tray is a no-op (or may not be
 270         * called at all); therefore, the medium needs to be pushed into the
 271         * slot here.
 272         * Do it after blk_insert_bs() so blk_is_inserted(blk) returns the @load
 273         * value passed here (i.e. true). */
 274        blk_dev_change_media_cb(blk, true, &local_err);
 275        if (local_err) {
 276            error_propagate(errp, local_err);
 277            blk_remove_bs(blk);
 278            return;
 279        }
 280    }
 281}
 282
 283static void blockdev_insert_medium(bool has_device, const char *device,
 284                                   bool has_id, const char *id,
 285                                   const char *node_name, Error **errp)
 286{
 287    BlockBackend *blk;
 288    BlockDriverState *bs;
 289
 290    blk = qmp_get_blk(has_device ? device : NULL,
 291                      has_id ? id : NULL,
 292                      errp);
 293    if (!blk) {
 294        return;
 295    }
 296
 297    bs = bdrv_find_node(node_name);
 298    if (!bs) {
 299        error_setg(errp, "Node '%s' not found", node_name);
 300        return;
 301    }
 302
 303    if (bdrv_has_blk(bs)) {
 304        error_setg(errp, "Node '%s' is already in use", node_name);
 305        return;
 306    }
 307
 308    qmp_blockdev_insert_anon_medium(blk, bs, errp);
 309}
 310
 311void qmp_blockdev_insert_medium(const char *id, const char *node_name,
 312                                Error **errp)
 313{
 314    blockdev_insert_medium(false, NULL, true, id, node_name, errp);
 315}
 316
 317void qmp_blockdev_change_medium(bool has_device, const char *device,
 318                                bool has_id, const char *id,
 319                                const char *filename,
 320                                bool has_format, const char *format,
 321                                bool has_read_only,
 322                                BlockdevChangeReadOnlyMode read_only,
 323                                Error **errp)
 324{
 325    BlockBackend *blk;
 326    BlockDriverState *medium_bs = NULL;
 327    int bdrv_flags;
 328    bool detect_zeroes;
 329    int rc;
 330    QDict *options = NULL;
 331    Error *err = NULL;
 332
 333    blk = qmp_get_blk(has_device ? device : NULL,
 334                      has_id ? id : NULL,
 335                      errp);
 336    if (!blk) {
 337        goto fail;
 338    }
 339
 340    if (blk_bs(blk)) {
 341        blk_update_root_state(blk);
 342    }
 343
 344    bdrv_flags = blk_get_open_flags_from_root_state(blk);
 345    bdrv_flags &= ~(BDRV_O_TEMPORARY | BDRV_O_SNAPSHOT | BDRV_O_NO_BACKING |
 346        BDRV_O_PROTOCOL | BDRV_O_AUTO_RDONLY);
 347
 348    if (!has_read_only) {
 349        read_only = BLOCKDEV_CHANGE_READ_ONLY_MODE_RETAIN;
 350    }
 351
 352    switch (read_only) {
 353    case BLOCKDEV_CHANGE_READ_ONLY_MODE_RETAIN:
 354        break;
 355
 356    case BLOCKDEV_CHANGE_READ_ONLY_MODE_READ_ONLY:
 357        bdrv_flags &= ~BDRV_O_RDWR;
 358        break;
 359
 360    case BLOCKDEV_CHANGE_READ_ONLY_MODE_READ_WRITE:
 361        bdrv_flags |= BDRV_O_RDWR;
 362        break;
 363
 364    default:
 365        abort();
 366    }
 367
 368    options = qdict_new();
 369    detect_zeroes = blk_get_detect_zeroes_from_root_state(blk);
 370    qdict_put_str(options, "detect-zeroes", detect_zeroes ? "on" : "off");
 371
 372    if (has_format) {
 373        qdict_put_str(options, "driver", format);
 374    }
 375
 376    medium_bs = bdrv_open(filename, NULL, options, bdrv_flags, errp);
 377    if (!medium_bs) {
 378        goto fail;
 379    }
 380
 381    rc = do_open_tray(has_device ? device : NULL,
 382                      has_id ? id : NULL,
 383                      false, &err);
 384    if (rc && rc != -ENOSYS) {
 385        error_propagate(errp, err);
 386        goto fail;
 387    }
 388    error_free(err);
 389    err = NULL;
 390
 391    blockdev_remove_medium(has_device, device, has_id, id, &err);
 392    if (err) {
 393        error_propagate(errp, err);
 394        goto fail;
 395    }
 396
 397    qmp_blockdev_insert_anon_medium(blk, medium_bs, &err);
 398    if (err) {
 399        error_propagate(errp, err);
 400        goto fail;
 401    }
 402
 403    qmp_blockdev_close_tray(has_device, device, has_id, id, errp);
 404
 405fail:
 406    /* If the medium has been inserted, the device has its own reference, so
 407     * ours must be relinquished; and if it has not been inserted successfully,
 408     * the reference must be relinquished anyway */
 409    bdrv_unref(medium_bs);
 410}
 411
 412void qmp_eject(bool has_device, const char *device,
 413               bool has_id, const char *id,
 414               bool has_force, bool force, Error **errp)
 415{
 416    Error *local_err = NULL;
 417    int rc;
 418
 419    if (!has_force) {
 420        force = false;
 421    }
 422
 423    rc = do_open_tray(has_device ? device : NULL,
 424                      has_id ? id : NULL,
 425                      force, &local_err);
 426    if (rc && rc != -ENOSYS) {
 427        error_propagate(errp, local_err);
 428        return;
 429    }
 430    error_free(local_err);
 431
 432    blockdev_remove_medium(has_device, device, has_id, id, errp);
 433}
 434
 435/* throttling disk I/O limits */
 436void qmp_block_set_io_throttle(BlockIOThrottle *arg, Error **errp)
 437{
 438    ThrottleConfig cfg;
 439    BlockDriverState *bs;
 440    BlockBackend *blk;
 441    AioContext *aio_context;
 442
 443    blk = qmp_get_blk(arg->has_device ? arg->device : NULL,
 444                      arg->has_id ? arg->id : NULL,
 445                      errp);
 446    if (!blk) {
 447        return;
 448    }
 449
 450    aio_context = blk_get_aio_context(blk);
 451    aio_context_acquire(aio_context);
 452
 453    bs = blk_bs(blk);
 454    if (!bs) {
 455        error_setg(errp, "Device has no medium");
 456        goto out;
 457    }
 458
 459    throttle_config_init(&cfg);
 460    cfg.buckets[THROTTLE_BPS_TOTAL].avg = arg->bps;
 461    cfg.buckets[THROTTLE_BPS_READ].avg  = arg->bps_rd;
 462    cfg.buckets[THROTTLE_BPS_WRITE].avg = arg->bps_wr;
 463
 464    cfg.buckets[THROTTLE_OPS_TOTAL].avg = arg->iops;
 465    cfg.buckets[THROTTLE_OPS_READ].avg  = arg->iops_rd;
 466    cfg.buckets[THROTTLE_OPS_WRITE].avg = arg->iops_wr;
 467
 468    if (arg->has_bps_max) {
 469        cfg.buckets[THROTTLE_BPS_TOTAL].max = arg->bps_max;
 470    }
 471    if (arg->has_bps_rd_max) {
 472        cfg.buckets[THROTTLE_BPS_READ].max = arg->bps_rd_max;
 473    }
 474    if (arg->has_bps_wr_max) {
 475        cfg.buckets[THROTTLE_BPS_WRITE].max = arg->bps_wr_max;
 476    }
 477    if (arg->has_iops_max) {
 478        cfg.buckets[THROTTLE_OPS_TOTAL].max = arg->iops_max;
 479    }
 480    if (arg->has_iops_rd_max) {
 481        cfg.buckets[THROTTLE_OPS_READ].max = arg->iops_rd_max;
 482    }
 483    if (arg->has_iops_wr_max) {
 484        cfg.buckets[THROTTLE_OPS_WRITE].max = arg->iops_wr_max;
 485    }
 486
 487    if (arg->has_bps_max_length) {
 488        cfg.buckets[THROTTLE_BPS_TOTAL].burst_length = arg->bps_max_length;
 489    }
 490    if (arg->has_bps_rd_max_length) {
 491        cfg.buckets[THROTTLE_BPS_READ].burst_length = arg->bps_rd_max_length;
 492    }
 493    if (arg->has_bps_wr_max_length) {
 494        cfg.buckets[THROTTLE_BPS_WRITE].burst_length = arg->bps_wr_max_length;
 495    }
 496    if (arg->has_iops_max_length) {
 497        cfg.buckets[THROTTLE_OPS_TOTAL].burst_length = arg->iops_max_length;
 498    }
 499    if (arg->has_iops_rd_max_length) {
 500        cfg.buckets[THROTTLE_OPS_READ].burst_length = arg->iops_rd_max_length;
 501    }
 502    if (arg->has_iops_wr_max_length) {
 503        cfg.buckets[THROTTLE_OPS_WRITE].burst_length = arg->iops_wr_max_length;
 504    }
 505
 506    if (arg->has_iops_size) {
 507        cfg.op_size = arg->iops_size;
 508    }
 509
 510    if (!throttle_is_valid(&cfg, errp)) {
 511        goto out;
 512    }
 513
 514    if (throttle_enabled(&cfg)) {
 515        /* Enable I/O limits if they're not enabled yet, otherwise
 516         * just update the throttling group. */
 517        if (!blk_get_public(blk)->throttle_group_member.throttle_state) {
 518            blk_io_limits_enable(blk,
 519                                 arg->has_group ? arg->group :
 520                                 arg->has_device ? arg->device :
 521                                 arg->id);
 522        } else if (arg->has_group) {
 523            blk_io_limits_update_group(blk, arg->group);
 524        }
 525        /* Set the new throttling configuration */
 526        blk_set_io_limits(blk, &cfg);
 527    } else if (blk_get_public(blk)->throttle_group_member.throttle_state) {
 528        /* If all throttling settings are set to 0, disable I/O limits */
 529        blk_io_limits_disable(blk);
 530    }
 531
 532out:
 533    aio_context_release(aio_context);
 534}
 535
 536void qmp_block_latency_histogram_set(
 537    const char *id,
 538    bool has_boundaries, uint64List *boundaries,
 539    bool has_boundaries_read, uint64List *boundaries_read,
 540    bool has_boundaries_write, uint64List *boundaries_write,
 541    bool has_boundaries_flush, uint64List *boundaries_flush,
 542    Error **errp)
 543{
 544    BlockBackend *blk = qmp_get_blk(NULL, id, errp);
 545    BlockAcctStats *stats;
 546    int ret;
 547
 548    if (!blk) {
 549        return;
 550    }
 551
 552    stats = blk_get_stats(blk);
 553
 554    if (!has_boundaries && !has_boundaries_read && !has_boundaries_write &&
 555        !has_boundaries_flush)
 556    {
 557        block_latency_histograms_clear(stats);
 558        return;
 559    }
 560
 561    if (has_boundaries || has_boundaries_read) {
 562        ret = block_latency_histogram_set(
 563            stats, BLOCK_ACCT_READ,
 564            has_boundaries_read ? boundaries_read : boundaries);
 565        if (ret) {
 566            error_setg(errp, "Device '%s' set read boundaries fail", id);
 567            return;
 568        }
 569    }
 570
 571    if (has_boundaries || has_boundaries_write) {
 572        ret = block_latency_histogram_set(
 573            stats, BLOCK_ACCT_WRITE,
 574            has_boundaries_write ? boundaries_write : boundaries);
 575        if (ret) {
 576            error_setg(errp, "Device '%s' set write boundaries fail", id);
 577            return;
 578        }
 579    }
 580
 581    if (has_boundaries || has_boundaries_flush) {
 582        ret = block_latency_histogram_set(
 583            stats, BLOCK_ACCT_FLUSH,
 584            has_boundaries_flush ? boundaries_flush : boundaries);
 585        if (ret) {
 586            error_setg(errp, "Device '%s' set flush boundaries fail", id);
 587            return;
 588        }
 589    }
 590}
 591