Print this page
Possibility to physically reserve space without writing leaf blocks
@@ -72,10 +72,11 @@
#include <sys/sid.h>
#include "fs/fs_subr.h"
#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>
#include <sys/kidmap.h>
#include <sys/cred.h>
@@ -290,19 +291,24 @@
return (error);
*off = noff;
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,
int *rvalp, caller_context_t *ct)
{
offset_t off;
int error;
zfsvfs_t *zfsvfs;
znode_t *zp;
+ uint64_t size;
switch (com) {
case _FIOFFS:
return (zfs_sync(vp->v_vfsp, 0, cred));
@@ -330,10 +336,15 @@
if (error)
return (error);
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));
}
/*
@@ -954,10 +965,106 @@
ZFS_EXIT(zfsvfs);
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)
{
znode_t *zp = zgd->zgd_private;
objset_t *os = zp->z_zfsvfs->z_os;