root / block / qapi.c @ b6b8a333
History | View | Annotate | Download (16.3 kB)
1 |
/*
|
---|---|
2 |
* Block layer qmp and info dump related functions
|
3 |
*
|
4 |
* Copyright (c) 2003-2008 Fabrice Bellard
|
5 |
*
|
6 |
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
7 |
* of this software and associated documentation files (the "Software"), to deal
|
8 |
* in the Software without restriction, including without limitation the rights
|
9 |
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
10 |
* copies of the Software, and to permit persons to whom the Software is
|
11 |
* furnished to do so, subject to the following conditions:
|
12 |
*
|
13 |
* The above copyright notice and this permission notice shall be included in
|
14 |
* all copies or substantial portions of the Software.
|
15 |
*
|
16 |
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
17 |
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
18 |
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
|
19 |
* THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
20 |
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
21 |
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
22 |
* THE SOFTWARE.
|
23 |
*/
|
24 |
|
25 |
#include "block/qapi.h" |
26 |
#include "block/block_int.h" |
27 |
#include "qmp-commands.h" |
28 |
|
29 |
/*
|
30 |
* Returns 0 on success, with *p_list either set to describe snapshot
|
31 |
* information, or NULL because there are no snapshots. Returns -errno on
|
32 |
* error, with *p_list untouched.
|
33 |
*/
|
34 |
int bdrv_query_snapshot_info_list(BlockDriverState *bs,
|
35 |
SnapshotInfoList **p_list, |
36 |
Error **errp) |
37 |
{ |
38 |
int i, sn_count;
|
39 |
QEMUSnapshotInfo *sn_tab = NULL;
|
40 |
SnapshotInfoList *info_list, *cur_item = NULL, *head = NULL; |
41 |
SnapshotInfo *info; |
42 |
|
43 |
sn_count = bdrv_snapshot_list(bs, &sn_tab); |
44 |
if (sn_count < 0) { |
45 |
const char *dev = bdrv_get_device_name(bs); |
46 |
switch (sn_count) {
|
47 |
case -ENOMEDIUM:
|
48 |
error_setg(errp, "Device '%s' is not inserted", dev);
|
49 |
break;
|
50 |
case -ENOTSUP:
|
51 |
error_setg(errp, |
52 |
"Device '%s' does not support internal snapshots",
|
53 |
dev); |
54 |
break;
|
55 |
default:
|
56 |
error_setg_errno(errp, -sn_count, |
57 |
"Can't list snapshots of device '%s'", dev);
|
58 |
break;
|
59 |
} |
60 |
return sn_count;
|
61 |
} |
62 |
|
63 |
for (i = 0; i < sn_count; i++) { |
64 |
info = g_new0(SnapshotInfo, 1);
|
65 |
info->id = g_strdup(sn_tab[i].id_str); |
66 |
info->name = g_strdup(sn_tab[i].name); |
67 |
info->vm_state_size = sn_tab[i].vm_state_size; |
68 |
info->date_sec = sn_tab[i].date_sec; |
69 |
info->date_nsec = sn_tab[i].date_nsec; |
70 |
info->vm_clock_sec = sn_tab[i].vm_clock_nsec / 1000000000;
|
71 |
info->vm_clock_nsec = sn_tab[i].vm_clock_nsec % 1000000000;
|
72 |
|
73 |
info_list = g_new0(SnapshotInfoList, 1);
|
74 |
info_list->value = info; |
75 |
|
76 |
/* XXX: waiting for the qapi to support qemu-queue.h types */
|
77 |
if (!cur_item) {
|
78 |
head = cur_item = info_list; |
79 |
} else {
|
80 |
cur_item->next = info_list; |
81 |
cur_item = info_list; |
82 |
} |
83 |
|
84 |
} |
85 |
|
86 |
g_free(sn_tab); |
87 |
*p_list = head; |
88 |
return 0; |
89 |
} |
90 |
|
91 |
/**
|
92 |
* bdrv_query_image_info:
|
93 |
* @bs: block device to examine
|
94 |
* @p_info: location to store image information
|
95 |
* @errp: location to store error information
|
96 |
*
|
97 |
* Store "flat" image information in @p_info.
|
98 |
*
|
99 |
* "Flat" means it does *not* query backing image information,
|
100 |
* i.e. (*pinfo)->has_backing_image will be set to false and
|
101 |
* (*pinfo)->backing_image to NULL even when the image does in fact have
|
102 |
* a backing image.
|
103 |
*
|
104 |
* @p_info will be set only on success. On error, store error in @errp.
|
105 |
*/
|
106 |
void bdrv_query_image_info(BlockDriverState *bs,
|
107 |
ImageInfo **p_info, |
108 |
Error **errp) |
109 |
{ |
110 |
uint64_t total_sectors; |
111 |
const char *backing_filename; |
112 |
char backing_filename2[1024]; |
113 |
BlockDriverInfo bdi; |
114 |
int ret;
|
115 |
Error *err = NULL;
|
116 |
ImageInfo *info = g_new0(ImageInfo, 1);
|
117 |
|
118 |
bdrv_get_geometry(bs, &total_sectors); |
119 |
|
120 |
info->filename = g_strdup(bs->filename); |
121 |
info->format = g_strdup(bdrv_get_format_name(bs)); |
122 |
info->virtual_size = total_sectors * 512;
|
123 |
info->actual_size = bdrv_get_allocated_file_size(bs); |
124 |
info->has_actual_size = info->actual_size >= 0;
|
125 |
if (bdrv_is_encrypted(bs)) {
|
126 |
info->encrypted = true;
|
127 |
info->has_encrypted = true;
|
128 |
} |
129 |
if (bdrv_get_info(bs, &bdi) >= 0) { |
130 |
if (bdi.cluster_size != 0) { |
131 |
info->cluster_size = bdi.cluster_size; |
132 |
info->has_cluster_size = true;
|
133 |
} |
134 |
info->dirty_flag = bdi.is_dirty; |
135 |
info->has_dirty_flag = true;
|
136 |
} |
137 |
backing_filename = bs->backing_file; |
138 |
if (backing_filename[0] != '\0') { |
139 |
info->backing_filename = g_strdup(backing_filename); |
140 |
info->has_backing_filename = true;
|
141 |
bdrv_get_full_backing_filename(bs, backing_filename2, |
142 |
sizeof(backing_filename2));
|
143 |
|
144 |
if (strcmp(backing_filename, backing_filename2) != 0) { |
145 |
info->full_backing_filename = |
146 |
g_strdup(backing_filename2); |
147 |
info->has_full_backing_filename = true;
|
148 |
} |
149 |
|
150 |
if (bs->backing_format[0]) { |
151 |
info->backing_filename_format = g_strdup(bs->backing_format); |
152 |
info->has_backing_filename_format = true;
|
153 |
} |
154 |
} |
155 |
|
156 |
ret = bdrv_query_snapshot_info_list(bs, &info->snapshots, &err); |
157 |
switch (ret) {
|
158 |
case 0: |
159 |
if (info->snapshots) {
|
160 |
info->has_snapshots = true;
|
161 |
} |
162 |
break;
|
163 |
/* recoverable error */
|
164 |
case -ENOMEDIUM:
|
165 |
case -ENOTSUP:
|
166 |
error_free(err); |
167 |
break;
|
168 |
default:
|
169 |
error_propagate(errp, err); |
170 |
qapi_free_ImageInfo(info); |
171 |
return;
|
172 |
} |
173 |
|
174 |
*p_info = info; |
175 |
} |
176 |
|
177 |
/* @p_info will be set only on success. */
|
178 |
void bdrv_query_info(BlockDriverState *bs,
|
179 |
BlockInfo **p_info, |
180 |
Error **errp) |
181 |
{ |
182 |
BlockInfo *info = g_malloc0(sizeof(*info));
|
183 |
BlockDriverState *bs0; |
184 |
ImageInfo **p_image_info; |
185 |
Error *local_err = NULL;
|
186 |
info->device = g_strdup(bs->device_name); |
187 |
info->type = g_strdup("unknown");
|
188 |
info->locked = bdrv_dev_is_medium_locked(bs); |
189 |
info->removable = bdrv_dev_has_removable_media(bs); |
190 |
|
191 |
if (bdrv_dev_has_removable_media(bs)) {
|
192 |
info->has_tray_open = true;
|
193 |
info->tray_open = bdrv_dev_is_tray_open(bs); |
194 |
} |
195 |
|
196 |
if (bdrv_iostatus_is_enabled(bs)) {
|
197 |
info->has_io_status = true;
|
198 |
info->io_status = bs->iostatus; |
199 |
} |
200 |
|
201 |
if (bs->dirty_bitmap) {
|
202 |
info->has_dirty = true;
|
203 |
info->dirty = g_malloc0(sizeof(*info->dirty));
|
204 |
info->dirty->count = bdrv_get_dirty_count(bs) * BDRV_SECTOR_SIZE; |
205 |
info->dirty->granularity = |
206 |
((int64_t) BDRV_SECTOR_SIZE << hbitmap_granularity(bs->dirty_bitmap)); |
207 |
} |
208 |
|
209 |
if (bs->drv) {
|
210 |
info->has_inserted = true;
|
211 |
info->inserted = g_malloc0(sizeof(*info->inserted));
|
212 |
info->inserted->file = g_strdup(bs->filename); |
213 |
info->inserted->ro = bs->read_only; |
214 |
info->inserted->drv = g_strdup(bs->drv->format_name); |
215 |
info->inserted->encrypted = bs->encrypted; |
216 |
info->inserted->encryption_key_missing = bdrv_key_required(bs); |
217 |
|
218 |
if (bs->backing_file[0]) { |
219 |
info->inserted->has_backing_file = true;
|
220 |
info->inserted->backing_file = g_strdup(bs->backing_file); |
221 |
} |
222 |
|
223 |
info->inserted->backing_file_depth = bdrv_get_backing_file_depth(bs); |
224 |
|
225 |
if (bs->io_limits_enabled) {
|
226 |
ThrottleConfig cfg; |
227 |
throttle_get_config(&bs->throttle_state, &cfg); |
228 |
info->inserted->bps = cfg.buckets[THROTTLE_BPS_TOTAL].avg; |
229 |
info->inserted->bps_rd = cfg.buckets[THROTTLE_BPS_READ].avg; |
230 |
info->inserted->bps_wr = cfg.buckets[THROTTLE_BPS_WRITE].avg; |
231 |
|
232 |
info->inserted->iops = cfg.buckets[THROTTLE_OPS_TOTAL].avg; |
233 |
info->inserted->iops_rd = cfg.buckets[THROTTLE_OPS_READ].avg; |
234 |
info->inserted->iops_wr = cfg.buckets[THROTTLE_OPS_WRITE].avg; |
235 |
|
236 |
info->inserted->has_bps_max = |
237 |
cfg.buckets[THROTTLE_BPS_TOTAL].max; |
238 |
info->inserted->bps_max = |
239 |
cfg.buckets[THROTTLE_BPS_TOTAL].max; |
240 |
info->inserted->has_bps_rd_max = |
241 |
cfg.buckets[THROTTLE_BPS_READ].max; |
242 |
info->inserted->bps_rd_max = |
243 |
cfg.buckets[THROTTLE_BPS_READ].max; |
244 |
info->inserted->has_bps_wr_max = |
245 |
cfg.buckets[THROTTLE_BPS_WRITE].max; |
246 |
info->inserted->bps_wr_max = |
247 |
cfg.buckets[THROTTLE_BPS_WRITE].max; |
248 |
|
249 |
info->inserted->has_iops_max = |
250 |
cfg.buckets[THROTTLE_OPS_TOTAL].max; |
251 |
info->inserted->iops_max = |
252 |
cfg.buckets[THROTTLE_OPS_TOTAL].max; |
253 |
info->inserted->has_iops_rd_max = |
254 |
cfg.buckets[THROTTLE_OPS_READ].max; |
255 |
info->inserted->iops_rd_max = |
256 |
cfg.buckets[THROTTLE_OPS_READ].max; |
257 |
info->inserted->has_iops_wr_max = |
258 |
cfg.buckets[THROTTLE_OPS_WRITE].max; |
259 |
info->inserted->iops_wr_max = |
260 |
cfg.buckets[THROTTLE_OPS_WRITE].max; |
261 |
|
262 |
info->inserted->has_iops_size = cfg.op_size; |
263 |
info->inserted->iops_size = cfg.op_size; |
264 |
} |
265 |
|
266 |
bs0 = bs; |
267 |
p_image_info = &info->inserted->image; |
268 |
while (1) { |
269 |
bdrv_query_image_info(bs0, p_image_info, &local_err); |
270 |
if (error_is_set(&local_err)) {
|
271 |
error_propagate(errp, local_err); |
272 |
goto err;
|
273 |
} |
274 |
if (bs0->drv && bs0->backing_hd) {
|
275 |
bs0 = bs0->backing_hd; |
276 |
(*p_image_info)->has_backing_image = true;
|
277 |
p_image_info = &((*p_image_info)->backing_image); |
278 |
} else {
|
279 |
break;
|
280 |
} |
281 |
} |
282 |
} |
283 |
|
284 |
*p_info = info; |
285 |
return;
|
286 |
|
287 |
err:
|
288 |
qapi_free_BlockInfo(info); |
289 |
} |
290 |
|
291 |
BlockStats *bdrv_query_stats(const BlockDriverState *bs)
|
292 |
{ |
293 |
BlockStats *s; |
294 |
|
295 |
s = g_malloc0(sizeof(*s));
|
296 |
|
297 |
if (bs->device_name[0]) { |
298 |
s->has_device = true;
|
299 |
s->device = g_strdup(bs->device_name); |
300 |
} |
301 |
|
302 |
s->stats = g_malloc0(sizeof(*s->stats));
|
303 |
s->stats->rd_bytes = bs->nr_bytes[BDRV_ACCT_READ]; |
304 |
s->stats->wr_bytes = bs->nr_bytes[BDRV_ACCT_WRITE]; |
305 |
s->stats->rd_operations = bs->nr_ops[BDRV_ACCT_READ]; |
306 |
s->stats->wr_operations = bs->nr_ops[BDRV_ACCT_WRITE]; |
307 |
s->stats->wr_highest_offset = bs->wr_highest_sector * BDRV_SECTOR_SIZE; |
308 |
s->stats->flush_operations = bs->nr_ops[BDRV_ACCT_FLUSH]; |
309 |
s->stats->wr_total_time_ns = bs->total_time_ns[BDRV_ACCT_WRITE]; |
310 |
s->stats->rd_total_time_ns = bs->total_time_ns[BDRV_ACCT_READ]; |
311 |
s->stats->flush_total_time_ns = bs->total_time_ns[BDRV_ACCT_FLUSH]; |
312 |
|
313 |
if (bs->file) {
|
314 |
s->has_parent = true;
|
315 |
s->parent = bdrv_query_stats(bs->file); |
316 |
} |
317 |
|
318 |
return s;
|
319 |
} |
320 |
|
321 |
BlockInfoList *qmp_query_block(Error **errp) |
322 |
{ |
323 |
BlockInfoList *head = NULL, **p_next = &head;
|
324 |
BlockDriverState *bs = NULL;
|
325 |
Error *local_err = NULL;
|
326 |
|
327 |
while ((bs = bdrv_next(bs))) {
|
328 |
BlockInfoList *info = g_malloc0(sizeof(*info));
|
329 |
bdrv_query_info(bs, &info->value, &local_err); |
330 |
if (error_is_set(&local_err)) {
|
331 |
error_propagate(errp, local_err); |
332 |
goto err;
|
333 |
} |
334 |
|
335 |
*p_next = info; |
336 |
p_next = &info->next; |
337 |
} |
338 |
|
339 |
return head;
|
340 |
|
341 |
err:
|
342 |
qapi_free_BlockInfoList(head); |
343 |
return NULL; |
344 |
} |
345 |
|
346 |
BlockStatsList *qmp_query_blockstats(Error **errp) |
347 |
{ |
348 |
BlockStatsList *head = NULL, **p_next = &head;
|
349 |
BlockDriverState *bs = NULL;
|
350 |
|
351 |
while ((bs = bdrv_next(bs))) {
|
352 |
BlockStatsList *info = g_malloc0(sizeof(*info));
|
353 |
info->value = bdrv_query_stats(bs); |
354 |
|
355 |
*p_next = info; |
356 |
p_next = &info->next; |
357 |
} |
358 |
|
359 |
return head;
|
360 |
} |
361 |
|
362 |
#define NB_SUFFIXES 4 |
363 |
|
364 |
static char *get_human_readable_size(char *buf, int buf_size, int64_t size) |
365 |
{ |
366 |
static const char suffixes[NB_SUFFIXES] = "KMGT"; |
367 |
int64_t base; |
368 |
int i;
|
369 |
|
370 |
if (size <= 999) { |
371 |
snprintf(buf, buf_size, "%" PRId64, size);
|
372 |
} else {
|
373 |
base = 1024;
|
374 |
for (i = 0; i < NB_SUFFIXES; i++) { |
375 |
if (size < (10 * base)) { |
376 |
snprintf(buf, buf_size, "%0.1f%c",
|
377 |
(double)size / base,
|
378 |
suffixes[i]); |
379 |
break;
|
380 |
} else if (size < (1000 * base) || i == (NB_SUFFIXES - 1)) { |
381 |
snprintf(buf, buf_size, "%" PRId64 "%c", |
382 |
((size + (base >> 1)) / base),
|
383 |
suffixes[i]); |
384 |
break;
|
385 |
} |
386 |
base = base * 1024;
|
387 |
} |
388 |
} |
389 |
return buf;
|
390 |
} |
391 |
|
392 |
void bdrv_snapshot_dump(fprintf_function func_fprintf, void *f, |
393 |
QEMUSnapshotInfo *sn) |
394 |
{ |
395 |
char buf1[128], date_buf[128], clock_buf[128]; |
396 |
struct tm tm;
|
397 |
time_t ti; |
398 |
int64_t secs; |
399 |
|
400 |
if (!sn) {
|
401 |
func_fprintf(f, |
402 |
"%-10s%-20s%7s%20s%15s",
|
403 |
"ID", "TAG", "VM SIZE", "DATE", "VM CLOCK"); |
404 |
} else {
|
405 |
ti = sn->date_sec; |
406 |
localtime_r(&ti, &tm); |
407 |
strftime(date_buf, sizeof(date_buf),
|
408 |
"%Y-%m-%d %H:%M:%S", &tm);
|
409 |
secs = sn->vm_clock_nsec / 1000000000;
|
410 |
snprintf(clock_buf, sizeof(clock_buf),
|
411 |
"%02d:%02d:%02d.%03d",
|
412 |
(int)(secs / 3600), |
413 |
(int)((secs / 60) % 60), |
414 |
(int)(secs % 60), |
415 |
(int)((sn->vm_clock_nsec / 1000000) % 1000)); |
416 |
func_fprintf(f, |
417 |
"%-10s%-20s%7s%20s%15s",
|
418 |
sn->id_str, sn->name, |
419 |
get_human_readable_size(buf1, sizeof(buf1),
|
420 |
sn->vm_state_size), |
421 |
date_buf, |
422 |
clock_buf); |
423 |
} |
424 |
} |
425 |
|
426 |
void bdrv_image_info_dump(fprintf_function func_fprintf, void *f, |
427 |
ImageInfo *info) |
428 |
{ |
429 |
char size_buf[128], dsize_buf[128]; |
430 |
if (!info->has_actual_size) {
|
431 |
snprintf(dsize_buf, sizeof(dsize_buf), "unavailable"); |
432 |
} else {
|
433 |
get_human_readable_size(dsize_buf, sizeof(dsize_buf),
|
434 |
info->actual_size); |
435 |
} |
436 |
get_human_readable_size(size_buf, sizeof(size_buf), info->virtual_size);
|
437 |
func_fprintf(f, |
438 |
"image: %s\n"
|
439 |
"file format: %s\n"
|
440 |
"virtual size: %s (%" PRId64 " bytes)\n" |
441 |
"disk size: %s\n",
|
442 |
info->filename, info->format, size_buf, |
443 |
info->virtual_size, |
444 |
dsize_buf); |
445 |
|
446 |
if (info->has_encrypted && info->encrypted) {
|
447 |
func_fprintf(f, "encrypted: yes\n");
|
448 |
} |
449 |
|
450 |
if (info->has_cluster_size) {
|
451 |
func_fprintf(f, "cluster_size: %" PRId64 "\n", |
452 |
info->cluster_size); |
453 |
} |
454 |
|
455 |
if (info->has_dirty_flag && info->dirty_flag) {
|
456 |
func_fprintf(f, "cleanly shut down: no\n");
|
457 |
} |
458 |
|
459 |
if (info->has_backing_filename) {
|
460 |
func_fprintf(f, "backing file: %s", info->backing_filename);
|
461 |
if (info->has_full_backing_filename) {
|
462 |
func_fprintf(f, " (actual path: %s)", info->full_backing_filename);
|
463 |
} |
464 |
func_fprintf(f, "\n");
|
465 |
if (info->has_backing_filename_format) {
|
466 |
func_fprintf(f, "backing file format: %s\n",
|
467 |
info->backing_filename_format); |
468 |
} |
469 |
} |
470 |
|
471 |
if (info->has_snapshots) {
|
472 |
SnapshotInfoList *elem; |
473 |
|
474 |
func_fprintf(f, "Snapshot list:\n");
|
475 |
bdrv_snapshot_dump(func_fprintf, f, NULL);
|
476 |
func_fprintf(f, "\n");
|
477 |
|
478 |
/* Ideally bdrv_snapshot_dump() would operate on SnapshotInfoList but
|
479 |
* we convert to the block layer's native QEMUSnapshotInfo for now.
|
480 |
*/
|
481 |
for (elem = info->snapshots; elem; elem = elem->next) {
|
482 |
QEMUSnapshotInfo sn = { |
483 |
.vm_state_size = elem->value->vm_state_size, |
484 |
.date_sec = elem->value->date_sec, |
485 |
.date_nsec = elem->value->date_nsec, |
486 |
.vm_clock_nsec = elem->value->vm_clock_sec * 1000000000ULL +
|
487 |
elem->value->vm_clock_nsec, |
488 |
}; |
489 |
|
490 |
pstrcpy(sn.id_str, sizeof(sn.id_str), elem->value->id);
|
491 |
pstrcpy(sn.name, sizeof(sn.name), elem->value->name);
|
492 |
bdrv_snapshot_dump(func_fprintf, f, &sn); |
493 |
func_fprintf(f, "\n");
|
494 |
} |
495 |
} |
496 |
} |