Whamcloud - gitweb
e2fsck: merge dirs_to_hash when threads finish
[tools/e2fsprogs.git] / e2fsck / pass1.c
index 385b3fb..922de3d 100644 (file)
@@ -48,6 +48,9 @@
 #include <errno.h>
 #endif
 #include <assert.h>
+#ifdef HAVE_PTHREAD
+#include <pthread.h>
+#endif
 
 #include "e2fsck.h"
 #include <ext2fs/ext2_ext_attr.h>
@@ -85,7 +88,6 @@ static void alloc_imagic_map(e2fsck_t ctx);
 static void mark_inode_bad(e2fsck_t ctx, ino_t ino);
 static void add_casefolded_dir(e2fsck_t ctx, ino_t ino);
 static void handle_fs_bad_blocks(e2fsck_t ctx);
-static void process_inodes(e2fsck_t ctx, char *block_buf);
 static EXT2_QSORT_TYPE process_inode_cmp(const void *a, const void *b);
 static errcode_t scan_callback(ext2_filsys fs, ext2_inode_scan scan,
                                  dgrp_t group, void * priv_data);
@@ -120,15 +122,15 @@ struct process_inode_block {
 };
 
 struct scan_callback_struct {
-       e2fsck_t        ctx;
-       char            *block_buf;
+       e2fsck_t                         ctx;
+       char                            *block_buf;
+       struct process_inode_block      *inodes_to_process;
+       int                             *process_inode_count;
 };
 
-/*
- * For the inodes to process list.
- */
-static struct process_inode_block *inodes_to_process;
-static int process_inode_count;
+static void process_inodes(e2fsck_t ctx, char *block_buf,
+                          struct process_inode_block *inodes_to_process,
+                          int *process_inode_count);
 
 static __u64 ext2_max_sizes[EXT2_MAX_BLOCK_LOG_SIZE -
                            EXT2_MIN_BLOCK_LOG_SIZE + 1];
@@ -1164,10 +1166,9 @@ static int e2fsck_should_abort(e2fsck_t ctx)
        return 0;
 }
 
-void e2fsck_pass1_thread(e2fsck_t ctx)
+void e2fsck_pass1_run(e2fsck_t ctx)
 {
        int     i;
-       __u64   max_sizes;
        ext2_filsys fs = ctx->fs;
        ext2_ino_t      ino = 0;
        struct ext2_inode *inode = NULL;
@@ -1190,6 +1191,8 @@ void e2fsck_pass1_thread(e2fsck_t ctx)
        ext2_ino_t      ino_threshold = 0;
        dgrp_t          ra_group = 0;
        struct ea_quota ea_ibody_quota;
+       struct process_inode_block *inodes_to_process;
+       int             process_inode_count;
 
        init_resource_track(&rtrack, ctx->fs->io);
        clear_problem_context(&pctx);
@@ -1214,17 +1217,6 @@ void e2fsck_pass1_thread(e2fsck_t ctx)
        mtrace_print("Pass 1");
 #endif
 
-#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
-
        imagic_fs = ext2fs_has_feature_imagic_inodes(sb);
        extent_fs = ext2fs_has_feature_extents(sb);
        inlinedata_fs = ext2fs_has_feature_inline_data(sb);
@@ -1245,6 +1237,7 @@ void e2fsck_pass1_thread(e2fsck_t ctx)
        }
        pctx.errcode = e2fsck_allocate_inode_bitmap(fs,
                        _("directory inode map"),
+                       ctx->global_ctx ? EXT2FS_BMAP64_RBTREE :
                        EXT2FS_BMAP64_AUTODIR,
                        "inode_dir_map", &ctx->inode_dir_map);
        if (pctx.errcode) {
@@ -1362,6 +1355,8 @@ void e2fsck_pass1_thread(e2fsck_t ctx)
        ctx->stashed_inode = inode;
        scan_struct.ctx = ctx;
        scan_struct.block_buf = block_buf;
+       scan_struct.inodes_to_process = inodes_to_process;
+       scan_struct.process_inode_count = &process_inode_count;
        ext2fs_set_inode_callback(scan, scan_callback, &scan_struct);
        if (ctx->progress && ((ctx->progress)(ctx, 1, 0,
                                              ctx->fs->group_desc_count)))
@@ -1383,6 +1378,23 @@ void e2fsck_pass1_thread(e2fsck_t ctx)
        /* Set up ctx->lost_and_found if possible */
        (void) e2fsck_get_lost_and_found(ctx, 0);
 
+#ifdef HAVE_PTHREAD
+       if (ctx->global_ctx) {
+               if (ctx->options & E2F_OPT_DEBUG &&
+                   ctx->options & E2F_OPT_MULTITHREAD)
+                       fprintf(stderr, "thread %d jumping to group %d\n",
+                                       ctx->thread_info.et_thread_index,
+                                       ctx->thread_info.et_group_start);
+               pctx.errcode = ext2fs_inode_scan_goto_blockgroup(scan,
+                                       ctx->thread_info.et_group_start);
+               if (pctx.errcode) {
+                       fix_problem(ctx, PR_1_PASS_HEADER, &pctx);
+                       ctx->flags |= E2F_FLAG_ABORT;
+                       goto endit;
+               }
+       }
+#endif
+
        while (1) {
                if (ino % (fs->super->s_inodes_per_group * 4) == 1) {
                        if (e2fsck_mmp_update(fs))
@@ -1426,6 +1438,8 @@ void e2fsck_pass1_thread(e2fsck_t ctx)
                        ext2fs_mark_inode_bitmap2(ctx->inode_used_map, ino);
                        continue;
                }
+               if (pctx.errcode == EXT2_ET_SCAN_FINISHED)
+                       break;
                if (pctx.errcode &&
                    pctx.errcode != EXT2_ET_INODE_CSUM_INVALID &&
                    pctx.errcode != EXT2_ET_INODE_IS_GARBAGE) {
@@ -1435,6 +1449,10 @@ void e2fsck_pass1_thread(e2fsck_t ctx)
                }
                if (!ino)
                        break;
+#ifdef HAVE_PTHREAD
+               if (ctx->global_ctx)
+                       ctx->thread_info.et_inode_number++;
+#endif
                pctx.ino = ino;
                pctx.inode = inode;
                ctx->stashed_ino = ino;
@@ -1992,13 +2010,15 @@ void e2fsck_pass1_thread(e2fsck_t ctx)
                        goto endit;
 
                if (process_inode_count >= ctx->process_inode_size) {
-                       process_inodes(ctx, block_buf);
+                       process_inodes(ctx, block_buf, inodes_to_process,
+                                      &process_inode_count);
 
                        if (e2fsck_should_abort(ctx))
                                goto endit;
                }
        }
-       process_inodes(ctx, block_buf);
+       process_inodes(ctx, block_buf, inodes_to_process,
+                      &process_inode_count);
        ext2fs_close_inode_scan(scan);
        scan = NULL;
 
@@ -2121,6 +2141,28 @@ 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)
 {
@@ -2131,12 +2173,38 @@ static errcode_t e2fsck_pass1_copy_bitmap(ext2_filsys fs, ext2fs_generic_bitmap
                return ret;
 
        (*dest)->fs = fs;
-       ext2fs_free_generic_bmap(*src);
-       *src = NULL;
 
        return 0;
 }
 
+static void e2fsck_pass1_free_bitmap(ext2fs_generic_bitmap *bitmap)
+{
+       if (*bitmap) {
+               ext2fs_free_generic_bmap(*bitmap);
+               *bitmap = NULL;
+       }
+
+}
+
+static errcode_t e2fsck_pass1_merge_bitmap(ext2_filsys fs, ext2fs_generic_bitmap *src,
+                                         ext2fs_generic_bitmap *dest)
+{
+       errcode_t ret = 0;
+
+       if (*src) {
+               if (*dest == NULL) {
+                       *dest = *src;
+                       *src = NULL;
+               } else {
+                       ret = ext2fs_merge_bitmap(*src, *dest, NULL, NULL);
+                       if (ret)
+                               return ret;
+               }
+               (*dest)->fs = fs;
+       }
+
+       return 0;
+}
 
 static errcode_t e2fsck_pass1_copy_fs(ext2_filsys dest, e2fsck_t src_context,
                                      ext2_filsys src)
@@ -2144,6 +2212,9 @@ static errcode_t e2fsck_pass1_copy_fs(ext2_filsys dest, e2fsck_t src_context,
        errcode_t       retval;
 
        memcpy(dest, src, sizeof(struct struct_ext2_filsys));
+       dest->inode_map = NULL;
+       dest->block_map = NULL;
+       dest->badblocks = NULL;
        if (dest->dblist)
                dest->dblist->fs = dest;
        if (src->block_map) {
@@ -2160,7 +2231,8 @@ static errcode_t e2fsck_pass1_copy_fs(ext2_filsys dest, e2fsck_t src_context,
        }
 
        if (src->badblocks) {
-               retval = ext2fs_badblocks_copy(src->badblocks, &dest->badblocks);
+               retval = ext2fs_badblocks_copy(src->badblocks,
+                                              &dest->badblocks);
                if (retval)
                        return retval;
        }
@@ -2203,51 +2275,93 @@ static int e2fsck_pass1_merge_fs(ext2_filsys dest, ext2_filsys src)
        errcode_t retval = 0;
        io_channel dest_io;
        io_channel dest_image_io;
+       ext2fs_inode_bitmap inode_map;
+       ext2fs_block_bitmap block_map;
+       ext2_badblocks_list badblocks;
+       ext2_dblist dblist;
+       int flags;
 
        dest_io = dest->io;
        dest_image_io = dest->image_io;
+       inode_map = dest->inode_map;
+       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;
        dest->image_io = dest_image_io;
        dest->icache = icache;
+       dest->inode_map = inode_map;
+       dest->block_map = block_map;
+       dest->badblocks = badblocks;
+       dest->dblist = dblist;
        if (dest->dblist)
                dest->dblist->fs = dest;
-       if (src->inode_map) {
-               retval = e2fsck_pass1_copy_bitmap(dest, &src->inode_map,
-                                                 &dest->inode_map);
-               if (retval)
-                       return retval;
-       }
-       if (src->block_map) {
-               retval = e2fsck_pass1_copy_bitmap(dest, &src->block_map,
-                                                 &dest->block_map);
-               if (retval)
-                       return retval;
-       }
+       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);
                src->icache = NULL;
        }
 
-       if (src->badblocks) {
-               retval = ext2fs_badblocks_copy(src->badblocks, &dest->badblocks);
+       retval = e2fsck_pass1_merge_bitmap(dest, &src->inode_map,
+                                          &dest->inode_map);
+       if (retval)
+               goto out;
 
-               ext2fs_badblocks_list_free(src->badblocks);
-               src->badblocks = NULL;
+       retval = e2fsck_pass1_merge_bitmap(dest, &src->block_map,
+                                         &dest->block_map);
+       if (retval)
+               goto out;
+
+       if (src->dblist) {
+               if (dest->dblist) {
+                       retval = ext2fs_merge_dblist(src->dblist,
+                                                    dest->dblist);
+                       if (retval)
+                               goto out;
+               } else {
+                       dest->dblist = src->dblist;
+                       dest->dblist->fs = dest;
+                       src->dblist = NULL;
+               }
        }
 
+       if (src->badblocks) {
+               if (dest->badblocks == NULL)
+                       retval = ext2fs_badblocks_copy(src->badblocks,
+                                                      &dest->badblocks);
+               else
+                       retval = ext2fs_badblocks_merge(src->badblocks,
+                                                       dest->badblocks);
+       }
+out:
        io_channel_close(src->io);
+       if (src->inode_map)
+               ext2fs_free_generic_bmap(src->inode_map);
+       if (src->block_map)
+               ext2fs_free_generic_bmap(src->block_map);
+       if (src->badblocks)
+               ext2fs_badblocks_list_free(src->badblocks);
+       if (src->dblist)
+               ext2fs_free_dblist(src->dblist);
+
        return retval;
 }
 
-static errcode_t e2fsck_pass1_thread_prepare(e2fsck_t global_ctx, e2fsck_t *thread_ctx)
+static errcode_t e2fsck_pass1_thread_prepare(e2fsck_t global_ctx, e2fsck_t *thread_ctx,
+                                            int thread_index, int num_threads)
 {
-       errcode_t       retval;
-       e2fsck_t        thread_context;
-       ext2_filsys     thread_fs;
-       ext2_filsys     global_fs = global_ctx->fs;
+       errcode_t               retval;
+       e2fsck_t                thread_context;
+       ext2_filsys             thread_fs;
+       ext2_filsys             global_fs = global_ctx->fs;
+       struct e2fsck_thread    *tinfo;
+       dgrp_t                  average_group;
 
        assert(global_ctx->inode_used_map == NULL);
        assert(global_ctx->inode_dir_map == NULL);
@@ -2284,6 +2398,29 @@ static errcode_t e2fsck_pass1_thread_prepare(e2fsck_t global_ctx, e2fsck_t *thre
        }
        thread_fs->priv_data = thread_context;
 
+       thread_context->thread_info.et_thread_index = thread_index;
+       set_up_logging(thread_context);
+
+       /*
+        * Distribute work to multiple threads:
+        * Each thread work on fs->group_desc_count / nthread groups.
+        */
+       tinfo = &thread_context->thread_info;
+       average_group = thread_fs->group_desc_count / num_threads;
+       if (average_group == 0)
+               average_group = 1;
+       tinfo->et_group_start = average_group * thread_index;
+       if (thread_index == num_threads - 1)
+               tinfo->et_group_end = thread_fs->group_desc_count;
+       else
+               tinfo->et_group_end = average_group * (thread_index + 1);
+       tinfo->et_group_next = tinfo->et_group_start;
+       tinfo->et_inode_number = 0;
+       tinfo->et_log_buf[0] = '\0';
+       tinfo->et_log_length = 0;
+       if (thread_context->options & E2F_OPT_MULTITHREAD)
+               log_out(thread_context, _("Scan group range [%d, %d)\n"),
+                       tinfo->et_group_start, tinfo->et_group_end);
        thread_context->fs = thread_fs;
        *thread_ctx = thread_context;
        return 0;
@@ -2294,14 +2431,137 @@ out_context:
        return retval;
 }
 
+static void e2fsck_pass1_merge_dir_info(e2fsck_t global_ctx, e2fsck_t thread_ctx)
+{
+       if (thread_ctx->dir_info == NULL)
+               return;
+
+       if (global_ctx->dir_info == NULL) {
+               global_ctx->dir_info = thread_ctx->dir_info;
+               thread_ctx->dir_info = NULL;
+               return;
+       }
+
+       e2fsck_merge_dir_info(global_ctx, thread_ctx->dir_info,
+                             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)
+{
+       if (*src_icount) {
+               if (*dest_icount == NULL) {
+                       *dest_icount = *src_icount;
+                       *src_icount = NULL;
+               } else {
+                       errcode_t ret;
+
+                       ret = ext2fs_icount_merge(*src_icount,
+                                                 *dest_icount);
+                       if (ret)
+                               return ret;
+               }
+       }
+
+       return 0;
+}
+
+static errcode_t e2fsck_pass1_merge_icounts(e2fsck_t global_ctx, e2fsck_t thread_ctx)
+{
+       errcode_t ret;
+
+       ret = e2fsck_pass1_merge_icount(&global_ctx->inode_count,
+                                       &thread_ctx->inode_count);
+       if (ret)
+               return ret;
+       ret = e2fsck_pass1_merge_icount(&global_ctx->inode_link_info,
+                                       &thread_ctx->inode_link_info);
+
+       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 int e2fsck_pass1_thread_join_one(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;
+       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 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;
+
 #ifdef HAVE_SETJMP_H
-       jmp_buf         old_jmp;
+       jmp_buf          old_jmp;
 
        memcpy(old_jmp, global_ctx->abort_loc, sizeof(jmp_buf));
 #endif
@@ -2309,6 +2569,44 @@ static int e2fsck_pass1_thread_join_one(e2fsck_t global_ctx, e2fsck_t thread_ctx
 #ifdef HAVE_SETJMP_H
        memcpy(global_ctx->abort_loc, old_jmp, sizeof(jmp_buf));
 #endif
+
+       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_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->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->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;
+
        /* Keep the global singal flags*/
        global_ctx->flags |= (flags & E2F_FLAG_SIGNAL_MASK) |
                             (global_ctx->flags & E2F_FLAG_SIGNAL_MASK);
@@ -2320,84 +2618,80 @@ static int e2fsck_pass1_thread_join_one(e2fsck_t global_ctx, e2fsck_t thread_ctx
        }
        global_fs->priv_data = global_ctx;
        global_ctx->fs = global_fs;
-
-       if (thread_ctx->inode_used_map) {
-               retval = e2fsck_pass1_copy_bitmap(global_fs,
-                                       &thread_ctx->inode_used_map,
-                                       &global_ctx->inode_used_map);
-               if (retval)
-                       return retval;
+       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,
+                       _("while merging icounts\n"));
+               return retval;
        }
-       if (thread_ctx->inode_bad_map) {
-               retval = e2fsck_pass1_copy_bitmap(global_fs,
-                                       &thread_ctx->inode_bad_map,
-                                       &global_ctx->inode_bad_map);
-               if (retval)
-                       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;
        }
-       if (thread_ctx->inode_dir_map) {
-               retval = e2fsck_pass1_copy_bitmap(global_fs,
+
+       retval = e2fsck_pass1_merge_bitmap(global_fs,
+                               &thread_ctx->inode_used_map,
+                               &global_ctx->inode_used_map);
+       if (retval)
+               return retval;
+
+       retval = e2fsck_pass1_merge_bitmap(global_fs,
+                               &thread_ctx->inode_bad_map,
+                               &global_ctx->inode_bad_map);
+       if (retval)
+               return retval;
+       retval = e2fsck_pass1_merge_bitmap(global_fs,
                                        &thread_ctx->inode_dir_map,
                                        &global_ctx->inode_dir_map);
-               if (retval)
-                       return retval;
-       }
-       if (thread_ctx->inode_bb_map) {
-               retval = e2fsck_pass1_copy_bitmap(global_fs,
-                                       &thread_ctx->inode_bb_map,
-                                       &global_ctx->inode_bb_map);
-               if (retval)
-                       return retval;
-       }
-       if (thread_ctx->inode_imagic_map) {
-               retval = e2fsck_pass1_copy_bitmap(global_fs,
-                                       &thread_ctx->inode_imagic_map,
-                                       &global_ctx->inode_imagic_map);
-               if (retval)
-                       return retval;
-       }
-       if (thread_ctx->inode_reg_map) {
-               retval = e2fsck_pass1_copy_bitmap(global_fs,
-                                       &thread_ctx->inode_reg_map,
-                                       &global_ctx->inode_reg_map);
-               if (retval)
-                       return retval;
-       }
-       if (thread_ctx->inodes_to_rebuild) {
-               retval = e2fsck_pass1_copy_bitmap(global_fs,
-                                       &thread_ctx->inodes_to_rebuild,
-                                       &global_ctx->inodes_to_rebuild);
-               if (retval)
-                       return retval;
-       }
-       if (thread_ctx->block_found_map) {
-               retval = e2fsck_pass1_copy_bitmap(global_fs,
-                                       &thread_ctx->block_found_map,
-                                       &global_ctx->block_found_map);
-               if (retval)
-                       return retval;
-       }
-       if (thread_ctx->block_dup_map) {
-               retval = e2fsck_pass1_copy_bitmap(global_fs,
-                                       &thread_ctx->block_dup_map,
-                                       &global_ctx->block_dup_map);
-               if (retval)
-                       return retval;
-       }
-       if (thread_ctx->block_ea_map) {
-               retval = e2fsck_pass1_copy_bitmap(global_fs,
-                                       &thread_ctx->block_ea_map,
-                                       &global_ctx->block_ea_map);
-               if (retval)
-                       return retval;
-       }
-       if (thread_ctx->block_metadata_map) {
-               retval = e2fsck_pass1_copy_bitmap(global_fs,
-                                       &thread_ctx->block_metadata_map,
-                                       &global_ctx->block_metadata_map);
-               if (retval)
-                       return retval;
-       }
+       if (retval)
+               return retval;
+       retval = e2fsck_pass1_merge_bitmap(global_fs,
+                               &thread_ctx->inode_bb_map,
+                               &global_ctx->inode_bb_map);
+       if (retval)
+               return retval;
+       retval = e2fsck_pass1_merge_bitmap(global_fs,
+                               &thread_ctx->inode_imagic_map,
+                               &global_ctx->inode_imagic_map);
+       if (retval)
+               return retval;
+       retval = e2fsck_pass1_merge_bitmap(global_fs,
+                               &thread_ctx->inode_reg_map,
+                               &global_ctx->inode_reg_map);
+       if (retval)
+               return retval;
+       retval = e2fsck_pass1_merge_bitmap(global_fs,
+                               &thread_ctx->inodes_to_rebuild,
+                               &global_ctx->inodes_to_rebuild);
+       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;
 
        return 0;
 }
@@ -2408,23 +2702,83 @@ static int e2fsck_pass1_thread_join(e2fsck_t global_ctx, e2fsck_t thread_ctx)
 
        retval = e2fsck_pass1_thread_join_one(global_ctx, thread_ctx);
        ext2fs_free_mem(&thread_ctx->fs);
+       if (thread_ctx->logf)
+               fclose(thread_ctx->logf);
+       if (thread_ctx->problem_logf) {
+               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_dup_map);
+       e2fsck_pass1_free_bitmap(&thread_ctx->block_ea_map);
+       e2fsck_pass1_free_bitmap(&thread_ctx->block_metadata_map);
+       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);
        ext2fs_free_mem(&thread_ctx);
 
        return retval;
 }
 
-void e2fsck_pass1_multithread(e2fsck_t ctx)
+static int e2fsck_pass1_threads_join(struct e2fsck_thread_info *infos,
+                                     int num_threads, e2fsck_t global_ctx)
 {
-       errcode_t       retval;
-       e2fsck_t        thread_ctx;
+       errcode_t                        rc;
+       errcode_t                        ret = 0;
+       int                              i;
+       struct e2fsck_thread_info       *pinfo;
 
-       retval = e2fsck_pass1_thread_prepare(ctx, &thread_ctx);
-       if (retval) {
-               com_err(ctx->program_name, 0,
-                       _("while preparing pass1 thread\n"));
-               ctx->flags |= E2F_FLAG_ABORT;
-               return;
+       for (i = 0; i < num_threads; i++) {
+               pinfo = &infos[i];
+
+               if (!pinfo->eti_started)
+                       continue;
+
+               rc = pthread_join(pinfo->eti_thread_id, NULL);
+               if (rc) {
+                       com_err(global_ctx->program_name, rc,
+                               _("while joining thread\n"));
+                       if (ret == 0)
+                               ret = rc;
+               }
+               rc = e2fsck_pass1_thread_join(global_ctx, infos[i].eti_thread_ctx);
+               if (rc) {
+                       com_err(global_ctx->program_name, rc,
+                               _("while joining pass1 thread\n"));
+                       if (ret == 0)
+                               ret = rc;
+               }
+       }
+       free(infos);
+
+       return ret;
+}
+
+static void *e2fsck_pass1_thread(void *arg)
+{
+       struct e2fsck_thread_info       *info = arg;
+       e2fsck_t                         thread_ctx = info->eti_thread_ctx;
+#ifdef DEBUG_THREADS
+       struct e2fsck_thread_debug      *thread_debug = info->eti_debug;
+#endif
+
+#ifdef DEBUG_THREADS
+       pthread_mutex_lock(&thread_debug->etd_mutex);
+       while (info->eti_thread_index > thread_debug->etd_finished_threads) {
+               pthread_cond_wait(&thread_debug->etd_cond,
+                                 &thread_debug->etd_mutex);
        }
+       pthread_mutex_unlock(&thread_debug->etd_mutex);
+#endif
 
 #ifdef HAVE_SETJMP_H
        /*
@@ -2435,25 +2789,176 @@ void e2fsck_pass1_multithread(e2fsck_t ctx)
         */
        if (setjmp(thread_ctx->abort_loc)) {
                thread_ctx->flags &= ~E2F_FLAG_SETJMP_OK;
-               e2fsck_pass1_thread_join(ctx, thread_ctx);
-               return;
+               goto out;
        }
        thread_ctx->flags |= E2F_FLAG_SETJMP_OK;
 #endif
 
-       e2fsck_pass1_thread(thread_ctx);
-       retval = e2fsck_pass1_thread_join(ctx, thread_ctx);
+       e2fsck_pass1_run(thread_ctx);
+
+out:
+       if (thread_ctx->options & E2F_OPT_MULTITHREAD)
+               log_out(thread_ctx,
+                       _("Scanned group range [%lu, %lu), inodes %lu\n"),
+                       thread_ctx->thread_info.et_group_start,
+                       thread_ctx->thread_info.et_group_end,
+                       thread_ctx->thread_info.et_inode_number);
+
+#ifdef DEBUG_THREADS
+       pthread_mutex_lock(&thread_debug->etd_mutex);
+       thread_debug->etd_finished_threads++;
+       pthread_cond_broadcast(&thread_debug->etd_cond);
+       pthread_mutex_unlock(&thread_debug->etd_mutex);
+#endif
+
+       return NULL;
+}
+
+static int e2fsck_pass1_threads_start(struct e2fsck_thread_info **pinfo,
+                                     int num_threads, e2fsck_t global_ctx)
+{
+       struct e2fsck_thread_info       *infos;
+       pthread_attr_t                   attr;
+       errcode_t                        retval;
+       errcode_t                        ret;
+       struct e2fsck_thread_info       *tmp_pinfo;
+       int                              i;
+       e2fsck_t                         thread_ctx;
+#ifdef DEBUG_THREADS
+       struct e2fsck_thread_debug       thread_debug =
+               {PTHREAD_MUTEX_INITIALIZER, PTHREAD_COND_INITIALIZER, 0};
+
+       thread_debug.etd_finished_threads = 0;
+#endif
+
+       retval = pthread_attr_init(&attr);
        if (retval) {
-               com_err(ctx->program_name, 0,
-                       _("while joining pass1 thread\n"));
-               ctx->flags |= E2F_FLAG_ABORT;
-               return;
+               com_err(global_ctx->program_name, retval,
+                       _("while setting pthread attribute\n"));
+               return retval;
+       }
+
+       infos = calloc(num_threads, sizeof(struct e2fsck_thread_info));
+       if (infos == NULL) {
+               retval = -ENOMEM;
+               com_err(global_ctx->program_name, retval,
+                       _("while allocating memory for threads\n"));
+               pthread_attr_destroy(&attr);
+               return retval;
+       }
+
+       for (i = 0; i < num_threads; i++) {
+               tmp_pinfo = &infos[i];
+               tmp_pinfo->eti_thread_index = i;
+#ifdef DEBUG_THREADS
+               tmp_pinfo->eti_debug = &thread_debug;
+#endif
+               retval = e2fsck_pass1_thread_prepare(global_ctx, &thread_ctx,
+                                                    i, num_threads);
+               if (retval) {
+                       com_err(global_ctx->program_name, retval,
+                               _("while preparing pass1 thread\n"));
+                       break;
+               }
+               tmp_pinfo->eti_thread_ctx = thread_ctx;
+
+               retval = pthread_create(&tmp_pinfo->eti_thread_id, &attr,
+                                       &e2fsck_pass1_thread, tmp_pinfo);
+               if (retval) {
+                       com_err(global_ctx->program_name, retval,
+                               _("while creating thread\n"));
+                       e2fsck_pass1_thread_join(global_ctx, thread_ctx);
+                       break;
+               }
+
+               tmp_pinfo->eti_started = 1;
+       }
+
+       /* destroy the thread attribute object, since it is no longer needed */
+       ret = pthread_attr_destroy(&attr);
+       if (ret) {
+               com_err(global_ctx->program_name, ret,
+                       _("while destroying thread attribute\n"));
+               if (retval == 0)
+                       retval = ret;
+       }
+
+       if (retval) {
+               e2fsck_pass1_threads_join(infos, num_threads, global_ctx);
+               return retval;
+       }
+       *pinfo = infos;
+       return 0;
+}
+
+static void e2fsck_pass1_multithread(e2fsck_t global_ctx)
+{
+       struct e2fsck_thread_info       *infos = NULL;
+       int                              num_threads = 1;
+       errcode_t                        retval;
+
+       retval = e2fsck_pass1_threads_start(&infos, num_threads, global_ctx);
+       if (retval) {
+               com_err(global_ctx->program_name, retval,
+                       _("while starting pass1 threads\n"));
+               goto out_abort;
+       }
+
+       retval = e2fsck_pass1_threads_join(infos, num_threads, global_ctx);
+       if (retval) {
+               com_err(global_ctx->program_name, retval,
+                       _("while joining pass1 threads\n"));
+               goto out_abort;
        }
+       return;
+out_abort:
+       global_ctx->flags |= E2F_FLAG_ABORT;
+       return;
+}
+#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)
 {
-       e2fsck_pass1_multithread(ctx);
+
+       init_ext2_max_sizes();
+#ifdef HAVE_PTHREAD
+       if (multiple_threads_supported(ctx)) {
+               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);
 }
 
 #undef FINISH_INODE_LOOP
@@ -2468,24 +2973,42 @@ static errcode_t scan_callback(ext2_filsys fs,
 {
        struct scan_callback_struct *scan_struct;
        e2fsck_t ctx;
+       struct e2fsck_thread *tinfo;
 
        scan_struct = (struct scan_callback_struct *) priv_data;
        ctx = scan_struct->ctx;
 
-       process_inodes((e2fsck_t) fs->priv_data, scan_struct->block_buf);
+       process_inodes((e2fsck_t) fs->priv_data, scan_struct->block_buf,
+                      scan_struct->inodes_to_process,
+                      scan_struct->process_inode_count);
 
        if (ctx->progress)
                if ((ctx->progress)(ctx, 1, group+1,
                                    ctx->fs->group_desc_count))
                        return EXT2_ET_CANCEL_REQUESTED;
 
+#ifdef HAVE_PTHREAD
+       if (ctx->global_ctx) {
+               tinfo = &ctx->thread_info;
+               tinfo->et_group_next++;
+               if (ctx->options & E2F_OPT_DEBUG &&
+                   ctx->options & E2F_OPT_MULTITHREAD)
+                       log_out(ctx, _("group %d finished\n"),
+                               tinfo->et_group_next);
+               if (tinfo->et_group_next >= tinfo->et_group_end)
+                       return EXT2_ET_SCAN_FINISHED;
+       }
+#endif
+
        return 0;
 }
 
 /*
  * Process the inodes in the "inodes to process" list.
  */
-static void process_inodes(e2fsck_t ctx, char *block_buf)
+static void process_inodes(e2fsck_t ctx, char *block_buf,
+                          struct process_inode_block *inodes_to_process,
+                          int *process_inode_count)
 {
        int                     i;
        struct ext2_inode       *old_stashed_inode;
@@ -2497,15 +3020,15 @@ static void process_inodes(e2fsck_t ctx, char *block_buf)
 #if 0
        printf("begin process_inodes: ");
 #endif
-       if (process_inode_count == 0)
+       if (*process_inode_count == 0)
                return;
        old_operation = ehandler_operation(0);
        old_stashed_inode = ctx->stashed_inode;
        old_stashed_ino = ctx->stashed_ino;
-       qsort(inodes_to_process, process_inode_count,
+       qsort(inodes_to_process, *process_inode_count,
                      sizeof(struct process_inode_block), process_inode_cmp);
        clear_problem_context(&pctx);
-       for (i=0; i < process_inode_count; i++) {
+       for (i=0; i < *process_inode_count; i++) {
                pctx.inode = ctx->stashed_inode =
                        (struct ext2_inode *) &inodes_to_process[i].inode;
                pctx.ino = ctx->stashed_ino = inodes_to_process[i].ino;
@@ -2523,7 +3046,7 @@ static void process_inodes(e2fsck_t ctx, char *block_buf)
        }
        ctx->stashed_inode = old_stashed_inode;
        ctx->stashed_ino = old_stashed_ino;
-       process_inode_count = 0;
+       *process_inode_count = 0;
 #if 0
        printf("end process inodes\n");
 #endif