--- old/usr/src/common/zfs/zfeature_common.c	2014-10-28 11:57:18.616728901 -0700
+++ new/usr/src/common/zfs/zfeature_common.c	2014-10-28 11:57:18.620090333 -0700
@@ -221,4 +221,9 @@
 	    "com.delphix:embedded_data", "embedded_data",
 	    "Blocks which compress very well use even less space.",
 	    B_FALSE, B_TRUE, B_TRUE, NULL);
+
+	zfeature_register(SPA_FEATURE_SPACE_RESERVATION,
+	    "org.nexenta:space_reservation", "space_reservation",
+	    "Possibility to physically reserve space on disk", B_FALSE, B_FALSE,
+	    B_FALSE, NULL);
 }
--- old/usr/src/common/zfs/zfeature_common.h	2014-10-28 11:57:18.745647236 -0700
+++ new/usr/src/common/zfs/zfeature_common.h	2014-10-28 11:57:18.749117428 -0700
@@ -51,6 +51,7 @@
 	SPA_FEATURE_EMBEDDED_DATA,
 	SPA_FEATURE_BOOKMARKS,
 	SPA_FEATURE_FS_SS_LIMIT,
+	SPA_FEATURE_SPACE_RESERVATION,
 	SPA_FEATURES
 } spa_feature_t;
 
--- old/usr/src/uts/common/fs/zfs/dbuf.c	2014-10-28 11:57:18.883417588 -0700
+++ new/usr/src/uts/common/fs/zfs/dbuf.c	2014-10-28 11:57:18.886770577 -0700
@@ -1021,7 +1021,15 @@
 }
 
 dbuf_dirty_record_t *
-dbuf_dirty(dmu_buf_impl_t *db, dmu_tx_t *tx)
+dbuf_zero_dirty(dmu_buf_impl_t *db, dmu_tx_t *tx)
+{
+	ASSERT(db->db_objset != NULL);
+
+	return (dbuf_dirty(db, tx, B_TRUE));
+}
+
+dbuf_dirty_record_t *
+dbuf_dirty(dmu_buf_impl_t *db, dmu_tx_t *tx, boolean_t zero_write)
 {
 	dnode_t *dn;
 	objset_t *os;
@@ -1152,6 +1160,7 @@
 	 * transaction group won't leak out when we sync the older txg.
 	 */
 	dr = kmem_zalloc(sizeof (dbuf_dirty_record_t), KM_SLEEP);
+	dr->dr_zero_write = zero_write;
 	if (db->db_level == 0) {
 		void *data_old = db->db_buf;
 
@@ -1265,7 +1274,7 @@
 		if (drop_struct_lock)
 			rw_exit(&dn->dn_struct_rwlock);
 		ASSERT3U(db->db_level+1, ==, parent->db_level);
-		di = dbuf_dirty(parent, tx);
+		di = dbuf_dirty(parent, tx, B_FALSE);
 		if (parent_held)
 			dbuf_rele(parent, FTAG);
 
@@ -1408,7 +1417,7 @@
 		rf |= DB_RF_HAVESTRUCT;
 	DB_DNODE_EXIT(db);
 	(void) dbuf_read(db, NULL, rf);
-	(void) dbuf_dirty(db, tx);
+	(void) dbuf_dirty(db, tx, B_FALSE);
 }
 
 void
@@ -1435,7 +1444,25 @@
 	    dmu_tx_private_ok(tx));
 
 	dbuf_noread(db);
-	(void) dbuf_dirty(db, tx);
+	(void) dbuf_dirty(db, tx, B_FALSE);
+}
+
+
+void
+dmu_buf_will_zero_fill(dmu_buf_t *db_fake, dmu_tx_t *tx)
+{
+	dmu_buf_impl_t *db = (dmu_buf_impl_t *)db_fake;
+
+	ASSERT(db->db_blkid != DMU_BONUS_BLKID);
+	ASSERT(tx->tx_txg != 0);
+	ASSERT(db->db_level == 0);
+	ASSERT(!refcount_is_zero(&db->db_holds));
+
+	ASSERT(db->db.db_object != DMU_META_DNODE_OBJECT ||
+	    dmu_tx_private_ok(tx));
+
+	dbuf_noread(db);
+	(void) dbuf_zero_dirty(db, tx);
 }
 
 #pragma weak dmu_buf_fill_done = dbuf_fill_done
@@ -1520,7 +1547,7 @@
 	if (db->db_state == DB_CACHED &&
 	    refcount_count(&db->db_holds) - 1 > db->db_dirtycnt) {
 		mutex_exit(&db->db_mtx);
-		(void) dbuf_dirty(db, tx);
+		(void) dbuf_dirty(db, tx, B_FALSE);
 		bcopy(buf->b_data, db->db.db_data, db->db.db_size);
 		VERIFY(arc_buf_remove_ref(buf, db));
 		xuio_stat_wbuf_copied();
@@ -1551,7 +1578,7 @@
 	dbuf_set_data(db, buf);
 	db->db_state = DB_FILL;
 	mutex_exit(&db->db_mtx);
-	(void) dbuf_dirty(db, tx);
+	(void) dbuf_dirty(db, tx, B_FALSE);
 	dmu_buf_fill_done(&db->db, tx);
 }
 
@@ -2820,6 +2847,16 @@
 	dmu_write_policy(os, dn, db->db_level, wp_flag, &zp);
 	DB_DNODE_EXIT(db);
 
+	if (dr->dr_zero_write) {
+		zp.zp_zero_write = B_TRUE;
+
+		if (!spa_feature_is_active(os->os_spa, SPA_FEATURE_SPACE_RESERVATION))
+		{
+			spa_feature_incr(os->os_spa,
+			    SPA_FEATURE_SPACE_RESERVATION, tx);
+		}
+	}
+
 	if (db->db_level == 0 &&
 	    dr->dt.dl.dr_override_state == DR_OVERRIDDEN) {
 		/*
--- old/usr/src/uts/common/fs/zfs/dmu.c	2014-10-28 11:57:19.082610739 -0700
+++ new/usr/src/uts/common/fs/zfs/dmu.c	2014-10-28 11:57:19.086643881 -0700
@@ -810,6 +810,28 @@
 }
 
 void
+dmu_write_zero(objset_t *os, uint64_t object, uint64_t offset, uint64_t size, dmu_tx_t *tx)
+{
+	dmu_buf_t	**dbp;
+	int		numbufs, i;
+
+	VERIFY(0 == dmu_buf_hold_array(os, object, offset, size,
+	    FALSE, FTAG, &numbufs, &dbp));
+
+	for (i = 0; i < numbufs; i++) {
+		dmu_buf_t *db = dbp[i];
+
+		dmu_buf_will_zero_fill(db, tx);
+
+		memset(db->db_data, 0, db->db_size);
+
+		dmu_buf_fill_done(db, tx);
+	}
+
+	dmu_buf_rele_array(dbp, numbufs, FTAG);
+}
+
+void
 dmu_write(objset_t *os, uint64_t object, uint64_t offset, uint64_t size,
     const void *buf, dmu_tx_t *tx)
 {
@@ -1796,6 +1818,7 @@
 	zp->zp_dedup = dedup;
 	zp->zp_dedup_verify = dedup && dedup_verify;
 	zp->zp_nopwrite = nopwrite;
+	zp->zp_zero_write = B_FALSE;
 }
 
 int
--- old/usr/src/uts/common/fs/zfs/dnode.c	2014-10-28 11:57:19.277752356 -0700
+++ new/usr/src/uts/common/fs/zfs/dnode.c	2014-10-28 11:57:19.283498641 -0700
@@ -1299,7 +1299,7 @@
 	 */
 	VERIFY(dnode_add_ref(dn, (void *)(uintptr_t)tx->tx_txg));
 
-	(void) dbuf_dirty(dn->dn_dbuf, tx);
+	(void) dbuf_dirty(dn->dn_dbuf, tx, B_FALSE);
 
 	dsl_dataset_dirty(os->os_dsl_dataset, tx);
 }
@@ -1462,7 +1462,7 @@
 		/* dirty the left indirects */
 		db = dbuf_hold_level(dn, old_nlevels, 0, FTAG);
 		ASSERT(db != NULL);
-		new = dbuf_dirty(db, tx);
+		new = dbuf_dirty(db, tx, B_FALSE);
 		dbuf_rele(db, FTAG);
 
 		/* transfer the dirty records to the new indirect */
--- old/usr/src/uts/common/fs/zfs/sys/dbuf.h	2014-10-28 11:57:19.453579194 -0700
+++ new/usr/src/uts/common/fs/zfs/sys/dbuf.h	2014-10-28 11:57:19.456839661 -0700
@@ -143,6 +143,8 @@
 			boolean_t dr_nopwrite;
 		} dl;
 	} dt;
+
+	boolean_t dr_zero_write;
 } dbuf_dirty_record_t;
 
 typedef struct dmu_buf_impl {
@@ -275,9 +277,11 @@
 int dbuf_read(dmu_buf_impl_t *db, zio_t *zio, uint32_t flags);
 void dmu_buf_will_not_fill(dmu_buf_t *db, dmu_tx_t *tx);
 void dmu_buf_will_fill(dmu_buf_t *db, dmu_tx_t *tx);
+void dmu_buf_will_zero_fill(dmu_buf_t *db, dmu_tx_t *tx);
 void dmu_buf_fill_done(dmu_buf_t *db, dmu_tx_t *tx);
 void dbuf_assign_arcbuf(dmu_buf_impl_t *db, arc_buf_t *buf, dmu_tx_t *tx);
-dbuf_dirty_record_t *dbuf_dirty(dmu_buf_impl_t *db, dmu_tx_t *tx);
+dbuf_dirty_record_t *dbuf_dirty(dmu_buf_impl_t *db, dmu_tx_t *tx, boolean_t zero_write);
+dbuf_dirty_record_t *dbuf_zero_dirty(dmu_buf_impl_t *db, dmu_tx_t *tx);
 arc_buf_t *dbuf_loan_arcbuf(dmu_buf_impl_t *db);
 void dmu_buf_write_embedded(dmu_buf_t *dbuf, void *data,
     bp_embedded_type_t etype, enum zio_compress comp,
--- old/usr/src/uts/common/fs/zfs/sys/dmu.h	2014-10-28 11:57:19.587267052 -0700
+++ new/usr/src/uts/common/fs/zfs/sys/dmu.h	2014-10-28 11:57:19.590669424 -0700
@@ -612,6 +612,7 @@
 	void *buf, uint32_t flags);
 void dmu_write(objset_t *os, uint64_t object, uint64_t offset, uint64_t size,
 	const void *buf, dmu_tx_t *tx);
+void dmu_write_zero(objset_t *os, uint64_t object, uint64_t offset, uint64_t size, dmu_tx_t *tx);
 void dmu_prealloc(objset_t *os, uint64_t object, uint64_t offset, uint64_t size,
 	dmu_tx_t *tx);
 int dmu_read_uio(objset_t *os, uint64_t object, struct uio *uio, uint64_t size);
--- old/usr/src/uts/common/fs/zfs/sys/spa.h	2014-10-28 11:57:19.731061650 -0700
+++ new/usr/src/uts/common/fs/zfs/sys/spa.h	2014-10-28 11:57:19.734713864 -0700
@@ -375,6 +375,9 @@
 #define	BP_GET_LEVEL(bp)		BF64_GET((bp)->blk_prop, 56, 5)
 #define	BP_SET_LEVEL(bp, x)		BF64_SET((bp)->blk_prop, 56, 5, x)
 
+#define BP_GET_PROP_RESERVATION(bp)          BF64_GET((bp)->blk_prop, 61, 1)
+#define BP_SET_PROP_RESERVATION(bp, x)       BF64_SET((bp)->blk_prop, 61, 1, x)
+
 #define	BP_GET_DEDUP(bp)		BF64_GET((bp)->blk_prop, 62, 1)
 #define	BP_SET_DEDUP(bp, x)		BF64_SET((bp)->blk_prop, 62, 1, x)
 
--- old/usr/src/uts/common/fs/zfs/sys/zio.h	2014-10-28 11:57:19.883580687 -0700
+++ new/usr/src/uts/common/fs/zfs/sys/zio.h	2014-10-28 11:57:19.887152534 -0700
@@ -306,6 +306,7 @@
 	boolean_t		zp_dedup;
 	boolean_t		zp_dedup_verify;
 	boolean_t		zp_nopwrite;
+	boolean_t		zp_zero_write;
 } zio_prop_t;
 
 typedef struct zio_cksum_report zio_cksum_report_t;
--- old/usr/src/uts/common/fs/zfs/zfs_vnops.c	2014-10-28 11:57:20.023137848 -0700
+++ new/usr/src/uts/common/fs/zfs/zfs_vnops.c	2014-10-28 11:57:20.026609394 -0700
@@ -74,6 +74,7 @@
 #include <sys/zfs_ctldir.h>
 #include <sys/zfs_fuid.h>
 #include <sys/zfs_sa.h>
+#include <sys/zfeature.h>
 #include <sys/dnlc.h>
 #include <sys/zfs_rlock.h>
 #include <sys/extdirent.h>
@@ -292,6 +293,10 @@
 	return (error);
 }
 
+
+static int zfs_zero_write(vnode_t *vp, uint64_t size, cred_t *cr,
+    caller_context_t *ct);
+
 /* ARGSUSED */
 static int
 zfs_ioctl(vnode_t *vp, int com, intptr_t data, int flag, cred_t *cred,
@@ -301,6 +306,7 @@
 	int error;
 	zfsvfs_t *zfsvfs;
 	znode_t *zp;
+	uint64_t size;
 
 	switch (com) {
 	case _FIOFFS:
@@ -332,6 +338,11 @@
 		if (ddi_copyout(&off, (void *)data, sizeof (off), flag))
 			return (SET_ERROR(EFAULT));
 		return (0);
+	case _FIO_RESERVE_SPACE:
+		if (ddi_copyin((void *)data, &size, sizeof (size), flag))
+			return (EFAULT);
+		error = zfs_zero_write(vp, size, cred, ct);
+		return (error);
 	}
 	return (SET_ERROR(ENOTTY));
 }
@@ -956,6 +967,102 @@
 	return (0);
 }
 
+#define ZFS_RESERVE_CHUNK (2 * 1024 * 1024)
+/* ARGSUSED */
+static int
+zfs_zero_write(vnode_t *vp, uint64_t size, cred_t *cr, caller_context_t *ct)
+{
+	znode_t		*zp = VTOZ(vp);
+	zfsvfs_t	*zfsvfs = zp->z_zfsvfs;
+	int		count = 0;
+	sa_bulk_attr_t	bulk[4];
+	uint64_t	mtime[2], ctime[2];
+	rl_t		*rl;
+	int		error = 0;
+	dmu_tx_t	*tx = NULL;
+	uint64_t	end_size;
+	uint64_t	pos = 0;
+
+	if (zp->z_size > 0)
+		return (EFBIG);
+	if (size == 0)
+		return (0);
+
+	ZFS_ENTER(zfsvfs);
+	ZFS_VERIFY_ZP(zp);
+
+	if (!spa_feature_is_enabled(zfsvfs->z_os->os_spa,
+	    SPA_FEATURE_SPACE_RESERVATION))
+	{
+		ZFS_EXIT(zfsvfs);
+		return (ENOTSUP);
+	}
+
+	SA_ADD_BULK_ATTR(bulk, count, SA_ZPL_MTIME(zfsvfs), NULL, &mtime, 16);
+	SA_ADD_BULK_ATTR(bulk, count, SA_ZPL_CTIME(zfsvfs), NULL, &ctime, 16);
+	SA_ADD_BULK_ATTR(bulk, count, SA_ZPL_SIZE(zfsvfs), NULL,
+	    &zp->z_size, 8);
+	SA_ADD_BULK_ATTR(bulk, count, SA_ZPL_FLAGS(zfsvfs), NULL,
+	    &zp->z_pflags, 8);
+
+	/*
+	 * If immutable or not appending then return EPERM
+	 */
+	if ((zp->z_pflags & (ZFS_IMMUTABLE | ZFS_READONLY))) {
+		ZFS_EXIT(zfsvfs);
+		return (EPERM);
+	}
+
+	rl = zfs_range_lock(zp, 0, size, RL_WRITER);
+
+	if (zfs_owner_overquota(zfsvfs, zp, B_FALSE) ||
+	    zfs_owner_overquota(zfsvfs, zp, B_TRUE)) {
+		error = EDQUOT;
+		goto out;
+	}
+
+	while (pos < size) {
+		uint64_t length = size - pos;
+		length = MIN(length, ZFS_RESERVE_CHUNK);
+again:
+		tx = dmu_tx_create(zfsvfs->z_os);
+		dmu_tx_hold_sa(tx, zp->z_sa_hdl, B_FALSE);
+		dmu_tx_hold_write(tx, zp->z_id, pos, length);
+		zfs_sa_upgrade_txholds(tx, zp);
+		error = dmu_tx_assign(tx, TXG_NOWAIT);
+		if (error) {
+			if (error == ERESTART) {
+				dmu_tx_wait(tx);
+				dmu_tx_abort(tx);
+				goto again;
+			}
+			dmu_tx_abort(tx);
+			goto out;
+		}
+
+		if (pos == 0)
+			zfs_grow_blocksize(zp, MIN(size, zfsvfs->z_max_blksz), tx);
+		dmu_write_zero(zfsvfs->z_os, zp->z_id, pos, length, tx);
+
+		zfs_tstamp_update_setup(zp, CONTENT_MODIFIED, mtime, ctime, B_TRUE);
+
+		pos += length;
+		while ((end_size = zp->z_size) < pos)
+			(void) atomic_cas_64(&zp->z_size, end_size, pos);
+
+		error = sa_bulk_update(zp->z_sa_hdl, bulk, count, tx);
+
+		dmu_tx_commit(tx);
+		if (error)
+			goto out;
+	}
+out:
+	zfs_range_unlock(rl);
+	ZFS_EXIT(zfsvfs);
+
+	return (error);
+}
+
 void
 zfs_get_done(zgd_t *zgd, int error)
 {
--- old/usr/src/uts/common/fs/zfs/zio.c	2014-10-28 11:57:20.277340499 -0700
+++ new/usr/src/uts/common/fs/zfs/zio.c	2014-10-28 11:57:20.281453620 -0700
@@ -958,6 +958,12 @@
 {
 	blkptr_t *bp = zio->io_bp;
 
+	if (!BP_IS_EMBEDDED(bp) && BP_GET_PROP_RESERVATION(bp)) {
+		memset(zio->io_orig_data, 0, zio->io_orig_size);
+		zio->io_pipeline = ZIO_INTERLOCK_STAGES;
+		return (ZIO_PIPELINE_CONTINUE);
+	}
+
 	if (BP_GET_COMPRESS(bp) != ZIO_COMPRESS_OFF &&
 	    zio->io_child_type == ZIO_CHILD_LOGICAL &&
 	    !(zio->io_flags & ZIO_FLAG_RAW)) {
@@ -993,6 +999,8 @@
 	spa_t *spa = zio->io_spa;
 	zio_prop_t *zp = &zio->io_prop;
 	enum zio_compress compress = zp->zp_compress;
+	enum zio_checksum checksum = zp->zp_checksum;
+	uint8_t dedup = zp->zp_dedup;
 	blkptr_t *bp = zio->io_bp;
 	uint64_t lsize = zio->io_size;
 	uint64_t psize = lsize;
@@ -1011,6 +1019,12 @@
 
 	ASSERT(zio->io_child_type != ZIO_CHILD_DDT);
 
+	if (zp->zp_zero_write && !(zio->io_pipeline & ZIO_GANG_STAGES)) {
+		dedup = B_FALSE;
+		compress = ZIO_COMPRESS_OFF;
+		checksum = ZIO_CHECKSUM_OFF;
+	}
+
 	if (zio->io_bp_override) {
 		ASSERT(bp->blk_birth != zio->io_txg);
 		ASSERT(BP_GET_DEDUP(zio->io_bp_override) == 0);
@@ -1034,13 +1048,13 @@
 
 		ASSERT(!zp->zp_nopwrite);
 
-		if (BP_IS_HOLE(bp) || !zp->zp_dedup)
+		if (BP_IS_HOLE(bp) || !dedup)
 			return (ZIO_PIPELINE_CONTINUE);
 
-		ASSERT(zio_checksum_table[zp->zp_checksum].ci_dedup ||
+		ASSERT(zio_checksum_table[checksum].ci_dedup ||
 		    zp->zp_dedup_verify);
 
-		if (BP_GET_CHECKSUM(bp) == zp->zp_checksum) {
+		if (BP_GET_CHECKSUM(bp) == checksum) {
 			BP_SET_DEDUP(bp, 1);
 			zio->io_pipeline |= ZIO_STAGE_DDT_WRITE;
 			return (ZIO_PIPELINE_CONTINUE);
@@ -1153,6 +1167,17 @@
 		BP_SET_CHECKSUM(bp, zp->zp_checksum);
 		BP_SET_DEDUP(bp, zp->zp_dedup);
 		BP_SET_BYTEORDER(bp, ZFS_HOST_BYTEORDER);
+		if (zp->zp_zero_write  && !(zio->io_pipeline & ZIO_GANG_STAGES)) {
+			boolean_t need_allocate = B_FALSE;
+			if (zio->io_pipeline & ZIO_STAGE_DVA_ALLOCATE)
+				need_allocate = B_TRUE;
+			zio->io_pipeline = ZIO_INTERLOCK_STAGES;
+			if (need_allocate)
+				zio->io_pipeline |= ZIO_STAGE_DVA_ALLOCATE;
+			BP_SET_PROP_RESERVATION(bp, 1);
+		} else {
+			BP_SET_PROP_RESERVATION(bp, 0);
+		}
 		if (zp->zp_dedup) {
 			ASSERT(zio->io_child_type == ZIO_CHILD_LOGICAL);
 			ASSERT(!(zio->io_flags & ZIO_FLAG_IO_REWRITE));
@@ -1871,6 +1896,7 @@
 		zp.zp_copies = gio->io_prop.zp_copies;
 		zp.zp_dedup = B_FALSE;
 		zp.zp_dedup_verify = B_FALSE;
+		zp.zp_zero_write = B_FALSE;
 		zp.zp_nopwrite = B_FALSE;
 
 		zio_nowait(zio_write(zio, spa, txg, &gbh->zg_blkptr[g],
--- old/usr/src/uts/common/sys/filio.h	2014-10-28 11:57:20.486599912 -0700
+++ new/usr/src/uts/common/sys/filio.h	2014-10-28 11:57:20.490010517 -0700
@@ -136,6 +136,7 @@
  * boot archive compression
  */
 #define	_FIO_COMPRESSED		_IO('f', 99) /* mark file as compressed */
+#define	_FIO_RESERVE_SPACE	_IO('f', 100) /* Reserve space */
 
 #ifdef	__cplusplus
 }
