qemu

FORK: QEMU emulator
git clone https://git.neptards.moe/neptards/qemu.git
Log | Files | Refs | Submodules | LICENSE

qapi-sysemu.c (17307B)


      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 
     41 static 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  */
     75 static 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 
    119 void 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 
    140 void 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 
    176 static 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 
    229 out:
    230     aio_context_release(aio_context);
    231 }
    232 
    233 void qmp_blockdev_remove_medium(const char *id, Error **errp)
    234 {
    235     blockdev_remove_medium(false, NULL, true, id, errp);
    236 }
    237 
    238 static 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 
    283 static 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 
    311 void 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 
    317 void 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 
    406 fail:
    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 
    413 void 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 */
    437 void 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 
    533 out:
    534     aio_context_release(aio_context);
    535 }
    536 
    537 void 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 }