1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
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
42static 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
67
68
69
70
71
72
73
74
75
76static 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
120void 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
139void 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
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
172static 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
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
216
217
218
219 blk_dev_change_media_cb(blk, false, &error_abort);
220 }
221
222out:
223 aio_context_release(aio_context);
224}
225
226void qmp_blockdev_remove_medium(const char *id, Error **errp)
227{
228 blockdev_remove_medium(NULL, id, errp);
229}
230
231static 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
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
263
264
265
266
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
276static 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
301void 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
307void 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
395fail:
396
397
398
399 bdrv_unref(medium_bs);
400}
401
402void 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
423void 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
501
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
508 blk_set_io_limits(blk, &cfg);
509 } else if (blk_get_public(blk)->throttle_group_member.throttle_state) {
510
511 blk_io_limits_disable(blk);
512 }
513
514out:
515 aio_context_release(aio_context);
516}
517
518void 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