diff options
Diffstat (limited to 'fs/ext3')
-rw-r--r-- | fs/ext3/inode.c | 85 |
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 | */ | ||
1158 | static 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 */ | ||
1171 | skip: | ||
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 | |||
1151 | static int ext3_prepare_write(struct file *file, struct page *page, | 1211 | static 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 | ||
1159 | retry: | 1220 | retry: |
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 | } |
1176 | prepare_write_failed: | 1238 | return ret; |
1177 | if (ret) | 1239 | |
1178 | ext3_journal_stop(handle); | 1240 | failure: |
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; |
1181 | out: | 1246 | /* retry number exceeded, or other error like -EDQUOT */ |
1182 | return ret; | 1247 | return ret; |
1183 | } | 1248 | } |
1184 | 1249 | ||