ext4: test journal checkpoint ioctl
authorLeah Rumancik <leah.rumancik@gmail.com>
Mon, 7 Jun 2021 21:55:09 +0000 (21:55 +0000)
committerEryu Guan <guaneryu@gmail.com>
Sun, 13 Jun 2021 15:39:50 +0000 (23:39 +0800)
Test for commit "ext4: add ioctl EXT4_IOC_CHECKPOINT". Tests journal
checkpointing and journal erasing via EXT4_IOC_CHECKPOINT with flag
EXT4_IOC_CHECKPOINT_FLAG_ZEROOUT set.

Signed-off-by: Leah Rumancik <leah.rumancik@gmail.com>
Reviewed-by: Eryu Guan <guaneryu@gmail.com>
Signed-off-by: Eryu Guan <guaneryu@gmail.com>
.gitignore
src/Makefile
src/checkpoint_journal.c [new file with mode: 0644]
tests/ext4/050 [new file with mode: 0755]
tests/ext4/050.out [new file with mode: 0644]
tests/ext4/group

index c62c155..d3194e7 100644 (file)
@@ -61,6 +61,7 @@ tags
 /src/bulkstat_null_ocount
 /src/bulkstat_unlink_test
 /src/bulkstat_unlink_test_modified
 /src/bulkstat_null_ocount
 /src/bulkstat_unlink_test
 /src/bulkstat_unlink_test_modified
+/src/checkpoint_journal
 /src/chprojid_fail
 /src/cloner
 /src/dbtest
 /src/chprojid_fail
 /src/cloner
 /src/dbtest
index 1279e4b..5c76aa9 100644 (file)
@@ -18,7 +18,7 @@ TARGETS = dirstress fill fill2 getpagesize holes lstat64 \
        t_ext4_dax_journal_corruption t_ext4_dax_inline_corruption \
        t_ofd_locks t_mmap_collision mmap-write-concurrent \
        t_get_file_time t_create_short_dirs t_create_long_dirs t_enospc \
        t_ext4_dax_journal_corruption t_ext4_dax_inline_corruption \
        t_ofd_locks t_mmap_collision mmap-write-concurrent \
        t_get_file_time t_create_short_dirs t_create_long_dirs t_enospc \
-       t_mmap_writev_overlap
+       t_mmap_writev_overlap checkpoint_journal
 
 LINUX_TARGETS = xfsctl bstat t_mtab getdevicesize preallo_rw_pattern_reader \
        preallo_rw_pattern_writer ftrunc trunc fs_perms testx looptest \
 
 LINUX_TARGETS = xfsctl bstat t_mtab getdevicesize preallo_rw_pattern_reader \
        preallo_rw_pattern_writer ftrunc trunc fs_perms testx looptest \
diff --git a/src/checkpoint_journal.c b/src/checkpoint_journal.c
new file mode 100644 (file)
index 0000000..a4a6b69
--- /dev/null
@@ -0,0 +1,97 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * Copyright (c) 2021 Google
+ * All Rights Reserved.
+ *
+ * checkpoint_journal.c
+ *
+ * Flush journal log and checkpoint journal for ext4 file system and
+ * optionally, issue discard or zeroout for the journal log blocks.
+ *
+ * Arguments:
+ * 1) mount point for device
+ * 2) flags (optional)
+ *     set --erase=discard to enable discarding journal blocks
+ *     set --erase=zeroout to enable zero-filling journal blocks
+ *     set --dry-run flag to only perform input checking
+ */
+
+#include <sys/ioctl.h>
+#include <fcntl.h>
+#include <stdlib.h>
+#include <stdio.h>
+#include <unistd.h>
+#include <errno.h>
+#include <string.h>
+#include <linux/fs.h>
+#include <getopt.h>
+
+#if !defined(EXT4_IOC_CHECKPOINT)
+#define EXT4_IOC_CHECKPOINT    _IOW('f', 43, __u32)
+#endif
+
+#if !defined(EXT4_IOC_CHECKPOINT_FLAG_DISCARD)
+#define EXT4_IOC_CHECKPOINT_FLAG_DISCARD               1
+#define EXT4_IOC_CHECKPOINT_FLAG_ZEROOUT               2
+#define EXT4_IOC_CHECKPOINT_FLAG_DRY_RUN               4
+#endif
+
+int main(int argc, char** argv) {
+       int fd, c, ret = 0, option_index = 0;
+       char* rpath;
+       unsigned int flags = 0;
+
+       static struct option long_options[] =
+       {
+               {"dry-run", no_argument, 0, 'd'},
+               {"erase", required_argument, 0, 'e'},
+               {0, 0, 0, 0}
+       };
+
+       /* get optional flags */
+       while ((c = getopt_long(argc, argv, "de:", long_options,
+                               &option_index)) != -1) {
+               switch (c) {
+               case 'd':
+                       flags |= EXT4_IOC_CHECKPOINT_FLAG_DRY_RUN;
+                       break;
+               case 'e':
+                       if (strcmp(optarg, "discard") == 0) {
+                               flags |= EXT4_IOC_CHECKPOINT_FLAG_DISCARD;
+                       } else if (strcmp(optarg, "zeroout") == 0) {
+                               flags |= EXT4_IOC_CHECKPOINT_FLAG_ZEROOUT;
+                       } else {
+                               fprintf(stderr, "Error: invalid erase option\n");
+                               return 1;
+                       }
+                       break;
+               default:
+                       return 1;
+               }
+       }
+
+       if (optind != argc - 1) {
+               fprintf(stderr, "Error: invalid number of arguments\n");
+               return 1;
+       }
+
+       /* get fd to file system */
+       rpath = realpath(argv[optind], NULL);
+       fd = open(rpath, O_RDONLY);
+       free(rpath);
+
+       if (fd == -1) {
+               fprintf(stderr, "Error: unable to open device %s: %s\n",
+                       argv[optind], strerror(errno));
+               return 1;
+       }
+
+       ret = ioctl(fd, EXT4_IOC_CHECKPOINT, &flags);
+
+       if (ret)
+               fprintf(stderr, "checkpoint ioctl returned error: %s\n", strerror(errno));
+
+       close(fd);
+       return ret;
+}
+
diff --git a/tests/ext4/050 b/tests/ext4/050
new file mode 100755 (executable)
index 0000000..c56623a
--- /dev/null
@@ -0,0 +1,115 @@
+#!/bin/bash
+# SPDX-License-Identifier: GPL-2.0
+# Copyright (c) 2021 Google, Inc. All Rights Reserved.
+#
+# FS QA Test No. 050
+#
+# Test checkpoint and zeroout of journal via ioctl EXT4_IOC_CHECKPOINT
+#
+
+seq=`basename $0`
+seqres=$RESULT_DIR/$seq
+echo "QA output created by $seq"
+
+status=1       # failure is the default!
+
+# get standard environment, filters and checks
+. ./common/rc
+. ./common/filter
+
+# remove previous $seqres.full before test
+rm -f $seqres.full
+
+# real QA test starts here
+_supported_fs ext4
+
+_require_scratch
+_require_command "$DEBUGFS_PROG" debugfs
+
+checkpoint_journal=$here/src/checkpoint_journal
+_require_test_program "checkpoint_journal"
+
+# convert output from stat<journal_inode> to list of block numbers
+get_journal_extents() {
+       inode_info=$($DEBUGFS_PROG $SCRATCH_DEV -R "stat <8>" 2>> $seqres.full)
+       echo -e "\nJournal info:" >> $seqres.full
+       echo "$inode_info" >> $seqres.full
+
+       extents_line=$(echo "$inode_info" | awk '/EXTENTS:/{ print NR; exit }')
+       get_extents=$(echo "$inode_info" | sed -n "$(($extents_line + 1))"p)
+
+       # get just the physical block numbers
+       get_extents=$(echo "$get_extents" |  perl -pe 's|\(.*?\):||g' | sed -e 's/, /\n/g' | perl -pe 's|(\d+)-(\d+)|\1 \2|g')
+
+       echo "$get_extents"
+}
+
+
+# checks all extents are zero'd out except for the superblock
+# arg 1: extents (output of get_journal_extents())
+check_extents() {
+       echo -e "\nChecking extents:" >> $seqres.full
+       echo "$1" >> $seqres.full
+
+       super_block="true"
+       echo "$1" | while IFS= read line; do
+               start_block=$(echo $line | cut -f1 -d' ')
+               end_block=$(echo $line | cut -f2 -d' ' -s)
+
+               # if first block of journal, shouldn't be wiped
+               if [ "$super_block" == "true" ]; then
+                       super_block="false"
+
+                       #if super block only block in this extent, skip extent
+                       if [ -z "$end_block" ]; then
+                               continue;
+                       fi
+                       start_block=$(($start_block + 1))
+               fi
+
+               if [ ! -z "$end_block" ]; then
+                       blocks=$(($end_block - $start_block + 1))
+               else
+                       blocks=1
+               fi
+
+               check=$(od $SCRATCH_DEV --skip-bytes=$(($start_block * $blocksize)) --read-bytes=$(($blocks * $blocksize)) -An -v | sed -e 's/[0 \t\n\r]//g')
+
+               [ ! -z "$check" ] && echo "error" && break
+       done
+}
+
+testdir="${SCRATCH_MNT}/testdir"
+
+_scratch_mkfs_sized $((64 * 1024 * 1024)) >> $seqres.full 2>&1
+_require_metadata_journaling $SCRATCH_DEV
+_scratch_mount >> $seqres.full 2>&1
+blocksize=$(_get_block_size $SCRATCH_MNT)
+mkdir $testdir
+
+# check if ioctl present, skip test if not present
+$checkpoint_journal $SCRATCH_MNT --dry-run || _notrun "journal checkpoint ioctl not present on device"
+
+# create some files to add some entries to journal
+for i in {1..100}; do
+       echo > $testdir/$i
+done
+
+# make sure these files get to the journal
+sync --file-system $testdir/1
+
+# call ioctl to checkpoint and zero-fill journal blocks
+$checkpoint_journal $SCRATCH_MNT --erase=zeroout || _fail "ioctl returned error"
+
+extents=$(get_journal_extents)
+
+# check journal blocks zeroed out
+ret=$(check_extents "$extents")
+[ "$ret" = "error" ] && _fail "Journal was not zero-filled"
+
+_scratch_unmount >> $seqres.full 2>&1
+
+echo "Silence is golden"
+
+status=0
+exit
diff --git a/tests/ext4/050.out b/tests/ext4/050.out
new file mode 100644 (file)
index 0000000..73794d8
--- /dev/null
@@ -0,0 +1,2 @@
+QA output created by 050
+Silence is golden
index c246feb..16eee91 100644 (file)
@@ -52,6 +52,7 @@
 047 auto quick dax
 048 auto quick dir
 049 auto quick
 047 auto quick dax
 048 auto quick dir
 049 auto quick
+050 auto ioctl quick
 271 auto rw quick
 301 aio auto ioctl rw stress defrag
 302 aio auto ioctl rw stress defrag
 271 auto rw quick
 301 aio auto ioctl rw stress defrag
 302 aio auto ioctl rw stress defrag