| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823 |
- // SPDX-License-Identifier: GPL-2.0-only
- /*
- * Copyright (c) 2023, Microsoft Corporation.
- *
- * mshv_root module's main interrupt handler and associated functionality.
- *
- * Authors: Microsoft Linux virtualization team
- */
- #include <linux/kernel.h>
- #include <linux/slab.h>
- #include <linux/mm.h>
- #include <linux/interrupt.h>
- #include <linux/io.h>
- #include <linux/random.h>
- #include <linux/cpuhotplug.h>
- #include <linux/reboot.h>
- #include <asm/mshyperv.h>
- #include <linux/acpi.h>
- #include "mshv_eventfd.h"
- #include "mshv.h"
- static int synic_cpuhp_online;
- static struct hv_synic_pages __percpu *synic_pages;
- static int mshv_sint_vector = -1; /* hwirq for the SynIC SINTs */
- static int mshv_sint_irq = -1; /* Linux IRQ for mshv_sint_vector */
- static u32 synic_event_ring_get_queued_port(u32 sint_index)
- {
- struct hv_synic_event_ring_page **event_ring_page;
- volatile struct hv_synic_event_ring *ring;
- struct hv_synic_pages *spages;
- u8 **synic_eventring_tail;
- u32 message;
- u8 tail;
- spages = this_cpu_ptr(synic_pages);
- event_ring_page = &spages->synic_event_ring_page;
- synic_eventring_tail = (u8 **)this_cpu_ptr(hv_synic_eventring_tail);
- if (unlikely(!*synic_eventring_tail)) {
- pr_debug("Missing synic event ring tail!\n");
- return 0;
- }
- tail = (*synic_eventring_tail)[sint_index];
- if (unlikely(!*event_ring_page)) {
- pr_debug("Missing synic event ring page!\n");
- return 0;
- }
- ring = &(*event_ring_page)->sint_event_ring[sint_index];
- /*
- * Get the message.
- */
- message = ring->data[tail];
- if (!message) {
- if (ring->ring_full) {
- /*
- * Ring is marked full, but we would have consumed all
- * the messages. Notify the hypervisor that ring is now
- * empty and check again.
- */
- ring->ring_full = 0;
- hv_call_notify_port_ring_empty(sint_index);
- message = ring->data[tail];
- }
- if (!message) {
- ring->signal_masked = 0;
- /*
- * Unmask the signal and sync with hypervisor
- * before one last check for any message.
- */
- mb();
- message = ring->data[tail];
- /*
- * Ok, lets bail out.
- */
- if (!message)
- return 0;
- }
- ring->signal_masked = 1;
- }
- /*
- * Clear the message in the ring buffer.
- */
- ring->data[tail] = 0;
- if (++tail == HV_SYNIC_EVENT_RING_MESSAGE_COUNT)
- tail = 0;
- (*synic_eventring_tail)[sint_index] = tail;
- return message;
- }
- static bool
- mshv_doorbell_isr(struct hv_message *msg)
- {
- struct hv_notification_message_payload *notification;
- u32 port;
- if (msg->header.message_type != HVMSG_SYNIC_SINT_INTERCEPT)
- return false;
- notification = (struct hv_notification_message_payload *)msg->u.payload;
- if (notification->sint_index != HV_SYNIC_DOORBELL_SINT_INDEX)
- return false;
- while ((port = synic_event_ring_get_queued_port(HV_SYNIC_DOORBELL_SINT_INDEX))) {
- struct port_table_info ptinfo = { 0 };
- if (mshv_portid_lookup(port, &ptinfo)) {
- pr_debug("Failed to get port info from port_table!\n");
- continue;
- }
- if (ptinfo.hv_port_type != HV_PORT_TYPE_DOORBELL) {
- pr_debug("Not a doorbell port!, port: %d, port_type: %d\n",
- port, ptinfo.hv_port_type);
- continue;
- }
- /* Invoke the callback */
- ptinfo.hv_port_doorbell.doorbell_cb(port,
- ptinfo.hv_port_doorbell.data);
- }
- return true;
- }
- static bool mshv_async_call_completion_isr(struct hv_message *msg)
- {
- bool handled = false;
- struct hv_async_completion_message_payload *async_msg;
- struct mshv_partition *partition;
- u64 partition_id;
- if (msg->header.message_type != HVMSG_ASYNC_CALL_COMPLETION)
- goto out;
- async_msg =
- (struct hv_async_completion_message_payload *)msg->u.payload;
- partition_id = async_msg->partition_id;
- /*
- * Hold this lock for the rest of the isr, because the partition could
- * be released anytime.
- * e.g. the MSHV_RUN_VP thread could wake on another cpu; it could
- * release the partition unless we hold this!
- */
- rcu_read_lock();
- partition = mshv_partition_find(partition_id);
- if (unlikely(!partition)) {
- pr_debug("failed to find partition %llu\n", partition_id);
- goto unlock_out;
- }
- partition->async_hypercall_status = async_msg->status;
- complete(&partition->async_hypercall);
- handled = true;
- unlock_out:
- rcu_read_unlock();
- out:
- return handled;
- }
- static void kick_vp(struct mshv_vp *vp)
- {
- atomic64_inc(&vp->run.vp_signaled_count);
- vp->run.kicked_by_hv = 1;
- wake_up(&vp->run.vp_suspend_queue);
- }
- static void
- handle_bitset_message(const struct hv_vp_signal_bitset_scheduler_message *msg)
- {
- int bank_idx, vps_signaled = 0, bank_mask_size;
- struct mshv_partition *partition;
- const struct hv_vpset *vpset;
- const u64 *bank_contents;
- u64 partition_id = msg->partition_id;
- if (msg->vp_bitset.bitset.format != HV_GENERIC_SET_SPARSE_4K) {
- pr_debug("scheduler message format is not HV_GENERIC_SET_SPARSE_4K");
- return;
- }
- if (msg->vp_count == 0) {
- pr_debug("scheduler message with no VP specified");
- return;
- }
- rcu_read_lock();
- partition = mshv_partition_find(partition_id);
- if (unlikely(!partition)) {
- pr_debug("failed to find partition %llu\n", partition_id);
- goto unlock_out;
- }
- vpset = &msg->vp_bitset.bitset;
- bank_idx = -1;
- bank_contents = vpset->bank_contents;
- bank_mask_size = sizeof(vpset->valid_bank_mask) * BITS_PER_BYTE;
- while (true) {
- int vp_bank_idx = -1;
- int vp_bank_size = sizeof(*bank_contents) * BITS_PER_BYTE;
- int vp_index;
- bank_idx = find_next_bit((unsigned long *)&vpset->valid_bank_mask,
- bank_mask_size, bank_idx + 1);
- if (bank_idx == bank_mask_size)
- break;
- while (true) {
- struct mshv_vp *vp;
- vp_bank_idx = find_next_bit((unsigned long *)bank_contents,
- vp_bank_size, vp_bank_idx + 1);
- if (vp_bank_idx == vp_bank_size)
- break;
- vp_index = (bank_idx * vp_bank_size) + vp_bank_idx;
- /* This shouldn't happen, but just in case. */
- if (unlikely(vp_index >= MSHV_MAX_VPS)) {
- pr_debug("VP index %u out of bounds\n",
- vp_index);
- goto unlock_out;
- }
- vp = partition->pt_vp_array[vp_index];
- if (unlikely(!vp)) {
- pr_debug("failed to find VP %u\n", vp_index);
- goto unlock_out;
- }
- kick_vp(vp);
- vps_signaled++;
- }
- bank_contents++;
- }
- unlock_out:
- rcu_read_unlock();
- if (vps_signaled != msg->vp_count)
- pr_debug("asked to signal %u VPs but only did %u\n",
- msg->vp_count, vps_signaled);
- }
- static void
- handle_pair_message(const struct hv_vp_signal_pair_scheduler_message *msg)
- {
- struct mshv_partition *partition = NULL;
- struct mshv_vp *vp;
- int idx;
- rcu_read_lock();
- for (idx = 0; idx < msg->vp_count; idx++) {
- u64 partition_id = msg->partition_ids[idx];
- u32 vp_index = msg->vp_indexes[idx];
- if (idx == 0 || partition->pt_id != partition_id) {
- partition = mshv_partition_find(partition_id);
- if (unlikely(!partition)) {
- pr_debug("failed to find partition %llu\n",
- partition_id);
- break;
- }
- }
- /* This shouldn't happen, but just in case. */
- if (unlikely(vp_index >= MSHV_MAX_VPS)) {
- pr_debug("VP index %u out of bounds\n", vp_index);
- break;
- }
- vp = partition->pt_vp_array[vp_index];
- if (!vp) {
- pr_debug("failed to find VP %u\n", vp_index);
- break;
- }
- kick_vp(vp);
- }
- rcu_read_unlock();
- }
- static bool
- mshv_scheduler_isr(struct hv_message *msg)
- {
- if (msg->header.message_type != HVMSG_SCHEDULER_VP_SIGNAL_BITSET &&
- msg->header.message_type != HVMSG_SCHEDULER_VP_SIGNAL_PAIR)
- return false;
- if (msg->header.message_type == HVMSG_SCHEDULER_VP_SIGNAL_BITSET)
- handle_bitset_message((struct hv_vp_signal_bitset_scheduler_message *)
- msg->u.payload);
- else
- handle_pair_message((struct hv_vp_signal_pair_scheduler_message *)
- msg->u.payload);
- return true;
- }
- static bool
- mshv_intercept_isr(struct hv_message *msg)
- {
- struct mshv_partition *partition;
- bool handled = false;
- struct mshv_vp *vp;
- u64 partition_id;
- u32 vp_index;
- partition_id = msg->header.sender;
- rcu_read_lock();
- partition = mshv_partition_find(partition_id);
- if (unlikely(!partition)) {
- pr_debug("failed to find partition %llu\n",
- partition_id);
- goto unlock_out;
- }
- if (msg->header.message_type == HVMSG_X64_APIC_EOI) {
- /*
- * Check if this gsi is registered in the
- * ack_notifier list and invoke the callback
- * if registered.
- */
- /*
- * If there is a notifier, the ack callback is supposed
- * to handle the VMEXIT. So we need not pass this message
- * to vcpu thread.
- */
- struct hv_x64_apic_eoi_message *eoi_msg =
- (struct hv_x64_apic_eoi_message *)&msg->u.payload[0];
- if (mshv_notify_acked_gsi(partition, eoi_msg->interrupt_vector)) {
- handled = true;
- goto unlock_out;
- }
- }
- /*
- * We should get an opaque intercept message here for all intercept
- * messages, since we're using the mapped VP intercept message page.
- *
- * The intercept message will have been placed in intercept message
- * page at this point.
- *
- * Make sure the message type matches our expectation.
- */
- if (msg->header.message_type != HVMSG_OPAQUE_INTERCEPT) {
- pr_debug("wrong message type %d", msg->header.message_type);
- goto unlock_out;
- }
- /*
- * Since we directly index the vp, and it has to exist for us to be here
- * (because the vp is only deleted when the partition is), no additional
- * locking is needed here
- */
- vp_index =
- ((struct hv_opaque_intercept_message *)msg->u.payload)->vp_index;
- vp = partition->pt_vp_array[vp_index];
- if (unlikely(!vp)) {
- pr_debug("failed to find VP %u\n", vp_index);
- goto unlock_out;
- }
- kick_vp(vp);
- handled = true;
- unlock_out:
- rcu_read_unlock();
- return handled;
- }
- void mshv_isr(void)
- {
- struct hv_synic_pages *spages = this_cpu_ptr(synic_pages);
- struct hv_message_page **msg_page = &spages->hyp_synic_message_page;
- struct hv_message *msg;
- bool handled;
- if (unlikely(!(*msg_page))) {
- pr_debug("Missing synic page!\n");
- return;
- }
- msg = &((*msg_page)->sint_message[HV_SYNIC_INTERCEPTION_SINT_INDEX]);
- /*
- * If the type isn't set, there isn't really a message;
- * it may be some other hyperv interrupt
- */
- if (msg->header.message_type == HVMSG_NONE)
- return;
- handled = mshv_doorbell_isr(msg);
- if (!handled)
- handled = mshv_scheduler_isr(msg);
- if (!handled)
- handled = mshv_async_call_completion_isr(msg);
- if (!handled)
- handled = mshv_intercept_isr(msg);
- if (handled) {
- /*
- * Acknowledge message with hypervisor if another message is
- * pending.
- */
- msg->header.message_type = HVMSG_NONE;
- /*
- * Ensure the write is complete so the hypervisor will deliver
- * the next message if available.
- */
- mb();
- if (msg->header.message_flags.msg_pending)
- hv_set_non_nested_msr(HV_MSR_EOM, 0);
- add_interrupt_randomness(mshv_sint_vector);
- } else {
- pr_warn_once("%s: unknown message type 0x%x\n", __func__,
- msg->header.message_type);
- }
- }
- static int mshv_synic_cpu_init(unsigned int cpu)
- {
- union hv_synic_simp simp;
- union hv_synic_siefp siefp;
- union hv_synic_sirbp sirbp;
- union hv_synic_sint sint;
- union hv_synic_scontrol sctrl;
- struct hv_synic_pages *spages = this_cpu_ptr(synic_pages);
- struct hv_message_page **msg_page = &spages->hyp_synic_message_page;
- struct hv_synic_event_flags_page **event_flags_page =
- &spages->synic_event_flags_page;
- struct hv_synic_event_ring_page **event_ring_page =
- &spages->synic_event_ring_page;
- /* Setup the Synic's message page */
- simp.as_uint64 = hv_get_non_nested_msr(HV_MSR_SIMP);
- simp.simp_enabled = true;
- *msg_page = memremap(simp.base_simp_gpa << HV_HYP_PAGE_SHIFT,
- HV_HYP_PAGE_SIZE,
- MEMREMAP_WB);
- if (!(*msg_page))
- return -EFAULT;
- hv_set_non_nested_msr(HV_MSR_SIMP, simp.as_uint64);
- /* Setup the Synic's event flags page */
- siefp.as_uint64 = hv_get_non_nested_msr(HV_MSR_SIEFP);
- siefp.siefp_enabled = true;
- *event_flags_page = memremap(siefp.base_siefp_gpa << PAGE_SHIFT,
- PAGE_SIZE, MEMREMAP_WB);
- if (!(*event_flags_page))
- goto cleanup;
- hv_set_non_nested_msr(HV_MSR_SIEFP, siefp.as_uint64);
- /* Setup the Synic's event ring page */
- sirbp.as_uint64 = hv_get_non_nested_msr(HV_MSR_SIRBP);
- sirbp.sirbp_enabled = true;
- *event_ring_page = memremap(sirbp.base_sirbp_gpa << PAGE_SHIFT,
- PAGE_SIZE, MEMREMAP_WB);
- if (!(*event_ring_page))
- goto cleanup;
- hv_set_non_nested_msr(HV_MSR_SIRBP, sirbp.as_uint64);
- if (mshv_sint_irq != -1)
- enable_percpu_irq(mshv_sint_irq, 0);
- /* Enable intercepts */
- sint.as_uint64 = 0;
- sint.vector = mshv_sint_vector;
- sint.masked = false;
- sint.auto_eoi = hv_recommend_using_aeoi();
- hv_set_non_nested_msr(HV_MSR_SINT0 + HV_SYNIC_INTERCEPTION_SINT_INDEX,
- sint.as_uint64);
- /* Doorbell SINT */
- sint.as_uint64 = 0;
- sint.vector = mshv_sint_vector;
- sint.masked = false;
- sint.as_intercept = 1;
- sint.auto_eoi = hv_recommend_using_aeoi();
- hv_set_non_nested_msr(HV_MSR_SINT0 + HV_SYNIC_DOORBELL_SINT_INDEX,
- sint.as_uint64);
- /* Enable global synic bit */
- sctrl.as_uint64 = hv_get_non_nested_msr(HV_MSR_SCONTROL);
- sctrl.enable = 1;
- hv_set_non_nested_msr(HV_MSR_SCONTROL, sctrl.as_uint64);
- return 0;
- cleanup:
- if (*event_ring_page) {
- sirbp.sirbp_enabled = false;
- hv_set_non_nested_msr(HV_MSR_SIRBP, sirbp.as_uint64);
- memunmap(*event_ring_page);
- }
- if (*event_flags_page) {
- siefp.siefp_enabled = false;
- hv_set_non_nested_msr(HV_MSR_SIEFP, siefp.as_uint64);
- memunmap(*event_flags_page);
- }
- if (*msg_page) {
- simp.simp_enabled = false;
- hv_set_non_nested_msr(HV_MSR_SIMP, simp.as_uint64);
- memunmap(*msg_page);
- }
- return -EFAULT;
- }
- static int mshv_synic_cpu_exit(unsigned int cpu)
- {
- union hv_synic_sint sint;
- union hv_synic_simp simp;
- union hv_synic_siefp siefp;
- union hv_synic_sirbp sirbp;
- union hv_synic_scontrol sctrl;
- struct hv_synic_pages *spages = this_cpu_ptr(synic_pages);
- struct hv_message_page **msg_page = &spages->hyp_synic_message_page;
- struct hv_synic_event_flags_page **event_flags_page =
- &spages->synic_event_flags_page;
- struct hv_synic_event_ring_page **event_ring_page =
- &spages->synic_event_ring_page;
- /* Disable the interrupt */
- sint.as_uint64 = hv_get_non_nested_msr(HV_MSR_SINT0 + HV_SYNIC_INTERCEPTION_SINT_INDEX);
- sint.masked = true;
- hv_set_non_nested_msr(HV_MSR_SINT0 + HV_SYNIC_INTERCEPTION_SINT_INDEX,
- sint.as_uint64);
- /* Disable Doorbell SINT */
- sint.as_uint64 = hv_get_non_nested_msr(HV_MSR_SINT0 + HV_SYNIC_DOORBELL_SINT_INDEX);
- sint.masked = true;
- hv_set_non_nested_msr(HV_MSR_SINT0 + HV_SYNIC_DOORBELL_SINT_INDEX,
- sint.as_uint64);
- if (mshv_sint_irq != -1)
- disable_percpu_irq(mshv_sint_irq);
- /* Disable Synic's event ring page */
- sirbp.as_uint64 = hv_get_non_nested_msr(HV_MSR_SIRBP);
- sirbp.sirbp_enabled = false;
- hv_set_non_nested_msr(HV_MSR_SIRBP, sirbp.as_uint64);
- memunmap(*event_ring_page);
- /* Disable Synic's event flags page */
- siefp.as_uint64 = hv_get_non_nested_msr(HV_MSR_SIEFP);
- siefp.siefp_enabled = false;
- hv_set_non_nested_msr(HV_MSR_SIEFP, siefp.as_uint64);
- memunmap(*event_flags_page);
- /* Disable Synic's message page */
- simp.as_uint64 = hv_get_non_nested_msr(HV_MSR_SIMP);
- simp.simp_enabled = false;
- hv_set_non_nested_msr(HV_MSR_SIMP, simp.as_uint64);
- memunmap(*msg_page);
- /* Disable global synic bit */
- sctrl.as_uint64 = hv_get_non_nested_msr(HV_MSR_SCONTROL);
- sctrl.enable = 0;
- hv_set_non_nested_msr(HV_MSR_SCONTROL, sctrl.as_uint64);
- return 0;
- }
- int
- mshv_register_doorbell(u64 partition_id, doorbell_cb_t doorbell_cb, void *data,
- u64 gpa, u64 val, u64 flags)
- {
- struct hv_connection_info connection_info = { 0 };
- union hv_connection_id connection_id = { 0 };
- struct port_table_info *port_table_info;
- struct hv_port_info port_info = { 0 };
- union hv_port_id port_id = { 0 };
- int ret;
- port_table_info = kmalloc_obj(*port_table_info);
- if (!port_table_info)
- return -ENOMEM;
- port_table_info->hv_port_type = HV_PORT_TYPE_DOORBELL;
- port_table_info->hv_port_doorbell.doorbell_cb = doorbell_cb;
- port_table_info->hv_port_doorbell.data = data;
- ret = mshv_portid_alloc(port_table_info);
- if (ret < 0) {
- kfree(port_table_info);
- return ret;
- }
- port_id.u.id = ret;
- port_info.port_type = HV_PORT_TYPE_DOORBELL;
- port_info.doorbell_port_info.target_sint = HV_SYNIC_DOORBELL_SINT_INDEX;
- port_info.doorbell_port_info.target_vp = HV_ANY_VP;
- ret = hv_call_create_port(hv_current_partition_id, port_id, partition_id,
- &port_info,
- 0, 0, NUMA_NO_NODE);
- if (ret < 0) {
- mshv_portid_free(port_id.u.id);
- return ret;
- }
- connection_id.u.id = port_id.u.id;
- connection_info.port_type = HV_PORT_TYPE_DOORBELL;
- connection_info.doorbell_connection_info.gpa = gpa;
- connection_info.doorbell_connection_info.trigger_value = val;
- connection_info.doorbell_connection_info.flags = flags;
- ret = hv_call_connect_port(hv_current_partition_id, port_id, partition_id,
- connection_id, &connection_info, 0, NUMA_NO_NODE);
- if (ret < 0) {
- hv_call_delete_port(hv_current_partition_id, port_id);
- mshv_portid_free(port_id.u.id);
- return ret;
- }
- // lets use the port_id as the doorbell_id
- return port_id.u.id;
- }
- void
- mshv_unregister_doorbell(u64 partition_id, int doorbell_portid)
- {
- union hv_port_id port_id = { 0 };
- union hv_connection_id connection_id = { 0 };
- connection_id.u.id = doorbell_portid;
- hv_call_disconnect_port(partition_id, connection_id);
- port_id.u.id = doorbell_portid;
- hv_call_delete_port(hv_current_partition_id, port_id);
- mshv_portid_free(doorbell_portid);
- }
- static int mshv_synic_reboot_notify(struct notifier_block *nb,
- unsigned long code, void *unused)
- {
- if (!hv_root_partition())
- return 0;
- cpuhp_remove_state(synic_cpuhp_online);
- return 0;
- }
- static struct notifier_block mshv_synic_reboot_nb = {
- .notifier_call = mshv_synic_reboot_notify,
- };
- #ifndef HYPERVISOR_CALLBACK_VECTOR
- static DEFINE_PER_CPU(long, mshv_evt);
- static irqreturn_t mshv_percpu_isr(int irq, void *dev_id)
- {
- mshv_isr();
- return IRQ_HANDLED;
- }
- #ifdef CONFIG_ACPI
- static int __init mshv_acpi_setup_sint_irq(void)
- {
- return acpi_register_gsi(NULL, mshv_sint_vector, ACPI_EDGE_SENSITIVE,
- ACPI_ACTIVE_HIGH);
- }
- static void mshv_acpi_cleanup_sint_irq(void)
- {
- acpi_unregister_gsi(mshv_sint_vector);
- }
- #else
- static int __init mshv_acpi_setup_sint_irq(void)
- {
- return -ENODEV;
- }
- static void mshv_acpi_cleanup_sint_irq(void)
- {
- }
- #endif
- static int __init mshv_sint_vector_setup(void)
- {
- int ret;
- struct hv_register_assoc reg = {
- .name = HV_ARM64_REGISTER_SINT_RESERVED_INTERRUPT_ID,
- };
- union hv_input_vtl input_vtl = { 0 };
- if (acpi_disabled)
- return -ENODEV;
- ret = hv_call_get_vp_registers(HV_VP_INDEX_SELF, HV_PARTITION_ID_SELF,
- 1, input_vtl, ®);
- if (ret || !reg.value.reg64)
- return -ENODEV;
- mshv_sint_vector = reg.value.reg64;
- ret = mshv_acpi_setup_sint_irq();
- if (ret < 0) {
- pr_err("Failed to setup IRQ for MSHV SINT vector %d: %d\n",
- mshv_sint_vector, ret);
- goto out_fail;
- }
- mshv_sint_irq = ret;
- ret = request_percpu_irq(mshv_sint_irq, mshv_percpu_isr, "MSHV",
- &mshv_evt);
- if (ret)
- goto out_unregister;
- return 0;
- out_unregister:
- mshv_acpi_cleanup_sint_irq();
- out_fail:
- return ret;
- }
- static void mshv_sint_vector_cleanup(void)
- {
- free_percpu_irq(mshv_sint_irq, &mshv_evt);
- mshv_acpi_cleanup_sint_irq();
- }
- #else /* !HYPERVISOR_CALLBACK_VECTOR */
- static int __init mshv_sint_vector_setup(void)
- {
- mshv_sint_vector = HYPERVISOR_CALLBACK_VECTOR;
- return 0;
- }
- static void mshv_sint_vector_cleanup(void)
- {
- }
- #endif /* HYPERVISOR_CALLBACK_VECTOR */
- int __init mshv_synic_init(struct device *dev)
- {
- int ret = 0;
- ret = mshv_sint_vector_setup();
- if (ret)
- return ret;
- synic_pages = alloc_percpu(struct hv_synic_pages);
- if (!synic_pages) {
- dev_err(dev, "Failed to allocate percpu synic page\n");
- ret = -ENOMEM;
- goto sint_vector_cleanup;
- }
- ret = cpuhp_setup_state(CPUHP_AP_ONLINE_DYN, "mshv_synic",
- mshv_synic_cpu_init,
- mshv_synic_cpu_exit);
- if (ret < 0) {
- dev_err(dev, "Failed to setup cpu hotplug state: %i\n", ret);
- goto free_synic_pages;
- }
- synic_cpuhp_online = ret;
- ret = register_reboot_notifier(&mshv_synic_reboot_nb);
- if (ret)
- goto remove_cpuhp_state;
- return 0;
- remove_cpuhp_state:
- cpuhp_remove_state(synic_cpuhp_online);
- free_synic_pages:
- free_percpu(synic_pages);
- sint_vector_cleanup:
- mshv_sint_vector_cleanup();
- return ret;
- }
- void mshv_synic_exit(void)
- {
- unregister_reboot_notifier(&mshv_synic_reboot_nb);
- cpuhp_remove_state(synic_cpuhp_online);
- free_percpu(synic_pages);
- mshv_sint_vector_cleanup();
- }
|