KVM: s390: irq routing for adapter interrupts.

Introduce a new interrupt class for s390 adapter interrupts and enable
irqfds for s390.

This is depending on a new s390 specific vm capability, KVM_CAP_S390_IRQCHIP,
that needs to be enabled by userspace.

Acked-by: Christian Borntraeger <borntraeger@de.ibm.com>
Signed-off-by: Cornelia Huck <cornelia.huck@de.ibm.com>
This commit is contained in:
Cornelia Huck 2013-07-15 13:36:01 +02:00
parent 841b91c584
commit 8422359877
9 changed files with 209 additions and 5 deletions

View file

@ -586,8 +586,8 @@ struct kvm_fpu {
4.24 KVM_CREATE_IRQCHIP 4.24 KVM_CREATE_IRQCHIP
Capability: KVM_CAP_IRQCHIP Capability: KVM_CAP_IRQCHIP, KVM_CAP_S390_IRQCHIP (s390)
Architectures: x86, ia64, ARM, arm64 Architectures: x86, ia64, ARM, arm64, s390
Type: vm ioctl Type: vm ioctl
Parameters: none Parameters: none
Returns: 0 on success, -1 on error Returns: 0 on success, -1 on error
@ -596,7 +596,10 @@ Creates an interrupt controller model in the kernel. On x86, creates a virtual
ioapic, a virtual PIC (two PICs, nested), and sets up future vcpus to have a ioapic, a virtual PIC (two PICs, nested), and sets up future vcpus to have a
local APIC. IRQ routing for GSIs 0-15 is set to both PIC and IOAPIC; GSI 16-23 local APIC. IRQ routing for GSIs 0-15 is set to both PIC and IOAPIC; GSI 16-23
only go to the IOAPIC. On ia64, a IOSAPIC is created. On ARM/arm64, a GIC is only go to the IOAPIC. On ia64, a IOSAPIC is created. On ARM/arm64, a GIC is
created. created. On s390, a dummy irq routing table is created.
Note that on s390 the KVM_CAP_S390_IRQCHIP vm capability needs to be enabled
before KVM_CREATE_IRQCHIP can be used.
4.25 KVM_IRQ_LINE 4.25 KVM_IRQ_LINE
@ -1336,7 +1339,7 @@ KVM_ASSIGN_DEV_IRQ. Partial deassignment of host or guest IRQ is allowed.
4.52 KVM_SET_GSI_ROUTING 4.52 KVM_SET_GSI_ROUTING
Capability: KVM_CAP_IRQ_ROUTING Capability: KVM_CAP_IRQ_ROUTING
Architectures: x86 ia64 Architectures: x86 ia64 s390
Type: vm ioctl Type: vm ioctl
Parameters: struct kvm_irq_routing (in) Parameters: struct kvm_irq_routing (in)
Returns: 0 on success, -1 on error Returns: 0 on success, -1 on error
@ -1359,6 +1362,7 @@ struct kvm_irq_routing_entry {
union { union {
struct kvm_irq_routing_irqchip irqchip; struct kvm_irq_routing_irqchip irqchip;
struct kvm_irq_routing_msi msi; struct kvm_irq_routing_msi msi;
struct kvm_irq_routing_s390_adapter adapter;
__u32 pad[8]; __u32 pad[8];
} u; } u;
}; };
@ -1366,6 +1370,7 @@ struct kvm_irq_routing_entry {
/* gsi routing entry types */ /* gsi routing entry types */
#define KVM_IRQ_ROUTING_IRQCHIP 1 #define KVM_IRQ_ROUTING_IRQCHIP 1
#define KVM_IRQ_ROUTING_MSI 2 #define KVM_IRQ_ROUTING_MSI 2
#define KVM_IRQ_ROUTING_S390_ADAPTER 3
No flags are specified so far, the corresponding field must be set to zero. No flags are specified so far, the corresponding field must be set to zero.
@ -1381,6 +1386,14 @@ struct kvm_irq_routing_msi {
__u32 pad; __u32 pad;
}; };
struct kvm_irq_routing_s390_adapter {
__u64 ind_addr;
__u64 summary_addr;
__u64 ind_offset;
__u32 summary_offset;
__u32 adapter_id;
};
4.53 KVM_ASSIGN_SET_MSIX_NR 4.53 KVM_ASSIGN_SET_MSIX_NR

View file

@ -24,6 +24,14 @@
#define KVM_MAX_VCPUS 64 #define KVM_MAX_VCPUS 64
#define KVM_USER_MEM_SLOTS 32 #define KVM_USER_MEM_SLOTS 32
/*
* These seem to be used for allocating ->chip in the routing table,
* which we don't use. 4096 is an out-of-thin-air value. If we need
* to look at ->chip later on, we'll need to revisit this.
*/
#define KVM_NR_IRQCHIPS 1
#define KVM_IRQCHIP_NUM_PINS 4096
struct sca_entry { struct sca_entry {
atomic_t scn; atomic_t scn;
__u32 reserved; __u32 reserved;
@ -274,6 +282,7 @@ struct kvm_arch{
struct kvm_device *flic; struct kvm_device *flic;
struct gmap *gmap; struct gmap *gmap;
int css_support; int css_support;
int use_irqchip;
struct s390_io_adapter *adapters[MAX_S390_IO_ADAPTERS]; struct s390_io_adapter *adapters[MAX_S390_IO_ADAPTERS];
}; };

View file

@ -25,6 +25,8 @@ config KVM
select HAVE_KVM_EVENTFD select HAVE_KVM_EVENTFD
select KVM_ASYNC_PF select KVM_ASYNC_PF
select KVM_ASYNC_PF_SYNC select KVM_ASYNC_PF_SYNC
select HAVE_KVM_IRQCHIP
select HAVE_KVM_IRQ_ROUTING
---help--- ---help---
Support hosting paravirtualized guest machines using the SIE Support hosting paravirtualized guest machines using the SIE
virtualization capability on the mainframe. This should work virtualization capability on the mainframe. This should work

View file

@ -7,7 +7,7 @@
# as published by the Free Software Foundation. # as published by the Free Software Foundation.
KVM := ../../../virt/kvm KVM := ../../../virt/kvm
common-objs = $(KVM)/kvm_main.o $(KVM)/eventfd.o $(KVM)/async_pf.o common-objs = $(KVM)/kvm_main.o $(KVM)/eventfd.o $(KVM)/async_pf.o $(KVM)/irqchip.o
ccflags-y := -Ivirt/kvm -Iarch/s390/kvm ccflags-y := -Ivirt/kvm -Iarch/s390/kvm

View file

@ -13,6 +13,7 @@
#include <linux/interrupt.h> #include <linux/interrupt.h>
#include <linux/kvm_host.h> #include <linux/kvm_host.h>
#include <linux/hrtimer.h> #include <linux/hrtimer.h>
#include <linux/mmu_context.h>
#include <linux/signal.h> #include <linux/signal.h>
#include <linux/slab.h> #include <linux/slab.h>
#include <asm/asm-offsets.h> #include <asm/asm-offsets.h>
@ -1284,3 +1285,123 @@ struct kvm_device_ops kvm_flic_ops = {
.create = flic_create, .create = flic_create,
.destroy = flic_destroy, .destroy = flic_destroy,
}; };
static unsigned long get_ind_bit(__u64 addr, unsigned long bit_nr, bool swap)
{
unsigned long bit;
bit = bit_nr + (addr % PAGE_SIZE) * 8;
return swap ? (bit ^ (BITS_PER_LONG - 1)) : bit;
}
static struct s390_map_info *get_map_info(struct s390_io_adapter *adapter,
u64 addr)
{
struct s390_map_info *map;
if (!adapter)
return NULL;
list_for_each_entry(map, &adapter->maps, list) {
if (map->guest_addr == addr)
return map;
}
return NULL;
}
static int adapter_indicators_set(struct kvm *kvm,
struct s390_io_adapter *adapter,
struct kvm_s390_adapter_int *adapter_int)
{
unsigned long bit;
int summary_set, idx;
struct s390_map_info *info;
void *map;
info = get_map_info(adapter, adapter_int->ind_addr);
if (!info)
return -1;
map = page_address(info->page);
bit = get_ind_bit(info->addr, adapter_int->ind_offset, adapter->swap);
set_bit(bit, map);
idx = srcu_read_lock(&kvm->srcu);
mark_page_dirty(kvm, info->guest_addr >> PAGE_SHIFT);
set_page_dirty_lock(info->page);
info = get_map_info(adapter, adapter_int->summary_addr);
if (!info) {
srcu_read_unlock(&kvm->srcu, idx);
return -1;
}
map = page_address(info->page);
bit = get_ind_bit(info->addr, adapter_int->summary_offset,
adapter->swap);
summary_set = test_and_set_bit(bit, map);
mark_page_dirty(kvm, info->guest_addr >> PAGE_SHIFT);
set_page_dirty_lock(info->page);
srcu_read_unlock(&kvm->srcu, idx);
return summary_set ? 0 : 1;
}
/*
* < 0 - not injected due to error
* = 0 - coalesced, summary indicator already active
* > 0 - injected interrupt
*/
static int set_adapter_int(struct kvm_kernel_irq_routing_entry *e,
struct kvm *kvm, int irq_source_id, int level,
bool line_status)
{
int ret;
struct s390_io_adapter *adapter;
/* We're only interested in the 0->1 transition. */
if (!level)
return 0;
adapter = get_io_adapter(kvm, e->adapter.adapter_id);
if (!adapter)
return -1;
down_read(&adapter->maps_lock);
ret = adapter_indicators_set(kvm, adapter, &e->adapter);
up_read(&adapter->maps_lock);
if ((ret > 0) && !adapter->masked) {
struct kvm_s390_interrupt s390int = {
.type = KVM_S390_INT_IO(1, 0, 0, 0),
.parm = 0,
.parm64 = (adapter->isc << 27) | 0x80000000,
};
ret = kvm_s390_inject_vm(kvm, &s390int);
if (ret == 0)
ret = 1;
}
return ret;
}
int kvm_set_routing_entry(struct kvm_irq_routing_table *rt,
struct kvm_kernel_irq_routing_entry *e,
const struct kvm_irq_routing_entry *ue)
{
int ret;
switch (ue->type) {
case KVM_IRQ_ROUTING_S390_ADAPTER:
e->set = set_adapter_int;
e->adapter.summary_addr = ue->u.adapter.summary_addr;
e->adapter.ind_addr = ue->u.adapter.ind_addr;
e->adapter.summary_offset = ue->u.adapter.summary_offset;
e->adapter.ind_offset = ue->u.adapter.ind_offset;
e->adapter.adapter_id = ue->u.adapter.adapter_id;
ret = 0;
break;
default:
ret = -EINVAL;
}
return ret;
}
int kvm_set_msi(struct kvm_kernel_irq_routing_entry *e, struct kvm *kvm,
int irq_source_id, int level, bool line_status)
{
return -EINVAL;
}

22
arch/s390/kvm/irq.h Normal file
View file

@ -0,0 +1,22 @@
/*
* s390 irqchip routines
*
* Copyright IBM Corp. 2014
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License (version 2 only)
* as published by the Free Software Foundation.
*
* Author(s): Cornelia Huck <cornelia.huck@de.ibm.com>
*/
#ifndef __KVM_IRQ_H
#define __KVM_IRQ_H
#include <linux/kvm_host.h>
static inline int irqchip_in_kernel(struct kvm *kvm)
{
return 1;
}
#endif

View file

@ -196,6 +196,10 @@ static int kvm_vm_ioctl_enable_cap(struct kvm *kvm, struct kvm_enable_cap *cap)
return -EINVAL; return -EINVAL;
switch (cap->cap) { switch (cap->cap) {
case KVM_CAP_S390_IRQCHIP:
kvm->arch.use_irqchip = 1;
r = 0;
break;
default: default:
r = -EINVAL; r = -EINVAL;
break; break;
@ -228,6 +232,18 @@ long kvm_arch_vm_ioctl(struct file *filp,
r = kvm_vm_ioctl_enable_cap(kvm, &cap); r = kvm_vm_ioctl_enable_cap(kvm, &cap);
break; break;
} }
case KVM_CREATE_IRQCHIP: {
struct kvm_irq_routing_entry routing;
r = -EINVAL;
if (kvm->arch.use_irqchip) {
/* Set up dummy routing. */
memset(&routing, 0, sizeof(routing));
kvm_set_irq_routing(kvm, &routing, 0, 0);
r = 0;
}
break;
}
default: default:
r = -ENOTTY; r = -ENOTTY;
} }
@ -284,6 +300,7 @@ int kvm_arch_init_vm(struct kvm *kvm, unsigned long type)
} }
kvm->arch.css_support = 0; kvm->arch.css_support = 0;
kvm->arch.use_irqchip = 0;
return 0; return 0;
out_nogmap: out_nogmap:

View file

@ -297,6 +297,14 @@ static inline unsigned long kvm_dirty_bitmap_bytes(struct kvm_memory_slot *memsl
return ALIGN(memslot->npages, BITS_PER_LONG) / 8; return ALIGN(memslot->npages, BITS_PER_LONG) / 8;
} }
struct kvm_s390_adapter_int {
u64 ind_addr;
u64 summary_addr;
u64 ind_offset;
u32 summary_offset;
u32 adapter_id;
};
struct kvm_kernel_irq_routing_entry { struct kvm_kernel_irq_routing_entry {
u32 gsi; u32 gsi;
u32 type; u32 type;
@ -309,6 +317,7 @@ struct kvm_kernel_irq_routing_entry {
unsigned pin; unsigned pin;
} irqchip; } irqchip;
struct msi_msg msi; struct msi_msg msi;
struct kvm_s390_adapter_int adapter;
}; };
struct hlist_node link; struct hlist_node link;
}; };

View file

@ -742,6 +742,7 @@ struct kvm_ppc_smmu_info {
#define KVM_CAP_HYPERV_TIME 96 #define KVM_CAP_HYPERV_TIME 96
#define KVM_CAP_IOAPIC_POLARITY_IGNORED 97 #define KVM_CAP_IOAPIC_POLARITY_IGNORED 97
#define KVM_CAP_ENABLE_CAP_VM 98 #define KVM_CAP_ENABLE_CAP_VM 98
#define KVM_CAP_S390_IRQCHIP 99
#ifdef KVM_CAP_IRQ_ROUTING #ifdef KVM_CAP_IRQ_ROUTING
@ -757,9 +758,18 @@ struct kvm_irq_routing_msi {
__u32 pad; __u32 pad;
}; };
struct kvm_irq_routing_s390_adapter {
__u64 ind_addr;
__u64 summary_addr;
__u64 ind_offset;
__u32 summary_offset;
__u32 adapter_id;
};
/* gsi routing entry types */ /* gsi routing entry types */
#define KVM_IRQ_ROUTING_IRQCHIP 1 #define KVM_IRQ_ROUTING_IRQCHIP 1
#define KVM_IRQ_ROUTING_MSI 2 #define KVM_IRQ_ROUTING_MSI 2
#define KVM_IRQ_ROUTING_S390_ADAPTER 3
struct kvm_irq_routing_entry { struct kvm_irq_routing_entry {
__u32 gsi; __u32 gsi;
@ -769,6 +779,7 @@ struct kvm_irq_routing_entry {
union { union {
struct kvm_irq_routing_irqchip irqchip; struct kvm_irq_routing_irqchip irqchip;
struct kvm_irq_routing_msi msi; struct kvm_irq_routing_msi msi;
struct kvm_irq_routing_s390_adapter adapter;
__u32 pad[8]; __u32 pad[8];
} u; } u;
}; };