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
This commit is contained in:
parent
742f9efd6b
commit
f3a84dabad
@ -290,6 +290,8 @@ int dmar_enable_ir(struct dmar_unit *unit);
|
||||
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, dmar_gaddr_t size,
|
||||
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; \
|
||||
|
@ -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;
|
||||
|
||||
|
@ -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_unit *unit,
|
||||
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
|
||||
|
@ -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_unit *unit)
|
||||
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_unit *unit)
|
||||
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 *unit)
|
||||
/* 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 *unit)
|
||||
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 *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, u_int barrier_id)
|
||||
|
||||
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_verbose, CTLFLAG_RWTUN,
|
||||
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,
|
||||
|
Loading…
Reference in New Issue
Block a user