Whamcloud - gitweb
filefrag: add -E option to display the extent status cache
authorTheodore Ts'o <tytso@mit.edu>
Thu, 23 Jan 2020 15:46:10 +0000 (10:46 -0500)
committerTheodore Ts'o <tytso@mit.edu>
Thu, 23 Jan 2020 15:46:10 +0000 (10:46 -0500)
Ext4 has an extent status cache; add the fiemap extensions so we can
query the kernel for the extent status cache information.

Signed-off-by: Theodore Ts'o <tytso@mit.edu>
lib/ext2fs/fiemap.h
misc/filefrag.8.in
misc/filefrag.c

index 0d1072a..33ab8fb 100644 (file)
@@ -45,10 +45,23 @@ struct fiemap {
 #define FS_IOC_FIEMAP  _IOWR('f', 11, struct fiemap)
 #endif
 
+#if defined(__linux__) && !defined(FS_IOC_GETSTATE)
+#define EXT4_IOC_GETSTATE              _IOW('f', 41, __u32)
+#endif
+
+#if defined(__linux__) && !defined(EXT4_IOC_GET_ES_CACHE)
+#define EXT4_IOC_GET_ES_CACHE          _IOWR('f', 42, struct fiemap)
+#endif
+
+#if defined(__linux__) && !defined(EXT4_STATE_FLAG_EXT_PRECACHED)
+#define EXT4_STATE_FLAG_EXT_PRECACHED  0x00000001
+#endif
+
 #define FIEMAP_MAX_OFFSET      (~0ULL)
 
 #define FIEMAP_FLAG_SYNC       0x00000001 /* sync file data before map */
 #define FIEMAP_FLAG_XATTR      0x00000002 /* map extended attribute tree */
+#define FIEMAP_FLAG_CACHE      0x00000004 /* request caching of the extents */
 
 #define FIEMAP_FLAGS_COMPAT    (FIEMAP_FLAG_SYNC | FIEMAP_FLAG_XATTR)
 
@@ -74,4 +87,7 @@ struct fiemap {
 #define FIEMAP_EXTENT_SHARED           0x00002000 /* Space shared with other
                                                    * files. */
 
+#define EXT4_FIEMAP_EXTENT_HOLE                0x08000000 /* Entry in extent status
+                                                     cache for a hole*/
+
 #endif /* _LINUX_FIEMAP_H */
index 5292672..302d856 100644 (file)
@@ -43,6 +43,14 @@ is unspecified it defaults to 1024 bytes.
 .B \-e
 Print output in extent format, even for block-mapped files.
 .TP
+.B \-E
+Display the contents of ext4's extent status cache.  This feature is not
+supported on all kernels, and is only supported on ext4 file systems.
+.TP
+.TP
+.B -P
+Pre-load the ext4's extent status cache for the file.  This feature is not
+supported on all kernels, and is only supported on ext4 file systems.
 .B \-k
 Use 1024\-byte blocksize for output (identical to '\-b 1024').
 .TP
index 1eec146..c845bec 100644 (file)
@@ -55,9 +55,11 @@ extern int optind;
 int verbose = 0;
 int blocksize;         /* Use specified blocksize (default 1kB) */
 int sync_file = 0;     /* fsync file before getting the mapping */
+int precache_file = 0; /* precache the file before getting the mapping */
 int xattr_map = 0;     /* get xattr mapping */
 int force_bmap;        /* force use of FIBMAP instead of FIEMAP */
 int force_extent;      /* print output in extent format always */
+int use_extent_cache;  /* Use extent cache */
 int logical_width = 8;
 int physical_width = 10;
 const char *ext_fmt = "%4d: %*llu..%*llu: %*llu..%*llu: %6llu: %s\n";
@@ -145,6 +147,7 @@ static void print_extent_info(struct fiemap_extent *fm_extent, int cur_ex,
        unsigned long long logical_blk;
        unsigned long long ext_len;
        unsigned long long ext_blks;
+       unsigned long long ext_blks_phys;
        __u32 fe_flags, mask;
        char flags[256] = "";
 
@@ -161,13 +164,15 @@ static void print_extent_info(struct fiemap_extent *fm_extent, int cur_ex,
                physical_blk = fm_extent->fe_physical >> blk_shift;
        }
 
-       if (expected)
+       fe_flags = fm_extent->fe_flags;
+       if (expected &&
+           !(fe_flags & FIEMAP_EXTENT_UNKNOWN) &&
+           !(fe_flags & EXT4_FIEMAP_EXTENT_HOLE))
                sprintf(flags, ext_fmt == hex_fmt ? "%*llx: " : "%*llu: ",
                        physical_width, expected >> blk_shift);
        else
                sprintf(flags, "%.*s  ", physical_width, "                   ");
 
-       fe_flags = fm_extent->fe_flags;
        print_flag(&fe_flags, FIEMAP_EXTENT_LAST, flags, "last,");
        print_flag(&fe_flags, FIEMAP_EXTENT_UNKNOWN, flags, "unknown_loc,");
        print_flag(&fe_flags, FIEMAP_EXTENT_DELALLOC, flags, "delalloc,");
@@ -179,6 +184,7 @@ static void print_extent_info(struct fiemap_extent *fm_extent, int cur_ex,
        print_flag(&fe_flags, FIEMAP_EXTENT_UNWRITTEN, flags, "unwritten,");
        print_flag(&fe_flags, FIEMAP_EXTENT_MERGED, flags, "merged,");
        print_flag(&fe_flags, FIEMAP_EXTENT_SHARED, flags, "shared,");
+       print_flag(&fe_flags, EXT4_FIEMAP_EXTENT_HOLE, flags, "hole,");
        /* print any unknown flags as hex values */
        for (mask = 1; fe_flags != 0 && mask != 0; mask <<= 1) {
                char hex[sizeof(mask) * 2 + 4]; /* 2 chars/byte + 0x, + NUL */
@@ -197,10 +203,17 @@ static void print_extent_info(struct fiemap_extent *fm_extent, int cur_ex,
        if (flags[0] != '\0')
                flags[strnlen(flags, sizeof(flags)) - 1] = '\0';
 
+       if ((fm_extent->fe_flags & FIEMAP_EXTENT_UNKNOWN) ||
+           (fm_extent->fe_flags & EXT4_FIEMAP_EXTENT_HOLE)) {
+               ext_len = 0;
+               ext_blks_phys = 0;
+       } else
+               ext_blks_phys = ext_blks;
+
        printf(ext_fmt, cur_ex, logical_width, logical_blk,
               logical_width, logical_blk + ext_blks,
               physical_width, physical_blk,
-              physical_width, physical_blk + ext_blks,
+              physical_width, physical_blk + ext_blks_phys,
               ext_len, flags);
 }
 
@@ -217,6 +230,7 @@ static int filefrag_fiemap(int fd, int blk_shift, int *num_extents,
        unsigned long long expected_dense = 0;
        unsigned long flags = 0;
        unsigned int i;
+       unsigned long cmd = FS_IOC_FIEMAP;
        int fiemap_header_printed = 0;
        int tot_extents = 0, n = 0;
        int last = 0;
@@ -228,14 +242,20 @@ static int filefrag_fiemap(int fd, int blk_shift, int *num_extents,
        if (sync_file)
                flags |= FIEMAP_FLAG_SYNC;
 
+       if (precache_file)
+               flags |= FIEMAP_FLAG_CACHE;
+
        if (xattr_map)
                flags |= FIEMAP_FLAG_XATTR;
 
+       if (use_extent_cache)
+               cmd = EXT4_IOC_GET_ES_CACHE;
+
        do {
                fiemap->fm_length = ~0ULL;
                fiemap->fm_flags = flags;
                fiemap->fm_extent_count = count;
-               rc = ioctl(fd, FS_IOC_FIEMAP, (unsigned long) fiemap);
+               rc = ioctl(fd, cmd, (unsigned long) fiemap);
                if (rc < 0) {
                        static int fiemap_incompat_printed;
 
@@ -462,18 +482,39 @@ static int frag_report(const char *filename)
        else
                blk_shift = int_log2(blksize);
 
-       width = int_log10(numblocks);
+       if (use_extent_cache)
+               width = 10;
+       else
+               width = int_log10(numblocks);
        if (width > logical_width)
                logical_width = width;
-       if (verbose)
-               printf("File size of %s is %llu (%llu block%s of %d bytes)\n",
+       if (verbose) {
+               __u32 state;
+
+               printf("File size of %s is %llu (%llu block%s of %d bytes)",
                       filename, (unsigned long long)st.st_size,
                       numblocks * blksize >> blk_shift,
                       numblocks == 1 ? "" : "s", 1 << blk_shift);
+               if (use_extent_cache &&
+                   ioctl(fd, EXT4_IOC_GETSTATE, &state) == 0 &&
+                   (state & EXT4_STATE_FLAG_EXT_PRECACHED))
+                       fputs(" -- pre-cached", stdout);
+               fputc('\n', stdout);
+       }
 
        if (!force_bmap) {
                rc = filefrag_fiemap(fd, blk_shift, &num_extents, &st);
                expected = 0;
+               if (rc < 0 &&
+                   (use_extent_cache || precache_file || xattr_map)) {
+                       if (rc != -EBADR)
+                               fprintf(stderr, "%s: %s: %s\n ",
+                                       filename,
+                                       use_extent_cache ?
+                                       "EXT4_IOC_GET_ES_CACHE" :
+                                       "FS_IOC_FIEMAP", strerror(-rc));
+                       goto out_close;
+               }
        }
 
        if (force_bmap || rc < 0) { /* FIEMAP failed, try FIBMAP instead */
@@ -517,7 +558,7 @@ out_close:
 
 static void usage(const char *progname)
 {
-       fprintf(stderr, "Usage: %s [-b{blocksize}] [-BeksvxX] file ...\n",
+       fprintf(stderr, "Usage: %s [-b{blocksize}] [-BeEksvxX] file ...\n",
                progname);
        exit(1);
 }
@@ -527,7 +568,7 @@ int main(int argc, char**argv)
        char **cpp;
        int rc = 0, c;
 
-       while ((c = getopt(argc, argv, "Bb::eksvxX")) != EOF) {
+       while ((c = getopt(argc, argv, "Bb::eEkPsvxX")) != EOF) {
                switch (c) {
                case 'B':
                        force_bmap++;
@@ -569,6 +610,9 @@ int main(int argc, char**argv)
                                blocksize = 1024;
                        }
                        break;
+               case 'E':
+                       use_extent_cache++;
+                       /* fallthrough */
                case 'e':
                        force_extent++;
                        if (!verbose)
@@ -577,6 +621,9 @@ int main(int argc, char**argv)
                case 'k':
                        blocksize = 1024;
                        break;
+               case 'P':
+                       precache_file++;
+                       break;
                case 's':
                        sync_file++;
                        break;