]> git.ipfire.org Git - thirdparty/e2fsprogs.git/commitdiff
e2fsck: Discard free data and inode blocks.
authorLukas Czerner <lczerner@redhat.com>
Thu, 18 Nov 2010 03:38:38 +0000 (03:38 +0000)
committerTheodore Ts'o <tytso@mit.edu>
Tue, 23 Nov 2010 02:01:41 +0000 (21:01 -0500)
In Pass 5 when we are checking block and inode bitmaps we have great
opportunity to discard free space and unused inodes on the device,
because bitmaps has just been verified as valid. This commit takes
advantage of this opportunity and discards both, all free space and
unused inodes.

I have added new set of options, 'nodiscard' and 'discard'. When the
underlying devices does not support discard, or discard ends with an
error, or when any kind of error occurs on the filesystem, no further
discard attempt will be made and the e2fsck will behave as it would
with nodiscard option provided.

As an addition, when there is any not-yet-zeroed inode table and
discard zeroes data, then inode table is marked as zeroed.

Signed-off-by: Lukas Czerner <lczerner@redhat.com>
Signed-off-by: Theodore Ts'o <tytso@mit.edu>
e2fsck/e2fsck.8.in
e2fsck/e2fsck.h
e2fsck/pass5.c
e2fsck/unix.c
lib/ext2fs/test_io.c

index 3fb15e6d3af62ee7336f7ef692a4f3c065c1ba38..4c2dd985e5544b4014b24f0a1b8af51958f2fdce 100644 (file)
@@ -189,6 +189,20 @@ be 1 or 2.  The default extended attribute version format is 2.
 .BI fragcheck
 During pass 1, print a detailed report of any discontiguous blocks for
 files in the filesystem.
+.TP
+.BI discard
+Attempt to discard free blocks and unused inode blocks after the full
+filesystem check (discarding blocks is useful on solid state devices and sparse
+/ thin-provisioned storage). Note that discard is done in pass 5 AFTER the
+filesystem has been fully checked and only if it does not contain recognizable
+errors. However there might be cases where
+.B e2fsck
+does not fully recognise a problem and hence in this case this
+option may prevent you from further manual data recovery.
+.TP
+.BI nodiscard
+Do not attempt to discard free blocks and unused inode blocks. This option is
+exacly the opposite of discard option. This is set as default.
 .RE
 .TP
 .B \-f
index d4df5f35e8ba6c97989d780f023be6c69b55af3c..a3772469c9c74ec0cad8106ed892e871a62588e3 100644 (file)
@@ -155,6 +155,7 @@ struct resource_track {
 #define E2F_OPT_WRITECHECK     0x0200
 #define E2F_OPT_COMPRESS_DIRS  0x0400
 #define E2F_OPT_FRAGCHECK      0x0800
+#define E2F_OPT_DISCARD                0x1000
 
 /*
  * E2fsck flags
index cbc12f398229e7831301a13cb1751577db15e37f..b423d287f589518349274d9447cde19fecfdc41b 100644 (file)
  *
  */
 
+#include <stdint.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <sys/ioctl.h>
+#include <fcntl.h>
+#include <errno.h>
+
 #include "e2fsck.h"
 #include "problem.h"
 
+#define DIV_ROUND_UP(n,d) (((n) + (d) - 1) / (d))
+
 static void check_block_bitmaps(e2fsck_t ctx);
 static void check_inode_bitmaps(e2fsck_t ctx);
 static void check_inode_end(e2fsck_t ctx);
@@ -64,6 +73,26 @@ 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)
+{
+       ext2_filsys fs = ctx->fs;
+       int ret = 0;
+
+       /*
+        * If the filesystem has changed it means that there was an corruption
+        * which should be repaired, but in some cases just one e2fsck run is
+        * not enough to fix the problem, hence it is not safe to run discard
+        * in this case.
+        */
+       if (ext2fs_test_changed(ctx->fs))
+               ctx->options &= ~E2F_OPT_DISCARD;
+
+       if ((ctx->options & E2F_OPT_DISCARD) &&
+           (io_channel_discard(fs->io, start, count)))
+               ctx->options &= ~E2F_OPT_DISCARD;
+}
+
 #define NO_BLK ((blk64_t) -1)
 
 static void print_bitmap_problem(e2fsck_t ctx, int problem,
@@ -108,6 +137,7 @@ static void check_block_bitmaps(e2fsck_t ctx)
        int     group = 0;
        int     blocks = 0;
        blk64_t free_blocks = 0;
+       blk64_t first_free = ext2fs_blocks_count(fs->super);
        int     group_free = 0;
        int     actual, bitmap;
        struct problem_context  pctx;
@@ -120,6 +150,7 @@ 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,
@@ -281,10 +312,25 @@ redo_counts:
                ctx->flags |= E2F_FLAG_PROG_SUPPRESS;
                had_problem++;
 
+               /*
+                * If there a problem we should turn off the discard so we
+                * do not compromise the filesystem.
+                */
+               ctx->options &= ~E2F_OPT_DISCARD;
+
        do_counts:
                if (!bitmap && (!skip_group || csum_flag)) {
                        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));
+                       }
+                       first_free = ext2fs_blocks_count(fs->super);
                }
                blocks ++;
                if ((blocks == fs->super->s_blocks_per_group) ||
@@ -381,6 +427,7 @@ 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;
 
        clear_problem_context(&pctx);
        free_array = (int *) e2fsck_allocate_memory(ctx,
@@ -500,6 +547,11 @@ redo_counts:
                }
                ctx->flags |= E2F_FLAG_PROG_SUPPRESS;
                had_problem++;
+               /*
+                * If there a problem we should turn off the discard so we
+                * do not compromise the filesystem.
+                */
+               ctx->options &= ~E2F_OPT_DISCARD;
 
 do_counts:
                if (bitmap) {
@@ -509,11 +561,43 @@ do_counts:
                        group_free++;
                        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 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)) &&
+                           !(ext2fs_bg_flags_test(fs, group,
+                                                 EXT2_BG_INODE_ZEROED))) {
+                               ext2fs_bg_flags_set(fs, group,
+                                                   EXT2_BG_INODE_ZEROED);
+                               ext2fs_group_desc_csum_set(fs, group);
+                       }
+
                        group ++;
                        inodes = 0;
                        skip_group = 0;
index 7eb269cc49cc52cc81e035b74cd1240c214d4139..4cf55a93e51e1f7719b4f925cefc1a69f6a9c417 100644 (file)
@@ -594,6 +594,12 @@ static void parse_extended_opts(e2fsck_t ctx, const char *opts)
                } else if (strcmp(token, "fragcheck") == 0) {
                        ctx->options |= E2F_OPT_FRAGCHECK;
                        continue;
+               } else if (strcmp(token, "discard") == 0) {
+                       ctx->options |= E2F_OPT_DISCARD;
+                       continue;
+               } else if (strcmp(token, "nodiscard") == 0) {
+                       ctx->options &= ~E2F_OPT_DISCARD;
+                       continue;
                } else {
                        fprintf(stderr, _("Unknown extended option: %s\n"),
                                token);
@@ -609,6 +615,8 @@ static void parse_extended_opts(e2fsck_t ctx, const char *opts)
                       "Valid extended options are:\n"), stderr);
                fputs(("\tea_ver=<ea_version (1 or 2)>\n"), stderr);
                fputs(("\tfragcheck\n"), stderr);
+               fputs(("\tdiscard\n"), stderr);
+               fputs(("\tnodiscard\n"), stderr);
                fputc('\n', stderr);
                exit(1);
        }
@@ -667,6 +675,7 @@ static errcode_t PRS(int argc, char *argv[], e2fsck_t *ret_ctx)
                ctx->program_name = *argv;
        else
                ctx->program_name = "e2fsck";
+
        while ((c = getopt (argc, argv, "panyrcC:B:dE:fvtFVM:b:I:j:P:l:L:N:SsDk")) != EOF)
                switch (c) {
                case 'C':
@@ -943,7 +952,6 @@ static errcode_t try_open_fs(e2fsck_t ctx, int flags, io_manager io_ptr,
        return retval;
 }
 
-
 static const char *my_ver_string = E2FSPROGS_VERSION;
 static const char *my_ver_date = E2FSPROGS_DATE;
 
index 242d4421676a91fadefd03f4b091617976fcba0f..7da1ee6313ab6a86d27a7418724874d1c9fd41c7 100644 (file)
@@ -509,7 +509,8 @@ static errcode_t test_discard(io_channel channel, unsigned long long block,
        data = (struct test_private_data *) channel->private_data;
        EXT2_CHECK_MAGIC(data, EXT2_ET_MAGIC_TEST_IO_CHANNEL);
 
-       retval = io_channel_discard(channel, block, count);
+       if (data->real)
+               retval = io_channel_discard(data->real, block, count);
        if (data->flags & TEST_FLAG_DISCARD)
                fprintf(data->outfile,
                        "Test_io: discard(%llu, %llu) returned %s\n",