From fbc2d93153f57b85b1b5632401148e3378ea60de Mon Sep 17 00:00:00 2001 From: Jitendra Patidar Date: Thu, 25 Jul 2019 06:28:23 -0700 Subject: [PATCH] ZFS fix to make xattr=sa logging to ZIL on xattr create/remove/update. As such, there are no specific synchronous symentics defined for the xattrs. But for xattr=on, it does log to ZIL and zil_commit() is done, if sync=always is set on dataset. This provides sync symentics for xattr=on with sync=always set on dataset. For the xattr=sa implementation, it doesn't log to ZIL, so, even with sync=always, xattrs are not guranteed to be synced before xattr call returns to caller. so xattr can be lost if system crash happens, before txg carring xattr transaction is synced. This change makes xattr=sa logging to ZIL on xattr create/remove/update and xattrs are synced to ZIL (zil_commit() done) for sync=always. This make xattr=sa behaviour similar to xattr=on. This could also provide basic framework to support implementing sync symentics at file level for xattr=sa. Signed-off-by: Jitendra Patidar Closes #8768 Change-Id: Ic24306def9515d34ac07fba4cd7d341ddbfc75c3 --- cmd/zdb/zdb_il.c | 27 ++++ cmd/ztest/ztest.c | 1 + include/sys/zfs_sa.h | 2 +- include/sys/zfs_znode.h | 2 + include/sys/zil.h | 13 +- include/zfeature_common.h | 1 + lib/libzfs/libzfs.abi | 9 +- man/man4/zfs.4 | 4 + module/os/freebsd/zfs/zfs_vnops_os.c | 4 +- module/os/linux/zfs/zpl_xattr.c | 2 +- module/zcommon/zfeature_common.c | 12 ++ module/zfs/zfs_log.c | 34 ++++ module/zfs/zfs_replay.c | 87 +++++++++++ module/zfs/zfs_sa.c | 23 ++- tests/runfiles/common.run | 2 +- tests/zfs-tests/include/tunables.cfg | 1 + .../cli_root/zpool_get/zpool_get.cfg | 1 + .../tests/functional/slog/Makefile.am | 3 +- .../tests/functional/slog/slog_016_pos.ksh | 147 ++++++++++++++++++ 19 files changed, 361 insertions(+), 14 deletions(-) create mode 100755 tests/zfs-tests/tests/functional/slog/slog_016_pos.ksh diff --git a/cmd/zdb/zdb_il.c b/cmd/zdb/zdb_il.c index 553765b71716..697c55f38ea8 100644 --- a/cmd/zdb/zdb_il.c +++ b/cmd/zdb/zdb_il.c @@ -266,6 +266,32 @@ zil_prt_rec_setattr(zilog_t *zilog, int txtype, const void *arg) } } +/* ARGSUSED */ +static void +zil_prt_rec_setsaxattr(zilog_t *zilog, int txtype, const void *arg) +{ + const lr_setsaxattr_t *lr = arg; + char *name; + char *val; + int i; + + name = (char *)(lr + 1); + (void) printf("%sfoid %llu\n", tab_prefix, + (u_longlong_t)lr->lr_foid); + + (void) printf("%sXAT_NAME %s\n", tab_prefix, name); + if (lr->lr_size == 0) { + (void) printf("%sXAT_VALUE NULL\n", tab_prefix); + } else { + printf("%sXAT_VALUE ", tab_prefix); + val = name + (strlen(name) + 1); + for (i = 0; i < lr->lr_size; i++) { + printf("%c", *val); + val++; + } + } +} + /* ARGSUSED */ static void zil_prt_rec_acl(zilog_t *zilog, int txtype, const void *arg) @@ -305,6 +331,7 @@ static zil_rec_info_t zil_rec_info[TX_MAX_TYPE] = { {.zri_print = zil_prt_rec_create, .zri_name = "TX_MKDIR_ATTR "}, {.zri_print = zil_prt_rec_create, .zri_name = "TX_MKDIR_ACL_ATTR "}, {.zri_print = zil_prt_rec_write, .zri_name = "TX_WRITE2 "}, + {.zri_print = zil_prt_rec_setsaxattr, .zri_name = "TX_SETSAXATTR "}, }; /* ARGSUSED */ diff --git a/cmd/ztest/ztest.c b/cmd/ztest/ztest.c index 73694b0b352b..d7b51df6d441 100644 --- a/cmd/ztest/ztest.c +++ b/cmd/ztest/ztest.c @@ -2383,6 +2383,7 @@ zil_replay_func_t *ztest_replay_vector[TX_MAX_TYPE] = { NULL, /* TX_MKDIR_ATTR */ NULL, /* TX_MKDIR_ACL_ATTR */ NULL, /* TX_WRITE2 */ + NULL, /* TX_SETSAXATTR */ }; /* diff --git a/include/sys/zfs_sa.h b/include/sys/zfs_sa.h index 1ca7ced331c5..ae9ee805e4a9 100644 --- a/include/sys/zfs_sa.h +++ b/include/sys/zfs_sa.h @@ -139,7 +139,7 @@ void zfs_sa_symlink(struct znode *, char *link, int len, dmu_tx_t *); void zfs_sa_get_scanstamp(struct znode *, xvattr_t *); void zfs_sa_set_scanstamp(struct znode *, xvattr_t *, dmu_tx_t *); int zfs_sa_get_xattr(struct znode *); -int zfs_sa_set_xattr(struct znode *); +int zfs_sa_set_xattr(struct znode *, const char *, const void *, size_t); void zfs_sa_upgrade(struct sa_handle *, dmu_tx_t *); void zfs_sa_upgrade_txholds(dmu_tx_t *, struct znode *); void zfs_sa_init(void); diff --git a/include/sys/zfs_znode.h b/include/sys/zfs_znode.h index 1bf25a77d3a0..e20c18cc2b62 100644 --- a/include/sys/zfs_znode.h +++ b/include/sys/zfs_znode.h @@ -286,6 +286,8 @@ extern void zfs_log_acl(zilog_t *zilog, dmu_tx_t *tx, znode_t *zp, vsecattr_t *vsecp, zfs_fuid_info_t *fuidp); extern void zfs_xvattr_set(znode_t *zp, xvattr_t *xvap, dmu_tx_t *tx); extern void zfs_upgrade(zfsvfs_t *zfsvfs, dmu_tx_t *tx); +extern void zfs_log_setsaxattr(zilog_t *zilog, dmu_tx_t *tx, int txtype, + znode_t *zp, const char *name, const void *value, size_t size); extern void zfs_znode_update_vfs(struct znode *); diff --git a/include/sys/zil.h b/include/sys/zil.h index cefbccb32f22..2f1e27c4243a 100644 --- a/include/sys/zil.h +++ b/include/sys/zil.h @@ -162,7 +162,8 @@ typedef enum zil_create { #define TX_MKDIR_ATTR 18 /* mkdir with attr */ #define TX_MKDIR_ACL_ATTR 19 /* mkdir with ACL + attrs */ #define TX_WRITE2 20 /* dmu_sync EALREADY write */ -#define TX_MAX_TYPE 21 /* Max transaction type */ +#define TX_SETSAXATTR 21 /* Set sa xattrs on file */ +#define TX_MAX_TYPE 22 /* Max transaction type */ /* * The transactions for mkdir, symlink, remove, rmdir, link, and rename @@ -182,7 +183,8 @@ typedef enum zil_create { (txtype) == TX_SETATTR || \ (txtype) == TX_ACL_V0 || \ (txtype) == TX_ACL || \ - (txtype) == TX_WRITE2) + (txtype) == TX_WRITE2 || \ + (txtype) == TX_SETSAXATTR) /* * The number of dnode slots consumed by the object is stored in the 8 @@ -335,6 +337,13 @@ typedef struct { /* optional attribute lr_attr_t may be here */ } lr_setattr_t; +typedef struct { + lr_t lr_common; /* common portion of log record */ + uint64_t lr_foid; /* file object to change attributes */ + uint64_t lr_size; + /* xattr name and value follows */ +} lr_setsaxattr_t; + typedef struct { lr_t lr_common; /* common portion of log record */ uint64_t lr_foid; /* obj id of file */ diff --git a/include/zfeature_common.h b/include/zfeature_common.h index 874cbd9ff714..580f5ff3e0ee 100644 --- a/include/zfeature_common.h +++ b/include/zfeature_common.h @@ -75,6 +75,7 @@ typedef enum spa_feature { SPA_FEATURE_DEVICE_REBUILD, SPA_FEATURE_ZSTD_COMPRESS, SPA_FEATURE_DRAID, + SPA_FEATURE_ZILSAXATTR, SPA_FEATURES } spa_feature_t; diff --git a/lib/libzfs/libzfs.abi b/lib/libzfs/libzfs.abi index 9a1d95d96ce9..efb23e3e45a0 100644 --- a/lib/libzfs/libzfs.abi +++ b/lib/libzfs/libzfs.abi @@ -347,7 +347,7 @@ - + @@ -4396,7 +4396,8 @@ - + + @@ -6712,8 +6713,8 @@ - - + + diff --git a/man/man4/zfs.4 b/man/man4/zfs.4 index 6da8d42b42bd..94d7f88c947c 100644 --- a/man/man4/zfs.4 +++ b/man/man4/zfs.4 @@ -2073,6 +2073,10 @@ Limit SLOG write size per commit executed with synchronous priority. Any writes above that will be executed with lower (asynchronous) priority to limit potential SLOG device abuse by single active ZIL writer. . +.It Sy zfs_zil_saxattr_enabled Ns = Ns Sy 1 Pq int +Enable xattr=sa extended attribute logging in zil. +Setting value to 0 disables xattr=sa logging in zil. +. .It Sy zfs_embedded_slog_min_ms Ns = Ns Sy 64 Pq int Usually, one metaslab from each normal-class vdev is dedicated for use by the ZIL to log synchronous writes. diff --git a/module/os/freebsd/zfs/zfs_vnops_os.c b/module/os/freebsd/zfs/zfs_vnops_os.c index 46a632b0385c..6fada4cca07d 100644 --- a/module/os/freebsd/zfs/zfs_vnops_os.c +++ b/module/os/freebsd/zfs/zfs_vnops_os.c @@ -5488,7 +5488,7 @@ zfs_deleteextattr_sa(struct vop_deleteextattr_args *ap, const char *attrname) nvl = zp->z_xattr_cached; error = nvlist_remove(nvl, attrname, DATA_TYPE_BYTE_ARRAY); if (error == 0) - error = zfs_sa_set_xattr(zp); + error = zfs_sa_set_xattr(zp, attrname, NULL, 0); if (error != 0) { zp->z_xattr_cached = NULL; nvlist_free(nvl); @@ -5626,7 +5626,7 @@ zfs_setextattr_sa(struct vop_setextattr_args *ap, const char *attrname) error = nvlist_add_byte_array(nvl, attrname, buf, entry_size); kmem_free(buf, entry_size); if (error == 0) - error = zfs_sa_set_xattr(zp); + error = zfs_sa_set_xattr(zp, attrname, buf, entry_size); if (error != 0) { zp->z_xattr_cached = NULL; nvlist_free(nvl); diff --git a/module/os/linux/zfs/zpl_xattr.c b/module/os/linux/zfs/zpl_xattr.c index 66f197e4c77a..30fedc6778cc 100644 --- a/module/os/linux/zfs/zpl_xattr.c +++ b/module/os/linux/zfs/zpl_xattr.c @@ -578,7 +578,7 @@ zpl_xattr_set_sa(struct inode *ip, const char *name, const void *value, * will be reconstructed from the ARC when next accessed. */ if (error == 0) - error = -zfs_sa_set_xattr(zp); + error = -zfs_sa_set_xattr(zp, name, value, size); if (error) { nvlist_free(nvl); diff --git a/module/zcommon/zfeature_common.c b/module/zcommon/zfeature_common.c index fc0e09605eef..95b52414a291 100644 --- a/module/zcommon/zfeature_common.c +++ b/module/zcommon/zfeature_common.c @@ -598,6 +598,18 @@ zpool_feature_init(void) zfeature_register(SPA_FEATURE_DRAID, "org.openzfs:draid", "draid", "Support for distributed spare RAID", ZFEATURE_FLAG_MOS, ZFEATURE_TYPE_BOOLEAN, NULL); + + { + static const spa_feature_t zilsaxattr_deps[] = { + SPA_FEATURE_EXTENSIBLE_DATASET, + SPA_FEATURE_NONE + }; + zfeature_register(SPA_FEATURE_ZILSAXATTR, + "org.zfsonlinux:zilsaxattr", "zilsaxattr", + "Zil sa xattr.", + ZFEATURE_FLAG_READONLY_COMPAT | ZFEATURE_FLAG_ACTIVATE_ON_ENABLE, + ZFEATURE_TYPE_BOOLEAN, zilsaxattr_deps); + } } #if defined(_KERNEL) diff --git a/module/zfs/zfs_log.c b/module/zfs/zfs_log.c index 30d5c4821ae5..ae82fb81b22f 100644 --- a/module/zfs/zfs_log.c +++ b/module/zfs/zfs_log.c @@ -714,6 +714,40 @@ zfs_log_setattr(zilog_t *zilog, dmu_tx_t *tx, int txtype, zil_itx_assign(zilog, itx, tx); } +/* + * Handles TX_SETSAXATTR transactions. + */ +void +zfs_log_setsaxattr(zilog_t *zilog, dmu_tx_t *tx, int txtype, + znode_t *zp, const char *name, const void *value, size_t size) +{ + itx_t *itx; + lr_setsaxattr_t *lr; + size_t recsize = sizeof (lr_setsaxattr_t); + void *xattrstart; + int namelen; + + if (zil_replaying(zilog, tx) || zp->z_unlinked) + return; + + namelen = strlen(name) + 1; + recsize += (namelen + size); + itx = zil_itx_create(txtype, recsize); + lr = (lr_setsaxattr_t *)&itx->itx_lr; + lr->lr_foid = zp->z_id; + xattrstart = (char *)(lr + 1); + bcopy(name, xattrstart, namelen); + if (value != NULL) { + bcopy(value, (char *)xattrstart + namelen, size); + lr->lr_size = size; + } else { + lr->lr_size = 0; + } + + itx->itx_sync = (zp->z_sync_cnt != 0); + zil_itx_assign(zilog, itx, tx); +} + /* * Handles TX_ACL transactions. */ diff --git a/module/zfs/zfs_replay.c b/module/zfs/zfs_replay.c index cba5e8c9cd0b..178f369edb9c 100644 --- a/module/zfs/zfs_replay.c +++ b/module/zfs/zfs_replay.c @@ -47,6 +47,9 @@ #include #include #include +#include +#include +#include /* * NB: FreeBSD expects to be able to do vnode locking in lookup and @@ -868,6 +871,89 @@ zfs_replay_setattr(void *arg1, void *arg2, boolean_t byteswap) return (error); } +static int +zfs_replay_setsaxattr(void *arg1, void *arg2, boolean_t byteswap) +{ + zfsvfs_t *zfsvfs = arg1; + lr_setsaxattr_t *lr = arg2; + znode_t *zp; + nvlist_t *nvl; + size_t sa_size; + char *name; + char *value; + size_t size; + int error = 0; + + ASSERT(spa_feature_is_active(zfsvfs->z_os->os_spa, + SPA_FEATURE_ZILSAXATTR)); + if (byteswap) { + byteswap_uint64_array(lr, sizeof (*lr)); + } + + if ((error = zfs_zget(zfsvfs, lr->lr_foid, &zp)) != 0) + return (error); + + rw_enter(&zp->z_xattr_lock, RW_WRITER); + mutex_enter(&zp->z_lock); + if (zp->z_xattr_cached == NULL) + error = -zfs_sa_get_xattr(zp); + mutex_exit(&zp->z_lock); + + if (error) + goto out; + + ASSERT(zp->z_xattr_cached); + nvl = zp->z_xattr_cached; + + /* Get xattr name, value and size from log record */ + size = lr->lr_size; + name = (char *)(lr + 1); + if (size == 0) { + value = NULL; + error = -nvlist_remove(nvl, name, DATA_TYPE_BYTE_ARRAY); + } else { + value = name + strlen(name) + 1; + /* Limited to 32k to keep nvpair memory allocations small */ + if (size > DXATTR_MAX_ENTRY_SIZE) { + error = (-EFBIG); + goto out; + } + + /* Prevent the DXATTR SA from consuming the entire SA region */ + error = -nvlist_size(nvl, &sa_size, NV_ENCODE_XDR); + if (error) + goto out; + + if (sa_size > DXATTR_MAX_SA_SIZE) { + error = (-EFBIG); + goto out; + } + + error = -nvlist_add_byte_array(nvl, name, (uchar_t *)value, + size); + } + + /* + * Update the SA for additions, modifications, and removals. On + * error drop the inconsistent cached version of the nvlist, it + * will be reconstructed from the ARC when next accessed. + */ + if (error == 0) + error = -zfs_sa_set_xattr(zp, name, value, size); + + if (error) { + nvlist_free(nvl); + zp->z_xattr_cached = NULL; + } + + ASSERT3S(error, <=, 0); + +out: + rw_exit(&zp->z_xattr_lock); + zrele(zp); + return (error); +} + static int zfs_replay_acl_v0(void *arg1, void *arg2, boolean_t byteswap) { @@ -989,4 +1075,5 @@ zil_replay_func_t *zfs_replay_vector[TX_MAX_TYPE] = { zfs_replay_create, /* TX_MKDIR_ATTR */ zfs_replay_create_acl, /* TX_MKDIR_ACL_ATTR */ zfs_replay_write2, /* TX_WRITE2 */ + zfs_replay_setsaxattr, /* TX_SETSAXATTR */ }; diff --git a/module/zfs/zfs_sa.c b/module/zfs/zfs_sa.c index 67be131da63b..78ed889e3a3e 100644 --- a/module/zfs/zfs_sa.c +++ b/module/zfs/zfs_sa.c @@ -27,8 +27,10 @@ #include #include #include +#include #include #include +#include /* * ZPL attribute registration table. @@ -69,6 +71,8 @@ sa_attr_reg_t zfs_attr_table[ZPL_END+1] = { {NULL, 0, 0, 0} }; +int zfs_zil_saxattr_enabled = 1; + #ifdef _KERNEL int zfs_sa_readlink(znode_t *zp, zfs_uio_t *uio) @@ -219,13 +223,14 @@ zfs_sa_get_xattr(znode_t *zp) } int -zfs_sa_set_xattr(znode_t *zp) +zfs_sa_set_xattr(znode_t *zp, const char *name, const void *value, size_t vsize) { zfsvfs_t *zfsvfs = ZTOZSB(zp); + zilog_t *zilog; dmu_tx_t *tx; char *obj; size_t size; - int error; + int error, logsaxattr = 0; ASSERT(RW_WRITE_HELD(&zp->z_xattr_lock)); ASSERT(zp->z_xattr_cached); @@ -244,6 +249,11 @@ zfs_sa_set_xattr(znode_t *zp) if (error) goto out_free; + zilog = zfsvfs->z_log; + if (spa_feature_is_active(zfsvfs->z_os->os_spa, + SPA_FEATURE_ZILSAXATTR) && zfs_zil_saxattr_enabled) { + logsaxattr = 1; + } tx = dmu_tx_create(zfsvfs->z_os); dmu_tx_hold_sa_create(tx, size); dmu_tx_hold_sa(tx, zp->z_sa_hdl, B_TRUE); @@ -256,6 +266,10 @@ zfs_sa_set_xattr(znode_t *zp) sa_bulk_attr_t bulk[2]; uint64_t ctime[2]; + if (logsaxattr) { + zfs_log_setsaxattr(zilog, tx, TX_SETSAXATTR, zp, name, + value, vsize); + } zfs_tstamp_update_setup(zp, STATE_CHANGED, NULL, ctime); SA_ADD_BULK_ATTR(bulk, count, SA_ZPL_DXATTR(zfsvfs), NULL, obj, size); @@ -264,6 +278,8 @@ zfs_sa_set_xattr(znode_t *zp) VERIFY0(sa_bulk_update(zp->z_sa_hdl, bulk, count, tx)); dmu_tx_commit(tx); + if (logsaxattr && zfsvfs->z_os->os_sync == ZFS_SYNC_ALWAYS) + zil_commit(zilog, 0); } out_free: vmem_free(obj, size); @@ -433,6 +449,9 @@ zfs_sa_upgrade_txholds(dmu_tx_t *tx, znode_t *zp) } } +ZFS_MODULE_PARAM(zfs, zfs_, zil_saxattr_enabled, INT, ZMOD_RW, + "Enable xattr=sa extended attribute logging in zil."); + EXPORT_SYMBOL(zfs_attr_table); EXPORT_SYMBOL(zfs_sa_readlink); EXPORT_SYMBOL(zfs_sa_symlink); diff --git a/tests/runfiles/common.run b/tests/runfiles/common.run index e87c1cd641ff..0ae3b274f9d7 100644 --- a/tests/runfiles/common.run +++ b/tests/runfiles/common.run @@ -835,7 +835,7 @@ tests = ['slog_001_pos', 'slog_002_pos', 'slog_003_pos', 'slog_004_pos', 'slog_005_pos', 'slog_006_pos', 'slog_007_pos', 'slog_008_neg', 'slog_009_neg', 'slog_010_neg', 'slog_011_neg', 'slog_012_neg', 'slog_013_pos', 'slog_014_pos', 'slog_015_neg', 'slog_replay_fs_001', - 'slog_replay_fs_002', 'slog_replay_volume'] + 'slog_replay_fs_002', 'slog_replay_volume', 'slog_016_pos'] tags = ['functional', 'slog'] [tests/functional/snapshot] diff --git a/tests/zfs-tests/include/tunables.cfg b/tests/zfs-tests/include/tunables.cfg index a1b75a48292f..21a55d294f32 100644 --- a/tests/zfs-tests/include/tunables.cfg +++ b/tests/zfs-tests/include/tunables.cfg @@ -89,6 +89,7 @@ VOL_RECURSIVE vol.recursive UNSUPPORTED ZEVENT_LEN_MAX zevent.len_max zfs_zevent_len_max ZEVENT_RETAIN_MAX zevent.retain_max zfs_zevent_retain_max ZIO_SLOW_IO_MS zio.slow_io_ms zio_slow_io_ms +ZIL_SAXATTR_ENABLED zil_saxattr_enabled zfs_zil_saxattr_enabled %%%% while read name FreeBSD Linux; do eval "export ${name}=\$${UNAME}" diff --git a/tests/zfs-tests/tests/functional/cli_root/zpool_get/zpool_get.cfg b/tests/zfs-tests/tests/functional/cli_root/zpool_get/zpool_get.cfg index 6075e1f1abbd..d99f578f383e 100644 --- a/tests/zfs-tests/tests/functional/cli_root/zpool_get/zpool_get.cfg +++ b/tests/zfs-tests/tests/functional/cli_root/zpool_get/zpool_get.cfg @@ -96,6 +96,7 @@ if is_linux || is_freebsd; then "feature@bookmark_v2" "feature@livelist" "feature@zstd_compress" + "feature@zilsaxattr" ) fi diff --git a/tests/zfs-tests/tests/functional/slog/Makefile.am b/tests/zfs-tests/tests/functional/slog/Makefile.am index 33e3a6d3a496..92c3fd6c8e75 100644 --- a/tests/zfs-tests/tests/functional/slog/Makefile.am +++ b/tests/zfs-tests/tests/functional/slog/Makefile.am @@ -19,7 +19,8 @@ dist_pkgdata_SCRIPTS = \ slog_015_neg.ksh \ slog_replay_fs_001.ksh \ slog_replay_fs_002.ksh \ - slog_replay_volume.ksh + slog_replay_volume.ksh \ + slog_016_pos.ksh dist_pkgdata_DATA = \ slog.cfg \ diff --git a/tests/zfs-tests/tests/functional/slog/slog_016_pos.ksh b/tests/zfs-tests/tests/functional/slog/slog_016_pos.ksh new file mode 100755 index 000000000000..be626a5e81f9 --- /dev/null +++ b/tests/zfs-tests/tests/functional/slog/slog_016_pos.ksh @@ -0,0 +1,147 @@ +#!/bin/ksh -p +# +# CDDL HEADER START +# +# The contents of this file are subject to the terms of the +# Common Development and Distribution License (the "License"). +# You may not use this file except in compliance with the License. +# +# You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE +# or http://www.opensolaris.org/os/licensing. +# See the License for the specific language governing permissions +# and limitations under the License. +# +# When distributing Covered Code, include this CDDL HEADER in each +# file and include the License file at usr/src/OPENSOLARIS.LICENSE. +# If applicable, add the following below this CDDL HEADER, with the +# fields enclosed by brackets "[]" replaced with your own identifying +# information: Portions Copyright [yyyy] [name of copyright owner] +# +# CDDL HEADER END +# + +# +# Copyright (c) 2021 by Nutanix. All rights reserved. +# + +. $STF_SUITE/tests/functional/slog/slog.kshlib + +# +# DESCRIPTION: +# Verify saxattr logging in to ZIL works +# +# STRATEGY: +# 1. Create an empty file system (TESTFS) +# 2. Freeze TESTFS +# 3. Create Xattrs. +# 4. Unmount filesystem +# +# 5. Remount TESTFS +# 6. Check xattrs. +# + +verify_runnable "global" + +function cleanup_testenv +{ + cleanup + log_must set_tunable32 ZIL_SAXATTR_ENABLED $orig_zil_saxattr_enabled +} + +log_assert "Verify saxattr logging in to ZIL works" + +orig_zil_saxattr_enabled=$(get_tunable ZIL_SAXATTR_ENABLED) + +log_onexit cleanup_testenv +log_must setup + +NFILES=10 +function validate_zil_saxattr +{ + saxattrzil=$1 + + log_must set_tunable32 ZIL_SAXATTR_ENABLED $saxattrzil + + # + # 1. Create an empty file system (TESTFS) + # + log_must zpool create $TESTPOOL $VDEV log mirror $LDEV + log_must zfs set compression=on $TESTPOOL + log_must zfs create -o xattr=sa $TESTPOOL/$TESTFS + log_must mkdir -p $TESTDIR + + # + # This dd command works around an issue where ZIL records aren't created + # after freezing the pool unless a ZIL header already exists. Create a + # file synchronously to force ZFS to write one out. + # + log_must dd if=/dev/zero of=/$TESTPOOL/$TESTFS/sync \ + conv=fdatasync,fsync bs=1 count=1 + + # + # 2. Freeze TESTFS + # + log_must zpool freeze $TESTPOOL + + rm /$TESTPOOL/$TESTFS/sync + # + # 3. Create xattrs + # + for i in $(seq $NFILES); do + log_must mkdir /$TESTPOOL/$TESTFS/xattr.d.$i + log_must set_xattr test test /$TESTPOOL/$TESTFS/xattr.d.$i + + log_must touch /$TESTPOOL/$TESTFS/xattr.f.$i + log_must set_xattr test test /$TESTPOOL/$TESTFS/xattr.f.$i + done + + # + # 4. Unmount filesystem and export the pool + # + # At this stage TESTFS is empty again and unfrozen, and the + # intent log contains a complete set of deltas to replay it. + # + log_must zfs unmount /$TESTPOOL/$TESTFS + + log_note "Verify transactions to replay:" + log_must zdb -iv $TESTPOOL/$TESTFS + + log_must zpool export $TESTPOOL + + # + # 5. Remount TESTFS + # + # Import the pool to unfreeze it and claim log blocks. It has to be + # `zpool import -f` because we can't write a frozen pool's labels! + # + log_must zpool import -f -d $VDIR $TESTPOOL + + # + # 6. Verify Xattr + # If saxattrzil=0, then xattr=sa logging in zil is not enabled, + # So, xattrs would be lost. + # If saxattrzil=1, then xattr=sa logging in zil is enabled, + # So, xattrs shouldn't be lost. + # + for i in $(seq $NFILES); do + if [ $saxattrzil -eq 0 ]; then + log_mustnot get_xattr test /$TESTPOOL/$TESTFS/xattr.d.$i + log_mustnot get_xattr test /$TESTPOOL/$TESTFS/xattr.f.$i + else + log_must get_xattr test /$TESTPOOL/$TESTFS/xattr.d.$i + log_must get_xattr test /$TESTPOOL/$TESTFS/xattr.f.$i + fi + done + + cleanup + log_must setup +} + +#Validate xattr=sa logging in zil disabled. +validate_zil_saxattr 0 + +#Validate xattr=sa logging in zil enabled. +validate_zil_saxattr 1 + +log_pass "Verify saxattr logging in to ZIL works"