Commit 82e11e857be3ffd2a0a952c9db8aa2379e2b9e44

Authored by Andreas Rohner
Committed by Linus Torvalds
1 parent 2cc88f3a5f

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

... ... @@ -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
... ... @@ -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