changeset 10345:160b63eaeb0a

6824013 Implement broadcast EOI suppression for Nehalem based processors having interrupt remapping support
author Saurabh Misra <Saurabh.Mishra@Sun.COM>
date Wed, 19 Aug 2009 14:53:50 -0700
parents 67004039f55e
children 9f0b25e42dc5
files usr/src/uts/i86pc/io/intel_iommu.c usr/src/uts/i86pc/io/mp_platform_common.c usr/src/uts/i86pc/io/pcplusmp/apic.c usr/src/uts/i86pc/io/pcplusmp/apic_regops.c usr/src/uts/i86pc/sys/apic.h
diffstat 5 files changed, 74 insertions(+), 39 deletions(-) [+]
line wrap: on
line diff
--- a/usr/src/uts/i86pc/io/intel_iommu.c	Wed Aug 19 12:13:19 2009 -0700
+++ b/usr/src/uts/i86pc/io/intel_iommu.c	Wed Aug 19 14:53:50 2009 -0700
@@ -208,9 +208,15 @@
 static uint_t intrr_irta_s = INTRR_MAX_IRTA_SIZE;
 
 /*
- * whether disable interrupt remapping in LOCAL_APIC mode
+ * If true, arrange to suppress broadcast EOI by setting edge-triggered mode
+ * even for level-triggered interrupts in the interrupt-remapping engine.
+ * If false, broadcast EOI can still be suppressed if the CPU supports the
+ * APIC_SVR_SUPPRESS_BROADCAST_EOI bit.  In both cases, the IOAPIC is still
+ * programmed with the correct trigger mode, and pcplusmp must send an EOI
+ * to the IOAPIC by writing to the IOAPIC's EOI register to make up for the
+ * missing broadcast EOI.
  */
-static int intrr_only_for_x2apic = 1;
+static int intrr_suppress_brdcst_eoi = 0;
 
 /*
  * whether verify the source id of interrupt request
@@ -282,7 +288,7 @@
 static void intr_remap_get_sid(apic_irq_t *);
 
 static int intr_remap_init(int);
-static void intr_remap_enable(void);
+static void intr_remap_enable(int);
 static void intr_remap_alloc_entry(apic_irq_t *);
 static void intr_remap_map_entry(apic_irq_t *, void *);
 static void intr_remap_free_entry(apic_irq_t *);
@@ -4265,13 +4271,6 @@
 
 	intrr_apic_mode = apic_mode;
 
-	if ((intrr_apic_mode != LOCAL_X2APIC) && intrr_only_for_x2apic) {
-		/*
-		 * interrupt remapping is not a must in apic mode
-		 */
-		return (DDI_FAILURE);
-	}
-
 	for_each_in_list(&iommu_states, iommu) {
 		if ((iommu->iu_enabled & QINV_ENABLE) &&
 		    IOMMU_ECAP_GET_IR(iommu->iu_excapability)) {
@@ -4294,10 +4293,12 @@
 
 /* enable interrupt remapping */
 static void
-intr_remap_enable(void)
+intr_remap_enable(int suppress_brdcst_eoi)
 {
 	intel_iommu_state_t *iommu;
 
+	intrr_suppress_brdcst_eoi = suppress_brdcst_eoi;
+
 	for_each_in_list(&iommu_states, iommu) {
 		if (iommu->iu_intr_remap_tbl)
 			intr_remap_enable_unit(iommu);
@@ -4508,10 +4509,17 @@
 		tm = RDT_TM(irdt->ir_lo);
 		dlm = RDT_DLM(irdt->ir_lo);
 		dst = irdt->ir_hi;
+
+		/*
+		 * Mark the IRTE's TM as Edge to suppress broadcast EOI.
+		 */
+		if (intrr_suppress_brdcst_eoi) {
+			tm = TRIGGER_MODE_EDGE;
+		}
 	} else {
 		dm = MSI_ADDR_DM_PHYSICAL;
 		rh = MSI_ADDR_RH_FIXED;
-		tm = MSI_DATA_TM_EDGE;
+		tm = TRIGGER_MODE_EDGE;
 		dlm = 0;
 		dst = mregs->mr_addr;
 	}
--- a/usr/src/uts/i86pc/io/mp_platform_common.c	Wed Aug 19 12:13:19 2009 -0700
+++ b/usr/src/uts/i86pc/io/mp_platform_common.c	Wed Aug 19 14:53:50 2009 -0700
@@ -645,6 +645,13 @@
 	}
 #endif
 
+	/*
+	 * Check for directed-EOI capability in the local APIC.
+	 */
+	if (apic_directed_EOI_supported() == 1) {
+		apic_set_directed_EOI_handler();
+	}
+
 	id = apic_reg_ops->apic_read(APIC_LID_REG);
 	local_ids[0] = (uchar_t)(id >> 24);
 	apic_nproc = index = 1;
--- a/usr/src/uts/i86pc/io/pcplusmp/apic.c	Wed Aug 19 12:13:19 2009 -0700
+++ b/usr/src/uts/i86pc/io/pcplusmp/apic.c	Wed Aug 19 14:53:50 2009 -0700
@@ -368,7 +368,7 @@
 /* default apic ops without interrupt remapping */
 static apic_intrr_ops_t apic_nointrr_ops = {
 	(int (*)(int))return_instr,
-	(void (*)(void))return_instr,
+	(void (*)(int))return_instr,
 	(void (*)(apic_irq_t *))return_instr,
 	(void (*)(apic_irq_t *, void *))return_instr,
 	(void (*)(apic_irq_t *))return_instr,
@@ -601,13 +601,14 @@
 		    AV_HIGH_ORDER >> cpun);
 	}
 
-	if (apic_direct_EOI) {
+	if (apic_directed_EOI_supported()) {
 		/*
-		 * Set 12th bit in Spurious Interrupt Vector
-		 * Register to support level triggered interrupt
-		 * directed EOI.
+		 * Setting the 12th bit in the Spurious Interrupt Vector
+		 * Register suppresses broadcast EOIs generated by the local
+		 * APIC. The suppression of broadcast EOIs happens only when
+		 * interrupts are level-triggered.
 		 */
-		svr |= (0x1 << APIC_SVR);
+		svr |= APIC_SVR_SUPPRESS_BROADCAST_EOI;
 	}
 
 	/* need to enable APIC before unmasking NMI */
@@ -753,7 +754,6 @@
 {
 	int i, j;
 	uint_t isr;
-	uint32_t ver;
 
 	/*
 	 * initialize interrupt remapping before apic
@@ -796,16 +796,6 @@
 	    "pcplusmp NMI handler", (caddr_t)NULL))
 		cmn_err(CE_WARN, "pcplusmp: Unable to add nmi handler");
 
-	ver = apic_reg_ops->apic_read(APIC_VERS_REG);
-	/*
-	 * In order to determine support for Directed EOI capability,
-	 * we check for 24th bit in Local APIC Version Register.
-	 */
-	if (ver & (0x1 << APIC_DIRECTED_EOI)) {
-		apic_direct_EOI = 1;
-		apic_change_eoi();
-	}
-
 	apic_init_intr();
 
 	/* enable apic mode if imcr present */
@@ -2596,11 +2586,24 @@
 static void
 apic_intrr_init(int apic_mode)
 {
+	int suppress_brdcst_eoi = 0;
+
 	if (psm_vt_ops != NULL) {
 		if (((apic_intrr_ops_t *)psm_vt_ops)->apic_intrr_init(apic_mode)
 		    == DDI_SUCCESS) {
 			apic_vt_ops = psm_vt_ops;
-			apic_vt_ops->apic_intrr_enable();
+
+			/*
+			 * We leverage the interrupt remapping engine to
+			 * suppress broadcast EOI; thus we must send the
+			 * directed EOI with the directed-EOI handler.
+			 */
+			if (apic_directed_EOI_supported() == 0) {
+				suppress_brdcst_eoi = 1;
+				apic_set_directed_EOI_handler();
+			}
+
+			apic_vt_ops->apic_intrr_enable(suppress_brdcst_eoi);
 		}
 	}
 }
--- a/usr/src/uts/i86pc/io/pcplusmp/apic_regops.c	Wed Aug 19 12:13:19 2009 -0700
+++ b/usr/src/uts/i86pc/io/pcplusmp/apic_regops.c	Wed Aug 19 14:53:50 2009 -0700
@@ -91,8 +91,6 @@
 /*
  * APIC register ops related data sturctures and functions.
  */
-int	apic_direct_EOI = 0;			/* Directed EOI Support */
-
 void apic_send_EOI();
 void apic_send_directed_EOI(uint32_t irq);
 
@@ -289,11 +287,23 @@
 }
 
 void
-apic_change_eoi()
+apic_set_directed_EOI_handler()
 {
 	apic_reg_ops->apic_send_eoi = apic_send_directed_EOI;
 }
 
+int
+apic_directed_EOI_supported()
+{
+	uint32_t ver;
+
+	ver = apic_reg_ops->apic_read(APIC_VERS_REG);
+	if (ver & APIC_DIRECTED_EOI_BIT)
+		return (1);
+
+	return (0);
+}
+
 /*
  * Change apic_reg_ops depending upon the apic_mode.
  */
--- a/usr/src/uts/i86pc/sys/apic.h	Wed Aug 19 12:13:19 2009 -0700
+++ b/usr/src/uts/i86pc/sys/apic.h	Wed Aug 19 14:53:50 2009 -0700
@@ -110,8 +110,8 @@
 #define	X2APIC_SELF_IPI		0xFC
 
 /* General x2APIC constants used at various places */
-#define	APIC_SVR		12
-#define	APIC_DIRECTED_EOI	24
+#define	APIC_SVR_SUPPRESS_BROADCAST_EOI		0x1000
+#define	APIC_DIRECTED_EOI_BIT			0x1000000
 
 /* IRR register	*/
 #define	APIC_IRR_REG		0x80
@@ -428,6 +428,12 @@
 #define	MSI_ADDR_DM_SHIFT	2
 
 /*
+ * TM is either edge or level.
+ */
+#define	TRIGGER_MODE_EDGE		0x0	/* edge sensitive */
+#define	TRIGGER_MODE_LEVEL		0x1	/* level sensitive */
+
+/*
  * definitions for MSI Data
  */
 #define	MSI_DATA_DELIVERY_FIXED		0x0	/* Fixed delivery */
@@ -437,8 +443,8 @@
 #define	MSI_DATA_DELIVERY_INIT		0x5
 #define	MSI_DATA_DELIVERY_EXTINT	0x7
 #define	MSI_DATA_DELIVERY_SHIFT		8
-#define	MSI_DATA_TM_EDGE		0x0	/* MSI is edge sensitive */
-#define	MSI_DATA_TM_LEVEL		0x1	/* level sensitive */
+#define	MSI_DATA_TM_EDGE		TRIGGER_MODE_EDGE
+#define	MSI_DATA_TM_LEVEL		TRIGGER_MODE_LEVEL
 #define	MSI_DATA_TM_SHIFT		15
 #define	MSI_DATA_LEVEL_DEASSERT		0x0
 #define	MSI_DATA_LEVEL_ASSERT		0x1	/* Edge always assert */
@@ -552,7 +558,7 @@
  */
 typedef struct apic_intrr_ops {
 	int	(*apic_intrr_init)(int);
-	void	(*apic_intrr_enable)(void);
+	void	(*apic_intrr_enable)(int);
 	void	(*apic_intrr_alloc_entry)(apic_irq_t *);
 	void	(*apic_intrr_map_entry)(apic_irq_t *, void *);
 	void	(*apic_intrr_free_entry)(apic_irq_t *);
@@ -850,10 +856,11 @@
 extern uchar_t apic_ipls[];
 extern apic_reg_ops_t *apic_reg_ops;
 extern int apic_mode;
-extern int apic_direct_EOI;
 extern void x2apic_update_psm();
 extern void apic_change_ops();
 extern void apic_common_send_ipi(int, int);
+extern void apic_set_directed_EOI_handler();
+extern int apic_directed_EOI_supported();
 
 extern apic_intrr_ops_t *apic_vt_ops;