/* * Hyper-V guest/hypervisor interaction * * Copyright (c) 2015-2018 Virtuozzo International GmbH. * * 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 "sysemu/kvm.h" #include "hw/hyperv/hyperv.h" struct HvSintRoute { uint32_t sint; CPUState *cs; int gsi; EventNotifier sint_set_notifier; EventNotifier sint_ack_notifier; HvSintAckClb sint_ack_clb; void *sint_ack_clb_data; unsigned refcount; }; static CPUState *hyperv_find_vcpu(uint32_t vp_index) { CPUState *cs = qemu_get_cpu(vp_index); assert(hyperv_vp_index(cs) == vp_index); return cs; } static void kvm_hv_sint_ack_handler(EventNotifier *notifier) { HvSintRoute *sint_route = container_of(notifier, HvSintRoute, sint_ack_notifier); event_notifier_test_and_clear(notifier); sint_route->sint_ack_clb(sint_route->sint_ack_clb_data); } HvSintRoute *hyperv_sint_route_new(uint32_t vp_index, uint32_t sint, HvSintAckClb sint_ack_clb, void *sint_ack_clb_data) { HvSintRoute *sint_route; EventNotifier *ack_notifier; int r, gsi; CPUState *cs; cs = hyperv_find_vcpu(vp_index); if (!cs) { return NULL; } sint_route = g_new0(HvSintRoute, 1); r = event_notifier_init(&sint_route->sint_set_notifier, false); if (r) { goto err; } ack_notifier = sint_ack_clb ? &sint_route->sint_ack_notifier : NULL; if (ack_notifier) { r = event_notifier_init(ack_notifier, false); if (r) { goto err_sint_set_notifier; } event_notifier_set_handler(ack_notifier, kvm_hv_sint_ack_handler); } gsi = kvm_irqchip_add_hv_sint_route(kvm_state, vp_index, sint); if (gsi < 0) { goto err_gsi; } r = kvm_irqchip_add_irqfd_notifier_gsi(kvm_state, &sint_route->sint_set_notifier, ack_notifier, gsi); if (r) { goto err_irqfd; } sint_route->gsi = gsi; sint_route->sint_ack_clb = sint_ack_clb; sint_route->sint_ack_clb_data = sint_ack_clb_data; sint_route->cs = cs; sint_route->sint = sint; sint_route->refcount = 1; return sint_route; err_irqfd: kvm_irqchip_release_virq(kvm_state, gsi); err_gsi: if (ack_notifier) { event_notifier_set_handler(ack_notifier, NULL); event_notifier_cleanup(ack_notifier); } err_sint_set_notifier: event_notifier_cleanup(&sint_route->sint_set_notifier); err: g_free(sint_route); return NULL; } void hyperv_sint_route_ref(HvSintRoute *sint_route) { sint_route->refcount++; } void hyperv_sint_route_unref(HvSintRoute *sint_route) { if (!sint_route) { return; } assert(sint_route->refcount > 0); if (--sint_route->refcount) { return; } kvm_irqchip_remove_irqfd_notifier_gsi(kvm_state, &sint_route->sint_set_notifier, sint_route->gsi); kvm_irqchip_release_virq(kvm_state, sint_route->gsi); if (sint_route->sint_ack_clb) { event_notifier_set_handler(&sint_route->sint_ack_notifier, NULL); event_notifier_cleanup(&sint_route->sint_ack_notifier); } event_notifier_cleanup(&sint_route->sint_set_notifier); g_free(sint_route); } int hyperv_sint_route_set_sint(HvSintRoute *sint_route) { return event_notifier_set(&sint_route->sint_set_notifier); }