Whamcloud - gitweb
e2fsck: allow admin specify number of threads
[tools/e2fsprogs.git] / e2fsck / pass1.c
index c27b520..948d512 100644 (file)
@@ -382,8 +382,10 @@ static problem_t check_large_ea_inode(e2fsck_t ctx,
                pctx->num = entry->e_value_inum;
                if (fix_problem(ctx, PR_1_ATTR_SET_EA_INODE_FL, pctx)) {
                        inode.i_flags |= EXT4_EA_INODE_FL;
+                       e2fsck_pass1_fix_lock(ctx);
                        ext2fs_write_inode(ctx->fs, entry->e_value_inum,
                                           &inode);
+                       e2fsck_pass1_fix_unlock(ctx);
                } else {
                        return PR_1_ATTR_NO_EA_INODE_FL;
                }
@@ -647,6 +649,31 @@ static void check_inode_extra_space(e2fsck_t ctx, struct problem_context *pctx,
 
 }
 
+static _INLINE_ int is_blocks_used(e2fsck_t ctx, blk64_t block,
+                                  unsigned int num)
+{
+       int retval;
+
+       /* used to avoid duplicate output from below */
+       retval = ext2fs_test_block_bitmap_range2_valid(ctx->block_found_map,
+                                                      block, num);
+       if (!retval)
+               return 0;
+
+       retval = ext2fs_test_block_bitmap_range2(ctx->block_found_map, block, num);
+       if (retval) {
+               e2fsck_pass1_block_map_r_lock(ctx);
+               if (ctx->global_ctx)
+                       retval = ext2fs_test_block_bitmap_range2(
+                                       ctx->global_ctx->block_found_map, block, num);
+               e2fsck_pass1_block_map_r_unlock(ctx);
+               if (retval)
+                       return 0;
+       }
+
+       return 1;
+}
+
 /*
  * Check to see if the inode might really be a directory, despite i_mode
  *
@@ -677,14 +704,14 @@ static void check_is_really_dir(e2fsck_t ctx, struct problem_context *pctx,
            LINUX_S_ISLNK(inode->i_mode) || inode->i_block[0] == 0)
                return;
 
-       /* 
+       /*
         * Check the block numbers in the i_block array for validity:
         * zero blocks are skipped (but the first one cannot be zero -
         * see above), other blocks are checked against the first and
         * max data blocks (from the the superblock) and against the
         * block bitmap. Any invalid block found means this cannot be
         * a directory.
-        * 
+        *
         * If there are non-zero blocks past the fourth entry, then
         * this cannot be a device file: we remember that for the next
         * check.
@@ -752,8 +779,7 @@ static void check_is_really_dir(e2fsck_t ctx, struct problem_context *pctx,
 
                        if (blk < ctx->fs->super->s_first_data_block ||
                            blk >= ext2fs_blocks_count(ctx->fs->super) ||
-                           ext2fs_fast_test_block_bitmap2(ctx->block_found_map,
-                                                          blk))
+                           is_blocks_used(ctx, blk, 1))
                                return; /* Invalid block, can't be dir */
                }
                blk = inode->i_block[0];
@@ -874,8 +900,11 @@ static errcode_t recheck_bad_inode_checksum(ext2_filsys fs, ext2_ino_t ino,
        if (!fix_problem(ctx, PR_1_INODE_ONLY_CSUM_INVALID, pctx))
                return 0;
 
+
+       e2fsck_pass1_fix_lock(ctx);
        retval = ext2fs_write_inode_full(fs, ino, (struct ext2_inode *)&inode,
                                         sizeof(inode));
+       e2fsck_pass1_fix_unlock(ctx);
        return retval;
 }
 
@@ -913,6 +942,7 @@ static void reserve_block_for_lnf_repair(e2fsck_t ctx)
                return;
        ext2fs_mark_block_bitmap2(ctx->block_found_map, blk);
        ctx->lnf_repair_block = blk;
+       return;
 }
 
 static errcode_t get_inline_data_ea_size(ext2_filsys fs, ext2_ino_t ino,
@@ -1166,6 +1196,257 @@ static int e2fsck_should_abort(e2fsck_t ctx)
        return 0;
 }
 
+static void init_ext2_max_sizes()
+{
+       int     i;
+       __u64   max_sizes;
+
+       /*
+        * Init ext2_max_sizes which will be immutable and shared between
+        * threads
+        */
+#define EXT2_BPP(bits) (1ULL << ((bits) - 2))
+
+       for (i = EXT2_MIN_BLOCK_LOG_SIZE; i <= EXT2_MAX_BLOCK_LOG_SIZE; i++) {
+               max_sizes = EXT2_NDIR_BLOCKS + EXT2_BPP(i);
+               max_sizes = max_sizes + EXT2_BPP(i) * EXT2_BPP(i);
+               max_sizes = max_sizes + EXT2_BPP(i) * EXT2_BPP(i) * EXT2_BPP(i);
+               max_sizes = (max_sizes * (1UL << i));
+               ext2_max_sizes[i - EXT2_MIN_BLOCK_LOG_SIZE] = max_sizes;
+       }
+#undef EXT2_BPP
+}
+
+#ifdef HAVE_PTHREAD
+/* TODO: tdb needs to be handled properly for multiple threads*/
+static int multiple_threads_supported(e2fsck_t ctx)
+{
+#ifdef CONFIG_TDB
+       unsigned int            threshold;
+       ext2_ino_t              num_dirs;
+       errcode_t               retval;
+       char                    *tdb_dir;
+       int                     enable;
+
+       profile_get_string(ctx->profile, "scratch_files", "directory", 0, 0,
+                          &tdb_dir);
+       profile_get_uint(ctx->profile, "scratch_files",
+                        "numdirs_threshold", 0, 0, &threshold);
+       profile_get_boolean(ctx->profile, "scratch_files",
+                           "icount", 0, 1, &enable);
+
+       retval = ext2fs_get_num_dirs(ctx->fs, &num_dirs);
+       if (retval)
+               num_dirs = 1024;        /* Guess */
+
+       /* tdb is unsupported now */
+       if (enable && tdb_dir && !access(tdb_dir, W_OK) &&
+           (!threshold || num_dirs > threshold))
+               return 0;
+#endif
+       return 1;
+}
+
+/**
+ * Even though we could specify number of threads,
+ * but it might be more than the whole filesystem
+ * block groups, correct it here.
+ */
+static void e2fsck_pass1_set_thread_num(e2fsck_t ctx)
+{
+       unsigned flexbg_size = 1;
+       ext2_filsys fs = ctx->fs;
+       int num_threads = ctx->fs_num_threads;
+       int max_threads;
+
+       if (num_threads < 1) {
+               num_threads = 1;
+               goto out;
+       }
+
+       if (!multiple_threads_supported(ctx)) {
+               num_threads = 1;
+               fprintf(stderr, "Fall through single thread for pass1 "
+                       "because tdb could not handle properly\n");
+               goto out;
+       }
+
+       if (ext2fs_has_feature_flex_bg(fs->super))
+               flexbg_size = 1 << fs->super->s_log_groups_per_flex;
+       max_threads = fs->group_desc_count / flexbg_size;
+       if (max_threads == 0)
+               max_threads = 1;
+
+       if (num_threads > max_threads) {
+               fprintf(stderr, "Use max possible thread num: %d instead\n",
+                               max_threads);
+               num_threads = max_threads;
+       }
+out:
+       ctx->fs_num_threads = num_threads;
+}
+#endif
+
+/*
+ * We need call mark_table_blocks() before multiple
+ * thread start, since all known system blocks should be
+ * marked and checked later.
+ */
+static errcode_t e2fsck_pass1_prepare(e2fsck_t ctx)
+{
+       struct problem_context pctx;
+       ext2_filsys fs = ctx->fs;
+
+       init_ext2_max_sizes();
+#ifdef HAVE_PTHREAD
+       e2fsck_pass1_set_thread_num(ctx);
+#endif
+
+       clear_problem_context(&pctx);
+       if (!(ctx->options & E2F_OPT_PREEN))
+               fix_problem(ctx, PR_1_PASS_HEADER, &pctx);
+
+       pctx.errcode = e2fsck_allocate_subcluster_bitmap(ctx->fs,
+                       _("in-use block map"), EXT2FS_BMAP64_RBTREE,
+                       "block_found_map", &ctx->block_found_map);
+       if (pctx.errcode) {
+               pctx.num = 1;
+               fix_problem(ctx, PR_1_ALLOCATE_BBITMAP_ERROR, &pctx);
+               ctx->flags |= E2F_FLAG_ABORT;
+               return pctx.errcode;
+       }
+       pctx.errcode = e2fsck_allocate_block_bitmap(ctx->fs,
+                       _("metadata block map"), EXT2FS_BMAP64_RBTREE,
+                       "block_metadata_map", &ctx->block_metadata_map);
+       if (pctx.errcode) {
+               pctx.num = 1;
+               fix_problem(ctx, PR_1_ALLOCATE_BBITMAP_ERROR, &pctx);
+               ctx->flags |= E2F_FLAG_ABORT;
+               return pctx.errcode;
+       }
+
+       mark_table_blocks(ctx);
+       pctx.errcode = ext2fs_convert_subcluster_bitmap(ctx->fs,
+                                               &ctx->block_found_map);
+       if (pctx.errcode) {
+               fix_problem(ctx, PR_1_CONVERT_SUBCLUSTER, &pctx);
+               ctx->flags |= E2F_FLAG_ABORT;
+               return pctx.errcode;
+       }
+
+       pctx.errcode = e2fsck_allocate_block_bitmap(ctx->fs,
+                       _("multiply claimed block map"),
+                       EXT2FS_BMAP64_RBTREE, "block_dup_map",
+                       &ctx->block_dup_map);
+       if (pctx.errcode) {
+               pctx.num = 3;
+               fix_problem(ctx, PR_1_ALLOCATE_BBITMAP_ERROR,
+                           &pctx);
+               /* Should never get here */
+               ctx->flags |= E2F_FLAG_ABORT;
+               return pctx.errcode;
+       }
+
+       if (ext2fs_has_feature_mmp(fs->super) &&
+           fs->super->s_mmp_block > fs->super->s_first_data_block &&
+           fs->super->s_mmp_block < ext2fs_blocks_count(fs->super))
+               ext2fs_mark_block_bitmap2(ctx->block_found_map,
+                                         fs->super->s_mmp_block);
+#ifdef HAVE_PTHREAD
+       pthread_mutex_init(&ctx->fs_fix_mutex, NULL);
+       pthread_rwlock_init(&ctx->fs_block_map_rwlock, NULL);
+#endif
+
+       return 0;
+}
+
+static void e2fsck_pass1_post(e2fsck_t ctx)
+{
+       struct problem_context pctx;
+       ext2_filsys fs = ctx->fs;
+
+       char *block_buf =
+               (char *)e2fsck_allocate_memory(ctx, ctx->fs->blocksize * 3,
+                                             "block interate buffer");
+       reserve_block_for_root_repair(ctx);
+       reserve_block_for_lnf_repair(ctx);
+
+       /*
+        * If any extended attribute blocks' reference counts need to
+        * be adjusted, either up (ctx->refcount_extra), or down
+        * (ctx->refcount), then fix them.
+        */
+       if (ctx->refcount) {
+               adjust_extattr_refcount(ctx, ctx->refcount, block_buf, -1);
+               ea_refcount_free(ctx->refcount);
+               ctx->refcount = 0;
+       }
+       if (ctx->refcount_extra) {
+               adjust_extattr_refcount(ctx, ctx->refcount_extra,
+                                       block_buf, +1);
+               ea_refcount_free(ctx->refcount_extra);
+               ctx->refcount_extra = 0;
+       }
+
+       if (ctx->invalid_bitmaps)
+               handle_fs_bad_blocks(ctx);
+
+       /* We don't need the block_ea_map any more */
+       if (ctx->block_ea_map) {
+               ext2fs_free_block_bitmap(ctx->block_ea_map);
+               ctx->block_ea_map = 0;
+       }
+
+       if (ctx->flags & E2F_FLAG_RESIZE_INODE) {
+               struct ext2_inode *inode;
+               int inode_size = EXT2_INODE_SIZE(fs->super);
+               inode = e2fsck_allocate_memory(ctx, inode_size,
+                                              "scratch inode");
+
+               clear_problem_context(&pctx);
+               pctx.errcode = ext2fs_create_resize_inode(fs);
+               if (pctx.errcode) {
+                       if (!fix_problem(ctx, PR_1_RESIZE_INODE_CREATE,
+                                        &pctx)) {
+                               ctx->flags |= E2F_FLAG_ABORT;
+                               ext2fs_free_mem(&inode);
+                               ext2fs_free_mem(&block_buf);
+                               return;
+                       }
+                       pctx.errcode = 0;
+               }
+               if (!pctx.errcode) {
+                       e2fsck_read_inode(ctx, EXT2_RESIZE_INO, inode,
+                                         "recreate inode");
+                       inode->i_mtime = ctx->now;
+                       e2fsck_write_inode(ctx, EXT2_RESIZE_INO, inode,
+                                          "recreate inode");
+               }
+               ctx->flags &= ~E2F_FLAG_RESIZE_INODE;
+               ext2fs_free_mem(&inode);
+       }
+
+       if (ctx->flags & E2F_FLAG_RESTART) {
+               ext2fs_free_mem(&block_buf);
+               return;
+       }
+
+       if (ctx->block_dup_map) {
+               if (!(ctx->flags & E2F_FLAG_DUP_BLOCK)) {
+                       ext2fs_free_mem(&block_buf);
+                       return;
+               }
+               if (ctx->options & E2F_OPT_PREEN) {
+                       clear_problem_context(&pctx);
+                       fix_problem(ctx, PR_1_DUP_BLOCKS_PREENSTOP, &pctx);
+               }
+               e2fsck_pass1_dupblocks(ctx, block_buf);
+               ext2fs_free_mem(&block_buf);
+               ctx->flags &= ~E2F_FLAG_DUP_BLOCK;
+       }
+}
+
+
 void e2fsck_pass1_run(e2fsck_t ctx)
 {
        int     i;
@@ -1204,9 +1485,6 @@ void e2fsck_pass1_run(e2fsck_t ctx)
                ctx->readahead_kb = e2fsck_guess_readahead(ctx->fs);
        pass1_readahead(ctx, &ra_group, &ino_threshold);
 
-       if (!(ctx->options & E2F_OPT_PREEN))
-               fix_problem(ctx, PR_1_PASS_HEADER, &pctx);
-
        if (ext2fs_has_feature_dir_index(fs->super) &&
            !(ctx->options & E2F_OPT_NO)) {
                if (ext2fs_u32_list_create(&ctx->dirs_to_hash, 50))
@@ -1255,24 +1533,6 @@ void e2fsck_pass1_run(e2fsck_t ctx)
                ctx->flags |= E2F_FLAG_ABORT;
                return;
        }
-       pctx.errcode = e2fsck_allocate_subcluster_bitmap(fs,
-                       _("in-use block map"), EXT2FS_BMAP64_RBTREE,
-                       "block_found_map", &ctx->block_found_map);
-       if (pctx.errcode) {
-               pctx.num = 1;
-               fix_problem(ctx, PR_1_ALLOCATE_BBITMAP_ERROR, &pctx);
-               ctx->flags |= E2F_FLAG_ABORT;
-               return;
-       }
-       pctx.errcode = e2fsck_allocate_block_bitmap(fs,
-                       _("metadata block map"), EXT2FS_BMAP64_RBTREE,
-                       "block_metadata_map", &ctx->block_metadata_map);
-       if (pctx.errcode) {
-               pctx.num = 1;
-               fix_problem(ctx, PR_1_ALLOCATE_BBITMAP_ERROR, &pctx);
-               ctx->flags |= E2F_FLAG_ABORT;
-               return;
-       }
        if (casefold_fs) {
                pctx.errcode =
                        e2fsck_allocate_inode_bitmap(fs,
@@ -1328,14 +1588,6 @@ void e2fsck_pass1_run(e2fsck_t ctx)
                }
        }
 
-       mark_table_blocks(ctx);
-       pctx.errcode = ext2fs_convert_subcluster_bitmap(fs,
-                                               &ctx->block_found_map);
-       if (pctx.errcode) {
-               fix_problem(ctx, PR_1_CONVERT_SUBCLUSTER, &pctx);
-               ctx->flags |= E2F_FLAG_ABORT;
-               goto endit;
-       }
        block_buf = (char *) e2fsck_allocate_memory(ctx, fs->blocksize * 3,
                                                    "block interate buffer");
        if (EXT2_INODE_SIZE(fs->super) == EXT2_GOOD_OLD_INODE_SIZE)
@@ -1369,12 +1621,6 @@ void e2fsck_pass1_run(e2fsck_t ctx)
             fs->super->s_mkfs_time < fs->super->s_inodes_count))
                low_dtime_check = 0;
 
-       if (ext2fs_has_feature_mmp(fs->super) &&
-           fs->super->s_mmp_block > fs->super->s_first_data_block &&
-           fs->super->s_mmp_block < ext2fs_blocks_count(fs->super))
-               ext2fs_mark_block_bitmap2(ctx->block_found_map,
-                                         fs->super->s_mmp_block);
-
        /* Set up ctx->lost_and_found if possible */
        (void) e2fsck_get_lost_and_found(ctx, 0);
 
@@ -1547,8 +1793,10 @@ void e2fsck_pass1_run(e2fsck_t ctx)
                        pctx.errcode = get_inline_data_ea_size(fs, ino, &size);
                        if (!pctx.errcode &&
                            fix_problem(ctx, PR_1_INLINE_DATA_FEATURE, &pctx)) {
+                               e2fsck_pass1_fix_lock(ctx);
                                ext2fs_set_feature_inline_data(sb);
                                ext2fs_mark_super_dirty(fs);
+                               e2fsck_pass1_fix_unlock(ctx);
                                inlinedata_fs = 1;
                        } else if (fix_problem(ctx, PR_1_INLINE_DATA_SET, &pctx)) {
                                e2fsck_clear_inode(ctx, ino, inode, 0, "pass1");
@@ -1636,9 +1884,11 @@ void e2fsck_pass1_run(e2fsck_t ctx)
                        if ((ext2fs_extent_header_verify(inode->i_block,
                                                 sizeof(inode->i_block)) == 0) &&
                            fix_problem(ctx, PR_1_EXTENT_FEATURE, &pctx)) {
+                               e2fsck_pass1_fix_lock(ctx);
                                ext2fs_set_feature_extents(sb);
                                ext2fs_mark_super_dirty(fs);
                                extent_fs = 1;
+                               e2fsck_pass1_fix_unlock(ctx);
                        } else if (fix_problem(ctx, PR_1_EXTENTS_SET, &pctx)) {
                        clear_inode:
                                e2fsck_clear_inode(ctx, ino, inode, 0, "pass1");
@@ -1700,8 +1950,11 @@ void e2fsck_pass1_run(e2fsck_t ctx)
                                failed_csum = 0;
                        }
 
-                       pctx.errcode = ext2fs_copy_bitmap(ctx->block_found_map,
-                                                         &pb.fs_meta_blocks);
+                       e2fsck_pass1_block_map_r_lock(ctx);
+                       pctx.errcode = ext2fs_copy_bitmap(ctx->global_ctx ?
+                                       ctx->global_ctx->block_found_map :
+                                       ctx->block_found_map, &pb.fs_meta_blocks);
+                       e2fsck_pass1_block_map_r_unlock(ctx);
                        if (pctx.errcode) {
                                pctx.num = 4;
                                fix_problem(ctx, PR_1_ALLOCATE_BBITMAP_ERROR, &pctx);
@@ -2022,26 +2275,6 @@ void e2fsck_pass1_run(e2fsck_t ctx)
        ext2fs_close_inode_scan(scan);
        scan = NULL;
 
-       reserve_block_for_root_repair(ctx);
-       reserve_block_for_lnf_repair(ctx);
-
-       /*
-        * If any extended attribute blocks' reference counts need to
-        * be adjusted, either up (ctx->refcount_extra), or down
-        * (ctx->refcount), then fix them.
-        */
-       if (ctx->refcount) {
-               adjust_extattr_refcount(ctx, ctx->refcount, block_buf, -1);
-               ea_refcount_free(ctx->refcount);
-               ctx->refcount = 0;
-       }
-       if (ctx->refcount_extra) {
-               adjust_extattr_refcount(ctx, ctx->refcount_extra,
-                                       block_buf, +1);
-               ea_refcount_free(ctx->refcount_extra);
-               ctx->refcount_extra = 0;
-       }
-
        if (ctx->ea_block_quota_blocks) {
                ea_refcount_free(ctx->ea_block_quota_blocks);
                ctx->ea_block_quota_blocks = 0;
@@ -2052,39 +2285,9 @@ void e2fsck_pass1_run(e2fsck_t ctx)
                ctx->ea_block_quota_inodes = 0;
        }
 
-       if (ctx->invalid_bitmaps)
-               handle_fs_bad_blocks(ctx);
-
-       /* We don't need the block_ea_map any more */
-       if (ctx->block_ea_map) {
-               ext2fs_free_block_bitmap(ctx->block_ea_map);
-               ctx->block_ea_map = 0;
-       }
-
        /* We don't need the encryption policy => ID map any more */
        destroy_encryption_policy_map(ctx);
 
-       if (ctx->flags & E2F_FLAG_RESIZE_INODE) {
-               clear_problem_context(&pctx);
-               pctx.errcode = ext2fs_create_resize_inode(fs);
-               if (pctx.errcode) {
-                       if (!fix_problem(ctx, PR_1_RESIZE_INODE_CREATE,
-                                        &pctx)) {
-                               ctx->flags |= E2F_FLAG_ABORT;
-                               goto endit;
-                       }
-                       pctx.errcode = 0;
-               }
-               if (!pctx.errcode) {
-                       e2fsck_read_inode(ctx, EXT2_RESIZE_INO, inode,
-                                         "recreate inode");
-                       inode->i_mtime = ctx->now;
-                       e2fsck_write_inode(ctx, EXT2_RESIZE_INO, inode,
-                                          "recreate inode");
-               }
-               ctx->flags &= ~E2F_FLAG_RESIZE_INODE;
-       }
-
        if (ctx->flags & E2F_FLAG_RESTART) {
                /*
                 * Only the master copy of the superblock and block
@@ -2111,13 +2314,6 @@ void e2fsck_pass1_run(e2fsck_t ctx)
                }
        }
 
-       if (ctx->block_dup_map) {
-               if (ctx->options & E2F_OPT_PREEN) {
-                       clear_problem_context(&pctx);
-                       fix_problem(ctx, PR_1_DUP_BLOCKS_PREENSTOP, &pctx);
-               }
-               e2fsck_pass1_dupblocks(ctx, block_buf);
-       }
        ctx->flags |= E2F_FLAG_ALLOC_OK;
 endit:
        e2fsck_use_inode_shortcuts(ctx, 0);
@@ -2143,27 +2339,6 @@ endit:
                ctx->invalid_bitmaps++;
 }
 
-static void init_ext2_max_sizes()
-{
-       int     i;
-       __u64   max_sizes;
-
-       /*
-        * Init ext2_max_sizes which will be immutable and shared between
-        * threads
-        */
-#define EXT2_BPP(bits) (1ULL << ((bits) - 2))
-
-       for (i = EXT2_MIN_BLOCK_LOG_SIZE; i <= EXT2_MAX_BLOCK_LOG_SIZE; i++) {
-               max_sizes = EXT2_NDIR_BLOCKS + EXT2_BPP(i);
-               max_sizes = max_sizes + EXT2_BPP(i) * EXT2_BPP(i);
-               max_sizes = max_sizes + EXT2_BPP(i) * EXT2_BPP(i) * EXT2_BPP(i);
-               max_sizes = (max_sizes * (1UL << i));
-               ext2_max_sizes[i - EXT2_MIN_BLOCK_LOG_SIZE] = max_sizes;
-       }
-#undef EXT2_BPP
-}
-
 #ifdef HAVE_PTHREAD
 static errcode_t e2fsck_pass1_copy_bitmap(ext2_filsys fs, ext2fs_generic_bitmap *src,
                                          ext2fs_generic_bitmap *dest)
@@ -2281,6 +2456,7 @@ static int e2fsck_pass1_merge_fs(ext2_filsys dest, ext2_filsys src)
        ext2fs_block_bitmap block_map;
        ext2_badblocks_list badblocks;
        ext2_dblist dblist;
+       int flags;
 
        dest_io = dest->io;
        dest_image_io = dest->image_io;
@@ -2288,6 +2464,7 @@ static int e2fsck_pass1_merge_fs(ext2_filsys dest, ext2_filsys src)
        block_map = dest->block_map;
        badblocks = dest->badblocks;
        dblist = dest->dblist;
+       flags = dest->flags;
 
        memcpy(dest, src, sizeof(struct struct_ext2_filsys));
        dest->io = dest_io;
@@ -2299,6 +2476,9 @@ static int e2fsck_pass1_merge_fs(ext2_filsys dest, ext2_filsys src)
        dest->dblist = dblist;
        if (dest->dblist)
                dest->dblist->fs = dest;
+       dest->flags = src->flags | flags;
+       if (!(src->flags & EXT2_FLAG_VALID) || !(flags & EXT2_FLAG_VALID))
+               ext2fs_unmark_valid(dest);
 
        if (src->icache) {
                ext2fs_free_inode_cache(src->icache);
@@ -2350,6 +2530,62 @@ out:
        return retval;
 }
 
+static void e2fsck_pass1_copy_invalid_bitmaps(e2fsck_t global_ctx,
+                                             e2fsck_t thread_ctx)
+{
+       dgrp_t i, j;
+       dgrp_t grp_start = thread_ctx->thread_info.et_group_start;
+       dgrp_t grp_end = thread_ctx->thread_info.et_group_end;
+       dgrp_t total = grp_end - grp_start;
+
+       thread_ctx->invalid_inode_bitmap_flag =
+                       e2fsck_allocate_memory(global_ctx, sizeof(int) * total,
+                                               "invalid_inode_bitmap");
+       thread_ctx->invalid_block_bitmap_flag =
+                       e2fsck_allocate_memory(global_ctx, sizeof(int) * total,
+                                              "invalid_block_bitmap");
+       thread_ctx->invalid_inode_table_flag =
+                       e2fsck_allocate_memory(global_ctx, sizeof(int) * total,
+                                              "invalid_inode_table");
+
+       memcpy(thread_ctx->invalid_block_bitmap_flag,
+              &global_ctx->invalid_block_bitmap_flag[grp_start],
+              total * sizeof(int));
+       memcpy(thread_ctx->invalid_inode_bitmap_flag,
+              &global_ctx->invalid_inode_bitmap_flag[grp_start],
+              total * sizeof(int));
+       memcpy(thread_ctx->invalid_inode_table_flag,
+              &global_ctx->invalid_inode_table_flag[grp_start],
+              total * sizeof(int));
+
+       thread_ctx->invalid_bitmaps = 0;
+       for (i = grp_start, j = 0; i < grp_end; i++, j++) {
+               if (thread_ctx->invalid_block_bitmap_flag[j])
+                       thread_ctx->invalid_bitmaps++;
+               if (thread_ctx->invalid_inode_bitmap_flag[j])
+                       thread_ctx->invalid_bitmaps++;
+               if (thread_ctx->invalid_inode_table_flag[j])
+                       thread_ctx->invalid_bitmaps++;
+       }
+}
+
+static void e2fsck_pass1_merge_invalid_bitmaps(e2fsck_t global_ctx,
+                                              e2fsck_t thread_ctx)
+{
+       dgrp_t i, j;
+       dgrp_t grp_start = thread_ctx->thread_info.et_group_start;
+       dgrp_t grp_end = thread_ctx->thread_info.et_group_end;
+       dgrp_t total = grp_end - grp_start;
+
+       memcpy(&global_ctx->invalid_block_bitmap_flag[grp_start],
+              thread_ctx->invalid_block_bitmap_flag, total * sizeof(int));
+       memcpy(&global_ctx->invalid_inode_bitmap_flag[grp_start],
+              thread_ctx->invalid_inode_bitmap_flag, total * sizeof(int));
+       memcpy(&global_ctx->invalid_inode_table_flag[grp_start],
+              thread_ctx->invalid_inode_table_flag, total * sizeof(int));
+       global_ctx->invalid_bitmaps += thread_ctx->invalid_bitmaps;
+}
+
 static errcode_t e2fsck_pass1_thread_prepare(e2fsck_t global_ctx, e2fsck_t *thread_ctx,
                                             int thread_index, int num_threads)
 {
@@ -2367,10 +2603,10 @@ static errcode_t e2fsck_pass1_thread_prepare(e2fsck_t global_ctx, e2fsck_t *thre
        assert(global_ctx->inode_reg_map == NULL);
        assert(global_ctx->inodes_to_rebuild == NULL);
 
-       assert(global_ctx->block_found_map == NULL);
-       assert(global_ctx->block_dup_map == NULL);
+       assert(global_ctx->block_found_map != NULL);
+       assert(global_ctx->block_metadata_map != NULL);
+       assert(global_ctx->block_dup_map != NULL);
        assert(global_ctx->block_ea_map == NULL);
-       assert(global_ctx->block_metadata_map == NULL);
        assert(global_ctx->fs->dblist == NULL);
 
        retval = ext2fs_get_mem(sizeof(struct e2fsck_struct), &thread_context);
@@ -2379,8 +2615,15 @@ static errcode_t e2fsck_pass1_thread_prepare(e2fsck_t global_ctx, e2fsck_t *thre
                return retval;
        }
        memcpy(thread_context, global_ctx, sizeof(struct e2fsck_struct));
-       thread_context->global_ctx = global_ctx;
+       thread_context->block_dup_map = NULL;
+
+       retval = e2fsck_allocate_block_bitmap(global_ctx->fs,
+                               _("in-use block map"), EXT2FS_BMAP64_RBTREE,
+                               "block_found_map", &thread_context->block_found_map);
+       if (retval)
+               goto out_context;
 
+       thread_context->global_ctx = global_ctx;
        retval = ext2fs_get_mem(sizeof(struct struct_ext2_filsys), &thread_fs);
        if (retval) {
                com_err(global_ctx->program_name, retval, "while allocating memory");
@@ -2419,11 +2662,20 @@ static errcode_t e2fsck_pass1_thread_prepare(e2fsck_t global_ctx, e2fsck_t *thre
                log_out(thread_context, _("Scan group range [%d, %d)\n"),
                        tinfo->et_group_start, tinfo->et_group_end);
        thread_context->fs = thread_fs;
+       retval = quota_init_context(&thread_context->qctx, thread_fs, 0);
+       if (retval) {
+               com_err(global_ctx->program_name, retval,
+                       "while init quota context");
+               goto out_fs;
+       }
        *thread_ctx = thread_context;
+       e2fsck_pass1_copy_invalid_bitmaps(global_ctx, thread_context);
        return 0;
 out_fs:
        ext2fs_free_mem(&thread_fs);
 out_context:
+       if (thread_context->block_found_map)
+               ext2fs_free_mem(&thread_context->block_found_map);
        ext2fs_free_mem(&thread_context);
        return retval;
 }
@@ -2443,6 +2695,22 @@ static void e2fsck_pass1_merge_dir_info(e2fsck_t global_ctx, e2fsck_t thread_ctx
                              global_ctx->dir_info);
 }
 
+static void e2fsck_pass1_merge_dx_dir(e2fsck_t global_ctx, e2fsck_t thread_ctx)
+{
+       if (thread_ctx->dx_dir_info == NULL)
+               return;
+
+       if (global_ctx->dx_dir_info == NULL) {
+               global_ctx->dx_dir_info = thread_ctx->dx_dir_info;
+               global_ctx->dx_dir_info_size = thread_ctx->dx_dir_info_size;
+               global_ctx->dx_dir_info_count = thread_ctx->dx_dir_info_count;
+               thread_ctx->dx_dir_info = NULL;
+               return;
+       }
+
+       e2fsck_merge_dx_dir(global_ctx, thread_ctx);
+}
+
 static inline errcode_t
 e2fsck_pass1_merge_icount(ext2_icount_t *dest_icount,
                          ext2_icount_t *src_icount)
@@ -2478,6 +2746,174 @@ static errcode_t e2fsck_pass1_merge_icounts(e2fsck_t global_ctx, e2fsck_t thread
        return ret;
 }
 
+static errcode_t e2fsck_pass1_merge_dirs_to_hash(e2fsck_t global_ctx,
+                                                e2fsck_t thread_ctx)
+{
+       errcode_t retval = 0;
+
+       if (!thread_ctx->dirs_to_hash)
+               return 0;
+
+       if (!global_ctx->dirs_to_hash)
+               retval = ext2fs_badblocks_copy(thread_ctx->dirs_to_hash,
+                                              &global_ctx->dirs_to_hash);
+       else
+               retval = ext2fs_badblocks_merge(thread_ctx->dirs_to_hash,
+                                               global_ctx->dirs_to_hash);
+
+       return retval;
+}
+
+static errcode_t e2fsck_pass1_merge_ea_inode_refs(e2fsck_t global_ctx,
+                                                 e2fsck_t thread_ctx)
+{
+       ea_value_t count;
+       blk64_t blk;
+       errcode_t retval;
+
+       if (!thread_ctx->ea_inode_refs)
+               return 0;
+
+       if (!global_ctx->ea_inode_refs) {
+               global_ctx->ea_inode_refs = thread_ctx->ea_inode_refs;
+               thread_ctx->ea_inode_refs = NULL;
+               return 0;
+       }
+
+       ea_refcount_intr_begin(thread_ctx->ea_inode_refs);
+       while (1) {
+               if ((blk = ea_refcount_intr_next(thread_ctx->ea_inode_refs,
+                                                &count)) == 0)
+                       break;
+               if (!global_ctx->block_ea_map ||
+                   !ext2fs_fast_test_block_bitmap2(global_ctx->block_ea_map,
+                                                   blk)) {
+                       retval = ea_refcount_store(global_ctx->ea_inode_refs,
+                                                  blk, count);
+                       if (retval)
+                               return retval;
+               }
+       }
+
+       return retval;
+}
+
+static ea_value_t ea_refcount_usage(e2fsck_t ctx, blk64_t blk,
+                                   ea_value_t *orig)
+{
+       ea_value_t count_cur;
+       ea_value_t count_extra = 0;
+       ea_value_t count_orig;
+
+       ea_refcount_fetch(ctx->refcount_orig, blk, &count_orig);
+       ea_refcount_fetch(ctx->refcount, blk, &count_cur);
+       /* most of time this is not needed */
+       if (ctx->refcount_extra && count_cur == 0)
+               ea_refcount_fetch(ctx->refcount_extra, blk, &count_extra);
+
+       if (!count_orig)
+               count_orig = *orig;
+       else if (orig)
+               *orig = count_orig;
+
+       return count_orig + count_extra - count_cur;
+}
+
+static errcode_t e2fsck_pass1_merge_ea_refcount(e2fsck_t global_ctx,
+                                               e2fsck_t thread_ctx)
+{
+       ea_value_t count;
+       blk64_t blk;
+       errcode_t retval = 0;
+
+       if (!thread_ctx->refcount)
+               return 0;
+
+       if (!global_ctx->refcount) {
+               global_ctx->refcount = thread_ctx->refcount;
+               thread_ctx->refcount = NULL;
+               global_ctx->refcount_extra = thread_ctx->refcount;
+               thread_ctx->refcount_extra = NULL;
+               return 0;
+       }
+
+       ea_refcount_intr_begin(thread_ctx->refcount);
+       while (1) {
+               if ((blk = ea_refcount_intr_next(thread_ctx->refcount,
+                                                &count)) == 0)
+                       break;
+               /**
+                * this EA has never seen before, so just store its
+                * refcount and refcount_extra into global_ctx if needed.
+                */
+               if (!global_ctx->block_ea_map ||
+                   !ext2fs_fast_test_block_bitmap2(global_ctx->block_ea_map,
+                                                   blk)) {
+                       ea_value_t extra;
+
+                       retval = ea_refcount_store(global_ctx->refcount,
+                                                  blk, count);
+                       if (retval)
+                               return retval;
+
+                       if (count > 0 || !thread_ctx->refcount_extra)
+                               continue;
+                       ea_refcount_fetch(thread_ctx->refcount_extra, blk,
+                                         &extra);
+                       if (extra == 0)
+                               continue;
+
+                       if (!global_ctx->refcount_extra) {
+                               retval = ea_refcount_create(0,
+                                               &global_ctx->refcount_extra);
+                               if (retval)
+                                       return retval;
+                       }
+                       retval = ea_refcount_store(global_ctx->refcount_extra,
+                                                  blk, extra);
+                       if (retval)
+                               return retval;
+               } else {
+                       ea_value_t orig;
+                       ea_value_t thread_usage;
+                       ea_value_t global_usage;
+                       ea_value_t new;
+
+                       thread_usage = ea_refcount_usage(thread_ctx,
+                                                        blk, &orig);
+                       global_usage = ea_refcount_usage(global_ctx,
+                                                        blk, &orig);
+                       if (thread_usage + global_usage <= orig) {
+                               new = orig - thread_usage - global_usage;
+                               retval = ea_refcount_store(global_ctx->refcount,
+                                                          blk, new);
+                               if (retval)
+                                       return retval;
+                               continue;
+                       }
+                       /* update it is as zero */
+                       retval = ea_refcount_store(global_ctx->refcount,
+                                                  blk, 0);
+                       if (retval)
+                               return retval;
+                       /* Ooops, this EA was referenced more than it stated */
+                       if (!global_ctx->refcount_extra) {
+                               retval = ea_refcount_create(0,
+                                               &global_ctx->refcount_extra);
+                               if (retval)
+                                       return retval;
+                       }
+                       new = global_usage + thread_usage - orig;
+                       retval = ea_refcount_store(global_ctx->refcount_extra,
+                                                  blk, new);
+                       if (retval)
+                               return retval;
+               }
+       }
+
+       return retval;
+}
+
 static int e2fsck_pass1_thread_join_one(e2fsck_t global_ctx, e2fsck_t thread_ctx)
 {
        errcode_t        retval;
@@ -2488,18 +2924,49 @@ static int e2fsck_pass1_thread_join_one(e2fsck_t global_ctx, e2fsck_t thread_ctx
        FILE            *global_problem_logf = global_ctx->problem_logf;
        ext2fs_inode_bitmap inode_bad_map = global_ctx->inode_bad_map;
        struct dir_info_db *dir_info = global_ctx->dir_info;
+       struct dx_dir_info *dx_dir_info = global_ctx->dx_dir_info;
        ext2fs_inode_bitmap inode_used_map = global_ctx->inode_used_map;
        ext2fs_inode_bitmap inode_dir_map = global_ctx->inode_dir_map;
        ext2fs_inode_bitmap inode_bb_map = global_ctx->inode_bb_map;
        ext2fs_inode_bitmap inode_imagic_map = global_ctx->inode_imagic_map;
        ext2fs_inode_bitmap inode_reg_map = global_ctx->inode_reg_map;
-       ext2fs_block_bitmap block_found_map = global_ctx->block_found_map;
-       ext2fs_block_bitmap block_dup_map = global_ctx->block_dup_map;
-       ext2fs_block_bitmap block_ea_map = global_ctx->block_ea_map;
-       ext2fs_block_bitmap block_metadata_map = global_ctx->block_metadata_map;
        ext2fs_block_bitmap inodes_to_rebuild = global_ctx->inodes_to_rebuild;
        ext2_icount_t inode_count = global_ctx->inode_count;
        ext2_icount_t inode_link_info = global_ctx->inode_link_info;
+       __u32 fs_directory_count = global_ctx->fs_directory_count;
+       __u32 fs_regular_count = global_ctx->fs_regular_count;
+       __u32 fs_blockdev_count = global_ctx->fs_blockdev_count;
+       __u32 fs_chardev_count = global_ctx->fs_chardev_count;
+       __u32 fs_links_count = global_ctx->fs_links_count;
+       __u32 fs_symlinks_count = global_ctx->fs_symlinks_count;
+       __u32 fs_fast_symlinks_count = global_ctx->fs_fast_symlinks_count;
+       __u32 fs_fifo_count = global_ctx->fs_fifo_count;
+       __u32 fs_total_count = global_ctx->fs_total_count;
+       __u32 fs_badblocks_count = global_ctx->fs_badblocks_count;
+       __u32 fs_sockets_count = global_ctx->fs_sockets_count;
+       __u32 fs_ind_count = global_ctx->fs_ind_count;
+       __u32 fs_dind_count = global_ctx->fs_dind_count;
+       __u32 fs_tind_count = global_ctx->fs_tind_count;
+       __u32 fs_fragmented = global_ctx->fs_fragmented;
+       __u32 fs_fragmented_dir = global_ctx->fs_fragmented_dir;
+       __u32 large_files = global_ctx->large_files;
+       ext2_ino_t dx_dir_info_size = global_ctx->dx_dir_info_size;
+       ext2_ino_t dx_dir_info_count = global_ctx->dx_dir_info_count;
+       ext2_u32_list dirs_to_hash = global_ctx->dirs_to_hash;
+       quota_ctx_t qctx = global_ctx->qctx;
+       int *invalid_block_bitmap_flag = global_ctx->invalid_block_bitmap_flag;
+       int *invalid_inode_bitmap_flag = global_ctx->invalid_inode_bitmap_flag;
+       int *invalid_inode_table_flag  = global_ctx->invalid_inode_table_flag;
+       int invalid_bitmaps = global_ctx->invalid_bitmaps;
+       ext2_refcount_t refcount = global_ctx->refcount;
+       ext2_refcount_t refcount_extra = global_ctx->refcount_extra;
+       ext2_refcount_t refcount_orig = global_ctx->refcount_orig;
+       ext2_refcount_t ea_block_quota_blocks = global_ctx->ea_block_quota_blocks;
+       ext2_refcount_t ea_block_quota_inodes = global_ctx->ea_block_quota_inodes;
+       ext2fs_block_bitmap block_ea_map = global_ctx->block_ea_map;
+       ext2_refcount_t ea_inode_refs = global_ctx->ea_inode_refs;
+       ext2fs_block_bitmap  block_found_map = global_ctx->block_found_map;
+       ext2fs_block_bitmap  block_dup_map = global_ctx->block_dup_map;
 
 #ifdef HAVE_SETJMP_H
        jmp_buf          old_jmp;
@@ -2518,18 +2985,42 @@ static int e2fsck_pass1_thread_join_one(e2fsck_t global_ctx, e2fsck_t thread_ctx
        global_ctx->inode_imagic_map = inode_imagic_map;
        global_ctx->inodes_to_rebuild = inodes_to_rebuild;
        global_ctx->inode_reg_map = inode_reg_map;
-       global_ctx->block_found_map = block_found_map;
        global_ctx->block_dup_map = block_dup_map;
-       global_ctx->block_ea_map = block_ea_map;
-       global_ctx->block_metadata_map = block_metadata_map;
+       global_ctx->block_found_map = block_found_map;
        global_ctx->dir_info = dir_info;
        e2fsck_pass1_merge_dir_info(global_ctx, thread_ctx);
+       global_ctx->dx_dir_info = dx_dir_info;
+       global_ctx->dx_dir_info_count = dx_dir_info_count;
+       global_ctx->dx_dir_info_size = dx_dir_info_size;
+       e2fsck_pass1_merge_dx_dir(global_ctx, thread_ctx);
        global_ctx->inode_count = inode_count;
        global_ctx->inode_link_info = inode_link_info;
-
-       /* Keep the global singal flags*/
-       global_ctx->flags |= (flags & E2F_FLAG_SIGNAL_MASK) |
-                            (global_ctx->flags & E2F_FLAG_SIGNAL_MASK);
+       global_ctx->refcount = refcount;
+       global_ctx->refcount_extra = refcount_extra;
+       global_ctx->refcount_orig = refcount_orig;
+       global_ctx->ea_block_quota_blocks = ea_block_quota_blocks;
+       global_ctx->ea_block_quota_inodes = ea_block_quota_inodes;
+       global_ctx->block_ea_map = block_ea_map;
+       global_ctx->ea_inode_refs = ea_inode_refs;
+       global_ctx->fs_directory_count += fs_directory_count;
+       global_ctx->fs_regular_count += fs_regular_count;
+       global_ctx->fs_blockdev_count += fs_blockdev_count;
+       global_ctx->fs_chardev_count += fs_chardev_count;
+       global_ctx->fs_links_count += fs_links_count;
+       global_ctx->fs_symlinks_count += fs_symlinks_count;
+       global_ctx->fs_fast_symlinks_count += fs_fast_symlinks_count;
+       global_ctx->fs_fifo_count += fs_fifo_count;
+       global_ctx->fs_total_count += fs_total_count;
+       global_ctx->fs_badblocks_count += fs_badblocks_count;
+       global_ctx->fs_sockets_count += fs_sockets_count;
+       global_ctx->fs_ind_count += fs_ind_count;
+       global_ctx->fs_dind_count += fs_dind_count;
+       global_ctx->fs_tind_count += fs_tind_count;
+       global_ctx->fs_fragmented += fs_fragmented;
+       global_ctx->fs_fragmented_dir += fs_fragmented_dir;
+       global_ctx->large_files += large_files;
+
+       global_ctx->flags |= flags;
 
        retval = e2fsck_pass1_merge_fs(global_fs, thread_fs);
        if (retval) {
@@ -2548,6 +3039,27 @@ static int e2fsck_pass1_thread_join_one(e2fsck_t global_ctx, e2fsck_t thread_ctx
                return retval;
        }
 
+       global_ctx->dirs_to_hash = dirs_to_hash;
+       retval = e2fsck_pass1_merge_dirs_to_hash(global_ctx, thread_ctx);
+       if (retval) {
+               com_err(global_ctx->program_name, 0,
+                       _("while merging dirs to hash\n"));
+               return retval;
+       }
+
+       e2fsck_pass1_merge_ea_inode_refs(global_ctx, thread_ctx);
+       e2fsck_pass1_merge_ea_refcount(global_ctx, thread_ctx);
+       global_ctx->qctx = qctx;
+       retval = quota_merge_and_update_usage(global_ctx->qctx,
+                                             thread_ctx->qctx);
+       if (retval)
+               return retval;
+       global_ctx->invalid_block_bitmap_flag = invalid_block_bitmap_flag;
+       global_ctx->invalid_inode_bitmap_flag = invalid_inode_bitmap_flag;
+       global_ctx->invalid_inode_table_flag = invalid_inode_table_flag;
+       global_ctx->invalid_bitmaps = invalid_bitmaps;
+       e2fsck_pass1_merge_invalid_bitmaps(global_ctx, thread_ctx);
+
        retval = e2fsck_pass1_merge_bitmap(global_fs,
                                &thread_ctx->inode_used_map,
                                &global_ctx->inode_used_map);
@@ -2585,25 +3097,27 @@ static int e2fsck_pass1_thread_join_one(e2fsck_t global_ctx, e2fsck_t thread_ctx
        if (retval)
                return retval;
        retval = e2fsck_pass1_merge_bitmap(global_fs,
-                               &thread_ctx->block_found_map,
-                               &global_ctx->block_found_map);
-       if (retval)
-               return retval;
-       retval = e2fsck_pass1_merge_bitmap(global_fs,
-                               &thread_ctx->block_dup_map,
-                               &global_ctx->block_dup_map);
-       if (retval)
-               return retval;
-       retval = e2fsck_pass1_merge_bitmap(global_fs,
                                &thread_ctx->block_ea_map,
                                &global_ctx->block_ea_map);
        if (retval)
                return retval;
-       retval = e2fsck_pass1_merge_bitmap(global_fs,
-                               &thread_ctx->block_metadata_map,
-                               &global_ctx->block_metadata_map);
-       if (retval)
-               return retval;
+
+       if (ext2fs_has_feature_shared_blocks(global_fs->super) &&
+           !(global_ctx->options & E2F_OPT_UNSHARE_BLOCKS))
+               return 0;
+       /*
+        * This need be done after merging block_ea_map
+        * because ea block might be shared, we need exclude
+        * them from dup blocks.
+        */
+       e2fsck_pass1_block_map_w_lock(thread_ctx);
+       retval = ext2fs_merge_bitmap(thread_ctx->block_found_map,
+                                    global_ctx->block_found_map,
+                                    global_ctx->block_dup_map,
+                                    global_ctx->block_ea_map);
+       e2fsck_pass1_block_map_w_unlock(thread_ctx);
+       if (retval == EEXIST)
+               global_ctx->flags |= E2F_FLAG_DUP_BLOCK;
 
        return 0;
 }
@@ -2628,12 +3142,24 @@ static int e2fsck_pass1_thread_join(e2fsck_t global_ctx, e2fsck_t thread_ctx)
        e2fsck_pass1_free_bitmap(&thread_ctx->inode_reg_map);
        e2fsck_pass1_free_bitmap(&thread_ctx->inodes_to_rebuild);
        e2fsck_pass1_free_bitmap(&thread_ctx->block_found_map);
-       e2fsck_pass1_free_bitmap(&thread_ctx->block_dup_map);
        e2fsck_pass1_free_bitmap(&thread_ctx->block_ea_map);
-       e2fsck_pass1_free_bitmap(&thread_ctx->block_metadata_map);
+       if (thread_ctx->refcount)
+               ea_refcount_free(thread_ctx->refcount);
+       if (thread_ctx->refcount_extra)
+               ea_refcount_free(thread_ctx->refcount_extra);
+       if (thread_ctx->ea_inode_refs)
+               ea_refcount_free(thread_ctx->ea_inode_refs);
+       if (thread_ctx->refcount_orig)
+               ea_refcount_free(thread_ctx->refcount_orig);
        e2fsck_free_dir_info(thread_ctx);
        ext2fs_free_icount(thread_ctx->inode_count);
        ext2fs_free_icount(thread_ctx->inode_link_info);
+       if (thread_ctx->dirs_to_hash)
+               ext2fs_badblocks_list_free(thread_ctx->dirs_to_hash);
+       quota_release_context(&thread_ctx->qctx);
+       ext2fs_free_mem(&thread_ctx->invalid_block_bitmap_flag);
+       ext2fs_free_mem(&thread_ctx->invalid_inode_bitmap_flag);
+       ext2fs_free_mem(&thread_ctx->invalid_inode_table_flag);
        ext2fs_free_mem(&thread_ctx);
 
        return retval;
@@ -2647,6 +3173,8 @@ static int e2fsck_pass1_threads_join(struct e2fsck_thread_info *infos,
        int                              i;
        struct e2fsck_thread_info       *pinfo;
 
+       /* merge invalid bitmaps will recalculate it */
+       global_ctx->invalid_bitmaps = 0;
        for (i = 0; i < num_threads; i++) {
                pinfo = &infos[i];
 
@@ -2803,9 +3331,9 @@ static int e2fsck_pass1_threads_start(struct e2fsck_thread_info **pinfo,
 
 static void e2fsck_pass1_multithread(e2fsck_t global_ctx)
 {
-       struct e2fsck_thread_info       *infos = NULL;
-       int                              num_threads = 1;
-       errcode_t                        retval;
+       struct e2fsck_thread_info *infos = NULL;
+       int num_threads = global_ctx->fs_num_threads;
+       errcode_t retval;
 
        retval = e2fsck_pass1_threads_start(&infos, num_threads, global_ctx);
        if (retval) {
@@ -2827,48 +3355,24 @@ out_abort:
 }
 #endif
 
-/* TODO: tdb needs to be handled properly for multiple threads*/
-static int multiple_threads_supported(e2fsck_t ctx)
-{
-#ifdef CONFIG_TDB
-       unsigned int            threshold;
-       ext2_ino_t              num_dirs;
-       errcode_t               retval;
-       char                    *tdb_dir;
-       int                     enable;
-
-       profile_get_string(ctx->profile, "scratch_files", "directory", 0, 0,
-                          &tdb_dir);
-       profile_get_uint(ctx->profile, "scratch_files",
-                        "numdirs_threshold", 0, 0, &threshold);
-       profile_get_boolean(ctx->profile, "scratch_files",
-                           "icount", 0, 1, &enable);
-
-       retval = ext2fs_get_num_dirs(ctx->fs, &num_dirs);
-       if (retval)
-               num_dirs = 1024;        /* Guess */
-
-       /* tdb is unsupported now */
-       if (enable && tdb_dir && !access(tdb_dir, W_OK) &&
-           (!threshold || num_dirs > threshold))
-               return 0;
- #endif
-       return 1;
-}
-
 void e2fsck_pass1(e2fsck_t ctx)
 {
+       errcode_t retval;
+       int need_single = 1;
 
-       init_ext2_max_sizes();
+       retval = e2fsck_pass1_prepare(ctx);
+       if (retval)
+               return;
 #ifdef HAVE_PTHREAD
-       if (multiple_threads_supported(ctx)) {
+       if (ctx->fs_num_threads > 1 ||
+           ctx->options & E2F_OPT_MULTITHREAD) {
+               need_single = 0;
                e2fsck_pass1_multithread(ctx);
-               return;
        }
-       fprintf(stderr, "Fall through single thread for pass1 "
-                       "because tdb could not handle properly\n");
 #endif
-       e2fsck_pass1_run(ctx);
+       if (need_single)
+               e2fsck_pass1_run(ctx);
+       e2fsck_pass1_post(ctx);
 }
 
 #undef FINISH_INODE_LOOP
@@ -3077,30 +3581,20 @@ static void alloc_imagic_map(e2fsck_t ctx)
  */
 static _INLINE_ void mark_block_used(e2fsck_t ctx, blk64_t block)
 {
-       struct          problem_context pctx;
+       struct problem_context pctx;
+       e2fsck_t global_ctx = ctx->global_ctx ? ctx->global_ctx : ctx;
 
        clear_problem_context(&pctx);
 
-       if (ext2fs_fast_test_block_bitmap2(ctx->block_found_map, block)) {
+       if (is_blocks_used(ctx, block, 1)) {
                if (ext2fs_has_feature_shared_blocks(ctx->fs->super) &&
                    !(ctx->options & E2F_OPT_UNSHARE_BLOCKS)) {
                        return;
                }
-               if (!ctx->block_dup_map) {
-                       pctx.errcode = e2fsck_allocate_block_bitmap(ctx->fs,
-                                       _("multiply claimed block map"),
-                                       EXT2FS_BMAP64_RBTREE, "block_dup_map",
-                                       &ctx->block_dup_map);
-                       if (pctx.errcode) {
-                               pctx.num = 3;
-                               fix_problem(ctx, PR_1_ALLOCATE_BBITMAP_ERROR,
-                                           &pctx);
-                               /* Should never get here */
-                               ctx->flags |= E2F_FLAG_ABORT;
-                               return;
-                       }
-               }
-               ext2fs_fast_mark_block_bitmap2(ctx->block_dup_map, block);
+               ctx->flags |= E2F_FLAG_DUP_BLOCK;
+               e2fsck_pass1_block_map_w_lock(ctx);
+               ext2fs_fast_mark_block_bitmap2(global_ctx->block_dup_map, block);
+               e2fsck_pass1_block_map_w_unlock(ctx);
        } else {
                ext2fs_fast_mark_block_bitmap2(ctx->block_found_map, block);
        }
@@ -3113,9 +3607,9 @@ static _INLINE_ void mark_block_used(e2fsck_t ctx, blk64_t block)
 static _INLINE_ void mark_blocks_used(e2fsck_t ctx, blk64_t block,
                                      unsigned int num)
 {
-       if (ext2fs_test_block_bitmap_range2(ctx->block_found_map, block, num))
+       if (!is_blocks_used(ctx, block, num)) {
                ext2fs_mark_block_bitmap_range2(ctx->block_found_map, block, num);
-       else {
+       else {
                unsigned int i;
 
                for (i = 0; i < num; i += EXT2FS_CLUSTER_RATIO(ctx->fs))
@@ -3123,6 +3617,18 @@ static _INLINE_ void mark_blocks_used(e2fsck_t ctx, blk64_t block,
        }
 }
 
+static errcode_t _INLINE_ e2fsck_write_ext_attr3(e2fsck_t ctx, blk64_t block,
+                                                void *inbuf, ext2_ino_t inum)
+{
+       errcode_t retval;
+       ext2_filsys fs = ctx->fs;
+
+       e2fsck_pass1_fix_lock(ctx);
+       retval = ext2fs_write_ext_attr3(fs, block, inbuf, inum);
+       e2fsck_pass1_fix_unlock(ctx);
+
+       return retval;
+}
 /*
  * Adjust the extended attribute block's reference counts at the end
  * of pass 1, either by subtracting out references for EA blocks that
@@ -3159,7 +3665,7 @@ static void adjust_extattr_refcount(e2fsck_t ctx, ext2_refcount_t refcount,
                pctx.num = should_be;
                if (fix_problem(ctx, PR_1_EXTATTR_REFCOUNT, &pctx)) {
                        header->h_refcount = should_be;
-                       pctx.errcode = ext2fs_write_ext_attr3(fs, blk,
+                       pctx.errcode = e2fsck_write_ext_attr3(ctx, blk,
                                                             block_buf,
                                                             pctx.ino);
                        if (pctx.errcode) {
@@ -3226,6 +3732,15 @@ static int check_ext_attr(e2fsck_t ctx, struct problem_context *pctx,
 
        /* Create the EA refcount structure if necessary */
        if (!ctx->refcount) {
+               pctx->errcode = ea_refcount_create(0,
+                                       &ctx->refcount_orig);
+               if (pctx->errcode) {
+                       pctx->num = 1;
+                       fix_problem(ctx, PR_1_ALLOCATE_REFCOUNT, pctx);
+                       ctx->flags |= E2F_FLAG_ABORT;
+                       return 0;
+               }
+
                pctx->errcode = ea_refcount_create(0, &ctx->refcount);
                if (pctx->errcode) {
                        pctx->num = 1;
@@ -3390,7 +3905,7 @@ static int check_ext_attr(e2fsck_t ctx, struct problem_context *pctx,
         */
        if (failed_csum &&
            fix_problem(ctx, PR_1_EA_BLOCK_ONLY_CSUM_INVALID, pctx)) {
-               pctx->errcode = ext2fs_write_ext_attr3(fs, blk, block_buf,
+               pctx->errcode = e2fsck_write_ext_attr3(ctx, blk, block_buf,
                                                       pctx->ino);
                if (pctx->errcode)
                        return 0;
@@ -3430,7 +3945,13 @@ refcount_fail:
 
        inc_ea_inode_refs(ctx, pctx, first, end);
        ea_refcount_store(ctx->refcount, blk, header->h_refcount - 1);
-       mark_block_used(ctx, blk);
+       ea_refcount_store(ctx->refcount_orig, blk, header->h_refcount);
+       /**
+        * It might be racy that this block has been merged in the
+        * global found map.
+        */
+       if (!is_blocks_used(ctx, blk, 1))
+               ext2fs_fast_mark_block_bitmap2(ctx->block_found_map, blk);
        ext2fs_fast_mark_block_bitmap2(ctx->block_ea_map, blk);
        return 1;
 
@@ -3701,10 +4222,12 @@ static void scan_extent_node(e2fsck_t ctx, struct problem_context *pctx,
                if (try_repairs && is_dir && problem == 0 &&
                    (extent.e_flags & EXT2_EXTENT_FLAGS_UNINIT) &&
                    fix_problem(ctx, PR_1_UNINIT_DBLOCK, pctx)) {
+                       e2fsck_pass1_fix_lock(ctx);
                        extent.e_flags &= ~EXT2_EXTENT_FLAGS_UNINIT;
                        pb->inode_modified = 1;
                        pctx->errcode = ext2fs_extent_replace(ehandle, 0,
                                                              &extent);
+                       e2fsck_pass1_fix_unlock(ctx);
                        if (pctx->errcode)
                                return;
                        failed_csum = 0;
@@ -3746,15 +4269,19 @@ report_problem:
                                        }
                                        continue;
                                }
+                               e2fsck_pass1_fix_lock(ctx);
                                e2fsck_read_bitmaps(ctx);
                                pb->inode_modified = 1;
                                pctx->errcode =
                                        ext2fs_extent_delete(ehandle, 0);
+                               e2fsck_pass1_fix_unlock(ctx);
                                if (pctx->errcode) {
                                        pctx->str = "ext2fs_extent_delete";
                                        return;
                                }
+                               e2fsck_pass1_fix_lock(ctx);
                                pctx->errcode = ext2fs_extent_fix_parents(ehandle);
+                               e2fsck_pass1_fix_unlock(ctx);
                                if (pctx->errcode &&
                                    pctx->errcode != EXT2_ET_NO_CURRENT_NODE) {
                                        pctx->str = "ext2fs_extent_fix_parents";
@@ -3823,9 +4350,11 @@ report_problem:
                                pctx->num = e_info.curr_level - 1;
                                problem = PR_1_EXTENT_INDEX_START_INVALID;
                                if (fix_problem(ctx, problem, pctx)) {
+                                       e2fsck_pass1_fix_lock(ctx);
                                        pb->inode_modified = 1;
                                        pctx->errcode =
                                                ext2fs_extent_fix_parents(ehandle);
+                                       e2fsck_pass1_fix_unlock(ctx);
                                        if (pctx->errcode) {
                                                pctx->str = "ext2fs_extent_fix_parents";
                                                return;
@@ -3889,15 +4418,19 @@ report_problem:
                        pctx->blk = extent.e_lblk;
                        pctx->blk2 = new_lblk;
                        if (fix_problem(ctx, PR_1_COLLAPSE_DBLOCK, pctx)) {
+                               e2fsck_pass1_fix_lock(ctx);
                                extent.e_lblk = new_lblk;
                                pb->inode_modified = 1;
                                pctx->errcode = ext2fs_extent_replace(ehandle,
                                                                0, &extent);
+                               e2fsck_pass1_fix_unlock(ctx);
                                if (pctx->errcode) {
                                        pctx->errcode = 0;
                                        goto alloc_later;
                                }
+                               e2fsck_pass1_fix_lock(ctx);
                                pctx->errcode = ext2fs_extent_fix_parents(ehandle);
+                               e2fsck_pass1_fix_unlock(ctx);
                                if (pctx->errcode)
                                        goto failed_add_dir_block;
                                pctx->errcode = ext2fs_extent_goto(ehandle,
@@ -3993,8 +4526,10 @@ alloc_later:
        /* Failed csum but passes checks?  Ask to fix checksum. */
        if (failed_csum &&
            fix_problem(ctx, PR_1_EXTENT_ONLY_CSUM_INVALID, pctx)) {
+               e2fsck_pass1_fix_lock(ctx);
                pb->inode_modified = 1;
                pctx->errcode = ext2fs_extent_replace(ehandle, 0, &extent);
+               e2fsck_pass1_fix_unlock(ctx);
                if (pctx->errcode)
                        return;
        }
@@ -4705,8 +5240,7 @@ static int process_bad_block(ext2_filsys fs,
                                *block_nr = 0;
                                return BLOCK_CHANGED;
                        }
-               } else if (ext2fs_test_block_bitmap2(ctx->block_found_map,
-                                                   blk)) {
+               } else if (is_blocks_used(ctx, blk, 1)) {
                        p->bbcheck = 1;
                        if (fix_problem(ctx, PR_1_BBINODE_BAD_METABLOCK,
                                        pctx)) {
@@ -4715,8 +5249,9 @@ static int process_bad_block(ext2_filsys fs,
                        }
                        if (e2fsck_should_abort(ctx))
                                return BLOCK_ABORT;
-               } else
+               } else {
                        mark_block_used(ctx, blk);
+               }
                return 0;
        }
 #if 0
@@ -4729,7 +5264,7 @@ static int process_bad_block(ext2_filsys fs,
         * there's an overlap between the filesystem table blocks
         * (bitmaps and inode table) and the bad block list.
         */
-       if (!ext2fs_test_block_bitmap2(ctx->block_found_map, blk)) {
+       if (!is_blocks_used(ctx, blk, 1)) {
                ext2fs_mark_block_bitmap2(ctx->block_found_map, blk);
                return 0;
        }