From 68569684adae20b6bf227ec1512b9a0bb83fbf5b Mon Sep 17 00:00:00 2001 From: Andrew Morton Date: Wed, 2 Apr 2003 16:29:43 -0800 Subject: [PATCH] [PATCH] ext3 journal commit I/O error fix From: Hua Zhong The current ext3 totally ignores I/O errors that happened during a journal_force_commit time, causing user space to falsely believe it has succeeded, which actually did not. This patch checks IO error during journal_commit_transaction. and aborts the journal when there is I/O error. Originally I thought about reporting the error without doing aborting the journal, but it probably needs a new flag. Aborting the journal seems to be the easy way to signal "hey sth is wrong..". --- fs/ext3/fsync.c | 3 +-- fs/jbd/commit.c | 26 +++++++++++++++++++++++--- fs/jbd/journal.c | 11 ++++++++++- fs/jbd/transaction.c | 6 +++--- include/linux/jbd.h | 2 +- 5 files changed, 38 insertions(+), 10 deletions(-) diff --git a/fs/ext3/fsync.c b/fs/ext3/fsync.c index e42ce093011ff..cdf736d4b867b 100644 --- a/fs/ext3/fsync.c +++ b/fs/ext3/fsync.c @@ -72,6 +72,5 @@ int ext3_sync_file(struct file * file, struct dentry *dentry, int datasync) * (they were dirtied by commit). But that's OK - the blocks are * safe in-journal, which is all fsync() needs to ensure. */ - ext3_force_commit(inode->i_sb); - return 0; + return ext3_force_commit(inode->i_sb); } diff --git a/fs/jbd/commit.c b/fs/jbd/commit.c index b1bb64d1b23f9..f67f28239af27 100644 --- a/fs/jbd/commit.c +++ b/fs/jbd/commit.c @@ -73,7 +73,7 @@ void journal_commit_transaction(journal_t *journal) #endif lock_kernel(); - + J_ASSERT (journal->j_running_transaction != NULL); J_ASSERT (journal->j_committing_transaction == NULL); @@ -173,6 +173,7 @@ void journal_commit_transaction(journal_t *journal) * on the transaction lists. Data blocks go first. */ + err = 0; /* * Whenever we unlock the journal and sleep, things can get added * onto ->t_datalist, so we have to keep looping back to write_out_data @@ -251,9 +252,13 @@ write_out_data_locked: jh = jh->b_tprev; /* Wait on the last written */ bh = jh2bh(jh); if (buffer_locked(bh)) { + get_bh(bh); spin_unlock(&journal_datalist_lock); unlock_journal(journal); wait_on_buffer(bh); + if (unlikely(!buffer_uptodate(bh))) + err = -EIO; + put_bh(bh); /* the journal_head may have been removed now */ lock_journal(journal); goto write_out_data; @@ -446,7 +451,10 @@ start_journal_io: jbd_debug(3, "JBD: commit phase 4\n"); - /* akpm: these are BJ_IO, and journal_datalist_lock is not needed */ + /* + * akpm: these are BJ_IO, and journal_datalist_lock is not needed. + * See __journal_try_to_free_buffer. + */ wait_for_iobuf: while (commit_transaction->t_iobuf_list != NULL) { struct buffer_head *bh; @@ -455,6 +463,8 @@ start_journal_io: if (buffer_locked(bh)) { unlock_journal(journal); wait_on_buffer(bh); + if (unlikely(!buffer_uptodate(bh))) + err = -EIO; lock_journal(journal); goto wait_for_iobuf; } @@ -516,6 +526,8 @@ start_journal_io: if (buffer_locked(bh)) { unlock_journal(journal); wait_on_buffer(bh); + if (unlikely(!buffer_uptodate(bh))) + err = -EIO; lock_journal(journal); goto wait_for_ctlbuf; } @@ -563,7 +575,9 @@ start_journal_io: struct buffer_head *bh = jh2bh(descriptor); set_buffer_uptodate(bh); sync_dirty_buffer(bh); - __brelse(bh); /* One for getblk() */ + if (unlikely(!buffer_uptodate(bh))) + err = -EIO; + put_bh(bh); /* One for getblk() */ journal_unlock_journal_head(descriptor); } @@ -574,6 +588,12 @@ start_journal_io: skip_commit: /* The journal should be unlocked by now. */ + if (err) { + lock_journal(journal); + __journal_abort_hard(journal); + unlock_journal(journal); + } + /* Call any callbacks that had been registered for handles in this * transaction. It is up to the callback to free any allocated * memory. diff --git a/fs/jbd/journal.c b/fs/jbd/journal.c index 167e632c4b518..a429a2aa0ac5e 100644 --- a/fs/jbd/journal.c +++ b/fs/jbd/journal.c @@ -580,8 +580,10 @@ out: * Wait for a specified commit to complete. * The caller may not hold the journal lock. */ -void log_wait_commit (journal_t *journal, tid_t tid) +int log_wait_commit (journal_t *journal, tid_t tid) { + int err = 0; + lock_kernel(); #ifdef CONFIG_JBD_DEBUG lock_journal(journal); @@ -598,7 +600,14 @@ void log_wait_commit (journal_t *journal, tid_t tid) wake_up(&journal->j_wait_commit); sleep_on(&journal->j_wait_done_commit); } + + if (unlikely(is_journal_aborted(journal))) { + printk(KERN_EMERG "journal commit I/O error\n"); + err = -EIO; + } + unlock_kernel(); + return err; } /* diff --git a/fs/jbd/transaction.c b/fs/jbd/transaction.c index 74b51b7fd5843..f309a7249fcd5 100644 --- a/fs/jbd/transaction.c +++ b/fs/jbd/transaction.c @@ -1402,7 +1402,7 @@ int journal_stop(handle_t *handle) * to wait for the commit to complete. */ if (handle->h_sync && !(current->flags & PF_MEMALLOC)) - log_wait_commit(journal, tid); + err = log_wait_commit(journal, tid); } jbd_free_handle(handle); return err; @@ -1418,7 +1418,7 @@ int journal_stop(handle_t *handle) int journal_force_commit(journal_t *journal) { handle_t *handle; - int ret = 0; + int ret; lock_kernel(); handle = journal_start(journal, 1); @@ -1427,7 +1427,7 @@ int journal_force_commit(journal_t *journal) goto out; } handle->h_sync = 1; - journal_stop(handle); + ret = journal_stop(handle); out: unlock_kernel(); return ret; diff --git a/include/linux/jbd.h b/include/linux/jbd.h index 2362b90db9e73..2cddeb1f450e6 100644 --- a/include/linux/jbd.h +++ b/include/linux/jbd.h @@ -855,7 +855,7 @@ extern void journal_brelse_array(struct buffer_head *b[], int n); extern int log_space_left (journal_t *); /* Called with journal locked */ extern tid_t log_start_commit (journal_t *, transaction_t *); -extern void log_wait_commit (journal_t *, tid_t); +extern int log_wait_commit (journal_t *, tid_t); extern int log_do_checkpoint (journal_t *, int); extern void log_wait_for_space(journal_t *, int nblocks); -- 2.53.0