Message ID | 20230201143148.1744093-34-dwmw2@infradead.org |
---|---|
State | New |
Headers | show |
Series | Xen HVM support under KVM | expand |
On 01/02/2023 14:31, David Woodhouse wrote: > From: David Woodhouse <dwmw@amazon.co.uk> > > Signed-off-by: David Woodhouse <dwmw@amazon.co.uk> > --- > hw/i386/kvm/xen_evtchn.c | 69 +++++++++++++++++++++++++++++++++++++++ > hw/i386/kvm/xen_evtchn.h | 2 ++ > target/i386/kvm/xen-emu.c | 15 +++++++++ > 3 files changed, 86 insertions(+) > > diff --git a/hw/i386/kvm/xen_evtchn.c b/hw/i386/kvm/xen_evtchn.c > index da2f5711dd..d8527483b9 100644 > --- a/hw/i386/kvm/xen_evtchn.c > +++ b/hw/i386/kvm/xen_evtchn.c > @@ -13,6 +13,7 @@ > #include "qemu/host-utils.h" > #include "qemu/module.h" > #include "qemu/main-loop.h" > +#include "qemu/log.h" > #include "qapi/error.h" > #include "qom/object.h" > #include "exec/target_page.h" > @@ -231,6 +232,43 @@ static void inject_callback(XenEvtchnState *s, uint32_t vcpu) > kvm_xen_inject_vcpu_callback_vector(vcpu, type); > } > > +static void deassign_kernel_port(evtchn_port_t port) > +{ > + struct kvm_xen_hvm_attr ha; > + int ret; > + > + ha.type = KVM_XEN_ATTR_TYPE_EVTCHN; > + ha.u.evtchn.send_port = port; > + ha.u.evtchn.flags = KVM_XEN_EVTCHN_DEASSIGN; > + > + ret = kvm_vm_ioctl(kvm_state, KVM_XEN_HVM_SET_ATTR, &ha); > + if (ret) { > + qemu_log_mask(LOG_GUEST_ERROR, "Failed to unbind kernel port %d: %s\n", > + port, strerror(ret)); > + } > +} > + > +static int assign_kernel_port(uint16_t type, evtchn_port_t port, > + uint32_t vcpu_id) > +{ > + CPUState *cpu = qemu_get_cpu(vcpu_id); > + struct kvm_xen_hvm_attr ha; > + > + if (!cpu) { > + return -ENOENT; > + } > + > + ha.type = KVM_XEN_ATTR_TYPE_EVTCHN; > + ha.u.evtchn.send_port = port; > + ha.u.evtchn.type = type; > + ha.u.evtchn.flags = 0; > + ha.u.evtchn.deliver.port.port = port; > + ha.u.evtchn.deliver.port.vcpu = kvm_arch_vcpu_id(cpu); > + ha.u.evtchn.deliver.port.priority = KVM_IRQ_ROUTING_XEN_EVTCHN_PRIO_2LEVEL; > + > + return kvm_vm_ioctl(kvm_state, KVM_XEN_HVM_SET_ATTR, &ha); > +} > + > static bool valid_port(evtchn_port_t port) > { > if (!port) { > @@ -549,6 +587,12 @@ static int close_port(XenEvtchnState *s, evtchn_port_t port) > p->type_val, 0); > break; > > + case EVTCHNSTAT_ipi: > + if (s->evtchn_in_kernel) { > + deassign_kernel_port(port); > + } > + break; > + > default: > break; > } > @@ -638,3 +682,28 @@ int xen_evtchn_bind_virq_op(struct evtchn_bind_virq *virq) > > return ret; > } > + > +int xen_evtchn_bind_ipi_op(struct evtchn_bind_ipi *ipi) > +{ > + XenEvtchnState *s = xen_evtchn_singleton; > + int ret; > + > + if (!s) { > + return -ENOTSUP; > + } > + > + if (!valid_vcpu(ipi->vcpu)) { > + return -ENOENT; > + } > + > + qemu_mutex_lock(&s->port_lock); > + > + ret = allocate_port(s, ipi->vcpu, EVTCHNSTAT_ipi, 0, &ipi->port); > + if (!ret && s->evtchn_in_kernel) { > + assign_kernel_port(EVTCHNSTAT_ipi, ipi->port, ipi->vcpu); Should the error not be propagated here? Paul > + } > + > + qemu_mutex_unlock(&s->port_lock); > + > + return ret; > +} > diff --git a/hw/i386/kvm/xen_evtchn.h b/hw/i386/kvm/xen_evtchn.h > index 0ea13dda3a..107f420848 100644 > --- a/hw/i386/kvm/xen_evtchn.h > +++ b/hw/i386/kvm/xen_evtchn.h > @@ -19,9 +19,11 @@ struct evtchn_status; > struct evtchn_close; > struct evtchn_unmask; > struct evtchn_bind_virq; > +struct evtchn_bind_ipi; > int xen_evtchn_status_op(struct evtchn_status *status); > int xen_evtchn_close_op(struct evtchn_close *close); > int xen_evtchn_unmask_op(struct evtchn_unmask *unmask); > int xen_evtchn_bind_virq_op(struct evtchn_bind_virq *virq); > +int xen_evtchn_bind_ipi_op(struct evtchn_bind_ipi *ipi); > > #endif /* QEMU_XEN_EVTCHN_H */ > diff --git a/target/i386/kvm/xen-emu.c b/target/i386/kvm/xen-emu.c > index 0c4988ad63..4a20ccdf78 100644 > --- a/target/i386/kvm/xen-emu.c > +++ b/target/i386/kvm/xen-emu.c > @@ -891,6 +891,21 @@ static bool kvm_xen_hcall_evtchn_op(struct kvm_xen_exit *exit, X86CPU *cpu, > } > break; > } > + case EVTCHNOP_bind_ipi: { > + struct evtchn_bind_ipi ipi; > + > + qemu_build_assert(sizeof(ipi) == 8); > + if (kvm_copy_from_gva(cs, arg, &ipi, sizeof(ipi))) { > + err = -EFAULT; > + break; > + } > + > + err = xen_evtchn_bind_ipi_op(&ipi); > + if (!err && kvm_copy_to_gva(cs, arg, &ipi, sizeof(ipi))) { > + err = -EFAULT; > + } > + break; > + } > default: > return false; > }
On Fri, 2023-02-10 at 13:55 +0000, Paul Durrant wrote: > > > + ret = allocate_port(s, ipi->vcpu, EVTCHNSTAT_ipi, 0, &ipi->port); > > + if (!ret && s->evtchn_in_kernel) { > > + assign_kernel_port(EVTCHNSTAT_ipi, ipi->port, ipi->vcpu); > > Should the error not be propagated here? Nah, we'll just fall back to doing it in userspace. The kernel acceleration is nice to have (and I mean *really* nice to have, for IPIs), but it works without.
On 10/02/2023 14:15, David Woodhouse wrote: > On Fri, 2023-02-10 at 13:55 +0000, Paul Durrant wrote: >> >>> + ret = allocate_port(s, ipi->vcpu, EVTCHNSTAT_ipi, 0, &ipi->port); >>> + if (!ret && s->evtchn_in_kernel) { >>> + assign_kernel_port(EVTCHNSTAT_ipi, ipi->port, ipi->vcpu); >> >> Should the error not be propagated here? > > > Nah, we'll just fall back to doing it in userspace. > > The kernel acceleration is nice to have (and I mean *really* nice to > have, for IPIs), but it works without. Ok. Reviewed-by: Paul Durrant <paul@xen.org>
diff --git a/hw/i386/kvm/xen_evtchn.c b/hw/i386/kvm/xen_evtchn.c index da2f5711dd..d8527483b9 100644 --- a/hw/i386/kvm/xen_evtchn.c +++ b/hw/i386/kvm/xen_evtchn.c @@ -13,6 +13,7 @@ #include "qemu/host-utils.h" #include "qemu/module.h" #include "qemu/main-loop.h" +#include "qemu/log.h" #include "qapi/error.h" #include "qom/object.h" #include "exec/target_page.h" @@ -231,6 +232,43 @@ static void inject_callback(XenEvtchnState *s, uint32_t vcpu) kvm_xen_inject_vcpu_callback_vector(vcpu, type); } +static void deassign_kernel_port(evtchn_port_t port) +{ + struct kvm_xen_hvm_attr ha; + int ret; + + ha.type = KVM_XEN_ATTR_TYPE_EVTCHN; + ha.u.evtchn.send_port = port; + ha.u.evtchn.flags = KVM_XEN_EVTCHN_DEASSIGN; + + ret = kvm_vm_ioctl(kvm_state, KVM_XEN_HVM_SET_ATTR, &ha); + if (ret) { + qemu_log_mask(LOG_GUEST_ERROR, "Failed to unbind kernel port %d: %s\n", + port, strerror(ret)); + } +} + +static int assign_kernel_port(uint16_t type, evtchn_port_t port, + uint32_t vcpu_id) +{ + CPUState *cpu = qemu_get_cpu(vcpu_id); + struct kvm_xen_hvm_attr ha; + + if (!cpu) { + return -ENOENT; + } + + ha.type = KVM_XEN_ATTR_TYPE_EVTCHN; + ha.u.evtchn.send_port = port; + ha.u.evtchn.type = type; + ha.u.evtchn.flags = 0; + ha.u.evtchn.deliver.port.port = port; + ha.u.evtchn.deliver.port.vcpu = kvm_arch_vcpu_id(cpu); + ha.u.evtchn.deliver.port.priority = KVM_IRQ_ROUTING_XEN_EVTCHN_PRIO_2LEVEL; + + return kvm_vm_ioctl(kvm_state, KVM_XEN_HVM_SET_ATTR, &ha); +} + static bool valid_port(evtchn_port_t port) { if (!port) { @@ -549,6 +587,12 @@ static int close_port(XenEvtchnState *s, evtchn_port_t port) p->type_val, 0); break; + case EVTCHNSTAT_ipi: + if (s->evtchn_in_kernel) { + deassign_kernel_port(port); + } + break; + default: break; } @@ -638,3 +682,28 @@ int xen_evtchn_bind_virq_op(struct evtchn_bind_virq *virq) return ret; } + +int xen_evtchn_bind_ipi_op(struct evtchn_bind_ipi *ipi) +{ + XenEvtchnState *s = xen_evtchn_singleton; + int ret; + + if (!s) { + return -ENOTSUP; + } + + if (!valid_vcpu(ipi->vcpu)) { + return -ENOENT; + } + + qemu_mutex_lock(&s->port_lock); + + ret = allocate_port(s, ipi->vcpu, EVTCHNSTAT_ipi, 0, &ipi->port); + if (!ret && s->evtchn_in_kernel) { + assign_kernel_port(EVTCHNSTAT_ipi, ipi->port, ipi->vcpu); + } + + qemu_mutex_unlock(&s->port_lock); + + return ret; +} diff --git a/hw/i386/kvm/xen_evtchn.h b/hw/i386/kvm/xen_evtchn.h index 0ea13dda3a..107f420848 100644 --- a/hw/i386/kvm/xen_evtchn.h +++ b/hw/i386/kvm/xen_evtchn.h @@ -19,9 +19,11 @@ struct evtchn_status; struct evtchn_close; struct evtchn_unmask; struct evtchn_bind_virq; +struct evtchn_bind_ipi; int xen_evtchn_status_op(struct evtchn_status *status); int xen_evtchn_close_op(struct evtchn_close *close); int xen_evtchn_unmask_op(struct evtchn_unmask *unmask); int xen_evtchn_bind_virq_op(struct evtchn_bind_virq *virq); +int xen_evtchn_bind_ipi_op(struct evtchn_bind_ipi *ipi); #endif /* QEMU_XEN_EVTCHN_H */ diff --git a/target/i386/kvm/xen-emu.c b/target/i386/kvm/xen-emu.c index 0c4988ad63..4a20ccdf78 100644 --- a/target/i386/kvm/xen-emu.c +++ b/target/i386/kvm/xen-emu.c @@ -891,6 +891,21 @@ static bool kvm_xen_hcall_evtchn_op(struct kvm_xen_exit *exit, X86CPU *cpu, } break; } + case EVTCHNOP_bind_ipi: { + struct evtchn_bind_ipi ipi; + + qemu_build_assert(sizeof(ipi) == 8); + if (kvm_copy_from_gva(cs, arg, &ipi, sizeof(ipi))) { + err = -EFAULT; + break; + } + + err = xen_evtchn_bind_ipi_op(&ipi); + if (!err && kvm_copy_to_gva(cs, arg, &ipi, sizeof(ipi))) { + err = -EFAULT; + } + break; + } default: return false; }