xref: /openbmc/qemu/block/qapi-sysemu.c (revision d2dfe0b5)
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 
42 static 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  */
76 static 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 
120 void 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 
139 void 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 
172 static 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 
222 out:
223     aio_context_release(aio_context);
224 }
225 
226 void qmp_blockdev_remove_medium(const char *id, Error **errp)
227 {
228     blockdev_remove_medium(NULL, id, errp);
229 }
230 
231 static 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 
276 static 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 
301 void 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 
307 void 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 
395 fail:
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 
402 void 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 */
423 void 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 
514 out:
515     aio_context_release(aio_context);
516 }
517 
518 void 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