aboutsummaryrefslogtreecommitdiffstats
path: root/fs/ext3
diff options
context:
space:
mode:
authorAndrey Savochkin <saw@sw.ru>2006-12-06 23:37:34 -0500
committerLinus Torvalds <torvalds@woody.osdl.org>2006-12-07 11:39:37 -0500
commite92a4d595b464c4aae64be39ca61a9ffe9c8b278 (patch)
tree7d80804e689ea1f01fc8ceaea2a1b1da78081c60 /fs/ext3
parent04903664325acb3f199dd8a4b8f1aa437e9fd6b2 (diff)
[PATCH] retries in ext3_prepare_write() violate ordering requirements
In journal=ordered or journal=data mode retry in ext3_prepare_write() breaks the requirements of journaling of data with respect to metadata. The fix is to call commit_write to commit allocated zero blocks before retry. Signed-off-by: Kirill Korotaev <dev@openvz.org> Cc: Ingo Molnar <mingo@elte.hu> Cc: Ken Chen <kenneth.w.chen@intel.com> Cc: <linux-ext4@vger.kernel.org> Signed-off-by: Andrew Morton <akpm@osdl.org> Signed-off-by: Linus Torvalds <torvalds@osdl.org>
Diffstat (limited to 'fs/ext3')
-rw-r--r--fs/ext3/inode.c85
1 files changed, 75 insertions, 10 deletions
diff --git a/fs/ext3/inode.c b/fs/ext3/inode.c
index 03ba5bcab186..beaf25f5112f 100644
--- a/fs/ext3/inode.c
+++ b/fs/ext3/inode.c
@@ -1148,37 +1148,102 @@ static int do_journal_get_write_access(handle_t *handle,
1148 return ext3_journal_get_write_access(handle, bh); 1148 return ext3_journal_get_write_access(handle, bh);
1149} 1149}
1150 1150
1151/*
1152 * The idea of this helper function is following:
1153 * if prepare_write has allocated some blocks, but not all of them, the
1154 * transaction must include the content of the newly allocated blocks.
1155 * This content is expected to be set to zeroes by block_prepare_write().
1156 * 2006/10/14 SAW
1157 */
1158static int ext3_prepare_failure(struct file *file, struct page *page,
1159 unsigned from, unsigned to)
1160{
1161 struct address_space *mapping;
1162 struct buffer_head *bh, *head, *next;
1163 unsigned block_start, block_end;
1164 unsigned blocksize;
1165 int ret;
1166 handle_t *handle = ext3_journal_current_handle();
1167
1168 mapping = page->mapping;
1169 if (ext3_should_writeback_data(mapping->host)) {
1170 /* optimization: no constraints about data */
1171skip:
1172 return ext3_journal_stop(handle);
1173 }
1174
1175 head = page_buffers(page);
1176 blocksize = head->b_size;
1177 for ( bh = head, block_start = 0;
1178 bh != head || !block_start;
1179 block_start = block_end, bh = next)
1180 {
1181 next = bh->b_this_page;
1182 block_end = block_start + blocksize;
1183 if (block_end <= from)
1184 continue;
1185 if (block_start >= to) {
1186 block_start = to;
1187 break;
1188 }
1189 if (!buffer_mapped(bh))
1190 /* prepare_write failed on this bh */
1191 break;
1192 if (ext3_should_journal_data(mapping->host)) {
1193 ret = do_journal_get_write_access(handle, bh);
1194 if (ret) {
1195 ext3_journal_stop(handle);
1196 return ret;
1197 }
1198 }
1199 /*
1200 * block_start here becomes the first block where the current iteration
1201 * of prepare_write failed.
1202 */
1203 }
1204 if (block_start <= from)
1205 goto skip;
1206
1207 /* commit allocated and zeroed buffers */
1208 return mapping->a_ops->commit_write(file, page, from, block_start);
1209}
1210
1151static int ext3_prepare_write(struct file *file, struct page *page, 1211static int ext3_prepare_write(struct file *file, struct page *page,
1152 unsigned from, unsigned to) 1212 unsigned from, unsigned to)
1153{ 1213{
1154 struct inode *inode = page->mapping->host; 1214 struct inode *inode = page->mapping->host;
1155 int ret, needed_blocks = ext3_writepage_trans_blocks(inode); 1215 int ret, ret2;
1216 int needed_blocks = ext3_writepage_trans_blocks(inode);
1156 handle_t *handle; 1217 handle_t *handle;
1157 int retries = 0; 1218 int retries = 0;
1158 1219
1159retry: 1220retry:
1160 handle = ext3_journal_start(inode, needed_blocks); 1221 handle = ext3_journal_start(inode, needed_blocks);
1161 if (IS_ERR(handle)) { 1222 if (IS_ERR(handle))
1162 ret = PTR_ERR(handle); 1223 return PTR_ERR(handle);
1163 goto out;
1164 }
1165 if (test_opt(inode->i_sb, NOBH) && ext3_should_writeback_data(inode)) 1224 if (test_opt(inode->i_sb, NOBH) && ext3_should_writeback_data(inode))
1166 ret = nobh_prepare_write(page, from, to, ext3_get_block); 1225 ret = nobh_prepare_write(page, from, to, ext3_get_block);
1167 else 1226 else
1168 ret = block_prepare_write(page, from, to, ext3_get_block); 1227 ret = block_prepare_write(page, from, to, ext3_get_block);
1169 if (ret) 1228 if (ret)
1170 goto prepare_write_failed; 1229 goto failure;
1171 1230
1172 if (ext3_should_journal_data(inode)) { 1231 if (ext3_should_journal_data(inode)) {
1173 ret = walk_page_buffers(handle, page_buffers(page), 1232 ret = walk_page_buffers(handle, page_buffers(page),
1174 from, to, NULL, do_journal_get_write_access); 1233 from, to, NULL, do_journal_get_write_access);
1234 if (ret)
1235 /* fatal error, just put the handle and return */
1236 journal_stop(handle);
1175 } 1237 }
1176prepare_write_failed: 1238 return ret;
1177 if (ret) 1239
1178 ext3_journal_stop(handle); 1240failure:
1241 ret2 = ext3_prepare_failure(file, page, from, to);
1242 if (ret2 < 0)
1243 return ret2;
1179 if (ret == -ENOSPC && ext3_should_retry_alloc(inode->i_sb, &retries)) 1244 if (ret == -ENOSPC && ext3_should_retry_alloc(inode->i_sb, &retries))
1180 goto retry; 1245 goto retry;
1181out: 1246 /* retry number exceeded, or other error like -EDQUOT */
1182 return ret; 1247 return ret;
1183} 1248}
1184 1249