diff options
author | Theodore Ts'o <tytso@mit.edu> | 2008-08-02 21:10:38 -0400 |
---|---|---|
committer | Theodore Ts'o <tytso@mit.edu> | 2008-08-02 21:10:38 -0400 |
commit | bc965ab3f2b4b7bb898b11d61d25295c2053b8ac (patch) | |
tree | 7f8cbd4e5d9c2a4e63fb35d5444f77bfb667c00f /fs/ext4 | |
parent | 0123c93998511978556b03d2bb023af92aa24d55 (diff) |
ext4: Fix lack of credits BUG() when deleting a badly fragmented inode
The extents codepath for ext4_truncate() requests journal transaction
credits in very small chunks, requesting only what is needed. This
means there may not be enough credits left on the transaction handle
after ext4_truncate() returns and then when ext4_delete_inode() tries
finish up its work, it may not have enough transaction credits,
causing a BUG() oops in the jbd2 core.
Also, reserve an extra 2 blocks when starting an ext4_delete_inode()
since we need to update the inode bitmap, as well as update the
orphaned inode linked list.
Signed-off-by: "Theodore Ts'o" <tytso@mit.edu>
Diffstat (limited to 'fs/ext4')
-rw-r--r-- | fs/ext4/inode.c | 30 |
1 files changed, 29 insertions, 1 deletions
diff --git a/fs/ext4/inode.c b/fs/ext4/inode.c index 37f834bc7cd6..2697eaf0368f 100644 --- a/fs/ext4/inode.c +++ b/fs/ext4/inode.c | |||
@@ -191,6 +191,7 @@ static int ext4_journal_test_restart(handle_t *handle, struct inode *inode) | |||
191 | void ext4_delete_inode (struct inode * inode) | 191 | void ext4_delete_inode (struct inode * inode) |
192 | { | 192 | { |
193 | handle_t *handle; | 193 | handle_t *handle; |
194 | int err; | ||
194 | 195 | ||
195 | if (ext4_should_order_data(inode)) | 196 | if (ext4_should_order_data(inode)) |
196 | ext4_begin_ordered_truncate(inode, 0); | 197 | ext4_begin_ordered_truncate(inode, 0); |
@@ -199,8 +200,9 @@ void ext4_delete_inode (struct inode * inode) | |||
199 | if (is_bad_inode(inode)) | 200 | if (is_bad_inode(inode)) |
200 | goto no_delete; | 201 | goto no_delete; |
201 | 202 | ||
202 | handle = start_transaction(inode); | 203 | handle = ext4_journal_start(inode, blocks_for_truncate(inode)+3); |
203 | if (IS_ERR(handle)) { | 204 | if (IS_ERR(handle)) { |
205 | ext4_std_error(inode->i_sb, PTR_ERR(handle)); | ||
204 | /* | 206 | /* |
205 | * If we're going to skip the normal cleanup, we still need to | 207 | * If we're going to skip the normal cleanup, we still need to |
206 | * make sure that the in-core orphan linked list is properly | 208 | * make sure that the in-core orphan linked list is properly |
@@ -213,8 +215,34 @@ void ext4_delete_inode (struct inode * inode) | |||
213 | if (IS_SYNC(inode)) | 215 | if (IS_SYNC(inode)) |
214 | handle->h_sync = 1; | 216 | handle->h_sync = 1; |
215 | inode->i_size = 0; | 217 | inode->i_size = 0; |
218 | err = ext4_mark_inode_dirty(handle, inode); | ||
219 | if (err) { | ||
220 | ext4_warning(inode->i_sb, __func__, | ||
221 | "couldn't mark inode dirty (err %d)", err); | ||
222 | goto stop_handle; | ||
223 | } | ||
216 | if (inode->i_blocks) | 224 | if (inode->i_blocks) |
217 | ext4_truncate(inode); | 225 | ext4_truncate(inode); |
226 | |||
227 | /* | ||
228 | * ext4_ext_truncate() doesn't reserve any slop when it | ||
229 | * restarts journal transactions; therefore there may not be | ||
230 | * enough credits left in the handle to remove the inode from | ||
231 | * the orphan list and set the dtime field. | ||
232 | */ | ||
233 | if (handle->h_buffer_credits < 3) { | ||
234 | err = ext4_journal_extend(handle, 3); | ||
235 | if (err > 0) | ||
236 | err = ext4_journal_restart(handle, 3); | ||
237 | if (err != 0) { | ||
238 | ext4_warning(inode->i_sb, __func__, | ||
239 | "couldn't extend journal (err %d)", err); | ||
240 | stop_handle: | ||
241 | ext4_journal_stop(handle); | ||
242 | goto no_delete; | ||
243 | } | ||
244 | } | ||
245 | |||
218 | /* | 246 | /* |
219 | * Kill off the orphan record which ext4_truncate created. | 247 | * Kill off the orphan record which ext4_truncate created. |
220 | * AKPM: I think this can be inside the above `if'. | 248 | * AKPM: I think this can be inside the above `if'. |