diff options
Diffstat (limited to 'fs/ext3/inode.c')
| -rw-r--r-- | fs/ext3/inode.c | 85 |
1 files changed, 10 insertions, 75 deletions
diff --git a/fs/ext3/inode.c b/fs/ext3/inode.c index 8a824f4ce5c6..a5b150f7e8a2 100644 --- a/fs/ext3/inode.c +++ b/fs/ext3/inode.c | |||
| @@ -1148,102 +1148,37 @@ 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 | |||
| 1211 | static int ext3_prepare_write(struct file *file, struct page *page, | 1151 | static int ext3_prepare_write(struct file *file, struct page *page, |
| 1212 | unsigned from, unsigned to) | 1152 | unsigned from, unsigned to) |
| 1213 | { | 1153 | { |
| 1214 | struct inode *inode = page->mapping->host; | 1154 | struct inode *inode = page->mapping->host; |
| 1215 | int ret, ret2; | 1155 | int ret, needed_blocks = ext3_writepage_trans_blocks(inode); |
| 1216 | int needed_blocks = ext3_writepage_trans_blocks(inode); | ||
| 1217 | handle_t *handle; | 1156 | handle_t *handle; |
| 1218 | int retries = 0; | 1157 | int retries = 0; |
| 1219 | 1158 | ||
| 1220 | retry: | 1159 | retry: |
| 1221 | handle = ext3_journal_start(inode, needed_blocks); | 1160 | handle = ext3_journal_start(inode, needed_blocks); |
| 1222 | if (IS_ERR(handle)) | 1161 | if (IS_ERR(handle)) { |
| 1223 | return PTR_ERR(handle); | 1162 | ret = PTR_ERR(handle); |
| 1163 | goto out; | ||
| 1164 | } | ||
| 1224 | if (test_opt(inode->i_sb, NOBH) && ext3_should_writeback_data(inode)) | 1165 | if (test_opt(inode->i_sb, NOBH) && ext3_should_writeback_data(inode)) |
| 1225 | ret = nobh_prepare_write(page, from, to, ext3_get_block); | 1166 | ret = nobh_prepare_write(page, from, to, ext3_get_block); |
| 1226 | else | 1167 | else |
| 1227 | ret = block_prepare_write(page, from, to, ext3_get_block); | 1168 | ret = block_prepare_write(page, from, to, ext3_get_block); |
| 1228 | if (ret) | 1169 | if (ret) |
| 1229 | goto failure; | 1170 | goto prepare_write_failed; |
| 1230 | 1171 | ||
| 1231 | if (ext3_should_journal_data(inode)) { | 1172 | if (ext3_should_journal_data(inode)) { |
| 1232 | ret = walk_page_buffers(handle, page_buffers(page), | 1173 | ret = walk_page_buffers(handle, page_buffers(page), |
| 1233 | from, to, NULL, do_journal_get_write_access); | 1174 | from, to, NULL, do_journal_get_write_access); |
| 1234 | if (ret) | ||
| 1235 | /* fatal error, just put the handle and return */ | ||
| 1236 | journal_stop(handle); | ||
| 1237 | } | 1175 | } |
| 1238 | return ret; | 1176 | prepare_write_failed: |
| 1239 | 1177 | if (ret) | |
| 1240 | failure: | 1178 | ext3_journal_stop(handle); |
| 1241 | ret2 = ext3_prepare_failure(file, page, from, to); | ||
| 1242 | if (ret2 < 0) | ||
| 1243 | return ret2; | ||
| 1244 | if (ret == -ENOSPC && ext3_should_retry_alloc(inode->i_sb, &retries)) | 1179 | if (ret == -ENOSPC && ext3_should_retry_alloc(inode->i_sb, &retries)) |
| 1245 | goto retry; | 1180 | goto retry; |
| 1246 | /* retry number exceeded, or other error like -EDQUOT */ | 1181 | out: |
| 1247 | return ret; | 1182 | return ret; |
| 1248 | } | 1183 | } |
| 1249 | 1184 | ||
