#include "block_int.h"
#include "module.h"
#include "qemu-objects.h"
+#include "qemu-coroutine.h"
#ifdef CONFIG_BSD
#include <sys/types.h>
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 BlockDriverAIOCB *bdrv_co_aio_readv_em(BlockDriverState *bs,
+ int64_t sector_num, QEMUIOVector *qiov, int nb_sectors,
+ BlockDriverCompletionFunc *cb, void *opaque);
+static BlockDriverAIOCB *bdrv_co_aio_writev_em(BlockDriverState *bs,
+ int64_t sector_num, QEMUIOVector *qiov, int nb_sectors,
+ BlockDriverCompletionFunc *cb, void *opaque);
+static int coroutine_fn bdrv_co_readv_em(BlockDriverState *bs,
+ int64_t sector_num, int nb_sectors,
+ QEMUIOVector *iov);
+static int coroutine_fn bdrv_co_writev_em(BlockDriverState *bs,
+ int64_t sector_num, int nb_sectors,
+ QEMUIOVector *iov);
+static int coroutine_fn bdrv_co_flush_em(BlockDriverState *bs);
static QTAILQ_HEAD(, BlockDriverState) bdrv_states =
QTAILQ_HEAD_INITIALIZER(bdrv_states);
/* 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;
void bdrv_register(BlockDriver *bdrv)
{
- if (!bdrv->bdrv_aio_readv) {
- /* add AIO emulation layer */
- bdrv->bdrv_aio_readv = bdrv_aio_readv_em;
- bdrv->bdrv_aio_writev = bdrv_aio_writev_em;
- } else if (!bdrv->bdrv_read) {
- /* add synchronous IO emulation layer */
+ if (bdrv->bdrv_co_readv) {
+ /* Emulate AIO by coroutines, and sync by AIO */
+ bdrv->bdrv_aio_readv = bdrv_co_aio_readv_em;
+ bdrv->bdrv_aio_writev = bdrv_co_aio_writev_em;
bdrv->bdrv_read = bdrv_read_em;
bdrv->bdrv_write = bdrv_write_em;
+ } else {
+ bdrv->bdrv_co_readv = bdrv_co_readv_em;
+ bdrv->bdrv_co_writev = bdrv_co_writev_em;
+
+ if (!bdrv->bdrv_aio_readv) {
+ /* add AIO emulation layer */
+ bdrv->bdrv_aio_readv = bdrv_aio_readv_em;
+ bdrv->bdrv_aio_writev = bdrv_aio_writev_em;
+ } else if (!bdrv->bdrv_read) {
+ /* add synchronous IO emulation layer */
+ bdrv->bdrv_read = bdrv_read_em;
+ bdrv->bdrv_write = bdrv_write_em;
+ }
}
if (!bdrv->bdrv_aio_flush)
{
BlockDriverState *bs;
- bs = qemu_mallocz(sizeof(BlockDriverState));
+ bs = g_malloc0(sizeof(BlockDriverState));
pstrcpy(bs->device_name, sizeof(bs->device_name), device_name);
if (device_name[0] != '\0') {
QTAILQ_INSERT_TAIL(&bdrv_states, bs, list);
drv = bdrv_find_protocol(filename);
if (drv == NULL) {
- drv = bdrv_find_format("file");
+ return -ENOENT;
}
return bdrv_create(drv, filename, options);
}
#endif
-#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
-
/*
* Detect host devices. By convention, /dev/cdrom[N] is always
* recognized as a host CDROM.
return drv1;
}
-#ifdef _WIN32
- if (is_windows_drive(filename) ||
- is_windows_drive_prefix(filename))
- return bdrv_find_format("file");
-#endif
-
- p = strchr(filename, ':');
- if (!p) {
+ if (!path_has_protocol(filename)) {
return bdrv_find_format("file");
}
+ p = strchr(filename, ':');
+ assert(p != NULL);
len = p - filename;
if (len > sizeof(protocol) - 1)
len = sizeof(protocol) - 1;
}
bs->drv = drv;
- bs->opaque = qemu_mallocz(drv->instance_size);
+ bs->opaque = g_malloc0(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))
+ if (flags & BDRV_O_CACHE_WB)
bs->enable_write_cache = 1;
/*
open_flags = flags & ~(BDRV_O_SNAPSHOT | BDRV_O_NO_BACKING);
/*
- * Snapshots should be writeable.
+ * Snapshots should be writable.
*/
if (bs->is_temporary) {
open_flags |= BDRV_O_RDWR;
bdrv_delete(bs->file);
bs->file = NULL;
}
- qemu_free(bs->opaque);
+ g_free(bs->opaque);
bs->opaque = NULL;
bs->drv = NULL;
return ret;
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 */
back_flags =
/* 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;
bs->backing_hd = NULL;
}
bs->drv->bdrv_close(bs);
- qemu_free(bs->opaque);
+ g_free(bs->opaque);
#ifdef _WIN32
if (bs->is_temporary) {
unlink(bs->filename);
/* 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);
}
}
}
}
+/* make a BlockDriverState anonymous by removing from bdrv_state list.
+ Also, NULL terminate the device_name to prevent double remove */
+void bdrv_make_anon(BlockDriverState *bs)
+{
+ if (bs->device_name[0] != '\0') {
+ QTAILQ_REMOVE(&bdrv_states, bs, list);
+ }
+ bs->device_name[0] = '\0';
+}
+
void bdrv_delete(BlockDriverState *bs)
{
assert(!bs->peer);
/* remove from list, if necessary */
- if (bs->device_name[0] != '\0') {
- QTAILQ_REMOVE(&bdrv_states, bs, list);
- }
+ bdrv_make_anon(bs);
bdrv_close(bs);
if (bs->file != NULL) {
}
assert(bs != bs_snapshots);
- qemu_free(bs);
+ g_free(bs);
}
int bdrv_attach(BlockDriverState *bs, DeviceState *qdev)
{
assert(bs->peer == qdev);
bs->peer = NULL;
+ bs->change_cb = NULL;
+ bs->change_opaque = NULL;
}
DeviceState *bdrv_get_attached(BlockDriverState *bs)
* Run consistency checks on an image
*
* 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
+ * free of errors) or -errno when an internal error occurred. The results of the
* check are stored in res.
*/
int bdrv_check(BlockDriverState *bs, BdrvCheckResult *res)
}
total_sectors = bdrv_getlength(bs) >> BDRV_SECTOR_BITS;
- buf = qemu_malloc(COMMIT_BUF_SECTORS * BDRV_SECTOR_SIZE);
+ buf = g_malloc(COMMIT_BUF_SECTORS * BDRV_SECTOR_SIZE);
for (sector = 0; sector < total_sectors; sector += n) {
if (drv->bdrv_is_allocated(bs, sector, COMMIT_BUF_SECTORS, &n)) {
bdrv_flush(bs->backing_hd);
ro_cleanup:
- qemu_free(buf);
+ g_free(buf);
if (ro) {
/* re-open as RO */
nb_sectors * BDRV_SECTOR_SIZE);
}
+static inline bool bdrv_has_async_rw(BlockDriver *drv)
+{
+ return drv->bdrv_co_readv != bdrv_co_readv_em
+ || drv->bdrv_aio_readv != bdrv_aio_readv_em;
+}
+
+static inline bool bdrv_has_async_flush(BlockDriver *drv)
+{
+ return drv->bdrv_aio_flush != bdrv_aio_flush_em;
+}
+
/* return < 0 if error. See bdrv_write() for the return codes */
int bdrv_read(BlockDriverState *bs, int64_t sector_num,
uint8_t *buf, int nb_sectors)
if (!drv)
return -ENOMEDIUM;
+
+ if (bdrv_has_async_rw(drv) && qemu_in_coroutine()) {
+ QEMUIOVector qiov;
+ struct iovec iov = {
+ .iov_base = (void *)buf,
+ .iov_len = nb_sectors * BDRV_SECTOR_SIZE,
+ };
+
+ qemu_iovec_init_external(&qiov, &iov, 1);
+ return bdrv_co_readv(bs, sector_num, nb_sectors, &qiov);
+ }
+
if (bdrv_check_request(bs, sector_num, nb_sectors))
return -EIO;
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;
const uint8_t *buf, int nb_sectors)
{
BlockDriver *drv = bs->drv;
+
if (!bs->drv)
return -ENOMEDIUM;
+
+ if (bdrv_has_async_rw(drv) && qemu_in_coroutine()) {
+ QEMUIOVector qiov;
+ struct iovec iov = {
+ .iov_base = (void *)buf,
+ .iov_len = nb_sectors * BDRV_SECTOR_SIZE,
+ };
+
+ qemu_iovec_init_external(&qiov, &iov, 1);
+ return bdrv_co_writev(bs, sector_num, nb_sectors, &qiov);
+ }
+
if (bs->read_only)
return -EACCES;
if (bdrv_check_request(bs, sector_num, nb_sectors))
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)
+int coroutine_fn bdrv_co_readv(BlockDriverState *bs, int64_t sector_num,
+ int nb_sectors, QEMUIOVector *qiov)
{
- return bdrv_pwrite_sync(bs, BDRV_SECTOR_SIZE * sector_num,
- buf, BDRV_SECTOR_SIZE * nb_sectors);
+ BlockDriver *drv = bs->drv;
+
+ trace_bdrv_co_readv(bs, sector_num, nb_sectors);
+
+ if (!drv) {
+ return -ENOMEDIUM;
+ }
+ if (bdrv_check_request(bs, sector_num, nb_sectors)) {
+ return -EIO;
+ }
+
+ return drv->bdrv_co_readv(bs, sector_num, nb_sectors, qiov);
+}
+
+int coroutine_fn bdrv_co_writev(BlockDriverState *bs, int64_t sector_num,
+ int nb_sectors, QEMUIOVector *qiov)
+{
+ BlockDriver *drv = bs->drv;
+
+ trace_bdrv_co_writev(bs, sector_num, nb_sectors);
+
+ if (!bs->drv) {
+ return -ENOMEDIUM;
+ }
+ if (bs->read_only) {
+ return -EACCES;
+ }
+ if (bdrv_check_request(bs, sector_num, nb_sectors)) {
+ return -EIO;
+ }
+
+ if (bs->dirty_bitmap) {
+ 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_co_writev(bs, sector_num, nb_sectors, qiov);
}
/**
return -ENOTSUP;
if (bs->read_only)
return -EACCES;
+ 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;
}
+/**
+ * Length of a allocated file in bytes. Sparse files are counted by actual
+ * allocated space. Return < 0 if error or unknown.
+ */
+int64_t bdrv_get_allocated_file_size(BlockDriverState *bs)
+{
+ BlockDriver *drv = bs->drv;
+ if (!drv) {
+ return -ENOMEDIUM;
+ }
+ if (drv->bdrv_get_allocated_file_size) {
+ return drv->bdrv_get_allocated_file_size(bs);
+ }
+ if (bs->file) {
+ return bdrv_get_allocated_file_size(bs->file);
+ }
+ return -ENOTSUP;
+}
+
/**
* Length of a file in bytes. Return < 0 if error or unknown.
*/
if (!drv)
return -ENOMEDIUM;
- /* 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;
+ if (bs->growable || bs->removable) {
+ if (drv->bdrv_getlength) {
+ return drv->bdrv_getlength(bs);
+ }
}
- return drv->bdrv_getlength(bs);
+ return bs->total_sectors * BDRV_SECTOR_SIZE;
}
/* return 0 as number of sectors if no device present or error */
bs->secs = secs;
}
-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;
*psecs = bs->secs;
}
-int bdrv_get_type_hint(BlockDriverState *bs)
+/* 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)
{
- return bs->type;
+ 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_translation_hint(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;
}
return bs->device_name;
}
-void bdrv_flush(BlockDriverState *bs)
+int bdrv_flush(BlockDriverState *bs)
{
if (bs->open_flags & BDRV_O_NO_FLUSH) {
- return;
+ return 0;
+ }
+
+ if (bs->drv && bdrv_has_async_flush(bs->drv) && qemu_in_coroutine()) {
+ return bdrv_co_flush_em(bs);
+ }
+
+ if (bs->drv && bs->drv->bdrv_flush) {
+ return bs->drv->bdrv_flush(bs);
}
- if (bs->drv && bs->drv->bdrv_flush)
- 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)
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);
+}
+
/*
* Returns true iff the specified sector is present in the disk image. Drivers
* not implementing the functionality are assumed to not support backing files,
bs_dict = qobject_to_qdict(obj);
- monitor_printf(mon, "%s: type=%s removable=%d",
+ monitor_printf(mon, "%s: removable=%d",
qdict_get_str(bs_dict, "device"),
- qdict_get_str(bs_dict, "type"),
qdict_get_bool(bs_dict, "removable"));
if (qdict_get_bool(bs_dict, "removable")) {
QTAILQ_FOREACH(bs, &bdrv_states, list) {
QObject *bs_obj;
- const char *type = "unknown";
-
- switch(bs->type) {
- case BDRV_TYPE_HD:
- type = "hd";
- break;
- case BDRV_TYPE_CDROM:
- type = "cdrom";
- break;
- case BDRV_TYPE_FLOPPY:
- type = "floppy";
- break;
- }
- bs_obj = qobject_from_jsonf("{ 'device': %s, 'type': %s, "
+ bs_obj = qobject_from_jsonf("{ 'device': %s, 'type': 'unknown', "
"'removable': %i, 'locked': %i }",
- bs->device_name, type, bs->removable,
+ bs->device_name, bs->removable,
bs->locked);
if (bs->drv) {
cb, opaque);
if (ret) {
- /* Update stats even though technically transfer has not happened. */
- bs->rd_bytes += (unsigned) nb_sectors * BDRV_SECTOR_SIZE;
- bs->rd_ops ++;
+ /* Update stats even though technically transfer has not happened. */
+ bs->rd_bytes += (unsigned) nb_sectors * BDRV_SECTOR_SIZE;
+ bs->rd_ops++;
}
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);
+ g_free(b);
+}
+
+static BlockCompleteData *blk_dirty_cb_alloc(BlockDriverState *bs,
+ int64_t sector_num,
+ int nb_sectors,
+ BlockDriverCompletionFunc *cb,
+ void *opaque)
+{
+ BlockCompleteData *blkdata = g_malloc0(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);
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,
if (mcb->callbacks[i].free_qiov) {
qemu_iovec_destroy(mcb->callbacks[i].free_qiov);
}
- qemu_free(mcb->callbacks[i].free_qiov);
+ g_free(mcb->callbacks[i].free_qiov);
qemu_vfree(mcb->callbacks[i].free_buf);
}
}
mcb->num_requests--;
if (mcb->num_requests == 0) {
multiwrite_user_cb(mcb);
- qemu_free(mcb);
+ g_free(mcb);
}
}
if (merge) {
size_t size;
- QEMUIOVector *qiov = qemu_mallocz(sizeof(*qiov));
+ QEMUIOVector *qiov = g_malloc0(sizeof(*qiov));
qemu_iovec_init(qiov,
reqs[outidx].qiov->niov + reqs[i].qiov->niov + 1);
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;
}
// Create MultiwriteCB structure
- mcb = qemu_mallocz(sizeof(*mcb) + num_reqs * sizeof(*mcb->callbacks));
+ mcb = g_malloc0(sizeof(*mcb) + num_reqs * sizeof(*mcb->callbacks));
mcb->num_requests = 0;
mcb->num_callbacks = num_reqs;
for (i = 0; i < mcb->num_callbacks; i++) {
reqs[i].error = -EIO;
}
- qemu_free(mcb);
+ g_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);
}
return bdrv_aio_rw_vector(bs, sector_num, qiov, nb_sectors, cb, opaque, 1);
}
+
+typedef struct BlockDriverAIOCBCoroutine {
+ BlockDriverAIOCB common;
+ BlockRequest req;
+ bool is_write;
+ QEMUBH* bh;
+} BlockDriverAIOCBCoroutine;
+
+static void bdrv_aio_co_cancel_em(BlockDriverAIOCB *blockacb)
+{
+ qemu_aio_flush();
+}
+
+static AIOPool bdrv_em_co_aio_pool = {
+ .aiocb_size = sizeof(BlockDriverAIOCBCoroutine),
+ .cancel = bdrv_aio_co_cancel_em,
+};
+
+static void bdrv_co_rw_bh(void *opaque)
+{
+ BlockDriverAIOCBCoroutine *acb = opaque;
+
+ acb->common.cb(acb->common.opaque, acb->req.error);
+ qemu_bh_delete(acb->bh);
+ qemu_aio_release(acb);
+}
+
+static void coroutine_fn bdrv_co_rw(void *opaque)
+{
+ BlockDriverAIOCBCoroutine *acb = opaque;
+ BlockDriverState *bs = acb->common.bs;
+
+ if (!acb->is_write) {
+ acb->req.error = bs->drv->bdrv_co_readv(bs, acb->req.sector,
+ acb->req.nb_sectors, acb->req.qiov);
+ } else {
+ acb->req.error = bs->drv->bdrv_co_writev(bs, acb->req.sector,
+ acb->req.nb_sectors, acb->req.qiov);
+ }
+
+ acb->bh = qemu_bh_new(bdrv_co_rw_bh, acb);
+ qemu_bh_schedule(acb->bh);
+}
+
+static BlockDriverAIOCB *bdrv_co_aio_rw_vector(BlockDriverState *bs,
+ int64_t sector_num,
+ QEMUIOVector *qiov,
+ int nb_sectors,
+ BlockDriverCompletionFunc *cb,
+ void *opaque,
+ bool is_write)
+{
+ Coroutine *co;
+ BlockDriverAIOCBCoroutine *acb;
+
+ acb = qemu_aio_get(&bdrv_em_co_aio_pool, bs, cb, opaque);
+ acb->req.sector = sector_num;
+ acb->req.nb_sectors = nb_sectors;
+ acb->req.qiov = qiov;
+ acb->is_write = is_write;
+
+ co = qemu_coroutine_create(bdrv_co_rw);
+ qemu_coroutine_enter(co, acb);
+
+ return &acb->common;
+}
+
+static BlockDriverAIOCB *bdrv_co_aio_readv_em(BlockDriverState *bs,
+ int64_t sector_num, QEMUIOVector *qiov, int nb_sectors,
+ BlockDriverCompletionFunc *cb, void *opaque)
+{
+ return bdrv_co_aio_rw_vector(bs, sector_num, qiov, nb_sectors, cb, opaque,
+ false);
+}
+
+static BlockDriverAIOCB *bdrv_co_aio_writev_em(BlockDriverState *bs,
+ int64_t sector_num, QEMUIOVector *qiov, int nb_sectors,
+ BlockDriverCompletionFunc *cb, void *opaque)
+{
+ return bdrv_co_aio_rw_vector(bs, sector_num, qiov, nb_sectors, cb, opaque,
+ true);
+}
+
static BlockDriverAIOCB *bdrv_aio_flush_em(BlockDriverState *bs,
BlockDriverCompletionFunc *cb, void *opaque)
{
struct iovec iov;
QEMUIOVector qiov;
- async_context_push();
-
async_ret = NOT_DONE;
iov.iov_base = (void *)buf;
iov.iov_len = nb_sectors * BDRV_SECTOR_SIZE;
fail:
- async_context_pop();
return async_ret;
}
struct iovec iov;
QEMUIOVector qiov;
- async_context_push();
-
async_ret = NOT_DONE;
iov.iov_base = (void *)buf;
iov.iov_len = nb_sectors * BDRV_SECTOR_SIZE;
}
fail:
- async_context_pop();
return async_ret;
}
acb = pool->free_aiocb;
pool->free_aiocb = acb->next;
} else {
- acb = qemu_mallocz(pool->aiocb_size);
+ acb = g_malloc0(pool->aiocb_size);
acb->pool = pool;
}
acb->bs = bs;
pool->free_aiocb = acb;
}
+/**************************************************************/
+/* Coroutine block device emulation */
+
+typedef struct CoroutineIOCompletion {
+ Coroutine *coroutine;
+ int ret;
+} CoroutineIOCompletion;
+
+static void bdrv_co_io_em_complete(void *opaque, int ret)
+{
+ CoroutineIOCompletion *co = opaque;
+
+ co->ret = ret;
+ qemu_coroutine_enter(co->coroutine, NULL);
+}
+
+static int coroutine_fn bdrv_co_io_em(BlockDriverState *bs, int64_t sector_num,
+ int nb_sectors, QEMUIOVector *iov,
+ bool is_write)
+{
+ CoroutineIOCompletion co = {
+ .coroutine = qemu_coroutine_self(),
+ };
+ BlockDriverAIOCB *acb;
+
+ if (is_write) {
+ acb = bdrv_aio_writev(bs, sector_num, iov, nb_sectors,
+ bdrv_co_io_em_complete, &co);
+ } else {
+ acb = bdrv_aio_readv(bs, sector_num, iov, nb_sectors,
+ bdrv_co_io_em_complete, &co);
+ }
+
+ trace_bdrv_co_io(is_write, acb);
+ if (!acb) {
+ return -EIO;
+ }
+ qemu_coroutine_yield();
+
+ return co.ret;
+}
+
+static int coroutine_fn bdrv_co_readv_em(BlockDriverState *bs,
+ int64_t sector_num, int nb_sectors,
+ QEMUIOVector *iov)
+{
+ return bdrv_co_io_em(bs, sector_num, nb_sectors, iov, false);
+}
+
+static int coroutine_fn bdrv_co_writev_em(BlockDriverState *bs,
+ int64_t sector_num, int nb_sectors,
+ QEMUIOVector *iov)
+{
+ return bdrv_co_io_em(bs, sector_num, nb_sectors, iov, true);
+}
+
+static int coroutine_fn bdrv_co_flush_em(BlockDriverState *bs)
+{
+ CoroutineIOCompletion co = {
+ .coroutine = qemu_coroutine_self(),
+ };
+ BlockDriverAIOCB *acb;
+
+ acb = bdrv_aio_flush(bs, bdrv_co_io_em_complete, &co);
+ if (!acb) {
+ return -EIO;
+ }
+ qemu_coroutine_yield();
+ return co.ret;
+}
+
/**************************************************************/
/* removable device support */
int bdrv_eject(BlockDriverState *bs, int eject_flag)
{
BlockDriver *drv = bs->drv;
- int ret;
- if (bs->locked) {
+ if (eject_flag && bs->locked) {
return -EBUSY;
}
- if (!drv || !drv->bdrv_eject) {
- ret = -ENOTSUP;
- } else {
- ret = drv->bdrv_eject(bs, eject_flag);
+ if (drv && drv->bdrv_eject) {
+ drv->bdrv_eject(bs, eject_flag);
}
- if (ret == -ENOTSUP) {
- ret = 0;
- }
- if (ret >= 0) {
- bs->tray_open = eject_flag;
- }
-
- return ret;
+ bs->tray_open = eject_flag;
+ return 0;
}
int bdrv_is_locked(BlockDriverState *bs)
{
BlockDriver *drv = bs->drv;
+ trace_bdrv_set_locked(bs, locked);
+
bs->locked = locked;
if (drv && drv->bdrv_set_locked) {
drv->bdrv_set_locked(bs, locked);
BDRV_SECTORS_PER_DIRTY_CHUNK * 8 - 1;
bitmap_size /= BDRV_SECTORS_PER_DIRTY_CHUNK * 8;
- bs->dirty_bitmap = qemu_mallocz(bitmap_size);
+ bs->dirty_bitmap = g_malloc0(bitmap_size);
}
} else {
if (bs->dirty_bitmap) {
- qemu_free(bs->dirty_bitmap);
+ g_free(bs->dirty_bitmap);
bs->dirty_bitmap = NULL;
}
}
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, *size;
+ 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
+ size = get_option_parameter(param, BLOCK_OPT_SIZE);
+ if (size && 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;
+}