diff options
Diffstat (limited to 'fs/ext4')
-rw-r--r-- | fs/ext4/inode.c | 85 |
1 files changed, 75 insertions, 10 deletions
diff --git a/fs/ext4/inode.c b/fs/ext4/inode.c index 0a60ec5a16db..1d85d4ec9598 100644 --- a/fs/ext4/inode.c +++ b/fs/ext4/inode.c | |||
@@ -1147,37 +1147,102 @@ static int do_journal_get_write_access(handle_t *handle, | |||
1147 | return ext4_journal_get_write_access(handle, bh); | 1147 | return ext4_journal_get_write_access(handle, bh); |
1148 | } | 1148 | } |
1149 | 1149 | ||
1150 | /* | ||
1151 | * The idea of this helper function is following: | ||
1152 | * if prepare_write has allocated some blocks, but not all of them, the | ||
1153 | * transaction must include the content of the newly allocated blocks. | ||
1154 | * This content is expected to be set to zeroes by block_prepare_write(). | ||
1155 | * 2006/10/14 SAW | ||
1156 | */ | ||
1157 | static int ext4_prepare_failure(struct file *file, struct page *page, | ||
1158 | unsigned from, unsigned to) | ||
1159 | { | ||
1160 | struct address_space *mapping; | ||
1161 | struct buffer_head *bh, *head, *next; | ||
1162 | unsigned block_start, block_end; | ||
1163 | unsigned blocksize; | ||
1164 | int ret; | ||
1165 | handle_t *handle = ext4_journal_current_handle(); | ||
1166 | |||
1167 | mapping = page->mapping; | ||
1168 | if (ext4_should_writeback_data(mapping->host)) { | ||
1169 | /* optimization: no constraints about data */ | ||
1170 | skip: | ||
1171 | return ext4_journal_stop(handle); | ||
1172 | } | ||
1173 | |||
1174 | head = page_buffers(page); | ||
1175 | blocksize = head->b_size; | ||
1176 | for ( bh = head, block_start = 0; | ||
1177 | bh != head || !block_start; | ||
1178 | block_start = block_end, bh = next) | ||
1179 | { | ||
1180 | next = bh->b_this_page; | ||
1181 | block_end = block_start + blocksize; | ||
1182 | if (block_end <= from) | ||
1183 | continue; | ||
1184 | if (block_start >= to) { | ||
1185 | block_start = to; | ||
1186 | break; | ||
1187 | } | ||
1188 | if (!buffer_mapped(bh)) | ||
1189 | /* prepare_write failed on this bh */ | ||
1190 | break; | ||
1191 | if (ext4_should_journal_data(mapping->host)) { | ||
1192 | ret = do_journal_get_write_access(handle, bh); | ||
1193 | if (ret) { | ||
1194 | ext4_journal_stop(handle); | ||
1195 | return ret; | ||
1196 | } | ||
1197 | } | ||
1198 | /* | ||
1199 | * block_start here becomes the first block where the current iteration | ||
1200 | * of prepare_write failed. | ||
1201 | */ | ||
1202 | } | ||
1203 | if (block_start <= from) | ||
1204 | goto skip; | ||
1205 | |||
1206 | /* commit allocated and zeroed buffers */ | ||
1207 | return mapping->a_ops->commit_write(file, page, from, block_start); | ||
1208 | } | ||
1209 | |||
1150 | static int ext4_prepare_write(struct file *file, struct page *page, | 1210 | static int ext4_prepare_write(struct file *file, struct page *page, |
1151 | unsigned from, unsigned to) | 1211 | unsigned from, unsigned to) |
1152 | { | 1212 | { |
1153 | struct inode *inode = page->mapping->host; | 1213 | struct inode *inode = page->mapping->host; |
1154 | int ret, needed_blocks = ext4_writepage_trans_blocks(inode); | 1214 | int ret, ret2; |
1215 | int needed_blocks = ext4_writepage_trans_blocks(inode); | ||
1155 | handle_t *handle; | 1216 | handle_t *handle; |
1156 | int retries = 0; | 1217 | int retries = 0; |
1157 | 1218 | ||
1158 | retry: | 1219 | retry: |
1159 | handle = ext4_journal_start(inode, needed_blocks); | 1220 | handle = ext4_journal_start(inode, needed_blocks); |
1160 | if (IS_ERR(handle)) { | 1221 | if (IS_ERR(handle)) |
1161 | ret = PTR_ERR(handle); | 1222 | return PTR_ERR(handle); |
1162 | goto out; | ||
1163 | } | ||
1164 | if (test_opt(inode->i_sb, NOBH) && ext4_should_writeback_data(inode)) | 1223 | if (test_opt(inode->i_sb, NOBH) && ext4_should_writeback_data(inode)) |
1165 | ret = nobh_prepare_write(page, from, to, ext4_get_block); | 1224 | ret = nobh_prepare_write(page, from, to, ext4_get_block); |
1166 | else | 1225 | else |
1167 | ret = block_prepare_write(page, from, to, ext4_get_block); | 1226 | ret = block_prepare_write(page, from, to, ext4_get_block); |
1168 | if (ret) | 1227 | if (ret) |
1169 | goto prepare_write_failed; | 1228 | goto failure; |
1170 | 1229 | ||
1171 | if (ext4_should_journal_data(inode)) { | 1230 | if (ext4_should_journal_data(inode)) { |
1172 | ret = walk_page_buffers(handle, page_buffers(page), | 1231 | ret = walk_page_buffers(handle, page_buffers(page), |
1173 | from, to, NULL, do_journal_get_write_access); | 1232 | from, to, NULL, do_journal_get_write_access); |
1233 | if (ret) | ||
1234 | /* fatal error, just put the handle and return */ | ||
1235 | journal_stop(handle); | ||
1174 | } | 1236 | } |
1175 | prepare_write_failed: | 1237 | return ret; |
1176 | if (ret) | 1238 | |
1177 | ext4_journal_stop(handle); | 1239 | failure: |
1240 | ret2 = ext4_prepare_failure(file, page, from, to); | ||
1241 | if (ret2 < 0) | ||
1242 | return ret2; | ||
1178 | if (ret == -ENOSPC && ext4_should_retry_alloc(inode->i_sb, &retries)) | 1243 | if (ret == -ENOSPC && ext4_should_retry_alloc(inode->i_sb, &retries)) |
1179 | goto retry; | 1244 | goto retry; |
1180 | out: | 1245 | /* retry number exceeded, or other error like -EDQUOT */ |
1181 | return ret; | 1246 | return ret; |
1182 | } | 1247 | } |
1183 | 1248 | ||