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