Whamcloud - gitweb
LU-14642 flr: allow layout version update from client/MDS 43/45443/21
authorBobi Jam <bobijam@whamcloud.com>
Mon, 25 Oct 2021 08:45:29 +0000 (16:45 +0800)
committerOleg Drokin <green@whamcloud.com>
Sat, 17 Sep 2022 06:23:05 +0000 (06:23 +0000)
Client write request always carries its layout version so
that OFD can reject the request if the carried layout version
is a stale one.

This patch makes OFD allow layout version change request from
client as well as MDS. And during resync write, all OST objects
will get layout version updated.

Signed-off-by: Bobi Jam <bobijam@whamcloud.com>
Change-Id: I655044f69a4509a2b0cfe99f86de2ce4ee846979
Reviewed-on: https://review.whamcloud.com/45443
Reviewed-by: Andreas Dilger <adilger@whamcloud.com>
Tested-by: jenkins <devops@whamcloud.com>
Tested-by: Maloo <maloo@whamcloud.com>
Reviewed-by: Alex Zhuravlev <bzzz@whamcloud.com>
Reviewed-by: Oleg Drokin <green@whamcloud.com>
12 files changed:
lustre/include/obd_support.h
lustre/include/uapi/linux/lustre/lustre_user.h
lustre/llite/file.c
lustre/lod/lod_internal.h
lustre/lod/lod_object.c
lustre/lod/lod_qos.c
lustre/mdd/mdd_object.c
lustre/ofd/ofd_internal.h
lustre/ofd/ofd_io.c
lustre/ofd/ofd_objects.c
lustre/osp/osp_sync.c
lustre/tests/sanity-flr.sh

index 41cf729..8f5efb0 100644 (file)
@@ -691,7 +691,7 @@ extern char obd_jobid_var[];
 
 /* FLR */
 #define OBD_FAIL_FLR_LV_DELAY                  0x1A01
-#define OBD_FAIL_FLR_LV_INC                    0x1A02
+#define OBD_FAIL_FLR_LV_INC                    0x1A02 /* unused since 2.15 */
 #define OBD_FAIL_FLR_RANDOM_PICK_MIRROR        0x1A03
 
 /* DT */
index edeff5a..341176f 100644 (file)
@@ -889,10 +889,6 @@ enum lov_comp_md_entry_flags {
 #define LCME_TEMPLATE_FLAGS    (LCME_FL_PREF_RW | LCME_FL_NOSYNC | \
                                 LCME_FL_EXTENSION)
 
-/* the highest bit in obdo::o_layout_version is used to mark if the file is
- * being resynced. */
-#define LU_LAYOUT_RESYNC       LCME_FL_NEG
-
 /* lcme_id can be specified as certain flags, and the the first
  * bit of lcme_id is used to indicate that the ID is representing
  * certain LCME_FL_* but not a real ID. Which implies we can have
@@ -904,6 +900,16 @@ enum lcme_id {
        LCME_ID_NOT_ID  = LCME_FL_NEG
 };
 
+/* layout version equals to lcme_id, except some bits have special meanings */
+enum layout_version_flags {
+       /* layout version reaches the high water mark to be increased to
+        * circularly reuse the smallest value
+        */
+       LU_LAYOUT_HIGEN         = 0x40000000,
+       /* the highest bit is used to mark if the file is being resynced */
+       LU_LAYOUT_RESYNC        = 0x80000000,
+};
+
 #define LCME_ID_MASK   LCME_ID_MAX
 
 struct lov_comp_md_entry_v1 {
index 240add5..ed628bd 100644 (file)
@@ -3656,6 +3656,7 @@ static long ll_file_unlock_lease(struct file *file, struct ll_ioc_lease *ioc,
        bool lease_broken = false;
        fmode_t fmode = 0;
        enum mds_op_bias bias = 0;
+       int fdv;
        struct file *layout_file = NULL;
        void *data = NULL;
        size_t data_size = 0;
@@ -3692,16 +3693,15 @@ static long ll_file_unlock_lease(struct file *file, struct ll_ioc_lease *ioc,
                bias = MDS_CLOSE_RESYNC_DONE;
                break;
        case LL_LEASE_LAYOUT_MERGE: {
-               int fd;
 
                if (ioc->lil_count != 1)
                        GOTO(out_lease_close, rc = -EINVAL);
 
                arg += sizeof(*ioc);
-               if (copy_from_user(&fd, (void __user *)arg, sizeof(__u32)))
+               if (copy_from_user(&fdv, (void __user *)arg, sizeof(__u32)))
                        GOTO(out_lease_close, rc = -EFAULT);
 
-               layout_file = fget(fd);
+               layout_file = fget(fdv);
                if (!layout_file)
                        GOTO(out_lease_close, rc = -EBADF);
 
@@ -3714,7 +3714,6 @@ static long ll_file_unlock_lease(struct file *file, struct ll_ioc_lease *ioc,
                break;
        }
        case LL_LEASE_LAYOUT_SPLIT: {
-               int fdv;
                int mirror_id;
 
                if (ioc->lil_count != 2)
@@ -3785,28 +3784,23 @@ out_lease_close:
        EXIT;
 
 out:
-       switch (ioc->lil_flags) {
-       case LL_LEASE_RESYNC_DONE:
-               if (data)
-                       OBD_FREE(data, data_size);
-               break;
-       case LL_LEASE_LAYOUT_MERGE:
-       case LL_LEASE_LAYOUT_SPLIT:
-               if (layout_file)
-                       fput(layout_file);
+       if (ioc->lil_flags == LL_LEASE_RESYNC_DONE && data)
+               OBD_FREE(data, data_size);
 
-               ll_layout_refresh(inode, &fd->fd_layout_version);
-               break;
-       case LL_LEASE_PCC_ATTACH:
+       if (layout_file)
+               fput(layout_file);
+
+       if (ioc->lil_flags == LL_LEASE_PCC_ATTACH) {
                if (!rc)
                        rc = rc2;
                rc = pcc_readwrite_attach_fini(file, inode,
                                               param.pa_layout_gen,
                                               lease_broken, rc,
                                               attached);
-               break;
        }
 
+       ll_layout_refresh(inode, &fd->fd_layout_version);
+
        if (!rc)
                rc = ll_lease_type_from_fmode(fmode);
        RETURN(rc);
index 78ec188..43a1c09 100644 (file)
@@ -365,8 +365,10 @@ static inline void lod_obj_inc_layout_gen(struct lod_object *lo)
        lo->ldo_layout_gen++;
        lo->ldo_layout_gen |= preserve;
        /* Zero is not a valid generation */
-       if (unlikely((lo->ldo_layout_gen & LCME_ID_MASK) == 0))
+       if (unlikely((lo->ldo_layout_gen & LCME_ID_MASK) == 0)) {
                lo->ldo_layout_gen++;
+               lo->ldo_layout_gen &= ~LU_LAYOUT_RESYNC;
+       }
 }
 
 struct lod_it {
index 9e7073b..a53ca44 100644 (file)
@@ -1224,60 +1224,6 @@ unlock:
        RETURN(rc);
 }
 
-static bool lod_obj_attr_set_comp_skip_cb(const struct lu_env *env,
-               struct lod_object *lo, int comp_idx,
-               struct lod_obj_stripe_cb_data *data)
-{
-       struct lod_layout_component *lod_comp = &lo->ldo_comp_entries[comp_idx];
-       bool skipped = false;
-
-       if (!(data->locd_attr->la_valid & LA_LAYOUT_VERSION))
-               return skipped;
-
-       switch (lo->ldo_flr_state) {
-       case LCM_FL_WRITE_PENDING: {
-               int i;
-
-               /* skip stale components */
-               if (lod_comp->llc_flags & LCME_FL_STALE) {
-                       skipped = true;
-                       break;
-               }
-
-               /* skip valid and overlapping components, therefore any
-                * attempts to write overlapped components will never succeed
-                * because client will get EINPROGRESS. */
-               for (i = 0; i < lo->ldo_comp_cnt; i++) {
-                       if (i == comp_idx)
-                               continue;
-
-                       if (lo->ldo_comp_entries[i].llc_flags & LCME_FL_STALE)
-                               continue;
-
-                       if (lu_extent_is_overlapped(&lod_comp->llc_extent,
-                                       &lo->ldo_comp_entries[i].llc_extent)) {
-                               skipped = true;
-                               break;
-                       }
-               }
-               break;
-       }
-       case LCM_FL_RDONLY:
-       case LCM_FL_SYNC_PENDING:
-               break;
-       default:
-               LASSERTF(0, "impossible: %d\n", lo->ldo_flr_state);
-               break;
-       }
-
-       CDEBUG(D_LAYOUT, DFID": %s to set component %x to version: %u\n",
-              PFID(lu_object_fid(&lo->ldo_obj.do_lu)),
-              skipped ? "skipped" : "chose", lod_comp->llc_id,
-              data->locd_attr->la_layout_version);
-
-       return skipped;
-}
-
 static inline int
 lod_obj_stripe_attr_set_cb(const struct lu_env *env, struct lod_object *lo,
                           struct dt_object *dt, struct thandle *th,
@@ -1473,7 +1419,6 @@ static int lod_attr_set(const struct lu_env *env,
 
                data.locd_attr = attr;
                data.locd_declare = false;
-               data.locd_comp_skip_cb = lod_obj_attr_set_comp_skip_cb;
                data.locd_stripe_cb = lod_obj_stripe_attr_set_cb;
                rc = lod_obj_for_each_stripe(env, lo, th, &data);
        }
@@ -3522,7 +3467,6 @@ static int lod_declare_layout_split(const struct lu_env *env,
        /* fix on-disk layout gen */
        lcm->lcm_layout_gen = cpu_to_le32(lo->ldo_layout_gen);
 
-
        /* transfer layout version to OST objects. */
        if (lo->ldo_mirror_count > 1) {
                struct lod_obj_stripe_cb_data data = { {0} };
@@ -8001,19 +7945,13 @@ static int lod_declare_update_rdonly(const struct lu_env *env,
         * This way it can make sure that the layout version is
         * monotonously increased in this writing era. */
        lod_obj_inc_layout_gen(lo);
-       if (lo->ldo_layout_gen > (LCME_ID_MAX >> 1)) {
-               __u32 layout_version;
-
-               get_random_bytes(&layout_version, sizeof(layout_version));
-               lo->ldo_layout_gen = layout_version & 0xffff;
-       }
 
        rc = lod_declare_instantiate_components(env, lo, th, 0);
        if (rc)
                GOTO(out, rc);
 
        layout_attr->la_valid = LA_LAYOUT_VERSION;
-       layout_attr->la_layout_version = 0; /* set current version */
+       layout_attr->la_layout_version = 0;
        if (mlc->mlc_opc == MD_LAYOUT_RESYNC)
                layout_attr->la_layout_version = LU_LAYOUT_RESYNC;
        rc = lod_declare_attr_set(env, &lo->ldo_obj, layout_attr, th);
@@ -8163,20 +8101,20 @@ static int lod_declare_update_write_pending(const struct lu_env *env,
        if (rc)
                GOTO(out, rc);
 
+       lod_obj_inc_layout_gen(lo);
+
        /* 3. transfer layout version to OST objects.
         * transfer new layout version to OST objects so that stale writes
         * can be denied. It also ends an era of writing by setting
         * LU_LAYOUT_RESYNC. Normal client can never use this bit to
         * send write RPC; only resync RPCs could do it. */
        layout_attr->la_valid = LA_LAYOUT_VERSION;
-       layout_attr->la_layout_version = 0; /* set current version */
+       layout_attr->la_layout_version = 0;
        if (mlc->mlc_opc == MD_LAYOUT_RESYNC)
                layout_attr->la_layout_version = LU_LAYOUT_RESYNC;
        rc = lod_declare_attr_set(env, &lo->ldo_obj, layout_attr, th);
        if (rc)
                GOTO(out, rc);
-
-       lod_obj_inc_layout_gen(lo);
 out:
        if (rc)
                lod_striping_free(env, lo);
@@ -8262,7 +8200,7 @@ static int lod_declare_update_sync_pending(const struct lu_env *env,
        lod_obj_inc_layout_gen(lo);
 
        layout_attr->la_valid = LA_LAYOUT_VERSION;
-       layout_attr->la_layout_version = 0; /* set current version */
+       layout_attr->la_layout_version = 0;
        rc = lod_declare_attr_set(env, &lo->ldo_obj, layout_attr, th);
        if (rc)
                GOTO(out, rc);
index 54e2e08..6920974 100644 (file)
@@ -2763,6 +2763,7 @@ put_ldts:
                        }
                        lod_comp->llc_stripe_count = 0;
                } else {
+                       lod_comp->llc_layout_gen = 0;
                        lod_comp->llc_stripe = stripe;
                        lod_comp->llc_ost_indices = ost_indices;
                        lod_comp->llc_stripes_allocated = stripe_len;
index b58559a..033b95a 100644 (file)
@@ -1651,7 +1651,7 @@ static int mdd_xattr_merge(const struct lu_env *env, struct md_object *md_obj,
                GOTO(out, rc);
 
        rc = mdo_xattr_del(env, vic, XATTR_NAME_LOV, handle);
-       if (rc) /* wtf? */
+       if (rc)
                GOTO(out_restore, rc);
 
        (void)mdd_changelog_data_store(env, mdd, CL_LAYOUT, 0, obj, handle,
index f3f4de7..dc476cb 100644 (file)
@@ -501,4 +501,23 @@ static inline int ofd_validate_seq(struct obd_export *exp, __u64 seq)
        return 0;
 }
 
+/**
+ * whether the requestion IO contains older layout version than that on the
+ * disk.
+ */
+static inline bool ofd_layout_version_less(__u32 req_version,
+                                          __u32 ondisk_version)
+{
+       __u32 req = req_version & ~LU_LAYOUT_RESYNC;
+       __u32 ondisk = ondisk_version & ~LU_LAYOUT_RESYNC;
+
+       /**
+        * request layout version could be circularly increased to the samllest
+        * value, in that case @req < @ondisk but @req does not have the high
+        * end bit set while @ondisk does.
+        */
+       return (req < ondisk) &&
+               ((req & LU_LAYOUT_HIGEN) == (ondisk & LU_LAYOUT_HIGEN));
+}
+
 #endif /* _OFD_INTERNAL_H */
index a620aa1..1ff552e 100644 (file)
@@ -439,7 +439,6 @@ int ofd_verify_ff(const struct lu_env *env, struct ofd_object *fo,
 int ofd_verify_layout_version(const struct lu_env *env,
                              struct ofd_object *fo, const struct obdo *oa)
 {
-       __u32 layout_version;
        int rc;
        ENTRY;
 
@@ -449,34 +448,24 @@ int ofd_verify_layout_version(const struct lu_env *env,
        rc = ofd_object_ff_load(env, fo);
        if (rc < 0) {
                if (rc == -ENODATA)
-                       rc = -EINPROGRESS;
+                       rc = 0;
                GOTO(out, rc);
        }
 
-       layout_version = fo->ofo_ff.ff_layout_version;
-       if (oa->o_layout_version >= layout_version &&
-           oa->o_layout_version <= layout_version + fo->ofo_ff.ff_range)
-               GOTO(out, rc = 0);
-
-       /* normal traffic, decide if to return ESTALE or EINPROGRESS */
-       layout_version &= ~LU_LAYOUT_RESYNC;
-
-       /* this update is not legitimate */
-       if ((oa->o_layout_version & ~LU_LAYOUT_RESYNC) <= layout_version)
+       /**
+        * this update is not legitimate, whose layout version is older than
+        * that on the disk.
+        */
+       if (ofd_layout_version_less(oa->o_layout_version,
+                                   fo->ofo_ff.ff_layout_version))
                GOTO(out, rc = -ESTALE);
 
-       /* layout version may not be transmitted yet */
-       if ((oa->o_layout_version & ~LU_LAYOUT_RESYNC) > layout_version)
-               GOTO(out, rc = -EINPROGRESS);
-
-       EXIT;
-
 out:
-       CDEBUG(D_INODE, DFID " verify layout version: %u vs. %u/%u, rc: %d\n",
+       CDEBUG(D_INODE, DFID " verify layout version: %u vs. %u/%u: rc = %d\n",
               PFID(lu_object_fid(&fo->ofo_obj.do_lu)),
               oa->o_layout_version, fo->ofo_ff.ff_layout_version,
               fo->ofo_ff.ff_range, rc);
-       return rc;
+       RETURN(rc);
 
 }
 
@@ -821,7 +810,6 @@ static int ofd_preprw_write(const struct lu_env *env, struct obd_export *exp,
                rc = ofd_verify_layout_version(env, fo, oa);
                if (rc)
                        GOTO(err, rc);
-               oa->o_valid &= ~OBD_MD_LAYOUT_VERSION;
        }
 
        rc = dt_write_prep(env, ofd_object_child(fo), lnb, *nr_local);
index dd934aa..02d3f45 100644 (file)
@@ -592,15 +592,17 @@ int ofd_object_ff_update(const struct lu_env *env, struct ofd_object *fo,
                       PFID(lu_object_fid(&fo->ofo_obj.do_lu)),
                       ff->ff_layout_version, oa->o_layout_version);
 
-               /* only the MDS has the authority to update layout version */
-               if (!(exp_connect_flags(ofd_info(env)->fti_exp) &
-                     OBD_CONNECT_MDS)) {
-                       CERROR(DFID": update layout version from client\n",
-                              PFID(&fo->ofo_ff.ff_parent));
-
-                       RETURN(-EPERM);
-               }
-
+               /**
+                * resync write from client on non-primary objects and
+                * resync start from MDS on primary objects will contain
+                * LU_LAYOUT_RESYNC flag in the @oa.
+                *
+                * The layout version checking for write/punch from client
+                * happens in ofd_verify_layout_version() before coming to
+                * here, so that resync with smaller layout version client
+                * will be rejected there, the biggest resync version will
+                * be recorded in the OFD objects.
+                */
                if (ff->ff_layout_version & LU_LAYOUT_RESYNC) {
                        /* this opens a new era of writing */
                        ff->ff_layout_version = 0;
@@ -608,7 +610,8 @@ int ofd_object_ff_update(const struct lu_env *env, struct ofd_object *fo,
                }
 
                /* it's not allowed to change it to a smaller value */
-               if (oa->o_layout_version < ff->ff_layout_version)
+               if (ofd_layout_version_less(oa->o_layout_version,
+                                           ff->ff_layout_version))
                        RETURN(-EINVAL);
 
                if (ff->ff_layout_version == 0 ||
@@ -942,8 +945,6 @@ int ofd_object_punch(const struct lu_env *env, struct ofd_object *fo,
                rc = ofd_verify_layout_version(env, fo, oa);
                if (rc)
                        GOTO(unlock, rc);
-
-               oa->o_valid &= ~OBD_MD_LAYOUT_VERSION;
        }
 
        if (oa->o_valid & OBD_MD_FLFLAGS && oa->o_flags & LUSTRE_ENCRYPT_FL) {
index 2f1db6b..8d3ea09 100644 (file)
@@ -767,13 +767,6 @@ static int osp_sync_new_setattr_job(struct osp_device *d,
        else
                body->oa.o_valid |= rec->lsr_valid;
 
-       if (body->oa.o_valid & OBD_MD_LAYOUT_VERSION) {
-               OBD_FAIL_TIMEOUT(OBD_FAIL_FLR_LV_DELAY, cfs_fail_val);
-               if (unlikely(OBD_FAIL_CHECK(OBD_FAIL_FLR_LV_INC)))
-                       body->oa.o_layout_version = LU_LAYOUT_RESYNC |
-                                       (body->oa.o_layout_version + 1);
-       }
-
        osp_sync_send_new_rpc(d, llh, h, req);
        RETURN(0);
 }
index 2cb697b..805e165 100644 (file)
@@ -1434,16 +1434,24 @@ test_35() {
 }
 run_test 35 "allow to write to mirrored files"
 
+get_file_layout_version() {
+       $LFS getstripe $1 | awk '/lcm_layout_gen/{print $2}'
+}
+
+get_ost_layout_version() {
+       $MULTIOP $1 oXc | awk '/ostlayoutversion/{print $2}'
+}
+
 verify_ost_layout_version() {
        local tf=$1
 
        # get file layout version
-       local flv=$($LFS getstripe $tf | awk '/lcm_layout_gen/{print $2}')
+       local flv=$(get_file_layout_version $tf)
 
        # layout version from OST objects
-       local olv=$($MULTIOP $tf oXc | awk '/ostlayoutversion/{print $2}')
+       local olv=$(get_ost_layout_version $tf)
 
-       [ $flv -eq $olv ] || error "layout version mismatch: $flv vs. $olv"
+       (( flv >= olv )) || error "layout version mismatch: $flv vs. $olv"
 }
 
 create_file_36() {
@@ -1458,7 +1466,7 @@ create_file_36() {
        done
 }
 
-test_36() {
+test_36a() {
        local tf=$DIR/$tfile
 
        stack_trap "rm -f $tf $tf-2 $tf-3"
@@ -1493,27 +1501,127 @@ test_36() {
        local st=$(date +%s)
        $MULTIOP $tf-2 oO_WRONLY:w1024Yc || error "write mirrored file error"
 
-       [ $(date +%s) -ge $((st+delay_sec)) ] ||
-               error "write finished before layout version is transmitted"
-
        # verify OST layout version
        verify_ost_layout_version $tf
 
        do_facet $mds_facet $LCTL set_param fail_loc=0
+}
+run_test 36a "write to mirrored files"
 
-       # test case 3
-       mds_idx=mds$(($($LFS getstripe -m $tf-3) + 1))
+test_36b() {
+       local tf=$DIR/$tfile
 
-       #define OBD_FAIL_FLR_LV_INC 0x1A02
-       do_facet $mds_facet $LCTL set_param fail_loc=0x1A02
+       (( OST1_VERSION >= $(version_code 2.15.50) )) ||
+               skip "Need OST version at least 2.15.50"
 
-       # write open file should return error
-       $MULTIOP $tf-3 oO_WRONLY:O_SYNC:w1024c &&
-               error "write a mirrored file succeeded" || true
+       (( OSTCOUNT >= 2 )) || skip "need >= 2 OSTs"
 
-       do_facet $mds_facet $LCTL set_param fail_loc=0
+       # create 2 mirrors using different OSTs
+       $LFS setstripe -N -c1 -i0 --flags=prefer -N -c1 -i1 $tf ||
+               error "create mirrored file"
+
+       # write 1M data to one mirror
+       dd if=/dev/zero of=$tf bs=1M count=1 || error "write file error"
+       sync
+
+       # set prefer mirror to another mirror
+       $LFS setstripe --comp-set -I0x10001 --comp-flags=^prefer $tf ||
+               error "clear prefer mirror error"
+       $LFS setstripe --comp-set -I0x20002 --comp-flags=prefer $tf ||
+               error "set prefer mirror error"
+
+       # the second write should not hung
+       dd if=/dev/zero of=$tf bs=1M count=1 || error "write file error"
+}
+run_test 36b "write should not hung when prefered mirror is stale"
+
+test_36c() {
+       local tf=$DIR/$tfile
+
+       (( OST1_VERSION >= $(version_code 2.15.50) )) ||
+               skip "Need OST version at least 2.15.50"
+
+       (( OSTCOUNT >= 2 )) || skip "need >= 2 OSTs"
+
+       # create 2 mirrors using different OSTs
+       $LFS setstripe -N -c1 -i0 --flags=prefer -N -c1 -i1 $tf ||
+               error "create mirrored file"
+
+       # write it in the background
+       dd if=/dev/zero of=$tf bs=1M count=600 &
+       local pid=$!
+
+       sleep 1
+
+       $LFS setstripe --comp-set -I0x10001 --comp-flags=^prefer $tf ||
+               error "clear prefer mirror error"
+       $LFS setstripe --comp-set -I0x20002 --comp-flags=prefer $tf ||
+               error "set prefer mirror error"
+
+       wait $pid
+}
+run_test 36c "change prefer mirror during write shouldn't hung"
+
+test_36d() {
+       local tf=$DIR/$tfile
+
+       (( OST1_VERSION >= $(version_code 2.15.50) )) ||
+               skip "Need OST version at least 2.15.50"
+
+       echo " ** create $tf"
+       $LFS mirror create -N $tf || error "create $tf failed"
+
+       for i in 1 2; do
+               echo " ** mirror extend $tf ($i/2)"
+               $LFS mirror extend -N $tf || error "mirror extend $tf failed"
+               flv=$(get_file_layout_version $tf)
+               olv=$(get_ost_layout_version $tf)
+               echo "    flv=$flv olv=$olv"
+       done
+
+       for i in 1 2; do
+               echo " ** write $tf ($i/2)"
+               dd if=/dev/zero of=$tf bs=1k count=1 || error "write $tf failed"
+               flv=$(get_file_layout_version $tf)
+               olv=$(get_ost_layout_version $tf)
+               echo "    flv=$flv olv=$olv"
+               (( flv == olv )) ||
+                       error "write update OST layout failed $flv/$olv"
+       done
+
+       echo " ** resync $tf"
+       $LFS mirror resync $tf || error "mirror resync $tf failed"
+       flv=$(get_file_layout_version $tf)
+       olv=$(get_ost_layout_version $tf)
+       echo "    flv=$flv olv=$olv"
+
+       for i in 1 2; do
+               echo " ** truncate $tf ($i/2)"
+               $TRUNCATE $tf $((1024 * 1024)) || error "truncate $tf fails"
+               flv=$(get_file_layout_version $tf)
+               olv=$(get_ost_layout_version $tf)
+               echo "    flv=$flv olv=$olv"
+               (( flv == olv || flv == olv + 1 )) ||
+                       error "truncate update OST layout failed $flv/$olv"
+       done
+
+       echo " ** resync $tf"
+       $LFS mirror resync $tf || error "mirror resync $tf failed"
+       flv=$(get_file_layout_version $tf)
+       olv=$(get_ost_layout_version $tf)
+       echo "    flv=$flv olv=$olv"
+
+       for i in 1 2; do
+               echo " ** write $tf ($i/2)"
+               dd if=/dev/zero of=$tf bs=1k count=1 || error "write $tf failed"
+               flv=$(get_file_layout_version $tf)
+               olv=$(get_ost_layout_version $tf)
+               echo "    flv=$flv olv=$olv"
+               (( flv == olv )) ||
+                       error "write update OST layout failed $flv/$olv"
+       done
 }
-run_test 36 "write to mirrored files"
+run_test 36d "write/punch FLR file update OST layout version"
 
 create_files_37() {
        local tf
@@ -2646,6 +2754,8 @@ run_test 50A "mirror split update layout generation"
 test_50a() {
        $LCTL get_param osc.*.import | grep -q 'connect_flags:.*seek' ||
                skip "OST does not support SEEK_HOLE"
+       [ "$FSTYPE" != "zfs" ] ||
+               skip "lseek for ZFS is not accurate if obj is not committed"
 
        local file=$DIR/$tdir/$tfile
        local offset
@@ -2744,6 +2854,8 @@ run_test 50a "mirror extend/copy preserves sparseness"
 test_50b() {
        $LCTL get_param osc.*.import | grep -q 'connect_flags:.*seek' ||
                skip "OST does not support SEEK_HOLE"
+       [ "$FSTYPE" != "zfs" ] ||
+               skip "lseek for ZFS is not accurate if obj is not committed"
 
        local file=$DIR/$tdir/$tfile
        local offset
@@ -2856,6 +2968,8 @@ test_50d() {
                skip "OST does not support SEEK_HOLE"
        (( $LINUX_VERSION_CODE > $(version_code 3.0.0) )) ||
                skip "client kernel does not support SEEK_HOLE"
+       [ "$FSTYPE" != "zfs" ] ||
+               skip "lseek for ZFS is not accurate if obj is not committed"
 
        local file=$DIR/$tdir/$tfile
        local offset