Module Name: src
Committed By: rmind
Date: Wed May 26 04:55:24 UTC 2010
Modified Files:
src/sys/arch/x86/conf [rmind-uvmplock]: files.x86
src/sys/arch/x86/include [rmind-uvmplock]: pmap.h
src/sys/arch/x86/x86 [rmind-uvmplock]: pmap.c
Added Files:
src/sys/arch/x86/x86 [rmind-uvmplock]: pmap_tlb.c
Log Message:
Split x86 TLB shootdown code into a separate file.
Code part is under TNF license, as per pmap.c 1.105.2.4 revision.
To generate a diff of this commit:
cvs rdiff -u -r1.54 -r1.54.4.1 src/sys/arch/x86/conf/files.x86
cvs rdiff -u -r1.29.2.3 -r1.29.2.4 src/sys/arch/x86/include/pmap.h
cvs rdiff -u -r1.105.2.5 -r1.105.2.6 src/sys/arch/x86/x86/pmap.c
cvs rdiff -u -r0 -r1.1.2.1 src/sys/arch/x86/x86/pmap_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.54 src/sys/arch/x86/conf/files.x86:1.54.4.1
--- src/sys/arch/x86/conf/files.x86:1.54 Mon Oct 5 23:59:31 2009
+++ src/sys/arch/x86/conf/files.x86 Wed May 26 04:55:23 2010
@@ -1,4 +1,4 @@
-# $NetBSD: files.x86,v 1.54 2009/10/05 23:59:31 rmind Exp $
+# $NetBSD: files.x86,v 1.54.4.1 2010/05/26 04:55:23 rmind Exp $
# options for MP configuration through the MP spec
defflag opt_mpbios.h MPBIOS MPVERBOSE MPDEBUG MPBIOS_SCANPCI
@@ -65,6 +65,7 @@
file arch/x86/x86/patch.c
file arch/x86/x86/platform.c
file arch/x86/x86/pmap.c
+file arch/x86/x86/pmap_tlb.c
file arch/x86/x86/sys_machdep.c
file arch/x86/x86/syscall.c
file arch/x86/x86/vm_machdep.c
Index: src/sys/arch/x86/include/pmap.h
diff -u src/sys/arch/x86/include/pmap.h:1.29.2.3 src/sys/arch/x86/include/pmap.h:1.29.2.4
--- src/sys/arch/x86/include/pmap.h:1.29.2.3 Mon Apr 26 04:48:49 2010
+++ src/sys/arch/x86/include/pmap.h Wed May 26 04:55:23 2010
@@ -1,4 +1,4 @@
-/* $NetBSD: pmap.h,v 1.29.2.3 2010/04/26 04:48:49 rmind Exp $ */
+/* $NetBSD: pmap.h,v 1.29.2.4 2010/05/26 04:55:23 rmind Exp $ */
/*
*
@@ -251,8 +251,9 @@
TLBSHOOT__MAX,
} tlbwhy_t;
-void pmap_tlb_shootdown(pmap_t, vaddr_t, pt_entry_t, tlbwhy_t);
-void pmap_tlb_shootnow(void);
+void pmap_tlb_init(void);
+void pmap_tlb_shootdown(pmap_t, vaddr_t, pt_entry_t, tlbwhy_t);
+void pmap_tlb_shootnow(void);
#define __HAVE_PMAP_EMAP
Index: src/sys/arch/x86/x86/pmap.c
diff -u src/sys/arch/x86/x86/pmap.c:1.105.2.5 src/sys/arch/x86/x86/pmap.c:1.105.2.6
--- src/sys/arch/x86/x86/pmap.c:1.105.2.5 Mon Apr 26 04:48:49 2010
+++ src/sys/arch/x86/x86/pmap.c Wed May 26 04:55:23 2010
@@ -1,4 +1,4 @@
-/* $NetBSD: pmap.c,v 1.105.2.5 2010/04/26 04:48:49 rmind Exp $ */
+/* $NetBSD: pmap.c,v 1.105.2.6 2010/05/26 04:55:23 rmind Exp $ */
/*-
* Copyright (c) 2008, 2010 The NetBSD Foundation, Inc.
@@ -177,7 +177,7 @@
*/
#include <sys/cdefs.h>
-__KERNEL_RCSID(0, "$NetBSD: pmap.c,v 1.105.2.5 2010/04/26 04:48:49 rmind Exp $");
+__KERNEL_RCSID(0, "$NetBSD: pmap.c,v 1.105.2.6 2010/05/26 04:55:23 rmind Exp $");
#include "opt_user_ldt.h"
#include "opt_lockdebug.h"
@@ -321,15 +321,6 @@
* - pmaps_lock
* this lock protects the list of active pmaps (headed by "pmaps").
* we lock it when adding or removing pmaps from this list.
- *
- * tlb shootdown
- *
- * tlb shootdowns are hard interrupts that operate outside the spl
- * framework: they don't 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 stub to handle the interrupts is short and does
- * one of the following: invalidate a set of pages, all user tlb entries
- * or the entire tlb.
*/
const vaddr_t ptp_masks[] = PTP_MASK_INITIALIZER;
@@ -372,41 +363,6 @@
struct evcnt pmap_ldt_evcnt;
/*
- * TLB shootdown state.
- */
-struct evcnt pmap_tlb_evcnt __aligned(64);
-struct pmap_tlb_packet pmap_tlb_packet __aligned(64);
-struct pmap_tlb_mailbox pmap_tlb_mailbox __aligned(64);
-
-/*
- * 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
-
-/*
* global data structures
*/
@@ -1761,32 +1717,14 @@
mutex_init(&pv_hash_locks[i].lock, MUTEX_NODEBUG, IPL_VM);
}
-#ifdef TLBSTATS
- 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
-#if 0 /* XXXrmind */
- evcnt_attach_dynamic(&pmap_tlb_evcnt, EVCNT_TYPE_INTR,
- NULL, "TLB", "shootdown");
+ pmap_tlb_init();
+
evcnt_attach_dynamic(&pmap_iobmp_evcnt, EVCNT_TYPE_MISC,
NULL, "x86", "io bitmap copy");
evcnt_attach_dynamic(&pmap_ldt_evcnt, EVCNT_TYPE_MISC,
NULL, "x86", "ldt sync");
-#endif
+
+
/*
* done: pmap module is up (and ready for business)
*/
@@ -4617,214 +4555,6 @@
}
#endif
-static inline void
-pmap_tlbstat_count(struct pmap *pm, vaddr_t va, tlbwhy_t why)
-{
-#ifdef TLBSTATS
- uint32_t mask;
-
- 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) {
- mask = pm->pm_cpus | pm->pm_kernel_cpus;
- } else {
- mask = pm->pm_cpus;
- }
- if ((mask & curcpu()->ci_cpumask) != 0) {
- atomic_inc_64(&tlbstat_local[why].ev_count);
- }
- if ((mask & ~curcpu()->ci_cpumask) != 0) {
- atomic_inc_64(&tlbstat_remote[why].ev_count);
- }
-#endif
-}
-
-/*
- * pmap_tlb_shootdown: invalidate a page on all CPUs using pmap 'pm'
- */
-
-__noinline void
-pmap_tlb_shootdown(struct pmap *pm, vaddr_t va, pt_entry_t pte, tlbwhy_t why)
-{
- struct pmap_tlb_packet *tp;
- int s;
-
- KASSERT((pte & PG_G) == 0 || pm == pmap_kernel());
-
- /*
- * 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 = (struct pmap_tlb_packet *)curcpu()->ci_pmap_data;
- tp->tp_pte |= (uint16_t)pte;
- if (tp->tp_count < TP_MAXVA && va != (vaddr_t)-1LL) {
- /* Flush a single page. */
- tp->tp_va[tp->tp_count++] = va;
- } else {
- /* Flush everything. */
- tp->tp_count = (uint16_t)-1;
- }
-
- if (pm == pmap_kernel()) {
- tp->tp_cpumask = cpus_running;
- } else if (va >= VM_MAXUSER_ADDRESS) {
- tp->tp_cpumask |= (pm->pm_cpus | pm->pm_kernel_cpus);
- tp->tp_usermask |= (pm->pm_cpus | pm->pm_kernel_cpus);;
- } else {
- tp->tp_cpumask |= pm->pm_cpus;
- tp->tp_usermask |= pm->pm_cpus;
- }
- pmap_tlbstat_count(pm, va, why);
- splx(s);
-}
-
-/*
- * pmap_tlb_shootnow: process pending TLB shootdowns queued on current CPU.
- *
- * => Must be called with preemption disabled.
- */
-
-__noinline void
-pmap_tlb_shootnow(void)
-{
- struct pmap_tlb_packet *tp;
- struct pmap_tlb_mailbox *tm;
- struct cpu_info *ci, *lci;
- CPU_INFO_ITERATOR cii;
- uint32_t remote;
- uintptr_t gen;
- int s, err, i, count;
-
- KASSERT(kpreempt_disabled());
-
- s = splvm();
- ci = curcpu();
- tp = (struct pmap_tlb_packet *)ci->ci_pmap_data;
- if (tp->tp_count == 0) {
- splx(s);
- return;
- }
- tm = &pmap_tlb_mailbox;
- remote = tp->tp_cpumask & ~ci->ci_cpumask;
- gen = 0; /* XXXgcc */
- if (remote != 0) {
- /*
- * Gain ownership of the shootdown mailbox. We must stay
- * at splvm once we own it or could deadlock against an
- * interrupt on this cpu trying to do the same.
- */
- while (atomic_cas_32(&tm->tm_pending, 0, remote) != 0) {
- splx(s);
- count = SPINLOCK_BACKOFF_MIN;
- while (tm->tm_pending != 0) {
- 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.
- */
- gen = ++tm->tm_gen;
- memcpy(&pmap_tlb_packet, tp, sizeof(*tp));
- pmap_tlb_evcnt.ev_count++;
-
- /*
- * Initiate shootdowns on remote CPUs.
- */
- if (tp->tp_cpumask == cpus_running) {
- err = x86_ipi(LAPIC_TLB_VECTOR, LAPIC_DEST_ALLEXCL,
- LAPIC_DLMODE_FIXED);
- } else {
- err = 0;
- for (CPU_INFO_FOREACH(cii, lci)) {
- if ((lci->ci_cpumask & remote) == 0) {
- continue;
- }
- if ((lci->ci_flags & CPUF_RUNNING) == 0) {
- remote &= ~lci->ci_cpumask;
- atomic_and_32(&tm->tm_pending, remote);
- continue;
- }
- err |= x86_ipi(LAPIC_TLB_VECTOR,
- lci->ci_cpuid, LAPIC_DLMODE_FIXED);
- }
- }
- if (__predict_false(err != 0)) {
- panic("pmap_tlb_shootdown: IPI failed");
- }
- }
-
- /*
- * Shootdowns on remote CPUs are now in flight. In the meantime,
- * perform local shootdowns and do not forget to update emap gen.
- */
- if ((tp->tp_cpumask & ci->ci_cpumask) != 0) {
- if (tp->tp_count == (uint16_t)-1) {
- u_int egen = uvm_emap_gen_return();
- if ((tp->tp_pte & PG_G) != 0) {
- tlbflushg();
- } else {
- tlbflush();
- }
- uvm_emap_update(egen);
- } else {
- for (i = tp->tp_count - 1; i >= 0; i--) {
- pmap_update_pg(tp->tp_va[i]);
- }
- }
- }
-
- /*
- * 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
- tp->tp_count = 0;
- tp->tp_pte = 0;
- tp->tp_cpumask = 0;
- tp->tp_usermask = 0;
- splx(s);
-
- /*
- * Now wait for the current generation of updates to be
- * processed by remote CPUs.
- */
- if (remote != 0 && tm->tm_pending != 0) {
- count = SPINLOCK_BACKOFF_MIN;
- while (tm->tm_pending != 0 && tm->tm_gen == gen) {
- SPINLOCK_BACKOFF(count);
- }
- }
-}
-
/*
* pmap_update: process deferred invalidations and frees.
*/
Added files:
Index: src/sys/arch/x86/x86/pmap_tlb.c
diff -u /dev/null src/sys/arch/x86/x86/pmap_tlb.c:1.1.2.1
--- /dev/null Wed May 26 04:55:24 2010
+++ src/sys/arch/x86/x86/pmap_tlb.c Wed May 26 04:55:24 2010
@@ -0,0 +1,323 @@
+/* $NetBSD: pmap_tlb.c,v 1.1.2.1 2010/05/26 04:55:24 rmind Exp $ */
+
+/*-
+ * Copyright (c) 2008, 2010 The NetBSD Foundation, Inc.
+ * All rights reserved.
+ *
+ * This code is derived from software contributed to The NetBSD Foundation
+ * by Andrew Doran.
+ *
+ * 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 stub to handle the interrupts 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: pmap_tlb.c,v 1.1.2.1 2010/05/26 04:55:24 rmind 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>
+#include <x86/pmap.h>
+#include <x86/i82489reg.h>
+#include <x86/i82489var.h>
+
+/*
+ * TLB shootdown state.
+ */
+static struct evcnt pmap_tlb_evcnt __aligned(64);
+struct pmap_tlb_packet pmap_tlb_packet __aligned(64);
+struct pmap_tlb_mailbox pmap_tlb_mailbox __aligned(64);
+
+/*
+ * 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)
+{
+
+ evcnt_attach_dynamic(&pmap_tlb_evcnt, EVCNT_TYPE_INTR,
+ NULL, "TLB", "shootdown");
+
+#ifdef TLBSTATS
+ 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
+}
+
+static inline void
+pmap_tlbstat_count(struct pmap *pm, vaddr_t va, tlbwhy_t why)
+{
+#ifdef TLBSTATS
+ uint32_t mask;
+
+ 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) {
+ mask = pm->pm_cpus | pm->pm_kernel_cpus;
+ } else {
+ mask = pm->pm_cpus;
+ }
+ if ((mask & curcpu()->ci_cpumask) != 0) {
+ atomic_inc_64(&tlbstat_local[why].ev_count);
+ }
+ if ((mask & ~curcpu()->ci_cpumask) != 0) {
+ atomic_inc_64(&tlbstat_remote[why].ev_count);
+ }
+#endif
+}
+
+/*
+ * 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)
+{
+ struct pmap_tlb_packet *tp;
+ int s;
+
+ KASSERT((pte & PG_G) == 0 || pm == pmap_kernel());
+
+ /*
+ * 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 = (struct pmap_tlb_packet *)curcpu()->ci_pmap_data;
+ tp->tp_pte |= (uint16_t)pte;
+ if (tp->tp_count < TP_MAXVA && va != (vaddr_t)-1LL) {
+ /* Flush a single page. */
+ tp->tp_va[tp->tp_count++] = va;
+ } else {
+ /* Flush everything. */
+ tp->tp_count = (uint16_t)-1;
+ }
+
+ if (pm == pmap_kernel()) {
+ tp->tp_cpumask = cpus_running;
+ } else if (va >= VM_MAXUSER_ADDRESS) {
+ tp->tp_cpumask |= (pm->pm_cpus | pm->pm_kernel_cpus);
+ tp->tp_usermask |= (pm->pm_cpus | pm->pm_kernel_cpus);
+ } else {
+ tp->tp_cpumask |= pm->pm_cpus;
+ tp->tp_usermask |= pm->pm_cpus;
+ }
+ pmap_tlbstat_count(pm, va, why);
+ splx(s);
+}
+
+/*
+ * pmap_tlb_shootnow: process pending TLB shootdowns queued on current CPU.
+ *
+ * => Must be called with preemption disabled.
+ */
+void
+pmap_tlb_shootnow(void)
+{
+ struct pmap_tlb_packet *tp;
+ struct pmap_tlb_mailbox *tm;
+ struct cpu_info *ci, *lci;
+ CPU_INFO_ITERATOR cii;
+ uint32_t remote;
+ uintptr_t gen;
+ int s, err, i, count;
+
+ KASSERT(kpreempt_disabled());
+
+ s = splvm();
+ ci = curcpu();
+ tp = (struct pmap_tlb_packet *)ci->ci_pmap_data;
+ if (tp->tp_count == 0) {
+ splx(s);
+ return;
+ }
+ gen = 0; /* XXXgcc */
+ tm = &pmap_tlb_mailbox;
+ remote = tp->tp_cpumask & ~ci->ci_cpumask;
+ if (remote != 0) {
+ /*
+ * 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.
+ */
+ while (atomic_cas_32(&tm->tm_pending, 0, remote) != 0) {
+ splx(s);
+ count = SPINLOCK_BACKOFF_MIN;
+ while (tm->tm_pending != 0) {
+ 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.
+ */
+ gen = ++tm->tm_gen;
+ memcpy(&pmap_tlb_packet, tp, sizeof(*tp));
+ pmap_tlb_evcnt.ev_count++;
+
+ /*
+ * Initiate shootdowns on remote CPUs.
+ */
+ if (tp->tp_cpumask == cpus_running) {
+ err = x86_ipi(LAPIC_TLB_VECTOR, LAPIC_DEST_ALLEXCL,
+ LAPIC_DLMODE_FIXED);
+ } else {
+ err = 0;
+ for (CPU_INFO_FOREACH(cii, lci)) {
+ if ((lci->ci_cpumask & remote) == 0) {
+ continue;
+ }
+ if ((lci->ci_flags & CPUF_RUNNING) == 0) {
+ remote &= ~lci->ci_cpumask;
+ atomic_and_32(&tm->tm_pending, remote);
+ continue;
+ }
+ err |= x86_ipi(LAPIC_TLB_VECTOR,
+ lci->ci_cpuid, LAPIC_DLMODE_FIXED);
+ }
+ }
+ if (__predict_false(err != 0)) {
+ panic("pmap_tlb_shootdown: IPI failed");
+ }
+ }
+
+ /*
+ * Shootdowns on remote CPUs are now in flight. In the meantime,
+ * perform local shootdowns and do not forget to update emap gen.
+ */
+ if ((tp->tp_cpumask & ci->ci_cpumask) != 0) {
+ if (tp->tp_count == (uint16_t)-1) {
+ u_int egen = uvm_emap_gen_return();
+ if ((tp->tp_pte & PG_G) != 0) {
+ tlbflushg();
+ } else {
+ tlbflush();
+ }
+ uvm_emap_update(egen);
+ } else {
+ for (i = tp->tp_count - 1; i >= 0; i--) {
+ pmap_update_pg(tp->tp_va[i]);
+ }
+ }
+ }
+
+ /*
+ * 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
+ tp->tp_count = 0;
+ tp->tp_pte = 0;
+ tp->tp_cpumask = 0;
+ tp->tp_usermask = 0;
+ splx(s);
+
+ /*
+ * Now wait for the current generation of updates to be
+ * processed by remote CPUs.
+ */
+ if (remote != 0 && tm->tm_pending != 0) {
+ count = SPINLOCK_BACKOFF_MIN;
+ while (tm->tm_pending != 0 && tm->tm_gen == gen) {
+ SPINLOCK_BACKOFF(count);
+ }
+ }
+}