Merge branch 'master' into next

Conflicts:
	e2fsck/e2fsck.h
	e2fsck/unix.c
This commit is contained in:
Theodore Ts'o 2010-12-16 23:20:11 -05:00
commit 25623feab4
10 changed files with 206 additions and 70 deletions

View File

@ -193,6 +193,20 @@ or repairs.
.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

View File

@ -156,6 +156,7 @@ struct resource_track {
#define E2F_OPT_COMPRESS_DIRS 0x0400
#define E2F_OPT_FRAGCHECK 0x0800
#define E2F_OPT_JOURNAL_ONLY 0x1000 /* only replay the journal */
#define E2F_OPT_DISCARD 0x2000
/*
* E2fsck flags

View File

@ -10,9 +10,18 @@
*
*/
#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;

View File

@ -604,6 +604,12 @@ static void parse_extended_opts(e2fsck_t ctx, const char *opts)
continue;
}
ctx->options |= E2F_OPT_JOURNAL_ONLY;
} 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);
@ -620,6 +626,8 @@ static void parse_extended_opts(e2fsck_t ctx, const char *opts)
fputs(("\tea_ver=<ea_version (1 or 2)>\n"), stderr);
fputs(("\tfragcheck\n"), stderr);
fputs(("\tjournal_only\n"), stderr);
fputs(("\tdiscard\n"), stderr);
fputs(("\tnodiscard\n"), stderr);
fputc('\n', stderr);
exit(1);
}
@ -678,6 +686,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':
@ -961,7 +970,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;

View File

@ -49,7 +49,7 @@ blk64_t ext2fs_inode_data_blocks2(ext2_filsys fs,
struct ext2_inode *inode)
{
return (inode->i_blocks |
((fs->super->s_feature_incompat &
((fs->super->s_feature_ro_compat &
EXT4_FEATURE_RO_COMPAT_HUGE_FILE) ?
(__u64) inode->osd2.linux2.l_i_blocks_hi << 32 : 0)) -
(inode->i_file_acl ? fs->blocksize >> 9 : 0);
@ -62,7 +62,7 @@ blk64_t ext2fs_inode_i_blocks(ext2_filsys fs,
struct ext2_inode *inode)
{
return (inode->i_blocks |
((fs->super->s_feature_incompat &
((fs->super->s_feature_ro_compat &
EXT4_FEATURE_RO_COMPAT_HUGE_FILE) ?
(__u64)inode->osd2.linux2.l_i_blocks_hi << 32 : 0));
}

View File

@ -29,6 +29,9 @@ typedef struct struct_io_channel *io_channel;
typedef struct struct_io_stats *io_stats;
#define CHANNEL_FLAGS_WRITETHROUGH 0x01
#define CHANNEL_FLAGS_DISCARD_ZEROES 0x02
#define io_channel_discard_zeroes_data(i) (i->flags & CHANNEL_FLAGS_DISCARD_ZEROES)
struct struct_io_channel {
errcode_t magic;
@ -83,6 +86,8 @@ struct struct_io_manager {
int count, void *data);
errcode_t (*write_blk64)(io_channel channel, unsigned long long block,
int count, const void *data);
errcode_t (*discard)(io_channel channel, unsigned long long block,
unsigned long long count);
long reserved[16];
};

View File

@ -99,3 +99,14 @@ errcode_t io_channel_write_blk64(io_channel channel, unsigned long long block,
return (channel->manager->write_blk)(channel, (unsigned long) block,
count, data);
}
errcode_t io_channel_discard(io_channel channel, unsigned long long block,
unsigned long long count)
{
EXT2_CHECK_MAGIC(channel, EXT2_ET_MAGIC_IO_CHANNEL);
if (channel->manager->discard)
return (channel->manager->discard)(channel, block, count);
return EXT2_ET_UNIMPLEMENTED;
}

View File

@ -73,7 +73,8 @@ static errcode_t test_write_byte(io_channel channel, unsigned long offset,
static errcode_t test_set_option(io_channel channel, const char *option,
const char *arg);
static errcode_t test_get_stats(io_channel channel, io_stats *stats);
static errcode_t test_discard(io_channel channel, unsigned long long block,
unsigned long long count);
static struct struct_io_manager struct_test_manager = {
EXT2_ET_MAGIC_IO_MANAGER,
@ -89,6 +90,7 @@ static struct struct_io_manager struct_test_manager = {
test_get_stats,
test_read_blk64,
test_write_blk64,
test_discard,
};
io_manager test_io_manager = &struct_test_manager;
@ -120,6 +122,7 @@ void (*test_io_cb_write_byte)
#define TEST_FLAG_FLUSH 0x08
#define TEST_FLAG_DUMP 0x10
#define TEST_FLAG_SET_OPTION 0x20
#define TEST_FLAG_DISCARD 0x40
static void test_dump_block(io_channel channel,
struct test_private_data *data,
@ -495,3 +498,22 @@ static errcode_t test_get_stats(io_channel channel, io_stats *stats)
}
return retval;
}
static errcode_t test_discard(io_channel channel, unsigned long long block,
unsigned long long count)
{
struct test_private_data *data;
errcode_t retval = 0;
EXT2_CHECK_MAGIC(channel, EXT2_ET_MAGIC_IO_CHANNEL);
data = (struct test_private_data *) channel->private_data;
EXT2_CHECK_MAGIC(data, EXT2_ET_MAGIC_TEST_IO_CHANNEL);
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",
block, count, retval ? error_message(retval) : "OK");
return retval;
}

View File

@ -115,6 +115,8 @@ static errcode_t unix_read_blk64(io_channel channel, unsigned long long block,
int count, void *data);
static errcode_t unix_write_blk64(io_channel channel, unsigned long long block,
int count, const void *data);
static errcode_t unix_discard(io_channel channel, unsigned long long block,
unsigned long long count);
static struct struct_io_manager struct_unix_manager = {
EXT2_ET_MAGIC_IO_MANAGER,
@ -130,6 +132,7 @@ static struct struct_io_manager struct_unix_manager = {
unix_get_stats,
unix_read_blk64,
unix_write_blk64,
unix_discard,
};
io_manager unix_io_manager = &struct_unix_manager;
@ -422,12 +425,18 @@ static errcode_t flush_cached_blocks(io_channel channel,
}
#endif /* NO_IO_CACHE */
#ifdef __linux__
#ifndef BLKDISCARDZEROES
#define BLKDISCARDZEROES _IO(0x12,124)
#endif
#endif
static errcode_t unix_open(const char *name, int flags, io_channel *channel)
{
io_channel io = NULL;
struct unix_private_data *data = NULL;
errcode_t retval;
int open_flags;
int open_flags, zeroes = 0;
struct stat st;
#ifdef __linux__
struct utsname ut;
@ -484,6 +493,12 @@ static errcode_t unix_open(const char *name, int flags, io_channel *channel)
}
#endif
#ifdef BLKDISCARDZEROES
ioctl(data->dev, BLKDISCARDZEROES, &zeroes);
if (zeroes)
io->flags |= CHANNEL_FLAGS_DISCARD_ZEROES;
#endif
#if defined(__CYGWIN__) || defined(__FreeBSD__) || defined(__FreeBSD_kernel__)
/*
* Some operating systems require that the buffers be aligned,
@ -834,3 +849,31 @@ static errcode_t unix_set_option(io_channel channel, const char *option,
}
return EXT2_ET_INVALID_ARGUMENT;
}
#if defined(__linux__) && !defined(BLKDISCARD)
#define BLKDISCARD _IO(0x12,119)
#endif
static errcode_t unix_discard(io_channel channel, unsigned long long block,
unsigned long long count)
{
#ifdef BLKDISCARD
struct unix_private_data *data;
__uint64_t range[2];
int ret;
EXT2_CHECK_MAGIC(channel, EXT2_ET_MAGIC_IO_CHANNEL);
data = (struct unix_private_data *) channel->private_data;
EXT2_CHECK_MAGIC(data, EXT2_ET_MAGIC_UNIX_IO_CHANNEL);
range[0] = (__uint64_t)(block) * channel->block_size;
range[1] = (__uint64_t)(count) * channel->block_size;
ret = ioctl(data->dev, BLKDISCARD, &range);
if (ret < 0)
return errno;
return 0;
#else
return EXT2_ET_UNIMPLEMENTED;
#endif
}

View File

@ -1960,69 +1960,6 @@ static int mke2fs_setup_tdb(const char *name, io_manager *io_ptr)
return retval;
}
#ifdef __linux__
#ifndef BLKDISCARD
#define BLKDISCARD _IO(0x12,119)
#endif
#ifndef BLKDISCARDZEROES
#define BLKDISCARDZEROES _IO(0x12,124)
#endif
/*
* Return zero if the discard succeeds, and -1 if the discard fails.
*/
static int mke2fs_discard_blocks(ext2_filsys fs)
{
int fd;
int ret;
int blocksize;
__u64 blocks;
__uint64_t range[2];
blocks = ext2fs_blocks_count(fs->super);
blocksize = EXT2_BLOCK_SIZE(fs->super);
range[0] = 0;
range[1] = blocks * blocksize;
fd = open64(fs->device_name, O_RDWR);
if (fd > 0) {
ret = ioctl(fd, BLKDISCARD, &range);
if (verbose) {
printf(_("Calling BLKDISCARD from %llu to %llu "),
(unsigned long long) range[0],
(unsigned long long) range[1]);
if (ret)
printf(_("failed.\n"));
else
printf(_("succeeded.\n"));
}
close(fd);
}
return ret;
}
static int mke2fs_discard_zeroes_data(ext2_filsys fs)
{
int fd;
int ret;
int discard_zeroes_data = 0;
fd = open64(fs->device_name, O_RDWR);
if (fd > 0) {
ioctl(fd, BLKDISCARDZEROES, &discard_zeroes_data);
close(fd);
}
return discard_zeroes_data;
}
#else
#define mke2fs_discard_blocks(fs) 1
#define mke2fs_discard_zeroes_data(fs) 0
#endif
int main (int argc, char *argv[])
{
errcode_t retval = 0;
@ -2083,9 +2020,20 @@ int main (int argc, char *argv[])
/* Can't undo discard ... */
if (discard && (io_ptr != undo_io_manager)) {
retval = mke2fs_discard_blocks(fs);
blk64_t blocks = ext2fs_blocks_count(fs->super);
if (verbose)
printf(_("Calling BLKDISCARD from 0 to %llu... "),
(unsigned long long) blocks);
retval = io_channel_discard(fs->io, 0, blocks, fs->blocksize);
if (verbose) {
if (retval)
printf(_("failed (%s)\n"),
error_message(retval));
else
printf(_("succeeded\n"));
}
if (!retval && mke2fs_discard_zeroes_data(fs)) {
if (!retval && io_channel_discard_zeroes_data(fs->io)) {
if (verbose)
printf(_("Discard succeeded and will return 0s "
" - skipping inode table wipe\n"));