aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorTheodore Ts'o <tytso@mit.edu>2008-08-02 21:10:38 -0400
committerTheodore Ts'o <tytso@mit.edu>2008-08-02 21:10:38 -0400
commitbc965ab3f2b4b7bb898b11d61d25295c2053b8ac (patch)
tree7f8cbd4e5d9c2a4e63fb35d5444f77bfb667c00f
parent0123c93998511978556b03d2bb023af92aa24d55 (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>
-rw-r--r--fs/ext4/inode.c30
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)
191void ext4_delete_inode (struct inode * inode) 191void 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'.