1 /* 2 * Hyper-V guest/hypervisor interaction 3 * 4 * Copyright (c) 2015-2018 Virtuozzo International GmbH. 5 * 6 * This work is licensed under the terms of the GNU GPL, version 2 or later. 7 * See the COPYING file in the top-level directory. 8 */ 9 10 #include "qemu/osdep.h" 11 #include "qemu/main-loop.h" 12 #include "sysemu/kvm.h" 13 #include "hw/hyperv/hyperv.h" 14 15 struct HvSintRoute { 16 uint32_t sint; 17 CPUState *cs; 18 int gsi; 19 EventNotifier sint_set_notifier; 20 EventNotifier sint_ack_notifier; 21 HvSintAckClb sint_ack_clb; 22 void *sint_ack_clb_data; 23 unsigned refcount; 24 }; 25 26 static CPUState *hyperv_find_vcpu(uint32_t vp_index) 27 { 28 CPUState *cs = qemu_get_cpu(vp_index); 29 assert(hyperv_vp_index(cs) == vp_index); 30 return cs; 31 } 32 33 static void kvm_hv_sint_ack_handler(EventNotifier *notifier) 34 { 35 HvSintRoute *sint_route = container_of(notifier, HvSintRoute, 36 sint_ack_notifier); 37 event_notifier_test_and_clear(notifier); 38 sint_route->sint_ack_clb(sint_route->sint_ack_clb_data); 39 } 40 41 HvSintRoute *hyperv_sint_route_new(uint32_t vp_index, uint32_t sint, 42 HvSintAckClb sint_ack_clb, 43 void *sint_ack_clb_data) 44 { 45 HvSintRoute *sint_route; 46 EventNotifier *ack_notifier; 47 int r, gsi; 48 CPUState *cs; 49 50 cs = hyperv_find_vcpu(vp_index); 51 if (!cs) { 52 return NULL; 53 } 54 55 sint_route = g_new0(HvSintRoute, 1); 56 r = event_notifier_init(&sint_route->sint_set_notifier, false); 57 if (r) { 58 goto err; 59 } 60 61 ack_notifier = sint_ack_clb ? &sint_route->sint_ack_notifier : NULL; 62 if (ack_notifier) { 63 r = event_notifier_init(ack_notifier, false); 64 if (r) { 65 goto err_sint_set_notifier; 66 } 67 68 event_notifier_set_handler(ack_notifier, kvm_hv_sint_ack_handler); 69 } 70 71 gsi = kvm_irqchip_add_hv_sint_route(kvm_state, vp_index, sint); 72 if (gsi < 0) { 73 goto err_gsi; 74 } 75 76 r = kvm_irqchip_add_irqfd_notifier_gsi(kvm_state, 77 &sint_route->sint_set_notifier, 78 ack_notifier, gsi); 79 if (r) { 80 goto err_irqfd; 81 } 82 sint_route->gsi = gsi; 83 sint_route->sint_ack_clb = sint_ack_clb; 84 sint_route->sint_ack_clb_data = sint_ack_clb_data; 85 sint_route->cs = cs; 86 sint_route->sint = sint; 87 sint_route->refcount = 1; 88 89 return sint_route; 90 91 err_irqfd: 92 kvm_irqchip_release_virq(kvm_state, gsi); 93 err_gsi: 94 if (ack_notifier) { 95 event_notifier_set_handler(ack_notifier, NULL); 96 event_notifier_cleanup(ack_notifier); 97 } 98 err_sint_set_notifier: 99 event_notifier_cleanup(&sint_route->sint_set_notifier); 100 err: 101 g_free(sint_route); 102 103 return NULL; 104 } 105 106 void hyperv_sint_route_ref(HvSintRoute *sint_route) 107 { 108 sint_route->refcount++; 109 } 110 111 void hyperv_sint_route_unref(HvSintRoute *sint_route) 112 { 113 if (!sint_route) { 114 return; 115 } 116 117 assert(sint_route->refcount > 0); 118 119 if (--sint_route->refcount) { 120 return; 121 } 122 123 kvm_irqchip_remove_irqfd_notifier_gsi(kvm_state, 124 &sint_route->sint_set_notifier, 125 sint_route->gsi); 126 kvm_irqchip_release_virq(kvm_state, sint_route->gsi); 127 if (sint_route->sint_ack_clb) { 128 event_notifier_set_handler(&sint_route->sint_ack_notifier, NULL); 129 event_notifier_cleanup(&sint_route->sint_ack_notifier); 130 } 131 event_notifier_cleanup(&sint_route->sint_set_notifier); 132 g_free(sint_route); 133 } 134 135 int hyperv_sint_route_set_sint(HvSintRoute *sint_route) 136 { 137 return event_notifier_set(&sint_route->sint_set_notifier); 138 } 139