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