From 86865da06067787137f1fba027a4ff86ab7a4130 Mon Sep 17 00:00:00 2001 From: Joao Martins Date: Thu, 6 Dec 2018 10:22:05 -0500 Subject: [PATCH] i386/xen: redirect evtchn callers with XEN_EMULATE 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 --- hw/xen/xen-bus.c | 17 ++++++- hw/xen/xen-legacy-backend.c | 14 ++++++ hw/xen/xen_pvdev.c | 5 +- include/hw/xen/xen-bus.h | 4 ++ include/hw/xen/xen-legacy-backend.h | 8 +++ target/i386/xen-proto.h | 11 +++++ target/i386/xen.c | 19 ++++++++ target/i386/xen_evtchn.c | 76 ++++++++++++++++++++++++++++- target/i386/xen_evtchn.h | 4 ++ 9 files changed, 154 insertions(+), 4 deletions(-) diff --git a/hw/xen/xen-bus.c b/hw/xen/xen-bus.c index d4974f189d..f6b131a2f9 100644 --- a/hw/xen/xen-bus.c +++ b/hw/xen/xen-bus.c @@ -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"); } diff --git a/hw/xen/xen-legacy-backend.c b/hw/xen/xen-legacy-backend.c index 322c1065d3..2821c7a0dc 100644 --- a/hw/xen/xen-legacy-backend.c +++ b/hw/xen/xen-legacy-backend.c @@ -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) { diff --git a/hw/xen/xen_pvdev.c b/hw/xen/xen_pvdev.c index 6ef09cbf9d..e9c2ca0159 100644 --- a/hw/xen/xen_pvdev.c +++ b/hw/xen/xen_pvdev.c @@ -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); } /* ------------------------------------------------------------- */ diff --git a/include/hw/xen/xen-bus.h b/include/hw/xen/xen-bus.h index befd9fcb56..11d22fb258 100644 --- a/include/hw/xen/xen-bus.h +++ b/include/hw/xen/xen-bus.h @@ -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 */ diff --git a/include/hw/xen/xen-legacy-backend.h b/include/hw/xen/xen-legacy-backend.h index 12a05e8fca..901edbc272 100644 --- a/include/hw/xen/xen-legacy-backend.h +++ b/include/hw/xen/xen-legacy-backend.h @@ -13,11 +13,16 @@ #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; diff --git a/target/i386/xen-proto.h b/target/i386/xen-proto.h index 3bc6567395..8b64874677 100644 --- a/target/i386/xen-proto.h +++ b/target/i386/xen-proto.h @@ -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 { diff --git a/target/i386/xen.c b/target/i386/xen.c index 2cc1f1cbe4..937a068893 100644 --- a/target/i386/xen.c +++ b/target/i386/xen.c @@ -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; } diff --git a/target/i386/xen_evtchn.c b/target/i386/xen_evtchn.c index 23df4a813d..7965da6c8c 100644 --- a/target/i386/xen_evtchn.c +++ b/target/i386/xen_evtchn.c @@ -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; diff --git a/target/i386/xen_evtchn.h b/target/i386/xen_evtchn.h index 97b5d6f097..18c0a9475e 100644 --- a/target/i386/xen_evtchn.h +++ b/target/i386/xen_evtchn.h @@ -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); -- 2.50.1