hwpmc/core: Adopt to upcoming Skylake TSX errata.

The forthcoming microcode update will fix a TSX bug by clobbering PMC3
when TSX instructions are executed (even speculatively).  There is an
alternate mode where CPU executes all TSX instructions by aborting
them, in which case PMC3 is still available to OS.  Any code that
correctly uses TSX must be ready to handle abort anyway.

Since it is believed that FreeBSD population of hwpmc(4) users is
significantly larger than the population of TSX users, switch the
microcode into TSX abort mode whenever a pmc is allocated, and back to
bug avoidance mode when the last pmc is deallocated.

Sponsored by:	The FreeBSD Foundation
MFC after:	1 week
This commit is contained in:
Konstantin Belousov 2019-03-12 19:33:25 +00:00
parent 3193b25a5a
commit 22d7708455
Notes: svn2git 2020-12-20 02:59:44 +00:00
svn path=/head/; revision=345078

View File

@ -37,6 +37,7 @@ __FBSDID("$FreeBSD$");
#include <sys/bus.h>
#include <sys/pmc.h>
#include <sys/pmckern.h>
#include <sys/smp.h>
#include <sys/systm.h>
#include <machine/intr_machdep.h>
@ -103,6 +104,9 @@ static int core_iap_width;
static int core_iap_npmc;
static int core_iap_wroffset;
static u_int pmc_alloc_refs;
static bool pmc_tsx_force_abort_set;
static int
core_pcpu_noop(struct pmc_mdep *md, int cpu)
{
@ -216,6 +220,15 @@ iaf_reload_count_to_perfctr_value(pmc_value_t rlc)
return (1ULL << core_iaf_width) - rlc;
}
static void
tweak_tsx_force_abort(void *arg)
{
u_int val;
val = (uintptr_t)arg;
wrmsr(MSR_TSX_FORCE_ABORT, val);
}
static int
iaf_allocate_pmc(int cpu, int ri, struct pmc *pm,
const struct pmc_op_pmcallocate *a)
@ -253,6 +266,12 @@ iaf_allocate_pmc(int cpu, int ri, struct pmc *pm,
if (ev == 0x0 && umask == 0x3 && ri != 2)
return (EINVAL);
pmc_alloc_refs++;
if ((cpu_stdext_feature3 & CPUID_STDEXT3_TSXFA) != 0 &&
!pmc_tsx_force_abort_set) {
pmc_tsx_force_abort_set = true;
smp_rendezvous(NULL, tweak_tsx_force_abort, NULL, (void *)1);
}
flags = 0;
if (config & IAP_OS)
@ -389,6 +408,12 @@ iaf_release_pmc(int cpu, int ri, struct pmc *pmc)
KASSERT(core_pcpu[cpu]->pc_corepmcs[ri + core_iaf_ri].phw_pmc == NULL,
("[core,%d] PHW pmc non-NULL", __LINE__));
MPASS(pmc_alloc_refs > 0);
if (pmc_alloc_refs-- == 1 && pmc_tsx_force_abort_set) {
pmc_tsx_force_abort_set = false;
smp_rendezvous(NULL, tweak_tsx_force_abort, NULL, (void *)0);
}
return (0);
}