#include <windows.h>
#endif
+#define NOT_DONE 0x7fffffff /* used while emulated sync operation in progress */
+
static void bdrv_dev_change_media_cb(BlockDriverState *bs, bool load);
static BlockDriverAIOCB *bdrv_aio_readv_em(BlockDriverState *bs,
int64_t sector_num, QEMUIOVector *qiov, int nb_sectors,
static BlockDriverAIOCB *bdrv_aio_writev_em(BlockDriverState *bs,
int64_t sector_num, QEMUIOVector *qiov, int nb_sectors,
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);
-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 int coroutine_fn bdrv_co_do_readv(BlockDriverState *bs,
+ int64_t sector_num, int nb_sectors, QEMUIOVector *qiov);
+static int coroutine_fn bdrv_co_do_writev(BlockDriverState *bs,
+ int64_t sector_num, int nb_sectors, QEMUIOVector *qiov);
+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);
+static void coroutine_fn bdrv_co_do_rw(void *opaque);
static QTAILQ_HEAD(, BlockDriverState) bdrv_states =
QTAILQ_HEAD_INITIALIZER(bdrv_states);
void bdrv_register(BlockDriver *bdrv)
{
- 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 {
+ /* Block drivers without coroutine functions need emulation */
+ if (!bdrv->bdrv_co_readv) {
bdrv->bdrv_co_readv = bdrv_co_readv_em;
bdrv->bdrv_co_writev = bdrv_co_writev_em;
+ /* bdrv_co_readv_em()/brdv_co_writev_em() work in terms of aio, so if
+ * the block driver lacks aio we need to emulate that too.
+ */
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)
- bdrv->bdrv_aio_flush = bdrv_aio_flush_em;
-
QLIST_INSERT_HEAD(&bdrv_drivers, bdrv, list);
}
if (device_name[0] != '\0') {
QTAILQ_INSERT_TAIL(&bdrv_states, bs, list);
}
+ bdrv_iostatus_disable(bs);
return bs;
}
return -EBUSY;
}
bs->dev = dev;
+ bdrv_iostatus_reset(bs);
return 0;
}
nb_sectors * BDRV_SECTOR_SIZE);
}
-static inline bool bdrv_has_async_rw(BlockDriver *drv)
+typedef struct RwCo {
+ BlockDriverState *bs;
+ int64_t sector_num;
+ int nb_sectors;
+ QEMUIOVector *qiov;
+ bool is_write;
+ int ret;
+} RwCo;
+
+static void coroutine_fn bdrv_rw_co_entry(void *opaque)
{
- return drv->bdrv_co_readv != bdrv_co_readv_em
- || drv->bdrv_aio_readv != bdrv_aio_readv_em;
+ RwCo *rwco = opaque;
+
+ if (!rwco->is_write) {
+ rwco->ret = bdrv_co_do_readv(rwco->bs, rwco->sector_num,
+ rwco->nb_sectors, rwco->qiov);
+ } else {
+ rwco->ret = bdrv_co_do_writev(rwco->bs, rwco->sector_num,
+ rwco->nb_sectors, rwco->qiov);
+ }
}
-static inline bool bdrv_has_async_flush(BlockDriver *drv)
+/*
+ * Process a synchronous request using coroutines
+ */
+static int bdrv_rw_co(BlockDriverState *bs, int64_t sector_num, uint8_t *buf,
+ int nb_sectors, bool is_write)
{
- return drv->bdrv_aio_flush != bdrv_aio_flush_em;
+ QEMUIOVector qiov;
+ struct iovec iov = {
+ .iov_base = (void *)buf,
+ .iov_len = nb_sectors * BDRV_SECTOR_SIZE,
+ };
+ Coroutine *co;
+ RwCo rwco = {
+ .bs = bs,
+ .sector_num = sector_num,
+ .nb_sectors = nb_sectors,
+ .qiov = &qiov,
+ .is_write = is_write,
+ .ret = NOT_DONE,
+ };
+
+ qemu_iovec_init_external(&qiov, &iov, 1);
+
+ if (qemu_in_coroutine()) {
+ /* Fast-path if already in coroutine context */
+ bdrv_rw_co_entry(&rwco);
+ } else {
+ co = qemu_coroutine_create(bdrv_rw_co_entry);
+ qemu_coroutine_enter(co, &rwco);
+ while (rwco.ret == NOT_DONE) {
+ qemu_aio_wait();
+ }
+ }
+ return rwco.ret;
}
/* 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)
{
- BlockDriver *drv = bs->drv;
-
- 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;
-
- return drv->bdrv_read(bs, sector_num, buf, nb_sectors);
+ return bdrv_rw_co(bs, sector_num, buf, nb_sectors, false);
}
static void set_dirty_bitmap(BlockDriverState *bs, int64_t sector_num,
int bdrv_write(BlockDriverState *bs, int64_t sector_num,
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 -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_write(bs, sector_num, buf, nb_sectors);
+ return bdrv_rw_co(bs, sector_num, (uint8_t *)buf, nb_sectors, true);
}
int bdrv_pread(BlockDriverState *bs, int64_t offset,
return 0;
}
-int coroutine_fn bdrv_co_readv(BlockDriverState *bs, int64_t sector_num,
- int nb_sectors, QEMUIOVector *qiov)
+/*
+ * Handle a read request in coroutine context
+ */
+static int coroutine_fn bdrv_co_do_readv(BlockDriverState *bs,
+ int64_t sector_num, int nb_sectors, QEMUIOVector *qiov)
{
BlockDriver *drv = bs->drv;
- trace_bdrv_co_readv(bs, sector_num, nb_sectors);
-
if (!drv) {
return -ENOMEDIUM;
}
return drv->bdrv_co_readv(bs, sector_num, nb_sectors, qiov);
}
-int coroutine_fn bdrv_co_writev(BlockDriverState *bs, int64_t sector_num,
+int coroutine_fn bdrv_co_readv(BlockDriverState *bs, int64_t sector_num,
int nb_sectors, QEMUIOVector *qiov)
{
- BlockDriver *drv = bs->drv;
+ trace_bdrv_co_readv(bs, sector_num, nb_sectors);
- trace_bdrv_co_writev(bs, sector_num, nb_sectors);
+ return bdrv_co_do_readv(bs, sector_num, nb_sectors, qiov);
+}
+
+/*
+ * Handle a write request in coroutine context
+ */
+static int coroutine_fn bdrv_co_do_writev(BlockDriverState *bs,
+ int64_t sector_num, int nb_sectors, QEMUIOVector *qiov)
+{
+ BlockDriver *drv = bs->drv;
+ int ret;
if (!bs->drv) {
return -ENOMEDIUM;
return -EIO;
}
+ ret = drv->bdrv_co_writev(bs, sector_num, nb_sectors, qiov);
+
if (bs->dirty_bitmap) {
set_dirty_bitmap(bs, 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 ret;
+}
+
+int coroutine_fn bdrv_co_writev(BlockDriverState *bs, int64_t sector_num,
+ int nb_sectors, QEMUIOVector *qiov)
+{
+ trace_bdrv_co_writev(bs, sector_num, nb_sectors);
+
+ return bdrv_co_do_writev(bs, sector_num, nb_sectors, qiov);
}
/**
return bs->device_name;
}
-int bdrv_flush(BlockDriverState *bs)
-{
- if (bs->open_flags & BDRV_O_NO_FLUSH) {
- 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);
- }
-
- /*
- * 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;
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,
monitor_printf(mon, " tray-open=%d",
qdict_get_bool(bs_dict, "tray-open"));
}
+
+ if (qdict_haskey(bs_dict, "io-status")) {
+ monitor_printf(mon, " io-status=%s", qdict_get_str(bs_dict, "io-status"));
+ }
+
if (qdict_haskey(bs_dict, "inserted")) {
QDict *qdict = qobject_to_qdict(qdict_get(bs_dict, "inserted"));
qlist_iter(qobject_to_qlist(data), bdrv_print_dict, mon);
}
+static const char *const io_status_name[BDRV_IOS_MAX] = {
+ [BDRV_IOS_OK] = "ok",
+ [BDRV_IOS_FAILED] = "failed",
+ [BDRV_IOS_ENOSPC] = "nospace",
+};
+
void bdrv_info(Monitor *mon, QObject **ret_data)
{
QList *bs_list;
qdict_put(bs_dict, "tray-open",
qbool_from_int(bdrv_dev_is_tray_open(bs)));
}
+
+ if (bdrv_iostatus_is_enabled(bs)) {
+ qdict_put(bs_dict, "io-status",
+ qstring_from_str(io_status_name[bs->iostatus]));
+ }
+
if (bs->drv) {
QObject *obj;
QEMUIOVector *qiov, int nb_sectors,
BlockDriverCompletionFunc *cb, void *opaque)
{
- BlockDriver *drv = bs->drv;
-
trace_bdrv_aio_readv(bs, sector_num, nb_sectors, opaque);
- if (!drv)
- return NULL;
- if (bdrv_check_request(bs, sector_num, nb_sectors))
- return NULL;
-
- return drv->bdrv_aio_readv(bs, sector_num, qiov, nb_sectors,
- cb, opaque);
-}
-
-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;
+ return bdrv_co_aio_rw_vector(bs, sector_num, qiov, nb_sectors,
+ cb, opaque, false);
}
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;
- if (bs->read_only)
- return NULL;
- if (bdrv_check_request(bs, sector_num, nb_sectors))
- return NULL;
-
- if (bs->dirty_bitmap) {
- 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) {
- if (bs->wr_highest_sector < sector_num + nb_sectors - 1) {
- bs->wr_highest_sector = sector_num + nb_sectors - 1;
- }
- }
-
- return ret;
+ return bdrv_co_aio_rw_vector(bs, sector_num, qiov, nb_sectors,
+ cb, opaque, true);
}
return -1;
}
-BlockDriverAIOCB *bdrv_aio_flush(BlockDriverState *bs,
- BlockDriverCompletionFunc *cb, void *opaque)
-{
- 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);
-}
-
void bdrv_aio_cancel(BlockDriverAIOCB *acb)
{
acb->pool->cancel(acb);
if (is_write) {
qemu_iovec_to_buffer(acb->qiov, acb->bounce);
- acb->ret = bdrv_write(bs, sector_num, acb->bounce, nb_sectors);
+ acb->ret = bs->drv->bdrv_write(bs, sector_num, acb->bounce, nb_sectors);
} else {
- acb->ret = bdrv_read(bs, sector_num, acb->bounce, nb_sectors);
+ acb->ret = bs->drv->bdrv_read(bs, sector_num, acb->bounce, nb_sectors);
}
qemu_bh_schedule(acb->bh);
.cancel = bdrv_aio_co_cancel_em,
};
-static void bdrv_co_rw_bh(void *opaque)
+static void bdrv_co_em_bh(void *opaque)
{
BlockDriverAIOCBCoroutine *acb = opaque;
qemu_aio_release(acb);
}
-static void coroutine_fn bdrv_co_rw(void *opaque)
+/* Invoke bdrv_co_do_readv/bdrv_co_do_writev */
+static void coroutine_fn bdrv_co_do_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.error = bdrv_co_do_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.error = bdrv_co_do_writev(bs, acb->req.sector,
acb->req.nb_sectors, acb->req.qiov);
}
- acb->bh = qemu_bh_new(bdrv_co_rw_bh, acb);
+ acb->bh = qemu_bh_new(bdrv_co_em_bh, acb);
qemu_bh_schedule(acb->bh);
}
acb->req.qiov = qiov;
acb->is_write = is_write;
- co = qemu_coroutine_create(bdrv_co_rw);
+ co = qemu_coroutine_create(bdrv_co_do_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)
+static void coroutine_fn bdrv_aio_flush_co_entry(void *opaque)
{
- return bdrv_co_aio_rw_vector(bs, sector_num, qiov, nb_sectors, cb, opaque,
- false);
-}
+ BlockDriverAIOCBCoroutine *acb = opaque;
+ BlockDriverState *bs = acb->common.bs;
-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);
+ acb->req.error = bdrv_co_flush(bs);
+ acb->bh = qemu_bh_new(bdrv_co_em_bh, acb);
+ qemu_bh_schedule(acb->bh);
}
-static BlockDriverAIOCB *bdrv_aio_flush_em(BlockDriverState *bs,
+BlockDriverAIOCB *bdrv_aio_flush(BlockDriverState *bs,
BlockDriverCompletionFunc *cb, void *opaque)
{
- BlockDriverAIOCBSync *acb;
+ trace_bdrv_aio_flush(bs, opaque);
- acb = qemu_aio_get(&bdrv_em_aio_pool, bs, cb, opaque);
- acb->is_write = 1; /* don't bounce in the completion hadler */
- acb->qiov = NULL;
- acb->bounce = NULL;
- acb->ret = 0;
+ Coroutine *co;
+ BlockDriverAIOCBCoroutine *acb;
- if (!acb->bh)
- acb->bh = qemu_bh_new(bdrv_aio_bh_cb, acb);
+ acb = qemu_aio_get(&bdrv_em_co_aio_pool, bs, cb, opaque);
+ co = qemu_coroutine_create(bdrv_aio_flush_co_entry);
+ qemu_coroutine_enter(co, acb);
- bdrv_flush(bs);
- qemu_bh_schedule(acb->bh);
return &acb->common;
}
-static BlockDriverAIOCB *bdrv_aio_noop_em(BlockDriverState *bs,
- BlockDriverCompletionFunc *cb, void *opaque)
+static void coroutine_fn bdrv_aio_discard_co_entry(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);
- }
+ BlockDriverAIOCBCoroutine *acb = opaque;
+ BlockDriverState *bs = acb->common.bs;
+ acb->req.error = bdrv_co_discard(bs, acb->req.sector, acb->req.nb_sectors);
+ acb->bh = qemu_bh_new(bdrv_co_em_bh, acb);
qemu_bh_schedule(acb->bh);
- return &acb->common;
-}
-
-/**************************************************************/
-/* sync block device emulation */
-
-static void bdrv_rw_em_cb(void *opaque, int ret)
-{
- *(int *)opaque = ret;
}
-#define NOT_DONE 0x7fffffff
-
-static int bdrv_read_em(BlockDriverState *bs, int64_t sector_num,
- uint8_t *buf, int nb_sectors)
+BlockDriverAIOCB *bdrv_aio_discard(BlockDriverState *bs,
+ int64_t sector_num, int nb_sectors,
+ BlockDriverCompletionFunc *cb, void *opaque)
{
- int async_ret;
- BlockDriverAIOCB *acb;
- struct iovec iov;
- QEMUIOVector qiov;
-
- async_ret = NOT_DONE;
- iov.iov_base = (void *)buf;
- 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);
- if (acb == NULL) {
- async_ret = -1;
- goto fail;
- }
-
- while (async_ret == NOT_DONE) {
- qemu_aio_wait();
- }
-
-
-fail:
- return async_ret;
-}
+ Coroutine *co;
+ BlockDriverAIOCBCoroutine *acb;
-static int bdrv_write_em(BlockDriverState *bs, int64_t sector_num,
- const uint8_t *buf, int nb_sectors)
-{
- int async_ret;
- BlockDriverAIOCB *acb;
- struct iovec iov;
- QEMUIOVector qiov;
+ trace_bdrv_aio_discard(bs, sector_num, nb_sectors, opaque);
- async_ret = NOT_DONE;
- iov.iov_base = (void *)buf;
- 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 (acb == NULL) {
- async_ret = -1;
- goto fail;
- }
- while (async_ret == NOT_DONE) {
- qemu_aio_wait();
- }
+ acb = qemu_aio_get(&bdrv_em_co_aio_pool, bs, cb, opaque);
+ acb->req.sector = sector_num;
+ acb->req.nb_sectors = nb_sectors;
+ co = qemu_coroutine_create(bdrv_aio_discard_co_entry);
+ qemu_coroutine_enter(co, acb);
-fail:
- return async_ret;
+ return &acb->common;
}
void bdrv_init(void)
BlockDriverAIOCB *acb;
if (is_write) {
- acb = bdrv_aio_writev(bs, sector_num, iov, nb_sectors,
- bdrv_co_io_em_complete, &co);
+ acb = bs->drv->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);
+ acb = bs->drv->bdrv_aio_readv(bs, sector_num, iov, nb_sectors,
+ bdrv_co_io_em_complete, &co);
}
trace_bdrv_co_io_em(bs, sector_num, nb_sectors, is_write, acb);
return bdrv_co_io_em(bs, sector_num, nb_sectors, iov, true);
}
-static int coroutine_fn bdrv_co_flush_em(BlockDriverState *bs)
+static void coroutine_fn bdrv_flush_co_entry(void *opaque)
{
- CoroutineIOCompletion co = {
- .coroutine = qemu_coroutine_self(),
+ RwCo *rwco = opaque;
+
+ rwco->ret = bdrv_co_flush(rwco->bs);
+}
+
+int coroutine_fn bdrv_co_flush(BlockDriverState *bs)
+{
+ if (bs->open_flags & BDRV_O_NO_FLUSH) {
+ return 0;
+ } else if (!bs->drv) {
+ return 0;
+ } else if (bs->drv->bdrv_co_flush) {
+ return bs->drv->bdrv_co_flush(bs);
+ } else if (bs->drv->bdrv_aio_flush) {
+ BlockDriverAIOCB *acb;
+ CoroutineIOCompletion co = {
+ .coroutine = qemu_coroutine_self(),
+ };
+
+ acb = bs->drv->bdrv_aio_flush(bs, bdrv_co_io_em_complete, &co);
+ if (acb == NULL) {
+ return -EIO;
+ } else {
+ qemu_coroutine_yield();
+ return co.ret;
+ }
+ } else {
+ /*
+ * 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;
+ }
+}
+
+int bdrv_flush(BlockDriverState *bs)
+{
+ Coroutine *co;
+ RwCo rwco = {
+ .bs = bs,
+ .ret = NOT_DONE,
};
- BlockDriverAIOCB *acb;
- acb = bdrv_aio_flush(bs, bdrv_co_io_em_complete, &co);
- if (!acb) {
+ if (qemu_in_coroutine()) {
+ /* Fast-path if already in coroutine context */
+ bdrv_flush_co_entry(&rwco);
+ } else {
+ co = qemu_coroutine_create(bdrv_flush_co_entry);
+ qemu_coroutine_enter(co, &rwco);
+ while (rwco.ret == NOT_DONE) {
+ qemu_aio_wait();
+ }
+ }
+
+ return rwco.ret;
+}
+
+static void coroutine_fn bdrv_discard_co_entry(void *opaque)
+{
+ RwCo *rwco = opaque;
+
+ rwco->ret = bdrv_co_discard(rwco->bs, rwco->sector_num, rwco->nb_sectors);
+}
+
+int coroutine_fn bdrv_co_discard(BlockDriverState *bs, int64_t sector_num,
+ int nb_sectors)
+{
+ if (!bs->drv) {
+ return -ENOMEDIUM;
+ } else if (bdrv_check_request(bs, sector_num, nb_sectors)) {
return -EIO;
+ } else if (bs->read_only) {
+ return -EROFS;
+ } else if (bs->drv->bdrv_co_discard) {
+ return bs->drv->bdrv_co_discard(bs, sector_num, nb_sectors);
+ } else if (bs->drv->bdrv_aio_discard) {
+ BlockDriverAIOCB *acb;
+ CoroutineIOCompletion co = {
+ .coroutine = qemu_coroutine_self(),
+ };
+
+ acb = bs->drv->bdrv_aio_discard(bs, sector_num, nb_sectors,
+ bdrv_co_io_em_complete, &co);
+ if (acb == NULL) {
+ return -EIO;
+ } else {
+ qemu_coroutine_yield();
+ return co.ret;
+ }
+ } else {
+ return 0;
}
- qemu_coroutine_yield();
- return co.ret;
+}
+
+int bdrv_discard(BlockDriverState *bs, int64_t sector_num, int nb_sectors)
+{
+ Coroutine *co;
+ RwCo rwco = {
+ .bs = bs,
+ .sector_num = sector_num,
+ .nb_sectors = nb_sectors,
+ .ret = NOT_DONE,
+ };
+
+ if (qemu_in_coroutine()) {
+ /* Fast-path if already in coroutine context */
+ bdrv_discard_co_entry(&rwco);
+ } else {
+ co = qemu_coroutine_create(bdrv_discard_co_entry);
+ qemu_coroutine_enter(co, &rwco);
+ while (rwco.ret == NOT_DONE) {
+ qemu_aio_wait();
+ }
+ }
+
+ return rwco.ret;
}
/**************************************************************/
return bs->in_use;
}
+void bdrv_iostatus_enable(BlockDriverState *bs)
+{
+ bs->iostatus = BDRV_IOS_OK;
+}
+
+/* The I/O status is only enabled if the drive explicitly
+ * enables it _and_ the VM is configured to stop on errors */
+bool bdrv_iostatus_is_enabled(const BlockDriverState *bs)
+{
+ return (bs->iostatus != BDRV_IOS_INVAL &&
+ (bs->on_write_error == BLOCK_ERR_STOP_ENOSPC ||
+ bs->on_write_error == BLOCK_ERR_STOP_ANY ||
+ bs->on_read_error == BLOCK_ERR_STOP_ANY));
+}
+
+void bdrv_iostatus_disable(BlockDriverState *bs)
+{
+ bs->iostatus = BDRV_IOS_INVAL;
+}
+
+void bdrv_iostatus_reset(BlockDriverState *bs)
+{
+ if (bdrv_iostatus_is_enabled(bs)) {
+ bs->iostatus = BDRV_IOS_OK;
+ }
+}
+
+/* XXX: Today this is set by device models because it makes the implementation
+ quite simple. However, the block layer knows about the error, so it's
+ possible to implement this without device models being involved */
+void bdrv_iostatus_set_err(BlockDriverState *bs, int error)
+{
+ if (bdrv_iostatus_is_enabled(bs) && bs->iostatus == BDRV_IOS_OK) {
+ assert(error >= 0);
+ bs->iostatus = error == ENOSPC ? BDRV_IOS_ENOSPC : BDRV_IOS_FAILED;
+ }
+}
+
void
bdrv_acct_start(BlockDriverState *bs, BlockAcctCookie *cookie, int64_t bytes,
enum BlockAcctType type)