Whamcloud - gitweb
LU-12137 llite: use ->iterate_shared() for readdir
[fs/lustre-release.git] / lustre / osd-ldiskfs / osd_scrub.c
index cc0d8b7..9166eda 100644 (file)
@@ -6,21 +6,21 @@
  * This program is free software; you can redistribute it and/or modify
  * it under the terms of the GNU General Public License version 2 only,
  * as published by the Free Software Foundation.
-
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
- * GNU General Public License version 2 for more details.  A copy is
- * included in the COPYING file that accompanied this code.
-
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * General Public License version 2 for more details (a copy is included
+ * in the LICENSE file that accompanied this code).
+ *
  * You should have received a copy of the GNU General Public License
- * along with this program; if not, write to the Free Software
- * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ * version 2 along with this program; If not, see
+ * http://www.gnu.org/licenses/gpl-2.0.html
  *
  * GPL HEADER END
  */
 /*
- * Copyright (c) 2012, 2013, Intel Corporation.
+ * Copyright (c) 2012, 2017, Intel Corporation.
  */
 /*
  * lustre/osd-ldiskfs/osd_scrub.c
  * Author: Fan Yong <yong.fan@whamcloud.com>
  */
 
-#define DEBUG_SUBSYSTEM S_MDS
+#define DEBUG_SUBSYSTEM S_LFSCK
 
-#include <lustre/lustre_idl.h>
+#include <linux/kthread.h>
+#include <uapi/linux/lustre/lustre_idl.h>
 #include <lustre_disk.h>
 #include <dt_object.h>
 #include <linux/xattr.h>
+#include <lustre_scrub.h>
+#include <lustre_nodemap.h>
 
 #include "osd_internal.h"
 #include "osd_oi.h"
 #include "osd_scrub.h"
 
-#define HALF_SEC       (HZ >> 1)
-
 #define OSD_OTABLE_MAX_HASH            0x00000000ffffffffULL
 
-#define SCRUB_NEXT_BREAK       1 /* exit current loop and process next group */
-#define SCRUB_NEXT_CONTINUE    2 /* skip current object and process next bit */
-#define SCRUB_NEXT_EXIT        3 /* exit all the loops */
-#define SCRUB_NEXT_WAIT        4 /* wait for free cache slot */
-#define SCRUB_NEXT_CRASH       5 /* simulate system crash during OI scrub */
-#define SCRUB_NEXT_FATAL       6 /* simulate failure during OI scrub */
-#define SCRUB_NEXT_NOSCRUB     7 /* new created object, no scrub on it */
-#define SCRUB_NEXT_NOLMA       8 /* the inode has no FID-in-LMA */
-#define SCRUB_NEXT_OSTOBJ      9 /* for OST-object */
-#define SCRUB_NEXT_OSTOBJ_OLD  10 /* old OST-object, no LMA or no FID-on-OST
-                                   * flags in LMA */
-
-/* misc functions */
-
-static inline struct osd_device *osd_scrub2dev(struct osd_scrub *scrub)
-{
-       return container_of0(scrub, struct osd_device, od_scrub);
-}
-
-static inline struct super_block *osd_scrub2sb(struct osd_scrub *scrub)
-{
-       return osd_sb(osd_scrub2dev(scrub));
-}
-
-static inline int osd_scrub_has_window(struct osd_scrub *scrub,
+static inline int osd_scrub_has_window(struct lustre_scrub *scrub,
                                       struct osd_otable_cache *ooc)
 {
        return scrub->os_pos_current < ooc->ooc_pos_preload + SCRUB_WINDOW_SIZE;
@@ -92,23 +69,25 @@ static int osd_scrub_refresh_mapping(struct osd_thread_info *info,
                                     const struct lu_fid *fid,
                                     const struct osd_inode_id *id,
                                     int ops, bool force,
-                                    enum oi_check_flags flags)
+                                    enum oi_check_flags flags, bool *exist)
 {
        handle_t *th;
        int       rc;
        ENTRY;
 
-       if (dev->od_scrub.os_file.sf_param & SP_DRYRUN && !force)
+       if (dev->od_scrub.os_scrub.os_file.sf_param & SP_DRYRUN && !force)
                RETURN(0);
 
        /* DTO_INDEX_INSERT is enough for other two ops:
         * delete/update, but save stack. */
-       th = ldiskfs_journal_start_sb(osd_sb(dev),
+       th = osd_journal_start_sb(osd_sb(dev), LDISKFS_HT_MISC,
                                osd_dto_credits_noquota[DTO_INDEX_INSERT]);
        if (IS_ERR(th)) {
                rc = PTR_ERR(th);
-               CERROR("%s: fail to start trans for scrub %d: rc = %d\n",
-                      osd_name(dev), ops, rc);
+               CDEBUG(D_LFSCK, "%s: fail to start trans for scrub op %d "
+                      DFID" => %u/%u: rc = %d\n", osd_name(dev), ops,
+                      PFID(fid), id ? id->oii_ino : -1, id ? id->oii_gen : -1,
+                      rc);
                RETURN(rc);
        }
 
@@ -121,7 +100,7 @@ static int osd_scrub_refresh_mapping(struct osd_thread_info *info,
                }
                break;
        case DTO_INDEX_INSERT:
-               rc = osd_oi_insert(info, dev, fid, id, th, flags);
+               rc = osd_oi_insert(info, dev, fid, id, th, flags, exist);
                if (unlikely(rc == -EEXIST)) {
                        rc = 1;
                        /* XXX: There are trouble things when adding OI
@@ -171,197 +150,40 @@ static int osd_scrub_refresh_mapping(struct osd_thread_info *info,
        }
 
        ldiskfs_journal_stop(th);
-       return rc;
-}
-
-/* OI_scrub file ops */
-
-static void osd_scrub_file_to_cpu(struct scrub_file *des,
-                                 struct scrub_file *src)
-{
-       memcpy(des->sf_uuid, src->sf_uuid, 16);
-       des->sf_flags   = le64_to_cpu(src->sf_flags);
-       des->sf_magic   = le32_to_cpu(src->sf_magic);
-       des->sf_status  = le16_to_cpu(src->sf_status);
-       des->sf_param   = le16_to_cpu(src->sf_param);
-       des->sf_time_last_complete      =
-                               le64_to_cpu(src->sf_time_last_complete);
-       des->sf_time_latest_start       =
-                               le64_to_cpu(src->sf_time_latest_start);
-       des->sf_time_last_checkpoint    =
-                               le64_to_cpu(src->sf_time_last_checkpoint);
-       des->sf_pos_latest_start        =
-                               le64_to_cpu(src->sf_pos_latest_start);
-       des->sf_pos_last_checkpoint     =
-                               le64_to_cpu(src->sf_pos_last_checkpoint);
-       des->sf_pos_first_inconsistent  =
-                               le64_to_cpu(src->sf_pos_first_inconsistent);
-       des->sf_items_checked           =
-                               le64_to_cpu(src->sf_items_checked);
-       des->sf_items_updated           =
-                               le64_to_cpu(src->sf_items_updated);
-       des->sf_items_failed            =
-                               le64_to_cpu(src->sf_items_failed);
-       des->sf_items_updated_prior     =
-                               le64_to_cpu(src->sf_items_updated_prior);
-       des->sf_run_time        = le32_to_cpu(src->sf_run_time);
-       des->sf_success_count   = le32_to_cpu(src->sf_success_count);
-       des->sf_oi_count        = le16_to_cpu(src->sf_oi_count);
-       des->sf_internal_flags  = le16_to_cpu(src->sf_internal_flags);
-       memcpy(des->sf_oi_bitmap, src->sf_oi_bitmap, SCRUB_OI_BITMAP_SIZE);
-}
-
-static void osd_scrub_file_to_le(struct scrub_file *des,
-                                struct scrub_file *src)
-{
-       memcpy(des->sf_uuid, src->sf_uuid, 16);
-       des->sf_flags   = cpu_to_le64(src->sf_flags);
-       des->sf_magic   = cpu_to_le32(src->sf_magic);
-       des->sf_status  = cpu_to_le16(src->sf_status);
-       des->sf_param   = cpu_to_le16(src->sf_param);
-       des->sf_time_last_complete      =
-                               cpu_to_le64(src->sf_time_last_complete);
-       des->sf_time_latest_start       =
-                               cpu_to_le64(src->sf_time_latest_start);
-       des->sf_time_last_checkpoint    =
-                               cpu_to_le64(src->sf_time_last_checkpoint);
-       des->sf_pos_latest_start        =
-                               cpu_to_le64(src->sf_pos_latest_start);
-       des->sf_pos_last_checkpoint     =
-                               cpu_to_le64(src->sf_pos_last_checkpoint);
-       des->sf_pos_first_inconsistent  =
-                               cpu_to_le64(src->sf_pos_first_inconsistent);
-       des->sf_items_checked           =
-                               cpu_to_le64(src->sf_items_checked);
-       des->sf_items_updated           =
-                               cpu_to_le64(src->sf_items_updated);
-       des->sf_items_failed            =
-                               cpu_to_le64(src->sf_items_failed);
-       des->sf_items_updated_prior     =
-                               cpu_to_le64(src->sf_items_updated_prior);
-       des->sf_run_time        = cpu_to_le32(src->sf_run_time);
-       des->sf_success_count   = cpu_to_le32(src->sf_success_count);
-       des->sf_oi_count        = cpu_to_le16(src->sf_oi_count);
-       des->sf_internal_flags  = cpu_to_le16(src->sf_internal_flags);
-       memcpy(des->sf_oi_bitmap, src->sf_oi_bitmap, SCRUB_OI_BITMAP_SIZE);
-}
-
-static void osd_scrub_file_init(struct osd_scrub *scrub, __u8 *uuid)
-{
-       struct scrub_file *sf = &scrub->os_file;
-
-       memset(sf, 0, sizeof(*sf));
-       memcpy(sf->sf_uuid, uuid, 16);
-       sf->sf_magic = SCRUB_MAGIC_V1;
-       sf->sf_status = SS_INIT;
-}
-
-void osd_scrub_file_reset(struct osd_scrub *scrub, __u8 *uuid, __u64 flags)
-{
-       struct scrub_file *sf = &scrub->os_file;
-
-       CDEBUG(D_LFSCK, "Reset OI scrub file, flags = "LPX64"\n", flags);
-       memcpy(sf->sf_uuid, uuid, 16);
-       sf->sf_status = SS_INIT;
-       sf->sf_flags |= flags;
-       sf->sf_run_time = 0;
-       sf->sf_time_latest_start = 0;
-       sf->sf_time_last_checkpoint = 0;
-       sf->sf_pos_latest_start = 0;
-       sf->sf_pos_last_checkpoint = 0;
-       sf->sf_pos_first_inconsistent = 0;
-       sf->sf_items_checked = 0;
-       sf->sf_items_updated = 0;
-       sf->sf_items_failed = 0;
-       sf->sf_items_updated_prior = 0;
-       sf->sf_items_noscrub = 0;
-       sf->sf_items_igif = 0;
-}
-
-static int osd_scrub_file_load(struct osd_scrub *scrub)
-{
-       loff_t  pos  = 0;
-       char   *name = LDISKFS_SB(osd_scrub2sb(scrub))->s_es->s_volume_name;
-       int     len  = sizeof(scrub->os_file_disk);
-       int     rc;
-
-       rc = osd_ldiskfs_read(scrub->os_inode, &scrub->os_file_disk, len, &pos);
-       if (rc == len) {
-               struct scrub_file *sf = &scrub->os_file;
-
-               osd_scrub_file_to_cpu(sf, &scrub->os_file_disk);
-               if (sf->sf_magic != SCRUB_MAGIC_V1) {
-                       CWARN("%.16s: invalid scrub magic 0x%x != 0x%x\n,",
-                             name, sf->sf_magic, SCRUB_MAGIC_V1);
-                       /* Process it as new scrub file. */
-                       rc = -ENOENT;
-               } else {
-                       rc = 0;
-               }
-       } else if (rc != 0) {
-               CERROR("%.16s: fail to load scrub file, expected = %d, "
-                      "rc = %d\n", name, len, rc);
-               if (rc > 0)
-                       rc = -EFAULT;
-       } else {
-               /* return -ENOENT for empty scrub file case. */
-               rc = -ENOENT;
-       }
-
-       return rc;
-}
-
-int osd_scrub_file_store(struct osd_scrub *scrub)
-{
-       struct osd_device *dev;
-       handle_t          *jh;
-       loff_t             pos     = 0;
-       int                len     = sizeof(scrub->os_file_disk);
-       int                credits;
-       int                rc;
-
-       dev = container_of0(scrub, struct osd_device, od_scrub);
-       credits = osd_dto_credits_noquota[DTO_WRITE_BASE] +
-                 osd_dto_credits_noquota[DTO_WRITE_BLOCK];
-       jh = osd_journal_start_sb(osd_sb(dev), LDISKFS_HT_MISC, credits);
-       if (IS_ERR(jh)) {
-               rc = PTR_ERR(jh);
-               CERROR("%.16s: fail to start trans for scrub store, rc = %d\n",
-                      LDISKFS_SB(osd_scrub2sb(scrub))->s_es->s_volume_name,rc);
-               return rc;
-       }
+       if (rc < 0)
+               CDEBUG(D_LFSCK, "%s: fail to refresh OI map for scrub op %d "
+                      DFID" => %u/%u: rc = %d\n", osd_name(dev), ops,
+                      PFID(fid), id ? id->oii_ino : -1, id ? id->oii_gen : -1,
+                      rc);
 
-       osd_scrub_file_to_le(&scrub->os_file_disk, &scrub->os_file);
-       rc = osd_ldiskfs_write_record(scrub->os_inode, &scrub->os_file_disk,
-                                     len, 0, &pos, jh);
-       ldiskfs_journal_stop(jh);
-       if (rc != 0)
-               CERROR("%.16s: fail to store scrub file, expected = %d, "
-                      "rc = %d\n",
-                      LDISKFS_SB(osd_scrub2sb(scrub))->s_es->s_volume_name,
-                      len, rc);
-       scrub->os_time_last_checkpoint = cfs_time_current();
-       scrub->os_time_next_checkpoint = scrub->os_time_last_checkpoint +
-                               cfs_time_seconds(SCRUB_CHECKPOINT_INTERVAL);
-       return rc;
+       RETURN(rc);
 }
 
 static int
 osd_scrub_convert_ff(struct osd_thread_info *info, struct osd_device *dev,
                     struct inode *inode, const struct lu_fid *fid)
 {
-       struct filter_fid_old   *ff      = &info->oti_ff;
-       struct dentry           *dentry  = &info->oti_obj_dentry;
-       handle_t                *jh;
-       int                      size    = 0;
-       int                      rc;
-       bool                     removed = false;
-       bool                     reset   = true;
+       struct filter_fid_18_23 *ff = &info->oti_ff_old;
+       struct dentry *dentry = &info->oti_obj_dentry;
+       struct lu_fid *tfid = &info->oti_fid;
+       bool fid_18_23 = false;
+       handle_t *jh;
+       int size = 0;
+       int rc;
        ENTRY;
 
-       if (dev->od_scrub.os_file.sf_param & SP_DRYRUN)
+       if (dev->od_scrub.os_scrub.os_file.sf_param & SP_DRYRUN)
                RETURN(0);
 
+       if (fid_is_idif(fid) && dev->od_index_in_idif == 0) {
+               struct ost_id *oi = &info->oti_ostid;
+
+               fid_to_ostid(fid, oi);
+               ostid_to_fid(tfid, oi, 0);
+       } else {
+               *tfid = *fid;
+       }
+
        /* We want the LMA to fit into the 256-byte OST inode, so operate
         * as following:
         * 1) read old XATTR_NAME_FID and save the parent FID;
@@ -376,9 +198,8 @@ osd_scrub_convert_ff(struct osd_thread_info *info, struct osd_device *dev,
                                osd_dto_credits_noquota[DTO_XATTR_SET] * 3);
        if (IS_ERR(jh)) {
                rc = PTR_ERR(jh);
-               CERROR("%s: fail to start trans for convert ff: "DFID
-                      ": rc = %d\n",
-                      osd_name(dev), PFID(fid), rc);
+               CDEBUG(D_LFSCK, "%s: fail to start trans for convert ff "
+                      DFID": rc = %d\n", osd_name(dev), PFID(tfid), rc);
                RETURN(rc);
        }
 
@@ -386,25 +207,26 @@ osd_scrub_convert_ff(struct osd_thread_info *info, struct osd_device *dev,
        rc = __osd_xattr_get(inode, dentry, XATTR_NAME_FID, ff, sizeof(*ff));
        if (rc == sizeof(*ff)) {
                /* 2) delete the old XATTR_NAME_FID */
-               ll_vfs_dq_init(inode);
-               rc = inode->i_op->removexattr(dentry, XATTR_NAME_FID);
-               if (rc != 0)
+               dquot_initialize(inode);
+               rc = osd_removexattr(dentry, inode, XATTR_NAME_FID);
+               if (rc)
                        GOTO(stop, rc);
 
-               removed = true;
-       } else if (unlikely(rc == -ENODATA)) {
-               reset = false;
-       } else if (rc != sizeof(struct filter_fid)) {
+               fid_18_23 = true;
+       } else if (rc != -ENODATA && rc < (int)sizeof(struct filter_fid_24_29)) {
                GOTO(stop, rc = -EINVAL);
        }
 
        /* 3) make new LMA and add it */
-       rc = osd_ea_fid_set(info, inode, fid, LMAC_FID_ON_OST, 0);
-       if (rc == 0 && reset)
-               size = sizeof(struct filter_fid);
-       else if (rc != 0 && removed)
-               /* If failed, we should try to add the old back. */
-               size = sizeof(struct filter_fid_old);
+       rc = osd_ea_fid_set(info, inode, tfid, LMAC_FID_ON_OST, 0);
+       if (fid_18_23) {
+               if (rc)
+                       /* If failed, we should try to add the old back. */
+                       size = sizeof(*ff);
+               else
+                       /* The new PFID EA will only contains ::ff_parent */
+                       size = sizeof(ff->ff_parent);
+       }
 
        /* 4) generate new XATTR_NAME_FID with the saved parent FID and add it*/
        if (size > 0) {
@@ -420,6 +242,9 @@ osd_scrub_convert_ff(struct osd_thread_info *info, struct osd_device *dev,
 
 stop:
        ldiskfs_journal_stop(jh);
+       if (rc < 0)
+               CDEBUG(D_LFSCK, "%s: fail to convert ff "DFID": rc = %d\n",
+                      osd_name(dev), PFID(tfid), rc);
        return rc;
 }
 
@@ -427,7 +252,7 @@ static int
 osd_scrub_check_update(struct osd_thread_info *info, struct osd_device *dev,
                       struct osd_idmap_cache *oic, int val)
 {
-       struct osd_scrub             *scrub  = &dev->od_scrub;
+       struct lustre_scrub *scrub = &dev->od_scrub.os_scrub;
        struct scrub_file            *sf     = &scrub->os_file;
        struct lu_fid                *fid    = &oic->oic_fid;
        struct osd_inode_id          *lid    = &oic->oic_lid;
@@ -435,9 +260,9 @@ osd_scrub_check_update(struct osd_thread_info *info, struct osd_device *dev,
        struct osd_inconsistent_item *oii    = NULL;
        struct inode                 *inode  = NULL;
        int                           ops    = DTO_INDEX_UPDATE;
-       int                           idx;
        int                           rc;
        bool                          converted = false;
+       bool                          exist     = false;
        ENTRY;
 
        down_write(&scrub->os_rwsem);
@@ -446,8 +271,8 @@ osd_scrub_check_update(struct osd_thread_info *info, struct osd_device *dev,
                GOTO(out, rc = val);
 
        if (scrub->os_in_prior)
-               oii = cfs_list_entry(oic, struct osd_inconsistent_item,
-                                    oii_cache);
+               oii = list_entry(oic, struct osd_inconsistent_item,
+                                oii_cache);
 
        if (lid->oii_ino < sf->sf_pos_latest_start && oii == NULL)
                GOTO(out, rc = 0);
@@ -465,6 +290,10 @@ osd_scrub_check_update(struct osd_thread_info *info, struct osd_device *dev,
                        GOTO(out, rc);
                }
 
+               /* The inode has been reused as EA inode, ignore it. */
+               if (unlikely(osd_is_ea_inode(inode)))
+                       GOTO(out, rc = 0);
+
                sf->sf_flags |= SF_UPGRADE;
                sf->sf_internal_flags &= ~SIF_NO_HANDLE_OLD_FID;
                dev->od_check_ff = 1;
@@ -479,14 +308,19 @@ osd_scrub_check_update(struct osd_thread_info *info, struct osd_device *dev,
            (!scrub->os_convert_igif || OBD_FAIL_CHECK(OBD_FAIL_FID_NOLMA)))
                GOTO(out, rc = 0);
 
-       if ((oii != NULL && oii->oii_insert) || (val == SCRUB_NEXT_NOLMA))
+       if ((oii != NULL && oii->oii_insert) || (val == SCRUB_NEXT_NOLMA)) {
+               ops = DTO_INDEX_INSERT;
+
                goto iget;
+       }
 
        rc = osd_oi_lookup(info, dev, fid, lid2,
                (val == SCRUB_NEXT_OSTOBJ ||
                 val == SCRUB_NEXT_OSTOBJ_OLD) ? OI_KNOWN_ON_OST : 0);
        if (rc != 0) {
-               if (rc != -ENOENT && rc != -ESTALE)
+               if (rc == -ENOENT)
+                       ops = DTO_INDEX_INSERT;
+               else if (rc != -ESTALE)
                        GOTO(out, rc);
 
 iget:
@@ -499,11 +333,15 @@ iget:
                                        rc = 0;
                                GOTO(out, rc);
                        }
+
+                       /* The inode has been reused as EA inode, ignore it. */
+                       if (unlikely(osd_is_ea_inode(inode)))
+                               GOTO(out, rc = 0);
                }
 
-               scrub->os_full_speed = 1;
-               ops = DTO_INDEX_INSERT;
-               idx = osd_oi_fid2idx(dev, fid);
+               if (!scrub->os_partial_scan)
+                       scrub->os_full_speed = 1;
+
                switch (val) {
                case SCRUB_NEXT_NOLMA:
                        sf->sf_flags |= SF_UPGRADE;
@@ -521,9 +359,6 @@ iget:
                case SCRUB_NEXT_OSTOBJ_OLD:
                        break;
                default:
-                       sf->sf_flags |= SF_RECREATED;
-                       if (unlikely(!ldiskfs_test_bit(idx, sf->sf_oi_bitmap)))
-                               ldiskfs_set_bit(idx, sf->sf_oi_bitmap);
                        break;
                }
        } else if (osd_id_eq(lid, lid2)) {
@@ -532,7 +367,9 @@ iget:
 
                GOTO(out, rc = 0);
        } else {
-               scrub->os_full_speed = 1;
+               if (!scrub->os_partial_scan)
+                       scrub->os_full_speed = 1;
+
                sf->sf_flags |= SF_INCONSISTENT;
 
                /* XXX: If the device is restored from file-level backup, then
@@ -552,15 +389,21 @@ iget:
 
        rc = osd_scrub_refresh_mapping(info, dev, fid, lid, ops, false,
                        (val == SCRUB_NEXT_OSTOBJ ||
-                        val == SCRUB_NEXT_OSTOBJ_OLD) ? OI_KNOWN_ON_OST : 0);
+                        val == SCRUB_NEXT_OSTOBJ_OLD) ? OI_KNOWN_ON_OST : 0,
+                       &exist);
        if (rc == 0) {
                if (scrub->os_in_prior)
                        sf->sf_items_updated_prior++;
                else
                        sf->sf_items_updated++;
 
-               /* The target has been changed, need to be re-loaded. */
-               lu_object_purge(info->oti_env, osd2lu_dev(dev), fid);
+               if (ops == DTO_INDEX_INSERT && val == 0 && !exist) {
+                       int idx = osd_oi_fid2idx(dev, fid);
+
+                       sf->sf_flags |= SF_RECREATED;
+                       if (unlikely(!ldiskfs_test_bit(idx, sf->sf_oi_bitmap)))
+                               ldiskfs_set_bit(idx, sf->sf_oi_bitmap);
+               }
        }
 
        GOTO(out, rc);
@@ -578,33 +421,33 @@ out:
        /* There may be conflict unlink during the OI scrub,
         * if happend, then remove the new added OI mapping. */
        if (ops == DTO_INDEX_INSERT && inode != NULL && !IS_ERR(inode) &&
-           unlikely(inode->i_nlink == 0))
+           unlikely(ldiskfs_test_inode_state(inode,
+                                             LDISKFS_STATE_LUSTRE_DESTROY)))
                osd_scrub_refresh_mapping(info, dev, fid, lid,
                                DTO_INDEX_DELETE, false,
                                (val == SCRUB_NEXT_OSTOBJ ||
                                 val == SCRUB_NEXT_OSTOBJ_OLD) ?
-                               OI_KNOWN_ON_OST : 0);
+                               OI_KNOWN_ON_OST : 0, NULL);
        up_write(&scrub->os_rwsem);
 
        if (inode != NULL && !IS_ERR(inode))
                iput(inode);
 
        if (oii != NULL) {
-               LASSERT(!cfs_list_empty(&oii->oii_list));
-
                spin_lock(&scrub->os_lock);
-               cfs_list_del_init(&oii->oii_list);
+               if (likely(!list_empty(&oii->oii_list)))
+                       list_del(&oii->oii_list);
                spin_unlock(&scrub->os_lock);
+
                OBD_FREE_PTR(oii);
        }
+
        RETURN(sf->sf_param & SP_FAILOUT ? rc : 0);
 }
 
-/* OI scrub APIs */
-
-static int osd_scrub_prep(struct osd_device *dev)
+static int osd_scrub_prep(const struct lu_env *env, struct osd_device *dev)
 {
-       struct osd_scrub     *scrub  = &dev->od_scrub;
+       struct lustre_scrub *scrub = &dev->od_scrub.os_scrub;
        struct ptlrpc_thread *thread = &scrub->os_thread;
        struct scrub_file    *sf     = &scrub->os_file;
        __u32                 flags  = scrub->os_start_flags;
@@ -612,41 +455,48 @@ static int osd_scrub_prep(struct osd_device *dev)
        bool                  drop_dryrun = false;
        ENTRY;
 
+       CDEBUG(D_LFSCK, "%s: OI scrub prep, flags = 0x%x\n",
+              osd_scrub2name(scrub), flags);
+
        down_write(&scrub->os_rwsem);
        if (flags & SS_SET_FAILOUT)
                sf->sf_param |= SP_FAILOUT;
-
-       if (flags & SS_CLEAR_FAILOUT)
+       else if (flags & SS_CLEAR_FAILOUT)
                sf->sf_param &= ~SP_FAILOUT;
 
-       if (flags & SS_SET_DRYRUN)
+       if (flags & SS_SET_DRYRUN) {
                sf->sf_param |= SP_DRYRUN;
-
-       if (flags & SS_CLEAR_DRYRUN && sf->sf_param & SP_DRYRUN) {
+       } else if (flags & SS_CLEAR_DRYRUN && sf->sf_param & SP_DRYRUN) {
                sf->sf_param &= ~SP_DRYRUN;
                drop_dryrun = true;
        }
 
        if (flags & SS_RESET)
-               osd_scrub_file_reset(scrub,
-                       LDISKFS_SB(osd_sb(dev))->s_es->s_uuid, 0);
+               scrub_file_reset(scrub, dev->od_uuid, 0);
 
-       if (flags & SS_AUTO) {
+       if (flags & SS_AUTO_FULL) {
                scrub->os_full_speed = 1;
+               scrub->os_partial_scan = 0;
                sf->sf_flags |= SF_AUTO;
-               /* For the case of OI scrub auto triggered, NOT dryrun. */
-               sf->sf_param &= ~SP_FAILOUT;
+       } else if (flags & SS_AUTO_PARTIAL) {
+               scrub->os_full_speed = 0;
+               scrub->os_partial_scan = 1;
+               sf->sf_flags |= SF_AUTO;
+       } else if (sf->sf_flags & (SF_RECREATED | SF_INCONSISTENT |
+                                  SF_UPGRADE)) {
+               scrub->os_full_speed = 1;
+               scrub->os_partial_scan = 0;
        } else {
                scrub->os_full_speed = 0;
+               scrub->os_partial_scan = 0;
        }
 
-       if (sf->sf_flags & (SF_RECREATED | SF_INCONSISTENT | SF_UPGRADE))
-               scrub->os_full_speed = 1;
-
-       scrub->os_in_prior = 0;
        spin_lock(&scrub->os_lock);
+       scrub->os_in_prior = 0;
        scrub->os_waiting = 0;
        scrub->os_paused = 0;
+       scrub->os_in_join = 0;
+       scrub->os_full_scrub = 0;
        spin_unlock(&scrub->os_lock);
        scrub->os_new_checked = 0;
        if (drop_dryrun && sf->sf_pos_first_inconsistent != 0)
@@ -658,9 +508,10 @@ static int osd_scrub_prep(struct osd_device *dev)
 
        scrub->os_pos_current = sf->sf_pos_latest_start;
        sf->sf_status = SS_SCANNING;
-       sf->sf_time_latest_start = cfs_time_current_sec();
+       sf->sf_time_latest_start = ktime_get_real_seconds();
        sf->sf_time_last_checkpoint = sf->sf_time_latest_start;
-       rc = osd_scrub_file_store(scrub);
+       sf->sf_pos_last_checkpoint = sf->sf_pos_latest_start - 1;
+       rc = scrub_file_store(env, scrub);
        if (rc == 0) {
                spin_lock(&scrub->os_lock);
                thread_set_flags(thread, SVC_RUNNING);
@@ -672,34 +523,17 @@ static int osd_scrub_prep(struct osd_device *dev)
        RETURN(rc);
 }
 
-static int osd_scrub_checkpoint(struct osd_scrub *scrub)
-{
-       struct scrub_file *sf = &scrub->os_file;
-       int                rc;
-
-       if (likely(cfs_time_before(cfs_time_current(),
-                                  scrub->os_time_next_checkpoint) ||
-                  scrub->os_new_checked == 0))
-               return 0;
-
-       down_write(&scrub->os_rwsem);
-       sf->sf_items_checked += scrub->os_new_checked;
-       scrub->os_new_checked = 0;
-       sf->sf_pos_last_checkpoint = scrub->os_pos_current;
-       sf->sf_time_last_checkpoint = cfs_time_current_sec();
-       sf->sf_run_time += cfs_duration_sec(cfs_time_current() + HALF_SEC -
-                                           scrub->os_time_last_checkpoint);
-       rc = osd_scrub_file_store(scrub);
-       up_write(&scrub->os_rwsem);
-
-       return rc;
-}
-
-static void osd_scrub_post(struct osd_scrub *scrub, int result)
+static int osd_scrub_post(const struct lu_env *env, struct osd_device *dev,
+                         int result)
 {
+       struct lustre_scrub *scrub = &dev->od_scrub.os_scrub;
        struct scrub_file *sf = &scrub->os_file;
+       int rc;
        ENTRY;
 
+       CDEBUG(D_LFSCK, "%s: OI scrub post with result = %d\n",
+              osd_scrub2name(scrub), result);
+
        down_write(&scrub->os_rwsem);
        spin_lock(&scrub->os_lock);
        thread_set_flags(&scrub->os_thread, SVC_STOPPING);
@@ -709,11 +543,8 @@ static void osd_scrub_post(struct osd_scrub *scrub, int result)
                scrub->os_new_checked = 0;
                sf->sf_pos_last_checkpoint = scrub->os_pos_current;
        }
-       sf->sf_time_last_checkpoint = cfs_time_current_sec();
+       sf->sf_time_last_checkpoint = ktime_get_real_seconds();
        if (result > 0) {
-               struct osd_device *dev =
-                       container_of0(scrub, struct osd_device, od_scrub);
-
                dev->od_igif_inoi = 1;
                dev->od_check_ff = 0;
                sf->sf_status = SS_COMPLETED;
@@ -732,51 +563,53 @@ static void osd_scrub_post(struct osd_scrub *scrub, int result)
        } else {
                sf->sf_status = SS_FAILED;
        }
-       sf->sf_run_time += cfs_duration_sec(cfs_time_current() + HALF_SEC -
-                                           scrub->os_time_last_checkpoint);
-       result = osd_scrub_file_store(scrub);
-       if (result < 0)
-               CERROR("%.16s: fail to osd_scrub_post, rc = %d\n",
-                      LDISKFS_SB(osd_scrub2sb(scrub))->s_es->s_volume_name,
-                      result);
+       sf->sf_run_time += ktime_get_seconds() -
+                          scrub->os_time_last_checkpoint;
+
+       rc = scrub_file_store(env, scrub);
        up_write(&scrub->os_rwsem);
 
-       EXIT;
+       RETURN(rc < 0 ? rc : result);
 }
 
 /* iteration engine */
 
-struct osd_iit_param {
-       struct super_block *sb;
-       struct buffer_head *bitmap;
-       ldiskfs_group_t bg;
-       __u32 gbase;
-       __u32 offset;
-};
-
 typedef int (*osd_iit_next_policy)(struct osd_thread_info *info,
                                   struct osd_device *dev,
                                   struct osd_iit_param *param,
                                   struct osd_idmap_cache **oic,
-                                  int noslot);
+                                  const bool noslot);
 
 typedef int (*osd_iit_exec_policy)(struct osd_thread_info *info,
                                   struct osd_device *dev,
                                   struct osd_iit_param *param,
                                   struct osd_idmap_cache *oic,
-                                  int *noslot, int rc);
+                                  bool *noslot, int rc);
 
-static int osd_iit_next(struct osd_iit_param *param, __u32 *pos)
+static int osd_iit_next(struct osd_iit_param *param, __u64 *pos)
 {
+       __u32 offset;
+
+again:
        param->offset = ldiskfs_find_next_bit(param->bitmap->b_data,
                        LDISKFS_INODES_PER_GROUP(param->sb), param->offset);
        if (param->offset >= LDISKFS_INODES_PER_GROUP(param->sb)) {
                *pos = 1 + (param->bg+1) * LDISKFS_INODES_PER_GROUP(param->sb);
                return SCRUB_NEXT_BREAK;
-       } else {
-               *pos = param->gbase + param->offset;
-               return 0;
        }
+
+       offset = param->offset++;
+       if (unlikely(*pos == param->gbase + offset && *pos != param->start)) {
+               /* We should NOT find the same object more than once. */
+               CERROR("%s: scan the same object multiple times at the pos: "
+                      "group = %u, base = %u, offset = %u, start = %u\n",
+                      param->sb->s_id, (__u32)param->bg, param->gbase,
+                      offset, param->start);
+               goto again;
+       }
+
+       *pos = param->gbase + offset;
+       return 0;
 }
 
 /**
@@ -793,19 +626,20 @@ static int osd_scrub_check_local_fldb(struct osd_thread_info *info,
         *      quite possible for FID-on-MDT. */
        if (dev->od_is_ost)
                return SCRUB_NEXT_OSTOBJ_OLD;
-       else
-               return 0;
+
+       return 0;
 }
 
 static int osd_scrub_get_fid(struct osd_thread_info *info,
                             struct osd_device *dev, struct inode *inode,
                             struct lu_fid *fid, bool scrub)
 {
-       struct lustre_mdt_attrs *lma     = &info->oti_mdt_attrs;
-       int                      rc;
-       bool                     has_lma = false;
+       struct lustre_mdt_attrs *lma = &info->oti_ost_attrs.loa_lma;
+       bool has_lma = false;
+       int rc;
 
-       rc = osd_get_lma(info, inode, &info->oti_obj_dentry, lma);
+       rc = osd_get_lma(info, inode, &info->oti_obj_dentry,
+                        &info->oti_ost_attrs);
        if (rc == 0) {
                has_lma = true;
                if (lma->lma_compat & LMAC_NOT_IN_OI ||
@@ -839,7 +673,7 @@ static int osd_scrub_get_fid(struct osd_thread_info *info,
                rc = osd_get_idif(info, inode, &info->oti_obj_dentry, fid);
                if (rc == 0) {
                        if (scrub)
-                               /* It is old 2.x (x <= 3) or 1.8 OST-object. */
+                               /* It is 2.3 or older OST-object. */
                                rc = SCRUB_NEXT_OSTOBJ_OLD;
                        return rc;
                }
@@ -850,7 +684,7 @@ static int osd_scrub_get_fid(struct osd_thread_info *info,
                                 * to generate its FID, ignore it directly. */
                                rc = SCRUB_NEXT_CONTINUE;
                        else
-                               /* It is 2.4 OST-object. */
+                               /* It is 2.4 or newer OST-object. */
                                rc = SCRUB_NEXT_OSTOBJ_OLD;
                        return rc;
                }
@@ -859,7 +693,7 @@ static int osd_scrub_get_fid(struct osd_thread_info *info,
                        return rc;
 
                if (!has_lma) {
-                       if (dev->od_scrub.os_convert_igif) {
+                       if (dev->od_scrub.os_scrub.os_convert_igif) {
                                lu_igif_build(fid, inode->i_ino,
                                              inode->i_generation);
                                if (scrub)
@@ -892,6 +726,19 @@ static int osd_iit_iget(struct osd_thread_info *info, struct osd_device *dev,
        int           rc;
        ENTRY;
 
+       /* Not handle the backend root object and agent parent object.
+        * They are neither visible to namespace nor have OI mappings. */
+       if (unlikely(pos == osd_sb(dev)->s_root->d_inode->i_ino ||
+                    is_remote_parent_ino(dev, pos)))
+               RETURN(SCRUB_NEXT_CONTINUE);
+
+        /* Skip project quota inode since it is greater than s_first_ino. */
+#ifdef HAVE_PROJECT_QUOTA
+       if (ldiskfs_has_feature_project(sb) &&
+           pos == le32_to_cpu(LDISKFS_SB(sb)->s_es->s_prj_quota_inum))
+               RETURN(SCRUB_NEXT_CONTINUE);
+#endif
+
        osd_id_gen(lid, pos, OSD_OII_NOGEN);
        inode = osd_iget(info, dev, lid);
        if (IS_ERR(inode)) {
@@ -901,11 +748,15 @@ static int osd_iit_iget(struct osd_thread_info *info, struct osd_device *dev,
                if (rc == -ENOENT || rc == -ESTALE)
                        RETURN(SCRUB_NEXT_CONTINUE);
 
-               CERROR("%.16s: fail to read inode, ino# = %u, rc = %d\n",
-                      LDISKFS_SB(sb)->s_es->s_volume_name, pos, rc);
+               CDEBUG(D_LFSCK, "%s: fail to read inode, ino# = %u: "
+                      "rc = %d\n", osd_dev2name(dev), pos, rc);
                RETURN(rc);
        }
 
+       /* It is an EA inode, no OI mapping for it, skip it. */
+       if (osd_is_ea_inode(inode))
+               GOTO(put, rc = SCRUB_NEXT_CONTINUE);
+
        if (scrub &&
            ldiskfs_test_inode_state(inode, LDISKFS_STATE_LUSTRE_NOSCRUB)) {
                /* Only skip it for the first OI scrub accessing. */
@@ -924,22 +775,23 @@ put:
 
 static int osd_scrub_next(struct osd_thread_info *info, struct osd_device *dev,
                          struct osd_iit_param *param,
-                         struct osd_idmap_cache **oic, int noslot)
+                         struct osd_idmap_cache **oic, const bool noslot)
 {
-       struct osd_scrub     *scrub  = &dev->od_scrub;
+       struct lustre_scrub *scrub = &dev->od_scrub.os_scrub;
        struct ptlrpc_thread *thread = &scrub->os_thread;
-       struct lu_fid        *fid;
-       struct osd_inode_id  *lid;
-       int                   rc;
+       struct lu_fid *fid;
+       struct osd_inode_id *lid;
+       int rc;
 
        if (OBD_FAIL_CHECK(OBD_FAIL_OSD_SCRUB_DELAY) && cfs_fail_val > 0) {
                struct l_wait_info lwi;
 
                lwi = LWI_TIMEOUT(cfs_time_seconds(cfs_fail_val), NULL, NULL);
-               l_wait_event(thread->t_ctl_waitq,
-                            !cfs_list_empty(&scrub->os_inconsistent_items) ||
-                            !thread_is_running(thread),
-                            &lwi);
+               if (likely(lwi.lwi_timeout > 0))
+                       l_wait_event(thread->t_ctl_waitq,
+                               !list_empty(&scrub->os_inconsistent_items) ||
+                               !thread_is_running(thread),
+                               &lwi);
        }
 
        if (OBD_FAIL_CHECK(OBD_FAIL_OSD_SCRUB_CRASH)) {
@@ -955,24 +807,31 @@ static int osd_scrub_next(struct osd_thread_info *info, struct osd_device *dev,
        if (unlikely(!thread_is_running(thread)))
                return SCRUB_NEXT_EXIT;
 
-       if (!cfs_list_empty(&scrub->os_inconsistent_items)) {
-               struct osd_inconsistent_item *oii;
+       if (!list_empty(&scrub->os_inconsistent_items)) {
+               spin_lock(&scrub->os_lock);
+               if (likely(!list_empty(&scrub->os_inconsistent_items))) {
+                       struct osd_inconsistent_item *oii;
 
-               oii = cfs_list_entry(scrub->os_inconsistent_items.next,
-                                    struct osd_inconsistent_item, oii_list);
-               *oic = &oii->oii_cache;
-               scrub->os_in_prior = 1;
-               return 0;
+                       oii = list_entry(scrub->os_inconsistent_items.next,
+                               struct osd_inconsistent_item, oii_list);
+                       spin_unlock(&scrub->os_lock);
+
+                       *oic = &oii->oii_cache;
+                       scrub->os_in_prior = 1;
+
+                       return 0;
+               }
+               spin_unlock(&scrub->os_lock);
        }
 
-       if (noslot != 0)
+       if (noslot)
                return SCRUB_NEXT_WAIT;
 
        rc = osd_iit_next(param, &scrub->os_pos_current);
        if (rc != 0)
                return rc;
 
-       *oic = &scrub->os_oic;
+       *oic = &dev->od_scrub.os_oic;
        fid = &(*oic)->oic_fid;
        lid = &(*oic)->oic_lid;
        rc = osd_iit_iget(info, dev, fid, lid,
@@ -982,39 +841,34 @@ static int osd_scrub_next(struct osd_thread_info *info, struct osd_device *dev,
 
 static int osd_preload_next(struct osd_thread_info *info,
                            struct osd_device *dev, struct osd_iit_param *param,
-                           struct osd_idmap_cache **oic, int noslot)
+                           struct osd_idmap_cache **oic, const bool noslot)
 {
-       struct osd_otable_cache *ooc    = &dev->od_otable_it->ooi_cache;
-       struct osd_scrub        *scrub;
-       struct ptlrpc_thread    *thread;
-       int                      rc;
-
-       rc = osd_iit_next(param, &ooc->ooc_pos_preload);
-       if (rc != 0)
-               return rc;
+       struct osd_otable_cache *ooc = &dev->od_otable_it->ooi_cache;
+       struct lustre_scrub *scrub = &dev->od_scrub.os_scrub;
+       struct ptlrpc_thread *thread = &scrub->os_thread;
+       int rc;
 
-       scrub = &dev->od_scrub;
-       thread = &scrub->os_thread;
        if (thread_is_running(thread) &&
            ooc->ooc_pos_preload >= scrub->os_pos_current)
                return SCRUB_NEXT_EXIT;
 
+       rc = osd_iit_next(param, &ooc->ooc_pos_preload);
+       if (rc)
+               return rc;
+
        rc = osd_iit_iget(info, dev,
                          &ooc->ooc_cache[ooc->ooc_producer_idx].oic_fid,
                          &ooc->ooc_cache[ooc->ooc_producer_idx].oic_lid,
                          ooc->ooc_pos_preload, param->sb, false);
-       /* If succeed, it needs to move forward; otherwise up layer LFSCK may
-        * ignore the failure, so it still need to skip the inode next time. */
-       ooc->ooc_pos_preload = param->gbase + ++(param->offset);
        return rc;
 }
 
 static inline int
-osd_scrub_wakeup(struct osd_scrub *scrub, struct osd_otable_it *it)
+osd_scrub_wakeup(struct lustre_scrub *scrub, struct osd_otable_it *it)
 {
        spin_lock(&scrub->os_lock);
        if (osd_scrub_has_window(scrub, &it->ooi_cache) ||
-           !cfs_list_empty(&scrub->os_inconsistent_items) ||
+           !list_empty(&scrub->os_inconsistent_items) ||
            it->ooi_waiting || !thread_is_running(&scrub->os_thread))
                scrub->os_waiting = 0;
        else
@@ -1026,36 +880,36 @@ osd_scrub_wakeup(struct osd_scrub *scrub, struct osd_otable_it *it)
 
 static int osd_scrub_exec(struct osd_thread_info *info, struct osd_device *dev,
                          struct osd_iit_param *param,
-                         struct osd_idmap_cache *oic, int *noslot, int rc)
+                         struct osd_idmap_cache *oic, bool *noslot, int rc)
 {
-       struct l_wait_info       lwi    = { 0 };
-       struct osd_scrub        *scrub  = &dev->od_scrub;
-       struct scrub_file       *sf     = &scrub->os_file;
-       struct ptlrpc_thread    *thread = &scrub->os_thread;
-       struct osd_otable_it    *it     = dev->od_otable_it;
-       struct osd_otable_cache *ooc    = it ? &it->ooi_cache : NULL;
+       struct l_wait_info lwi = { 0 };
+       struct lustre_scrub *scrub = &dev->od_scrub.os_scrub;
+       struct scrub_file *sf = &scrub->os_file;
+       struct ptlrpc_thread *thread = &scrub->os_thread;
+       struct osd_otable_it *it = dev->od_otable_it;
+       struct osd_otable_cache *ooc = it ? &it->ooi_cache : NULL;
 
        switch (rc) {
-       case SCRUB_NEXT_CONTINUE:
-               goto next;
-       case SCRUB_NEXT_WAIT:
-               goto wait;
        case SCRUB_NEXT_NOSCRUB:
                down_write(&scrub->os_rwsem);
                scrub->os_new_checked++;
                sf->sf_items_noscrub++;
                up_write(&scrub->os_rwsem);
-               goto next;
+       case SCRUB_NEXT_CONTINUE:
+       case SCRUB_NEXT_WAIT:
+               goto wait;
        }
 
        rc = osd_scrub_check_update(info, dev, oic, rc);
-       if (rc != 0)
+       if (rc != 0) {
+               scrub->os_in_prior = 0;
                return rc;
+       }
 
-       rc = osd_scrub_checkpoint(scrub);
-       if (rc != 0) {
-               CERROR("%.16s: fail to checkpoint, pos = %u, rc = %d\n",
-                      LDISKFS_SB(param->sb)->s_es->s_volume_name,
+       rc = scrub_checkpoint(info->oti_env, scrub);
+       if (rc) {
+               CDEBUG(D_LFSCK, "%s: fail to checkpoint, pos = %llu: "
+                      "rc = %d\n", osd_scrub2name(scrub),
                       scrub->os_pos_current, rc);
                /* Continue, as long as the scrub itself can go ahead. */
        }
@@ -1065,9 +919,6 @@ static int osd_scrub_exec(struct osd_thread_info *info, struct osd_device *dev,
                return 0;
        }
 
-next:
-       scrub->os_pos_current = param->gbase + ++(param->offset);
-
 wait:
        if (it != NULL && it->ooi_waiting && ooc != NULL &&
            ooc->ooc_pos_preload < scrub->os_pos_current) {
@@ -1077,28 +928,28 @@ wait:
                spin_unlock(&scrub->os_lock);
        }
 
-       if (scrub->os_full_speed || rc == SCRUB_NEXT_CONTINUE)
+       if (rc == SCRUB_NEXT_CONTINUE)
                return 0;
 
-       if (osd_scrub_has_window(scrub, ooc)) {
-               *noslot = 0;
+       if (scrub->os_full_speed || !ooc || osd_scrub_has_window(scrub, ooc)) {
+               *noslot = false;
                return 0;
        }
 
-       l_wait_event(thread->t_ctl_waitq,
-                    osd_scrub_wakeup(scrub, it),
-                    &lwi);
+       if (it != NULL)
+               l_wait_event(thread->t_ctl_waitq, osd_scrub_wakeup(scrub, it),
+                            &lwi);
 
-       if (osd_scrub_has_window(scrub, ooc))
-               *noslot = 0;
+       if (!ooc || osd_scrub_has_window(scrub, ooc))
+               *noslot = false;
        else
-               *noslot = 1;
+               *noslot = true;
        return 0;
 }
 
 static int osd_preload_exec(struct osd_thread_info *info,
                            struct osd_device *dev, struct osd_iit_param *param,
-                           struct osd_idmap_cache *oic, int *noslot, int rc)
+                           struct osd_idmap_cache *oic, bool *noslot, int rc)
 {
        struct osd_otable_cache *ooc = &dev->od_otable_it->ooi_cache;
 
@@ -1113,103 +964,296 @@ static int osd_preload_exec(struct osd_thread_info *info,
 #define SCRUB_IT_ALL   1
 #define SCRUB_IT_CRASH 2
 
-static int osd_inode_iteration(struct osd_thread_info *info,
-                              struct osd_device *dev, __u32 max, bool preload)
+static void osd_scrub_join(const struct lu_env *env, struct osd_device *dev,
+                          __u32 flags, bool inconsistent)
 {
-       osd_iit_next_policy   next;
-       osd_iit_exec_policy   exec;
-       __u32                *pos;
-       __u32                *count;
-       struct osd_iit_param  param;
-       __u32                 limit;
-       int                   noslot = 0;
+       struct lustre_scrub *scrub = &dev->od_scrub.os_scrub;
+       struct ptlrpc_thread *thread = &scrub->os_thread;
+       struct scrub_file    *sf     = &scrub->os_file;
        int                   rc;
        ENTRY;
 
-       if (!preload) {
-               struct osd_scrub *scrub = &dev->od_scrub;
+       LASSERT(!(flags & SS_AUTO_PARTIAL));
 
-               next = osd_scrub_next;
-               exec = osd_scrub_exec;
-               pos = &scrub->os_pos_current;
-               count = &scrub->os_new_checked;
-       } else {
-               struct osd_otable_cache *ooc = &dev->od_otable_it->ooi_cache;
+       down_write(&scrub->os_rwsem);
+       scrub->os_in_join = 1;
+       if (flags & SS_SET_FAILOUT)
+               sf->sf_param |= SP_FAILOUT;
+       else if (flags & SS_CLEAR_FAILOUT)
+               sf->sf_param &= ~SP_FAILOUT;
 
-               next = osd_preload_next;
-               exec = osd_preload_exec;
-               pos = &ooc->ooc_pos_preload;
-               count = &ooc->ooc_cached_items;
+       if (flags & SS_SET_DRYRUN)
+               sf->sf_param |= SP_DRYRUN;
+       else if (flags & SS_CLEAR_DRYRUN)
+               sf->sf_param &= ~SP_DRYRUN;
+
+       if (flags & SS_RESET) {
+               scrub_file_reset(scrub, dev->od_uuid,
+                                inconsistent ? SF_INCONSISTENT : 0);
+               sf->sf_status = SS_SCANNING;
        }
-       param.sb = osd_sb(dev);
-       limit = le32_to_cpu(LDISKFS_SB(param.sb)->s_es->s_inodes_count);
 
-       while (*pos <= limit && *count < max) {
-               struct osd_idmap_cache *oic = NULL;
+       if (sf->sf_flags & (SF_RECREATED | SF_INCONSISTENT | SF_UPGRADE))
+               scrub->os_full_speed = 1;
+       else
+               scrub->os_full_speed = 0;
 
-               param.bg = (*pos - 1) / LDISKFS_INODES_PER_GROUP(param.sb);
-               param.offset = (*pos - 1) % LDISKFS_INODES_PER_GROUP(param.sb);
-               param.gbase = 1 + param.bg * LDISKFS_INODES_PER_GROUP(param.sb);
-               param.bitmap = ldiskfs_read_inode_bitmap(param.sb, param.bg);
-               if (param.bitmap == NULL) {
-                       CERROR("%.16s: fail to read bitmap for %u, "
-                              "scrub will stop, urgent mode\n",
-                              LDISKFS_SB(param.sb)->s_es->s_volume_name,
-                              (__u32)param.bg);
-                       RETURN(-EIO);
-               }
+       if (flags & SS_AUTO_FULL) {
+               sf->sf_flags |= SF_AUTO;
+               scrub->os_full_speed = 1;
+       }
 
-               while (param.offset < LDISKFS_INODES_PER_GROUP(param.sb) &&
-                      *count < max) {
-                       rc = next(info, dev, &param, &oic, noslot);
-                       switch (rc) {
-                       case SCRUB_NEXT_BREAK:
-                               goto next_group;
-                       case SCRUB_NEXT_EXIT:
-                               brelse(param.bitmap);
-                               RETURN(0);
-                       case SCRUB_NEXT_CRASH:
-                               brelse(param.bitmap);
-                               RETURN(SCRUB_IT_CRASH);
-                       case SCRUB_NEXT_FATAL:
-                               brelse(param.bitmap);
-                               RETURN(-EINVAL);
-                       }
+       scrub->os_new_checked = 0;
+       if (sf->sf_pos_last_checkpoint != 0)
+               sf->sf_pos_latest_start = sf->sf_pos_last_checkpoint + 1;
+       else
+               sf->sf_pos_latest_start = LDISKFS_FIRST_INO(osd_sb(dev)) + 1;
 
-                       rc = exec(info, dev, &param, oic, &noslot, rc);
-                       if (rc != 0) {
-                               brelse(param.bitmap);
-                               RETURN(rc);
-                       }
-               }
+       scrub->os_pos_current = sf->sf_pos_latest_start;
+       sf->sf_time_latest_start = ktime_get_real_seconds();
+       sf->sf_time_last_checkpoint = sf->sf_time_latest_start;
+       sf->sf_pos_last_checkpoint = sf->sf_pos_latest_start - 1;
+       rc = scrub_file_store(env, scrub);
 
-next_group:
-               brelse(param.bitmap);
-       }
+       spin_lock(&scrub->os_lock);
+       scrub->os_waiting = 0;
+       scrub->os_paused = 0;
+       scrub->os_partial_scan = 0;
+       scrub->os_in_join = 0;
+       scrub->os_full_scrub = 0;
+       spin_unlock(&scrub->os_lock);
+       wake_up_all(&thread->t_ctl_waitq);
+       up_write(&scrub->os_rwsem);
 
-       if (*pos > limit)
-               RETURN(SCRUB_IT_ALL);
-       RETURN(0);
+       CDEBUG(D_LFSCK, "%s: joined in the OI scrub with flag %u: rc = %d\n",
+              osd_scrub2name(scrub), flags, rc);
+
+       EXIT;
 }
 
-static int osd_otable_it_preload(const struct lu_env *env,
-                                struct osd_otable_it *it)
+static int osd_inode_iteration(struct osd_thread_info *info,
+                              struct osd_device *dev, __u32 max, bool preload)
 {
-       struct osd_device       *dev   = it->ooi_dev;
-       struct osd_scrub        *scrub = &dev->od_scrub;
-       struct osd_otable_cache *ooc   = &it->ooi_cache;
-       int                      rc;
+       struct lustre_scrub *scrub  = &dev->od_scrub.os_scrub;
+       struct ptlrpc_thread *thread = &scrub->os_thread;
+       struct scrub_file *sf = &scrub->os_file;
+       osd_iit_next_policy next;
+       osd_iit_exec_policy exec;
+       __u64 *pos;
+       __u64 *count;
+       struct osd_iit_param *param;
+       struct l_wait_info lwi = { 0 };
+       __u32 limit;
+       int rc;
+       bool noslot = true;
        ENTRY;
 
-       rc = osd_inode_iteration(osd_oti_get(env), dev,
-                                OSD_OTABLE_IT_CACHE_SIZE, true);
+       if (preload)
+               goto full;
+
+       param = &dev->od_scrub.os_iit_param;
+       memset(param, 0, sizeof(*param));
+       param->sb = osd_sb(dev);
+
+       while (scrub->os_partial_scan && !scrub->os_in_join) {
+               struct osd_idmap_cache *oic = NULL;
+
+               rc = osd_scrub_next(info, dev, param, &oic, noslot);
+               switch (rc) {
+               case SCRUB_NEXT_EXIT:
+                       RETURN(0);
+               case SCRUB_NEXT_CRASH:
+                       RETURN(SCRUB_IT_CRASH);
+               case SCRUB_NEXT_FATAL:
+                       RETURN(-EINVAL);
+               case SCRUB_NEXT_WAIT: {
+                       struct kstatfs *ksfs = &info->oti_ksfs;
+                       __u64 saved_flags;
+
+                       if (dev->od_full_scrub_ratio == OFSR_NEVER ||
+                           unlikely(sf->sf_items_updated_prior == 0))
+                               goto wait;
+
+                       if (dev->od_full_scrub_ratio == OFSR_DIRECTLY ||
+                           scrub->os_full_scrub) {
+                               osd_scrub_join(info->oti_env, dev,
+                                              SS_AUTO_FULL | SS_RESET, true);
+                               goto full;
+                       }
+
+                       rc = param->sb->s_op->statfs(param->sb->s_root, ksfs);
+                       if (rc == 0) {
+                               __u64 used = ksfs->f_files - ksfs->f_ffree;
+
+                               do_div(used, sf->sf_items_updated_prior);
+                               /* If we hit too much inconsistent OI
+                                * mappings during the partial scan,
+                                * then scan the device completely. */
+                               if (used < dev->od_full_scrub_ratio) {
+                                       osd_scrub_join(info->oti_env, dev,
+                                               SS_AUTO_FULL | SS_RESET, true);
+                                       goto full;
+                               }
+                       }
+
+wait:
+                       if (OBD_FAIL_CHECK(OBD_FAIL_OSD_SCRUB_DELAY) &&
+                           cfs_fail_val > 0)
+                               continue;
+
+                       saved_flags = sf->sf_flags;
+                       sf->sf_flags &= ~(SF_RECREATED | SF_INCONSISTENT |
+                                         SF_UPGRADE | SF_AUTO);
+                       sf->sf_status = SS_COMPLETED;
+                       l_wait_event(thread->t_ctl_waitq,
+                                    !thread_is_running(thread) ||
+                                    !scrub->os_partial_scan ||
+                                    scrub->os_in_join ||
+                                    !list_empty(&scrub->os_inconsistent_items),
+                                    &lwi);
+                       sf->sf_flags = saved_flags;
+                       sf->sf_status = SS_SCANNING;
+
+                       if (unlikely(!thread_is_running(thread)))
+                               RETURN(0);
+
+                       if (!scrub->os_partial_scan || scrub->os_in_join)
+                               goto full;
+
+                       continue;
+               }
+               default:
+                       LASSERTF(rc == 0, "rc = %d\n", rc);
+
+                       osd_scrub_exec(info, dev, param, oic, &noslot, rc);
+                       break;
+               }
+       }
+
+full:
+       if (!preload) {
+               l_wait_event(thread->t_ctl_waitq,
+                            !thread_is_running(thread) || !scrub->os_in_join,
+                            &lwi);
+
+               if (unlikely(!thread_is_running(thread)))
+                       RETURN(0);
+       }
+
+       noslot = false;
+       if (!preload) {
+               next = osd_scrub_next;
+               exec = osd_scrub_exec;
+               pos = &scrub->os_pos_current;
+               count = &scrub->os_new_checked;
+               param->start = *pos;
+               param->bg = (*pos - 1) / LDISKFS_INODES_PER_GROUP(param->sb);
+               param->offset =
+                       (*pos - 1) % LDISKFS_INODES_PER_GROUP(param->sb);
+               param->gbase =
+                       1 + param->bg * LDISKFS_INODES_PER_GROUP(param->sb);
+       } else {
+               struct osd_otable_cache *ooc = &dev->od_otable_it->ooi_cache;
+
+               next = osd_preload_next;
+               exec = osd_preload_exec;
+               pos = &ooc->ooc_pos_preload;
+               count = &ooc->ooc_cached_items;
+               param = &dev->od_otable_it->ooi_iit_param;
+       }
+
+       rc = 0;
+       limit = le32_to_cpu(LDISKFS_SB(osd_sb(dev))->s_es->s_inodes_count);
+       while (*pos <= limit && *count < max) {
+               struct ldiskfs_group_desc *desc;
+               bool next_group = false;
+
+               desc = ldiskfs_get_group_desc(param->sb, param->bg, NULL);
+               if (!desc)
+                       RETURN(-EIO);
+
+               if (desc->bg_flags & cpu_to_le16(LDISKFS_BG_INODE_UNINIT)) {
+                       next_group = true;
+                       goto next_group;
+               }
+
+               param->bitmap = ldiskfs_read_inode_bitmap(param->sb, param->bg);
+               if (!param->bitmap) {
+                       CERROR("%s: fail to read bitmap for %u, "
+                              "scrub will stop, urgent mode\n",
+                              osd_scrub2name(scrub), (__u32)param->bg);
+                       RETURN(-EIO);
+               }
+
+               do {
+                       struct osd_idmap_cache *oic = NULL;
+
+                       if (param->offset +
+                               ldiskfs_itable_unused_count(param->sb, desc) >=
+                           LDISKFS_INODES_PER_GROUP(param->sb)) {
+                               next_group = true;
+                               goto next_group;
+                       }
+
+                       rc = next(info, dev, param, &oic, noslot);
+                       switch (rc) {
+                       case SCRUB_NEXT_BREAK:
+                               next_group = true;
+                               goto next_group;
+                       case SCRUB_NEXT_EXIT:
+                               brelse(param->bitmap);
+                               RETURN(0);
+                       case SCRUB_NEXT_CRASH:
+                               brelse(param->bitmap);
+                               RETURN(SCRUB_IT_CRASH);
+                       case SCRUB_NEXT_FATAL:
+                               brelse(param->bitmap);
+                               RETURN(-EINVAL);
+                       }
+
+                       rc = exec(info, dev, param, oic, &noslot, rc);
+               } while (!rc && *pos <= limit && *count < max);
+
+next_group:
+               if (param->bitmap) {
+                       brelse(param->bitmap);
+                       param->bitmap = NULL;
+               }
+
+               if (rc < 0)
+                       GOTO(out, rc);
+
+               if (next_group) {
+                       param->bg++;
+                       param->offset = 0;
+                       param->gbase = 1 +
+                               param->bg * LDISKFS_INODES_PER_GROUP(param->sb);
+                       *pos = param->gbase;
+                       param->start = *pos;
+               }
+       }
+
+       if (*pos > limit)
+               RETURN(SCRUB_IT_ALL);
+
+out:
+       RETURN(rc);
+}
+
+static int osd_otable_it_preload(const struct lu_env *env,
+                                struct osd_otable_it *it)
+{
+       struct osd_device *dev = it->ooi_dev;
+       struct lustre_scrub *scrub = &dev->od_scrub.os_scrub;
+       struct osd_otable_cache *ooc   = &it->ooi_cache;
+       int                      rc;
+       ENTRY;
+
+       rc = osd_inode_iteration(osd_oti_get(env), dev,
+                                OSD_OTABLE_IT_CACHE_SIZE, true);
        if (rc == SCRUB_IT_ALL)
                it->ooi_all_cached = 1;
 
-       CDEBUG(D_LFSCK, "OSD pre-loaded: max = %u, preload = %u, rc = %d\n",
-              le32_to_cpu(LDISKFS_SB(osd_sb(dev))->s_es->s_inodes_count),
-              ooc->ooc_pos_preload, rc);
-
        if (scrub->os_waiting && osd_scrub_has_window(scrub, ooc)) {
                spin_lock(&scrub->os_lock);
                scrub->os_waiting = 0;
@@ -1222,29 +1266,28 @@ static int osd_otable_it_preload(const struct lu_env *env,
 
 static int osd_scrub_main(void *args)
 {
-       struct lu_env         env;
-       struct osd_device    *dev    = (struct osd_device *)args;
-       struct osd_scrub     *scrub  = &dev->od_scrub;
+       struct lu_env env;
+       struct osd_device *dev = (struct osd_device *)args;
+       struct lustre_scrub *scrub = &dev->od_scrub.os_scrub;
        struct ptlrpc_thread *thread = &scrub->os_thread;
-       struct super_block   *sb     = osd_sb(dev);
-       int                   rc;
+       int rc;
        ENTRY;
 
-       rc = lu_env_init(&env, LCT_LOCAL);
+       rc = lu_env_init(&env, LCT_LOCAL | LCT_DT_THREAD);
        if (rc != 0) {
-               CERROR("%.16s: OI scrub, fail to init env, rc = %d\n",
-                      LDISKFS_SB(sb)->s_es->s_volume_name, rc);
+               CDEBUG(D_LFSCK, "%s: OI scrub fail to init env: rc = %d\n",
+                      osd_scrub2name(scrub), rc);
                GOTO(noenv, rc);
        }
 
-       rc = osd_scrub_prep(dev);
+       rc = osd_scrub_prep(&env, dev);
        if (rc != 0) {
-               CERROR("%.16s: OI scrub, fail to scrub prep, rc = %d\n",
-                      LDISKFS_SB(sb)->s_es->s_volume_name, rc);
+               CDEBUG(D_LFSCK, "%s: OI scrub fail to scrub prep: rc = %d\n",
+                      osd_scrub2name(scrub), rc);
                GOTO(out, rc);
        }
 
-       if (!scrub->os_full_speed) {
+       if (!scrub->os_full_speed && !scrub->os_partial_scan) {
                struct l_wait_info lwi = { 0 };
                struct osd_otable_it *it = dev->od_otable_it;
                struct osd_otable_cache *ooc = &it->ooi_cache;
@@ -1258,26 +1301,32 @@ static int osd_scrub_main(void *args)
                scrub->os_pos_current = ooc->ooc_pos_preload;
        }
 
-       CDEBUG(D_LFSCK, "OI scrub: flags = 0x%x, pos = %u\n",
-              scrub->os_start_flags, scrub->os_pos_current);
+       CDEBUG(D_LFSCK, "%s: OI scrub start, flags = 0x%x, pos = %llu\n",
+              osd_scrub2name(scrub), scrub->os_start_flags,
+              scrub->os_pos_current);
 
        rc = osd_inode_iteration(osd_oti_get(&env), dev, ~0U, false);
-       if (unlikely(rc == SCRUB_IT_CRASH))
+       if (unlikely(rc == SCRUB_IT_CRASH)) {
+               spin_lock(&scrub->os_lock);
+               thread_set_flags(&scrub->os_thread, SVC_STOPPING);
+               spin_unlock(&scrub->os_lock);
                GOTO(out, rc = -EINVAL);
+       }
+
        GOTO(post, rc);
 
 post:
-       osd_scrub_post(scrub, rc);
-       CDEBUG(D_LFSCK, "OI scrub: stop, rc = %d, pos = %u\n",
-              rc, scrub->os_pos_current);
+       rc = osd_scrub_post(&env, dev, rc);
+       CDEBUG(D_LFSCK, "%s: OI scrub: stop, pos = %llu: rc = %d\n",
+              osd_scrub2name(scrub), scrub->os_pos_current, rc);
 
 out:
-       while (!cfs_list_empty(&scrub->os_inconsistent_items)) {
+       while (!list_empty(&scrub->os_inconsistent_items)) {
                struct osd_inconsistent_item *oii;
 
-               oii = cfs_list_entry(scrub->os_inconsistent_items.next,
-                                    struct osd_inconsistent_item, oii_list);
-               cfs_list_del_init(&oii->oii_list);
+               oii = list_entry(scrub->os_inconsistent_items.next,
+                                struct osd_inconsistent_item, oii_list);
+               list_del_init(&oii->oii_list);
                OBD_FREE_PTR(oii);
        }
        lu_env_fini(&env);
@@ -1295,12 +1344,29 @@ noenv:
 typedef int (*scandir_t)(struct osd_thread_info *, struct osd_device *,
                         struct dentry *, filldir_t filldir);
 
+#ifdef HAVE_FILLDIR_USE_CTX
+static int osd_ios_varfid_fill(struct dir_context *buf, const char *name,
+                              int namelen, loff_t offset, __u64 ino,
+                              unsigned d_type);
+static int osd_ios_lf_fill(struct dir_context *buf, const char *name,
+                          int namelen, loff_t offset, __u64 ino,
+                          unsigned d_type);
+static int osd_ios_dl_fill(struct dir_context *buf, const char *name,
+                          int namelen, loff_t offset, __u64 ino,
+                          unsigned d_type);
+static int osd_ios_uld_fill(struct dir_context *buf, const char *name,
+                           int namelen, loff_t offset, __u64 ino,
+                           unsigned d_type);
+#else
 static int osd_ios_varfid_fill(void *buf, const char *name, int namelen,
                               loff_t offset, __u64 ino, unsigned d_type);
 static int osd_ios_lf_fill(void *buf, const char *name, int namelen,
                           loff_t offset, __u64 ino, unsigned d_type);
 static int osd_ios_dl_fill(void *buf, const char *name, int namelen,
                           loff_t offset, __u64 ino, unsigned d_type);
+static int osd_ios_uld_fill(void *buf, const char *name, int namelen,
+                           loff_t offset, __u64 ino, unsigned d_type);
+#endif
 
 static int
 osd_ios_general_scan(struct osd_thread_info *info, struct osd_device *dev,
@@ -1313,17 +1379,11 @@ static int
 osd_ios_OBJECTS_scan(struct osd_thread_info *info, struct osd_device *dev,
                     struct dentry *dentry, filldir_t filldir);
 
-enum osd_lf_flags {
-       OLF_SCAN_SUBITEMS       = 0x0001,
-       OLF_HIDE_FID            = 0x0002,
-       OLF_SHOW_NAME           = 0x0004,
-       OLF_NO_OI               = 0x0008,
-};
-
 struct osd_lf_map {
        char            *olm_name;
        struct lu_fid    olm_fid;
        __u16            olm_flags;
+       __u16            olm_namelen;
        scandir_t        olm_scandir;
        filldir_t        olm_filldir;
 };
@@ -1331,112 +1391,340 @@ struct osd_lf_map {
 /* Add the new introduced local files in the list in the future. */
 static const struct osd_lf_map osd_lf_maps[] = {
        /* CATALOGS */
-       { CATLIST, { FID_SEQ_LOCAL_FILE, LLOG_CATALOGS_OID, 0 }, OLF_SHOW_NAME,
-               NULL, NULL },
+       {
+               .olm_name       = CATLIST,
+               .olm_fid        = {
+                       .f_seq  = FID_SEQ_LOCAL_FILE,
+                       .f_oid  = LLOG_CATALOGS_OID,
+               },
+               .olm_flags      = OLF_SHOW_NAME,
+               .olm_namelen    = sizeof(CATLIST) - 1,
+       },
 
        /* CONFIGS */
-       { MOUNT_CONFIGS_DIR, { FID_SEQ_LOCAL_FILE, MGS_CONFIGS_OID, 0 },
-               OLF_SCAN_SUBITEMS, osd_ios_general_scan,
-               osd_ios_varfid_fill },
+       {
+               .olm_name       = MOUNT_CONFIGS_DIR,
+               .olm_fid        = {
+                       .f_seq  = FID_SEQ_LOCAL_FILE,
+                       .f_oid  = MGS_CONFIGS_OID,
+               },
+               .olm_flags      = OLF_SCAN_SUBITEMS,
+               .olm_namelen    = sizeof(MOUNT_CONFIGS_DIR) - 1,
+               .olm_scandir    = osd_ios_general_scan,
+               .olm_filldir    = osd_ios_varfid_fill,
+       },
 
        /* NIDTBL_VERSIONS */
-       { MGS_NIDTBL_DIR, { 0, 0, 0 }, OLF_SCAN_SUBITEMS,
-               osd_ios_general_scan, osd_ios_varfid_fill },
+       {
+               .olm_name       = MGS_NIDTBL_DIR,
+               .olm_flags      = OLF_SCAN_SUBITEMS,
+               .olm_namelen    = sizeof(MGS_NIDTBL_DIR) - 1,
+               .olm_scandir    = osd_ios_general_scan,
+               .olm_filldir    = osd_ios_varfid_fill,
+       },
 
        /* PENDING */
-       { "PENDING", { 0, 0, 0 }, 0, NULL, NULL },
+       {
+               .olm_name       = MDT_ORPHAN_DIR,
+               .olm_namelen    = sizeof(MDT_ORPHAN_DIR) - 1,
+       },
 
        /* ROOT */
-       { "ROOT", { FID_SEQ_ROOT, 1, 0 },
-               OLF_SCAN_SUBITEMS | OLF_HIDE_FID, osd_ios_ROOT_scan, NULL },
+       {
+               .olm_name       = "ROOT",
+               .olm_fid        = {
+                       .f_seq  = FID_SEQ_ROOT,
+                       .f_oid  = FID_OID_ROOT,
+               },
+               .olm_flags      = OLF_SCAN_SUBITEMS | OLF_HIDE_FID,
+               .olm_namelen    = sizeof("ROOT") - 1,
+               .olm_scandir    = osd_ios_ROOT_scan,
+       },
 
        /* changelog_catalog */
-       { CHANGELOG_CATALOG, { 0, 0, 0 }, 0, NULL, NULL },
+       {
+               .olm_name       = CHANGELOG_CATALOG,
+               .olm_namelen    = sizeof(CHANGELOG_CATALOG) - 1,
+       },
 
        /* changelog_users */
-       { CHANGELOG_USERS, { 0, 0, 0 }, 0, NULL, NULL },
+       {
+               .olm_name       = CHANGELOG_USERS,
+               .olm_namelen    = sizeof(CHANGELOG_USERS) - 1,
+       },
 
        /* fld */
-       { "fld", { FID_SEQ_LOCAL_FILE, FLD_INDEX_OID, 0 }, OLF_SHOW_NAME,
-               NULL, NULL },
+       {
+               .olm_name       = "fld",
+               .olm_fid        = {
+                       .f_seq  = FID_SEQ_LOCAL_FILE,
+                       .f_oid  = FLD_INDEX_OID,
+               },
+               .olm_flags      = OLF_SHOW_NAME,
+               .olm_namelen    = sizeof("fld") - 1,
+       },
 
        /* last_rcvd */
-       { LAST_RCVD, { FID_SEQ_LOCAL_FILE, LAST_RECV_OID, 0 }, OLF_SHOW_NAME,
-               NULL, NULL },
-
-       /* lfsck_bookmark */
-       { "lfsck_bookmark", { 0, 0, 0 }, 0, NULL, NULL },
+       {
+               .olm_name       = LAST_RCVD,
+               .olm_fid        = {
+                       .f_seq  = FID_SEQ_LOCAL_FILE,
+                       .f_oid  = LAST_RECV_OID,
+               },
+               .olm_flags      = OLF_SHOW_NAME,
+               .olm_namelen    = sizeof(LAST_RCVD) - 1,
+       },
+
+       /* reply_data */
+       {
+               .olm_name       = REPLY_DATA,
+               .olm_fid        = {
+                       .f_seq  = FID_SEQ_LOCAL_FILE,
+                       .f_oid  = REPLY_DATA_OID,
+               },
+               .olm_flags      = OLF_SHOW_NAME,
+               .olm_namelen    = sizeof(REPLY_DATA) - 1,
+       },
 
        /* lov_objid */
-       { LOV_OBJID, { FID_SEQ_LOCAL_FILE, MDD_LOV_OBJ_OID, 0 }, OLF_SHOW_NAME,
-               NULL, NULL },
+       {
+               .olm_name       = LOV_OBJID,
+               .olm_fid        = {
+                       .f_seq  = FID_SEQ_LOCAL_FILE,
+                       .f_oid  = MDD_LOV_OBJ_OID,
+               },
+               .olm_flags      = OLF_SHOW_NAME,
+               .olm_namelen    = sizeof(LOV_OBJID) - 1,
+       },
 
        /* lov_objseq */
-       { LOV_OBJSEQ, { FID_SEQ_LOCAL_FILE, MDD_LOV_OBJ_OSEQ, 0 },
-               OLF_SHOW_NAME, NULL, NULL },
+       {
+               .olm_name       = LOV_OBJSEQ,
+               .olm_fid        = {
+                       .f_seq  = FID_SEQ_LOCAL_FILE,
+                       .f_oid  = MDD_LOV_OBJ_OSEQ,
+               },
+               .olm_flags      = OLF_SHOW_NAME,
+               .olm_namelen    = sizeof(LOV_OBJSEQ) - 1,
+       },
 
        /* quota_master */
-       { QMT_DIR, { 0, 0, 0 }, OLF_SCAN_SUBITEMS,
-               osd_ios_general_scan, osd_ios_varfid_fill },
+       {
+               .olm_name       = QMT_DIR,
+               .olm_flags      = OLF_SCAN_SUBITEMS,
+               .olm_namelen    = sizeof(QMT_DIR) - 1,
+               .olm_scandir    = osd_ios_general_scan,
+               .olm_filldir    = osd_ios_varfid_fill,
+       },
 
        /* quota_slave */
-       { QSD_DIR, { 0, 0, 0 }, OLF_SCAN_SUBITEMS,
-               osd_ios_general_scan, osd_ios_varfid_fill },
+       {
+               .olm_name       = QSD_DIR,
+               .olm_flags      = OLF_SCAN_SUBITEMS,
+               .olm_namelen    = sizeof(QSD_DIR) - 1,
+               .olm_scandir    = osd_ios_general_scan,
+               .olm_filldir    = osd_ios_varfid_fill,
+       },
 
        /* seq_ctl */
-       { "seq_ctl", { FID_SEQ_LOCAL_FILE, FID_SEQ_CTL_OID, 0 },
-               OLF_SHOW_NAME, NULL, NULL },
+       {
+               .olm_name       = "seq_ctl",
+               .olm_fid        = {
+                       .f_seq  = FID_SEQ_LOCAL_FILE,
+                       .f_oid  = FID_SEQ_CTL_OID,
+               },
+               .olm_flags      = OLF_SHOW_NAME,
+               .olm_namelen    = sizeof("seq_ctl") - 1,
+       },
 
        /* seq_srv */
-       { "seq_srv", { FID_SEQ_LOCAL_FILE, FID_SEQ_SRV_OID, 0 },
-               OLF_SHOW_NAME, NULL, NULL },
+       {
+               .olm_name       = "seq_srv",
+               .olm_fid        = {
+                       .f_seq  = FID_SEQ_LOCAL_FILE,
+                       .f_oid  = FID_SEQ_SRV_OID,
+               },
+               .olm_flags      = OLF_SHOW_NAME,
+               .olm_namelen    = sizeof("seq_srv") - 1,
+       },
 
        /* health_check */
-       { HEALTH_CHECK, { FID_SEQ_LOCAL_FILE, OFD_HEALTH_CHECK_OID, 0 },
-               OLF_SHOW_NAME, NULL, NULL },
+       {
+               .olm_name       = HEALTH_CHECK,
+               .olm_fid        = {
+                       .f_seq  = FID_SEQ_LOCAL_FILE,
+                       .f_oid  = OFD_HEALTH_CHECK_OID,
+               },
+               .olm_flags      = OLF_SHOW_NAME,
+               .olm_namelen    = sizeof(HEALTH_CHECK) - 1,
+       },
+
+       /* LFSCK */
+       {
+               .olm_name       = LFSCK_DIR,
+               .olm_flags      = OLF_SCAN_SUBITEMS,
+               .olm_namelen    = sizeof(LFSCK_DIR) - 1,
+               .olm_scandir    = osd_ios_general_scan,
+               .olm_filldir    = osd_ios_varfid_fill,
+       },
+
+       /* lfsck_bookmark */
+       {
+               .olm_name       = LFSCK_BOOKMARK,
+               .olm_namelen    = sizeof(LFSCK_BOOKMARK) - 1,
+       },
+
+       /* lfsck_layout */
+       {
+               .olm_name       = LFSCK_LAYOUT,
+               .olm_namelen    = sizeof(LFSCK_LAYOUT) - 1,
+       },
 
        /* lfsck_namespace */
-       { "lfsck_namespace", { 0, 0, 0 }, 0, NULL, NULL },
+       {
+               .olm_name       = LFSCK_NAMESPACE,
+               .olm_namelen    = sizeof(LFSCK_NAMESPACE) - 1,
+       },
 
        /* OBJECTS, upgrade from old device */
-       { OBJECTS, { 0, 0, 0 }, OLF_SCAN_SUBITEMS, osd_ios_OBJECTS_scan, NULL },
+       {
+               .olm_name       = OBJECTS,
+               .olm_flags      = OLF_SCAN_SUBITEMS,
+               .olm_namelen    = sizeof(OBJECTS) - 1,
+               .olm_scandir    = osd_ios_OBJECTS_scan,
+       },
 
        /* lquota_v2.user, upgrade from old device */
-       { "lquota_v2.user", { 0, 0, 0 }, 0, NULL, NULL },
+       {
+               .olm_name       = "lquota_v2.user",
+               .olm_namelen    = sizeof("lquota_v2.user") - 1,
+       },
 
        /* lquota_v2.group, upgrade from old device */
-       { "lquota_v2.group", { 0, 0, 0 }, 0, NULL, NULL },
+       {
+               .olm_name       = "lquota_v2.group",
+               .olm_namelen    = sizeof("lquota_v2.group") - 1,
+       },
 
        /* LAST_GROUP, upgrade from old device */
-       { "LAST_GROUP", { FID_SEQ_LOCAL_FILE, OFD_LAST_GROUP_OID, 0 },
-               OLF_SHOW_NAME, NULL, NULL },
-
-       /* SLAVE_LOG, llog for destroy slave stripes of striped dir */
-       { "SLAVE_LOG", { FID_SEQ_LOCAL_FILE, SLAVE_LLOG_CATALOGS_OID, 0 },
-              OLF_SHOW_NAME, NULL, NULL },
+       {
+               .olm_name       = "LAST_GROUP",
+               .olm_fid        = {
+                       .f_seq  = FID_SEQ_LOCAL_FILE,
+                       .f_oid  = OFD_LAST_GROUP_OID,
+               },
+               .olm_flags      = OLF_SHOW_NAME,
+               .olm_namelen    = sizeof("LAST_GROUP") - 1,
+       },
+
+       /* committed batchid for cross-MDT operation */
+       {
+               .olm_name       = "BATCHID",
+               .olm_fid        = {
+                       .f_seq  = FID_SEQ_LOCAL_FILE,
+                       .f_oid  = BATCHID_COMMITTED_OID,
+               },
+               .olm_flags      = OLF_SHOW_NAME,
+               .olm_namelen    = sizeof("BATCHID") - 1,
+       },
+
+       /* OSP update logs update_log{_dir} use f_seq = FID_SEQ_UPDATE_LOG{_DIR}
+        * and f_oid = index for their log files.  See lu_update_log{_dir}_fid()
+        * for more details. */
+
+       /* update_log */
+       {
+               .olm_name       = "update_log",
+               .olm_fid        = {
+                       .f_seq  = FID_SEQ_UPDATE_LOG,
+               },
+               .olm_flags      = OLF_SHOW_NAME | OLF_IDX_IN_FID,
+               .olm_namelen    = sizeof("update_log") - 1,
+       },
+
+       /* update_log_dir */
+       {
+               .olm_name       = "update_log_dir",
+               .olm_fid        = {
+                       .f_seq  = FID_SEQ_UPDATE_LOG_DIR,
+               },
+               .olm_flags      = OLF_SHOW_NAME | OLF_SCAN_SUBITEMS |
+                                 OLF_IDX_IN_FID,
+               .olm_namelen    = sizeof("update_log_dir") - 1,
+               .olm_scandir    = osd_ios_general_scan,
+               .olm_filldir    = osd_ios_uld_fill,
+       },
 
        /* lost+found */
-       { "lost+found", { 0, 0, 0 }, OLF_SCAN_SUBITEMS | OLF_NO_OI,
-               osd_ios_general_scan, osd_ios_lf_fill },
-
-       { NULL, { 0, 0, 0 }, 0, NULL, NULL }
+       {
+               .olm_name       = "lost+found",
+               .olm_fid        = {
+                       .f_seq  = FID_SEQ_LOCAL_FILE,
+                       .f_oid  = OSD_LPF_OID,
+               },
+               .olm_flags      = OLF_SCAN_SUBITEMS,
+               .olm_namelen    = sizeof("lost+found") - 1,
+               .olm_scandir    = osd_ios_general_scan,
+               .olm_filldir    = osd_ios_lf_fill,
+       },
+
+       /* hsm_actions */
+       {
+               .olm_name       = HSM_ACTIONS,
+       },
+
+       /* nodemap */
+       {
+               .olm_name       = LUSTRE_NODEMAP_NAME,
+       },
+
+       /* index_backup */
+       {
+               .olm_name       = INDEX_BACKUP_DIR,
+               .olm_fid        = {
+                       .f_seq  = FID_SEQ_LOCAL_FILE,
+                       .f_oid  = INDEX_BACKUP_OID,
+               },
+               .olm_flags      = OLF_SCAN_SUBITEMS | OLF_NOT_BACKUP,
+               .olm_namelen    = sizeof(INDEX_BACKUP_DIR) - 1,
+               .olm_scandir    = osd_ios_general_scan,
+               .olm_filldir    = osd_ios_varfid_fill,
+       },
+
+       {
+               .olm_name       = NULL
+       }
 };
 
 /* Add the new introduced files under .lustre/ in the list in the future. */
 static const struct osd_lf_map osd_dl_maps[] = {
        /* .lustre/fid */
-       { "fid", { FID_SEQ_DOT_LUSTRE, FID_OID_DOT_LUSTRE_OBF, 0 }, 0,
-               NULL, NULL },
+       {
+               .olm_name       = "fid",
+               .olm_fid        = {
+                       .f_seq  = FID_SEQ_DOT_LUSTRE,
+                       .f_oid  = FID_OID_DOT_LUSTRE_OBF,
+               },
+               .olm_namelen    = sizeof("fid") - 1,
+       },
 
        /* .lustre/lost+found */
-       { "lost+found", { FID_SEQ_DOT_LUSTRE, FID_OID_DOT_LUSTRE_LPF, 0 }, 0,
-               NULL, NULL },
-
-       { NULL, { 0, 0, 0 }, 0, NULL, NULL }
+       {
+               .olm_name       = "lost+found",
+               .olm_fid        = {
+                       .f_seq  = FID_SEQ_DOT_LUSTRE,
+                       .f_oid  = FID_OID_DOT_LUSTRE_LPF,
+               },
+               .olm_namelen    = sizeof("lost+found") - 1,
+       },
+
+       {
+               .olm_name       = NULL
+       }
 };
 
 struct osd_ios_item {
-       cfs_list_t       oii_list;
+       struct list_head oii_list;
        struct dentry   *oii_dentry;
        scandir_t        oii_scandir;
        filldir_t        oii_filldir;
@@ -1450,25 +1738,9 @@ struct osd_ios_filldir_buf {
        struct osd_thread_info  *oifb_info;
        struct osd_device       *oifb_dev;
        struct dentry           *oifb_dentry;
+       int                      oifb_items;
 };
 
-static inline struct dentry *
-osd_ios_lookup_one_len(const char *name, struct dentry *parent, int namelen)
-{
-       struct dentry *dentry;
-
-       CDEBUG(D_LFSCK, "init lookup one: parent = %.*s, name = %.*s\n",
-              parent->d_name.len, parent->d_name.name, namelen, name);
-
-       dentry = ll_lookup_one_len(name, parent, namelen);
-       if (!IS_ERR(dentry) && dentry->d_inode == NULL) {
-               dput(dentry);
-               return ERR_PTR(-ENOENT);
-       }
-
-       return dentry;
-}
-
 static int
 osd_ios_new_item(struct osd_device *dev, struct dentry *dentry,
                 scandir_t scandir, filldir_t filldir)
@@ -1480,15 +1752,128 @@ osd_ios_new_item(struct osd_device *dev, struct dentry *dentry,
        if (item == NULL)
                RETURN(-ENOMEM);
 
-       CFS_INIT_LIST_HEAD(&item->oii_list);
+       INIT_LIST_HEAD(&item->oii_list);
        item->oii_dentry = dget(dentry);
        item->oii_scandir = scandir;
        item->oii_filldir = filldir;
-       cfs_list_add_tail(&item->oii_list, &dev->od_ios_list);
+       list_add_tail(&item->oii_list, &dev->od_ios_list);
 
        RETURN(0);
 }
 
+static bool osd_index_need_recreate(const struct lu_env *env,
+                                   struct osd_device *dev, struct inode *inode)
+{
+       struct osd_directory *iam = &osd_oti_get(env)->oti_iam;
+       struct iam_container *bag = &iam->od_container;
+       int rc;
+       ENTRY;
+
+       rc = iam_container_init(bag, &iam->od_descr, inode);
+       if (rc)
+               RETURN(true);
+
+       rc = iam_container_setup(bag);
+       iam_container_fini(bag);
+       if (rc)
+               RETURN(true);
+
+       RETURN(false);
+}
+
+static void osd_ios_index_register(const struct lu_env *env,
+                                  struct osd_device *osd,
+                                  const struct lu_fid *fid,
+                                  struct inode *inode)
+{
+       struct osd_directory *iam = &osd_oti_get(env)->oti_iam;
+       struct iam_container *bag = &iam->od_container;
+       struct super_block *sb = osd_sb(osd);
+       struct iam_descr *descr;
+       __u32 keysize = 0;
+       __u32 recsize = 0;
+       int rc;
+       ENTRY;
+
+       /* Index must be a regular file. */
+       if (!S_ISREG(inode->i_mode))
+               RETURN_EXIT;
+
+       /* Index's size must be block aligned. */
+       if (inode->i_size < sb->s_blocksize ||
+           (inode->i_size & (sb->s_blocksize - 1)) != 0)
+               RETURN_EXIT;
+
+       iam_container_init(bag, &iam->od_descr, inode);
+       rc = iam_container_setup(bag);
+       if (rc)
+               GOTO(fini, rc = 1);
+
+       descr = bag->ic_descr;
+       /* May be regular file with IAM_LFIX_ROOT_MAGIC matched
+        * coincidentally, or corrupted index object, skip it. */
+       if (descr->id_ptr_size != 4)
+               GOTO(fini, rc = 1);
+
+       keysize = descr->id_key_size;
+       recsize = descr->id_rec_size;
+       rc = osd_index_register(osd, fid, keysize, recsize);
+
+       GOTO(fini, rc);
+
+fini:
+       iam_container_fini(bag);
+       if (!rc)
+               CDEBUG(D_LFSCK, "%s: index object "DFID" (%u/%u) registered\n",
+                      osd_name(osd), PFID(fid), keysize, recsize);
+}
+
+static void osd_index_restore(const struct lu_env *env, struct osd_device *dev,
+                             struct lustre_index_restore_unit *liru,
+                             void *buf, int bufsize)
+{
+       struct osd_thread_info *info = osd_oti_get(env);
+       struct osd_inode_id *id = &info->oti_id;
+       struct lu_fid *tgt_fid = &liru->liru_cfid;
+       struct inode *bak_inode = NULL;
+       struct ldiskfs_dir_entry_2 *de = NULL;
+       struct buffer_head *bh = NULL;
+       struct dentry *dentry;
+       char *name = buf;
+       struct lu_fid bak_fid;
+       int rc;
+       ENTRY;
+
+       lustre_fid2lbx(name, tgt_fid, bufsize);
+       dentry = osd_child_dentry_by_inode(env, dev->od_index_backup_inode,
+                                          name, strlen(name));
+       bh = osd_ldiskfs_find_entry(dev->od_index_backup_inode,
+                                   &dentry->d_name, &de, NULL, NULL);
+       if (IS_ERR(bh))
+               GOTO(log, rc = PTR_ERR(bh));
+
+       osd_id_gen(id, le32_to_cpu(de->inode), OSD_OII_NOGEN);
+       brelse(bh);
+       bak_inode = osd_iget_fid(info, dev, id, &bak_fid);
+       if (IS_ERR(bak_inode))
+               GOTO(log, rc = PTR_ERR(bak_inode));
+
+       iput(bak_inode);
+       /* The OI mapping for index may be invalid, since it will be
+        * re-created, not update the OI mapping, just cache it in RAM. */
+       osd_id_gen(id, liru->liru_clid, OSD_OII_NOGEN);
+       osd_add_oi_cache(info, dev, id, tgt_fid);
+       rc = lustre_index_restore(env, &dev->od_dt_dev, &liru->liru_pfid,
+                                 tgt_fid, &bak_fid, liru->liru_name,
+                                 &dev->od_index_backup_list, &dev->od_lock,
+                                 buf, bufsize);
+       GOTO(log, rc);
+
+log:
+       CDEBUG(D_WARNING, "%s: restore index '%s' with "DFID": rc = %d\n",
+              osd_name(dev), liru->liru_name, PFID(tgt_fid), rc);
+}
+
 /**
  * osd_ios_scan_one() - check/fix LMA FID and OI entry for one inode
  *
@@ -1498,37 +1883,80 @@ osd_ios_new_item(struct osd_device *dev, struct dentry *dentry,
  */
 static int
 osd_ios_scan_one(struct osd_thread_info *info, struct osd_device *dev,
-                struct inode *inode, const struct lu_fid *fid, int flags)
+                struct inode *parent, struct inode *inode,
+                const struct lu_fid *fid, const char *name,
+                int namelen, int flags)
 {
-       struct lustre_mdt_attrs *lma    = &info->oti_mdt_attrs;
+       struct lustre_mdt_attrs *lma    = &info->oti_ost_attrs.loa_lma;
        struct osd_inode_id     *id     = &info->oti_id;
        struct osd_inode_id     *id2    = &info->oti_id2;
-       struct osd_scrub        *scrub  = &dev->od_scrub;
+       struct lustre_scrub *scrub = &dev->od_scrub.os_scrub;
        struct scrub_file       *sf     = &scrub->os_file;
        struct lu_fid            tfid;
        int                      rc;
        ENTRY;
 
-       CDEBUG(D_LFSCK, "init scan one: ino = %ld\n", inode->i_ino);
+       if (!inode) {
+               CDEBUG(D_INODE, "%s: child '%.*s' lacks inode: rc = -2\n",
+                      osd_name(dev), namelen, name);
+               RETURN(-ENOENT);
+       }
+
+       rc = osd_get_lma(info, inode, &info->oti_obj_dentry,
+                        &info->oti_ost_attrs);
+       if (rc != 0 && rc != -ENODATA) {
+               CDEBUG(D_LFSCK, "%s: fail to get lma for init OI scrub: "
+                      "rc = %d\n", osd_name(dev), rc);
 
-       rc = osd_get_lma(info, inode, &info->oti_obj_dentry, lma);
-       if (rc != 0 && rc != -ENODATA)
                RETURN(rc);
+       }
 
        osd_id_gen(id, inode->i_ino, inode->i_generation);
        if (rc == -ENODATA) {
-               if (fid == NULL || fid_is_zero(fid) || flags & OLF_HIDE_FID)
+               if (fid == NULL || fid_is_zero(fid) || flags & OLF_HIDE_FID) {
                        lu_igif_build(&tfid, inode->i_ino, inode->i_generation);
-               else
+               } else {
                        tfid = *fid;
+                       if (flags & OLF_IDX_IN_FID) {
+                               LASSERT(dev->od_index >= 0);
+
+                               tfid.f_oid = dev->od_index;
+                       }
+               }
                rc = osd_ea_fid_set(info, inode, &tfid, 0, 0);
-               if (rc != 0)
+               if (rc != 0) {
+                       CDEBUG(D_LFSCK, "%s: fail to set LMA for init OI "
+                             "scrub: rc = %d\n", osd_name(dev), rc);
+
                        RETURN(rc);
+               }
        } else {
                if (lma->lma_compat & LMAC_NOT_IN_OI)
                        RETURN(0);
 
                tfid = lma->lma_self_fid;
+               if (lma->lma_compat & LMAC_IDX_BACKUP &&
+                   osd_index_need_recreate(info->oti_env, dev, inode)) {
+                       struct lu_fid *pfid = &info->oti_fid3;
+
+                       if (parent == osd_sb(dev)->s_root->d_inode) {
+                               lu_local_obj_fid(pfid, OSD_FS_ROOT_OID);
+                       } else {
+                               rc = osd_scrub_get_fid(info, dev, parent, pfid,
+                                                      false);
+                               if (rc)
+                                       RETURN(rc);
+                       }
+
+                       rc = lustre_liru_new(&dev->od_index_restore_list, pfid,
+                                       &tfid, inode->i_ino, name, namelen);
+
+                       RETURN(rc);
+               }
+
+               if (!(flags & OLF_NOT_BACKUP))
+                       osd_ios_index_register(info->oti_env, dev, &tfid,
+                                              inode);
        }
 
        rc = osd_oi_lookup(info, dev, &tfid, id2, 0);
@@ -1537,7 +1965,7 @@ osd_ios_scan_one(struct osd_thread_info *info, struct osd_device *dev,
                        RETURN(rc);
 
                rc = osd_scrub_refresh_mapping(info, dev, &tfid, id,
-                                              DTO_INDEX_INSERT, true, 0);
+                                              DTO_INDEX_INSERT, true, 0, NULL);
                if (rc > 0)
                        rc = 0;
 
@@ -1548,16 +1976,14 @@ osd_ios_scan_one(struct osd_thread_info *info, struct osd_device *dev,
                RETURN(0);
 
        if (!(sf->sf_flags & SF_INCONSISTENT)) {
-               osd_scrub_file_reset(scrub,
-                                    LDISKFS_SB(osd_sb(dev))->s_es->s_uuid,
-                                    SF_INCONSISTENT);
-               rc = osd_scrub_file_store(scrub);
+               scrub_file_reset(scrub, dev->od_uuid, SF_INCONSISTENT);
+               rc = scrub_file_store(info->oti_env, scrub);
                if (rc != 0)
                        RETURN(rc);
        }
 
        rc = osd_scrub_refresh_mapping(info, dev, &tfid, id,
-                                      DTO_INDEX_UPDATE, true, 0);
+                                      DTO_INDEX_UPDATE, true, 0, NULL);
        if (rc > 0)
                rc = 0;
 
@@ -1566,12 +1992,18 @@ osd_ios_scan_one(struct osd_thread_info *info, struct osd_device *dev,
 
 /**
  * It scans the /lost+found, and for the OST-object (with filter_fid
- * or filter_fid_old), move them back to its proper /O/<seq>/d<x>.
+ * or filter_fid_18_23), move them back to its proper /O/<seq>/d<x>.
  */
-static int osd_ios_lf_fill(void *buf, const char *name, int namelen,
+#ifdef HAVE_FILLDIR_USE_CTX
+static int osd_ios_lf_fill(struct dir_context *buf,
+#else
+static int osd_ios_lf_fill(void *buf,
+#endif
+                          const char *name, int namelen,
                           loff_t offset, __u64 ino, unsigned d_type)
 {
-       struct osd_ios_filldir_buf *fill_buf = buf;
+       struct osd_ios_filldir_buf *fill_buf =
+               (struct osd_ios_filldir_buf *)buf;
        struct osd_thread_info     *info     = fill_buf->oifb_info;
        struct osd_device          *dev      = fill_buf->oifb_dev;
        struct lu_fid              *fid      = &info->oti_fid;
@@ -1583,16 +2015,23 @@ static int osd_ios_lf_fill(void *buf, const char *name, int namelen,
        int                         rc;
        ENTRY;
 
+       fill_buf->oifb_items++;
+
        /* skip any '.' started names */
        if (name[0] == '.')
                RETURN(0);
 
        scrub->os_lf_scanned++;
-       child = osd_ios_lookup_one_len(name, parent, namelen);
+       child = osd_lookup_one_len_unlocked(dev, name, parent, namelen);
        if (IS_ERR(child)) {
-               CWARN("%s: cannot lookup child '%.*s': rc = %d\n",
+               CDEBUG(D_LFSCK, "%s: cannot lookup child '%.*s': rc = %d\n",
                      osd_name(dev), namelen, name, (int)PTR_ERR(child));
                RETURN(0);
+       } else if (!child->d_inode) {
+               dput(child);
+               CDEBUG(D_INODE, "%s: child '%.*s' lacks inode\n",
+                      osd_name(dev), namelen, name);
+               RETURN(0);
        }
 
        inode = child->d_inode;
@@ -1600,8 +2039,8 @@ static int osd_ios_lf_fill(void *buf, const char *name, int namelen,
                rc = osd_ios_new_item(dev, child, osd_ios_general_scan,
                                      osd_ios_lf_fill);
                if (rc != 0)
-                       CWARN("%s: cannot add child '%.*s': rc = %d\n",
-                             osd_name(dev), namelen, name, rc);
+                       CDEBUG(D_LFSCK, "%s: cannot add child '%.*s': "
+                             "rc = %d\n", osd_name(dev), namelen, name, rc);
                GOTO(put, rc);
        }
 
@@ -1616,8 +2055,9 @@ static int osd_ios_lf_fill(void *buf, const char *name, int namelen,
                               "/lost+found.\n", namelen, name, PFID(fid));
                        scrub->os_lf_repaired++;
                } else {
-                       CWARN("%s: cannot rename for '%.*s' "DFID": rc = %d\n",
-                             osd_name(dev), namelen, name, PFID(fid), rc);
+                       CDEBUG(D_LFSCK, "%s: cannot rename for '%.*s' "
+                              DFID": rc = %d\n",
+                              osd_name(dev), namelen, name, PFID(fid), rc);
                }
        }
 
@@ -1635,25 +2075,35 @@ put:
        return 0;
 }
 
-static int osd_ios_varfid_fill(void *buf, const char *name, int namelen,
+#ifdef HAVE_FILLDIR_USE_CTX
+static int osd_ios_varfid_fill(struct dir_context *buf,
+#else
+static int osd_ios_varfid_fill(void *buf,
+#endif
+                              const char *name, int namelen,
                               loff_t offset, __u64 ino, unsigned d_type)
 {
-       struct osd_ios_filldir_buf *fill_buf = buf;
+       struct osd_ios_filldir_buf *fill_buf =
+               (struct osd_ios_filldir_buf *)buf;
        struct osd_device          *dev      = fill_buf->oifb_dev;
        struct dentry              *child;
        int                         rc;
        ENTRY;
 
+       fill_buf->oifb_items++;
+
        /* skip any '.' started names */
        if (name[0] == '.')
                RETURN(0);
 
-       child = osd_ios_lookup_one_len(name, fill_buf->oifb_dentry, namelen);
+       child = osd_lookup_one_len_unlocked(dev, name, fill_buf->oifb_dentry,
+                                           namelen);
        if (IS_ERR(child))
                RETURN(PTR_ERR(child));
 
-       rc = osd_ios_scan_one(fill_buf->oifb_info, dev, child->d_inode,
-                             NULL, 0);
+       rc = osd_ios_scan_one(fill_buf->oifb_info, dev,
+                             fill_buf->oifb_dentry->d_inode, child->d_inode,
+                             NULL, name, namelen, 0);
        if (rc == 0 && S_ISDIR(child->d_inode->i_mode))
                rc = osd_ios_new_item(dev, child, osd_ios_general_scan,
                                      osd_ios_varfid_fill);
@@ -1662,22 +2112,30 @@ static int osd_ios_varfid_fill(void *buf, const char *name, int namelen,
        RETURN(rc);
 }
 
-static int osd_ios_dl_fill(void *buf, const char *name, int namelen,
+#ifdef HAVE_FILLDIR_USE_CTX
+static int osd_ios_dl_fill(struct dir_context *buf,
+#else
+static int osd_ios_dl_fill(void *buf,
+#endif
+                          const char *name, int namelen,
                           loff_t offset, __u64 ino, unsigned d_type)
 {
-       struct osd_ios_filldir_buf *fill_buf = buf;
+       struct osd_ios_filldir_buf *fill_buf =
+               (struct osd_ios_filldir_buf *)buf;
        struct osd_device          *dev      = fill_buf->oifb_dev;
        const struct osd_lf_map    *map;
        struct dentry              *child;
        int                         rc       = 0;
        ENTRY;
 
+       fill_buf->oifb_items++;
+
        /* skip any '.' started names */
        if (name[0] == '.')
                RETURN(0);
 
        for (map = osd_dl_maps; map->olm_name != NULL; map++) {
-               if (strlen(map->olm_name) != namelen)
+               if (map->olm_namelen != namelen)
                        continue;
 
                if (strncmp(map->olm_name, name, namelen) == 0)
@@ -1687,33 +2145,83 @@ static int osd_ios_dl_fill(void *buf, const char *name, int namelen,
        if (map->olm_name == NULL)
                RETURN(0);
 
-       child = osd_ios_lookup_one_len(name, fill_buf->oifb_dentry, namelen);
+       child = osd_lookup_one_len_unlocked(dev, name, fill_buf->oifb_dentry,
+                                           namelen);
        if (IS_ERR(child))
                RETURN(PTR_ERR(child));
 
-       rc = osd_ios_scan_one(fill_buf->oifb_info, dev, child->d_inode,
-                             &map->olm_fid, map->olm_flags);
+       rc = osd_ios_scan_one(fill_buf->oifb_info, dev,
+                             fill_buf->oifb_dentry->d_inode, child->d_inode,
+                             &map->olm_fid, name, namelen, map->olm_flags);
        dput(child);
 
        RETURN(rc);
 }
 
-static int osd_ios_root_fill(void *buf, const char *name, int namelen,
+#ifdef HAVE_FILLDIR_USE_CTX
+static int osd_ios_uld_fill(struct dir_context *buf,
+#else
+static int osd_ios_uld_fill(void *buf,
+#endif
+                           const char *name, int namelen,
+                           loff_t offset, __u64 ino, unsigned d_type)
+{
+       struct osd_ios_filldir_buf *fill_buf =
+               (struct osd_ios_filldir_buf *)buf;
+       struct osd_device *dev = fill_buf->oifb_dev;
+       struct dentry              *child;
+       struct lu_fid               tfid;
+       int                         rc       = 0;
+       ENTRY;
+
+       fill_buf->oifb_items++;
+
+       /* skip any non-DFID format name */
+       if (name[0] != '[')
+               RETURN(0);
+
+       child = osd_lookup_one_len_unlocked(dev, name, fill_buf->oifb_dentry,
+                                           namelen);
+       if (IS_ERR(child))
+               RETURN(PTR_ERR(child));
+
+       /* skip the start '[' */
+       sscanf(&name[1], SFID, RFID(&tfid));
+       if (fid_is_sane(&tfid))
+               rc = osd_ios_scan_one(fill_buf->oifb_info, fill_buf->oifb_dev,
+                                     fill_buf->oifb_dentry->d_inode,
+                                     child->d_inode, &tfid, name, namelen, 0);
+       else
+               rc = -EIO;
+       dput(child);
+
+       RETURN(rc);
+}
+
+#ifdef HAVE_FILLDIR_USE_CTX
+static int osd_ios_root_fill(struct dir_context *buf,
+#else
+static int osd_ios_root_fill(void *buf,
+#endif
+                            const char *name, int namelen,
                             loff_t offset, __u64 ino, unsigned d_type)
 {
-       struct osd_ios_filldir_buf *fill_buf = buf;
+       struct osd_ios_filldir_buf *fill_buf =
+               (struct osd_ios_filldir_buf *)buf;
        struct osd_device          *dev      = fill_buf->oifb_dev;
        const struct osd_lf_map    *map;
        struct dentry              *child;
        int                         rc       = 0;
        ENTRY;
 
+       fill_buf->oifb_items++;
+
        /* skip any '.' started names */
        if (name[0] == '.')
                RETURN(0);
 
        for (map = osd_lf_maps; map->olm_name != NULL; map++) {
-               if (strlen(map->olm_name) != namelen)
+               if (map->olm_namelen != namelen)
                        continue;
 
                if (strncmp(map->olm_name, name, namelen) == 0)
@@ -1723,16 +2231,21 @@ static int osd_ios_root_fill(void *buf, const char *name, int namelen,
        if (map->olm_name == NULL)
                RETURN(0);
 
-       child = osd_ios_lookup_one_len(name, fill_buf->oifb_dentry, namelen);
+       child = osd_lookup_one_len_unlocked(dev, name, fill_buf->oifb_dentry,
+                                           namelen);
        if (IS_ERR(child))
                RETURN(PTR_ERR(child));
+       else if (!child->d_inode)
+               GOTO(out_put, rc = -ENOENT);
 
        if (!(map->olm_flags & OLF_NO_OI))
-               rc = osd_ios_scan_one(fill_buf->oifb_info, dev, child->d_inode,
-                                     &map->olm_fid, map->olm_flags);
+               rc = osd_ios_scan_one(fill_buf->oifb_info, dev,
+                               fill_buf->oifb_dentry->d_inode, child->d_inode,
+                               &map->olm_fid, name, namelen, map->olm_flags);
        if (rc == 0 && map->olm_flags & OLF_SCAN_SUBITEMS)
                rc = osd_ios_new_item(dev, child, map->olm_scandir,
                                      map->olm_filldir);
+out_put:
        dput(child);
 
        RETURN(rc);
@@ -1749,7 +2262,7 @@ osd_ios_general_scan(struct osd_thread_info *info, struct osd_device *dev,
                                                .oifb_info = info,
                                                .oifb_dev = dev,
                                                .oifb_dentry = dentry };
-       struct file                  *filp  = &info->oti_it_ea.oie_file;
+       struct file                  *filp  = &info->oti_file;
        struct inode                 *inode = dentry->d_inode;
        const struct file_operations *fops  = inode->i_fop;
        int                           rc;
@@ -1758,20 +2271,24 @@ osd_ios_general_scan(struct osd_thread_info *info, struct osd_device *dev,
        LASSERT(filldir != NULL);
 
        filp->f_pos = 0;
-       filp->f_dentry = dentry;
+       filp->f_path.dentry = dentry;
        filp->f_mode = FMODE_64BITHASH;
        filp->f_mapping = inode->i_mapping;
        filp->f_op = fops;
        filp->private_data = NULL;
        set_file_inode(filp, inode);
 
+       do {
+               buf.oifb_items = 0;
 #ifdef HAVE_DIR_CONTEXT
-       buf.ctx.pos = filp->f_pos;
-       rc = fops->iterate(filp, &buf.ctx);
-       filp->f_pos = buf.ctx.pos;
+               buf.ctx.pos = filp->f_pos;
+               rc = fops->iterate_shared(filp, &buf.ctx);
+               filp->f_pos = buf.ctx.pos;
 #else
-       rc = fops->readdir(filp, &buf, filldir);
+               rc = fops->readdir(filp, &buf, filldir);
 #endif
+       } while (rc >= 0 && buf.oifb_items > 0 &&
+                filp->f_pos != LDISKFS_HTREE_EOF_64BIT);
        fops->release(inode, filp);
 
        RETURN(rc);
@@ -1781,10 +2298,10 @@ static int
 osd_ios_ROOT_scan(struct osd_thread_info *info, struct osd_device *dev,
                  struct dentry *dentry, filldir_t filldir)
 {
-       struct osd_scrub  *scrub  = &dev->od_scrub;
-       struct scrub_file *sf     = &scrub->os_file;
-       struct dentry     *child;
-       int                rc;
+       struct lustre_scrub *scrub = &dev->od_scrub.os_scrub;
+       struct scrub_file *sf = &scrub->os_file;
+       struct dentry *child;
+       int rc;
        ENTRY;
 
        /* It is existing MDT0 device. We only allow the case of object without
@@ -1801,53 +2318,58 @@ osd_ios_ROOT_scan(struct osd_thread_info *info, struct osd_device *dev,
         *      OI mapping crashed or lost also, then we have to give up under
         *      double failure cases. */
        scrub->os_convert_igif = 1;
-       child = osd_ios_lookup_one_len(dot_lustre_name, dentry,
-                                      strlen(dot_lustre_name));
+       child = osd_lookup_one_len_unlocked(dev, dot_lustre_name, dentry,
+                                           strlen(dot_lustre_name));
        if (IS_ERR(child)) {
-               rc = PTR_ERR(child);
-               if (rc == -ENOENT) {
-                       /* It is 1.8 MDT device. */
-                       if (!(sf->sf_flags & SF_UPGRADE)) {
-                               osd_scrub_file_reset(scrub,
-                                       LDISKFS_SB(osd_sb(dev))->s_es->s_uuid,
-                                       SF_UPGRADE);
-                               sf->sf_internal_flags &= ~SIF_NO_HANDLE_OLD_FID;
-                               rc = osd_scrub_file_store(scrub);
-                       } else {
-                               rc = 0;
-                       }
+               if (PTR_ERR(child) != -ENOENT)
+                       RETURN(PTR_ERR(child));
+               goto out_scrub;
+       }
+
+       /* For lustre-2.x (x <= 3), the ".lustre" has NO FID-in-LMA,
+        * so the client will get IGIF for the ".lustre" object when
+        * the MDT restart.
+        *
+        * From the OI scrub view, when the MDT upgrade to Lustre-2.4,
+        * it does not know whether there are some old clients cached
+        * the ".lustre" IGIF during the upgrading. Two choices:
+        *
+        * 1) Generate IGIF-in-LMA and IGIF-in-OI for the ".lustre".
+        *    It will allow the old connected clients to access the
+        *    ".lustre" with cached IGIF. But it will cause others
+        *    on the MDT failed to check "fid_is_dot_lustre()".
+        *
+        * 2) Use fixed FID {FID_SEQ_DOT_LUSTRE, FID_OID_DOT_LUSTRE, 0}
+        *    for ".lustre" in spite of whether there are some clients
+        *    cached the ".lustre" IGIF or not. It enables the check
+        *    "fid_is_dot_lustre()" on the MDT, although it will cause
+        *    that the old connected clients cannot access the ".lustre"
+        *    with the cached IGIF.
+        *
+        * Usually, it is rare case for the old connected clients
+        * to access the ".lustre" with cached IGIF. So we prefer
+        * to the solution 2).
+        */
+       rc = osd_ios_scan_one(info, dev, dentry->d_inode,
+                             child->d_inode, &LU_DOT_LUSTRE_FID,
+                             dot_lustre_name,
+                             strlen(dot_lustre_name), 0);
+       if (rc == -ENOENT) {
+out_scrub:
+               /* It is 1.8 MDT device. */
+               if (!(sf->sf_flags & SF_UPGRADE)) {
+                       scrub_file_reset(scrub, dev->od_uuid,
+                                        SF_UPGRADE);
+                       sf->sf_internal_flags &= ~SIF_NO_HANDLE_OLD_FID;
+                       rc = scrub_file_store(info->oti_env, scrub);
+               } else {
+                       rc = 0;
                }
-       } else {
-               /* For lustre-2.x (x <= 3), the ".lustre" has NO FID-in-LMA,
-                * so the client will get IGIF for the ".lustre" object when
-                * the MDT restart.
-                *
-                * From the OI scrub view, when the MDT upgrade to Lustre-2.4,
-                * it does not know whether there are some old clients cached
-                * the ".lustre" IGIF during the upgrading. Two choices:
-                *
-                * 1) Generate IGIF-in-LMA and IGIF-in-OI for the ".lustre".
-                *    It will allow the old connected clients to access the
-                *    ".lustre" with cached IGIF. But it will cause others
-                *    on the MDT failed to check "fid_is_dot_lustre()".
-                *
-                * 2) Use fixed FID {FID_SEQ_DOT_LUSTRE, FID_OID_DOT_LUSTRE, 0}
-                *    for ".lustre" in spite of whether there are some clients
-                *    cached the ".lustre" IGIF or not. It enables the check
-                *    "fid_is_dot_lustre()" on the MDT, although it will cause
-                *    that the old connected clients cannot access the ".lustre"
-                *    with the cached IGIF.
-                *
-                * Usually, it is rare case for the old connected clients
-                * to access the ".lustre" with cached IGIF. So we prefer
-                * to the solution 2). */
-               rc = osd_ios_scan_one(info, dev, child->d_inode,
-                                     &LU_DOT_LUSTRE_FID, 0);
-               if (rc == 0)
-                       rc = osd_ios_new_item(dev, child, osd_ios_general_scan,
-                                             osd_ios_dl_fill);
-               dput(child);
+       } else if (rc == 0) {
+               rc = osd_ios_new_item(dev, child, osd_ios_general_scan,
+                                     osd_ios_dl_fill);
        }
+       dput(child);
 
        RETURN(rc);
 }
@@ -1856,74 +2378,72 @@ static int
 osd_ios_OBJECTS_scan(struct osd_thread_info *info, struct osd_device *dev,
                     struct dentry *dentry, filldir_t filldir)
 {
-       struct osd_scrub  *scrub  = &dev->od_scrub;
-       struct scrub_file *sf     = &scrub->os_file;
-       struct dentry     *child;
-       int                rc;
+       struct lustre_scrub *scrub = &dev->od_scrub.os_scrub;
+       struct scrub_file *sf = &scrub->os_file;
+       struct dentry *child;
+       int rc;
        ENTRY;
 
        if (unlikely(sf->sf_internal_flags & SIF_NO_HANDLE_OLD_FID)) {
                sf->sf_internal_flags &= ~SIF_NO_HANDLE_OLD_FID;
-               rc = osd_scrub_file_store(scrub);
+               rc = scrub_file_store(info->oti_env, scrub);
                if (rc != 0)
                        RETURN(rc);
        }
 
-       child = osd_ios_lookup_one_len(ADMIN_USR, dentry, strlen(ADMIN_USR));
-       if (!IS_ERR(child)) {
-               rc = osd_ios_scan_one(info, dev, child->d_inode, NULL, 0);
-               dput(child);
-       } else {
+       child = osd_lookup_one_len_unlocked(dev, ADMIN_USR, dentry,
+                                           strlen(ADMIN_USR));
+       if (IS_ERR(child)) {
                rc = PTR_ERR(child);
+       } else {
+               rc = osd_ios_scan_one(info, dev, dentry->d_inode,
+                                     child->d_inode, NULL, ADMIN_USR,
+                                     strlen(ADMIN_USR), 0);
+               dput(child);
        }
 
        if (rc != 0 && rc != -ENOENT)
-               RETURN(rc);
-
-       child = osd_ios_lookup_one_len(ADMIN_GRP, dentry, strlen(ADMIN_GRP));
-       if (!IS_ERR(child)) {
-               rc = osd_ios_scan_one(info, dev, child->d_inode, NULL, 0);
-               dput(child);
-       } else {
-               rc = PTR_ERR(child);
-       }
+               GOTO(out, rc);
 
-       if (rc == -ENOENT)
-               rc = 0;
+       child = osd_lookup_one_len_unlocked(dev, ADMIN_GRP, dentry,
+                                           strlen(ADMIN_GRP));
+       if (IS_ERR(child))
+               GOTO(out, rc = PTR_ERR(child));
 
-       RETURN(rc);
+       rc = osd_ios_scan_one(info, dev, dentry->d_inode,
+                             child->d_inode, NULL, ADMIN_GRP,
+                             strlen(ADMIN_GRP), 0);
+       dput(child);
+out:
+       RETURN(rc == -ENOENT ? 0 : rc);
 }
 
-static int osd_initial_OI_scrub(struct osd_thread_info *info,
-                               struct osd_device *dev)
+static void osd_initial_OI_scrub(struct osd_thread_info *info,
+                                struct osd_device *dev)
 {
        struct osd_ios_item     *item    = NULL;
        scandir_t                scandir = osd_ios_general_scan;
        filldir_t                filldir = osd_ios_root_fill;
        struct dentry           *dentry  = osd_sb(dev)->s_root;
        const struct osd_lf_map *map     = osd_lf_maps;
-       int                      rc;
        ENTRY;
 
        /* Lookup IGIF in OI by force for initial OI scrub. */
        dev->od_igif_inoi = 1;
 
        while (1) {
-               rc = scandir(info, dev, dentry, filldir);
+               scandir(info, dev, dentry, filldir);
                if (item != NULL) {
                        dput(item->oii_dentry);
                        OBD_FREE_PTR(item);
                }
 
-               if (rc != 0)
-                       break;
-
-               if (cfs_list_empty(&dev->od_ios_list))
+               if (list_empty(&dev->od_ios_list))
                        break;
 
-               item = cfs_list_entry(dev->od_ios_list.next,
-                                     struct osd_ios_item, oii_list);
-               cfs_list_del_init(&item->oii_list);
+               item = list_entry(dev->od_ios_list.next,
+                                 struct osd_ios_item, oii_list);
+               list_del_init(&item->oii_list);
 
                LASSERT(item->oii_scandir != NULL);
                scandir = item->oii_scandir;
@@ -1931,17 +2451,6 @@ static int osd_initial_OI_scrub(struct osd_thread_info *info,
                dentry = item->oii_dentry;
        }
 
-       while (!cfs_list_empty(&dev->od_ios_list)) {
-               item = cfs_list_entry(dev->od_ios_list.next,
-                                     struct osd_ios_item, oii_list);
-               cfs_list_del_init(&item->oii_list);
-               dput(item->oii_dentry);
-               OBD_FREE_PTR(item);
-       }
-
-       if (rc != 0)
-               RETURN(rc);
-
        /* There maybe the case that the object has been removed, but its OI
         * mapping is still in the OI file, such as the "CATALOGS" after MDT
         * file-level backup/restore. So here cleanup the stale OI mappings. */
@@ -1953,19 +2462,45 @@ static int osd_initial_OI_scrub(struct osd_thread_info *info,
                        continue;
                }
 
-               child = osd_ios_lookup_one_len(map->olm_name,
-                                              osd_sb(dev)->s_root,
-                                              strlen(map->olm_name));
-               if (!IS_ERR(child))
-                       dput(child);
-               else if (PTR_ERR(child) == -ENOENT)
+               child = osd_lookup_one_len_unlocked(dev, map->olm_name,
+                                                   osd_sb(dev)->s_root,
+                                                   map->olm_namelen);
+               if (PTR_ERR(child) == -ENOENT ||
+                   (!IS_ERR(child) && !child->d_inode))
                        osd_scrub_refresh_mapping(info, dev, &map->olm_fid,
                                                  NULL, DTO_INDEX_DELETE,
-                                                 true, 0);
+                                                 true, 0, NULL);
+               if (!IS_ERR(child))
+                       dput(child);
                map++;
        }
 
-       RETURN(0);
+       if (!list_empty(&dev->od_index_restore_list)) {
+               char *buf;
+
+               OBD_ALLOC_LARGE(buf, INDEX_BACKUP_BUFSIZE);
+               if (!buf)
+                       CERROR("%s: not enough RAM for rebuild index\n",
+                              osd_name(dev));
+
+               while (!list_empty(&dev->od_index_restore_list)) {
+                       struct lustre_index_restore_unit *liru;
+
+                       liru = list_entry(dev->od_index_restore_list.next,
+                                         struct lustre_index_restore_unit,
+                                         liru_link);
+                       list_del(&liru->liru_link);
+                       if (buf)
+                               osd_index_restore(info->oti_env, dev, liru,
+                                                 buf, INDEX_BACKUP_BUFSIZE);
+                       OBD_FREE(liru, liru->liru_len);
+               }
+
+               if (buf)
+                       OBD_FREE_LARGE(buf, INDEX_BACKUP_BUFSIZE);
+       }
+
+       EXIT;
 }
 
 char *osd_lf_fid2name(const struct lu_fid *fid)
@@ -1989,97 +2524,39 @@ char *osd_lf_fid2name(const struct lu_fid *fid)
 
 /* OI scrub start/stop */
 
-static int do_osd_scrub_start(struct osd_device *dev, __u32 flags)
-{
-       struct osd_scrub     *scrub  = &dev->od_scrub;
-       struct ptlrpc_thread *thread = &scrub->os_thread;
-       struct l_wait_info    lwi    = { 0 };
-       struct task_struct   *task;
-       int                   rc;
-       ENTRY;
-
-again:
-       /* os_lock: sync status between stop and scrub thread */
-       spin_lock(&scrub->os_lock);
-       if (thread_is_running(thread)) {
-               spin_unlock(&scrub->os_lock);
-               RETURN(-EALREADY);
-       } else if (unlikely(thread_is_stopping(thread))) {
-               spin_unlock(&scrub->os_lock);
-               l_wait_event(thread->t_ctl_waitq,
-                            thread_is_stopped(thread),
-                            &lwi);
-               goto again;
-       }
-       spin_unlock(&scrub->os_lock);
-
-       if (scrub->os_file.sf_status == SS_COMPLETED) {
-               if (!(flags & SS_SET_FAILOUT))
-                       flags |= SS_CLEAR_FAILOUT;
-
-               if (!(flags & SS_SET_DRYRUN))
-                       flags |= SS_CLEAR_DRYRUN;
-
-               flags |= SS_RESET;
-       }
-
-       scrub->os_start_flags = flags;
-       thread_set_flags(thread, 0);
-       task = kthread_run(osd_scrub_main, dev, "OI_scrub");
-       if (IS_ERR(task)) {
-               rc = PTR_ERR(task);
-               CERROR("%.16s: cannot start iteration thread: rc = %d\n",
-                      LDISKFS_SB(osd_sb(dev))->s_es->s_volume_name, rc);
-               RETURN(rc);
-       }
-
-       l_wait_event(thread->t_ctl_waitq,
-                    thread_is_running(thread) || thread_is_stopped(thread),
-                    &lwi);
-
-       RETURN(0);
-}
-
-int osd_scrub_start(struct osd_device *dev)
+int osd_scrub_start(const struct lu_env *env, struct osd_device *dev,
+                   __u32 flags)
 {
+       struct lustre_scrub *scrub = &dev->od_scrub.os_scrub;
        int rc;
        ENTRY;
 
+       if (dev->od_dt_dev.dd_rdonly)
+               RETURN(-EROFS);
+
        /* od_otable_mutex: prevent curcurrent start/stop */
        mutex_lock(&dev->od_otable_mutex);
-       rc = do_osd_scrub_start(dev, SS_AUTO);
+       rc = scrub_start(osd_scrub_main, scrub, dev, flags);
+       if (rc == -EALREADY) {
+               rc = 0;
+               if ((scrub->os_file.sf_flags & SF_AUTO ||
+                    scrub->os_partial_scan) &&
+                   !(flags & SS_AUTO_PARTIAL))
+                       osd_scrub_join(env, dev, flags, false);
+       }
        mutex_unlock(&dev->od_otable_mutex);
 
-       RETURN(rc == -EALREADY ? 0 : rc);
+       RETURN(rc);
 }
 
-static void do_osd_scrub_stop(struct osd_scrub *scrub)
+void osd_scrub_stop(struct osd_device *dev)
 {
-       struct ptlrpc_thread *thread = &scrub->os_thread;
-       struct l_wait_info    lwi    = { 0 };
+       struct lustre_scrub *scrub = &dev->od_scrub.os_scrub;
 
-       /* os_lock: sync status between stop and scrub thread */
-       spin_lock(&scrub->os_lock);
-       if (!thread_is_init(thread) && !thread_is_stopped(thread)) {
-               thread_set_flags(thread, SVC_STOPPING);
-               spin_unlock(&scrub->os_lock);
-               wake_up_all(&thread->t_ctl_waitq);
-               l_wait_event(thread->t_ctl_waitq,
-                            thread_is_stopped(thread),
-                            &lwi);
-               /* Do not skip the last lock/unlock, which can guarantee that
-                * the caller cannot return until the OI scrub thread exit. */
-               spin_lock(&scrub->os_lock);
-       }
-       spin_unlock(&scrub->os_lock);
-}
-
-static void osd_scrub_stop(struct osd_device *dev)
-{
        /* od_otable_mutex: prevent curcurrent start/stop */
        mutex_lock(&dev->od_otable_mutex);
-       dev->od_scrub.os_paused = 1;
-       do_osd_scrub_stop(&dev->od_scrub);
+       scrub->os_paused = 1;
+       scrub_stop(scrub);
        mutex_unlock(&dev->od_otable_mutex);
 }
 
@@ -2089,56 +2566,73 @@ static const char osd_scrub_name[] = "OI_scrub";
 
 int osd_scrub_setup(const struct lu_env *env, struct osd_device *dev)
 {
-       struct osd_thread_info     *info   = osd_oti_get(env);
-       struct osd_scrub           *scrub  = &dev->od_scrub;
-       struct lvfs_run_ctxt       *ctxt   = &scrub->os_ctxt;
-       struct scrub_file          *sf     = &scrub->os_file;
-       struct super_block         *sb     = osd_sb(dev);
-       struct ldiskfs_super_block *es     = LDISKFS_SB(sb)->s_es;
-       struct lvfs_run_ctxt        saved;
-       struct file                *filp;
-       struct inode               *inode;
-       struct lu_fid              *fid    = &info->oti_fid;
-       int                         dirty  = 0;
-       int                         rc     = 0;
+       struct osd_thread_info *info = osd_oti_get(env);
+       struct lustre_scrub *scrub = &dev->od_scrub.os_scrub;
+       struct lvfs_run_ctxt *ctxt = &dev->od_scrub.os_ctxt;
+       struct scrub_file *sf = &scrub->os_file;
+       struct super_block *sb = osd_sb(dev);
+       struct lvfs_run_ctxt saved;
+       struct file *filp;
+       struct inode *inode;
+       struct lu_fid *fid = &info->oti_fid;
+       struct osd_inode_id *id = &info->oti_id;
+       struct dt_object *obj;
+       bool dirty = false;
+       bool restored = false;
+       int rc = 0;
        ENTRY;
 
-       memset(scrub, 0, sizeof(*scrub));
+       memset(&dev->od_scrub, 0, sizeof(struct osd_scrub));
        OBD_SET_CTXT_MAGIC(ctxt);
        ctxt->pwdmnt = dev->od_mnt;
        ctxt->pwd = dev->od_mnt->mnt_root;
-       ctxt->fs = get_ds();
+       ctxt->fs = KERNEL_DS;
 
        init_waitqueue_head(&scrub->os_thread.t_ctl_waitq);
        init_rwsem(&scrub->os_rwsem);
        spin_lock_init(&scrub->os_lock);
-       CFS_INIT_LIST_HEAD(&scrub->os_inconsistent_items);
+       INIT_LIST_HEAD(&scrub->os_inconsistent_items);
+       scrub->os_name = osd_name(dev);
 
        push_ctxt(&saved, ctxt);
-       filp = filp_open(osd_scrub_name, O_RDWR | O_CREAT, 0644);
+       filp = filp_open(osd_scrub_name, O_RDWR |
+                        (dev->od_dt_dev.dd_rdonly ? 0 : O_CREAT), 0644);
        if (IS_ERR(filp)) {
                pop_ctxt(&saved, ctxt);
                RETURN(PTR_ERR(filp));
        }
 
-       inode = filp->f_dentry->d_inode;
-       /* 'What the @fid is' is not imporatant, because the object
-        * has no OI mapping, and only is visible inside the OSD.*/
-       lu_igif_build(fid, inode->i_ino, inode->i_generation);
-       rc = osd_ea_fid_set(info, inode, fid, LMAC_NOT_IN_OI, 0);
-       if (rc != 0) {
-               filp_close(filp, 0);
-               pop_ctxt(&saved, ctxt);
-               RETURN(rc);
+       inode = file_inode(filp);
+       if (!dev->od_dt_dev.dd_rdonly) {
+               /* 'What the @fid is' is not imporatant, because the object
+                * has no OI mapping, and only is visible inside the OSD.*/
+               lu_igif_build(fid, inode->i_ino, inode->i_generation);
+               rc = osd_ea_fid_set(info, inode, fid, LMAC_NOT_IN_OI, 0);
+               if (rc) {
+                       filp_close(filp, NULL);
+                       pop_ctxt(&saved, ctxt);
+                       RETURN(rc);
+               }
        }
 
-       scrub->os_inode = igrab(inode);
-       filp_close(filp, 0);
+       osd_id_gen(id, inode->i_ino, inode->i_generation);
+       osd_add_oi_cache(info, dev, id, fid);
+       filp_close(filp, NULL);
        pop_ctxt(&saved, ctxt);
 
-       rc = osd_scrub_file_load(scrub);
-       if (rc == -ENOENT) {
-               osd_scrub_file_init(scrub, es->s_uuid);
+       obj = lu2dt(lu_object_find_slice(env, osd2lu_dev(dev), fid, NULL));
+       if (IS_ERR_OR_NULL(obj))
+               RETURN(obj ? PTR_ERR(obj) : -ENOENT);
+
+#ifndef HAVE_S_UUID_AS_UUID_T
+       memcpy(dev->od_uuid.b, sb->s_uuid, UUID_SIZE);
+#else
+       uuid_copy(&dev->od_uuid, &sb->s_uuid);
+#endif
+       scrub->os_obj = obj;
+       rc = scrub_file_load(env, scrub);
+       if (rc == -ENOENT || rc == -EFAULT) {
+               scrub_file_init(scrub, dev->od_uuid);
                /* If the "/O" dir does not exist when mount (indicated by
                 * osd_device::od_maybe_new), neither for the "/OI_scrub",
                 * then it is quite probably that the device is a new one,
@@ -2148,21 +2642,33 @@ int osd_scrub_setup(const struct lu_env *env, struct osd_device *dev)
                 * an old device, it can be found and cleared later.
                 *
                 * For the system with "SIF_NO_HANDLE_OLD_FID", we do not
-                * need to check "filter_fid_old" and to convert it to
+                * need to check "filter_fid_18_23" and to convert it to
                 * "filter_fid" for each object, and all the IGIF should
                 * have their FID mapping in OI files already. */
-               if (dev->od_maybe_new)
+               if (dev->od_maybe_new && rc == -ENOENT)
                        sf->sf_internal_flags = SIF_NO_HANDLE_OLD_FID;
-               dirty = 1;
-       } else if (rc != 0) {
-               RETURN(rc);
+               dirty = true;
+       } else if (rc < 0) {
+               GOTO(cleanup_obj, rc);
        } else {
-               if (memcmp(sf->sf_uuid, es->s_uuid, 16) != 0) {
-                       osd_scrub_file_reset(scrub, es->s_uuid,SF_INCONSISTENT);
-                       dirty = 1;
+               if (!uuid_equal(&sf->sf_uuid, &dev->od_uuid)) {
+                       CDEBUG(D_LFSCK,
+                              "%s: UUID has been changed from %pU to %pU\n",
+                              osd_dev2name(dev), &sf->sf_uuid, &dev->od_uuid);
+                       scrub_file_reset(scrub, dev->od_uuid, SF_INCONSISTENT);
+                       dirty = true;
+                       restored = true;
                } else if (sf->sf_status == SS_SCANNING) {
                        sf->sf_status = SS_CRASHED;
-                       dirty = 1;
+                       dirty = true;
+               }
+
+               if ((sf->sf_oi_count & (sf->sf_oi_count - 1)) != 0) {
+                       LCONSOLE_WARN("%s: invalid oi count %d, set it to %d\n",
+                                     osd_dev2name(dev), sf->sf_oi_count,
+                                     osd_oi_count);
+                       sf->sf_oi_count = osd_oi_count;
+                       dirty = true;
                }
        }
 
@@ -2171,51 +2677,55 @@ int osd_scrub_setup(const struct lu_env *env, struct osd_device *dev)
        else
                scrub->os_pos_current = LDISKFS_FIRST_INO(sb) + 1;
 
-       if (dirty != 0) {
-               rc = osd_scrub_file_store(scrub);
-               if (rc != 0)
-                       RETURN(rc);
+       if (dirty) {
+               rc = scrub_file_store(env, scrub);
+               if (rc)
+                       GOTO(cleanup_obj, rc);
        }
 
        /* Initialize OI files. */
-       rc = osd_oi_init(info, dev);
+       rc = osd_oi_init(info, dev, restored);
        if (rc < 0)
-               RETURN(rc);
+               GOTO(cleanup_obj, rc);
 
-       rc = osd_initial_OI_scrub(info, dev);
-       if (rc == 0) {
-               if (sf->sf_flags & SF_UPGRADE ||
-                   !(sf->sf_internal_flags & SIF_NO_HANDLE_OLD_FID ||
-                     sf->sf_success_count > 0)) {
-                       dev->od_igif_inoi = 0;
-                       dev->od_check_ff = dev->od_is_ost;
-               } else {
-                       dev->od_igif_inoi = 1;
-                       dev->od_check_ff = 0;
-               }
-
-               if (sf->sf_flags & SF_INCONSISTENT)
-                       /* The 'od_igif_inoi' will be set under the
-                        * following cases:
-                        * 1) new created system, or
-                        * 2) restored from file-level backup, or
-                        * 3) the upgrading completed.
-                        *
-                        * The 'od_igif_inoi' may be cleared by OI scrub
-                        * later if found that the system is upgrading. */
-                       dev->od_igif_inoi = 1;
+       if (!dev->od_dt_dev.dd_rdonly)
+               osd_initial_OI_scrub(info, dev);
 
-               if (!dev->od_noscrub &&
-                   ((sf->sf_status == SS_PAUSED) ||
-                    (sf->sf_status == SS_CRASHED &&
-                     sf->sf_flags & (SF_RECREATED | SF_INCONSISTENT |
-                                     SF_UPGRADE | SF_AUTO)) ||
-                    (sf->sf_status == SS_INIT &&
-                     sf->sf_flags & (SF_RECREATED | SF_INCONSISTENT |
-                                     SF_UPGRADE))))
-                       rc = osd_scrub_start(dev);
+       if (sf->sf_flags & SF_UPGRADE ||
+           !(sf->sf_internal_flags & SIF_NO_HANDLE_OLD_FID ||
+             sf->sf_success_count > 0)) {
+               dev->od_igif_inoi = 0;
+               dev->od_check_ff = dev->od_is_ost;
+       } else {
+               dev->od_igif_inoi = 1;
+               dev->od_check_ff = 0;
        }
 
+       if (sf->sf_flags & SF_INCONSISTENT)
+               /* The 'od_igif_inoi' will be set under the
+                * following cases:
+                * 1) new created system, or
+                * 2) restored from file-level backup, or
+                * 3) the upgrading completed.
+                *
+                * The 'od_igif_inoi' may be cleared by OI scrub
+                * later if found that the system is upgrading. */
+               dev->od_igif_inoi = 1;
+
+       if (!dev->od_dt_dev.dd_rdonly &&
+           dev->od_auto_scrub_interval != AS_NEVER &&
+           ((sf->sf_status == SS_PAUSED) ||
+            (sf->sf_status == SS_CRASHED &&
+             sf->sf_flags & (SF_RECREATED | SF_INCONSISTENT |
+                             SF_UPGRADE | SF_AUTO)) ||
+            (sf->sf_status == SS_INIT &&
+             sf->sf_flags & (SF_RECREATED | SF_INCONSISTENT |
+                             SF_UPGRADE))))
+               rc = osd_scrub_start(env, dev, SS_AUTO_FULL);
+
+       if (rc != 0)
+               GOTO(cleanup_oi, rc);
+
        /* it is possible that dcache entries may keep objects after they are
         * deleted by OSD. While it looks safe this can cause object data to
         * stay until umount causing failures in tests calculating free space,
@@ -2223,19 +2733,26 @@ int osd_scrub_setup(const struct lu_env *env, struct osd_device *dev)
         * anymore let's just free them after use here */
        shrink_dcache_sb(sb);
 
-       RETURN(rc);
+       RETURN(0);
+cleanup_oi:
+       osd_oi_fini(info, dev);
+cleanup_obj:
+       dt_object_put_nocache(env, scrub->os_obj);
+       scrub->os_obj = NULL;
+
+       return rc;
 }
 
 void osd_scrub_cleanup(const struct lu_env *env, struct osd_device *dev)
 {
-       struct osd_scrub *scrub = &dev->od_scrub;
+       struct lustre_scrub *scrub = &dev->od_scrub.os_scrub;
 
        LASSERT(dev->od_otable_it == NULL);
 
-       if (scrub->os_inode != NULL) {
+       if (scrub->os_obj != NULL) {
                osd_scrub_stop(dev);
-               iput(scrub->os_inode);
-               scrub->os_inode = NULL;
+               dt_object_put_nocache(env, scrub->os_obj);
+               scrub->os_obj = NULL;
        }
        if (dev->od_oi_table != NULL)
                osd_oi_fini(osd_oti_get(env), dev);
@@ -2244,13 +2761,12 @@ void osd_scrub_cleanup(const struct lu_env *env, struct osd_device *dev)
 /* object table based iteration APIs */
 
 static struct dt_it *osd_otable_it_init(const struct lu_env *env,
-                                      struct dt_object *dt, __u32 attr,
-                                      struct lustre_capa *capa)
+                                      struct dt_object *dt, __u32 attr)
 {
        enum dt_otable_it_flags flags = attr >> DT_OTABLE_IT_FLAGS_SHIFT;
        enum dt_otable_it_valid valid = attr & ~DT_OTABLE_IT_FLAGS_MASK;
        struct osd_device      *dev   = osd_dev(dt->do_lu.lo_dev);
-       struct osd_scrub       *scrub = &dev->od_scrub;
+       struct lustre_scrub *scrub = &dev->od_scrub.os_scrub;
        struct osd_otable_it   *it;
        __u32                   start = 0;
        int                     rc;
@@ -2288,15 +2804,19 @@ static struct dt_it *osd_otable_it_init(const struct lu_env *env,
                        start |= SS_CLEAR_DRYRUN;
        }
 
-       rc = do_osd_scrub_start(dev, start);
-       if (rc < 0 && rc != -EALREADY) {
+       rc = scrub_start(osd_scrub_main, scrub, dev, start & ~SS_AUTO_PARTIAL);
+       if (rc == -EALREADY) {
+               it->ooi_cache.ooc_pos_preload = scrub->os_pos_current;
+       } else  if (rc < 0) {
                dev->od_otable_it = NULL;
                OBD_FREE_PTR(it);
-               GOTO(out, it = ERR_PTR(rc));
+               it = ERR_PTR(rc);
+       } else {
+               /* We have to start from the begining. */
+               it->ooi_cache.ooc_pos_preload =
+                       LDISKFS_FIRST_INO(osd_sb(dev)) + 1;
        }
 
-       it->ooi_cache.ooc_pos_preload = scrub->os_pos_current;
-
        GOTO(out, it);
 
 out:
@@ -2311,7 +2831,7 @@ static void osd_otable_it_fini(const struct lu_env *env, struct dt_it *di)
 
        /* od_otable_mutex: prevent curcurrent init/fini */
        mutex_lock(&dev->od_otable_mutex);
-       do_osd_scrub_stop(&dev->od_scrub);
+       scrub_stop(&dev->od_scrub.os_scrub);
        LASSERT(dev->od_otable_it == it);
 
        dev->od_otable_it = NULL;
@@ -2330,7 +2850,7 @@ static void osd_otable_it_put(const struct lu_env *env, struct dt_it *di)
 }
 
 static inline int
-osd_otable_it_wakeup(struct osd_scrub *scrub, struct osd_otable_it *it)
+osd_otable_it_wakeup(struct lustre_scrub *scrub, struct osd_otable_it *it)
 {
        spin_lock(&scrub->os_lock);
        if (it->ooi_cache.ooc_pos_preload < scrub->os_pos_current ||
@@ -2348,7 +2868,7 @@ static int osd_otable_it_next(const struct lu_env *env, struct dt_it *di)
 {
        struct osd_otable_it    *it     = (struct osd_otable_it *)di;
        struct osd_device       *dev    = it->ooi_dev;
-       struct osd_scrub        *scrub  = &dev->od_scrub;
+       struct lustre_scrub *scrub = &dev->od_scrub.os_scrub;
        struct osd_otable_cache *ooc    = &it->ooi_cache;
        struct ptlrpc_thread    *thread = &scrub->os_thread;
        struct l_wait_info       lwi    = { 0 };
@@ -2449,7 +2969,8 @@ static int osd_otable_it_load(const struct lu_env *env,
        struct osd_otable_it    *it    = (struct osd_otable_it *)di;
        struct osd_device       *dev   = it->ooi_dev;
        struct osd_otable_cache *ooc   = &it->ooi_cache;
-       struct osd_scrub        *scrub = &dev->od_scrub;
+       struct lustre_scrub *scrub = &dev->od_scrub.os_scrub;
+       struct osd_iit_param    *param = &it->ooi_iit_param;
        int                      rc;
        ENTRY;
 
@@ -2457,10 +2978,14 @@ static int osd_otable_it_load(const struct lu_env *env,
        if (it->ooi_user_ready)
                RETURN(-EPERM);
 
+       LASSERT(!scrub->os_partial_scan);
+
        if (hash > OSD_OTABLE_MAX_HASH)
                hash = OSD_OTABLE_MAX_HASH;
 
-       ooc->ooc_pos_preload = hash;
+       /* The hash is the last checkpoint position,
+        * we will start from the next one. */
+       ooc->ooc_pos_preload = hash + 1;
        if (ooc->ooc_pos_preload <= LDISKFS_FIRST_INO(osd_sb(dev)))
                ooc->ooc_pos_preload = LDISKFS_FIRST_INO(osd_sb(dev)) + 1;
 
@@ -2468,6 +2993,15 @@ static int osd_otable_it_load(const struct lu_env *env,
        if (!scrub->os_full_speed)
                wake_up_all(&scrub->os_thread.t_ctl_waitq);
 
+       memset(param, 0, sizeof(*param));
+       param->sb = osd_sb(dev);
+       param->start = ooc->ooc_pos_preload;
+       param->bg = (ooc->ooc_pos_preload - 1) /
+                   LDISKFS_INODES_PER_GROUP(param->sb);
+       param->offset = (ooc->ooc_pos_preload - 1) %
+                       LDISKFS_INODES_PER_GROUP(param->sb);
+       param->gbase = 1 + param->bg * LDISKFS_INODES_PER_GROUP(param->sb);
+
        /* Unplug OSD layer iteration by the first next() call. */
        rc = osd_otable_it_next(env, (struct dt_it *)it);
 
@@ -2498,34 +3032,57 @@ const struct dt_index_operations osd_otable_ops = {
 
 /* high priority inconsistent items list APIs */
 
+#define SCRUB_BAD_OIMAP_DECAY_INTERVAL 60
+
 int osd_oii_insert(struct osd_device *dev, struct osd_idmap_cache *oic,
                   int insert)
 {
        struct osd_inconsistent_item *oii;
-       struct osd_scrub             *scrub  = &dev->od_scrub;
-       struct ptlrpc_thread         *thread = &scrub->os_thread;
-       int                           wakeup = 0;
+       struct osd_scrub *oscrub = &dev->od_scrub;
+       struct lustre_scrub *lscrub = &oscrub->os_scrub;
+       struct ptlrpc_thread *thread = &lscrub->os_thread;
+       int wakeup = 0;
        ENTRY;
 
        OBD_ALLOC_PTR(oii);
        if (unlikely(oii == NULL))
                RETURN(-ENOMEM);
 
-       CFS_INIT_LIST_HEAD(&oii->oii_list);
+       INIT_LIST_HEAD(&oii->oii_list);
        oii->oii_cache = *oic;
        oii->oii_insert = insert;
 
-       spin_lock(&scrub->os_lock);
+       if (lscrub->os_partial_scan) {
+               __u64 now = ktime_get_real_seconds();
+
+               /* If there haven't been errors in a long time,
+                * decay old count until either the errors are
+                * gone or we reach the current interval. */
+               while (unlikely(oscrub->os_bad_oimap_count > 0 &&
+                               oscrub->os_bad_oimap_time +
+                               SCRUB_BAD_OIMAP_DECAY_INTERVAL < now)) {
+                       oscrub->os_bad_oimap_count >>= 1;
+                       oscrub->os_bad_oimap_time +=
+                               SCRUB_BAD_OIMAP_DECAY_INTERVAL;
+               }
+
+               oscrub->os_bad_oimap_time = now;
+               if (++oscrub->os_bad_oimap_count >
+                   dev->od_full_scrub_threshold_rate)
+                       lscrub->os_full_scrub = 1;
+       }
+
+       spin_lock(&lscrub->os_lock);
        if (unlikely(!thread_is_running(thread))) {
-               spin_unlock(&scrub->os_lock);
+               spin_unlock(&lscrub->os_lock);
                OBD_FREE_PTR(oii);
                RETURN(-EAGAIN);
        }
 
-       if (cfs_list_empty(&scrub->os_inconsistent_items))
+       if (list_empty(&lscrub->os_inconsistent_items))
                wakeup = 1;
-       cfs_list_add_tail(&oii->oii_list, &scrub->os_inconsistent_items);
-       spin_unlock(&scrub->os_lock);
+       list_add_tail(&oii->oii_list, &lscrub->os_inconsistent_items);
+       spin_unlock(&lscrub->os_lock);
 
        if (wakeup != 0)
                wake_up_all(&thread->t_ctl_waitq);
@@ -2536,12 +3093,12 @@ int osd_oii_insert(struct osd_device *dev, struct osd_idmap_cache *oic,
 int osd_oii_lookup(struct osd_device *dev, const struct lu_fid *fid,
                   struct osd_inode_id *id)
 {
-       struct osd_scrub             *scrub = &dev->od_scrub;
+       struct lustre_scrub *scrub = &dev->od_scrub.os_scrub;
        struct osd_inconsistent_item *oii;
        ENTRY;
 
        spin_lock(&scrub->os_lock);
-       cfs_list_for_each_entry(oii, &scrub->os_inconsistent_items, oii_list) {
+       list_for_each_entry(oii, &scrub->os_inconsistent_items, oii_list) {
                if (lu_fid_eq(fid, &oii->oii_cache.oic_fid)) {
                        *id = oii->oii_cache.oic_lid;
                        spin_unlock(&scrub->os_lock);
@@ -2553,188 +3110,17 @@ int osd_oii_lookup(struct osd_device *dev, const struct lu_fid *fid,
        RETURN(-ENOENT);
 }
 
-/* OI scrub dump */
-
-static const char *scrub_status_names[] = {
-       "init",
-       "scanning",
-       "completed",
-       "failed",
-       "stopped",
-       "paused",
-       "crashed",
-       NULL
-};
-
-static const char *scrub_flags_names[] = {
-       "recreated",
-       "inconsistent",
-       "auto",
-       "upgrade",
-       NULL
-};
-
-static const char *scrub_param_names[] = {
-       "failout",
-       "dryrun",
-       NULL
-};
-
-static int scrub_bits_dump(struct seq_file *m, int bits, const char *names[],
-                          const char *prefix)
-{
-       int flag;
-       int rc;
-       int i;
-
-       rc = seq_printf(m, "%s:%c", prefix, bits != 0 ? ' ' : '\n');
-       if (rc < 0)
-               return rc;
-
-       for (i = 0, flag = 1; bits != 0; i++, flag = 1 << i) {
-               if (flag & bits) {
-                       bits &= ~flag;
-                       rc = seq_printf(m, "%s%c", names[i],
-                                       bits != 0 ? ',' : '\n');
-                       if (rc < 0)
-                               return rc;
-               }
-       }
-       return 0;
-}
-
-static int scrub_time_dump(struct seq_file *m, __u64 time, const char *prefix)
+void osd_scrub_dump(struct seq_file *m, struct osd_device *dev)
 {
-       int rc;
-
-       if (time != 0)
-               rc = seq_printf(m, "%s: "LPU64" seconds\n", prefix,
-                             cfs_time_current_sec() - time);
-       else
-               rc = seq_printf(m, "%s: N/A\n", prefix);
-       return rc;
-}
-
-static int scrub_pos_dump(struct seq_file *m, __u64 pos, const char *prefix)
-{
-       int rc;
-
-       if (pos != 0)
-               rc = seq_printf(m, "%s: "LPU64"\n", prefix, pos);
-       else
-               rc = seq_printf(m, "%s: N/A\n", prefix);
-       return rc;
-}
-
-int osd_scrub_dump(struct seq_file *m, struct osd_device *dev)
-{
-       struct osd_scrub  *scrub   = &dev->od_scrub;
-       struct scrub_file *sf      = &scrub->os_file;
-       __u64              checked;
-       __u64              speed;
-       int                rc;
-
-       down_read(&scrub->os_rwsem);
-       rc = seq_printf(m, "name: OI_scrub\n"
-                       "magic: 0x%x\n"
-                       "oi_files: %d\n"
-                       "status: %s\n",
-                       sf->sf_magic, (int)sf->sf_oi_count,
-                       scrub_status_names[sf->sf_status]);
-       if (rc < 0)
-               goto out;
-
-       rc = scrub_bits_dump(m, sf->sf_flags, scrub_flags_names,
-                            "flags");
-       if (rc < 0)
-               goto out;
-
-       rc = scrub_bits_dump(m, sf->sf_param, scrub_param_names,
-                            "param");
-       if (rc < 0)
-               goto out;
-
-       rc = scrub_time_dump(m, sf->sf_time_last_complete,
-                            "time_since_last_completed");
-       if (rc < 0)
-               goto out;
-
-       rc = scrub_time_dump(m, sf->sf_time_latest_start,
-                            "time_since_latest_start");
-       if (rc < 0)
-               goto out;
-
-       rc = scrub_time_dump(m, sf->sf_time_last_checkpoint,
-                            "time_since_last_checkpoint");
-       if (rc < 0)
-               goto out;
-
-       rc = scrub_pos_dump(m, sf->sf_pos_latest_start,
-                           "latest_start_position");
-       if (rc < 0)
-               goto out;
-
-       rc = scrub_pos_dump(m, sf->sf_pos_last_checkpoint,
-                           "last_checkpoint_position");
-       if (rc < 0)
-               goto out;
-
-       rc = scrub_pos_dump(m, sf->sf_pos_first_inconsistent,
-                           "first_failure_position");
-       if (rc < 0)
-               goto out;
-
-       checked = sf->sf_items_checked + scrub->os_new_checked;
-       rc = seq_printf(m, "checked: "LPU64"\n"
-                     "updated: "LPU64"\n"
-                     "failed: "LPU64"\n"
-                     "prior_updated: "LPU64"\n"
-                     "noscrub: "LPU64"\n"
-                     "igif: "LPU64"\n"
-                     "success_count: %u\n",
-                     checked, sf->sf_items_updated, sf->sf_items_failed,
-                     sf->sf_items_updated_prior, sf->sf_items_noscrub,
-                     sf->sf_items_igif, sf->sf_success_count);
-       if (rc < 0)
-               goto out;
-
-       speed = checked;
-       if (thread_is_running(&scrub->os_thread)) {
-               cfs_duration_t duration = cfs_time_current() -
-                                         scrub->os_time_last_checkpoint;
-               __u64 new_checked = scrub->os_new_checked * HZ;
-               __u32 rtime = sf->sf_run_time +
-                             cfs_duration_sec(duration + HALF_SEC);
-
-               if (duration != 0)
-                       do_div(new_checked, duration);
-               if (rtime != 0)
-                       do_div(speed, rtime);
-               rc = seq_printf(m, "run_time: %u seconds\n"
-                             "average_speed: "LPU64" objects/sec\n"
-                             "real-time_speed: "LPU64" objects/sec\n"
-                             "current_position: %u\n"
-                             "lf_scanned: "LPU64"\n"
-                             "lf_reparied: "LPU64"\n"
-                             "lf_failed: "LPU64"\n",
-                             rtime, speed, new_checked, scrub->os_pos_current,
-                             scrub->os_lf_scanned, scrub->os_lf_repaired,
-                             scrub->os_lf_failed);
-       } else {
-               if (sf->sf_run_time != 0)
-                       do_div(speed, sf->sf_run_time);
-               rc = seq_printf(m, "run_time: %u seconds\n"
-                             "average_speed: "LPU64" objects/sec\n"
-                             "real-time_speed: N/A\n"
-                             "current_position: N/A\n"
-                             "lf_scanned: "LPU64"\n"
-                             "lf_reparied: "LPU64"\n"
-                             "lf_failed: "LPU64"\n",
-                             sf->sf_run_time, speed, scrub->os_lf_scanned,
-                             scrub->os_lf_repaired, scrub->os_lf_failed);
-       }
+       struct osd_scrub *scrub = &dev->od_scrub;
 
-out:
-       up_read(&scrub->os_rwsem);
-       return (rc < 0 ? -ENOSPC : 0);
+       scrub_dump(m, &scrub->os_scrub);
+       seq_printf(m, "lf_scanned: %llu\n"
+                  "lf_%s: %llu\n"
+                  "lf_failed: %llu\n",
+                  scrub->os_lf_scanned,
+                  scrub->os_scrub.os_file.sf_param & SP_DRYRUN ?
+                       "inconsistent" : "repaired",
+                  scrub->os_lf_repaired,
+                  scrub->os_lf_failed);
 }