summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorTheodore Ts'o <tytso@mit.edu>2019-08-11 16:32:41 -0400
committerTheodore Ts'o <tytso@mit.edu>2019-08-11 16:32:41 -0400
commitbb5835edcdf8bf78bbe51cff13e332c439bc0567 (patch)
tree74e9498c0cb0f42a3158d0a892181316e5a8a243
parent1ad3ea6e0a694b0486eb2cbe60378ad0fbf23642 (diff)
ext4: add new ioctl EXT4_IOC_GET_ES_CACHE
For debugging reasons, it's useful to know the contents of the extent cache. Since the extent cache contains much of what is in the fiemap ioctl, use an fiemap-style interface to return this information. Signed-off-by: Theodore Ts'o <tytso@mit.edu>
-rw-r--r--fs/ext4/ext4.h10
-rw-r--r--fs/ext4/extents.c94
-rw-r--r--fs/ext4/extents_status.c10
-rw-r--r--fs/ext4/extents_status.h1
-rw-r--r--fs/ext4/inode.c6
-rw-r--r--fs/ext4/ioctl.c72
6 files changed, 182 insertions, 11 deletions
diff --git a/fs/ext4/ext4.h b/fs/ext4/ext4.h
index ee296797bcd2..e2d8ad27f4d1 100644
--- a/fs/ext4/ext4.h
+++ b/fs/ext4/ext4.h
@@ -652,6 +652,7 @@ enum {
652/* ioctl codes 19--39 are reserved for fscrypt */ 652/* ioctl codes 19--39 are reserved for fscrypt */
653#define EXT4_IOC_CLEAR_ES_CACHE _IO('f', 40) 653#define EXT4_IOC_CLEAR_ES_CACHE _IO('f', 40)
654#define EXT4_IOC_GETSTATE _IOW('f', 41, __u32) 654#define EXT4_IOC_GETSTATE _IOW('f', 41, __u32)
655#define EXT4_IOC_GET_ES_CACHE _IOWR('f', 42, struct fiemap)
655 656
656#define EXT4_IOC_FSGETXATTR FS_IOC_FSGETXATTR 657#define EXT4_IOC_FSGETXATTR FS_IOC_FSGETXATTR
657#define EXT4_IOC_FSSETXATTR FS_IOC_FSSETXATTR 658#define EXT4_IOC_FSSETXATTR FS_IOC_FSSETXATTR
@@ -692,6 +693,12 @@ enum {
692#define EXT4_IOC32_SETVERSION_OLD FS_IOC32_SETVERSION 693#define EXT4_IOC32_SETVERSION_OLD FS_IOC32_SETVERSION
693#endif 694#endif
694 695
696/*
697 * Returned by EXT4_IOC_GET_ES_CACHE as an additional possible flag.
698 * It indicates that the entry in extent status cache is for a hole.
699 */
700#define EXT4_FIEMAP_EXTENT_HOLE 0x08000000
701
695/* Max physical block we can address w/o extents */ 702/* Max physical block we can address w/o extents */
696#define EXT4_MAX_BLOCK_FILE_PHYS 0xFFFFFFFF 703#define EXT4_MAX_BLOCK_FILE_PHYS 0xFFFFFFFF
697 704
@@ -3258,6 +3265,9 @@ extern int ext4_ext_check_inode(struct inode *inode);
3258extern ext4_lblk_t ext4_ext_next_allocated_block(struct ext4_ext_path *path); 3265extern ext4_lblk_t ext4_ext_next_allocated_block(struct ext4_ext_path *path);
3259extern int ext4_fiemap(struct inode *inode, struct fiemap_extent_info *fieinfo, 3266extern int ext4_fiemap(struct inode *inode, struct fiemap_extent_info *fieinfo,
3260 __u64 start, __u64 len); 3267 __u64 start, __u64 len);
3268extern int ext4_get_es_cache(struct inode *inode,
3269 struct fiemap_extent_info *fieinfo,
3270 __u64 start, __u64 len);
3261extern int ext4_ext_precache(struct inode *inode); 3271extern int ext4_ext_precache(struct inode *inode);
3262extern int ext4_collapse_range(struct inode *inode, loff_t offset, loff_t len); 3272extern int ext4_collapse_range(struct inode *inode, loff_t offset, loff_t len);
3263extern int ext4_insert_range(struct inode *inode, loff_t offset, loff_t len); 3273extern int ext4_insert_range(struct inode *inode, loff_t offset, loff_t len);
diff --git a/fs/ext4/extents.c b/fs/ext4/extents.c
index 92266a2da7d6..0620d495fd8a 100644
--- a/fs/ext4/extents.c
+++ b/fs/ext4/extents.c
@@ -2315,6 +2315,52 @@ static int ext4_fill_fiemap_extents(struct inode *inode,
2315 return err; 2315 return err;
2316} 2316}
2317 2317
2318static int ext4_fill_es_cache_info(struct inode *inode,
2319 ext4_lblk_t block, ext4_lblk_t num,
2320 struct fiemap_extent_info *fieinfo)
2321{
2322 ext4_lblk_t next, end = block + num - 1;
2323 struct extent_status es;
2324 unsigned char blksize_bits = inode->i_sb->s_blocksize_bits;
2325 unsigned int flags;
2326 int err;
2327
2328 while (block <= end) {
2329 next = 0;
2330 flags = 0;
2331 if (!ext4_es_lookup_extent(inode, block, &next, &es))
2332 break;
2333 if (ext4_es_is_unwritten(&es))
2334 flags |= FIEMAP_EXTENT_UNWRITTEN;
2335 if (ext4_es_is_delayed(&es))
2336 flags |= (FIEMAP_EXTENT_DELALLOC |
2337 FIEMAP_EXTENT_UNKNOWN);
2338 if (ext4_es_is_hole(&es))
2339 flags |= EXT4_FIEMAP_EXTENT_HOLE;
2340 if (next == 0)
2341 flags |= FIEMAP_EXTENT_LAST;
2342 if (flags & (FIEMAP_EXTENT_DELALLOC|
2343 EXT4_FIEMAP_EXTENT_HOLE))
2344 es.es_pblk = 0;
2345 else
2346 es.es_pblk = ext4_es_pblock(&es);
2347 err = fiemap_fill_next_extent(fieinfo,
2348 (__u64)es.es_lblk << blksize_bits,
2349 (__u64)es.es_pblk << blksize_bits,
2350 (__u64)es.es_len << blksize_bits,
2351 flags);
2352 if (next == 0)
2353 break;
2354 block = next;
2355 if (err < 0)
2356 return err;
2357 if (err == 1)
2358 return 0;
2359 }
2360 return 0;
2361}
2362
2363
2318/* 2364/*
2319 * ext4_ext_determine_hole - determine hole around given block 2365 * ext4_ext_determine_hole - determine hole around given block
2320 * @inode: inode we lookup in 2366 * @inode: inode we lookup in
@@ -5017,8 +5063,6 @@ static int ext4_find_delayed_extent(struct inode *inode,
5017 5063
5018 return next_del; 5064 return next_del;
5019} 5065}
5020/* fiemap flags we can handle specified here */
5021#define EXT4_FIEMAP_FLAGS (FIEMAP_FLAG_SYNC|FIEMAP_FLAG_XATTR)
5022 5066
5023static int ext4_xattr_fiemap(struct inode *inode, 5067static int ext4_xattr_fiemap(struct inode *inode,
5024 struct fiemap_extent_info *fieinfo) 5068 struct fiemap_extent_info *fieinfo)
@@ -5055,10 +5099,16 @@ static int ext4_xattr_fiemap(struct inode *inode,
5055 return (error < 0 ? error : 0); 5099 return (error < 0 ? error : 0);
5056} 5100}
5057 5101
5058int ext4_fiemap(struct inode *inode, struct fiemap_extent_info *fieinfo, 5102static int _ext4_fiemap(struct inode *inode,
5059 __u64 start, __u64 len) 5103 struct fiemap_extent_info *fieinfo,
5104 __u64 start, __u64 len,
5105 int (*fill)(struct inode *, ext4_lblk_t,
5106 ext4_lblk_t,
5107 struct fiemap_extent_info *))
5060{ 5108{
5061 ext4_lblk_t start_blk; 5109 ext4_lblk_t start_blk;
5110 u32 ext4_fiemap_flags = FIEMAP_FLAG_SYNC|FIEMAP_FLAG_XATTR;
5111
5062 int error = 0; 5112 int error = 0;
5063 5113
5064 if (ext4_has_inline_data(inode)) { 5114 if (ext4_has_inline_data(inode)) {
@@ -5075,14 +5125,18 @@ int ext4_fiemap(struct inode *inode, struct fiemap_extent_info *fieinfo,
5075 error = ext4_ext_precache(inode); 5125 error = ext4_ext_precache(inode);
5076 if (error) 5126 if (error)
5077 return error; 5127 return error;
5128 fieinfo->fi_flags &= ~FIEMAP_FLAG_CACHE;
5078 } 5129 }
5079 5130
5080 /* fallback to generic here if not in extents fmt */ 5131 /* fallback to generic here if not in extents fmt */
5081 if (!(ext4_test_inode_flag(inode, EXT4_INODE_EXTENTS))) 5132 if (!(ext4_test_inode_flag(inode, EXT4_INODE_EXTENTS)) &&
5133 fill == ext4_fill_fiemap_extents)
5082 return generic_block_fiemap(inode, fieinfo, start, len, 5134 return generic_block_fiemap(inode, fieinfo, start, len,
5083 ext4_get_block); 5135 ext4_get_block);
5084 5136
5085 if (fiemap_check_flags(fieinfo, EXT4_FIEMAP_FLAGS)) 5137 if (fill == ext4_fill_es_cache_info)
5138 ext4_fiemap_flags &= FIEMAP_FLAG_XATTR;
5139 if (fiemap_check_flags(fieinfo, ext4_fiemap_flags))
5086 return -EBADR; 5140 return -EBADR;
5087 5141
5088 if (fieinfo->fi_flags & FIEMAP_FLAG_XATTR) { 5142 if (fieinfo->fi_flags & FIEMAP_FLAG_XATTR) {
@@ -5101,12 +5155,36 @@ int ext4_fiemap(struct inode *inode, struct fiemap_extent_info *fieinfo,
5101 * Walk the extent tree gathering extent information 5155 * Walk the extent tree gathering extent information
5102 * and pushing extents back to the user. 5156 * and pushing extents back to the user.
5103 */ 5157 */
5104 error = ext4_fill_fiemap_extents(inode, start_blk, 5158 error = fill(inode, start_blk, len_blks, fieinfo);
5105 len_blks, fieinfo);
5106 } 5159 }
5107 return error; 5160 return error;
5108} 5161}
5109 5162
5163int ext4_fiemap(struct inode *inode, struct fiemap_extent_info *fieinfo,
5164 __u64 start, __u64 len)
5165{
5166 return _ext4_fiemap(inode, fieinfo, start, len,
5167 ext4_fill_fiemap_extents);
5168}
5169
5170int ext4_get_es_cache(struct inode *inode, struct fiemap_extent_info *fieinfo,
5171 __u64 start, __u64 len)
5172{
5173 if (ext4_has_inline_data(inode)) {
5174 int has_inline;
5175
5176 down_read(&EXT4_I(inode)->xattr_sem);
5177 has_inline = ext4_has_inline_data(inode);
5178 up_read(&EXT4_I(inode)->xattr_sem);
5179 if (has_inline)
5180 return 0;
5181 }
5182
5183 return _ext4_fiemap(inode, fieinfo, start, len,
5184 ext4_fill_es_cache_info);
5185}
5186
5187
5110/* 5188/*
5111 * ext4_access_path: 5189 * ext4_access_path:
5112 * Function to access the path buffer for marking it dirty. 5190 * Function to access the path buffer for marking it dirty.
diff --git a/fs/ext4/extents_status.c b/fs/ext4/extents_status.c
index 02cc8eb3eb0e..a959adc59bcd 100644
--- a/fs/ext4/extents_status.c
+++ b/fs/ext4/extents_status.c
@@ -899,6 +899,7 @@ void ext4_es_cache_extent(struct inode *inode, ext4_lblk_t lblk,
899 * Return: 1 on found, 0 on not 899 * Return: 1 on found, 0 on not
900 */ 900 */
901int ext4_es_lookup_extent(struct inode *inode, ext4_lblk_t lblk, 901int ext4_es_lookup_extent(struct inode *inode, ext4_lblk_t lblk,
902 ext4_lblk_t *next_lblk,
902 struct extent_status *es) 903 struct extent_status *es)
903{ 904{
904 struct ext4_es_tree *tree; 905 struct ext4_es_tree *tree;
@@ -948,6 +949,15 @@ out:
948 if (!ext4_es_is_referenced(es1)) 949 if (!ext4_es_is_referenced(es1))
949 ext4_es_set_referenced(es1); 950 ext4_es_set_referenced(es1);
950 stats->es_stats_cache_hits++; 951 stats->es_stats_cache_hits++;
952 if (next_lblk) {
953 node = rb_next(&es1->rb_node);
954 if (node) {
955 es1 = rb_entry(node, struct extent_status,
956 rb_node);
957 *next_lblk = es1->es_lblk;
958 } else
959 *next_lblk = 0;
960 }
951 } else { 961 } else {
952 stats->es_stats_cache_misses++; 962 stats->es_stats_cache_misses++;
953 } 963 }
diff --git a/fs/ext4/extents_status.h b/fs/ext4/extents_status.h
index e16785f431e7..eb56a1289031 100644
--- a/fs/ext4/extents_status.h
+++ b/fs/ext4/extents_status.h
@@ -140,6 +140,7 @@ extern void ext4_es_find_extent_range(struct inode *inode,
140 ext4_lblk_t lblk, ext4_lblk_t end, 140 ext4_lblk_t lblk, ext4_lblk_t end,
141 struct extent_status *es); 141 struct extent_status *es);
142extern int ext4_es_lookup_extent(struct inode *inode, ext4_lblk_t lblk, 142extern int ext4_es_lookup_extent(struct inode *inode, ext4_lblk_t lblk,
143 ext4_lblk_t *next_lblk,
143 struct extent_status *es); 144 struct extent_status *es);
144extern bool ext4_es_scan_range(struct inode *inode, 145extern bool ext4_es_scan_range(struct inode *inode,
145 int (*matching_fn)(struct extent_status *es), 146 int (*matching_fn)(struct extent_status *es),
diff --git a/fs/ext4/inode.c b/fs/ext4/inode.c
index a6523516d681..4b92c7603907 100644
--- a/fs/ext4/inode.c
+++ b/fs/ext4/inode.c
@@ -527,7 +527,7 @@ int ext4_map_blocks(handle_t *handle, struct inode *inode,
527 return -EFSCORRUPTED; 527 return -EFSCORRUPTED;
528 528
529 /* Lookup extent status tree firstly */ 529 /* Lookup extent status tree firstly */
530 if (ext4_es_lookup_extent(inode, map->m_lblk, &es)) { 530 if (ext4_es_lookup_extent(inode, map->m_lblk, NULL, &es)) {
531 if (ext4_es_is_written(&es) || ext4_es_is_unwritten(&es)) { 531 if (ext4_es_is_written(&es) || ext4_es_is_unwritten(&es)) {
532 map->m_pblk = ext4_es_pblock(&es) + 532 map->m_pblk = ext4_es_pblock(&es) +
533 map->m_lblk - es.es_lblk; 533 map->m_lblk - es.es_lblk;
@@ -695,7 +695,7 @@ found:
695 * extent status tree. 695 * extent status tree.
696 */ 696 */
697 if ((flags & EXT4_GET_BLOCKS_PRE_IO) && 697 if ((flags & EXT4_GET_BLOCKS_PRE_IO) &&
698 ext4_es_lookup_extent(inode, map->m_lblk, &es)) { 698 ext4_es_lookup_extent(inode, map->m_lblk, NULL, &es)) {
699 if (ext4_es_is_written(&es)) 699 if (ext4_es_is_written(&es))
700 goto out_sem; 700 goto out_sem;
701 } 701 }
@@ -1868,7 +1868,7 @@ static int ext4_da_map_blocks(struct inode *inode, sector_t iblock,
1868 (unsigned long) map->m_lblk); 1868 (unsigned long) map->m_lblk);
1869 1869
1870 /* Lookup extent status tree firstly */ 1870 /* Lookup extent status tree firstly */
1871 if (ext4_es_lookup_extent(inode, iblock, &es)) { 1871 if (ext4_es_lookup_extent(inode, iblock, NULL, &es)) {
1872 if (ext4_es_is_hole(&es)) { 1872 if (ext4_es_is_hole(&es)) {
1873 retval = 0; 1873 retval = 0;
1874 down_read(&EXT4_I(inode)->i_data_sem); 1874 down_read(&EXT4_I(inode)->i_data_sem);
diff --git a/fs/ext4/ioctl.c b/fs/ext4/ioctl.c
index ffb7bde4900d..d6242b7b8718 100644
--- a/fs/ext4/ioctl.c
+++ b/fs/ext4/ioctl.c
@@ -745,6 +745,74 @@ static void ext4_fill_fsxattr(struct inode *inode, struct fsxattr *fa)
745 fa->fsx_projid = from_kprojid(&init_user_ns, ei->i_projid); 745 fa->fsx_projid = from_kprojid(&init_user_ns, ei->i_projid);
746} 746}
747 747
748/* copied from fs/ioctl.c */
749static int fiemap_check_ranges(struct super_block *sb,
750 u64 start, u64 len, u64 *new_len)
751{
752 u64 maxbytes = (u64) sb->s_maxbytes;
753
754 *new_len = len;
755
756 if (len == 0)
757 return -EINVAL;
758
759 if (start > maxbytes)
760 return -EFBIG;
761
762 /*
763 * Shrink request scope to what the fs can actually handle.
764 */
765 if (len > maxbytes || (maxbytes - len) < start)
766 *new_len = maxbytes - start;
767
768 return 0;
769}
770
771/* So that the fiemap access checks can't overflow on 32 bit machines. */
772#define FIEMAP_MAX_EXTENTS (UINT_MAX / sizeof(struct fiemap_extent))
773
774static int ext4_ioctl_get_es_cache(struct file *filp, unsigned long arg)
775{
776 struct fiemap fiemap;
777 struct fiemap __user *ufiemap = (struct fiemap __user *) arg;
778 struct fiemap_extent_info fieinfo = { 0, };
779 struct inode *inode = file_inode(filp);
780 struct super_block *sb = inode->i_sb;
781 u64 len;
782 int error;
783
784 if (copy_from_user(&fiemap, ufiemap, sizeof(fiemap)))
785 return -EFAULT;
786
787 if (fiemap.fm_extent_count > FIEMAP_MAX_EXTENTS)
788 return -EINVAL;
789
790 error = fiemap_check_ranges(sb, fiemap.fm_start, fiemap.fm_length,
791 &len);
792 if (error)
793 return error;
794
795 fieinfo.fi_flags = fiemap.fm_flags;
796 fieinfo.fi_extents_max = fiemap.fm_extent_count;
797 fieinfo.fi_extents_start = ufiemap->fm_extents;
798
799 if (fiemap.fm_extent_count != 0 &&
800 !access_ok(fieinfo.fi_extents_start,
801 fieinfo.fi_extents_max * sizeof(struct fiemap_extent)))
802 return -EFAULT;
803
804 if (fieinfo.fi_flags & FIEMAP_FLAG_SYNC)
805 filemap_write_and_wait(inode->i_mapping);
806
807 error = ext4_get_es_cache(inode, &fieinfo, fiemap.fm_start, len);
808 fiemap.fm_flags = fieinfo.fi_flags;
809 fiemap.fm_mapped_extents = fieinfo.fi_extents_mapped;
810 if (copy_to_user(ufiemap, &fiemap, sizeof(fiemap)))
811 error = -EFAULT;
812
813 return error;
814}
815
748long ext4_ioctl(struct file *filp, unsigned int cmd, unsigned long arg) 816long ext4_ioctl(struct file *filp, unsigned int cmd, unsigned long arg)
749{ 817{
750 struct inode *inode = file_inode(filp); 818 struct inode *inode = file_inode(filp);
@@ -1139,6 +1207,9 @@ resizefs_out:
1139 return put_user(state, (__u32 __user *) arg); 1207 return put_user(state, (__u32 __user *) arg);
1140 } 1208 }
1141 1209
1210 case EXT4_IOC_GET_ES_CACHE:
1211 return ext4_ioctl_get_es_cache(filp, arg);
1212
1142 case EXT4_IOC_FSGETXATTR: 1213 case EXT4_IOC_FSGETXATTR:
1143 { 1214 {
1144 struct fsxattr fa; 1215 struct fsxattr fa;
@@ -1259,6 +1330,7 @@ long ext4_compat_ioctl(struct file *file, unsigned int cmd, unsigned long arg)
1259 case FS_IOC_GETFSMAP: 1330 case FS_IOC_GETFSMAP:
1260 case EXT4_IOC_CLEAR_ES_CACHE: 1331 case EXT4_IOC_CLEAR_ES_CACHE:
1261 case EXT4_IOC_GETSTATE: 1332 case EXT4_IOC_GETSTATE:
1333 case EXT4_IOC_GET_ES_CACHE:
1262 break; 1334 break;
1263 default: 1335 default:
1264 return -ENOIOCTLCMD; 1336 return -ENOIOCTLCMD;