Commit 98b1d33d authored by Matthew Rosato's avatar Matthew Rosato Committed by Christian Borntraeger
Browse files

KVM: s390: pci: do initial setup for AEN interpretation



Initial setup for Adapter Event Notification Interpretation for zPCI
passthrough devices.  Specifically, allocate a structure for forwarding of
adapter events and pass the address of this structure to firmware.

Reviewed-by: default avatarChristian Borntraeger <borntraeger@linux.ibm.com>
Signed-off-by: default avatarMatthew Rosato <mjrosato@linux.ibm.com>
Link: https://lore.kernel.org/r/20220606203325.110625-13-mjrosato@linux.ibm.com


Signed-off-by: default avatarChristian Borntraeger <borntraeger@linux.ibm.com>
parent 6438e307
Loading
Loading
Loading
Loading
+4 −0
Original line number Diff line number Diff line
@@ -9,6 +9,7 @@
#include <asm-generic/pci.h>
#include <asm/pci_clp.h>
#include <asm/pci_debug.h>
#include <asm/pci_insn.h>
#include <asm/sclp.h>

#define PCIBIOS_MIN_IO		0x1000
@@ -204,6 +205,9 @@ extern const struct attribute_group *zpci_attr_groups[];
extern unsigned int s390_pci_force_floating __initdata;
extern unsigned int s390_pci_no_rid;

extern union zpci_sic_iib *zpci_aipb;
extern struct airq_iv *zpci_aif_sbv;

/* -----------------------------------------------------------------------------
  Prototypes
----------------------------------------------------------------------------- */
+12 −0
Original line number Diff line number Diff line
@@ -101,6 +101,7 @@ struct zpci_fib {
/* Set Interruption Controls Operation Controls  */
#define	SIC_IRQ_MODE_ALL		0
#define	SIC_IRQ_MODE_SINGLE		1
#define	SIC_SET_AENI_CONTROLS		2
#define	SIC_IRQ_MODE_DIRECT		4
#define	SIC_IRQ_MODE_D_ALL		16
#define	SIC_IRQ_MODE_D_SINGLE		17
@@ -127,9 +128,20 @@ struct zpci_cdiib {
	u64 : 64;
} __packed __aligned(8);

/* adapter interruption parameters block */
struct zpci_aipb {
	u64 faisb;
	u64 gait;
	u16 : 13;
	u16 afi : 3;
	u32 : 32;
	u16 faal;
} __packed __aligned(8);

union zpci_sic_iib {
	struct zpci_diib diib;
	struct zpci_cdiib cdiib;
	struct zpci_aipb aipb;
};

DECLARE_STATIC_KEY_FALSE(have_mio);
+14 −0
Original line number Diff line number Diff line
@@ -32,6 +32,7 @@
#include "kvm-s390.h"
#include "gaccess.h"
#include "trace-s390.h"
#include "pci.h"

#define PFAULT_INIT 0x0600
#define PFAULT_DONE 0x0680
@@ -3328,6 +3329,11 @@ void kvm_s390_gib_destroy(void)
{
	if (!gib)
		return;
	if (kvm_s390_pci_interp_allowed() && aift) {
		mutex_lock(&aift->aift_lock);
		kvm_s390_pci_aen_exit();
		mutex_unlock(&aift->aift_lock);
	}
	chsc_sgib(0);
	unregister_adapter_interrupt(&gib_alert_irq);
	free_page((unsigned long)gib);
@@ -3365,6 +3371,14 @@ int kvm_s390_gib_init(u8 nisc)
		goto out_unreg_gal;
	}

	if (kvm_s390_pci_interp_allowed()) {
		if (kvm_s390_pci_aen_init(nisc)) {
			pr_err("Initializing AEN for PCI failed\n");
			rc = -EIO;
			goto out_unreg_gal;
		}
	}

	KVM_EVENT(3, "gib 0x%pK (nisc=%d) initialized", gib, gib->nisc);
	goto out;

+11 −0
Original line number Diff line number Diff line
@@ -47,6 +47,7 @@
#include <asm/fpu/api.h>
#include "kvm-s390.h"
#include "gaccess.h"
#include "pci.h"

#define CREATE_TRACE_POINTS
#include "trace.h"
@@ -502,6 +503,14 @@ int kvm_arch_init(void *opaque)
		goto out;
	}

	if (kvm_s390_pci_interp_allowed()) {
		rc = kvm_s390_pci_init();
		if (rc) {
			pr_err("Unable to allocate AIFT for PCI\n");
			goto out;
		}
	}

	rc = kvm_s390_gib_init(GAL_ISC);
	if (rc)
		goto out;
@@ -516,6 +525,8 @@ int kvm_arch_init(void *opaque)
void kvm_arch_exit(void)
{
	kvm_s390_gib_destroy();
	if (kvm_s390_pci_interp_allowed())
		kvm_s390_pci_exit();
	debug_unregister(kvm_s390_dbf);
	debug_unregister(kvm_s390_dbf_uv);
}
+160 −0
Original line number Diff line number Diff line
@@ -9,8 +9,149 @@

#include <linux/kvm_host.h>
#include <linux/pci.h>
#include <asm/pci.h>
#include <asm/pci_insn.h>
#include "pci.h"

struct zpci_aift *aift;

static inline int __set_irq_noiib(u16 ctl, u8 isc)
{
	union zpci_sic_iib iib = {{0}};

	return zpci_set_irq_ctrl(ctl, isc, &iib);
}

void kvm_s390_pci_aen_exit(void)
{
	unsigned long flags;
	struct kvm_zdev **gait_kzdev;

	lockdep_assert_held(&aift->aift_lock);

	/*
	 * Contents of the aipb remain registered for the life of the host
	 * kernel, the information preserved in zpci_aipb and zpci_aif_sbv
	 * in case we insert the KVM module again later.  Clear the AIFT
	 * information and free anything not registered with underlying
	 * firmware.
	 */
	spin_lock_irqsave(&aift->gait_lock, flags);
	gait_kzdev = aift->kzdev;
	aift->gait = NULL;
	aift->sbv = NULL;
	aift->kzdev = NULL;
	spin_unlock_irqrestore(&aift->gait_lock, flags);

	kfree(gait_kzdev);
}

static int zpci_setup_aipb(u8 nisc)
{
	struct page *page;
	int size, rc;

	zpci_aipb = kzalloc(sizeof(union zpci_sic_iib), GFP_KERNEL);
	if (!zpci_aipb)
		return -ENOMEM;

	aift->sbv = airq_iv_create(ZPCI_NR_DEVICES, AIRQ_IV_ALLOC, 0);
	if (!aift->sbv) {
		rc = -ENOMEM;
		goto free_aipb;
	}
	zpci_aif_sbv = aift->sbv;
	size = get_order(PAGE_ALIGN(ZPCI_NR_DEVICES *
						sizeof(struct zpci_gaite)));
	page = alloc_pages(GFP_KERNEL | __GFP_ZERO, size);
	if (!page) {
		rc = -ENOMEM;
		goto free_sbv;
	}
	aift->gait = (struct zpci_gaite *)page_to_phys(page);

	zpci_aipb->aipb.faisb = virt_to_phys(aift->sbv->vector);
	zpci_aipb->aipb.gait = virt_to_phys(aift->gait);
	zpci_aipb->aipb.afi = nisc;
	zpci_aipb->aipb.faal = ZPCI_NR_DEVICES;

	/* Setup Adapter Event Notification Interpretation */
	if (zpci_set_irq_ctrl(SIC_SET_AENI_CONTROLS, 0, zpci_aipb)) {
		rc = -EIO;
		goto free_gait;
	}

	return 0;

free_gait:
	free_pages((unsigned long)aift->gait, size);
free_sbv:
	airq_iv_release(aift->sbv);
	zpci_aif_sbv = NULL;
free_aipb:
	kfree(zpci_aipb);
	zpci_aipb = NULL;

	return rc;
}

static int zpci_reset_aipb(u8 nisc)
{
	/*
	 * AEN registration can only happen once per system boot.  If
	 * an aipb already exists then AEN was already registered and
	 * we can re-use the aipb contents.  This can only happen if
	 * the KVM module was removed and re-inserted.  However, we must
	 * ensure that the same forwarding ISC is used as this is assigned
	 * during KVM module load.
	 */
	if (zpci_aipb->aipb.afi != nisc)
		return -EINVAL;

	aift->sbv = zpci_aif_sbv;
	aift->gait = (struct zpci_gaite *)zpci_aipb->aipb.gait;

	return 0;
}

int kvm_s390_pci_aen_init(u8 nisc)
{
	int rc = 0;

	/* If already enabled for AEN, bail out now */
	if (aift->gait || aift->sbv)
		return -EPERM;

	mutex_lock(&aift->aift_lock);
	aift->kzdev = kcalloc(ZPCI_NR_DEVICES, sizeof(struct kvm_zdev),
			      GFP_KERNEL);
	if (!aift->kzdev) {
		rc = -ENOMEM;
		goto unlock;
	}

	if (!zpci_aipb)
		rc = zpci_setup_aipb(nisc);
	else
		rc = zpci_reset_aipb(nisc);
	if (rc)
		goto free_zdev;

	/* Enable floating IRQs */
	if (__set_irq_noiib(SIC_IRQ_MODE_SINGLE, nisc)) {
		rc = -EIO;
		kvm_s390_pci_aen_exit();
	}

	goto unlock;

free_zdev:
	kfree(aift->kzdev);
unlock:
	mutex_unlock(&aift->aift_lock);
	return rc;
}

static int kvm_s390_pci_dev_open(struct zpci_dev *zdev)
{
	struct kvm_zdev *kzdev;
@@ -34,3 +175,22 @@ static void kvm_s390_pci_dev_release(struct zpci_dev *zdev)
	zdev->kzdev = NULL;
	kfree(kzdev);
}

int kvm_s390_pci_init(void)
{
	aift = kzalloc(sizeof(struct zpci_aift), GFP_KERNEL);
	if (!aift)
		return -ENOMEM;

	spin_lock_init(&aift->gait_lock);
	mutex_init(&aift->aift_lock);

	return 0;
}

void kvm_s390_pci_exit(void)
{
	mutex_destroy(&aift->aift_lock);

	kfree(aift);
}
Loading