qemu

Форк
0
/
qapi-sysemu.c 
574 строки · 16.1 Кб
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 "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

42
static 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
 * Attempt to open the tray of @device.
67
 * If @force, ignore its tray lock.
68
 * Else, if the tray is locked, don't open it, but ask the guest to open it.
69
 * On error, store an error through @errp and return -errno.
70
 * If @device does not exist, return -ENODEV.
71
 * If it has no removable media, return -ENOTSUP.
72
 * If it has no tray, return -ENOSYS.
73
 * If the guest was asked to open the tray, return -EINPROGRESS.
74
 * Else, return 0.
75
 */
76
static 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

120
void 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

139
void 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
        /* Ignore this command on tray-less devices */
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

172
static void GRAPH_UNLOCKED
173
blockdev_remove_medium(const char *device, const char *id, Error **errp)
174
{
175
    BlockBackend *blk;
176
    BlockDriverState *bs;
177
    bool has_attached_device;
178

179
    GLOBAL_STATE_CODE();
180

181
    blk = qmp_get_blk(device, id, errp);
182
    if (!blk) {
183
        return;
184
    }
185

186
    /* For BBs without a device, we can exchange the BDS tree at will */
187
    has_attached_device = blk_get_attached_dev(blk);
188

189
    if (has_attached_device && !blk_dev_has_removable_media(blk)) {
190
        error_setg(errp, "Device '%s' is not removable", device ?: id);
191
        return;
192
    }
193

194
    if (has_attached_device && blk_dev_has_tray(blk) &&
195
        !blk_dev_is_tray_open(blk))
196
    {
197
        error_setg(errp, "Tray of device '%s' is not open", device ?: id);
198
        return;
199
    }
200

201
    bs = blk_bs(blk);
202
    if (!bs) {
203
        return;
204
    }
205

206
    bdrv_graph_rdlock_main_loop();
207
    if (bdrv_op_is_blocked(bs, BLOCK_OP_TYPE_EJECT, errp)) {
208
        bdrv_graph_rdunlock_main_loop();
209
        return;
210
    }
211
    bdrv_graph_rdunlock_main_loop();
212

213
    blk_remove_bs(blk);
214

215
    if (!blk_dev_has_tray(blk)) {
216
        /* For tray-less devices, blockdev-open-tray is a no-op (or may not be
217
         * called at all); therefore, the medium needs to be ejected here.
218
         * Do it after blk_remove_bs() so blk_is_inserted(blk) returns the @load
219
         * value passed here (i.e. false). */
220
        blk_dev_change_media_cb(blk, false, &error_abort);
221
    }
222
}
223

224
void qmp_blockdev_remove_medium(const char *id, Error **errp)
225
{
226
    blockdev_remove_medium(NULL, id, errp);
227
}
228

229
static void qmp_blockdev_insert_anon_medium(BlockBackend *blk,
230
                                            BlockDriverState *bs, Error **errp)
231
{
232
    Error *local_err = NULL;
233
    bool has_device;
234
    int ret;
235

236
    /* For BBs without a device, we can exchange the BDS tree at will */
237
    has_device = blk_get_attached_dev(blk);
238

239
    if (has_device && !blk_dev_has_removable_media(blk)) {
240
        error_setg(errp, "Device is not removable");
241
        return;
242
    }
243

244
    if (has_device && blk_dev_has_tray(blk) && !blk_dev_is_tray_open(blk)) {
245
        error_setg(errp, "Tray of the device is not open");
246
        return;
247
    }
248

249
    if (blk_bs(blk)) {
250
        error_setg(errp, "There already is a medium in the device");
251
        return;
252
    }
253

254
    ret = blk_insert_bs(blk, bs, errp);
255
    if (ret < 0) {
256
        return;
257
    }
258

259
    if (!blk_dev_has_tray(blk)) {
260
        /* For tray-less devices, blockdev-close-tray is a no-op (or may not be
261
         * called at all); therefore, the medium needs to be pushed into the
262
         * slot here.
263
         * Do it after blk_insert_bs() so blk_is_inserted(blk) returns the @load
264
         * value passed here (i.e. true). */
265
        blk_dev_change_media_cb(blk, true, &local_err);
266
        if (local_err) {
267
            error_propagate(errp, local_err);
268
            blk_remove_bs(blk);
269
            return;
270
        }
271
    }
272
}
273

274
static void blockdev_insert_medium(const char *device, const char *id,
275
                                   const char *node_name, Error **errp)
276
{
277
    BlockBackend *blk;
278
    BlockDriverState *bs;
279

280
    GRAPH_RDLOCK_GUARD_MAINLOOP();
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

301
void 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

307
void 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
    medium_bs = bdrv_open(filename, NULL, options, bdrv_flags, errp);
366

367
    if (!medium_bs) {
368
        goto fail;
369
    }
370

371
    rc = do_open_tray(device, id, force, &err);
372
    if (rc && rc != -ENOSYS) {
373
        error_propagate(errp, err);
374
        goto fail;
375
    }
376
    error_free(err);
377
    err = NULL;
378

379
    blockdev_remove_medium(device, id, &err);
380
    if (err) {
381
        error_propagate(errp, err);
382
        goto fail;
383
    }
384

385
    qmp_blockdev_insert_anon_medium(blk, medium_bs, &err);
386
    if (err) {
387
        error_propagate(errp, err);
388
        goto fail;
389
    }
390

391
    qmp_blockdev_close_tray(device, id, errp);
392

393
fail:
394
    /* If the medium has been inserted, the device has its own reference, so
395
     * ours must be relinquished; and if it has not been inserted successfully,
396
     * the reference must be relinquished anyway */
397
    bdrv_unref(medium_bs);
398
}
399

400
void qmp_eject(const char *device, const char *id,
401
               bool has_force, bool force, Error **errp)
402
{
403
    Error *local_err = NULL;
404
    int rc;
405

406
    if (!has_force) {
407
        force = false;
408
    }
409

410
    rc = do_open_tray(device, id, force, &local_err);
411
    if (rc && rc != -ENOSYS) {
412
        error_propagate(errp, local_err);
413
        return;
414
    }
415
    error_free(local_err);
416

417
    blockdev_remove_medium(device, id, errp);
418
}
419

420
/* throttling disk I/O limits */
421
void qmp_block_set_io_throttle(BlockIOThrottle *arg, Error **errp)
422
{
423
    ThrottleConfig cfg;
424
    BlockDriverState *bs;
425
    BlockBackend *blk;
426

427
    blk = qmp_get_blk(arg->device, arg->id, errp);
428
    if (!blk) {
429
        return;
430
    }
431

432
    bs = blk_bs(blk);
433
    if (!bs) {
434
        error_setg(errp, "Device has no medium");
435
        return;
436
    }
437

438
    throttle_config_init(&cfg);
439
    cfg.buckets[THROTTLE_BPS_TOTAL].avg = arg->bps;
440
    cfg.buckets[THROTTLE_BPS_READ].avg  = arg->bps_rd;
441
    cfg.buckets[THROTTLE_BPS_WRITE].avg = arg->bps_wr;
442

443
    cfg.buckets[THROTTLE_OPS_TOTAL].avg = arg->iops;
444
    cfg.buckets[THROTTLE_OPS_READ].avg  = arg->iops_rd;
445
    cfg.buckets[THROTTLE_OPS_WRITE].avg = arg->iops_wr;
446

447
    if (arg->has_bps_max) {
448
        cfg.buckets[THROTTLE_BPS_TOTAL].max = arg->bps_max;
449
    }
450
    if (arg->has_bps_rd_max) {
451
        cfg.buckets[THROTTLE_BPS_READ].max = arg->bps_rd_max;
452
    }
453
    if (arg->has_bps_wr_max) {
454
        cfg.buckets[THROTTLE_BPS_WRITE].max = arg->bps_wr_max;
455
    }
456
    if (arg->has_iops_max) {
457
        cfg.buckets[THROTTLE_OPS_TOTAL].max = arg->iops_max;
458
    }
459
    if (arg->has_iops_rd_max) {
460
        cfg.buckets[THROTTLE_OPS_READ].max = arg->iops_rd_max;
461
    }
462
    if (arg->has_iops_wr_max) {
463
        cfg.buckets[THROTTLE_OPS_WRITE].max = arg->iops_wr_max;
464
    }
465

466
    if (arg->has_bps_max_length) {
467
        cfg.buckets[THROTTLE_BPS_TOTAL].burst_length = arg->bps_max_length;
468
    }
469
    if (arg->has_bps_rd_max_length) {
470
        cfg.buckets[THROTTLE_BPS_READ].burst_length = arg->bps_rd_max_length;
471
    }
472
    if (arg->has_bps_wr_max_length) {
473
        cfg.buckets[THROTTLE_BPS_WRITE].burst_length = arg->bps_wr_max_length;
474
    }
475
    if (arg->has_iops_max_length) {
476
        cfg.buckets[THROTTLE_OPS_TOTAL].burst_length = arg->iops_max_length;
477
    }
478
    if (arg->has_iops_rd_max_length) {
479
        cfg.buckets[THROTTLE_OPS_READ].burst_length = arg->iops_rd_max_length;
480
    }
481
    if (arg->has_iops_wr_max_length) {
482
        cfg.buckets[THROTTLE_OPS_WRITE].burst_length = arg->iops_wr_max_length;
483
    }
484

485
    if (arg->has_iops_size) {
486
        cfg.op_size = arg->iops_size;
487
    }
488

489
    if (!throttle_is_valid(&cfg, errp)) {
490
        return;
491
    }
492

493
    if (throttle_enabled(&cfg)) {
494
        /* Enable I/O limits if they're not enabled yet, otherwise
495
         * just update the throttling group. */
496
        if (!blk_get_public(blk)->throttle_group_member.throttle_state) {
497
            blk_io_limits_enable(blk, arg->group ?: arg->device ?: arg->id);
498
        } else if (arg->group) {
499
            blk_io_limits_update_group(blk, arg->group);
500
        }
501
        /* Set the new throttling configuration */
502
        blk_set_io_limits(blk, &cfg);
503
    } else if (blk_get_public(blk)->throttle_group_member.throttle_state) {
504
        /* If all throttling settings are set to 0, disable I/O limits */
505
        blk_io_limits_disable(blk);
506
    }
507
}
508

509
void qmp_block_latency_histogram_set(
510
    const char *id,
511
    bool has_boundaries, uint64List *boundaries,
512
    bool has_boundaries_read, uint64List *boundaries_read,
513
    bool has_boundaries_write, uint64List *boundaries_write,
514
    bool has_boundaries_append, uint64List *boundaries_append,
515
    bool has_boundaries_flush, uint64List *boundaries_flush,
516
    Error **errp)
517
{
518
    BlockBackend *blk = qmp_get_blk(NULL, id, errp);
519
    BlockAcctStats *stats;
520
    int ret;
521

522
    if (!blk) {
523
        return;
524
    }
525

526
    stats = blk_get_stats(blk);
527

528
    if (!has_boundaries && !has_boundaries_read && !has_boundaries_write &&
529
        !has_boundaries_flush)
530
    {
531
        block_latency_histograms_clear(stats);
532
        return;
533
    }
534

535
    if (has_boundaries || has_boundaries_read) {
536
        ret = block_latency_histogram_set(
537
            stats, BLOCK_ACCT_READ,
538
            has_boundaries_read ? boundaries_read : boundaries);
539
        if (ret) {
540
            error_setg(errp, "Device '%s' set read boundaries fail", id);
541
            return;
542
        }
543
    }
544

545
    if (has_boundaries || has_boundaries_write) {
546
        ret = block_latency_histogram_set(
547
            stats, BLOCK_ACCT_WRITE,
548
            has_boundaries_write ? boundaries_write : boundaries);
549
        if (ret) {
550
            error_setg(errp, "Device '%s' set write boundaries fail", id);
551
            return;
552
        }
553
    }
554

555
    if (has_boundaries || has_boundaries_append) {
556
        ret = block_latency_histogram_set(
557
                stats, BLOCK_ACCT_ZONE_APPEND,
558
                has_boundaries_append ? boundaries_append : boundaries);
559
        if (ret) {
560
            error_setg(errp, "Device '%s' set append write boundaries fail", id);
561
            return;
562
        }
563
    }
564

565
    if (has_boundaries || has_boundaries_flush) {
566
        ret = block_latency_histogram_set(
567
            stats, BLOCK_ACCT_FLUSH,
568
            has_boundaries_flush ? boundaries_flush : boundaries);
569
        if (ret) {
570
            error_setg(errp, "Device '%s' set flush boundaries fail", id);
571
            return;
572
        }
573
    }
574
}
575

Использование cookies

Мы используем файлы cookie в соответствии с Политикой конфиденциальности и Политикой использования cookies.

Нажимая кнопку «Принимаю», Вы даете АО «СберТех» согласие на обработку Ваших персональных данных в целях совершенствования нашего веб-сайта и Сервиса GitVerse, а также повышения удобства их использования.

Запретить использование cookies Вы можете самостоятельно в настройках Вашего браузера.