--- old/usr/src/uts/common/fs/zfs/zvol.c	Mon Aug  5 17:27:14 2019
+++ new/usr/src/uts/common/fs/zfs/zvol.c	Mon Aug  5 17:27:14 2019
@@ -26,6 +26,7 @@
  * Copyright 2011 Nexenta Systems, Inc.  All rights reserved.
  * Copyright (c) 2012, 2017 by Delphix. All rights reserved.
  * Copyright (c) 2013, Joyent, Inc. All rights reserved.
+ * Copyright 2017 Nexenta Systems, Inc.  All rights reserved.
  * Copyright (c) 2014 Integros [integros.com]
  */
 
@@ -88,6 +89,7 @@
 #include <sys/dmu_tx.h>
 #include <sys/zfeature.h>
 #include <sys/zio_checksum.h>
+#include <sys/dkioc_free_util.h>
 #include <sys/zil_impl.h>
 
 #include "zfs_namecheck.h"
@@ -972,6 +974,7 @@
 	return (error);
 }
 
+/* ARGSUSED */
 static void
 zvol_get_done(zgd_t *zgd, int error)
 {
@@ -980,9 +983,6 @@
 
 	zfs_range_unlock(zgd->zgd_rl);
 
-	if (error == 0 && zgd->zgd_bp)
-		zil_lwb_add_block(zgd->zgd_lwb, zgd->zgd_bp);
-
 	kmem_free(zgd, sizeof (zgd_t));
 }
 
@@ -1069,16 +1069,45 @@
 {
 	uint32_t blocksize = zv->zv_volblocksize;
 	zilog_t *zilog = zv->zv_zilog;
+	spa_t *spa = zilog->zl_spa;
+	spa_meta_placement_t *mp = &spa->spa_meta_policy;
+	boolean_t slogging, zil_to_special, write_to_special;
+	ssize_t immediate_write_sz;
 	itx_wr_state_t write_state;
 
 	if (zil_replaying(zilog, tx))
 		return;
 
-	if (zilog->zl_logbias == ZFS_LOGBIAS_THROUGHPUT)
+	/*
+	 * See comments in zfs_log_write()
+	 */
+
+	immediate_write_sz = (zilog->zl_logbias == ZFS_LOGBIAS_THROUGHPUT)
+	    ? 0 : zvol_immediate_write_sz;
+
+	zil_to_special = !spa_has_slogs(spa) &&
+	    spa_can_special_be_used(spa) &&
+	    mp->spa_sync_to_special != SYNC_TO_SPECIAL_DISABLED;
+
+	write_to_special = !spa_has_slogs(spa) &&
+	    spa_write_data_to_special(spa, zilog->zl_os) &&
+	    (mp->spa_sync_to_special == SYNC_TO_SPECIAL_ALWAYS ||
+	    (mp->spa_sync_to_special == SYNC_TO_SPECIAL_BALANCED &&
+	    spa->spa_avg_stat_rotor % 100 < spa->spa_special_to_normal_ratio));
+
+	slogging = (spa_has_slogs(spa) || zil_to_special) &&
+	    (zilog->zl_logbias == ZFS_LOGBIAS_LATENCY);
+
+	if (blocksize > immediate_write_sz && !slogging &&
+	    resid >= blocksize && off % blocksize == 0)
 		write_state = WR_INDIRECT;
+	else if (zilog->zl_logbias == ZFS_LOGBIAS_THROUGHPUT)
+		write_state = WR_INDIRECT;
 	else if (!spa_has_slogs(zilog->zl_spa) &&
 	    resid >= blocksize && blocksize > zvol_immediate_write_sz)
 		write_state = WR_INDIRECT;
+	else if (write_to_special)
+		 write_state = WR_INDIRECT;
 	else if (sync)
 		write_state = WR_COPIED;
 	else
@@ -1128,7 +1157,7 @@
     uint64_t size, boolean_t doread, boolean_t isdump)
 {
 	vdev_disk_t *dvd;
-	int c;
+	int c, rc;
 	int numerrors = 0;
 
 	if (vd->vdev_ops == &vdev_mirror_ops ||
@@ -1160,20 +1189,38 @@
 
 	offset += VDEV_LABEL_START_SIZE;
 
+	rw_enter(&vd->vdev_tsd_lock, RW_READER);
+	dvd = vd->vdev_tsd;
 	if (ddi_in_panic() || isdump) {
 		ASSERT(!doread);
-		if (doread)
+		if (doread) {
+			rw_exit(&vd->vdev_tsd_lock);
 			return (SET_ERROR(EIO));
-		dvd = vd->vdev_tsd;
+		}
+		/* We assume here dvd is not NULL */
 		ASSERT3P(dvd, !=, NULL);
-		return (ldi_dump(dvd->vd_lh, addr, lbtodb(offset),
-		    lbtodb(size)));
+
+		/* If our assumption is wrong, we do not want to crash */
+		if (dvd != NULL && dvd->vd_lh != NULL) {
+			rc = ldi_dump(dvd->vd_lh, addr, lbtodb(offset),
+			    lbtodb(size));
+		} else {
+			rc = SET_ERROR(ENXIO);
+		}
 	} else {
-		dvd = vd->vdev_tsd;
+		/* We assume here dvd is not NULL */
 		ASSERT3P(dvd, !=, NULL);
-		return (vdev_disk_ldi_physio(dvd->vd_lh, addr, size,
-		    offset, doread ? B_READ : B_WRITE));
+
+		/* If our assumption is wrong, we do not want to crash */
+		if (dvd != NULL && dvd->vd_lh != NULL) {
+			rc = vdev_disk_ldi_physio(dvd->vd_lh, addr, size,
+			    offset, doread ? B_READ : B_WRITE);
+		} else {
+			rc = SET_ERROR(ENXIO);
+		}
 	}
+	rw_exit(&vd->vdev_tsd_lock);
+	return (rc);
 }
 
 static int
@@ -1780,44 +1827,61 @@
 
 	case DKIOCFREE:
 	{
-		dkioc_free_t df;
+		dkioc_free_list_t *dfl;
 		dmu_tx_t *tx;
 
+		mutex_exit(&zfsdev_state_lock);
+
 		if (!zvol_unmap_enabled)
 			break;
 
-		if (ddi_copyin((void *)arg, &df, sizeof (df), flag)) {
-			error = SET_ERROR(EFAULT);
-			break;
+		if (!(flag & FKIOCTL)) {
+			dfl = dfl_copyin((void *)arg, flag, KM_SLEEP);
+			if (dfl == NULL) {
+				error = SET_ERROR(EFAULT);
+				break;
+			}
+		} else {
+			dfl = (dkioc_free_list_t *)arg;
 		}
 
-		/*
-		 * Apply Postel's Law to length-checking.  If they overshoot,
-		 * just blank out until the end, if there's a need to blank
-		 * out anything.
-		 */
-		if (df.df_start >= zv->zv_volsize)
-			break;	/* No need to do anything... */
+		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,
+			    end = start + length;
 
-		mutex_exit(&zfsdev_state_lock);
+			/*
+			 * Apply Postel's Law to length-checking.  If they
+			 * overshoot, just blank out until the end, if there's
+			 * a need to blank out anything.
+			 */
+			if (start >= zv->zv_volsize)
+				continue;	/* No need to do anything... */
+			if (end > zv->zv_volsize) {
+				end = DMU_OBJECT_END;
+				length = end - start;
+			}
 
-		rl = zfs_range_lock(&zv->zv_znode, df.df_start, df.df_length,
-		    RL_WRITER);
-		tx = dmu_tx_create(zv->zv_objset);
-		dmu_tx_mark_netfree(tx);
-		error = dmu_tx_assign(tx, TXG_WAIT);
-		if (error != 0) {
-			dmu_tx_abort(tx);
-		} else {
-			zvol_log_truncate(zv, tx, df.df_start,
-			    df.df_length, B_TRUE);
-			dmu_tx_commit(tx);
-			error = dmu_free_long_range(zv->zv_objset, ZVOL_OBJ,
-			    df.df_start, df.df_length);
-		}
+			rl = zfs_range_lock(&zv->zv_znode, start, length,
+			    RL_WRITER);
+			tx = dmu_tx_create(zv->zv_objset);
+			error = dmu_tx_assign(tx, TXG_WAIT);
+			if (error != 0) {
+				dmu_tx_abort(tx);
+			} else {
+				zvol_log_truncate(zv, tx, start, length,
+				    B_TRUE);
+				dmu_tx_commit(tx);
+				error = dmu_free_long_range(zv->zv_objset,
+				    ZVOL_OBJ, start, length);
+			}
 
-		zfs_range_unlock(rl);
+			zfs_range_unlock(rl);
 
+			if (error != 0)
+				break;
+		}
+
 		/*
 		 * If the write-cache is disabled, 'sync' property
 		 * is set to 'always', or if the caller is asking for
@@ -1829,10 +1893,13 @@
 		if ((error == 0) && zvol_unmap_sync_enabled &&
 		    (!(zv->zv_flags & ZVOL_WCE) ||
 		    (zv->zv_objset->os_sync == ZFS_SYNC_ALWAYS) ||
-		    (df.df_flags & DF_WAIT_SYNC))) {
+		    (dfl->dfl_flags & DF_WAIT_SYNC))) {
 			zil_commit(zv->zv_zilog, ZVOL_OBJ);
 		}
 
+		if (!(flag & FKIOCTL))
+			dfl_free(dfl);
+
 		return (error);
 	}
 
