Skip to content

Commit

Permalink
KVM: s390: pci: enable host forwarding of Adapter Event Notifications
Browse files Browse the repository at this point in the history
In cases where interrupts are not forwarded to the guest via firmware,
KVM is responsible for ensuring delivery.  When an interrupt presents
with the forwarding bit, we must process the forwarding tables until
all interrupts are delivered.

Reviewed-by: Christian Borntraeger <borntraeger@linux.ibm.com>
Reviewed-by: Pierre Morel <pmorel@linux.ibm.com>
Signed-off-by: Matthew Rosato <mjrosato@linux.ibm.com>
Link: https://lore.kernel.org/r/20220606203325.110625-14-mjrosato@linux.ibm.com
Signed-off-by: Christian Borntraeger <borntraeger@linux.ibm.com>
  • Loading branch information
Matthew Rosato authored and Christian Borntraeger committed Jul 11, 2022
1 parent 98b1d33 commit 73f91b0
Show file tree
Hide file tree
Showing 5 changed files with 103 additions and 2 deletions.
1 change: 1 addition & 0 deletions arch/s390/include/asm/kvm_host.h
Original file line number Diff line number Diff line change
Expand Up @@ -759,6 +759,7 @@ struct kvm_vm_stat {
u64 inject_pfault_done;
u64 inject_service_signal;
u64 inject_virtio;
u64 aen_forward;
};

struct kvm_arch_memory_slot {
Expand Down
13 changes: 13 additions & 0 deletions arch/s390/include/asm/tpi.h
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,19 @@ struct tpi_info {
u32 :12;
} __packed __aligned(4);

/* I/O-Interruption Code as stored by TPI for an Adapter I/O */
struct tpi_adapter_info {
u32 aism:8;
u32 :22;
u32 error:1;
u32 forward:1;
u32 reserved;
u32 adapter_IO:1;
u32 directed_irq:1;
u32 isc:3;
u32 :27;
} __packed __aligned(4);

#endif /* __ASSEMBLY__ */

#endif /* _ASM_S390_TPI_H */
78 changes: 77 additions & 1 deletion arch/s390/kvm/interrupt.c
Original file line number Diff line number Diff line change
Expand Up @@ -3313,11 +3313,87 @@ int kvm_s390_gisc_unregister(struct kvm *kvm, u32 gisc)
}
EXPORT_SYMBOL_GPL(kvm_s390_gisc_unregister);

static void aen_host_forward(unsigned long si)
{
struct kvm_s390_gisa_interrupt *gi;
struct zpci_gaite *gaite;
struct kvm *kvm;

gaite = (struct zpci_gaite *)aift->gait +
(si * sizeof(struct zpci_gaite));
if (gaite->count == 0)
return;
if (gaite->aisb != 0)
set_bit_inv(gaite->aisbo, (unsigned long *)gaite->aisb);

kvm = kvm_s390_pci_si_to_kvm(aift, si);
if (!kvm)
return;
gi = &kvm->arch.gisa_int;

if (!(gi->origin->g1.simm & AIS_MODE_MASK(gaite->gisc)) ||
!(gi->origin->g1.nimm & AIS_MODE_MASK(gaite->gisc))) {
gisa_set_ipm_gisc(gi->origin, gaite->gisc);
if (hrtimer_active(&gi->timer))
hrtimer_cancel(&gi->timer);
hrtimer_start(&gi->timer, 0, HRTIMER_MODE_REL);
kvm->stat.aen_forward++;
}
}

static void aen_process_gait(u8 isc)
{
bool found = false, first = true;
union zpci_sic_iib iib = {{0}};
unsigned long si, flags;

spin_lock_irqsave(&aift->gait_lock, flags);

if (!aift->gait) {
spin_unlock_irqrestore(&aift->gait_lock, flags);
return;
}

for (si = 0;;) {
/* Scan adapter summary indicator bit vector */
si = airq_iv_scan(aift->sbv, si, airq_iv_end(aift->sbv));
if (si == -1UL) {
if (first || found) {
/* Re-enable interrupts. */
zpci_set_irq_ctrl(SIC_IRQ_MODE_SINGLE, isc,
&iib);
first = found = false;
} else {
/* Interrupts on and all bits processed */
break;
}
found = false;
si = 0;
/* Scan again after re-enabling interrupts */
continue;
}
found = true;
aen_host_forward(si);
}

spin_unlock_irqrestore(&aift->gait_lock, flags);
}

static void gib_alert_irq_handler(struct airq_struct *airq,
struct tpi_info *tpi_info)
{
struct tpi_adapter_info *info = (struct tpi_adapter_info *)tpi_info;

inc_irq_stat(IRQIO_GAL);
process_gib_alert_list();

if ((info->forward || info->error) &&
IS_ENABLED(CONFIG_VFIO_PCI_ZDEV_KVM)) {
aen_process_gait(info->isc);
if (info->aism != 0)
process_gib_alert_list();
} else {
process_gib_alert_list();
}
}

static struct airq_struct gib_alert_irq = {
Expand Down
3 changes: 2 additions & 1 deletion arch/s390/kvm/kvm-s390.c
Original file line number Diff line number Diff line change
Expand Up @@ -64,7 +64,8 @@ const struct _kvm_stats_desc kvm_vm_stats_desc[] = {
STATS_DESC_COUNTER(VM, inject_float_mchk),
STATS_DESC_COUNTER(VM, inject_pfault_done),
STATS_DESC_COUNTER(VM, inject_service_signal),
STATS_DESC_COUNTER(VM, inject_virtio)
STATS_DESC_COUNTER(VM, inject_virtio),
STATS_DESC_COUNTER(VM, aen_forward)
};

const struct kvm_stats_header kvm_vm_stats_header = {
Expand Down
10 changes: 10 additions & 0 deletions arch/s390/kvm/pci.h
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@
#include <linux/kvm_host.h>
#include <linux/pci.h>
#include <linux/mutex.h>
#include <linux/kvm_host.h>
#include <asm/airq.h>
#include <asm/cpu.h>

Expand Down Expand Up @@ -40,6 +41,15 @@ struct zpci_aift {

extern struct zpci_aift *aift;

static inline struct kvm *kvm_s390_pci_si_to_kvm(struct zpci_aift *aift,
unsigned long si)
{
if (!IS_ENABLED(CONFIG_VFIO_PCI_ZDEV_KVM) || aift->kzdev == 0 ||
aift->kzdev[si] == 0)
return 0;
return aift->kzdev[si]->kvm;
};

int kvm_s390_pci_aen_init(u8 nisc);
void kvm_s390_pci_aen_exit(void);

Expand Down

0 comments on commit 73f91b0

Please sign in to comment.