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