#include <stdarg.h>
#include <string.h>
#include <unistd.h>
-#include <signal.h>
#include <inttypes.h>
#include <time.h>
#include <fcntl.h>
#include "hw/xen/xen_backend.h"
#include "xen_blkif.h"
#include "sysemu/blockdev.h"
+#include "sysemu/block-backend.h"
+#include "qapi/qmp/qdict.h"
+#include "qapi/qmp/qstring.h"
/* ------------------------------------------------------------- */
typedef struct PersistentGrant PersistentGrant;
+struct PersistentRegion {
+ void *addr;
+ int num;
+};
+
+typedef struct PersistentRegion PersistentRegion;
+
struct ioreq {
blkif_request_t req;
int16_t status;
off_t start;
QEMUIOVector v;
int presync;
- int postsync;
uint8_t mapped;
/* grant mapping */
gboolean feature_discard;
gboolean feature_persistent;
GTree *persistent_gnts;
+ GSList *persistent_regions;
unsigned int persistent_gnt_count;
unsigned int max_grants;
/* qemu block driver */
DriveInfo *dinfo;
- BlockDriverState *bs;
+ BlockBackend *blk;
QEMUBH *bh;
};
ioreq->status = 0;
ioreq->start = 0;
ioreq->presync = 0;
- ioreq->postsync = 0;
ioreq->mapped = 0;
memset(ioreq->domids, 0, sizeof(ioreq->domids));
g_free(grant);
}
+static void remove_persistent_region(gpointer data, gpointer dev)
+{
+ PersistentRegion *region = data;
+ struct XenBlkDev *blkdev = dev;
+ XenGnttab gnt = blkdev->xendev.gnttabdev;
+
+ if (xc_gnttab_munmap(gnt, region->addr, region->num) != 0) {
+ xen_be_printf(&blkdev->xendev, 0,
+ "xc_gnttab_munmap region %p failed: %s\n",
+ region->addr, strerror(errno));
+ }
+ xen_be_printf(&blkdev->xendev, 3,
+ "unmapped grant region %p with %d pages\n",
+ region->addr, region->num);
+ g_free(region);
+}
+
static struct ioreq *ioreq_start(struct XenBlkDev *blkdev)
{
struct ioreq *ioreq = NULL;
void *page[BLKIF_MAX_SEGMENTS_PER_REQUEST];
int i, j, new_maps = 0;
PersistentGrant *grant;
+ PersistentRegion *region;
/* domids and refs variables will contain the information necessary
* to map the grants that are needed to fulfill this request.
*
}
}
}
- if (ioreq->blkdev->feature_persistent) {
+ if (ioreq->blkdev->feature_persistent && new_maps != 0 &&
+ (!batch_maps || (ioreq->blkdev->persistent_gnt_count + new_maps <=
+ ioreq->blkdev->max_grants))) {
+ /*
+ * If we are using persistent grants and batch mappings only
+ * add the new maps to the list of persistent grants if the whole
+ * area can be persistently mapped.
+ */
+ if (batch_maps) {
+ region = g_malloc0(sizeof(*region));
+ region->addr = ioreq->pages;
+ region->num = new_maps;
+ ioreq->blkdev->persistent_regions = g_slist_append(
+ ioreq->blkdev->persistent_regions,
+ region);
+ }
while ((ioreq->blkdev->persistent_gnt_count < ioreq->blkdev->max_grants)
&& new_maps) {
/* Go through the list of newly mapped grants and add as many
grant);
ioreq->blkdev->persistent_gnt_count++;
}
+ assert(!batch_maps || new_maps == 0);
}
for (i = 0; i < ioreq->v.niov; i++) {
ioreq->v.iov[i].iov_base += (uintptr_t)page[i];
if (ioreq->aio_inflight > 0) {
return;
}
- if (ioreq->postsync) {
- ioreq->postsync = 0;
- ioreq->aio_inflight++;
- bdrv_aio_flush(ioreq->blkdev->bs, qemu_aio_complete, ioreq);
- return;
- }
ioreq->status = ioreq->aio_errors ? BLKIF_RSP_ERROR : BLKIF_RSP_OKAY;
ioreq_unmap(ioreq);
break;
}
case BLKIF_OP_READ:
- bdrv_acct_done(ioreq->blkdev->bs, &ioreq->acct);
+ if (ioreq->status == BLKIF_RSP_OKAY) {
+ block_acct_done(blk_get_stats(ioreq->blkdev->blk), &ioreq->acct);
+ } else {
+ block_acct_failed(blk_get_stats(ioreq->blkdev->blk), &ioreq->acct);
+ }
break;
case BLKIF_OP_DISCARD:
default:
ioreq->aio_inflight++;
if (ioreq->presync) {
- bdrv_aio_flush(ioreq->blkdev->bs, qemu_aio_complete, ioreq);
+ blk_aio_flush(ioreq->blkdev->blk, qemu_aio_complete, ioreq);
return 0;
}
switch (ioreq->req.operation) {
case BLKIF_OP_READ:
- bdrv_acct_start(blkdev->bs, &ioreq->acct, ioreq->v.size, BDRV_ACCT_READ);
+ block_acct_start(blk_get_stats(blkdev->blk), &ioreq->acct,
+ ioreq->v.size, BLOCK_ACCT_READ);
ioreq->aio_inflight++;
- bdrv_aio_readv(blkdev->bs, ioreq->start / BLOCK_SIZE,
- &ioreq->v, ioreq->v.size / BLOCK_SIZE,
- qemu_aio_complete, ioreq);
+ blk_aio_readv(blkdev->blk, ioreq->start / BLOCK_SIZE,
+ &ioreq->v, ioreq->v.size / BLOCK_SIZE,
+ qemu_aio_complete, ioreq);
break;
case BLKIF_OP_WRITE:
case BLKIF_OP_FLUSH_DISKCACHE:
break;
}
- bdrv_acct_start(blkdev->bs, &ioreq->acct, ioreq->v.size, BDRV_ACCT_WRITE);
+ block_acct_start(blk_get_stats(blkdev->blk), &ioreq->acct,
+ ioreq->v.size,
+ ioreq->req.operation == BLKIF_OP_WRITE ?
+ BLOCK_ACCT_WRITE : BLOCK_ACCT_FLUSH);
ioreq->aio_inflight++;
- bdrv_aio_writev(blkdev->bs, ioreq->start / BLOCK_SIZE,
- &ioreq->v, ioreq->v.size / BLOCK_SIZE,
- qemu_aio_complete, ioreq);
+ blk_aio_writev(blkdev->blk, ioreq->start / BLOCK_SIZE,
+ &ioreq->v, ioreq->v.size / BLOCK_SIZE,
+ qemu_aio_complete, ioreq);
break;
case BLKIF_OP_DISCARD:
{
struct blkif_request_discard *discard_req = (void *)&ioreq->req;
ioreq->aio_inflight++;
- bdrv_aio_discard(blkdev->bs,
+ blk_aio_discard(blkdev->blk,
discard_req->sector_number, discard_req->nr_sectors,
qemu_aio_complete, ioreq);
break;
break;
default:
dst = NULL;
+ return 0;
}
memcpy(dst, &resp, sizeof(resp));
blkdev->rings.common.rsp_prod_pvt++;
/* parse them */
if (ioreq_parse(ioreq) != 0) {
+
+ switch (ioreq->req.operation) {
+ case BLKIF_OP_READ:
+ block_acct_invalid(blk_get_stats(blkdev->blk),
+ BLOCK_ACCT_READ);
+ break;
+ case BLKIF_OP_WRITE:
+ block_acct_invalid(blk_get_stats(blkdev->blk),
+ BLOCK_ACCT_WRITE);
+ break;
+ case BLKIF_OP_FLUSH_DISKCACHE:
+ block_acct_invalid(blk_get_stats(blkdev->blk),
+ BLOCK_ACCT_FLUSH);
+ default:
+ break;
+ };
+
if (blk_send_response_one(ioreq)) {
xen_be_send_notify(&blkdev->xendev);
}
blkdev->dinfo = drive_get(IF_XEN, 0, index);
if (!blkdev->dinfo) {
Error *local_err = NULL;
+ QDict *options = NULL;
+
+ if (strcmp(blkdev->fileproto, "<unset>")) {
+ options = qdict_new();
+ qdict_put(options, "driver", qstring_from_str(blkdev->fileproto));
+ }
+
/* setup via xenbus -> create new block driver instance */
xen_be_printf(&blkdev->xendev, 2, "create new bdrv (xenbus setup)\n");
- blkdev->bs = bdrv_new(blkdev->dev, &local_err);
- if (local_err) {
- blkdev->bs = NULL;
- }
- if (blkdev->bs) {
- BlockDriver *drv = bdrv_find_whitelisted_format(blkdev->fileproto,
- readonly);
- if (bdrv_open(&blkdev->bs, blkdev->filename, NULL, NULL, qflags,
- drv, &local_err) != 0)
- {
- xen_be_printf(&blkdev->xendev, 0, "error: %s\n",
- error_get_pretty(local_err));
- error_free(local_err);
- bdrv_unref(blkdev->bs);
- blkdev->bs = NULL;
- }
- }
- if (!blkdev->bs) {
+ blkdev->blk = blk_new_open(blkdev->dev, blkdev->filename, NULL, options,
+ qflags, &local_err);
+ if (!blkdev->blk) {
+ xen_be_printf(&blkdev->xendev, 0, "error: %s\n",
+ error_get_pretty(local_err));
+ error_free(local_err);
return -1;
}
} else {
/* setup via qemu cmdline -> already setup for us */
xen_be_printf(&blkdev->xendev, 2, "get configured bdrv (cmdline setup)\n");
- blkdev->bs = blkdev->dinfo->bdrv;
- if (bdrv_is_read_only(blkdev->bs) && !readonly) {
+ blkdev->blk = blk_by_legacy_dinfo(blkdev->dinfo);
+ if (blk_is_read_only(blkdev->blk) && !readonly) {
xen_be_printf(&blkdev->xendev, 0, "Unexpected read-only drive");
- blkdev->bs = NULL;
+ blkdev->blk = NULL;
return -1;
}
- /* blkdev->bs is not create by us, we get a reference
- * so we can bdrv_unref() unconditionally */
- bdrv_ref(blkdev->bs);
+ /* blkdev->blk is not create by us, we get a reference
+ * so we can blk_unref() unconditionally */
+ blk_ref(blkdev->blk);
}
- bdrv_attach_dev_nofail(blkdev->bs, blkdev);
- blkdev->file_size = bdrv_getlength(blkdev->bs);
+ blk_attach_dev_nofail(blkdev->blk, blkdev);
+ blkdev->file_size = blk_getlength(blkdev->blk);
if (blkdev->file_size < 0) {
- xen_be_printf(&blkdev->xendev, 1, "bdrv_getlength: %d (%s) | drv %s\n",
+ BlockDriverState *bs = blk_bs(blkdev->blk);
+ const char *drv_name = bs ? bdrv_get_format_name(bs) : NULL;
+ xen_be_printf(&blkdev->xendev, 1, "blk_getlength: %d (%s) | drv %s\n",
(int)blkdev->file_size, strerror(-blkdev->file_size),
- bdrv_get_format_name(blkdev->bs) ?: "-");
+ drv_name ?: "-");
blkdev->file_size = 0;
}
blkdev->max_grants = max_requests * BLKIF_MAX_SEGMENTS_PER_REQUEST;
blkdev->persistent_gnts = g_tree_new_full((GCompareDataFunc)int_cmp,
NULL, NULL,
+ batch_maps ?
+ (GDestroyNotify)g_free :
(GDestroyNotify)destroy_grant);
+ blkdev->persistent_regions = NULL;
blkdev->persistent_gnt_count = 0;
}
{
struct XenBlkDev *blkdev = container_of(xendev, struct XenBlkDev, xendev);
- if (blkdev->bs) {
- bdrv_detach_dev(blkdev->bs, blkdev);
- bdrv_unref(blkdev->bs);
- blkdev->bs = NULL;
+ if (blkdev->blk) {
+ blk_detach_dev(blkdev->blk, blkdev);
+ blk_unref(blkdev->blk);
+ blkdev->blk = NULL;
}
xen_be_unbind_evtchn(&blkdev->xendev);
blkdev->cnt_map--;
blkdev->sring = NULL;
}
+
+ /*
+ * Unmap persistent grants before switching to the closed state
+ * so the frontend can free them.
+ *
+ * In the !batch_maps case g_tree_destroy will take care of unmapping
+ * the grant, but in the batch_maps case we need to iterate over every
+ * region in persistent_regions and unmap it.
+ */
+ if (blkdev->feature_persistent) {
+ g_tree_destroy(blkdev->persistent_gnts);
+ assert(batch_maps || blkdev->persistent_gnt_count == 0);
+ if (batch_maps) {
+ blkdev->persistent_gnt_count = 0;
+ g_slist_foreach(blkdev->persistent_regions,
+ (GFunc)remove_persistent_region, blkdev);
+ g_slist_free(blkdev->persistent_regions);
+ }
+ blkdev->feature_persistent = false;
+ }
}
static int blk_free(struct XenDevice *xendev)
struct XenBlkDev *blkdev = container_of(xendev, struct XenBlkDev, xendev);
struct ioreq *ioreq;
- if (blkdev->bs || blkdev->sring) {
+ if (blkdev->blk || blkdev->sring) {
blk_disconnect(xendev);
}
- /* Free persistent grants */
- if (blkdev->feature_persistent) {
- g_tree_destroy(blkdev->persistent_gnts);
- }
-
while (!QLIST_EMPTY(&blkdev->freelist)) {
ioreq = QLIST_FIRST(&blkdev->freelist);
QLIST_REMOVE(ioreq, list);