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_force, bool force,
 322                                bool has_read_only,
 323                                BlockdevChangeReadOnlyMode read_only,
 324                                Error **errp)
 325{
 326    BlockBackend *blk;
 327    BlockDriverState *medium_bs = NULL;
 328    int bdrv_flags;
 329    bool detect_zeroes;
 330    int rc;
 331    QDict *options = NULL;
 332    Error *err = NULL;
 333
 334    blk = qmp_get_blk(has_device ? device : NULL,
 335                      has_id ? id : NULL,
 336                      errp);
 337    if (!blk) {
 338        goto fail;
 339    }
 340
 341    if (blk_bs(blk)) {
 342        blk_update_root_state(blk);
 343    }
 344
 345    bdrv_flags = blk_get_open_flags_from_root_state(blk);
 346    bdrv_flags &= ~(BDRV_O_TEMPORARY | BDRV_O_SNAPSHOT | BDRV_O_NO_BACKING |
 347        BDRV_O_PROTOCOL | BDRV_O_AUTO_RDONLY);
 348
 349    if (!has_read_only) {
 350        read_only = BLOCKDEV_CHANGE_READ_ONLY_MODE_RETAIN;
 351    }
 352
 353    switch (read_only) {
 354    case BLOCKDEV_CHANGE_READ_ONLY_MODE_RETAIN:
 355        break;
 356
 357    case BLOCKDEV_CHANGE_READ_ONLY_MODE_READ_ONLY:
 358        bdrv_flags &= ~BDRV_O_RDWR;
 359        break;
 360
 361    case BLOCKDEV_CHANGE_READ_ONLY_MODE_READ_WRITE:
 362        bdrv_flags |= BDRV_O_RDWR;
 363        break;
 364
 365    default:
 366        abort();
 367    }
 368
 369    options = qdict_new();
 370    detect_zeroes = blk_get_detect_zeroes_from_root_state(blk);
 371    qdict_put_str(options, "detect-zeroes", detect_zeroes ? "on" : "off");
 372
 373    if (has_format) {
 374        qdict_put_str(options, "driver", format);
 375    }
 376
 377    medium_bs = bdrv_open(filename, NULL, options, bdrv_flags, errp);
 378    if (!medium_bs) {
 379        goto fail;
 380    }
 381
 382    rc = do_open_tray(has_device ? device : NULL,
 383                      has_id ? id : NULL,
 384                      force, &err);
 385    if (rc && rc != -ENOSYS) {
 386        error_propagate(errp, err);
 387        goto fail;
 388    }
 389    error_free(err);
 390    err = NULL;
 391
 392    blockdev_remove_medium(has_device, device, has_id, id, &err);
 393    if (err) {
 394        error_propagate(errp, err);
 395        goto fail;
 396    }
 397
 398    qmp_blockdev_insert_anon_medium(blk, medium_bs, &err);
 399    if (err) {
 400        error_propagate(errp, err);
 401        goto fail;
 402    }
 403
 404    qmp_blockdev_close_tray(has_device, device, has_id, id, errp);
 405
 406fail:
 407    /* If the medium has been inserted, the device has its own reference, so
 408     * ours must be relinquished; and if it has not been inserted successfully,
 409     * the reference must be relinquished anyway */
 410    bdrv_unref(medium_bs);
 411}
 412
 413void qmp_eject(bool has_device, const char *device,
 414               bool has_id, const char *id,
 415               bool has_force, bool force, Error **errp)
 416{
 417    Error *local_err = NULL;
 418    int rc;
 419
 420    if (!has_force) {
 421        force = false;
 422    }
 423
 424    rc = do_open_tray(has_device ? device : NULL,
 425                      has_id ? id : NULL,
 426                      force, &local_err);
 427    if (rc && rc != -ENOSYS) {
 428        error_propagate(errp, local_err);
 429        return;
 430    }
 431    error_free(local_err);
 432
 433    blockdev_remove_medium(has_device, device, has_id, id, errp);
 434}
 435
 436/* throttling disk I/O limits */
 437void qmp_block_set_io_throttle(BlockIOThrottle *arg, Error **errp)
 438{
 439    ThrottleConfig cfg;
 440    BlockDriverState *bs;
 441    BlockBackend *blk;
 442    AioContext *aio_context;
 443
 444    blk = qmp_get_blk(arg->has_device ? arg->device : NULL,
 445                      arg->has_id ? arg->id : NULL,
 446                      errp);
 447    if (!blk) {
 448        return;
 449    }
 450
 451    aio_context = blk_get_aio_context(blk);
 452    aio_context_acquire(aio_context);
 453
 454    bs = blk_bs(blk);
 455    if (!bs) {
 456        error_setg(errp, "Device has no medium");
 457        goto out;
 458    }
 459
 460    throttle_config_init(&cfg);
 461    cfg.buckets[THROTTLE_BPS_TOTAL].avg = arg->bps;
 462    cfg.buckets[THROTTLE_BPS_READ].avg  = arg->bps_rd;
 463    cfg.buckets[THROTTLE_BPS_WRITE].avg = arg->bps_wr;
 464
 465    cfg.buckets[THROTTLE_OPS_TOTAL].avg = arg->iops;
 466    cfg.buckets[THROTTLE_OPS_READ].avg  = arg->iops_rd;
 467    cfg.buckets[THROTTLE_OPS_WRITE].avg = arg->iops_wr;
 468
 469    if (arg->has_bps_max) {
 470        cfg.buckets[THROTTLE_BPS_TOTAL].max = arg->bps_max;
 471    }
 472    if (arg->has_bps_rd_max) {
 473        cfg.buckets[THROTTLE_BPS_READ].max = arg->bps_rd_max;
 474    }
 475    if (arg->has_bps_wr_max) {
 476        cfg.buckets[THROTTLE_BPS_WRITE].max = arg->bps_wr_max;
 477    }
 478    if (arg->has_iops_max) {
 479        cfg.buckets[THROTTLE_OPS_TOTAL].max = arg->iops_max;
 480    }
 481    if (arg->has_iops_rd_max) {
 482        cfg.buckets[THROTTLE_OPS_READ].max = arg->iops_rd_max;
 483    }
 484    if (arg->has_iops_wr_max) {
 485        cfg.buckets[THROTTLE_OPS_WRITE].max = arg->iops_wr_max;
 486    }
 487
 488    if (arg->has_bps_max_length) {
 489        cfg.buckets[THROTTLE_BPS_TOTAL].burst_length = arg->bps_max_length;
 490    }
 491    if (arg->has_bps_rd_max_length) {
 492        cfg.buckets[THROTTLE_BPS_READ].burst_length = arg->bps_rd_max_length;
 493    }
 494    if (arg->has_bps_wr_max_length) {
 495        cfg.buckets[THROTTLE_BPS_WRITE].burst_length = arg->bps_wr_max_length;
 496    }
 497    if (arg->has_iops_max_length) {
 498        cfg.buckets[THROTTLE_OPS_TOTAL].burst_length = arg->iops_max_length;
 499    }
 500    if (arg->has_iops_rd_max_length) {
 501        cfg.buckets[THROTTLE_OPS_READ].burst_length = arg->iops_rd_max_length;
 502    }
 503    if (arg->has_iops_wr_max_length) {
 504        cfg.buckets[THROTTLE_OPS_WRITE].burst_length = arg->iops_wr_max_length;
 505    }
 506
 507    if (arg->has_iops_size) {
 508        cfg.op_size = arg->iops_size;
 509    }
 510
 511    if (!throttle_is_valid(&cfg, errp)) {
 512        goto out;
 513    }
 514
 515    if (throttle_enabled(&cfg)) {
 516        /* Enable I/O limits if they're not enabled yet, otherwise
 517         * just update the throttling group. */
 518        if (!blk_get_public(blk)->throttle_group_member.throttle_state) {
 519            blk_io_limits_enable(blk,
 520                                 arg->has_group ? arg->group :
 521                                 arg->has_device ? arg->device :
 522                                 arg->id);
 523        } else if (arg->has_group) {
 524            blk_io_limits_update_group(blk, arg->group);
 525        }
 526        /* Set the new throttling configuration */
 527        blk_set_io_limits(blk, &cfg);
 528    } else if (blk_get_public(blk)->throttle_group_member.throttle_state) {
 529        /* If all throttling settings are set to 0, disable I/O limits */
 530        blk_io_limits_disable(blk);
 531    }
 532
 533out:
 534    aio_context_release(aio_context);
 535}
 536
 537void qmp_block_latency_histogram_set(
 538    const char *id,
 539    bool has_boundaries, uint64List *boundaries,
 540    bool has_boundaries_read, uint64List *boundaries_read,
 541    bool has_boundaries_write, uint64List *boundaries_write,
 542    bool has_boundaries_flush, uint64List *boundaries_flush,
 543    Error **errp)
 544{
 545    BlockBackend *blk = qmp_get_blk(NULL, id, errp);
 546    BlockAcctStats *stats;
 547    int ret;
 548
 549    if (!blk) {
 550        return;
 551    }
 552
 553    stats = blk_get_stats(blk);
 554
 555    if (!has_boundaries && !has_boundaries_read && !has_boundaries_write &&
 556        !has_boundaries_flush)
 557    {
 558        block_latency_histograms_clear(stats);
 559        return;
 560    }
 561
 562    if (has_boundaries || has_boundaries_read) {
 563        ret = block_latency_histogram_set(
 564            stats, BLOCK_ACCT_READ,
 565            has_boundaries_read ? boundaries_read : boundaries);
 566        if (ret) {
 567            error_setg(errp, "Device '%s' set read boundaries fail", id);
 568            return;
 569        }
 570    }
 571
 572    if (has_boundaries || has_boundaries_write) {
 573        ret = block_latency_histogram_set(
 574            stats, BLOCK_ACCT_WRITE,
 575            has_boundaries_write ? boundaries_write : boundaries);
 576        if (ret) {
 577            error_setg(errp, "Device '%s' set write boundaries fail", id);
 578            return;
 579        }
 580    }
 581
 582    if (has_boundaries || has_boundaries_flush) {
 583        ret = block_latency_histogram_set(
 584            stats, BLOCK_ACCT_FLUSH,
 585            has_boundaries_flush ? boundaries_flush : boundaries);
 586        if (ret) {
 587            error_setg(errp, "Device '%s' set flush boundaries fail", id);
 588            return;
 589        }
 590    }
 591}
 592