diff options
author | Andreas Rohner <andreas.rohner@gmx.net> | 2014-04-03 17:50:29 -0400 |
---|---|---|
committer | Linus Torvalds <torvalds@linux-foundation.org> | 2014-04-03 19:21:25 -0400 |
commit | 82e11e857be3ffd2a0a952c9db8aa2379e2b9e44 (patch) | |
tree | cec1b7aeec361bf287c359d0a2c00c1c3315ffe0 /fs/nilfs2/sufile.c | |
parent | 2cc88f3a5f16ae9a3c8f54de1b2fd4a397b36075 (diff) |
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>
Diffstat (limited to 'fs/nilfs2/sufile.c')
-rw-r--r-- | fs/nilfs2/sufile.c | 152 |
1 files changed, 152 insertions, 0 deletions
diff --git a/fs/nilfs2/sufile.c b/fs/nilfs2/sufile.c index 5628b99d454e..84e384dae663 100644 --- a/fs/nilfs2/sufile.c +++ b/fs/nilfs2/sufile.c | |||
@@ -1001,6 +1001,158 @@ ssize_t nilfs_sufile_set_suinfo(struct inode *sufile, void *buf, | |||
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 | * nilfs_sufile_read - read or get sufile inode | 1156 | * nilfs_sufile_read - read or get sufile inode |
1005 | * @sb: super block instance | 1157 | * @sb: super block instance |
1006 | * @susize: size of a segment usage entry | 1158 | * @susize: size of a segment usage entry |