File Index
Files and directories
Sign in
FILE

mshv_irq.c

File preview with syntax highlighting.

linux-6.19.14/drivers/hv/mshv_irq.c C
  1// SPDX-License-Identifier: GPL-2.0-only
  2/*
  3 * Copyright (c) 2023, Microsoft Corporation.
  4 *
  5 * Authors: Microsoft Linux virtualization team
  6 */
  7
  8#include <linux/kernel.h>
  9#include <linux/module.h>
 10#include <linux/slab.h>
 11#include <asm/mshyperv.h>
 12
 13#include "mshv_eventfd.h"
 14#include "mshv.h"
 15#include "mshv_root.h"
 16
 17/* called from the ioctl code, user wants to update the guest irq table */
 18int mshv_update_routing_table(struct mshv_partition *partition,
 19			      const struct mshv_user_irq_entry *ue,
 20			      unsigned int numents)
 21{
 22	struct mshv_girq_routing_table *new = NULL, *old;
 23	u32 i, nr_rt_entries = 0;
 24	int r = 0;
 25
 26	if (numents == 0)
 27		goto swap_routes;
 28
 29	for (i = 0; i < numents; i++) {
 30		if (ue[i].gsi >= MSHV_MAX_GUEST_IRQS)
 31			return -EINVAL;
 32
 33		if (ue[i].address_hi)
 34			return -EINVAL;
 35
 36		nr_rt_entries = max(nr_rt_entries, ue[i].gsi);
 37	}
 38	nr_rt_entries += 1;
 39
 40	new = kzalloc(struct_size(new, mshv_girq_info_tbl, nr_rt_entries),
 41		      GFP_KERNEL_ACCOUNT);
 42	if (!new)
 43		return -ENOMEM;
 44
 45	new->num_rt_entries = nr_rt_entries;
 46	for (i = 0; i < numents; i++) {
 47		struct mshv_guest_irq_ent *girq;
 48
 49		girq = &new->mshv_girq_info_tbl[ue[i].gsi];
 50
 51		/*
 52		 * Allow only one to one mapping between GSI and MSI routing.
 53		 */
 54		if (girq->guest_irq_num != 0) {
 55			r = -EINVAL;
 56			goto out;
 57		}
 58
 59		girq->guest_irq_num = ue[i].gsi;
 60		girq->girq_addr_lo = ue[i].address_lo;
 61		girq->girq_addr_hi = ue[i].address_hi;
 62		girq->girq_irq_data = ue[i].data;
 63		girq->girq_entry_valid = true;
 64	}
 65
 66swap_routes:
 67	mutex_lock(&partition->pt_irq_lock);
 68	old = rcu_dereference_protected(partition->pt_girq_tbl, 1);
 69	rcu_assign_pointer(partition->pt_girq_tbl, new);
 70	mshv_irqfd_routing_update(partition);
 71	mutex_unlock(&partition->pt_irq_lock);
 72
 73	synchronize_srcu_expedited(&partition->pt_irq_srcu);
 74	new = old;
 75
 76out:
 77	kfree(new);
 78
 79	return r;
 80}
 81
 82/* vm is going away, kfree the irq routing table */
 83void mshv_free_routing_table(struct mshv_partition *partition)
 84{
 85	struct mshv_girq_routing_table *rt =
 86				   rcu_access_pointer(partition->pt_girq_tbl);
 87
 88	kfree(rt);
 89}
 90
 91struct mshv_guest_irq_ent
 92mshv_ret_girq_entry(struct mshv_partition *partition, u32 irqnum)
 93{
 94	struct mshv_guest_irq_ent entry = { 0 };
 95	struct mshv_girq_routing_table *girq_tbl;
 96
 97	girq_tbl = srcu_dereference_check(partition->pt_girq_tbl,
 98					  &partition->pt_irq_srcu,
 99					  lockdep_is_held(&partition->pt_irq_lock));
100	if (!girq_tbl || irqnum >= girq_tbl->num_rt_entries) {
101		/*
102		 * Premature register_irqfd, setting valid_entry = 0
103		 * would ignore this entry anyway
104		 */
105		entry.guest_irq_num = irqnum;
106		return entry;
107	}
108
109	return girq_tbl->mshv_girq_info_tbl[irqnum];
110}
111
112void mshv_copy_girq_info(struct mshv_guest_irq_ent *ent,
113			 struct mshv_lapic_irq *lirq)
114{
115	memset(lirq, 0, sizeof(*lirq));
116	if (!ent || !ent->girq_entry_valid)
117		return;
118
119	lirq->lapic_vector = ent->girq_irq_data & 0xFF;
120	lirq->lapic_apic_id = (ent->girq_addr_lo >> 12) & 0xFF;
121	lirq->lapic_control.interrupt_type = (ent->girq_irq_data & 0x700) >> 8;
122#if IS_ENABLED(CONFIG_X86)
123	lirq->lapic_control.level_triggered = (ent->girq_irq_data >> 15) & 0x1;
124	lirq->lapic_control.logical_dest_mode = (ent->girq_addr_lo >> 2) & 0x1;
125#elif IS_ENABLED(CONFIG_ARM64)
126	lirq->lapic_control.asserted = 1;
127#endif
128}
~/ Read-only file preview.