--- old/exception_lists/keywords	Tue May  7 04:49:55 2019
+++ new/exception_lists/keywords	Tue May  7 04:49:55 2019
@@ -22,6 +22,7 @@
 # Copyright 2017 Nexenta Systems, Inc.
 # Copyright (c) 2013 by Delphix. All rights reserved.
 # Copyright 2016 Toomas Soome <tsoome@me.com>
+# Copyright 2018 Joyent, Inc.
 #
 
 syntax: glob
@@ -35,6 +36,8 @@
 usr/src/data/locale/data/zh_TW.UTF-8.src
 usr/src/data/terminfo/termcap.src
 usr/src/data/terminfo/terminfo.src
+usr/src/data/ucode/amd/*
+usr/src/data/ucode/intel/*
 usr/src/test/zfs-tests/tests/functional/cli_root/zpool_upgrade/blockfiles/zfs-pool-v15.dat.bz2
 usr/src/test/zfs-tests/tests/functional/delegate/delegate_common.kshlib
 usr/src/test/test-runner/cmd/run
--- old/usr/src/pkg/manifests/system-header.mf	Tue May  7 04:49:55 2019
+++ new/usr/src/pkg/manifests/system-header.mf	Tue May  7 04:49:55 2019
@@ -1679,6 +1679,7 @@
 $(i386_ONLY)file path=usr/platform/i86pc/include/sys/ddi_subrdefs.h
 $(i386_ONLY)file path=usr/platform/i86pc/include/sys/debug_info.h
 $(i386_ONLY)file path=usr/platform/i86pc/include/sys/fastboot.h
+$(i386_ONLY)file path=usr/platform/i86pc/include/sys/ht.h
 $(i386_ONLY)file path=usr/platform/i86pc/include/sys/mach_mmu.h
 $(i386_ONLY)file path=usr/platform/i86pc/include/sys/machclock.h
 $(i386_ONLY)file path=usr/platform/i86pc/include/sys/machcpuvar.h
@@ -1733,6 +1734,7 @@
 $(sparc_ONLY)file path=usr/platform/sun4u/include/sys/fhc.h
 $(sparc_ONLY)file path=usr/platform/sun4u/include/sys/gpio_87317.h
 $(sparc_ONLY)file path=usr/platform/sun4u/include/sys/hpc3130_events.h
+$(sparc_ONLY)file path=usr/platform/sun4u/include/sys/ht.h
 $(sparc_ONLY)file path=usr/platform/sun4u/include/sys/i2c/clients/hpc3130.h
 $(sparc_ONLY)file path=usr/platform/sun4u/include/sys/i2c/clients/i2c_client.h
 $(sparc_ONLY)file path=usr/platform/sun4u/include/sys/i2c/clients/lm75.h
@@ -1795,6 +1797,7 @@
 $(sparc_ONLY)file path=usr/platform/sun4v/include/sys/eeprom.h
 $(sparc_ONLY)file path=usr/platform/sun4v/include/sys/fcode.h
 $(sparc_ONLY)file path=usr/platform/sun4v/include/sys/hsvc.h
+$(sparc_ONLY)file path=usr/platform/sun4v/include/sys/ht.h
 $(sparc_ONLY)file path=usr/platform/sun4v/include/sys/hypervisor_api.h
 $(sparc_ONLY)file path=usr/platform/sun4v/include/sys/idprom.h
 $(sparc_ONLY)file path=usr/platform/sun4v/include/sys/intr.h
--- old/usr/src/uts/common/disp/cpupart.c	Tue May  7 04:49:55 2019
+++ new/usr/src/uts/common/disp/cpupart.c	Tue May  7 04:49:55 2019
@@ -20,7 +20,7 @@
  */
 /*
  * Copyright (c) 1996, 2010, Oracle and/or its affiliates. All rights reserved.
- * Copyright (c) 2017 by Delphix. All rights reserved.
+ * Copyright 2018 Joyent, Inc.
  */
 
 #include <sys/types.h>
@@ -325,7 +325,7 @@
 	kthread_t *t;
 	int	move_threads = 1;
 	lgrp_id_t lgrpid;
-	proc_t 	*p;
+	proc_t	*p;
 	int lgrp_diff_lpl;
 	lpl_t	*cpu_lpl;
 	int	ret;
@@ -570,8 +570,8 @@
 				/* Update CPU last ran on if it was this CPU */
 				if (t->t_cpu == cp && t->t_cpupart == oldpp &&
 				    t->t_bound_cpu != cp) {
-					t->t_cpu = disp_lowpri_cpu(ncp,
-					    t->t_lpl, t->t_pri, NULL);
+					t->t_cpu = disp_lowpri_cpu(ncp, t,
+					    t->t_pri);
 				}
 				t = t->t_forw;
 			} while (t != p->p_tlist);
@@ -623,8 +623,8 @@
 			/* Update CPU last ran on if it was this CPU */
 			if (t->t_cpu == cp && t->t_cpupart == oldpp &&
 			    t->t_bound_cpu != cp) {
-				t->t_cpu = disp_lowpri_cpu(ncp, t->t_lpl,
-				    t->t_pri, NULL);
+				t->t_cpu = disp_lowpri_cpu(ncp, t,
+				    t->t_pri);
 			}
 
 			t = t->t_next;
@@ -879,7 +879,7 @@
 static int
 cpupart_unbind_threads(cpupart_t *pp, boolean_t unbind_all)
 {
-	void 	*projbuf, *zonebuf;
+	void	*projbuf, *zonebuf;
 	kthread_t *t;
 	proc_t	*p;
 	int	err = 0;
--- old/usr/src/uts/common/disp/disp.c	Tue May  7 04:49:55 2019
+++ new/usr/src/uts/common/disp/disp.c	Tue May  7 04:49:56 2019
@@ -23,6 +23,10 @@
  * Use is subject to license terms.
  */
 
+/*
+ * Copyright (c) 2018, Joyent, Inc. All rights reserved.
+ */
+
 /*	Copyright (c) 1984, 1986, 1987, 1988, 1989 AT&T	*/
 /*	  All Rights Reserved  	*/
 
@@ -56,6 +60,7 @@
 #include <sys/dtrace.h>
 #include <sys/sdt.h>
 #include <sys/archsystm.h>
+#include <sys/ht.h>
 
 #include <vm/as.h>
 
@@ -1115,8 +1120,6 @@
 	 */
 }
 
-#define	CPU_IDLING(pri)	((pri) == -1)
-
 static void
 cpu_resched(cpu_t *cp, pri_t tpri)
 {
@@ -1123,7 +1126,7 @@
 	int	call_poke_cpu = 0;
 	pri_t   cpupri = cp->cpu_dispatch_pri;
 
-	if (!CPU_IDLING(cpupri) && (cpupri < tpri)) {
+	if (cpupri != CPU_IDLE_PRI && cpupri < tpri) {
 		TRACE_2(TR_FAC_DISP, TR_CPU_RESCHED,
 		    "CPU_RESCHED:Tpri %d Cpupri %d", tpri, cpupri);
 		if (tpri >= upreemptpri && cp->cpu_runrun == 0) {
@@ -1219,17 +1222,17 @@
 		/*
 		 * We'll generally let this thread continue to run where
 		 * it last ran...but will consider migration if:
-		 * - We thread probably doesn't have much cache warmth.
+		 * - The thread probably doesn't have much cache warmth.
+		 * - HT exclusion would prefer us to run elsewhere
 		 * - The CPU where it last ran is the target of an offline
 		 *   request.
-		 * - The thread last ran outside it's home lgroup.
+		 * - The thread last ran outside its home lgroup.
 		 */
 		if ((!THREAD_HAS_CACHE_WARMTH(tp)) ||
-		    (tp->t_cpu == cpu_inmotion)) {
-			cp = disp_lowpri_cpu(tp->t_cpu, tp->t_lpl, tpri, NULL);
-		} else if (!LGRP_CONTAINS_CPU(tp->t_lpl->lpl_lgrp, tp->t_cpu)) {
-			cp = disp_lowpri_cpu(tp->t_cpu, tp->t_lpl, tpri,
-			    self ? tp->t_cpu : NULL);
+		    !ht_should_run(tp, tp->t_cpu) ||
+		    (tp->t_cpu == cpu_inmotion) ||
+		    !LGRP_CONTAINS_CPU(tp->t_lpl->lpl_lgrp, tp->t_cpu)) {
+			cp = disp_lowpri_cpu(tp->t_cpu, tp, tpri);
 		} else {
 			cp = tp->t_cpu;
 		}
@@ -1258,7 +1261,8 @@
 					newcp = cp->cpu_next_part;
 				}
 
-				if (RUNQ_LEN(newcp, tpri) < qlen) {
+				if (ht_should_run(tp, newcp) &&
+				    RUNQ_LEN(newcp, tpri) < qlen) {
 					DTRACE_PROBE3(runq__balance,
 					    kthread_t *, tp,
 					    cpu_t *, cp, cpu_t *, newcp);
@@ -1269,8 +1273,8 @@
 			/*
 			 * Migrate to a cpu in the new partition.
 			 */
-			cp = disp_lowpri_cpu(tp->t_cpupart->cp_cpulist,
-			    tp->t_lpl, tp->t_pri, NULL);
+			cp = disp_lowpri_cpu(tp->t_cpupart->cp_cpulist, tp,
+			    tp->t_pri);
 		}
 		ASSERT((cp->cpu_flags & CPU_QUIESCED) == 0);
 	} else {
@@ -1407,7 +1411,7 @@
 			/*
 			 * We'll generally let this thread continue to run
 			 * where it last ran, but will consider migration if:
-			 * - The thread last ran outside it's home lgroup.
+			 * - The thread last ran outside its home lgroup.
 			 * - The CPU where it last ran is the target of an
 			 *   offline request (a thread_nomigrate() on the in
 			 *   motion CPU relies on this when forcing a preempt).
@@ -1415,14 +1419,11 @@
 			 *   it last ran, and it is considered not likely to
 			 *   have significant cache warmth.
 			 */
-			if ((!LGRP_CONTAINS_CPU(tp->t_lpl->lpl_lgrp, cp)) ||
-			    (cp == cpu_inmotion)) {
-				cp = disp_lowpri_cpu(tp->t_cpu, tp->t_lpl, tpri,
-				    (tp == curthread) ? cp : NULL);
-			} else if ((tpri < cp->cpu_disp->disp_maxrunpri) &&
-			    (!THREAD_HAS_CACHE_WARMTH(tp))) {
-				cp = disp_lowpri_cpu(tp->t_cpu, tp->t_lpl, tpri,
-				    NULL);
+			if (!LGRP_CONTAINS_CPU(tp->t_lpl->lpl_lgrp, cp) ||
+			    cp == cpu_inmotion ||
+			    (tpri < cp->cpu_disp->disp_maxrunpri &&
+			    !THREAD_HAS_CACHE_WARMTH(tp))) {
+				cp = disp_lowpri_cpu(tp->t_cpu, tp, tpri);
 			}
 		} else {
 			/*
@@ -1429,7 +1430,7 @@
 			 * Migrate to a cpu in the new partition.
 			 */
 			cp = disp_lowpri_cpu(tp->t_cpupart->cp_cpulist,
-			    tp->t_lpl, tp->t_pri, NULL);
+			    tp, tp->t_pri);
 		}
 		ASSERT((cp->cpu_flags & CPU_QUIESCED) == 0);
 	} else {
@@ -1580,7 +1581,7 @@
 		/* migrate to a cpu in the new partition */
 		cp = tp->t_cpupart->cp_cpulist;
 	}
-	cp = disp_lowpri_cpu(cp, tp->t_lpl, tp->t_pri, NULL);
+	cp = disp_lowpri_cpu(cp, tp, tp->t_pri);
 	disp_lock_enter_high(&cp->cpu_disp->disp_lock);
 	ASSERT((cp->cpu_flags & CPU_QUIESCED) == 0);
 
@@ -2553,80 +2554,85 @@
 }
 
 /*
- * disp_lowpri_cpu - find CPU running the lowest priority thread.
- *	The hint passed in is used as a starting point so we don't favor
- *	CPU 0 or any other CPU.  The caller should pass in the most recently
- *	used CPU for the thread.
+ * Return a score rating this CPU for running this thread: lower is better.
  *
- *	The lgroup and priority are used to determine the best CPU to run on
- *	in a NUMA machine.  The lgroup specifies which CPUs are closest while
- *	the thread priority will indicate whether the thread will actually run
- *	there.  To pick the best CPU, the CPUs inside and outside of the given
- *	lgroup which are running the lowest priority threads are found.  The
- *	remote CPU is chosen only if the thread will not run locally on a CPU
- *	within the lgroup, but will run on the remote CPU. If the thread
- *	cannot immediately run on any CPU, the best local CPU will be chosen.
+ * If curthread is looking for a new CPU, then we ignore cpu_dispatch_pri for
+ * curcpu (as that's our own priority).
  *
- *	The lpl specified also identifies the cpu partition from which
- *	disp_lowpri_cpu should select a CPU.
+ * If a cpu is the target of an offline request, then try to avoid it.
  *
- *	curcpu is used to indicate that disp_lowpri_cpu is being called on
- *      behalf of the current thread. (curthread is looking for a new cpu)
- *      In this case, cpu_dispatch_pri for this thread's cpu should be
- *      ignored.
+ * Otherwise we'll use double the effective dispatcher priority for the CPU.
  *
- *      If a cpu is the target of an offline request then try to avoid it.
+ * We do this so ht_adjust_cpu_score() can increment the score if needed,
+ * without ending up over-riding a dispatcher priority.
+ */
+static pri_t
+cpu_score(cpu_t *cp, kthread_t *tp)
+{
+	pri_t score;
+
+	if (tp == curthread && cp == curthread->t_cpu)
+		score = 2 * CPU_IDLE_PRI;
+	else if (cp == cpu_inmotion)
+		score = SHRT_MAX;
+	else
+		score = 2 * cp->cpu_dispatch_pri;
+
+	if (2 * cp->cpu_disp->disp_maxrunpri > score)
+		score = 2 * cp->cpu_disp->disp_maxrunpri;
+	if (2 * cp->cpu_chosen_level > score)
+		score = 2 * cp->cpu_chosen_level;
+
+	return (ht_adjust_cpu_score(tp, cp, score));
+}
+
+/*
+ * disp_lowpri_cpu - find a suitable CPU to run the given thread.
  *
- *	This function must be called at either high SPL, or with preemption
- *	disabled, so that the "hint" CPU cannot be removed from the online
- *	CPU list while we are traversing it.
+ * We are looking for a CPU with an effective dispatch priority lower than the
+ * thread's, so that the thread will run immediately rather than be enqueued.
+ * For NUMA locality, we prefer "home" CPUs within the thread's ->t_lpl group.
+ * If we don't find an available CPU there, we will expand our search to include
+ * wider locality levels. (Note these groups are already divided by CPU
+ * partition.)
+ *
+ * If the thread cannot immediately run on *any* CPU, we'll enqueue ourselves on
+ * the best home CPU we found.
+ *
+ * The hint passed in is used as a starting point so we don't favor CPU 0 or any
+ * other CPU.  The caller should pass in the most recently used CPU for the
+ * thread; it's of course possible that this CPU isn't in the home lgroup.
+ *
+ * This function must be called at either high SPL, or with preemption disabled,
+ * so that the "hint" CPU cannot be removed from the online CPU list while we
+ * are traversing it.
  */
 cpu_t *
-disp_lowpri_cpu(cpu_t *hint, lpl_t *lpl, pri_t tpri, cpu_t *curcpu)
+disp_lowpri_cpu(cpu_t *hint, kthread_t *tp, pri_t tpri)
 {
 	cpu_t	*bestcpu;
 	cpu_t	*besthomecpu;
 	cpu_t   *cp, *cpstart;
 
-	pri_t   bestpri;
-	pri_t   cpupri;
-
 	klgrpset_t	done;
-	klgrpset_t	cur_set;
 
 	lpl_t		*lpl_iter, *lpl_leaf;
-	int		i;
 
-	/*
-	 * Scan for a CPU currently running the lowest priority thread.
-	 * Cannot get cpu_lock here because it is adaptive.
-	 * We do not require lock on CPU list.
-	 */
 	ASSERT(hint != NULL);
-	ASSERT(lpl != NULL);
-	ASSERT(lpl->lpl_ncpu > 0);
+	ASSERT(tp->t_lpl->lpl_ncpu > 0);
 
-	/*
-	 * First examine local CPUs. Note that it's possible the hint CPU
-	 * passed in in remote to the specified home lgroup. If our priority
-	 * isn't sufficient enough such that we can run immediately at home,
-	 * then examine CPUs remote to our home lgroup.
-	 * We would like to give preference to CPUs closest to "home".
-	 * If we can't find a CPU where we'll run at a given level
-	 * of locality, we expand our search to include the next level.
-	 */
 	bestcpu = besthomecpu = NULL;
 	klgrpset_clear(done);
-	/* start with lpl we were passed */
 
-	lpl_iter = lpl;
+	lpl_iter = tp->t_lpl;
 
 	do {
+		pri_t best = SHRT_MAX;
+		klgrpset_t cur_set;
 
-		bestpri = SHRT_MAX;
 		klgrpset_clear(cur_set);
 
-		for (i = 0; i < lpl_iter->lpl_nrset; i++) {
+		for (int i = 0; i < lpl_iter->lpl_nrset; i++) {
 			lpl_leaf = lpl_iter->lpl_rset[i];
 			if (klgrpset_ismember(done, lpl_leaf->lpl_lgrpid))
 				continue;
@@ -2639,34 +2645,25 @@
 				cp = cpstart = lpl_leaf->lpl_cpus;
 
 			do {
-				if (cp == curcpu)
-					cpupri = -1;
-				else if (cp == cpu_inmotion)
-					cpupri = SHRT_MAX;
-				else
-					cpupri = cp->cpu_dispatch_pri;
-				if (cp->cpu_disp->disp_maxrunpri > cpupri)
-					cpupri = cp->cpu_disp->disp_maxrunpri;
-				if (cp->cpu_chosen_level > cpupri)
-					cpupri = cp->cpu_chosen_level;
-				if (cpupri < bestpri) {
-					if (CPU_IDLING(cpupri)) {
-						ASSERT((cp->cpu_flags &
-						    CPU_QUIESCED) == 0);
-						return (cp);
-					}
+				pri_t score = cpu_score(cp, tp);
+
+				if (score < best) {
+					best = score;
 					bestcpu = cp;
-					bestpri = cpupri;
+
+					/* An idle CPU: we're done. */
+					if (score / 2 == CPU_IDLE_PRI)
+						goto out;
 				}
 			} while ((cp = cp->cpu_next_lpl) != cpstart);
 		}
 
-		if (bestcpu && (tpri > bestpri)) {
-			ASSERT((bestcpu->cpu_flags & CPU_QUIESCED) == 0);
-			return (bestcpu);
-		}
+		if (bestcpu != NULL && tpri > (best / 2))
+			goto out;
+
 		if (besthomecpu == NULL)
 			besthomecpu = bestcpu;
+
 		/*
 		 * Add the lgrps we just considered to the "done" set
 		 */
@@ -2678,8 +2675,11 @@
 	 * The specified priority isn't high enough to run immediately
 	 * anywhere, so just return the best CPU from the home lgroup.
 	 */
-	ASSERT((besthomecpu->cpu_flags & CPU_QUIESCED) == 0);
-	return (besthomecpu);
+	bestcpu = besthomecpu;
+
+out:
+	ASSERT((bestcpu->cpu_flags & CPU_QUIESCED) == 0);
+	return (bestcpu);
 }
 
 /*
@@ -2698,4 +2698,20 @@
 static void
 generic_enq_thread(cpu_t *cpu, int bound)
 {
+}
+
+cpu_t *
+disp_choose_best_cpu(void)
+{
+	kthread_t *t = curthread;
+	cpu_t *curcpu = CPU;
+
+	ASSERT(t->t_preempt > 0);
+	ASSERT(t->t_state == TS_ONPROC);
+	ASSERT(t->t_schedflag & TS_VCPU);
+
+	if (ht_should_run(t, curcpu))
+		return (curcpu);
+
+	return (disp_lowpri_cpu(curcpu, t, t->t_pri));
 }
--- old/usr/src/uts/common/disp/thread.c	Tue May  7 04:49:56 2019
+++ new/usr/src/uts/common/disp/thread.c	Tue May  7 04:49:56 2019
@@ -75,7 +75,12 @@
 #include <sys/cpucaps.h>
 #include <sys/kiconv.h>
 #include <sys/ctype.h>
+#include <sys/ht.h>
 
+#ifndef	STACK_GROWTH_DOWN
+#error Stacks do not grow downward; 3b2 zombie attack detected!
+#endif
+
 struct kmem_cache *thread_cache;	/* cache of free threads */
 struct kmem_cache *lwp_cache;		/* cache of free lwps */
 struct kmem_cache *turnstile_cache;	/* cache of free turnstiles */
@@ -516,8 +521,8 @@
 	if (CPU->cpu_part == &cp_default)
 		t->t_cpu = CPU;
 	else
-		t->t_cpu = disp_lowpri_cpu(cp_default.cp_cpulist, t->t_lpl,
-		    t->t_pri, NULL);
+		t->t_cpu = disp_lowpri_cpu(cp_default.cp_cpulist, t,
+		    t->t_pri);
 
 	t->t_disp_queue = t->t_cpu->cpu_disp;
 	kpreempt_enable();
@@ -1326,6 +1331,8 @@
 	itp = t->t_intr;		/* interrupted thread */
 	t->t_intr = NULL;		/* clear interrupt ptr */
 
+	ht_end_intr();
+
 	/*
 	 * Get state from interrupt thread for the one
 	 * it interrupted.
--- old/usr/src/uts/common/fs/zfs/zvol.c	Tue May  7 04:49:56 2019
+++ new/usr/src/uts/common/fs/zfs/zvol.c	Tue May  7 04:49:56 2019
@@ -25,7 +25,6 @@
  *
  * Copyright 2017 Nexenta Systems, Inc.  All rights reserved.
  * Copyright (c) 2012, 2017 by Delphix. All rights reserved.
- * Copyright (c) 2013, Joyent, Inc. All rights reserved.
  * Copyright (c) 2014 Integros [integros.com]
  * Copyright (c) 2019, Joyent, Inc.
  */
@@ -90,6 +89,7 @@
 #include <sys/zfeature.h>
 #include <sys/zio_checksum.h>
 #include <sys/zil_impl.h>
+#include <sys/ht.h>
 #include <sys/dkioc_free_util.h>
 #include <sys/zfs_rlock.h>
 
@@ -1272,6 +1272,8 @@
 	    (zv->zv_objset->os_sync == ZFS_SYNC_ALWAYS)) &&
 	    !doread && !is_dumpified;
 
+	ht_begin_unsafe();
+
 	/*
 	 * There must be no buffer changes when doing a dmu_sync() because
 	 * we can't change the data whilst calculating the checksum.
@@ -1319,6 +1321,8 @@
 		zil_commit(zv->zv_zilog, ZVOL_OBJ);
 	biodone(bp);
 
+	ht_end_unsafe();
+
 	return (0);
 }
 
@@ -1396,6 +1400,8 @@
 		return (error);
 	}
 
+	ht_begin_unsafe();
+
 	locked_range_t *lr = rangelock_enter(&zv->zv_rangelock,
 	    uio->uio_loffset, uio->uio_resid, RL_READER);
 	while (uio->uio_resid > 0 && uio->uio_loffset < volsize) {
@@ -1415,6 +1421,8 @@
 	}
 	rangelock_exit(lr);
 
+	ht_end_unsafe();
+
 	return (error);
 }
 
@@ -1443,6 +1451,8 @@
 		return (error);
 	}
 
+	ht_begin_unsafe();
+
 	sync = !(zv->zv_flags & ZVOL_WCE) ||
 	    (zv->zv_objset->os_sync == ZFS_SYNC_ALWAYS);
 
@@ -1474,6 +1484,9 @@
 
 	if (sync)
 		zil_commit(zv->zv_zilog, ZVOL_OBJ);
+
+	ht_end_unsafe();
+
 	return (error);
 }
 
@@ -1714,11 +1727,17 @@
 	case DKIOCFLUSHWRITECACHE:
 		dkc = (struct dk_callback *)arg;
 		mutex_exit(&zfsdev_state_lock);
+
+		ht_begin_unsafe();
+
 		zil_commit(zv->zv_zilog, ZVOL_OBJ);
 		if ((flag & FKIOCTL) && dkc != NULL && dkc->dkc_callback) {
 			(*dkc->dkc_callback)(dkc->dkc_cookie, error);
 			error = 0;
 		}
+
+		ht_end_unsafe();
+
 		return (error);
 
 	case DKIOCGETWCE:
@@ -1743,7 +1762,9 @@
 		} else {
 			zv->zv_flags &= ~ZVOL_WCE;
 			mutex_exit(&zfsdev_state_lock);
+			ht_begin_unsafe();
 			zil_commit(zv->zv_zilog, ZVOL_OBJ);
+			ht_end_unsafe();
 		}
 		return (0);
 	}
@@ -1796,6 +1817,8 @@
 
 		mutex_exit(&zfsdev_state_lock);
 
+		ht_begin_unsafe();
+
 		for (int i = 0; i < dfl->dfl_num_exts; i++) {
 			uint64_t start = dfl->dfl_exts[i].dfle_start,
 			    length = dfl->dfl_exts[i].dfle_length,
@@ -1851,6 +1874,8 @@
 		if (!(flag & FKIOCTL))
 			dfl_free(dfl);
 
+		ht_end_unsafe();
+
 		return (error);
 	}
 
--- old/usr/src/uts/common/os/cpu.c	Tue May  7 04:49:57 2019
+++ new/usr/src/uts/common/os/cpu.c	Tue May  7 04:49:57 2019
@@ -399,6 +399,9 @@
  * CPUs prior to a successful return, it should take extra precautions (such as
  * their own call to kpreempt_disable) to ensure that safety.
  *
+ * CPU_BEST can be used to pick a "best" CPU to migrate to, including
+ * potentially the current CPU.
+ *
  * A CPU affinity reference count is maintained by thread_affinity_set and
  * thread_affinity_clear (incrementing and decrementing it, respectively),
  * maintaining CPU affinity while the count is non-zero, and allowing regions
@@ -415,6 +418,10 @@
 		VERIFY3P(t, ==, curthread);
 		kpreempt_disable();
 		cp = CPU;
+	} else if (cpu_id == CPU_BEST) {
+		VERIFY3P(t, ==, curthread);
+		kpreempt_disable();
+		cp = disp_choose_best_cpu();
 	} else {
 		/*
 		 * We should be asserting that cpu_lock is held here, but
@@ -452,9 +459,8 @@
 		thread_unlock(t);
 	}
 
-	if (cpu_id == CPU_CURRENT) {
+	if (cpu_id == CPU_CURRENT || cpu_id == CPU_BEST)
 		kpreempt_enable();
-	}
 }
 
 /*
@@ -1489,8 +1495,8 @@
 				 * Update CPU last ran on if it was this CPU
 				 */
 				if (t->t_cpu == cp && t->t_bound_cpu != cp)
-					t->t_cpu = disp_lowpri_cpu(ncp,
-					    t->t_lpl, t->t_pri, NULL);
+					t->t_cpu = disp_lowpri_cpu(ncp, t,
+					    t->t_pri);
 				ASSERT(t->t_cpu != cp || t->t_bound_cpu == cp ||
 				    t->t_weakbound_cpu == cp);
 
@@ -1532,10 +1538,9 @@
 			 * Update CPU last ran on if it was this CPU
 			 */
 
-			if (t->t_cpu == cp && t->t_bound_cpu != cp) {
-				t->t_cpu = disp_lowpri_cpu(ncp,
-				    t->t_lpl, t->t_pri, NULL);
-			}
+			if (t->t_cpu == cp && t->t_bound_cpu != cp)
+				t->t_cpu = disp_lowpri_cpu(ncp, t, t->t_pri);
+
 			ASSERT(t->t_cpu != cp || t->t_bound_cpu == cp ||
 			    t->t_weakbound_cpu == cp);
 			t = t->t_next;
--- old/usr/src/uts/common/os/lgrp.c	Tue May  7 04:49:57 2019
+++ new/usr/src/uts/common/os/lgrp.c	Tue May  7 04:49:57 2019
@@ -21,6 +21,7 @@
 /*
  * Copyright 2009 Sun Microsystems, Inc.  All rights reserved.
  * Use is subject to license terms.
+ * Copyright 2018 Joyent, Inc.
  */
 
 /*
@@ -90,6 +91,7 @@
 #include <sys/pg.h>
 #include <sys/promif.h>
 #include <sys/sdt.h>
+#include <sys/ht.h>
 
 lgrp_gen_t	lgrp_gen = 0;		/* generation of lgroup hierarchy */
 lgrp_t *lgrp_table[NLGRPS_MAX]; /* table of all initialized lgrp_t structs */
@@ -520,6 +522,8 @@
 {
 	klgrpset_t changed;
 
+	ht_init();
+
 	/*
 	 * Update lgroup topology (if necessary)
 	 */
--- old/usr/src/uts/common/sys/cpuvar.h	Tue May  7 04:49:57 2019
+++ new/usr/src/uts/common/sys/cpuvar.h	Tue May  7 04:49:57 2019
@@ -23,6 +23,7 @@
  * Copyright (c) 1989, 2010, Oracle and/or its affiliates. All rights reserved.
  * Copyright (c) 2012 by Delphix. All rights reserved.
  * Copyright 2014 Igor Kozhukhov <ikozhukhov@gmail.com>.
+ * Copyright 2018 Joyent, Inc.
  * Copyright 2017 RackTop Systems.
  */
 
@@ -572,13 +573,19 @@
 #endif
 
 /*
- * CPU_CURRENT indicates to thread_affinity_set to use CPU->cpu_id
- * as the target and to grab cpu_lock instead of requiring the caller
- * to grab it.
+ * CPU_CURRENT indicates to thread_affinity_set() to use whatever curthread's
+ * current CPU is; holding cpu_lock is not required.
  */
 #define	CPU_CURRENT	-3
 
 /*
+ * CPU_BEST can be used by thread_affinity_set() callers to set affinity to a
+ * good CPU (in particular, an ht_acquire()-friendly choice); holding cpu_lock
+ * is not required.
+ */
+#define	CPU_BEST	-4
+
+/*
  * Per-CPU statistics
  *
  * cpu_stats_t contains numerous system and VM-related statistics, in the form
--- old/usr/src/uts/common/sys/disp.h	Tue May  7 04:49:58 2019
+++ new/usr/src/uts/common/sys/disp.h	Tue May  7 04:49:58 2019
@@ -23,6 +23,8 @@
  * Use is subject to license terms.
  *
  * Copyright 2013 Nexenta Systems, Inc.  All rights reserved.
+ *
+ * Copyright 2018 Joyent, Inc.
  */
 
 /*	Copyright (c) 1984, 1986, 1987, 1988, 1989 AT&T	*/
@@ -63,11 +65,11 @@
 	/*
 	 * Priorities:
 	 *	disp_maxrunpri is the maximum run priority of runnable threads
-	 * 	on this queue.  It is -1 if nothing is runnable.
+	 *	on this queue.  It is -1 if nothing is runnable.
 	 *
 	 *	disp_max_unbound_pri is the maximum run priority of threads on
 	 *	this dispatch queue but runnable by any CPU.  This may be left
-	 * 	artificially high, then corrected when some CPU tries to take
+	 *	artificially high, then corrected when some CPU tries to take
 	 *	an unbound thread.  It is -1 if nothing is runnable.
 	 */
 	pri_t		disp_maxrunpri;	/* maximum run priority */
@@ -151,8 +153,7 @@
 extern void		cpu_rechoose(kthread_t *);
 extern void		cpu_surrender(kthread_t *);
 extern void		kpreempt(int);
-extern struct cpu	*disp_lowpri_cpu(struct cpu *, struct lgrp_ld *, pri_t,
-			    struct cpu *);
+extern struct cpu	*disp_lowpri_cpu(struct cpu *, kthread_t *, pri_t);
 extern int		disp_bound_threads(struct cpu *, int);
 extern int		disp_bound_anythreads(struct cpu *, int);
 extern int		disp_bound_partition(struct cpu *, int);
@@ -167,6 +168,8 @@
 extern void		disp_swapped_enq(kthread_t *);
 extern int		disp_anywork(void);
 
+extern struct cpu	*disp_choose_best_cpu(void);
+
 #define	KPREEMPT_SYNC		(-1)
 #define	kpreempt_disable()				\
 	{						\
@@ -183,6 +186,8 @@
 
 #endif	/* _KERNEL */
 
+#define	CPU_IDLE_PRI (-1)
+
 #ifdef	__cplusplus
 }
 #endif
--- old/usr/src/uts/common/sys/thread.h	Tue May  7 04:49:58 2019
+++ new/usr/src/uts/common/sys/thread.h	Tue May  7 04:49:58 2019
@@ -351,6 +351,8 @@
 	kmutex_t	t_wait_mutex;	/* used in CV wait functions */
 
 	char		*t_name;	/* thread name */
+
+	uint64_t	t_unsafe;	/* unsafe to run with HT VCPU thread */
 } kthread_t;
 
 /*
@@ -413,6 +415,7 @@
 #define	TS_SIGNALLED	0x0010	/* thread was awakened by cv_signal() */
 #define	TS_PROJWAITQ	0x0020	/* thread is on its project's waitq */
 #define	TS_ZONEWAITQ	0x0040	/* thread is on its zone's waitq */
+#define	TS_VCPU		0x0080	/* thread will enter guest context */
 #define	TS_CSTART	0x0100	/* setrun() by continuelwps() */
 #define	TS_UNPAUSE	0x0200	/* setrun() by unpauselwps() */
 #define	TS_XSTART	0x0400	/* setrun() by SIGCONT */
--- old/usr/src/uts/i86pc/Makefile.files	Tue May  7 04:49:58 2019
+++ new/usr/src/uts/i86pc/Makefile.files	Tue May  7 04:49:58 2019
@@ -69,6 +69,7 @@
 	hment.o			\
 	hold_page.o		\
 	hrtimers.o		\
+	ht.o			\
 	htable.o		\
 	hypercall.o		\
 	hypersubr.o		\
--- old/usr/src/uts/i86pc/io/apix/apix_intr.c	Tue May  7 04:49:58 2019
+++ new/usr/src/uts/i86pc/io/apix/apix_intr.c	Tue May  7 04:49:58 2019
@@ -22,6 +22,7 @@
 /*
  * Copyright (c) 2010, Oracle and/or its affiliates. All rights reserved.
  * Copyright 2018 Western Digital Corporation.  All rights reserved.
+ * Copyright 2018 Joyent, Inc.
  */
 
 #include <sys/cpuvar.h>
@@ -68,6 +69,7 @@
 #include <vm/hat_i86.h>
 #include <sys/stack.h>
 #include <sys/apix.h>
+#include <sys/ht.h>
 
 static void apix_post_hardint(int);
 
@@ -280,6 +282,7 @@
 
 	it->t_intr = t;
 	cpu->cpu_thread = it;
+	ht_begin_intr(pil);
 
 	/*
 	 * Set bit for this pil in CPU's interrupt active bitmask.
@@ -350,7 +353,9 @@
 	it->t_link = cpu->cpu_intr_thread;
 	cpu->cpu_intr_thread = it;
 	it->t_state = TS_FREE;
+	ht_end_intr();
 	cpu->cpu_thread = t;
+
 	if (t->t_flag & T_INTR_THREAD)
 		t->t_intr_start = now;
 	basespl = cpu->cpu_base_spl;
@@ -466,6 +471,8 @@
 		}
 	}
 
+	ht_begin_intr(pil);
+
 	/* store starting timestamp in CPu structure for this IPL */
 	mcpu->pil_high_start[pil - (LOCK_LEVEL + 1)] = now;
 
@@ -556,6 +563,8 @@
 			t->t_intr_start = now;
 	}
 
+	ht_end_intr();
+
 	mcpu->mcpu_pri = oldpil;
 	if (pil < CBE_HIGH_PIL)
 		(void) (*setlvlx)(oldpil, 0);
@@ -668,6 +677,7 @@
 	it->t_state = TS_ONPROC;
 
 	cpu->cpu_thread = it;
+	ht_begin_intr(pil);
 
 	/*
 	 * Initialize thread priority level from intr_pri
@@ -756,7 +766,9 @@
 	cpu->cpu_intr_thread = it;
 	it->t_state = TS_FREE;
 
+	ht_end_intr();
 	cpu->cpu_thread = t;
+
 	if (t->t_flag & T_INTR_THREAD)
 		t->t_intr_start = now;
 	basespl = cpu->cpu_base_spl;
--- old/usr/src/uts/i86pc/io/apix/apix_utils.c	Tue May  7 04:49:59 2019
+++ new/usr/src/uts/i86pc/io/apix/apix_utils.c	Tue May  7 04:49:59 2019
@@ -29,6 +29,7 @@
 /*
  * Copyright 2013 Nexenta Systems, Inc.  All rights reserved.
  * Copyright 2013 Pluribus Networks, Inc.
+ * Copyright 2018 Joyent, Inc.
  */
 
 #include <sys/processor.h>
@@ -66,6 +67,7 @@
 #include <sys/x_call.h>
 #include <sys/reboot.h>
 #include <sys/apix.h>
+#include <sys/ht.h>
 
 static int apix_get_avail_vector_oncpu(uint32_t, int, int);
 static apix_vector_t *apix_init_vector(processorid_t, uchar_t);
@@ -803,6 +805,9 @@
 
 	vecp->v_share++;
 	vecp->v_pri = (ipl > vecp->v_pri) ? ipl : vecp->v_pri;
+
+	ht_intr_alloc_pil(vecp->v_pri);
+
 	if (vecp->v_autovect == NULL) {	/* Nothing on list - put it at head */
 		vecp->v_autovect = mem;
 		return;
@@ -1468,7 +1473,7 @@
 	if (!apix_is_cpu_enabled(newcpu))
 		return (NULL);
 
-	if (vecp->v_cpuid == newcpu) 	/* rebind to the same cpu */
+	if (vecp->v_cpuid == newcpu)	/* rebind to the same cpu */
 		return (vecp);
 
 	APIX_ENTER_CPU_LOCK(oldcpu);
--- old/usr/src/uts/i86pc/io/pcplusmp/apic.c	Tue May  7 04:49:59 2019
+++ new/usr/src/uts/i86pc/io/pcplusmp/apic.c	Tue May  7 04:49:59 2019
@@ -82,6 +82,7 @@
 #include <sys/hpet.h>
 #include <sys/apic_common.h>
 #include <sys/apic_timer.h>
+#include <sys/ht.h>
 
 /*
  *	Local Function Prototypes
@@ -300,6 +301,11 @@
 		apic_ipltopri[j] = (i << APIC_IPL_SHIFT) + APIC_BASE_VECT;
 	apic_init_common();
 
+	/*
+	 * For pcplusmp, we'll keep things simple and always disable this.
+	 */
+	ht_intr_alloc_pil(XC_CPUPOKE_PIL);
+
 	apic_pir_vect = apic_get_ipivect(XC_CPUPOKE_PIL, -1);
 
 #if !defined(__amd64)
--- old/usr/src/uts/i86pc/os/cpuid.c	Tue May  7 04:49:59 2019
+++ new/usr/src/uts/i86pc/os/cpuid.c	Tue May  7 04:49:59 2019
@@ -2120,6 +2120,19 @@
 }
 
 static void
+spec_l1d_flush_noop(void)
+{
+}
+
+static void
+spec_l1d_flush_msr(void)
+{
+	wrmsr(MSR_IA32_FLUSH_CMD, IA32_FLUSH_CMD_L1D);
+}
+
+void (*spec_l1d_flush)(void) = spec_l1d_flush_noop;
+
+static void
 cpuid_scan_security(cpu_t *cpu, uchar_t *featureset)
 {
 	struct cpuid_info *cpi = cpu->cpu_m.mcpu_cpi;
@@ -2204,6 +2217,43 @@
 		if (ecp->cp_edx & CPUID_INTC_EDX_7_0_FLUSH_CMD)
 			add_x86_feature(featureset, X86FSET_FLUSH_CMD);
 	}
+
+	if (cpu->cpu_id != 0)
+		return;
+
+	/*
+	 * We're the boot CPU, so let's figure out our L1TF status.
+	 *
+	 * First, if this is a RDCL_NO CPU, then we are not vulnerable: we don't
+	 * need to exclude with ht_acquire(), and we don't need to flush.
+	 */
+	if (is_x86_feature(featureset, X86FSET_RDCL_NO)) {
+		extern int ht_exclusion;
+		ht_exclusion = 0;
+		spec_l1d_flush = spec_l1d_flush_noop;
+		membar_producer();
+		return;
+	}
+
+	/*
+	 * If HT is enabled, we will need HT exclusion, as well as the flush on
+	 * VM entry.  If HT isn't enabled, we still need at least the flush for
+	 * the L1TF sequential case.
+	 *
+	 * However, if X86FSET_L1D_VM_NO is set, we're most likely running
+	 * inside a VM ourselves, and we don't need the flush.
+	 *
+	 * If we don't have the FLUSH_CMD available at all, we'd better just
+	 * hope HT is disabled.
+	 */
+	if (is_x86_feature(featureset, X86FSET_FLUSH_CMD) &&
+	    !is_x86_feature(featureset, X86FSET_L1D_VM_NO)) {
+		spec_l1d_flush = spec_l1d_flush_msr;
+	} else {
+		spec_l1d_flush = spec_l1d_flush_noop;
+	}
+
+	membar_producer();
 }
 
 /*
--- /dev/null	Tue May  7 04:50:00 2019
+++ new/usr/src/uts/i86pc/os/ht.c	Tue May  7 04:50:00 2019
@@ -0,0 +1,613 @@
+/*
+ * This file and its contents are supplied under the terms of the
+ * Common Development and Distribution License ("CDDL"), version 1.0.
+ * You may only use this file in accordance with the terms of version
+ * 1.0 of the CDDL.
+ *
+ * A full copy of the text of the CDDL should have accompanied this
+ * source.  A copy of the CDDL is also available via the Internet at
+ * http://www.illumos.org/license/CDDL.
+ */
+
+/*
+ * Copyright 2018 Joyent, Inc.
+ */
+
+/*
+ * HT exclusion: prevent a sibling in a hyper-threaded core from running in VMX
+ * non-root guest mode, when certain threads are running on the other sibling.
+ * This avoids speculation-based information leaks such as L1TF being available
+ * to the untrusted guest.  The stance we take is that threads from the same
+ * zone as the guest VPCU thread are considered safe to run alongside, but all
+ * other threads (except the idle thread), and all interrupts, are unsafe.  Note
+ * that due to the implementation here, there are significant sections of e.g.
+ * the dispatcher code that can run concurrently with a guest, until the thread
+ * reaches ht_mark().  This code assumes there are only two HT threads per core.
+ *
+ * The entry points are as follows:
+ *
+ * ht_mark_as_vcpu()
+ *
+ * All threads that enter guest mode (i.e. VCPU threads) need to call this at
+ * least once, which sets TS_VCPU in ->t_schedflag.
+ *
+ * ht_mark()
+ *
+ * A new ->cpu_thread is now curthread (although interrupt threads have their
+ * own separate handling).  After preventing any interrupts, we will take our
+ * own CPU's spinlock and update our own state in mcpu_ht.
+ *
+ * If our sibling is poisoned (i.e. in guest mode or the little bit of code
+ * around it), and we're not compatible (that is, same zone ID, or the idle
+ * thread), then we need to ht_kick() that sibling.  ht_kick() itself waits for
+ * the sibling to call ht_release(), and it will not re-enter guest mode until
+ * allowed.
+ *
+ * Note that we ignore the fact a process can change its zone ID: poisoning
+ * threads never do so, and we can ignore the other cases.
+ *
+ * ht_acquire()
+ *
+ * We are a VCPU thread about to start guest execution.  Interrupts are
+ * disabled.  We must have already run ht_mark() to be in this code, so there's
+ * no need to take our *own* spinlock in order to mark ourselves as CM_POISONED.
+ * Instead, we take our sibling's lock to also mark ourselves as poisoned in the
+ * sibling cpu_ht_t.  This is so ht_mark() will only ever need to look at its
+ * local mcpu_ht.
+ *
+ * We'll loop here for up to ht_acquire_wait_time microseconds; this is mainly
+ * to wait out any sibling interrupt: many of them will complete quicker than
+ * this.
+ *
+ * Finally, if we succeeded in acquiring the core, we'll flush the L1 cache as
+ * mitigation against L1TF: no incompatible thread will now be able to populate
+ * the L1 cache until *we* ht_release().
+ *
+ * ht_release()
+ *
+ * Simply unpoison ourselves similarly to ht_acquire(); ht_kick() will wait for
+ * this to happen if needed.
+ *
+ * ht_begin_intr()
+ *
+ * In an interrupt prolog.  We're either a hilevel interrupt, or a pinning
+ * interrupt.  In both cases, we mark our interrupt depth, and potentially
+ * ht_kick().  This enforces exclusion, but doesn't otherwise modify ->ch_state:
+ * we want the dispatcher code to essentially ignore interrupts.
+ *
+ * ht_end_intr()
+ *
+ * In an interrupt epilogue *or* thread_unpin().  In the first case, we never
+ * slept, and we can simply decrement our counter.  In the second case, we're an
+ * interrupt thread about to sleep: we'll still just decrement our counter, and
+ * henceforth treat the thread as a normal thread when it next gets scheduled,
+ * until it finally gets to its epilogue.
+ *
+ * ht_mark_unsafe() / ht_mark_safe()
+ *
+ * Mark the current thread as temporarily unsafe (guests should not be executing
+ * while a sibling is marked unsafe).  This can be used for a thread that's
+ * otherwise considered safe, if it needs to handle potentially sensitive data.
+ * Right now, this means certain I/O handling operations that reach down into
+ * the networking and ZFS sub-systems.
+ *
+ * ht_should_run(thread, cpu)
+ *
+ * This is used by the dispatcher when making scheduling decisions: if the
+ * sibling is compatible with the given thread, we return B_TRUE. This is
+ * essentially trying to guess if any subsequent ht_acquire() will fail, by
+ * peeking at the sibling CPU's state.  The peek is racy, but if we get things
+ * wrong, the "only" consequence is that ht_acquire() may lose.
+ *
+ * ht_adjust_cpu_score()
+ *
+ * Used when scoring other CPUs in disp_lowpri_cpu().  If we shouldn't run here,
+ * we'll add a small penalty to the score.  This also makes sure a VCPU thread
+ * migration behaves properly.
+ */
+
+#include <sys/archsystm.h>
+#include <sys/disp.h>
+#include <sys/cmt.h>
+#include <sys/systm.h>
+#include <sys/cpu.h>
+#include <sys/var.h>
+#include <sys/xc_levels.h>
+#include <sys/cmn_err.h>
+#include <sys/sysmacros.h>
+#include <sys/x86_archext.h>
+
+#define	CS_SHIFT (8)
+#define	CS_MASK ((1 << CS_SHIFT) - 1)
+#define	CS_MARK(s) ((s) & CS_MASK)
+#define	CS_ZONE(s) ((s) >> CS_SHIFT)
+#define	CS_MK(s, z) ((s) | (z << CS_SHIFT))
+
+typedef enum ch_mark {
+	CM_IDLE = 0,	/* running CPU idle thread */
+	CM_THREAD,	/* running general non-VCPU thread */
+	CM_UNSAFE,	/* running ->t_unsafe thread */
+	CM_VCPU,	/* running VCPU thread */
+	CM_POISONED	/* running in guest */
+} ch_mark_t;
+
+/* Double-check our false-sharing padding. */
+CTASSERT(offsetof(cpu_ht_t, ch_sib) == 64);
+CTASSERT(CM_IDLE == 0);
+CTASSERT(CM_POISONED < (1 << CS_SHIFT));
+CTASSERT(CM_POISONED > CM_VCPU);
+CTASSERT(CM_VCPU > CM_UNSAFE);
+
+static uint_t empty_pil = XC_CPUPOKE_PIL;
+
+/*
+ * If disabled, no HT exclusion is performed, and system is potentially
+ * vulnerable to L1TF if hyper-threading is enabled, and we don't have the "not
+ * vulnerable" CPUID bit.
+ */
+int ht_exclusion = 1;
+
+/*
+ * How long ht_acquire() will spin trying to acquire the core, in micro-seconds.
+ * This is enough time to wait out a significant proportion of interrupts.
+ */
+clock_t ht_acquire_wait_time = 64;
+
+static cpu_t *
+ht_find_sibling(cpu_t *cp)
+{
+	for (uint_t i = 0; i < GROUP_SIZE(&cp->cpu_pg->cmt_pgs); i++) {
+		pg_cmt_t *pg = GROUP_ACCESS(&cp->cpu_pg->cmt_pgs, i);
+		group_t *cg = &pg->cmt_pg.pghw_pg.pg_cpus;
+
+		if (pg->cmt_pg.pghw_hw != PGHW_IPIPE)
+			continue;
+
+		if (GROUP_SIZE(cg) == 1)
+			break;
+
+		VERIFY3U(GROUP_SIZE(cg), ==, 2);
+
+		if (GROUP_ACCESS(cg, 0) != cp)
+			return (GROUP_ACCESS(cg, 0));
+
+		VERIFY3P(GROUP_ACCESS(cg, 1), !=, cp);
+
+		return (GROUP_ACCESS(cg, 1));
+	}
+
+	return (NULL);
+}
+
+/*
+ * Initialize HT links.  We have to be careful here not to race with
+ * ht_begin/end_intr(), which also complicates trying to do this initialization
+ * from a cross-call; hence the slightly odd approach below.
+ */
+void
+ht_init(void)
+{
+	cpu_t *scp = CPU;
+	cpu_t *cp = scp;
+	ulong_t flags;
+
+	if (!ht_exclusion)
+		return;
+
+	mutex_enter(&cpu_lock);
+
+	do {
+		thread_affinity_set(curthread, cp->cpu_id);
+		flags = intr_clear();
+
+		cp->cpu_m.mcpu_ht.ch_intr_depth = 0;
+		cp->cpu_m.mcpu_ht.ch_state = CS_MK(CM_THREAD, GLOBAL_ZONEID);
+		cp->cpu_m.mcpu_ht.ch_sibstate = CS_MK(CM_THREAD, GLOBAL_ZONEID);
+		ASSERT3P(cp->cpu_m.mcpu_ht.ch_sib, ==, NULL);
+		cp->cpu_m.mcpu_ht.ch_sib = ht_find_sibling(cp);
+
+		intr_restore(flags);
+		thread_affinity_clear(curthread);
+	} while ((cp = cp->cpu_next_onln) != scp);
+
+	mutex_exit(&cpu_lock);
+}
+
+/*
+ * We're adding an interrupt handler of some kind at the given PIL.  If this
+ * happens to be the same PIL as XC_CPUPOKE_PIL, then we need to disable our
+ * pil_needs_kick() optimization, as there is now potentially an unsafe
+ * interrupt handler at that PIL.  This typically won't occur, so we're not that
+ * careful about what's actually getting added, which CPU it's on, or if it gets
+ * removed.  This also presumes that softints can't cover our empty_pil.
+ */
+void
+ht_intr_alloc_pil(uint_t pil)
+{
+	ASSERT(pil <= PIL_MAX);
+
+	if (empty_pil == pil)
+		empty_pil = PIL_MAX + 1;
+}
+
+/*
+ * If our sibling is also a VCPU thread from a different zone, we need one of
+ * them to give up, otherwise they will just battle each other for exclusion
+ * until they exhaust their quantum.
+ *
+ * We arbitrate between them by dispatch priority: clearly, a higher-priority
+ * thread deserves to win the acquisition.  However, under CPU load, it'll be
+ * very common to see both threads with ->t_pri == 1.  If so, we'll break the
+ * tie by cpu_id (which is hopefully arbitrary enough).
+ *
+ * If we lose, the VMM code will take this as a hint to call
+ * thread_affinity_set(CPU_BEST), which will likely migrate the VCPU thread
+ * somewhere else.
+ *
+ * Note that all of this state examination is racy, as we don't own any locks
+ * here.
+ */
+static boolean_t
+yield_to_vcpu(cpu_t *sib, zoneid_t zoneid)
+{
+	cpu_ht_t *sibht = &sib->cpu_m.mcpu_ht;
+	uint64_t sibstate = sibht->ch_state;
+
+	/*
+	 * If we're likely just waiting for an interrupt, don't yield.
+	 */
+	if (sibht->ch_intr_depth != 0)
+		return (B_FALSE);
+
+	/*
+	 * We're only interested in VCPUs from a different zone.
+	 */
+	if (CS_MARK(sibstate) < CM_VCPU || CS_ZONE(sibstate) == zoneid)
+		return (B_FALSE);
+
+	if (curthread->t_pri < sib->cpu_dispatch_pri)
+		return (B_TRUE);
+
+	if (curthread->t_pri == sib->cpu_dispatch_pri &&
+	    CPU->cpu_id < sib->cpu_id)
+		return (B_TRUE);
+
+	return (B_FALSE);
+}
+
+static inline boolean_t
+sibling_compatible(cpu_ht_t *sibht, zoneid_t zoneid)
+{
+	uint64_t sibstate = sibht->ch_state;
+
+	if (sibht->ch_intr_depth != 0)
+		return (B_FALSE);
+
+	if (CS_MARK(sibstate) == CM_UNSAFE)
+		return (B_FALSE);
+
+	if (CS_MARK(sibstate) == CM_IDLE)
+		return (B_TRUE);
+
+	return (CS_ZONE(sibstate) == zoneid);
+}
+
+int
+ht_acquire(void)
+{
+	clock_t wait = ht_acquire_wait_time;
+	cpu_ht_t *ht = &CPU->cpu_m.mcpu_ht;
+	zoneid_t zoneid = getzoneid();
+	cpu_ht_t *sibht;
+	int ret = 0;
+
+	ASSERT(!interrupts_enabled());
+
+	if (ht->ch_sib == NULL) {
+		/* For the "sequential" L1TF case. */
+		spec_l1d_flush();
+		return (1);
+	}
+
+	sibht = &ht->ch_sib->cpu_m.mcpu_ht;
+
+	/* A VCPU thread should never change zone. */
+	ASSERT3U(CS_ZONE(ht->ch_state), ==, zoneid);
+	ASSERT3U(CS_MARK(ht->ch_state), ==, CM_VCPU);
+	ASSERT3U(zoneid, !=, GLOBAL_ZONEID);
+	ASSERT3U(curthread->t_preempt, >=, 1);
+	ASSERT(curthread->t_schedflag & TS_VCPU);
+
+	while (ret == 0 && wait > 0) {
+
+		if (yield_to_vcpu(ht->ch_sib, zoneid)) {
+			ret = -1;
+			break;
+		}
+
+		if (sibling_compatible(sibht, zoneid)) {
+			lock_set(&sibht->ch_lock);
+
+			if (sibling_compatible(sibht, zoneid)) {
+				ht->ch_state = CS_MK(CM_POISONED, zoneid);
+				sibht->ch_sibstate = CS_MK(CM_POISONED, zoneid);
+				membar_enter();
+				ret = 1;
+			}
+
+			lock_clear(&sibht->ch_lock);
+		} else {
+			drv_usecwait(10);
+			wait -= 10;
+		}
+	}
+
+	DTRACE_PROBE4(ht__acquire, int, ret, uint64_t, sibht->ch_state,
+	    uint64_t, sibht->ch_intr_depth, clock_t, wait);
+
+	if (ret == 1)
+		spec_l1d_flush();
+
+	return (ret);
+}
+
+void
+ht_release(void)
+{
+	cpu_ht_t *ht = &CPU->cpu_m.mcpu_ht;
+	zoneid_t zoneid = getzoneid();
+	cpu_ht_t *sibht;
+
+	ASSERT(!interrupts_enabled());
+
+	if (ht->ch_sib == NULL)
+		return;
+
+	ASSERT3U(zoneid, !=, GLOBAL_ZONEID);
+	ASSERT3U(CS_ZONE(ht->ch_state), ==, zoneid);
+	ASSERT3U(CS_MARK(ht->ch_state), ==, CM_POISONED);
+	ASSERT3U(curthread->t_preempt, >=, 1);
+
+	sibht = &ht->ch_sib->cpu_m.mcpu_ht;
+
+	lock_set(&sibht->ch_lock);
+
+	ht->ch_state = CS_MK(CM_VCPU, zoneid);
+	sibht->ch_sibstate = CS_MK(CM_VCPU, zoneid);
+	membar_producer();
+
+	lock_clear(&sibht->ch_lock);
+}
+
+static void
+ht_kick(cpu_ht_t *ht, zoneid_t zoneid)
+{
+	uint64_t sibstate;
+
+	ASSERT(LOCK_HELD(&ht->ch_lock));
+	ASSERT(!interrupts_enabled());
+
+	poke_cpu(ht->ch_sib->cpu_id);
+
+	membar_consumer();
+	sibstate = ht->ch_sibstate;
+
+	if (CS_MARK(sibstate) != CM_POISONED || CS_ZONE(sibstate) == zoneid)
+		return;
+
+	lock_clear(&ht->ch_lock);
+
+	/*
+	 * Spin until we can see the sibling has been kicked out or is otherwise
+	 * OK.
+	 */
+	for (;;) {
+		membar_consumer();
+		sibstate = ht->ch_sibstate;
+
+		if (CS_MARK(sibstate) != CM_POISONED ||
+		    CS_ZONE(sibstate) == zoneid)
+			break;
+
+		SMT_PAUSE();
+	}
+
+	lock_set(&ht->ch_lock);
+}
+
+static boolean_t
+pil_needs_kick(uint_t pil)
+{
+	return (pil != empty_pil);
+}
+
+void
+ht_begin_intr(uint_t pil)
+{
+	ulong_t flags;
+	cpu_ht_t *ht;
+
+	ASSERT(pil <= PIL_MAX);
+
+	flags = intr_clear();
+	ht = &CPU->cpu_m.mcpu_ht;
+
+	if (ht->ch_sib == NULL) {
+		intr_restore(flags);
+		return;
+	}
+
+	if (atomic_inc_64_nv(&ht->ch_intr_depth) == 1 && pil_needs_kick(pil)) {
+		lock_set(&ht->ch_lock);
+
+		membar_consumer();
+
+		if (CS_MARK(ht->ch_sibstate) == CM_POISONED)
+			ht_kick(ht, GLOBAL_ZONEID);
+
+		lock_clear(&ht->ch_lock);
+	}
+
+	intr_restore(flags);
+}
+
+void
+ht_end_intr(void)
+{
+	ulong_t flags;
+	cpu_ht_t *ht;
+
+	flags = intr_clear();
+	ht = &CPU->cpu_m.mcpu_ht;
+
+	if (ht->ch_sib == NULL) {
+		intr_restore(flags);
+		return;
+	}
+
+	ASSERT3U(ht->ch_intr_depth, >, 0);
+	atomic_dec_64(&ht->ch_intr_depth);
+
+	intr_restore(flags);
+}
+
+static inline boolean_t
+ht_need_kick(cpu_ht_t *ht, zoneid_t zoneid)
+{
+	membar_consumer();
+
+	if (CS_MARK(ht->ch_sibstate) != CM_POISONED)
+		return (B_FALSE);
+
+	if (CS_MARK(ht->ch_state) == CM_UNSAFE)
+		return (B_TRUE);
+
+	return (CS_ZONE(ht->ch_sibstate) != zoneid);
+}
+
+void
+ht_mark(void)
+{
+	zoneid_t zoneid = getzoneid();
+	kthread_t *t = curthread;
+	ulong_t flags;
+	cpu_ht_t *ht;
+	cpu_t *cp;
+
+	flags = intr_clear();
+
+	cp = CPU;
+	ht = &cp->cpu_m.mcpu_ht;
+
+	if (ht->ch_sib == NULL) {
+		intr_restore(flags);
+		return;
+	}
+
+	lock_set(&ht->ch_lock);
+
+	/*
+	 * If we were a nested interrupt and went through the resume_from_intr()
+	 * path, we can now be resuming to a pinning interrupt thread; in which
+	 * case, skip marking, until we later resume to a "real" thread.
+	 */
+	if (ht->ch_intr_depth > 0) {
+		ASSERT3P(t->t_intr, !=, NULL);
+
+		if (ht_need_kick(ht, zoneid))
+			ht_kick(ht, zoneid);
+		goto out;
+	}
+
+	if (t == t->t_cpu->cpu_idle_thread) {
+		ASSERT3U(zoneid, ==, GLOBAL_ZONEID);
+		ht->ch_state = CS_MK(CM_IDLE, zoneid);
+	} else {
+		uint64_t state = CM_THREAD;
+
+		if (t->t_unsafe)
+			state = CM_UNSAFE;
+		else if (t->t_schedflag & TS_VCPU)
+			state = CM_VCPU;
+
+		ht->ch_state = CS_MK(state, zoneid);
+
+		if (ht_need_kick(ht, zoneid))
+			ht_kick(ht, zoneid);
+	}
+
+out:
+	membar_producer();
+	lock_clear(&ht->ch_lock);
+	intr_restore(flags);
+}
+
+void
+ht_begin_unsafe(void)
+{
+	curthread->t_unsafe++;
+	ht_mark();
+}
+
+void
+ht_end_unsafe(void)
+{
+	ASSERT3U(curthread->t_unsafe, >, 0);
+	curthread->t_unsafe--;
+	ht_mark();
+}
+
+void
+ht_mark_as_vcpu(void)
+{
+	thread_lock(curthread);
+	curthread->t_schedflag |= TS_VCPU;
+	ht_mark();
+	thread_unlock(curthread);
+}
+
+boolean_t
+ht_should_run(kthread_t *t, cpu_t *cp)
+{
+	uint64_t sibstate;
+	cpu_t *sib;
+
+	if (t == t->t_cpu->cpu_idle_thread)
+		return (B_TRUE);
+
+	if ((sib = cp->cpu_m.mcpu_ht.ch_sib) == NULL)
+		return (B_TRUE);
+
+	sibstate = sib->cpu_m.mcpu_ht.ch_state;
+
+	if ((t->t_schedflag & TS_VCPU)) {
+		if (CS_MARK(sibstate) == CM_IDLE)
+			return (B_TRUE);
+		if (CS_MARK(sibstate) == CM_UNSAFE)
+			return (B_FALSE);
+		return (CS_ZONE(sibstate) == ttozone(t)->zone_id);
+	}
+
+	if (CS_MARK(sibstate) < CM_VCPU)
+		return (B_TRUE);
+
+	return (CS_ZONE(sibstate) == ttozone(t)->zone_id);
+}
+
+pri_t
+ht_adjust_cpu_score(kthread_t *t, struct cpu *cp, pri_t score)
+{
+	if (ht_should_run(t, cp))
+		return (score);
+
+	/*
+	 * If we're a VCPU thread scoring our current CPU, we are most likely
+	 * asking to be rescheduled elsewhere after losing ht_acquire().  In
+	 * this case, the current CPU is not a good choice, most likely, and we
+	 * should go elsewhere.
+	 */
+	if ((t->t_schedflag & TS_VCPU) && cp == t->t_cpu && score < 0)
+		return ((v.v_maxsyspri + 1) * 2);
+
+	return (score + 1);
+}
--- old/usr/src/uts/i86pc/os/intr.c	Tue May  7 04:50:00 2019
+++ new/usr/src/uts/i86pc/os/intr.c	Tue May  7 04:50:00 2019
@@ -466,25 +466,22 @@
 #include <sys/ontrap.h>
 #include <sys/x86_archext.h>
 #include <sys/promif.h>
+#include <sys/ht.h>
 #include <vm/hat_i86.h>
 #if defined(__xpv)
 #include <sys/hypervisor.h>
 #endif
 
-#if defined(__amd64) && !defined(__xpv)
-/* If this fails, then the padding numbers in machcpuvar.h are wrong. */
-CTASSERT((offsetof(cpu_t, cpu_m) + offsetof(struct machcpu, mcpu_pad)) <
-    MMU_PAGESIZE);
-CTASSERT((offsetof(cpu_t, cpu_m) + offsetof(struct machcpu, mcpu_kpti)) >=
-    MMU_PAGESIZE);
-CTASSERT((offsetof(cpu_t, cpu_m) + offsetof(struct machcpu, mcpu_kpti_dbg)) <
-    2 * MMU_PAGESIZE);
-CTASSERT((offsetof(cpu_t, cpu_m) + offsetof(struct machcpu, mcpu_pad2)) <
-    2 * MMU_PAGESIZE);
+/* If these fail, then the padding numbers in machcpuvar.h are wrong. */
+#if !defined(__xpv)
+#define	MCOFF(member)	\
+	(offsetof(cpu_t, cpu_m) + offsetof(struct machcpu, member))
+CTASSERT(MCOFF(mcpu_pad) == MACHCPU_SIZE);
+CTASSERT(MCOFF(mcpu_pad2) == MMU_PAGESIZE);
+CTASSERT((MCOFF(mcpu_kpti) & 0xF) == 0);
 CTASSERT(((sizeof (struct kpti_frame)) & 0xF) == 0);
-CTASSERT(((offsetof(cpu_t, cpu_m) +
-    offsetof(struct machcpu, mcpu_kpti_dbg)) & 0xF) == 0);
 CTASSERT((offsetof(struct kpti_frame, kf_tr_rsp) & 0xF) == 0);
+CTASSERT(MCOFF(mcpu_pad3) < 2 * MMU_PAGESIZE);
 #endif
 
 #if defined(__xpv) && defined(DEBUG)
@@ -600,6 +597,8 @@
 		}
 	}
 
+	ht_begin_intr(pil);
+
 	/*
 	 * Store starting timestamp in CPU structure for this PIL.
 	 */
@@ -704,6 +703,8 @@
 			t->t_intr_start = now;
 	}
 
+	ht_end_intr();
+
 	mcpu->mcpu_pri = oldpil;
 	(void) (*setlvlx)(oldpil, vecnum);
 
@@ -766,6 +767,8 @@
 	it->t_state = TS_ONPROC;
 
 	cpu->cpu_thread = it;		/* new curthread on this cpu */
+	ht_begin_intr(pil);
+
 	it->t_pil = (uchar_t)pil;
 	it->t_pri = intr_pri + (pri_t)pil;
 	it->t_intr_start = now;
@@ -856,6 +859,7 @@
 	mcpu->mcpu_pri = pil;
 	(*setlvlx)(pil, vec);
 	t->t_intr_start = now;
+	ht_end_intr();
 	cpu->cpu_thread = t;
 }
 
@@ -1043,6 +1047,7 @@
 
 	it->t_intr = t;
 	cpu->cpu_thread = it;
+	ht_begin_intr(pil);
 
 	/*
 	 * Set bit for this pil in CPU's interrupt active bitmask.
@@ -1103,7 +1108,9 @@
 	it->t_link = cpu->cpu_intr_thread;
 	cpu->cpu_intr_thread = it;
 	it->t_state = TS_FREE;
+	ht_end_intr();
 	cpu->cpu_thread = t;
+
 	if (t->t_flag & T_INTR_THREAD)
 		t->t_intr_start = now;
 	basespl = cpu->cpu_base_spl;
--- old/usr/src/uts/i86pc/sys/Makefile	Tue May  7 04:50:00 2019
+++ new/usr/src/uts/i86pc/sys/Makefile	Tue May  7 04:50:00 2019
@@ -21,6 +21,7 @@
 #
 # Copyright 2009 Sun Microsystems, Inc.  All rights reserved.
 # Use is subject to license terms.
+# Copyright 2018 Joyent, Inc.
 #
 # uts/i86pc/sys/Makefile
 #
@@ -43,8 +44,9 @@
 	clock.h		\
 	cram.h		\
 	ddi_subrdefs.h	\
-	debug_info.h 	\
+	debug_info.h	\
 	fastboot.h	\
+	ht.h		\
 	mach_mmu.h	\
 	machclock.h	\
 	machcpuvar.h	\
--- /dev/null	Tue May  7 04:50:00 2019
+++ new/usr/src/uts/i86pc/sys/ht.h	Tue May  7 04:50:00 2019
@@ -0,0 +1,47 @@
+/*
+ * This file and its contents are supplied under the terms of the
+ * Common Development and Distribution License ("CDDL"), version 1.0.
+ * You may only use this file in accordance with the terms of version
+ * 1.0 of the CDDL.
+ *
+ * A full copy of the text of the CDDL should have accompanied this
+ * source.  A copy of the CDDL is also available via the Internet at
+ * http://www.illumos.org/license/CDDL.
+ */
+
+/*
+ * Copyright 2018 Joyent, Inc.
+ */
+
+#ifndef	_SYS_HT_H
+#define	_SYS_HT_H
+
+#include <sys/types.h>
+#include <sys/thread.h>
+
+#ifdef	__cplusplus
+extern "C" {
+#endif
+
+struct cpu;
+
+extern void ht_init(void);
+extern void ht_intr_alloc_pil(uint_t);
+
+extern int ht_acquire(void);
+extern void ht_release(void);
+extern void ht_mark(void);
+extern void ht_begin_unsafe(void);
+extern void ht_end_unsafe(void);
+extern void ht_begin_intr(uint_t);
+extern void ht_end_intr(void);
+extern void ht_mark_as_vcpu(void);
+
+extern boolean_t ht_should_run(kthread_t *, struct cpu *);
+extern pri_t ht_adjust_cpu_score(kthread_t *, struct cpu *, pri_t);
+
+#ifdef	__cplusplus
+}
+#endif
+
+#endif	/* _SYS_HT_H */
--- old/usr/src/uts/i86pc/sys/machcpuvar.h	Tue May  7 04:50:00 2019
+++ new/usr/src/uts/i86pc/sys/machcpuvar.h	Tue May  7 04:50:00 2019
@@ -134,6 +134,15 @@
 	uint64_t	kf_upper_redzone;
 };
 
+typedef struct cpu_ht {
+	lock_t ch_lock;
+	char ch_pad[56];
+	struct cpu *ch_sib;
+	volatile uint64_t ch_intr_depth;
+	volatile uint64_t ch_state;
+	volatile uint64_t ch_sibstate;
+} cpu_ht_t;
+
 /*
  * This first value, MACHCPU_SIZE is the size of all the members in the cpu_t
  * AND struct machcpu, before we get to the mcpu_pad and the kpti area.
@@ -141,9 +150,9 @@
  * page-tables, and hence must be page-aligned and page-sized. See
  * hat_pcp_setup().
  *
- * There is a CTASSERT in os/intr.c that checks these numbers.
+ * There are CTASSERTs in os/intr.c that verify this all works out.
  */
-#define	MACHCPU_SIZE	(572 + 1584)
+#define	MACHCPU_SIZE	(1568 + 688)
 #define	MACHCPU_PAD	(MMU_PAGESIZE - MACHCPU_SIZE)
 #define	MACHCPU_PAD2	(MMU_PAGESIZE - 16 - 3 * sizeof (struct kpti_frame))
 
@@ -219,6 +228,8 @@
 	 */
 	volatile uint32_t	mcpu_istamp;
 
+	cpu_ht_t		mcpu_ht;
+
 	char			mcpu_pad[MACHCPU_PAD];
 
 	/* This is the start of the page */
--- old/usr/src/uts/i86xpv/Makefile.files	Tue May  7 04:50:01 2019
+++ new/usr/src/uts/i86xpv/Makefile.files	Tue May  7 04:50:01 2019
@@ -61,6 +61,7 @@
 	hment.o			\
 	hold_page.o		\
 	hrtimers.o		\
+	ht.o			\
 	htable.o		\
 	i86_mmu.o		\
 	ibft.o			\
@@ -236,7 +237,7 @@
 # since only C headers are included when #defined(__lint) is true.
 #
 
-ASSYM_DEPS +=			\
+ASSYM_DEPS      +=		\
 	copy.o			\
 	desctbls_asm.o		\
 	ddi_i86_asm.o		\
--- old/usr/src/uts/intel/ia32/ml/copy.s	Tue May  7 04:50:01 2019
+++ new/usr/src/uts/intel/ia32/ml/copy.s	Tue May  7 04:50:01 2019
@@ -36,7 +36,7 @@
 /*         All Rights Reserved						*/
 
 /*
- * Copyright 2016 Joyent, Inc.
+ * Copyright (c) 2018 Joyent, Inc.
  */
 
 #include <sys/errno.h>
@@ -866,8 +866,8 @@
 bcopy_patch_end:
 
 	.p2align 4
-	.globl bcopy_ck_size
-bcopy_ck_size:
+	ALTENTRY(bcopy_ck_size)
+
 	cmpq	$BCOPY_DFLT_REP, %rdx
 	jae	L(use_rep)
 
@@ -956,6 +956,7 @@
 	jnz	L(do_remainder)
 	ret
 #undef	L
+	SET_SIZE(bcopy_ck_size)
 
 #ifdef DEBUG
 	/*
--- old/usr/src/uts/intel/ia32/ml/swtch.s	Tue May  7 04:50:01 2019
+++ new/usr/src/uts/intel/ia32/ml/swtch.s	Tue May  7 04:50:01 2019
@@ -24,7 +24,7 @@
  */
 
 /*
- * Copyright (c) 2018 Joyent, Inc.
+ * Copyright 2019 Joyent, Inc.
  */
 
 /*
@@ -31,14 +31,6 @@
  * Process switching routines.
  */
 
-#if defined(__lint)
-#include <sys/thread.h>
-#include <sys/systm.h>
-#include <sys/time.h>
-#else	/* __lint */
-#include "assym.h"
-#endif	/* __lint */
-
 #include <sys/asm_linkage.h>
 #include <sys/asm_misc.h>
 #include <sys/regset.h>
@@ -47,6 +39,8 @@
 #include <sys/segments.h>
 #include <sys/psw.h>
 
+#include "assym.h"
+
 /*
  * resume(thread_id_t t);
  *
@@ -74,16 +68,10 @@
  * off the stack.
  */
 
-#if !defined(__lint)
-
 #if LWP_PCB_FPU != 0
 #error LWP_PCB_FPU MUST be defined as 0 for code in swtch.s to work
 #endif	/* LWP_PCB_FPU != 0 */
 
-#endif	/* !__lint */
-
-#if defined(__amd64)
-
 /*
  * Save non-volatile regs other than %rsp (%rbx, %rbp, and %r12 - %r15)
  *
@@ -153,88 +141,6 @@
 	jnz	0b;					\
 1:
 
-#elif defined (__i386)
-
-/*
- * Save non-volatile registers (%ebp, %esi, %edi and %ebx)
- *
- * The stack frame must be created before the save of %esp so that tracebacks
- * of swtch()ed-out processes show the process as having last called swtch().
- */
-#define SAVE_REGS(thread_t, retaddr)			\
-	movl	%ebp, T_EBP(thread_t);			\
-	movl	%ebx, T_EBX(thread_t);			\
-	movl	%esi, T_ESI(thread_t);			\
-	movl	%edi, T_EDI(thread_t);			\
-	pushl	%ebp;					\
-	movl	%esp, %ebp;				\
-	movl	%esp, T_SP(thread_t);			\
-	movl	retaddr, T_PC(thread_t);		\
-	movl	8(%ebp), %edi;				\
-	pushl	%edi;					\
-	call	__dtrace_probe___sched_off__cpu;	\
-	addl	$CLONGSIZE, %esp
-
-/*
- * Restore non-volatile registers (%ebp, %esi, %edi and %ebx)
- *
- * We don't do a 'leave,' because reloading %rsp/%rbp from the label_t
- * already has the effect of putting the stack back the way it was when
- * we came in.
- */
-#define RESTORE_REGS(scratch_reg)			\
-	movl	%gs:CPU_THREAD, scratch_reg;		\
-	movl	T_EBP(scratch_reg), %ebp;		\
-	movl	T_EBX(scratch_reg), %ebx;		\
-	movl	T_ESI(scratch_reg), %esi;		\
-	movl	T_EDI(scratch_reg), %edi
-
-/*
- * Get pointer to a thread's hat structure
- */
-#define GET_THREAD_HATP(hatp, thread_t, scratch_reg)	\
-	movl	T_PROCP(thread_t), hatp;		\
-	movl	P_AS(hatp), scratch_reg;		\
-	movl	A_HAT(scratch_reg), hatp
-
-/*
- * If we are resuming an interrupt thread, store a timestamp in the thread
- * structure.  If an interrupt occurs between tsc_read() and its subsequent
- * store, the timestamp will be stale by the time it is stored.  We can detect
- * this by doing a compare-and-swap on the thread's timestamp, since any
- * interrupt occurring in this window will put a new timestamp in the thread's
- * t_intr_start field.
- */
-#define	STORE_INTR_START(thread_t)			\
-	testw	$T_INTR_THREAD, T_FLAGS(thread_t);	\
-	jz	1f;					\
-	pushl	%ecx;					\
-0:							\
-	pushl	T_INTR_START(thread_t);			\
-	pushl	T_INTR_START+4(thread_t);		\
-	call	tsc_read;				\
-	movl	%eax, %ebx;				\
-	movl	%edx, %ecx;				\
-	popl	%edx;					\
-	popl	%eax;					\
-	cmpxchg8b T_INTR_START(thread_t);		\
-	jnz	0b;					\
-	popl	%ecx;					\
-1:
-
-#endif	/* __amd64 */
-
-#if defined(__lint)
-
-/* ARGSUSED */
-void
-resume(kthread_t *t)
-{}
-
-#else	/* __lint */
-
-#if defined(__amd64)
-
 	.global	kpti_enable
 
 	ENTRY(resume)
@@ -436,6 +342,8 @@
 	call	smap_disable
 .nosmap:
 
+	call	ht_mark
+
 	/*
 	 * Restore non-volatile registers, then have spl0 return to the
 	 * resuming thread's PC after first setting the priority as low as
@@ -456,202 +364,6 @@
 	SET_SIZE(_resume_from_idle)
 	SET_SIZE(resume)
 
-#elif defined (__i386)
-
-	ENTRY(resume)
-	movl	%gs:CPU_THREAD, %eax
-	movl	$resume_return, %ecx
-
-	/*
-	 * Save non-volatile registers, and set return address for current
-	 * thread to resume_return.
-	 *
-	 * %edi = t (new thread) when done.
-	 */
-	SAVE_REGS(%eax,  %ecx)
-
-	LOADCPU(%ebx)			/* %ebx = CPU */
-	movl	CPU_THREAD(%ebx), %esi	/* %esi = curthread */
-
-#ifdef DEBUG
-	call	assert_ints_enabled	/* panics if we are cli'd */
-#endif
-	/*
-	 * Call savectx if thread has installed context ops.
-	 *
-	 * Note that if we have floating point context, the save op
-	 * (either fpsave_begin or fpxsave_begin) will issue the
-	 * async save instruction (fnsave or fxsave respectively)
-	 * that we fwait for below.
-	 */
-	movl	T_CTX(%esi), %eax	/* should current thread savectx? */
-	testl	%eax, %eax
-	jz	.nosavectx		/* skip call when zero */
-	pushl	%esi			/* arg = thread pointer */
-	call	savectx			/* call ctx ops */
-	addl	$4, %esp		/* restore stack pointer */
-.nosavectx:
-
-        /*
-         * Call savepctx if process has installed context ops.
-         */
-	movl	T_PROCP(%esi), %eax	/* %eax = proc */
-	cmpl	$0, P_PCTX(%eax)	/* should current thread savectx? */
-	je	.nosavepctx		/* skip call when zero */
-	pushl	%eax			/* arg = proc pointer */
-	call	savepctx		/* call ctx ops */
-	addl	$4, %esp
-.nosavepctx:
-
-	/*
-	 * Temporarily switch to the idle thread's stack
-	 */
-	movl	CPU_IDLE_THREAD(%ebx), %eax 	/* idle thread pointer */
-
-	/*
-	 * Set the idle thread as the current thread
-	 */
-	movl	T_SP(%eax), %esp	/* It is safe to set esp */
-	movl	%eax, CPU_THREAD(%ebx)
-
-	/* switch in the hat context for the new thread */
-	GET_THREAD_HATP(%ecx, %edi, %ecx)
-	pushl	%ecx
-	call	hat_switch
-	addl	$4, %esp
-
-	/*
-	 * Clear and unlock previous thread's t_lock
-	 * to allow it to be dispatched by another processor.
-	 */
-	movb	$0, T_LOCK(%esi)
-
-	/*
-	 * IMPORTANT: Registers at this point must be:
-	 *       %edi = new thread
-	 *
-	 * Here we are in the idle thread, have dropped the old thread.
-	 */
-	ALTENTRY(_resume_from_idle)
-	/*
-	 * spin until dispatched thread's mutex has
-	 * been unlocked. this mutex is unlocked when
-	 * it becomes safe for the thread to run.
-	 */
-.L4:
-	lock
-	btsl	$0, T_LOCK(%edi) /* lock new thread's mutex */
-	jc	.L4_2			/* lock did not succeed */
-
-	/*
-	 * Fix CPU structure to indicate new running thread.
-	 * Set pointer in new thread to the CPU structure.
-	 */
-	LOADCPU(%esi)			/* load current CPU pointer */
-	movl	T_STACK(%edi), %eax	/* here to use v pipeline of */
-					/* Pentium. Used few lines below */
-	cmpl	%esi, T_CPU(%edi)
-	jne	.L5_2
-.L5_1:
-	/*
-	 * Setup esp0 (kernel stack) in TSS to curthread's stack.
-	 * (Note: Since we don't have saved 'regs' structure for all
-	 *	  the threads we can't easily determine if we need to
-	 *	  change esp0. So, we simply change the esp0 to bottom 
-	 *	  of the thread stack and it will work for all cases.)
-	 */
-	movl	CPU_TSS(%esi), %ecx
-	addl	$REGSIZE+MINFRAME, %eax	/* to the bottom of thread stack */
-#if !defined(__xpv)
-	movl	%eax, TSS_ESP0(%ecx)
-#else
-	pushl	%eax
-	pushl	$KDS_SEL
-	call	HYPERVISOR_stack_switch
-	addl	$8, %esp
-#endif	/* __xpv */
-
-	movl	%edi, CPU_THREAD(%esi)	/* set CPU's thread pointer */
-	mfence				/* synchronize with mutex_exit() */
-	xorl	%ebp, %ebp		/* make $<threadlist behave better */
-	movl	T_LWP(%edi), %eax 	/* set associated lwp to  */
-	movl	%eax, CPU_LWP(%esi) 	/* CPU's lwp ptr */
-
-	movl	T_SP(%edi), %esp	/* switch to outgoing thread's stack */
-	movl	T_PC(%edi), %esi	/* saved return addr */
-
-	/*
-	 * Call restorectx if context ops have been installed.
-	 */
-	movl	T_CTX(%edi), %eax	/* should resumed thread restorectx? */
-	testl	%eax, %eax
-	jz	.norestorectx		/* skip call when zero */
-	pushl	%edi			/* arg = thread pointer */
-	call	restorectx		/* call ctx ops */
-	addl	$4, %esp		/* restore stack pointer */
-.norestorectx:
-
-	/*
-	 * Call restorepctx if context ops have been installed for the proc.
-	 */
-	movl	T_PROCP(%edi), %eax
-	cmpl	$0, P_PCTX(%eax)
-	je	.norestorepctx
-	pushl	%eax			/* arg = proc pointer */
-	call	restorepctx
-	addl	$4, %esp		/* restore stack pointer */
-.norestorepctx:
-
-	STORE_INTR_START(%edi)
-
-	/*
-	 * Restore non-volatile registers, then have spl0 return to the
-	 * resuming thread's PC after first setting the priority as low as
-	 * possible and blocking all interrupt threads that may be active.
-	 */
-	movl	%esi, %eax		/* save return address */
-	RESTORE_REGS(%ecx)
-	pushl	%eax			/* push return address for spl0() */
-	call	__dtrace_probe___sched_on__cpu
-	jmp	spl0
-
-resume_return:
-	/*
-	 * Remove stack frame created in SAVE_REGS()
-	 */
-	addl	$CLONGSIZE, %esp
-	ret
-
-.L4_2:
-	pause
-	cmpb	$0, T_LOCK(%edi)
-	je	.L4
-	jmp	.L4_2
-
-.L5_2:
-	/* cp->cpu_stats.sys.cpumigrate++ */
-	addl    $1, CPU_STATS_SYS_CPUMIGRATE(%esi)
-	adcl    $0, CPU_STATS_SYS_CPUMIGRATE+4(%esi)
-	movl	%esi, T_CPU(%edi)	/* set new thread's CPU pointer */
-	jmp	.L5_1
-
-	SET_SIZE(_resume_from_idle)
-	SET_SIZE(resume)
-
-#endif	/* __amd64 */
-#endif	/* __lint */
-
-#if defined(__lint)
-
-/* ARGSUSED */
-void
-resume_from_zombie(kthread_t *t)
-{}
-
-#else	/* __lint */
-
-#if defined(__amd64)
-
 	ENTRY(resume_from_zombie)
 	movq	%gs:CPU_THREAD, %rax
 	leaq	resume_from_zombie_return(%rip), %r11
@@ -726,88 +438,6 @@
 	ret
 	SET_SIZE(resume_from_zombie)
 
-#elif defined (__i386)
-
-	ENTRY(resume_from_zombie)
-	movl	%gs:CPU_THREAD, %eax
-	movl	$resume_from_zombie_return, %ecx
-
-	/*
-	 * Save non-volatile registers, and set return address for current
-	 * thread to resume_from_zombie_return.
-	 *
-	 * %edi = t (new thread) when done.
-	 */
-	SAVE_REGS(%eax, %ecx)
-
-#ifdef DEBUG
-	call	assert_ints_enabled	/* panics if we are cli'd */
-#endif
-	movl	%gs:CPU_THREAD, %esi	/* %esi = curthread */
-
-	/* clean up the fp unit. It might be left enabled */
-
-	movl	%cr0, %eax
-	testl	$CR0_TS, %eax
-	jnz	.zfpu_disabled		/* if TS already set, nothing to do */
-	fninit				/* init fpu & discard pending error */
-	orl	$CR0_TS, %eax
-	movl	%eax, %cr0
-.zfpu_disabled:
-
-	/*
-	 * Temporarily switch to the idle thread's stack so that the zombie
-	 * thread's stack can be reclaimed by the reaper.
-	 */
-	movl	%gs:CPU_IDLE_THREAD, %eax /* idle thread pointer */
-	movl	T_SP(%eax), %esp	/* get onto idle thread stack */
-
-	/*
-	 * Set the idle thread as the current thread.
-	 */
-	movl	%eax, %gs:CPU_THREAD
-
-	/*
-	 * switch in the hat context for the new thread
-	 */
-	GET_THREAD_HATP(%ecx, %edi, %ecx)
-	pushl	%ecx
-	call	hat_switch
-	addl	$4, %esp
-
-	/*
-	 * Put the zombie on death-row.
-	 */
-	pushl	%esi
-	call	reapq_add
-	addl	$4, %esp
-	jmp	_resume_from_idle	/* finish job of resume */
-
-resume_from_zombie_return:
-	RESTORE_REGS(%ecx)		/* restore non-volatile registers */
-	call	__dtrace_probe___sched_on__cpu
-
-	/*
-	 * Remove stack frame created in SAVE_REGS()
-	 */
-	addl	$CLONGSIZE, %esp
-	ret
-	SET_SIZE(resume_from_zombie)
-
-#endif	/* __amd64 */
-#endif	/* __lint */
-
-#if defined(__lint)
-
-/* ARGSUSED */
-void
-resume_from_intr(kthread_t *t)
-{}
-
-#else	/* __lint */
-
-#if defined(__amd64)
-
 	ENTRY(resume_from_intr)
 	movq	%gs:CPU_THREAD, %rax
 	leaq	resume_from_intr_return(%rip), %r11
@@ -834,6 +464,8 @@
 
 	STORE_INTR_START(%r12)
 
+	call	ht_mark
+
 	/*
 	 * Restore non-volatile registers, then have spl0 return to the
 	 * resuming thread's PC after first setting the priority as low as
@@ -853,69 +485,6 @@
 	ret
 	SET_SIZE(resume_from_intr)
 
-#elif defined (__i386)
-
-	ENTRY(resume_from_intr)
-	movl	%gs:CPU_THREAD, %eax
-	movl	$resume_from_intr_return, %ecx
-
-	/*
-	 * Save non-volatile registers, and set return address for current
-	 * thread to resume_return.
-	 *
-	 * %edi = t (new thread) when done.
-	 */
-	SAVE_REGS(%eax, %ecx)
-
-#ifdef DEBUG
-	call	assert_ints_enabled	/* panics if we are cli'd */
-#endif
-	movl	%gs:CPU_THREAD, %esi	/* %esi = curthread */
-	movl	%edi, %gs:CPU_THREAD	/* set CPU's thread pointer */
-	mfence				/* synchronize with mutex_exit() */
-	movl	T_SP(%edi), %esp	/* restore resuming thread's sp */
-	xorl	%ebp, %ebp		/* make $<threadlist behave better */
-
-	/*
-	 * Unlock outgoing thread's mutex dispatched by another processor.
-	 */
-	xorl	%eax,%eax
-	xchgb	%al, T_LOCK(%esi)
-
-	STORE_INTR_START(%edi)
-
-	/*
-	 * Restore non-volatile registers, then have spl0 return to the
-	 * resuming thread's PC after first setting the priority as low as
-	 * possible and blocking all interrupt threads that may be active.
-	 */
-	movl	T_PC(%edi), %eax	/* saved return addr */
-	RESTORE_REGS(%ecx)
-	pushl	%eax			/* push return address for spl0() */
-	call	__dtrace_probe___sched_on__cpu
-	jmp	spl0
-
-resume_from_intr_return:
-	/*
-	 * Remove stack frame created in SAVE_REGS()
-	 */
-	addl	$CLONGSIZE, %esp
-	ret
-	SET_SIZE(resume_from_intr)
-
-#endif	/* __amd64 */
-#endif /* __lint */
-
-#if defined(__lint)
-
-void
-thread_start(void)
-{}
-
-#else   /* __lint */
-
-#if defined(__amd64)
-
 	ENTRY(thread_start)
 	popq	%rax		/* start() */
 	popq	%rdi		/* arg */
@@ -925,19 +494,3 @@
 	call	thread_exit	/* destroy thread if it returns. */
 	/*NOTREACHED*/
 	SET_SIZE(thread_start)
-
-#elif defined(__i386)
-
-	ENTRY(thread_start)
-	popl	%eax
-	movl	%esp, %ebp
-	addl	$8, %ebp
-	call	*%eax
-	addl	$8, %esp
-	call	thread_exit	/* destroy thread if it returns. */
-	/*NOTREACHED*/
-	SET_SIZE(thread_start)
-
-#endif	/* __i386 */
-
-#endif  /* __lint */
--- old/usr/src/uts/intel/sys/x86_archext.h	Tue May  7 04:50:02 2019
+++ new/usr/src/uts/intel/sys/x86_archext.h	Tue May  7 04:50:02 2019
@@ -951,6 +951,8 @@
 
 extern const char CyrixInstead[];
 
+extern void (*spec_l1d_flush)(void);
+
 #endif
 
 #if defined(_KERNEL)
--- old/usr/src/uts/intel/zfs/Makefile	Tue May  7 04:50:02 2019
+++ new/usr/src/uts/intel/zfs/Makefile	Tue May  7 04:50:02 2019
@@ -29,7 +29,7 @@
 #
 # Copyright (c) 2016 by Delphix. All rights reserved.
 #
-# Copyright (c) 2018, Joyent, Inc.
+# Copyright 2018 Joyent, Inc.
 
 #
 #	Path to the base of the uts directory tree (usually /usr/src/uts).
@@ -73,6 +73,7 @@
 INC_PATH	+= -I$(SRC)/common
 INC_PATH	+= -I$(COMMONBASE)/zfs
 
+CPPFLAGS	+= -I$(UTSBASE)/i86pc
 C99LMODE=	-Xc99=%all
 
 #
--- old/usr/src/uts/sparc/zfs/Makefile	Tue May  7 04:50:02 2019
+++ new/usr/src/uts/sparc/zfs/Makefile	Tue May  7 04:50:02 2019
@@ -29,6 +29,8 @@
 #
 # Copyright (c) 2016 by Delphix. All rights reserved.
 #
+# Copyright 2018 Joyent, Inc.
+#
 
 #
 #	Path to the base of the uts directory tree (usually /usr/src/uts).
@@ -74,6 +76,7 @@
 INC_PATH	+= -I$(UTSBASE)/common/fs/zfs/lua
 INC_PATH	+= -I$(SRC)/common
 INC_PATH	+= -I$(COMMONBASE)/zfs
+INC_PATH	+= -I$(UTSBASE)/sun4
 
 C99LMODE=	-Xc99=%all
 
--- /dev/null	Tue May  7 04:50:02 2019
+++ new/usr/src/uts/sun4/sys/ht.h	Tue May  7 04:50:02 2019
@@ -0,0 +1,38 @@
+/*
+ * This file and its contents are supplied under the terms of the
+ * Common Development and Distribution License ("CDDL"), version 1.0.
+ * You may only use this file in accordance with the terms of version
+ * 1.0 of the CDDL.
+ *
+ * A full copy of the text of the CDDL should have accompanied this
+ * source.  A copy of the CDDL is also available via the Internet at
+ * http://www.illumos.org/license/CDDL.
+ */
+
+/*
+ * Copyright 2018 Joyent, Inc.
+ */
+
+#ifndef	_SYS_HT_H
+#define	_SYS_HT_H
+
+#include <sys/types.h>
+#include <sys/thread.h>
+
+#ifdef	__cplusplus
+extern "C" {
+#endif
+
+#define	ht_init() {}
+
+#define	ht_should_run(t, c) (B_TRUE)
+#define	ht_adjust_cpu_score(t, c, p) (p)
+#define	ht_begin_unsafe(void) {}
+#define	ht_end_unsafe(void) {}
+#define	ht_end_intr(void) {}
+
+#ifdef	__cplusplus
+}
+#endif
+
+#endif	/* _SYS_HT_H */
--- old/usr/src/uts/sun4u/sys/Makefile	Tue May  7 04:50:02 2019
+++ new/usr/src/uts/sun4u/sys/Makefile	Tue May  7 04:50:02 2019
@@ -22,7 +22,7 @@
 # Use is subject to license terms.
 # Copyright 2019 Peter Tribble.
 #
-# uts/sun4u/sys/Makefile
+# Copyright 2018 Joyent, Inc.
 #
 UTSBASE	= ../..
 
@@ -41,7 +41,7 @@
 	clock.h			\
 	cmp.h			\
 	cpc_ultra.h		\
-	cpu_sgnblk_defs.h 	\
+	cpu_sgnblk_defs.h	\
 	ddi_subrdefs.h		\
 	dvma.h			\
 	eeprom.h		\
@@ -48,11 +48,12 @@
 	errclassify.h		\
 	fcode.h			\
 	fc_plat.h		\
+	ht.h			\
 	idprom.h		\
 	intr.h			\
 	intreg.h		\
 	ivintr.h		\
-	memlist_plat.h  	\
+	memlist_plat.h		\
 	memnode.h		\
 	nexusdebug.h		\
 	prom_debug.h		\
--- old/usr/src/uts/sun4v/sys/Makefile	Tue May  7 04:50:03 2019
+++ new/usr/src/uts/sun4v/sys/Makefile	Tue May  7 04:50:03 2019
@@ -22,9 +22,8 @@
 # Copyright 2009 Sun Microsystems, Inc.  All rights reserved.
 # Use is subject to license terms.
 #
+# Copyright 2018 Joyent, Inc.
 #
-# uts/sun4v/sys/Makefile
-#
 # include global definitions
 UTSBASE	= ../..
 
@@ -42,16 +41,17 @@
 	clock.h			\
 	cmp.h			\
 	cpc_ultra.h		\
-	cpu_sgnblk_defs.h 	\
+	cpu_sgnblk_defs.h	\
 	ddi_subrdefs.h		\
 	dvma.h			\
 	eeprom.h		\
 	fcode.h			\
+	ht.h			\
 	idprom.h		\
 	intr.h			\
 	intreg.h		\
 	ivintr.h		\
-	memlist_plat.h	 	\
+	memlist_plat.h		\
 	memnode.h		\
 	nexusdebug.h		\
 	prom_debug.h		\
