Whamcloud - gitweb
tests: use make rules to run tests in parallel
[tools/e2fsprogs.git] / e2fsck / pass5.c
index f9d746c..8312fe0 100644 (file)
@@ -10,6 +10,7 @@
  *
  */
 
+#include "config.h"
 #include <stdint.h>
 #include <sys/types.h>
 #include <sys/stat.h>
@@ -73,8 +74,8 @@ void e2fsck_pass5(e2fsck_t ctx)
        print_resource_track(ctx, _("Pass 5"), &rtrack, ctx->fs->io);
 }
 
-static void e2fsck_discard_blocks(e2fsck_t ctx, io_manager manager,
-                                 blk64_t start, blk64_t count)
+static void e2fsck_discard_blocks(e2fsck_t ctx, blk64_t start,
+                                 blk64_t count)
 {
        ext2_filsys fs = ctx->fs;
 
@@ -84,7 +85,7 @@ static void e2fsck_discard_blocks(e2fsck_t ctx, io_manager manager,
         * not enough to fix the problem, hence it is not safe to run discard
         * in this case.
         */
-       if (ext2fs_test_changed(ctx->fs))
+       if (ext2fs_test_changed(fs))
                ctx->options &= ~E2F_OPT_DISCARD;
 
        if ((ctx->options & E2F_OPT_DISCARD) &&
@@ -92,6 +93,62 @@ static void e2fsck_discard_blocks(e2fsck_t ctx, io_manager manager,
                ctx->options &= ~E2F_OPT_DISCARD;
 }
 
+/*
+ * This will try to discard number 'count' inodes starting at
+ * inode number 'start' within the 'group'. Note that 'start'
+ * is 1-based, it means that we need to adjust it by -1 in this
+ * function to compute right offset in the particular inode table.
+ */
+static void e2fsck_discard_inodes(e2fsck_t ctx, dgrp_t group,
+                                 ext2_ino_t start, int count)
+{
+       ext2_filsys fs = ctx->fs;
+       blk64_t blk, num;
+
+       /*
+        * Sanity check for 'start'
+        */
+       if ((start < 1) || (start > EXT2_INODES_PER_GROUP(fs->super))) {
+               printf("PROGRAMMING ERROR: Got start %d outside of group %d!"
+                      " Disabling discard\n",
+                       start, group);
+               ctx->options &= ~E2F_OPT_DISCARD;
+       }
+
+       /*
+        * Do not attempt to discard if E2F_OPT_DISCARD is not set. And also
+        * skip the discard on this group if discard does not zero data.
+        * The reason is that if the inode table is not zeroed discard would
+        * no help us since we need to zero it anyway, or if the inode table
+        * is zeroed then the read after discard would not be deterministic
+        * anyway and we would not be able to assume that this inode table
+        * was zeroed anymore so we would have to zero it again, which does
+        * not really make sense.
+        */
+       if (!(ctx->options & E2F_OPT_DISCARD) ||
+           !io_channel_discard_zeroes_data(fs->io))
+               return;
+
+       /*
+        * Start is inode number within the group which starts
+        * counting from 1, so we need to adjust it.
+        */
+       start -= 1;
+
+       /*
+        * We can discard only blocks containing only unused
+        * inodes in the table.
+        */
+       blk = DIV_ROUND_UP(start,
+               EXT2_INODES_PER_BLOCK(fs->super));
+       count -= (blk * EXT2_INODES_PER_BLOCK(fs->super) - start);
+       blk += ext2fs_inode_table_loc(fs, group);
+       num = count / EXT2_INODES_PER_BLOCK(fs->super);
+
+       if (num > 0)
+               e2fsck_discard_blocks(ctx, blk, num);
+}
+
 #define NO_BLK ((blk64_t) -1)
 
 static void print_bitmap_problem(e2fsck_t ctx, int problem,
@@ -138,12 +195,12 @@ static void check_block_bitmaps(e2fsck_t ctx)
 {
        ext2_filsys fs = ctx->fs;
        blk64_t i;
-       int     *free_array;
+       unsigned int    *free_array;
        int     group = 0;
-       int     blocks = 0;
+       unsigned int    blocks = 0;
        blk64_t free_blocks = 0;
        blk64_t first_free = ext2fs_blocks_count(fs->super);
-       int     group_free = 0;
+       unsigned int    group_free = 0;
        int     actual, bitmap;
        struct problem_context  pctx;
        int     problem, save_problem, fixit, had_problem;
@@ -155,11 +212,10 @@ static void check_block_bitmaps(e2fsck_t ctx)
        int     cmp_block = 0;
        int     redo_flag = 0;
        blk64_t super_blk, old_desc_blk, new_desc_blk;
-       io_manager      manager = ctx->fs->io->manager;
 
        clear_problem_context(&pctx);
-       free_array = (int *) e2fsck_allocate_memory(ctx,
-           fs->group_desc_count * sizeof(int), "free block count array");
+       free_array = (unsigned int *) e2fsck_allocate_memory(ctx,
+           fs->group_desc_count * sizeof(unsigned int), "free block count array");
 
        if ((B2C(fs->super->s_first_data_block) <
             ext2fs_get_block_bitmap_start2(ctx->block_found_map)) ||
@@ -225,9 +281,8 @@ redo_counts:
                                count = 0;
                                cmp_block = fs->super->s_clusters_per_group;
                                if (group == (int)fs->group_desc_count - 1)
-                                       cmp_block =
-                                               EXT2FS_NUM_B2C(fs,
-               ext2fs_blocks_count(fs->super) % fs->super->s_blocks_per_group);
+                                       cmp_block = EXT2FS_NUM_B2C(fs,
+                                                   ext2fs_group_blocks_count(fs, group));
                        }
 
                        bitmap = 0;
@@ -278,7 +333,7 @@ redo_counts:
                else
                        bitmap = ext2fs_fast_test_block_bitmap2(fs->block_map, i);
 
-               if (actual == bitmap)
+               if (!actual == !bitmap)
                        goto do_counts;
 
                if (!actual && bitmap) {
@@ -325,23 +380,29 @@ redo_counts:
                ctx->options &= ~E2F_OPT_DISCARD;
 
        do_counts:
-               if (!bitmap && (!skip_group || csum_flag)) {
+               if (!bitmap) {
                        group_free++;
                        free_blocks++;
                        if (first_free > i)
                                first_free = i;
-               } else {
-                       if ((i > first_free) &&
-                          (ctx->options & E2F_OPT_DISCARD)) {
-                               e2fsck_discard_blocks(ctx, manager, first_free,
-                                                     (i - first_free));
-                       }
+               } else if (i > first_free) {
+                       e2fsck_discard_blocks(ctx, first_free,
+                                             (i - first_free));
                        first_free = ext2fs_blocks_count(fs->super);
                }
                blocks ++;
                if ((blocks == fs->super->s_clusters_per_group) ||
                    (EXT2FS_B2C(fs, i) ==
                     EXT2FS_B2C(fs, ext2fs_blocks_count(fs->super)-1))) {
+                       /*
+                        * If the last block of this group is free, then we can
+                        * discard it as well.
+                        */
+                       if (!bitmap && i >= first_free)
+                               e2fsck_discard_blocks(ctx, first_free,
+                                                     (i - first_free) + 1);
+                       first_free = ext2fs_blocks_count(fs->super);
+
                        free_array[group] = group_free;
                        group ++;
                        blocks = 0;
@@ -410,8 +471,7 @@ redo_counts:
                if (fix_problem(ctx, PR_5_FREE_BLOCK_COUNT, &pctx)) {
                        ext2fs_free_blocks_count_set(fs->super, free_blocks);
                        ext2fs_mark_super_dirty(fs);
-               } else
-                       ext2fs_unmark_valid(fs);
+               }
        }
 errout:
        ext2fs_free_mem(&free_array);
@@ -426,8 +486,8 @@ static void check_inode_bitmaps(e2fsck_t ctx)
        int             dirs_count = 0;
        int             group = 0;
        unsigned int    inodes = 0;
-       int             *free_array;
-       int             *dir_array;
+       ext2_ino_t      *free_array;
+       ext2_ino_t      *dir_array;
        int             actual, bitmap;
        errcode_t       retval;
        struct problem_context  pctx;
@@ -435,14 +495,14 @@ static void check_inode_bitmaps(e2fsck_t ctx)
        int             csum_flag;
        int             skip_group = 0;
        int             redo_flag = 0;
-       io_manager      manager = ctx->fs->io->manager;
+       ext2_ino_t              first_free = fs->super->s_inodes_per_group + 1;
 
        clear_problem_context(&pctx);
-       free_array = (int *) e2fsck_allocate_memory(ctx,
-           fs->group_desc_count * sizeof(int), "free inode count array");
+       free_array = (ext2_ino_t *) e2fsck_allocate_memory(ctx,
+           fs->group_desc_count * sizeof(ext2_ino_t), "free inode count array");
 
-       dir_array = (int *) e2fsck_allocate_memory(ctx,
-          fs->group_desc_count * sizeof(int), "directory count array");
+       dir_array = (ext2_ino_t *) e2fsck_allocate_memory(ctx,
+          fs->group_desc_count * sizeof(ext2_ino_t), "directory count array");
 
        if ((1 < ext2fs_get_inode_bitmap_start2(ctx->inode_used_map)) ||
            (fs->super->s_inodes_count >
@@ -498,6 +558,7 @@ redo_counts:
                                 * are 0, count the free inode,
                                 * skip the current block group.
                                 */
+                               first_free = 1;
                                inodes = fs->super->s_inodes_per_group - 1;
                                group_free = inodes;
                                free_inodes += inodes;
@@ -512,7 +573,7 @@ redo_counts:
                        bitmap = actual;
                else if (!skip_group)
                        bitmap = ext2fs_fast_test_inode_bitmap2(fs->inode_map, i);
-               if (actual == bitmap)
+               if (!actual == !bitmap)
                        goto do_counts;
 
                if (!actual && bitmap) {
@@ -562,50 +623,46 @@ redo_counts:
                ctx->options &= ~E2F_OPT_DISCARD;
 
 do_counts:
+               inodes++;
                if (bitmap) {
                        if (ext2fs_test_inode_bitmap2(ctx->inode_dir_map, i))
                                dirs_count++;
-               } else if (!skip_group || csum_flag) {
+                       if (inodes > first_free) {
+                               e2fsck_discard_inodes(ctx, group, first_free,
+                                                     inodes - first_free);
+                               first_free = fs->super->s_inodes_per_group + 1;
+                       }
+               } else {
                        group_free++;
                        free_inodes++;
+                       if (first_free > inodes)
+                               first_free = inodes;
                }
 
-               inodes++;
                if ((inodes == fs->super->s_inodes_per_group) ||
                    (i == fs->super->s_inodes_count)) {
-
-                       free_array[group] = group_free;
-                       dir_array[group] = dirs_count;
-
-                       /* Discard inode table */
-                       if (ctx->options & E2F_OPT_DISCARD) {
-                               blk64_t used_blks, blk, num;
-
-                               used_blks = DIV_ROUND_UP(
-                                       (EXT2_INODES_PER_GROUP(fs->super) -
-                                       group_free),
-                                       EXT2_INODES_PER_BLOCK(fs->super));
-
-                               blk = ext2fs_inode_table_loc(fs, group) +
-                                     used_blks;
-                               num = fs->inode_blocks_per_group -
-                                     used_blks;
-                               e2fsck_discard_blocks(ctx, manager, blk, num);
-                       }
-
+                       /*
+                        * If the last inode is free, we can discard it as well.
+                        */
+                       if (!bitmap && inodes >= first_free)
+                               e2fsck_discard_inodes(ctx, group, first_free,
+                                                     inodes - first_free + 1);
                        /*
                         * If discard zeroes data and the group inode table
                         * was not zeroed yet, set itable as zeroed
                         */
                        if ((ctx->options & E2F_OPT_DISCARD) &&
-                           (io_channel_discard_zeroes_data(fs->io)) &&
+                           io_channel_discard_zeroes_data(fs->io) &&
                            !(ext2fs_bg_flags_test(fs, group,
-                                                 EXT2_BG_INODE_ZEROED))) {
+                                                  EXT2_BG_INODE_ZEROED))) {
                                ext2fs_bg_flags_set(fs, group,
                                                    EXT2_BG_INODE_ZEROED);
                                ext2fs_group_desc_csum_set(fs, group);
                        }
 
+                       first_free = fs->super->s_inodes_per_group + 1;
+                       free_array[group] = group_free;
+                       dir_array[group] = dirs_count;
                        group ++;
                        inodes = 0;
                        skip_group = 0;
@@ -688,8 +745,7 @@ do_counts:
                if (fix_problem(ctx, PR_5_FREE_INODE_COUNT, &pctx)) {
                        fs->super->s_free_inodes_count = free_inodes;
                        ext2fs_mark_super_dirty(fs);
-               } else
-                       ext2fs_unmark_valid(fs);
+               }
        }
 errout:
        ext2fs_free_mem(&free_array);