]> git.ipfire.org Git - thirdparty/e2fsprogs.git/commitdiff
e2fsprogs: Use punch hole as "discard" on regular files
authorLukas Czerner <lczerner@redhat.com>
Fri, 16 Sep 2011 03:44:59 +0000 (23:44 -0400)
committerTheodore Ts'o <tytso@mit.edu>
Fri, 16 Sep 2011 03:49:20 +0000 (23:49 -0400)
If e2fsprogs tools (mke2fs, e2fsck) is run on regular file instead of
on block device, we can use punch hole instead of regular discard
command which would not work on regular file anyway. This gives us
several advantages. First of all when e2fsck is run with '-E discard'
parameter it will punch out all ununsed space from the image, hence
trimming down the file system image. And secondly, when creating an
file system on regular file (with '-E discard' which is default), we
can use punch hole to clear the file content, hence we can skip inode
table initialization, because reads from sparse area returns zeros. This
will result in faster file system creation (without the need to specify
lazy_itable_init) and smaller images.

This commit also fixes some tests that would fail due to mke2fs showing
discard progress, hence the output would differ.

Signed-off-by: Lukas Czerner <lczerner@redhat.com>
Signed-off-by: Theodore Ts'o <tytso@mit.edu>
configure
configure.in
lib/ext2fs/ext2_io.h
lib/ext2fs/unix_io.c
misc/mke2fs.c
tests/f_resize_inode/script
tests/m_bigjournal/script
tests/run_mke2fs

index 6bac035d112457576dbd151470ee381d66da3614..215d0312b40da754867e1a818a7b00d13a5d07c3 100755 (executable)
--- a/configure
+++ b/configure
@@ -10314,7 +10314,7 @@ fi
 done
 
 fi
-for ac_header in dirent.h errno.h execinfo.h getopt.h malloc.h mntent.h paths.h semaphore.h setjmp.h signal.h stdarg.h stdint.h stdlib.h termios.h termio.h unistd.h utime.h linux/fd.h linux/major.h net/if_dl.h netinet/in.h sys/disklabel.h sys/file.h sys/ioctl.h sys/mkdev.h sys/mman.h sys/prctl.h sys/queue.h sys/resource.h sys/select.h sys/socket.h sys/sockio.h sys/stat.h sys/syscall.h sys/sysmacros.h sys/time.h sys/types.h sys/un.h sys/wait.h
+for ac_header in dirent.h errno.h execinfo.h getopt.h malloc.h mntent.h paths.h semaphore.h setjmp.h signal.h stdarg.h stdint.h stdlib.h termios.h termio.h unistd.h utime.h linux/falloc.h linux/fd.h linux/major.h net/if_dl.h netinet/in.h sys/disklabel.h sys/file.h sys/ioctl.h sys/mkdev.h sys/mman.h sys/prctl.h sys/queue.h sys/resource.h sys/select.h sys/socket.h sys/sockio.h sys/stat.h sys/syscall.h sys/sysmacros.h sys/time.h sys/types.h sys/un.h sys/wait.h
 do :
   as_ac_Header=`$as_echo "ac_cv_header_$ac_header" | $as_tr_sh`
 ac_fn_c_check_header_mongrel "$LINENO" "$ac_header" "$as_ac_Header" "$ac_includes_default"
index 6b6c852ce4f89aba2624ac1280da9bed649b5000..8dc75a9b211726ecdbaf17f5b281e55dcd7ff99a 100644 (file)
@@ -802,7 +802,7 @@ if test $cross_compiling = no; then
 else
   AC_CHECK_PROGS(BUILD_CC, gcc cc)
 fi
-AC_CHECK_HEADERS(dirent.h errno.h execinfo.h getopt.h malloc.h mntent.h paths.h semaphore.h setjmp.h signal.h stdarg.h stdint.h stdlib.h termios.h termio.h unistd.h utime.h linux/fd.h linux/major.h net/if_dl.h netinet/in.h sys/disklabel.h sys/file.h sys/ioctl.h sys/mkdev.h sys/mman.h sys/prctl.h sys/queue.h sys/resource.h sys/select.h sys/socket.h sys/sockio.h sys/stat.h sys/syscall.h sys/sysmacros.h sys/time.h sys/types.h sys/un.h sys/wait.h)
+AC_CHECK_HEADERS(dirent.h errno.h execinfo.h getopt.h malloc.h mntent.h paths.h semaphore.h setjmp.h signal.h stdarg.h stdint.h stdlib.h termios.h termio.h unistd.h utime.h linux/falloc.h linux/fd.h linux/major.h net/if_dl.h netinet/in.h sys/disklabel.h sys/file.h sys/ioctl.h sys/mkdev.h sys/mman.h sys/prctl.h sys/queue.h sys/resource.h sys/select.h sys/socket.h sys/sockio.h sys/stat.h sys/syscall.h sys/sysmacros.h sys/time.h sys/types.h sys/un.h sys/wait.h)
 AC_CHECK_HEADERS(sys/disk.h sys/mount.h,,,
 [[
 #if HAVE_SYS_QUEUE_H
index e71ada99d2fcd47467f178a581072190de396efb..bcc2f8763995fce668de5fbc942771e32c13c2ec 100644 (file)
@@ -30,6 +30,7 @@ typedef struct struct_io_stats *io_stats;
 
 #define CHANNEL_FLAGS_WRITETHROUGH     0x01
 #define CHANNEL_FLAGS_DISCARD_ZEROES   0x02
+#define CHANNEL_FLAGS_BLOCK_DEVICE     0x04
 
 #define io_channel_discard_zeroes_data(i) (i->flags & CHANNEL_FLAGS_DISCARD_ZEROES)
 
index 21a273db6e4f6a47558a98a21da6d270c9925a8e..ecddfa6bc7a5c5c93edd0f06cceeb16cc86409dc 100644 (file)
@@ -49,6 +49,9 @@
 #if HAVE_SYS_RESOURCE_H
 #include <sys/resource.h>
 #endif
+#if HAVE_LINUX_FALLOC_H
+#include <linux/falloc.h>
+#endif
 
 #if defined(__linux__) && defined(_IO) && !defined(BLKROGET)
 #define BLKROGET   _IO(0x12, 94) /* Get read-only status (0 = read_write).  */
@@ -488,6 +491,20 @@ static errcode_t unix_open(const char *name, int flags, io_channel *channel)
                goto cleanup;
        }
 
+       /*
+        * If the device is really a block device, then set the
+        * appropriate flag, otherwise we can set DISCARD_ZEROES flag
+        * because we are going to use punch hole instead of discard
+        * and if it succeed, subsequent read from sparse area returns
+        * zero.
+        */
+       if (ext2fs_stat(io->name, &st) == 0) {
+               if (S_ISBLK(st.st_mode))
+                       io->flags |= CHANNEL_FLAGS_BLOCK_DEVICE;
+               else
+                       io->flags |= CHANNEL_FLAGS_DISCARD_ZEROES;
+       }
+
 #ifdef BLKSSZGET
        if (flags & IO_FLAG_DIRECT_IO) {
                if (ioctl(data->dev, BLKSSZGET, &data->align) != 0)
@@ -853,13 +870,12 @@ static errcode_t unix_set_option(io_channel channel, const char *option,
 }
 
 #if defined(__linux__) && !defined(BLKDISCARD)
-#define BLKDISCARD     _IO(0x12,119)
+#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;
@@ -868,14 +884,35 @@ static errcode_t unix_discard(io_channel channel, unsigned long long block,
        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;
+       if (channel->flags & CHANNEL_FLAGS_BLOCK_DEVICE) {
+#ifdef BLKDISCARD
+               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)
+               ret = ioctl(data->dev, BLKDISCARD, &range);
+#else
+               goto unimplemented;
+#endif
+       } else {
+#ifdef FALLOC_FL_PUNCH_HOLE
+               /*
+                * If we are not on block device, try to use punch hole
+                * to reclaim free space.
+                */
+               ret = fallocate(data->dev,
+                               FALLOC_FL_PUNCH_HOLE | FALLOC_FL_KEEP_SIZE,
+                               (off_t)(block) * channel->block_size,
+                               (off_t)(count) * channel->block_size);
+#else
+               goto unimplemented;
+#endif
+       }
+       if (ret < 0) {
+               if (errno == EOPNOTSUPP)
+                       goto unimplemented;
                return errno;
+       }
        return 0;
-#else
+unimplemented:
        return EXT2_ET_UNIMPLEMENTED;
-#endif
 }
index 63538cd91e4a8acedd37c5b7b2cf9316697cd993..abcfbf022ebb8e7139a9f4f84df60c0fd5b3135b 100644 (file)
@@ -2077,12 +2077,18 @@ static int mke2fs_discard_device(ext2_filsys fs)
        struct ext2fs_numeric_progress_struct progress;
        blk64_t blocks = ext2fs_blocks_count(fs->super);
        blk64_t count = DISCARD_STEP_MB;
-       blk64_t cur = 0;
+       blk64_t cur;
        int retval = 0;
 
-       retval = io_channel_discard(fs->io, 0, 0);
+       /*
+        * Let's try if discard really works on the device, so
+        * we do not print numeric progress resulting in failure
+        * afterwards.
+        */
+       retval = io_channel_discard(fs->io, 0, fs->blocksize);
        if (retval)
                return retval;
+       cur = fs->blocksize;
 
        count *= (1024 * 1024);
        count /= fs->blocksize;
index a010fd9827ba31938c12d42d1b353fc8ec74eed7..ca934e9e9c15f900cf86012c159129e880654b43 100644 (file)
@@ -17,6 +17,7 @@ dd if=/dev/zero of=$TMPFILE bs=1k count=512 > /dev/null 2>&1
 echo mke2fs -F -O resize_inode -o Linux -b 1024 -g 1024 test.img 16384 > $OUT
 $MKE2FS -F -O resize_inode -o Linux -b 1024 -g 1024 $TMPFILE 16384 2>&1 \
        | sed -e '1d' | grep -v "automatically checked" | 
+       grep -v 'Discarding device blocks' |
        grep -v "whichever comes first" >> $OUT 
 
 $FSCK $FSCK_OPT  -N test_filesys $TMPFILE > $OUT.new 2>&1
index 29e0a24289cbd7f0d8f20d00447d726d1f4a61f7..1e21fdfec32653671d7227f23b8db6d55f52acf3 100644 (file)
@@ -1,4 +1,4 @@
 DESCRIPTION="journal over 4GB in size"
 FS_SIZE=11000000
-MKE2FS_OPTS="-t ext4 -G 512 -N 1280 -J size=5000 -q -E lazy_journal_init,lazy_itable_init"
+MKE2FS_OPTS="-t ext4 -G 512 -N 1280 -J size=5000 -q -E lazy_journal_init,lazy_itable_init,nodiscard"
 . $cmd_dir/run_mke2fs
index a3a77432bd2554861a936b2a22499f1a9ef1b296..f5249968bfc7ad77569573440b89570c8962c6fb 100644 (file)
@@ -11,7 +11,7 @@ MKE2FS_SKIP_PROGRESS=true
 MKE2FS_SKIP_CHECK_MSG=true
 export MKE2FS_SKIP_PROGRESS MKE2FS_SKIP_CHECK_MSG
 > $TMPFILE
-PREP_CMD='$MKE2FS -F -o Linux $MKE2FS_OPTS $TMPFILE $FS_SIZE 2>&1 | sed -e 1d | tr -d \\015 > $OUT1 ; $DEBUGFS -R features $TMPFILE 2>&1 | sed -e 1d | tr -d \\015 >> $OUT1 ; echo " " >> $OUT1'
+PREP_CMD='$MKE2FS -F -o Linux $MKE2FS_OPTS $TMPFILE $FS_SIZE 2>&1 | sed -e 1d | grep -v "Discarding device blocks" | tr -d \\015 > $OUT1 ; $DEBUGFS -R features $TMPFILE 2>&1 | sed -e 1d | tr -d \\015 >> $OUT1 ; echo " " >> $OUT1'
 AFTER_CMD='$DUMPE2FS $TMPFILE 2>&1 | sed -f $cmd_dir/filter_dumpe2fs | tr -d \\015 >> $OUT1'
 . $cmd_dir/run_e2fsck