Use EA to store 'data_version' for DoM files explicitly.
Unlike OST objects the 'inode_version' of DoM file is changed
by metadata operations as well and that leads to problems
during HSM operations, e.g. writing HSM EA with file data
version inside causes DoM object version update making this
HSM EA version obsoleted, also any metadata update on
restored file makes it dirty and prevents second release.
DoM files have now explicitly updated 'data_version' in
addition to ordinary 'inode_version'. The 'data_version'
is updated along with 'inode_version' upon write/truncate and
fallocate operations and is stored as 'trusted.dataver' EA.
Layout swap procedure is updated to move data version between
files being swept along with HSM attributes.
If DoM file is migrated to RAID0 file then 'dataver' EA is
deleted.
Corresponding test 1f is added to sanity-hsm.sh and
207j to sanity.sh.
Test-Parameters: clientversion=2.12.4 testlist=sanity-hsm
Signed-off-by: Mikhail Pershin <mpershin@whamcloud.com>
Change-Id: I4689c56394c7323d32cd6f7dd86f58beb6e53353
Reviewed-on: https://review.whamcloud.com/c/fs/lustre-release/+/47139
Reviewed-by: Andreas Dilger <adilger@whamcloud.com>
Reviewed-by: Oleg Drokin <green@whamcloud.com>
Reviewed-by: Sergey Cheremencev <scherementsev@ddn.com>
Tested-by: Maloo <maloo@whamcloud.com>
Tested-by: jenkins <devops@whamcloud.com>
}
int dt_declare_version_set(const struct lu_env *env, struct dt_object *o,
- struct thandle *th);
+ struct thandle *th);
void dt_version_set(const struct lu_env *env, struct dt_object *o,
- dt_obj_version_t version, struct thandle *th);
+ dt_obj_version_t version, struct thandle *th);
+int dt_declare_data_version_set(const struct lu_env *env, struct dt_object *o,
+ struct thandle *th);
+void dt_data_version_set(const struct lu_env *env, struct dt_object *o,
+ dt_obj_version_t version, struct thandle *th);
+int dt_declare_data_version_del(const struct lu_env *env, struct dt_object *o,
+ struct thandle *th);
+void dt_data_version_del(const struct lu_env *env, struct dt_object *o,
+ struct thandle *th);
dt_obj_version_t dt_version_get(const struct lu_env *env, struct dt_object *o);
-
+dt_obj_version_t dt_data_version_get(const struct lu_env *env,
+ struct dt_object *o);
+dt_obj_version_t dt_data_version_init(const struct lu_env *env,
+ struct dt_object *o);
int dt_read(const struct lu_env *env, struct dt_object *dt,
struct lu_buf *buf, loff_t *pos);
* this value onto disk for recovery when tgt_txn_stop_cb() is called.
*/
__u64 tsi_opdata;
+ bool tsi_dv_update;
/*
* Additional fail id that can be set by handler.
return tsi;
}
-static inline void tgt_vbr_obj_set(const struct lu_env *env,
- struct dt_object *obj)
+static inline void tgt_vbr_obj_data_set(const struct lu_env *env,
+ struct dt_object *obj, bool dv_update)
{
struct tgt_session_info *tsi;
if (env->le_ses != NULL) {
tsi = tgt_ses_info(env);
tsi->tsi_vbr_obj = obj;
+ tsi->tsi_dv_update = dv_update;
}
}
+static inline void tgt_vbr_obj_set(const struct lu_env *env,
+ struct dt_object *obj)
+{
+ return tgt_vbr_obj_data_set(env, obj, false);
+}
+
static inline void tgt_open_obj_set(const struct lu_env *env,
struct dt_object *obj)
{
#define XATTR_NAME_LFSCK_BITMAP "trusted.lfsck_bitmap"
#define XATTR_NAME_DUMMY "trusted.dummy"
#define XATTR_NAME_PROJID "trusted.projid"
+#define XATTR_NAME_DATAVER "trusted.dataver"
#define LL_XATTR_NAME_ENCRYPTION_CONTEXT_OLD XATTR_SECURITY_PREFIX"c"
#define LL_XATTR_NAME_ENCRYPTION_CONTEXT XATTR_ENCRYPTION_PREFIX"c"
ioc->idv_version = io->u.ci_data_version.dv_data_version;
ioc->idv_layout_version = io->u.ci_data_version.dv_layout_version;
-
cl_io_fini(env, io);
if (unlikely(io->ci_need_restart))
struct mdd_object *mdd_obj,
const char *xattr_name,
struct thandle *handle);
+int mdd_dom_fixup(const struct lu_env *env, struct mdd_device *mdd,
+ struct mdd_object *mo, struct mdd_object *vo);
/* mdd_trans.c */
void mdd_object_make_hint(const struct lu_env *env, struct mdd_object *parent,
return 0;
}
-static int mdd_dom_data_truncate(const struct lu_env *env,
- struct mdd_device *mdd, struct mdd_object *mo);
-
static int mdd_xattr_split(const struct lu_env *env, struct md_object *md_obj,
struct md_rejig_data *mrd)
{
/* Truncate local DOM data if all went well */
if (!rc && dom_stripe)
- mdd_dom_data_truncate(env, mdd, obj);
+ mdd_dom_fixup(env, mdd, obj, NULL);
lu_buf_free(buf_save);
lu_buf_free(buf);
return mdd_lmm_gen(lmm, gen, false);
}
-static int mdd_dom_data_truncate(const struct lu_env *env,
- struct mdd_device *mdd, struct mdd_object *mo)
+int mdd_dom_fixup(const struct lu_env *env, struct mdd_device *mdd,
+ struct mdd_object *mo, struct mdd_object *vo)
{
+ struct dt_object *dom, *vlt;
+ dt_obj_version_t dv = 0;
struct thandle *th;
- struct dt_object *dom;
int rc;
+ ENTRY;
+
+ if (vo) {
+ vlt = dt_object_locate(mdd_object_child(vo), mdd->mdd_bottom);
+ if (!vlt)
+ GOTO(out, rc = -ENOENT);
+ dv = dt_data_version_get(env, vlt);
+ if (!dv)
+ GOTO(out, rc = -ENODATA);
+ }
+
dom = dt_object_locate(mdd_object_child(mo), mdd->mdd_bottom);
- if (!dom)
- GOTO(out, rc = -ENODATA);
th = dt_trans_create(env, mdd->mdd_bottom);
if (IS_ERR(th))
GOTO(out, rc = PTR_ERR(th));
- rc = dt_declare_punch(env, dom, 0, OBD_OBJECT_EOF, th);
- if (rc)
- GOTO(stop, rc);
+ if (vo) {
+ rc = dt_declare_data_version_set(env, dom, th);
+ if (rc)
+ GOTO(stop, rc);
+ } else {
+ rc = dt_declare_data_version_del(env, dom, th);
+ if (rc)
+ GOTO(stop, rc);
+ rc = dt_declare_punch(env, dom, 0, OBD_OBJECT_EOF, th);
+ if (rc)
+ GOTO(stop, rc);
+ }
rc = dt_trans_start_local(env, mdd->mdd_bottom, th);
if (rc != 0)
GOTO(stop, rc);
- rc = dt_punch(env, dom, 0, OBD_OBJECT_EOF, th);
+ if (vo) {
+ dt_data_version_set(env, dom, dv, th);
+ } else {
+ dt_data_version_del(env, dom, th);
+ rc = dt_punch(env, dom, 0, OBD_OBJECT_EOF, th);
+ }
+
stop:
dt_trans_stop(env, mdd->mdd_bottom, th);
out:
/* Ignore failure but report the error */
if (rc)
- CERROR("%s: can't truncate DOM inode "DFID" data: rc = %d\n",
+ CERROR("%s: can't manage DOM file "DFID" data: rc = %d\n",
mdd_obj_dev_name(mo), PFID(mdd_object_fid(mo)), rc);
return rc;
}
struct lu_buf *snd_hsm_buf = &info->mdi_buf[3];
struct ost_id *saved_oi = NULL;
struct thandle *handle;
- struct mdd_object *dom_o = NULL;
+ struct mdd_object *dom_o = NULL, *vlt_o = NULL;
__u64 domsize_dom, domsize_vlt;
__u32 fst_gen, snd_gen, saved_gen;
int fst_fl;
* target file must be volatile and orphan.
*/
if (fst_o->mod_flags & (ORPHAN_OBJ | VOLATILE_OBJ)) {
+ vlt_o = domsize_vlt ? fst_o : NULL;
dom_o = domsize_dom ? snd_o : NULL;
} else if (snd_o->mod_flags & (ORPHAN_OBJ | VOLATILE_OBJ)) {
swap(domsize_dom, domsize_vlt);
+ vlt_o = domsize_vlt ? snd_o : NULL;
dom_o = domsize_dom ? fst_o : NULL;
} else if (domsize_dom > 0 || domsize_vlt > 0) {
/* 'lfs swap_layouts' case, neither file should have DoM */
mdd_obj_dev_name(fst_o),
PFID(mdd_object_fid(fst_o)), rc);
GOTO(stop, rc);
- } else if (domsize_vlt > 0) {
- /* Migration with the same DOM component size, no need to
- * truncate local data, it is still being used */
- dom_o = NULL;
}
/* swapping 2 non existant layouts is a success */
stop:
rc = mdd_trans_stop(env, mdd, rc, handle);
- /* Truncate local DOM data if all went well */
+ /* Truncate local DOM data if all went well, except migration case
+ * with the same DOM component size. In that case a local data is
+ * still in use and shouldn't be deleted.
+ */
if (!rc && dom_o)
- mdd_dom_data_truncate(env, mdd, dom_o);
+ mdd_dom_fixup(env, mdd, dom_o, vlt_o);
lu_buf_free(fst_buf);
lu_buf_free(snd_buf);
GOTO(out_stop, rc);
}
- tgt_vbr_obj_set(env, dob);
+ tgt_vbr_obj_data_set(env, dob, true);
rc = dt_trans_start(env, dt, th);
if (rc)
GOTO(out_stop, rc);
if (rc)
GOTO(stop, rc);
- tgt_vbr_obj_set(env, dob);
+ tgt_vbr_obj_data_set(env, dob, true);
rc = dt_trans_start(env, dt, th);
if (rc)
GOTO(stop, rc);
if (rc)
GOTO(stop, rc);
- tgt_vbr_obj_set(env, dob);
+ tgt_vbr_obj_data_set(env, dob, true);
rc = dt_trans_start(env, dt, th);
if (rc)
GOTO(stop, rc);
GOTO(out, rc = -EBADF);
/* Get version first */
- version = dt_version_get(tsi->tsi_env, mdt_obj2dt(mo));
+ version = dt_data_version_get(tsi->tsi_env, mdt_obj2dt(mo));
if (version && version != -EOPNOTSUPP) {
repbody->mbo_valid |= OBD_MD_FLDATAVERSION;
/* re-use mbo_ioepoch to transfer version */
vbuf.lb_buf = NULL;
vbuf.lb_len = sizeof(dt_obj_version_t);
return dt_declare_xattr_set(env, o, &vbuf, xname, 0, th);
-
}
EXPORT_SYMBOL(dt_declare_version_set);
LASSERT(o);
vbuf.lb_buf = &version;
vbuf.lb_len = sizeof(version);
-
rc = dt_xattr_set(env, o, &vbuf, xname, 0, th);
if (rc < 0)
CDEBUG(D_INODE, "Can't set version, rc %d\n", rc);
CDEBUG(D_INODE, "Can't get version, rc %d\n", rc);
version = 0;
}
+
return version;
}
EXPORT_SYMBOL(dt_version_get);
+int dt_declare_data_version_set(const struct lu_env *env, struct dt_object *o,
+ struct thandle *th)
+{
+ struct lu_buf vbuf;
+
+ vbuf.lb_buf = NULL;
+ vbuf.lb_len = sizeof(dt_obj_version_t);
+
+ return dt_declare_xattr_set(env, o, &vbuf, XATTR_NAME_DATAVER, 0, th);
+}
+EXPORT_SYMBOL(dt_declare_data_version_set);
+
+void dt_data_version_set(const struct lu_env *env, struct dt_object *o,
+ dt_obj_version_t version, struct thandle *th)
+{
+ struct lu_buf vbuf;
+
+ CDEBUG(D_INODE, DFID": set new data version -> %llu\n",
+ PFID(lu_object_fid(&o->do_lu)), version);
+
+ /* version should never be set to zero */
+ LASSERT(version);
+ vbuf.lb_buf = &version;
+ vbuf.lb_len = sizeof(version);
+ dt_xattr_set(env, o, &vbuf, XATTR_NAME_DATAVER, 0, th);
+}
+EXPORT_SYMBOL(dt_data_version_set);
+
+int dt_declare_data_version_del(const struct lu_env *env, struct dt_object *o,
+ struct thandle *th)
+{
+ return dt_declare_xattr_del(env, o, XATTR_NAME_DATAVER, th);
+}
+EXPORT_SYMBOL(dt_declare_data_version_del);
+
+void dt_data_version_del(const struct lu_env *env, struct dt_object *o,
+ struct thandle *th)
+{
+ /* file doesn't need explicit data version anymore */
+ CDEBUG(D_INODE, DFID": remove explicit data version\n",
+ PFID(lu_object_fid(&o->do_lu)));
+ dt_xattr_del(env, o, XATTR_NAME_DATAVER, th);
+}
+EXPORT_SYMBOL(dt_data_version_del);
+
+/* Initialize explicit data version, e.g. for DoM files.
+ * It uses inode version as initial value.
+ */
+dt_obj_version_t dt_data_version_init(const struct lu_env *env,
+ struct dt_object *o)
+{
+ struct dt_device *dt = lu2dt_dev(o->do_lu.lo_dev);
+ dt_obj_version_t dv;
+ struct thandle *th;
+ int rc;
+
+ ENTRY;
+
+ dv = dt_version_get(env, o);
+ if (!dv)
+ RETURN(1);
+
+ th = dt_trans_create(env, dt);
+ if (IS_ERR(th))
+ GOTO(out, rc = PTR_ERR(th));
+
+ rc = dt_declare_data_version_set(env, o, th);
+ if (rc)
+ GOTO(stop, rc);
+
+ rc = dt_trans_start_local(env, dt, th);
+ if (rc)
+ GOTO(stop, rc);
+
+ dt_data_version_set(env, o, dv, th);
+stop:
+ dt_trans_stop(env, dt, th);
+out:
+ /* Ignore failure but report the error */
+ if (rc)
+ CDEBUG(D_INODE, "can't init data version for "DFID": rc = %d\n",
+ PFID(lu_object_fid(&o->do_lu)), rc);
+
+ RETURN(dv);
+}
+
+dt_obj_version_t dt_data_version_get(const struct lu_env *env,
+ struct dt_object *o)
+{
+ struct lu_buf vbuf;
+ dt_obj_version_t version;
+ int rc;
+
+ vbuf.lb_buf = &version;
+ vbuf.lb_len = sizeof(version);
+ rc = dt_xattr_get(env, o, &vbuf, XATTR_NAME_DATAVER);
+
+ CDEBUG(D_INODE, DFID": get data version %llu: rc = %d\n",
+ PFID(lu_object_fid(&o->do_lu)), version, rc);
+
+ if (rc == sizeof(version))
+ return version;
+
+ /* data version EA wasn't set yet on the object, initialize it now */
+ if (rc == -ENODATA)
+ return dt_data_version_init(env, o);
+
+ CDEBUG(D_INODE, "Can't get data version: rc = %d\n", rc);
+
+ return 0;
+}
+EXPORT_SYMBOL(dt_data_version_get);
+
/* list of all supported index types */
/* directories */
/** VBR: set new versions */
if (th->th_result == 0 && obj != NULL) {
struct dt_object *dto = dt_object_locate(obj, th->th_dev);
+
dt_version_set(env, dto, tti->tti_transno, th);
+ if (unlikely(tsi->tsi_dv_update))
+ dt_data_version_set(env, dto, tti->tti_transno, th);
}
/* filling reply data */
!lu_object_remote(&tsi->tsi_vbr_obj->do_lu)) {
dto = dt_object_locate(tsi->tsi_vbr_obj, th->th_dev);
rc = dt_declare_version_set(env, dto, th);
+ if (!rc && tsi->tsi_dv_update)
+ rc = dt_declare_data_version_set(env, dto, th);
}
return rc;
}
run_test 1e "Archive, Release and Restore SEL file"
+test_1f() {
+ (( $MDS1_VERSION >= $(version_code 2.15.55.203) )) ||
+ skip "need MDS version at least 2.15.55.203"
+ local dom=$DIR/$tdir/$tfile
+
+ mkdir_on_mdt0 $DIR/$tdir
+ $LFS setstripe -E 512K -L mdt -E -1 -c 2 $DIR/$tdir ||
+ error "failed to set default stripe"
+
+ test_1bde_base $dom
+
+ [[ $($LFS getstripe --component-start=0 -L $dom) == 'mdt' ]] ||
+ error "MDT stripe isn't set"
+
+ # check that metadata change doesn't prevent second release
+ chmod 600 $dom || error "chmod failed"
+
+ echo "release again $dom"
+ $LFS hsm_release $dom || error "second release failed"
+ $LFS hsm_state $dom
+ echo "verify released state: "
+ check_hsm_flags $dom "0x0000000d" && echo "pass"
+}
+run_test 1f "DoM file release after restore"
+
test_2() {
local f=$DIR/$tdir/$tfile
}
run_test 270i "DoM: setting invalid DoM striping should fail"
+test_270j() {
+ (( $MDS1_VERSION >= $(version_code 2.15.55.203) )) ||
+ skip "Need MDS version at least 2.15.55.203"
+
+ local dom=$DIR/$tdir/$tfile
+ local odv
+ local ndv
+
+ mkdir -p $DIR/$tdir
+
+ $LFS setstripe -E 1M -L mdt -E -1 -c1 $dom
+
+ odv=$($LFS data_version $dom)
+ chmod 666 $dom
+ mv $dom ${dom}_moved
+ link ${dom}_moved $dom
+ setfattr -n user.attrx -v "some_attr" $dom
+ ndv=$($LFS data_version $dom)
+ (( $ndv == $odv )) ||
+ error "data version was changed by metadata operations"
+
+ dd if=/dev/urandom of=$dom bs=1M count=1 ||
+ error "failed to write data into $dom"
+ cancel_lru_locks mdc
+ ndv=$($LFS data_version $dom)
+ (( $ndv != $odv )) ||
+ error "data version wasn't changed on write"
+
+ odv=$ndv
+ $TRUNCATE $dom 1000 || error "failed to truncate $dom"
+ ndv=$($LFS data_version $dom)
+ (( $ndv != $odv )) ||
+ error "data version wasn't changed on truncate down"
+
+ odv=$ndv
+ $TRUNCATE $dom 25000
+ ndv=$($LFS data_version $dom)
+ (( $ndv != $odv )) ||
+ error "data version wasn't changed on truncate up"
+
+ # check also fallocate for ldiskfs
+ if [[ "$mds1_FSTYPE" == ldiskfs ]]; then
+ odv=$ndv
+ fallocate -l 1048576 $dom
+ ndv=$($LFS data_version $dom)
+ (( $ndv != $odv )) ||
+ error "data version wasn't changed on fallocate"
+
+ odv=$ndv
+ fallocate -p --offset 4096 -l 4096 $dom
+ ndv=$($LFS data_version $dom)
+ (( $ndv != $odv )) ||
+ error "data version wasn't changed on fallocate punch"
+ fi
+}
+run_test 270j "DoM migration: DOM file to the OST-striped file (plain)"
+
test_271a() {
[ $MDS1_VERSION -lt $(version_code 2.10.55) ] &&
skip "Need MDS version at least 2.10.55"
$LFS migrate -c2 $dom ||
error "failed to migrate to the new composite layout"
- [ $($LFS getstripe -L $dom) != 'mdt' ] ||
+ [[ $($LFS getstripe --component-start=0 -L $dom) != 'mdt' ]] ||
error "MDT stripe was not removed"
+ ! getfattr -n trusted.dataver $dom &> /dev/null ||
+ error "$dir1 shouldn't have DATAVER EA"
cancel_lru_locks mdc
local new_md5=$(md5sum $dom)
$LFS migrate -E 2M -c1 -E -1 -c2 $dom ||
error "failed to migrate to the new composite layout"
- [ $($LFS getstripe -L $dom) == 'mdt' ] &&
+ [[ $($LFS getstripe --component-start=0 -L $dom) != 'mdt' ]] ||
error "MDT stripe was not removed"
cancel_lru_locks mdc