*/
#include "config-host.h"
#include "qemu-common.h"
+#include "trace.h"
#include "monitor.h"
#include "block_int.h"
#include "module.h"
BlockDriverCompletionFunc *cb, void *opaque);
static BlockDriverAIOCB *bdrv_aio_flush_em(BlockDriverState *bs,
BlockDriverCompletionFunc *cb, void *opaque);
+static BlockDriverAIOCB *bdrv_aio_noop_em(BlockDriverState *bs,
+ BlockDriverCompletionFunc *cb, void *opaque);
static int bdrv_read_em(BlockDriverState *bs, int64_t sector_num,
uint8_t *buf, int nb_sectors);
static int bdrv_write_em(BlockDriverState *bs, int64_t sector_num,
const uint8_t *buf, int nb_sectors);
-BlockDriverState *bdrv_first;
+static QTAILQ_HEAD(, BlockDriverState) bdrv_states =
+ QTAILQ_HEAD_INITIALIZER(bdrv_states);
+
+static QLIST_HEAD(, BlockDriver) bdrv_drivers =
+ QLIST_HEAD_INITIALIZER(bdrv_drivers);
-static BlockDriver *first_drv;
+/* The device to use for VM snapshots */
+static BlockDriverState *bs_snapshots;
/* If non-zero, use only whitelisted block drivers */
static int use_bdrv_whitelist;
+#ifdef _WIN32
+static int is_windows_drive_prefix(const char *filename)
+{
+ return (((filename[0] >= 'a' && filename[0] <= 'z') ||
+ (filename[0] >= 'A' && filename[0] <= 'Z')) &&
+ filename[1] == ':');
+}
+
+int is_windows_drive(const char *filename)
+{
+ if (is_windows_drive_prefix(filename) &&
+ filename[2] == '\0')
+ return 1;
+ if (strstart(filename, "\\\\.\\", NULL) ||
+ strstart(filename, "//./", NULL))
+ return 1;
+ return 0;
+}
+#endif
+
+/* check if the path starts with "<protocol>:" */
+static int path_has_protocol(const char *path)
+{
+#ifdef _WIN32
+ if (is_windows_drive(path) ||
+ is_windows_drive_prefix(path)) {
+ return 0;
+ }
+#endif
+
+ return strchr(path, ':') != NULL;
+}
+
int path_is_absolute(const char *path)
{
const char *p;
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 = bdrv_find_protocol(filename);
+ if (drv == NULL) {
+ return -ENOENT;
+ }
+
+ return bdrv_create(drv, filename, options);
+}
+
#ifdef _WIN32
void get_tmp_filename(char *filename, int size)
{
}
#endif
-#ifdef _WIN32
-static int is_windows_drive_prefix(const char *filename)
+/*
+ * Detect host devices. By convention, /dev/cdrom[N] is always
+ * recognized as a host CDROM.
+ */
+static BlockDriver *find_hdev_driver(const char *filename)
{
- return (((filename[0] >= 'a' && filename[0] <= 'z') ||
- (filename[0] >= 'A' && filename[0] <= 'Z')) &&
- filename[1] == ':');
-}
+ int score_max = 0, score;
+ BlockDriver *drv = NULL, *d;
-int is_windows_drive(const char *filename)
-{
- if (is_windows_drive_prefix(filename) &&
- filename[2] == '\0')
- return 1;
- if (strstart(filename, "\\\\.\\", NULL) ||
- strstart(filename, "//./", NULL))
- return 1;
- return 0;
+ QLIST_FOREACH(d, &bdrv_drivers, list) {
+ if (d->bdrv_probe_device) {
+ score = d->bdrv_probe_device(filename);
+ if (score > score_max) {
+ score_max = score;
+ drv = d;
+ }
+ }
+ }
+
+ return drv;
}
-#endif
-static BlockDriver *find_protocol(const char *filename)
+BlockDriver *bdrv_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
+ /* TODO Drivers without bdrv_file_open must be specified explicitly */
+
+ /*
+ * XXX(hch): we really should not let host device detection
+ * override an explicit protocol specification, but moving this
+ * later breaks access to device names with colons in them.
+ * Thanks to the brain-dead persistent naming schemes on udev-
+ * based Linux systems those actually are quite common.
+ */
+ drv1 = find_hdev_driver(filename);
+ if (drv1) {
+ return drv1;
+ }
+
+ if (!path_has_protocol(filename)) {
+ return bdrv_find_format("file");
+ }
p = strchr(filename, ':');
- if (!p)
- return bdrv_find_format("raw");
+ assert(p != NULL);
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) {
+ QLIST_FOREACH(drv1, &bdrv_drivers, list) {
if (drv1->protocol_name &&
- !strcmp(drv1->protocol_name, protocol))
+ !strcmp(drv1->protocol_name, protocol)) {
return drv1;
- }
- return NULL;
-}
-
-/*
- * Detect host devices. By convention, /dev/cdrom[N] is always
- * recognized as a host CDROM.
- */
-static BlockDriver *find_hdev_driver(const char *filename)
-{
- int score_max = 0, score;
- BlockDriver *drv = NULL, *d;
-
- for (d = first_drv; d; d = d->next) {
- if (d->bdrv_probe_device) {
- score = d->bdrv_probe_device(filename);
- if (score > score_max) {
- score_max = score;
- drv = d;
- }
}
}
-
- return drv;
+ return NULL;
}
-static BlockDriver *find_image_format(const char *filename)
+static int find_image_format(const char *filename, BlockDriver **pdrv)
{
int ret, score, score_max;
BlockDriver *drv1, *drv;
uint8_t buf[2048];
BlockDriverState *bs;
- drv = find_protocol(filename);
- /* no need to test disk image formats for vvfat */
- if (drv && strcmp(drv->format_name, "vvfat") == 0)
- return drv;
-
ret = bdrv_file_open(&bs, filename, 0);
- if (ret < 0)
- return NULL;
+ if (ret < 0) {
+ *pdrv = NULL;
+ return ret;
+ }
+
+ /* Return the raw BlockDriver * to scsi-generic devices or empty drives */
+ if (bs->sg || !bdrv_is_inserted(bs)) {
+ bdrv_delete(bs);
+ drv = bdrv_find_format("raw");
+ if (!drv) {
+ ret = -ENOENT;
+ }
+ *pdrv = drv;
+ return ret;
+ }
+
ret = bdrv_pread(bs, 0, buf, sizeof(buf));
bdrv_delete(bs);
if (ret < 0) {
- return NULL;
+ *pdrv = NULL;
+ return ret;
}
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;
+ if (!drv) {
+ ret = -ENOENT;
+ }
+ *pdrv = drv;
+ return ret;
}
+/**
+ * Set the current 'total_sectors' value
+ */
+static int refresh_total_sectors(BlockDriverState *bs, int64_t hint)
+{
+ BlockDriver *drv = bs->drv;
+
+ /* Do not attempt drv->bdrv_getlength() on scsi-generic devices */
+ if (bs->sg)
+ return 0;
+
+ /* 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->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 = bdrv_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;
- bs->open_flags = flags;
- /* 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;
}
- total_size = bdrv_getlength(bs1) >> BDRV_SECTOR_BITS;
+ total_size = bdrv_getlength(bs1) & BDRV_SECTOR_MASK;
if (bs1->drv && bs1->drv->protocol_name)
is_protocol = 1;
bdrv_qcow2 = bdrv_find_format("qcow2");
options = parse_option_parameters("", bdrv_qcow2->create_options, NULL);
- set_option_parameter_int(options, BLOCK_OPT_SIZE, total_size * 512);
+ set_option_parameter_int(options, BLOCK_OPT_SIZE, total_size);
set_option_parameter(options, BLOCK_OPT_BACKING_FILE, backing_filename);
if (drv) {
set_option_parameter(options, BLOCK_OPT_BACKING_FMT,
}
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) {
+ ret = find_image_format(filename, &drv);
}
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;
-
- /*
- * 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;
}
- bs->keep_read_only = bs->read_only = !(open_flags & BDRV_O_RDWR);
- 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')
+
+ if (path_has_protocol(bs->backing_file)) {
+ pstrcpy(backing_filename, sizeof(backing_filename),
+ bs->backing_file);
+ } else {
+ 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);
+ }
/* backing files always opened read-only */
- open_flags &= ~BDRV_O_RDWR;
-
- ret = bdrv_open2(bs->backing_hd, backing_filename, open_flags,
- back_drv);
+ 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;
/* call the change callback */
bs->media_changed = 1;
if (bs->change_cb)
- bs->change_cb(bs->change_opaque);
+ bs->change_cb(bs->change_opaque, CHANGE_MEDIA);
}
+
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 == bs_snapshots) {
+ bs_snapshots = NULL;
+ }
+ 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)
- bs->change_cb(bs->change_opaque);
+ bs->change_cb(bs->change_opaque, CHANGE_MEDIA);
+ }
+}
+
+void bdrv_close_all(void)
+{
+ BlockDriverState *bs;
+
+ QTAILQ_FOREACH(bs, &bdrv_states, list) {
+ bdrv_close(bs);
}
}
void bdrv_delete(BlockDriverState *bs)
{
- BlockDriverState **pbs;
+ assert(!bs->peer);
- 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);
+ }
+
+ assert(bs != bs_snapshots);
qemu_free(bs);
}
+int bdrv_attach(BlockDriverState *bs, DeviceState *qdev)
+{
+ if (bs->peer) {
+ return -EBUSY;
+ }
+ bs->peer = qdev;
+ return 0;
+}
+
+void bdrv_detach(BlockDriverState *bs, DeviceState *qdev)
+{
+ assert(bs->peer == qdev);
+ bs->peer = NULL;
+}
+
+DeviceState *bdrv_get_attached(BlockDriverState *bs)
+{
+ return bs->peer;
+}
+
/*
* Run consistency checks on an image
*
- * Returns the number of errors or -errno when an internal error occurs
+ * Returns 0 if the check could be completed (it doesn't mean that the image is
+ * free of errors) or -errno when an internal error occured. The results of the
+ * check are stored in res.
*/
-int bdrv_check(BlockDriverState *bs)
+int bdrv_check(BlockDriverState *bs, BdrvCheckResult *res)
{
if (bs->drv->bdrv_check == NULL) {
return -ENOTSUP;
}
- return bs->drv->bdrv_check(bs);
+ memset(res, 0, sizeof(*res));
+ return bs->drv->bdrv_check(bs, res);
}
+#define COMMIT_BUF_SECTORS 2048
+
/* commit COW file into the raw image */
int bdrv_commit(BlockDriverState *bs)
{
BlockDriver *drv = bs->drv;
- int64_t i, total_sectors;
- int n, j, ro, open_flags;
+ BlockDriver *backing_drv;
+ int64_t sector, total_sectors;
+ int n, ro, open_flags;
int ret = 0, rw_ret = 0;
- unsigned char sector[512];
+ uint8_t *buf;
char filename[1024];
BlockDriverState *bs_rw, *bs_ro;
if (bs->backing_hd->keep_read_only) {
return -EACCES;
}
-
+
+ backing_drv = bs->backing_hd->drv;
ro = bs->backing_hd->read_only;
strncpy(filename, bs->backing_hd->filename, sizeof(filename));
open_flags = bs->backing_hd->open_flags;
bdrv_delete(bs->backing_hd);
bs->backing_hd = NULL;
bs_rw = bdrv_new("");
- rw_ret = bdrv_open2(bs_rw, filename, open_flags | BDRV_O_RDWR, NULL);
+ rw_ret = bdrv_open(bs_rw, filename, open_flags | BDRV_O_RDWR,
+ backing_drv);
if (rw_ret < 0) {
bdrv_delete(bs_rw);
/* try to re-open read-only */
bs_ro = bdrv_new("");
- ret = bdrv_open2(bs_ro, filename, open_flags & ~BDRV_O_RDWR, NULL);
+ ret = bdrv_open(bs_ro, filename, open_flags & ~BDRV_O_RDWR,
+ backing_drv);
if (ret < 0) {
bdrv_delete(bs_ro);
/* drive not functional anymore */
}
total_sectors = bdrv_getlength(bs) >> BDRV_SECTOR_BITS;
- for (i = 0; i < total_sectors;) {
- if (drv->bdrv_is_allocated(bs, i, 65536, &n)) {
- for(j = 0; j < n; j++) {
- if (bdrv_read(bs, i, sector, 1) != 0) {
- ret = -EIO;
- goto ro_cleanup;
- }
+ buf = qemu_malloc(COMMIT_BUF_SECTORS * BDRV_SECTOR_SIZE);
- if (bdrv_write(bs->backing_hd, i, sector, 1) != 0) {
- ret = -EIO;
- goto ro_cleanup;
- }
- i++;
- }
- } else {
- i += n;
+ for (sector = 0; sector < total_sectors; sector += n) {
+ if (drv->bdrv_is_allocated(bs, sector, COMMIT_BUF_SECTORS, &n)) {
+
+ if (bdrv_read(bs, sector, buf, n) != 0) {
+ ret = -EIO;
+ goto ro_cleanup;
+ }
+
+ if (bdrv_write(bs->backing_hd, sector, buf, n) != 0) {
+ ret = -EIO;
+ goto ro_cleanup;
+ }
}
}
bdrv_flush(bs->backing_hd);
ro_cleanup:
+ qemu_free(buf);
if (ro) {
/* re-open as RO */
bdrv_delete(bs->backing_hd);
bs->backing_hd = NULL;
bs_ro = bdrv_new("");
- ret = bdrv_open2(bs_ro, filename, open_flags & ~BDRV_O_RDWR, NULL);
+ ret = bdrv_open(bs_ro, filename, open_flags & ~BDRV_O_RDWR,
+ backing_drv);
if (ret < 0) {
bdrv_delete(bs_ro);
/* drive not functional anymore */
return ret;
}
+void bdrv_commit_all(void)
+{
+ BlockDriverState *bs;
+
+ QTAILQ_FOREACH(bs, &bdrv_states, list) {
+ bdrv_commit(bs);
+ }
+}
+
/*
* Return values:
* 0 - success
static int bdrv_check_request(BlockDriverState *bs, int64_t sector_num,
int nb_sectors)
{
- return bdrv_check_byte_request(bs, sector_num * 512, nb_sectors * 512);
+ return bdrv_check_byte_request(bs, sector_num * BDRV_SECTOR_SIZE,
+ nb_sectors * BDRV_SECTOR_SIZE);
}
/* return < 0 if error. See bdrv_write() for the return codes */
bit = start % (sizeof(unsigned long) * 8);
val = bs->dirty_bitmap[idx];
if (dirty) {
- if (!(val & (1 << bit))) {
+ if (!(val & (1UL << bit))) {
bs->dirty_count++;
- val |= 1 << bit;
+ val |= 1UL << bit;
}
} else {
- if (val & (1 << bit)) {
+ if (val & (1UL << bit)) {
bs->dirty_count--;
- val &= ~(1 << bit);
+ val &= ~(1UL << bit);
}
}
bs->dirty_bitmap[idx] = val;
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);
}
return count1;
}
+/*
+ * Writes to the file and ensures that no writes are reordered across this
+ * request (acts as a barrier)
+ *
+ * Returns 0 on success, -errno in error cases.
+ */
+int bdrv_pwrite_sync(BlockDriverState *bs, int64_t offset,
+ const void *buf, int count)
+{
+ int ret;
+
+ ret = bdrv_pwrite(bs, offset, buf, count);
+ if (ret < 0) {
+ return ret;
+ }
+
+ /* No flush needed for cache=writethrough, it uses O_DSYNC */
+ if ((bs->open_flags & BDRV_O_CACHE_MASK) != 0) {
+ bdrv_flush(bs);
+ }
+
+ return 0;
+}
+
+/*
+ * Writes to the file and ensures that no writes are reordered across this
+ * request (acts as a barrier)
+ *
+ * Returns 0 on success, -errno in error cases.
+ */
+int bdrv_write_sync(BlockDriverState *bs, int64_t sector_num,
+ const uint8_t *buf, int nb_sectors)
+{
+ return bdrv_pwrite_sync(bs, BDRV_SECTOR_SIZE * sector_num,
+ buf, BDRV_SECTOR_SIZE * nb_sectors);
+}
+
/**
* Truncate file to 'offset' bytes (needed only for file protocols)
*/
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);
+ if (bdrv_in_use(bs))
+ return -EBUSY;
+ ret = drv->bdrv_truncate(bs, offset);
+ if (ret == 0) {
+ ret = refresh_total_sectors(bs, offset >> BDRV_SECTOR_BITS);
+ if (bs->change_cb) {
+ bs->change_cb(bs->change_opaque, CHANGE_SIZE);
+ }
+ }
+ 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);
static int guess_disk_lchs(BlockDriverState *bs,
int *pcylinders, int *pheads, int *psectors)
{
- uint8_t buf[512];
+ uint8_t buf[BDRV_SECTOR_SIZE];
int ret, i, heads, sectors, cylinders;
struct partition *p;
uint32_t nr_sects;
bs->secs = secs;
}
-void bdrv_set_type_hint(BlockDriverState *bs, int type)
+void bdrv_set_type_hint(BlockDriverState *bs, int type)
+{
+ bs->type = type;
+ bs->removable = ((type == BDRV_TYPE_CDROM ||
+ type == BDRV_TYPE_FLOPPY));
+}
+
+void bdrv_set_translation_hint(BlockDriverState *bs, int translation)
+{
+ bs->translation = translation;
+}
+
+void bdrv_get_geometry_hint(BlockDriverState *bs,
+ int *pcyls, int *pheads, int *psecs)
+{
+ *pcyls = bs->cyls;
+ *pheads = bs->heads;
+ *psecs = bs->secs;
+}
+
+/* Recognize floppy formats */
+typedef struct FDFormat {
+ FDriveType drive;
+ uint8_t last_sect;
+ uint8_t max_track;
+ uint8_t max_head;
+} FDFormat;
+
+static const FDFormat fd_formats[] = {
+ /* First entry is default format */
+ /* 1.44 MB 3"1/2 floppy disks */
+ { FDRIVE_DRV_144, 18, 80, 1, },
+ { FDRIVE_DRV_144, 20, 80, 1, },
+ { FDRIVE_DRV_144, 21, 80, 1, },
+ { FDRIVE_DRV_144, 21, 82, 1, },
+ { FDRIVE_DRV_144, 21, 83, 1, },
+ { FDRIVE_DRV_144, 22, 80, 1, },
+ { FDRIVE_DRV_144, 23, 80, 1, },
+ { FDRIVE_DRV_144, 24, 80, 1, },
+ /* 2.88 MB 3"1/2 floppy disks */
+ { FDRIVE_DRV_288, 36, 80, 1, },
+ { FDRIVE_DRV_288, 39, 80, 1, },
+ { FDRIVE_DRV_288, 40, 80, 1, },
+ { FDRIVE_DRV_288, 44, 80, 1, },
+ { FDRIVE_DRV_288, 48, 80, 1, },
+ /* 720 kB 3"1/2 floppy disks */
+ { FDRIVE_DRV_144, 9, 80, 1, },
+ { FDRIVE_DRV_144, 10, 80, 1, },
+ { FDRIVE_DRV_144, 10, 82, 1, },
+ { FDRIVE_DRV_144, 10, 83, 1, },
+ { FDRIVE_DRV_144, 13, 80, 1, },
+ { FDRIVE_DRV_144, 14, 80, 1, },
+ /* 1.2 MB 5"1/4 floppy disks */
+ { FDRIVE_DRV_120, 15, 80, 1, },
+ { FDRIVE_DRV_120, 18, 80, 1, },
+ { FDRIVE_DRV_120, 18, 82, 1, },
+ { FDRIVE_DRV_120, 18, 83, 1, },
+ { FDRIVE_DRV_120, 20, 80, 1, },
+ /* 720 kB 5"1/4 floppy disks */
+ { FDRIVE_DRV_120, 9, 80, 1, },
+ { FDRIVE_DRV_120, 11, 80, 1, },
+ /* 360 kB 5"1/4 floppy disks */
+ { FDRIVE_DRV_120, 9, 40, 1, },
+ { FDRIVE_DRV_120, 9, 40, 0, },
+ { FDRIVE_DRV_120, 10, 41, 1, },
+ { FDRIVE_DRV_120, 10, 42, 1, },
+ /* 320 kB 5"1/4 floppy disks */
+ { FDRIVE_DRV_120, 8, 40, 1, },
+ { FDRIVE_DRV_120, 8, 40, 0, },
+ /* 360 kB must match 5"1/4 better than 3"1/2... */
+ { FDRIVE_DRV_144, 9, 80, 0, },
+ /* end */
+ { FDRIVE_DRV_NONE, -1, -1, 0, },
+};
+
+void bdrv_get_floppy_geometry_hint(BlockDriverState *bs, int *nb_heads,
+ int *max_track, int *last_sect,
+ FDriveType drive_in, FDriveType *drive)
+{
+ const FDFormat *parse;
+ uint64_t nb_sectors, size;
+ int i, first_match, match;
+
+ bdrv_get_geometry_hint(bs, nb_heads, max_track, last_sect);
+ if (*nb_heads != 0 && *max_track != 0 && *last_sect != 0) {
+ /* User defined disk */
+ } else {
+ bdrv_get_geometry(bs, &nb_sectors);
+ match = -1;
+ first_match = -1;
+ for (i = 0; ; i++) {
+ parse = &fd_formats[i];
+ if (parse->drive == FDRIVE_DRV_NONE) {
+ break;
+ }
+ if (drive_in == parse->drive ||
+ drive_in == FDRIVE_DRV_NONE) {
+ size = (parse->max_head + 1) * parse->max_track *
+ parse->last_sect;
+ if (nb_sectors == size) {
+ match = i;
+ break;
+ }
+ if (first_match == -1) {
+ first_match = i;
+ }
+ }
+ }
+ if (match == -1) {
+ if (first_match == -1) {
+ match = 1;
+ } else {
+ match = first_match;
+ }
+ parse = &fd_formats[match];
+ }
+ *nb_heads = parse->max_head + 1;
+ *max_track = parse->max_track;
+ *last_sect = parse->last_sect;
+ *drive = parse->drive;
+ }
+}
+
+int bdrv_get_type_hint(BlockDriverState *bs)
{
- bs->type = type;
- bs->removable = ((type == BDRV_TYPE_CDROM ||
- type == BDRV_TYPE_FLOPPY));
+ return bs->type;
}
-void bdrv_set_translation_hint(BlockDriverState *bs, int translation)
+int bdrv_get_translation_hint(BlockDriverState *bs)
{
- bs->translation = translation;
+ return bs->translation;
}
-void bdrv_get_geometry_hint(BlockDriverState *bs,
- int *pcyls, int *pheads, int *psecs)
+void bdrv_set_on_error(BlockDriverState *bs, BlockErrorAction on_read_error,
+ BlockErrorAction on_write_error)
{
- *pcyls = bs->cyls;
- *pheads = bs->heads;
- *psecs = bs->secs;
+ bs->on_read_error = on_read_error;
+ bs->on_write_error = on_write_error;
}
-int bdrv_get_type_hint(BlockDriverState *bs)
+BlockErrorAction bdrv_get_on_error(BlockDriverState *bs, int is_read)
{
- return bs->type;
+ return is_read ? bs->on_read_error : bs->on_write_error;
}
-int bdrv_get_translation_hint(BlockDriverState *bs)
+void bdrv_set_removable(BlockDriverState *bs, int removable)
{
- return bs->translation;
+ bs->removable = removable;
+ if (removable && bs == bs_snapshots) {
+ bs_snapshots = NULL;
+ }
}
int bdrv_is_removable(BlockDriverState *bs)
/* XXX: no longer used */
void bdrv_set_change_cb(BlockDriverState *bs,
- void (*change_cb)(void *opaque), void *opaque)
+ void (*change_cb)(void *opaque, int reason),
+ void *opaque)
{
bs->change_cb = change_cb;
bs->change_opaque = opaque;
/* call the change callback now, we skipped it on open */
bs->media_changed = 1;
if (bs->change_cb)
- bs->change_cb(bs->change_opaque);
+ bs->change_cb(bs->change_opaque, CHANGE_MEDIA);
}
return ret;
}
{
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 *bdrv_next(BlockDriverState *bs)
+{
+ if (!bs) {
+ return QTAILQ_FIRST(&bdrv_states);
+ }
+ return QTAILQ_NEXT(bs, list);
+}
+
void bdrv_iterate(void (*it)(void *opaque, BlockDriverState *bs), void *opaque)
{
BlockDriverState *bs;
- for (bs = bdrv_first; bs != NULL; bs = bs->next) {
+ QTAILQ_FOREACH(bs, &bdrv_states, list) {
it(opaque, bs);
}
}
return bs->device_name;
}
-void bdrv_flush(BlockDriverState *bs)
+int bdrv_flush(BlockDriverState *bs)
{
- if (bs->drv && bs->drv->bdrv_flush)
- bs->drv->bdrv_flush(bs);
+ if (bs->open_flags & BDRV_O_NO_FLUSH) {
+ return 0;
+ }
+
+ if (bs->drv && bs->drv->bdrv_flush) {
+ return bs->drv->bdrv_flush(bs);
+ }
+
+ /*
+ * Some block drivers always operate in either writethrough or unsafe mode
+ * and don't support bdrv_flush therefore. Usually qemu doesn't know how
+ * the server works (because the behaviour is hardcoded or depends on
+ * server-side configuration), so we can't ensure that everything is safe
+ * on disk. Returning an error doesn't work because that would break guests
+ * even if the server operates in writethrough mode.
+ *
+ * Let's hope the user knows what he's doing.
+ */
+ return 0;
}
void bdrv_flush_all(void)
{
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->bdrv_has_zero_init) {
+ return bs->drv->bdrv_has_zero_init(bs);
+ }
+
+ return 1;
+}
+
+int bdrv_discard(BlockDriverState *bs, int64_t sector_num, int nb_sectors)
+{
+ if (!bs->drv) {
+ return -ENOMEDIUM;
+ }
+ if (!bs->drv->bdrv_discard) {
+ return 0;
+ }
+ return bs->drv->bdrv_discard(bs, sector_num, nb_sectors);
}
/*
qlist_iter(qobject_to_qlist(data), bdrv_print_dict, mon);
}
-/**
- * bdrv_info(): Block devices information
- *
- * Each block device information is stored in a QDict and the
- * returned QObject is a QList of all devices.
- *
- * The QDict contains the following:
- *
- * - "device": device name
- * - "type": device type
- * - "removable": true if the device is removable, false otherwise
- * - "locked": true if the device is locked, false otherwise
- * - "inserted": only present if the device is inserted, it is a QDict
- * containing the following:
- * - "file": device file name
- * - "ro": true if read-only, false otherwise
- * - "drv": driver format name
- * - "backing_file": backing file name if one is used
- * - "encrypted": true if encrypted, false otherwise
- *
- * Example:
- *
- * [ { "device": "ide0-hd0", "type": "hd", "removable": false, "locked": false,
- * "inserted": { "file": "/tmp/foobar", "ro": false, "drv": "qcow2" } },
- * { "device": "floppy0", "type": "floppy", "removable": true,
- * "locked": false } ]
- */
void bdrv_info(Monitor *mon, QObject **ret_data)
{
QList *bs_list;
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);
}
-/**
- * bdrv_info_stats(): show block device statistics
- *
- * Each device statistic information is stored in a QDict and
- * the returned QObject is a QList of all devices.
- *
- * The QDict contains the following:
- *
- * - "device": device name
- * - "stats": A QDict with the statistics information, it contains:
- * - "rd_bytes": bytes read
- * - "wr_bytes": bytes written
- * - "rd_operations": read operations
- * - "wr_operations": write operations
- *
- * Example:
- *
- * [ { "device": "ide0-hd0",
- * "stats": { "rd_bytes": 512,
- * "wr_bytes": 0,
- * "rd_operations": 1,
- * "wr_operations": 0 } },
- * { "device": "ide1-cd0",
- * "stats": { "rd_bytes": 0,
- * "wr_bytes": 0,
- * "rd_operations": 0,
- * "wr_operations": 0 } } ]
- */
+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 *
+ (uint64_t)BDRV_SECTOR_SIZE);
+ 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;
+}
+
void bdrv_info_stats(Monitor *mon, QObject **ret_data)
{
QObject *obj;
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);
}
BlockDriver *drv = bs->drv;
if (!drv)
return -ENOMEDIUM;
- if (!drv->bdrv_save_vmstate)
- return -ENOTSUP;
- return drv->bdrv_save_vmstate(bs, buf, pos, size);
+ if (drv->bdrv_save_vmstate)
+ return drv->bdrv_save_vmstate(bs, buf, pos, size);
+ if (bs->file)
+ return bdrv_save_vmstate(bs->file, buf, pos, size);
+ return -ENOTSUP;
}
int bdrv_load_vmstate(BlockDriverState *bs, uint8_t *buf,
BlockDriver *drv = bs->drv;
if (!drv)
return -ENOMEDIUM;
- if (!drv->bdrv_load_vmstate)
- return -ENOTSUP;
- return drv->bdrv_load_vmstate(bs, buf, pos, size);
+ if (drv->bdrv_load_vmstate)
+ return drv->bdrv_load_vmstate(bs, buf, pos, size);
+ if (bs->file)
+ return bdrv_load_vmstate(bs->file, buf, pos, size);
+ return -ENOTSUP;
+}
+
+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 */
+int bdrv_can_snapshot(BlockDriverState *bs)
+{
+ BlockDriver *drv = bs->drv;
+ if (!drv || bdrv_is_removable(bs) || bdrv_is_read_only(bs)) {
+ return 0;
+ }
+
+ if (!drv->bdrv_snapshot_create) {
+ if (bs->file != NULL) {
+ return bdrv_can_snapshot(bs->file);
+ }
+ return 0;
+ }
+
+ return 1;
+}
+
+int bdrv_is_snapshot(BlockDriverState *bs)
+{
+ return !!(bs->open_flags & BDRV_O_SNAPSHOT);
+}
+
+BlockDriverState *bdrv_snapshots(void)
+{
+ BlockDriverState *bs;
+
+ if (bs_snapshots) {
+ return bs_snapshots;
+ }
+
+ bs = NULL;
+ while ((bs = bdrv_next(bs))) {
+ if (bdrv_can_snapshot(bs)) {
+ bs_snapshots = bs;
+ return bs;
+ }
+ }
+ return NULL;
+}
+
int bdrv_snapshot_create(BlockDriverState *bs,
QEMUSnapshotInfo *sn_info)
{
BlockDriver *drv = bs->drv;
if (!drv)
return -ENOMEDIUM;
- if (!drv->bdrv_snapshot_create)
- return -ENOTSUP;
- return drv->bdrv_snapshot_create(bs, sn_info);
+ if (drv->bdrv_snapshot_create)
+ return drv->bdrv_snapshot_create(bs, sn_info);
+ if (bs->file)
+ return bdrv_snapshot_create(bs->file, sn_info);
+ return -ENOTSUP;
}
int bdrv_snapshot_goto(BlockDriverState *bs,
const char *snapshot_id)
{
BlockDriver *drv = bs->drv;
+ int ret, open_ret;
+
if (!drv)
return -ENOMEDIUM;
- if (!drv->bdrv_snapshot_goto)
- return -ENOTSUP;
- return drv->bdrv_snapshot_goto(bs, snapshot_id);
+ if (drv->bdrv_snapshot_goto)
+ return drv->bdrv_snapshot_goto(bs, snapshot_id);
+
+ if (bs->file) {
+ drv->bdrv_close(bs);
+ ret = bdrv_snapshot_goto(bs->file, snapshot_id);
+ open_ret = drv->bdrv_open(bs, bs->open_flags);
+ if (open_ret < 0) {
+ bdrv_delete(bs->file);
+ bs->drv = NULL;
+ return open_ret;
+ }
+ return ret;
+ }
+
+ return -ENOTSUP;
}
int bdrv_snapshot_delete(BlockDriverState *bs, const char *snapshot_id)
BlockDriver *drv = bs->drv;
if (!drv)
return -ENOMEDIUM;
- if (!drv->bdrv_snapshot_delete)
- return -ENOTSUP;
- return drv->bdrv_snapshot_delete(bs, snapshot_id);
+ if (drv->bdrv_snapshot_delete)
+ return drv->bdrv_snapshot_delete(bs, snapshot_id);
+ if (bs->file)
+ return bdrv_snapshot_delete(bs->file, snapshot_id);
+ return -ENOTSUP;
}
int bdrv_snapshot_list(BlockDriverState *bs,
BlockDriver *drv = bs->drv;
if (!drv)
return -ENOMEDIUM;
- if (!drv->bdrv_snapshot_list)
- return -ENOTSUP;
- return drv->bdrv_snapshot_list(bs, psn_info);
+ if (drv->bdrv_snapshot_list)
+ return drv->bdrv_snapshot_list(bs, psn_info);
+ if (bs->file)
+ return bdrv_snapshot_list(bs->file, psn_info);
+ return -ENOTSUP;
+}
+
+int bdrv_snapshot_load_tmp(BlockDriverState *bs,
+ const char *snapshot_name)
+{
+ BlockDriver *drv = bs->drv;
+ if (!drv) {
+ return -ENOMEDIUM;
+ }
+ if (!bs->read_only) {
+ return -EINVAL;
+ }
+ if (drv->bdrv_snapshot_load_tmp) {
+ return drv->bdrv_snapshot_load_tmp(bs, snapshot_name);
+ }
+ return -ENOTSUP;
}
#define NB_SUFFIXES 4
BlockDriver *drv = bs->drv;
BlockDriverAIOCB *ret;
+ trace_bdrv_aio_readv(bs, sector_num, nb_sectors, opaque);
+
if (!drv)
return NULL;
if (bdrv_check_request(bs, sector_num, nb_sectors))
return ret;
}
+typedef struct BlockCompleteData {
+ BlockDriverCompletionFunc *cb;
+ void *opaque;
+ BlockDriverState *bs;
+ int64_t sector_num;
+ int nb_sectors;
+} BlockCompleteData;
+
+static void block_complete_cb(void *opaque, int ret)
+{
+ BlockCompleteData *b = opaque;
+
+ if (b->bs->dirty_bitmap) {
+ set_dirty_bitmap(b->bs, b->sector_num, b->nb_sectors, 1);
+ }
+ b->cb(b->opaque, ret);
+ qemu_free(b);
+}
+
+static BlockCompleteData *blk_dirty_cb_alloc(BlockDriverState *bs,
+ int64_t sector_num,
+ int nb_sectors,
+ BlockDriverCompletionFunc *cb,
+ void *opaque)
+{
+ BlockCompleteData *blkdata = qemu_mallocz(sizeof(BlockCompleteData));
+
+ blkdata->bs = bs;
+ blkdata->cb = cb;
+ blkdata->opaque = opaque;
+ blkdata->sector_num = sector_num;
+ blkdata->nb_sectors = nb_sectors;
+
+ return blkdata;
+}
+
BlockDriverAIOCB *bdrv_aio_writev(BlockDriverState *bs, int64_t sector_num,
QEMUIOVector *qiov, int nb_sectors,
BlockDriverCompletionFunc *cb, void *opaque)
{
BlockDriver *drv = bs->drv;
BlockDriverAIOCB *ret;
+ BlockCompleteData *blk_cb_data;
+
+ trace_bdrv_aio_writev(bs, sector_num, nb_sectors, opaque);
if (!drv)
return NULL;
return NULL;
if (bs->dirty_bitmap) {
- set_dirty_bitmap(bs, sector_num, nb_sectors, 1);
+ blk_cb_data = blk_dirty_cb_alloc(bs, sector_num, nb_sectors, cb,
+ opaque);
+ cb = &block_complete_cb;
+ opaque = blk_cb_data;
}
ret = drv->bdrv_aio_writev(bs, sector_num, qiov, nb_sectors,
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;
+ trace_multiwrite_cb(mcb, ret);
+
if (ret < 0 && !mcb->error) {
mcb->error = ret;
- multiwrite_user_cb(mcb);
}
mcb->num_requests--;
if (mcb->num_requests == 0) {
- if (mcb->error == 0) {
- multiwrite_user_cb(mcb);
- }
+ multiwrite_user_cb(mcb);
qemu_free(mcb);
}
}
static int multiwrite_req_compare(const void *a, const void *b)
{
- return (((BlockRequest*) a)->sector - ((BlockRequest*) b)->sector);
+ const BlockRequest *req1 = a, *req2 = b;
+
+ /*
+ * Note that we can't simply subtract req2->sector from req1->sector
+ * here as that could overflow the return value.
+ */
+ if (req1->sector > req2->sector) {
+ return 1;
+ } else if (req1->sector < req2->sector) {
+ return -1;
+ } else {
+ return 0;
+ }
}
/*
// Add the second request
qemu_iovec_concat(qiov, reqs[i].qiov, reqs[i].qiov->size);
- reqs[outidx].nb_sectors += reqs[i].nb_sectors;
+ reqs[outidx].nb_sectors = qiov->size >> 9;
reqs[outidx].qiov = qiov;
mcb->callbacks[i].free_qiov = reqs[outidx].qiov;
MultiwriteCB *mcb;
int i;
+ /* don't submit writes if we don't have a medium */
+ if (bs->drv == NULL) {
+ for (i = 0; i < num_reqs; i++) {
+ reqs[i].error = -ENOMEDIUM;
+ }
+ return -1;
+ }
+
if (num_reqs == 0) {
return 0;
}
// Check for mergable requests
num_reqs = multiwrite_merge(bs, reqs, num_reqs, mcb);
+ trace_bdrv_aio_multiwrite(mcb, mcb->num_callbacks, num_reqs);
+
+ /*
+ * Run the aio requests. As soon as one request can't be submitted
+ * successfully, fail all requests that are not yet submitted (we must
+ * return failure for all requests anyway)
+ *
+ * num_requests cannot be set to the right value immediately: If
+ * bdrv_aio_writev fails for some request, num_requests would be too high
+ * and therefore multiwrite_cb() would never recognize the multiwrite
+ * request as completed. We also cannot use the loop variable i to set it
+ * when the first request fails because the callback may already have been
+ * called for previously submitted requests. Thus, num_requests must be
+ * incremented for each request that is submitted.
+ *
+ * The problem that callbacks may be called early also means that we need
+ * to take care that num_requests doesn't become 0 before all requests are
+ * submitted - multiwrite_cb() would consider the multiwrite request
+ * completed. A dummy request that is "completed" by a manual call to
+ * multiwrite_cb() takes care of this.
+ */
+ mcb->num_requests = 1;
+
// Run the aio requests
for (i = 0; i < num_reqs; i++) {
+ mcb->num_requests++;
acb = bdrv_aio_writev(bs, reqs[i].sector, reqs[i].qiov,
reqs[i].nb_sectors, multiwrite_cb, mcb);
// We can only fail the whole thing if no request has been
// 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;
+ if (i == 0) {
+ trace_bdrv_aio_multiwrite_earlyfail(mcb);
goto fail;
} else {
- mcb->error = -EIO;
+ trace_bdrv_aio_multiwrite_latefail(mcb, i);
+ multiwrite_cb(mcb, -EIO);
break;
}
- } else {
- mcb->num_requests++;
}
}
+ /* Complete the dummy request */
+ multiwrite_cb(mcb, 0);
+
return 0;
fail:
- free(mcb);
+ for (i = 0; i < mcb->num_callbacks; i++) {
+ reqs[i].error = -EIO;
+ }
+ qemu_free(mcb);
return -1;
}
{
BlockDriver *drv = bs->drv;
+ trace_bdrv_aio_flush(bs, opaque);
+
+ if (bs->open_flags & BDRV_O_NO_FLUSH) {
+ return bdrv_aio_noop_em(bs, cb, opaque);
+ }
+
if (!drv)
return NULL;
return drv->bdrv_aio_flush(bs, cb, opaque);
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);
return &acb->common;
}
+static BlockDriverAIOCB *bdrv_aio_noop_em(BlockDriverState *bs,
+ BlockDriverCompletionFunc *cb, void *opaque)
+{
+ BlockDriverAIOCBSync *acb;
+
+ acb = qemu_aio_get(&bdrv_em_aio_pool, bs, cb, opaque);
+ acb->is_write = 1; /* don't bounce in the completion handler */
+ acb->qiov = NULL;
+ acb->bounce = NULL;
+ acb->ret = 0;
+
+ if (!acb->bh) {
+ acb->bh = qemu_bh_new(bdrv_aio_bh_cb, acb);
+ }
+
+ qemu_bh_schedule(acb->bh);
+ return &acb->common;
+}
+
/**************************************************************/
/* sync block device emulation */
async_ret = NOT_DONE;
iov.iov_base = (void *)buf;
- iov.iov_len = nb_sectors * 512;
+ iov.iov_len = nb_sectors * BDRV_SECTOR_SIZE;
qemu_iovec_init_external(&qiov, &iov, 1);
acb = bdrv_aio_readv(bs, sector_num, &qiov, nb_sectors,
bdrv_rw_em_cb, &async_ret);
async_ret = NOT_DONE;
iov.iov_base = (void *)buf;
- iov.iov_len = nb_sectors * 512;
+ iov.iov_len = nb_sectors * BDRV_SECTOR_SIZE;
qemu_iovec_init_external(&qiov, &iov, 1);
acb = bdrv_aio_writev(bs, sector_num, &qiov, nb_sectors,
bdrv_rw_em_cb, &async_ret);
if (!drv)
return 0;
if (!drv->bdrv_is_inserted)
- return 1;
+ return !bs->tray_open;
ret = drv->bdrv_is_inserted(bs);
return ret;
}
ret = drv->bdrv_eject(bs, eject_flag);
}
if (ret == -ENOTSUP) {
- if (eject_flag)
- bdrv_close(bs);
ret = 0;
}
+ if (ret >= 0) {
+ bs->tray_open = eject_flag;
+ }
return ret;
}
if (bs->dirty_bitmap &&
(sector << BDRV_SECTOR_BITS) < bdrv_getlength(bs)) {
- return bs->dirty_bitmap[chunk / (sizeof(unsigned long) * 8)] &
- (1 << (chunk % (sizeof(unsigned long) * 8)));
+ return !!(bs->dirty_bitmap[chunk / (sizeof(unsigned long) * 8)] &
+ (1UL << (chunk % (sizeof(unsigned long) * 8))));
} else {
return 0;
}
{
return bs->dirty_count;
}
+
+void bdrv_set_in_use(BlockDriverState *bs, int in_use)
+{
+ assert(bs->in_use != in_use);
+ bs->in_use = in_use;
+}
+
+int bdrv_in_use(BlockDriverState *bs)
+{
+ return bs->in_use;
+}
+
+int bdrv_img_create(const char *filename, const char *fmt,
+ const char *base_filename, const char *base_fmt,
+ char *options, uint64_t img_size, int flags)
+{
+ QEMUOptionParameter *param = NULL, *create_options = NULL;
+ QEMUOptionParameter *backing_fmt, *backing_file;
+ BlockDriverState *bs = NULL;
+ BlockDriver *drv, *proto_drv;
+ BlockDriver *backing_drv = NULL;
+ int ret = 0;
+
+ /* Find driver and parse its options */
+ drv = bdrv_find_format(fmt);
+ if (!drv) {
+ error_report("Unknown file format '%s'", fmt);
+ ret = -EINVAL;
+ goto out;
+ }
+
+ proto_drv = bdrv_find_protocol(filename);
+ if (!proto_drv) {
+ error_report("Unknown protocol '%s'", filename);
+ ret = -EINVAL;
+ goto out;
+ }
+
+ create_options = append_option_parameters(create_options,
+ drv->create_options);
+ create_options = append_option_parameters(create_options,
+ proto_drv->create_options);
+
+ /* Create parameter list with default values */
+ param = parse_option_parameters("", create_options, param);
+
+ set_option_parameter_int(param, BLOCK_OPT_SIZE, img_size);
+
+ /* Parse -o options */
+ if (options) {
+ param = parse_option_parameters(options, create_options, param);
+ if (param == NULL) {
+ error_report("Invalid options for file format '%s'.", fmt);
+ ret = -EINVAL;
+ goto out;
+ }
+ }
+
+ if (base_filename) {
+ if (set_option_parameter(param, BLOCK_OPT_BACKING_FILE,
+ base_filename)) {
+ error_report("Backing file not supported for file format '%s'",
+ fmt);
+ ret = -EINVAL;
+ goto out;
+ }
+ }
+
+ if (base_fmt) {
+ if (set_option_parameter(param, BLOCK_OPT_BACKING_FMT, base_fmt)) {
+ error_report("Backing file format not supported for file "
+ "format '%s'", fmt);
+ ret = -EINVAL;
+ goto out;
+ }
+ }
+
+ backing_file = get_option_parameter(param, BLOCK_OPT_BACKING_FILE);
+ if (backing_file && backing_file->value.s) {
+ if (!strcmp(filename, backing_file->value.s)) {
+ error_report("Error: Trying to create an image with the "
+ "same filename as the backing file");
+ ret = -EINVAL;
+ goto out;
+ }
+ }
+
+ backing_fmt = get_option_parameter(param, BLOCK_OPT_BACKING_FMT);
+ if (backing_fmt && backing_fmt->value.s) {
+ backing_drv = bdrv_find_format(backing_fmt->value.s);
+ if (!backing_drv) {
+ error_report("Unknown backing file format '%s'",
+ backing_fmt->value.s);
+ ret = -EINVAL;
+ goto out;
+ }
+ }
+
+ // The size for the image must always be specified, with one exception:
+ // If we are using a backing file, we can obtain the size from there
+ if (get_option_parameter(param, BLOCK_OPT_SIZE)->value.n == -1) {
+ if (backing_file && backing_file->value.s) {
+ uint64_t size;
+ char buf[32];
+
+ bs = bdrv_new("");
+
+ ret = bdrv_open(bs, backing_file->value.s, flags, backing_drv);
+ if (ret < 0) {
+ error_report("Could not open '%s'", backing_file->value.s);
+ goto out;
+ }
+ bdrv_get_geometry(bs, &size);
+ size *= 512;
+
+ snprintf(buf, sizeof(buf), "%" PRId64, size);
+ set_option_parameter(param, BLOCK_OPT_SIZE, buf);
+ } else {
+ error_report("Image creation needs a size parameter");
+ ret = -EINVAL;
+ goto out;
+ }
+ }
+
+ printf("Formatting '%s', fmt=%s ", filename, fmt);
+ print_option_parameters(param);
+ puts("");
+
+ ret = bdrv_create(drv, filename, param);
+
+ if (ret < 0) {
+ if (ret == -ENOTSUP) {
+ error_report("Formatting or formatting option not supported for "
+ "file format '%s'", fmt);
+ } else if (ret == -EFBIG) {
+ error_report("The image size is too large for file format '%s'",
+ fmt);
+ } else {
+ error_report("%s: error while creating %s: %s", filename, fmt,
+ strerror(-ret));
+ }
+ }
+
+out:
+ free_option_parameters(create_options);
+ free_option_parameters(param);
+
+ if (bs) {
+ bdrv_delete(bs);
+ }
+
+ return ret;
+}