Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion arch/riscv/include/asm/kvm_aia.h
Original file line number Diff line number Diff line change
Expand Up @@ -79,7 +79,7 @@ struct kvm_vcpu_aia {

#define irqchip_in_kernel(k) ((k)->arch.aia.in_kernel)

extern unsigned int kvm_riscv_aia_nr_hgei;
extern atomic_t kvm_riscv_aia_nr_hgei;
extern unsigned int kvm_riscv_aia_max_ids;
DECLARE_STATIC_KEY_FALSE(kvm_riscv_aia_available);
#define kvm_riscv_aia_available() \
Expand Down
88 changes: 53 additions & 35 deletions arch/riscv/kvm/aia.c
Original file line number Diff line number Diff line change
Expand Up @@ -21,13 +21,15 @@

struct aia_hgei_control {
raw_spinlock_t lock;
bool free_bitmap_initialized;
unsigned long free_bitmap;
struct kvm_vcpu *owners[BITS_PER_LONG];
unsigned int nr_hgei;
};
static DEFINE_PER_CPU(struct aia_hgei_control, aia_hgei);
static int hgei_parent_irq;

unsigned int kvm_riscv_aia_nr_hgei;
atomic_t kvm_riscv_aia_nr_hgei;
unsigned int kvm_riscv_aia_max_ids;
DEFINE_STATIC_KEY_FALSE(kvm_riscv_aia_available);

Expand Down Expand Up @@ -452,7 +454,7 @@ void kvm_riscv_aia_free_hgei(int cpu, int hgei)

raw_spin_lock_irqsave(&hgctrl->lock, flags);

if (hgei > 0 && hgei <= kvm_riscv_aia_nr_hgei) {
if (hgei > 0 && hgei <= hgctrl->nr_hgei) {
if (!(hgctrl->free_bitmap & BIT(hgei))) {
hgctrl->free_bitmap |= BIT(hgei);
hgctrl->owners[hgei] = NULL;
Expand Down Expand Up @@ -486,26 +488,18 @@ static irqreturn_t hgei_interrupt(int irq, void *dev_id)

static int aia_hgei_init(void)
{
int cpu, rc;
struct irq_domain *domain;
struct aia_hgei_control *hgctrl;
struct irq_domain *domain;
int cpu, rc;

/* Initialize per-CPU guest external interrupt line management */
for_each_possible_cpu(cpu) {
hgctrl = per_cpu_ptr(&aia_hgei, cpu);
raw_spin_lock_init(&hgctrl->lock);
if (kvm_riscv_aia_nr_hgei) {
hgctrl->free_bitmap =
BIT(kvm_riscv_aia_nr_hgei + 1) - 1;
hgctrl->free_bitmap &= ~BIT(0);
} else
hgctrl->free_bitmap = 0;
hgctrl->free_bitmap_initialized = false;
hgctrl->free_bitmap = 0;
}

/* Skip SGEI interrupt setup for zero guest external interrupts */
if (!kvm_riscv_aia_nr_hgei)
goto skip_sgei_interrupt;

/* Find INTC irq domain */
domain = irq_find_matching_fwnode(riscv_get_intc_hwnode(),
DOMAIN_BUS_ANY);
Expand All @@ -529,25 +523,60 @@ static int aia_hgei_init(void)
return rc;
}

skip_sgei_interrupt:
return 0;
}

static void aia_hgei_exit(void)
{
/* Do nothing for zero guest external interrupts */
if (!kvm_riscv_aia_nr_hgei)
return;

/* Free per-CPU SGEI interrupt */
free_percpu_irq(hgei_parent_irq, &aia_hgei);
}

void kvm_riscv_aia_enable(void)
{
const struct imsic_global_config *gc;
const struct imsic_local_config *lc;
struct aia_hgei_control *hgctrl;
unsigned long flags;
int aia_nr_hgei;

if (!kvm_riscv_aia_available())
return;

gc = imsic_get_global_config();
lc = (gc) ? this_cpu_ptr(gc->local) : NULL;
hgctrl = this_cpu_ptr(&aia_hgei);

/* Figure-out number of bits in HGEIE */
csr_write(CSR_HGEIE, -1UL);
hgctrl->nr_hgei = fls_long(csr_read(CSR_HGEIE));
csr_write(CSR_HGEIE, 0);
if (hgctrl->nr_hgei)
hgctrl->nr_hgei--;

/*
* Number of usable per-HART HGEI lines should be minimum of
* per-HART IMSIC guest files and number of bits in HGEIE.
*/
if (lc)
hgctrl->nr_hgei = min((ulong)hgctrl->nr_hgei, lc->nr_guest_files);
else
hgctrl->nr_hgei = 0;

/* Update the number of IMSIC guest files across all HARTs */
aia_nr_hgei = atomic_read(&kvm_riscv_aia_nr_hgei);
do {
if (aia_nr_hgei <= hgctrl->nr_hgei)
break;
} while (!atomic_try_cmpxchg(&kvm_riscv_aia_nr_hgei, &aia_nr_hgei, hgctrl->nr_hgei));

raw_spin_lock_irqsave(&hgctrl->lock, flags);
if (!hgctrl->free_bitmap_initialized) {
hgctrl->free_bitmap = (hgctrl->nr_hgei) ? GENMASK_ULL(hgctrl->nr_hgei, 1) : 0;
hgctrl->free_bitmap_initialized = true;
}
raw_spin_unlock_irqrestore(&hgctrl->lock, flags);

csr_write(CSR_HVICTL, aia_hvictl_value(false));
csr_write(CSR_HVIPRIO1, 0x0);
csr_write(CSR_HVIPRIO2, 0x0);
Expand Down Expand Up @@ -588,7 +617,7 @@ void kvm_riscv_aia_disable(void)

raw_spin_lock_irqsave(&hgctrl->lock, flags);

for (i = 0; i <= kvm_riscv_aia_nr_hgei; i++) {
for (i = 0; i <= hgctrl->nr_hgei; i++) {
vcpu = hgctrl->owners[i];
if (!vcpu)
continue;
Expand Down Expand Up @@ -628,26 +657,15 @@ int kvm_riscv_aia_init(void)
return -ENODEV;
gc = imsic_get_global_config();

/* Figure-out number of bits in HGEIE */
csr_write(CSR_HGEIE, -1UL);
kvm_riscv_aia_nr_hgei = fls_long(csr_read(CSR_HGEIE));
csr_write(CSR_HGEIE, 0);
if (kvm_riscv_aia_nr_hgei)
kvm_riscv_aia_nr_hgei--;

/*
* Number of usable HGEI lines should be minimum of per-HART
* IMSIC guest files and number of bits in HGEIE
*/
/* Set initial value of IMSIC guest files across all HARTs */
if (gc)
kvm_riscv_aia_nr_hgei = min((ulong)kvm_riscv_aia_nr_hgei,
gc->nr_guest_files);
atomic_set(&kvm_riscv_aia_nr_hgei, gc->nr_guest_files);
else
kvm_riscv_aia_nr_hgei = 0;
atomic_set(&kvm_riscv_aia_nr_hgei, 0);

/* Find number of guest MSI IDs */
kvm_riscv_aia_max_ids = IMSIC_MAX_ID;
if (gc && kvm_riscv_aia_nr_hgei)
if (gc)
kvm_riscv_aia_max_ids = gc->nr_guest_ids + 1;

/* Initialize guest external interrupt line management */
Expand Down
4 changes: 2 additions & 2 deletions arch/riscv/kvm/aia_device.c
Original file line number Diff line number Diff line change
Expand Up @@ -71,7 +71,7 @@ static int aia_config(struct kvm *kvm, unsigned long type,
* external interrupts (i.e. non-zero
* VS-level IMSIC pages).
*/
if (!kvm_riscv_aia_nr_hgei)
if (!atomic_read(&kvm_riscv_aia_nr_hgei))
return -EINVAL;
break;
default:
Expand Down Expand Up @@ -628,7 +628,7 @@ void kvm_riscv_aia_init_vm(struct kvm *kvm)
*/

/* Initialize default values in AIA global context */
aia->mode = (kvm_riscv_aia_nr_hgei) ?
aia->mode = (atomic_read(&kvm_riscv_aia_nr_hgei)) ?
KVM_DEV_RISCV_AIA_MODE_AUTO : KVM_DEV_RISCV_AIA_MODE_EMUL;
aia->nr_ids = kvm_riscv_aia_max_ids - 1;
aia->nr_sources = 0;
Expand Down
8 changes: 4 additions & 4 deletions arch/riscv/kvm/main.c
Original file line number Diff line number Diff line change
Expand Up @@ -168,10 +168,6 @@ static int __init riscv_kvm_init(void)

kvm_info("VMID %ld bits available\n", kvm_riscv_gstage_vmid_bits());

if (kvm_riscv_aia_available())
kvm_info("AIA available with %d guest external interrupts\n",
kvm_riscv_aia_nr_hgei);

kvm_riscv_setup_vendor_features();

kvm_register_perf_callbacks();
Expand All @@ -182,6 +178,10 @@ static int __init riscv_kvm_init(void)
return rc;
}

if (kvm_riscv_aia_available())
kvm_info("AIA available with %d guest external interrupts\n",
atomic_read(&kvm_riscv_aia_nr_hgei));

return 0;
}
module_init(riscv_kvm_init);
Expand Down
9 changes: 4 additions & 5 deletions drivers/irqchip/irq-riscv-imsic-state.c
Original file line number Diff line number Diff line change
Expand Up @@ -920,13 +920,12 @@ int __init imsic_setup_state(struct fwnode_handle *fwnode, void *opaque)
local->msi_va = mmios_va[index] + reloff;

/*
* KVM uses global->nr_guest_files to determine the available guest
* interrupt files on each CPU. Take the minimum number of guest
* interrupt files across all CPUs to avoid KVM incorrectly allocating
* an unexisted or unmapped guest interrupt file on some CPUs.
* KVM uses both local->nr_guest_files and global->nr_guest_files
* to determine the available guest interrupt files on each CPU.
*/
nr_guest_files = (resource_size(&mmios[index]) - reloff) / IMSIC_MMIO_PAGE_SZ - 1;
global->nr_guest_files = min(global->nr_guest_files, nr_guest_files);
local->nr_guest_files = min((BIT(global->guest_index_bits) - 1), nr_guest_files);
global->nr_guest_files = min(global->nr_guest_files, local->nr_guest_files);

nr_handlers++;
}
Expand Down
5 changes: 4 additions & 1 deletion include/linux/irqchip/riscv-imsic.h
Original file line number Diff line number Diff line change
Expand Up @@ -40,6 +40,9 @@
struct imsic_local_config {
phys_addr_t msi_pa;
void __iomem *msi_va;

/* Number of guest interrupt files per-HART */
u32 nr_guest_files;
};

struct imsic_global_config {
Expand Down Expand Up @@ -68,7 +71,7 @@ struct imsic_global_config {
/* Number of guest interrupt identities */
u32 nr_guest_ids;

/* Number of guest interrupt files per core */
/* Number of guest interrupt files across all HARTs */
u32 nr_guest_files;

/* Per-CPU IMSIC addresses */
Expand Down