Module Name: src
Committed By: jdolecek
Date: Mon Jan 22 19:37:45 UTC 2018
Modified Files:
src/sys/arch/x86/conf: files.x86
Added Files:
src/sys/arch/x86/x86: x86_tlb.c
Removed Files:
src/sys/arch/x86/x86: pmap_tlb.c
Log Message:
rename sys/arch/x86/x86/pmap_tlb.c to sys/arch/x86/x86/x86_tlb.c, so that
x86 can eventually use uvm/pmap/pmap_tlb.c; step to future PCID support
To generate a diff of this commit:
cvs rdiff -u -r1.91 -r1.92 src/sys/arch/x86/conf/files.x86
cvs rdiff -u -r1.8 -r0 src/sys/arch/x86/x86/pmap_tlb.c
cvs rdiff -u -r0 -r1.1 src/sys/arch/x86/x86/x86_tlb.c
Please note that diffs are not public domain; they are subject to the
copyright notices on the relevant files.
Modified files:
Index: src/sys/arch/x86/conf/files.x86
diff -u src/sys/arch/x86/conf/files.x86:1.91 src/sys/arch/x86/conf/files.x86:1.92
--- src/sys/arch/x86/conf/files.x86:1.91 Mon Jan 8 01:41:34 2018
+++ src/sys/arch/x86/conf/files.x86 Mon Jan 22 19:37:45 2018
@@ -1,4 +1,4 @@
-# $NetBSD: files.x86,v 1.91 2018/01/08 01:41:34 christos Exp $
+# $NetBSD: files.x86,v 1.92 2018/01/22 19:37:45 jdolecek Exp $
# options for MP configuration through the MP spec
defflag opt_mpbios.h MPBIOS MPVERBOSE MPDEBUG MPBIOS_SCANPCI
@@ -94,7 +94,7 @@ file arch/x86/x86/mtrr_i686.c mtrr
file arch/x86/x86/patch.c machdep
file arch/x86/x86/platform.c machdep
file arch/x86/x86/pmap.c machdep
-file arch/x86/x86/pmap_tlb.c machdep
+file arch/x86/x86/x86_tlb.c machdep
file arch/x86/x86/pmc.c machdep
file arch/x86/x86/procfs_machdep.c procfs
file arch/x86/x86/sys_machdep.c machdep
Added files:
Index: src/sys/arch/x86/x86/x86_tlb.c
diff -u /dev/null src/sys/arch/x86/x86/x86_tlb.c:1.1
--- /dev/null Mon Jan 22 19:37:45 2018
+++ src/sys/arch/x86/x86/x86_tlb.c Mon Jan 22 19:37:45 2018
@@ -0,0 +1,478 @@
+/* $NetBSD: x86_tlb.c,v 1.1 2018/01/22 19:37:45 jdolecek Exp $ */
+
+/*-
+ * Copyright (c) 2008-2012 The NetBSD Foundation, Inc.
+ * All rights reserved.
+ *
+ * This code is derived from software contributed to The NetBSD Foundation
+ * by Andrew Doran and Mindaugas Rasiukevicius.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS
+ * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
+ * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS
+ * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+ * POSSIBILITY OF SUCH DAMAGE.
+ */
+
+/*
+ * x86 pmap(9) module: TLB shootdowns.
+ *
+ * TLB shootdowns are hard interrupts that operate outside the SPL framework.
+ * They do not need to be blocked, provided that the pmap module gets the
+ * order of events correct. The calls are made by poking the LAPIC directly.
+ * The interrupt handler is short and does one of the following: invalidate
+ * a set of pages, all user TLB entries or the entire TLB.
+ */
+
+#include <sys/cdefs.h>
+__KERNEL_RCSID(0, "$NetBSD: x86_tlb.c,v 1.1 2018/01/22 19:37:45 jdolecek Exp $");
+
+#include <sys/param.h>
+#include <sys/kernel.h>
+
+#include <sys/systm.h>
+#include <sys/atomic.h>
+#include <sys/cpu.h>
+#include <sys/intr.h>
+#include <uvm/uvm.h>
+
+#include <machine/cpuvar.h>
+#ifdef XEN
+#include <xen/xenpmap.h>
+#endif /* XEN */
+#include <x86/i82489reg.h>
+#include <x86/i82489var.h>
+
+/*
+ * TLB shootdown structures.
+ */
+
+typedef struct {
+#ifdef _LP64
+ uintptr_t tp_va[14]; /* whole struct: 128 bytes */
+#else
+ uintptr_t tp_va[13]; /* whole struct: 64 bytes */
+#endif
+ uint16_t tp_count;
+ uint16_t tp_pte;
+ int tp_userpmap;
+ kcpuset_t * tp_cpumask;
+} pmap_tlb_packet_t;
+
+/*
+ * No more than N separate invlpg.
+ *
+ * Statistically, a value of six is big enough to cover the requested number
+ * of pages in ~ 95% of the TLB shootdowns we are getting. We therefore rarely
+ * reach the limit, and increasing it can actually reduce the performance due
+ * to the high cost of invlpg.
+ */
+#define TP_MAXVA 6
+
+/*
+ * TLB shootdown state.
+ */
+static pmap_tlb_packet_t pmap_tlb_packet __cacheline_aligned;
+static volatile u_int pmap_tlb_pendcount __cacheline_aligned;
+static volatile u_int pmap_tlb_gen __cacheline_aligned;
+static struct evcnt pmap_tlb_evcnt __cacheline_aligned;
+
+/*
+ * TLB shootdown statistics.
+ */
+#ifdef TLBSTATS
+static struct evcnt tlbstat_local[TLBSHOOT__MAX];
+static struct evcnt tlbstat_remote[TLBSHOOT__MAX];
+static struct evcnt tlbstat_kernel[TLBSHOOT__MAX];
+static struct evcnt tlbstat_single_req;
+static struct evcnt tlbstat_single_issue;
+static const char * tlbstat_name[ ] = {
+ "APTE",
+ "KENTER",
+ "KREMOVE",
+ "FREE_PTP1",
+ "FREE_PTP2",
+ "REMOVE_PTE",
+ "REMOVE_PTES",
+ "SYNC_PV1",
+ "SYNC_PV2",
+ "WRITE_PROTECT",
+ "ENTER",
+ "UPDATE",
+ "BUS_DMA",
+ "BUS_SPACE"
+};
+#endif
+
+void
+pmap_tlb_init(void)
+{
+
+ memset(&pmap_tlb_packet, 0, sizeof(pmap_tlb_packet_t));
+ pmap_tlb_pendcount = 0;
+ pmap_tlb_gen = 0;
+
+ evcnt_attach_dynamic(&pmap_tlb_evcnt, EVCNT_TYPE_INTR,
+ NULL, "TLB", "shootdown");
+
+#ifdef TLBSTATS
+ int i;
+
+ for (i = 0; i < TLBSHOOT__MAX; i++) {
+ evcnt_attach_dynamic(&tlbstat_local[i], EVCNT_TYPE_MISC,
+ NULL, "tlbshoot local", tlbstat_name[i]);
+ }
+ for (i = 0; i < TLBSHOOT__MAX; i++) {
+ evcnt_attach_dynamic(&tlbstat_remote[i], EVCNT_TYPE_MISC,
+ NULL, "tlbshoot remote", tlbstat_name[i]);
+ }
+ for (i = 0; i < TLBSHOOT__MAX; i++) {
+ evcnt_attach_dynamic(&tlbstat_kernel[i], EVCNT_TYPE_MISC,
+ NULL, "tlbshoot kernel", tlbstat_name[i]);
+ }
+ evcnt_attach_dynamic(&tlbstat_single_req, EVCNT_TYPE_MISC,
+ NULL, "tlbshoot single page", "requests");
+ evcnt_attach_dynamic(&tlbstat_single_issue, EVCNT_TYPE_MISC,
+ NULL, "tlbshoot single page", "issues");
+#endif
+}
+
+void
+pmap_tlb_cpu_init(struct cpu_info *ci)
+{
+ pmap_tlb_packet_t *tp = (pmap_tlb_packet_t *)ci->ci_pmap_data;
+
+ memset(tp, 0, sizeof(pmap_tlb_packet_t));
+ kcpuset_create(&tp->tp_cpumask, true);
+}
+
+static inline void
+pmap_tlbstat_count(struct pmap *pm, vaddr_t va, tlbwhy_t why)
+{
+#ifdef TLBSTATS
+ const cpuid_t cid = cpu_index(curcpu());
+ bool local = false, remote = false;
+
+ if (va != (vaddr_t)-1LL) {
+ atomic_inc_64(&tlbstat_single_req.ev_count);
+ }
+ if (pm == pmap_kernel()) {
+ atomic_inc_64(&tlbstat_kernel[why].ev_count);
+ return;
+ }
+
+ if (va >= VM_MAXUSER_ADDRESS) {
+ remote = kcpuset_isotherset(pm->pm_kernel_cpus, cid);
+ local = kcpuset_isset(pm->pm_kernel_cpus, cid);
+ }
+ remote |= kcpuset_isotherset(pm->pm_cpus, cid);
+ local |= kcpuset_isset(pm->pm_cpus, cid);
+
+ if (local) {
+ atomic_inc_64(&tlbstat_local[why].ev_count);
+ }
+ if (remote) {
+ atomic_inc_64(&tlbstat_remote[why].ev_count);
+ }
+#endif
+}
+
+static inline void
+pmap_tlb_invalidate(const pmap_tlb_packet_t *tp)
+{
+ int i;
+
+ /* Find out what we need to invalidate. */
+ if (tp->tp_count == (uint16_t)-1) {
+ u_int egen = uvm_emap_gen_return();
+ if (tp->tp_pte & PG_G) {
+ /* Invalidating user and kernel TLB entries. */
+ tlbflushg();
+ } else {
+ /* Invalidating user TLB entries only. */
+ tlbflush();
+ }
+ uvm_emap_update(egen);
+ } else {
+ /* Invalidating a single page or a range of pages. */
+ for (i = tp->tp_count - 1; i >= 0; i--) {
+ pmap_update_pg(tp->tp_va[i]);
+ }
+ }
+}
+
+/*
+ * pmap_tlb_shootdown: invalidate a page on all CPUs using pmap 'pm'.
+ */
+void
+pmap_tlb_shootdown(struct pmap *pm, vaddr_t va, pt_entry_t pte, tlbwhy_t why)
+{
+ pmap_tlb_packet_t *tp;
+ int s;
+
+#ifndef XEN
+ KASSERT((pte & PG_G) == 0 || pm == pmap_kernel());
+#endif
+
+ /*
+ * If tearing down the pmap, do nothing. We will flush later
+ * when we are ready to recycle/destroy it.
+ */
+ if (__predict_false(curlwp->l_md.md_gc_pmap == pm)) {
+ return;
+ }
+
+ if ((pte & PG_PS) != 0) {
+ va &= PG_LGFRAME;
+ }
+
+ /*
+ * Add the shootdown operation to our pending set.
+ */
+ s = splvm();
+ tp = (pmap_tlb_packet_t *)curcpu()->ci_pmap_data;
+
+ /* Whole address flush will be needed if PG_G is set. */
+ CTASSERT(PG_G == (uint16_t)PG_G);
+ tp->tp_pte |= (uint16_t)pte;
+
+ if (tp->tp_count == (uint16_t)-1) {
+ /*
+ * Already flushing everything.
+ */
+ } else if (tp->tp_count < TP_MAXVA && va != (vaddr_t)-1LL) {
+ /* Flush a single page. */
+ tp->tp_va[tp->tp_count++] = va;
+ KASSERT(tp->tp_count > 0);
+ } else {
+ /* Flush everything. */
+ tp->tp_count = (uint16_t)-1;
+ }
+
+ if (pm != pmap_kernel()) {
+ kcpuset_merge(tp->tp_cpumask, pm->pm_cpus);
+ if (va >= VM_MAXUSER_ADDRESS) {
+ kcpuset_merge(tp->tp_cpumask, pm->pm_kernel_cpus);
+ }
+ tp->tp_userpmap = 1;
+ } else {
+ kcpuset_copy(tp->tp_cpumask, kcpuset_running);
+ }
+ pmap_tlbstat_count(pm, va, why);
+ splx(s);
+}
+
+#ifdef MULTIPROCESSOR
+#ifdef XEN
+
+static inline void
+pmap_tlb_processpacket(pmap_tlb_packet_t *tp, kcpuset_t *target)
+{
+
+ if (tp->tp_count != (uint16_t)-1) {
+ /* Invalidating a single page or a range of pages. */
+ for (int i = tp->tp_count - 1; i >= 0; i--) {
+ xen_mcast_invlpg(tp->tp_va[i], target);
+ }
+ } else {
+ xen_mcast_tlbflush(target);
+ }
+
+ /* Remote CPUs have been synchronously flushed. */
+ pmap_tlb_pendcount = 0;
+}
+
+#else
+
+static inline void
+pmap_tlb_processpacket(pmap_tlb_packet_t *tp, kcpuset_t *target)
+{
+ int err = 0;
+
+ if (!kcpuset_match(target, kcpuset_attached)) {
+ const struct cpu_info * const self = curcpu();
+ CPU_INFO_ITERATOR cii;
+ struct cpu_info *lci;
+
+ for (CPU_INFO_FOREACH(cii, lci)) {
+ const cpuid_t lcid = cpu_index(lci);
+
+ if (__predict_false(lci == self) ||
+ !kcpuset_isset(target, lcid)) {
+ continue;
+ }
+ err |= x86_ipi(LAPIC_TLB_VECTOR,
+ lci->ci_cpuid, LAPIC_DLMODE_FIXED);
+ }
+ } else {
+ err = x86_ipi(LAPIC_TLB_VECTOR, LAPIC_DEST_ALLEXCL,
+ LAPIC_DLMODE_FIXED);
+ }
+ KASSERT(err == 0);
+}
+
+#endif /* XEN */
+#endif /* MULTIPROCESSOR */
+
+/*
+ * pmap_tlb_shootnow: process pending TLB shootdowns queued on current CPU.
+ *
+ * => Must be called with preemption disabled.
+ */
+void
+pmap_tlb_shootnow(void)
+{
+ pmap_tlb_packet_t *tp;
+ struct cpu_info *ci;
+ kcpuset_t *target;
+ u_int local, gen, rcpucount;
+ cpuid_t cid;
+ int s;
+
+ KASSERT(kpreempt_disabled());
+
+ ci = curcpu();
+ tp = (pmap_tlb_packet_t *)ci->ci_pmap_data;
+
+ /* Pre-check first. */
+ if (tp->tp_count == 0) {
+ return;
+ }
+
+ s = splvm();
+ if (tp->tp_count == 0) {
+ splx(s);
+ return;
+ }
+ cid = cpu_index(ci);
+
+ target = tp->tp_cpumask;
+ local = kcpuset_isset(target, cid) ? 1 : 0;
+ rcpucount = kcpuset_countset(target) - local;
+ gen = 0;
+
+#ifdef MULTIPROCESSOR
+ if (rcpucount) {
+ int count;
+
+ /*
+ * Gain ownership of the shootdown mailbox. We must stay
+ * at IPL_VM once we own it or could deadlock against an
+ * interrupt on this CPU trying to do the same.
+ */
+ KASSERT(rcpucount < ncpu);
+
+ while (atomic_cas_uint(&pmap_tlb_pendcount, 0, rcpucount)) {
+ splx(s);
+ count = SPINLOCK_BACKOFF_MIN;
+ while (pmap_tlb_pendcount) {
+ KASSERT(pmap_tlb_pendcount < ncpu);
+ SPINLOCK_BACKOFF(count);
+ }
+ s = splvm();
+ /* An interrupt might have done it for us. */
+ if (tp->tp_count == 0) {
+ splx(s);
+ return;
+ }
+ }
+
+ /*
+ * Start a new generation of updates. Copy our shootdown
+ * requests into the global buffer. Note that tp_cpumask
+ * will not be used by remote CPUs (it would be unsafe).
+ */
+ gen = ++pmap_tlb_gen;
+ memcpy(&pmap_tlb_packet, tp, sizeof(*tp));
+ pmap_tlb_evcnt.ev_count++;
+
+ /*
+ * Initiate shootdowns on remote CPUs.
+ */
+ pmap_tlb_processpacket(tp, target);
+ }
+#endif
+
+ /*
+ * Shootdowns on remote CPUs are now in flight. In the meantime,
+ * perform local shootdown if needed.
+ */
+ if (local) {
+ pmap_tlb_invalidate(tp);
+ }
+
+ /*
+ * Clear out our local buffer.
+ */
+#ifdef TLBSTATS
+ if (tp->tp_count != (uint16_t)-1) {
+ atomic_add_64(&tlbstat_single_issue.ev_count, tp->tp_count);
+ }
+#endif
+ kcpuset_zero(tp->tp_cpumask);
+ tp->tp_userpmap = 0;
+ tp->tp_count = 0;
+ tp->tp_pte = 0;
+ splx(s);
+
+ /*
+ * Now wait for the current generation of updates to be
+ * processed by remote CPUs.
+ */
+ if (rcpucount && pmap_tlb_pendcount) {
+ int count = SPINLOCK_BACKOFF_MIN;
+
+ while (pmap_tlb_pendcount && pmap_tlb_gen == gen) {
+ KASSERT(pmap_tlb_pendcount < ncpu);
+ SPINLOCK_BACKOFF(count);
+ }
+ }
+}
+
+/*
+ * pmap_tlb_intr: pmap shootdown interrupt handler to invalidate TLB entries.
+ *
+ * => Called from IPI only.
+ */
+void
+pmap_tlb_intr(void)
+{
+ const pmap_tlb_packet_t *tp = &pmap_tlb_packet;
+ struct cpu_info *ci = curcpu();
+
+ KASSERT(pmap_tlb_pendcount > 0);
+
+ /* First, TLB flush. */
+ pmap_tlb_invalidate(tp);
+
+ /*
+ * Check the current TLB state. If we do not want further
+ * invalidations for this pmap, then take the CPU out of
+ * the pmap's bitmask.
+ */
+ if (ci->ci_tlbstate == TLBSTATE_LAZY && tp->tp_userpmap) {
+ struct pmap *pm = ci->ci_pmap;
+ cpuid_t cid = cpu_index(ci);
+
+ kcpuset_atomic_clear(pm->pm_cpus, cid);
+ ci->ci_tlbstate = TLBSTATE_STALE;
+ }
+
+ /* Finally, ack the request. */
+ atomic_dec_uint(&pmap_tlb_pendcount);
+}