Commit 82e11e857be3ffd2a0a952c9db8aa2379e2b9e44
Committed by
Linus Torvalds
1 parent
2cc88f3a5f
Exists in
master
and in
13 other branches
nilfs2: add nilfs_sufile_trim_fs to trim clean segs
Add nilfs_sufile_trim_fs(), which takes an fstrim_range structure and calls blkdev_issue_discard for every clean segment in the specified range. The range is truncated to file system block boundaries. Signed-off-by: Andreas Rohner <andreas.rohner@gmx.net> Signed-off-by: Ryusuke Konishi <konishi.ryusuke@lab.ntt.co.jp> Signed-off-by: Andrew Morton <akpm@linux-foundation.org> Signed-off-by: Linus Torvalds <torvalds@linux-foundation.org>
Showing 2 changed files with 153 additions and 0 deletions Side-by-side Diff
fs/nilfs2/sufile.c
... | ... | @@ -1001,6 +1001,158 @@ |
1001 | 1001 | } |
1002 | 1002 | |
1003 | 1003 | /** |
1004 | + * nilfs_sufile_trim_fs() - trim ioctl handle function | |
1005 | + * @sufile: inode of segment usage file | |
1006 | + * @range: fstrim_range structure | |
1007 | + * | |
1008 | + * start: First Byte to trim | |
1009 | + * len: number of Bytes to trim from start | |
1010 | + * minlen: minimum extent length in Bytes | |
1011 | + * | |
1012 | + * Decription: nilfs_sufile_trim_fs goes through all segments containing bytes | |
1013 | + * from start to start+len. start is rounded up to the next block boundary | |
1014 | + * and start+len is rounded down. For each clean segment blkdev_issue_discard | |
1015 | + * function is invoked. | |
1016 | + * | |
1017 | + * Return Value: On success, 0 is returned or negative error code, otherwise. | |
1018 | + */ | |
1019 | +int nilfs_sufile_trim_fs(struct inode *sufile, struct fstrim_range *range) | |
1020 | +{ | |
1021 | + struct the_nilfs *nilfs = sufile->i_sb->s_fs_info; | |
1022 | + struct buffer_head *su_bh; | |
1023 | + struct nilfs_segment_usage *su; | |
1024 | + void *kaddr; | |
1025 | + size_t n, i, susz = NILFS_MDT(sufile)->mi_entry_size; | |
1026 | + sector_t seg_start, seg_end, start_block, end_block; | |
1027 | + sector_t start = 0, nblocks = 0; | |
1028 | + u64 segnum, segnum_end, minlen, len, max_blocks, ndiscarded = 0; | |
1029 | + int ret = 0; | |
1030 | + unsigned int sects_per_block; | |
1031 | + | |
1032 | + sects_per_block = (1 << nilfs->ns_blocksize_bits) / | |
1033 | + bdev_logical_block_size(nilfs->ns_bdev); | |
1034 | + len = range->len >> nilfs->ns_blocksize_bits; | |
1035 | + minlen = range->minlen >> nilfs->ns_blocksize_bits; | |
1036 | + max_blocks = ((u64)nilfs->ns_nsegments * nilfs->ns_blocks_per_segment); | |
1037 | + | |
1038 | + if (!len || range->start >= max_blocks << nilfs->ns_blocksize_bits) | |
1039 | + return -EINVAL; | |
1040 | + | |
1041 | + start_block = (range->start + nilfs->ns_blocksize - 1) >> | |
1042 | + nilfs->ns_blocksize_bits; | |
1043 | + | |
1044 | + /* | |
1045 | + * range->len can be very large (actually, it is set to | |
1046 | + * ULLONG_MAX by default) - truncate upper end of the range | |
1047 | + * carefully so as not to overflow. | |
1048 | + */ | |
1049 | + if (max_blocks - start_block < len) | |
1050 | + end_block = max_blocks - 1; | |
1051 | + else | |
1052 | + end_block = start_block + len - 1; | |
1053 | + | |
1054 | + segnum = nilfs_get_segnum_of_block(nilfs, start_block); | |
1055 | + segnum_end = nilfs_get_segnum_of_block(nilfs, end_block); | |
1056 | + | |
1057 | + down_read(&NILFS_MDT(sufile)->mi_sem); | |
1058 | + | |
1059 | + while (segnum <= segnum_end) { | |
1060 | + n = nilfs_sufile_segment_usages_in_block(sufile, segnum, | |
1061 | + segnum_end); | |
1062 | + | |
1063 | + ret = nilfs_sufile_get_segment_usage_block(sufile, segnum, 0, | |
1064 | + &su_bh); | |
1065 | + if (ret < 0) { | |
1066 | + if (ret != -ENOENT) | |
1067 | + goto out_sem; | |
1068 | + /* hole */ | |
1069 | + segnum += n; | |
1070 | + continue; | |
1071 | + } | |
1072 | + | |
1073 | + kaddr = kmap_atomic(su_bh->b_page); | |
1074 | + su = nilfs_sufile_block_get_segment_usage(sufile, segnum, | |
1075 | + su_bh, kaddr); | |
1076 | + for (i = 0; i < n; ++i, ++segnum, su = (void *)su + susz) { | |
1077 | + if (!nilfs_segment_usage_clean(su)) | |
1078 | + continue; | |
1079 | + | |
1080 | + nilfs_get_segment_range(nilfs, segnum, &seg_start, | |
1081 | + &seg_end); | |
1082 | + | |
1083 | + if (!nblocks) { | |
1084 | + /* start new extent */ | |
1085 | + start = seg_start; | |
1086 | + nblocks = seg_end - seg_start + 1; | |
1087 | + continue; | |
1088 | + } | |
1089 | + | |
1090 | + if (start + nblocks == seg_start) { | |
1091 | + /* add to previous extent */ | |
1092 | + nblocks += seg_end - seg_start + 1; | |
1093 | + continue; | |
1094 | + } | |
1095 | + | |
1096 | + /* discard previous extent */ | |
1097 | + if (start < start_block) { | |
1098 | + nblocks -= start_block - start; | |
1099 | + start = start_block; | |
1100 | + } | |
1101 | + | |
1102 | + if (nblocks >= minlen) { | |
1103 | + kunmap_atomic(kaddr); | |
1104 | + | |
1105 | + ret = blkdev_issue_discard(nilfs->ns_bdev, | |
1106 | + start * sects_per_block, | |
1107 | + nblocks * sects_per_block, | |
1108 | + GFP_NOFS, 0); | |
1109 | + if (ret < 0) { | |
1110 | + put_bh(su_bh); | |
1111 | + goto out_sem; | |
1112 | + } | |
1113 | + | |
1114 | + ndiscarded += nblocks; | |
1115 | + kaddr = kmap_atomic(su_bh->b_page); | |
1116 | + su = nilfs_sufile_block_get_segment_usage( | |
1117 | + sufile, segnum, su_bh, kaddr); | |
1118 | + } | |
1119 | + | |
1120 | + /* start new extent */ | |
1121 | + start = seg_start; | |
1122 | + nblocks = seg_end - seg_start + 1; | |
1123 | + } | |
1124 | + kunmap_atomic(kaddr); | |
1125 | + put_bh(su_bh); | |
1126 | + } | |
1127 | + | |
1128 | + | |
1129 | + if (nblocks) { | |
1130 | + /* discard last extent */ | |
1131 | + if (start < start_block) { | |
1132 | + nblocks -= start_block - start; | |
1133 | + start = start_block; | |
1134 | + } | |
1135 | + if (start + nblocks > end_block + 1) | |
1136 | + nblocks = end_block - start + 1; | |
1137 | + | |
1138 | + if (nblocks >= minlen) { | |
1139 | + ret = blkdev_issue_discard(nilfs->ns_bdev, | |
1140 | + start * sects_per_block, | |
1141 | + nblocks * sects_per_block, | |
1142 | + GFP_NOFS, 0); | |
1143 | + if (!ret) | |
1144 | + ndiscarded += nblocks; | |
1145 | + } | |
1146 | + } | |
1147 | + | |
1148 | +out_sem: | |
1149 | + up_read(&NILFS_MDT(sufile)->mi_sem); | |
1150 | + | |
1151 | + range->len = ndiscarded << nilfs->ns_blocksize_bits; | |
1152 | + return ret; | |
1153 | +} | |
1154 | + | |
1155 | +/** | |
1004 | 1156 | * nilfs_sufile_read - read or get sufile inode |
1005 | 1157 | * @sb: super block instance |
1006 | 1158 | * @susize: size of a segment usage entry |
fs/nilfs2/sufile.h
... | ... | @@ -66,6 +66,7 @@ |
66 | 66 | int nilfs_sufile_resize(struct inode *sufile, __u64 newnsegs); |
67 | 67 | int nilfs_sufile_read(struct super_block *sb, size_t susize, |
68 | 68 | struct nilfs_inode *raw_inode, struct inode **inodep); |
69 | +int nilfs_sufile_trim_fs(struct inode *sufile, struct fstrim_range *range); | |
69 | 70 | |
70 | 71 | /** |
71 | 72 | * nilfs_sufile_scrap - make a segment garbage |