KVM: s390: pci: do initial setup for AEN interpretation
authorMatthew Rosato <mjrosato@linux.ibm.com>
Mon, 6 Jun 2022 20:33:16 +0000 (16:33 -0400)
committerChristian Borntraeger <borntraeger@linux.ibm.com>
Mon, 11 Jul 2022 07:54:28 +0000 (09:54 +0200)
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: Christian Borntraeger <borntraeger@linux.ibm.com>
Signed-off-by: Matthew Rosato <mjrosato@linux.ibm.com>
Link: https://lore.kernel.org/r/20220606203325.110625-13-mjrosato@linux.ibm.com
Signed-off-by: Christian Borntraeger <borntraeger@linux.ibm.com>
arch/s390/include/asm/pci.h
arch/s390/include/asm/pci_insn.h
arch/s390/kvm/interrupt.c
arch/s390/kvm/kvm-s390.c
arch/s390/kvm/pci.c
arch/s390/kvm/pci.h
arch/s390/pci/pci.c

index 50f1851..322060a 100644 (file)
@@ -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
 ----------------------------------------------------------------------------- */
index 5331082..e5f57cf 100644 (file)
@@ -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);
index 17ff475..37ff435 100644 (file)
@@ -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;
 
index 8fcb561..251eaaf 100644 (file)
@@ -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 @@ out:
 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);
 }
index e27191e..d566551 100644 (file)
@@ -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);
+}
index ce93978..c357d90 100644 (file)
 
 #include <linux/kvm_host.h>
 #include <linux/pci.h>
+#include <linux/mutex.h>
+#include <asm/airq.h>
+#include <asm/cpu.h>
 
 struct kvm_zdev {
        struct zpci_dev *zdev;
        struct kvm *kvm;
 };
 
+struct zpci_gaite {
+       u32 gisa;
+       u8 gisc;
+       u8 count;
+       u8 reserved;
+       u8 aisbo;
+       u64 aisb;
+};
+
+struct zpci_aift {
+       struct zpci_gaite *gait;
+       struct airq_iv *sbv;
+       struct kvm_zdev **kzdev;
+       spinlock_t gait_lock; /* Protects the gait, used during AEN forward */
+       struct mutex aift_lock; /* Protects the other structures in aift */
+};
+
+extern struct zpci_aift *aift;
+
+int kvm_s390_pci_aen_init(u8 nisc);
+void kvm_s390_pci_aen_exit(void);
+
+int kvm_s390_pci_init(void);
+void kvm_s390_pci_exit(void);
+
+static inline bool kvm_s390_pci_interp_allowed(void)
+{
+       struct cpuid cpu_id;
+
+       get_cpu_id(&cpu_id);
+       switch (cpu_id.machine) {
+       case 0x2817:
+       case 0x2818:
+       case 0x2827:
+       case 0x2828:
+       case 0x2964:
+       case 0x2965:
+               /* No SHM on certain machines */
+               return false;
+       default:
+               return (IS_ENABLED(CONFIG_VFIO_PCI_ZDEV_KVM) &&
+                       sclp.has_zpci_lsi && sclp.has_aeni && sclp.has_aisi &&
+                       sclp.has_aisii);
+       }
+}
+
 #endif /* __KVM_S390_PCI_H */
index c8b9d86..86cd4d8 100644 (file)
@@ -61,6 +61,12 @@ DEFINE_STATIC_KEY_FALSE(have_mio);
 
 static struct kmem_cache *zdev_fmb_cache;
 
+/* AEN structures that must be preserved over KVM module re-insertion */
+union zpci_sic_iib *zpci_aipb;
+EXPORT_SYMBOL_GPL(zpci_aipb);
+struct airq_iv *zpci_aif_sbv;
+EXPORT_SYMBOL_GPL(zpci_aif_sbv);
+
 struct zpci_dev *get_zdev_by_fid(u32 fid)
 {
        struct zpci_dev *tmp, *zdev = NULL;