svn commit: r316449 - stable/11/sys/x86/iommu

Konstantin Belousov kib at FreeBSD.org
Mon Apr 3 09:41:45 UTC 2017


Author: kib
Date: Mon Apr  3 09:41:43 2017
New Revision: 316449
URL: https://svnweb.freebsd.org/changeset/base/316449

Log:
  MFC r316011:
  Timeout DMAR commands.

Modified:
  stable/11/sys/x86/iommu/intel_dmar.h
  stable/11/sys/x86/iommu/intel_drv.c
  stable/11/sys/x86/iommu/intel_qi.c
  stable/11/sys/x86/iommu/intel_utils.c
Directory Properties:
  stable/11/   (props changed)

Modified: stable/11/sys/x86/iommu/intel_dmar.h
==============================================================================
--- stable/11/sys/x86/iommu/intel_dmar.h	Mon Apr  3 09:36:44 2017	(r316448)
+++ stable/11/sys/x86/iommu/intel_dmar.h	Mon Apr  3 09:41:43 2017	(r316449)
@@ -290,6 +290,8 @@ int dmar_enable_ir(struct dmar_unit *uni
 int dmar_disable_ir(struct dmar_unit *unit);
 bool dmar_barrier_enter(struct dmar_unit *dmar, u_int barrier_id);
 void dmar_barrier_exit(struct dmar_unit *dmar, u_int barrier_id);
+uint64_t dmar_get_timeout(void);
+void dmar_update_timeout(uint64_t newval);
 
 int dmar_fault_intr(void *arg);
 void dmar_enable_fault_intr(struct dmar_unit *unit);
@@ -507,6 +509,36 @@ dmar_test_boundary(dmar_gaddr_t start, d
 	return (start + size <= ((start + boundary) & ~(boundary - 1)));
 }
 
+extern struct timespec dmar_hw_timeout;
+
+#define	DMAR_WAIT_UNTIL(cond)					\
+{								\
+	struct timespec last, curr;				\
+	bool forever;						\
+								\
+	if (dmar_hw_timeout.tv_sec == 0 &&			\
+	    dmar_hw_timeout.tv_nsec == 0) {			\
+		forever = true;					\
+	} else {						\
+		forever = false;				\
+		nanouptime(&curr);				\
+		last = curr;					\
+		timespecadd(&last, &dmar_hw_timeout);		\
+	}							\
+	for (;;) {						\
+		if (cond) {					\
+			error = 0;				\
+			break;					\
+		}						\
+		nanouptime(&curr);				\
+		if (!forever && timespeccmp(&last, &curr, <)) {	\
+			error = ETIMEDOUT;			\
+			break;					\
+		}						\
+		cpu_spinwait();					\
+	}							\
+}
+
 #ifdef INVARIANTS
 #define	TD_PREP_PINNED_ASSERT						\
 	int old_td_pinned;						\

Modified: stable/11/sys/x86/iommu/intel_drv.c
==============================================================================
--- stable/11/sys/x86/iommu/intel_drv.c	Mon Apr  3 09:36:44 2017	(r316448)
+++ stable/11/sys/x86/iommu/intel_drv.c	Mon Apr  3 09:41:43 2017	(r316449)
@@ -402,6 +402,7 @@ dmar_attach(device_t dev)
 {
 	struct dmar_unit *unit;
 	ACPI_DMAR_HARDWARE_UNIT *dmaru;
+	uint64_t timeout;
 	int i, error;
 
 	unit = device_get_softc(dev);
@@ -426,6 +427,10 @@ dmar_attach(device_t dev)
 		dmar_print_caps(dev, unit, dmaru);
 	dmar_quirks_post_ident(unit);
 
+	timeout = dmar_get_timeout();
+	TUNABLE_UINT64_FETCH("hw.dmar.timeout", &timeout);
+	dmar_update_timeout(timeout);
+
 	for (i = 0; i < DMAR_INTR_TOTAL; i++)
 		unit->intrs[i].irq = -1;
 

Modified: stable/11/sys/x86/iommu/intel_qi.c
==============================================================================
--- stable/11/sys/x86/iommu/intel_qi.c	Mon Apr  3 09:36:44 2017	(r316448)
+++ stable/11/sys/x86/iommu/intel_qi.c	Mon Apr  3 09:41:43 2017	(r316449)
@@ -40,6 +40,7 @@ __FBSDID("$FreeBSD$");
 #include <sys/module.h>
 #include <sys/rman.h>
 #include <sys/taskqueue.h>
+#include <sys/time.h>
 #include <sys/tree.h>
 #include <sys/vmem.h>
 #include <machine/bus.h>
@@ -70,27 +71,27 @@ dmar_qi_seq_processed(const struct dmar_
 static int
 dmar_enable_qi(struct dmar_unit *unit)
 {
+	int error;
 
 	DMAR_ASSERT_LOCKED(unit);
 	unit->hw_gcmd |= DMAR_GCMD_QIE;
 	dmar_write4(unit, DMAR_GCMD_REG, unit->hw_gcmd);
-	/* XXXKIB should have a timeout */
-	while ((dmar_read4(unit, DMAR_GSTS_REG) & DMAR_GSTS_QIES) == 0)
-		cpu_spinwait();
-	return (0);
+	DMAR_WAIT_UNTIL(((dmar_read4(unit, DMAR_GSTS_REG) & DMAR_GSTS_QIES)
+	    != 0));
+	return (error);
 }
 
 static int
 dmar_disable_qi(struct dmar_unit *unit)
 {
+	int error;
 
 	DMAR_ASSERT_LOCKED(unit);
 	unit->hw_gcmd &= ~DMAR_GCMD_QIE;
 	dmar_write4(unit, DMAR_GCMD_REG, unit->hw_gcmd);
-	/* XXXKIB should have a timeout */
-	while ((dmar_read4(unit, DMAR_GSTS_REG) & DMAR_GSTS_QIES) != 0)
-		cpu_spinwait();
-	return (0);
+	DMAR_WAIT_UNTIL(((dmar_read4(unit, DMAR_GSTS_REG) & DMAR_GSTS_QIES)
+	    == 0));
+	return (error);
 }
 
 static void

Modified: stable/11/sys/x86/iommu/intel_utils.c
==============================================================================
--- stable/11/sys/x86/iommu/intel_utils.c	Mon Apr  3 09:36:44 2017	(r316448)
+++ stable/11/sys/x86/iommu/intel_utils.c	Mon Apr  3 09:41:43 2017	(r316449)
@@ -46,6 +46,7 @@ __FBSDID("$FreeBSD$");
 #include <sys/sysctl.h>
 #include <sys/systm.h>
 #include <sys/taskqueue.h>
+#include <sys/time.h>
 #include <sys/tree.h>
 #include <sys/vmem.h>
 #include <dev/pci/pcivar.h>
@@ -401,6 +402,7 @@ int
 dmar_load_root_entry_ptr(struct dmar_unit *unit)
 {
 	vm_page_t root_entry;
+	int error;
 
 	/*
 	 * Access to the GCMD register must be serialized while the
@@ -413,10 +415,9 @@ dmar_load_root_entry_ptr(struct dmar_uni
 	VM_OBJECT_RUNLOCK(unit->ctx_obj);
 	dmar_write8(unit, DMAR_RTADDR_REG, VM_PAGE_TO_PHYS(root_entry));
 	dmar_write4(unit, DMAR_GCMD_REG, unit->hw_gcmd | DMAR_GCMD_SRTP);
-	/* XXXKIB should have a timeout */
-	while ((dmar_read4(unit, DMAR_GSTS_REG) & DMAR_GSTS_RTPS) == 0)
-		cpu_spinwait();
-	return (0);
+	DMAR_WAIT_UNTIL(((dmar_read4(unit, DMAR_GSTS_REG) & DMAR_GSTS_RTPS)
+	    != 0));
+	return (error);
 }
 
 /*
@@ -426,6 +427,7 @@ dmar_load_root_entry_ptr(struct dmar_uni
 int
 dmar_inv_ctx_glob(struct dmar_unit *unit)
 {
+	int error;
 
 	/*
 	 * Access to the CCMD register must be serialized while the
@@ -441,10 +443,9 @@ dmar_inv_ctx_glob(struct dmar_unit *unit
 	 * writes the upper dword last.
 	 */
 	dmar_write8(unit, DMAR_CCMD_REG, DMAR_CCMD_ICC | DMAR_CCMD_CIRG_GLOB);
-	/* XXXKIB should have a timeout */
-	while ((dmar_read4(unit, DMAR_CCMD_REG + 4) & DMAR_CCMD_ICC32) != 0)
-		cpu_spinwait();
-	return (0);
+	DMAR_WAIT_UNTIL(((dmar_read4(unit, DMAR_CCMD_REG + 4) & DMAR_CCMD_ICC32)
+	    == 0));
+	return (error);
 }
 
 /*
@@ -453,7 +454,7 @@ dmar_inv_ctx_glob(struct dmar_unit *unit
 int
 dmar_inv_iotlb_glob(struct dmar_unit *unit)
 {
-	int reg;
+	int error, reg;
 
 	DMAR_ASSERT_LOCKED(unit);
 	KASSERT(!unit->qi_enabled, ("QI enabled"));
@@ -462,11 +463,9 @@ dmar_inv_iotlb_glob(struct dmar_unit *un
 	/* See a comment about DMAR_CCMD_ICC in dmar_inv_ctx_glob. */
 	dmar_write8(unit, reg + DMAR_IOTLB_REG_OFF, DMAR_IOTLB_IVT |
 	    DMAR_IOTLB_IIRG_GLB | DMAR_IOTLB_DR | DMAR_IOTLB_DW);
-	/* XXXKIB should have a timeout */
-	while ((dmar_read4(unit, reg + DMAR_IOTLB_REG_OFF + 4) &
-	    DMAR_IOTLB_IVT32) != 0)
-		cpu_spinwait();
-	return (0);
+	DMAR_WAIT_UNTIL(((dmar_read4(unit, reg + DMAR_IOTLB_REG_OFF + 4) &
+	    DMAR_IOTLB_IVT32) == 0));
+	return (error);
 }
 
 /*
@@ -476,6 +475,7 @@ dmar_inv_iotlb_glob(struct dmar_unit *un
 int
 dmar_flush_write_bufs(struct dmar_unit *unit)
 {
+	int error;
 
 	DMAR_ASSERT_LOCKED(unit);
 
@@ -486,42 +486,42 @@ dmar_flush_write_bufs(struct dmar_unit *
 	    ("dmar%d: no RWBF", unit->unit));
 
 	dmar_write4(unit, DMAR_GCMD_REG, unit->hw_gcmd | DMAR_GCMD_WBF);
-	/* XXXKIB should have a timeout */
-	while ((dmar_read4(unit, DMAR_GSTS_REG) & DMAR_GSTS_WBFS) == 0)
-		cpu_spinwait();
-	return (0);
+	DMAR_WAIT_UNTIL(((dmar_read4(unit, DMAR_GSTS_REG) & DMAR_GSTS_WBFS)
+	    != 0));
+	return (error);
 }
 
 int
 dmar_enable_translation(struct dmar_unit *unit)
 {
+	int error;
 
 	DMAR_ASSERT_LOCKED(unit);
 	unit->hw_gcmd |= DMAR_GCMD_TE;
 	dmar_write4(unit, DMAR_GCMD_REG, unit->hw_gcmd);
-	/* XXXKIB should have a timeout */
-	while ((dmar_read4(unit, DMAR_GSTS_REG) & DMAR_GSTS_TES) == 0)
-		cpu_spinwait();
-	return (0);
+	DMAR_WAIT_UNTIL(((dmar_read4(unit, DMAR_GSTS_REG) & DMAR_GSTS_TES)
+	    != 0));
+	return (error);
 }
 
 int
 dmar_disable_translation(struct dmar_unit *unit)
 {
+	int error;
 
 	DMAR_ASSERT_LOCKED(unit);
 	unit->hw_gcmd &= ~DMAR_GCMD_TE;
 	dmar_write4(unit, DMAR_GCMD_REG, unit->hw_gcmd);
-	/* XXXKIB should have a timeout */
-	while ((dmar_read4(unit, DMAR_GSTS_REG) & DMAR_GSTS_TES) != 0)
-		cpu_spinwait();
-	return (0);
+	DMAR_WAIT_UNTIL(((dmar_read4(unit, DMAR_GSTS_REG) & DMAR_GSTS_TES)
+	    == 0));
+	return (error);
 }
 
 int
 dmar_load_irt_ptr(struct dmar_unit *unit)
 {
 	uint64_t irta, s;
+	int error;
 
 	DMAR_ASSERT_LOCKED(unit);
 	irta = unit->irt_phys;
@@ -534,37 +534,36 @@ dmar_load_irt_ptr(struct dmar_unit *unit
 	irta |= s;
 	dmar_write8(unit, DMAR_IRTA_REG, irta);
 	dmar_write4(unit, DMAR_GCMD_REG, unit->hw_gcmd | DMAR_GCMD_SIRTP);
-	/* XXXKIB should have a timeout */
-	while ((dmar_read4(unit, DMAR_GSTS_REG) & DMAR_GSTS_IRTPS) == 0)
-		cpu_spinwait();
-	return (0);
+	DMAR_WAIT_UNTIL(((dmar_read4(unit, DMAR_GSTS_REG) & DMAR_GSTS_IRTPS)
+	    != 0));
+	return (error);
 }
 
 int
 dmar_enable_ir(struct dmar_unit *unit)
 {
+	int error;
 
 	DMAR_ASSERT_LOCKED(unit);
 	unit->hw_gcmd |= DMAR_GCMD_IRE;
 	unit->hw_gcmd &= ~DMAR_GCMD_CFI;
 	dmar_write4(unit, DMAR_GCMD_REG, unit->hw_gcmd);
-	/* XXXKIB should have a timeout */
-	while ((dmar_read4(unit, DMAR_GSTS_REG) & DMAR_GSTS_IRES) == 0)
-		cpu_spinwait();
-	return (0);
+	DMAR_WAIT_UNTIL(((dmar_read4(unit, DMAR_GSTS_REG) & DMAR_GSTS_IRES)
+	    != 0));
+	return (error);
 }
 
 int
 dmar_disable_ir(struct dmar_unit *unit)
 {
+	int error;
 
 	DMAR_ASSERT_LOCKED(unit);
 	unit->hw_gcmd &= ~DMAR_GCMD_IRE;
 	dmar_write4(unit, DMAR_GCMD_REG, unit->hw_gcmd);
-	/* XXXKIB should have a timeout */
-	while ((dmar_read4(unit, DMAR_GSTS_REG) & DMAR_GSTS_IRES) != 0)
-		cpu_spinwait();
-	return (0);
+	DMAR_WAIT_UNTIL(((dmar_read4(unit, DMAR_GSTS_REG) & DMAR_GSTS_IRES)
+	    == 0));
+	return (error);
 }
 
 #define BARRIER_F				\
@@ -619,6 +618,43 @@ dmar_barrier_exit(struct dmar_unit *dmar
 
 int dmar_match_verbose;
 int dmar_batch_coalesce = 100;
+struct timespec dmar_hw_timeout = {
+	.tv_sec = 0,
+	.tv_nsec = 1000000
+};
+
+static const uint64_t d = 1000000000;
+
+void
+dmar_update_timeout(uint64_t newval)
+{
+
+	/* XXXKIB not atomic */
+	dmar_hw_timeout.tv_sec = newval / d;
+	dmar_hw_timeout.tv_nsec = newval % d;
+}
+
+uint64_t
+dmar_get_timeout(void)
+{
+
+	return ((uint64_t)dmar_hw_timeout.tv_sec * d +
+	    dmar_hw_timeout.tv_nsec);
+}
+
+static int
+dmar_timeout_sysctl(SYSCTL_HANDLER_ARGS)
+{
+	uint64_t val;
+	int error;
+
+	val = dmar_get_timeout();
+	error = sysctl_handle_long(oidp, &val, 0, req);
+	if (error != 0 || req->newptr == NULL)
+		return (error);
+	dmar_update_timeout(val);
+	return (error);
+}
 
 static SYSCTL_NODE(_hw, OID_AUTO, dmar, CTLFLAG_RD, NULL, "");
 SYSCTL_INT(_hw_dmar, OID_AUTO, tbl_pagecnt, CTLFLAG_RD,
@@ -630,6 +666,10 @@ SYSCTL_INT(_hw_dmar, OID_AUTO, match_ver
 SYSCTL_INT(_hw_dmar, OID_AUTO, batch_coalesce, CTLFLAG_RWTUN,
     &dmar_batch_coalesce, 0,
     "Number of qi batches between interrupt");
+SYSCTL_PROC(_hw_dmar, OID_AUTO, timeout,
+    CTLTYPE_U64 | CTLFLAG_RW | CTLFLAG_MPSAFE, 0, 0,
+    dmar_timeout_sysctl, "QU",
+    "Timeout for command wait, in nanoseconds");
 #ifdef INVARIANTS
 int dmar_check_free;
 SYSCTL_INT(_hw_dmar, OID_AUTO, check_free, CTLFLAG_RWTUN,


More information about the svn-src-all mailing list