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 "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
41static 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
66
67
68
69
70
71
72
73
74
75static 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
119void 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
140void 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
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
176static 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
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
223
224
225
226 blk_dev_change_media_cb(blk, false, &error_abort);
227 }
228
229out:
230 aio_context_release(aio_context);
231}
232
233void qmp_blockdev_remove_medium(const char *id, Error **errp)
234{
235 blockdev_remove_medium(false, NULL, true, id, errp);
236}
237
238static 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
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
270
271
272
273
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
283static 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
311void 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
317void 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
405fail:
406
407
408
409 bdrv_unref(medium_bs);
410}
411
412void 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
436void 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
516
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
526 blk_set_io_limits(blk, &cfg);
527 } else if (blk_get_public(blk)->throttle_group_member.throttle_state) {
528
529 blk_io_limits_disable(blk);
530 }
531
532out:
533 aio_context_release(aio_context);
534}
535
536void 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