aboutsummaryrefslogtreecommitdiff
path: root/hw
diff options
context:
space:
mode:
authorPaul Durrant <paul.durrant@citrix.com>2019-01-08 14:48:52 +0000
committerAnthony PERARD <anthony.perard@citrix.com>2019-01-14 13:45:40 +0000
commita3d669c8bd1a93ad588d4dce5c70ea6405fa9bb9 (patch)
tree196905ed7a9eb35b1b4d0f4303216126226550a8 /hw
parent4b34b5b14054edded15888448b0aad3723b99c29 (diff)
downloadqemu-a3d669c8bd1a93ad588d4dce5c70ea6405fa9bb9.zip
qemu-a3d669c8bd1a93ad588d4dce5c70ea6405fa9bb9.tar.gz
qemu-a3d669c8bd1a93ad588d4dce5c70ea6405fa9bb9.tar.bz2
xen: add event channel interface for XenDevice-s
The legacy PV backend infrastructure provides functions to bind, unbind and send notifications to event channnels. Similar functionality will be required by XenDevice implementations so this patch adds the necessary support. Signed-off-by: Paul Durrant <paul.durrant@citrix.com> Reviewed-by: Anthony Perard <anthony.perard@citrix.com> Signed-off-by: Anthony PERARD <anthony.perard@citrix.com> Patch squashed with: Patch "xen: add event channel interface for XenDevice-s" makes use of the type xenevtchn_port_or_error_t, but this isn't avaiable before Xen 4.7. Also the function xen_device_bind_event_channel assign the return value of xenevtchn_bind_interdomain to channel->local_port but check the result for error with xendev->local_port. Fix by: - removing local_port from struct XenDevice as it isn't use anywere. - adding a compatibility typedef for xenevtchn_port_or_error_t for Xen 4.6 and earlier. As extra, replace the type of XenEventChannel->local_port by evtchn_port_t. Signed-off-by: Anthony PERARD <anthony.perard@citrix.com> Reviewed-by: Paul Durrant <paul.durrant@citrix.com>
Diffstat (limited to 'hw')
-rw-r--r--hw/xen/xen-bus.c103
1 files changed, 103 insertions, 0 deletions
diff --git a/hw/xen/xen-bus.c b/hw/xen/xen-bus.c
index faa9fd3..1488112 100644
--- a/hw/xen/xen-bus.c
+++ b/hw/xen/xen-bus.c
@@ -617,6 +617,83 @@ 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);
@@ -641,6 +718,12 @@ static void xen_device_unrealize(DeviceState *dev, Error **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;
@@ -657,6 +740,16 @@ static void xen_device_exit(Notifier *n, void *data)
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);
@@ -697,6 +790,16 @@ static void xen_device_realize(DeviceState *dev, Error **errp)
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);