xref: /openbmc/qemu/block/qapi-sysemu.c (revision 0d98fbb5)
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 }
592