Date: Mon, 27 Mar 2017 07:06:45 +0000 (UTC) From: Konstantin Belousov <kib@FreeBSD.org> To: src-committers@freebsd.org, svn-src-all@freebsd.org, svn-src-head@freebsd.org Subject: svn commit: r316011 - head/sys/x86/iommu Message-ID: <201703270706.v2R76jSk035236@repo.freebsd.org>
next in thread | raw e-mail | index | archive | help
Author: kib Date: Mon Mar 27 07:06:45 2017 New Revision: 316011 URL: https://svnweb.freebsd.org/changeset/base/316011 Log: Timeout DMAR commands. Implement timeouts for register-based DMAR commands. Tunable/sysctl hw.dmar.timeout specifies the timeout in nanoseconds, set it to zero to allow infinite wait. Default is 1ms. Runtime modification of the sysctl is not safe, it is allowed for debugging. Sponsored by: The FreeBSD Foundation MFC after: 1 week Modified: head/sys/x86/iommu/intel_dmar.h head/sys/x86/iommu/intel_drv.c head/sys/x86/iommu/intel_qi.c head/sys/x86/iommu/intel_utils.c Modified: head/sys/x86/iommu/intel_dmar.h ============================================================================== --- head/sys/x86/iommu/intel_dmar.h Mon Mar 27 07:02:27 2017 (r316010) +++ head/sys/x86/iommu/intel_dmar.h Mon Mar 27 07:06:45 2017 (r316011) @@ -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: head/sys/x86/iommu/intel_drv.c ============================================================================== --- head/sys/x86/iommu/intel_drv.c Mon Mar 27 07:02:27 2017 (r316010) +++ head/sys/x86/iommu/intel_drv.c Mon Mar 27 07:06:45 2017 (r316011) @@ -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: head/sys/x86/iommu/intel_qi.c ============================================================================== --- head/sys/x86/iommu/intel_qi.c Mon Mar 27 07:02:27 2017 (r316010) +++ head/sys/x86/iommu/intel_qi.c Mon Mar 27 07:06:45 2017 (r316011) @@ -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: head/sys/x86/iommu/intel_utils.c ============================================================================== --- head/sys/x86/iommu/intel_utils.c Mon Mar 27 07:02:27 2017 (r316010) +++ head/sys/x86/iommu/intel_utils.c Mon Mar 27 07:06:45 2017 (r316011) @@ -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,
Want to link to this message? Use this URL: <https://mail-archive.FreeBSD.org/cgi/mid.cgi?201703270706.v2R76jSk035236>