diff options
author | Steven Whitehouse <swhiteho@redhat.com> | 2010-08-11 04:37:53 -0400 |
---|---|---|
committer | Steven Whitehouse <swhiteho@redhat.com> | 2010-09-20 06:18:16 -0400 |
commit | ff8f33c8b30d7b7efdcf2548c7f6e64db6a89b29 (patch) | |
tree | a21bad4d2b48f743dd96277b1c966c35aefe1ce2 /fs/gfs2/bmap.c | |
parent | 2422084a94fcd5038406261b331672a13c92c050 (diff) |
GFS2: New truncate sequence
This updates GFS2's truncate code to use the new truncate
sequence correctly. This is a stepping stone to being
able to remove ip->i_disksize in favour of using i_size
everywhere now that the two sizes are always identical.
Signed-off-by: Steven Whitehouse <swhiteho@redhat.com>
Cc: Nick Piggin <npiggin@suse.de>
Cc: Christoph Hellwig <hch@lst.de>
Diffstat (limited to 'fs/gfs2/bmap.c')
-rw-r--r-- | fs/gfs2/bmap.c | 247 |
1 files changed, 119 insertions, 128 deletions
diff --git a/fs/gfs2/bmap.c b/fs/gfs2/bmap.c index 6f482809d1a3..20b971ad4973 100644 --- a/fs/gfs2/bmap.c +++ b/fs/gfs2/bmap.c | |||
@@ -50,7 +50,7 @@ struct strip_mine { | |||
50 | * @ip: the inode | 50 | * @ip: the inode |
51 | * @dibh: the dinode buffer | 51 | * @dibh: the dinode buffer |
52 | * @block: the block number that was allocated | 52 | * @block: the block number that was allocated |
53 | * @private: any locked page held by the caller process | 53 | * @page: The (optional) page. This is looked up if @page is NULL |
54 | * | 54 | * |
55 | * Returns: errno | 55 | * Returns: errno |
56 | */ | 56 | */ |
@@ -109,8 +109,7 @@ static int gfs2_unstuffer_page(struct gfs2_inode *ip, struct buffer_head *dibh, | |||
109 | /** | 109 | /** |
110 | * gfs2_unstuff_dinode - Unstuff a dinode when the data has grown too big | 110 | * gfs2_unstuff_dinode - Unstuff a dinode when the data has grown too big |
111 | * @ip: The GFS2 inode to unstuff | 111 | * @ip: The GFS2 inode to unstuff |
112 | * @unstuffer: the routine that handles unstuffing a non-zero length file | 112 | * @page: The (optional) page. This is looked up if the @page is NULL |
113 | * @private: private data for the unstuffer | ||
114 | * | 113 | * |
115 | * This routine unstuffs a dinode and returns it to a "normal" state such | 114 | * This routine unstuffs a dinode and returns it to a "normal" state such |
116 | * that the height can be grown in the traditional way. | 115 | * that the height can be grown in the traditional way. |
@@ -885,83 +884,14 @@ out: | |||
885 | } | 884 | } |
886 | 885 | ||
887 | /** | 886 | /** |
888 | * do_grow - Make a file look bigger than it is | ||
889 | * @ip: the inode | ||
890 | * @size: the size to set the file to | ||
891 | * | ||
892 | * Called with an exclusive lock on @ip. | ||
893 | * | ||
894 | * Returns: errno | ||
895 | */ | ||
896 | |||
897 | static int do_grow(struct gfs2_inode *ip, u64 size) | ||
898 | { | ||
899 | struct gfs2_sbd *sdp = GFS2_SB(&ip->i_inode); | ||
900 | struct gfs2_alloc *al; | ||
901 | struct buffer_head *dibh; | ||
902 | int error; | ||
903 | |||
904 | al = gfs2_alloc_get(ip); | ||
905 | if (!al) | ||
906 | return -ENOMEM; | ||
907 | |||
908 | error = gfs2_quota_lock_check(ip); | ||
909 | if (error) | ||
910 | goto out; | ||
911 | |||
912 | al->al_requested = sdp->sd_max_height + RES_DATA; | ||
913 | |||
914 | error = gfs2_inplace_reserve(ip); | ||
915 | if (error) | ||
916 | goto out_gunlock_q; | ||
917 | |||
918 | error = gfs2_trans_begin(sdp, | ||
919 | sdp->sd_max_height + al->al_rgd->rd_length + | ||
920 | RES_JDATA + RES_DINODE + RES_STATFS + RES_QUOTA, 0); | ||
921 | if (error) | ||
922 | goto out_ipres; | ||
923 | |||
924 | error = gfs2_meta_inode_buffer(ip, &dibh); | ||
925 | if (error) | ||
926 | goto out_end_trans; | ||
927 | |||
928 | if (size > sdp->sd_sb.sb_bsize - sizeof(struct gfs2_dinode)) { | ||
929 | if (gfs2_is_stuffed(ip)) { | ||
930 | error = gfs2_unstuff_dinode(ip, NULL); | ||
931 | if (error) | ||
932 | goto out_brelse; | ||
933 | } | ||
934 | } | ||
935 | |||
936 | ip->i_disksize = size; | ||
937 | ip->i_inode.i_mtime = ip->i_inode.i_ctime = CURRENT_TIME; | ||
938 | gfs2_trans_add_bh(ip->i_gl, dibh, 1); | ||
939 | gfs2_dinode_out(ip, dibh->b_data); | ||
940 | |||
941 | out_brelse: | ||
942 | brelse(dibh); | ||
943 | out_end_trans: | ||
944 | gfs2_trans_end(sdp); | ||
945 | out_ipres: | ||
946 | gfs2_inplace_release(ip); | ||
947 | out_gunlock_q: | ||
948 | gfs2_quota_unlock(ip); | ||
949 | out: | ||
950 | gfs2_alloc_put(ip); | ||
951 | return error; | ||
952 | } | ||
953 | |||
954 | |||
955 | /** | ||
956 | * gfs2_block_truncate_page - Deal with zeroing out data for truncate | 887 | * gfs2_block_truncate_page - Deal with zeroing out data for truncate |
957 | * | 888 | * |
958 | * This is partly borrowed from ext3. | 889 | * This is partly borrowed from ext3. |
959 | */ | 890 | */ |
960 | static int gfs2_block_truncate_page(struct address_space *mapping) | 891 | static int gfs2_block_truncate_page(struct address_space *mapping, loff_t from) |
961 | { | 892 | { |
962 | struct inode *inode = mapping->host; | 893 | struct inode *inode = mapping->host; |
963 | struct gfs2_inode *ip = GFS2_I(inode); | 894 | struct gfs2_inode *ip = GFS2_I(inode); |
964 | loff_t from = inode->i_size; | ||
965 | unsigned long index = from >> PAGE_CACHE_SHIFT; | 895 | unsigned long index = from >> PAGE_CACHE_SHIFT; |
966 | unsigned offset = from & (PAGE_CACHE_SIZE-1); | 896 | unsigned offset = from & (PAGE_CACHE_SIZE-1); |
967 | unsigned blocksize, iblock, length, pos; | 897 | unsigned blocksize, iblock, length, pos; |
@@ -1023,9 +953,11 @@ unlock: | |||
1023 | return err; | 953 | return err; |
1024 | } | 954 | } |
1025 | 955 | ||
1026 | static int trunc_start(struct gfs2_inode *ip, u64 size) | 956 | static int trunc_start(struct inode *inode, u64 oldsize, u64 newsize) |
1027 | { | 957 | { |
1028 | struct gfs2_sbd *sdp = GFS2_SB(&ip->i_inode); | 958 | struct gfs2_inode *ip = GFS2_I(inode); |
959 | struct gfs2_sbd *sdp = GFS2_SB(inode); | ||
960 | struct address_space *mapping = inode->i_mapping; | ||
1029 | struct buffer_head *dibh; | 961 | struct buffer_head *dibh; |
1030 | int journaled = gfs2_is_jdata(ip); | 962 | int journaled = gfs2_is_jdata(ip); |
1031 | int error; | 963 | int error; |
@@ -1039,31 +971,27 @@ static int trunc_start(struct gfs2_inode *ip, u64 size) | |||
1039 | if (error) | 971 | if (error) |
1040 | goto out; | 972 | goto out; |
1041 | 973 | ||
974 | gfs2_trans_add_bh(ip->i_gl, dibh, 1); | ||
975 | |||
1042 | if (gfs2_is_stuffed(ip)) { | 976 | if (gfs2_is_stuffed(ip)) { |
1043 | u64 dsize = size + sizeof(struct gfs2_dinode); | 977 | gfs2_buffer_clear_tail(dibh, sizeof(struct gfs2_dinode) + newsize); |
1044 | ip->i_disksize = size; | ||
1045 | ip->i_inode.i_mtime = ip->i_inode.i_ctime = CURRENT_TIME; | ||
1046 | gfs2_trans_add_bh(ip->i_gl, dibh, 1); | ||
1047 | gfs2_dinode_out(ip, dibh->b_data); | ||
1048 | if (dsize > dibh->b_size) | ||
1049 | dsize = dibh->b_size; | ||
1050 | gfs2_buffer_clear_tail(dibh, dsize); | ||
1051 | error = 1; | ||
1052 | } else { | 978 | } else { |
1053 | if (size & (u64)(sdp->sd_sb.sb_bsize - 1)) | 979 | if (newsize & (u64)(sdp->sd_sb.sb_bsize - 1)) { |
1054 | error = gfs2_block_truncate_page(ip->i_inode.i_mapping); | 980 | error = gfs2_block_truncate_page(mapping, newsize); |
1055 | 981 | if (error) | |
1056 | if (!error) { | 982 | goto out_brelse; |
1057 | ip->i_disksize = size; | ||
1058 | ip->i_inode.i_mtime = ip->i_inode.i_ctime = CURRENT_TIME; | ||
1059 | ip->i_diskflags |= GFS2_DIF_TRUNC_IN_PROG; | ||
1060 | gfs2_trans_add_bh(ip->i_gl, dibh, 1); | ||
1061 | gfs2_dinode_out(ip, dibh->b_data); | ||
1062 | } | 983 | } |
984 | ip->i_diskflags |= GFS2_DIF_TRUNC_IN_PROG; | ||
1063 | } | 985 | } |
1064 | 986 | ||
1065 | brelse(dibh); | 987 | i_size_write(inode, newsize); |
988 | ip->i_disksize = newsize; | ||
989 | ip->i_inode.i_mtime = ip->i_inode.i_ctime = CURRENT_TIME; | ||
990 | gfs2_dinode_out(ip, dibh->b_data); | ||
1066 | 991 | ||
992 | truncate_pagecache(inode, oldsize, newsize); | ||
993 | out_brelse: | ||
994 | brelse(dibh); | ||
1067 | out: | 995 | out: |
1068 | gfs2_trans_end(sdp); | 996 | gfs2_trans_end(sdp); |
1069 | return error; | 997 | return error; |
@@ -1143,86 +1071,149 @@ out: | |||
1143 | 1071 | ||
1144 | /** | 1072 | /** |
1145 | * do_shrink - make a file smaller | 1073 | * do_shrink - make a file smaller |
1146 | * @ip: the inode | 1074 | * @inode: the inode |
1147 | * @size: the size to make the file | 1075 | * @oldsize: the current inode size |
1148 | * @truncator: function to truncate the last partial block | 1076 | * @newsize: the size to make the file |
1149 | * | 1077 | * |
1150 | * Called with an exclusive lock on @ip. | 1078 | * Called with an exclusive lock on @inode. The @size must |
1079 | * be equal to or smaller than the current inode size. | ||
1151 | * | 1080 | * |
1152 | * Returns: errno | 1081 | * Returns: errno |
1153 | */ | 1082 | */ |
1154 | 1083 | ||
1155 | static int do_shrink(struct gfs2_inode *ip, u64 size) | 1084 | static int do_shrink(struct inode *inode, u64 oldsize, u64 newsize) |
1156 | { | 1085 | { |
1086 | struct gfs2_inode *ip = GFS2_I(inode); | ||
1157 | int error; | 1087 | int error; |
1158 | 1088 | ||
1159 | error = trunc_start(ip, size); | 1089 | error = trunc_start(inode, oldsize, newsize); |
1160 | if (error < 0) | 1090 | if (error < 0) |
1161 | return error; | 1091 | return error; |
1162 | if (error > 0) | 1092 | if (gfs2_is_stuffed(ip)) |
1163 | return 0; | 1093 | return 0; |
1164 | 1094 | ||
1165 | error = trunc_dealloc(ip, size); | 1095 | error = trunc_dealloc(ip, newsize); |
1166 | if (!error) | 1096 | if (error == 0) |
1167 | error = trunc_end(ip); | 1097 | error = trunc_end(ip); |
1168 | 1098 | ||
1169 | return error; | 1099 | return error; |
1170 | } | 1100 | } |
1171 | 1101 | ||
1172 | static int do_touch(struct gfs2_inode *ip, u64 size) | 1102 | void gfs2_trim_blocks(struct inode *inode) |
1173 | { | 1103 | { |
1174 | struct gfs2_sbd *sdp = GFS2_SB(&ip->i_inode); | 1104 | u64 size = inode->i_size; |
1105 | int ret; | ||
1106 | |||
1107 | ret = do_shrink(inode, size, size); | ||
1108 | WARN_ON(ret != 0); | ||
1109 | } | ||
1110 | |||
1111 | /** | ||
1112 | * do_grow - Touch and update inode size | ||
1113 | * @inode: The inode | ||
1114 | * @size: The new size | ||
1115 | * | ||
1116 | * This function updates the timestamps on the inode and | ||
1117 | * may also increase the size of the inode. This function | ||
1118 | * must not be called with @size any smaller than the current | ||
1119 | * inode size. | ||
1120 | * | ||
1121 | * Although it is not strictly required to unstuff files here, | ||
1122 | * earlier versions of GFS2 have a bug in the stuffed file reading | ||
1123 | * code which will result in a buffer overrun if the size is larger | ||
1124 | * than the max stuffed file size. In order to prevent this from | ||
1125 | * occuring, such files are unstuffed, but in other cases we can | ||
1126 | * just update the inode size directly. | ||
1127 | * | ||
1128 | * Returns: 0 on success, or -ve on error | ||
1129 | */ | ||
1130 | |||
1131 | static int do_grow(struct inode *inode, u64 size) | ||
1132 | { | ||
1133 | struct gfs2_inode *ip = GFS2_I(inode); | ||
1134 | struct gfs2_sbd *sdp = GFS2_SB(inode); | ||
1175 | struct buffer_head *dibh; | 1135 | struct buffer_head *dibh; |
1136 | struct gfs2_alloc *al = NULL; | ||
1176 | int error; | 1137 | int error; |
1177 | 1138 | ||
1178 | error = gfs2_trans_begin(sdp, RES_DINODE, 0); | 1139 | if (gfs2_is_stuffed(ip) && |
1140 | (size > (sdp->sd_sb.sb_bsize - sizeof(struct gfs2_dinode)))) { | ||
1141 | al = gfs2_alloc_get(ip); | ||
1142 | if (al == NULL) | ||
1143 | return -ENOMEM; | ||
1144 | |||
1145 | error = gfs2_quota_lock_check(ip); | ||
1146 | if (error) | ||
1147 | goto do_grow_alloc_put; | ||
1148 | |||
1149 | al->al_requested = 1; | ||
1150 | error = gfs2_inplace_reserve(ip); | ||
1151 | if (error) | ||
1152 | goto do_grow_qunlock; | ||
1153 | } | ||
1154 | |||
1155 | error = gfs2_trans_begin(sdp, RES_DINODE + 1, 0); | ||
1179 | if (error) | 1156 | if (error) |
1180 | return error; | 1157 | goto do_grow_release; |
1181 | 1158 | ||
1182 | down_write(&ip->i_rw_mutex); | 1159 | if (al) { |
1160 | error = gfs2_unstuff_dinode(ip, NULL); | ||
1161 | if (error) | ||
1162 | goto do_end_trans; | ||
1163 | } | ||
1183 | 1164 | ||
1184 | error = gfs2_meta_inode_buffer(ip, &dibh); | 1165 | error = gfs2_meta_inode_buffer(ip, &dibh); |
1185 | if (error) | 1166 | if (error) |
1186 | goto do_touch_out; | 1167 | goto do_end_trans; |
1187 | 1168 | ||
1169 | i_size_write(inode, size); | ||
1170 | ip->i_disksize = size; | ||
1188 | ip->i_inode.i_mtime = ip->i_inode.i_ctime = CURRENT_TIME; | 1171 | ip->i_inode.i_mtime = ip->i_inode.i_ctime = CURRENT_TIME; |
1189 | gfs2_trans_add_bh(ip->i_gl, dibh, 1); | 1172 | gfs2_trans_add_bh(ip->i_gl, dibh, 1); |
1190 | gfs2_dinode_out(ip, dibh->b_data); | 1173 | gfs2_dinode_out(ip, dibh->b_data); |
1191 | brelse(dibh); | 1174 | brelse(dibh); |
1192 | 1175 | ||
1193 | do_touch_out: | 1176 | do_end_trans: |
1194 | up_write(&ip->i_rw_mutex); | ||
1195 | gfs2_trans_end(sdp); | 1177 | gfs2_trans_end(sdp); |
1178 | do_grow_release: | ||
1179 | if (al) { | ||
1180 | gfs2_inplace_release(ip); | ||
1181 | do_grow_qunlock: | ||
1182 | gfs2_quota_unlock(ip); | ||
1183 | do_grow_alloc_put: | ||
1184 | gfs2_alloc_put(ip); | ||
1185 | } | ||
1196 | return error; | 1186 | return error; |
1197 | } | 1187 | } |
1198 | 1188 | ||
1199 | /** | 1189 | /** |
1200 | * gfs2_truncatei - make a file a given size | 1190 | * gfs2_setattr_size - make a file a given size |
1201 | * @ip: the inode | 1191 | * @inode: the inode |
1202 | * @size: the size to make the file | 1192 | * @newsize: the size to make the file |
1203 | * @truncator: function to truncate the last partial block | ||
1204 | * | 1193 | * |
1205 | * The file size can grow, shrink, or stay the same size. | 1194 | * The file size can grow, shrink, or stay the same size. This |
1195 | * is called holding i_mutex and an exclusive glock on the inode | ||
1196 | * in question. | ||
1206 | * | 1197 | * |
1207 | * Returns: errno | 1198 | * Returns: errno |
1208 | */ | 1199 | */ |
1209 | 1200 | ||
1210 | int gfs2_truncatei(struct gfs2_inode *ip, u64 size) | 1201 | int gfs2_setattr_size(struct inode *inode, u64 newsize) |
1211 | { | 1202 | { |
1212 | int error; | 1203 | int ret; |
1204 | u64 oldsize; | ||
1213 | 1205 | ||
1214 | if (gfs2_assert_warn(GFS2_SB(&ip->i_inode), S_ISREG(ip->i_inode.i_mode))) | 1206 | BUG_ON(!S_ISREG(inode->i_mode)); |
1215 | return -EINVAL; | ||
1216 | 1207 | ||
1217 | if (size > ip->i_disksize) | 1208 | ret = inode_newsize_ok(inode, newsize); |
1218 | error = do_grow(ip, size); | 1209 | if (ret) |
1219 | else if (size < ip->i_disksize) | 1210 | return ret; |
1220 | error = do_shrink(ip, size); | ||
1221 | else | ||
1222 | /* update time stamps */ | ||
1223 | error = do_touch(ip, size); | ||
1224 | 1211 | ||
1225 | return error; | 1212 | oldsize = inode->i_size; |
1213 | if (newsize >= oldsize) | ||
1214 | return do_grow(inode, newsize); | ||
1215 | |||
1216 | return do_shrink(inode, oldsize, newsize); | ||
1226 | } | 1217 | } |
1227 | 1218 | ||
1228 | int gfs2_truncatei_resume(struct gfs2_inode *ip) | 1219 | int gfs2_truncatei_resume(struct gfs2_inode *ip) |