Whamcloud - gitweb
e2fsck: misc cleanups for pfsck
[tools/e2fsprogs.git] / e2fsck / pass1.c
index 808476d..a0d6bc3 100644 (file)
@@ -991,8 +991,10 @@ static void finish_processing_inode(e2fsck_t ctx, ext2_ino_t ino,
 #define FINISH_INODE_LOOP(ctx, ino, pctx, failed_csum) \
        do { \
                finish_processing_inode((ctx), (ino), (pctx), (failed_csum)); \
-               if ((ctx)->flags & E2F_FLAG_ABORT) \
+               if ((ctx)->flags & E2F_FLAG_ABORT) { \
+                       e2fsck_pass1_check_unlock(ctx); \
                        return; \
+               } \
        } while (0)
 
 static int could_be_block_map(ext2_filsys fs, struct ext2_inode *inode)
@@ -1098,16 +1100,20 @@ out:
 static void pass1_readahead(e2fsck_t ctx, dgrp_t *group, ext2_ino_t *next_ino)
 {
        ext2_ino_t inodes_in_group = 0, inodes_per_block, inodes_per_buffer;
-       dgrp_t start = *group, grp;
+       dgrp_t start = *group, grp, grp_end = ctx->fs->group_desc_count;
        blk64_t blocks_to_read = 0;
        errcode_t err = EXT2_ET_INVALID_ARGUMENT;
 
+#ifdef HAVE_PTHREAD
+       if (ctx->fs->fs_num_threads > 1)
+               grp_end = ctx->thread_info.et_group_end;
+#endif
        if (ctx->readahead_kb == 0)
                goto out;
 
        /* Keep iterating groups until we have enough to readahead */
        inodes_per_block = EXT2_INODES_PER_BLOCK(ctx->fs->super);
-       for (grp = start; grp < ctx->fs->group_desc_count; grp++) {
+       for (grp = start; grp < grp_end; grp++) {
                if (ext2fs_bg_flags_test(ctx->fs, grp, EXT2_BG_INODE_UNINIT))
                        continue;
                inodes_in_group = ctx->fs->super->s_inodes_per_group -
@@ -1259,7 +1265,7 @@ 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 num_threads = ctx->pfs_num_threads;
        int max_threads;
 
        if (num_threads < 1) {
@@ -1279,6 +1285,8 @@ static void e2fsck_pass1_set_thread_num(e2fsck_t ctx)
        max_threads = fs->group_desc_count / flexbg_size;
        if (max_threads == 0)
                max_threads = 1;
+       if (max_threads > E2FSCK_MAX_THREADS)
+               max_threads = E2FSCK_MAX_THREADS;
 
        if (num_threads > max_threads) {
                fprintf(stderr, "Use max possible thread num: %d instead\n",
@@ -1286,7 +1294,7 @@ static void e2fsck_pass1_set_thread_num(e2fsck_t ctx)
                num_threads = max_threads;
        }
 out:
-       ctx->fs_num_threads = num_threads;
+       ctx->pfs_num_threads = num_threads;
        ctx->fs->fs_num_threads = num_threads;
 }
 #endif
@@ -1300,12 +1308,25 @@ static errcode_t e2fsck_pass1_prepare(e2fsck_t ctx)
 {
        struct problem_context pctx;
        ext2_filsys fs = ctx->fs;
+       unsigned long long readahead_kb;
 
        init_ext2_max_sizes();
-#ifdef HAVE_PTHREAD
+#ifdef HAVE_PTHREAD
        e2fsck_pass1_set_thread_num(ctx);
 #endif
+       /* If we can do readahead, figure out how many groups to pull in. */
+       if (!e2fsck_can_readahead(ctx->fs))
+               ctx->readahead_kb = 0;
+       else if (ctx->readahead_kb == ~0ULL)
+               ctx->readahead_kb = e2fsck_guess_readahead(ctx->fs);
 
+#ifdef HAVE_PTHREAD
+       /* don't use more than 1/10 of memory for threads checking */
+       readahead_kb = get_memory_size() / (10 * ctx->pfs_num_threads);
+       /* maybe better disable RA if this is too small? */
+       if (ctx->readahead_kb > readahead_kb)
+               ctx->readahead_kb = readahead_kb;
+#endif
        clear_problem_context(&pctx);
        if (!(ctx->options & E2F_OPT_PREEN))
                fix_problem(ctx, PR_1_PASS_HEADER, &pctx);
@@ -1357,8 +1378,10 @@ static errcode_t e2fsck_pass1_prepare(e2fsck_t ctx)
                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_fix_rwlock, NULL);
        pthread_rwlock_init(&ctx->fs_block_map_rwlock, NULL);
+       if (ctx->pfs_num_threads > 1)
+               ctx->fs_need_locking = 1;
 #endif
 
        return 0;
@@ -1368,9 +1391,12 @@ static void e2fsck_pass1_post(e2fsck_t ctx)
 {
        struct problem_context pctx;
        ext2_filsys fs = ctx->fs;
+       char *block_buf;
+
+       if (e2fsck_should_abort(ctx))
+               return;
 
-       char *block_buf =
-               (char *)e2fsck_allocate_memory(ctx, ctx->fs->blocksize * 3,
+       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);
@@ -1448,6 +1474,8 @@ static void e2fsck_pass1_post(e2fsck_t ctx)
                ext2fs_free_mem(&block_buf);
                ctx->flags &= ~E2F_FLAG_DUP_BLOCK;
        }
+
+       ctx->flags |= E2F_FLAG_ALLOC_OK;
 }
 
 
@@ -1477,18 +1505,13 @@ void e2fsck_pass1_run(e2fsck_t ctx)
        dgrp_t          ra_group = 0;
        struct ea_quota ea_ibody_quota;
        struct process_inode_block *inodes_to_process;
-       int             process_inode_count;
+       int             process_inode_count, check_mmp;
+       e2fsck_t        global_ctx = ctx->global_ctx ? ctx->global_ctx : ctx;
 
        init_resource_track(&rtrack, ctx->fs->io);
        clear_problem_context(&pctx);
 
-       /* If we can do readahead, figure out how many groups to pull in. */
-       if (!e2fsck_can_readahead(ctx->fs))
-               ctx->readahead_kb = 0;
-       else if (ctx->readahead_kb == ~0ULL)
-               ctx->readahead_kb = e2fsck_guess_readahead(ctx->fs);
        pass1_readahead(ctx, &ra_group, &ino_threshold);
-
        if (ext2fs_has_feature_dir_index(fs->super) &&
            !(ctx->options & E2F_OPT_NO)) {
                if (ext2fs_u32_list_create(&ctx->dirs_to_hash, 50))
@@ -1632,7 +1655,7 @@ void e2fsck_pass1_run(e2fsck_t ctx)
        if (ctx->global_ctx) {
                if (ctx->options & E2F_OPT_DEBUG &&
                    ctx->options & E2F_OPT_MULTITHREAD)
-                       fprintf(stderr, "thread %d jumping to group %d\n",
+                       fprintf(stderr, "thread %d jumping to group %u\n",
                                        ctx->thread_info.et_thread_index,
                                        ctx->thread_info.et_group_start);
                pctx.errcode = ext2fs_inode_scan_goto_blockgroup(scan,
@@ -1646,7 +1669,30 @@ void e2fsck_pass1_run(e2fsck_t ctx)
 #endif
 
        while (1) {
-               if (ino % (fs->super->s_inodes_per_group * 4) == 1) {
+               check_mmp = 0;
+               e2fsck_pass1_check_lock(ctx);
+#ifdef HAVE_PTHREAD
+               if (!global_ctx->mmp_update_thread) {
+                       e2fsck_pass1_block_map_w_lock(ctx);
+                       if (!global_ctx->mmp_update_thread) {
+                               global_ctx->mmp_update_thread =
+                                       ctx->thread_info.et_thread_index + 1;
+                               check_mmp = 1;
+                       }
+                       e2fsck_pass1_block_map_w_unlock(ctx);
+               }
+
+               /* only one active thread could update mmp block. */
+               e2fsck_pass1_block_map_r_lock(ctx);
+               if (global_ctx->mmp_update_thread ==
+                   ctx->thread_info.et_thread_index + 1)
+                       check_mmp = 1;
+               e2fsck_pass1_block_map_r_unlock(ctx);
+#else
+               check_mmp = 1;
+#endif
+
+               if (check_mmp && (ino % (fs->super->s_inodes_per_group * 4) == 1)) {
                        if (e2fsck_mmp_update(fs))
                                fatal_error(ctx, 0);
                }
@@ -1656,8 +1702,10 @@ void e2fsck_pass1_run(e2fsck_t ctx)
                if (ino > ino_threshold)
                        pass1_readahead(ctx, &ra_group, &ino_threshold);
                ehandler_operation(old_op);
-               if (e2fsck_should_abort(ctx))
+               if (e2fsck_should_abort(ctx)) {
+                       e2fsck_pass1_check_unlock(ctx);
                        goto endit;
+               }
                if (pctx.errcode == EXT2_ET_BAD_BLOCK_IN_INODE_TABLE) {
                        /*
                         * If badblocks says badblocks is bad, offer to clear
@@ -1678,27 +1726,45 @@ void e2fsck_pass1_run(e2fsck_t ctx)
                                        fix_problem(ctx, PR_1_ISCAN_ERROR,
                                                    &pctx);
                                        ctx->flags |= E2F_FLAG_ABORT;
+                                       e2fsck_pass1_check_unlock(ctx);
+                                       goto endit;
                                } else
                                        ctx->flags |= E2F_FLAG_RESTART;
-                               goto endit;
+                               err = ext2fs_inode_scan_goto_blockgroup(scan,
+                                                                       0);
+                               if (err) {
+                                       fix_problem(ctx, PR_1_ISCAN_ERROR,
+                                                   &pctx);
+                                       ctx->flags |= E2F_FLAG_ABORT;
+                                       e2fsck_pass1_check_unlock(ctx);
+                                       goto endit;
+                               }
+                               e2fsck_pass1_check_unlock(ctx);
+                               continue;
                        }
                        if (!ctx->inode_bb_map)
                                alloc_bb_map(ctx);
                        ext2fs_mark_inode_bitmap2(ctx->inode_bb_map, ino);
                        ext2fs_mark_inode_bitmap2(ctx->inode_used_map, ino);
+                       e2fsck_pass1_check_unlock(ctx);
                        continue;
                }
-               if (pctx.errcode == EXT2_ET_SCAN_FINISHED)
+               if (pctx.errcode == EXT2_ET_SCAN_FINISHED) {
+                       e2fsck_pass1_check_unlock(ctx);
                        break;
+               }
                if (pctx.errcode &&
                    pctx.errcode != EXT2_ET_INODE_CSUM_INVALID &&
                    pctx.errcode != EXT2_ET_INODE_IS_GARBAGE) {
                        fix_problem(ctx, PR_1_ISCAN_ERROR, &pctx);
                        ctx->flags |= E2F_FLAG_ABORT;
+                       e2fsck_pass1_check_unlock(ctx);
                        goto endit;
                }
-               if (!ino)
+               if (!ino) {
+                       e2fsck_pass1_check_unlock(ctx);
                        break;
+               }
 #ifdef HAVE_PTHREAD
                if (ctx->global_ctx)
                        ctx->thread_info.et_inode_number++;
@@ -1751,6 +1817,7 @@ void e2fsck_pass1_run(e2fsck_t ctx)
                                pctx.num = inode->i_links_count;
                                fix_problem(ctx, PR_1_ICOUNT_STORE, &pctx);
                                ctx->flags |= E2F_FLAG_ABORT;
+                               e2fsck_pass1_check_unlock(ctx);
                                goto endit;
                        }
                } else if ((ino >= EXT2_FIRST_INODE(fs->super)) &&
@@ -1765,6 +1832,7 @@ void e2fsck_pass1_run(e2fsck_t ctx)
                                }
                        }
                        FINISH_INODE_LOOP(ctx, ino, &pctx, failed_csum);
+                       e2fsck_pass1_check_unlock(ctx);
                        continue;
                }
 
@@ -1785,6 +1853,7 @@ void e2fsck_pass1_run(e2fsck_t ctx)
                                                               &pctx);
                        if (res < 0) {
                                /* skip FINISH_INODE_LOOP */
+                               e2fsck_pass1_check_unlock(ctx);
                                continue;
                        }
                }
@@ -1805,6 +1874,7 @@ void e2fsck_pass1_run(e2fsck_t ctx)
                        } else if (fix_problem(ctx, PR_1_INLINE_DATA_SET, &pctx)) {
                                e2fsck_clear_inode(ctx, ino, inode, 0, "pass1");
                                /* skip FINISH_INODE_LOOP */
+                               e2fsck_pass1_check_unlock(ctx);
                                continue;
                        }
                }
@@ -1849,6 +1919,7 @@ void e2fsck_pass1_run(e2fsck_t ctx)
                                        if (err) {
                                                pctx.errcode = err;
                                                ctx->flags |= E2F_FLAG_ABORT;
+                                               e2fsck_pass1_check_unlock(ctx);
                                                goto endit;
                                        }
                                        inode->i_flags &= ~EXT4_INLINE_DATA_FL;
@@ -1863,6 +1934,7 @@ void e2fsck_pass1_run(e2fsck_t ctx)
                                /* Some other kind of non-xattr error? */
                                pctx.errcode = err;
                                ctx->flags |= E2F_FLAG_ABORT;
+                               e2fsck_pass1_check_unlock(ctx);
                                goto endit;
                        }
                }
@@ -1900,6 +1972,7 @@ void e2fsck_pass1_run(e2fsck_t ctx)
                                        ext2fs_mark_inode_bitmap2(ctx->inode_used_map,
                                                                 ino);
                                /* skip FINISH_INODE_LOOP */
+                               e2fsck_pass1_check_unlock(ctx);
                                continue;
                        }
                }
@@ -1963,6 +2036,7 @@ void e2fsck_pass1_run(e2fsck_t ctx)
                                pctx.num = 4;
                                fix_problem(ctx, PR_1_ALLOCATE_BBITMAP_ERROR, &pctx);
                                ctx->flags |= E2F_FLAG_ABORT;
+                               e2fsck_pass1_check_unlock(ctx);
                                goto endit;
                        }
                        pb.ino = EXT2_BAD_INO;
@@ -1980,16 +2054,19 @@ void e2fsck_pass1_run(e2fsck_t ctx)
                        if (pctx.errcode) {
                                fix_problem(ctx, PR_1_BLOCK_ITERATE, &pctx);
                                ctx->flags |= E2F_FLAG_ABORT;
+                               e2fsck_pass1_check_unlock(ctx);
                                goto endit;
                        }
                        if (pb.bbcheck)
                                if (!fix_problem(ctx, PR_1_BBINODE_BAD_METABLOCK_PROMPT, &pctx)) {
                                ctx->flags |= E2F_FLAG_ABORT;
+                               e2fsck_pass1_check_unlock(ctx);
                                goto endit;
                        }
                        ext2fs_mark_inode_bitmap2(ctx->inode_used_map, ino);
                        clear_problem_context(&pctx);
                        FINISH_INODE_LOOP(ctx, ino, &pctx, failed_csum);
+                       e2fsck_pass1_check_unlock(ctx);
                        continue;
                } else if (ino == EXT2_ROOT_INO) {
                        /*
@@ -2031,6 +2108,7 @@ void e2fsck_pass1_run(e2fsck_t ctx)
                                }
                                check_blocks(ctx, &pctx, block_buf, NULL);
                                FINISH_INODE_LOOP(ctx, ino, &pctx, failed_csum);
+                               e2fsck_pass1_check_unlock(ctx);
                                continue;
                        }
                        if ((inode->i_links_count ||
@@ -2058,6 +2136,7 @@ void e2fsck_pass1_run(e2fsck_t ctx)
                                }
                                check_blocks(ctx, &pctx, block_buf, NULL);
                                FINISH_INODE_LOOP(ctx, ino, &pctx, failed_csum);
+                               e2fsck_pass1_check_unlock(ctx);
                                continue;
                        }
                        if ((inode->i_links_count ||
@@ -2096,11 +2175,13 @@ void e2fsck_pass1_run(e2fsck_t ctx)
                        }
                        check_blocks(ctx, &pctx, block_buf, NULL);
                        FINISH_INODE_LOOP(ctx, ino, &pctx, failed_csum);
+                       e2fsck_pass1_check_unlock(ctx);
                        continue;
                }
 
                if (!inode->i_links_count) {
                        FINISH_INODE_LOOP(ctx, ino, &pctx, failed_csum);
+                       e2fsck_pass1_check_unlock(ctx);
                        continue;
                }
                /*
@@ -2210,12 +2291,14 @@ void e2fsck_pass1_run(e2fsck_t ctx)
                        ctx->fs_symlinks_count++;
                        if (inode->i_flags & EXT4_INLINE_DATA_FL) {
                                FINISH_INODE_LOOP(ctx, ino, &pctx, failed_csum);
+                               e2fsck_pass1_check_unlock(ctx);
                                continue;
                        } else if (ext2fs_is_fast_symlink(inode)) {
                                ctx->fs_fast_symlinks_count++;
                                check_blocks(ctx, &pctx, block_buf,
                                             &ea_ibody_quota);
                                FINISH_INODE_LOOP(ctx, ino, &pctx, failed_csum);
+                               e2fsck_pass1_check_unlock(ctx);
                                continue;
                        }
                }
@@ -2263,16 +2346,21 @@ void e2fsck_pass1_run(e2fsck_t ctx)
 
                FINISH_INODE_LOOP(ctx, ino, &pctx, failed_csum);
 
-               if (ctx->flags & E2F_FLAG_SIGNAL_MASK)
+               if (e2fsck_should_abort(ctx)) {
+                       e2fsck_pass1_check_unlock(ctx);
                        goto endit;
+               }
 
                if (process_inode_count >= ctx->process_inode_size) {
                        process_inodes(ctx, block_buf, inodes_to_process,
                                       &process_inode_count);
 
-                       if (e2fsck_should_abort(ctx))
+                       if (e2fsck_should_abort(ctx)) {
+                               e2fsck_pass1_check_unlock(ctx);
                                goto endit;
+                       }
                }
+               e2fsck_pass1_check_unlock(ctx);
        }
        process_inodes(ctx, block_buf, inodes_to_process,
                       &process_inode_count);
@@ -2317,6 +2405,7 @@ void e2fsck_pass1_run(e2fsck_t ctx)
        }
 
        ctx->flags |= E2F_FLAG_ALLOC_OK;
+       ext2fs_free_mem(&inodes_to_process);
 endit:
        e2fsck_use_inode_shortcuts(ctx, 0);
        ext2fs_free_mem(&inodes_to_process);
@@ -2339,6 +2428,13 @@ endit:
                print_resource_track(ctx, _("Pass 1"), &rtrack, ctx->fs->io);
        else
                ctx->invalid_bitmaps++;
+#ifdef HAVE_PTHREAD
+       /* reset update_thread after this thread exit */
+       e2fsck_pass1_block_map_w_lock(ctx);
+       if (check_mmp)
+               global_ctx->mmp_update_thread = 0;
+       e2fsck_pass1_block_map_w_unlock(ctx);
+#endif
 }
 
 #ifdef HAVE_PTHREAD
@@ -2459,6 +2555,7 @@ static int e2fsck_pass1_merge_fs(ext2_filsys dest, ext2_filsys src)
        ext2_badblocks_list badblocks;
        ext2_dblist dblist;
        int flags;
+       e2fsck_t dest_ctx = dest->priv_data;
 
        dest_io = dest->io;
        dest_image_io = dest->image_io;
@@ -2476,6 +2573,7 @@ static int e2fsck_pass1_merge_fs(ext2_filsys dest, ext2_filsys src)
        dest->block_map = block_map;
        dest->badblocks = badblocks;
        dest->dblist = dblist;
+       dest->priv_data = dest_ctx;
        if (dest->dblist)
                dest->dblist->fs = dest;
        dest->flags = src->flags | flags;
@@ -2617,6 +2715,7 @@ static errcode_t e2fsck_pass1_thread_prepare(e2fsck_t global_ctx, e2fsck_t *thre
        }
        memcpy(thread_context, global_ctx, sizeof(struct e2fsck_struct));
        thread_context->block_dup_map = NULL;
+       thread_context->casefolded_dirs = NULL;
 
        retval = e2fsck_allocate_block_bitmap(global_ctx->fs,
                                _("in-use block map"), EXT2FS_BMAP64_RBTREE,
@@ -2908,124 +3007,69 @@ static errcode_t e2fsck_pass1_merge_ea_refcount(e2fsck_t global_ctx,
        return retval;
 }
 
-static int e2fsck_pass1_thread_join_one(e2fsck_t global_ctx, e2fsck_t thread_ctx)
+static errcode_t e2fsck_pass1_merge_casefolded_dirs(e2fsck_t global_ctx,
+                                                  e2fsck_t thread_ctx)
 {
-       errcode_t        retval;
-       int              flags = global_ctx->flags;
-       ext2_filsys      thread_fs = thread_ctx->fs;
-       ext2_filsys      global_fs = global_ctx->fs;
-       FILE            *global_logf = global_ctx->logf;
-       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 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;
+       errcode_t retval = 0;
 
-#ifdef HAVE_SETJMP_H
-       jmp_buf          old_jmp;
+       if (!thread_ctx->casefolded_dirs)
+               return 0;
 
-       memcpy(old_jmp, global_ctx->abort_loc, sizeof(jmp_buf));
-#endif
-       memcpy(global_ctx, thread_ctx, sizeof(struct e2fsck_struct));
-#ifdef HAVE_SETJMP_H
-       memcpy(global_ctx->abort_loc, old_jmp, sizeof(jmp_buf));
-#endif
+       if (!global_ctx->casefolded_dirs)
+               retval = ext2fs_badblocks_copy(thread_ctx->casefolded_dirs,
+                                              &global_ctx->casefolded_dirs);
+       else
+               retval = ext2fs_badblocks_merge(thread_ctx->casefolded_dirs,
+                                               global_ctx->casefolded_dirs);
+
+       return retval;
+}
+
+static errcode_t e2fsck_pass1_merge_context(e2fsck_t global_ctx,
+                                           e2fsck_t thread_ctx)
+{
+       ext2_filsys global_fs = global_ctx->fs;
+       errcode_t retval;
+       int i;
+
+       global_ctx->fs_directory_count += thread_ctx->fs_directory_count;
+       global_ctx->fs_regular_count += thread_ctx->fs_regular_count;
+       global_ctx->fs_blockdev_count += thread_ctx->fs_blockdev_count;
+       global_ctx->fs_chardev_count += thread_ctx->fs_chardev_count;
+       global_ctx->fs_links_count += thread_ctx->fs_links_count;
+       global_ctx->fs_symlinks_count += thread_ctx->fs_symlinks_count;
+       global_ctx->fs_fast_symlinks_count += thread_ctx->fs_fast_symlinks_count;
+       global_ctx->fs_fifo_count += thread_ctx->fs_fifo_count;
+       global_ctx->fs_total_count += thread_ctx->fs_total_count;
+       global_ctx->fs_badblocks_count += thread_ctx->fs_badblocks_count;
+       global_ctx->fs_sockets_count += thread_ctx->fs_sockets_count;
+       global_ctx->fs_ind_count += thread_ctx->fs_ind_count;
+       global_ctx->fs_dind_count += thread_ctx->fs_dind_count;
+       global_ctx->fs_tind_count += thread_ctx->fs_tind_count;
+       global_ctx->fs_fragmented += thread_ctx->fs_fragmented;
+       global_ctx->fs_fragmented_dir += thread_ctx->fs_fragmented_dir;
+       global_ctx->large_files += thread_ctx->large_files;
+       /* threads might enable E2F_OPT_YES */
+       global_ctx->options |= thread_ctx->options;
+       global_ctx->flags |= thread_ctx->flags;
+       /*
+        * The l+f inode may have been cleared, so zap it now and
+        * later passes will recalculate it if necessary
+        */
+       global_ctx->lost_and_found = 0;
+       /* merge extent depth count */
+       for (i = 0; i < MAX_EXTENT_DEPTH_COUNT; i++)
+               global_ctx->extent_depth_count[i] +=
+                       thread_ctx->extent_depth_count[i];
 
-       global_ctx->inode_used_map = inode_used_map;
-       global_ctx->inode_bad_map = inode_bad_map;
-       global_ctx->inode_dir_map = inode_dir_map;
-       global_ctx->inode_bb_map = inode_bb_map;
-       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_dup_map = block_dup_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;
-       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);
+
+       retval = e2fsck_pass1_merge_fs(global_ctx->fs, thread_ctx->fs);
        if (retval) {
                com_err(global_ctx->program_name, 0, _("while merging fs\n"));
                return retval;
        }
-       global_fs->priv_data = global_ctx;
-       global_ctx->fs = global_fs;
-       global_ctx->logf = global_logf;
-       global_ctx->problem_logf = global_problem_logf;
-       global_ctx->global_ctx = NULL;
        retval = e2fsck_pass1_merge_icounts(global_ctx, thread_ctx);
        if (retval) {
                com_err(global_ctx->program_name, 0,
@@ -3033,7 +3077,6 @@ 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,
@@ -3043,15 +3086,18 @@ static int e2fsck_pass1_thread_join_one(e2fsck_t global_ctx, e2fsck_t thread_ctx
 
        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;
+
+       retval = e2fsck_pass1_merge_casefolded_dirs(global_ctx, thread_ctx);
+       if (retval) {
+               com_err(global_ctx->program_name, 0,
+                       _("while merging casefolded dirs\n"));
+               return retval;
+       }
+
        e2fsck_pass1_merge_invalid_bitmaps(global_ctx, thread_ctx);
 
        retval = e2fsck_pass1_merge_bitmap(global_fs,
@@ -3120,7 +3166,7 @@ static int e2fsck_pass1_thread_join(e2fsck_t global_ctx, e2fsck_t thread_ctx)
 {
        errcode_t       retval;
 
-       retval = e2fsck_pass1_thread_join_one(global_ctx, thread_ctx);
+       retval = e2fsck_pass1_merge_context(global_ctx, thread_ctx);
        ext2fs_free_mem(&thread_ctx->fs);
        if (thread_ctx->logf)
                fclose(thread_ctx->logf);
@@ -3128,32 +3174,15 @@ static int e2fsck_pass1_thread_join(e2fsck_t global_ctx, e2fsck_t thread_ctx)
                fputs("</problem_log>\n", thread_ctx->problem_logf);
                fclose(thread_ctx->problem_logf);
        }
-       e2fsck_pass1_free_bitmap(&thread_ctx->inode_used_map);
-       e2fsck_pass1_free_bitmap(&thread_ctx->inode_bad_map);
-       e2fsck_pass1_free_bitmap(&thread_ctx->inode_dir_map);
-       e2fsck_pass1_free_bitmap(&thread_ctx->inode_bb_map);
-       e2fsck_pass1_free_bitmap(&thread_ctx->inode_imagic_map);
-       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_ea_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);
+       /*
+        * @block_metadata_map and @block_dup_map are
+        * shared, so we don't free them.
+        */
+       thread_ctx->block_metadata_map = NULL;
+       thread_ctx->block_dup_map = NULL;
+       e2fsck_reset_context(thread_ctx);
        ext2fs_free_mem(&thread_ctx);
 
        return retval;
@@ -3162,11 +3191,11 @@ static int e2fsck_pass1_thread_join(e2fsck_t global_ctx, e2fsck_t thread_ctx)
 static int e2fsck_pass1_threads_join(struct e2fsck_thread_info *infos,
                                     e2fsck_t global_ctx)
 {
-       errcode_t                        rc;
-       errcode_t                        ret = 0;
-       int                              i;
-       struct e2fsck_thread_info       *pinfo;
-       int                              num_threads = global_ctx->fs_num_threads;
+       errcode_t rc;
+       errcode_t ret = 0;
+       struct e2fsck_thread_info *pinfo;
+       int num_threads = global_ctx->pfs_num_threads;
+       int i;
 
        /* merge invalid bitmaps will recalculate it */
        global_ctx->invalid_bitmaps = 0;
@@ -3232,7 +3261,7 @@ static void *e2fsck_pass1_thread(void *arg)
 out:
        if (thread_ctx->options & E2F_OPT_MULTITHREAD)
                log_out(thread_ctx,
-                       _("Scanned group range [%lu, %lu), inodes %lu\n"),
+                       _("Scanned group range [%u, %u), inodes %u\n"),
                        thread_ctx->thread_info.et_group_start,
                        thread_ctx->thread_info.et_group_end,
                        thread_ctx->thread_info.et_inode_number);
@@ -3247,6 +3276,35 @@ out:
        return NULL;
 }
 
+static dgrp_t ext2fs_get_avg_group(ext2_filsys fs)
+{
+#ifdef HAVE_PTHREAD
+       dgrp_t average_group;
+       unsigned flexbg_size;
+
+       if (fs->fs_num_threads <= 1)
+               return fs->group_desc_count;
+
+       average_group = fs->group_desc_count / fs->fs_num_threads;
+       if (average_group <= 1)
+               return 1;
+
+       if (ext2fs_has_feature_flex_bg(fs->super)) {
+               int times = 1;
+
+               flexbg_size = 1 << fs->super->s_log_groups_per_flex;
+               if (average_group % flexbg_size) {
+                       times = average_group / flexbg_size;
+                       average_group = times * flexbg_size;
+               }
+       }
+
+       return average_group;
+#else
+       return fs->group_desc_count;
+#endif
+}
+
 static int e2fsck_pass1_threads_start(struct e2fsck_thread_info **pinfo,
                                      e2fsck_t global_ctx)
 {
@@ -3258,7 +3316,7 @@ static int e2fsck_pass1_threads_start(struct e2fsck_thread_info **pinfo,
        int                              i;
        e2fsck_t                         thread_ctx;
        dgrp_t                           average_group;
-       int                              num_threads = global_ctx->fs_num_threads;
+       int num_threads = global_ctx->pfs_num_threads;
 #ifdef DEBUG_THREADS
        struct e2fsck_thread_debug       thread_debug =
                {PTHREAD_MUTEX_INITIALIZER, PTHREAD_COND_INITIALIZER, 0};
@@ -3362,11 +3420,12 @@ void e2fsck_pass1(e2fsck_t ctx)
        if (retval)
                return;
 #ifdef HAVE_PTHREAD
-       if (ctx->fs_num_threads > 1 ||
-           ctx->options & E2F_OPT_MULTITHREAD) {
+       if (ctx->pfs_num_threads > 1 || ctx->options & E2F_OPT_MULTITHREAD) {
                need_single = 0;
                e2fsck_pass1_multithread(ctx);
        }
+       /* No lock is needed at this time */
+       ctx->fs_need_locking = 0;
 #endif
        if (need_single)
                e2fsck_pass1_run(ctx);