diff options
Diffstat (limited to 'fs/nilfs2/sufile.c')
| -rw-r--r-- | fs/nilfs2/sufile.c | 295 |
1 files changed, 295 insertions, 0 deletions
diff --git a/fs/nilfs2/sufile.c b/fs/nilfs2/sufile.c index 3127e9f438a7..2a869c35c362 100644 --- a/fs/nilfs2/sufile.c +++ b/fs/nilfs2/sufile.c | |||
| @@ -870,6 +870,289 @@ ssize_t nilfs_sufile_get_suinfo(struct inode *sufile, __u64 segnum, void *buf, | |||
| 870 | } | 870 | } |
| 871 | 871 | ||
| 872 | /** | 872 | /** |
| 873 | * nilfs_sufile_set_suinfo - sets segment usage info | ||
| 874 | * @sufile: inode of segment usage file | ||
| 875 | * @buf: array of suinfo_update | ||
| 876 | * @supsz: byte size of suinfo_update | ||
| 877 | * @nsup: size of suinfo_update array | ||
| 878 | * | ||
| 879 | * Description: Takes an array of nilfs_suinfo_update structs and updates | ||
| 880 | * segment usage accordingly. Only the fields indicated by the sup_flags | ||
| 881 | * are updated. | ||
| 882 | * | ||
| 883 | * Return Value: On success, 0 is returned. On error, one of the | ||
| 884 | * following negative error codes is returned. | ||
| 885 | * | ||
| 886 | * %-EIO - I/O error. | ||
| 887 | * | ||
| 888 | * %-ENOMEM - Insufficient amount of memory available. | ||
| 889 | * | ||
| 890 | * %-EINVAL - Invalid values in input (segment number, flags or nblocks) | ||
| 891 | */ | ||
| 892 | ssize_t nilfs_sufile_set_suinfo(struct inode *sufile, void *buf, | ||
| 893 | unsigned supsz, size_t nsup) | ||
| 894 | { | ||
| 895 | struct the_nilfs *nilfs = sufile->i_sb->s_fs_info; | ||
| 896 | struct buffer_head *header_bh, *bh; | ||
| 897 | struct nilfs_suinfo_update *sup, *supend = buf + supsz * nsup; | ||
| 898 | struct nilfs_segment_usage *su; | ||
| 899 | void *kaddr; | ||
| 900 | unsigned long blkoff, prev_blkoff; | ||
| 901 | int cleansi, cleansu, dirtysi, dirtysu; | ||
| 902 | long ncleaned = 0, ndirtied = 0; | ||
| 903 | int ret = 0; | ||
| 904 | |||
| 905 | if (unlikely(nsup == 0)) | ||
| 906 | return ret; | ||
| 907 | |||
| 908 | for (sup = buf; sup < supend; sup = (void *)sup + supsz) { | ||
| 909 | if (sup->sup_segnum >= nilfs->ns_nsegments | ||
| 910 | || (sup->sup_flags & | ||
| 911 | (~0UL << __NR_NILFS_SUINFO_UPDATE_FIELDS)) | ||
| 912 | || (nilfs_suinfo_update_nblocks(sup) && | ||
| 913 | sup->sup_sui.sui_nblocks > | ||
| 914 | nilfs->ns_blocks_per_segment)) | ||
| 915 | return -EINVAL; | ||
| 916 | } | ||
| 917 | |||
| 918 | down_write(&NILFS_MDT(sufile)->mi_sem); | ||
| 919 | |||
| 920 | ret = nilfs_sufile_get_header_block(sufile, &header_bh); | ||
| 921 | if (ret < 0) | ||
| 922 | goto out_sem; | ||
| 923 | |||
| 924 | sup = buf; | ||
| 925 | blkoff = nilfs_sufile_get_blkoff(sufile, sup->sup_segnum); | ||
| 926 | ret = nilfs_mdt_get_block(sufile, blkoff, 1, NULL, &bh); | ||
| 927 | if (ret < 0) | ||
| 928 | goto out_header; | ||
| 929 | |||
| 930 | for (;;) { | ||
| 931 | kaddr = kmap_atomic(bh->b_page); | ||
| 932 | su = nilfs_sufile_block_get_segment_usage( | ||
| 933 | sufile, sup->sup_segnum, bh, kaddr); | ||
| 934 | |||
| 935 | if (nilfs_suinfo_update_lastmod(sup)) | ||
| 936 | su->su_lastmod = cpu_to_le64(sup->sup_sui.sui_lastmod); | ||
| 937 | |||
| 938 | if (nilfs_suinfo_update_nblocks(sup)) | ||
| 939 | su->su_nblocks = cpu_to_le32(sup->sup_sui.sui_nblocks); | ||
| 940 | |||
| 941 | if (nilfs_suinfo_update_flags(sup)) { | ||
| 942 | /* | ||
| 943 | * Active flag is a virtual flag projected by running | ||
| 944 | * nilfs kernel code - drop it not to write it to | ||
| 945 | * disk. | ||
| 946 | */ | ||
| 947 | sup->sup_sui.sui_flags &= | ||
| 948 | ~(1UL << NILFS_SEGMENT_USAGE_ACTIVE); | ||
| 949 | |||
| 950 | cleansi = nilfs_suinfo_clean(&sup->sup_sui); | ||
| 951 | cleansu = nilfs_segment_usage_clean(su); | ||
| 952 | dirtysi = nilfs_suinfo_dirty(&sup->sup_sui); | ||
| 953 | dirtysu = nilfs_segment_usage_dirty(su); | ||
| 954 | |||
| 955 | if (cleansi && !cleansu) | ||
| 956 | ++ncleaned; | ||
| 957 | else if (!cleansi && cleansu) | ||
| 958 | --ncleaned; | ||
| 959 | |||
| 960 | if (dirtysi && !dirtysu) | ||
| 961 | ++ndirtied; | ||
| 962 | else if (!dirtysi && dirtysu) | ||
| 963 | --ndirtied; | ||
| 964 | |||
| 965 | su->su_flags = cpu_to_le32(sup->sup_sui.sui_flags); | ||
| 966 | } | ||
| 967 | |||
| 968 | kunmap_atomic(kaddr); | ||
| 969 | |||
| 970 | sup = (void *)sup + supsz; | ||
| 971 | if (sup >= supend) | ||
| 972 | break; | ||
| 973 | |||
| 974 | prev_blkoff = blkoff; | ||
| 975 | blkoff = nilfs_sufile_get_blkoff(sufile, sup->sup_segnum); | ||
| 976 | if (blkoff == prev_blkoff) | ||
| 977 | continue; | ||
| 978 | |||
| 979 | /* get different block */ | ||
| 980 | mark_buffer_dirty(bh); | ||
| 981 | put_bh(bh); | ||
| 982 | ret = nilfs_mdt_get_block(sufile, blkoff, 1, NULL, &bh); | ||
| 983 | if (unlikely(ret < 0)) | ||
| 984 | goto out_mark; | ||
| 985 | } | ||
| 986 | mark_buffer_dirty(bh); | ||
| 987 | put_bh(bh); | ||
| 988 | |||
| 989 | out_mark: | ||
| 990 | if (ncleaned || ndirtied) { | ||
| 991 | nilfs_sufile_mod_counter(header_bh, (u64)ncleaned, | ||
| 992 | (u64)ndirtied); | ||
| 993 | NILFS_SUI(sufile)->ncleansegs += ncleaned; | ||
| 994 | } | ||
| 995 | nilfs_mdt_mark_dirty(sufile); | ||
| 996 | out_header: | ||
| 997 | put_bh(header_bh); | ||
| 998 | out_sem: | ||
| 999 | up_write(&NILFS_MDT(sufile)->mi_sem); | ||
| 1000 | return ret; | ||
| 1001 | } | ||
| 1002 | |||
| 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 | /** | ||
| 873 | * nilfs_sufile_read - read or get sufile inode | 1156 | * nilfs_sufile_read - read or get sufile inode |
| 874 | * @sb: super block instance | 1157 | * @sb: super block instance |
| 875 | * @susize: size of a segment usage entry | 1158 | * @susize: size of a segment usage entry |
| @@ -886,6 +1169,18 @@ int nilfs_sufile_read(struct super_block *sb, size_t susize, | |||
| 886 | void *kaddr; | 1169 | void *kaddr; |
| 887 | int err; | 1170 | int err; |
| 888 | 1171 | ||
| 1172 | if (susize > sb->s_blocksize) { | ||
| 1173 | printk(KERN_ERR | ||
| 1174 | "NILFS: too large segment usage size: %zu bytes.\n", | ||
| 1175 | susize); | ||
| 1176 | return -EINVAL; | ||
| 1177 | } else if (susize < NILFS_MIN_SEGMENT_USAGE_SIZE) { | ||
| 1178 | printk(KERN_ERR | ||
| 1179 | "NILFS: too small segment usage size: %zu bytes.\n", | ||
| 1180 | susize); | ||
| 1181 | return -EINVAL; | ||
| 1182 | } | ||
| 1183 | |||
| 889 | sufile = nilfs_iget_locked(sb, NULL, NILFS_SUFILE_INO); | 1184 | sufile = nilfs_iget_locked(sb, NULL, NILFS_SUFILE_INO); |
| 890 | if (unlikely(!sufile)) | 1185 | if (unlikely(!sufile)) |
| 891 | return -ENOMEM; | 1186 | return -ENOMEM; |
