]> www.infradead.org Git - users/dwmw2/qemu.git/commitdiff
i386/xen: redirect evtchn callers with XEN_EMULATE
authorJoao Martins <joao.m.martins@oracle.com>
Thu, 6 Dec 2018 15:22:05 +0000 (10:22 -0500)
committerJoao Martins <joao.m.martins@oracle.com>
Tue, 19 Feb 2019 14:00:57 +0000 (09:00 -0500)
Similar to the gnttab callers we add one more operation to send
events. When guest sends an event which is handled in Qemu there's an
exit and so what we do here is to invoke the callback associated with
the event channel (the devhandler).

When sending events from the host (e.g.  qdisk after processing blk
requests) we can just inject the callback ourselves and we don't need
to call into /dev/xen/evtchn. Given Qemu is managing all DomU ports
hence it is more performant than just going via /dev/xen/evtchn.
Additionally we're less dependent on /dev/xen/* device machinery which
may not be available to userspace.

We clear eventfd when setting devhandler which means the event channel
is meant to be handled in userspace, and therefore should not be
intercepted by kernel.

Signed-off-by: Joao Martins <joao.m.martins@oracle.com>
hw/xen/xen-bus.c
hw/xen/xen-legacy-backend.c
hw/xen/xen_pvdev.c
include/hw/xen/xen-bus.h
include/hw/xen/xen-legacy-backend.h
target/i386/xen-proto.h
target/i386/xen.c
target/i386/xen_evtchn.c
target/i386/xen_evtchn.h

index d4974f189dbf05a2c469563be92a8fd754a0860d..f6b131a2f9587c281756fecf9231108a0b2c66bc 100644 (file)
@@ -20,6 +20,8 @@
 #include "sysemu/sysemu.h"
 #include "trace.h"
 
+XenDeviceHandler *xen_dev_handler;
+
 static char *xen_device_get_backend_path(XenDevice *xendev)
 {
     XenBus *xenbus = XEN_BUS(qdev_get_parent_bus(DEVICE(xendev)));
@@ -980,6 +982,12 @@ XenEventChannel *xen_device_bind_event_channel(XenDevice *xendev,
     XenEventChannel *channel = g_new0(XenEventChannel, 1);
     xenevtchn_port_or_error_t local_port;
 
+    if ((xen_mode == XEN_EMULATE) &&
+        !xen_dev_handler(port, handler, opaque)) {
+        local_port = port;
+        goto emulate;
+    }
+
     local_port = xenevtchn_bind_interdomain(xendev->xeh,
                                             xendev->frontend_id,
                                             port);
@@ -990,6 +998,7 @@ XenEventChannel *xen_device_bind_event_channel(XenDevice *xendev,
         return NULL;
     }
 
+emulate:
     channel->local_port = local_port;
     channel->handler = handler;
     channel->opaque = opaque;
@@ -1009,6 +1018,11 @@ void xen_device_notify_event_channel(XenDevice *xendev,
         return;
     }
 
+    if ((xen_mode == XEN_EMULATE) && xen_gnt_ops.send_notify) {
+        xen_gnt_ops.send_notify(xendev, channel->local_port, errp);
+        return;
+    }
+
     if (xenevtchn_notify(xendev->xeh, channel->local_port) < 0) {
         error_setg_errno(errp, errno, "xenevtchn_notify failed");
     }
@@ -1025,7 +1039,8 @@ void xen_device_unbind_event_channel(XenDevice *xendev,
 
     notifier_remove(&channel->notifier);
 
-    if (xenevtchn_unbind(xendev->xeh, channel->local_port) < 0) {
+    if ((xen_mode != XEN_EMULATE) &&
+        xenevtchn_unbind(xendev->xeh, channel->local_port) < 0) {
         error_setg_errno(errp, errno, "xenevtchn_unbind failed");
     }
 
index 322c1065d3948a136d10a4f13ebae8e86e7592c9..2821c7a0dc93248e6c46dd13604795b35d321da9 100644 (file)
@@ -42,6 +42,7 @@ BusState *xen_sysbus;
 /* ------------------------------------------------------------- */
 
 /* public */
+XenEvtchnHandler *xen_legacy_handler;
 struct xs_handle *xenstore;
 const char *xen_protocol;
 
@@ -261,11 +262,17 @@ static int xen_copy_grant_refs(struct XenLegacyDevice *xendev,
     return rc;
 }
 
+int xen_send_notify(struct XenLegacyDevice *xendev)
+{
+    return xenevtchn_notify(xendev->evtchndev, xendev->local_port);
+}
+
 struct XenLegacyBackendOps xen_legacy_gnt_ops = {
     .set_max_grefs = xen_set_max_grant_refs,
     .map_grefs = xen_map_grant_refs,
     .unmap_grefs = xen_unmap_grant_refs,
     .copy_grefs = xen_copy_grant_refs,
+    .send_notify = xen_send_notify,
 };
 
 void xen_be_set_max_grant_refs(struct XenLegacyDevice *xendev,
@@ -819,6 +826,13 @@ int xen_be_bind_evtchn(struct XenLegacyDevice *xendev)
     if (xendev->local_port != -1) {
         return 0;
     }
+
+    if ((xen_mode == XEN_EMULATE) &&
+        !xen_legacy_handler(xendev->remote_port, xendev->ops->event, xendev)) {
+        xendev->local_port = xendev->remote_port;
+        return 0;
+    }
+
     xendev->local_port = xenevtchn_bind_interdomain
         (xendev->evtchndev, xendev->dom, xendev->remote_port);
     if (xendev->local_port == -1) {
index 6ef09cbf9d6cfc442347ffeaf0b6432b52255945..e9c2ca0159485ee194d5c33e0d434c3d8909d967 100644 (file)
@@ -249,7 +249,8 @@ void xen_pv_evtchn_event(void *opaque)
 
 void xen_pv_unbind_evtchn(struct XenLegacyDevice *xendev)
 {
-    if (xendev->local_port == -1) {
+    if (xendev->local_port == -1 ||
+        xen_mode == XEN_EMULATE) {
         return;
     }
     qemu_set_fd_handler(xenevtchn_fd(xendev->evtchndev), NULL, NULL, NULL);
@@ -260,7 +261,7 @@ void xen_pv_unbind_evtchn(struct XenLegacyDevice *xendev)
 
 int xen_pv_send_notify(struct XenLegacyDevice *xendev)
 {
-    return xenevtchn_notify(xendev->evtchndev, xendev->local_port);
+    return xen_legacy_gnt_ops.send_notify(xendev);
 }
 
 /* ------------------------------------------------------------- */
index befd9fcb56308cf7af72135a924ba162eae9ef61..11d22fb2584889a018635d34c6730d7bdba005c1 100644 (file)
@@ -144,7 +144,11 @@ struct XenBackendOps {
     void (*copy_grefs)(struct XenDevice *xendev, bool to_domain,
                       XenDeviceGrantCopySegment segs[], unsigned int nr_segs,
                       Error **errp);
+    void (*send_notify)(struct XenDevice *xendev, int port, Error **errp);
 };
 
+typedef int XenDeviceHandler(int port, XenEventHandler cb, void *opaque);
+
 extern struct XenBackendOps xen_gnt_ops;
+extern XenDeviceHandler *xen_dev_handler;
 #endif /* HW_XEN_BUS_H */
index 12a05e8fca57daf3ad6e6d1db4e85aefbb42d1f2..901edbc272853c2e2a9cdd80cf71855bb4ebf662 100644 (file)
 #define XENBACKEND_DEVICE(obj) \
     OBJECT_CHECK(XenLegacyDevice, (obj), TYPE_XENBACKEND)
 
+typedef void XenLegacyDeviceHandler(struct XenLegacyDevice *device);
+typedef int XenEvtchnHandler(int port, XenLegacyDeviceHandler *cb,
+                             struct XenLegacyDevice *dev);
+
 /* variables */
 extern struct xs_handle *xenstore;
 extern const char *xen_protocol;
 extern DeviceState *xen_sysdev;
 extern BusState *xen_sysbus;
+extern XenEvtchnHandler *xen_legacy_handler;
 
 int xenstore_mkdir(char *path, int p);
 int xenstore_write_be_str(struct XenLegacyDevice *xendev, const char *node,
@@ -83,6 +88,8 @@ static inline void xen_be_unmap_grant_ref(struct XenLegacyDevice *xendev,
     return xen_be_unmap_grant_refs(xendev, ptr, 1);
 }
 
+int xen_send_notify(struct XenLegacyDevice *xendev);
+
 struct XenLegacyBackendOps {
     void (*set_max_grefs)(struct XenLegacyDevice *xendev, unsigned int nr_refs);
     void * (*map_grefs)(struct XenLegacyDevice *xendev, uint32_t *refs,
@@ -91,6 +98,7 @@ struct XenLegacyBackendOps {
                         unsigned int nr_refs);
     int (*copy_grefs)(struct XenLegacyDevice *xendev, bool to_domain,
                       XenGrantCopySegment segs[], unsigned int nr_segs);
+    int (*send_notify)(struct XenLegacyDevice *xendev);
 };
 
 extern struct XenLegacyBackendOps xen_legacy_gnt_ops;
index 3bc65673952ba8caa05a6bbe35ac4e81a6889faf..8b648746772c0cba6a7be621209412218f9a8ca5 100644 (file)
@@ -11,6 +11,7 @@
 #ifndef TARGET_I386_XEN_PROTO_H
 #define TARGET_I386_XEN_PROTO_H
 
+#include "hw/xen/xen-bus.h"
 #include "hw/xen/xen-legacy-backend.h"
 
 typedef struct XenGrantTable {
@@ -46,6 +47,16 @@ typedef struct XenEvtChn {
 #define XEN_EVTCHN_STATE_INUSE    1
 #define XEN_EVTCHN_STATE_UNBOUND  2
   int state;
+
+  bool is_legacy;
+  union {
+      XenLegacyDeviceHandler *legacy_handler;
+      XenEventHandler handler;
+  } callback;
+  union {
+      struct XenLegacyDevice *dev;
+      void *opaque;
+  } callback_arg;
 } XenEvtChn;
 
 typedef struct XenState {
index 2cc1f1cbe4a4bbb5df661552a6480c935200181b..937a0688939dd4172a0f1d8d8c01d32b65822b5a 100644 (file)
@@ -26,6 +26,7 @@
 #include "qapi/error.h"
 #include "qom/cpu.h"
 #include "hw/xen/xen.h"
+#include "hw/xen/xen_pvdev.h"
 #include "hw/xen/xen-legacy-backend.h"
 #include "hw/xen/xen-bus.h"
 
@@ -237,11 +238,18 @@ static void xen_dev_copy_grant_refs(struct XenDevice *xendev,
     }
 }
 
+static void xen_dev_send_notify(struct XenDevice *xendev, int port,
+                                Error **errp)
+{
+    kvm_xen_evtchn_host_send(port);
+}
+
 static struct XenBackendOps xen_dev_ops = {
     .set_max_grefs = xen_dev_set_max_grant_refs,
     .map_grefs = xen_dev_map_grant_refs,
     .unmap_grefs = xen_dev_unmap_grant_refs,
     .copy_grefs = xen_dev_copy_grant_refs,
+    .send_notify = xen_dev_send_notify,
 };
 
 static void xen_legacy_dev_set_max_grant_refs(struct XenLegacyDevice *xendev,
@@ -304,11 +312,18 @@ static int xen_legacy_dev_copy_grant_refs(struct XenLegacyDevice *xendev,
     return 0;
 }
 
+static int xen_legacy_dev_device_notify(struct XenLegacyDevice *xendev)
+{
+   kvm_xen_evtchn_host_send(xendev->local_port);
+   return 0;
+}
+
 static struct XenLegacyBackendOps xen_legacy_dev_ops = {
     .set_max_grefs = xen_legacy_dev_set_max_grant_refs,
     .map_grefs = xen_legacy_dev_map_grant_refs,
     .unmap_grefs = xen_legacy_dev_unmap_grant_refs,
     .copy_grefs = xen_legacy_dev_copy_grant_refs,
+    .send_notify = xen_legacy_dev_device_notify,
 };
 
 int kvm_xen_set_hypercall_page(CPUState *env)
@@ -359,7 +374,11 @@ void kvm_xen_init(XenState *xen)
 
     kvm_xen_evtchn_init(xen);
 
+    /* grant and device emulation ops */
+    xen_legacy_handler = kvm_xen_evtchn_set_legacyhandler;
     xen_legacy_gnt_ops = xen_legacy_dev_ops;
+
+    xen_dev_handler = kvm_xen_evtchn_set_devhandler;
     xen_gnt_ops = xen_dev_ops;
 }
 
index 23df4a813da1f9bf9e9f032bbe180ed2b24c492e..7965da6c8c151c656618bb77ec8dd161b6546fb5 100644 (file)
@@ -281,6 +281,53 @@ static int kvm_update_xen_event(KVMState *s, XenEvtChn *e,
     return __kvm_set_xen_event(s, e, n, KVM_XEN_EVENTFD_UPDATE);
 }
 
+int kvm_xen_evtchn_set_legacyhandler(int port, XenLegacyDeviceHandler *cb,
+                                     struct XenLegacyDevice *dev)
+{
+    struct XenEvtChn *evtchn;
+    CPUState *cpu;
+
+    evtchn = evtchn_from_port(port);
+    if (!evtchn) {
+        return -ENOENT;
+    }
+
+    cpu = qemu_get_cpu(evtchn->notify_vcpu_id);
+    if (!cpu) {
+        return -EINVAL;
+    }
+
+    kvm_clear_xen_event(cpu->kvm_state, evtchn);
+    evtchn->callback.legacy_handler = cb;
+    evtchn->callback_arg.dev = dev;
+    evtchn->is_legacy = true;
+
+    return 0;
+}
+
+int kvm_xen_evtchn_set_devhandler(int port, XenEventHandler cb, void *opaque)
+{
+    struct XenEvtChn *evtchn;
+    CPUState *cpu;
+
+    evtchn = evtchn_from_port(port);
+    if (!evtchn) {
+        return -ENOENT;
+    }
+
+    cpu = qemu_get_cpu(evtchn->notify_vcpu_id);
+    if (!cpu) {
+        return -EINVAL;
+    }
+
+    kvm_clear_xen_event(cpu->kvm_state, evtchn);
+    evtchn->callback.handler = cb;
+    evtchn->callback_arg.opaque = opaque;
+    evtchn->is_legacy = false;
+
+    return 0;
+}
+
 int kvm_xen_evtchn_bind_ipi(X86CPU *cpu, void *arg)
 {
     struct evtchn_bind_ipi *out = arg;
@@ -564,7 +611,13 @@ int kvm_xen_evtchn_send(X86CPU *cpu, void *arg)
         return -EINVAL;
     }
 
-    evtchn_2l_set_pending(X86_CPU(dest), evtchn);
+    if (!evtchn->callback.handler) {
+        evtchn_2l_set_pending(X86_CPU(dest), evtchn);
+    } else if (evtchn->is_legacy) {
+        evtchn->callback.legacy_handler(evtchn->callback_arg.dev);
+    } else {
+        evtchn->callback.handler(evtchn->callback_arg.opaque);
+    }
 
     trace_kvm_xen_evtchn_send(CPU(cpu)->cpu_index, evtchn->notify_vcpu_id,
                               send.port);
@@ -572,6 +625,27 @@ int kvm_xen_evtchn_send(X86CPU *cpu, void *arg)
     return 0;
 }
 
+int kvm_xen_evtchn_host_send(int port)
+{
+    struct XenEvtChn *evtchn;
+    CPUState *dest;
+
+    evtchn = evtchn_from_port(port);
+    if (!evtchn) {
+        return -ENOENT;
+    }
+
+    dest = qemu_get_cpu(evtchn->notify_vcpu_id);
+    if (!dest) {
+        return -EINVAL;
+    }
+
+    evtchn_2l_set_pending(X86_CPU(dest), evtchn);
+
+    trace_kvm_xen_evtchn_send(-1, evtchn->notify_vcpu_id, port);
+    return 0;
+}
+
 int kvm_xen_evtchn_vcpu_init(X86CPU *cpu, struct vcpu_info *vcpu)
 {
     int i;
index 97b5d6f0978ad10207cfa7d03c4bc71ad338786b..18c0a9475e3bbcbbde40b4ea68f0083656c043ed 100644 (file)
@@ -26,7 +26,11 @@ int kvm_xen_evtchn_close(X86CPU *cpu, void *arg);
 int kvm_xen_evtchn_unmask(X86CPU *cpu, void *arg);
 int kvm_xen_evtchn_status(X86CPU *cpu, void *arg);
 int kvm_xen_evtchn_send(X86CPU *cpu, void *arg);
+int kvm_xen_evtchn_host_send(int port);
 int kvm_xen_evtchn_vcpu_init(X86CPU *cpu, struct vcpu_info *info);
+int kvm_xen_evtchn_set_legacyhandler(int port, XenLegacyDeviceHandler *cb,
+                                     struct XenLegacyDevice *dev);
+int kvm_xen_evtchn_set_devhandler(int port, XenEventHandler cb, void *opaque);
 
 void evtchn_2l_set_pending(X86CPU *cpu, XenEvtChn *evtchn);