Module Name:    src
Committed By:   thorpej
Date:           Sat Sep 26 02:50:42 UTC 2020

Modified Files:
        src/sys/arch/alpha/pci: pci_6600.c

Log Message:
Support CPU interrupt affinity on Tsunami systems.


To generate a diff of this commit:
cvs rdiff -u -r1.27 -r1.28 src/sys/arch/alpha/pci/pci_6600.c

Please note that diffs are not public domain; they are subject to the
copyright notices on the relevant files.

Modified files:

Index: src/sys/arch/alpha/pci/pci_6600.c
diff -u src/sys/arch/alpha/pci/pci_6600.c:1.27 src/sys/arch/alpha/pci/pci_6600.c:1.28
--- src/sys/arch/alpha/pci/pci_6600.c:1.27	Wed Sep 23 18:48:50 2020
+++ src/sys/arch/alpha/pci/pci_6600.c	Sat Sep 26 02:50:41 2020
@@ -1,4 +1,4 @@
-/* $NetBSD: pci_6600.c,v 1.27 2020/09/23 18:48:50 thorpej Exp $ */
+/* $NetBSD: pci_6600.c,v 1.28 2020/09/26 02:50:41 thorpej Exp $ */
 
 /*-
  * Copyright (c) 1999 by Ross Harvey.  All rights reserved.
@@ -33,13 +33,14 @@
 
 #include <sys/cdefs.h>
 
-__KERNEL_RCSID(0, "$NetBSD: pci_6600.c,v 1.27 2020/09/23 18:48:50 thorpej Exp $");
+__KERNEL_RCSID(0, "$NetBSD: pci_6600.c,v 1.28 2020/09/26 02:50:41 thorpej Exp $");
 
 #include <sys/param.h>
 #include <sys/systm.h>
 #include <sys/kernel.h>
 #include <sys/device.h>
 #include <sys/malloc.h>
+#include <sys/cpu.h>
 
 #include <machine/autoconf.h>
 #define _ALPHA_BUS_DMA_PRIVATE
@@ -94,9 +95,15 @@ static void	 *dec_6600_pciide_compat_int
 
 static void	dec_6600_intr_enable(pci_chipset_tag_t, int irq);
 static void	dec_6600_intr_disable(pci_chipset_tag_t, int irq);
+static void	dec_6600_intr_set_affinity(pci_chipset_tag_t, int,
+		    struct cpu_info *);
 
-/* Software copy of enabled interrupt bits. */
+/*
+ * We keep 2 software copies of the interrupt enables: one global one,
+ * and one per-CPU for setting the interrupt affinity.
+ */
 static uint64_t	dec_6600_intr_enables __read_mostly;
+static uint64_t dec_6600_cpu_intr_enables[4] __read_mostly;
 
 void
 pci_6600_pickintr(struct tsp_config *pcp)
@@ -105,6 +112,8 @@ pci_6600_pickintr(struct tsp_config *pcp
 	pci_chipset_tag_t pc = &pcp->pc_pc;
 	char *cp;
 	int i;
+	struct cpu_info *ci;
+	CPU_INFO_ITERATOR cii;
 
 	pc->pc_intr_v = pcp;
 	pc->pc_intr_map = dec_6600_intr_map;
@@ -121,12 +130,25 @@ pci_6600_pickintr(struct tsp_config *pcp
 
 	pc->pc_intr_enable = dec_6600_intr_enable;
 	pc->pc_intr_disable = dec_6600_intr_disable;
+	pc->pc_intr_set_affinity = dec_6600_intr_set_affinity;
+
+	/* Note eligible CPUs for interrupt routing purposes. */
+	for (CPU_INFO_FOREACH(cii, ci)) {
+		KASSERT(ci->ci_cpuid < 4);
+		pc->pc_eligible_cpus |= __BIT(ci->ci_cpuid);
+	}
 
 	/*
 	 * System-wide and Pchip-0-only logic...
 	 */
 	if (sioprimary == NULL) {
 		sioprimary = pcp;
+		/*
+		 * Unless explicitly routed, all interrupts go to the
+		 * primary CPU.
+		 */
+		dec_6600_cpu_intr_enables[cpu_info_primary.ci_cpuid] =
+		    __BITS(0,63);
 		pc->pc_pciide_compat_intr_establish =
 		    dec_6600_pciide_compat_intr_establish;
 #define PCI_6600_IRQ_STR	8
@@ -146,7 +168,10 @@ pci_6600_pickintr(struct tsp_config *pcp
 		}
 #if NSIO
 		sio_intr_setup(pc, iot);
-		dec_6600_intr_enable(pc, PCI_SIO_IRQ);	/* irq line for sio */
+
+		mutex_enter(&cpu_lock);
+		dec_6600_intr_enable(pc, PCI_SIO_IRQ);
+		mutex_exit(&cpu_lock);
 #endif
 	} else {
 		pc->pc_shared_intrs = sioprimary->pc_pc.pc_shared_intrs;
@@ -266,21 +291,109 @@ dec_6600_intr_disestablish(pci_chipset_t
 }
 
 static void
-dec_6600_intr_enable(pci_chipset_tag_t const pc __unused, int const irq)
+dec_6600_intr_program(pci_chipset_tag_t const pc)
 {
-	dec_6600_intr_enables |= 1UL << irq;
+	unsigned int irq, cpuno, cnt;
+
+	/*
+	 * Validate the configuration before we program it: each enabled
+	 * IRQ must be routed to exactly one CPU.
+	 */
+	for (irq = 0; irq < PCI_NIRQ; irq++) {
+		if ((dec_6600_intr_enables & __BIT(irq)) == 0)
+			continue;
+		for (cpuno = 0, cnt = 0; cpuno < 4; cpuno++) {
+			if (dec_6600_cpu_intr_enables[cpuno] != 0 &&
+			    (pc->pc_eligible_cpus & __BIT(cpuno)) == 0)
+				panic("%s: interrupts enabled on non-existent CPU %u",
+				    __func__, cpuno);
+			if (dec_6600_cpu_intr_enables[cpuno] & __BIT(irq))
+				cnt++;
+		}
+		if (cnt != 1) {
+			panic("%s: irq %u enabled on %u CPUs", __func__,
+			    irq, cnt);
+		}
+	}
+
+	const uint64_t enab0 =
+	    dec_6600_intr_enables & dec_6600_cpu_intr_enables[0];
+	const uint64_t enab1 =
+	    dec_6600_intr_enables & dec_6600_cpu_intr_enables[1];
+	const uint64_t enab2 =
+	    dec_6600_intr_enables & dec_6600_cpu_intr_enables[2];
+	const uint64_t enab3 =
+	    dec_6600_intr_enables & dec_6600_cpu_intr_enables[3];
+
+	/* Don't touch DIMx registers for non-existent CPUs. */
+	uint64_t black_hole;
+	volatile uint64_t * const dim0 = (pc->pc_eligible_cpus & __BIT(0)) ?
+	    (void *)ALPHA_PHYS_TO_K0SEG(TS_C_DIM0) : &black_hole;
+	volatile uint64_t * const dim1 = (pc->pc_eligible_cpus & __BIT(1)) ?
+	    (void *)ALPHA_PHYS_TO_K0SEG(TS_C_DIM1) : &black_hole;
+	volatile uint64_t * const dim2 = (pc->pc_eligible_cpus & __BIT(2)) ?
+	    (void *)ALPHA_PHYS_TO_K0SEG(TS_C_DIM2) : &black_hole;
+	volatile uint64_t * const dim3 = (pc->pc_eligible_cpus & __BIT(3)) ?
+	    (void *)ALPHA_PHYS_TO_K0SEG(TS_C_DIM3) : &black_hole;
+
+	const unsigned long psl = alpha_pal_swpipl(ALPHA_PSL_IPL_HIGH);
+
+	alpha_mb();
+	*dim0 = enab0;
+	*dim1 = enab1;
+	*dim2 = enab2;
+	*dim3 = enab3;
 	alpha_mb();
-	STQP(TS_C_DIM0) = dec_6600_intr_enables;
+	(void) *dim0;
+	(void) *dim1;
+	(void) *dim2;
+	(void) *dim3;
 	alpha_mb();
+
+	alpha_pal_swpipl(psl);
 }
 
 static void
-dec_6600_intr_disable(pci_chipset_tag_t const pc __unused, int const irq)
+dec_6600_intr_enable(pci_chipset_tag_t const pc, int const irq)
 {
-	dec_6600_intr_enables &= ~(1UL << irq);
-	alpha_mb();
-	STQP(TS_C_DIM0) = dec_6600_intr_enables;
-	alpha_mb();
+
+	KASSERT(mutex_owned(&cpu_lock));
+
+	dec_6600_intr_enables |= __BIT(irq);
+	dec_6600_intr_program(pc);
+}
+
+static void
+dec_6600_intr_disable(pci_chipset_tag_t const pc, int const irq)
+{
+
+	KASSERT(mutex_owned(&cpu_lock));
+
+	dec_6600_intr_enables &= ~__BIT(irq);
+	dec_6600_intr_program(pc);
+}
+
+static void
+dec_6600_intr_set_affinity(pci_chipset_tag_t const pc, int const irq,
+    struct cpu_info * const ci)
+{
+	const uint64_t intr_bit = __BIT(irq);
+	cpuid_t cpuno;
+
+	KASSERT(mutex_owned(&cpu_lock));
+	KASSERT(ci->ci_cpuid < 4);
+	KASSERT(pc->pc_eligible_cpus & __BIT(ci->ci_cpuid));
+
+	for (cpuno = 0; cpuno < 4; cpuno++) {
+		if (cpuno == ci->ci_cpuid)
+			dec_6600_cpu_intr_enables[cpuno] |= intr_bit;
+		else
+			dec_6600_cpu_intr_enables[cpuno] &= ~intr_bit;
+	}
+
+	/* Only program the hardware if the irq is enabled. */
+	if (dec_6600_intr_enables & intr_bit)
+		dec_6600_intr_program(pc);
 }
 
 static void *

Reply via email to