Whamcloud - gitweb
LU-1866 lfsck: LFSCK for namespace consistency (2)
authorFan Yong <yong.fan@whamcloud.com>
Tue, 15 Jan 2013 09:53:46 +0000 (17:53 +0800)
committerOleg Drokin <oleg.drokin@intel.com>
Wed, 6 Feb 2013 21:07:59 +0000 (16:07 -0500)
It is the second part of LFSCK 1.5 for namespace consistency
check and repair. It mainly implements the LFSCK namespace
component functionality lfsck_operations::lfsck_exec_dir:
for linkEA verification and repair.

The basic functionality is to find out missed linkEA entries
and add them to the object. It is also part of handling IGIF
objects upgrading.

Test-Parameters: envdefinitions=ENABLE_QUOTA=yes testlist=sanity-lfsck

Signed-off-by: Fan Yong <fan.yong@intel.com>
Change-Id: I9df2a1fddfa4b15838ecbdfb6279c240d01d0f95
Reviewed-on: http://review.whamcloud.com/4913
Tested-by: Hudson
Reviewed-by: Alex Zhuravlev <alexey.zhuravlev@intel.com>
Tested-by: Maloo <whamcloud.maloo@gmail.com>
Reviewed-by: Andreas Dilger <andreas.dilger@intel.com>
lustre/include/obd_support.h
lustre/mdd/mdd_dir.c
lustre/mdd/mdd_lfsck.c
lustre/mdd/mdd_lfsck.h
lustre/osd-ldiskfs/osd_handler.c
lustre/tests/sanity-lfsck.sh
lustre/tests/test-framework.sh

index 43a69f9..dc86177 100644 (file)
@@ -465,6 +465,7 @@ int obd_alloc_fail(const void *ptr, const char *name, const char *type,
 
 #define OBD_FAIL_FID_INDIR     0x1501
 #define OBD_FAIL_FID_INLMA     0x1502
+#define OBD_FAIL_FID_IGIF      0x1504
 #define OBD_FAIL_FID_LOOKUP    0x1505
 #define OBD_FAIL_FID_NOLMA     0x1506
 
index 0fa00ba..e60f9e4 100644 (file)
@@ -2748,6 +2748,9 @@ static int mdd_links_rename(const struct lu_env *env,
        int rc = 0;
        ENTRY;
 
+       if (OBD_FAIL_CHECK(OBD_FAIL_FID_IGIF))
+               return 0;
+
        LASSERT(oldpfid != NULL || newpfid != NULL);
 
        if (mdd_obj->mod_flags & DEAD_OBJ)
index 349058b..78228ab 100644 (file)
 #define LFSCK_CHECKPOINT_INTERVAL      60
 #define MDS_DIR_DUMMY_START            0xffffffffffffffffULL
 
+#define LFSCK_NAMEENTRY_DEAD           1 /* The object has been unlinked. */
+#define LFSCK_NAMEENTRY_REMOVED        2 /* The entry has been removed. */
+#define LFSCK_NAMEENTRY_RECREATED      3 /* The entry has been recreated. */
+
 const char lfsck_bookmark_name[] = "lfsck_bookmark";
 const char lfsck_namespace_name[] = "lfsck_namespace";
 
@@ -766,6 +770,87 @@ out:
        return rc;
 }
 
+static int mdd_lfsck_namespace_lookup(const struct lu_env *env,
+                                     struct lfsck_component *com,
+                                     const struct lu_fid *fid,
+                                     __u8 *flags)
+{
+       struct lu_fid *key = &mdd_env_info(env)->mti_fid;
+       int            rc;
+
+       fid_cpu_to_be(key, fid);
+       rc = dt_lookup(env, com->lc_obj, (struct dt_rec *)flags,
+                      (const struct dt_key *)key, BYPASS_CAPA);
+       return rc;
+}
+
+static int mdd_lfsck_namespace_update(const struct lu_env *env,
+                                     struct lfsck_component *com,
+                                     const struct lu_fid *fid,
+                                     __u8 flags, bool force)
+{
+       struct mdd_device *mdd    = mdd_lfsck2mdd(com->lc_lfsck);
+       struct lu_fid     *key    = &mdd_env_info(env)->mti_fid;
+       struct thandle    *handle;
+       struct dt_object *obj     = com->lc_obj;
+       int               rc;
+       bool              exist   = false;
+       __u8              tf;
+       ENTRY;
+
+       rc = mdd_lfsck_namespace_lookup(env, com, fid, &tf);
+       if (rc != 0 && rc != -ENOENT)
+               RETURN(rc);
+
+       if (rc == 0) {
+               if (!force || flags == tf)
+                       RETURN(0);
+
+               exist = true;
+               handle = dt_trans_create(env, mdd->mdd_bottom);
+               if (IS_ERR(handle))
+                       RETURN(PTR_ERR(handle));
+
+               rc = dt_declare_delete(env, obj, (const struct dt_key *)fid,
+                                      handle);
+               if (rc != 0)
+                       GOTO(out, rc);
+       } else {
+               handle = dt_trans_create(env, mdd->mdd_bottom);
+               if (IS_ERR(handle))
+                       RETURN(PTR_ERR(handle));
+       }
+
+       rc = dt_declare_insert(env, obj, (const struct dt_rec *)&flags,
+                              (const struct dt_key *)fid, handle);
+       if (rc != 0)
+               GOTO(out, rc);
+
+       rc = dt_trans_start_local(env, mdd->mdd_bottom, handle);
+       if (rc != 0)
+               GOTO(out, rc);
+
+       fid_cpu_to_be(key, fid);
+       if (exist) {
+               rc = dt_delete(env, obj, (const struct dt_key *)key, handle,
+                              BYPASS_CAPA);
+               if (rc != 0) {
+                       CERROR("%s: fail to insert "DFID", rc = %d\n",
+                              mdd_lfsck2name(com->lc_lfsck), PFID(fid), rc);
+                       GOTO(out, rc);
+               }
+       }
+
+       rc = dt_insert(env, obj, (const struct dt_rec *)&flags,
+                      (const struct dt_key *)key, handle, BYPASS_CAPA, 1);
+
+       GOTO(out, rc);
+
+out:
+       dt_trans_stop(env, mdd->mdd_bottom, handle);
+       return rc;
+}
+
 /* namespace APIs */
 
 static int mdd_lfsck_namespace_reset(const struct lu_env *env,
@@ -961,16 +1046,72 @@ static int mdd_lfsck_namespace_exec_oit(const struct lu_env *env,
        return 0;
 }
 
-/* XXX: to be implemented in other patch.  */
+static int mdd_declare_lfsck_namespace_exec_dir(const struct lu_env *env,
+                                               struct mdd_object *obj,
+                                               struct thandle *handle)
+{
+       int rc;
+
+       /* For destroying all invalid linkEA entries. */
+       rc = mdo_declare_xattr_del(env, obj, XATTR_NAME_LINK, handle);
+       if (rc != 0)
+               return rc;
+
+       /* For insert new linkEA entry. */
+       rc = mdd_declare_links_add(env, obj, handle);
+       return rc;
+}
+
+static int mdd_lfsck_namespace_check_exist(const struct lu_env *env,
+                                          struct md_lfsck *lfsck,
+                                          struct mdd_object *obj,
+                                          const char *name)
+{
+       struct dt_object *dir = lfsck->ml_obj_dir;
+       struct lu_fid    *fid = &mdd_env_info(env)->mti_fid;
+       int               rc;
+       ENTRY;
+
+       if (unlikely(mdd_is_dead_obj(obj)))
+               RETURN(LFSCK_NAMEENTRY_DEAD);
+
+       rc = dt_lookup(env, dir, (struct dt_rec *)fid,
+                      (const struct dt_key *)name, BYPASS_CAPA);
+       if (rc == -ENOENT)
+               RETURN(LFSCK_NAMEENTRY_REMOVED);
+
+       if (rc < 0)
+               RETURN(rc);
+
+       if (!lu_fid_eq(fid, mdo2fid(obj)))
+               RETURN(LFSCK_NAMEENTRY_RECREATED);
+
+       RETURN(0);
+}
+
 static int mdd_lfsck_namespace_exec_dir(const struct lu_env *env,
                                        struct lfsck_component *com,
                                        struct mdd_object *obj,
                                        struct lu_dirent *ent)
 {
+       struct mdd_thread_info     *info     = mdd_env_info(env);
+       struct lu_attr             *la       = &info->mti_la;
+       struct md_lfsck            *lfsck    = com->lc_lfsck;
+       struct lfsck_bookmark      *bk       = &lfsck->ml_bookmark_ram;
        struct lfsck_namespace     *ns       =
                                (struct lfsck_namespace *)com->lc_file_ram;
+       struct mdd_device          *mdd      = mdd_lfsck2mdd(lfsck);
+       struct mdd_link_data        ldata    = { 0 };
+       const struct lu_fid        *pfid     =
+                               lu_object_fid(&lfsck->ml_obj_dir->do_lu);
+       const struct lu_fid        *cfid     = mdo2fid(obj);
        const struct lu_name       *cname;
-       int                         repaired;
+       struct thandle             *handle   = NULL;
+       bool                        repaired = false;
+       bool                        locked   = false;
+       int                         count    = 0;
+       int                         rc;
+       ENTRY;
 
        cname = mdd_name_get_const(env, ent->lde_name, ent->lde_namelen);
        down_write(&com->lc_sem);
@@ -978,17 +1119,184 @@ static int mdd_lfsck_namespace_exec_dir(const struct lu_env *env,
 
        if (ent->lde_attrs & LUDA_UPGRADE) {
                ns->ln_flags |= LF_UPGRADE;
-               repaired = 1;
+               repaired = true;
        } else if (ent->lde_attrs & LUDA_REPAIR) {
                ns->ln_flags |= LF_INCONSISTENT;
-               repaired = 1;
+               repaired = true;
+       }
+
+       if (ent->lde_name[0] == '.' &&
+           (ent->lde_namelen == 1 ||
+            (ent->lde_namelen == 2 && ent->lde_name[1] == '.')))
+               GOTO(out, rc = 0);
+
+       if (!(bk->lb_param & LPF_DRYRUN) &&
+           (com->lc_journal || repaired)) {
+
+again:
+               LASSERT(!locked);
+
+               com->lc_journal = 1;
+               handle = mdd_trans_create(env, mdd);
+               if (IS_ERR(handle))
+                       GOTO(out, rc = PTR_ERR(handle));
+
+               rc = mdd_declare_lfsck_namespace_exec_dir(env, obj, handle);
+               if (rc != 0)
+                       GOTO(stop, rc);
+
+               rc = mdd_trans_start(env, mdd, handle);
+               if (rc != 0)
+                       GOTO(stop, rc);
+
+               mdd_write_lock(env, obj, MOR_TGT_CHILD);
+               locked = true;
+       }
+
+       rc = mdd_lfsck_namespace_check_exist(env, lfsck, obj, ent->lde_name);
+       if (rc != 0)
+               GOTO(stop, rc);
+
+       rc = mdd_links_read(env, obj, &ldata);
+       if (rc == 0) {
+               count = ldata.ml_leh->leh_reccount;
+               rc = mdd_links_find(env, obj, &ldata, cname, pfid);
+               if (rc == 0) {
+                       /* For dir, if there are more than one linkea entries,
+                        * then remove all the other redundant linkea entries.*/
+                       if (unlikely(count > 1 &&
+                                    S_ISDIR(mdd_object_type(obj))))
+                               goto unmatch;
+
+                       goto record;
+               } else {
+
+unmatch:
+                       ns->ln_flags |= LF_INCONSISTENT;
+                       if (bk->lb_param & LPF_DRYRUN) {
+                               repaired = true;
+                               goto record;
+                       }
+
+                       /*For dir, remove the unmatched linkea entry directly.*/
+                       if (S_ISDIR(mdd_object_type(obj))) {
+                               if (!com->lc_journal)
+                                       goto again;
+
+                               rc = mdo_xattr_del(env, obj, XATTR_NAME_LINK,
+                                                  handle, BYPASS_CAPA);
+                               if (rc != 0)
+                                       GOTO(stop, rc);
+
+                               goto nodata;
+                       } else {
+                               goto add;
+                       }
+               }
+       } else if (unlikely(rc == -EINVAL)) {
+               ns->ln_flags |= LF_INCONSISTENT;
+               if (bk->lb_param & LPF_DRYRUN) {
+                       count = 1;
+                       repaired = true;
+                       goto record;
+               }
+
+               if (!com->lc_journal)
+                       goto again;
+
+               /* The magic crashed, we are not sure whether there are more
+                * corrupt data in the linkea, so remove all linkea entries. */
+               rc = mdo_xattr_del(env, obj, XATTR_NAME_LINK, handle,
+                                  BYPASS_CAPA);
+               if (rc != 0)
+                       GOTO(stop, rc);
+
+               goto nodata;
+       } else if (rc == -ENODATA) {
+               ns->ln_flags |= LF_UPGRADE;
+               if (bk->lb_param & LPF_DRYRUN) {
+                       count = 1;
+                       repaired = true;
+                       goto record;
+               }
+
+nodata:
+               rc = mdd_links_new(env, &ldata);
+               if (rc != 0)
+                       GOTO(stop, rc);
+
+add:
+               if (!com->lc_journal)
+                       goto again;
+
+               rc = mdd_links_add_buf(env, &ldata, cname, pfid);
+               if (rc != 0)
+                       GOTO(stop, rc);
+
+               rc = mdd_links_write(env, obj, &ldata, handle);
+               if (rc != 0)
+                       GOTO(stop, rc);
+
+               count = ldata.ml_leh->leh_reccount;
+               repaired = true;
        } else {
-               repaired = 0;
+               GOTO(stop, rc);
+       }
+
+record:
+       LASSERT(count > 0);
+
+       rc = mdd_la_get(env, obj, la, BYPASS_CAPA);
+       if (rc != 0)
+               GOTO(stop, rc);
+
+       if ((count == 1) &&
+           (la->la_nlink == 1 || S_ISDIR(mdd_object_type(obj))))
+               /* Usually, it is for single linked object or dir, do nothing.*/
+               GOTO(stop, rc);
+
+       /* Following modification will be in another transaction.  */
+       if (handle != NULL) {
+               LASSERT(mdd_write_locked(env, obj));
+
+               mdd_write_unlock(env, obj);
+               locked = false;
+
+               mdd_trans_stop(env, mdd, 0, handle);
+               handle = NULL;
        }
 
-       ns->ln_items_repaired += repaired;
+       ns->ln_mlinked_checked++;
+       rc = mdd_lfsck_namespace_update(env, com, cfid,
+                       count != la->la_nlink ? LLF_UNMATCH_NLINKS : 0, false);
+
+       GOTO(out, rc);
+
+stop:
+       if (locked)
+               mdd_write_unlock(env, obj);
+
+       if (handle != NULL)
+               mdd_trans_stop(env, mdd, rc, handle);
+
+out:
+       if (rc < 0) {
+               ns->ln_items_failed++;
+               if (mdd_lfsck_pos_is_zero(&ns->ln_pos_first_inconsistent))
+                       mdd_lfsck_pos_fill(env, lfsck,
+                                          &ns->ln_pos_first_inconsistent,
+                                          true, false);
+               if (!(bk->lb_param & LPF_FAILOUT))
+                       rc = 0;
+       } else {
+               if (repaired)
+                       ns->ln_items_repaired++;
+               else
+                       com->lc_journal = 0;
+               rc = 0;
+       }
        up_write(&com->lc_sem);
-       return 0;
+       return rc;
 }
 
 static int mdd_lfsck_namespace_post(const struct lu_env *env,
index 2410c4f..fc584c7 100644 (file)
@@ -312,4 +312,12 @@ struct md_lfsck {
                                 ml_initialized:1; /* lfsck_setup is called. */
 };
 
+enum lfsck_linkea_flags {
+       /* The linkea entries does not match the object nlinks. */
+       LLF_UNMATCH_NLINKS      = 0x01,
+
+       /* Fail to repair the multiple-linked objects during the double scan. */
+       LLF_REPAIR_FAILED       = 0x02,
+};
+
 #endif /* _MDD_LFSCK_H */
index fcde844..86a3dc9 100644 (file)
@@ -2222,6 +2222,9 @@ int osd_ea_fid_set(struct osd_thread_info *info, struct inode *inode,
        if (OBD_FAIL_CHECK(OBD_FAIL_FID_INLMA))
                return 0;
 
+       if (OBD_FAIL_CHECK(OBD_FAIL_FID_IGIF) && fid_is_client_visible(fid))
+               return 0;
+
        lustre_lma_init(lma, fid);
        lustre_lma_swab(lma);
 
index 6163542..9902d17 100644 (file)
@@ -47,10 +47,16 @@ MOUNT_OPTS_NOSCRUB="-o user_xattr,noscrub"
 lfsck_prep() {
        local ndirs=$1
        local nfiles=$2
+       local igif=$3
 
        echo "formatall"
        formatall > /dev/null
 
+       if [ ! -z $igif ]; then
+               #define OBD_FAIL_FID_IGIF       0x1504
+               do_facet $SINGLEMDS $LCTL set_param fail_loc=0x1504
+       fi
+
        echo "setupall"
        setupall > /dev/null
 
@@ -66,6 +72,11 @@ lfsck_prep() {
                mkdir $DIR/$tdir/e${i}
        done
 
+       if [ ! -z $igif ]; then
+               touch $DIR/$tdir/dummy
+               do_facet $SINGLEMDS $LCTL set_param fail_loc=0
+       fi
+
        echo "prepared."
        cleanup_mount $MOUNT > /dev/null || error "Fail to stop client!"
        echo "stop $SINGLEMDS"
@@ -242,6 +253,63 @@ test_4()
 }
 run_test 4 "FID-in-dirent can be rebuilt after MDT file-level backup/restore"
 
+test_5()
+{
+       lfsck_prep 1 1 1
+       mds_backup_restore 1 || error "(1) Fail to backup/restore!"
+       echo "start $SINGLEMDS with disabling OI scrub"
+       start $SINGLEMDS $MDT_DEVNAME $MOUNT_OPTS_NOSCRUB > /dev/null ||
+               error "(2) Fail to start MDS!"
+
+       local STATUS=$($SHOW_NAMESPACE | awk '/^status/ { print $2 }')
+       [ "$STATUS" == "init" ] ||
+               error "(3) Expect 'init', but got '$STATUS'"
+
+       #define OBD_FAIL_LFSCK_DELAY2           0x1601
+       do_facet $SINGLEMDS $LCTL set_param fail_val=1
+       do_facet $SINGLEMDS $LCTL set_param fail_loc=0x1601
+       $START_NAMESPACE || error "(4) Fail to start LFSCK for namespace!"
+
+       sleep 5
+       STATUS=$($SHOW_NAMESPACE | awk '/^status/ { print $2 }')
+       [ "$STATUS" == "scanning-phase1" ] ||
+               error "(5) Expect 'scanning-phase1', but got '$STATUS'"
+
+       local FLAGS=$($SHOW_NAMESPACE | awk '/^flags/ { print $2 }')
+       [ "$FLAGS" == "inconsistent,upgrade" ] ||
+               error "(6) Expect 'inconsistent,upgrade', but got '$FLAGS'"
+
+       do_facet $SINGLEMDS $LCTL set_param fail_loc=0
+       do_facet $SINGLEMDS $LCTL set_param fail_val=0
+       sleep 3
+       STATUS=$($SHOW_NAMESPACE | awk '/^status/ { print $2 }')
+       [ "$STATUS" == "completed" ] ||
+               error "(7) Expect 'completed', but got '$STATUS'"
+
+       FLAGS=$($SHOW_NAMESPACE | awk '/^flags/ { print $2 }')
+       [ -z "$FLAGS" ] || error "(8) Expect empty flags, but got '$FLAGS'"
+
+       local repaired=$($SHOW_NAMESPACE |
+                        awk '/^updated_phase1/ { print $2 }')
+       [ $repaired -ge 2 ] ||
+               error "(9) Fail to repair crashed linkEA: $repaired"
+
+       mount_client $MOUNT || error "(10) Fail to start client!"
+
+       #define OBD_FAIL_FID_LOOKUP     0x1505
+       do_facet $SINGLEMDS $LCTL set_param fail_loc=0x1505
+       stat $DIR/$tdir/dummy > /dev/null || error "(11) no FID-in-LMA."
+
+       ls $DIR/$tdir/ > /dev/null || error "(12) no FID-in-dirent."
+
+       do_facet $SINGLEMDS $LCTL set_param fail_loc=0
+       local dummyfid=$($LFS path2fid $DIR/$tdir/dummy)
+       local dummyname=$($LFS fid2path $DIR $dummyfid)
+       [ "$dummyname" == "$DIR/$tdir/dummy" ] ||
+               error "(13) Fail to generate linkEA: $dummyfid $dummyname"
+}
+run_test 5 "LFSCK can handle IFIG object upgrading"
+
 test_6a() {
        lfsck_prep 10 10
        echo "start $SINGLEMDS"
index e87097b..26dda7b 100644 (file)
@@ -5880,6 +5880,7 @@ mds_backup_restore() {
        local metadata=${TMP}/backup_restore.tgz
        local opts=${MDS_MOUNT_OPTS}
        local svc=${SINGLEMDS}_svc
+       local igif=$1
 
        if ! ${rcmd} test -b ${devname}; then
                opts=$(csa_add "$opts" -o loop)
@@ -5893,6 +5894,10 @@ mds_backup_restore() {
        ${rcmd} rm -f $metaea $metadata
        # step 3: mount dev
        ${rcmd} mount -t ldiskfs $opts $devname $mntpt || return 1
+       if [ ! -z $igif ]; then
+               # step 3.5: rm .lustre
+               ${rcmd} rm -rf $mntpt/ROOT/.lustre || return 1
+       fi
        # step 4: backup metaea
        echo "backup EA"
        ${rcmd} "cd $mntpt && getfattr -R -d -m '.*' -P . > $metaea && cd -" ||