aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorPeter Maydell <peter.maydell@linaro.org>2019-01-14 13:54:17 +0000
committerPeter Maydell <peter.maydell@linaro.org>2019-01-14 13:54:17 +0000
commitc9d18c1c150c84e7a976df989ad04ddf01083f46 (patch)
treeb4b04c95b9c75162cdf60dbcda51c9ca7563071b
parent7260438b7056469610ee166f7abe9ff8a26b8b16 (diff)
parentc6025bd197d0dbcc5067553fd12538d8b29383c2 (diff)
downloadqemu-c9d18c1c150c84e7a976df989ad04ddf01083f46.zip
qemu-c9d18c1c150c84e7a976df989ad04ddf01083f46.tar.gz
qemu-c9d18c1c150c84e7a976df989ad04ddf01083f46.tar.bz2
Merge remote-tracking branch 'remotes/aperard/tags/pull-xen-20190114' into staging
Xen queue * Xen PV backend 'qdevification'. Starting with xen_disk. * Performance improvements for xen-block. * Remove of the Xen PV domain builder. * bug fixes. # gpg: Signature made Mon 14 Jan 2019 13:46:33 GMT # gpg: using RSA key 0CF5572FD7FB55AF # gpg: Good signature from "Anthony PERARD <anthony.perard@gmail.com>" # gpg: aka "Anthony PERARD <anthony.perard@citrix.com>" # gpg: WARNING: This key is not certified with sufficiently trusted signatures! # gpg: It is not certain that the signature belongs to the owner. # Primary key fingerprint: 5379 2F71 024C 600F 778A 7161 D8D5 7199 DF83 42C8 # Subkey fingerprint: F80C 0063 08E2 2CFD 8A92 E798 0CF5 572F D7FB 55AF * remotes/aperard/tags/pull-xen-20190114: (25 commits) xen-block: avoid repeated memory allocation xen-block: improve response latency xen-block: improve batching behaviour xen: Replace few mentions of xend by libxl Remove broken Xen PV domain builder xen: remove the legacy 'xen_disk' backend MAINTAINERS: add myself as a Xen maintainer xen: automatically create XenBlockDevice-s xen: add a mechanism to automatically create XenDevice-s... xen: add implementations of xen-block connect and disconnect functions... xen: purge 'blk' and 'ioreq' from function names in dataplane/xen-block.c xen: remove 'ioreq' struct/varable/field names from dataplane/xen-block.c xen: remove 'XenBlkDev' and 'blkdev' names from dataplane/xen-block xen: add header and build dataplane/xen-block.c xen: remove unnecessary code from dataplane/xen-block.c xen: duplicate xen_disk.c as basis of dataplane/xen-block.c xen: add event channel interface for XenDevice-s xen: add grant table interface for XenDevice-s xen: add xenstore watcher infrastructure xen: create xenstore areas for XenDevice-s ... Signed-off-by: Peter Maydell <peter.maydell@linaro.org>
-rw-r--r--MAINTAINERS5
-rwxr-xr-xconfigure17
-rw-r--r--hw/9pfs/xen-9p-backend.c16
-rw-r--r--hw/block/Makefile.objs2
-rw-r--r--hw/block/dataplane/Makefile.objs1
-rw-r--r--hw/block/dataplane/xen-block.c827
-rw-r--r--hw/block/dataplane/xen-block.h29
-rw-r--r--hw/block/trace-events14
-rw-r--r--hw/block/xen-block.c963
-rw-r--r--hw/block/xen_disk.c1011
-rw-r--r--hw/char/xen_console.c12
-rw-r--r--hw/display/xenfb.c25
-rw-r--r--hw/i386/xen/xen-hvm.c5
-rw-r--r--hw/i386/xen/xen-mapcache.c2
-rw-r--r--hw/i386/xen/xen_platform.c2
-rw-r--r--hw/net/xen_nic.c14
-rw-r--r--hw/usb/xen-usb.c25
-rw-r--r--hw/xen/Makefile.objs2
-rw-r--r--hw/xen/trace-events26
-rw-r--r--hw/xen/xen-backend.c165
-rw-r--r--hw/xen/xen-bus-helper.c184
-rw-r--r--hw/xen/xen-bus.c1199
-rw-r--r--hw/xen/xen-common.c2
-rw-r--r--hw/xen/xen-legacy-backend.c (renamed from hw/xen/xen_backend.c)80
-rw-r--r--hw/xen/xen_devconfig.c2
-rw-r--r--hw/xen/xen_pt.c8
-rw-r--r--hw/xen/xen_pt_config_init.c6
-rw-r--r--hw/xen/xen_pt_graphics.c18
-rw-r--r--hw/xen/xen_pt_msi.c2
-rw-r--r--hw/xen/xen_pvdev.c20
-rw-r--r--hw/xenpv/Makefile.objs2
-rw-r--r--hw/xenpv/xen_domainbuild.c299
-rw-r--r--hw/xenpv/xen_domainbuild.h13
-rw-r--r--hw/xenpv/xen_machine_pv.c21
-rw-r--r--include/hw/xen/xen-backend.h39
-rw-r--r--include/hw/xen/xen-block.h94
-rw-r--r--include/hw/xen/xen-bus-helper.h45
-rw-r--r--include/hw/xen/xen-bus.h137
-rw-r--r--include/hw/xen/xen-legacy-backend.h (renamed from include/hw/xen/xen_backend.h)43
-rw-r--r--include/hw/xen/xen.h3
-rw-r--r--include/hw/xen/xen_common.h19
-rw-r--r--include/hw/xen/xen_pvdev.h38
-rw-r--r--include/qemu/module.h3
-rw-r--r--qemu-options.hx12
-rw-r--r--vl.c7
45 files changed, 3923 insertions, 1536 deletions
diff --git a/MAINTAINERS b/MAINTAINERS
index 87f9072..2a1520d 100644
--- a/MAINTAINERS
+++ b/MAINTAINERS
@@ -407,6 +407,7 @@ Guest CPU Cores (Xen):
X86
M: Stefano Stabellini <sstabellini@kernel.org>
M: Anthony Perard <anthony.perard@citrix.com>
+M: Paul Durrant <paul.durrant@citrix.com>
L: xen-devel@lists.xenproject.org
S: Supported
F: */xen*
@@ -414,10 +415,12 @@ F: hw/9pfs/xen-9p-backend.c
F: hw/char/xen_console.c
F: hw/display/xenfb.c
F: hw/net/xen_nic.c
-F: hw/block/xen_*
+F: hw/block/xen*
+F: hw/block/dataplane/xen*
F: hw/xen/
F: hw/xenpv/
F: hw/i386/xen/
+F: include/hw/block/dataplane/xen*
F: include/hw/xen/
F: include/sysemu/xen-mapcache.h
diff --git a/configure b/configure
index 4ea3f14..f992709 100755
--- a/configure
+++ b/configure
@@ -357,7 +357,6 @@ vnc_png=""
xkbcommon=""
xen=""
xen_ctrl_version=""
-xen_pv_domain_build="no"
xen_pci_passthrough=""
linux_aio=""
cap_ng=""
@@ -1119,10 +1118,6 @@ for opt do
;;
--enable-xen-pci-passthrough) xen_pci_passthrough="yes"
;;
- --disable-xen-pv-domain-build) xen_pv_domain_build="no"
- ;;
- --enable-xen-pv-domain-build) xen_pv_domain_build="yes"
- ;;
--disable-brlapi) brlapi="no"
;;
--enable-brlapi) brlapi="yes"
@@ -1685,8 +1680,6 @@ Advanced options (experts only):
--tls-priority default TLS protocol/cipher priority string
--enable-gprof QEMU profiling with gprof
--enable-profiler profiler support
- --enable-xen-pv-domain-build
- xen pv domain builder
--enable-debug-stack-usage
track the maximum stack usage of stacks created by qemu_alloc_stack
@@ -2678,12 +2671,6 @@ if test "$xen_pci_passthrough" != "no"; then
fi
fi
-if test "$xen_pv_domain_build" = "yes" &&
- test "$xen" != "yes"; then
- error_exit "User requested Xen PV domain builder support" \
- "which requires Xen support."
-fi
-
##########################################
# Windows Hypervisor Platform accelerator (WHPX) check
if test "$whpx" != "no" ; then
@@ -6069,7 +6056,6 @@ fi
echo "xen support $xen"
if test "$xen" = "yes" ; then
echo "xen ctrl version $xen_ctrl_version"
- echo "pv dom build $xen_pv_domain_build"
fi
echo "brlapi support $brlapi"
echo "bluez support $bluez"
@@ -6539,9 +6525,6 @@ fi
if test "$xen" = "yes" ; then
echo "CONFIG_XEN_BACKEND=y" >> $config_host_mak
echo "CONFIG_XEN_CTRL_INTERFACE_VERSION=$xen_ctrl_version" >> $config_host_mak
- if test "$xen_pv_domain_build" = "yes" ; then
- echo "CONFIG_XEN_PV_DOMAIN_BUILD=y" >> $config_host_mak
- fi
fi
if test "$linux_aio" = "yes" ; then
echo "CONFIG_LINUX_AIO=y" >> $config_host_mak
diff --git a/hw/9pfs/xen-9p-backend.c b/hw/9pfs/xen-9p-backend.c
index 9015fe7..25ab04d 100644
--- a/hw/9pfs/xen-9p-backend.c
+++ b/hw/9pfs/xen-9p-backend.c
@@ -12,7 +12,7 @@
#include "hw/hw.h"
#include "hw/9pfs/9p.h"
-#include "hw/xen/xen_backend.h"
+#include "hw/xen/xen-legacy-backend.h"
#include "hw/9pfs/xen-9pfs.h"
#include "qapi/error.h"
#include "qemu/config-file.h"
@@ -45,7 +45,7 @@ typedef struct Xen9pfsRing {
} Xen9pfsRing;
typedef struct Xen9pfsDev {
- struct XenDevice xendev; /* must be first */
+ struct XenLegacyDevice xendev; /* must be first */
V9fsState state;
char *path;
char *security_model;
@@ -56,7 +56,7 @@ typedef struct Xen9pfsDev {
Xen9pfsRing *rings;
} Xen9pfsDev;
-static void xen_9pfs_disconnect(struct XenDevice *xendev);
+static void xen_9pfs_disconnect(struct XenLegacyDevice *xendev);
static void xen_9pfs_in_sg(Xen9pfsRing *ring,
struct iovec *in_sg,
@@ -243,7 +243,7 @@ static const V9fsTransport xen_9p_transport = {
.push_and_notify = xen_9pfs_push_and_notify,
};
-static int xen_9pfs_init(struct XenDevice *xendev)
+static int xen_9pfs_init(struct XenLegacyDevice *xendev)
{
return 0;
}
@@ -305,7 +305,7 @@ static void xen_9pfs_evtchn_event(void *opaque)
qemu_bh_schedule(ring->bh);
}
-static void xen_9pfs_disconnect(struct XenDevice *xendev)
+static void xen_9pfs_disconnect(struct XenLegacyDevice *xendev)
{
Xen9pfsDev *xen_9pdev = container_of(xendev, Xen9pfsDev, xendev);
int i;
@@ -321,7 +321,7 @@ static void xen_9pfs_disconnect(struct XenDevice *xendev)
}
}
-static int xen_9pfs_free(struct XenDevice *xendev)
+static int xen_9pfs_free(struct XenLegacyDevice *xendev)
{
Xen9pfsDev *xen_9pdev = container_of(xendev, Xen9pfsDev, xendev);
int i;
@@ -354,7 +354,7 @@ static int xen_9pfs_free(struct XenDevice *xendev)
return 0;
}
-static int xen_9pfs_connect(struct XenDevice *xendev)
+static int xen_9pfs_connect(struct XenLegacyDevice *xendev)
{
Error *err = NULL;
int i;
@@ -467,7 +467,7 @@ out:
return -1;
}
-static void xen_9pfs_alloc(struct XenDevice *xendev)
+static void xen_9pfs_alloc(struct XenLegacyDevice *xendev)
{
xenstore_write_be_str(xendev, "versions", VERSIONS);
xenstore_write_be_int(xendev, "max-rings", MAX_RINGS);
diff --git a/hw/block/Makefile.objs b/hw/block/Makefile.objs
index 53ce575..e206b8e 100644
--- a/hw/block/Makefile.objs
+++ b/hw/block/Makefile.objs
@@ -4,7 +4,7 @@ common-obj-$(CONFIG_SSI_M25P80) += m25p80.o
common-obj-$(CONFIG_NAND) += nand.o
common-obj-$(CONFIG_PFLASH_CFI01) += pflash_cfi01.o
common-obj-$(CONFIG_PFLASH_CFI02) += pflash_cfi02.o
-common-obj-$(CONFIG_XEN) += xen_disk.o
+common-obj-$(CONFIG_XEN) += xen-block.o
common-obj-$(CONFIG_ECC) += ecc.o
common-obj-$(CONFIG_ONENAND) += onenand.o
common-obj-$(CONFIG_NVME_PCI) += nvme.o
diff --git a/hw/block/dataplane/Makefile.objs b/hw/block/dataplane/Makefile.objs
index e786f66..c6c68db 100644
--- a/hw/block/dataplane/Makefile.objs
+++ b/hw/block/dataplane/Makefile.objs
@@ -1 +1,2 @@
obj-y += virtio-blk.o
+obj-$(CONFIG_XEN) += xen-block.o
diff --git a/hw/block/dataplane/xen-block.c b/hw/block/dataplane/xen-block.c
new file mode 100644
index 0000000..d0d8905
--- /dev/null
+++ b/hw/block/dataplane/xen-block.c
@@ -0,0 +1,827 @@
+/*
+ * Copyright (c) 2018 Citrix Systems Inc.
+ * (c) Gerd Hoffmann <kraxel@redhat.com>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; under version 2 of the License.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * 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 "qemu/osdep.h"
+#include "qemu/error-report.h"
+#include "qapi/error.h"
+#include "hw/hw.h"
+#include "hw/xen/xen_common.h"
+#include "hw/block/xen_blkif.h"
+#include "sysemu/block-backend.h"
+#include "sysemu/iothread.h"
+#include "xen-block.h"
+
+typedef struct XenBlockRequest {
+ blkif_request_t req;
+ int16_t status;
+ off_t start;
+ QEMUIOVector v;
+ void *buf;
+ size_t size;
+ int presync;
+ int aio_inflight;
+ int aio_errors;
+ XenBlockDataPlane *dataplane;
+ QLIST_ENTRY(XenBlockRequest) list;
+ BlockAcctCookie acct;
+} XenBlockRequest;
+
+struct XenBlockDataPlane {
+ XenDevice *xendev;
+ XenEventChannel *event_channel;
+ unsigned int *ring_ref;
+ unsigned int nr_ring_ref;
+ void *sring;
+ int64_t file_blk;
+ int64_t file_size;
+ int protocol;
+ blkif_back_rings_t rings;
+ int more_work;
+ QLIST_HEAD(inflight_head, XenBlockRequest) inflight;
+ QLIST_HEAD(freelist_head, XenBlockRequest) freelist;
+ int requests_total;
+ int requests_inflight;
+ unsigned int max_requests;
+ BlockBackend *blk;
+ QEMUBH *bh;
+ IOThread *iothread;
+ AioContext *ctx;
+};
+
+static void reset_request(XenBlockRequest *request)
+{
+ memset(&request->req, 0, sizeof(request->req));
+ request->status = 0;
+ request->start = 0;
+ request->size = 0;
+ request->presync = 0;
+
+ request->aio_inflight = 0;
+ request->aio_errors = 0;
+
+ request->dataplane = NULL;
+ memset(&request->list, 0, sizeof(request->list));
+ memset(&request->acct, 0, sizeof(request->acct));
+
+ qemu_iovec_reset(&request->v);
+}
+
+static XenBlockRequest *xen_block_start_request(XenBlockDataPlane *dataplane)
+{
+ XenBlockRequest *request = NULL;
+
+ if (QLIST_EMPTY(&dataplane->freelist)) {
+ if (dataplane->requests_total >= dataplane->max_requests) {
+ goto out;
+ }
+ /* allocate new struct */
+ request = g_malloc0(sizeof(*request));
+ request->dataplane = dataplane;
+ /*
+ * We cannot need more pages per requests than this, and since we
+ * re-use requests, allocate the memory once here. It will be freed
+ * xen_block_dataplane_destroy() when the request list is freed.
+ */
+ request->buf = qemu_memalign(XC_PAGE_SIZE,
+ BLKIF_MAX_SEGMENTS_PER_REQUEST *
+ XC_PAGE_SIZE);
+ dataplane->requests_total++;
+ qemu_iovec_init(&request->v, 1);
+ } else {
+ /* get one from freelist */
+ request = QLIST_FIRST(&dataplane->freelist);
+ QLIST_REMOVE(request, list);
+ }
+ QLIST_INSERT_HEAD(&dataplane->inflight, request, list);
+ dataplane->requests_inflight++;
+
+out:
+ return request;
+}
+
+static void xen_block_finish_request(XenBlockRequest *request)
+{
+ XenBlockDataPlane *dataplane = request->dataplane;
+
+ QLIST_REMOVE(request, list);
+ dataplane->requests_inflight--;
+}
+
+static void xen_block_release_request(XenBlockRequest *request)
+{
+ XenBlockDataPlane *dataplane = request->dataplane;
+
+ QLIST_REMOVE(request, list);
+ reset_request(request);
+ request->dataplane = dataplane;
+ QLIST_INSERT_HEAD(&dataplane->freelist, request, list);
+ dataplane->requests_inflight--;
+}
+
+/*
+ * translate request into iovec + start offset
+ * do sanity checks along the way
+ */
+static int xen_block_parse_request(XenBlockRequest *request)
+{
+ XenBlockDataPlane *dataplane = request->dataplane;
+ size_t len;
+ int i;
+
+ switch (request->req.operation) {
+ case BLKIF_OP_READ:
+ break;
+ case BLKIF_OP_FLUSH_DISKCACHE:
+ request->presync = 1;
+ if (!request->req.nr_segments) {
+ return 0;
+ }
+ /* fall through */
+ case BLKIF_OP_WRITE:
+ break;
+ case BLKIF_OP_DISCARD:
+ return 0;
+ default:
+ error_report("error: unknown operation (%d)", request->req.operation);
+ goto err;
+ };
+
+ if (request->req.operation != BLKIF_OP_READ &&
+ blk_is_read_only(dataplane->blk)) {
+ error_report("error: write req for ro device");
+ goto err;
+ }
+
+ request->start = request->req.sector_number * dataplane->file_blk;
+ for (i = 0; i < request->req.nr_segments; i++) {
+ if (i == BLKIF_MAX_SEGMENTS_PER_REQUEST) {
+ error_report("error: nr_segments too big");
+ goto err;
+ }
+ if (request->req.seg[i].first_sect > request->req.seg[i].last_sect) {
+ error_report("error: first > last sector");
+ goto err;
+ }
+ if (request->req.seg[i].last_sect * dataplane->file_blk >=
+ XC_PAGE_SIZE) {
+ error_report("error: page crossing");
+ goto err;
+ }
+
+ len = (request->req.seg[i].last_sect -
+ request->req.seg[i].first_sect + 1) * dataplane->file_blk;
+ request->size += len;
+ }
+ if (request->start + request->size > dataplane->file_size) {
+ error_report("error: access beyond end of file");
+ goto err;
+ }
+ return 0;
+
+err:
+ request->status = BLKIF_RSP_ERROR;
+ return -1;
+}
+
+static int xen_block_copy_request(XenBlockRequest *request)
+{
+ XenBlockDataPlane *dataplane = request->dataplane;
+ XenDevice *xendev = dataplane->xendev;
+ XenDeviceGrantCopySegment segs[BLKIF_MAX_SEGMENTS_PER_REQUEST];
+ int i, count;
+ int64_t file_blk = dataplane->file_blk;
+ bool to_domain = (request->req.operation == BLKIF_OP_READ);
+ void *virt = request->buf;
+ Error *local_err = NULL;
+
+ if (request->req.nr_segments == 0) {
+ return 0;
+ }
+
+ count = request->req.nr_segments;
+
+ for (i = 0; i < count; i++) {
+ if (to_domain) {
+ segs[i].dest.foreign.ref = request->req.seg[i].gref;
+ segs[i].dest.foreign.offset = request->req.seg[i].first_sect *
+ file_blk;
+ segs[i].source.virt = virt;
+ } else {
+ segs[i].source.foreign.ref = request->req.seg[i].gref;
+ segs[i].source.foreign.offset = request->req.seg[i].first_sect *
+ file_blk;
+ segs[i].dest.virt = virt;
+ }
+ segs[i].len = (request->req.seg[i].last_sect -
+ request->req.seg[i].first_sect + 1) * file_blk;
+ virt += segs[i].len;
+ }
+
+ xen_device_copy_grant_refs(xendev, to_domain, segs, count, &local_err);
+
+ if (local_err) {
+ error_reportf_err(local_err, "failed to copy data: ");
+
+ request->aio_errors++;
+ return -1;
+ }
+
+ return 0;
+}
+
+static int xen_block_do_aio(XenBlockRequest *request);
+static int xen_block_send_response(XenBlockRequest *request);
+
+static void xen_block_complete_aio(void *opaque, int ret)
+{
+ XenBlockRequest *request = opaque;
+ XenBlockDataPlane *dataplane = request->dataplane;
+
+ aio_context_acquire(dataplane->ctx);
+
+ if (ret != 0) {
+ error_report("%s I/O error",
+ request->req.operation == BLKIF_OP_READ ?
+ "read" : "write");
+ request->aio_errors++;
+ }
+
+ request->aio_inflight--;
+ if (request->presync) {
+ request->presync = 0;
+ xen_block_do_aio(request);
+ goto done;
+ }
+ if (request->aio_inflight > 0) {
+ goto done;
+ }
+
+ switch (request->req.operation) {
+ case BLKIF_OP_READ:
+ /* in case of failure request->aio_errors is increased */
+ if (ret == 0) {
+ xen_block_copy_request(request);
+ }
+ break;
+ case BLKIF_OP_WRITE:
+ case BLKIF_OP_FLUSH_DISKCACHE:
+ if (!request->req.nr_segments) {
+ break;
+ }
+ break;
+ default:
+ break;
+ }
+
+ request->status = request->aio_errors ? BLKIF_RSP_ERROR : BLKIF_RSP_OKAY;
+ xen_block_finish_request(request);
+
+ switch (request->req.operation) {
+ case BLKIF_OP_WRITE:
+ case BLKIF_OP_FLUSH_DISKCACHE:
+ if (!request->req.nr_segments) {
+ break;
+ }
+ case BLKIF_OP_READ:
+ if (request->status == BLKIF_RSP_OKAY) {
+ block_acct_done(blk_get_stats(dataplane->blk), &request->acct);
+ } else {
+ block_acct_failed(blk_get_stats(dataplane->blk), &request->acct);
+ }
+ break;
+ case BLKIF_OP_DISCARD:
+ default:
+ break;
+ }
+ if (xen_block_send_response(request)) {
+ Error *local_err = NULL;
+
+ xen_device_notify_event_channel(dataplane->xendev,
+ dataplane->event_channel,
+ &local_err);
+ if (local_err) {
+ error_report_err(local_err);
+ }
+ }
+ xen_block_release_request(request);
+
+ qemu_bh_schedule(dataplane->bh);
+
+done:
+ aio_context_release(dataplane->ctx);
+}
+
+static bool xen_block_split_discard(XenBlockRequest *request,
+ blkif_sector_t sector_number,
+ uint64_t nr_sectors)
+{
+ XenBlockDataPlane *dataplane = request->dataplane;
+ int64_t byte_offset;
+ int byte_chunk;
+ uint64_t byte_remaining, limit;
+ uint64_t sec_start = sector_number;
+ uint64_t sec_count = nr_sectors;
+
+ /* Wrap around, or overflowing byte limit? */
+ if (sec_start + sec_count < sec_count ||
+ sec_start + sec_count > INT64_MAX / dataplane->file_blk) {
+ return false;
+ }
+
+ limit = BDRV_REQUEST_MAX_SECTORS * dataplane->file_blk;
+ byte_offset = sec_start * dataplane->file_blk;
+ byte_remaining = sec_count * dataplane->file_blk;
+
+ do {
+ byte_chunk = byte_remaining > limit ? limit : byte_remaining;
+ request->aio_inflight++;
+ blk_aio_pdiscard(dataplane->blk, byte_offset, byte_chunk,
+ xen_block_complete_aio, request);
+ byte_remaining -= byte_chunk;
+ byte_offset += byte_chunk;
+ } while (byte_remaining > 0);
+
+ return true;
+}
+
+static int xen_block_do_aio(XenBlockRequest *request)
+{
+ XenBlockDataPlane *dataplane = request->dataplane;
+
+ if (request->req.nr_segments &&
+ (request->req.operation == BLKIF_OP_WRITE ||
+ request->req.operation == BLKIF_OP_FLUSH_DISKCACHE) &&
+ xen_block_copy_request(request)) {
+ goto err;
+ }
+
+ request->aio_inflight++;
+ if (request->presync) {
+ blk_aio_flush(request->dataplane->blk, xen_block_complete_aio,
+ request);
+ return 0;
+ }
+
+ switch (request->req.operation) {
+ case BLKIF_OP_READ:
+ qemu_iovec_add(&request->v, request->buf, request->size);
+ block_acct_start(blk_get_stats(dataplane->blk), &request->acct,
+ request->v.size, BLOCK_ACCT_READ);
+ request->aio_inflight++;
+ blk_aio_preadv(dataplane->blk, request->start, &request->v, 0,
+ xen_block_complete_aio, request);
+ break;
+ case BLKIF_OP_WRITE:
+ case BLKIF_OP_FLUSH_DISKCACHE:
+ if (!request->req.nr_segments) {
+ break;
+ }
+
+ qemu_iovec_add(&request->v, request->buf, request->size);
+ block_acct_start(blk_get_stats(dataplane->blk), &request->acct,
+ request->v.size,
+ request->req.operation == BLKIF_OP_WRITE ?
+ BLOCK_ACCT_WRITE : BLOCK_ACCT_FLUSH);
+ request->aio_inflight++;
+ blk_aio_pwritev(dataplane->blk, request->start, &request->v, 0,
+ xen_block_complete_aio, request);
+ break;
+ case BLKIF_OP_DISCARD:
+ {
+ struct blkif_request_discard *req = (void *)&request->req;
+ if (!xen_block_split_discard(request, req->sector_number,
+ req->nr_sectors)) {
+ goto err;
+ }
+ break;
+ }
+ default:
+ /* unknown operation (shouldn't happen -- parse catches this) */
+ goto err;
+ }
+
+ xen_block_complete_aio(request, 0);
+
+ return 0;
+
+err:
+ xen_block_finish_request(request);
+ request->status = BLKIF_RSP_ERROR;
+ return -1;
+}
+
+static int xen_block_send_response(XenBlockRequest *request)
+{
+ XenBlockDataPlane *dataplane = request->dataplane;
+ int send_notify = 0;
+ int have_requests = 0;
+ blkif_response_t *resp;
+
+ /* Place on the response ring for the relevant domain. */
+ switch (dataplane->protocol) {
+ case BLKIF_PROTOCOL_NATIVE:
+ resp = (blkif_response_t *)RING_GET_RESPONSE(
+ &dataplane->rings.native,
+ dataplane->rings.native.rsp_prod_pvt);
+ break;
+ case BLKIF_PROTOCOL_X86_32:
+ resp = (blkif_response_t *)RING_GET_RESPONSE(
+ &dataplane->rings.x86_32_part,
+ dataplane->rings.x86_32_part.rsp_prod_pvt);
+ break;
+ case BLKIF_PROTOCOL_X86_64:
+ resp = (blkif_response_t *)RING_GET_RESPONSE(
+ &dataplane->rings.x86_64_part,
+ dataplane->rings.x86_64_part.rsp_prod_pvt);
+ break;
+ default:
+ return 0;
+ }
+
+ resp->id = request->req.id;
+ resp->operation = request->req.operation;
+ resp->status = request->status;
+
+ dataplane->rings.common.rsp_prod_pvt++;
+
+ RING_PUSH_RESPONSES_AND_CHECK_NOTIFY(&dataplane->rings.common,
+ send_notify);
+ if (dataplane->rings.common.rsp_prod_pvt ==
+ dataplane->rings.common.req_cons) {
+ /*
+ * Tail check for pending requests. Allows frontend to avoid
+ * notifications if requests are already in flight (lower
+ * overheads and promotes batching).
+ */
+ RING_FINAL_CHECK_FOR_REQUESTS(&dataplane->rings.common,
+ have_requests);
+ } else if (RING_HAS_UNCONSUMED_REQUESTS(&dataplane->rings.common)) {
+ have_requests = 1;
+ }
+
+ if (have_requests) {
+ dataplane->more_work++;
+ }
+ return send_notify;
+}
+
+static int xen_block_get_request(XenBlockDataPlane *dataplane,
+ XenBlockRequest *request, RING_IDX rc)
+{
+ switch (dataplane->protocol) {
+ case BLKIF_PROTOCOL_NATIVE: {
+ blkif_request_t *req =
+ RING_GET_REQUEST(&dataplane->rings.native, rc);
+
+ memcpy(&request->req, req, sizeof(request->req));
+ break;
+ }
+ case BLKIF_PROTOCOL_X86_32: {
+ blkif_x86_32_request_t *req =
+ RING_GET_REQUEST(&dataplane->rings.x86_32_part, rc);
+
+ blkif_get_x86_32_req(&request->req, req);
+ break;
+ }
+ case BLKIF_PROTOCOL_X86_64: {
+ blkif_x86_64_request_t *req =
+ RING_GET_REQUEST(&dataplane->rings.x86_64_part, rc);
+
+ blkif_get_x86_64_req(&request->req, req);
+ break;
+ }
+ }
+ /* Prevent the compiler from accessing the on-ring fields instead. */
+ barrier();
+ return 0;
+}
+
+/*
+ * Threshold of in-flight requests above which we will start using
+ * blk_io_plug()/blk_io_unplug() to batch requests.
+ */
+#define IO_PLUG_THRESHOLD 1
+
+static void xen_block_handle_requests(XenBlockDataPlane *dataplane)
+{
+ RING_IDX rc, rp;
+ XenBlockRequest *request;
+ int inflight_atstart = dataplane->requests_inflight;
+ int batched = 0;
+
+ dataplane->more_work = 0;
+
+ rc = dataplane->rings.common.req_cons;
+ rp = dataplane->rings.common.sring->req_prod;
+ xen_rmb(); /* Ensure we see queued requests up to 'rp'. */
+
+ /*
+ * If there was more than IO_PLUG_THRESHOLD requests in flight
+ * when we got here, this is an indication that there the bottleneck
+ * is below us, so it's worth beginning to batch up I/O requests
+ * rather than submitting them immediately. The maximum number
+ * of requests we're willing to batch is the number already in
+ * flight, so it can grow up to max_requests when the bottleneck
+ * is below us.
+ */
+ if (inflight_atstart > IO_PLUG_THRESHOLD) {
+ blk_io_plug(dataplane->blk);
+ }
+ while (rc != rp) {
+ /* pull request from ring */
+ if (RING_REQUEST_CONS_OVERFLOW(&dataplane->rings.common, rc)) {
+ break;
+ }
+ request = xen_block_start_request(dataplane);
+ if (request == NULL) {
+ dataplane->more_work++;
+ break;
+ }
+ xen_block_get_request(dataplane, request, rc);
+ dataplane->rings.common.req_cons = ++rc;
+
+ /* parse them */
+ if (xen_block_parse_request(request) != 0) {
+ switch (request->req.operation) {
+ case BLKIF_OP_READ:
+ block_acct_invalid(blk_get_stats(dataplane->blk),
+ BLOCK_ACCT_READ);
+ break;
+ case BLKIF_OP_WRITE:
+ block_acct_invalid(blk_get_stats(dataplane->blk),
+ BLOCK_ACCT_WRITE);
+ break;
+ case BLKIF_OP_FLUSH_DISKCACHE:
+ block_acct_invalid(blk_get_stats(dataplane->blk),
+ BLOCK_ACCT_FLUSH);
+ default:
+ break;
+ };
+
+ if (xen_block_send_response(request)) {
+ Error *local_err = NULL;
+
+ xen_device_notify_event_channel(dataplane->xendev,
+ dataplane->event_channel,
+ &local_err);
+ if (local_err) {
+ error_report_err(local_err);
+ }
+ }
+ xen_block_release_request(request);
+ continue;
+ }
+
+ if (inflight_atstart > IO_PLUG_THRESHOLD &&
+ batched >= inflight_atstart) {
+ blk_io_unplug(dataplane->blk);
+ }
+ xen_block_do_aio(request);
+ if (inflight_atstart > IO_PLUG_THRESHOLD) {
+ if (batched >= inflight_atstart) {
+ blk_io_plug(dataplane->blk);
+ batched = 0;
+ } else {
+ batched++;
+ }
+ }
+ }
+ if (inflight_atstart > IO_PLUG_THRESHOLD) {
+ blk_io_unplug(dataplane->blk);
+ }
+
+ if (dataplane->more_work &&
+ dataplane->requests_inflight < dataplane->max_requests) {
+ qemu_bh_schedule(dataplane->bh);
+ }
+}
+
+static void xen_block_dataplane_bh(void *opaque)
+{
+ XenBlockDataPlane *dataplane = opaque;
+
+ aio_context_acquire(dataplane->ctx);
+ xen_block_handle_requests(dataplane);
+ aio_context_release(dataplane->ctx);
+}
+
+static void xen_block_dataplane_event(void *opaque)
+{
+ XenBlockDataPlane *dataplane = opaque;
+
+ qemu_bh_schedule(dataplane->bh);
+}
+
+XenBlockDataPlane *xen_block_dataplane_create(XenDevice *xendev,
+ BlockConf *conf,
+ IOThread *iothread)
+{
+ XenBlockDataPlane *dataplane = g_new0(XenBlockDataPlane, 1);
+
+ dataplane->xendev = xendev;
+ dataplane->file_blk = conf->logical_block_size;
+ dataplane->blk = conf->blk;
+ dataplane->file_size = blk_getlength(dataplane->blk);
+
+ QLIST_INIT(&dataplane->inflight);
+ QLIST_INIT(&dataplane->freelist);
+
+ if (iothread) {
+ dataplane->iothread = iothread;
+ object_ref(OBJECT(dataplane->iothread));
+ dataplane->ctx = iothread_get_aio_context(dataplane->iothread);
+ } else {
+ dataplane->ctx = qemu_get_aio_context();
+ }
+ dataplane->bh = aio_bh_new(dataplane->ctx, xen_block_dataplane_bh,
+ dataplane);
+
+ return dataplane;
+}
+
+void xen_block_dataplane_destroy(XenBlockDataPlane *dataplane)
+{
+ XenBlockRequest *request;
+
+ if (!dataplane) {
+ return;
+ }
+
+ while (!QLIST_EMPTY(&dataplane->freelist)) {
+ request = QLIST_FIRST(&dataplane->freelist);
+ QLIST_REMOVE(request, list);
+ qemu_iovec_destroy(&request->v);
+ qemu_vfree(request->buf);
+ g_free(request);
+ }
+
+ qemu_bh_delete(dataplane->bh);
+ if (dataplane->iothread) {
+ object_unref(OBJECT(dataplane->iothread));
+ }
+
+ g_free(dataplane);
+}
+
+void xen_block_dataplane_stop(XenBlockDataPlane *dataplane)
+{
+ XenDevice *xendev;
+
+ if (!dataplane) {
+ return;
+ }
+
+ aio_context_acquire(dataplane->ctx);
+ blk_set_aio_context(dataplane->blk, qemu_get_aio_context());
+ aio_context_release(dataplane->ctx);
+
+ xendev = dataplane->xendev;
+
+ if (dataplane->event_channel) {
+ Error *local_err = NULL;
+
+ xen_device_unbind_event_channel(xendev, dataplane->event_channel,
+ &local_err);
+ dataplane->event_channel = NULL;
+
+ if (local_err) {
+ error_report_err(local_err);
+ }
+ }
+
+ if (dataplane->sring) {
+ Error *local_err = NULL;
+
+ xen_device_unmap_grant_refs(xendev, dataplane->sring,
+ dataplane->nr_ring_ref, &local_err);
+ dataplane->sring = NULL;
+
+ if (local_err) {
+ error_report_err(local_err);
+ }
+ }
+
+ g_free(dataplane->ring_ref);
+ dataplane->ring_ref = NULL;
+}
+
+void xen_block_dataplane_start(XenBlockDataPlane *dataplane,
+ const unsigned int ring_ref[],
+ unsigned int nr_ring_ref,
+ unsigned int event_channel,
+ unsigned int protocol,
+ Error **errp)
+{
+ XenDevice *xendev = dataplane->xendev;
+ Error *local_err = NULL;
+ unsigned int ring_size;
+ unsigned int i;
+
+ dataplane->nr_ring_ref = nr_ring_ref;
+ dataplane->ring_ref = g_new(unsigned int, nr_ring_ref);
+
+ for (i = 0; i < nr_ring_ref; i++) {
+ dataplane->ring_ref[i] = ring_ref[i];
+ }
+
+ dataplane->protocol = protocol;
+
+ ring_size = XC_PAGE_SIZE * dataplane->nr_ring_ref;
+ switch (dataplane->protocol) {
+ case BLKIF_PROTOCOL_NATIVE:
+ {
+ dataplane->max_requests = __CONST_RING_SIZE(blkif, ring_size);
+ break;
+ }
+ case BLKIF_PROTOCOL_X86_32:
+ {
+ dataplane->max_requests = __CONST_RING_SIZE(blkif_x86_32, ring_size);
+ break;
+ }
+ case BLKIF_PROTOCOL_X86_64:
+ {
+ dataplane->max_requests = __CONST_RING_SIZE(blkif_x86_64, ring_size);
+ break;
+ }
+ default:
+ error_setg(errp, "unknown protocol %u", dataplane->protocol);
+ return;
+ }
+
+ xen_device_set_max_grant_refs(xendev, dataplane->nr_ring_ref,
+ &local_err);
+ if (local_err) {
+ error_propagate(errp, local_err);
+ goto stop;
+ }
+
+ dataplane->sring = xen_device_map_grant_refs(xendev,
+ dataplane->ring_ref,
+ dataplane->nr_ring_ref,
+ PROT_READ | PROT_WRITE,
+ &local_err);
+ if (local_err) {
+ error_propagate(errp, local_err);
+ goto stop;
+ }
+
+ switch (dataplane->protocol) {
+ case BLKIF_PROTOCOL_NATIVE:
+ {
+ blkif_sring_t *sring_native = dataplane->sring;
+
+ BACK_RING_INIT(&dataplane->rings.native, sring_native, ring_size);
+ break;
+ }
+ case BLKIF_PROTOCOL_X86_32:
+ {
+ blkif_x86_32_sring_t *sring_x86_32 = dataplane->sring;
+
+ BACK_RING_INIT(&dataplane->rings.x86_32_part, sring_x86_32,
+ ring_size);
+ break;
+ }
+ case BLKIF_PROTOCOL_X86_64:
+ {
+ blkif_x86_64_sring_t *sring_x86_64 = dataplane->sring;
+
+ BACK_RING_INIT(&dataplane->rings.x86_64_part, sring_x86_64,
+ ring_size);
+ break;
+ }
+ }
+
+ dataplane->event_channel =
+ xen_device_bind_event_channel(xendev, event_channel,
+ xen_block_dataplane_event, dataplane,
+ &local_err);
+ if (local_err) {
+ error_propagate(errp, local_err);
+ goto stop;
+ }
+
+ aio_context_acquire(dataplane->ctx);
+ blk_set_aio_context(dataplane->blk, dataplane->ctx);
+ aio_context_release(dataplane->ctx);
+ return;
+
+stop:
+ xen_block_dataplane_stop(dataplane);
+}
diff --git a/hw/block/dataplane/xen-block.h b/hw/block/dataplane/xen-block.h
new file mode 100644
index 0000000..d6fa6d2
--- /dev/null
+++ b/hw/block/dataplane/xen-block.h
@@ -0,0 +1,29 @@
+/*
+ * Copyright (c) 2018 Citrix Systems Inc.
+ *
+ * This work is licensed under the terms of the GNU GPL, version 2 or later.
+ * See the COPYING file in the top-level directory.
+ */
+
+#ifndef HW_BLOCK_DATAPLANE_XEN_BLOCK_H
+#define HW_BLOCK_DATAPLANE_XEN_BLOCK_H
+
+#include "hw/block/block.h"
+#include "hw/xen/xen-bus.h"
+#include "sysemu/iothread.h"
+
+typedef struct XenBlockDataPlane XenBlockDataPlane;
+
+XenBlockDataPlane *xen_block_dataplane_create(XenDevice *xendev,
+ BlockConf *conf,
+ IOThread *iothread);
+void xen_block_dataplane_destroy(XenBlockDataPlane *dataplane);
+void xen_block_dataplane_start(XenBlockDataPlane *dataplane,
+ const unsigned int ring_ref[],
+ unsigned int nr_ring_ref,
+ unsigned int event_channel,
+ unsigned int protocol,
+ Error **errp);
+void xen_block_dataplane_stop(XenBlockDataPlane *dataplane);
+
+#endif /* HW_BLOCK_DATAPLANE_XEN_BLOCK_H */
diff --git a/hw/block/trace-events b/hw/block/trace-events
index 335c092..55e5a55 100644
--- a/hw/block/trace-events
+++ b/hw/block/trace-events
@@ -127,3 +127,17 @@ xen_disk_init(char *name) "%s"
xen_disk_connect(char *name) "%s"
xen_disk_disconnect(char *name) "%s"
xen_disk_free(char *name) "%s"
+
+# hw/block/xen-block.c
+xen_block_realize(const char *type, uint32_t disk, uint32_t partition) "%s d%up%u"
+xen_block_connect(const char *type, uint32_t disk, uint32_t partition) "%s d%up%u"
+xen_block_disconnect(const char *type, uint32_t disk, uint32_t partition) "%s d%up%u"
+xen_block_unrealize(const char *type, uint32_t disk, uint32_t partition) "%s d%up%u"
+xen_disk_realize(void) ""
+xen_disk_unrealize(void) ""
+xen_cdrom_realize(void) ""
+xen_cdrom_unrealize(void) ""
+xen_block_blockdev_add(char *str) "%s"
+xen_block_blockdev_del(const char *node_name) "%s"
+xen_block_device_create(unsigned int number) "%u"
+xen_block_device_destroy(unsigned int number) "%u"
diff --git a/hw/block/xen-block.c b/hw/block/xen-block.c
new file mode 100644
index 0000000..be28b63
--- /dev/null
+++ b/hw/block/xen-block.c
@@ -0,0 +1,963 @@
+/*
+ * Copyright (c) 2018 Citrix Systems Inc.
+ *
+ * This work is licensed under the terms of the GNU GPL, version 2 or later.
+ * See the COPYING file in the top-level directory.
+ */
+
+#include "qemu/osdep.h"
+#include "qemu/cutils.h"
+#include "qemu/option.h"
+#include "qapi/error.h"
+#include "qapi/qapi-commands-block-core.h"
+#include "qapi/qapi-commands-misc.h"
+#include "qapi/qapi-visit-block-core.h"
+#include "qapi/qobject-input-visitor.h"
+#include "qapi/visitor.h"
+#include "qapi/qmp/qdict.h"
+#include "qapi/qmp/qstring.h"
+#include "hw/hw.h"
+#include "hw/xen/xen_common.h"
+#include "hw/block/xen_blkif.h"
+#include "hw/xen/xen-block.h"
+#include "hw/xen/xen-backend.h"
+#include "sysemu/blockdev.h"
+#include "sysemu/block-backend.h"
+#include "sysemu/iothread.h"
+#include "dataplane/xen-block.h"
+#include "trace.h"
+
+static char *xen_block_get_name(XenDevice *xendev, Error **errp)
+{
+ XenBlockDevice *blockdev = XEN_BLOCK_DEVICE(xendev);
+ XenBlockVdev *vdev = &blockdev->props.vdev;
+
+ return g_strdup_printf("%lu", vdev->number);
+}
+
+static void xen_block_disconnect(XenDevice *xendev, Error **errp)
+{
+ XenBlockDevice *blockdev = XEN_BLOCK_DEVICE(xendev);
+ const char *type = object_get_typename(OBJECT(blockdev));
+ XenBlockVdev *vdev = &blockdev->props.vdev;
+
+ trace_xen_block_disconnect(type, vdev->disk, vdev->partition);
+
+ xen_block_dataplane_stop(blockdev->dataplane);
+}
+
+static void xen_block_connect(XenDevice *xendev, Error **errp)
+{
+ XenBlockDevice *blockdev = XEN_BLOCK_DEVICE(xendev);
+ const char *type = object_get_typename(OBJECT(blockdev));
+ XenBlockVdev *vdev = &blockdev->props.vdev;
+ unsigned int order, nr_ring_ref, *ring_ref, event_channel, protocol;
+ char *str;
+
+ trace_xen_block_connect(type, vdev->disk, vdev->partition);
+
+ if (xen_device_frontend_scanf(xendev, "ring-page-order", "%u",
+ &order) != 1) {
+ nr_ring_ref = 1;
+ ring_ref = g_new(unsigned int, nr_ring_ref);
+
+ if (xen_device_frontend_scanf(xendev, "ring-ref", "%u",
+ &ring_ref[0]) != 1) {
+ error_setg(errp, "failed to read ring-ref");
+ g_free(ring_ref);
+ return;
+ }
+ } else if (order <= blockdev->props.max_ring_page_order) {
+ unsigned int i;
+
+ nr_ring_ref = 1 << order;
+ ring_ref = g_new(unsigned int, nr_ring_ref);
+
+ for (i = 0; i < nr_ring_ref; i++) {
+ const char *key = g_strdup_printf("ring-ref%u", i);
+
+ if (xen_device_frontend_scanf(xendev, key, "%u",
+ &ring_ref[i]) != 1) {
+ error_setg(errp, "failed to read %s", key);
+ g_free((gpointer)key);
+ g_free(ring_ref);
+ return;
+ }
+
+ g_free((gpointer)key);
+ }
+ } else {
+ error_setg(errp, "invalid ring-page-order (%d)", order);
+ return;
+ }
+
+ if (xen_device_frontend_scanf(xendev, "event-channel", "%u",
+ &event_channel) != 1) {
+ error_setg(errp, "failed to read event-channel");
+ g_free(ring_ref);
+ return;
+ }
+
+ if (xen_device_frontend_scanf(xendev, "protocol", "%ms",
+ &str) != 1) {
+ protocol = BLKIF_PROTOCOL_NATIVE;
+ } else {
+ if (strcmp(str, XEN_IO_PROTO_ABI_X86_32) == 0) {
+ protocol = BLKIF_PROTOCOL_X86_32;
+ } else if (strcmp(str, XEN_IO_PROTO_ABI_X86_64) == 0) {
+ protocol = BLKIF_PROTOCOL_X86_64;
+ } else {
+ protocol = BLKIF_PROTOCOL_NATIVE;
+ }
+
+ free(str);
+ }
+
+ xen_block_dataplane_start(blockdev->dataplane, ring_ref, nr_ring_ref,
+ event_channel, protocol, errp);
+
+ g_free(ring_ref);
+}
+
+static void xen_block_unrealize(XenDevice *xendev, Error **errp)
+{
+ XenBlockDevice *blockdev = XEN_BLOCK_DEVICE(xendev);
+ XenBlockDeviceClass *blockdev_class =
+ XEN_BLOCK_DEVICE_GET_CLASS(xendev);
+ const char *type = object_get_typename(OBJECT(blockdev));
+ XenBlockVdev *vdev = &blockdev->props.vdev;
+
+ if (vdev->type == XEN_BLOCK_VDEV_TYPE_INVALID) {
+ return;
+ }
+
+ trace_xen_block_unrealize(type, vdev->disk, vdev->partition);
+
+ /* Disconnect from the frontend in case this has not already happened */
+ xen_block_disconnect(xendev, NULL);
+
+ xen_block_dataplane_destroy(blockdev->dataplane);
+ blockdev->dataplane = NULL;
+
+ if (blockdev_class->unrealize) {
+ blockdev_class->unrealize(blockdev, errp);
+ }
+}
+
+static void xen_block_realize(XenDevice *xendev, Error **errp)
+{
+ XenBlockDevice *blockdev = XEN_BLOCK_DEVICE(xendev);
+ XenBlockDeviceClass *blockdev_class =
+ XEN_BLOCK_DEVICE_GET_CLASS(xendev);
+ const char *type = object_get_typename(OBJECT(blockdev));
+ XenBlockVdev *vdev = &blockdev->props.vdev;
+ BlockConf *conf = &blockdev->props.conf;
+ Error *local_err = NULL;
+
+ if (vdev->type == XEN_BLOCK_VDEV_TYPE_INVALID) {
+ error_setg(errp, "vdev property not set");
+ return;
+ }
+
+ trace_xen_block_realize(type, vdev->disk, vdev->partition);
+
+ if (blockdev_class->realize) {
+ blockdev_class->realize(blockdev, &local_err);
+ if (local_err) {
+ error_propagate(errp, local_err);
+ return;
+ }
+ }
+
+ /*
+ * The blkif protocol does not deal with removable media, so it must
+ * always be present, even for CDRom devices.
+ */
+ assert(conf->blk);
+ if (!blk_is_inserted(conf->blk)) {
+ error_setg(errp, "device needs media, but drive is empty");
+ return;
+ }
+
+ if (!blkconf_apply_backend_options(conf, blockdev->info & VDISK_READONLY,
+ false, errp)) {
+ return;
+ }
+
+ if (!(blockdev->info & VDISK_CDROM) &&
+ !blkconf_geometry(conf, NULL, 65535, 255, 255, errp)) {
+ return;
+ }
+
+ blkconf_blocksizes(conf);
+
+ if (conf->logical_block_size > conf->physical_block_size) {
+ error_setg(
+ errp, "logical_block_size > physical_block_size not supported");
+ return;
+ }
+
+ blk_set_guest_block_size(conf->blk, conf->logical_block_size);
+
+ if (conf->discard_granularity > 0) {
+ xen_device_backend_printf(xendev, "feature-discard", "%u", 1);
+ }
+
+ xen_device_backend_printf(xendev, "feature-flush-cache", "%u", 1);
+ xen_device_backend_printf(xendev, "max-ring-page-order", "%u",
+ blockdev->props.max_ring_page_order);
+ xen_device_backend_printf(xendev, "info", "%u", blockdev->info);
+
+ xen_device_frontend_printf(xendev, "virtual-device", "%lu",
+ vdev->number);
+ xen_device_frontend_printf(xendev, "device-type", "%s",
+ blockdev->device_type);
+
+ xen_device_backend_printf(xendev, "sector-size", "%u",
+ conf->logical_block_size);
+ xen_device_backend_printf(xendev, "sectors", "%lu",
+ blk_getlength(conf->blk) /
+ conf->logical_block_size);
+
+ blockdev->dataplane =
+ xen_block_dataplane_create(xendev, conf, blockdev->props.iothread);
+}
+
+static void xen_block_frontend_changed(XenDevice *xendev,
+ enum xenbus_state frontend_state,
+ Error **errp)
+{
+ enum xenbus_state backend_state = xen_device_backend_get_state(xendev);
+ Error *local_err = NULL;
+
+ switch (frontend_state) {
+ case XenbusStateInitialised:
+ case XenbusStateConnected:
+ if (backend_state == XenbusStateConnected) {
+ break;
+ }
+
+ xen_block_disconnect(xendev, &local_err);
+ if (local_err) {
+ error_propagate(errp, local_err);
+ break;
+ }
+
+ xen_block_connect(xendev, &local_err);
+ if (local_err) {
+ error_propagate(errp, local_err);
+ break;
+ }
+
+ xen_device_backend_set_state(xendev, XenbusStateConnected);
+ break;
+
+ case XenbusStateClosing:
+ xen_device_backend_set_state(xendev, XenbusStateClosing);
+ break;
+
+ case XenbusStateClosed:
+ xen_block_disconnect(xendev, &local_err);
+ if (local_err) {
+ error_propagate(errp, local_err);
+ break;
+ }
+
+ xen_device_backend_set_state(xendev, XenbusStateClosed);
+ break;
+
+ default:
+ break;
+ }
+}
+
+static char *disk_to_vbd_name(unsigned int disk)
+{
+ char *name, *prefix = (disk >= 26) ?
+ disk_to_vbd_name((disk / 26) - 1) : g_strdup("");
+
+ name = g_strdup_printf("%s%c", prefix, 'a' + disk % 26);
+ g_free(prefix);
+
+ return name;
+}
+
+static void xen_block_get_vdev(Object *obj, Visitor *v, const char *name,
+ void *opaque, Error **errp)
+{
+ DeviceState *dev = DEVICE(obj);
+ Property *prop = opaque;
+ XenBlockVdev *vdev = qdev_get_prop_ptr(dev, prop);
+ char *str;
+
+ switch (vdev->type) {
+ case XEN_BLOCK_VDEV_TYPE_DP:
+ str = g_strdup_printf("d%lup%lu", vdev->disk, vdev->partition);
+ break;
+
+ case XEN_BLOCK_VDEV_TYPE_XVD:
+ case XEN_BLOCK_VDEV_TYPE_HD:
+ case XEN_BLOCK_VDEV_TYPE_SD: {
+ char *name = disk_to_vbd_name(vdev->disk);
+
+ str = g_strdup_printf("%s%s%lu",
+ (vdev->type == XEN_BLOCK_VDEV_TYPE_XVD) ?
+ "xvd" :
+ (vdev->type == XEN_BLOCK_VDEV_TYPE_HD) ?
+ "hd" :
+ "sd",
+ name, vdev->partition);
+ g_free(name);
+ break;
+ }
+ default:
+ error_setg(errp, "invalid vdev type");
+ return;
+ }
+
+ visit_type_str(v, name, &str, errp);
+ g_free(str);
+}
+
+static unsigned int vbd_name_to_disk(const char *name, const char **endp)
+{
+ unsigned int disk = 0;
+
+ while (*name != '\0') {
+ if (!g_ascii_isalpha(*name) || !g_ascii_islower(*name)) {
+ break;
+ }
+
+ disk *= 26;
+ disk += *name++ - 'a' + 1;
+ }
+ *endp = name;
+
+ return disk - 1;
+}
+
+static void xen_block_set_vdev(Object *obj, Visitor *v, const char *name,
+ void *opaque, Error **errp)
+{
+ DeviceState *dev = DEVICE(obj);
+ Property *prop = opaque;
+ XenBlockVdev *vdev = qdev_get_prop_ptr(dev, prop);
+ Error *local_err = NULL;
+ char *str, *p;
+ const char *end;
+
+ if (dev->realized) {
+ qdev_prop_set_after_realize(dev, name, errp);
+ return;
+ }
+
+ visit_type_str(v, name, &str, &local_err);
+ if (local_err) {
+ error_propagate(errp, local_err);
+ return;
+ }
+
+ p = strchr(str, 'd');
+ if (!p) {
+ goto invalid;
+ }
+
+ *p++ = '\0';
+ if (*str == '\0') {
+ vdev->type = XEN_BLOCK_VDEV_TYPE_DP;
+ } else if (strcmp(str, "xv") == 0) {
+ vdev->type = XEN_BLOCK_VDEV_TYPE_XVD;
+ } else if (strcmp(str, "h") == 0) {
+ vdev->type = XEN_BLOCK_VDEV_TYPE_HD;
+ } else if (strcmp(str, "s") == 0) {
+ vdev->type = XEN_BLOCK_VDEV_TYPE_SD;
+ } else {
+ goto invalid;
+ }
+
+ if (vdev->type == XEN_BLOCK_VDEV_TYPE_DP) {
+ if (qemu_strtoul(p, &end, 10, &vdev->disk)) {
+ goto invalid;
+ }
+
+ if (*end == 'p') {
+ p = (char *) ++end;
+ if (*end == '\0') {
+ goto invalid;
+ }
+ }
+ } else {
+ vdev->disk = vbd_name_to_disk(p, &end);
+ }
+
+ if (*end != '\0') {
+ p = (char *)end;
+
+ if (qemu_strtoul(p, &end, 10, &vdev->partition)) {
+ goto invalid;
+ }
+
+ if (*end != '\0') {
+ goto invalid;
+ }
+ } else {
+ vdev->partition = 0;
+ }
+
+ switch (vdev->type) {
+ case XEN_BLOCK_VDEV_TYPE_DP:
+ case XEN_BLOCK_VDEV_TYPE_XVD:
+ if (vdev->disk < (1 << 4) && vdev->partition < (1 << 4)) {
+ vdev->number = (202 << 8) | (vdev->disk << 4) |
+ vdev->partition;
+ } else if (vdev->disk < (1 << 20) && vdev->partition < (1 << 8)) {
+ vdev->number = (1 << 28) | (vdev->disk << 8) |
+ vdev->partition;
+ } else {
+ goto invalid;
+ }
+ break;
+
+ case XEN_BLOCK_VDEV_TYPE_HD:
+ if ((vdev->disk == 0 || vdev->disk == 1) &&
+ vdev->partition < (1 << 6)) {
+ vdev->number = (3 << 8) | (vdev->disk << 6) | vdev->partition;
+ } else if ((vdev->disk == 2 || vdev->disk == 3) &&
+ vdev->partition < (1 << 6)) {
+ vdev->number = (22 << 8) | ((vdev->disk - 2) << 6) |
+ vdev->partition;
+ } else {
+ goto invalid;
+ }
+ break;
+
+ case XEN_BLOCK_VDEV_TYPE_SD:
+ if (vdev->disk < (1 << 4) && vdev->partition < (1 << 4)) {
+ vdev->number = (8 << 8) | (vdev->disk << 4) | vdev->partition;
+ } else {
+ goto invalid;
+ }
+ break;
+
+ default:
+ goto invalid;
+ }
+
+ g_free(str);
+ return;
+
+invalid:
+ error_setg(errp, "invalid virtual disk specifier");
+
+ vdev->type = XEN_BLOCK_VDEV_TYPE_INVALID;
+ g_free(str);
+}
+
+/*
+ * This property deals with 'vdev' names adhering to the Xen VBD naming
+ * scheme described in:
+ *
+ * https://xenbits.xen.org/docs/unstable/man/xen-vbd-interface.7.html
+ */
+const PropertyInfo xen_block_prop_vdev = {
+ .name = "str",
+ .description = "Virtual Disk specifier: d*p*/xvd*/hd*/sd*",
+ .get = xen_block_get_vdev,
+ .set = xen_block_set_vdev,
+};
+
+static Property xen_block_props[] = {
+ DEFINE_PROP("vdev", XenBlockDevice, props.vdev,
+ xen_block_prop_vdev, XenBlockVdev),
+ DEFINE_BLOCK_PROPERTIES(XenBlockDevice, props.conf),
+ DEFINE_PROP_UINT32("max-ring-page-order", XenBlockDevice,
+ props.max_ring_page_order, 4),
+ DEFINE_PROP_LINK("iothread", XenBlockDevice, props.iothread,
+ TYPE_IOTHREAD, IOThread *),
+ DEFINE_PROP_END_OF_LIST()
+};
+
+static void xen_block_class_init(ObjectClass *class, void *data)
+{
+ DeviceClass *dev_class = DEVICE_CLASS(class);
+ XenDeviceClass *xendev_class = XEN_DEVICE_CLASS(class);
+
+ xendev_class->backend = "qdisk";
+ xendev_class->device = "vbd";
+ xendev_class->get_name = xen_block_get_name;
+ xendev_class->realize = xen_block_realize;
+ xendev_class->frontend_changed = xen_block_frontend_changed;
+ xendev_class->unrealize = xen_block_unrealize;
+
+ dev_class->props = xen_block_props;
+}
+
+static const TypeInfo xen_block_type_info = {
+ .name = TYPE_XEN_BLOCK_DEVICE,
+ .parent = TYPE_XEN_DEVICE,
+ .instance_size = sizeof(XenBlockDevice),
+ .abstract = true,
+ .class_size = sizeof(XenBlockDeviceClass),
+ .class_init = xen_block_class_init,
+};
+
+static void xen_disk_unrealize(XenBlockDevice *blockdev, Error **errp)
+{
+ trace_xen_disk_unrealize();
+}
+
+static void xen_disk_realize(XenBlockDevice *blockdev, Error **errp)
+{
+ BlockConf *conf = &blockdev->props.conf;
+
+ trace_xen_disk_realize();
+
+ blockdev->device_type = "disk";
+
+ if (!conf->blk) {
+ error_setg(errp, "drive property not set");
+ return;
+ }
+
+ blockdev->info = blk_is_read_only(conf->blk) ? VDISK_READONLY : 0;
+}
+
+static void xen_disk_class_init(ObjectClass *class, void *data)
+{
+ DeviceClass *dev_class = DEVICE_CLASS(class);
+ XenBlockDeviceClass *blockdev_class = XEN_BLOCK_DEVICE_CLASS(class);
+
+ blockdev_class->realize = xen_disk_realize;
+ blockdev_class->unrealize = xen_disk_unrealize;
+
+ dev_class->desc = "Xen Disk Device";
+}
+
+static const TypeInfo xen_disk_type_info = {
+ .name = TYPE_XEN_DISK_DEVICE,
+ .parent = TYPE_XEN_BLOCK_DEVICE,
+ .instance_size = sizeof(XenDiskDevice),
+ .class_init = xen_disk_class_init,
+};
+
+static void xen_cdrom_unrealize(XenBlockDevice *blockdev, Error **errp)
+{
+ trace_xen_cdrom_unrealize();
+}
+
+static void xen_cdrom_realize(XenBlockDevice *blockdev, Error **errp)
+{
+ BlockConf *conf = &blockdev->props.conf;
+
+ trace_xen_cdrom_realize();
+
+ blockdev->device_type = "cdrom";
+
+ if (!conf->blk) {
+ int rc;
+
+ /* Set up an empty drive */
+ conf->blk = blk_new(0, BLK_PERM_ALL);
+
+ rc = blk_attach_dev(conf->blk, DEVICE(blockdev));
+ if (!rc) {
+ error_setg_errno(errp, -rc, "failed to create drive");
+ return;
+ }
+ }
+
+ blockdev->info = VDISK_READONLY | VDISK_CDROM;
+}
+
+static void xen_cdrom_class_init(ObjectClass *class, void *data)
+{
+ DeviceClass *dev_class = DEVICE_CLASS(class);
+ XenBlockDeviceClass *blockdev_class = XEN_BLOCK_DEVICE_CLASS(class);
+
+ blockdev_class->realize = xen_cdrom_realize;
+ blockdev_class->unrealize = xen_cdrom_unrealize;
+
+ dev_class->desc = "Xen CD-ROM Device";
+}
+
+static const TypeInfo xen_cdrom_type_info = {
+ .name = TYPE_XEN_CDROM_DEVICE,
+ .parent = TYPE_XEN_BLOCK_DEVICE,
+ .instance_size = sizeof(XenCDRomDevice),
+ .class_init = xen_cdrom_class_init,
+};
+
+static void xen_block_register_types(void)
+{
+ type_register_static(&xen_block_type_info);
+ type_register_static(&xen_disk_type_info);
+ type_register_static(&xen_cdrom_type_info);
+}
+
+type_init(xen_block_register_types)
+
+static void xen_block_blockdev_del(const char *node_name, Error **errp)
+{
+ trace_xen_block_blockdev_del(node_name);
+
+ qmp_blockdev_del(node_name, errp);
+}
+
+static char *xen_block_blockdev_add(const char *id, QDict *qdict,
+ Error **errp)
+{
+ const char *driver = qdict_get_try_str(qdict, "driver");
+ BlockdevOptions *options = NULL;
+ Error *local_err = NULL;
+ char *node_name;
+ Visitor *v;
+
+ if (!driver) {
+ error_setg(errp, "no 'driver' parameter");
+ return NULL;
+ }
+
+ node_name = g_strdup_printf("%s-%s", id, driver);
+ qdict_put_str(qdict, "node-name", node_name);
+
+ trace_xen_block_blockdev_add(node_name);
+
+ v = qobject_input_visitor_new(QOBJECT(qdict));
+ visit_type_BlockdevOptions(v, NULL, &options, &local_err);
+ visit_free(v);
+
+ if (local_err) {
+ error_propagate(errp, local_err);
+ goto fail;
+ }
+
+ qmp_blockdev_add(options, &local_err);
+
+ if (local_err) {
+ error_propagate(errp, local_err);
+ goto fail;
+ }
+
+ qapi_free_BlockdevOptions(options);
+
+ return node_name;
+
+fail:
+ if (options) {
+ qapi_free_BlockdevOptions(options);
+ }
+ g_free(node_name);
+
+ return NULL;
+}
+
+static void xen_block_drive_destroy(XenBlockDrive *drive, Error **errp)
+{
+ char *node_name = drive->node_name;
+
+ if (node_name) {
+ Error *local_err = NULL;
+
+ xen_block_blockdev_del(node_name, &local_err);
+ if (local_err) {
+ error_propagate(errp, local_err);
+ return;
+ }
+ g_free(node_name);
+ drive->node_name = NULL;
+ }
+ g_free(drive->id);
+ g_free(drive);
+}
+
+static XenBlockDrive *xen_block_drive_create(const char *id,
+ const char *device_type,
+ QDict *opts, Error **errp)
+{
+ const char *params = qdict_get_try_str(opts, "params");
+ const char *mode = qdict_get_try_str(opts, "mode");
+ const char *direct_io_safe = qdict_get_try_str(opts, "direct-io-safe");
+ const char *discard_enable = qdict_get_try_str(opts, "discard-enable");
+ char *driver = NULL;
+ char *filename = NULL;
+ XenBlockDrive *drive = NULL;
+ Error *local_err = NULL;
+ QDict *file_layer;
+ QDict *driver_layer;
+
+ if (params) {
+ char **v = g_strsplit(params, ":", 2);
+
+ if (v[1] == NULL) {
+ filename = g_strdup(v[0]);
+ driver = g_strdup("raw");
+ } else {
+ if (strcmp(v[0], "aio") == 0) {
+ driver = g_strdup("raw");
+ } else if (strcmp(v[0], "vhd") == 0) {
+ driver = g_strdup("vpc");
+ } else {
+ driver = g_strdup(v[0]);
+ }
+ filename = g_strdup(v[1]);
+ }
+
+ g_strfreev(v);
+ }
+
+ if (!filename) {
+ error_setg(errp, "no filename");
+ goto done;
+ }
+ assert(driver);
+
+ drive = g_new0(XenBlockDrive, 1);
+ drive->id = g_strdup(id);
+
+ file_layer = qdict_new();
+
+ qdict_put_str(file_layer, "driver", "file");
+ qdict_put_str(file_layer, "filename", filename);
+
+ if (mode && *mode != 'w') {
+ qdict_put_bool(file_layer, "read-only", true);
+ }
+
+ if (direct_io_safe) {
+ unsigned long value;
+
+ if (!qemu_strtoul(direct_io_safe, NULL, 2, &value) && !!value) {
+ QDict *cache_qdict = qdict_new();
+
+ qdict_put_bool(cache_qdict, "direct", true);
+ qdict_put_obj(file_layer, "cache", QOBJECT(cache_qdict));
+
+ qdict_put_str(file_layer, "aio", "native");
+ }
+ }
+
+ if (discard_enable) {
+ unsigned long value;
+
+ if (!qemu_strtoul(discard_enable, NULL, 2, &value) && !!value) {
+ qdict_put_str(file_layer, "discard", "unmap");
+ }
+ }
+
+ /*
+ * It is necessary to turn file locking off as an emulated device
+ * may have already opened the same image file.
+ */
+ qdict_put_str(file_layer, "locking", "off");
+
+ driver_layer = qdict_new();
+
+ qdict_put_str(driver_layer, "driver", driver);
+ qdict_put_obj(driver_layer, "file", QOBJECT(file_layer));
+
+ g_assert(!drive->node_name);
+ drive->node_name = xen_block_blockdev_add(drive->id, driver_layer,
+ &local_err);
+
+done:
+ g_free(driver);
+ g_free(filename);
+
+ if (local_err) {
+ error_propagate(errp, local_err);
+ xen_block_drive_destroy(drive, NULL);
+ return NULL;
+ }
+
+ return drive;
+}
+
+static const char *xen_block_drive_get_node_name(XenBlockDrive *drive)
+{
+ return drive->node_name ? drive->node_name : "";
+}
+
+static void xen_block_iothread_destroy(XenBlockIOThread *iothread,
+ Error **errp)
+{
+ qmp_object_del(iothread->id, errp);
+
+ g_free(iothread->id);
+ g_free(iothread);
+}
+
+static XenBlockIOThread *xen_block_iothread_create(const char *id,
+ Error **errp)
+{
+ XenBlockIOThread *iothread = g_new(XenBlockIOThread, 1);
+ Error *local_err = NULL;
+
+ iothread->id = g_strdup(id);
+
+ qmp_object_add(TYPE_IOTHREAD, id, false, NULL, &local_err);
+ if (local_err) {
+ error_propagate(errp, local_err);
+
+ g_free(iothread->id);
+ g_free(iothread);
+ return NULL;
+ }
+
+ return iothread;
+}
+
+static void xen_block_device_create(XenBackendInstance *backend,
+ QDict *opts, Error **errp)
+{
+ XenBus *xenbus = xen_backend_get_bus(backend);
+ const char *name = xen_backend_get_name(backend);
+ unsigned long number;
+ const char *vdev, *device_type;
+ XenBlockDrive *drive = NULL;
+ XenBlockIOThread *iothread = NULL;
+ XenDevice *xendev = NULL;
+ Error *local_err = NULL;
+ const char *type;
+ XenBlockDevice *blockdev;
+
+ if (qemu_strtoul(name, NULL, 10, &number)) {
+ error_setg(errp, "failed to parse name '%s'", name);
+ goto fail;
+ }
+
+ trace_xen_block_device_create(number);
+
+ vdev = qdict_get_try_str(opts, "dev");
+ if (!vdev) {
+ error_setg(errp, "no dev parameter");
+ goto fail;
+ }
+
+ device_type = qdict_get_try_str(opts, "device-type");
+ if (!device_type) {
+ error_setg(errp, "no device-type parameter");
+ goto fail;
+ }
+
+ if (!strcmp(device_type, "disk")) {
+ type = TYPE_XEN_DISK_DEVICE;
+ } else if (!strcmp(device_type, "cdrom")) {
+ type = TYPE_XEN_CDROM_DEVICE;
+ } else {
+ error_setg(errp, "invalid device-type parameter '%s'", device_type);
+ goto fail;
+ }
+
+ drive = xen_block_drive_create(vdev, device_type, opts, &local_err);
+ if (!drive) {
+ error_propagate_prepend(errp, local_err, "failed to create drive: ");
+ goto fail;
+ }
+
+ iothread = xen_block_iothread_create(vdev, &local_err);
+ if (local_err) {
+ error_propagate_prepend(errp, local_err,
+ "failed to create iothread: ");
+ goto fail;
+ }
+
+ xendev = XEN_DEVICE(qdev_create(BUS(xenbus), type));
+ blockdev = XEN_BLOCK_DEVICE(xendev);
+
+ object_property_set_str(OBJECT(xendev), vdev, "vdev", &local_err);
+ if (local_err) {
+ error_propagate_prepend(errp, local_err, "failed to set 'vdev': ");
+ goto fail;
+ }
+
+ object_property_set_str(OBJECT(xendev),
+ xen_block_drive_get_node_name(drive), "drive",
+ &local_err);
+ if (local_err) {
+ error_propagate_prepend(errp, local_err, "failed to set 'drive': ");
+ goto fail;
+ }
+
+ object_property_set_str(OBJECT(xendev), iothread->id, "iothread",
+ &local_err);
+ if (local_err) {
+ error_propagate_prepend(errp, local_err,
+ "failed to set 'iothread': ");
+ goto fail;
+ }
+
+ blockdev->iothread = iothread;
+ blockdev->drive = drive;
+
+ object_property_set_bool(OBJECT(xendev), true, "realized", &local_err);
+ if (local_err) {
+ error_propagate_prepend(errp, local_err,
+ "realization of device %s failed: ",
+ type);
+ goto fail;
+ }
+
+ xen_backend_set_device(backend, xendev);
+ return;
+
+fail:
+ if (xendev) {
+ object_unparent(OBJECT(xendev));
+ }
+
+ if (iothread) {
+ xen_block_iothread_destroy(iothread, NULL);
+ }
+
+ if (drive) {
+ xen_block_drive_destroy(drive, NULL);
+ }
+}
+
+static void xen_block_device_destroy(XenBackendInstance *backend,
+ Error **errp)
+{
+ XenDevice *xendev = xen_backend_get_device(backend);
+ XenBlockDevice *blockdev = XEN_BLOCK_DEVICE(xendev);
+ XenBlockVdev *vdev = &blockdev->props.vdev;
+ XenBlockDrive *drive = blockdev->drive;
+ XenBlockIOThread *iothread = blockdev->iothread;
+
+ trace_xen_block_device_destroy(vdev->number);
+
+ object_unparent(OBJECT(xendev));
+
+ if (iothread) {
+ Error *local_err = NULL;
+
+ xen_block_iothread_destroy(iothread, &local_err);
+ if (local_err) {
+ error_propagate_prepend(errp, local_err,
+ "failed to destroy iothread: ");
+ return;
+ }
+ }
+
+ if (drive) {
+ Error *local_err = NULL;
+
+ xen_block_drive_destroy(drive, &local_err);
+ if (local_err) {
+ error_propagate_prepend(errp, local_err,
+ "failed to destroy drive: ");
+ }
+ }
+}
+
+static const XenBackendInfo xen_block_backend_info = {
+ .type = "qdisk",
+ .create = xen_block_device_create,
+ .destroy = xen_block_device_destroy,
+};
+
+static void xen_block_register_backend(void)
+{
+ xen_backend_register(&xen_block_backend_info);
+}
+
+xen_backend_init(xen_block_register_backend);
diff --git a/hw/block/xen_disk.c b/hw/block/xen_disk.c
deleted file mode 100644
index 2a254b9..0000000
--- a/hw/block/xen_disk.c
+++ /dev/null
@@ -1,1011 +0,0 @@
-/*
- * xen paravirt block device backend
- *
- * (c) Gerd Hoffmann <kraxel@redhat.com>
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation; under version 2 of the License.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
- *
- * 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 "qemu/osdep.h"
-#include "qemu/units.h"
-#include <sys/ioctl.h>
-#include <sys/uio.h>
-
-#include "hw/hw.h"
-#include "hw/xen/xen_backend.h"
-#include "xen_blkif.h"
-#include "sysemu/blockdev.h"
-#include "sysemu/iothread.h"
-#include "sysemu/block-backend.h"
-#include "qapi/error.h"
-#include "qapi/qmp/qdict.h"
-#include "qapi/qmp/qstring.h"
-#include "trace.h"
-
-/* ------------------------------------------------------------- */
-
-#define BLOCK_SIZE 512
-#define IOCB_COUNT (BLKIF_MAX_SEGMENTS_PER_REQUEST + 2)
-
-struct ioreq {
- blkif_request_t req;
- int16_t status;
-
- /* parsed request */
- off_t start;
- QEMUIOVector v;
- void *buf;
- size_t size;
- int presync;
-
- /* aio status */
- int aio_inflight;
- int aio_errors;
-
- struct XenBlkDev *blkdev;
- QLIST_ENTRY(ioreq) list;
- BlockAcctCookie acct;
-};
-
-#define MAX_RING_PAGE_ORDER 4
-
-struct XenBlkDev {
- struct XenDevice xendev; /* must be first */
- char *params;
- char *mode;
- char *type;
- char *dev;
- char *devtype;
- bool directiosafe;
- const char *fileproto;
- const char *filename;
- unsigned int ring_ref[1 << MAX_RING_PAGE_ORDER];
- unsigned int nr_ring_ref;
- void *sring;
- int64_t file_blk;
- int64_t file_size;
- int protocol;
- blkif_back_rings_t rings;
- int more_work;
-
- /* request lists */
- QLIST_HEAD(, ioreq) inflight;
- QLIST_HEAD(, ioreq) finished;
- QLIST_HEAD(, ioreq) freelist;
- int requests_total;
- int requests_inflight;
- int requests_finished;
- unsigned int max_requests;
-
- gboolean feature_discard;
-
- /* qemu block driver */
- DriveInfo *dinfo;
- BlockBackend *blk;
- QEMUBH *bh;
-
- IOThread *iothread;
- AioContext *ctx;
-};
-
-/* ------------------------------------------------------------- */
-
-static void ioreq_reset(struct ioreq *ioreq)
-{
- memset(&ioreq->req, 0, sizeof(ioreq->req));
- ioreq->status = 0;
- ioreq->start = 0;
- ioreq->buf = NULL;
- ioreq->size = 0;
- ioreq->presync = 0;
-
- ioreq->aio_inflight = 0;
- ioreq->aio_errors = 0;
-
- ioreq->blkdev = NULL;
- memset(&ioreq->list, 0, sizeof(ioreq->list));
- memset(&ioreq->acct, 0, sizeof(ioreq->acct));
-
- qemu_iovec_reset(&ioreq->v);
-}
-
-static struct ioreq *ioreq_start(struct XenBlkDev *blkdev)
-{
- struct ioreq *ioreq = NULL;
-
- if (QLIST_EMPTY(&blkdev->freelist)) {
- if (blkdev->requests_total >= blkdev->max_requests) {
- goto out;
- }
- /* allocate new struct */
- ioreq = g_malloc0(sizeof(*ioreq));
- ioreq->blkdev = blkdev;
- blkdev->requests_total++;
- qemu_iovec_init(&ioreq->v, 1);
- } else {
- /* get one from freelist */
- ioreq = QLIST_FIRST(&blkdev->freelist);
- QLIST_REMOVE(ioreq, list);
- }
- QLIST_INSERT_HEAD(&blkdev->inflight, ioreq, list);
- blkdev->requests_inflight++;
-
-out:
- return ioreq;
-}
-
-static void ioreq_finish(struct ioreq *ioreq)
-{
- struct XenBlkDev *blkdev = ioreq->blkdev;
-
- QLIST_REMOVE(ioreq, list);
- QLIST_INSERT_HEAD(&blkdev->finished, ioreq, list);
- blkdev->requests_inflight--;
- blkdev->requests_finished++;
-}
-
-static void ioreq_release(struct ioreq *ioreq, bool finish)
-{
- struct XenBlkDev *blkdev = ioreq->blkdev;
-
- QLIST_REMOVE(ioreq, list);
- ioreq_reset(ioreq);
- ioreq->blkdev = blkdev;
- QLIST_INSERT_HEAD(&blkdev->freelist, ioreq, list);
- if (finish) {
- blkdev->requests_finished--;
- } else {
- blkdev->requests_inflight--;
- }
-}
-
-/*
- * translate request into iovec + start offset
- * do sanity checks along the way
- */
-static int ioreq_parse(struct ioreq *ioreq)
-{
- struct XenBlkDev *blkdev = ioreq->blkdev;
- struct XenDevice *xendev = &blkdev->xendev;
- size_t len;
- int i;
-
- xen_pv_printf(xendev, 3,
- "op %d, nr %d, handle %d, id %" PRId64 ", sector %" PRId64 "\n",
- ioreq->req.operation, ioreq->req.nr_segments,
- ioreq->req.handle, ioreq->req.id, ioreq->req.sector_number);
- switch (ioreq->req.operation) {
- case BLKIF_OP_READ:
- break;
- case BLKIF_OP_FLUSH_DISKCACHE:
- ioreq->presync = 1;
- if (!ioreq->req.nr_segments) {
- return 0;
- }
- /* fall through */
- case BLKIF_OP_WRITE:
- break;
- case BLKIF_OP_DISCARD:
- return 0;
- default:
- xen_pv_printf(xendev, 0, "error: unknown operation (%d)\n",
- ioreq->req.operation);
- goto err;
- };
-
- if (ioreq->req.operation != BLKIF_OP_READ && blkdev->mode[0] != 'w') {
- xen_pv_printf(xendev, 0, "error: write req for ro device\n");
- goto err;
- }
-
- ioreq->start = ioreq->req.sector_number * blkdev->file_blk;
- for (i = 0; i < ioreq->req.nr_segments; i++) {
- if (i == BLKIF_MAX_SEGMENTS_PER_REQUEST) {
- xen_pv_printf(xendev, 0, "error: nr_segments too big\n");
- goto err;
- }
- if (ioreq->req.seg[i].first_sect > ioreq->req.seg[i].last_sect) {
- xen_pv_printf(xendev, 0, "error: first > last sector\n");
- goto err;
- }
- if (ioreq->req.seg[i].last_sect * BLOCK_SIZE >= XC_PAGE_SIZE) {
- xen_pv_printf(xendev, 0, "error: page crossing\n");
- goto err;
- }
-
- len = (ioreq->req.seg[i].last_sect - ioreq->req.seg[i].first_sect + 1) * blkdev->file_blk;
- ioreq->size += len;
- }
- if (ioreq->start + ioreq->size > blkdev->file_size) {
- xen_pv_printf(xendev, 0, "error: access beyond end of file\n");
- goto err;
- }
- return 0;
-
-err:
- ioreq->status = BLKIF_RSP_ERROR;
- return -1;
-}
-
-static int ioreq_grant_copy(struct ioreq *ioreq)
-{
- struct XenBlkDev *blkdev = ioreq->blkdev;
- struct XenDevice *xendev = &blkdev->xendev;
- XenGrantCopySegment segs[BLKIF_MAX_SEGMENTS_PER_REQUEST];
- int i, count, rc;
- int64_t file_blk = blkdev->file_blk;
- bool to_domain = (ioreq->req.operation == BLKIF_OP_READ);
- void *virt = ioreq->buf;
-
- if (ioreq->req.nr_segments == 0) {
- return 0;
- }
-
- count = ioreq->req.nr_segments;
-
- for (i = 0; i < count; i++) {
- if (to_domain) {
- segs[i].dest.foreign.ref = ioreq->req.seg[i].gref;
- segs[i].dest.foreign.offset = ioreq->req.seg[i].first_sect * file_blk;
- segs[i].source.virt = virt;
- } else {
- segs[i].source.foreign.ref = ioreq->req.seg[i].gref;
- segs[i].source.foreign.offset = ioreq->req.seg[i].first_sect * file_blk;
- segs[i].dest.virt = virt;
- }
- segs[i].len = (ioreq->req.seg[i].last_sect
- - ioreq->req.seg[i].first_sect + 1) * file_blk;
- virt += segs[i].len;
- }
-
- rc = xen_be_copy_grant_refs(xendev, to_domain, segs, count);
-
- if (rc) {
- xen_pv_printf(xendev, 0,
- "failed to copy data %d\n", rc);
- ioreq->aio_errors++;
- return -1;
- }
-
- return rc;
-}
-
-static int ioreq_runio_qemu_aio(struct ioreq *ioreq);
-
-static void qemu_aio_complete(void *opaque, int ret)
-{
- struct ioreq *ioreq = opaque;
- struct XenBlkDev *blkdev = ioreq->blkdev;
- struct XenDevice *xendev = &blkdev->xendev;
-
- aio_context_acquire(blkdev->ctx);
-
- if (ret != 0) {
- xen_pv_printf(xendev, 0, "%s I/O error\n",
- ioreq->req.operation == BLKIF_OP_READ ? "read" : "write");
- ioreq->aio_errors++;
- }
-
- ioreq->aio_inflight--;
- if (ioreq->presync) {
- ioreq->presync = 0;
- ioreq_runio_qemu_aio(ioreq);
- goto done;
- }
- if (ioreq->aio_inflight > 0) {
- goto done;
- }
-
- switch (ioreq->req.operation) {
- case BLKIF_OP_READ:
- /* in case of failure ioreq->aio_errors is increased */
- if (ret == 0) {
- ioreq_grant_copy(ioreq);
- }
- qemu_vfree(ioreq->buf);
- break;
- case BLKIF_OP_WRITE:
- case BLKIF_OP_FLUSH_DISKCACHE:
- if (!ioreq->req.nr_segments) {
- break;
- }
- qemu_vfree(ioreq->buf);
- break;
- default:
- break;
- }
-
- ioreq->status = ioreq->aio_errors ? BLKIF_RSP_ERROR : BLKIF_RSP_OKAY;
- ioreq_finish(ioreq);
-
- switch (ioreq->req.operation) {
- case BLKIF_OP_WRITE:
- case BLKIF_OP_FLUSH_DISKCACHE:
- if (!ioreq->req.nr_segments) {
- break;
- }
- case BLKIF_OP_READ:
- if (ioreq->status == BLKIF_RSP_OKAY) {
- block_acct_done(blk_get_stats(blkdev->blk), &ioreq->acct);
- } else {
- block_acct_failed(blk_get_stats(blkdev->blk), &ioreq->acct);
- }
- break;
- case BLKIF_OP_DISCARD:
- default:
- break;
- }
- qemu_bh_schedule(blkdev->bh);
-
-done:
- aio_context_release(blkdev->ctx);
-}
-
-static bool blk_split_discard(struct ioreq *ioreq, blkif_sector_t sector_number,
- uint64_t nr_sectors)
-{
- struct XenBlkDev *blkdev = ioreq->blkdev;
- int64_t byte_offset;
- int byte_chunk;
- uint64_t byte_remaining, limit;
- uint64_t sec_start = sector_number;
- uint64_t sec_count = nr_sectors;
-
- /* Wrap around, or overflowing byte limit? */
- if (sec_start + sec_count < sec_count ||
- sec_start + sec_count > INT64_MAX >> BDRV_SECTOR_BITS) {
- return false;
- }
-
- limit = BDRV_REQUEST_MAX_SECTORS << BDRV_SECTOR_BITS;
- byte_offset = sec_start << BDRV_SECTOR_BITS;
- byte_remaining = sec_count << BDRV_SECTOR_BITS;
-
- do {
- byte_chunk = byte_remaining > limit ? limit : byte_remaining;
- ioreq->aio_inflight++;
- blk_aio_pdiscard(blkdev->blk, byte_offset, byte_chunk,
- qemu_aio_complete, ioreq);
- byte_remaining -= byte_chunk;
- byte_offset += byte_chunk;
- } while (byte_remaining > 0);
-
- return true;
-}
-
-static int ioreq_runio_qemu_aio(struct ioreq *ioreq)
-{
- struct XenBlkDev *blkdev = ioreq->blkdev;
-
- ioreq->buf = qemu_memalign(XC_PAGE_SIZE, ioreq->size);
- if (ioreq->req.nr_segments &&
- (ioreq->req.operation == BLKIF_OP_WRITE ||
- ioreq->req.operation == BLKIF_OP_FLUSH_DISKCACHE) &&
- ioreq_grant_copy(ioreq)) {
- qemu_vfree(ioreq->buf);
- goto err;
- }
-
- ioreq->aio_inflight++;
- if (ioreq->presync) {
- blk_aio_flush(ioreq->blkdev->blk, qemu_aio_complete, ioreq);
- return 0;
- }
-
- switch (ioreq->req.operation) {
- case BLKIF_OP_READ:
- qemu_iovec_add(&ioreq->v, ioreq->buf, ioreq->size);
- block_acct_start(blk_get_stats(blkdev->blk), &ioreq->acct,
- ioreq->v.size, BLOCK_ACCT_READ);
- ioreq->aio_inflight++;
- blk_aio_preadv(blkdev->blk, ioreq->start, &ioreq->v, 0,
- qemu_aio_complete, ioreq);
- break;
- case BLKIF_OP_WRITE:
- case BLKIF_OP_FLUSH_DISKCACHE:
- if (!ioreq->req.nr_segments) {
- break;
- }
-
- qemu_iovec_add(&ioreq->v, ioreq->buf, ioreq->size);
- 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++;
- blk_aio_pwritev(blkdev->blk, ioreq->start, &ioreq->v, 0,
- qemu_aio_complete, ioreq);
- break;
- case BLKIF_OP_DISCARD:
- {
- struct blkif_request_discard *req = (void *)&ioreq->req;
- if (!blk_split_discard(ioreq, req->sector_number, req->nr_sectors)) {
- goto err;
- }
- break;
- }
- default:
- /* unknown operation (shouldn't happen -- parse catches this) */
- goto err;
- }
-
- qemu_aio_complete(ioreq, 0);
-
- return 0;
-
-err:
- ioreq_finish(ioreq);
- ioreq->status = BLKIF_RSP_ERROR;
- return -1;
-}
-
-static int blk_send_response_one(struct ioreq *ioreq)
-{
- struct XenBlkDev *blkdev = ioreq->blkdev;
- int send_notify = 0;
- int have_requests = 0;
- blkif_response_t *resp;
-
- /* Place on the response ring for the relevant domain. */
- switch (blkdev->protocol) {
- case BLKIF_PROTOCOL_NATIVE:
- resp = (blkif_response_t *) RING_GET_RESPONSE(&blkdev->rings.native,
- blkdev->rings.native.rsp_prod_pvt);
- break;
- case BLKIF_PROTOCOL_X86_32:
- resp = (blkif_response_t *) RING_GET_RESPONSE(&blkdev->rings.x86_32_part,
- blkdev->rings.x86_32_part.rsp_prod_pvt);
- break;
- case BLKIF_PROTOCOL_X86_64:
- resp = (blkif_response_t *) RING_GET_RESPONSE(&blkdev->rings.x86_64_part,
- blkdev->rings.x86_64_part.rsp_prod_pvt);
- break;
- default:
- return 0;
- }
-
- resp->id = ioreq->req.id;
- resp->operation = ioreq->req.operation;
- resp->status = ioreq->status;
-
- blkdev->rings.common.rsp_prod_pvt++;
-
- RING_PUSH_RESPONSES_AND_CHECK_NOTIFY(&blkdev->rings.common, send_notify);
- if (blkdev->rings.common.rsp_prod_pvt == blkdev->rings.common.req_cons) {
- /*
- * Tail check for pending requests. Allows frontend to avoid
- * notifications if requests are already in flight (lower
- * overheads and promotes batching).
- */
- RING_FINAL_CHECK_FOR_REQUESTS(&blkdev->rings.common, have_requests);
- } else if (RING_HAS_UNCONSUMED_REQUESTS(&blkdev->rings.common)) {
- have_requests = 1;
- }
-
- if (have_requests) {
- blkdev->more_work++;
- }
- return send_notify;
-}
-
-/* walk finished list, send outstanding responses, free requests */
-static void blk_send_response_all(struct XenBlkDev *blkdev)
-{
- struct ioreq *ioreq;
- int send_notify = 0;
-
- while (!QLIST_EMPTY(&blkdev->finished)) {
- ioreq = QLIST_FIRST(&blkdev->finished);
- send_notify += blk_send_response_one(ioreq);
- ioreq_release(ioreq, true);
- }
- if (send_notify) {
- xen_pv_send_notify(&blkdev->xendev);
- }
-}
-
-static int blk_get_request(struct XenBlkDev *blkdev, struct ioreq *ioreq, RING_IDX rc)
-{
- switch (blkdev->protocol) {
- case BLKIF_PROTOCOL_NATIVE:
- memcpy(&ioreq->req, RING_GET_REQUEST(&blkdev->rings.native, rc),
- sizeof(ioreq->req));
- break;
- case BLKIF_PROTOCOL_X86_32:
- blkif_get_x86_32_req(&ioreq->req,
- RING_GET_REQUEST(&blkdev->rings.x86_32_part, rc));
- break;
- case BLKIF_PROTOCOL_X86_64:
- blkif_get_x86_64_req(&ioreq->req,
- RING_GET_REQUEST(&blkdev->rings.x86_64_part, rc));
- break;
- }
- /* Prevent the compiler from accessing the on-ring fields instead. */
- barrier();
- return 0;
-}
-
-static void blk_handle_requests(struct XenBlkDev *blkdev)
-{
- RING_IDX rc, rp;
- struct ioreq *ioreq;
-
- blkdev->more_work = 0;
-
- rc = blkdev->rings.common.req_cons;
- rp = blkdev->rings.common.sring->req_prod;
- xen_rmb(); /* Ensure we see queued requests up to 'rp'. */
-
- blk_send_response_all(blkdev);
- while (rc != rp) {
- /* pull request from ring */
- if (RING_REQUEST_CONS_OVERFLOW(&blkdev->rings.common, rc)) {
- break;
- }
- ioreq = ioreq_start(blkdev);
- if (ioreq == NULL) {
- blkdev->more_work++;
- break;
- }
- blk_get_request(blkdev, ioreq, rc);
- blkdev->rings.common.req_cons = ++rc;
-
- /* 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_pv_send_notify(&blkdev->xendev);
- }
- ioreq_release(ioreq, false);
- continue;
- }
-
- ioreq_runio_qemu_aio(ioreq);
- }
-
- if (blkdev->more_work && blkdev->requests_inflight < blkdev->max_requests) {
- qemu_bh_schedule(blkdev->bh);
- }
-}
-
-/* ------------------------------------------------------------- */
-
-static void blk_bh(void *opaque)
-{
- struct XenBlkDev *blkdev = opaque;
-
- aio_context_acquire(blkdev->ctx);
- blk_handle_requests(blkdev);
- aio_context_release(blkdev->ctx);
-}
-
-static void blk_alloc(struct XenDevice *xendev)
-{
- struct XenBlkDev *blkdev = container_of(xendev, struct XenBlkDev, xendev);
- Error *err = NULL;
-
- trace_xen_disk_alloc(xendev->name);
-
- QLIST_INIT(&blkdev->inflight);
- QLIST_INIT(&blkdev->finished);
- QLIST_INIT(&blkdev->freelist);
-
- blkdev->iothread = iothread_create(xendev->name, &err);
- assert(!err);
-
- blkdev->ctx = iothread_get_aio_context(blkdev->iothread);
- blkdev->bh = aio_bh_new(blkdev->ctx, blk_bh, blkdev);
-}
-
-static void blk_parse_discard(struct XenBlkDev *blkdev)
-{
- struct XenDevice *xendev = &blkdev->xendev;
- int enable;
-
- blkdev->feature_discard = true;
-
- if (xenstore_read_be_int(xendev, "discard-enable", &enable) == 0) {
- blkdev->feature_discard = !!enable;
- }
-
- if (blkdev->feature_discard) {
- xenstore_write_be_int(xendev, "feature-discard", 1);
- }
-}
-
-static int blk_init(struct XenDevice *xendev)
-{
- struct XenBlkDev *blkdev = container_of(xendev, struct XenBlkDev, xendev);
- int info = 0;
- char *directiosafe = NULL;
-
- trace_xen_disk_init(xendev->name);
-
- /* read xenstore entries */
- if (blkdev->params == NULL) {
- char *h = NULL;
- blkdev->params = xenstore_read_be_str(xendev, "params");
- if (blkdev->params != NULL) {
- h = strchr(blkdev->params, ':');
- }
- if (h != NULL) {
- blkdev->fileproto = blkdev->params;
- blkdev->filename = h+1;
- *h = 0;
- } else {
- blkdev->fileproto = "<unset>";
- blkdev->filename = blkdev->params;
- }
- }
- if (!strcmp("aio", blkdev->fileproto)) {
- blkdev->fileproto = "raw";
- }
- if (!strcmp("vhd", blkdev->fileproto)) {
- blkdev->fileproto = "vpc";
- }
- if (blkdev->mode == NULL) {
- blkdev->mode = xenstore_read_be_str(xendev, "mode");
- }
- if (blkdev->type == NULL) {
- blkdev->type = xenstore_read_be_str(xendev, "type");
- }
- if (blkdev->dev == NULL) {
- blkdev->dev = xenstore_read_be_str(xendev, "dev");
- }
- if (blkdev->devtype == NULL) {
- blkdev->devtype = xenstore_read_be_str(xendev, "device-type");
- }
- directiosafe = xenstore_read_be_str(xendev, "direct-io-safe");
- blkdev->directiosafe = (directiosafe && atoi(directiosafe));
-
- /* do we have all we need? */
- if (blkdev->params == NULL ||
- blkdev->mode == NULL ||
- blkdev->type == NULL ||
- blkdev->dev == NULL) {
- goto out_error;
- }
-
- /* read-only ? */
- if (strcmp(blkdev->mode, "w")) {
- info |= VDISK_READONLY;
- }
-
- /* cdrom ? */
- if (blkdev->devtype && !strcmp(blkdev->devtype, "cdrom")) {
- info |= VDISK_CDROM;
- }
-
- blkdev->file_blk = BLOCK_SIZE;
-
- /* fill info
- * blk_connect supplies sector-size and sectors
- */
- xenstore_write_be_int(xendev, "feature-flush-cache", 1);
- xenstore_write_be_int(xendev, "info", info);
-
- xenstore_write_be_int(xendev, "max-ring-page-order",
- MAX_RING_PAGE_ORDER);
-
- blk_parse_discard(blkdev);
-
- g_free(directiosafe);
- return 0;
-
-out_error:
- g_free(blkdev->params);
- blkdev->params = NULL;
- g_free(blkdev->mode);
- blkdev->mode = NULL;
- g_free(blkdev->type);
- blkdev->type = NULL;
- g_free(blkdev->dev);
- blkdev->dev = NULL;
- g_free(blkdev->devtype);
- blkdev->devtype = NULL;
- g_free(directiosafe);
- blkdev->directiosafe = false;
- return -1;
-}
-
-static int blk_connect(struct XenDevice *xendev)
-{
- struct XenBlkDev *blkdev = container_of(xendev, struct XenBlkDev, xendev);
- int index, qflags;
- bool readonly = true;
- bool writethrough = true;
- int order, ring_ref;
- unsigned int ring_size, max_grants;
- unsigned int i;
-
- trace_xen_disk_connect(xendev->name);
-
- /* read-only ? */
- if (blkdev->directiosafe) {
- qflags = BDRV_O_NOCACHE | BDRV_O_NATIVE_AIO;
- } else {
- qflags = 0;
- writethrough = false;
- }
- if (strcmp(blkdev->mode, "w") == 0) {
- qflags |= BDRV_O_RDWR;
- readonly = false;
- }
- if (blkdev->feature_discard) {
- qflags |= BDRV_O_UNMAP;
- }
-
- /* init qemu block driver */
- index = (xendev->dev - 202 * 256) / 16;
- 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_str(options, "driver", blkdev->fileproto);
- }
-
- /* setup via xenbus -> create new block driver instance */
- xen_pv_printf(xendev, 2, "create new bdrv (xenbus setup)\n");
- blkdev->blk = blk_new_open(blkdev->filename, NULL, options,
- qflags, &local_err);
- if (!blkdev->blk) {
- xen_pv_printf(xendev, 0, "error: %s\n",
- error_get_pretty(local_err));
- error_free(local_err);
- return -1;
- }
- blk_set_enable_write_cache(blkdev->blk, !writethrough);
- } else {
- /* setup via qemu cmdline -> already setup for us */
- xen_pv_printf(xendev, 2,
- "get configured bdrv (cmdline setup)\n");
- blkdev->blk = blk_by_legacy_dinfo(blkdev->dinfo);
- if (blk_is_read_only(blkdev->blk) && !readonly) {
- xen_pv_printf(xendev, 0, "Unexpected read-only drive");
- blkdev->blk = NULL;
- return -1;
- }
- /* blkdev->blk is not create by us, we get a reference
- * so we can blk_unref() unconditionally */
- blk_ref(blkdev->blk);
- }
- blk_attach_dev_legacy(blkdev->blk, blkdev);
- blkdev->file_size = blk_getlength(blkdev->blk);
- if (blkdev->file_size < 0) {
- BlockDriverState *bs = blk_bs(blkdev->blk);
- const char *drv_name = bs ? bdrv_get_format_name(bs) : NULL;
- xen_pv_printf(xendev, 1, "blk_getlength: %d (%s) | drv %s\n",
- (int)blkdev->file_size, strerror(-blkdev->file_size),
- drv_name ?: "-");
- blkdev->file_size = 0;
- }
-
- xen_pv_printf(xendev, 1, "type \"%s\", fileproto \"%s\", filename \"%s\","
- " size %" PRId64 " (%" PRId64 " MB)\n",
- blkdev->type, blkdev->fileproto, blkdev->filename,
- blkdev->file_size, blkdev->file_size / MiB);
-
- /* Fill in number of sector size and number of sectors */
- xenstore_write_be_int(xendev, "sector-size", blkdev->file_blk);
- xenstore_write_be_int64(xendev, "sectors",
- blkdev->file_size / blkdev->file_blk);
-
- if (xenstore_read_fe_int(xendev, "ring-page-order",
- &order) == -1) {
- blkdev->nr_ring_ref = 1;
-
- if (xenstore_read_fe_int(xendev, "ring-ref",
- &ring_ref) == -1) {
- return -1;
- }
- blkdev->ring_ref[0] = ring_ref;
-
- } else if (order >= 0 && order <= MAX_RING_PAGE_ORDER) {
- blkdev->nr_ring_ref = 1 << order;
-
- for (i = 0; i < blkdev->nr_ring_ref; i++) {
- char *key;
-
- key = g_strdup_printf("ring-ref%u", i);
- if (!key) {
- return -1;
- }
-
- if (xenstore_read_fe_int(xendev, key,
- &ring_ref) == -1) {
- g_free(key);
- return -1;
- }
- blkdev->ring_ref[i] = ring_ref;
-
- g_free(key);
- }
- } else {
- xen_pv_printf(xendev, 0, "invalid ring-page-order: %d\n",
- order);
- return -1;
- }
-
- if (xenstore_read_fe_int(xendev, "event-channel",
- &xendev->remote_port) == -1) {
- return -1;
- }
-
- if (!xendev->protocol) {
- blkdev->protocol = BLKIF_PROTOCOL_NATIVE;
- } else if (strcmp(xendev->protocol, XEN_IO_PROTO_ABI_NATIVE) == 0) {
- blkdev->protocol = BLKIF_PROTOCOL_NATIVE;
- } else if (strcmp(xendev->protocol, XEN_IO_PROTO_ABI_X86_32) == 0) {
- blkdev->protocol = BLKIF_PROTOCOL_X86_32;
- } else if (strcmp(xendev->protocol, XEN_IO_PROTO_ABI_X86_64) == 0) {
- blkdev->protocol = BLKIF_PROTOCOL_X86_64;
- } else {
- blkdev->protocol = BLKIF_PROTOCOL_NATIVE;
- }
-
- ring_size = XC_PAGE_SIZE * blkdev->nr_ring_ref;
- switch (blkdev->protocol) {
- case BLKIF_PROTOCOL_NATIVE:
- {
- blkdev->max_requests = __CONST_RING_SIZE(blkif, ring_size);
- break;
- }
- case BLKIF_PROTOCOL_X86_32:
- {
- blkdev->max_requests = __CONST_RING_SIZE(blkif_x86_32, ring_size);
- break;
- }
- case BLKIF_PROTOCOL_X86_64:
- {
- blkdev->max_requests = __CONST_RING_SIZE(blkif_x86_64, ring_size);
- break;
- }
- default:
- return -1;
- }
-
- /* Add on the number needed for the ring pages */
- max_grants = blkdev->nr_ring_ref;
-
- xen_be_set_max_grant_refs(xendev, max_grants);
- blkdev->sring = xen_be_map_grant_refs(xendev, blkdev->ring_ref,
- blkdev->nr_ring_ref,
- PROT_READ | PROT_WRITE);
- if (!blkdev->sring) {
- return -1;
- }
-
- switch (blkdev->protocol) {
- case BLKIF_PROTOCOL_NATIVE:
- {
- blkif_sring_t *sring_native = blkdev->sring;
- BACK_RING_INIT(&blkdev->rings.native, sring_native, ring_size);
- break;
- }
- case BLKIF_PROTOCOL_X86_32:
- {
- blkif_x86_32_sring_t *sring_x86_32 = blkdev->sring;
-
- BACK_RING_INIT(&blkdev->rings.x86_32_part, sring_x86_32, ring_size);
- break;
- }
- case BLKIF_PROTOCOL_X86_64:
- {
- blkif_x86_64_sring_t *sring_x86_64 = blkdev->sring;
-
- BACK_RING_INIT(&blkdev->rings.x86_64_part, sring_x86_64, ring_size);
- break;
- }
- }
-
- blk_set_aio_context(blkdev->blk, blkdev->ctx);
-
- xen_be_bind_evtchn(xendev);
-
- xen_pv_printf(xendev, 1, "ok: proto %s, nr-ring-ref %u, "
- "remote port %d, local port %d\n",
- xendev->protocol, blkdev->nr_ring_ref,
- xendev->remote_port, xendev->local_port);
- return 0;
-}
-
-static void blk_disconnect(struct XenDevice *xendev)
-{
- struct XenBlkDev *blkdev = container_of(xendev, struct XenBlkDev, xendev);
-
- trace_xen_disk_disconnect(xendev->name);
-
- aio_context_acquire(blkdev->ctx);
-
- if (blkdev->blk) {
- blk_set_aio_context(blkdev->blk, qemu_get_aio_context());
- blk_detach_dev(blkdev->blk, blkdev);
- blk_unref(blkdev->blk);
- blkdev->blk = NULL;
- }
- xen_pv_unbind_evtchn(xendev);
-
- aio_context_release(blkdev->ctx);
-
- if (blkdev->sring) {
- xen_be_unmap_grant_refs(xendev, blkdev->sring,
- blkdev->nr_ring_ref);
- blkdev->sring = NULL;
- }
-}
-
-static int blk_free(struct XenDevice *xendev)
-{
- struct XenBlkDev *blkdev = container_of(xendev, struct XenBlkDev, xendev);
- struct ioreq *ioreq;
-
- trace_xen_disk_free(xendev->name);
-
- blk_disconnect(xendev);
-
- while (!QLIST_EMPTY(&blkdev->freelist)) {
- ioreq = QLIST_FIRST(&blkdev->freelist);
- QLIST_REMOVE(ioreq, list);
- qemu_iovec_destroy(&ioreq->v);
- g_free(ioreq);
- }
-
- 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);
- iothread_destroy(blkdev->iothread);
- return 0;
-}
-
-static void blk_event(struct XenDevice *xendev)
-{
- struct XenBlkDev *blkdev = container_of(xendev, struct XenBlkDev, xendev);
-
- qemu_bh_schedule(blkdev->bh);
-}
-
-struct XenDevOps xen_blkdev_ops = {
- .flags = DEVOPS_FLAG_NEED_GNTDEV,
- .size = sizeof(struct XenBlkDev),
- .alloc = blk_alloc,
- .init = blk_init,
- .initialise = blk_connect,
- .disconnect = blk_disconnect,
- .event = blk_event,
- .free = blk_free,
-};
diff --git a/hw/char/xen_console.c b/hw/char/xen_console.c
index b1a1e66..dc6ff0e 100644
--- a/hw/char/xen_console.c
+++ b/hw/char/xen_console.c
@@ -26,7 +26,7 @@
#include "qapi/error.h"
#include "hw/hw.h"
#include "chardev/char-fe.h"
-#include "hw/xen/xen_backend.h"
+#include "hw/xen/xen-legacy-backend.h"
#include <xen/io/console.h>
@@ -39,7 +39,7 @@ struct buffer {
};
struct XenConsole {
- struct XenDevice xendev; /* must be first */
+ struct XenLegacyDevice xendev; /* must be first */
struct buffer buffer;
char console[XEN_BUFSIZE];
int ring_ref;
@@ -173,7 +173,7 @@ static void xencons_send(struct XenConsole *con)
/* -------------------------------------------------------------------- */
-static int con_init(struct XenDevice *xendev)
+static int con_init(struct XenLegacyDevice *xendev)
{
struct XenConsole *con = container_of(xendev, struct XenConsole, xendev);
char *type, *dom, label[32];
@@ -222,7 +222,7 @@ out:
return ret;
}
-static int con_initialise(struct XenDevice *xendev)
+static int con_initialise(struct XenLegacyDevice *xendev)
{
struct XenConsole *con = container_of(xendev, struct XenConsole, xendev);
int limit;
@@ -259,7 +259,7 @@ static int con_initialise(struct XenDevice *xendev)
return 0;
}
-static void con_disconnect(struct XenDevice *xendev)
+static void con_disconnect(struct XenLegacyDevice *xendev)
{
struct XenConsole *con = container_of(xendev, struct XenConsole, xendev);
@@ -276,7 +276,7 @@ static void con_disconnect(struct XenDevice *xendev)
}
}
-static void con_event(struct XenDevice *xendev)
+static void con_event(struct XenLegacyDevice *xendev)
{
struct XenConsole *con = container_of(xendev, struct XenConsole, xendev);
diff --git a/hw/display/xenfb.c b/hw/display/xenfb.c
index 0330dc6..6202f11 100644
--- a/hw/display/xenfb.c
+++ b/hw/display/xenfb.c
@@ -30,7 +30,7 @@
#include "hw/hw.h"
#include "ui/input.h"
#include "ui/console.h"
-#include "hw/xen/xen_backend.h"
+#include "hw/xen/xen-legacy-backend.h"
#include <xen/event_channel.h>
#include <xen/io/fbif.h>
@@ -46,7 +46,7 @@
/* -------------------------------------------------------------------- */
struct common {
- struct XenDevice xendev; /* must be first */
+ struct XenLegacyDevice xendev; /* must be first */
void *page;
};
@@ -342,14 +342,14 @@ static QemuInputHandler xenfb_rel_mouse = {
.sync = xenfb_mouse_sync,
};
-static int input_init(struct XenDevice *xendev)
+static int input_init(struct XenLegacyDevice *xendev)
{
xenstore_write_be_int(xendev, "feature-abs-pointer", 1);
xenstore_write_be_int(xendev, "feature-raw-pointer", 1);
return 0;
}
-static int input_initialise(struct XenDevice *xendev)
+static int input_initialise(struct XenLegacyDevice *xendev)
{
struct XenInput *in = container_of(xendev, struct XenInput, c.xendev);
int rc;
@@ -361,7 +361,7 @@ static int input_initialise(struct XenDevice *xendev)
return 0;
}
-static void input_connected(struct XenDevice *xendev)
+static void input_connected(struct XenLegacyDevice *xendev)
{
struct XenInput *in = container_of(xendev, struct XenInput, c.xendev);
@@ -395,7 +395,7 @@ static void input_connected(struct XenDevice *xendev)
}
}
-static void input_disconnect(struct XenDevice *xendev)
+static void input_disconnect(struct XenLegacyDevice *xendev)
{
struct XenInput *in = container_of(xendev, struct XenInput, c.xendev);
@@ -410,7 +410,7 @@ static void input_disconnect(struct XenDevice *xendev)
common_unbind(&in->c);
}
-static void input_event(struct XenDevice *xendev)
+static void input_event(struct XenLegacyDevice *xendev)
{
struct XenInput *xenfb = container_of(xendev, struct XenInput, c.xendev);
struct xenkbd_page *page = xenfb->c.page;
@@ -867,7 +867,7 @@ static void xenfb_handle_events(struct XenFB *xenfb)
page->out_cons = cons;
}
-static int fb_init(struct XenDevice *xendev)
+static int fb_init(struct XenLegacyDevice *xendev)
{
#ifdef XENFB_TYPE_RESIZE
xenstore_write_be_int(xendev, "feature-resize", 1);
@@ -875,7 +875,7 @@ static int fb_init(struct XenDevice *xendev)
return 0;
}
-static int fb_initialise(struct XenDevice *xendev)
+static int fb_initialise(struct XenLegacyDevice *xendev)
{
struct XenFB *fb = container_of(xendev, struct XenFB, c.xendev);
struct xenfb_page *fb_page;
@@ -912,7 +912,7 @@ static int fb_initialise(struct XenDevice *xendev)
return 0;
}
-static void fb_disconnect(struct XenDevice *xendev)
+static void fb_disconnect(struct XenLegacyDevice *xendev)
{
struct XenFB *fb = container_of(xendev, struct XenFB, c.xendev);
@@ -935,7 +935,8 @@ static void fb_disconnect(struct XenDevice *xendev)
fb->bug_trigger = 0;
}
-static void fb_frontend_changed(struct XenDevice *xendev, const char *node)
+static void fb_frontend_changed(struct XenLegacyDevice *xendev,
+ const char *node)
{
struct XenFB *fb = container_of(xendev, struct XenFB, c.xendev);
@@ -953,7 +954,7 @@ static void fb_frontend_changed(struct XenDevice *xendev, const char *node)
}
}
-static void fb_event(struct XenDevice *xendev)
+static void fb_event(struct XenLegacyDevice *xendev)
{
struct XenFB *xenfb = container_of(xendev, struct XenFB, c.xendev);
diff --git a/hw/i386/xen/xen-hvm.c b/hw/i386/xen/xen-hvm.c
index 8c8562f..2939122 100644
--- a/hw/i386/xen/xen-hvm.c
+++ b/hw/i386/xen/xen-hvm.c
@@ -16,7 +16,8 @@
#include "hw/i386/pc.h"
#include "hw/i386/apic-msidef.h"
#include "hw/xen/xen_common.h"
-#include "hw/xen/xen_backend.h"
+#include "hw/xen/xen-legacy-backend.h"
+#include "hw/xen/xen-bus.h"
#include "qapi/error.h"
#include "qapi/qapi-commands-misc.h"
#include "qemu/error-report.h"
@@ -1484,6 +1485,8 @@ void xen_hvm_init(PCMachineState *pcms, MemoryRegion **ram_memory)
QLIST_INIT(&state->dev_list);
device_listener_register(&state->device_listener);
+ xen_bus_init();
+
/* Initialize backend core & drivers */
if (xen_be_init() != 0) {
error_report("xen backend core setup failed");
diff --git a/hw/i386/xen/xen-mapcache.c b/hw/i386/xen/xen-mapcache.c
index 02e823c..349f72d 100644
--- a/hw/i386/xen/xen-mapcache.c
+++ b/hw/i386/xen/xen-mapcache.c
@@ -14,7 +14,7 @@
#include <sys/resource.h>
-#include "hw/xen/xen_backend.h"
+#include "hw/xen/xen-legacy-backend.h"
#include "qemu/bitmap.h"
#include <xen/hvm/params.h>
diff --git a/hw/i386/xen/xen_platform.c b/hw/i386/xen/xen_platform.c
index deb7a0c..16afb54 100644
--- a/hw/i386/xen/xen_platform.c
+++ b/hw/i386/xen/xen_platform.c
@@ -30,7 +30,7 @@
#include "hw/pci/pci.h"
#include "hw/irq.h"
#include "hw/xen/xen_common.h"
-#include "hw/xen/xen_backend.h"
+#include "hw/xen/xen-legacy-backend.h"
#include "trace.h"
#include "exec/address-spaces.h"
#include "sysemu/block-backend.h"
diff --git a/hw/net/xen_nic.c b/hw/net/xen_nic.c
index 46a8dbf..37cda8e 100644
--- a/hw/net/xen_nic.c
+++ b/hw/net/xen_nic.c
@@ -28,14 +28,14 @@
#include "net/net.h"
#include "net/checksum.h"
#include "net/util.h"
-#include "hw/xen/xen_backend.h"
+#include "hw/xen/xen-legacy-backend.h"
#include <xen/io/netif.h>
/* ------------------------------------------------------------- */
struct XenNetDev {
- struct XenDevice xendev; /* must be first */
+ struct XenLegacyDevice xendev; /* must be first */
char *mac;
int tx_work;
int tx_ring_ref;
@@ -276,7 +276,7 @@ static NetClientInfo net_xen_info = {
.receive = net_rx_packet,
};
-static int net_init(struct XenDevice *xendev)
+static int net_init(struct XenLegacyDevice *xendev)
{
struct XenNetDev *netdev = container_of(xendev, struct XenNetDev, xendev);
@@ -308,7 +308,7 @@ static int net_init(struct XenDevice *xendev)
return 0;
}
-static int net_connect(struct XenDevice *xendev)
+static int net_connect(struct XenLegacyDevice *xendev)
{
struct XenNetDev *netdev = container_of(xendev, struct XenNetDev, xendev);
int rx_copy;
@@ -363,7 +363,7 @@ static int net_connect(struct XenDevice *xendev)
return 0;
}
-static void net_disconnect(struct XenDevice *xendev)
+static void net_disconnect(struct XenLegacyDevice *xendev)
{
struct XenNetDev *netdev = container_of(xendev, struct XenNetDev, xendev);
@@ -379,14 +379,14 @@ static void net_disconnect(struct XenDevice *xendev)
}
}
-static void net_event(struct XenDevice *xendev)
+static void net_event(struct XenLegacyDevice *xendev)
{
struct XenNetDev *netdev = container_of(xendev, struct XenNetDev, xendev);
net_tx_packets(netdev);
qemu_flush_queued_packets(qemu_get_queue(netdev->nic));
}
-static int net_free(struct XenDevice *xendev)
+static int net_free(struct XenLegacyDevice *xendev)
{
struct XenNetDev *netdev = container_of(xendev, struct XenNetDev, xendev);
diff --git a/hw/usb/xen-usb.c b/hw/usb/xen-usb.c
index 5758a10..b20d0cf 100644
--- a/hw/usb/xen-usb.c
+++ b/hw/usb/xen-usb.c
@@ -27,7 +27,7 @@
#include "qemu/option.h"
#include "hw/sysbus.h"
#include "hw/usb.h"
-#include "hw/xen/xen_backend.h"
+#include "hw/xen/xen-legacy-backend.h"
#include "monitor/qdev.h"
#include "qapi/qmp/qdict.h"
#include "qapi/qmp/qstring.h"
@@ -99,7 +99,7 @@ struct usbback_hotplug {
};
struct usbback_info {
- struct XenDevice xendev; /* must be first */
+ struct XenLegacyDevice xendev; /* must be first */
USBBus bus;
void *urb_sring;
void *conn_sring;
@@ -142,7 +142,7 @@ static int usbback_gnttab_map(struct usbback_req *usbback_req)
unsigned int nr_segs, i, prot;
uint32_t ref[USBIF_MAX_SEGMENTS_PER_REQUEST];
struct usbback_info *usbif = usbback_req->usbif;
- struct XenDevice *xendev = &usbif->xendev;
+ struct XenLegacyDevice *xendev = &usbif->xendev;
struct usbif_request_segment *seg;
void *addr;
@@ -220,7 +220,7 @@ static int usbback_gnttab_map(struct usbback_req *usbback_req)
static int usbback_init_packet(struct usbback_req *usbback_req)
{
- struct XenDevice *xendev = &usbback_req->usbif->xendev;
+ struct XenLegacyDevice *xendev = &usbback_req->usbif->xendev;
USBPacket *packet = &usbback_req->packet;
USBDevice *dev = usbback_req->stub->dev;
USBEndpoint *ep;
@@ -279,7 +279,7 @@ static void usbback_do_response(struct usbback_req *usbback_req, int32_t status,
{
struct usbback_info *usbif;
struct usbif_urb_response *res;
- struct XenDevice *xendev;
+ struct XenLegacyDevice *xendev;
unsigned int notify;
usbif = usbback_req->usbif;
@@ -824,7 +824,7 @@ static void usbback_process_port(struct usbback_info *usbif, unsigned port)
g_free(busid);
}
-static void usbback_disconnect(struct XenDevice *xendev)
+static void usbback_disconnect(struct XenLegacyDevice *xendev)
{
struct usbback_info *usbif;
unsigned int i;
@@ -853,7 +853,7 @@ static void usbback_disconnect(struct XenDevice *xendev)
TR_BUS(xendev, "finished\n");
}
-static int usbback_connect(struct XenDevice *xendev)
+static int usbback_connect(struct XenLegacyDevice *xendev)
{
struct usbback_info *usbif;
struct usbif_urb_sring *urb_sring;
@@ -913,7 +913,8 @@ static int usbback_connect(struct XenDevice *xendev)
return 0;
}
-static void usbback_backend_changed(struct XenDevice *xendev, const char *node)
+static void usbback_backend_changed(struct XenLegacyDevice *xendev,
+ const char *node)
{
struct usbback_info *usbif;
unsigned int i;
@@ -926,7 +927,7 @@ static void usbback_backend_changed(struct XenDevice *xendev, const char *node)
}
}
-static int usbback_init(struct XenDevice *xendev)
+static int usbback_init(struct XenLegacyDevice *xendev)
{
struct usbback_info *usbif;
@@ -1005,7 +1006,7 @@ static USBPortOps xen_usb_port_ops = {
static USBBusOps xen_usb_bus_ops = {
};
-static void usbback_alloc(struct XenDevice *xendev)
+static void usbback_alloc(struct XenLegacyDevice *xendev)
{
struct usbback_info *usbif;
USBPort *p;
@@ -1027,7 +1028,7 @@ static void usbback_alloc(struct XenDevice *xendev)
usbif->bh = qemu_bh_new(usbback_bh, usbif);
}
-static int usbback_free(struct XenDevice *xendev)
+static int usbback_free(struct XenLegacyDevice *xendev)
{
struct usbback_info *usbif;
struct usbback_req *usbback_req;
@@ -1066,7 +1067,7 @@ static int usbback_free(struct XenDevice *xendev)
return 0;
}
-static void usbback_event(struct XenDevice *xendev)
+static void usbback_event(struct XenLegacyDevice *xendev)
{
struct usbback_info *usbif;
diff --git a/hw/xen/Makefile.objs b/hw/xen/Makefile.objs
index 9ea5c73..84df60a 100644
--- a/hw/xen/Makefile.objs
+++ b/hw/xen/Makefile.objs
@@ -1,5 +1,5 @@
# xen backend driver support
-common-obj-$(CONFIG_XEN) += xen_backend.o xen_devconfig.o xen_pvdev.o xen-common.o
+common-obj-$(CONFIG_XEN) += xen-legacy-backend.o xen_devconfig.o xen_pvdev.o xen-common.o xen-bus.o xen-bus-helper.o xen-backend.o
obj-$(CONFIG_XEN_PCI_PASSTHROUGH) += xen-host-pci-device.o
obj-$(CONFIG_XEN_PCI_PASSTHROUGH) += xen_pt.o xen_pt_config_init.o xen_pt_graphics.o xen_pt_msi.o
diff --git a/hw/xen/trace-events b/hw/xen/trace-events
index c7e7a3b..f694462 100644
--- a/hw/xen/trace-events
+++ b/hw/xen/trace-events
@@ -12,3 +12,29 @@ xen_unmap_portio_range(uint32_t id, uint64_t start_addr, uint64_t end_addr) "id:
xen_map_pcidev(uint32_t id, uint8_t bus, uint8_t dev, uint8_t func) "id: %u bdf: %02x.%02x.%02x"
xen_unmap_pcidev(uint32_t id, uint8_t bus, uint8_t dev, uint8_t func) "id: %u bdf: %02x.%02x.%02x"
xen_domid_restrict(int err) "err: %u"
+
+# include/hw/xen/xen-bus.c
+xen_bus_realize(void) ""
+xen_bus_unrealize(void) ""
+xen_bus_enumerate(void) ""
+xen_bus_type_enumerate(const char *type) "type: %s"
+xen_bus_backend_create(const char *type, const char *path) "type: %s path: %s"
+xen_bus_add_watch(const char *node, const char *key, char *token) "node: %s key: %s token: %s"
+xen_bus_remove_watch(const char *node, const char *key, char *token) "node: %s key: %s token: %s"
+xen_bus_watch(const char *token) "token: %s"
+xen_device_realize(const char *type, char *name) "type: %s name: %s"
+xen_device_unrealize(const char *type, char *name) "type: %s name: %s"
+xen_device_backend_state(const char *type, char *name, const char *state) "type: %s name: %s -> %s"
+xen_device_backend_online(const char *type, char *name, bool online) "type: %s name: %s -> %u"
+xen_device_backend_changed(const char *type, char *name) "type: %s name: %s"
+xen_device_frontend_state(const char *type, char *name, const char *state) "type: %s name: %s -> %s"
+xen_device_frontend_changed(const char *type, char *name) "type: %s name: %s"
+xen_device_unplug(const char *type, char *name) "type: %s name: %s"
+
+# include/hw/xen/xen-bus-helper.c
+xs_node_create(const char *node) "%s"
+xs_node_destroy(const char *node) "%s"
+xs_node_vprintf(char *path, char *value) "%s %s"
+xs_node_vscanf(char *path, char *value) "%s %s"
+xs_node_watch(char *path) "%s"
+xs_node_unwatch(char *path) "%s"
diff --git a/hw/xen/xen-backend.c b/hw/xen/xen-backend.c
new file mode 100644
index 0000000..da065f8
--- /dev/null
+++ b/hw/xen/xen-backend.c
@@ -0,0 +1,165 @@
+/*
+ * Copyright (c) 2018 Citrix Systems Inc.
+ *
+ * This work is licensed under the terms of the GNU GPL, version 2 or later.
+ * See the COPYING file in the top-level directory.
+ */
+
+#include "qemu/osdep.h"
+#include "qemu/error-report.h"
+#include "qapi/error.h"
+#include "hw/xen/xen-backend.h"
+#include "hw/xen/xen-bus.h"
+
+typedef struct XenBackendImpl {
+ const char *type;
+ XenBackendDeviceCreate create;
+ XenBackendDeviceDestroy destroy;
+} XenBackendImpl;
+
+struct XenBackendInstance {
+ QLIST_ENTRY(XenBackendInstance) entry;
+ const XenBackendImpl *impl;
+ XenBus *xenbus;
+ char *name;
+ XenDevice *xendev;
+};
+
+static GHashTable *xen_backend_table_get(void)
+{
+ static GHashTable *table;
+
+ if (table == NULL) {
+ table = g_hash_table_new(g_str_hash, g_str_equal);
+ }
+
+ return table;
+}
+
+static void xen_backend_table_add(XenBackendImpl *impl)
+{
+ g_hash_table_insert(xen_backend_table_get(), (void *)impl->type, impl);
+}
+
+static const XenBackendImpl *xen_backend_table_lookup(const char *type)
+{
+ return g_hash_table_lookup(xen_backend_table_get(), type);
+}
+
+void xen_backend_register(const XenBackendInfo *info)
+{
+ XenBackendImpl *impl = g_new0(XenBackendImpl, 1);
+
+ g_assert(info->type);
+
+ if (xen_backend_table_lookup(info->type)) {
+ error_report("attempt to register duplicate Xen backend type '%s'",
+ info->type);
+ abort();
+ }
+
+ if (!info->create) {
+ error_report("backend type '%s' has no creator", info->type);
+ abort();
+ }
+
+ impl->type = info->type;
+ impl->create = info->create;
+ impl->destroy = info->destroy;
+
+ xen_backend_table_add(impl);
+}
+
+static QLIST_HEAD(, XenBackendInstance) backend_list;
+
+static void xen_backend_list_add(XenBackendInstance *backend)
+{
+ QLIST_INSERT_HEAD(&backend_list, backend, entry);
+}
+
+static XenBackendInstance *xen_backend_list_find(XenDevice *xendev)
+{
+ XenBackendInstance *backend;
+
+ QLIST_FOREACH(backend, &backend_list, entry) {
+ if (backend->xendev == xendev) {
+ return backend;
+ }
+ }
+
+ return NULL;
+}
+
+static void xen_backend_list_remove(XenBackendInstance *backend)
+{
+ QLIST_REMOVE(backend, entry);
+}
+
+void xen_backend_device_create(XenBus *xenbus, const char *type,
+ const char *name, QDict *opts, Error **errp)
+{
+ const XenBackendImpl *impl = xen_backend_table_lookup(type);
+ XenBackendInstance *backend;
+ Error *local_error = NULL;
+
+ if (!impl) {
+ return;
+ }
+
+ backend = g_new0(XenBackendInstance, 1);
+ backend->xenbus = xenbus;
+ backend->name = g_strdup(name);
+
+ impl->create(backend, opts, &local_error);
+ if (local_error) {
+ error_propagate(errp, local_error);
+ g_free(backend->name);
+ g_free(backend);
+ return;
+ }
+
+ backend->impl = impl;
+ xen_backend_list_add(backend);
+}
+
+XenBus *xen_backend_get_bus(XenBackendInstance *backend)
+{
+ return backend->xenbus;
+}
+
+const char *xen_backend_get_name(XenBackendInstance *backend)
+{
+ return backend->name;
+}
+
+void xen_backend_set_device(XenBackendInstance *backend,
+ XenDevice *xendev)
+{
+ g_assert(!backend->xendev);
+ backend->xendev = xendev;
+}
+
+XenDevice *xen_backend_get_device(XenBackendInstance *backend)
+{
+ return backend->xendev;
+}
+
+
+bool xen_backend_try_device_destroy(XenDevice *xendev, Error **errp)
+{
+ XenBackendInstance *backend = xen_backend_list_find(xendev);
+ const XenBackendImpl *impl;
+
+ if (!backend) {
+ return false;
+ }
+
+ impl = backend->impl;
+ impl->destroy(backend, errp);
+
+ xen_backend_list_remove(backend);
+ g_free(backend->name);
+ g_free(backend);
+
+ return true;
+}
diff --git a/hw/xen/xen-bus-helper.c b/hw/xen/xen-bus-helper.c
new file mode 100644
index 0000000..5f7a4b2
--- /dev/null
+++ b/hw/xen/xen-bus-helper.c
@@ -0,0 +1,184 @@
+/*
+ * Copyright (c) 2018 Citrix Systems Inc.
+ *
+ * This work is licensed under the terms of the GNU GPL, version 2 or later.
+ * See the COPYING file in the top-level directory.
+ */
+
+#include "qemu/osdep.h"
+#include "hw/hw.h"
+#include "hw/sysbus.h"
+#include "hw/xen/xen.h"
+#include "hw/xen/xen-bus.h"
+#include "hw/xen/xen-bus-helper.h"
+#include "qapi/error.h"
+
+#include <glib/gprintf.h>
+
+struct xs_state {
+ enum xenbus_state statenum;
+ const char *statestr;
+};
+#define XS_STATE(state) { state, #state }
+
+static struct xs_state xs_state[] = {
+ XS_STATE(XenbusStateUnknown),
+ XS_STATE(XenbusStateInitialising),
+ XS_STATE(XenbusStateInitWait),
+ XS_STATE(XenbusStateInitialised),
+ XS_STATE(XenbusStateConnected),
+ XS_STATE(XenbusStateClosing),
+ XS_STATE(XenbusStateClosed),
+ XS_STATE(XenbusStateReconfiguring),
+ XS_STATE(XenbusStateReconfigured),
+};
+
+#undef XS_STATE
+
+const char *xs_strstate(enum xenbus_state state)
+{
+ unsigned int i;
+
+ for (i = 0; i < ARRAY_SIZE(xs_state); i++) {
+ if (xs_state[i].statenum == state) {
+ return xs_state[i].statestr;
+ }
+ }
+
+ return "INVALID";
+}
+
+void xs_node_create(struct xs_handle *xsh, xs_transaction_t tid,
+ const char *node, struct xs_permissions perms[],
+ unsigned int nr_perms, Error **errp)
+{
+ trace_xs_node_create(node);
+
+ if (!xs_write(xsh, tid, node, "", 0)) {
+ error_setg_errno(errp, errno, "failed to create node '%s'", node);
+ return;
+ }
+
+ if (!xs_set_permissions(xsh, tid, node, perms, nr_perms)) {
+ error_setg_errno(errp, errno, "failed to set node '%s' permissions",
+ node);
+ }
+}
+
+void xs_node_destroy(struct xs_handle *xsh, xs_transaction_t tid,
+ const char *node, Error **errp)
+{
+ trace_xs_node_destroy(node);
+
+ if (!xs_rm(xsh, tid, node)) {
+ error_setg_errno(errp, errno, "failed to destroy node '%s'", node);
+ }
+}
+
+void xs_node_vprintf(struct xs_handle *xsh, xs_transaction_t tid,
+ const char *node, const char *key, Error **errp,
+ const char *fmt, va_list ap)
+{
+ char *path, *value;
+ int len;
+
+ path = (strlen(node) != 0) ? g_strdup_printf("%s/%s", node, key) :
+ g_strdup(key);
+ len = g_vasprintf(&value, fmt, ap);
+
+ trace_xs_node_vprintf(path, value);
+
+ if (!xs_write(xsh, tid, path, value, len)) {
+ error_setg_errno(errp, errno, "failed to write '%s' to '%s'",
+ value, path);
+ }
+
+ g_free(value);
+ g_free(path);
+}
+
+void xs_node_printf(struct xs_handle *xsh, xs_transaction_t tid,
+ const char *node, const char *key, Error **errp,
+ const char *fmt, ...)
+{
+ va_list ap;
+
+ va_start(ap, fmt);
+ xs_node_vprintf(xsh, tid, node, key, errp, fmt, ap);
+ va_end(ap);
+}
+
+int xs_node_vscanf(struct xs_handle *xsh, xs_transaction_t tid,
+ const char *node, const char *key, Error **errp,
+ const char *fmt, va_list ap)
+{
+ char *path, *value;
+ int rc;
+
+ path = (strlen(node) != 0) ? g_strdup_printf("%s/%s", node, key) :
+ g_strdup(key);
+ value = xs_read(xsh, tid, path, NULL);
+
+ trace_xs_node_vscanf(path, value);
+
+ if (value) {
+ rc = vsscanf(value, fmt, ap);
+ } else {
+ error_setg_errno(errp, errno, "failed to read from '%s'",
+ path);
+ rc = EOF;
+ }
+
+ free(value);
+ g_free(path);
+
+ return rc;
+}
+
+int xs_node_scanf(struct xs_handle *xsh, xs_transaction_t tid,
+ const char *node, const char *key, Error **errp,
+ const char *fmt, ...)
+{
+ va_list ap;
+ int rc;
+
+ va_start(ap, fmt);
+ rc = xs_node_vscanf(xsh, tid, node, key, errp, fmt, ap);
+ va_end(ap);
+
+ return rc;
+}
+
+void xs_node_watch(struct xs_handle *xsh, const char *node, const char *key,
+ char *token, Error **errp)
+{
+ char *path;
+
+ path = (strlen(node) != 0) ? g_strdup_printf("%s/%s", node, key) :
+ g_strdup(key);
+
+ trace_xs_node_watch(path);
+
+ if (!xs_watch(xsh, path, token)) {
+ error_setg_errno(errp, errno, "failed to watch node '%s'", path);
+ }
+
+ g_free(path);
+}
+
+void xs_node_unwatch(struct xs_handle *xsh, const char *node,
+ const char *key, const char *token, Error **errp)
+{
+ char *path;
+
+ path = (strlen(node) != 0) ? g_strdup_printf("%s/%s", node, key) :
+ g_strdup(key);
+
+ trace_xs_node_unwatch(path);
+
+ if (!xs_unwatch(xsh, path, token)) {
+ error_setg_errno(errp, errno, "failed to unwatch node '%s'", path);
+ }
+
+ g_free(path);
+}
diff --git a/hw/xen/xen-bus.c b/hw/xen/xen-bus.c
new file mode 100644
index 0000000..3aeccec
--- /dev/null
+++ b/hw/xen/xen-bus.c
@@ -0,0 +1,1199 @@
+/*
+ * Copyright (c) 2018 Citrix Systems Inc.
+ *
+ * This work is licensed under the terms of the GNU GPL, version 2 or later.
+ * See the COPYING file in the top-level directory.
+ */
+
+#include "qemu/osdep.h"
+#include "qemu/main-loop.h"
+#include "qemu/uuid.h"
+#include "hw/hw.h"
+#include "hw/sysbus.h"
+#include "hw/xen/xen.h"
+#include "hw/xen/xen-backend.h"
+#include "hw/xen/xen-bus.h"
+#include "hw/xen/xen-bus-helper.h"
+#include "monitor/monitor.h"
+#include "qapi/error.h"
+#include "qapi/qmp/qdict.h"
+#include "sysemu/sysemu.h"
+#include "trace.h"
+
+static char *xen_device_get_backend_path(XenDevice *xendev)
+{
+ XenBus *xenbus = XEN_BUS(qdev_get_parent_bus(DEVICE(xendev)));
+ XenDeviceClass *xendev_class = XEN_DEVICE_GET_CLASS(xendev);
+ const char *type = object_get_typename(OBJECT(xendev));
+ const char *backend = xendev_class->backend;
+
+ if (!backend) {
+ backend = type;
+ }
+
+ return g_strdup_printf("/local/domain/%u/backend/%s/%u/%s",
+ xenbus->backend_id, backend, xendev->frontend_id,
+ xendev->name);
+}
+
+static char *xen_device_get_frontend_path(XenDevice *xendev)
+{
+ XenDeviceClass *xendev_class = XEN_DEVICE_GET_CLASS(xendev);
+ const char *type = object_get_typename(OBJECT(xendev));
+ const char *device = xendev_class->device;
+
+ if (!device) {
+ device = type;
+ }
+
+ return g_strdup_printf("/local/domain/%u/device/%s/%s",
+ xendev->frontend_id, device, xendev->name);
+}
+
+static void xen_device_unplug(XenDevice *xendev, Error **errp)
+{
+ XenBus *xenbus = XEN_BUS(qdev_get_parent_bus(DEVICE(xendev)));
+ const char *type = object_get_typename(OBJECT(xendev));
+ Error *local_err = NULL;
+ xs_transaction_t tid;
+
+ trace_xen_device_unplug(type, xendev->name);
+
+ /* Mimic the way the Xen toolstack does an unplug */
+again:
+ tid = xs_transaction_start(xenbus->xsh);
+ if (tid == XBT_NULL) {
+ error_setg_errno(errp, errno, "failed xs_transaction_start");
+ return;
+ }
+
+ xs_node_printf(xenbus->xsh, tid, xendev->backend_path, "online",
+ &local_err, "%u", 0);
+ if (local_err) {
+ goto abort;
+ }
+
+ xs_node_printf(xenbus->xsh, tid, xendev->backend_path, "state",
+ &local_err, "%u", XenbusStateClosing);
+ if (local_err) {
+ goto abort;
+ }
+
+ if (!xs_transaction_end(xenbus->xsh, tid, false)) {
+ if (errno == EAGAIN) {
+ goto again;
+ }
+
+ error_setg_errno(errp, errno, "failed xs_transaction_end");
+ }
+
+ return;
+
+abort:
+ /*
+ * We only abort if there is already a failure so ignore any error
+ * from ending the transaction.
+ */
+ xs_transaction_end(xenbus->xsh, tid, true);
+ error_propagate(errp, local_err);
+}
+
+static void xen_bus_print_dev(Monitor *mon, DeviceState *dev, int indent)
+{
+ XenDevice *xendev = XEN_DEVICE(dev);
+
+ monitor_printf(mon, "%*sname = '%s' frontend_id = %u\n",
+ indent, "", xendev->name, xendev->frontend_id);
+}
+
+static char *xen_bus_get_dev_path(DeviceState *dev)
+{
+ return xen_device_get_backend_path(XEN_DEVICE(dev));
+}
+
+struct XenWatch {
+ char *node, *key;
+ char *token;
+ XenWatchHandler handler;
+ void *opaque;
+ Notifier notifier;
+};
+
+static void watch_notify(Notifier *n, void *data)
+{
+ XenWatch *watch = container_of(n, XenWatch, notifier);
+ const char *token = data;
+
+ if (!strcmp(watch->token, token)) {
+ watch->handler(watch->opaque);
+ }
+}
+
+static XenWatch *new_watch(const char *node, const char *key,
+ XenWatchHandler handler, void *opaque)
+{
+ XenWatch *watch = g_new0(XenWatch, 1);
+ QemuUUID uuid;
+
+ qemu_uuid_generate(&uuid);
+
+ watch->token = qemu_uuid_unparse_strdup(&uuid);
+ watch->node = g_strdup(node);
+ watch->key = g_strdup(key);
+ watch->handler = handler;
+ watch->opaque = opaque;
+ watch->notifier.notify = watch_notify;
+
+ return watch;
+}
+
+static void free_watch(XenWatch *watch)
+{
+ g_free(watch->token);
+ g_free(watch->key);
+ g_free(watch->node);
+
+ g_free(watch);
+}
+
+static XenWatch *xen_bus_add_watch(XenBus *xenbus, const char *node,
+ const char *key, XenWatchHandler handler,
+ void *opaque, Error **errp)
+{
+ XenWatch *watch = new_watch(node, key, handler, opaque);
+ Error *local_err = NULL;
+
+ trace_xen_bus_add_watch(watch->node, watch->key, watch->token);
+
+ notifier_list_add(&xenbus->watch_notifiers, &watch->notifier);
+
+ xs_node_watch(xenbus->xsh, node, key, watch->token, &local_err);
+ if (local_err) {
+ error_propagate(errp, local_err);
+
+ notifier_remove(&watch->notifier);
+ free_watch(watch);
+
+ return NULL;
+ }
+
+ return watch;
+}
+
+static void xen_bus_remove_watch(XenBus *xenbus, XenWatch *watch,
+ Error **errp)
+{
+ trace_xen_bus_remove_watch(watch->node, watch->key, watch->token);
+
+ xs_node_unwatch(xenbus->xsh, watch->node, watch->key, watch->token,
+ errp);
+
+ notifier_remove(&watch->notifier);
+ free_watch(watch);
+}
+
+static void xen_bus_backend_create(XenBus *xenbus, const char *type,
+ const char *name, char *path,
+ Error **errp)
+{
+ xs_transaction_t tid;
+ char **key;
+ QDict *opts;
+ unsigned int i, n;
+ Error *local_err = NULL;
+
+ trace_xen_bus_backend_create(type, path);
+
+again:
+ tid = xs_transaction_start(xenbus->xsh);
+ if (tid == XBT_NULL) {
+ error_setg(errp, "failed xs_transaction_start");
+ return;
+ }
+
+ key = xs_directory(xenbus->xsh, tid, path, &n);
+ if (!key) {
+ if (!xs_transaction_end(xenbus->xsh, tid, true)) {
+ error_setg_errno(errp, errno, "failed xs_transaction_end");
+ }
+ return;
+ }
+
+ opts = qdict_new();
+ for (i = 0; i < n; i++) {
+ char *val;
+
+ /*
+ * Assume anything found in the xenstore backend area, other than
+ * the keys created for a generic XenDevice, are parameters
+ * to be used to configure the backend.
+ */
+ if (!strcmp(key[i], "state") ||
+ !strcmp(key[i], "online") ||
+ !strcmp(key[i], "frontend") ||
+ !strcmp(key[i], "frontend-id") ||
+ !strcmp(key[i], "hotplug-status"))
+ continue;
+
+ if (xs_node_scanf(xenbus->xsh, tid, path, key[i], NULL, "%ms",
+ &val) == 1) {
+ qdict_put_str(opts, key[i], val);
+ free(val);
+ }
+ }
+
+ free(key);
+
+ if (!xs_transaction_end(xenbus->xsh, tid, false)) {
+ qobject_unref(opts);
+
+ if (errno == EAGAIN) {
+ goto again;
+ }
+
+ error_setg_errno(errp, errno, "failed xs_transaction_end");
+ return;
+ }
+
+ xen_backend_device_create(xenbus, type, name, opts, &local_err);
+ qobject_unref(opts);
+
+ if (local_err) {
+ error_propagate_prepend(errp, local_err,
+ "failed to create '%s' device '%s': ",
+ type, name);
+ }
+}
+
+static void xen_bus_type_enumerate(XenBus *xenbus, const char *type)
+{
+ char *domain_path = g_strdup_printf("backend/%s/%u", type, xen_domid);
+ char **backend;
+ unsigned int i, n;
+
+ trace_xen_bus_type_enumerate(type);
+
+ backend = xs_directory(xenbus->xsh, XBT_NULL, domain_path, &n);
+ if (!backend) {
+ goto out;
+ }
+
+ for (i = 0; i < n; i++) {
+ char *backend_path = g_strdup_printf("%s/%s", domain_path,
+ backend[i]);
+ enum xenbus_state backend_state;
+
+ if (xs_node_scanf(xenbus->xsh, XBT_NULL, backend_path, "state",
+ NULL, "%u", &backend_state) != 1)
+ backend_state = XenbusStateUnknown;
+
+ if (backend_state == XenbusStateInitialising) {
+ Error *local_err = NULL;
+
+ xen_bus_backend_create(xenbus, type, backend[i], backend_path,
+ &local_err);
+ if (local_err) {
+ error_report_err(local_err);
+ }
+ }
+
+ g_free(backend_path);
+ }
+
+ free(backend);
+
+out:
+ g_free(domain_path);
+}
+
+static void xen_bus_enumerate(void *opaque)
+{
+ XenBus *xenbus = opaque;
+ char **type;
+ unsigned int i, n;
+
+ trace_xen_bus_enumerate();
+
+ type = xs_directory(xenbus->xsh, XBT_NULL, "backend", &n);
+ if (!type) {
+ return;
+ }
+
+ for (i = 0; i < n; i++) {
+ xen_bus_type_enumerate(xenbus, type[i]);
+ }
+
+ free(type);
+}
+
+static void xen_bus_unrealize(BusState *bus, Error **errp)
+{
+ XenBus *xenbus = XEN_BUS(bus);
+
+ trace_xen_bus_unrealize();
+
+ if (xenbus->backend_watch) {
+ xen_bus_remove_watch(xenbus, xenbus->backend_watch, NULL);
+ xenbus->backend_watch = NULL;
+ }
+
+ if (!xenbus->xsh) {
+ return;
+ }
+
+ qemu_set_fd_handler(xs_fileno(xenbus->xsh), NULL, NULL, NULL);
+
+ xs_close(xenbus->xsh);
+}
+
+static void xen_bus_watch(void *opaque)
+{
+ XenBus *xenbus = opaque;
+ char **v;
+ const char *token;
+
+ g_assert(xenbus->xsh);
+
+ v = xs_check_watch(xenbus->xsh);
+ if (!v) {
+ return;
+ }
+
+ token = v[XS_WATCH_TOKEN];
+
+ trace_xen_bus_watch(token);
+
+ notifier_list_notify(&xenbus->watch_notifiers, (void *)token);
+
+ free(v);
+}
+
+static void xen_bus_realize(BusState *bus, Error **errp)
+{
+ XenBus *xenbus = XEN_BUS(bus);
+ unsigned int domid;
+ Error *local_err = NULL;
+
+ trace_xen_bus_realize();
+
+ xenbus->xsh = xs_open(0);
+ if (!xenbus->xsh) {
+ error_setg_errno(errp, errno, "failed xs_open");
+ goto fail;
+ }
+
+ if (xs_node_scanf(xenbus->xsh, XBT_NULL, "", /* domain root node */
+ "domid", NULL, "%u", &domid) == 1) {
+ xenbus->backend_id = domid;
+ } else {
+ xenbus->backend_id = 0; /* Assume lack of node means dom0 */
+ }
+
+ notifier_list_init(&xenbus->watch_notifiers);
+ qemu_set_fd_handler(xs_fileno(xenbus->xsh), xen_bus_watch, NULL,
+ xenbus);
+
+ module_call_init(MODULE_INIT_XEN_BACKEND);
+
+ xenbus->backend_watch =
+ xen_bus_add_watch(xenbus, "", /* domain root node */
+ "backend", xen_bus_enumerate, xenbus, &local_err);
+ if (local_err) {
+ /* This need not be treated as a hard error so don't propagate */
+ error_reportf_err(local_err,
+ "failed to set up enumeration watch: ");
+ }
+
+ return;
+
+fail:
+ xen_bus_unrealize(bus, &error_abort);
+}
+
+static void xen_bus_unplug_request(HotplugHandler *hotplug,
+ DeviceState *dev,
+ Error **errp)
+{
+ XenDevice *xendev = XEN_DEVICE(dev);
+
+ xen_device_unplug(xendev, errp);
+}
+
+static void xen_bus_class_init(ObjectClass *class, void *data)
+{
+ BusClass *bus_class = BUS_CLASS(class);
+ HotplugHandlerClass *hotplug_class = HOTPLUG_HANDLER_CLASS(class);
+
+ bus_class->print_dev = xen_bus_print_dev;
+ bus_class->get_dev_path = xen_bus_get_dev_path;
+ bus_class->realize = xen_bus_realize;
+ bus_class->unrealize = xen_bus_unrealize;
+
+ hotplug_class->unplug_request = xen_bus_unplug_request;
+}
+
+static const TypeInfo xen_bus_type_info = {
+ .name = TYPE_XEN_BUS,
+ .parent = TYPE_BUS,
+ .instance_size = sizeof(XenBus),
+ .class_size = sizeof(XenBusClass),
+ .class_init = xen_bus_class_init,
+ .interfaces = (InterfaceInfo[]) {
+ { TYPE_HOTPLUG_HANDLER },
+ { }
+ },
+};
+
+void xen_device_backend_printf(XenDevice *xendev, const char *key,
+ const char *fmt, ...)
+{
+ XenBus *xenbus = XEN_BUS(qdev_get_parent_bus(DEVICE(xendev)));
+ Error *local_err = NULL;
+ va_list ap;
+
+ g_assert(xenbus->xsh);
+
+ va_start(ap, fmt);
+ xs_node_vprintf(xenbus->xsh, XBT_NULL, xendev->backend_path, key,
+ &local_err, fmt, ap);
+ va_end(ap);
+
+ if (local_err) {
+ error_report_err(local_err);
+ }
+}
+
+static int xen_device_backend_scanf(XenDevice *xendev, const char *key,
+ const char *fmt, ...)
+{
+ XenBus *xenbus = XEN_BUS(qdev_get_parent_bus(DEVICE(xendev)));
+ va_list ap;
+ int rc;
+
+ g_assert(xenbus->xsh);
+
+ va_start(ap, fmt);
+ rc = xs_node_vscanf(xenbus->xsh, XBT_NULL, xendev->backend_path, key,
+ NULL, fmt, ap);
+ va_end(ap);
+
+ return rc;
+}
+
+void xen_device_backend_set_state(XenDevice *xendev,
+ enum xenbus_state state)
+{
+ const char *type = object_get_typename(OBJECT(xendev));
+
+ if (xendev->backend_state == state) {
+ return;
+ }
+
+ trace_xen_device_backend_state(type, xendev->name,
+ xs_strstate(state));
+
+ xendev->backend_state = state;
+ xen_device_backend_printf(xendev, "state", "%u", state);
+}
+
+enum xenbus_state xen_device_backend_get_state(XenDevice *xendev)
+{
+ return xendev->backend_state;
+}
+
+static void xen_device_backend_set_online(XenDevice *xendev, bool online)
+{
+ const char *type = object_get_typename(OBJECT(xendev));
+
+ if (xendev->backend_online == online) {
+ return;
+ }
+
+ trace_xen_device_backend_online(type, xendev->name, online);
+
+ xendev->backend_online = online;
+ xen_device_backend_printf(xendev, "online", "%u", online);
+}
+
+static void xen_device_backend_changed(void *opaque)
+{
+ XenDevice *xendev = opaque;
+ const char *type = object_get_typename(OBJECT(xendev));
+ enum xenbus_state state;
+ unsigned int online;
+
+ trace_xen_device_backend_changed(type, xendev->name);
+
+ if (xen_device_backend_scanf(xendev, "state", "%u", &state) != 1) {
+ state = XenbusStateUnknown;
+ }
+
+ xen_device_backend_set_state(xendev, state);
+
+ if (xen_device_backend_scanf(xendev, "online", "%u", &online) != 1) {
+ online = 0;
+ }
+
+ xen_device_backend_set_online(xendev, !!online);
+
+ /*
+ * If the toolstack (or unplug request callback) has set the backend
+ * state to Closing, but there is no active frontend (i.e. the
+ * state is not Connected) then set the backend state to Closed.
+ */
+ if (xendev->backend_state == XenbusStateClosing &&
+ xendev->frontend_state != XenbusStateConnected) {
+ xen_device_backend_set_state(xendev, XenbusStateClosed);
+ }
+
+ /*
+ * If a backend is still 'online' then its state should be cycled
+ * back round to InitWait in order for a new frontend instance to
+ * connect. This may happen when, for example, a frontend driver is
+ * re-installed or updated.
+ * If a backend is not 'online' then the device should be destroyed.
+ */
+ if (xendev->backend_online &&
+ xendev->backend_state == XenbusStateClosed) {
+ xen_device_backend_set_state(xendev, XenbusStateInitWait);
+ } else if (!xendev->backend_online &&
+ (xendev->backend_state == XenbusStateClosed ||
+ xendev->backend_state == XenbusStateInitialising ||
+ xendev->backend_state == XenbusStateInitWait ||
+ xendev->backend_state == XenbusStateUnknown)) {
+ Error *local_err = NULL;
+
+ if (!xen_backend_try_device_destroy(xendev, &local_err)) {
+ object_unparent(OBJECT(xendev));
+ }
+
+ if (local_err) {
+ error_report_err(local_err);
+ }
+ }
+}
+
+static void xen_device_backend_create(XenDevice *xendev, Error **errp)
+{
+ XenBus *xenbus = XEN_BUS(qdev_get_parent_bus(DEVICE(xendev)));
+ struct xs_permissions perms[2];
+ Error *local_err = NULL;
+
+ xendev->backend_path = xen_device_get_backend_path(xendev);
+
+ perms[0].id = xenbus->backend_id;
+ perms[0].perms = XS_PERM_NONE;
+ perms[1].id = xendev->frontend_id;
+ perms[1].perms = XS_PERM_READ;
+
+ g_assert(xenbus->xsh);
+
+ xs_node_create(xenbus->xsh, XBT_NULL, xendev->backend_path, perms,
+ ARRAY_SIZE(perms), &local_err);
+ if (local_err) {
+ error_propagate_prepend(errp, local_err,
+ "failed to create backend: ");
+ return;
+ }
+
+ xendev->backend_state_watch =
+ xen_bus_add_watch(xenbus, xendev->backend_path,
+ "state", xen_device_backend_changed,
+ xendev, &local_err);
+ if (local_err) {
+ error_propagate_prepend(errp, local_err,
+ "failed to watch backend state: ");
+ return;
+ }
+
+ xendev->backend_online_watch =
+ xen_bus_add_watch(xenbus, xendev->backend_path,
+ "online", xen_device_backend_changed,
+ xendev, &local_err);
+ if (local_err) {
+ error_propagate_prepend(errp, local_err,
+ "failed to watch backend online: ");
+ return;
+ }
+}
+
+static void xen_device_backend_destroy(XenDevice *xendev)
+{
+ XenBus *xenbus = XEN_BUS(qdev_get_parent_bus(DEVICE(xendev)));
+ Error *local_err = NULL;
+
+ if (xendev->backend_online_watch) {
+ xen_bus_remove_watch(xenbus, xendev->backend_online_watch, NULL);
+ xendev->backend_online_watch = NULL;
+ }
+
+ if (xendev->backend_state_watch) {
+ xen_bus_remove_watch(xenbus, xendev->backend_state_watch, NULL);
+ xendev->backend_state_watch = NULL;
+ }
+
+ if (!xendev->backend_path) {
+ return;
+ }
+
+ g_assert(xenbus->xsh);
+
+ xs_node_destroy(xenbus->xsh, XBT_NULL, xendev->backend_path,
+ &local_err);
+ g_free(xendev->backend_path);
+ xendev->backend_path = NULL;
+
+ if (local_err) {
+ error_report_err(local_err);
+ }
+}
+
+void xen_device_frontend_printf(XenDevice *xendev, const char *key,
+ const char *fmt, ...)
+{
+ XenBus *xenbus = XEN_BUS(qdev_get_parent_bus(DEVICE(xendev)));
+ Error *local_err = NULL;
+ va_list ap;
+
+ g_assert(xenbus->xsh);
+
+ va_start(ap, fmt);
+ xs_node_vprintf(xenbus->xsh, XBT_NULL, xendev->frontend_path, key,
+ &local_err, fmt, ap);
+ va_end(ap);
+
+ if (local_err) {
+ error_report_err(local_err);
+ }
+}
+
+int xen_device_frontend_scanf(XenDevice *xendev, const char *key,
+ const char *fmt, ...)
+{
+ XenBus *xenbus = XEN_BUS(qdev_get_parent_bus(DEVICE(xendev)));
+ va_list ap;
+ int rc;
+
+ g_assert(xenbus->xsh);
+
+ va_start(ap, fmt);
+ rc = xs_node_vscanf(xenbus->xsh, XBT_NULL, xendev->frontend_path, key,
+ NULL, fmt, ap);
+ va_end(ap);
+
+ return rc;
+}
+
+static void xen_device_frontend_set_state(XenDevice *xendev,
+ enum xenbus_state state)
+{
+ const char *type = object_get_typename(OBJECT(xendev));
+
+ if (xendev->frontend_state == state) {
+ return;
+ }
+
+ trace_xen_device_frontend_state(type, xendev->name,
+ xs_strstate(state));
+
+ xendev->frontend_state = state;
+ xen_device_frontend_printf(xendev, "state", "%u", state);
+}
+
+static void xen_device_frontend_changed(void *opaque)
+{
+ XenDevice *xendev = opaque;
+ XenDeviceClass *xendev_class = XEN_DEVICE_GET_CLASS(xendev);
+ const char *type = object_get_typename(OBJECT(xendev));
+ enum xenbus_state state;
+
+ trace_xen_device_frontend_changed(type, xendev->name);
+
+ if (xen_device_frontend_scanf(xendev, "state", "%u", &state) != 1) {
+ state = XenbusStateUnknown;
+ }
+
+ xen_device_frontend_set_state(xendev, state);
+
+ if (xendev_class->frontend_changed) {
+ Error *local_err = NULL;
+
+ xendev_class->frontend_changed(xendev, state, &local_err);
+
+ if (local_err) {
+ error_reportf_err(local_err, "frontend change error: ");
+ }
+ }
+}
+
+static void xen_device_frontend_create(XenDevice *xendev, Error **errp)
+{
+ XenBus *xenbus = XEN_BUS(qdev_get_parent_bus(DEVICE(xendev)));
+ struct xs_permissions perms[2];
+ Error *local_err = NULL;
+
+ xendev->frontend_path = xen_device_get_frontend_path(xendev);
+
+ perms[0].id = xendev->frontend_id;
+ perms[0].perms = XS_PERM_NONE;
+ perms[1].id = xenbus->backend_id;
+ perms[1].perms = XS_PERM_READ | XS_PERM_WRITE;
+
+ g_assert(xenbus->xsh);
+
+ xs_node_create(xenbus->xsh, XBT_NULL, xendev->frontend_path, perms,
+ ARRAY_SIZE(perms), &local_err);
+ if (local_err) {
+ error_propagate_prepend(errp, local_err,
+ "failed to create frontend: ");
+ return;
+ }
+
+ xendev->frontend_state_watch =
+ xen_bus_add_watch(xenbus, xendev->frontend_path, "state",
+ xen_device_frontend_changed, xendev, &local_err);
+ if (local_err) {
+ error_propagate_prepend(errp, local_err,
+ "failed to watch frontend state: ");
+ }
+}
+
+static void xen_device_frontend_destroy(XenDevice *xendev)
+{
+ XenBus *xenbus = XEN_BUS(qdev_get_parent_bus(DEVICE(xendev)));
+ Error *local_err = NULL;
+
+ if (xendev->frontend_state_watch) {
+ xen_bus_remove_watch(xenbus, xendev->frontend_state_watch, NULL);
+ xendev->frontend_state_watch = NULL;
+ }
+
+ if (!xendev->frontend_path) {
+ return;
+ }
+
+ g_assert(xenbus->xsh);
+
+ xs_node_destroy(xenbus->xsh, XBT_NULL, xendev->frontend_path,
+ &local_err);
+ g_free(xendev->frontend_path);
+ xendev->frontend_path = NULL;
+
+ if (local_err) {
+ error_report_err(local_err);
+ }
+}
+
+void xen_device_set_max_grant_refs(XenDevice *xendev, unsigned int nr_refs,
+ Error **errp)
+{
+ if (xengnttab_set_max_grants(xendev->xgth, nr_refs)) {
+ error_setg_errno(errp, errno, "xengnttab_set_max_grants failed");
+ }
+}
+
+void *xen_device_map_grant_refs(XenDevice *xendev, uint32_t *refs,
+ unsigned int nr_refs, int prot,
+ Error **errp)
+{
+ void *map = xengnttab_map_domain_grant_refs(xendev->xgth, nr_refs,
+ xendev->frontend_id, refs,
+ prot);
+
+ if (!map) {
+ error_setg_errno(errp, errno,
+ "xengnttab_map_domain_grant_refs failed");
+ }
+
+ return map;
+}
+
+void xen_device_unmap_grant_refs(XenDevice *xendev, void *map,
+ unsigned int nr_refs, Error **errp)
+{
+ if (xengnttab_unmap(xendev->xgth, map, nr_refs)) {
+ error_setg_errno(errp, errno, "xengnttab_unmap failed");
+ }
+}
+
+static void compat_copy_grant_refs(XenDevice *xendev, bool to_domain,
+ XenDeviceGrantCopySegment segs[],
+ unsigned int nr_segs, Error **errp)
+{
+ uint32_t *refs = g_new(uint32_t, nr_segs);
+ int prot = to_domain ? PROT_WRITE : PROT_READ;
+ void *map;
+ unsigned int i;
+
+ for (i = 0; i < nr_segs; i++) {
+ XenDeviceGrantCopySegment *seg = &segs[i];
+
+ refs[i] = to_domain ? seg->dest.foreign.ref :
+ seg->source.foreign.ref;
+ }
+
+ map = xengnttab_map_domain_grant_refs(xendev->xgth, nr_segs,
+ xendev->frontend_id, refs,
+ prot);
+ if (!map) {
+ error_setg_errno(errp, errno,
+ "xengnttab_map_domain_grant_refs failed");
+ goto done;
+ }
+
+ for (i = 0; i < nr_segs; i++) {
+ XenDeviceGrantCopySegment *seg = &segs[i];
+ void *page = map + (i * XC_PAGE_SIZE);
+
+ if (to_domain) {
+ memcpy(page + seg->dest.foreign.offset, seg->source.virt,
+ seg->len);
+ } else {
+ memcpy(seg->dest.virt, page + seg->source.foreign.offset,
+ seg->len);
+ }
+ }
+
+ if (xengnttab_unmap(xendev->xgth, map, nr_segs)) {
+ error_setg_errno(errp, errno, "xengnttab_unmap failed");
+ }
+
+done:
+ g_free(refs);
+}
+
+void xen_device_copy_grant_refs(XenDevice *xendev, bool to_domain,
+ XenDeviceGrantCopySegment segs[],
+ unsigned int nr_segs, Error **errp)
+{
+ xengnttab_grant_copy_segment_t *xengnttab_segs;
+ unsigned int i;
+
+ if (!xendev->feature_grant_copy) {
+ compat_copy_grant_refs(xendev, to_domain, segs, nr_segs, errp);
+ return;
+ }
+
+ xengnttab_segs = g_new0(xengnttab_grant_copy_segment_t, nr_segs);
+
+ for (i = 0; i < nr_segs; i++) {
+ XenDeviceGrantCopySegment *seg = &segs[i];
+ xengnttab_grant_copy_segment_t *xengnttab_seg = &xengnttab_segs[i];
+
+ if (to_domain) {
+ xengnttab_seg->flags = GNTCOPY_dest_gref;
+ xengnttab_seg->dest.foreign.domid = xendev->frontend_id;
+ xengnttab_seg->dest.foreign.ref = seg->dest.foreign.ref;
+ xengnttab_seg->dest.foreign.offset = seg->dest.foreign.offset;
+ xengnttab_seg->source.virt = seg->source.virt;
+ } else {
+ xengnttab_seg->flags = GNTCOPY_source_gref;
+ xengnttab_seg->source.foreign.domid = xendev->frontend_id;
+ xengnttab_seg->source.foreign.ref = seg->source.foreign.ref;
+ xengnttab_seg->source.foreign.offset =
+ seg->source.foreign.offset;
+ xengnttab_seg->dest.virt = seg->dest.virt;
+ }
+
+ xengnttab_seg->len = seg->len;
+ }
+
+ if (xengnttab_grant_copy(xendev->xgth, nr_segs, xengnttab_segs)) {
+ error_setg_errno(errp, errno, "xengnttab_grant_copy failed");
+ goto done;
+ }
+
+ for (i = 0; i < nr_segs; i++) {
+ xengnttab_grant_copy_segment_t *xengnttab_seg = &xengnttab_segs[i];
+
+ if (xengnttab_seg->status != GNTST_okay) {
+ error_setg(errp, "xengnttab_grant_copy seg[%u] failed", i);
+ break;
+ }
+ }
+
+done:
+ g_free(xengnttab_segs);
+}
+
+struct XenEventChannel {
+ evtchn_port_t local_port;
+ XenEventHandler handler;
+ void *opaque;
+ Notifier notifier;
+};
+
+static void event_notify(Notifier *n, void *data)
+{
+ XenEventChannel *channel = container_of(n, XenEventChannel, notifier);
+ unsigned long port = (unsigned long)data;
+
+ if (port == channel->local_port) {
+ channel->handler(channel->opaque);
+ }
+}
+
+XenEventChannel *xen_device_bind_event_channel(XenDevice *xendev,
+ unsigned int port,
+ XenEventHandler handler,
+ void *opaque, Error **errp)
+{
+ XenEventChannel *channel = g_new0(XenEventChannel, 1);
+ xenevtchn_port_or_error_t local_port;
+
+ local_port = xenevtchn_bind_interdomain(xendev->xeh,
+ xendev->frontend_id,
+ port);
+ if (local_port < 0) {
+ error_setg_errno(errp, errno, "xenevtchn_bind_interdomain failed");
+
+ g_free(channel);
+ return NULL;
+ }
+
+ channel->local_port = local_port;
+ channel->handler = handler;
+ channel->opaque = opaque;
+ channel->notifier.notify = event_notify;
+
+ notifier_list_add(&xendev->event_notifiers, &channel->notifier);
+
+ return channel;
+}
+
+void xen_device_notify_event_channel(XenDevice *xendev,
+ XenEventChannel *channel,
+ Error **errp)
+{
+ if (!channel) {
+ error_setg(errp, "bad channel");
+ return;
+ }
+
+ if (xenevtchn_notify(xendev->xeh, channel->local_port) < 0) {
+ error_setg_errno(errp, errno, "xenevtchn_notify failed");
+ }
+}
+
+void xen_device_unbind_event_channel(XenDevice *xendev,
+ XenEventChannel *channel,
+ Error **errp)
+{
+ if (!channel) {
+ error_setg(errp, "bad channel");
+ return;
+ }
+
+ notifier_remove(&channel->notifier);
+
+ if (xenevtchn_unbind(xendev->xeh, channel->local_port) < 0) {
+ error_setg_errno(errp, errno, "xenevtchn_unbind failed");
+ }
+
+ g_free(channel);
+}
+
+static void xen_device_unrealize(DeviceState *dev, Error **errp)
+{
+ XenDevice *xendev = XEN_DEVICE(dev);
+ XenDeviceClass *xendev_class = XEN_DEVICE_GET_CLASS(xendev);
+ const char *type = object_get_typename(OBJECT(xendev));
+
+ if (!xendev->name) {
+ return;
+ }
+
+ trace_xen_device_unrealize(type, xendev->name);
+
+ if (xendev->exit.notify) {
+ qemu_remove_exit_notifier(&xendev->exit);
+ xendev->exit.notify = NULL;
+ }
+
+ if (xendev_class->unrealize) {
+ xendev_class->unrealize(xendev, errp);
+ }
+
+ xen_device_frontend_destroy(xendev);
+ xen_device_backend_destroy(xendev);
+
+ if (xendev->xeh) {
+ qemu_set_fd_handler(xenevtchn_fd(xendev->xeh), NULL, NULL, NULL);
+ xenevtchn_close(xendev->xeh);
+ xendev->xeh = NULL;
+ }
+
+ if (xendev->xgth) {
+ xengnttab_close(xendev->xgth);
+ xendev->xgth = NULL;
+ }
+
+ g_free(xendev->name);
+ xendev->name = NULL;
+}
+
+static void xen_device_exit(Notifier *n, void *data)
+{
+ XenDevice *xendev = container_of(n, XenDevice, exit);
+
+ xen_device_unrealize(DEVICE(xendev), &error_abort);
+}
+
+static void xen_device_event(void *opaque)
+{
+ XenDevice *xendev = opaque;
+ unsigned long port = xenevtchn_pending(xendev->xeh);
+
+ notifier_list_notify(&xendev->event_notifiers, (void *)port);
+
+ xenevtchn_unmask(xendev->xeh, port);
+}
+
+static void xen_device_realize(DeviceState *dev, Error **errp)
+{
+ XenDevice *xendev = XEN_DEVICE(dev);
+ XenDeviceClass *xendev_class = XEN_DEVICE_GET_CLASS(xendev);
+ XenBus *xenbus = XEN_BUS(qdev_get_parent_bus(DEVICE(xendev)));
+ const char *type = object_get_typename(OBJECT(xendev));
+ Error *local_err = NULL;
+
+ if (xendev->frontend_id == DOMID_INVALID) {
+ xendev->frontend_id = xen_domid;
+ }
+
+ if (xendev->frontend_id >= DOMID_FIRST_RESERVED) {
+ error_setg(errp, "invalid frontend-id");
+ goto unrealize;
+ }
+
+ if (!xendev_class->get_name) {
+ error_setg(errp, "get_name method not implemented");
+ goto unrealize;
+ }
+
+ xendev->name = xendev_class->get_name(xendev, &local_err);
+ if (local_err) {
+ error_propagate_prepend(errp, local_err,
+ "failed to get device name: ");
+ goto unrealize;
+ }
+
+ trace_xen_device_realize(type, xendev->name);
+
+ xendev->xgth = xengnttab_open(NULL, 0);
+ if (!xendev->xgth) {
+ error_setg_errno(errp, errno, "failed xengnttab_open");
+ goto unrealize;
+ }
+
+ xendev->feature_grant_copy =
+ (xengnttab_grant_copy(xendev->xgth, 0, NULL) == 0);
+
+ xendev->xeh = xenevtchn_open(NULL, 0);
+ if (!xendev->xeh) {
+ error_setg_errno(errp, errno, "failed xenevtchn_open");
+ goto unrealize;
+ }
+
+ notifier_list_init(&xendev->event_notifiers);
+ qemu_set_fd_handler(xenevtchn_fd(xendev->xeh), xen_device_event, NULL,
+ xendev);
+
+ xen_device_backend_create(xendev, &local_err);
+ if (local_err) {
+ error_propagate(errp, local_err);
+ goto unrealize;
+ }
+
+ xen_device_frontend_create(xendev, &local_err);
+ if (local_err) {
+ error_propagate(errp, local_err);
+ goto unrealize;
+ }
+
+ if (xendev_class->realize) {
+ xendev_class->realize(xendev, &local_err);
+ if (local_err) {
+ error_propagate(errp, local_err);
+ goto unrealize;
+ }
+ }
+
+ xen_device_backend_printf(xendev, "frontend", "%s",
+ xendev->frontend_path);
+ xen_device_backend_printf(xendev, "frontend-id", "%u",
+ xendev->frontend_id);
+ xen_device_backend_printf(xendev, "hotplug-status", "connected");
+
+ xen_device_backend_set_online(xendev, true);
+ xen_device_backend_set_state(xendev, XenbusStateInitWait);
+
+ xen_device_frontend_printf(xendev, "backend", "%s",
+ xendev->backend_path);
+ xen_device_frontend_printf(xendev, "backend-id", "%u",
+ xenbus->backend_id);
+
+ xen_device_frontend_set_state(xendev, XenbusStateInitialising);
+
+ xendev->exit.notify = xen_device_exit;
+ qemu_add_exit_notifier(&xendev->exit);
+ return;
+
+unrealize:
+ xen_device_unrealize(dev, &error_abort);
+}
+
+static Property xen_device_props[] = {
+ DEFINE_PROP_UINT16("frontend-id", XenDevice, frontend_id,
+ DOMID_INVALID),
+ DEFINE_PROP_END_OF_LIST()
+};
+
+static void xen_device_class_init(ObjectClass *class, void *data)
+{
+ DeviceClass *dev_class = DEVICE_CLASS(class);
+
+ dev_class->realize = xen_device_realize;
+ dev_class->unrealize = xen_device_unrealize;
+ dev_class->props = xen_device_props;
+ dev_class->bus_type = TYPE_XEN_BUS;
+}
+
+static const TypeInfo xen_device_type_info = {
+ .name = TYPE_XEN_DEVICE,
+ .parent = TYPE_DEVICE,
+ .instance_size = sizeof(XenDevice),
+ .abstract = true,
+ .class_size = sizeof(XenDeviceClass),
+ .class_init = xen_device_class_init,
+};
+
+typedef struct XenBridge {
+ SysBusDevice busdev;
+} XenBridge;
+
+#define TYPE_XEN_BRIDGE "xen-bridge"
+
+static const TypeInfo xen_bridge_type_info = {
+ .name = TYPE_XEN_BRIDGE,
+ .parent = TYPE_SYS_BUS_DEVICE,
+ .instance_size = sizeof(XenBridge),
+};
+
+static void xen_register_types(void)
+{
+ type_register_static(&xen_bridge_type_info);
+ type_register_static(&xen_bus_type_info);
+ type_register_static(&xen_device_type_info);
+}
+
+type_init(xen_register_types)
+
+void xen_bus_init(void)
+{
+ DeviceState *dev = qdev_create(NULL, TYPE_XEN_BRIDGE);
+ BusState *bus = qbus_create(TYPE_XEN_BUS, dev, NULL);
+
+ qdev_init_nofail(dev);
+ qbus_set_bus_hotplug_handler(bus, &error_abort);
+}
diff --git a/hw/xen/xen-common.c b/hw/xen/xen-common.c
index 18a9045..0e9e58f 100644
--- a/hw/xen/xen-common.c
+++ b/hw/xen/xen-common.c
@@ -10,7 +10,7 @@
#include "qemu/osdep.h"
#include "qemu/error-report.h"
-#include "hw/xen/xen_backend.h"
+#include "hw/xen/xen-legacy-backend.h"
#include "chardev/char.h"
#include "sysemu/accel.h"
#include "migration/misc.h"
diff --git a/hw/xen/xen_backend.c b/hw/xen/xen-legacy-backend.c
index 0bc6b1d..36fd1e9 100644
--- a/hw/xen/xen_backend.c
+++ b/hw/xen/xen-legacy-backend.c
@@ -30,7 +30,7 @@
#include "hw/boards.h"
#include "qemu/log.h"
#include "qapi/error.h"
-#include "hw/xen/xen_backend.h"
+#include "hw/xen/xen-legacy-backend.h"
#include "hw/xen/xen_pvdev.h"
#include "monitor/qdev.h"
@@ -42,49 +42,54 @@ BusState *xen_sysbus;
/* ------------------------------------------------------------- */
/* public */
-struct xs_handle *xenstore = NULL;
+struct xs_handle *xenstore;
const char *xen_protocol;
/* private */
static bool xen_feature_grant_copy;
static int debug;
-int xenstore_write_be_str(struct XenDevice *xendev, const char *node, const char *val)
+int xenstore_write_be_str(struct XenLegacyDevice *xendev, const char *node,
+ const char *val)
{
return xenstore_write_str(xendev->be, node, val);
}
-int xenstore_write_be_int(struct XenDevice *xendev, const char *node, int ival)
+int xenstore_write_be_int(struct XenLegacyDevice *xendev, const char *node,
+ int ival)
{
return xenstore_write_int(xendev->be, node, ival);
}
-int xenstore_write_be_int64(struct XenDevice *xendev, const char *node, int64_t ival)
+int xenstore_write_be_int64(struct XenLegacyDevice *xendev, const char *node,
+ int64_t ival)
{
return xenstore_write_int64(xendev->be, node, ival);
}
-char *xenstore_read_be_str(struct XenDevice *xendev, const char *node)
+char *xenstore_read_be_str(struct XenLegacyDevice *xendev, const char *node)
{
return xenstore_read_str(xendev->be, node);
}
-int xenstore_read_be_int(struct XenDevice *xendev, const char *node, int *ival)
+int xenstore_read_be_int(struct XenLegacyDevice *xendev, const char *node,
+ int *ival)
{
return xenstore_read_int(xendev->be, node, ival);
}
-char *xenstore_read_fe_str(struct XenDevice *xendev, const char *node)
+char *xenstore_read_fe_str(struct XenLegacyDevice *xendev, const char *node)
{
return xenstore_read_str(xendev->fe, node);
}
-int xenstore_read_fe_int(struct XenDevice *xendev, const char *node, int *ival)
+int xenstore_read_fe_int(struct XenLegacyDevice *xendev, const char *node,
+ int *ival)
{
return xenstore_read_int(xendev->fe, node, ival);
}
-int xenstore_read_fe_uint64(struct XenDevice *xendev, const char *node,
+int xenstore_read_fe_uint64(struct XenLegacyDevice *xendev, const char *node,
uint64_t *uval)
{
return xenstore_read_uint64(xendev->fe, node, uval);
@@ -92,7 +97,7 @@ int xenstore_read_fe_uint64(struct XenDevice *xendev, const char *node,
/* ------------------------------------------------------------- */
-int xen_be_set_state(struct XenDevice *xendev, enum xenbus_state state)
+int xen_be_set_state(struct XenLegacyDevice *xendev, enum xenbus_state state)
{
int rc;
@@ -106,7 +111,7 @@ int xen_be_set_state(struct XenDevice *xendev, enum xenbus_state state)
return 0;
}
-void xen_be_set_max_grant_refs(struct XenDevice *xendev,
+void xen_be_set_max_grant_refs(struct XenLegacyDevice *xendev,
unsigned int nr_refs)
{
assert(xendev->ops->flags & DEVOPS_FLAG_NEED_GNTDEV);
@@ -117,7 +122,7 @@ void xen_be_set_max_grant_refs(struct XenDevice *xendev,
}
}
-void *xen_be_map_grant_refs(struct XenDevice *xendev, uint32_t *refs,
+void *xen_be_map_grant_refs(struct XenLegacyDevice *xendev, uint32_t *refs,
unsigned int nr_refs, int prot)
{
void *ptr;
@@ -135,7 +140,7 @@ void *xen_be_map_grant_refs(struct XenDevice *xendev, uint32_t *refs,
return ptr;
}
-void xen_be_unmap_grant_refs(struct XenDevice *xendev, void *ptr,
+void xen_be_unmap_grant_refs(struct XenLegacyDevice *xendev, void *ptr,
unsigned int nr_refs)
{
assert(xendev->ops->flags & DEVOPS_FLAG_NEED_GNTDEV);
@@ -146,7 +151,7 @@ void xen_be_unmap_grant_refs(struct XenDevice *xendev, void *ptr,
}
}
-static int compat_copy_grant_refs(struct XenDevice *xendev,
+static int compat_copy_grant_refs(struct XenLegacyDevice *xendev,
bool to_domain,
XenGrantCopySegment segs[],
unsigned int nr_segs)
@@ -195,7 +200,7 @@ static int compat_copy_grant_refs(struct XenDevice *xendev,
return 0;
}
-int xen_be_copy_grant_refs(struct XenDevice *xendev,
+int xen_be_copy_grant_refs(struct XenLegacyDevice *xendev,
bool to_domain,
XenGrantCopySegment segs[],
unsigned int nr_segs)
@@ -259,10 +264,11 @@ int xen_be_copy_grant_refs(struct XenDevice *xendev,
/*
* get xen backend device, allocate a new one if it doesn't exist.
*/
-static struct XenDevice *xen_be_get_xendev(const char *type, int dom, int dev,
- struct XenDevOps *ops)
+static struct XenLegacyDevice *xen_be_get_xendev(const char *type, int dom,
+ int dev,
+ struct XenDevOps *ops)
{
- struct XenDevice *xendev;
+ struct XenLegacyDevice *xendev;
xendev = xen_pv_find_xendev(type, dom, dev);
if (xendev) {
@@ -314,7 +320,8 @@ static struct XenDevice *xen_be_get_xendev(const char *type, int dom, int dev,
* Node specifies the changed field. node = NULL means
* update all fields (used for initialization).
*/
-static void xen_be_backend_changed(struct XenDevice *xendev, const char *node)
+static void xen_be_backend_changed(struct XenLegacyDevice *xendev,
+ const char *node)
{
if (node == NULL || strcmp(node, "online") == 0) {
if (xenstore_read_be_int(xendev, "online", &xendev->online) == -1) {
@@ -330,7 +337,8 @@ static void xen_be_backend_changed(struct XenDevice *xendev, const char *node)
}
}
-static void xen_be_frontend_changed(struct XenDevice *xendev, const char *node)
+static void xen_be_frontend_changed(struct XenLegacyDevice *xendev,
+ const char *node)
{
int fe_state;
@@ -373,7 +381,7 @@ static void xen_be_frontend_changed(struct XenDevice *xendev, const char *node)
* only affects the xendev->be_state variable as xenbus should
* already be put into that state by xend.
*/
-static int xen_be_try_setup(struct XenDevice *xendev)
+static int xen_be_try_setup(struct XenLegacyDevice *xendev)
{
char token[XEN_BUFSIZE];
int be_state;
@@ -417,7 +425,7 @@ static int xen_be_try_setup(struct XenDevice *xendev)
*
* Goes to InitWait on success.
*/
-static int xen_be_try_init(struct XenDevice *xendev)
+static int xen_be_try_init(struct XenLegacyDevice *xendev)
{
int rc = 0;
@@ -446,7 +454,7 @@ static int xen_be_try_init(struct XenDevice *xendev)
*
* Goes to Connected on success.
*/
-static int xen_be_try_initialise(struct XenDevice *xendev)
+static int xen_be_try_initialise(struct XenLegacyDevice *xendev)
{
int rc = 0;
@@ -487,7 +495,7 @@ static int xen_be_try_initialise(struct XenDevice *xendev)
* frontend being Connected. Note that this may be called more
* than once since the backend state is not modified.
*/
-static void xen_be_try_connected(struct XenDevice *xendev)
+static void xen_be_try_connected(struct XenLegacyDevice *xendev)
{
if (!xendev->ops->connected) {
return;
@@ -510,7 +518,8 @@ static void xen_be_try_connected(struct XenDevice *xendev)
*
* Goes to Closed when done.
*/
-static void xen_be_disconnect(struct XenDevice *xendev, enum xenbus_state state)
+static void xen_be_disconnect(struct XenLegacyDevice *xendev,
+ enum xenbus_state state)
{
if (xendev->be_state != XenbusStateClosing &&
xendev->be_state != XenbusStateClosed &&
@@ -529,7 +538,7 @@ static void xen_be_disconnect(struct XenDevice *xendev, enum xenbus_state state)
/*
* Try to reset xendev, for reconnection by another frontend instance.
*/
-static int xen_be_try_reset(struct XenDevice *xendev)
+static int xen_be_try_reset(struct XenLegacyDevice *xendev)
{
if (xendev->fe_state != XenbusStateInitialising) {
return -1;
@@ -543,7 +552,7 @@ static int xen_be_try_reset(struct XenDevice *xendev)
/*
* state change dispatcher function
*/
-void xen_be_check_state(struct XenDevice *xendev)
+void xen_be_check_state(struct XenLegacyDevice *xendev)
{
int rc = 0;
@@ -587,7 +596,7 @@ void xen_be_check_state(struct XenDevice *xendev)
static int xenstore_scan(const char *type, int dom, struct XenDevOps *ops)
{
- struct XenDevice *xendev;
+ struct XenLegacyDevice *xendev;
char path[XEN_BUFSIZE], token[XEN_BUFSIZE];
char **dev = NULL;
unsigned int cdev, j;
@@ -620,7 +629,7 @@ static int xenstore_scan(const char *type, int dom, struct XenDevOps *ops)
void xenstore_update_be(char *watch, char *type, int dom,
struct XenDevOps *ops)
{
- struct XenDevice *xendev;
+ struct XenLegacyDevice *xendev;
char path[XEN_BUFSIZE], *bepath;
unsigned int len, dev;
@@ -628,9 +637,9 @@ void xenstore_update_be(char *watch, char *type, int dom,
if (strncmp(path, watch, len) != 0) {
return;
}
- if (sscanf(watch+len, "/%u/%255s", &dev, path) != 2) {
+ if (sscanf(watch + len, "/%u/%255s", &dev, path) != 2) {
strcpy(path, "");
- if (sscanf(watch+len, "/%u", &dev) != 1) {
+ if (sscanf(watch + len, "/%u", &dev) != 1) {
dev = -1;
}
}
@@ -651,7 +660,7 @@ void xenstore_update_be(char *watch, char *type, int dom,
}
}
-void xenstore_update_fe(char *watch, struct XenDevice *xendev)
+void xenstore_update_fe(char *watch, struct XenLegacyDevice *xendev)
{
char *node;
unsigned int len;
@@ -744,7 +753,6 @@ void xen_be_register_common(void)
xen_be_register("console", &xen_console_ops);
xen_be_register("vkbd", &xen_kbdmouse_ops);
- xen_be_register("qdisk", &xen_blkdev_ops);
#ifdef CONFIG_VIRTFS
xen_be_register("9pfs", &xen_9pfs_ops);
#endif
@@ -753,7 +761,7 @@ void xen_be_register_common(void)
#endif
}
-int xen_be_bind_evtchn(struct XenDevice *xendev)
+int xen_be_bind_evtchn(struct XenLegacyDevice *xendev)
{
if (xendev->local_port != -1) {
return 0;
@@ -789,7 +797,7 @@ static const TypeInfo xendev_type_info = {
.name = TYPE_XENBACKEND,
.parent = TYPE_XENSYSDEV,
.class_init = xendev_class_init,
- .instance_size = sizeof(struct XenDevice),
+ .instance_size = sizeof(struct XenLegacyDevice),
};
static void xen_sysbus_class_init(ObjectClass *klass, void *data)
diff --git a/hw/xen/xen_devconfig.c b/hw/xen/xen_devconfig.c
index 3500d88..315dbc9 100644
--- a/hw/xen/xen_devconfig.c
+++ b/hw/xen/xen_devconfig.c
@@ -1,5 +1,5 @@
#include "qemu/osdep.h"
-#include "hw/xen/xen_backend.h"
+#include "hw/xen/xen-legacy-backend.h"
#include "qemu/option.h"
#include "sysemu/blockdev.h"
diff --git a/hw/xen/xen_pt.c b/hw/xen/xen_pt.c
index f1f3a37..5539d56 100644
--- a/hw/xen/xen_pt.c
+++ b/hw/xen/xen_pt.c
@@ -59,7 +59,7 @@
#include "hw/pci/pci.h"
#include "hw/xen/xen.h"
#include "hw/i386/pc.h"
-#include "hw/xen/xen_backend.h"
+#include "hw/xen/xen-legacy-backend.h"
#include "xen_pt.h"
#include "qemu/range.h"
#include "exec/address-spaces.h"
@@ -847,6 +847,12 @@ static void xen_pt_realize(PCIDevice *d, Error **errp)
}
machine_irq = s->real_device.irq;
+ if (machine_irq == 0) {
+ XEN_PT_LOG(d, "machine irq is 0\n");
+ cmd |= PCI_COMMAND_INTX_DISABLE;
+ goto out;
+ }
+
rc = xc_physdev_map_pirq(xen_xc, xen_domid, machine_irq, &pirq);
if (rc < 0) {
error_setg_errno(errp, errno, "Mapping machine irq %u to"
diff --git a/hw/xen/xen_pt_config_init.c b/hw/xen/xen_pt_config_init.c
index 47f9010..31ec5ad 100644
--- a/hw/xen/xen_pt_config_init.c
+++ b/hw/xen/xen_pt_config_init.c
@@ -15,7 +15,7 @@
#include "qemu/osdep.h"
#include "qapi/error.h"
#include "qemu/timer.h"
-#include "hw/xen/xen_backend.h"
+#include "hw/xen/xen-legacy-backend.h"
#include "xen_pt.h"
#define XEN_PT_MERGE_VALUE(value, data, val_mask) \
@@ -300,7 +300,9 @@ static int xen_pt_irqpin_reg_init(XenPCIPassthroughState *s,
XenPTRegInfo *reg, uint32_t real_offset,
uint32_t *data)
{
- *data = xen_pt_pci_read_intx(s);
+ if (s->real_device.irq) {
+ *data = xen_pt_pci_read_intx(s);
+ }
return 0;
}
diff --git a/hw/xen/xen_pt_graphics.c b/hw/xen/xen_pt_graphics.c
index 135c8df..b697327 100644
--- a/hw/xen/xen_pt_graphics.c
+++ b/hw/xen/xen_pt_graphics.c
@@ -5,7 +5,7 @@
#include "qapi/error.h"
#include "xen_pt.h"
#include "xen-host-pci-device.h"
-#include "hw/xen/xen_backend.h"
+#include "hw/xen/xen-legacy-backend.h"
static unsigned long igd_guest_opregion;
static unsigned long igd_host_opregion;
@@ -185,8 +185,19 @@ void xen_pt_setup_vga(XenPCIPassthroughState *s, XenHostPCIDevice *dev,
return;
}
+ if (bios_size < sizeof(struct rom_header)) {
+ error_setg(errp, "VGA: VBIOS image corrupt (too small)");
+ return;
+ }
+
/* Currently we fixed this address as a primary. */
rom = (struct rom_header *)bios;
+
+ if (rom->pcioffset + sizeof(struct pci_data) > bios_size) {
+ error_setg(errp, "VGA: VBIOS image corrupt (bad pcioffset field)");
+ return;
+ }
+
pd = (void *)(bios + (unsigned char)rom->pcioffset);
/* We may need to fixup Device Identification. */
@@ -194,6 +205,11 @@ void xen_pt_setup_vga(XenPCIPassthroughState *s, XenHostPCIDevice *dev,
pd->device = s->real_device.device_id;
len = rom->size * 512;
+ if (len > bios_size) {
+ error_setg(errp, "VGA: VBIOS image corrupt (bad size field)");
+ return;
+ }
+
/* Then adjust the bios checksum */
for (c = (char *)bios; c < ((char *)bios + len); c++) {
checksum += *c;
diff --git a/hw/xen/xen_pt_msi.c b/hw/xen/xen_pt_msi.c
index cc514f9..fb4b887 100644
--- a/hw/xen/xen_pt_msi.c
+++ b/hw/xen/xen_pt_msi.c
@@ -11,7 +11,7 @@
#include "qemu/osdep.h"
-#include "hw/xen/xen_backend.h"
+#include "hw/xen/xen-legacy-backend.h"
#include "xen_pt.h"
#include "hw/i386/apic-msidef.h"
diff --git a/hw/xen/xen_pvdev.c b/hw/xen/xen_pvdev.c
index f026556..6ef09cb 100644
--- a/hw/xen/xen_pvdev.c
+++ b/hw/xen/xen_pvdev.c
@@ -20,7 +20,7 @@
#include "qemu/osdep.h"
#include "qemu/log.h"
#include "hw/qdev-core.h"
-#include "hw/xen/xen_backend.h"
+#include "hw/xen/xen-legacy-backend.h"
#include "hw/xen/xen_pvdev.h"
/* private */
@@ -34,7 +34,7 @@ struct xs_dirs {
static QTAILQ_HEAD(, xs_dirs) xs_cleanup =
QTAILQ_HEAD_INITIALIZER(xs_cleanup);
-static QTAILQ_HEAD(, XenDevice) xendevs =
+static QTAILQ_HEAD(, XenLegacyDevice) xendevs =
QTAILQ_HEAD_INITIALIZER(xendevs);
/* ------------------------------------------------------------- */
@@ -195,7 +195,7 @@ const char *xenbus_strstate(enum xenbus_state state)
* 2 == noisy debug messages (logfile only).
* 3 == will flood your log (logfile only).
*/
-void xen_pv_printf(struct XenDevice *xendev, int msg_level,
+void xen_pv_printf(struct XenLegacyDevice *xendev, int msg_level,
const char *fmt, ...)
{
va_list args;
@@ -230,7 +230,7 @@ void xen_pv_printf(struct XenDevice *xendev, int msg_level,
void xen_pv_evtchn_event(void *opaque)
{
- struct XenDevice *xendev = opaque;
+ struct XenLegacyDevice *xendev = opaque;
evtchn_port_t port;
port = xenevtchn_pending(xendev->evtchndev);
@@ -247,7 +247,7 @@ void xen_pv_evtchn_event(void *opaque)
}
}
-void xen_pv_unbind_evtchn(struct XenDevice *xendev)
+void xen_pv_unbind_evtchn(struct XenLegacyDevice *xendev)
{
if (xendev->local_port == -1) {
return;
@@ -258,16 +258,16 @@ void xen_pv_unbind_evtchn(struct XenDevice *xendev)
xendev->local_port = -1;
}
-int xen_pv_send_notify(struct XenDevice *xendev)
+int xen_pv_send_notify(struct XenLegacyDevice *xendev)
{
return xenevtchn_notify(xendev->evtchndev, xendev->local_port);
}
/* ------------------------------------------------------------- */
-struct XenDevice *xen_pv_find_xendev(const char *type, int dom, int dev)
+struct XenLegacyDevice *xen_pv_find_xendev(const char *type, int dom, int dev)
{
- struct XenDevice *xendev;
+ struct XenLegacyDevice *xendev;
QTAILQ_FOREACH(xendev, &xendevs, next) {
if (xendev->dom != dom) {
@@ -287,7 +287,7 @@ struct XenDevice *xen_pv_find_xendev(const char *type, int dom, int dev)
/*
* release xen backend device.
*/
-void xen_pv_del_xendev(struct XenDevice *xendev)
+void xen_pv_del_xendev(struct XenLegacyDevice *xendev)
{
if (xendev->ops->free) {
xendev->ops->free(xendev);
@@ -312,7 +312,7 @@ void xen_pv_del_xendev(struct XenDevice *xendev)
qdev_unplug(&xendev->qdev, NULL);
}
-void xen_pv_insert_xendev(struct XenDevice *xendev)
+void xen_pv_insert_xendev(struct XenLegacyDevice *xendev)
{
QTAILQ_INSERT_TAIL(&xendevs, xendev, next);
}
diff --git a/hw/xenpv/Makefile.objs b/hw/xenpv/Makefile.objs
index bbf5873..8bfa458 100644
--- a/hw/xenpv/Makefile.objs
+++ b/hw/xenpv/Makefile.objs
@@ -1,4 +1,2 @@
# Xen PV machine support
obj-$(CONFIG_XEN) += xen_machine_pv.o
-# Xen PV machine builder support
-obj-$(CONFIG_XEN_PV_DOMAIN_BUILD) += xen_domainbuild.o
diff --git a/hw/xenpv/xen_domainbuild.c b/hw/xenpv/xen_domainbuild.c
deleted file mode 100644
index 2859280..0000000
--- a/hw/xenpv/xen_domainbuild.c
+++ /dev/null
@@ -1,299 +0,0 @@
-#include "qemu/osdep.h"
-#include "qemu/units.h"
-#include "hw/xen/xen_backend.h"
-#include "xen_domainbuild.h"
-#include "qemu/timer.h"
-#include "qemu/log.h"
-
-#include <xenguest.h>
-
-static int xenstore_domain_mkdir(char *path)
-{
- struct xs_permissions perms_ro[] = {{
- .id = 0, /* set owner: dom0 */
- },{
- .id = xen_domid,
- .perms = XS_PERM_READ,
- }};
- struct xs_permissions perms_rw[] = {{
- .id = 0, /* set owner: dom0 */
- },{
- .id = xen_domid,
- .perms = XS_PERM_READ | XS_PERM_WRITE,
- }};
- const char *writable[] = { "device", "control", "error", NULL };
- char subpath[256];
- int i;
-
- if (!xs_mkdir(xenstore, 0, path)) {
- fprintf(stderr, "%s: xs_mkdir %s: failed\n", __func__, path);
- return -1;
- }
- if (!xs_set_permissions(xenstore, 0, path, perms_ro, 2)) {
- fprintf(stderr, "%s: xs_set_permissions failed\n", __func__);
- return -1;
- }
-
- for (i = 0; writable[i]; i++) {
- snprintf(subpath, sizeof(subpath), "%s/%s", path, writable[i]);
- if (!xs_mkdir(xenstore, 0, subpath)) {
- fprintf(stderr, "%s: xs_mkdir %s: failed\n", __func__, subpath);
- return -1;
- }
- if (!xs_set_permissions(xenstore, 0, subpath, perms_rw, 2)) {
- fprintf(stderr, "%s: xs_set_permissions failed\n", __func__);
- return -1;
- }
- }
- return 0;
-}
-
-int xenstore_domain_init1(const char *kernel, const char *ramdisk,
- const char *cmdline)
-{
- char *dom, uuid_string[42], vm[256], path[256];
- int i;
-
- qemu_uuid_unparse(&qemu_uuid, uuid_string);
- dom = xs_get_domain_path(xenstore, xen_domid);
- snprintf(vm, sizeof(vm), "/vm/%s", uuid_string);
-
- xenstore_domain_mkdir(dom);
-
- xenstore_write_str(vm, "image/ostype", "linux");
- if (kernel)
- xenstore_write_str(vm, "image/kernel", kernel);
- if (ramdisk)
- xenstore_write_str(vm, "image/ramdisk", ramdisk);
- if (cmdline)
- xenstore_write_str(vm, "image/cmdline", cmdline);
-
- /* name + id */
- xenstore_write_str(vm, "name", qemu_name ? qemu_name : "no-name");
- xenstore_write_str(vm, "uuid", uuid_string);
- xenstore_write_str(dom, "name", qemu_name ? qemu_name : "no-name");
- xenstore_write_int(dom, "domid", xen_domid);
- xenstore_write_str(dom, "vm", vm);
-
- /* memory */
- xenstore_write_int(dom, "memory/target", ram_size / KiB);
- xenstore_write_int(vm, "memory", ram_size / MiB);
- xenstore_write_int(vm, "maxmem", ram_size / MiB);
-
- /* cpus */
- for (i = 0; i < smp_cpus; i++) {
- snprintf(path, sizeof(path), "cpu/%d/availability",i);
- xenstore_write_str(dom, path, "online");
- }
- xenstore_write_int(vm, "vcpu_avail", smp_cpus);
- xenstore_write_int(vm, "vcpus", smp_cpus);
-
- /* vnc password */
- xenstore_write_str(vm, "vncpassword", "" /* FIXME */);
-
- free(dom);
- return 0;
-}
-
-int xenstore_domain_init2(int xenstore_port, int xenstore_mfn,
- int console_port, int console_mfn)
-{
- char *dom;
-
- dom = xs_get_domain_path(xenstore, xen_domid);
-
- /* signal new domain */
- xs_introduce_domain(xenstore,
- xen_domid,
- xenstore_mfn,
- xenstore_port);
-
- /* xenstore */
- xenstore_write_int(dom, "store/ring-ref", xenstore_mfn);
- xenstore_write_int(dom, "store/port", xenstore_port);
-
- /* console */
- xenstore_write_str(dom, "console/type", "ioemu");
- xenstore_write_int(dom, "console/limit", 128 * KiB);
- xenstore_write_int(dom, "console/ring-ref", console_mfn);
- xenstore_write_int(dom, "console/port", console_port);
- xen_config_dev_console(0);
-
- free(dom);
- return 0;
-}
-
-/* ------------------------------------------------------------- */
-
-static QEMUTimer *xen_poll;
-
-/* check domain state once per second */
-static void xen_domain_poll(void *opaque)
-{
- struct xc_dominfo info;
- int rc;
-
- rc = xc_domain_getinfo(xen_xc, xen_domid, 1, &info);
- if ((rc != 1) || (info.domid != xen_domid)) {
- qemu_log("xen: domain %d is gone\n", xen_domid);
- goto quit;
- }
- if (info.dying) {
- qemu_log("xen: domain %d is dying (%s%s)\n", xen_domid,
- info.crashed ? "crashed" : "",
- info.shutdown ? "shutdown" : "");
- goto quit;
- }
-
- timer_mod(xen_poll, qemu_clock_get_ms(QEMU_CLOCK_REALTIME) + 1000);
- return;
-
-quit:
- qemu_system_shutdown_request(SHUTDOWN_CAUSE_GUEST_SHUTDOWN);
-}
-
-static int xen_domain_watcher(void)
-{
- int qemu_running = 1;
- int fd[2], i, n, rc;
- char byte;
-
- if (pipe(fd) != 0) {
- qemu_log("%s: Huh? pipe error: %s\n", __func__, strerror(errno));
- return -1;
- }
- if (fork() != 0)
- return 0; /* not child */
-
- /* close all file handles, except stdio/out/err,
- * our watch pipe and the xen interface handle */
- n = getdtablesize();
- for (i = 3; i < n; i++) {
- if (i == fd[0])
- continue;
- close(i);
- }
-
- /*
- * Reopen xc interface, since the original is unsafe after fork
- * and was closed above.
- */
- xen_xc = xc_interface_open(0, 0, 0);
-
- /* ignore term signals */
- signal(SIGINT, SIG_IGN);
- signal(SIGTERM, SIG_IGN);
-
- /* wait for qemu exiting */
- while (qemu_running) {
- rc = read(fd[0], &byte, 1);
- switch (rc) {
- case -1:
- if (errno == EINTR)
- continue;
- qemu_log("%s: Huh? read error: %s\n", __func__, strerror(errno));
- qemu_running = 0;
- break;
- case 0:
- /* EOF -> qemu exited */
- qemu_running = 0;
- break;
- default:
- qemu_log("%s: Huh? data on the watch pipe?\n", __func__);
- break;
- }
- }
-
- /* cleanup */
- qemu_log("%s: destroy domain %d\n", __func__, xen_domid);
- xc_domain_destroy(xen_xc, xen_domid);
- _exit(0);
-}
-
-/* normal cleanup */
-static void xen_domain_cleanup(void)
-{
- char *dom;
-
- dom = xs_get_domain_path(xenstore, xen_domid);
- if (dom) {
- xs_rm(xenstore, 0, dom);
- free(dom);
- }
- xs_release_domain(xenstore, xen_domid);
-}
-
-int xen_domain_build_pv(const char *kernel, const char *ramdisk,
- const char *cmdline)
-{
- uint32_t ssidref = 0;
- uint32_t flags = 0;
- xen_domain_handle_t uuid;
- unsigned int xenstore_port = 0, console_port = 0;
- unsigned long xenstore_mfn = 0, console_mfn = 0;
- int rc;
-
- memcpy(uuid, &qemu_uuid, sizeof(uuid));
- rc = xen_domain_create(xen_xc, ssidref, uuid, flags, &xen_domid);
- if (rc < 0) {
- fprintf(stderr, "xen: xc_domain_create() failed\n");
- goto err;
- }
- qemu_log("xen: created domain %d\n", xen_domid);
- atexit(xen_domain_cleanup);
- if (xen_domain_watcher() == -1) {
- goto err;
- }
-
- xenstore_domain_init1(kernel, ramdisk, cmdline);
-
- rc = xc_domain_max_vcpus(xen_xc, xen_domid, smp_cpus);
- if (rc < 0) {
- fprintf(stderr, "xen: xc_domain_max_vcpus() failed\n");
- goto err;
- }
-
-#if 0
- rc = xc_domain_setcpuweight(xen_xc, xen_domid, 256);
- if (rc < 0) {
- fprintf(stderr, "xen: xc_domain_setcpuweight() failed\n");
- goto err;
- }
-#endif
-
- rc = xc_domain_setmaxmem(xen_xc, xen_domid, ram_size / KiB);
- if (rc < 0) {
- fprintf(stderr, "xen: xc_domain_setmaxmem() failed\n");
- goto err;
- }
-
- xenstore_port = xc_evtchn_alloc_unbound(xen_xc, xen_domid, 0);
- console_port = xc_evtchn_alloc_unbound(xen_xc, xen_domid, 0);
-
- rc = xc_linux_build(xen_xc, xen_domid, ram_size / MiB,
- kernel, ramdisk, cmdline,
- 0, flags,
- xenstore_port, &xenstore_mfn,
- console_port, &console_mfn);
- if (rc < 0) {
- fprintf(stderr, "xen: xc_linux_build() failed\n");
- goto err;
- }
-
- xenstore_domain_init2(xenstore_port, xenstore_mfn,
- console_port, console_mfn);
-
- qemu_log("xen: unpausing domain %d\n", xen_domid);
- rc = xc_domain_unpause(xen_xc, xen_domid);
- if (rc < 0) {
- fprintf(stderr, "xen: xc_domain_unpause() failed\n");
- goto err;
- }
-
- xen_poll = timer_new_ms(QEMU_CLOCK_REALTIME, xen_domain_poll, NULL);
- timer_mod(xen_poll, qemu_clock_get_ms(QEMU_CLOCK_REALTIME) + 1000);
- return 0;
-
-err:
- return -1;
-}
diff --git a/hw/xenpv/xen_domainbuild.h b/hw/xenpv/xen_domainbuild.h
deleted file mode 100644
index 652d9b4..0000000
--- a/hw/xenpv/xen_domainbuild.h
+++ /dev/null
@@ -1,13 +0,0 @@
-#ifndef QEMU_HW_XEN_DOMAINBUILD_H
-#define QEMU_HW_XEN_DOMAINBUILD_H
-
-#include "hw/xen/xen_common.h"
-
-int xenstore_domain_init1(const char *kernel, const char *ramdisk,
- const char *cmdline);
-int xenstore_domain_init2(int xenstore_port, int xenstore_mfn,
- int console_port, int console_mfn);
-int xen_domain_build_pv(const char *kernel, const char *ramdisk,
- const char *cmdline);
-
-#endif /* QEMU_HW_XEN_DOMAINBUILD_H */
diff --git a/hw/xenpv/xen_machine_pv.c b/hw/xenpv/xen_machine_pv.c
index 44d67b8..dcaf2a0 100644
--- a/hw/xenpv/xen_machine_pv.c
+++ b/hw/xenpv/xen_machine_pv.c
@@ -26,8 +26,8 @@
#include "qemu/error-report.h"
#include "hw/hw.h"
#include "hw/boards.h"
-#include "hw/xen/xen_backend.h"
-#include "xen_domainbuild.h"
+#include "hw/xen/xen-legacy-backend.h"
+#include "hw/xen/xen-bus.h"
#include "sysemu/block-backend.h"
static void xen_init_pv(MachineState *machine)
@@ -43,21 +43,8 @@ static void xen_init_pv(MachineState *machine)
switch (xen_mode) {
case XEN_ATTACH:
- /* nothing to do, xend handles everything */
+ /* nothing to do, libxl handles everything */
break;
-#ifdef CONFIG_XEN_PV_DOMAIN_BUILD
- case XEN_CREATE: {
- const char *kernel_filename = machine->kernel_filename;
- const char *kernel_cmdline = machine->kernel_cmdline;
- const char *initrd_filename = machine->initrd_filename;
- if (xen_domain_build_pv(kernel_filename, initrd_filename,
- kernel_cmdline) < 0) {
- error_report("xen pv domain creation failed");
- exit(1);
- }
- break;
- }
-#endif
case XEN_EMULATE:
error_report("xen emulation not implemented (yet)");
exit(1);
@@ -93,6 +80,8 @@ static void xen_init_pv(MachineState *machine)
xen_config_dev_nic(nd_table + i);
}
+ xen_bus_init();
+
/* config cleanup hook */
atexit(xen_config_cleanup);
}
diff --git a/include/hw/xen/xen-backend.h b/include/hw/xen/xen-backend.h
new file mode 100644
index 0000000..010d712
--- /dev/null
+++ b/include/hw/xen/xen-backend.h
@@ -0,0 +1,39 @@
+/*
+ * Copyright (c) 2018 Citrix Systems Inc.
+ *
+ * This work is licensed under the terms of the GNU GPL, version 2 or later.
+ * See the COPYING file in the top-level directory.
+ */
+
+#ifndef HW_XEN_BACKEND_H
+#define HW_XEN_BACKEND_H
+
+#include "hw/xen/xen-bus.h"
+
+typedef struct XenBackendInstance XenBackendInstance;
+
+typedef void (*XenBackendDeviceCreate)(XenBackendInstance *backend,
+ QDict *opts, Error **errp);
+typedef void (*XenBackendDeviceDestroy)(XenBackendInstance *backend,
+ Error **errp);
+
+typedef struct XenBackendInfo {
+ const char *type;
+ XenBackendDeviceCreate create;
+ XenBackendDeviceDestroy destroy;
+} XenBackendInfo;
+
+XenBus *xen_backend_get_bus(XenBackendInstance *backend);
+const char *xen_backend_get_name(XenBackendInstance *backend);
+
+void xen_backend_set_device(XenBackendInstance *backend,
+ XenDevice *xendevice);
+XenDevice *xen_backend_get_device(XenBackendInstance *backend);
+
+void xen_backend_register(const XenBackendInfo *info);
+
+void xen_backend_device_create(XenBus *xenbus, const char *type,
+ const char *name, QDict *opts, Error **errp);
+bool xen_backend_try_device_destroy(XenDevice *xendev, Error **errp);
+
+#endif /* HW_XEN_BACKEND_H */
diff --git a/include/hw/xen/xen-block.h b/include/hw/xen/xen-block.h
new file mode 100644
index 0000000..11d351b
--- /dev/null
+++ b/include/hw/xen/xen-block.h
@@ -0,0 +1,94 @@
+/*
+ * Copyright (c) 2018 Citrix Systems Inc.
+ *
+ * This work is licensed under the terms of the GNU GPL, version 2 or later.
+ * See the COPYING file in the top-level directory.
+ */
+
+#ifndef HW_XEN_BLOCK_H
+#define HW_XEN_BLOCK_H
+
+#include "hw/xen/xen-bus.h"
+#include "hw/block/block.h"
+#include "hw/block/dataplane/xen-block.h"
+#include "sysemu/iothread.h"
+
+typedef enum XenBlockVdevType {
+ XEN_BLOCK_VDEV_TYPE_INVALID,
+ XEN_BLOCK_VDEV_TYPE_DP,
+ XEN_BLOCK_VDEV_TYPE_XVD,
+ XEN_BLOCK_VDEV_TYPE_HD,
+ XEN_BLOCK_VDEV_TYPE_SD,
+ XEN_BLOCK_VDEV_TYPE__MAX
+} XenBlockVdevType;
+
+typedef struct XenBlockVdev {
+ XenBlockVdevType type;
+ unsigned long disk;
+ unsigned long partition;
+ unsigned long number;
+} XenBlockVdev;
+
+
+typedef struct XenBlockProperties {
+ XenBlockVdev vdev;
+ BlockConf conf;
+ unsigned int max_ring_page_order;
+ IOThread *iothread;
+} XenBlockProperties;
+
+typedef struct XenBlockDrive {
+ char *id;
+ char *node_name;
+} XenBlockDrive;
+
+typedef struct XenBlockIOThread {
+ char *id;
+} XenBlockIOThread;
+
+typedef struct XenBlockDevice {
+ XenDevice xendev;
+ XenBlockProperties props;
+ const char *device_type;
+ unsigned int info;
+ XenBlockDataPlane *dataplane;
+ XenBlockDrive *drive;
+ XenBlockIOThread *iothread;
+} XenBlockDevice;
+
+typedef void (*XenBlockDeviceRealize)(XenBlockDevice *blockdev, Error **errp);
+typedef void (*XenBlockDeviceUnrealize)(XenBlockDevice *blockdev, Error **errp);
+
+typedef struct XenBlockDeviceClass {
+ /*< private >*/
+ XenDeviceClass parent_class;
+ /*< public >*/
+ XenBlockDeviceRealize realize;
+ XenBlockDeviceUnrealize unrealize;
+} XenBlockDeviceClass;
+
+#define TYPE_XEN_BLOCK_DEVICE "xen-block"
+#define XEN_BLOCK_DEVICE(obj) \
+ OBJECT_CHECK(XenBlockDevice, (obj), TYPE_XEN_BLOCK_DEVICE)
+#define XEN_BLOCK_DEVICE_CLASS(class) \
+ OBJECT_CLASS_CHECK(XenBlockDeviceClass, (class), TYPE_XEN_BLOCK_DEVICE)
+#define XEN_BLOCK_DEVICE_GET_CLASS(obj) \
+ OBJECT_GET_CLASS(XenBlockDeviceClass, (obj), TYPE_XEN_BLOCK_DEVICE)
+
+typedef struct XenDiskDevice {
+ XenBlockDevice blockdev;
+} XenDiskDevice;
+
+#define TYPE_XEN_DISK_DEVICE "xen-disk"
+#define XEN_DISK_DEVICE(obj) \
+ OBJECT_CHECK(XenDiskDevice, (obj), TYPE_XEN_DISK_DEVICE)
+
+typedef struct XenCDRomDevice {
+ XenBlockDevice blockdev;
+} XenCDRomDevice;
+
+#define TYPE_XEN_CDROM_DEVICE "xen-cdrom"
+#define XEN_CDROM_DEVICE(obj) \
+ OBJECT_CHECK(XenCDRomDevice, (obj), TYPE_XEN_CDROM_DEVICE)
+
+#endif /* HW_XEN_BLOCK_H */
diff --git a/include/hw/xen/xen-bus-helper.h b/include/hw/xen/xen-bus-helper.h
new file mode 100644
index 0000000..4c0f747
--- /dev/null
+++ b/include/hw/xen/xen-bus-helper.h
@@ -0,0 +1,45 @@
+/*
+ * Copyright (c) 2018 Citrix Systems Inc.
+ *
+ * This work is licensed under the terms of the GNU GPL, version 2 or later.
+ * See the COPYING file in the top-level directory.
+ */
+
+#ifndef HW_XEN_BUS_HELPER_H
+#define HW_XEN_BUS_HELPER_H
+
+#include "hw/xen/xen_common.h"
+
+const char *xs_strstate(enum xenbus_state state);
+
+void xs_node_create(struct xs_handle *xsh, xs_transaction_t tid,
+ const char *node, struct xs_permissions perms[],
+ unsigned int nr_perms, Error **errp);
+void xs_node_destroy(struct xs_handle *xsh, xs_transaction_t tid,
+ const char *node, Error **errp);
+
+/* Write to node/key unless node is empty, in which case write to key */
+void xs_node_vprintf(struct xs_handle *xsh, xs_transaction_t tid,
+ const char *node, const char *key, Error **errp,
+ const char *fmt, va_list ap)
+ GCC_FMT_ATTR(6, 0);
+void xs_node_printf(struct xs_handle *xsh, xs_transaction_t tid,
+ const char *node, const char *key, Error **errp,
+ const char *fmt, ...)
+ GCC_FMT_ATTR(6, 7);
+
+/* Read from node/key unless node is empty, in which case read from key */
+int xs_node_vscanf(struct xs_handle *xsh, xs_transaction_t tid,
+ const char *node, const char *key, Error **errp,
+ const char *fmt, va_list ap);
+int xs_node_scanf(struct xs_handle *xsh, xs_transaction_t tid,
+ const char *node, const char *key, Error **errp,
+ const char *fmt, ...);
+
+/* Watch node/key unless node is empty, in which case watch key */
+void xs_node_watch(struct xs_handle *xsh, const char *node, const char *key,
+ char *token, Error **errp);
+void xs_node_unwatch(struct xs_handle *xsh, const char *node, const char *key,
+ const char *token, Error **errp);
+
+#endif /* HW_XEN_BUS_HELPER_H */
diff --git a/include/hw/xen/xen-bus.h b/include/hw/xen/xen-bus.h
new file mode 100644
index 0000000..3183f10
--- /dev/null
+++ b/include/hw/xen/xen-bus.h
@@ -0,0 +1,137 @@
+/*
+ * Copyright (c) 2018 Citrix Systems Inc.
+ *
+ * This work is licensed under the terms of the GNU GPL, version 2 or later.
+ * See the COPYING file in the top-level directory.
+ */
+
+#ifndef HW_XEN_BUS_H
+#define HW_XEN_BUS_H
+
+#include "hw/xen/xen_common.h"
+#include "hw/sysbus.h"
+#include "qemu/notify.h"
+
+typedef void (*XenWatchHandler)(void *opaque);
+
+typedef struct XenWatch XenWatch;
+
+typedef struct XenDevice {
+ DeviceState qdev;
+ domid_t frontend_id;
+ char *name;
+ char *backend_path, *frontend_path;
+ enum xenbus_state backend_state, frontend_state;
+ Notifier exit;
+ XenWatch *backend_state_watch, *frontend_state_watch;
+ bool backend_online;
+ XenWatch *backend_online_watch;
+ xengnttab_handle *xgth;
+ bool feature_grant_copy;
+ xenevtchn_handle *xeh;
+ NotifierList event_notifiers;
+} XenDevice;
+
+typedef char *(*XenDeviceGetName)(XenDevice *xendev, Error **errp);
+typedef void (*XenDeviceRealize)(XenDevice *xendev, Error **errp);
+typedef void (*XenDeviceFrontendChanged)(XenDevice *xendev,
+ enum xenbus_state frontend_state,
+ Error **errp);
+typedef void (*XenDeviceUnrealize)(XenDevice *xendev, Error **errp);
+
+typedef struct XenDeviceClass {
+ /*< private >*/
+ DeviceClass parent_class;
+ /*< public >*/
+ const char *backend;
+ const char *device;
+ XenDeviceGetName get_name;
+ XenDeviceRealize realize;
+ XenDeviceFrontendChanged frontend_changed;
+ XenDeviceUnrealize unrealize;
+} XenDeviceClass;
+
+#define TYPE_XEN_DEVICE "xen-device"
+#define XEN_DEVICE(obj) \
+ OBJECT_CHECK(XenDevice, (obj), TYPE_XEN_DEVICE)
+#define XEN_DEVICE_CLASS(class) \
+ OBJECT_CLASS_CHECK(XenDeviceClass, (class), TYPE_XEN_DEVICE)
+#define XEN_DEVICE_GET_CLASS(obj) \
+ OBJECT_GET_CLASS(XenDeviceClass, (obj), TYPE_XEN_DEVICE)
+
+typedef struct XenBus {
+ BusState qbus;
+ domid_t backend_id;
+ struct xs_handle *xsh;
+ NotifierList watch_notifiers;
+ XenWatch *backend_watch;
+} XenBus;
+
+typedef struct XenBusClass {
+ /*< private >*/
+ BusClass parent_class;
+} XenBusClass;
+
+#define TYPE_XEN_BUS "xen-bus"
+#define XEN_BUS(obj) \
+ OBJECT_CHECK(XenBus, (obj), TYPE_XEN_BUS)
+#define XEN_BUS_CLASS(class) \
+ OBJECT_CLASS_CHECK(XenBusClass, (class), TYPE_XEN_BUS)
+#define XEN_BUS_GET_CLASS(obj) \
+ OBJECT_GET_CLASS(XenBusClass, (obj), TYPE_XEN_BUS)
+
+void xen_bus_init(void);
+
+void xen_device_backend_set_state(XenDevice *xendev,
+ enum xenbus_state state);
+enum xenbus_state xen_device_backend_get_state(XenDevice *xendev);
+
+void xen_device_backend_printf(XenDevice *xendev, const char *key,
+ const char *fmt, ...)
+ GCC_FMT_ATTR(3, 4);
+void xen_device_frontend_printf(XenDevice *xendev, const char *key,
+ const char *fmt, ...)
+ GCC_FMT_ATTR(3, 4);
+
+int xen_device_frontend_scanf(XenDevice *xendev, const char *key,
+ const char *fmt, ...);
+
+void xen_device_set_max_grant_refs(XenDevice *xendev, unsigned int nr_refs,
+ Error **errp);
+void *xen_device_map_grant_refs(XenDevice *xendev, uint32_t *refs,
+ unsigned int nr_refs, int prot,
+ Error **errp);
+void xen_device_unmap_grant_refs(XenDevice *xendev, void *map,
+ unsigned int nr_refs, Error **errp);
+
+typedef struct XenDeviceGrantCopySegment {
+ union {
+ void *virt;
+ struct {
+ uint32_t ref;
+ off_t offset;
+ } foreign;
+ } source, dest;
+ size_t len;
+} XenDeviceGrantCopySegment;
+
+void xen_device_copy_grant_refs(XenDevice *xendev, bool to_domain,
+ XenDeviceGrantCopySegment segs[],
+ unsigned int nr_segs, Error **errp);
+
+typedef struct XenEventChannel XenEventChannel;
+
+typedef void (*XenEventHandler)(void *opaque);
+
+XenEventChannel *xen_device_bind_event_channel(XenDevice *xendev,
+ unsigned int port,
+ XenEventHandler handler,
+ void *opaque, Error **errp);
+void xen_device_notify_event_channel(XenDevice *xendev,
+ XenEventChannel *channel,
+ Error **errp);
+void xen_device_unbind_event_channel(XenDevice *xendev,
+ XenEventChannel *channel,
+ Error **errp);
+
+#endif /* HW_XEN_BUS_H */
diff --git a/include/hw/xen/xen_backend.h b/include/hw/xen/xen-legacy-backend.h
index 9c17fdd..20cb47b 100644
--- a/include/hw/xen/xen_backend.h
+++ b/include/hw/xen/xen-legacy-backend.h
@@ -11,7 +11,7 @@
#define TYPE_XENBACKEND "xen-backend"
#define XENBACKEND_DEVICE(obj) \
- OBJECT_CHECK(XenDevice, (obj), TYPE_XENBACKEND)
+ OBJECT_CHECK(XenLegacyDevice, (obj), TYPE_XENBACKEND)
/* variables */
extern struct xs_handle *xenstore;
@@ -20,32 +20,37 @@ extern DeviceState *xen_sysdev;
extern BusState *xen_sysbus;
int xenstore_mkdir(char *path, int p);
-int xenstore_write_be_str(struct XenDevice *xendev, const char *node, const char *val);
-int xenstore_write_be_int(struct XenDevice *xendev, const char *node, int ival);
-int xenstore_write_be_int64(struct XenDevice *xendev, const char *node, int64_t ival);
-char *xenstore_read_be_str(struct XenDevice *xendev, const char *node);
-int xenstore_read_be_int(struct XenDevice *xendev, const char *node, int *ival);
-void xenstore_update_fe(char *watch, struct XenDevice *xendev);
+int xenstore_write_be_str(struct XenLegacyDevice *xendev, const char *node,
+ const char *val);
+int xenstore_write_be_int(struct XenLegacyDevice *xendev, const char *node,
+ int ival);
+int xenstore_write_be_int64(struct XenLegacyDevice *xendev, const char *node,
+ int64_t ival);
+char *xenstore_read_be_str(struct XenLegacyDevice *xendev, const char *node);
+int xenstore_read_be_int(struct XenLegacyDevice *xendev, const char *node,
+ int *ival);
+void xenstore_update_fe(char *watch, struct XenLegacyDevice *xendev);
void xenstore_update_be(char *watch, char *type, int dom,
struct XenDevOps *ops);
-char *xenstore_read_fe_str(struct XenDevice *xendev, const char *node);
-int xenstore_read_fe_int(struct XenDevice *xendev, const char *node, int *ival);
-int xenstore_read_fe_uint64(struct XenDevice *xendev, const char *node,
+char *xenstore_read_fe_str(struct XenLegacyDevice *xendev, const char *node);
+int xenstore_read_fe_int(struct XenLegacyDevice *xendev, const char *node,
+ int *ival);
+int xenstore_read_fe_uint64(struct XenLegacyDevice *xendev, const char *node,
uint64_t *uval);
-void xen_be_check_state(struct XenDevice *xendev);
+void xen_be_check_state(struct XenLegacyDevice *xendev);
/* xen backend driver bits */
int xen_be_init(void);
void xen_be_register_common(void);
int xen_be_register(const char *type, struct XenDevOps *ops);
-int xen_be_set_state(struct XenDevice *xendev, enum xenbus_state state);
-int xen_be_bind_evtchn(struct XenDevice *xendev);
-void xen_be_set_max_grant_refs(struct XenDevice *xendev,
+int xen_be_set_state(struct XenLegacyDevice *xendev, enum xenbus_state state);
+int xen_be_bind_evtchn(struct XenLegacyDevice *xendev);
+void xen_be_set_max_grant_refs(struct XenLegacyDevice *xendev,
unsigned int nr_refs);
-void *xen_be_map_grant_refs(struct XenDevice *xendev, uint32_t *refs,
+void *xen_be_map_grant_refs(struct XenLegacyDevice *xendev, uint32_t *refs,
unsigned int nr_refs, int prot);
-void xen_be_unmap_grant_refs(struct XenDevice *xendev, void *ptr,
+void xen_be_unmap_grant_refs(struct XenLegacyDevice *xendev, void *ptr,
unsigned int nr_refs);
typedef struct XenGrantCopySegment {
@@ -59,17 +64,17 @@ typedef struct XenGrantCopySegment {
size_t len;
} XenGrantCopySegment;
-int xen_be_copy_grant_refs(struct XenDevice *xendev,
+int xen_be_copy_grant_refs(struct XenLegacyDevice *xendev,
bool to_domain, XenGrantCopySegment segs[],
unsigned int nr_segs);
-static inline void *xen_be_map_grant_ref(struct XenDevice *xendev,
+static inline void *xen_be_map_grant_ref(struct XenLegacyDevice *xendev,
uint32_t ref, int prot)
{
return xen_be_map_grant_refs(xendev, &ref, 1, prot);
}
-static inline void xen_be_unmap_grant_ref(struct XenDevice *xendev,
+static inline void xen_be_unmap_grant_ref(struct XenLegacyDevice *xendev,
void *ptr)
{
return xen_be_unmap_grant_refs(xendev, ptr, 1);
diff --git a/include/hw/xen/xen.h b/include/hw/xen/xen.h
index 7efcdaa..ba039c1 100644
--- a/include/hw/xen/xen.h
+++ b/include/hw/xen/xen.h
@@ -15,8 +15,7 @@
/* xen-machine.c */
enum xen_mode {
XEN_EMULATE = 0, // xen emulation, using xenner (default)
- XEN_CREATE, // create xen domain
- XEN_ATTACH // attach to xen domain created by xend
+ XEN_ATTACH // attach to xen domain created by libxl
};
extern uint32_t xen_domid;
diff --git a/include/hw/xen/xen_common.h b/include/hw/xen/xen_common.h
index 93f631e..9a8155e 100644
--- a/include/hw/xen/xen_common.h
+++ b/include/hw/xen/xen_common.h
@@ -32,6 +32,7 @@ extern xc_interface *xen_xc;
typedef xc_interface xenforeignmemory_handle;
typedef xc_evtchn xenevtchn_handle;
typedef xc_gnttab xengnttab_handle;
+typedef evtchn_port_or_error_t xenevtchn_port_or_error_t;
#define xenevtchn_open(l, f) xc_evtchn_open(l, f);
#define xenevtchn_close(h) xc_evtchn_close(h)
@@ -661,24 +662,6 @@ static inline int xen_set_ioreq_server_state(domid_t dom,
#endif
-#ifdef CONFIG_XEN_PV_DOMAIN_BUILD
-#if CONFIG_XEN_CTRL_INTERFACE_VERSION < 40700
-static inline int xen_domain_create(xc_interface *xc, uint32_t ssidref,
- xen_domain_handle_t handle, uint32_t flags,
- uint32_t *pdomid)
-{
- return xc_domain_create(xc, ssidref, handle, flags, pdomid);
-}
-#else
-static inline int xen_domain_create(xc_interface *xc, uint32_t ssidref,
- xen_domain_handle_t handle, uint32_t flags,
- uint32_t *pdomid)
-{
- return xc_domain_create(xc, ssidref, handle, flags, pdomid, NULL);
-}
-#endif
-#endif
-
/* Xen before 4.8 */
#if CONFIG_XEN_CTRL_INTERFACE_VERSION < 40800
diff --git a/include/hw/xen/xen_pvdev.h b/include/hw/xen/xen_pvdev.h
index d473e9b..83e5174 100644
--- a/include/hw/xen/xen_pvdev.h
+++ b/include/hw/xen/xen_pvdev.h
@@ -6,7 +6,7 @@
#define XEN_BUFSIZE 1024
-struct XenDevice;
+struct XenLegacyDevice;
/* driver uses grant tables -> open gntdev device (xendev->gnttabdev) */
#define DEVOPS_FLAG_NEED_GNTDEV 1
@@ -16,19 +16,21 @@ struct XenDevice;
struct XenDevOps {
size_t size;
uint32_t flags;
- void (*alloc)(struct XenDevice *xendev);
- int (*init)(struct XenDevice *xendev);
- int (*initialise)(struct XenDevice *xendev);
- void (*connected)(struct XenDevice *xendev);
- void (*event)(struct XenDevice *xendev);
- void (*disconnect)(struct XenDevice *xendev);
- int (*free)(struct XenDevice *xendev);
- void (*backend_changed)(struct XenDevice *xendev, const char *node);
- void (*frontend_changed)(struct XenDevice *xendev, const char *node);
+ void (*alloc)(struct XenLegacyDevice *xendev);
+ int (*init)(struct XenLegacyDevice *xendev);
+ int (*initialise)(struct XenLegacyDevice *xendev);
+ void (*connected)(struct XenLegacyDevice *xendev);
+ void (*event)(struct XenLegacyDevice *xendev);
+ void (*disconnect)(struct XenLegacyDevice *xendev);
+ int (*free)(struct XenLegacyDevice *xendev);
+ void (*backend_changed)(struct XenLegacyDevice *xendev,
+ const char *node);
+ void (*frontend_changed)(struct XenLegacyDevice *xendev,
+ const char *node);
int (*backend_register)(void);
};
-struct XenDevice {
+struct XenLegacyDevice {
DeviceState qdev;
const char *type;
int dom;
@@ -49,7 +51,7 @@ struct XenDevice {
xengnttab_handle *gnttabdev;
struct XenDevOps *ops;
- QTAILQ_ENTRY(XenDevice) next;
+ QTAILQ_ENTRY(XenLegacyDevice) next;
};
/* ------------------------------------------------------------- */
@@ -66,14 +68,14 @@ void xenstore_update(void *unused);
const char *xenbus_strstate(enum xenbus_state state);
void xen_pv_evtchn_event(void *opaque);
-void xen_pv_insert_xendev(struct XenDevice *xendev);
-void xen_pv_del_xendev(struct XenDevice *xendev);
-struct XenDevice *xen_pv_find_xendev(const char *type, int dom, int dev);
+void xen_pv_insert_xendev(struct XenLegacyDevice *xendev);
+void xen_pv_del_xendev(struct XenLegacyDevice *xendev);
+struct XenLegacyDevice *xen_pv_find_xendev(const char *type, int dom, int dev);
-void xen_pv_unbind_evtchn(struct XenDevice *xendev);
-int xen_pv_send_notify(struct XenDevice *xendev);
+void xen_pv_unbind_evtchn(struct XenLegacyDevice *xendev);
+int xen_pv_send_notify(struct XenLegacyDevice *xendev);
-void xen_pv_printf(struct XenDevice *xendev, int msg_level,
+void xen_pv_printf(struct XenLegacyDevice *xendev, int msg_level,
const char *fmt, ...) GCC_FMT_ATTR(3, 4);
#endif /* QEMU_HW_XEN_PVDEV_H */
diff --git a/include/qemu/module.h b/include/qemu/module.h
index 54300ab..55dd2be 100644
--- a/include/qemu/module.h
+++ b/include/qemu/module.h
@@ -44,6 +44,7 @@ typedef enum {
MODULE_INIT_OPTS,
MODULE_INIT_QOM,
MODULE_INIT_TRACE,
+ MODULE_INIT_XEN_BACKEND,
MODULE_INIT_MAX
} module_init_type;
@@ -51,6 +52,8 @@ typedef enum {
#define opts_init(function) module_init(function, MODULE_INIT_OPTS)
#define type_init(function) module_init(function, MODULE_INIT_QOM)
#define trace_init(function) module_init(function, MODULE_INIT_TRACE)
+#define xen_backend_init(function) module_init(function, \
+ MODULE_INIT_XEN_BACKEND)
#define block_module_load_one(lib) module_load_one("block-", lib)
#define ui_module_load_one(lib) module_load_one("ui-", lib)
diff --git a/qemu-options.hx b/qemu-options.hx
index d4f3564..521511e 100644
--- a/qemu-options.hx
+++ b/qemu-options.hx
@@ -3394,13 +3394,9 @@ ETEXI
DEF("xen-domid", HAS_ARG, QEMU_OPTION_xen_domid,
"-xen-domid id specify xen guest domain id\n", QEMU_ARCH_ALL)
-DEF("xen-create", 0, QEMU_OPTION_xen_create,
- "-xen-create create domain using xen hypercalls, bypassing xend\n"
- " warning: should not be used when xend is in use\n",
- QEMU_ARCH_ALL)
DEF("xen-attach", 0, QEMU_OPTION_xen_attach,
"-xen-attach attach to existing xen domain\n"
- " xend will use this when starting QEMU\n",
+ " libxl will use this when starting QEMU\n",
QEMU_ARCH_ALL)
DEF("xen-domid-restrict", 0, QEMU_OPTION_xen_domid_restrict,
"-xen-domid-restrict restrict set of available xen operations\n"
@@ -3411,14 +3407,10 @@ STEXI
@item -xen-domid @var{id}
@findex -xen-domid
Specify xen guest domain @var{id} (XEN only).
-@item -xen-create
-@findex -xen-create
-Create domain using xen hypercalls, bypassing xend.
-Warning: should not be used when xend is in use (XEN only).
@item -xen-attach
@findex -xen-attach
Attach to existing xen domain.
-xend will use this when starting QEMU (XEN only).
+libxl will use this when starting QEMU (XEN only).
@findex -xen-domid-restrict
Restrict set of available xen operations to specified domain id (XEN only).
ETEXI
diff --git a/vl.c b/vl.c
index 9b8ea3f..bc9fbec 100644
--- a/vl.c
+++ b/vl.c
@@ -3856,13 +3856,6 @@ int main(int argc, char **argv, char **envp)
}
xen_domid = atoi(optarg);
break;
- case QEMU_OPTION_xen_create:
- if (!(xen_available())) {
- error_report("Option not supported for this target");
- exit(1);
- }
- xen_mode = XEN_CREATE;
- break;
case QEMU_OPTION_xen_attach:
if (!(xen_available())) {
error_report("Option not supported for this target");