aboutsummaryrefslogtreecommitdiffstats
path: root/fs/nilfs2/sufile.c
diff options
context:
space:
mode:
authorAndreas Rohner <andreas.rohner@gmx.net>2014-04-03 17:50:29 -0400
committerLinus Torvalds <torvalds@linux-foundation.org>2014-04-03 19:21:25 -0400
commit82e11e857be3ffd2a0a952c9db8aa2379e2b9e44 (patch)
treecec1b7aeec361bf287c359d0a2c00c1c3315ffe0 /fs/nilfs2/sufile.c
parent2cc88f3a5f16ae9a3c8f54de1b2fd4a397b36075 (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.c152
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 */
1019int 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
1148out_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