diff mbox

[v2,4/5] KVM: s390: irq routing for adapter interrupts.

Message ID 1395079899-29239-5-git-send-email-cornelia.huck@de.ibm.com
State New
Headers show

Commit Message

Cornelia Huck March 17, 2014, 6:11 p.m. UTC
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.

Signed-off-by: Cornelia Huck <cornelia.huck@de.ibm.com>
---
 Documentation/virtual/kvm/api.txt               |   21 +++-
 Documentation/virtual/kvm/devices/s390_flic.txt |    6 +-
 arch/s390/include/asm/kvm_host.h                |   10 ++
 arch/s390/kvm/Kconfig                           |    2 +
 arch/s390/kvm/Makefile                          |    2 +-
 arch/s390/kvm/interrupt.c                       |  132 ++++++++++++++++++++++-
 arch/s390/kvm/irq.h                             |   22 ++++
 arch/s390/kvm/kvm-s390.c                        |   17 +++
 include/linux/kvm_host.h                        |    9 ++
 include/uapi/linux/kvm.h                        |   11 ++
 10 files changed, 222 insertions(+), 10 deletions(-)
 create mode 100644 arch/s390/kvm/irq.h

Comments

Christian Borntraeger March 21, 2014, 9:32 a.m. UTC | #1
On 17/03/14 19:11, Cornelia Huck wrote:
> 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.
> 
> Signed-off-by: Cornelia Huck <cornelia.huck@de.ibm.com>
> ---
>  Documentation/virtual/kvm/api.txt               |   21 +++-
>  Documentation/virtual/kvm/devices/s390_flic.txt |    6 +-
>  arch/s390/include/asm/kvm_host.h                |   10 ++
>  arch/s390/kvm/Kconfig                           |    2 +
>  arch/s390/kvm/Makefile                          |    2 +-
>  arch/s390/kvm/interrupt.c                       |  132 ++++++++++++++++++++++-
>  arch/s390/kvm/irq.h                             |   22 ++++
>  arch/s390/kvm/kvm-s390.c                        |   17 +++
>  include/linux/kvm_host.h                        |    9 ++
>  include/uapi/linux/kvm.h                        |   11 ++
>  10 files changed, 222 insertions(+), 10 deletions(-)
>  create mode 100644 arch/s390/kvm/irq.h
> 
> diff --git a/Documentation/virtual/kvm/api.txt b/Documentation/virtual/kvm/api.txt
> index faf6fe9..2cb1640 100644
> --- a/Documentation/virtual/kvm/api.txt
> +++ b/Documentation/virtual/kvm/api.txt
> @@ -586,8 +586,8 @@ struct kvm_fpu {
> 
>  4.24 KVM_CREATE_IRQCHIP
> 
> -Capability: KVM_CAP_IRQCHIP
> -Architectures: x86, ia64, ARM, arm64
> +Capability: KVM_CAP_IRQCHIP, KVM_CAP_S390_IRQCHIP (s390)
> +Architectures: x86, ia64, ARM, arm64, s390
>  Type: vm ioctl
>  Parameters: none
>  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
>  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
> -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
> @@ -1336,7 +1339,7 @@ KVM_ASSIGN_DEV_IRQ. Partial deassignment of host or guest IRQ is allowed.
>  4.52 KVM_SET_GSI_ROUTING
> 
>  Capability: KVM_CAP_IRQ_ROUTING
> -Architectures: x86 ia64
> +Architectures: x86 ia64 s390
>  Type: vm ioctl
>  Parameters: struct kvm_irq_routing (in)
>  Returns: 0 on success, -1 on error
> @@ -1359,6 +1362,7 @@ struct kvm_irq_routing_entry {
>  	union {
>  		struct kvm_irq_routing_irqchip irqchip;
>  		struct kvm_irq_routing_msi msi;
> +		struct kvm_irq_routing_s390_adapter adapter;
>  		__u32 pad[8];
>  	} u;
>  };
> @@ -1366,6 +1370,7 @@ struct kvm_irq_routing_entry {
>  /* gsi routing entry types */
>  #define KVM_IRQ_ROUTING_IRQCHIP 1
>  #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.
> 
> @@ -1381,6 +1386,14 @@ struct kvm_irq_routing_msi {
>  	__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
> 
> diff --git a/Documentation/virtual/kvm/devices/s390_flic.txt b/Documentation/virtual/kvm/devices/s390_flic.txt
> index db16111..4ceef53 100644
> --- a/Documentation/virtual/kvm/devices/s390_flic.txt
> +++ b/Documentation/virtual/kvm/devices/s390_flic.txt
> @@ -82,8 +82,10 @@ struct kvm_s390_io_adapter_req {
>        mask or unmask the adapter, as specified in mask
> 
>      KVM_S390_IO_ADAPTER_MAP
> -      pin a userspace page for the address provided in addr and add it to the
> +      perform a gmap translation for the guest address provided in addr,
> +      pin a userspace page for the translated address and add it to the
>        list of mappings
> 
>      KVM_S390_IO_ADAPTER_UNMAP
> -      release a userspace page as specified in addr from the list of mappings
> +      release a userspace page for the translated address specified in addr
> +      from the list of mappings
> diff --git a/arch/s390/include/asm/kvm_host.h b/arch/s390/include/asm/kvm_host.h
> index 356f595..82bf697 100644
> --- a/arch/s390/include/asm/kvm_host.h
> +++ b/arch/s390/include/asm/kvm_host.h
> @@ -24,6 +24,14 @@
>  #define KVM_MAX_VCPUS 64
>  #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 {
>  	atomic_t scn;
>  	__u32	reserved;
> @@ -248,6 +256,7 @@ struct kvm_arch_memory_slot {
> 
>  struct s390_map_info {
>  	struct list_head list;
> +	__u64 guest_addr;
>  	__u64 addr;
>  	struct page *page;
>  };
> @@ -271,6 +280,7 @@ struct kvm_arch{
>  	struct kvm_device *flic;
>  	struct gmap *gmap;
>  	int css_support;
> +	int use_irqchip;
>  	struct s390_io_adapter *adapters[MAX_S390_IO_ADAPTERS];
>  };
> 
> diff --git a/arch/s390/kvm/Kconfig b/arch/s390/kvm/Kconfig
> index c8bacbc..10d529a 100644
> --- a/arch/s390/kvm/Kconfig
> +++ b/arch/s390/kvm/Kconfig
> @@ -25,6 +25,8 @@ config KVM
>  	select HAVE_KVM_EVENTFD
>  	select KVM_ASYNC_PF
>  	select KVM_ASYNC_PF_SYNC
> +	select HAVE_KVM_IRQCHIP
> +	select HAVE_KVM_IRQ_ROUTING
>  	---help---
>  	  Support hosting paravirtualized guest machines using the SIE
>  	  virtualization capability on the mainframe. This should work
> diff --git a/arch/s390/kvm/Makefile b/arch/s390/kvm/Makefile
> index a47d2c3..d3adb37 100644
> --- a/arch/s390/kvm/Makefile
> +++ b/arch/s390/kvm/Makefile
> @@ -7,7 +7,7 @@
>  # as published by the Free Software Foundation.
> 
>  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
> 
> diff --git a/arch/s390/kvm/interrupt.c b/arch/s390/kvm/interrupt.c
> index 94b337e..8155bb4 100644
> --- a/arch/s390/kvm/interrupt.c
> +++ b/arch/s390/kvm/interrupt.c
> @@ -13,6 +13,7 @@
>  #include <linux/interrupt.h>
>  #include <linux/kvm_host.h>
>  #include <linux/hrtimer.h>
> +#include <linux/mmu_context.h>
>  #include <linux/signal.h>
>  #include <linux/slab.h>
>  #include <asm/asm-offsets.h>
> @@ -1118,8 +1119,13 @@ static int kvm_s390_adapter_map(struct kvm *kvm, unsigned int id, __u64 addr)
>  		goto out;
>  	}
>  	INIT_LIST_HEAD(&map->list);
> -	map->addr = addr;
> -	ret = get_user_pages_fast(addr, 1, 1, &map->page);
> +	map->guest_addr = addr;
> +	map->addr = gmap_translate(addr, kvm->arch.gmap);
> +	if (map->addr == -EFAULT) {
> +		ret = -EFAULT;
> +		goto out;
> +	}
> +	ret = get_user_pages_fast(map->addr, 1, 1, &map->page);
>  	if (ret < 0)
>  		goto out;
>  	BUG_ON(ret != 1);
> @@ -1144,7 +1150,7 @@ static int kvm_s390_adapter_unmap(struct kvm *kvm, unsigned int id, __u64 addr)
> 
>  	down_write(&adapter->maps_lock);
>  	list_for_each_entry_safe(map, tmp, &adapter->maps, list) {
> -		if (map->addr == addr) {
> +		if (map->guest_addr == addr) {
>  			found = 1;
>  			list_del(&map->list);
>  			put_page(map->page);


Can't these two hunks be merged into the previous patch? 

Otherwise:
Acked-by: Christian Borntraeger <borntraeger@de.ibm.com>



> @@ -1272,3 +1278,123 @@ struct kvm_device_ops kvm_flic_ops = {
>  	.create = flic_create,
>  	.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;
> +}
> diff --git a/arch/s390/kvm/irq.h b/arch/s390/kvm/irq.h
> new file mode 100644
> index 0000000..d98e415
> --- /dev/null
> +++ b/arch/s390/kvm/irq.h
> @@ -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
> diff --git a/arch/s390/kvm/kvm-s390.c b/arch/s390/kvm/kvm-s390.c
> index 2e6fbb0..ce5b659 100644
> --- a/arch/s390/kvm/kvm-s390.c
> +++ b/arch/s390/kvm/kvm-s390.c
> @@ -196,6 +196,10 @@ static int kvm_vm_ioctl_enable_cap(struct kvm *kvm, struct kvm_enable_cap *cap)
>  		return -EINVAL;
> 
>  	switch (cap->cap) {
> +	case KVM_CAP_S390_IRQCHIP:
> +		kvm->arch.use_irqchip = 1;
> +		r = 0;
> +		break;
>  	default:
>  		r = -EINVAL;
>  		break;
> @@ -228,6 +232,18 @@ long kvm_arch_vm_ioctl(struct file *filp,
>  		r = kvm_vm_ioctl_enable_cap(kvm, &cap);
>  		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:
>  		r = -ENOTTY;
>  	}
> @@ -284,6 +300,7 @@ int kvm_arch_init_vm(struct kvm *kvm, unsigned long type)
>  	}
> 
>  	kvm->arch.css_support = 0;
> +	kvm->arch.use_irqchip = 0;
> 
>  	return 0;
>  out_nogmap:
> diff --git a/include/linux/kvm_host.h b/include/linux/kvm_host.h
> index 9816b68..da7510b 100644
> --- a/include/linux/kvm_host.h
> +++ b/include/linux/kvm_host.h
> @@ -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;
>  }
> 
> +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 {
>  	u32 gsi;
>  	u32 type;
> @@ -309,6 +317,7 @@ struct kvm_kernel_irq_routing_entry {
>  			unsigned pin;
>  		} irqchip;
>  		struct msi_msg msi;
> +		struct kvm_s390_adapter_int adapter;
>  	};
>  	struct hlist_node link;
>  };
> diff --git a/include/uapi/linux/kvm.h b/include/uapi/linux/kvm.h
> index 46ea1b4..a8f4ee5 100644
> --- a/include/uapi/linux/kvm.h
> +++ b/include/uapi/linux/kvm.h
> @@ -742,6 +742,7 @@ struct kvm_ppc_smmu_info {
>  #define KVM_CAP_HYPERV_TIME 96
>  #define KVM_CAP_IOAPIC_POLARITY_IGNORED 97
>  #define KVM_CAP_ENABLE_CAP_VM 98
> +#define KVM_CAP_S390_IRQCHIP 99
> 
>  #ifdef KVM_CAP_IRQ_ROUTING
> 
> @@ -757,9 +758,18 @@ struct kvm_irq_routing_msi {
>  	__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 */
>  #define KVM_IRQ_ROUTING_IRQCHIP 1
>  #define KVM_IRQ_ROUTING_MSI 2
> +#define KVM_IRQ_ROUTING_S390_ADAPTER 3
> 
>  struct kvm_irq_routing_entry {
>  	__u32 gsi;
> @@ -769,6 +779,7 @@ struct kvm_irq_routing_entry {
>  	union {
>  		struct kvm_irq_routing_irqchip irqchip;
>  		struct kvm_irq_routing_msi msi;
> +		struct kvm_irq_routing_s390_adapter adapter;
>  		__u32 pad[8];
>  	} u;
>  };
>
Cornelia Huck March 21, 2014, 10:08 a.m. UTC | #2
On Fri, 21 Mar 2014 10:32:03 +0100
Christian Borntraeger <borntraeger@de.ibm.com> wrote:

> On 17/03/14 19:11, Cornelia Huck wrote:
> > 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.
> > 
> > Signed-off-by: Cornelia Huck <cornelia.huck@de.ibm.com>
> > ---
> >  Documentation/virtual/kvm/api.txt               |   21 +++-
> >  Documentation/virtual/kvm/devices/s390_flic.txt |    6 +-
> >  arch/s390/include/asm/kvm_host.h                |   10 ++
> >  arch/s390/kvm/Kconfig                           |    2 +
> >  arch/s390/kvm/Makefile                          |    2 +-
> >  arch/s390/kvm/interrupt.c                       |  132 ++++++++++++++++++++++-
> >  arch/s390/kvm/irq.h                             |   22 ++++
> >  arch/s390/kvm/kvm-s390.c                        |   17 +++
> >  include/linux/kvm_host.h                        |    9 ++
> >  include/uapi/linux/kvm.h                        |   11 ++
> >  10 files changed, 222 insertions(+), 10 deletions(-)
> >  create mode 100644 arch/s390/kvm/irq.h

> > diff --git a/arch/s390/kvm/interrupt.c b/arch/s390/kvm/interrupt.c
> > index 94b337e..8155bb4 100644
> > --- a/arch/s390/kvm/interrupt.c
> > +++ b/arch/s390/kvm/interrupt.c
> > @@ -13,6 +13,7 @@
> >  #include <linux/interrupt.h>
> >  #include <linux/kvm_host.h>
> >  #include <linux/hrtimer.h>
> > +#include <linux/mmu_context.h>
> >  #include <linux/signal.h>
> >  #include <linux/slab.h>
> >  #include <asm/asm-offsets.h>
> > @@ -1118,8 +1119,13 @@ static int kvm_s390_adapter_map(struct kvm *kvm, unsigned int id, __u64 addr)
> >  		goto out;
> >  	}
> >  	INIT_LIST_HEAD(&map->list);
> > -	map->addr = addr;
> > -	ret = get_user_pages_fast(addr, 1, 1, &map->page);
> > +	map->guest_addr = addr;
> > +	map->addr = gmap_translate(addr, kvm->arch.gmap);
> > +	if (map->addr == -EFAULT) {
> > +		ret = -EFAULT;
> > +		goto out;
> > +	}
> > +	ret = get_user_pages_fast(map->addr, 1, 1, &map->page);
> >  	if (ret < 0)
> >  		goto out;
> >  	BUG_ON(ret != 1);
> > @@ -1144,7 +1150,7 @@ static int kvm_s390_adapter_unmap(struct kvm *kvm, unsigned int id, __u64 addr)
> > 
> >  	down_write(&adapter->maps_lock);
> >  	list_for_each_entry_safe(map, tmp, &adapter->maps, list) {
> > -		if (map->addr == addr) {
> > +		if (map->guest_addr == addr) {
> >  			found = 1;
> >  			list_del(&map->list);
> >  			put_page(map->page);
> 
> 
> Can't these two hunks be merged into the previous patch? 

The guest_addr stuff should be in the previous patch, proably some
rebasing fallout. Will fix.

> 
> Otherwise:
> Acked-by: Christian Borntraeger <borntraeger@de.ibm.com>
>
diff mbox

Patch

diff --git a/Documentation/virtual/kvm/api.txt b/Documentation/virtual/kvm/api.txt
index faf6fe9..2cb1640 100644
--- a/Documentation/virtual/kvm/api.txt
+++ b/Documentation/virtual/kvm/api.txt
@@ -586,8 +586,8 @@  struct kvm_fpu {
 
 4.24 KVM_CREATE_IRQCHIP
 
-Capability: KVM_CAP_IRQCHIP
-Architectures: x86, ia64, ARM, arm64
+Capability: KVM_CAP_IRQCHIP, KVM_CAP_S390_IRQCHIP (s390)
+Architectures: x86, ia64, ARM, arm64, s390
 Type: vm ioctl
 Parameters: none
 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
 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
-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
@@ -1336,7 +1339,7 @@  KVM_ASSIGN_DEV_IRQ. Partial deassignment of host or guest IRQ is allowed.
 4.52 KVM_SET_GSI_ROUTING
 
 Capability: KVM_CAP_IRQ_ROUTING
-Architectures: x86 ia64
+Architectures: x86 ia64 s390
 Type: vm ioctl
 Parameters: struct kvm_irq_routing (in)
 Returns: 0 on success, -1 on error
@@ -1359,6 +1362,7 @@  struct kvm_irq_routing_entry {
 	union {
 		struct kvm_irq_routing_irqchip irqchip;
 		struct kvm_irq_routing_msi msi;
+		struct kvm_irq_routing_s390_adapter adapter;
 		__u32 pad[8];
 	} u;
 };
@@ -1366,6 +1370,7 @@  struct kvm_irq_routing_entry {
 /* gsi routing entry types */
 #define KVM_IRQ_ROUTING_IRQCHIP 1
 #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.
 
@@ -1381,6 +1386,14 @@  struct kvm_irq_routing_msi {
 	__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
 
diff --git a/Documentation/virtual/kvm/devices/s390_flic.txt b/Documentation/virtual/kvm/devices/s390_flic.txt
index db16111..4ceef53 100644
--- a/Documentation/virtual/kvm/devices/s390_flic.txt
+++ b/Documentation/virtual/kvm/devices/s390_flic.txt
@@ -82,8 +82,10 @@  struct kvm_s390_io_adapter_req {
       mask or unmask the adapter, as specified in mask
 
     KVM_S390_IO_ADAPTER_MAP
-      pin a userspace page for the address provided in addr and add it to the
+      perform a gmap translation for the guest address provided in addr,
+      pin a userspace page for the translated address and add it to the
       list of mappings
 
     KVM_S390_IO_ADAPTER_UNMAP
-      release a userspace page as specified in addr from the list of mappings
+      release a userspace page for the translated address specified in addr
+      from the list of mappings
diff --git a/arch/s390/include/asm/kvm_host.h b/arch/s390/include/asm/kvm_host.h
index 356f595..82bf697 100644
--- a/arch/s390/include/asm/kvm_host.h
+++ b/arch/s390/include/asm/kvm_host.h
@@ -24,6 +24,14 @@ 
 #define KVM_MAX_VCPUS 64
 #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 {
 	atomic_t scn;
 	__u32	reserved;
@@ -248,6 +256,7 @@  struct kvm_arch_memory_slot {
 
 struct s390_map_info {
 	struct list_head list;
+	__u64 guest_addr;
 	__u64 addr;
 	struct page *page;
 };
@@ -271,6 +280,7 @@  struct kvm_arch{
 	struct kvm_device *flic;
 	struct gmap *gmap;
 	int css_support;
+	int use_irqchip;
 	struct s390_io_adapter *adapters[MAX_S390_IO_ADAPTERS];
 };
 
diff --git a/arch/s390/kvm/Kconfig b/arch/s390/kvm/Kconfig
index c8bacbc..10d529a 100644
--- a/arch/s390/kvm/Kconfig
+++ b/arch/s390/kvm/Kconfig
@@ -25,6 +25,8 @@  config KVM
 	select HAVE_KVM_EVENTFD
 	select KVM_ASYNC_PF
 	select KVM_ASYNC_PF_SYNC
+	select HAVE_KVM_IRQCHIP
+	select HAVE_KVM_IRQ_ROUTING
 	---help---
 	  Support hosting paravirtualized guest machines using the SIE
 	  virtualization capability on the mainframe. This should work
diff --git a/arch/s390/kvm/Makefile b/arch/s390/kvm/Makefile
index a47d2c3..d3adb37 100644
--- a/arch/s390/kvm/Makefile
+++ b/arch/s390/kvm/Makefile
@@ -7,7 +7,7 @@ 
 # as published by the Free Software Foundation.
 
 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
 
diff --git a/arch/s390/kvm/interrupt.c b/arch/s390/kvm/interrupt.c
index 94b337e..8155bb4 100644
--- a/arch/s390/kvm/interrupt.c
+++ b/arch/s390/kvm/interrupt.c
@@ -13,6 +13,7 @@ 
 #include <linux/interrupt.h>
 #include <linux/kvm_host.h>
 #include <linux/hrtimer.h>
+#include <linux/mmu_context.h>
 #include <linux/signal.h>
 #include <linux/slab.h>
 #include <asm/asm-offsets.h>
@@ -1118,8 +1119,13 @@  static int kvm_s390_adapter_map(struct kvm *kvm, unsigned int id, __u64 addr)
 		goto out;
 	}
 	INIT_LIST_HEAD(&map->list);
-	map->addr = addr;
-	ret = get_user_pages_fast(addr, 1, 1, &map->page);
+	map->guest_addr = addr;
+	map->addr = gmap_translate(addr, kvm->arch.gmap);
+	if (map->addr == -EFAULT) {
+		ret = -EFAULT;
+		goto out;
+	}
+	ret = get_user_pages_fast(map->addr, 1, 1, &map->page);
 	if (ret < 0)
 		goto out;
 	BUG_ON(ret != 1);
@@ -1144,7 +1150,7 @@  static int kvm_s390_adapter_unmap(struct kvm *kvm, unsigned int id, __u64 addr)
 
 	down_write(&adapter->maps_lock);
 	list_for_each_entry_safe(map, tmp, &adapter->maps, list) {
-		if (map->addr == addr) {
+		if (map->guest_addr == addr) {
 			found = 1;
 			list_del(&map->list);
 			put_page(map->page);
@@ -1272,3 +1278,123 @@  struct kvm_device_ops kvm_flic_ops = {
 	.create = flic_create,
 	.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;
+}
diff --git a/arch/s390/kvm/irq.h b/arch/s390/kvm/irq.h
new file mode 100644
index 0000000..d98e415
--- /dev/null
+++ b/arch/s390/kvm/irq.h
@@ -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
diff --git a/arch/s390/kvm/kvm-s390.c b/arch/s390/kvm/kvm-s390.c
index 2e6fbb0..ce5b659 100644
--- a/arch/s390/kvm/kvm-s390.c
+++ b/arch/s390/kvm/kvm-s390.c
@@ -196,6 +196,10 @@  static int kvm_vm_ioctl_enable_cap(struct kvm *kvm, struct kvm_enable_cap *cap)
 		return -EINVAL;
 
 	switch (cap->cap) {
+	case KVM_CAP_S390_IRQCHIP:
+		kvm->arch.use_irqchip = 1;
+		r = 0;
+		break;
 	default:
 		r = -EINVAL;
 		break;
@@ -228,6 +232,18 @@  long kvm_arch_vm_ioctl(struct file *filp,
 		r = kvm_vm_ioctl_enable_cap(kvm, &cap);
 		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:
 		r = -ENOTTY;
 	}
@@ -284,6 +300,7 @@  int kvm_arch_init_vm(struct kvm *kvm, unsigned long type)
 	}
 
 	kvm->arch.css_support = 0;
+	kvm->arch.use_irqchip = 0;
 
 	return 0;
 out_nogmap:
diff --git a/include/linux/kvm_host.h b/include/linux/kvm_host.h
index 9816b68..da7510b 100644
--- a/include/linux/kvm_host.h
+++ b/include/linux/kvm_host.h
@@ -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;
 }
 
+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 {
 	u32 gsi;
 	u32 type;
@@ -309,6 +317,7 @@  struct kvm_kernel_irq_routing_entry {
 			unsigned pin;
 		} irqchip;
 		struct msi_msg msi;
+		struct kvm_s390_adapter_int adapter;
 	};
 	struct hlist_node link;
 };
diff --git a/include/uapi/linux/kvm.h b/include/uapi/linux/kvm.h
index 46ea1b4..a8f4ee5 100644
--- a/include/uapi/linux/kvm.h
+++ b/include/uapi/linux/kvm.h
@@ -742,6 +742,7 @@  struct kvm_ppc_smmu_info {
 #define KVM_CAP_HYPERV_TIME 96
 #define KVM_CAP_IOAPIC_POLARITY_IGNORED 97
 #define KVM_CAP_ENABLE_CAP_VM 98
+#define KVM_CAP_S390_IRQCHIP 99
 
 #ifdef KVM_CAP_IRQ_ROUTING
 
@@ -757,9 +758,18 @@  struct kvm_irq_routing_msi {
 	__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 */
 #define KVM_IRQ_ROUTING_IRQCHIP 1
 #define KVM_IRQ_ROUTING_MSI 2
+#define KVM_IRQ_ROUTING_S390_ADAPTER 3
 
 struct kvm_irq_routing_entry {
 	__u32 gsi;
@@ -769,6 +779,7 @@  struct kvm_irq_routing_entry {
 	union {
 		struct kvm_irq_routing_irqchip irqchip;
 		struct kvm_irq_routing_msi msi;
+		struct kvm_irq_routing_s390_adapter adapter;
 		__u32 pad[8];
 	} u;
 };