*
* You should have received a copy of the GNU General Public License along
* with this program; if not, see <http://www.gnu.org/licenses/>.
+ *
+ * Contributions after 2012-01-13 are licensed under the terms of the
+ * GNU GPL, version 2 or (at your option) any later version.
*/
#include <stdio.h>
#include <sys/mman.h>
#include <sys/uio.h>
-#include <xs.h>
-#include <xenctrl.h>
-#include <xen/io/xenbus.h>
-
#include "hw.h"
-#include "block_int.h"
#include "qemu-char.h"
-#include "xen_blkif.h"
#include "xen_backend.h"
+#include "xen_blkif.h"
#include "blockdev.h"
/* ------------------------------------------------------------- */
-static int syncwrite = 0;
static int batch_maps = 0;
static int max_requests = 32;
-static int use_aio = 1;
/* ------------------------------------------------------------- */
QEMUIOVector v;
int presync;
int postsync;
+ uint8_t mapped;
/* grant mapping */
uint32_t domids[BLKIF_MAX_SEGMENTS_PER_REQUEST];
struct XenBlkDev *blkdev;
QLIST_ENTRY(ioreq) list;
+ BlockAcctCookie acct;
};
struct XenBlkDev {
goto out;
}
/* allocate new struct */
- ioreq = qemu_mallocz(sizeof(*ioreq));
+ ioreq = g_malloc0(sizeof(*ioreq));
ioreq->blkdev = blkdev;
blkdev->requests_total++;
qemu_iovec_init(&ioreq->v, BLKIF_MAX_SEGMENTS_PER_REQUEST);
blkdev->requests_finished++;
}
-static void ioreq_release(struct ioreq *ioreq)
+static void ioreq_release(struct ioreq *ioreq, bool finish)
{
struct XenBlkDev *blkdev = ioreq->blkdev;
memset(ioreq, 0, sizeof(*ioreq));
ioreq->blkdev = blkdev;
QLIST_INSERT_HEAD(&blkdev->freelist, ioreq, list);
- blkdev->requests_finished--;
+ if (finish) {
+ blkdev->requests_finished--;
+ } else {
+ blkdev->requests_inflight--;
+ }
}
/*
ioreq->presync = 1;
return 0;
}
- if (!syncwrite) {
- ioreq->presync = ioreq->postsync = 1;
- }
+ ioreq->presync = ioreq->postsync = 1;
/* fall through */
case BLKIF_OP_WRITE:
ioreq->prot = PROT_READ; /* from memory */
- if (syncwrite) {
- ioreq->postsync = 1;
- }
break;
default:
xen_be_printf(&blkdev->xendev, 0, "error: unknown operation (%d)\n",
XenGnttab gnt = ioreq->blkdev->xendev.gnttabdev;
int i;
- if (ioreq->v.niov == 0) {
+ if (ioreq->v.niov == 0 || ioreq->mapped == 0) {
return;
}
if (batch_maps) {
ioreq->page[i] = NULL;
}
}
+ ioreq->mapped = 0;
}
static int ioreq_map(struct ioreq *ioreq)
XenGnttab gnt = ioreq->blkdev->xendev.gnttabdev;
int i;
- if (ioreq->v.niov == 0) {
+ if (ioreq->v.niov == 0 || ioreq->mapped == 1) {
return 0;
}
if (batch_maps) {
ioreq->blkdev->cnt_map++;
}
}
+ ioreq->mapped = 1;
return 0;
}
-static int ioreq_runio_qemu_sync(struct ioreq *ioreq)
-{
- struct XenBlkDev *blkdev = ioreq->blkdev;
- int i, rc;
- off_t pos;
-
- if (ioreq->req.nr_segments && ioreq_map(ioreq) == -1) {
- goto err_no_map;
- }
- if (ioreq->presync) {
- bdrv_flush(blkdev->bs);
- }
-
- switch (ioreq->req.operation) {
- case BLKIF_OP_READ:
- pos = ioreq->start;
- for (i = 0; i < ioreq->v.niov; i++) {
- rc = bdrv_read(blkdev->bs, pos / BLOCK_SIZE,
- ioreq->v.iov[i].iov_base,
- ioreq->v.iov[i].iov_len / BLOCK_SIZE);
- if (rc != 0) {
- xen_be_printf(&blkdev->xendev, 0, "rd I/O error (%p, len %zd)\n",
- ioreq->v.iov[i].iov_base,
- ioreq->v.iov[i].iov_len);
- goto err;
- }
- pos += ioreq->v.iov[i].iov_len;
- }
- break;
- case BLKIF_OP_WRITE:
- case BLKIF_OP_WRITE_BARRIER:
- if (!ioreq->req.nr_segments) {
- break;
- }
- pos = ioreq->start;
- for (i = 0; i < ioreq->v.niov; i++) {
- rc = bdrv_write(blkdev->bs, pos / BLOCK_SIZE,
- ioreq->v.iov[i].iov_base,
- ioreq->v.iov[i].iov_len / BLOCK_SIZE);
- if (rc != 0) {
- xen_be_printf(&blkdev->xendev, 0, "wr I/O error (%p, len %zd)\n",
- ioreq->v.iov[i].iov_base,
- ioreq->v.iov[i].iov_len);
- goto err;
- }
- pos += ioreq->v.iov[i].iov_len;
- }
- break;
- default:
- /* unknown operation (shouldn't happen -- parse catches this) */
- goto err;
- }
-
- if (ioreq->postsync) {
- bdrv_flush(blkdev->bs);
- }
- ioreq->status = BLKIF_RSP_OKAY;
-
- ioreq_unmap(ioreq);
- ioreq_finish(ioreq);
- return 0;
-
-err:
- ioreq_unmap(ioreq);
-err_no_map:
- ioreq_finish(ioreq);
- ioreq->status = BLKIF_RSP_ERROR;
- return -1;
-}
+static int ioreq_runio_qemu_aio(struct ioreq *ioreq);
static void qemu_aio_complete(void *opaque, int ret)
{
}
ioreq->aio_inflight--;
+ if (ioreq->presync) {
+ ioreq->presync = 0;
+ ioreq_runio_qemu_aio(ioreq);
+ return;
+ }
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);
ioreq_finish(ioreq);
+ bdrv_acct_done(ioreq->blkdev->bs, &ioreq->acct);
qemu_bh_schedule(ioreq->blkdev->bh);
}
ioreq->aio_inflight++;
if (ioreq->presync) {
- bdrv_flush(blkdev->bs); /* FIXME: aio_flush() ??? */
+ bdrv_aio_flush(ioreq->blkdev->bs, 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);
ioreq->aio_inflight++;
bdrv_aio_readv(blkdev->bs, ioreq->start / BLOCK_SIZE,
&ioreq->v, ioreq->v.size / BLOCK_SIZE,
if (!ioreq->req.nr_segments) {
break;
}
+
+ bdrv_acct_start(blkdev->bs, &ioreq->acct, ioreq->v.size, BDRV_ACCT_WRITE);
ioreq->aio_inflight++;
bdrv_aio_writev(blkdev->bs, ioreq->start / BLOCK_SIZE,
&ioreq->v, ioreq->v.size / BLOCK_SIZE,
goto err;
}
- if (ioreq->postsync) {
- bdrv_flush(blkdev->bs); /* FIXME: aio_flush() ??? */
- }
qemu_aio_complete(ioreq, 0);
return 0;
while (!QLIST_EMPTY(&blkdev->finished)) {
ioreq = QLIST_FIRST(&blkdev->finished);
send_notify += blk_send_response_one(ioreq);
- ioreq_release(ioreq);
+ ioreq_release(ioreq, true);
}
if (send_notify) {
xen_be_send_notify(&blkdev->xendev);
rp = blkdev->rings.common.sring->req_prod;
xen_rmb(); /* Ensure we see queued requests up to 'rp'. */
- if (use_aio) {
- blk_send_response_all(blkdev);
- }
+ blk_send_response_all(blkdev);
while (rc != rp) {
/* pull request from ring */
if (RING_REQUEST_CONS_OVERFLOW(&blkdev->rings.common, rc)) {
if (blk_send_response_one(ioreq)) {
xen_be_send_notify(&blkdev->xendev);
}
- ioreq_release(ioreq);
+ ioreq_release(ioreq, false);
continue;
}
- if (use_aio) {
- /* run i/o in aio mode */
- ioreq_runio_qemu_aio(ioreq);
- } else {
- /* run i/o in sync mode */
- ioreq_runio_qemu_sync(ioreq);
- }
- }
- if (!use_aio) {
- blk_send_response_all(blkdev);
+ ioreq_runio_qemu_aio(ioreq);
}
if (blkdev->more_work && blkdev->requests_inflight < max_requests) {
blk_handle_requests(blkdev);
}
+/*
+ * We need to account for the grant allocations requiring contiguous
+ * chunks; the worst case number would be
+ * max_req * max_seg + (max_req - 1) * (max_seg - 1) + 1,
+ * but in order to keep things simple just use
+ * 2 * max_req * max_seg.
+ */
+#define MAX_GRANTS(max_req, max_seg) (2 * (max_req) * (max_seg))
+
static void blk_alloc(struct XenDevice *xendev)
{
struct XenBlkDev *blkdev = container_of(xendev, struct XenBlkDev, xendev);
if (xen_mode != XEN_EMULATE) {
batch_maps = 1;
}
+ if (xc_gnttab_set_max_grants(xendev->gnttabdev,
+ MAX_GRANTS(max_requests, BLKIF_MAX_SEGMENTS_PER_REQUEST)) < 0) {
+ xen_be_printf(xendev, 0, "xc_gnttab_set_max_grants failed: %s\n",
+ strerror(errno));
+ }
}
static int blk_init(struct XenDevice *xendev)
{
struct XenBlkDev *blkdev = container_of(xendev, struct XenBlkDev, xendev);
- int index, qflags, have_barriers, info = 0;
+ int index, qflags, info = 0;
/* read xenstore entries */
if (blkdev->params == NULL) {
}
/* read-only ? */
+ qflags = BDRV_O_NOCACHE | BDRV_O_CACHE_WB | BDRV_O_NATIVE_AIO;
if (strcmp(blkdev->mode, "w") == 0) {
- qflags = BDRV_O_RDWR;
+ qflags |= BDRV_O_RDWR;
} else {
- qflags = 0;
info |= VDISK_READONLY;
}
xen_be_printf(&blkdev->xendev, 2, "get configured bdrv (cmdline setup)\n");
blkdev->bs = blkdev->dinfo->bdrv;
}
+ bdrv_attach_dev_nofail(blkdev->bs, blkdev);
blkdev->file_blk = BLOCK_SIZE;
blkdev->file_size = bdrv_getlength(blkdev->bs);
if (blkdev->file_size < 0) {
xen_be_printf(&blkdev->xendev, 1, "bdrv_getlength: %d (%s) | drv %s\n",
(int)blkdev->file_size, strerror(-blkdev->file_size),
- blkdev->bs->drv ? blkdev->bs->drv->format_name : "-");
+ bdrv_get_format_name(blkdev->bs) ?: "-");
blkdev->file_size = 0;
}
- have_barriers = blkdev->bs->drv && blkdev->bs->drv->bdrv_flush ? 1 : 0;
xen_be_printf(xendev, 1, "type \"%s\", fileproto \"%s\", filename \"%s\","
" size %" PRId64 " (%" PRId64 " MB)\n",
blkdev->file_size, blkdev->file_size >> 20);
/* fill info */
- xenstore_write_be_int(&blkdev->xendev, "feature-barrier", have_barriers);
+ xenstore_write_be_int(&blkdev->xendev, "feature-barrier", 1);
xenstore_write_be_int(&blkdev->xendev, "info", info);
xenstore_write_be_int(&blkdev->xendev, "sector-size", blkdev->file_blk);
xenstore_write_be_int(&blkdev->xendev, "sectors",
return 0;
out_error:
- qemu_free(blkdev->params);
+ g_free(blkdev->params);
blkdev->params = NULL;
- qemu_free(blkdev->mode);
+ g_free(blkdev->mode);
blkdev->mode = NULL;
- qemu_free(blkdev->type);
+ g_free(blkdev->type);
blkdev->type = NULL;
- qemu_free(blkdev->dev);
+ g_free(blkdev->dev);
blkdev->dev = NULL;
- qemu_free(blkdev->devtype);
+ g_free(blkdev->devtype);
blkdev->devtype = NULL;
return -1;
}
if (!blkdev->dinfo) {
/* close/delete only if we created it ourself */
bdrv_close(blkdev->bs);
+ bdrv_detach_dev(blkdev->bs, blkdev);
bdrv_delete(blkdev->bs);
}
blkdev->bs = NULL;
struct XenBlkDev *blkdev = container_of(xendev, struct XenBlkDev, xendev);
struct ioreq *ioreq;
+ if (blkdev->bs || blkdev->sring) {
+ blk_disconnect(xendev);
+ }
+
while (!QLIST_EMPTY(&blkdev->freelist)) {
ioreq = QLIST_FIRST(&blkdev->freelist);
QLIST_REMOVE(ioreq, list);
qemu_iovec_destroy(&ioreq->v);
- qemu_free(ioreq);
+ g_free(ioreq);
}
- qemu_free(blkdev->params);
- qemu_free(blkdev->mode);
- qemu_free(blkdev->type);
- qemu_free(blkdev->dev);
- qemu_free(blkdev->devtype);
+ g_free(blkdev->params);
+ g_free(blkdev->mode);
+ g_free(blkdev->type);
+ g_free(blkdev->dev);
+ g_free(blkdev->devtype);
qemu_bh_delete(blkdev->bh);
return 0;
}
.flags = DEVOPS_FLAG_NEED_GNTDEV,
.alloc = blk_alloc,
.init = blk_init,
- .connect = blk_connect,
+ .initialise = blk_connect,
.disconnect = blk_disconnect,
.event = blk_event,
.free = blk_free,