uint8_t *buf, int nb_sectors);
static int bdrv_write_em(BlockDriverState *bs, int64_t sector_num,
const uint8_t *buf, int nb_sectors);
+static BlockDriver *find_protocol(const char *filename);
-BlockDriverState *bdrv_first;
+static QTAILQ_HEAD(, BlockDriverState) bdrv_states =
+ QTAILQ_HEAD_INITIALIZER(bdrv_states);
-static BlockDriver *first_drv;
+static QLIST_HEAD(, BlockDriver) bdrv_drivers =
+ QLIST_HEAD_INITIALIZER(bdrv_drivers);
/* If non-zero, use only whitelisted block drivers */
static int use_bdrv_whitelist;
if (!bdrv->bdrv_aio_flush)
bdrv->bdrv_aio_flush = bdrv_aio_flush_em;
- bdrv->next = first_drv;
- first_drv = bdrv;
+ QLIST_INSERT_HEAD(&bdrv_drivers, bdrv, list);
}
/* create a new block device (by default it is empty) */
BlockDriverState *bdrv_new(const char *device_name)
{
- BlockDriverState **pbs, *bs;
+ BlockDriverState *bs;
bs = qemu_mallocz(sizeof(BlockDriverState));
pstrcpy(bs->device_name, sizeof(bs->device_name), device_name);
if (device_name[0] != '\0') {
- /* insert at the end */
- pbs = &bdrv_first;
- while (*pbs != NULL)
- pbs = &(*pbs)->next;
- *pbs = bs;
+ QTAILQ_INSERT_TAIL(&bdrv_states, bs, list);
}
return bs;
}
BlockDriver *bdrv_find_format(const char *format_name)
{
BlockDriver *drv1;
- for(drv1 = first_drv; drv1 != NULL; drv1 = drv1->next) {
- if (!strcmp(drv1->format_name, format_name))
+ QLIST_FOREACH(drv1, &bdrv_drivers, list) {
+ if (!strcmp(drv1->format_name, format_name)) {
return drv1;
+ }
}
return NULL;
}
return drv->bdrv_create(filename, options);
}
+int bdrv_create_file(const char* filename, QEMUOptionParameter *options)
+{
+ BlockDriver *drv;
+
+ drv = find_protocol(filename);
+ if (drv == NULL) {
+ drv = bdrv_find_format("file");
+ }
+
+ return bdrv_create(drv, filename, options);
+}
+
#ifdef _WIN32
void get_tmp_filename(char *filename, int size)
{
}
#endif
-static BlockDriver *find_protocol(const char *filename)
-{
- BlockDriver *drv1;
- char protocol[128];
- int len;
- const char *p;
-
-#ifdef _WIN32
- if (is_windows_drive(filename) ||
- is_windows_drive_prefix(filename))
- return bdrv_find_format("raw");
-#endif
- p = strchr(filename, ':');
- if (!p)
- return bdrv_find_format("raw");
- len = p - filename;
- if (len > sizeof(protocol) - 1)
- len = sizeof(protocol) - 1;
- memcpy(protocol, filename, len);
- protocol[len] = '\0';
- for(drv1 = first_drv; drv1 != NULL; drv1 = drv1->next) {
- if (drv1->protocol_name &&
- !strcmp(drv1->protocol_name, protocol))
- return drv1;
- }
- return NULL;
-}
-
/*
* Detect host devices. By convention, /dev/cdrom[N] is always
* recognized as a host CDROM.
int score_max = 0, score;
BlockDriver *drv = NULL, *d;
- for (d = first_drv; d; d = d->next) {
+ QLIST_FOREACH(d, &bdrv_drivers, list) {
if (d->bdrv_probe_device) {
score = d->bdrv_probe_device(filename);
if (score > score_max) {
return drv;
}
+static BlockDriver *find_protocol(const char *filename)
+{
+ BlockDriver *drv1;
+ char protocol[128];
+ int len;
+ const char *p;
+ int is_drive;
+
+ /* TODO Drivers without bdrv_file_open must be specified explicitly */
+
+#ifdef _WIN32
+ is_drive = is_windows_drive(filename) ||
+ is_windows_drive_prefix(filename);
+#else
+ is_drive = 0;
+#endif
+ p = strchr(filename, ':');
+ if (!p || is_drive) {
+ drv1 = find_hdev_driver(filename);
+ if (!drv1) {
+ drv1 = bdrv_find_format("file");
+ }
+ return drv1;
+ }
+ len = p - filename;
+ if (len > sizeof(protocol) - 1)
+ len = sizeof(protocol) - 1;
+ memcpy(protocol, filename, len);
+ protocol[len] = '\0';
+ QLIST_FOREACH(drv1, &bdrv_drivers, list) {
+ if (drv1->protocol_name &&
+ !strcmp(drv1->protocol_name, protocol)) {
+ return drv1;
+ }
+ }
+ return NULL;
+}
+
static BlockDriver *find_image_format(const char *filename)
{
int ret, score, score_max;
}
score_max = 0;
- for(drv1 = first_drv; drv1 != NULL; drv1 = drv1->next) {
+ drv = NULL;
+ QLIST_FOREACH(drv1, &bdrv_drivers, list) {
if (drv1->bdrv_probe) {
score = drv1->bdrv_probe(buf, ret, filename);
if (score > score_max) {
return drv;
}
+/**
+ * Set the current 'total_sectors' value
+ */
+static int refresh_total_sectors(BlockDriverState *bs, int64_t hint)
+{
+ BlockDriver *drv = bs->drv;
+
+ /* query actual device if possible, otherwise just trust the hint */
+ if (drv->bdrv_getlength) {
+ int64_t length = drv->bdrv_getlength(bs);
+ if (length < 0) {
+ return length;
+ }
+ hint = length >> BDRV_SECTOR_BITS;
+ }
+
+ bs->total_sectors = hint;
+ return 0;
+}
+
+/*
+ * Common part for opening disk images and files
+ */
+static int bdrv_open_common(BlockDriverState *bs, const char *filename,
+ int flags, BlockDriver *drv)
+{
+ int ret, open_flags;
+
+ assert(drv != NULL);
+
+ bs->file = NULL;
+ bs->total_sectors = 0;
+ bs->is_temporary = 0;
+ bs->encrypted = 0;
+ bs->valid_key = 0;
+ bs->open_flags = flags;
+ /* buffer_alignment defaulted to 512, drivers can change this value */
+ bs->buffer_alignment = 512;
+
+ pstrcpy(bs->filename, sizeof(bs->filename), filename);
+
+ if (use_bdrv_whitelist && !bdrv_is_whitelisted(drv)) {
+ return -ENOTSUP;
+ }
+
+ bs->drv = drv;
+ bs->opaque = qemu_mallocz(drv->instance_size);
+
+ /*
+ * Yes, BDRV_O_NOCACHE aka O_DIRECT means we have to present a
+ * write cache to the guest. We do need the fdatasync to flush
+ * out transactions for block allocations, and we maybe have a
+ * volatile write cache in our backing device to deal with.
+ */
+ if (flags & (BDRV_O_CACHE_WB|BDRV_O_NOCACHE))
+ bs->enable_write_cache = 1;
+
+ /*
+ * Clear flags that are internal to the block layer before opening the
+ * image.
+ */
+ open_flags = flags & ~(BDRV_O_SNAPSHOT | BDRV_O_NO_BACKING);
+
+ /*
+ * Snapshots should be writeable.
+ */
+ if (bs->is_temporary) {
+ open_flags |= BDRV_O_RDWR;
+ }
+
+ /* Open the image, either directly or using a protocol */
+ if (drv->bdrv_file_open) {
+ ret = drv->bdrv_file_open(bs, filename, open_flags);
+ } else {
+ ret = bdrv_file_open(&bs->file, filename, open_flags);
+ if (ret >= 0) {
+ ret = drv->bdrv_open(bs, open_flags);
+ }
+ }
+
+ if (ret < 0) {
+ goto free_and_fail;
+ }
+
+ bs->keep_read_only = bs->read_only = !(open_flags & BDRV_O_RDWR);
+
+ ret = refresh_total_sectors(bs, bs->total_sectors);
+ if (ret < 0) {
+ goto free_and_fail;
+ }
+
+#ifndef _WIN32
+ if (bs->is_temporary) {
+ unlink(filename);
+ }
+#endif
+ return 0;
+
+free_and_fail:
+ if (bs->file) {
+ bdrv_delete(bs->file);
+ bs->file = NULL;
+ }
+ qemu_free(bs->opaque);
+ bs->opaque = NULL;
+ bs->drv = NULL;
+ return ret;
+}
+
+/*
+ * Opens a file using a protocol (file, host_device, nbd, ...)
+ */
int bdrv_file_open(BlockDriverState **pbs, const char *filename, int flags)
{
BlockDriverState *bs;
+ BlockDriver *drv;
int ret;
+ drv = find_protocol(filename);
+ if (!drv) {
+ return -ENOENT;
+ }
+
bs = bdrv_new("");
- ret = bdrv_open2(bs, filename, flags | BDRV_O_FILE, NULL);
+ ret = bdrv_open_common(bs, filename, flags, drv);
if (ret < 0) {
bdrv_delete(bs);
return ret;
return 0;
}
-int bdrv_open(BlockDriverState *bs, const char *filename, int flags)
-{
- return bdrv_open2(bs, filename, flags, NULL);
-}
-
-int bdrv_open2(BlockDriverState *bs, const char *filename, int flags,
- BlockDriver *drv)
+/*
+ * Opens a disk image (raw, qcow2, vmdk, ...)
+ */
+int bdrv_open(BlockDriverState *bs, const char *filename, int flags,
+ BlockDriver *drv)
{
- int ret, open_flags;
- char tmp_filename[PATH_MAX];
- char backing_filename[PATH_MAX];
-
- bs->is_temporary = 0;
- bs->encrypted = 0;
- bs->valid_key = 0;
- /* buffer_alignment defaulted to 512, drivers can change this value */
- bs->buffer_alignment = 512;
+ int ret;
if (flags & BDRV_O_SNAPSHOT) {
BlockDriverState *bs1;
int is_protocol = 0;
BlockDriver *bdrv_qcow2;
QEMUOptionParameter *options;
+ char tmp_filename[PATH_MAX];
+ char backing_filename[PATH_MAX];
/* if snapshot, we create a temporary backing file and open it
instead of opening 'filename' directly */
/* if there is a backing file, use it */
bs1 = bdrv_new("");
- ret = bdrv_open2(bs1, filename, 0, drv);
+ ret = bdrv_open(bs1, filename, 0, drv);
if (ret < 0) {
bdrv_delete(bs1);
return ret;
}
ret = bdrv_create(bdrv_qcow2, tmp_filename, options);
+ free_option_parameters(options);
if (ret < 0) {
return ret;
}
bs->is_temporary = 1;
}
- pstrcpy(bs->filename, sizeof(bs->filename), filename);
- if (flags & BDRV_O_FILE) {
- drv = find_protocol(filename);
- } else if (!drv) {
- drv = find_hdev_driver(filename);
- if (!drv) {
- drv = find_image_format(filename);
- }
+ /* Find the right image format driver */
+ if (!drv) {
+ drv = find_image_format(filename);
}
if (!drv) {
ret = -ENOENT;
goto unlink_and_fail;
}
- if (use_bdrv_whitelist && !bdrv_is_whitelisted(drv)) {
- ret = -ENOTSUP;
- goto unlink_and_fail;
- }
- bs->drv = drv;
- bs->opaque = qemu_mallocz(drv->instance_size);
-
- /*
- * Yes, BDRV_O_NOCACHE aka O_DIRECT means we have to present a
- * write cache to the guest. We do need the fdatasync to flush
- * out transactions for block allocations, and we maybe have a
- * volatile write cache in our backing device to deal with.
- */
- if (flags & (BDRV_O_CACHE_WB|BDRV_O_NOCACHE))
- bs->enable_write_cache = 1;
-
- bs->read_only = (flags & BDRV_O_RDWR) == 0;
-
- /*
- * Clear flags that are internal to the block layer before opening the
- * image.
- */
- open_flags = flags & ~(BDRV_O_FILE | BDRV_O_SNAPSHOT | BDRV_O_NO_BACKING);
-
- /*
- * Snapshots should be writeable.
- *
- * XXX(hch): and what is the point of a snapshot during a read-only open?
- */
- if (!(flags & BDRV_O_FILE) && bs->is_temporary) {
- open_flags |= BDRV_O_RDWR;
- }
-
- ret = drv->bdrv_open(bs, filename, open_flags);
+ /* Open the image */
+ ret = bdrv_open_common(bs, filename, flags, drv);
if (ret < 0) {
- goto free_and_fail;
+ goto unlink_and_fail;
}
- if (drv->bdrv_getlength) {
- bs->total_sectors = bdrv_getlength(bs) >> BDRV_SECTOR_BITS;
- }
-#ifndef _WIN32
- if (bs->is_temporary) {
- unlink(filename);
- }
-#endif
+ /* If there is a backing file, use it */
if ((flags & BDRV_O_NO_BACKING) == 0 && bs->backing_file[0] != '\0') {
- /* if there is a backing file, use it */
+ char backing_filename[PATH_MAX];
+ int back_flags;
BlockDriver *back_drv = NULL;
+
bs->backing_hd = bdrv_new("");
path_combine(backing_filename, sizeof(backing_filename),
filename, bs->backing_file);
if (bs->backing_format[0] != '\0')
back_drv = bdrv_find_format(bs->backing_format);
- ret = bdrv_open2(bs->backing_hd, backing_filename, open_flags,
- back_drv);
- bs->backing_hd->read_only = (open_flags & BDRV_O_RDWR) == 0;
+
+ /* backing files always opened read-only */
+ back_flags =
+ flags & ~(BDRV_O_RDWR | BDRV_O_SNAPSHOT | BDRV_O_NO_BACKING);
+
+ ret = bdrv_open(bs->backing_hd, backing_filename, back_flags, back_drv);
if (ret < 0) {
bdrv_close(bs);
return ret;
}
+ if (bs->is_temporary) {
+ bs->backing_hd->keep_read_only = !(flags & BDRV_O_RDWR);
+ } else {
+ /* base image inherits from "parent" */
+ bs->backing_hd->keep_read_only = bs->keep_read_only;
+ }
}
if (!bdrv_key_required(bs)) {
if (bs->change_cb)
bs->change_cb(bs->change_opaque);
}
+
return 0;
-free_and_fail:
- qemu_free(bs->opaque);
- bs->opaque = NULL;
- bs->drv = NULL;
unlink_and_fail:
- if (bs->is_temporary)
+ if (bs->is_temporary) {
unlink(filename);
+ }
return ret;
}
void bdrv_close(BlockDriverState *bs)
{
if (bs->drv) {
- if (bs->backing_hd)
+ if (bs->backing_hd) {
bdrv_delete(bs->backing_hd);
+ bs->backing_hd = NULL;
+ }
bs->drv->bdrv_close(bs);
qemu_free(bs->opaque);
#ifdef _WIN32
bs->opaque = NULL;
bs->drv = NULL;
+ if (bs->file != NULL) {
+ bdrv_close(bs->file);
+ }
+
/* call the change callback */
bs->media_changed = 1;
if (bs->change_cb)
void bdrv_delete(BlockDriverState *bs)
{
- BlockDriverState **pbs;
-
- pbs = &bdrv_first;
- while (*pbs != bs && *pbs != NULL)
- pbs = &(*pbs)->next;
- if (*pbs == bs)
- *pbs = bs->next;
+ /* remove from list, if necessary */
+ if (bs->device_name[0] != '\0') {
+ QTAILQ_REMOVE(&bdrv_states, bs, list);
+ }
bdrv_close(bs);
+ if (bs->file != NULL) {
+ bdrv_delete(bs->file);
+ }
+
qemu_free(bs);
}
{
BlockDriver *drv = bs->drv;
int64_t i, total_sectors;
- int n, j;
- int ret = 0;
+ int n, j, ro, open_flags;
+ int ret = 0, rw_ret = 0;
unsigned char sector[512];
+ char filename[1024];
+ BlockDriverState *bs_rw, *bs_ro;
if (!drv)
return -ENOMEDIUM;
-
- if (bs->read_only) {
- return -EACCES;
+
+ if (!bs->backing_hd) {
+ return -ENOTSUP;
}
- if (!bs->backing_hd) {
- return -ENOTSUP;
+ if (bs->backing_hd->keep_read_only) {
+ return -EACCES;
+ }
+
+ ro = bs->backing_hd->read_only;
+ strncpy(filename, bs->backing_hd->filename, sizeof(filename));
+ open_flags = bs->backing_hd->open_flags;
+
+ if (ro) {
+ /* re-open as RW */
+ bdrv_delete(bs->backing_hd);
+ bs->backing_hd = NULL;
+ bs_rw = bdrv_new("");
+ rw_ret = bdrv_open(bs_rw, filename, open_flags | BDRV_O_RDWR, drv);
+ if (rw_ret < 0) {
+ bdrv_delete(bs_rw);
+ /* try to re-open read-only */
+ bs_ro = bdrv_new("");
+ ret = bdrv_open(bs_ro, filename, open_flags & ~BDRV_O_RDWR, drv);
+ if (ret < 0) {
+ bdrv_delete(bs_ro);
+ /* drive not functional anymore */
+ bs->drv = NULL;
+ return ret;
+ }
+ bs->backing_hd = bs_ro;
+ return rw_ret;
+ }
+ bs->backing_hd = bs_rw;
}
total_sectors = bdrv_getlength(bs) >> BDRV_SECTOR_BITS;
if (drv->bdrv_is_allocated(bs, i, 65536, &n)) {
for(j = 0; j < n; j++) {
if (bdrv_read(bs, i, sector, 1) != 0) {
- return -EIO;
+ ret = -EIO;
+ goto ro_cleanup;
}
if (bdrv_write(bs->backing_hd, i, sector, 1) != 0) {
- return -EIO;
+ ret = -EIO;
+ goto ro_cleanup;
}
i++;
}
*/
if (bs->backing_hd)
bdrv_flush(bs->backing_hd);
+
+ro_cleanup:
+
+ if (ro) {
+ /* re-open as RO */
+ bdrv_delete(bs->backing_hd);
+ bs->backing_hd = NULL;
+ bs_ro = bdrv_new("");
+ ret = bdrv_open(bs_ro, filename, open_flags & ~BDRV_O_RDWR, drv);
+ if (ret < 0) {
+ bdrv_delete(bs_ro);
+ /* drive not functional anymore */
+ bs->drv = NULL;
+ return ret;
+ }
+ bs->backing_hd = bs_ro;
+ bs->backing_hd->keep_read_only = 0;
+ }
+
return ret;
}
set_dirty_bitmap(bs, sector_num, nb_sectors, 1);
}
+ if (bs->wr_highest_sector < sector_num + nb_sectors - 1) {
+ bs->wr_highest_sector = sector_num + nb_sectors - 1;
+ }
+
return drv->bdrv_write(bs, sector_num, buf, nb_sectors);
}
int bdrv_truncate(BlockDriverState *bs, int64_t offset)
{
BlockDriver *drv = bs->drv;
+ int ret;
if (!drv)
return -ENOMEDIUM;
if (!drv->bdrv_truncate)
return -ENOTSUP;
if (bs->read_only)
return -EACCES;
- return drv->bdrv_truncate(bs, offset);
+ ret = drv->bdrv_truncate(bs, offset);
+ if (ret == 0) {
+ ret = refresh_total_sectors(bs, offset >> BDRV_SECTOR_BITS);
+ }
+ return ret;
}
/**
BlockDriver *drv = bs->drv;
if (!drv)
return -ENOMEDIUM;
- if (!drv->bdrv_getlength) {
- /* legacy mode */
+
+ /* Fixed size devices use the total_sectors value for speed instead of
+ issuing a length query (like lseek) on each call. Also, legacy block
+ drivers don't provide a bdrv_getlength function and must use
+ total_sectors. */
+ if (!bs->growable || !drv->bdrv_getlength) {
return bs->total_sectors * BDRV_SECTOR_SIZE;
}
return drv->bdrv_getlength(bs);
if (!bs->encrypted)
return 0;
}
- if (!bs->encrypted || !bs->drv || !bs->drv->bdrv_set_key)
- return -1;
+ if (!bs->encrypted) {
+ return -EINVAL;
+ } else if (!bs->drv || !bs->drv->bdrv_set_key) {
+ return -ENOMEDIUM;
+ }
ret = bs->drv->bdrv_set_key(bs, key);
if (ret < 0) {
bs->valid_key = 0;
{
BlockDriver *drv;
- for (drv = first_drv; drv != NULL; drv = drv->next) {
+ QLIST_FOREACH(drv, &bdrv_drivers, list) {
it(opaque, drv->format_name);
}
}
{
BlockDriverState *bs;
- for (bs = bdrv_first; bs != NULL; bs = bs->next) {
- if (!strcmp(name, bs->device_name))
+ QTAILQ_FOREACH(bs, &bdrv_states, list) {
+ if (!strcmp(name, bs->device_name)) {
return bs;
+ }
}
return NULL;
}
{
BlockDriverState *bs;
- for (bs = bdrv_first; bs != NULL; bs = bs->next) {
+ QTAILQ_FOREACH(bs, &bdrv_states, list) {
it(opaque, bs);
}
}
{
BlockDriverState *bs;
- for (bs = bdrv_first; bs != NULL; bs = bs->next)
- if (bs->drv && !bdrv_is_read_only(bs) &&
- (!bdrv_is_removable(bs) || bdrv_is_inserted(bs)))
+ QTAILQ_FOREACH(bs, &bdrv_states, list) {
+ if (bs->drv && !bdrv_is_read_only(bs) &&
+ (!bdrv_is_removable(bs) || bdrv_is_inserted(bs))) {
bdrv_flush(bs);
+ }
+ }
+}
+
+int bdrv_has_zero_init(BlockDriverState *bs)
+{
+ assert(bs->drv);
+
+ if (bs->drv->no_zero_init) {
+ return 0;
+ } else if (bs->file) {
+ return bdrv_has_zero_init(bs->file);
+ }
+
+ return 1;
}
/*
bs_list = qlist_new();
- for (bs = bdrv_first; bs != NULL; bs = bs->next) {
+ QTAILQ_FOREACH(bs, &bdrv_states, list) {
QObject *bs_obj;
const char *type = "unknown";
qlist_iter(qobject_to_qlist(data), bdrv_stats_iter, mon);
}
+static QObject* bdrv_info_stats_bs(BlockDriverState *bs)
+{
+ QObject *res;
+ QDict *dict;
+
+ res = qobject_from_jsonf("{ 'stats': {"
+ "'rd_bytes': %" PRId64 ","
+ "'wr_bytes': %" PRId64 ","
+ "'rd_operations': %" PRId64 ","
+ "'wr_operations': %" PRId64 ","
+ "'wr_highest_offset': %" PRId64
+ "} }",
+ bs->rd_bytes, bs->wr_bytes,
+ bs->rd_ops, bs->wr_ops,
+ bs->wr_highest_sector * 512);
+ dict = qobject_to_qdict(res);
+
+ if (*bs->device_name) {
+ qdict_put(dict, "device", qstring_from_str(bs->device_name));
+ }
+
+ if (bs->file) {
+ QObject *parent = bdrv_info_stats_bs(bs->file);
+ qdict_put_obj(dict, "parent", parent);
+ }
+
+ return res;
+}
+
/**
* bdrv_info_stats(): show block device statistics
*
* - "wr_bytes": bytes written
* - "rd_operations": read operations
* - "wr_operations": write operations
- *
+ * - "wr_highest_offset": Highest offset of a sector written since the
+ * BlockDriverState has been opened
+ * - "parent": Contains recursively the statistics of the underlying
+ * protocol (e.g. the host file for a qcow2 image). If there is no
+ * underlying protocol, this field is omitted.
+ *
* Example:
*
* [ { "device": "ide0-hd0",
* "stats": { "rd_bytes": 512,
* "wr_bytes": 0,
* "rd_operations": 1,
- * "wr_operations": 0 } },
+ * "wr_operations": 0,
+ * "wr_highest_offset": 0,
+ * "parent": {
+ * "stats": { "rd_bytes": 1024,
+ * "wr_bytes": 0,
+ * "rd_operations": 2,
+ * "wr_operations": 0,
+ * "wr_highest_offset": 0,
+ * }
+ * } } },
* { "device": "ide1-cd0",
* "stats": { "rd_bytes": 0,
* "wr_bytes": 0,
* "rd_operations": 0,
- * "wr_operations": 0 } } ]
+ * "wr_operations": 0,
+ * "wr_highest_offset": 0 } },
*/
void bdrv_info_stats(Monitor *mon, QObject **ret_data)
{
devices = qlist_new();
- for (bs = bdrv_first; bs != NULL; bs = bs->next) {
- obj = qobject_from_jsonf("{ 'device': %s, 'stats': {"
- "'rd_bytes': %" PRId64 ","
- "'wr_bytes': %" PRId64 ","
- "'rd_operations': %" PRId64 ","
- "'wr_operations': %" PRId64
- "} }",
- bs->device_name,
- bs->rd_bytes, bs->wr_bytes,
- bs->rd_ops, bs->wr_ops);
+ QTAILQ_FOREACH(bs, &bdrv_states, list) {
+ obj = bdrv_info_stats_bs(bs);
qlist_append_obj(devices, obj);
}
return drv->bdrv_load_vmstate(bs, buf, pos, size);
}
+void bdrv_debug_event(BlockDriverState *bs, BlkDebugEvent event)
+{
+ BlockDriver *drv = bs->drv;
+
+ if (!drv || !drv->bdrv_debug_event) {
+ return;
+ }
+
+ return drv->bdrv_debug_event(bs, event);
+
+}
+
/**************************************************************/
/* handling of snapshots */
cb, opaque);
if (ret) {
- /* Update stats even though technically transfer has not happened. */
- bs->wr_bytes += (unsigned) nb_sectors * BDRV_SECTOR_SIZE;
- bs->wr_ops ++;
+ /* Update stats even though technically transfer has not happened. */
+ bs->wr_bytes += (unsigned) nb_sectors * BDRV_SECTOR_SIZE;
+ bs->wr_ops ++;
+ if (bs->wr_highest_sector < sector_num + nb_sectors - 1) {
+ bs->wr_highest_sector = sector_num + nb_sectors - 1;
+ }
}
return ret;
for (i = 0; i < mcb->num_callbacks; i++) {
mcb->callbacks[i].cb(mcb->callbacks[i].opaque, mcb->error);
+ if (mcb->callbacks[i].free_qiov) {
+ qemu_iovec_destroy(mcb->callbacks[i].free_qiov);
+ }
qemu_free(mcb->callbacks[i].free_qiov);
qemu_vfree(mcb->callbacks[i].free_buf);
}
{
MultiwriteCB *mcb = opaque;
- if (ret < 0) {
+ if (ret < 0 && !mcb->error) {
mcb->error = ret;
multiwrite_user_cb(mcb);
}
// submitted yet. Otherwise we'll wait for the submitted AIOs to
// complete and report the error in the callback.
if (mcb->num_requests == 0) {
- reqs[i].error = EIO;
+ reqs[i].error = -EIO;
goto fail;
} else {
- mcb->error = EIO;
+ mcb->num_requests++;
+ multiwrite_cb(mcb, -EIO);
break;
}
} else {
static void bdrv_aio_cancel_em(BlockDriverAIOCB *blockacb)
{
- BlockDriverAIOCBSync *acb = (BlockDriverAIOCBSync *)blockacb;
+ BlockDriverAIOCBSync *acb =
+ container_of(blockacb, BlockDriverAIOCBSync, common);
qemu_bh_delete(acb->bh);
acb->bh = NULL;
qemu_aio_release(acb);