aboutsummaryrefslogtreecommitdiffstats
path: root/fs/gfs2/bmap.c
diff options
context:
space:
mode:
authorSteven Whitehouse <swhiteho@redhat.com>2010-08-11 04:37:53 -0400
committerSteven Whitehouse <swhiteho@redhat.com>2010-09-20 06:18:16 -0400
commitff8f33c8b30d7b7efdcf2548c7f6e64db6a89b29 (patch)
treea21bad4d2b48f743dd96277b1c966c35aefe1ce2 /fs/gfs2/bmap.c
parent2422084a94fcd5038406261b331672a13c92c050 (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.c247
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
897static 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
941out_brelse:
942 brelse(dibh);
943out_end_trans:
944 gfs2_trans_end(sdp);
945out_ipres:
946 gfs2_inplace_release(ip);
947out_gunlock_q:
948 gfs2_quota_unlock(ip);
949out:
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 */
960static int gfs2_block_truncate_page(struct address_space *mapping) 891static 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
1026static int trunc_start(struct gfs2_inode *ip, u64 size) 956static 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);
993out_brelse:
994 brelse(dibh);
1067out: 995out:
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
1155static int do_shrink(struct gfs2_inode *ip, u64 size) 1084static 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
1172static int do_touch(struct gfs2_inode *ip, u64 size) 1102void 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
1131static 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
1193do_touch_out: 1176do_end_trans:
1194 up_write(&ip->i_rw_mutex);
1195 gfs2_trans_end(sdp); 1177 gfs2_trans_end(sdp);
1178do_grow_release:
1179 if (al) {
1180 gfs2_inplace_release(ip);
1181do_grow_qunlock:
1182 gfs2_quota_unlock(ip);
1183do_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
1210int gfs2_truncatei(struct gfs2_inode *ip, u64 size) 1201int 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
1228int gfs2_truncatei_resume(struct gfs2_inode *ip) 1219int gfs2_truncatei_resume(struct gfs2_inode *ip)