--- usr/src/uts/common/fs/zfs/arc.c.orig	2016-12-01 09:14:59.000000000 +0100
+++ usr/src/uts/common/fs/zfs/arc.c	2016-12-01 09:18:46.000000000 +0100
@@ -880,6 +880,9 @@
 boolean_t l2arc_noprefetch = B_TRUE;		/* don't cache prefetch bufs */
 boolean_t l2arc_feed_again = B_TRUE;		/* turbo warmup */
 boolean_t l2arc_norw = B_TRUE;			/* no reads during writes */
+boolean_t arc_evict_l2_first = B_TRUE;		/* first evict buffers from ARC which are in L2ARC */
+boolean_t arc_evict_l2_only = B_FALSE;		/* only evict buffers from ARC which are in L2ARC */
+
 
 /*
  * L2ARC Internals
@@ -898,10 +901,20 @@
 	refcount_t		l2ad_alloc;	/* allocated bytes */
 };
 
+typedef struct l2arc_spa {
+	uint64_t	l2as_spa;	/* spa which has an alive (!dead) L2 device */
+	list_node_t	l2as_node;	/* spa list node */
+} l2arc_spa_t;
+
 static list_t L2ARC_dev_list;			/* device list */
 static list_t *l2arc_dev_list;			/* device list pointer */
 static kmutex_t l2arc_dev_mtx;			/* device list mutex */
 static l2arc_dev_t *l2arc_dev_last;		/* last device used */
+static list_t L2ARC_spa_list;			/* spa list (current) */
+static list_t *l2arc_spa_list;			/* spa list (current) pointer */
+static kmutex_t l2arc_spa_mtx;			/* spa list (current) mutex */
+static list_t L2ARC_spa_new_list;		/* spa list (new) */
+static list_t *l2arc_spa_new_list;		/* spa list (new) pointer */
 static list_t L2ARC_free_on_write;		/* free after write buf list */
 static list_t *l2arc_free_on_write;		/* free after write list ptr */
 static kmutex_t l2arc_free_on_write_mtx;	/* mutex for list */
@@ -2507,9 +2520,34 @@
 	return (bytes_evicted);
 }
 
+/*
+ * Based on l2arc_spa_list, returns true if the
+ * given spa has an alive (!dead) L2 device,
+ * false otherwise.
+ */
+static boolean_t
+l2arc_alive(uint64_t spa)
+{
+	l2arc_spa_t *l2arc_spa = NULL;
+	mutex_enter(&l2arc_spa_mtx);
+	l2arc_spa = list_head(l2arc_spa_list);
+	while (l2arc_spa != NULL) {
+		if (l2arc_spa->l2as_spa == spa)
+			break;
+		l2arc_spa = list_next(l2arc_spa_list, l2arc_spa);
+	}
+	mutex_exit(&l2arc_spa_mtx);
+	return (l2arc_spa != NULL);
+}
+
+typedef enum evict_header_type_t {
+	L2_ONLY,
+	L1_AND_L2,
+} evict_header_type_t;
+
 static uint64_t
 arc_evict_state_impl(multilist_t *ml, int idx, arc_buf_hdr_t *marker,
-    uint64_t spa, int64_t bytes)
+    uint64_t spa, int64_t bytes, evict_header_type_t header_type)
 {
 	multilist_sublist_t *mls;
 	uint64_t bytes_evicted = 0;
@@ -2559,6 +2597,29 @@
 			continue;
 		}
 
+		/*
+		 * This buffer will be evicted if :
+		 * - all buffers have to be evicted or
+		 * - we did not ask to prefer buffers with L2 header or
+		 * - it has a L2 header
+		 * Otherwise, it will be evicted :
+		 * - if it is not L2ARC eligible or does not have an alive L2,
+		 *   during the 1st phase of eviction (L2_ONLY)
+		 * - in the other cases,
+		 *   during the 2nd phase of eviction (L1_AND_L2)
+		 */
+		if (bytes != ARC_EVICT_ALL &&
+		    (arc_evict_l2_first || arc_evict_l2_only) &&
+		    !HDR_HAS_L2HDR(hdr)) {
+			if (!HDR_L2CACHE(hdr) || !l2arc_alive(hdr->b_spa)) {
+				if (header_type == L1_AND_L2)
+					continue;
+			} else {
+				if (header_type == L2_ONLY)
+					continue;
+			}
+		}
+
 		hash_lock = HDR_LOCK(hdr);
 
 		/*
@@ -2645,6 +2706,36 @@
 	num_sublists = multilist_get_num_sublists(ml);
 
 	/*
+	 * Do the job for each type of headers :
+	 * - L2_ONLY is used to indicate that we first
+	 *   only evict buffers with L2 header (L2-backed) ;
+	 * - L1_AND_L2 catches all buffers.
+	 */
+	evict_header_type_t header_type = L2_ONLY;
+top:
+	/*
+	 * Skip the 1st phase of eviction (only buffers with L2 header)
+	 * if all buffers have to be evicted or
+	 * if we did not ask to prefer buffers with L2 header.
+	 */ 
+	if (header_type == L2_ONLY)
+		if (bytes == ARC_EVICT_ALL ||
+		    (!arc_evict_l2_first && !arc_evict_l2_only))
+			header_type = L1_AND_L2;
+	/*
+	 * Skip the 2nd phase of eviction (all type of buffers)
+	 * if all buffers do not have to be evicted and
+	 * if enough buffers have been evicted
+	 * (i.e. if at least some buffers have been evicted
+	 * when we asked to evict buffers with L2 header only).
+	 */
+	if (header_type == L1_AND_L2)
+		if ((bytes != ARC_EVICT_ALL) &&
+		    (total_evicted >= bytes ||
+		     (arc_evict_l2_only && total_evicted > 0)))
+			return (total_evicted);
+
+	/*
 	 * If we've tried to evict from each sublist, made some
 	 * progress, but still have not hit the target number of bytes
 	 * to evict, we want to keep trying. The markers allow us to
@@ -2694,7 +2785,7 @@
 				break;
 
 			bytes_evicted = arc_evict_state_impl(ml, sublist_idx,
-			    markers[sublist_idx], spa, bytes_remaining);
+			    markers[sublist_idx], spa, bytes_remaining, header_type);
 
 			scan_evicted += bytes_evicted;
 			total_evicted += bytes_evicted;
@@ -2737,6 +2828,11 @@
 	}
 	kmem_free(markers, sizeof (*markers) * num_sublists);
 
+	if (header_type == L2_ONLY) {
+		header_type = L1_AND_L2;
+		goto top;
+	}
+
 	return (total_evicted);
 }
 
@@ -5410,8 +5506,26 @@
 			next = list_head(l2arc_dev_list);
 		} else {
 			next = list_next(l2arc_dev_list, next);
-			if (next == NULL)
+			if (next == NULL) {
 				next = list_head(l2arc_dev_list);
+				/*
+				 * We are at the end of the l2arc_dev_list,
+				 * so activate the new list of spa with alive L2
+				 */
+				list_t *l2arc_tmp_list = l2arc_spa_list;
+				mutex_enter(&l2arc_spa_mtx);
+				l2arc_spa_list = l2arc_spa_new_list;
+				mutex_exit(&l2arc_spa_mtx);
+				l2arc_spa_new_list = l2arc_tmp_list;
+				/* and let's re-init the new list */
+				l2arc_spa_t *l2arc_spa = list_head(l2arc_spa_new_list);
+				while (l2arc_spa != NULL) {
+					list_remove(l2arc_spa_new_list, l2arc_spa);
+					kmem_free(l2arc_spa, sizeof (l2arc_spa_t));
+					l2arc_spa = list_head(l2arc_spa_new_list);
+				}
+
+			}
 		}
 
 		/* if we have come back to the start, bail out */
@@ -5425,6 +5539,21 @@
 	/* if we were unable to find any usable vdevs, return NULL */
 	if (vdev_is_dead(next->l2ad_vdev))
 		next = NULL;
+	/* else, store the vdev's spa in the new list of spa with alive L2 */
+	else {
+		uint64_t spa = spa_load_guid(next->l2ad_spa);
+		l2arc_spa_t *l2arc_spa = list_head(l2arc_spa_new_list);
+		while (l2arc_spa != NULL) {
+			if (l2arc_spa->l2as_spa == spa)
+				break;
+			l2arc_spa = list_next(l2arc_spa_new_list, l2arc_spa);
+		}
+		if (l2arc_spa == NULL) {
+			l2arc_spa = kmem_zalloc(sizeof (l2arc_spa_t), KM_SLEEP);
+			l2arc_spa->l2as_spa = spa;
+			list_insert_head(l2arc_spa_new_list, l2arc_spa);
+		}
+	}
 
 	l2arc_dev_last = next;
 
@@ -5930,7 +6059,7 @@
 			buf_sz = hdr->b_size;
 			buf_a_sz = vdev_psize_to_asize(dev->l2ad_vdev, buf_sz);
 
-			if ((write_asize + buf_a_sz) > target_sz) {
+			if ((write_sz + buf_sz) > target_sz) {
 				full = B_TRUE;
 				mutex_exit(hash_lock);
 				break;
@@ -6119,7 +6248,6 @@
 
 	mutex_exit(&dev->l2ad_mtx);
 
-	ASSERT3U(write_asize, <=, target_sz);
 	ARCSTAT_BUMP(arcstat_l2_writes_sent);
 	ARCSTAT_INCR(arcstat_l2_write_bytes, write_asize);
 	ARCSTAT_INCR(arcstat_l2_size, write_sz);
@@ -6139,7 +6267,7 @@
 	(void) zio_wait(pio);
 	dev->l2ad_writing = B_FALSE;
 
-	return (write_asize);
+	return (write_sz);
 }
 
 /*
@@ -6525,12 +6653,19 @@
 	mutex_init(&l2arc_feed_thr_lock, NULL, MUTEX_DEFAULT, NULL);
 	cv_init(&l2arc_feed_thr_cv, NULL, CV_DEFAULT, NULL);
 	mutex_init(&l2arc_dev_mtx, NULL, MUTEX_DEFAULT, NULL);
+	mutex_init(&l2arc_spa_mtx, NULL, MUTEX_DEFAULT, NULL);
 	mutex_init(&l2arc_free_on_write_mtx, NULL, MUTEX_DEFAULT, NULL);
 
 	l2arc_dev_list = &L2ARC_dev_list;
+	l2arc_spa_list = &L2ARC_spa_list;
+	l2arc_spa_new_list = &L2ARC_spa_new_list;
 	l2arc_free_on_write = &L2ARC_free_on_write;
 	list_create(l2arc_dev_list, sizeof (l2arc_dev_t),
 	    offsetof(l2arc_dev_t, l2ad_node));
+	list_create(l2arc_spa_list, sizeof (l2arc_spa_t),
+	    offsetof(l2arc_spa_t, l2as_node));
+	list_create(l2arc_spa_new_list, sizeof (l2arc_spa_t),
+	    offsetof(l2arc_spa_t, l2as_node));
 	list_create(l2arc_free_on_write, sizeof (l2arc_data_free_t),
 	    offsetof(l2arc_data_free_t, l2df_list_node));
 }
@@ -6549,9 +6684,30 @@
 	mutex_destroy(&l2arc_feed_thr_lock);
 	cv_destroy(&l2arc_feed_thr_cv);
 	mutex_destroy(&l2arc_dev_mtx);
+	mutex_destroy(&l2arc_spa_mtx);
 	mutex_destroy(&l2arc_free_on_write_mtx);
 
+	/*
+	 * l2arc_spa lists could however still have some members,
+	 * let's free them before destroying the lists.
+	 */
+	l2arc_spa_t *l2arc_spa;
+	l2arc_spa = list_head(l2arc_spa_list);
+	while (l2arc_spa != NULL) {
+		list_remove(l2arc_spa_list, l2arc_spa);
+		kmem_free(l2arc_spa, sizeof (l2arc_spa_t));
+		l2arc_spa = list_head(l2arc_spa_list);
+	}
+	l2arc_spa = list_head(l2arc_spa_new_list);
+	while (l2arc_spa != NULL) {
+		list_remove(l2arc_spa_new_list, l2arc_spa);
+		kmem_free(l2arc_spa, sizeof (l2arc_spa_t));
+		l2arc_spa = list_head(l2arc_spa_new_list);
+	}
+
 	list_destroy(l2arc_dev_list);
+	list_destroy(l2arc_spa_list);
+	list_destroy(l2arc_spa_new_list);
 	list_destroy(l2arc_free_on_write);
 }
 
--- usr/src/uts/common/fs/zfs/dbuf.c.orig	2016-12-01 09:14:59.000000000 +0100
+++ usr/src/uts/common/fs/zfs/dbuf.c	2016-12-01 09:18:50.000000000 +0100
@@ -2010,6 +2010,11 @@
 			    ARC_FLAG_NOWAIT | ARC_FLAG_PREFETCH;
 			zbookmark_phys_t zb;
 
+			if (DBUF_IS_L2CACHEABLE(db))
+				aflags |= ARC_FLAG_L2CACHE;
+			if (DBUF_IS_L2COMPRESSIBLE(db))
+				aflags |= ARC_FLAG_L2COMPRESS;
+
 			SET_BOOKMARK(&zb, ds ? ds->ds_object : DMU_META_OBJSET,
 			    dn->dn_object, 0, blkid);
 
