xref: /openbmc/qemu/block/qapi-sysemu.c (revision e0c72452)
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 GRAPH_UNLOCKED
173 blockdev_remove_medium(const char *device, const char *id, Error **errp)
174 {
175     BlockBackend *blk;
176     BlockDriverState *bs;
177     AioContext *aio_context;
178     bool has_attached_device;
179 
180     GLOBAL_STATE_CODE();
181 
182     blk = qmp_get_blk(device, id, errp);
183     if (!blk) {
184         return;
185     }
186 
187     /* For BBs without a device, we can exchange the BDS tree at will */
188     has_attached_device = blk_get_attached_dev(blk);
189 
190     if (has_attached_device && !blk_dev_has_removable_media(blk)) {
191         error_setg(errp, "Device '%s' is not removable", device ?: id);
192         return;
193     }
194 
195     if (has_attached_device && blk_dev_has_tray(blk) &&
196         !blk_dev_is_tray_open(blk))
197     {
198         error_setg(errp, "Tray of device '%s' is not open", device ?: id);
199         return;
200     }
201 
202     bs = blk_bs(blk);
203     if (!bs) {
204         return;
205     }
206 
207     aio_context = bdrv_get_aio_context(bs);
208     aio_context_acquire(aio_context);
209 
210     bdrv_graph_rdlock_main_loop();
211     if (bdrv_op_is_blocked(bs, BLOCK_OP_TYPE_EJECT, errp)) {
212         bdrv_graph_rdunlock_main_loop();
213         goto out;
214     }
215     bdrv_graph_rdunlock_main_loop();
216 
217     blk_remove_bs(blk);
218 
219     if (!blk_dev_has_tray(blk)) {
220         /* For tray-less devices, blockdev-open-tray is a no-op (or may not be
221          * called at all); therefore, the medium needs to be ejected here.
222          * Do it after blk_remove_bs() so blk_is_inserted(blk) returns the @load
223          * value passed here (i.e. false). */
224         blk_dev_change_media_cb(blk, false, &error_abort);
225     }
226 
227 out:
228     aio_context_release(aio_context);
229 }
230 
231 void qmp_blockdev_remove_medium(const char *id, Error **errp)
232 {
233     blockdev_remove_medium(NULL, id, errp);
234 }
235 
236 static void qmp_blockdev_insert_anon_medium(BlockBackend *blk,
237                                             BlockDriverState *bs, Error **errp)
238 {
239     Error *local_err = NULL;
240     bool has_device;
241     int ret;
242 
243     /* For BBs without a device, we can exchange the BDS tree at will */
244     has_device = blk_get_attached_dev(blk);
245 
246     if (has_device && !blk_dev_has_removable_media(blk)) {
247         error_setg(errp, "Device is not removable");
248         return;
249     }
250 
251     if (has_device && blk_dev_has_tray(blk) && !blk_dev_is_tray_open(blk)) {
252         error_setg(errp, "Tray of the device is not open");
253         return;
254     }
255 
256     if (blk_bs(blk)) {
257         error_setg(errp, "There already is a medium in the device");
258         return;
259     }
260 
261     ret = blk_insert_bs(blk, bs, errp);
262     if (ret < 0) {
263         return;
264     }
265 
266     if (!blk_dev_has_tray(blk)) {
267         /* For tray-less devices, blockdev-close-tray is a no-op (or may not be
268          * called at all); therefore, the medium needs to be pushed into the
269          * slot here.
270          * Do it after blk_insert_bs() so blk_is_inserted(blk) returns the @load
271          * value passed here (i.e. true). */
272         blk_dev_change_media_cb(blk, true, &local_err);
273         if (local_err) {
274             error_propagate(errp, local_err);
275             blk_remove_bs(blk);
276             return;
277         }
278     }
279 }
280 
281 static void blockdev_insert_medium(const char *device, const char *id,
282                                    const char *node_name, Error **errp)
283 {
284     BlockBackend *blk;
285     BlockDriverState *bs;
286 
287     GRAPH_RDLOCK_GUARD_MAINLOOP();
288 
289     blk = qmp_get_blk(device, id, errp);
290     if (!blk) {
291         return;
292     }
293 
294     bs = bdrv_find_node(node_name);
295     if (!bs) {
296         error_setg(errp, "Node '%s' not found", node_name);
297         return;
298     }
299 
300     if (bdrv_has_blk(bs)) {
301         error_setg(errp, "Node '%s' is already in use", node_name);
302         return;
303     }
304 
305     qmp_blockdev_insert_anon_medium(blk, bs, errp);
306 }
307 
308 void qmp_blockdev_insert_medium(const char *id, const char *node_name,
309                                 Error **errp)
310 {
311     blockdev_insert_medium(NULL, id, node_name, errp);
312 }
313 
314 void qmp_blockdev_change_medium(const char *device,
315                                 const char *id,
316                                 const char *filename,
317                                 const char *format,
318                                 bool has_force, bool force,
319                                 bool has_read_only,
320                                 BlockdevChangeReadOnlyMode read_only,
321                                 Error **errp)
322 {
323     BlockBackend *blk;
324     BlockDriverState *medium_bs = NULL;
325     int bdrv_flags;
326     bool detect_zeroes;
327     int rc;
328     QDict *options = NULL;
329     Error *err = NULL;
330 
331     blk = qmp_get_blk(device, id, errp);
332     if (!blk) {
333         goto fail;
334     }
335 
336     if (blk_bs(blk)) {
337         blk_update_root_state(blk);
338     }
339 
340     bdrv_flags = blk_get_open_flags_from_root_state(blk);
341     bdrv_flags &= ~(BDRV_O_TEMPORARY | BDRV_O_SNAPSHOT | BDRV_O_NO_BACKING |
342         BDRV_O_PROTOCOL | BDRV_O_AUTO_RDONLY);
343 
344     if (!has_read_only) {
345         read_only = BLOCKDEV_CHANGE_READ_ONLY_MODE_RETAIN;
346     }
347 
348     switch (read_only) {
349     case BLOCKDEV_CHANGE_READ_ONLY_MODE_RETAIN:
350         break;
351 
352     case BLOCKDEV_CHANGE_READ_ONLY_MODE_READ_ONLY:
353         bdrv_flags &= ~BDRV_O_RDWR;
354         break;
355 
356     case BLOCKDEV_CHANGE_READ_ONLY_MODE_READ_WRITE:
357         bdrv_flags |= BDRV_O_RDWR;
358         break;
359 
360     default:
361         abort();
362     }
363 
364     options = qdict_new();
365     detect_zeroes = blk_get_detect_zeroes_from_root_state(blk);
366     qdict_put_str(options, "detect-zeroes", detect_zeroes ? "on" : "off");
367 
368     if (format) {
369         qdict_put_str(options, "driver", format);
370     }
371 
372     aio_context_acquire(qemu_get_aio_context());
373     medium_bs = bdrv_open(filename, NULL, options, bdrv_flags, errp);
374     aio_context_release(qemu_get_aio_context());
375 
376     if (!medium_bs) {
377         goto fail;
378     }
379 
380     rc = do_open_tray(device, id, force, &err);
381     if (rc && rc != -ENOSYS) {
382         error_propagate(errp, err);
383         goto fail;
384     }
385     error_free(err);
386     err = NULL;
387 
388     blockdev_remove_medium(device, id, &err);
389     if (err) {
390         error_propagate(errp, err);
391         goto fail;
392     }
393 
394     qmp_blockdev_insert_anon_medium(blk, medium_bs, &err);
395     if (err) {
396         error_propagate(errp, err);
397         goto fail;
398     }
399 
400     qmp_blockdev_close_tray(device, id, errp);
401 
402 fail:
403     /* If the medium has been inserted, the device has its own reference, so
404      * ours must be relinquished; and if it has not been inserted successfully,
405      * the reference must be relinquished anyway */
406     bdrv_unref(medium_bs);
407 }
408 
409 void qmp_eject(const char *device, const char *id,
410                bool has_force, bool force, Error **errp)
411 {
412     Error *local_err = NULL;
413     int rc;
414 
415     if (!has_force) {
416         force = false;
417     }
418 
419     rc = do_open_tray(device, id, force, &local_err);
420     if (rc && rc != -ENOSYS) {
421         error_propagate(errp, local_err);
422         return;
423     }
424     error_free(local_err);
425 
426     blockdev_remove_medium(device, id, errp);
427 }
428 
429 /* throttling disk I/O limits */
430 void qmp_block_set_io_throttle(BlockIOThrottle *arg, Error **errp)
431 {
432     ThrottleConfig cfg;
433     BlockDriverState *bs;
434     BlockBackend *blk;
435     AioContext *aio_context;
436 
437     blk = qmp_get_blk(arg->device, arg->id, errp);
438     if (!blk) {
439         return;
440     }
441 
442     aio_context = blk_get_aio_context(blk);
443     aio_context_acquire(aio_context);
444 
445     bs = blk_bs(blk);
446     if (!bs) {
447         error_setg(errp, "Device has no medium");
448         goto out;
449     }
450 
451     throttle_config_init(&cfg);
452     cfg.buckets[THROTTLE_BPS_TOTAL].avg = arg->bps;
453     cfg.buckets[THROTTLE_BPS_READ].avg  = arg->bps_rd;
454     cfg.buckets[THROTTLE_BPS_WRITE].avg = arg->bps_wr;
455 
456     cfg.buckets[THROTTLE_OPS_TOTAL].avg = arg->iops;
457     cfg.buckets[THROTTLE_OPS_READ].avg  = arg->iops_rd;
458     cfg.buckets[THROTTLE_OPS_WRITE].avg = arg->iops_wr;
459 
460     if (arg->has_bps_max) {
461         cfg.buckets[THROTTLE_BPS_TOTAL].max = arg->bps_max;
462     }
463     if (arg->has_bps_rd_max) {
464         cfg.buckets[THROTTLE_BPS_READ].max = arg->bps_rd_max;
465     }
466     if (arg->has_bps_wr_max) {
467         cfg.buckets[THROTTLE_BPS_WRITE].max = arg->bps_wr_max;
468     }
469     if (arg->has_iops_max) {
470         cfg.buckets[THROTTLE_OPS_TOTAL].max = arg->iops_max;
471     }
472     if (arg->has_iops_rd_max) {
473         cfg.buckets[THROTTLE_OPS_READ].max = arg->iops_rd_max;
474     }
475     if (arg->has_iops_wr_max) {
476         cfg.buckets[THROTTLE_OPS_WRITE].max = arg->iops_wr_max;
477     }
478 
479     if (arg->has_bps_max_length) {
480         cfg.buckets[THROTTLE_BPS_TOTAL].burst_length = arg->bps_max_length;
481     }
482     if (arg->has_bps_rd_max_length) {
483         cfg.buckets[THROTTLE_BPS_READ].burst_length = arg->bps_rd_max_length;
484     }
485     if (arg->has_bps_wr_max_length) {
486         cfg.buckets[THROTTLE_BPS_WRITE].burst_length = arg->bps_wr_max_length;
487     }
488     if (arg->has_iops_max_length) {
489         cfg.buckets[THROTTLE_OPS_TOTAL].burst_length = arg->iops_max_length;
490     }
491     if (arg->has_iops_rd_max_length) {
492         cfg.buckets[THROTTLE_OPS_READ].burst_length = arg->iops_rd_max_length;
493     }
494     if (arg->has_iops_wr_max_length) {
495         cfg.buckets[THROTTLE_OPS_WRITE].burst_length = arg->iops_wr_max_length;
496     }
497 
498     if (arg->has_iops_size) {
499         cfg.op_size = arg->iops_size;
500     }
501 
502     if (!throttle_is_valid(&cfg, errp)) {
503         goto out;
504     }
505 
506     if (throttle_enabled(&cfg)) {
507         /* Enable I/O limits if they're not enabled yet, otherwise
508          * just update the throttling group. */
509         if (!blk_get_public(blk)->throttle_group_member.throttle_state) {
510             blk_io_limits_enable(blk, arg->group ?: arg->device ?: arg->id);
511         } else if (arg->group) {
512             blk_io_limits_update_group(blk, arg->group);
513         }
514         /* Set the new throttling configuration */
515         blk_set_io_limits(blk, &cfg);
516     } else if (blk_get_public(blk)->throttle_group_member.throttle_state) {
517         /* If all throttling settings are set to 0, disable I/O limits */
518         blk_io_limits_disable(blk);
519     }
520 
521 out:
522     aio_context_release(aio_context);
523 }
524 
525 void qmp_block_latency_histogram_set(
526     const char *id,
527     bool has_boundaries, uint64List *boundaries,
528     bool has_boundaries_read, uint64List *boundaries_read,
529     bool has_boundaries_write, uint64List *boundaries_write,
530     bool has_boundaries_append, uint64List *boundaries_append,
531     bool has_boundaries_flush, uint64List *boundaries_flush,
532     Error **errp)
533 {
534     BlockBackend *blk = qmp_get_blk(NULL, id, errp);
535     BlockAcctStats *stats;
536     int ret;
537 
538     if (!blk) {
539         return;
540     }
541 
542     stats = blk_get_stats(blk);
543 
544     if (!has_boundaries && !has_boundaries_read && !has_boundaries_write &&
545         !has_boundaries_flush)
546     {
547         block_latency_histograms_clear(stats);
548         return;
549     }
550 
551     if (has_boundaries || has_boundaries_read) {
552         ret = block_latency_histogram_set(
553             stats, BLOCK_ACCT_READ,
554             has_boundaries_read ? boundaries_read : boundaries);
555         if (ret) {
556             error_setg(errp, "Device '%s' set read boundaries fail", id);
557             return;
558         }
559     }
560 
561     if (has_boundaries || has_boundaries_write) {
562         ret = block_latency_histogram_set(
563             stats, BLOCK_ACCT_WRITE,
564             has_boundaries_write ? boundaries_write : boundaries);
565         if (ret) {
566             error_setg(errp, "Device '%s' set write boundaries fail", id);
567             return;
568         }
569     }
570 
571     if (has_boundaries || has_boundaries_append) {
572         ret = block_latency_histogram_set(
573                 stats, BLOCK_ACCT_ZONE_APPEND,
574                 has_boundaries_append ? boundaries_append : boundaries);
575         if (ret) {
576             error_setg(errp, "Device '%s' set append 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