--- old/usr/src/uts/common/fs/zfs/arc.c	Fri Nov  7 19:13:13 2014
+++ new/usr/src/uts/common/fs/zfs/arc.c	Fri Nov  7 19:13:13 2014
@@ -149,6 +149,11 @@
 static kcondvar_t	arc_reclaim_thr_cv;	/* used to signal reclaim thr */
 static uint8_t		arc_thread_exit;
 
+static kmutex_t		arc_pressure_thr_lock;
+static kcondvar_t	arc_pressure_thr_cv;
+static uint8_t		arc_pressure_thread_exit;
+static uint64_t		arc_pressure_threshold;
+
 #define	ARC_REDUCE_DNLC_PERCENT	3
 uint_t arc_reduce_dnlc_percent = ARC_REDUCE_DNLC_PERCENT;
 
@@ -295,6 +300,7 @@
 	kstat_named_t arcstat_hdr_size;
 	kstat_named_t arcstat_data_size;
 	kstat_named_t arcstat_other_size;
+	kstat_named_t arcstat_growth_rate;
 	kstat_named_t arcstat_l2_hits;
 	kstat_named_t arcstat_l2_misses;
 	kstat_named_t arcstat_l2_feeds;
@@ -361,6 +367,7 @@
 	{ "hdr_size",			KSTAT_DATA_UINT64 },
 	{ "data_size",			KSTAT_DATA_UINT64 },
 	{ "other_size",			KSTAT_DATA_UINT64 },
+	{ "growth_rate",		KSTAT_DATA_UINT64 },
 	{ "l2_hits",			KSTAT_DATA_UINT64 },
 	{ "l2_misses",			KSTAT_DATA_UINT64 },
 	{ "l2_feeds",			KSTAT_DATA_UINT64 },
@@ -430,6 +437,23 @@
 		}							\
 	}
 
+/*
+ * This macro allows us to use kstats as floating averages. Each time we
+ * update this kstat, we first factor it and the update value by `factor'
+ * to shrink the new value's contribution to the overall average. This
+ * macro assumes that integer loads and stores are atomic, but is not
+ * safe for multiple writers updating the kstat in parallel (only the
+ * last writer's update will remain).
+ */
+#define ARCSTAT_F_AVG(stat, value, factor) \
+	do { \
+		uint64_t x = ARCSTAT(stat); \
+		x = x - x / factor + (value) / factor; \
+		ARCSTAT(stat) = x; \
+		_NOTE(NOTREACHED) \
+		_NOTE(CONSTCOND) \
+	} while (0)
+
 kstat_t			*arc_ksp;
 static arc_state_t	*arc_anon;
 static arc_state_t	*arc_mru;
@@ -461,6 +485,7 @@
 static int		arc_no_grow;	/* Don't try to grow cache size */
 static uint64_t		arc_tempreserve;
 static uint64_t		arc_loaned_bytes;
+static uint64_t		arc_bytes_allocd = 0;
 
 typedef struct l2arc_buf_hdr l2arc_buf_hdr_t;
 
@@ -1274,6 +1299,7 @@
 
 	ARCSTAT_INCR(arcstat_meta_used, space);
 	atomic_add_64(&arc_size, space);
+	atomic_add_64(&arc_bytes_allocd, space);
 }
 
 void
@@ -1310,6 +1336,7 @@
 	if (arc_evict_needed(ARC_BUFC_DATA))
 		cv_signal(&arc_reclaim_thr_cv);
 	atomic_add_64(&arc_size, size);
+	atomic_add_64(&arc_bytes_allocd, size);
 	return (zio_data_buf_alloc(size));
 }
 
@@ -2111,7 +2138,42 @@
 	}
 }
 
+#define	ACCURACY	1000
+
 static void
+arc_reclaim_bytes(uint64_t to_evict)
+{
+	uint64_t to_evict_data_mru, to_evict_data_mfu;
+	uint64_t to_evict_meta_mru, to_evict_meta_mfu;
+
+	to_evict_meta_mru = (((arc_mru->arcs_lsize[ARC_BUFC_METADATA] *
+	    ACCURACY) / (arc_mru->arcs_size + arc_mfu->arcs_size)) *
+	    to_evict) / ACCURACY;
+	to_evict_data_mru = (((arc_mru->arcs_lsize[ARC_BUFC_DATA] *
+	    ACCURACY) / (arc_mru->arcs_size + arc_mfu->arcs_size)) *
+	    to_evict) / ACCURACY;
+	to_evict_meta_mfu = (((arc_mfu->arcs_lsize[ARC_BUFC_METADATA] *
+	    ACCURACY) / (arc_mru->arcs_size + arc_mfu->arcs_size)) *
+	    to_evict) / ACCURACY;
+	to_evict_data_mfu = (((arc_mfu->arcs_lsize[ARC_BUFC_DATA] *
+	    ACCURACY) / (arc_mru->arcs_size + arc_mfu->arcs_size)) *
+	    to_evict) / ACCURACY;
+
+	if (to_evict_meta_mru > 0)
+		(void) arc_evict(arc_mru, NULL, to_evict_meta_mru, FALSE,
+		    ARC_BUFC_METADATA);
+	if (to_evict_data_mru > 0)
+		(void) arc_evict(arc_mru, NULL, to_evict_data_mru, FALSE,
+		    ARC_BUFC_DATA);
+	if (to_evict_meta_mfu > 0)
+		(void) arc_evict(arc_mfu, NULL, to_evict_meta_mfu, FALSE,
+		    ARC_BUFC_METADATA);
+	if (to_evict_data_mfu > 0)
+		(void) arc_evict(arc_mfu, NULL, to_evict_data_mfu, FALSE,
+		    ARC_BUFC_DATA);
+}
+
+static void
 arc_do_user_evicts(void)
 {
 	mutex_enter(&arc_eviction_mtx);
@@ -2249,6 +2311,27 @@
 		arc_adjust();
 }
 
+#define	PHYSMEM_PRESSURE_FRACTION	100
+
+static boolean_t
+arc_mem_pressure(void)
+{
+#ifdef _KERNEL
+	uint64_t extra = desfree + physmem / PHYSMEM_PRESSURE_FRACTION;
+
+	if ((freemem < lotsfree + needfree + extra) ||
+	    (needfree || availrmem < swapfs_minfree + swapfs_reserve + extra) ||
+	    (zio_arena != NULL && vmem_size(zio_arena, VMEM_FREE) <
+	    (vmem_size(zio_arena, VMEM_ALLOC) >> 4) +
+	    physmem / PHYSMEM_PRESSURE_FRACTION))
+		return (B_TRUE);
+
+	return (freemem < physmem / PHYSMEM_PRESSURE_FRACTION);
+#else
+	return (0);
+#endif
+}
+
 /*
  * Determine if the system is under memory pressure and is asking
  * to reclaim memory. A return value of 1 indicates that the system
@@ -2391,7 +2474,84 @@
 		vmem_qcache_reap(zio_arena);
 }
 
+#define	RECLAIMS_PER_SEC	20
+#define	STAT_UPDATES_PER_SEC	5
+
+/*
+ * During heavy use, the ARC naturally wants to oscilate its arc_c around
+ * a maximum memory pressure point which corresponds to the arc_reclaim_needed
+ * function evaluating to 1. This results in the arc_size slowly growing
+ * towards this reclaim_needed threshold and exceeding it periodically. Once
+ * this happens, both arc_c and arc_size are down-adjusted by the
+ * arc_reclaim_thread and kmem_reap is initiated. This is problematic on
+ * bigmem systems with a small recordsize (4k or 8k), because reaping a kmem
+ * cache which contains very large numbers of objects is extremely expensive
+ * from an xcall perspective (several seconds of heavy CPU use):
+ *
+ * (mem)
+ * ^         arc_reclaim_thread reacts
+ * |           |                   |
+ * |           V                   V
+ * |
+ * |           +                   +
+ * |          /|                  /|
+ * | ......./..|................/..|.............. arc_reclaim_needed threshold
+ * |      /     \_____________/     \___________/(etc)
+ * |    /          kmem reap          kmem reap
+ * |  /
+ * |/
+ * +----------------------------------------------------------------->
+ *                                                            (time)
+ *
+ * To help address this stairstep pattern, the arc_pressure_thread periodically
+ * gauges the distance of the current arc_size to the arc_reclaim_needed
+ * threshold by way of an estimation algorithm (in arc_mem_pressure).
+ */
 static void
+arc_pressure_thread(void)
+{
+	clock_t			last_update = ddi_get_lbolt();
+	callb_cpr_t		cpr;
+
+	CALLB_CPR_INIT(&cpr, &arc_pressure_thr_lock, callb_generic_cpr, FTAG);
+
+	mutex_enter(&arc_pressure_thr_lock);
+	while (arc_pressure_thread_exit == 0) {
+		clock_t now;
+
+		now = ddi_get_lbolt();
+		if (now - last_update >= hz / STAT_UPDATES_PER_SEC) {
+			uint64_t new_rate;
+
+			new_rate = (atomic_swap_64(&arc_bytes_allocd, 0) *
+			    hz) / (now - last_update);
+
+			if (ARCSTAT(arcstat_growth_rate) < new_rate)
+				ARCSTAT(arcstat_growth_rate) = new_rate;
+			else
+				ARCSTAT_F_AVG(arcstat_growth_rate, new_rate, 4);
+			last_update = now;
+		}
+
+		arc_pressure_threshold = arc_c - ARCSTAT(arcstat_growth_rate);
+		if (arc_size > arc_pressure_threshold) {
+			arc_reclaim_bytes(arc_size - arc_pressure_threshold);
+		}
+
+		CALLB_CPR_SAFE_BEGIN(&cpr);
+		(void) cv_timedwait(&arc_pressure_thr_cv,
+		    &arc_pressure_thr_lock,
+		    ddi_get_lbolt() + hz / RECLAIMS_PER_SEC);
+		CALLB_CPR_SAFE_END(&cpr, &arc_pressure_thr_lock);
+	}
+
+	arc_pressure_thread_exit = 0;
+	cv_broadcast(&arc_pressure_thr_cv);
+	CALLB_CPR_EXIT(&cpr);		/* drops arc_pressure_thr_lock */
+	thread_exit();
+}
+
+static void
 arc_reclaim_thread(void)
 {
 	clock_t			growtime = 0;
@@ -2500,7 +2660,8 @@
 	 * If we're within (2 * maxblocksize) bytes of the target
 	 * cache size, increment the target cache size
 	 */
-	if (arc_size > arc_c - (2ULL << SPA_MAXBLOCKSHIFT)) {
+	if (arc_size > arc_c - (2ULL << SPA_MAXBLOCKSHIFT) ||
+	    (arc_size >= arc_pressure_threshold && arc_mem_pressure() == 0)) {
 		atomic_add_64(&arc_c, (int64_t)bytes);
 		if (arc_c > arc_c_max)
 			arc_c = arc_c_max;
@@ -2573,6 +2734,7 @@
 			buf->b_data = zio_data_buf_alloc(size);
 			ARCSTAT_INCR(arcstat_data_size, size);
 			atomic_add_64(&arc_size, size);
+			atomic_add_64(&arc_bytes_allocd, size);
 		}
 		goto out;
 	}
@@ -2605,6 +2767,7 @@
 			buf->b_data = zio_data_buf_alloc(size);
 			ARCSTAT_INCR(arcstat_data_size, size);
 			atomic_add_64(&arc_size, size);
+			atomic_add_64(&arc_bytes_allocd, size);
 		}
 		ARCSTAT_BUMP(arcstat_recycle_miss);
 	}
@@ -3771,6 +3934,9 @@
 	mutex_init(&arc_reclaim_thr_lock, NULL, MUTEX_DEFAULT, NULL);
 	cv_init(&arc_reclaim_thr_cv, NULL, CV_DEFAULT, NULL);
 
+	mutex_init(&arc_pressure_thr_lock, NULL, MUTEX_DEFAULT, NULL);
+	cv_init(&arc_pressure_thr_cv, NULL, CV_DEFAULT, NULL);
+
 	/* Convert seconds to clock ticks */
 	arc_min_prefetch_lifespan = 1 * hz;
 
@@ -3786,6 +3952,8 @@
 	arc_c = MIN(arc_c, vmem_size(heap_arena, VMEM_ALLOC | VMEM_FREE) / 8);
 #endif
 
+	/* initial sensible value */
+	arc_pressure_threshold = arc_c;
 	/* set min cache to 1/32 of all memory, or 64MB, whichever is more */
 	arc_c_min = MAX(arc_c / 4, 64<<20);
 	/* set max to 3/4 of all memory, or all but 1GB, whichever is more */
@@ -3887,6 +4055,8 @@
 
 	(void) thread_create(NULL, 0, arc_reclaim_thread, NULL, 0, &p0,
 	    TS_RUN, minclsyspri);
+	(void) thread_create(NULL, 0, arc_pressure_thread, NULL, 0, &p0,
+	    TS_RUN, minclsyspri);
 
 	arc_dead = FALSE;
 	arc_warm = B_FALSE;
@@ -3916,6 +4086,12 @@
 		cv_wait(&arc_reclaim_thr_cv, &arc_reclaim_thr_lock);
 	mutex_exit(&arc_reclaim_thr_lock);
 
+	mutex_enter(&arc_pressure_thr_lock);
+	arc_pressure_thread_exit = 1;
+	while (arc_pressure_thread_exit != 0)
+		cv_wait(&arc_pressure_thr_cv, &arc_pressure_thr_lock);
+	mutex_exit(&arc_pressure_thr_lock);
+
 	arc_flush(NULL);
 
 	arc_dead = TRUE;
@@ -3928,6 +4104,8 @@
 	mutex_destroy(&arc_eviction_mtx);
 	mutex_destroy(&arc_reclaim_thr_lock);
 	cv_destroy(&arc_reclaim_thr_cv);
+	mutex_destroy(&arc_pressure_thr_lock);
+	cv_destroy(&arc_pressure_thr_cv);
 
 	list_destroy(&arc_mru->arcs_list[ARC_BUFC_METADATA]);
 	list_destroy(&arc_mru_ghost->arcs_list[ARC_BUFC_METADATA]);
