@@ -61,6 +61,9 @@ static errcode_t resize_progress_func(ext2_resize_t rfs, int pass,
ext2fs_progress_close(progress);
progress = 0;
switch (pass) {
+ case E2_RSZ_DISCARD_DEVICE:
+ label = _("Discarding device");
+ break;
case E2_RSZ_EXTEND_ITABLE_PASS:
label = _("Extending the inode table");
break;
@@ -181,6 +184,10 @@ static void parse_extended_opts(int *flags, const char *opts)
lazy = 1;
if (lazy)
*flags |= RESIZE_LAZY_ITABLE_INIT;
+ } else if (!strcmp(token, "discard")) {
+ *flags |= RESIZE_DISCARD;
+ } else if (!strcmp(token, "nodiscard")) {
+ *flags &= ~RESIZE_DISCARD;
} else {
r_usage++;
badopt = token;
@@ -192,7 +199,9 @@ static void parse_extended_opts(int *flags, const char *opts)
"and may take an argument which\n"
"\tis set off by an equals ('=') sign.\n\n"
"Valid extended options are:\n"
- "\tlazy_itable_init=<0 to disable, 1 to enable>\n\n"),
+ "\tlazy_itable_init=<0 to disable, 1 to enable>\n"
+ "\tdiscard\n"
+ "\tnodiscard\n\n"),
badopt ? badopt : "");
free(buf);
exit(1);
@@ -148,6 +148,20 @@ resize noticeably, but it requires the kernel to finish
initializing the filesystem in the background when the filesystem is
mounted. If the option value is omitted, it defaults to 1 to
enable lazy inode table initialization.
+TP
+.BI discard
+Attempt to discard blocks
+.BR resize2fs
+is going to use to extend the filesystem (discarding blocks is useful on solid
+state devices and sparse / thin-provisioned storage) before the resize. If the
+device advertises that discard also zeroes data (any subsequent read after the
+discard and before write returns zero), then mark all not-yet-zeroed inode
+tables as zeroed. This significantly speed up filesystem resize if
+.BR lazy_itable_init
+is not specified.
+.TP
+.BI nodiscard
+Do not attempt to discard blocks before resize. This is the default.
.SH KNOWN BUGS
The minimum size of the filesystem as estimated by resize2fs may be
incorrect, especially for filesystems with 1k and 2k blocksizes.
@@ -66,6 +66,54 @@ static errcode_t fix_sb_journal_backup(ext2_filsys fs);
#define SUPER_OVERHEAD(fs) (1 + (fs)->desc_blocks +\
(fs)->super->s_reserved_gdt_blocks)
+#define DISCARD_STEP_MB (2048)
+
+static int resize2fs_discard_device(ext2_resize_t rfs, blk64_t start,
+ blk64_t end)
+{
+ ext2_filsys fs = rfs->new_fs;
+ blk64_t blocks = end - start;
+ blk64_t count = DISCARD_STEP_MB;
+ blk64_t cur = 0;
+ int retval = 0;
+
+ if (start >= end)
+ return -EINVAL;
+
+ count *= (1024 * 1024);
+ count /= fs->blocksize;
+
+ if (rfs->progress) {
+ retval = rfs->progress(rfs, E2_RSZ_DISCARD_DEVICE,
+ cur, blocks);
+ if (retval)
+ return retval;
+ }
+
+ while (cur < blocks) {
+ if (cur + count > blocks)
+ count = blocks - cur;
+ retval = io_channel_discard(fs->io, start + cur, count,
+ fs->blocksize);
+ if (retval)
+ break;
+
+ cur += count;
+ if (rfs->progress) {
+ retval = rfs->progress(rfs, E2_RSZ_DISCARD_DEVICE,
+ cur, blocks);
+ if (retval)
+ break;
+ }
+ }
+
+ if (retval && rfs->progress) {
+ printf(_("\nDiscard Failed - "));
+ printf("%s\n",error_message(retval));
+ }
+ return retval;
+}
+
/*
* This is the top-level routine which does the dirty deed....
*/
@@ -103,6 +151,28 @@ errcode_t resize_fs(ext2_filsys fs, blk64_t *new_size, int flags,
if (retval)
goto errout;
+ /*
+ * Attempt to discard space which we are going to use to extend the
+ * file system. We do no need to abort file system resize if this
+ * fails due to EOPNOTSUPP, just clear RESIZE_DISCARD flag
+ */
+ if ((flags & RESIZE_DISCARD) &&
+ (*new_size > ext2fs_blocks_count(fs->super))) {
+ if (io_channel_discard_zeroes_data(rfs->new_fs->io))
+ flags |= RESIZE_DISCARD_ZEROES;
+ retval = resize2fs_discard_device(rfs,
+ ext2fs_blocks_count(fs->super),
+ *new_size);
+ if (retval = -EOPNOTSUPP)
+ flags &= ~(RESIZE_DISCARD & RESIZE_DISCARD_ZEROES);
+ else {
+ fprintf(stderr, _("Warning: Something went wrong"
+ "while discarding, maybe due programming"
+ "error, or the device is broken. Exiting!\n"));
+ exit(retval);
+ }
+ }
+
retval = adjust_superblock(rfs, *new_size, flags);
if (retval)
goto errout;
@@ -499,6 +569,8 @@ retry:
adjblocks = 0;
ext2fs_bg_flags_zap(fs, i);
+ if (csum_flag && (flags & RESIZE_DISCARD_ZEROES))
+ ext2fs_bg_flags_set(fs, i, EXT2_BG_INODE_ZEROED);
if (csum_flag && (flags & RESIZE_LAZY_ITABLE_INIT))
ext2fs_bg_flags_set(fs, i, EXT2_BG_INODE_UNINIT);
else if (csum_flag)
@@ -672,7 +744,7 @@ static errcode_t adjust_superblock(ext2_resize_t rfs, blk64_t new_size,
if (retval)
goto errout;
- if (!(flags & RESIZE_LAZY_ITABLE_INIT))
+ if (!(flags & (RESIZE_LAZY_ITABLE_INIT | RESIZE_DISCARD_ZEROES)))
retval = write_inode_tables(rfs, fs);
errout:
return retval;
@@ -80,6 +80,8 @@ typedef struct ext2_sim_progress *ext2_sim_progmeter;
#define RESIZE_PERCENT_COMPLETE 0x0100
#define RESIZE_VERBOSE 0x0200
#define RESIZE_LAZY_ITABLE_INIT 0x0400 /* Do not initialize inode tables*/
+#define RESIZE_DISCARD 0x0800 /* Discard space before attempt to resize */
+#define RESIZE_DISCARD_ZEROES 0x1000 /* Discard zeroes data */
/*
* The core state structure for the ext2 resizer
@@ -115,6 +117,7 @@ struct ext2_resize_struct {
/*
* Progress pass numbers...
*/
+#define E2_RSZ_DISCARD_DEVICE 0
#define E2_RSZ_EXTEND_ITABLE_PASS 1
#define E2_RSZ_BLOCK_RELOC_PASS 2
#define E2_RSZ_INODE_SCAN_PASS 3
This commit adds two new extended options - discard and nodiscard. When '-E discard' is specified, resize2fs will attempt to discard the portion of the device which will be used to extend the file system. As a side-effect, if the discard also zeroes data (every subsequent read form discarded block will return zeros) resize2fs will skip inode table initialization, but still set the EXT2_BG_INODE_ZEROED flag. The default is 'nodiscard'. Signed-off-by: Lukas Czerner <lczerner@redhat.com> --- resize/main.c | 11 ++++++- resize/resize2fs.8.in | 14 +++++++++ resize/resize2fs.c | 74 ++++++++++++++++++++++++++++++++++++++++++++++++- resize/resize2fs.h | 3 ++ 4 files changed, 100 insertions(+), 2 deletions(-)