mirror of
https://git.kernel.org/pub/scm/linux/kernel/git/herbert/cryptodev-2.6.git
synced 2026-04-25 00:52:45 -04:00
When unmapping a vLPI, WARN if nullifying vCPU affinity fails, not just if
failure occurs when freeing an ITE. If undoing vCPU affinity fails, then
odds are very good that vLPI state tracking has has gotten out of whack,
i.e. that KVM and the GIC disagree on the state of an IRQ/vLPI. At best,
inconsistent state means there is a lurking bug/flaw somewhere. At worst,
the inconsistency could eventually be fatal to the host, e.g. if an ITS
command fails because KVM's view of things doesn't match reality/hardware.
Note, only the call from kvm_arch_irq_bypass_del_producer() by way of
kvm_vgic_v4_unset_forwarding() doesn't already WARN. Common KVM's
kvm_irq_routing_update() WARNs if kvm_arch_update_irqfd_routing() fails.
For that path, if its_unmap_vlpi() fails in kvm_vgic_v4_unset_forwarding(),
the only possible causes are that the GIC doesn't have a v4 ITS (from
its_irq_set_vcpu_affinity()):
/* Need a v4 ITS */
if (!is_v4(its_dev->its))
return -EINVAL;
guard(raw_spinlock)(&its_dev->event_map.vlpi_lock);
/* Unmap request? */
if (!info)
return its_vlpi_unmap(d);
or that KVM has gotten out of sync with the GIC/ITS (from its_vlpi_unmap()):
if (!its_dev->event_map.vm || !irqd_is_forwarded_to_vcpu(d))
return -EINVAL;
All of the above failure scenarios are warnable offences, as they should
never occur absent a kernel/KVM bug.
Acked-by: Oliver Upton <oliver.upton@linux.dev>
Link: https://lore.kernel.org/all/aFWY2LTVIxz5rfhh@google.com
Signed-off-by: Sean Christopherson <seanjc@google.com>
161 lines
3.8 KiB
C
161 lines
3.8 KiB
C
/* SPDX-License-Identifier: GPL-2.0-only */
|
|
/*
|
|
* Copyright (C) 2016,2017 ARM Limited, All Rights Reserved.
|
|
* Author: Marc Zyngier <marc.zyngier@arm.com>
|
|
*/
|
|
|
|
#ifndef __LINUX_IRQCHIP_ARM_GIC_V4_H
|
|
#define __LINUX_IRQCHIP_ARM_GIC_V4_H
|
|
|
|
struct its_vpe;
|
|
|
|
/*
|
|
* Maximum number of ITTs when GITS_TYPER.VMOVP == 0, using the
|
|
* ITSList mechanism to perform inter-ITS synchronization.
|
|
*/
|
|
#define GICv4_ITS_LIST_MAX 16
|
|
|
|
/* Embedded in kvm.arch */
|
|
struct its_vm {
|
|
struct fwnode_handle *fwnode;
|
|
struct irq_domain *domain;
|
|
struct page *vprop_page;
|
|
struct its_vpe **vpes;
|
|
int nr_vpes;
|
|
irq_hw_number_t db_lpi_base;
|
|
unsigned long *db_bitmap;
|
|
int nr_db_lpis;
|
|
/*
|
|
* Ensures mutual exclusion between updates to vlpi_count[]
|
|
* and map/unmap when using the ITSList mechanism.
|
|
*
|
|
* The lock order for any sequence involving the ITSList is
|
|
* vmapp_lock -> vpe_lock ->vmovp_lock.
|
|
*/
|
|
raw_spinlock_t vmapp_lock;
|
|
u32 vlpi_count[GICv4_ITS_LIST_MAX];
|
|
};
|
|
|
|
/* Embedded in kvm_vcpu.arch */
|
|
struct its_vpe {
|
|
struct page *vpt_page;
|
|
struct its_vm *its_vm;
|
|
/* per-vPE VLPI tracking */
|
|
atomic_t vlpi_count;
|
|
/* Doorbell interrupt */
|
|
int irq;
|
|
irq_hw_number_t vpe_db_lpi;
|
|
/* VPE resident */
|
|
bool resident;
|
|
/* VPT parse complete */
|
|
bool ready;
|
|
union {
|
|
/* GICv4.0 implementations */
|
|
struct {
|
|
/* VPE proxy mapping */
|
|
int vpe_proxy_event;
|
|
/* Implementation Defined Area Invalid */
|
|
bool idai;
|
|
};
|
|
/* GICv4.1 implementations */
|
|
struct {
|
|
struct fwnode_handle *fwnode;
|
|
struct irq_domain *sgi_domain;
|
|
struct {
|
|
u8 priority;
|
|
bool enabled;
|
|
bool group;
|
|
} sgi_config[16];
|
|
};
|
|
};
|
|
|
|
/* Track the VPE being mapped */
|
|
atomic_t vmapp_count;
|
|
|
|
/*
|
|
* Ensures mutual exclusion between affinity setting of the
|
|
* vPE and vLPI operations using vpe->col_idx.
|
|
*/
|
|
raw_spinlock_t vpe_lock;
|
|
/*
|
|
* This collection ID is used to indirect the target
|
|
* redistributor for this VPE. The ID itself isn't involved in
|
|
* programming of the ITS.
|
|
*/
|
|
u16 col_idx;
|
|
/* Unique (system-wide) VPE identifier */
|
|
u16 vpe_id;
|
|
/* Pending VLPIs on schedule out? */
|
|
bool pending_last;
|
|
};
|
|
|
|
/*
|
|
* struct its_vlpi_map: structure describing the mapping of a
|
|
* VLPI. Only to be interpreted in the context of a physical interrupt
|
|
* it complements. To be used as the vcpu_info passed to
|
|
* irq_set_vcpu_affinity().
|
|
*
|
|
* @vm: Pointer to the GICv4 notion of a VM
|
|
* @vpe: Pointer to the GICv4 notion of a virtual CPU (VPE)
|
|
* @vintid: Virtual LPI number
|
|
* @properties: Priority and enable bits (as written in the prop table)
|
|
* @db_enabled: Is the VPE doorbell to be generated?
|
|
*/
|
|
struct its_vlpi_map {
|
|
struct its_vm *vm;
|
|
struct its_vpe *vpe;
|
|
u32 vintid;
|
|
u8 properties;
|
|
bool db_enabled;
|
|
};
|
|
|
|
enum its_vcpu_info_cmd_type {
|
|
MAP_VLPI,
|
|
GET_VLPI,
|
|
PROP_UPDATE_VLPI,
|
|
PROP_UPDATE_AND_INV_VLPI,
|
|
SCHEDULE_VPE,
|
|
DESCHEDULE_VPE,
|
|
COMMIT_VPE,
|
|
INVALL_VPE,
|
|
PROP_UPDATE_VSGI,
|
|
};
|
|
|
|
struct its_cmd_info {
|
|
enum its_vcpu_info_cmd_type cmd_type;
|
|
union {
|
|
struct its_vlpi_map *map;
|
|
u8 config;
|
|
bool req_db;
|
|
struct {
|
|
bool g0en;
|
|
bool g1en;
|
|
};
|
|
struct {
|
|
u8 priority;
|
|
bool group;
|
|
};
|
|
};
|
|
};
|
|
|
|
int its_alloc_vcpu_irqs(struct its_vm *vm);
|
|
void its_free_vcpu_irqs(struct its_vm *vm);
|
|
int its_make_vpe_resident(struct its_vpe *vpe, bool g0en, bool g1en);
|
|
int its_make_vpe_non_resident(struct its_vpe *vpe, bool db);
|
|
int its_commit_vpe(struct its_vpe *vpe);
|
|
int its_invall_vpe(struct its_vpe *vpe);
|
|
int its_map_vlpi(int irq, struct its_vlpi_map *map);
|
|
int its_get_vlpi(int irq, struct its_vlpi_map *map);
|
|
void its_unmap_vlpi(int irq);
|
|
int its_prop_update_vlpi(int irq, u8 config, bool inv);
|
|
int its_prop_update_vsgi(int irq, u8 priority, bool group);
|
|
|
|
struct irq_domain_ops;
|
|
int its_init_v4(struct irq_domain *domain,
|
|
const struct irq_domain_ops *vpe_ops,
|
|
const struct irq_domain_ops *sgi_ops);
|
|
|
|
bool gic_cpuif_has_vsgi(void);
|
|
|
|
#endif
|