From: Jeffrey Mahoney This patch allows ReiserFS to handle I/O errors in the journal (or journal flush) where it would have previously panicked. The new behavior is to mark the filesystem read-only, disallow new transactions to be started, and to allow existing transactions to complete (though not to commit). The resultant filesystem can be safely umounted, and checked via normal mechanisms. As it is a journaling filesystem, the filesystem itself will be in a similar state to the power being cut to the machine, once umounted. Signed-off-by: Jeff Mahoney Signed-off-by: Andrew Morton --- 25-akpm/fs/reiserfs/bitmap.c | 13 + 25-akpm/fs/reiserfs/dir.c | 7 25-akpm/fs/reiserfs/file.c | 125 +++++++++-- 25-akpm/fs/reiserfs/inode.c | 196 +++++++++++++----- 25-akpm/fs/reiserfs/journal.c | 355 ++++++++++++++++++++++++--------- 25-akpm/fs/reiserfs/namei.c | 155 ++++++++++---- 25-akpm/fs/reiserfs/objectid.c | 2 25-akpm/fs/reiserfs/prints.c | 43 +++ 25-akpm/fs/reiserfs/resize.c | 26 +- 25-akpm/fs/reiserfs/stree.c | 57 ++++- 25-akpm/fs/reiserfs/super.c | 136 +++++++++--- 25-akpm/fs/reiserfs/tail_conversion.c | 3 25-akpm/include/linux/reiserfs_fs.h | 15 - 25-akpm/include/linux/reiserfs_fs_sb.h | 26 ++ 14 files changed, 883 insertions(+), 276 deletions(-) diff -puN fs/reiserfs/bitmap.c~reiserfs-add-i-o-error-handling-to-journal-operations fs/reiserfs/bitmap.c --- 25/fs/reiserfs/bitmap.c~reiserfs-add-i-o-error-handling-to-journal-operations 2004-10-05 11:11:22.905600648 -0700 +++ 25-akpm/fs/reiserfs/bitmap.c 2004-10-05 11:11:22.930596848 -0700 @@ -137,6 +137,8 @@ static int scan_bitmap_block (struct rei int end, next; int org = *beg; + BUG_ON (!th->t_trans_id); + RFALSE(bmap_n >= SB_BMAP_NR (s), "Bitmap %d is out of range (0..%d)",bmap_n, SB_BMAP_NR (s) - 1); PROC_INFO_INC( s, scan_bitmap.bmap ); /* this is unclear and lacks comments, explain how journal bitmaps @@ -290,6 +292,8 @@ static int scan_bitmap (struct reiserfs_ int end_bm, end_off; int off_max = s->s_blocksize << 3; + BUG_ON (!th->t_trans_id); + PROC_INFO_INC( s, scan_bitmap.call ); if ( SB_FREE_BLOCKS(s) <= 0) return 0; // No point in looking for more free blocks @@ -348,6 +352,8 @@ static void _reiserfs_free_block (struct struct reiserfs_bitmap_info *apbi; int nr, offset; + BUG_ON (!th->t_trans_id); + PROC_INFO_INC( s, free_block ); rs = SB_DISK_SUPER_BLOCK (s); @@ -389,6 +395,8 @@ void reiserfs_free_block (struct reiserf { struct super_block * s = th->t_super; + BUG_ON (!th->t_trans_id); + RFALSE(!s, "vs-4061: trying to free block on nonexistent device"); RFALSE(is_reusable (s, block, 1) == 0, "vs-4071: can not free such block"); /* mark it before we clear it, just in case */ @@ -401,6 +409,7 @@ void reiserfs_free_prealloc_block (struc struct inode *inode, b_blocknr_t block) { RFALSE(!th->t_super, "vs-4060: trying to free block on nonexistent device"); RFALSE(is_reusable (th->t_super, block, 1) == 0, "vs-4070: can not free such block"); + BUG_ON (!th->t_trans_id); _reiserfs_free_block(th, inode, block, 1) ; } @@ -410,6 +419,7 @@ static void __discard_prealloc (struct r unsigned long save = ei->i_prealloc_block ; int dirty = 0; struct inode *inode = &ei->vfs_inode; + BUG_ON (!th->t_trans_id); #ifdef CONFIG_REISERFS_CHECK if (ei->i_prealloc_count < 0) reiserfs_warning (th->t_super, "zam-4001:%s: inode has negative prealloc blocks count.", __FUNCTION__ ); @@ -431,6 +441,7 @@ void reiserfs_discard_prealloc (struct r struct inode *inode) { struct reiserfs_inode_info *ei = REISERFS_I(inode); + BUG_ON (!th->t_trans_id); if (ei->i_prealloc_count) __discard_prealloc(th, ei); } @@ -439,6 +450,8 @@ void reiserfs_discard_all_prealloc (stru { struct list_head * plist = &SB_JOURNAL(th->t_super)->j_prealloc_list; + BUG_ON (!th->t_trans_id); + while (!list_empty(plist)) { struct reiserfs_inode_info *ei; ei = list_entry(plist->next, struct reiserfs_inode_info, i_prealloc_list); diff -puN fs/reiserfs/dir.c~reiserfs-add-i-o-error-handling-to-journal-operations fs/reiserfs/dir.c --- 25/fs/reiserfs/dir.c~reiserfs-add-i-o-error-handling-to-journal-operations 2004-10-05 11:11:22.906600496 -0700 +++ 25-akpm/fs/reiserfs/dir.c 2004-10-05 11:11:22.931596696 -0700 @@ -26,10 +26,13 @@ struct file_operations reiserfs_dir_oper int reiserfs_dir_fsync(struct file *filp, struct dentry *dentry, int datasync) { struct inode *inode = dentry->d_inode; + int err; reiserfs_write_lock(inode->i_sb); - reiserfs_commit_for_inode(inode) ; + err = reiserfs_commit_for_inode(inode) ; reiserfs_write_unlock(inode->i_sb) ; - return 0 ; + if (err < 0) + return err; + return 0; } diff -puN fs/reiserfs/file.c~reiserfs-add-i-o-error-handling-to-journal-operations fs/reiserfs/file.c --- 25/fs/reiserfs/file.c~reiserfs-add-i-o-error-handling-to-journal-operations 2004-10-05 11:11:22.908600192 -0700 +++ 25-akpm/fs/reiserfs/file.c 2004-10-05 11:11:22.933596392 -0700 @@ -35,6 +35,8 @@ static int reiserfs_file_release (struct { struct reiserfs_transaction_handle th ; + int err; + int jbegin_failure = 0; if (!S_ISREG (inode->i_mode)) BUG (); @@ -49,26 +51,58 @@ static int reiserfs_file_release (struct reiserfs_write_lock(inode->i_sb); down (&inode->i_sem); - journal_begin(&th, inode->i_sb, JOURNAL_PER_BALANCE_CNT * 3) ; + /* freeing preallocation only involves relogging blocks that + * are already in the current transaction. preallocation gets + * freed at the end of each transaction, so it is impossible for + * us to log any additional blocks + */ + err = journal_begin(&th, inode->i_sb, 1); + if (err) { + /* uh oh, we can't allow the inode to go away while there + * is still preallocation blocks pending. Try to join the + * aborted transaction + */ + jbegin_failure = err; + err = journal_join_abort(&th, inode->i_sb, 1); + + if (err) { + /* hmpf, our choices here aren't good. We can pin the inode + * which will disallow unmount from every happening, we can + * do nothing, which will corrupt random memory on unmount, + * or we can forcibly remove the file from the preallocation + * list, which will leak blocks on disk. Lets pin the inode + * and let the admin know what is going on. + */ + igrab(inode); + reiserfs_warning(inode->i_sb, "pinning inode %lu because the " + "preallocation can't be freed"); + goto out; + } + } reiserfs_update_inode_transaction(inode) ; #ifdef REISERFS_PREALLOCATE reiserfs_discard_prealloc (&th, inode); #endif - journal_end(&th, inode->i_sb, JOURNAL_PER_BALANCE_CNT * 3) ; + err = journal_end(&th, inode->i_sb, 1); - if (atomic_read(&inode->i_count) <= 1 && + /* copy back the error code from journal_begin */ + if (!err) + err = jbegin_failure; + + if (!err && atomic_read(&inode->i_count) <= 1 && (REISERFS_I(inode)->i_flags & i_pack_on_close_mask) && tail_has_to_be_packed (inode)) { /* if regular file is released by last holder and it has been appended (we append by unformatted node only) or its direct item(s) had to be converted, then it may have to be indirect2direct converted */ - reiserfs_truncate_file(inode, 0) ; + err = reiserfs_truncate_file(inode, 0) ; } +out: up (&inode->i_sem); reiserfs_write_unlock(inode->i_sb); - return 0; + return err; } static void reiserfs_vfs_truncate_file(struct inode *inode) { @@ -99,6 +133,8 @@ static int reiserfs_sync_file( reiserfs_write_unlock(p_s_inode->i_sb); if (barrier_done != 1) blkdev_issue_flush(p_s_inode->i_sb->s_bdev, NULL); + if (barrier_done < 0) + return barrier_done; return ( n_err < 0 ) ? -EIO : 0; } @@ -146,7 +182,6 @@ int reiserfs_allocate_blocks_for_region( // of the fact that we already prepared // current block for journal int will_prealloc = 0; - RFALSE(!blocks_to_allocate, "green-9004: tried to allocate zero blocks?"); /* only preallocate if this is a small write */ @@ -166,7 +201,9 @@ int reiserfs_allocate_blocks_for_region( /* If we came here, it means we absolutely need to open a transaction, since we need to allocate some blocks */ reiserfs_write_lock(inode->i_sb); // Journaling stuff and we need that. - journal_begin(th, inode->i_sb, JOURNAL_PER_BALANCE_CNT * 3 + 1); // Wish I know if this number enough + res = journal_begin(th, inode->i_sb, JOURNAL_PER_BALANCE_CNT * 3 + 1); // Wish I know if this number enough + if (res) + goto error_exit; reiserfs_update_inode_transaction(inode) ; /* Look for the in-tree position of our write, need path for block allocator */ @@ -194,7 +231,9 @@ int reiserfs_allocate_blocks_for_region( /* We flush the transaction in case of no space. This way some blocks might become free */ SB_JOURNAL(inode->i_sb)->j_must_wait = 1; - restart_transaction(th, inode, &path); + res = restart_transaction(th, inode, &path); + if (res) + goto error_exit; /* We might have scheduled, so search again */ res = search_for_position_by_key(inode->i_sb, &key, &path); @@ -322,8 +361,14 @@ int reiserfs_allocate_blocks_for_region( } /* Now we want to check if transaction is too full, and if it is we restart it. This will also free the path. */ - if (journal_transaction_should_end(th, th->t_blocks_allocated)) - restart_transaction(th, inode, &path); + if (journal_transaction_should_end(th, th->t_blocks_allocated)) { + res = restart_transaction(th, inode, &path); + if (res) { + pathrelse (&path); + kfree(zeros); + goto error_exit; + } + } /* Well, need to recalculate path and stuff */ set_cpu_key_k_offset( &key, cpu_key_k_offset(&key) + (to_paste << inode->i_blkbits)); @@ -349,6 +394,7 @@ int reiserfs_allocate_blocks_for_region( // we are going to overwrite, so there is nothing to scan through for holes. for ( curr_block = 0, itempos = path.pos_in_item ; curr_block < blocks_to_allocate && res == POSITION_FOUND ; ) { retry: + if ( itempos >= ih_item_len(ih)/UNFM_P_SIZE ) { /* We run out of data in this indirect item, let's look for another one. */ @@ -526,8 +572,14 @@ error_exit_free_blocks: reiserfs_free_block(th, inode, le32_to_cpu(allocated_blocks[i]), 1); error_exit: - reiserfs_update_sd(th, inode); // update any changes we made to blk count - journal_end(th, inode->i_sb, JOURNAL_PER_BALANCE_CNT * 3 + 1); + if (th->t_trans_id) { + int err; + // update any changes we made to blk count + reiserfs_update_sd(th, inode); + err = journal_end(th, inode->i_sb, JOURNAL_PER_BALANCE_CNT * 3 + 1); + if (err) + res = err; + } reiserfs_write_unlock(inode->i_sb); kfree(allocated_blocks); @@ -603,12 +655,15 @@ int reiserfs_commit_page(struct inode *i int bh_per_page = PAGE_CACHE_SIZE / s->s_blocksize; struct reiserfs_transaction_handle th; th.t_trans_id = 0; + int ret = 0; blocksize = 1 << inode->i_blkbits; if (logit) { reiserfs_write_lock(s); - journal_begin(&th, s, bh_per_page + 1); + ret = journal_begin(&th, s, bh_per_page + 1); + if (ret) + goto drop_write_lock; reiserfs_update_inode_transaction(inode); } for(bh = head = page_buffers(page), block_start = 0; @@ -640,7 +695,8 @@ int reiserfs_commit_page(struct inode *i } } if (logit) { - journal_end(&th, s, bh_per_page + 1); + ret = journal_end(&th, s, bh_per_page + 1); +drop_write_lock: reiserfs_write_unlock(s); } /* @@ -651,7 +707,7 @@ int reiserfs_commit_page(struct inode *i */ if (!partial) SetPageUptodate(page); - return 0; + return ret; } @@ -717,7 +773,9 @@ int reiserfs_submit_file_region_for_writ reiserfs_write_lock(inode->i_sb); if (!sd_update) reiserfs_update_sd(th, inode); - journal_end(th, th->t_super, th->t_blocks_allocated); + status = journal_end(th, th->t_super, th->t_blocks_allocated); + if (status) + retval = status; reiserfs_write_unlock(inode->i_sb); } th->t_trans_id = 0; @@ -1100,6 +1158,7 @@ ssize_t reiserfs_file_write( struct file size_t already_written = 0; // Number of bytes already written to the file. loff_t pos; // Current position in the file. ssize_t res; // return value of various functions that we call. + int err = 0; struct inode *inode = file->f_dentry->d_inode; // Inode of the file that we are writing to. /* To simplify coding at this time, we store locked pages in array for now */ @@ -1114,24 +1173,40 @@ ssize_t reiserfs_file_write( struct file If we will crash while doing direct io, finish_unfinished will cut the garbage from the file end. */ reiserfs_write_lock(inode->i_sb); - journal_begin(&th, inode->i_sb, JOURNAL_PER_BALANCE_CNT ); + err = journal_begin(&th, inode->i_sb, JOURNAL_PER_BALANCE_CNT ); + if (err) { + reiserfs_write_unlock (inode->i_sb); + return err; + } reiserfs_update_inode_transaction(inode); add_save_link (&th, inode, 1 /* Truncate */); - journal_end(&th, inode->i_sb, JOURNAL_PER_BALANCE_CNT ); - reiserfs_write_unlock(inode->i_sb); after_file_end = 1; + err = journal_end(&th, inode->i_sb, JOURNAL_PER_BALANCE_CNT ); + reiserfs_write_unlock(inode->i_sb); + if (err) + return err; } result = generic_file_write(file, buf, count, ppos); if ( after_file_end ) { /* Now update i_size and remove the savelink */ struct reiserfs_transaction_handle th; reiserfs_write_lock(inode->i_sb); - journal_begin(&th, inode->i_sb, 1); + err = journal_begin(&th, inode->i_sb, 1); + if (err) { + reiserfs_write_unlock (inode->i_sb); + return err; + } reiserfs_update_inode_transaction(inode); reiserfs_update_sd(&th, inode); - journal_end(&th, inode->i_sb, 1); - remove_save_link (inode, 1/* truncate */); + err = journal_end(&th, inode->i_sb, 1); + if (err) { + reiserfs_write_unlock (inode->i_sb); + return err; + } + err = remove_save_link (inode, 1/* truncate */); reiserfs_write_unlock(inode->i_sb); + if (err) + return err; } return result; @@ -1280,8 +1355,12 @@ ssize_t reiserfs_file_write( struct file /* this is only true on error */ if (th.t_trans_id) { reiserfs_write_lock(inode->i_sb); - journal_end(&th, th.t_super, th.t_blocks_allocated); + err = journal_end(&th, th.t_super, th.t_blocks_allocated); reiserfs_write_unlock(inode->i_sb); + if (err) { + res = err; + goto out; + } } if ((file->f_flags & O_SYNC) || IS_SYNC(inode)) diff -puN fs/reiserfs/inode.c~reiserfs-add-i-o-error-handling-to-journal-operations fs/reiserfs/inode.c --- 25/fs/reiserfs/inode.c~reiserfs-add-i-o-error-handling-to-journal-operations 2004-10-05 11:11:22.910599888 -0700 +++ 25-akpm/fs/reiserfs/inode.c 2004-10-05 11:11:22.938595632 -0700 @@ -47,21 +47,32 @@ void reiserfs_delete_inode (struct inode reiserfs_delete_xattrs (inode); - journal_begin(&th, inode->i_sb, jbegin_count) ; + if (journal_begin(&th, inode->i_sb, jbegin_count)) { + up (&inode->i_sem); + goto out; + } reiserfs_update_inode_transaction(inode) ; - reiserfs_delete_object (&th, inode); + if (reiserfs_delete_object (&th, inode)) { + up (&inode->i_sem); + goto out; + } - journal_end(&th, inode->i_sb, jbegin_count) ; + if (journal_end(&th, inode->i_sb, jbegin_count)) { + up (&inode->i_sem); + goto out; + } up (&inode->i_sem); /* all items of file are deleted, so we can remove "save" link */ - remove_save_link (inode, 0/* not truncate */); + remove_save_link (inode, 0/* not truncate */); /* we can't do anything + * about an error here */ } else { /* no object items are in the tree */ ; } +out: clear_inode (inode); /* note this must go after the journal_end to prevent deadlock */ inode->i_blocks = 0; reiserfs_write_unlock(inode->i_sb); @@ -201,20 +212,28 @@ static int file_capable (struct inode * return 0; } -/*static*/ void restart_transaction(struct reiserfs_transaction_handle *th, +/*static*/ int restart_transaction(struct reiserfs_transaction_handle *th, struct inode *inode, struct path *path) { struct super_block *s = th->t_super ; int len = th->t_blocks_allocated ; + int err; + + BUG_ON (!th->t_trans_id); + BUG_ON (!th->t_refcount); /* we cannot restart while nested */ if (th->t_refcount > 1) { - return ; + return 0 ; } pathrelse(path) ; reiserfs_update_sd(th, inode) ; - journal_end(th, s, len) ; - journal_begin(th, s, JOURNAL_PER_BALANCE_CNT * 6) ; - reiserfs_update_inode_transaction(inode) ; + err = journal_end(th, s, len) ; + if (!err) { + err = journal_begin(th, s, JOURNAL_PER_BALANCE_CNT * 6) ; + if (!err) + reiserfs_update_inode_transaction(inode) ; + } + return err; } // it is called by get_block when create == 0. Returns block number @@ -443,9 +462,11 @@ static int reiserfs_get_blocks_direct_io ret = reiserfs_get_block(inode, iblock, bh_result, create | GET_BLOCK_NO_DANGLE) ; + if (ret) + goto out; /* don't allow direct io onto tail pages */ - if (ret == 0 && buffer_mapped(bh_result) && bh_result->b_blocknr == 0) { + if (buffer_mapped(bh_result) && bh_result->b_blocknr == 0) { /* make sure future calls to the direct io funcs for this offset ** in the file fail by unmapping the buffer */ @@ -455,11 +476,15 @@ static int reiserfs_get_blocks_direct_io /* Possible unpacked tail. Flush the data before pages have disappeared */ if (REISERFS_I(inode)->i_flags & i_pack_on_close_mask) { + int err; lock_kernel(); - reiserfs_commit_for_inode(inode); + err = reiserfs_commit_for_inode(inode); REISERFS_I(inode)->i_flags &= ~i_pack_on_close_mask; unlock_kernel(); + if (err < 0) + ret = err; } +out: return ret ; } @@ -539,6 +564,7 @@ static inline int _allocate_block(struct b_blocknr_t *allocated_block_nr, struct path * path, int flags) { + BUG_ON (!th->t_trans_id); #ifdef REISERFS_PREALLOCATE if (!(flags & GET_BLOCK_NO_ISEM)) { @@ -551,7 +577,7 @@ static inline int _allocate_block(struct int reiserfs_get_block (struct inode * inode, sector_t block, struct buffer_head * bh_result, int create) { - int repeat, retval; + int repeat, retval = 0; b_blocknr_t allocated_block_nr = 0;// b_blocknr_t is (unsigned) 32 bit int INITIALIZE_PATH(path); int pos_in_item; @@ -655,7 +681,9 @@ start_trans: ** research if we succeed on the second try */ SB_JOURNAL(inode->i_sb)->j_next_async_flush = 1; - restart_transaction(th, inode, &path) ; + retval = restart_transaction(th, inode, &path) ; + if (retval) + goto failure; repeat = _allocate_block(th, block, inode, &allocated_block_nr, NULL, create); if (repeat != NO_DISK_SPACE && repeat != QUOTA_EXCEEDED) { @@ -696,8 +724,9 @@ start_trans: } set_block_dev_mapped(bh_result, unfm_ptr, inode); pathrelse (&path); + retval = 0; if (!dangle && th) - reiserfs_end_persistent_transaction(th); + retval = reiserfs_end_persistent_transaction(th); reiserfs_write_unlock(inode->i_sb); @@ -705,7 +734,7 @@ start_trans: ** there is no need to make sure the inode is updated with this ** transaction */ - return 0; + return retval; } if (!th) { @@ -766,9 +795,12 @@ start_trans: * ugly, but we can only end the transaction if * we aren't nested */ + BUG_ON (!th->t_refcount); if (th->t_refcount == 1) { - reiserfs_end_persistent_transaction(th); + retval = reiserfs_end_persistent_transaction(th); th = NULL; + if (retval) + goto failure; } retval = convert_tail_for_hole(inode, bh_result, tail_offset) ; @@ -898,7 +930,9 @@ start_trans: ** ending their transaction will be able to continue. */ if (journal_transaction_should_end(th, th->t_blocks_allocated)) { - restart_transaction(th, inode, &path) ; + retval = restart_transaction(th, inode, &path) ; + if (retval) + goto failure; } /* inserting indirect pointers for a hole can take a ** long time. reschedule if needed @@ -929,10 +963,15 @@ start_trans: retval = 0; failure: - if (th && !dangle) { - reiserfs_update_sd(th, inode) ; - reiserfs_end_persistent_transaction(th); + if (th && (!dangle || (retval && !th->t_trans_id))) { + int err; + if (th->t_trans_id) + reiserfs_update_sd(th, inode); + err = reiserfs_end_persistent_transaction(th); + if (err) + retval = err; } + reiserfs_write_unlock(inode->i_sb); reiserfs_check_path(&path) ; return retval; @@ -1215,6 +1254,8 @@ void reiserfs_update_sd_size (struct rei struct item_head *ih, tmp_ih ; int retval; + BUG_ON (!th->t_trans_id); + make_cpu_key (&key, inode, SD_OFFSET, TYPE_STAT_DATA, 3);//key type is unimportant for(;;) { @@ -1508,12 +1549,8 @@ int reiserfs_write_inode (struct inode * struct reiserfs_transaction_handle th ; int jbegin_count = 1 ; - if (inode->i_sb->s_flags & MS_RDONLY) { - reiserfs_warning (inode->i_sb, - "clm-6005: writing inode %lu on readonly FS", - inode->i_ino) ; + if (inode->i_sb->s_flags & MS_RDONLY) return -EROFS; - } /* memory pressure can sometimes initiate write_inode calls with sync == 1, ** these cases are just when the system needs ram, not when the ** inode needs to reach disk for safety, and they can safely be @@ -1521,9 +1558,10 @@ int reiserfs_write_inode (struct inode * */ if (do_sync && !(current->flags & PF_MEMALLOC)) { reiserfs_write_lock(inode->i_sb); - journal_begin(&th, inode->i_sb, jbegin_count) ; - reiserfs_update_sd (&th, inode); - journal_end_sync(&th, inode->i_sb, jbegin_count) ; + if (!journal_begin(&th, inode->i_sb, jbegin_count)) { + reiserfs_update_sd (&th, inode); + journal_end_sync(&th, inode->i_sb, jbegin_count) ; + } reiserfs_write_unlock(inode->i_sb); } return 0; @@ -1551,6 +1589,8 @@ static int reiserfs_new_directory (struc char * body = empty_dir; struct cpu_key key; int retval; + + BUG_ON (!th->t_trans_id); _make_cpu_key (&key, KEY_FORMAT_3_5, le32_to_cpu (ih->ih_key.k_dir_id), le32_to_cpu (ih->ih_key.k_objectid), DOT_OFFSET, TYPE_DIRENTRY, 3/*key length*/); @@ -1602,6 +1642,8 @@ static int reiserfs_new_symlink (struct struct cpu_key key; int retval; + BUG_ON (!th->t_trans_id); + _make_cpu_key (&key, KEY_FORMAT_3_5, le32_to_cpu (ih->ih_key.k_dir_id), le32_to_cpu (ih->ih_key.k_objectid), @@ -1652,6 +1694,8 @@ int reiserfs_new_inode (struct reiserfs_ struct stat_data sd; int retval; int err; + + BUG_ON (!th->t_trans_id); if (!dir || !dir->i_nlink) { err = -EPERM; @@ -1926,7 +1970,7 @@ unlock: ** ** some code taken from block_truncate_page */ -void reiserfs_truncate_file(struct inode *p_s_inode, int update_timestamps) { +int reiserfs_truncate_file(struct inode *p_s_inode, int update_timestamps) { struct reiserfs_transaction_handle th ; /* we want the offset for the first byte after the end of the file */ unsigned long offset = p_s_inode->i_size & (PAGE_CACHE_SIZE - 1) ; @@ -1962,18 +2006,28 @@ void reiserfs_truncate_file(struct inode /* it is enough to reserve space in transaction for 2 balancings: one for "save" link adding and another for the first cut_from_item. 1 is for update_sd */ - journal_begin(&th, p_s_inode->i_sb, JOURNAL_PER_BALANCE_CNT * 2 + 1 ) ; + error = journal_begin (&th, p_s_inode->i_sb, + JOURNAL_PER_BALANCE_CNT * 2 + 1); + if (error) + goto out; reiserfs_update_inode_transaction(p_s_inode) ; if (update_timestamps) /* we are doing real truncate: if the system crashes before the last transaction of truncating gets committed - on reboot the file either appears truncated properly or not truncated at all */ add_save_link (&th, p_s_inode, 1); - reiserfs_do_truncate (&th, p_s_inode, page, update_timestamps) ; - journal_end(&th, p_s_inode->i_sb, JOURNAL_PER_BALANCE_CNT * 2 + 1 ) ; + error = reiserfs_do_truncate (&th, p_s_inode, page, update_timestamps) ; + if (error) + goto out; + error = journal_end (&th, p_s_inode->i_sb, JOURNAL_PER_BALANCE_CNT * 2 + 1); + if (error) + goto out; - if (update_timestamps) - remove_save_link (p_s_inode, 1/* truncate */); + if (update_timestamps) { + error = remove_save_link (p_s_inode, 1/* truncate */); + if (error) + goto out; + } if (page) { length = offset & (blocksize - 1) ; @@ -1995,6 +2049,14 @@ void reiserfs_truncate_file(struct inode } reiserfs_write_unlock(p_s_inode->i_sb); + return 0; +out: + if (page) { + unlock_page (page); + page_cache_release (page); + } + reiserfs_write_unlock(p_s_inode->i_sb); + return error; } static int map_block_for_writepage(struct inode *inode, @@ -2064,7 +2126,9 @@ research: if (!trans_running) { /* vs-3050 is gone, no need to drop the path */ - journal_begin(&th, inode->i_sb, jbegin_count) ; + retval = journal_begin(&th, inode->i_sb, jbegin_count) ; + if (retval) + goto out; reiserfs_update_inode_transaction(inode) ; trans_running = 1; if (fs_changed(fs_gen, inode->i_sb) && item_moved(&tmp_ih, &path)) { @@ -2104,7 +2168,9 @@ research: out: pathrelse(&path) ; if (trans_running) { - journal_end(&th, inode->i_sb, jbegin_count) ; + int err = journal_end(&th, inode->i_sb, jbegin_count) ; + if (err) + retval = err; trans_running = 0; } reiserfs_write_unlock(inode->i_sb); @@ -2210,7 +2276,11 @@ static int reiserfs_write_full_page(stru if (checked) { ClearPageChecked(page); reiserfs_write_lock(s); - journal_begin(&th, s, bh_per_page + 1); + error = journal_begin(&th, s, bh_per_page + 1); + if (error) { + reiserfs_write_unlock(s); + goto fail; + } reiserfs_update_inode_transaction(inode); } /* now go through and lock any dirty buffers on the page */ @@ -2245,8 +2315,10 @@ static int reiserfs_write_full_page(stru } while((bh = bh->b_this_page) != head); if (checked) { - journal_end(&th, s, bh_per_page + 1); + error = journal_end(&th, s, bh_per_page + 1); reiserfs_write_unlock(s); + if (error) + goto fail; } BUG_ON(PageWriteback(page)); set_page_writeback(page); @@ -2352,7 +2424,9 @@ int reiserfs_prepare_write(struct file * fix_tail_page_for_writing(page) ; if (reiserfs_transaction_running(inode->i_sb)) { struct reiserfs_transaction_handle *th; - th = (struct reiserfs_transaction_handle *)current->journal_info; + th = (struct reiserfs_transaction_handle *)current->journal_info; + BUG_ON (!th->t_refcount); + BUG_ON (!th->t_trans_id); old_ref = th->t_refcount; th->t_refcount++; } @@ -2374,9 +2448,12 @@ int reiserfs_prepare_write(struct file * if (old_ref) th->t_refcount--; else { + int err; reiserfs_write_lock(inode->i_sb); - reiserfs_end_persistent_transaction(th); + err = reiserfs_end_persistent_transaction(th); reiserfs_write_unlock(inode->i_sb); + if (err) + ret = err; } } } @@ -2417,20 +2494,28 @@ static int reiserfs_commit_write(struct (have_small_tails (inode->i_sb) && inode->i_size > i_block_size(inode)) ) REISERFS_I(inode)->i_flags &= ~i_pack_on_close_mask ; - journal_begin(&myth, inode->i_sb, 1) ; + ret = journal_begin(&myth, inode->i_sb, 1) ; + if (ret) { + reiserfs_write_unlock(inode->i_sb); + goto journal_error; + } reiserfs_update_inode_transaction(inode) ; inode->i_size = pos ; reiserfs_update_sd(&myth, inode) ; update_sd = 1; - journal_end(&myth, inode->i_sb, 1) ; + ret = journal_end(&myth, inode->i_sb, 1) ; reiserfs_write_unlock(inode->i_sb); + if (ret) + goto journal_error; } if (th) { reiserfs_write_lock(inode->i_sb); if (!update_sd) reiserfs_update_sd(th, inode) ; - reiserfs_end_persistent_transaction(th); + ret = reiserfs_end_persistent_transaction(th); reiserfs_write_unlock(inode->i_sb); + if (ret) + goto out; } /* we test for O_SYNC here so we can commit the transaction @@ -2438,10 +2523,22 @@ static int reiserfs_commit_write(struct */ if (f && (f->f_flags & O_SYNC)) { reiserfs_write_lock(inode->i_sb); - reiserfs_commit_for_inode(inode) ; + ret = reiserfs_commit_for_inode(inode) ; reiserfs_write_unlock(inode->i_sb); } +out: return ret ; + +journal_error: + if (th) { + reiserfs_write_lock(inode->i_sb); + if (!update_sd) + reiserfs_update_sd(th, inode) ; + ret = reiserfs_end_persistent_transaction(th); + reiserfs_write_unlock(inode->i_sb); + } + + return ret; } void sd_attrs_to_i_attrs( __u16 sd_attrs, struct inode *inode ) @@ -2667,11 +2764,16 @@ int reiserfs_setattr(struct dentry *dent if (attr->ia_size > inode->i_size) { error = generic_cont_expand(inode, attr->ia_size) ; if (REISERFS_I(inode)->i_prealloc_count > 0) { + int err; struct reiserfs_transaction_handle th ; /* we're changing at most 2 bitmaps, inode + super */ - journal_begin(&th, inode->i_sb, 4) ; - reiserfs_discard_prealloc (&th, inode); - journal_end(&th, inode->i_sb, 4) ; + err = journal_begin(&th, inode->i_sb, 4) ; + if (!err) { + reiserfs_discard_prealloc (&th, inode); + err = journal_end(&th, inode->i_sb, 4) ; + } + if (err) + error = err; } if (error) goto out; diff -puN fs/reiserfs/journal.c~reiserfs-add-i-o-error-handling-to-journal-operations fs/reiserfs/journal.c --- 25/fs/reiserfs/journal.c~reiserfs-add-i-o-error-handling-to-journal-operations 2004-10-05 11:11:22.912599584 -0700 +++ 25-akpm/fs/reiserfs/journal.c 2004-10-05 11:11:22.946594416 -0700 @@ -93,12 +93,6 @@ static struct workqueue_struct *commit_w #define COMMIT_NOW 2 /* end and commit this transaction */ #define WAIT 4 /* wait for the log blocks to hit the disk*/ -/* state bits for the journal */ -#define WRITERS_BLOCKED 1 /* set when new writers not allowed */ -#define WRITERS_QUEUED 2 /* set when log is full due to too many - * writers - */ - static int do_journal_end(struct reiserfs_transaction_handle *,struct super_block *,unsigned long nblocks,int flags) ; static int flush_journal_list(struct super_block *s, struct reiserfs_journal_list *jl, int flushall) ; static int flush_commit_list(struct super_block *s, struct reiserfs_journal_list *jl, int flushall) ; @@ -109,6 +103,18 @@ static int release_journal_dev( struct s static int dirty_one_transaction(struct super_block *s, struct reiserfs_journal_list *jl); static void flush_async_commits(void *p); +static void queue_log_writer(struct super_block *s); + +/* values for join in do_journal_begin_r */ +enum { + JBEGIN_REG = 0, /* regular journal begin */ + JBEGIN_JOIN = 1, /* join the running transaction if at all possible */ + JBEGIN_ABORT = 2, /* called from cleanup code, ignores aborted flag */ +}; + +static int do_journal_begin_r(struct reiserfs_transaction_handle *th, + struct super_block * p_s_sb, + unsigned long nblocks,int join); static void init_journal_hash(struct super_block *p_s_sb) { struct reiserfs_journal *journal = SB_JOURNAL (p_s_sb); @@ -771,7 +777,7 @@ static int write_ordered_buffers(spinloc { struct buffer_head *bh; struct reiserfs_jh *jh; - int ret = 0; + int ret = j->j_errno; struct buffer_chunk chunk; struct list_head tmp; INIT_LIST_HEAD(&tmp); @@ -795,11 +801,11 @@ static int write_ordered_buffers(spinloc cond_resched(); spin_lock(lock); goto loop_next; - } + } if (buffer_dirty(bh)) { list_del_init(&jh->list); list_add(&jh->list, &tmp); - add_to_chunk(&chunk, bh, lock, write_ordered_chunk); + add_to_chunk(&chunk, bh, lock, write_ordered_chunk); } else { reiserfs_free_jh(bh); unlock_buffer(bh); @@ -824,8 +830,9 @@ loop_next: wait_on_buffer(bh); spin_lock(lock); } - if (!buffer_uptodate(bh)) + if (!buffer_uptodate(bh)) { ret = -EIO; + } put_bh(bh); cond_resched_lock(lock); } @@ -917,6 +924,7 @@ static int flush_commit_list(struct supe unsigned long trans_id = jl->j_trans_id; struct reiserfs_journal *journal = SB_JOURNAL (s); int barrier = 0; + int retval = 0; reiserfs_check_lock_depth(s, "flush_commit_list") ; @@ -927,10 +935,8 @@ static int flush_commit_list(struct supe /* before we can put our commit blocks on disk, we have to make sure everyone older than ** us is on disk too */ - if (jl->j_len <= 0) - BUG(); - if (trans_id == journal->j_trans_id) - BUG(); + BUG_ON (jl->j_len <= 0); + BUG_ON (trans_id == journal->j_trans_id); get_journal_list(jl); if (flushall) { @@ -946,8 +952,7 @@ static int flush_commit_list(struct supe up(&jl->j_commit_lock); goto put_jl; } - if (jl->j_trans_id == 0) - BUG(); + BUG_ON (jl->j_trans_id == 0); /* this commit is done, exit */ if (atomic_read(&(jl->j_commit_left)) <= 0) { @@ -964,8 +969,7 @@ static int flush_commit_list(struct supe journal, jl, &jl->j_bh_list); lock_kernel(); } - if (!list_empty(&jl->j_bh_list)) - BUG(); + BUG_ON (!list_empty(&jl->j_bh_list)); /* * for the description block and all the log blocks, submit any buffers * that haven't already reached the disk @@ -975,7 +979,7 @@ static int flush_commit_list(struct supe bn = SB_ONDISK_JOURNAL_1st_BLOCK(s) + (jl->j_start+i) % SB_ONDISK_JOURNAL_SIZE(s); tbh = journal_find_get_block(s, bn) ; - if (buffer_dirty(tbh)) + if (buffer_dirty(tbh)) /* redundant, ll_rw_block() checks */ ll_rw_block(WRITE, 1, &tbh) ; put_bh(tbh) ; } @@ -1003,18 +1007,20 @@ static int flush_commit_list(struct supe // since we're using ll_rw_blk above, it might have skipped over // a locked buffer. Double check here // - if (buffer_dirty(tbh)) + if (buffer_dirty(tbh)) /* redundant, sync_dirty_buffer() checks */ sync_dirty_buffer(tbh); - if (!buffer_uptodate(tbh)) { - reiserfs_panic(s, "journal-601, buffer write failed\n") ; + if (unlikely (!buffer_uptodate(tbh))) { +#ifdef CONFIG_REISERFS_CHECK + reiserfs_warning(s, "journal-601, buffer write failed") ; +#endif + retval = -EIO; } put_bh(tbh) ; /* once for journal_find_get_block */ put_bh(tbh) ; /* once due to original getblk in do_journal_end */ atomic_dec(&(jl->j_commit_left)) ; } - if (atomic_read(&(jl->j_commit_left)) != 1) - BUG(); + BUG_ON (atomic_read(&(jl->j_commit_left)) != 1); if (!barrier) { if (buffer_dirty(jl->j_commit_bh)) @@ -1025,8 +1031,15 @@ static int flush_commit_list(struct supe wait_on_buffer(jl->j_commit_bh); check_barrier_completion(s, jl->j_commit_bh); - if (!buffer_uptodate(jl->j_commit_bh)) { - reiserfs_panic(s, "journal-615: buffer write failed\n") ; + + /* If there was a write error in the journal - we can't commit this + * transaction - it will be invalid and, if successful, will just end + * up propogating the write error out to the filesystem. */ + if (unlikely (!buffer_uptodate(jl->j_commit_bh))) { +#ifdef CONFIG_REISERFS_CHECK + reiserfs_warning(s, "journal-615: buffer write failed") ; +#endif + retval = -EIO; } bforget(jl->j_commit_bh) ; if (journal->j_last_commit_id != 0 && @@ -1040,8 +1053,11 @@ static int flush_commit_list(struct supe /* now, every commit block is on the disk. It is safe to allow blocks freed during this transaction to be reallocated */ cleanup_freed_for_journal_list(s, jl) ; + retval = retval ? retval : journal->j_errno; + /* mark the metadata dirty */ - dirty_one_transaction(s, jl); + if (!retval) + dirty_one_transaction(s, jl); atomic_dec(&(jl->j_commit_left)) ; if (flushall) { @@ -1050,7 +1066,10 @@ static int flush_commit_list(struct supe up(&jl->j_commit_lock); put_jl: put_journal_list(s, jl); - return 0 ; + + if (retval) + reiserfs_abort (s, retval, "Journal write error in %s", __FUNCTION__); + return retval; } /* @@ -1113,11 +1132,18 @@ static void remove_all_from_journal_list static int _update_journal_header_block(struct super_block *p_s_sb, unsigned long offset, unsigned long trans_id) { struct reiserfs_journal_header *jh ; struct reiserfs_journal *journal = SB_JOURNAL (p_s_sb); + + if (reiserfs_is_journal_aborted (journal)) + return -EIO; + if (trans_id >= journal->j_last_flush_trans_id) { if (buffer_locked((journal->j_header_bh))) { wait_on_buffer((journal->j_header_bh)) ; - if (!buffer_uptodate(journal->j_header_bh)) { - reiserfs_panic(p_s_sb, "journal-699: buffer write failed\n") ; + if (unlikely (!buffer_uptodate(journal->j_header_bh))) { +#ifdef CONFIG_REISERFS_CHECK + reiserfs_warning (p_s_sb, "journal-699: buffer write failed") ; +#endif + return -EIO; } } journal->j_last_flush_trans_id = trans_id ; @@ -1154,10 +1180,7 @@ sync: static int update_journal_header_block(struct super_block *p_s_sb, unsigned long offset, unsigned long trans_id) { - if (_update_journal_header_block(p_s_sb, offset, trans_id)) { - reiserfs_panic(p_s_sb, "journal-712: buffer write failed\n") ; - } - return 0 ; + return _update_journal_header_block(p_s_sb, offset, trans_id); } /* ** flush any and all journal lists older than you are @@ -1176,8 +1199,12 @@ static int flush_older_journal_lists(str */ restart: entry = journal->j_journal_list.next; + /* Did we wrap? */ + if (entry == &journal->j_journal_list) + return 0; other_jl = JOURNAL_LIST_ENTRY(entry); if (other_jl->j_trans_id < trans_id) { + BUG_ON (other_jl->j_refcount <= 0); /* do not flush all */ flush_journal_list(p_s_sb, other_jl, 0) ; @@ -1215,17 +1242,15 @@ static int flush_journal_list(struct sup struct buffer_head *saved_bh ; unsigned long j_len_saved = jl->j_len ; struct reiserfs_journal *journal = SB_JOURNAL (s); + int err = 0; - if (j_len_saved <= 0) { - BUG(); - } + BUG_ON (j_len_saved <= 0); if (atomic_read(&journal->j_wcount) != 0) { reiserfs_warning(s, "clm-2048: flush_journal_list called with wcount %d", atomic_read(&journal->j_wcount)) ; } - if (jl->j_trans_id == 0) - BUG(); + BUG_ON (jl->j_trans_id == 0); /* if flushall == 0, the lock is already held */ if (flushall) { @@ -1251,7 +1276,7 @@ static int flush_journal_list(struct sup */ flush_commit_list(s, jl, 1) ; - if (!(jl->j_state & LIST_DIRTY)) + if (!(jl->j_state & LIST_DIRTY) && !reiserfs_is_journal_aborted (journal)) BUG(); /* are we done now? */ @@ -1275,6 +1300,11 @@ static int flush_journal_list(struct sup if (cn->blocknr == 0) { goto free_cnode ; } + + /* This transaction failed commit. Don't write out to the disk */ + if (!(jl->j_state & LIST_DIRTY)) + goto free_cnode; + pjl = find_newer_jl_for_cn(cn) ; /* the order is important here. We check pjl to make sure we ** don't clear BH_JDirty_wait if we aren't the one writing this @@ -1289,8 +1319,7 @@ static int flush_journal_list(struct sup get_bh(saved_bh) ; if (buffer_journal_dirty(saved_bh)) { - if (!can_dirty(cn)) - BUG(); + BUG_ON (!can_dirty (cn)); was_jwait = 1 ; was_dirty = 1 ; } else if (can_dirty(cn)) { @@ -1330,8 +1359,7 @@ static int flush_journal_list(struct sup get_bh(saved_bh) ; set_bit(BLOCK_NEEDS_FLUSH, &cn->state) ; lock_buffer(saved_bh); - if (cn->blocknr != saved_bh->b_blocknr) - BUG(); + BUG_ON (cn->blocknr != saved_bh->b_blocknr); if (buffer_dirty(saved_bh)) submit_logged_buffer(saved_bh) ; else @@ -1363,14 +1391,16 @@ free_cnode: if (!cn->bh) { reiserfs_panic(s, "journal-1012: cn->bh is NULL\n") ; } - if (!buffer_uptodate(cn->bh)) { - reiserfs_panic(s, "journal-949: buffer write failed\n") ; - } + if (unlikely (!buffer_uptodate(cn->bh))) { +#ifdef CONFIG_REISERFS_CHECK + reiserfs_warning(s, "journal-949: buffer write failed\n") ; +#endif + err = -EIO; + } /* note, we must clear the JDirty_wait bit after the up to date ** check, otherwise we race against our flushpage routine */ - if (!test_clear_buffer_journal_dirty (cn->bh)) - BUG(); + BUG_ON (!test_clear_buffer_journal_dirty (cn->bh)); /* undo the inc from journal_mark_dirty */ put_bh(cn->bh) ; @@ -1380,7 +1410,11 @@ free_cnode: } } + if (err) + reiserfs_abort (s, -EIO, "Write error while pushing transaction to disk in %s", __FUNCTION__); flush_older_and_return: + + /* before we can update the journal header block, we _must_ flush all ** real blocks from all older transactions to disk. This is because ** once the header block is updated, this transaction will not be @@ -1390,6 +1424,7 @@ flush_older_and_return: flush_older_journal_lists(s, jl); } + err = journal->j_errno; /* before we can remove everything from the hash tables for this ** transaction, we must make sure it can never be replayed ** @@ -1398,11 +1433,13 @@ flush_older_and_return: ** we only need to update the journal header block for the last list ** being flushed */ - if (flushall) { - update_journal_header_block(s, (jl->j_start + jl->j_len + 2) % SB_ONDISK_JOURNAL_SIZE(s), jl->j_trans_id) ; + if (!err && flushall) { + err = update_journal_header_block(s, (jl->j_start + jl->j_len + 2) % SB_ONDISK_JOURNAL_SIZE(s), jl->j_trans_id) ; + if (err) + reiserfs_abort (s, -EIO, "Write error while updating journal header in %s", __FUNCTION__); } remove_all_from_journal_list(s, jl, 0) ; - list_del(&jl->j_list); + list_del_init(&jl->j_list); journal->j_num_lists--; del_from_work_list(s, jl); @@ -1427,7 +1464,7 @@ flush_older_and_return: put_journal_list(s, jl); if (flushall) up(&journal->j_flush_sem); - return 0 ; + return err ; } static int write_one_transaction(struct super_block *s, @@ -1497,8 +1534,7 @@ static int dirty_one_transaction(struct pjl = find_newer_jl_for_cn(cn) ; if (!pjl && cn->blocknr && cn->bh && buffer_journal_dirty(cn->bh)) { - if (!can_dirty(cn)) - BUG(); + BUG_ON (!can_dirty(cn)); /* if the buffer is prepared, it will either be logged * or restored. If restored, we need to make sure * it actually gets marked dirty @@ -1543,7 +1579,7 @@ static int kupdate_transactions(struct s (!num_trans && written < num_blocks)) { if (jl->j_len == 0 || (jl->j_state & LIST_TOUCHED) || - atomic_read(&jl->j_commit_left)) + atomic_read(&jl->j_commit_left) || !(jl->j_state & LIST_DIRTY)) { del_from_work_list(s, jl); break; @@ -1693,18 +1729,33 @@ static void free_journal_ram(struct supe */ static int do_journal_release(struct reiserfs_transaction_handle *th, struct super_block *p_s_sb, int error) { struct reiserfs_transaction_handle myth ; + int flushed = 0; + struct reiserfs_journal *journal = SB_JOURNAL(p_s_sb); /* we only want to flush out transactions if we were called with error == 0 */ if (!error && !(p_s_sb->s_flags & MS_RDONLY)) { /* end the current trans */ + BUG_ON (!th->t_trans_id); do_journal_end(th, p_s_sb,10, FLUSH_ALL) ; /* make sure something gets logged to force our way into the flush code */ - journal_join(&myth, p_s_sb, 1) ; - reiserfs_prepare_for_journal(p_s_sb, SB_BUFFER_WITH_SB(p_s_sb), 1) ; - journal_mark_dirty(&myth, p_s_sb, SB_BUFFER_WITH_SB(p_s_sb)) ; - do_journal_end(&myth, p_s_sb,1, FLUSH_ALL) ; + if (!journal_join(&myth, p_s_sb, 1)) { + reiserfs_prepare_for_journal(p_s_sb, SB_BUFFER_WITH_SB(p_s_sb), 1) ; + journal_mark_dirty(&myth, p_s_sb, SB_BUFFER_WITH_SB(p_s_sb)) ; + do_journal_end(&myth, p_s_sb,1, FLUSH_ALL) ; + flushed = 1; + } + } + + /* this also catches errors during the do_journal_end above */ + if (!error && reiserfs_is_journal_aborted(journal)) { + memset(&myth, 0, sizeof(myth)); + if (!journal_join_abort(&myth, p_s_sb, 1)) { + reiserfs_prepare_for_journal(p_s_sb, SB_BUFFER_WITH_SB(p_s_sb), 1) ; + journal_mark_dirty(&myth, p_s_sb, SB_BUFFER_WITH_SB(p_s_sb)) ; + do_journal_end(&myth, p_s_sb, 1, FLUSH_ALL) ; + } } reiserfs_mounted_fs_count-- ; @@ -2314,6 +2365,7 @@ int journal_init(struct super_block *p_s INIT_LIST_HEAD (&journal->j_prealloc_list); INIT_LIST_HEAD(&journal->j_working_list); INIT_LIST_HEAD(&journal->j_journal_list); + journal->j_persistent_trans = 0; if (reiserfs_allocate_list_bitmaps(p_s_sb, journal->j_list_bitmap, SB_BMAP_NR(p_s_sb))) @@ -2492,6 +2544,7 @@ int journal_transaction_should_end(struc struct reiserfs_journal *journal = SB_JOURNAL (th->t_super); time_t now = get_seconds() ; /* cannot restart while nested */ + BUG_ON (!th->t_trans_id); if (th->t_refcount > 1) return 0 ; if ( journal->j_must_wait > 0 || @@ -2509,8 +2562,9 @@ int journal_transaction_should_end(struc */ void reiserfs_block_writes(struct reiserfs_transaction_handle *th) { struct reiserfs_journal *journal = SB_JOURNAL (th->t_super); + BUG_ON (!th->t_trans_id); journal->j_must_wait = 1 ; - set_bit(WRITERS_BLOCKED, &journal->j_state) ; + set_bit(J_WRITERS_BLOCKED, &journal->j_state) ; return ; } @@ -2519,7 +2573,7 @@ void reiserfs_block_writes(struct reiser */ void reiserfs_allow_writes(struct super_block *s) { struct reiserfs_journal *journal = SB_JOURNAL (s); - clear_bit(WRITERS_BLOCKED, &journal->j_state) ; + clear_bit(J_WRITERS_BLOCKED, &journal->j_state) ; wake_up(&journal->j_join_wait) ; } @@ -2529,13 +2583,13 @@ void reiserfs_allow_writes(struct super_ void reiserfs_wait_on_write_block(struct super_block *s) { struct reiserfs_journal *journal = SB_JOURNAL (s); wait_event(journal->j_join_wait, - !test_bit(WRITERS_BLOCKED, &journal->j_state)) ; + !test_bit(J_WRITERS_BLOCKED, &journal->j_state)) ; } static void queue_log_writer(struct super_block *s) { wait_queue_t wait; struct reiserfs_journal *journal = SB_JOURNAL (s); - set_bit(WRITERS_QUEUED, &journal->j_state); + set_bit(J_WRITERS_QUEUED, &journal->j_state); /* * we don't want to use wait_event here because @@ -2544,7 +2598,7 @@ static void queue_log_writer(struct supe init_waitqueue_entry(&wait, current); add_wait_queue(&journal->j_join_wait, &wait); set_current_state(TASK_UNINTERRUPTIBLE); - if (test_bit(WRITERS_QUEUED, &journal->j_state)) + if (test_bit(J_WRITERS_QUEUED, &journal->j_state)) schedule(); current->state = TASK_RUNNING; remove_wait_queue(&journal->j_join_wait, &wait); @@ -2552,7 +2606,7 @@ static void queue_log_writer(struct supe static void wake_queued_writers(struct super_block *s) { struct reiserfs_journal *journal = SB_JOURNAL (s); - if (test_and_clear_bit(WRITERS_QUEUED, &journal->j_state)) + if (test_and_clear_bit(J_WRITERS_QUEUED, &journal->j_state)) wake_up(&journal->j_join_wait); } @@ -2590,10 +2644,9 @@ static int do_journal_begin_r(struct rei struct reiserfs_journal *journal = SB_JOURNAL(p_s_sb); struct reiserfs_transaction_handle myth; int sched_count = 0; + int retval; reiserfs_check_lock_depth(p_s_sb, "journal_begin") ; - RFALSE( p_s_sb->s_flags & MS_RDONLY, - "clm-2078: calling journal_begin on readonly FS") ; PROC_INFO_INC( p_s_sb, journal.journal_being ); /* set here for journal_join */ @@ -2602,9 +2655,14 @@ static int do_journal_begin_r(struct rei relock: lock_journal(p_s_sb) ; + if (join != JBEGIN_ABORT && reiserfs_is_journal_aborted (journal)) { + unlock_journal (p_s_sb); + retval = journal->j_errno; + goto out_fail; + } journal->j_bcount++; - if (test_bit(WRITERS_BLOCKED, &journal->j_state)) { + if (test_bit(J_WRITERS_BLOCKED, &journal->j_state)) { unlock_journal(p_s_sb) ; reiserfs_wait_on_write_block(p_s_sb) ; PROC_INFO_INC( p_s_sb, journal.journal_relock_writers ); @@ -2647,15 +2705,20 @@ relock: } goto relock; } - journal_join(&myth, p_s_sb, 1) ; + retval = journal_join(&myth, p_s_sb, 1) ; + if (retval) + goto out_fail; /* someone might have ended the transaction while we joined */ if (old_trans_id != journal->j_trans_id) { - do_journal_end(&myth, p_s_sb, 1, 0) ; + retval = do_journal_end(&myth, p_s_sb, 1, 0) ; } else { - do_journal_end(&myth, p_s_sb, 1, COMMIT_NOW) ; + retval = do_journal_end(&myth, p_s_sb, 1, COMMIT_NOW) ; } + if (retval) + goto out_fail; + PROC_INFO_INC( p_s_sb, journal.journal_relock_wcount ); goto relock ; } @@ -2669,7 +2732,16 @@ relock: th->t_blocks_allocated = nblocks ; th->t_trans_id = journal->j_trans_id ; unlock_journal(p_s_sb) ; + INIT_LIST_HEAD (&th->t_list); return 0 ; + +out_fail: + memset (th, 0, sizeof (*th)); + /* Re-set th->t_super, so we can properly keep track of how many + * persistent transactions there are. We need to do this so if this + * call is part of a failed restart_transaction, we can free it later */ + th->t_super = p_s_sb; + return retval; } struct reiserfs_transaction_handle * @@ -2696,16 +2768,23 @@ reiserfs_persistent_transaction(struct s reiserfs_kfree(th, sizeof(struct reiserfs_transaction_handle), s) ; return NULL; } + + SB_JOURNAL(s)->j_persistent_trans++; return th ; } int reiserfs_end_persistent_transaction(struct reiserfs_transaction_handle *th) { struct super_block *s = th->t_super; - int ret; - ret = journal_end(th, th->t_super, th->t_blocks_allocated); - if (th->t_refcount == 0) + int ret = 0; + if (th->t_trans_id) + ret = journal_end(th, th->t_super, th->t_blocks_allocated); + else + ret = -EIO; + if (th->t_refcount == 0) { + SB_JOURNAL(s)->j_persistent_trans--; reiserfs_kfree(th, sizeof(struct reiserfs_transaction_handle), s) ; + } return ret; } @@ -2719,7 +2798,20 @@ static int journal_join(struct reiserfs_ if (cur_th && cur_th->t_refcount > 1) { BUG() ; } - return do_journal_begin_r(th, p_s_sb, nblocks, 1) ; + return do_journal_begin_r(th, p_s_sb, nblocks, JBEGIN_JOIN) ; +} + +int journal_join_abort(struct reiserfs_transaction_handle *th, struct super_block *p_s_sb, unsigned long nblocks) { + struct reiserfs_transaction_handle *cur_th = current->journal_info; + + /* this keeps do_journal_end from NULLing out the current->journal_info + ** pointer + */ + th->t_handle_save = cur_th ; + if (cur_th && cur_th->t_refcount > 1) { + BUG() ; + } + return do_journal_begin_r(th, p_s_sb, nblocks, JBEGIN_ABORT) ; } int journal_begin(struct reiserfs_transaction_handle *th, struct super_block * p_s_sb, unsigned long nblocks) { @@ -2730,6 +2822,7 @@ int journal_begin(struct reiserfs_transa if (cur_th) { /* we are nesting into the current transaction */ if (cur_th->t_super == p_s_sb) { + BUG_ON (!cur_th->t_refcount); cur_th->t_refcount++ ; memcpy(th, cur_th, sizeof(*th)); if (th->t_refcount <= 1) @@ -2747,9 +2840,18 @@ int journal_begin(struct reiserfs_transa } else { current->journal_info = th; } - ret = do_journal_begin_r(th, p_s_sb, nblocks, 0) ; + ret = do_journal_begin_r(th, p_s_sb, nblocks, JBEGIN_REG) ; if (current->journal_info != th) BUG() ; + + /* I guess this boils down to being the reciprocal of clm-2100 above. + * If do_journal_begin_r fails, we need to put it back, since journal_end + * won't be called to do it. */ + if (ret) + current->journal_info = th->t_handle_save; + else + BUG_ON (!th->t_refcount); + return ret ; } @@ -2767,12 +2869,14 @@ int journal_mark_dirty(struct reiserfs_t struct reiserfs_journal_cnode *cn = NULL; int count_already_incd = 0 ; int prepared = 0 ; + BUG_ON (!th->t_trans_id); PROC_INFO_INC( p_s_sb, journal.mark_dirty ); if (th->t_trans_id != journal->j_trans_id) { reiserfs_panic(th->t_super, "journal-1577: handle trans id %ld != current trans id %ld\n", th->t_trans_id, journal->j_trans_id); } + p_s_sb->s_dirt = 1; prepared = test_clear_buffer_journal_prepared (bh); @@ -2860,6 +2964,11 @@ int journal_end(struct reiserfs_transact reiserfs_warning (p_s_sb, "REISER-NESTING: th NULL, refcount %d", th->t_refcount); + if (!th->t_trans_id) { + WARN_ON (1); + return -EIO; + } + th->t_refcount--; if (th->t_refcount > 0) { struct reiserfs_transaction_handle *cur_th = current->journal_info ; @@ -2976,6 +3085,7 @@ static int can_dirty(struct reiserfs_jou int journal_end_sync(struct reiserfs_transaction_handle *th, struct super_block *p_s_sb, unsigned long nblocks) { struct reiserfs_journal *journal = SB_JOURNAL (p_s_sb); + BUG_ON (!th->t_trans_id); /* you can sync while nested, very, very bad */ if (th->t_refcount > 1) { BUG() ; @@ -3008,7 +3118,7 @@ static void flush_async_commits(void *p) * this is a little racey, but there's no harm in missing * the filemap_fdata_write */ - if (!atomic_read(&journal->j_async_throttle)) { + if (!atomic_read(&journal->j_async_throttle) && !reiserfs_is_journal_aborted (journal)) { atomic_inc(&journal->j_async_throttle); filemap_fdatawrite(p_s_sb->s_bdev->bd_inode->i_mapping); atomic_dec(&journal->j_async_throttle); @@ -3040,14 +3150,15 @@ int reiserfs_flush_old_commits(struct su journal->j_len > 0 && (now - journal->j_trans_start_time) > journal->j_max_trans_age) { - journal_join(&th, p_s_sb, 1) ; - reiserfs_prepare_for_journal(p_s_sb, SB_BUFFER_WITH_SB(p_s_sb), 1) ; - journal_mark_dirty(&th, p_s_sb, SB_BUFFER_WITH_SB(p_s_sb)) ; - - /* we're only being called from kreiserfsd, it makes no sense to do - ** an async commit so that kreiserfsd can do it later - */ - do_journal_end(&th, p_s_sb,1, COMMIT_NOW | WAIT) ; + if (!journal_join(&th, p_s_sb, 1)) { + reiserfs_prepare_for_journal(p_s_sb, SB_BUFFER_WITH_SB(p_s_sb), 1) ; + journal_mark_dirty(&th, p_s_sb, SB_BUFFER_WITH_SB(p_s_sb)) ; + + /* we're only being called from kreiserfsd, it makes no sense to do + ** an async commit so that kreiserfsd can do it later + */ + do_journal_end(&th, p_s_sb,1, COMMIT_NOW | WAIT) ; + } } return p_s_sb->s_dirt; } @@ -3073,6 +3184,8 @@ static int check_journal_end(struct reis struct reiserfs_journal_list *jl; struct reiserfs_journal *journal = SB_JOURNAL (p_s_sb); + BUG_ON (!th->t_trans_id); + if (th->t_trans_id != journal->j_trans_id) { reiserfs_panic(th->t_super, "journal-1577: handle trans id %ld != current trans id %ld\n", th->t_trans_id, journal->j_trans_id); @@ -3178,6 +3291,7 @@ int journal_mark_freed(struct reiserfs_t struct buffer_head *bh = NULL ; struct reiserfs_list_bitmap *jb = NULL ; int cleaned = 0 ; + BUG_ON (!th->t_trans_id); cn = get_journal_hash_dev(p_s_sb, journal->j_hash_table, blocknr); if (cn && cn->bh) { @@ -3269,18 +3383,21 @@ static int __commit_trans_jl(struct inod goto flush_commit_only; } - journal_begin(&th, sb, 1) ; + ret = journal_begin(&th, sb, 1) ; + if (ret) + return ret; /* someone might have ended this transaction while we joined */ if (journal->j_trans_id != id) { reiserfs_prepare_for_journal(sb, SB_BUFFER_WITH_SB(sb), 1) ; journal_mark_dirty(&th, sb, SB_BUFFER_WITH_SB(sb)) ; - journal_end(&th, sb, 1) ; + ret = journal_end(&th, sb, 1) ; goto flush_commit_only; } - journal_end_sync(&th, sb, 1) ; - ret = 1; + ret = journal_end_sync(&th, sb, 1) ; + if (!ret) + ret = 1; } else { /* this gets tricky, we have to make sure the journal list in @@ -3297,6 +3414,8 @@ flush_commit_only: if (atomic_read(&jl->j_commit_left) > 1) ret = 1; flush_commit_list(sb, jl, 1) ; + if (journal->j_errno) + ret = journal->j_errno; } } /* otherwise the list is gone, and long since committed */ @@ -3390,6 +3509,9 @@ static void flush_old_journal_lists(stru ** If no_async, won't return until all commit blocks are on disk. ** ** keep reading, there are comments as you go along +** +** If the journal is aborted, we just clean up. Things like flushing +** journal lists, etc just won't happen. */ static int do_journal_end(struct reiserfs_transaction_handle *th, struct super_block * p_s_sb, unsigned long nblocks, int flags) { @@ -3411,8 +3533,8 @@ static int do_journal_end(struct reiserf unsigned long commit_trans_id; int trans_half; - if (th->t_refcount > 1) - BUG() ; + BUG_ON (th->t_refcount > 1); + BUG_ON (!th->t_trans_id); current->journal_info = th->t_handle_save; reiserfs_check_lock_depth(p_s_sb, "journal end"); @@ -3707,7 +3829,7 @@ first_jl: atomic_set(&(journal->j_jlock), 0) ; unlock_journal(p_s_sb) ; /* wake up any body waiting to join. */ - clear_bit(WRITERS_QUEUED, &journal->j_state); + clear_bit(J_WRITERS_QUEUED, &journal->j_state); wake_up(&(journal->j_join_wait)) ; if (!flush && wait_on_commit && @@ -3716,6 +3838,49 @@ first_jl: } out: reiserfs_check_lock_depth(p_s_sb, "journal end2"); - th->t_trans_id = 0; - return 0 ; + + memset (th, 0, sizeof (*th)); + /* Re-set th->t_super, so we can properly keep track of how many + * persistent transactions there are. We need to do this so if this + * call is part of a failed restart_transaction, we can free it later */ + th->t_super = p_s_sb; + + return journal->j_errno; +} + +void +__reiserfs_journal_abort_hard (struct super_block *sb) +{ + struct reiserfs_journal *journal = SB_JOURNAL (sb); + if (test_bit (J_ABORTED, &journal->j_state)) + return; + + printk (KERN_CRIT "REISERFS: Aborting journal for filesystem on %s\n", + reiserfs_bdevname (sb)); + + sb->s_flags |= MS_RDONLY; + set_bit (J_ABORTED, &journal->j_state); + +#ifdef CONFIG_REISERFS_CHECK + dump_stack(); +#endif +} + +void +__reiserfs_journal_abort_soft (struct super_block *sb, int errno) +{ + struct reiserfs_journal *journal = SB_JOURNAL (sb); + if (test_bit (J_ABORTED, &journal->j_state)) + return; + + if (!journal->j_errno) + journal->j_errno = errno; + + __reiserfs_journal_abort_hard (sb); +} + +void +reiserfs_journal_abort (struct super_block *sb, int errno) +{ + return __reiserfs_journal_abort_soft (sb, errno); } diff -puN fs/reiserfs/namei.c~reiserfs-add-i-o-error-handling-to-journal-operations fs/reiserfs/namei.c --- 25/fs/reiserfs/namei.c~reiserfs-add-i-o-error-handling-to-journal-operations 2004-10-05 11:11:22.914599280 -0700 +++ 25-akpm/fs/reiserfs/namei.c 2004-10-05 11:11:22.949593960 -0700 @@ -430,6 +430,7 @@ static int reiserfs_add_entry (struct re int buflen, paste_size; int retval; + BUG_ON (!th->t_trans_id); /* cannot allow items to be added into a busy deleted directory */ if (!namelen) @@ -606,16 +607,21 @@ static int reiserfs_create (struct inode if (locked) reiserfs_write_lock_xattrs (dir->i_sb); - journal_begin(&th, dir->i_sb, jbegin_count) ; - retval = reiserfs_new_inode (&th, dir, mode, NULL, 0/*i_size*/, dentry, inode); - - if (locked) - reiserfs_write_unlock_xattrs (dir->i_sb); - + retval = journal_begin(&th, dir->i_sb, jbegin_count); if (retval) { + drop_new_inode (inode); goto out_failed; } + + retval = reiserfs_new_inode (&th, dir, mode, 0, 0/*i_size*/, dentry, inode); + if (retval) + goto out_failed; + if (locked) { + reiserfs_write_unlock_xattrs (dir->i_sb); + locked = 0; + } + inode->i_op = &reiserfs_file_inode_operations; inode->i_fop = &reiserfs_file_operations; inode->i_mapping->a_ops = &reiserfs_address_space_operations ; @@ -623,9 +629,12 @@ static int reiserfs_create (struct inode retval = reiserfs_add_entry (&th, dir, dentry->d_name.name, dentry->d_name.len, inode, 1/*visible*/); if (retval) { + int err; inode->i_nlink--; reiserfs_update_sd (&th, inode); - journal_end(&th, dir->i_sb, jbegin_count) ; + err = journal_end(&th, dir->i_sb, jbegin_count) ; + if (err) + retval = err; iput (inode); goto out_failed; } @@ -633,9 +642,11 @@ static int reiserfs_create (struct inode reiserfs_update_inode_transaction(dir) ; d_instantiate(dentry, inode); - journal_end(&th, dir->i_sb, jbegin_count) ; + retval = journal_end(&th, dir->i_sb, jbegin_count) ; out_failed: + if (locked) + reiserfs_write_unlock_xattrs (dir->i_sb); reiserfs_write_unlock(dir->i_sb); return retval; } @@ -666,17 +677,23 @@ static int reiserfs_mknod (struct inode if (locked) reiserfs_write_lock_xattrs (dir->i_sb); - journal_begin(&th, dir->i_sb, jbegin_count) ; + retval = journal_begin(&th, dir->i_sb, jbegin_count) ; + if (retval) { + drop_new_inode (inode); + goto out_failed; + } retval = reiserfs_new_inode (&th, dir, mode, NULL, 0/*i_size*/, dentry, inode); - - if (locked) - reiserfs_write_unlock_xattrs (dir->i_sb); - if (retval) { goto out_failed; } + if (locked) { + reiserfs_write_unlock_xattrs (dir->i_sb); + locked = 0; + } + + inode->i_op = &reiserfs_special_inode_operations; init_special_inode(inode, inode->i_mode, rdev) ; @@ -689,17 +706,22 @@ static int reiserfs_mknod (struct inode retval = reiserfs_add_entry (&th, dir, dentry->d_name.name, dentry->d_name.len, inode, 1/*visible*/); if (retval) { + int err; inode->i_nlink--; reiserfs_update_sd (&th, inode); - journal_end(&th, dir->i_sb, jbegin_count) ; + err = journal_end(&th, dir->i_sb, jbegin_count) ; + if (err) + retval = err; iput (inode); goto out_failed; } d_instantiate(dentry, inode); - journal_end(&th, dir->i_sb, jbegin_count) ; + retval = journal_end(&th, dir->i_sb, jbegin_count) ; out_failed: + if (locked) + reiserfs_write_unlock_xattrs (dir->i_sb); reiserfs_write_unlock(dir->i_sb); return retval; } @@ -730,7 +752,13 @@ static int reiserfs_mkdir (struct inode reiserfs_write_lock(dir->i_sb); if (locked) reiserfs_write_lock_xattrs (dir->i_sb); - journal_begin(&th, dir->i_sb, jbegin_count) ; + + retval = journal_begin(&th, dir->i_sb, jbegin_count) ; + if (retval) { + drop_new_inode (inode); + goto out_failed; + } + /* inc the link count now, so another writer doesn't overflow it while ** we sleep later on. @@ -741,13 +769,16 @@ static int reiserfs_mkdir (struct inode old_format_only (dir->i_sb) ? EMPTY_DIR_SIZE_V1 : EMPTY_DIR_SIZE, dentry, inode); - if (locked) - reiserfs_write_unlock_xattrs (dir->i_sb); - if (retval) { dir->i_nlink-- ; goto out_failed; } + + if (locked) { + reiserfs_write_unlock_xattrs (dir->i_sb); + locked = 0; + } + reiserfs_update_inode_transaction(inode) ; reiserfs_update_inode_transaction(dir) ; @@ -758,10 +789,13 @@ static int reiserfs_mkdir (struct inode retval = reiserfs_add_entry (&th, dir, dentry->d_name.name, dentry->d_name.len, inode, 1/*visible*/); if (retval) { + int err; inode->i_nlink = 0; DEC_DIR_INODE_NLINK(dir); reiserfs_update_sd (&th, inode); - journal_end(&th, dir->i_sb, jbegin_count) ; + err = journal_end(&th, dir->i_sb, jbegin_count) ; + if (err) + retval = err; iput (inode); goto out_failed; } @@ -770,8 +804,10 @@ static int reiserfs_mkdir (struct inode reiserfs_update_sd (&th, dir); d_instantiate(dentry, inode); - journal_end(&th, dir->i_sb, jbegin_count) ; + retval = journal_end(&th, dir->i_sb, jbegin_count) ; out_failed: + if (locked) + reiserfs_write_unlock_xattrs (dir->i_sb); reiserfs_write_unlock(dir->i_sb); return retval; } @@ -791,7 +827,7 @@ static inline int reiserfs_empty_dir(str static int reiserfs_rmdir (struct inode * dir, struct dentry *dentry) { - int retval; + int retval, err; struct inode * inode; struct reiserfs_transaction_handle th ; int jbegin_count; @@ -803,7 +839,9 @@ static int reiserfs_rmdir (struct inode jbegin_count = JOURNAL_PER_BALANCE_CNT * 2 + 2; reiserfs_write_lock(dir->i_sb); - journal_begin(&th, dir->i_sb, jbegin_count) ; + retval = journal_begin(&th, dir->i_sb, jbegin_count) ; + if (retval) + goto out_rmdir; de.de_gen_number_bit_string = NULL; if ( (retval = reiserfs_find_entry (dir, dentry->d_name.name, dentry->d_name.len, &path, &de)) == NAME_NOT_FOUND) { @@ -852,24 +890,25 @@ static int reiserfs_rmdir (struct inode /* prevent empty directory from getting lost */ add_save_link (&th, inode, 0/* not truncate */); - journal_end(&th, dir->i_sb, jbegin_count) ; + retval = journal_end(&th, dir->i_sb, jbegin_count) ; reiserfs_check_path(&path) ; +out_rmdir: reiserfs_write_unlock(dir->i_sb); - return 0; + return retval; end_rmdir: /* we must release path, because we did not call reiserfs_cut_from_item, or reiserfs_cut_from_item does not release path if operation was not complete */ pathrelse (&path); - journal_end(&th, dir->i_sb, jbegin_count) ; + err = journal_end(&th, dir->i_sb, jbegin_count) ; reiserfs_write_unlock(dir->i_sb); - return retval; + return err ? err : retval; } static int reiserfs_unlink (struct inode * dir, struct dentry *dentry) { - int retval; + int retval, err; struct inode * inode; struct reiserfs_dir_entry de; INITIALIZE_PATH (path); @@ -884,7 +923,9 @@ static int reiserfs_unlink (struct inode jbegin_count = JOURNAL_PER_BALANCE_CNT * 2 + 2; reiserfs_write_lock(dir->i_sb); - journal_begin(&th, dir->i_sb, jbegin_count) ; + retval = journal_begin(&th, dir->i_sb, jbegin_count) ; + if (retval) + goto out_unlink; de.de_gen_number_bit_string = NULL; if ( (retval = reiserfs_find_entry (dir, dentry->d_name.name, dentry->d_name.len, &path, &de)) == NAME_NOT_FOUND) { @@ -938,15 +979,18 @@ static int reiserfs_unlink (struct inode /* prevent file from getting lost */ add_save_link (&th, inode, 0/* not truncate */); - journal_end(&th, dir->i_sb, jbegin_count) ; + retval = journal_end(&th, dir->i_sb, jbegin_count) ; reiserfs_check_path(&path) ; reiserfs_write_unlock(dir->i_sb); - return 0; + return retval; end_unlink: pathrelse (&path); - journal_end(&th, dir->i_sb, jbegin_count) ; + err = journal_end(&th, dir->i_sb, jbegin_count) ; reiserfs_check_path(&path) ; + if (err) + retval = err; +out_unlink: reiserfs_write_unlock(dir->i_sb); return retval; } @@ -989,7 +1033,12 @@ static int reiserfs_symlink (struct inod /* We would inherit the default ACL here, but symlinks don't get ACLs */ - journal_begin(&th, parent_dir->i_sb, jbegin_count) ; + retval = journal_begin(&th, parent_dir->i_sb, jbegin_count) ; + if (retval) { + drop_new_inode (inode); + reiserfs_kfree (name, item_len, parent_dir->i_sb); + goto out_failed; + } retval = reiserfs_new_inode (&th, parent_dir, mode, name, strlen (symname), dentry, inode); @@ -1011,15 +1060,18 @@ static int reiserfs_symlink (struct inod retval = reiserfs_add_entry (&th, parent_dir, dentry->d_name.name, dentry->d_name.len, inode, 1/*visible*/); if (retval) { + int err; inode->i_nlink--; reiserfs_update_sd (&th, inode); - journal_end(&th, parent_dir->i_sb, jbegin_count) ; + err = journal_end(&th, parent_dir->i_sb, jbegin_count) ; + if (err) + retval = err; iput (inode); goto out_failed; } d_instantiate(dentry, inode); - journal_end(&th, parent_dir->i_sb, jbegin_count) ; + retval = journal_end(&th, parent_dir->i_sb, jbegin_count) ; out_failed: reiserfs_write_unlock(parent_dir->i_sb); return retval; @@ -1045,7 +1097,12 @@ static int reiserfs_link (struct dentry /* inc before scheduling so reiserfs_unlink knows we are here */ inode->i_nlink++; - journal_begin(&th, dir->i_sb, jbegin_count) ; + retval = journal_begin(&th, dir->i_sb, jbegin_count) ; + if (retval) { + inode->i_nlink--; + reiserfs_write_unlock (dir->i_sb); + return retval; + } /* create new entry */ retval = reiserfs_add_entry (&th, dir, dentry->d_name.name, dentry->d_name.len, @@ -1055,10 +1112,11 @@ static int reiserfs_link (struct dentry reiserfs_update_inode_transaction(dir) ; if (retval) { + int err; inode->i_nlink--; - journal_end(&th, dir->i_sb, jbegin_count) ; + err = journal_end(&th, dir->i_sb, jbegin_count) ; reiserfs_write_unlock(dir->i_sb); - return retval; + return err ? err : retval; } inode->i_ctime = CURRENT_TIME; @@ -1066,9 +1124,9 @@ static int reiserfs_link (struct dentry atomic_inc(&inode->i_count) ; d_instantiate(dentry, inode); - journal_end(&th, dir->i_sb, jbegin_count) ; + retval = journal_end(&th, dir->i_sb, jbegin_count) ; reiserfs_write_unlock(dir->i_sb); - return 0; + return retval; } @@ -1195,7 +1253,12 @@ static int reiserfs_rename (struct inode } } - journal_begin(&th, old_dir->i_sb, jbegin_count) ; + retval = journal_begin(&th, old_dir->i_sb, jbegin_count) ; + if (retval) { + reiserfs_write_unlock (old_dir->i_sb); + return retval; + } + /* add new entry (or find the existing one) */ retval = reiserfs_add_entry (&th, new_dir, new_dentry->d_name.name, new_dentry->d_name.len, @@ -1206,9 +1269,9 @@ static int reiserfs_rename (struct inode "vs-7050: new entry is found, new inode == 0\n"); } } else if (retval) { - journal_end(&th, old_dir->i_sb, jbegin_count) ; + int err = journal_end(&th, old_dir->i_sb, jbegin_count) ; reiserfs_write_unlock(old_dir->i_sb); - return retval; + return err ? err : retval; } reiserfs_update_inode_transaction(old_dir) ; @@ -1357,9 +1420,9 @@ static int reiserfs_rename (struct inode reiserfs_update_sd (&th, new_dentry_inode); } - journal_end(&th, old_dir->i_sb, jbegin_count) ; + retval = journal_end(&th, old_dir->i_sb, jbegin_count) ; reiserfs_write_unlock(old_dir->i_sb); - return 0; + return retval; } /* @@ -1414,5 +1477,3 @@ struct inode_operations reiserfs_special .permission = reiserfs_permission, }; - - diff -puN fs/reiserfs/objectid.c~reiserfs-add-i-o-error-handling-to-journal-operations fs/reiserfs/objectid.c --- 25/fs/reiserfs/objectid.c~reiserfs-add-i-o-error-handling-to-journal-operations 2004-10-05 11:11:22.915599128 -0700 +++ 25-akpm/fs/reiserfs/objectid.c 2004-10-05 11:11:22.950593808 -0700 @@ -55,6 +55,7 @@ __u32 reiserfs_get_unused_objectid (stru __u32 * map = objectid_map (s, rs); __u32 unused_objectid; + BUG_ON (!th->t_trans_id); check_objectid_map (s, map); @@ -99,6 +100,7 @@ void reiserfs_release_objectid (struct r __u32 * map = objectid_map (s, rs); int i = 0; + BUG_ON (!th->t_trans_id); //return; check_objectid_map (s, map); diff -puN fs/reiserfs/prints.c~reiserfs-add-i-o-error-handling-to-journal-operations fs/reiserfs/prints.c --- 25/fs/reiserfs/prints.c~reiserfs-add-i-o-error-handling-to-journal-operations 2004-10-05 11:11:22.916598976 -0700 +++ 25-akpm/fs/reiserfs/prints.c 2004-10-05 11:11:22.951593656 -0700 @@ -366,6 +366,49 @@ void reiserfs_panic (struct super_block reiserfs_bdevname (sb), error_buf); } +static void +do_handle_error (struct super_block *sb, int errno) +{ + if (reiserfs_error_panic (sb)) { + panic ("REISERFS: panic (device %s): Panic forced after error\n", + reiserfs_bdevname (sb)); + } + + if (reiserfs_error_ro (sb)) { + printk (KERN_CRIT "REISERFS: error (device %s): Re-mounting fs " + "readonly\n", reiserfs_bdevname (sb)); + reiserfs_journal_abort (sb, errno); + } +} + +void +reiserfs_error (struct super_block * sb, int errno, const char *fmt, ...) +{ + do_reiserfs_warning (fmt); + printk (KERN_CRIT "REISERFS: error (device %s): %s\n", + reiserfs_bdevname (sb), error_buf); + do_handle_error (sb, errno); +} + +void +reiserfs_abort (struct super_block *sb, int errno, const char *fmt, ...) +{ + do_reiserfs_warning (fmt); + + if (reiserfs_error_panic (sb)) { + panic (KERN_CRIT "REISERFS: panic (device %s): %s\n", + reiserfs_bdevname (sb), error_buf); + } + + if (sb->s_flags & MS_RDONLY) + return; + + printk (KERN_CRIT "REISERFS: abort (device %s): %s\n", + reiserfs_bdevname (sb), error_buf); + + sb->s_flags |= MS_RDONLY; + reiserfs_journal_abort (sb, errno); +} void print_virtual_node (struct virtual_node * vn) { diff -puN fs/reiserfs/resize.c~reiserfs-add-i-o-error-handling-to-journal-operations fs/reiserfs/resize.c --- 25/fs/reiserfs/resize.c~reiserfs-add-i-o-error-handling-to-journal-operations 2004-10-05 11:11:22.918598672 -0700 +++ 25-akpm/fs/reiserfs/resize.c 2004-10-05 11:11:22.952593504 -0700 @@ -19,8 +19,10 @@ int reiserfs_resize (struct super_block * s, unsigned long block_count_new) { + int err = 0; struct reiserfs_super_block * sb; struct reiserfs_bitmap_info *bitmap; + struct reiserfs_bitmap_info *old_bitmap = SB_AP_BITMAP(s);; struct buffer_head * bh; struct reiserfs_transaction_handle th; unsigned int bmap_nr_new, bmap_nr; @@ -107,12 +109,19 @@ int reiserfs_resize (struct super_block * block pointers */ bitmap = vmalloc(sizeof(struct reiserfs_bitmap_info) * bmap_nr_new); if (!bitmap) { + /* Journal bitmaps are still supersized, but the memory isn't + * leaked, so I guess it's ok */ printk("reiserfs_resize: unable to allocate memory.\n"); return -ENOMEM; } memset (bitmap, 0, sizeof (struct reiserfs_bitmap_info) * SB_BMAP_NR(s)); for (i = 0; i < bmap_nr; i++) - bitmap[i] = SB_AP_BITMAP(s)[i]; + bitmap[i] = old_bitmap[i]; + + /* This doesn't go through the journal, but it doesn't have to. + * The changes are still atomic: We're synced up when the journal + * transaction begins, and the new bitmaps don't matter if the + * transaction fails. */ for (i = bmap_nr; i < bmap_nr_new; i++) { bitmap[i].bh = sb_getblk(s, i * s->s_blocksize * 8); memset(bitmap[i].bh->b_data, 0, sb_blocksize(sb)); @@ -126,12 +135,16 @@ int reiserfs_resize (struct super_block bitmap[i].free_count = sb_blocksize(sb) * 8 - 1; } /* free old bitmap blocks array */ - vfree(SB_AP_BITMAP(s)); SB_AP_BITMAP(s) = bitmap; + vfree (old_bitmap); } - /* begin transaction */ - journal_begin(&th, s, 10); + /* begin transaction, if there was an error, it's fine. Yes, we have + * incorrect bitmaps now, but none of it is ever going to touch the + * disk anyway. */ + err = journal_begin(&th, s, 10); + if (err) + return err; /* correct last bitmap blocks in old and new disk layout */ reiserfs_prepare_for_journal(s, SB_AP_BITMAP(s)[bmap_nr - 1].bh, 1); @@ -165,8 +178,5 @@ int reiserfs_resize (struct super_block journal_mark_dirty(&th, s, SB_BUFFER_WITH_SB(s)); SB_JOURNAL(s)->j_must_wait = 1; - journal_end(&th, s, 10); - - return 0; + return journal_end(&th, s, 10); } - diff -puN fs/reiserfs/stree.c~reiserfs-add-i-o-error-handling-to-journal-operations fs/reiserfs/stree.c --- 25/fs/reiserfs/stree.c~reiserfs-add-i-o-error-handling-to-journal-operations 2004-10-05 11:11:22.919598520 -0700 +++ 25-akpm/fs/reiserfs/stree.c 2004-10-05 11:11:22.955593048 -0700 @@ -1036,6 +1036,8 @@ static char prepare_for_delete_or_cut( struct item_head * p_le_ih = PATH_PITEM_HEAD(p_s_path); struct buffer_head * p_s_bh = PATH_PLAST_BUFFER(p_s_path); + BUG_ON (!th->t_trans_id); + /* Stat_data item. */ if ( is_statdata_le_ih (p_le_ih) ) { @@ -1222,6 +1224,9 @@ static void init_tb_struct( struct path * p_s_path, int n_size ) { + + BUG_ON (!th->t_trans_id); + memset (p_s_tb,'\0',sizeof(struct tree_balance)); p_s_tb->transaction_handle = th ; p_s_tb->tb_sb = p_s_sb; @@ -1290,6 +1295,8 @@ int reiserfs_delete_item (struct reiserf int n_iter = 0; #endif + BUG_ON (!th->t_trans_id); + init_tb_struct(th, &s_del_balance, p_s_sb, p_s_path, 0/*size is unknown*/); while ( 1 ) { @@ -1419,6 +1426,8 @@ void reiserfs_delete_solid_item (struct struct cpu_key cpu_key; int retval; int quota_cut_bytes = 0; + + BUG_ON (!th->t_trans_id); le_key2cpu_key (&cpu_key, key); @@ -1474,12 +1483,16 @@ void reiserfs_delete_solid_item (struct } -void reiserfs_delete_object (struct reiserfs_transaction_handle *th, struct inode * inode) +int reiserfs_delete_object (struct reiserfs_transaction_handle *th, struct inode * inode) { + int err; inode->i_size = 0; + BUG_ON (!th->t_trans_id); /* for directory this deletes item containing "." and ".." */ - reiserfs_do_truncate (th, inode, NULL, 0/*no timestamp updates*/); + err = reiserfs_do_truncate (th, inode, NULL, 0/*no timestamp updates*/); + if (err) + return err; #if defined( USE_INODE_GENERATION_COUNTER ) if( !old_format_only ( th -> t_super ) ) @@ -1493,6 +1506,8 @@ void reiserfs_delete_object (struct reis /* USE_INODE_GENERATION_COUNTER */ #endif reiserfs_delete_solid_item (th, inode, INODE_PKEY (inode)); + + return err; } static void @@ -1542,6 +1557,7 @@ static int maybe_indirect_to_direct (str struct super_block * p_s_sb = p_s_inode->i_sb; int n_block_size = p_s_sb->s_blocksize; int cut_bytes; + BUG_ON (!th->t_trans_id); if (n_new_file_size != p_s_inode->i_size) BUG (); @@ -1574,6 +1590,7 @@ static void indirect_to_direct_roll_back struct cpu_key tail_key; int tail_len; int removed; + BUG_ON (!th->t_trans_id); make_cpu_key (&tail_key, inode, inode->i_size + 1, TYPE_DIRECT, 4);// !!!! tail_key.key_length = 4; @@ -1623,6 +1640,8 @@ int reiserfs_cut_from_item (struct reise int retval2 = -1; int quota_cut_bytes; loff_t tail_pos = 0; + + BUG_ON (!th->t_trans_id); init_tb_struct(th, &s_cut_balance, p_s_inode->i_sb, p_s_path, n_cut_size); @@ -1775,6 +1794,7 @@ int reiserfs_cut_from_item (struct reise static void truncate_directory (struct reiserfs_transaction_handle *th, struct inode * inode) { + BUG_ON (!th->t_trans_id); if (inode->i_nlink) reiserfs_warning (inode->i_sb, "vs-5655: truncate_directory: link count != 0"); @@ -1792,7 +1812,7 @@ static void truncate_directory (struct r /* Truncate file to the new size. Note, this must be called with a transaction already started */ -void reiserfs_do_truncate (struct reiserfs_transaction_handle *th, +int reiserfs_do_truncate (struct reiserfs_transaction_handle *th, struct inode * p_s_inode, /* ->i_size contains new size */ struct page *page, /* up to date for last block */ @@ -1808,14 +1828,16 @@ void reiserfs_do_truncate (struct reiser n_new_file_size;/* New file size. */ int n_deleted; /* Number of deleted or truncated bytes. */ int retval; + int err = 0; + BUG_ON (!th->t_trans_id); if ( ! (S_ISREG(p_s_inode->i_mode) || S_ISDIR(p_s_inode->i_mode) || S_ISLNK(p_s_inode->i_mode)) ) - return; + return 0; if (S_ISDIR(p_s_inode->i_mode)) { // deletion of directory - no need to update timestamps truncate_directory (th, p_s_inode); - return; + return 0; } /* Get new file size. */ @@ -1828,13 +1850,15 @@ void reiserfs_do_truncate (struct reiser if (retval == IO_ERROR) { reiserfs_warning (p_s_inode->i_sb, "vs-5657: reiserfs_do_truncate: " "i/o failure occurred trying to truncate %K", &s_item_key); - return; + err = -EIO; + goto out; } if (retval == POSITION_FOUND || retval == FILE_NOT_FOUND) { - pathrelse (&s_search_path); reiserfs_warning (p_s_inode->i_sb, "PAP-5660: reiserfs_do_truncate: " "wrong result %d of search for %K", retval, &s_item_key); - return; + + err = -EIO; + goto out; } s_search_path.pos_in_item --; @@ -1872,7 +1896,7 @@ void reiserfs_do_truncate (struct reiser if (n_deleted < 0) { reiserfs_warning (p_s_inode->i_sb, "vs-5665: reiserfs_do_truncate: reiserfs_cut_from_item failed"); reiserfs_check_path(&s_search_path) ; - return; + return 0; } RFALSE( n_deleted > n_file_size, @@ -1902,8 +1926,13 @@ void reiserfs_do_truncate (struct reiser } reiserfs_update_sd(th, p_s_inode) ; - journal_end(th, p_s_inode->i_sb, orig_len_alloc) ; - journal_begin(th, p_s_inode->i_sb, JOURNAL_PER_BALANCE_CNT * 6) ; + err = journal_end(th, p_s_inode->i_sb, orig_len_alloc) ; + if (err) + goto out; + err = journal_begin (th, p_s_inode->i_sb, + JOURNAL_PER_BALANCE_CNT * 6); + if (err) + goto out; reiserfs_update_inode_transaction(p_s_inode) ; } } while ( n_file_size > ROUND_UP (n_new_file_size) && @@ -1920,7 +1949,9 @@ update_and_out: } reiserfs_update_sd (th, p_s_inode); +out: pathrelse(&s_search_path) ; + return err; } @@ -1963,6 +1994,8 @@ int reiserfs_paste_into_item (struct rei int retval; int fs_gen; + BUG_ON (!th->t_trans_id); + fs_gen = get_generation(inode->i_sb) ; #ifdef REISERQUOTA_DEBUG @@ -2035,6 +2068,8 @@ int reiserfs_insert_item(struct reiserfs int fs_gen = 0 ; int quota_bytes = 0 ; + BUG_ON (!th->t_trans_id); + if (inode) { /* Do we count quotas for item? */ fs_gen = get_generation(inode->i_sb); quota_bytes = ih_item_len(p_s_ih); diff -puN fs/reiserfs/super.c~reiserfs-add-i-o-error-handling-to-journal-operations fs/reiserfs/super.c --- 25/fs/reiserfs/super.c~reiserfs-add-i-o-error-handling-to-journal-operations 2004-10-05 11:11:22.921598216 -0700 +++ 25-akpm/fs/reiserfs/super.c 2004-10-05 11:11:22.958592592 -0700 @@ -24,6 +24,7 @@ #include #include #include +#include struct file_system_type reiserfs_fs_type; @@ -66,11 +67,14 @@ static void reiserfs_sync_fs (struct sup if (!(s->s_flags & MS_RDONLY)) { struct reiserfs_transaction_handle th; reiserfs_write_lock(s); - journal_begin(&th, s, 1); - journal_end_sync(&th, s, 1); - reiserfs_flush_old_commits(s); - s->s_dirt = 0; + if (!journal_begin(&th, s, 1)) + if (!journal_end_sync(&th, s, 1)) + reiserfs_flush_old_commits(s); + s->s_dirt = 0; /* Even if it's not true. + * We'll loop forever in sync_supers otherwise */ reiserfs_write_unlock(s); + } else { + s->s_dirt = 0; } } @@ -84,11 +88,15 @@ static void reiserfs_write_super_lockfs struct reiserfs_transaction_handle th ; reiserfs_write_lock(s); if (!(s->s_flags & MS_RDONLY)) { - journal_begin(&th, s, 1) ; - reiserfs_prepare_for_journal(s, SB_BUFFER_WITH_SB(s), 1); - journal_mark_dirty(&th, s, SB_BUFFER_WITH_SB (s)); - reiserfs_block_writes(&th) ; - journal_end_sync(&th, s, 1) ; + int err = journal_begin(&th, s, 1) ; + if (err) { + reiserfs_block_writes(&th) ; + } else { + reiserfs_prepare_for_journal(s, SB_BUFFER_WITH_SB(s), 1); + journal_mark_dirty(&th, s, SB_BUFFER_WITH_SB (s)); + reiserfs_block_writes(&th) ; + journal_end_sync(&th, s, 1) ; + } } s->s_dirt = 0; reiserfs_write_unlock(s); @@ -108,29 +116,32 @@ extern const struct key MAX_KEY; protecting unlink is bigger that a key lf "save link" which protects truncate), so there left no items to make truncate completion on */ -static void remove_save_link_only (struct super_block * s, struct key * key, int oid_free) +static int remove_save_link_only (struct super_block * s, struct key * key, int oid_free) { struct reiserfs_transaction_handle th; + int err; /* we are going to do one balancing */ - journal_begin (&th, s, JOURNAL_PER_BALANCE_CNT); + err = journal_begin (&th, s, JOURNAL_PER_BALANCE_CNT); + if (err) + return err; reiserfs_delete_solid_item (&th, NULL, key); if (oid_free) /* removals are protected by direct items */ reiserfs_release_objectid (&th, le32_to_cpu (key->k_objectid)); - journal_end (&th, s, JOURNAL_PER_BALANCE_CNT); + return journal_end (&th, s, JOURNAL_PER_BALANCE_CNT); } /* look for uncompleted unlinks and truncates and complete them */ -static void finish_unfinished (struct super_block * s) +static int finish_unfinished (struct super_block * s) { INITIALIZE_PATH (path); struct cpu_key max_cpu_key, obj_key; struct key save_link_key; - int retval; + int retval = 0; struct item_head * ih; struct buffer_head * bh; int item_pos; @@ -147,7 +158,7 @@ static void finish_unfinished (struct su done = 0; REISERFS_SB(s)->s_is_unlinked_ok = 1; - while (1) { + while (!retval) { retval = search_item (s, &max_cpu_key, &path); if (retval != ITEM_NOT_FOUND) { reiserfs_warning (s, "vs-2140: finish_unfinished: search_by_key returned %d", @@ -189,7 +200,7 @@ static void finish_unfinished (struct su "save" link and release objectid */ reiserfs_warning (s, "vs-2180: finish_unfinished: iget failed for %K", &obj_key); - remove_save_link_only (s, &save_link_key, 1); + retval = remove_save_link_only (s, &save_link_key, 1); continue; } @@ -197,7 +208,7 @@ static void finish_unfinished (struct su /* file is not unlinked */ reiserfs_warning (s, "vs-2185: finish_unfinished: file %K is not unlinked", &obj_key); - remove_save_link_only (s, &save_link_key, 0); + retval = remove_save_link_only (s, &save_link_key, 0); continue; } @@ -207,7 +218,7 @@ static void finish_unfinished (struct su then boot into old kernel, remove the file and create dir with the same key. */ reiserfs_warning(s, "green-2101: impossible truncate on a directory %k. Please report", INODE_PKEY (inode)); - remove_save_link_only (s, &save_link_key, 0); + retval = remove_save_link_only (s, &save_link_key, 0); truncate = 0; iput (inode); continue; @@ -220,12 +231,13 @@ static void finish_unfinished (struct su reiserfs_info (s, "Truncating %k to %Ld ..", INODE_PKEY (inode), inode->i_size); reiserfs_truncate_file (inode, 0/*don't update modification time*/); - remove_save_link (inode, truncate); + retval = remove_save_link (inode, truncate); } else { REISERFS_I(inode) -> i_flags |= i_link_saved_unlink_mask; /* not completed unlink (rmdir) found */ reiserfs_info (s, "Removing %k..", INODE_PKEY (inode)); /* removal gets completed in iput */ + retval = 0; } iput (inode); @@ -238,6 +250,7 @@ static void finish_unfinished (struct su if (done) reiserfs_info (s, "There were %d uncompleted unlinks/truncates. " "Completed\n", done); + return retval; } /* to protect file being unlinked from getting lost we "safe" link files @@ -253,6 +266,8 @@ void add_save_link (struct reiserfs_tran struct item_head ih; __u32 link; + BUG_ON (!th->t_trans_id); + /* file can only get one "save link" of each kind */ RFALSE( truncate && ( REISERFS_I(inode) -> i_flags & i_link_saved_truncate_mask ), @@ -317,14 +332,16 @@ void add_save_link (struct reiserfs_tran /* this opens transaction unlike add_save_link */ -void remove_save_link (struct inode * inode, int truncate) +int remove_save_link (struct inode * inode, int truncate) { struct reiserfs_transaction_handle th; struct key key; - + int err; /* we are going to do one balancing only */ - journal_begin (&th, inode->i_sb, JOURNAL_PER_BALANCE_CNT); + err = journal_begin (&th, inode->i_sb, JOURNAL_PER_BALANCE_CNT); + if (err) + return err; /* setup key of "save" link */ key.k_dir_id = cpu_to_le32 (MAX_KEY_OBJECTID); @@ -352,7 +369,7 @@ void remove_save_link (struct inode * in } else REISERFS_I(inode) -> i_flags &= ~i_link_saved_truncate_mask; - journal_end (&th, inode->i_sb, JOURNAL_PER_BALANCE_CNT); + return journal_end (&th, inode->i_sb, JOURNAL_PER_BALANCE_CNT); } @@ -360,6 +377,7 @@ static void reiserfs_put_super (struct s { int i; struct reiserfs_transaction_handle th ; + th.t_trans_id = 0; if (REISERFS_SB(s)->xattr_root) { d_invalidate (REISERFS_SB(s)->xattr_root); @@ -373,10 +391,11 @@ static void reiserfs_put_super (struct s /* change file system state to current state if it was mounted with read-write permissions */ if (!(s->s_flags & MS_RDONLY)) { - journal_begin(&th, s, 10) ; - reiserfs_prepare_for_journal(s, SB_BUFFER_WITH_SB(s), 1) ; - set_sb_umount_state( SB_DISK_SUPER_BLOCK(s), REISERFS_SB(s)->s_mount_state ); - journal_mark_dirty(&th, s, SB_BUFFER_WITH_SB (s)); + if (!journal_begin(&th, s, 10)) { + reiserfs_prepare_for_journal(s, SB_BUFFER_WITH_SB(s), 1) ; + set_sb_umount_state( SB_DISK_SUPER_BLOCK(s), REISERFS_SB(s)->s_mount_state ); + journal_mark_dirty(&th, s, SB_BUFFER_WITH_SB (s)); + } } /* note, journal_release checks for readonly mount, and can decide not @@ -461,6 +480,7 @@ static void destroy_inodecache(void) static void reiserfs_dirty_inode (struct inode * inode) { struct reiserfs_transaction_handle th ; + int err = 0; if (inode->i_sb->s_flags & MS_RDONLY) { reiserfs_warning(inode->i_sb, "clm-6006: writing inode %lu on readonly FS", inode->i_ino) ; @@ -471,7 +491,11 @@ static void reiserfs_dirty_inode (struct /* this is really only used for atime updates, so they don't have ** to be included in O_SYNC or fsync */ - journal_begin(&th, inode->i_sb, 1) ; + err = journal_begin(&th, inode->i_sb, 1) ; + if (err) { + reiserfs_write_unlock (inode->i_sb); + return; + } reiserfs_update_sd (&th, inode); journal_end(&th, inode->i_sb, 1) ; reiserfs_write_unlock(inode->i_sb); @@ -575,6 +599,18 @@ static const arg_desc_t tails[] = { {NULL, 0, 0} }; +static const arg_desc_t error_actions[] = { + {"panic", 1 << REISERFS_ERROR_PANIC, + (1 << REISERFS_ERROR_RO | 1 << REISERFS_ERROR_CONTINUE)}, + {"ro-remount", 1 << REISERFS_ERROR_RO, + (1 << REISERFS_ERROR_PANIC | 1 << REISERFS_ERROR_CONTINUE)}, +#ifdef REISERFS_JOURNAL_ERROR_ALLOWS_NO_LOG + {"continue", 1 << REISERFS_ERROR_CONTINUE, + (1 << REISERFS_ERROR_PANIC | 1 << REISERFS_ERROR_RO)}, +#endif + {NULL, 0, 0}, +}; + int reiserfs_default_io_size = 128 * 1024; /* Default recommended I/O size is 128k. There might be broken applications that are confused by this. Use nolargeio mount option @@ -725,6 +761,7 @@ static int reiserfs_parse_options (struc {"commit", .arg_required = 'c', .values = NULL}, {"usrquota",}, {"grpquota",}, + {"errors", .arg_required = 'e', .values = error_actions}, {NULL,} }; @@ -862,6 +899,7 @@ static int reiserfs_remount (struct supe unsigned long safe_mask = 0; unsigned int commit_max_age = (unsigned int)-1; struct reiserfs_journal *journal = SB_JOURNAL(s); + int err; rs = SB_DISK_SUPER_BLOCK (s); @@ -882,6 +920,9 @@ static int reiserfs_remount (struct supe safe_mask |= 1 << REISERFS_POSIXACL; safe_mask |= 1 << REISERFS_BARRIER_FLUSH; safe_mask |= 1 << REISERFS_BARRIER_NONE; + safe_mask |= 1 << REISERFS_ERROR_RO; + safe_mask |= 1 << REISERFS_ERROR_CONTINUE; + safe_mask |= 1 << REISERFS_ERROR_PANIC; /* Update the bitmask, taking care to keep * the bits we're not allowed to change here */ @@ -915,7 +956,10 @@ static int reiserfs_remount (struct supe return 0; } - journal_begin(&th, s, 10) ; + err = journal_begin(&th, s, 10) ; + if (err) + return err; + /* Mounting a rw partition read-only. */ reiserfs_prepare_for_journal(s, SB_BUFFER_WITH_SB(s), 1) ; set_sb_umount_state( rs, REISERFS_SB(s)->s_mount_state ); @@ -927,11 +971,16 @@ static int reiserfs_remount (struct supe return 0; /* We are read-write already */ } + if (reiserfs_is_journal_aborted (journal)) + return journal->j_errno; + handle_data_mode(s, mount_options); handle_barrier_mode(s, mount_options); REISERFS_SB(s)->s_mount_state = sb_umount_state(rs) ; s->s_flags &= ~MS_RDONLY ; /* now it is safe to call journal_begin */ - journal_begin(&th, s, 10) ; + err = journal_begin(&th, s, 10) ; + if (err) + return err; /* Mount a partition which is read-only, read-write */ reiserfs_prepare_for_journal(s, SB_BUFFER_WITH_SB(s), 1) ; @@ -944,7 +993,9 @@ static int reiserfs_remount (struct supe } /* this will force a full flush of all journal lists */ SB_JOURNAL(s)->j_must_wait = 1 ; - journal_end(&th, s, 10) ; + err = journal_end(&th, s, 10) ; + if (err) + return err; s->s_dirt = 0; if (!( *mount_flags & MS_RDONLY ) ) { @@ -1372,8 +1423,9 @@ static int reiserfs_fill_super (struct s } s->s_fs_info = sbi; memset (sbi, 0, sizeof (struct reiserfs_sb_info)); - /* Set default values for options: non-aggressive tails */ - REISERFS_SB(s)->s_mount_opt = ( 1 << REISERFS_SMALLTAIL ); + /* Set default values for options: non-aggressive tails, RO on errors */ + REISERFS_SB(s)->s_mount_opt |= (1 << REISERFS_SMALLTAIL); + REISERFS_SB(s)->s_mount_opt |= (1 << REISERFS_ERROR_RO); /* no preallocation minimum, be smart in reiserfs_file_write instead */ REISERFS_SB(s)->s_alloc_options.preallocmin = 0; @@ -1501,7 +1553,12 @@ static int reiserfs_fill_super (struct s if (!(s->s_flags & MS_RDONLY)) { - journal_begin(&th, s, 1) ; + errval = journal_begin(&th, s, 1) ; + if (errval) { + dput (s->s_root); + s->s_root = NULL; + goto error; + } reiserfs_prepare_for_journal(s, SB_BUFFER_WITH_SB(s), 1) ; set_sb_umount_state( rs, REISERFS_ERROR_FS ); @@ -1531,9 +1588,14 @@ static int reiserfs_fill_super (struct s } journal_mark_dirty(&th, s, SB_BUFFER_WITH_SB (s)); - journal_end(&th, s, 1) ; + errval = journal_end(&th, s, 1) ; + if (errval) { + dput (s->s_root); + s->s_root = NULL; + goto error; + } - if (reiserfs_xattr_init (s, s->s_flags)) { + if ((errval = reiserfs_xattr_init (s, s->s_flags))) { dput (s->s_root); s->s_root = NULL; goto error; @@ -1546,7 +1608,7 @@ static int reiserfs_fill_super (struct s reiserfs_info (s, "using 3.5.x disk format\n") ; } - if (reiserfs_xattr_init (s, s->s_flags)) { + if ((errval = reiserfs_xattr_init (s, s->s_flags))) { dput (s->s_root); s->s_root = NULL; goto error; diff -puN fs/reiserfs/tail_conversion.c~reiserfs-add-i-o-error-handling-to-journal-operations fs/reiserfs/tail_conversion.c --- 25/fs/reiserfs/tail_conversion.c~reiserfs-add-i-o-error-handling-to-journal-operations 2004-10-05 11:11:22.922598064 -0700 +++ 25-akpm/fs/reiserfs/tail_conversion.c 2004-10-05 11:11:22.959592440 -0700 @@ -34,6 +34,7 @@ int direct2indirect (struct reiserfs_tra that will be inserted in the tree. */ + BUG_ON (!th->t_trans_id); REISERFS_SB(sb)->s_direct2indirect ++; @@ -184,6 +185,8 @@ int indirect2direct (struct reiserfs_tra loff_t pos, pos1; /* position of first byte of the tail */ struct cpu_key key; + BUG_ON (!th->t_trans_id); + REISERFS_SB(p_s_sb)->s_indirect2direct ++; *p_c_mode = M_SKIP_BALANCING; diff -puN include/linux/reiserfs_fs.h~reiserfs-add-i-o-error-handling-to-journal-operations include/linux/reiserfs_fs.h --- 25/include/linux/reiserfs_fs.h~reiserfs-add-i-o-error-handling-to-journal-operations 2004-10-05 11:11:22.924597760 -0700 +++ 25-akpm/include/linux/reiserfs_fs.h 2004-10-05 11:11:22.961592136 -0700 @@ -1751,6 +1751,7 @@ struct reiserfs_transaction_handle { void *t_handle_save ; /* save existing current->journal_info */ unsigned displace_new_blocks:1; /* if new block allocation occurres, that block should be displaced from others */ + struct list_head t_list; } ; /* used to keep track of ordered and tail writes, attached to the buffer @@ -1810,12 +1811,14 @@ int journal_mark_freed(struct reiserfs_t int journal_transaction_should_end(struct reiserfs_transaction_handle *, int) ; int reiserfs_in_journal(struct super_block *p_s_sb, int bmap_nr, int bit_nr, int searchall, b_blocknr_t *next) ; int journal_begin(struct reiserfs_transaction_handle *, struct super_block *p_s_sb, unsigned long) ; - +int journal_join_abort(struct reiserfs_transaction_handle *, struct super_block *p_s_sb, unsigned long) ; +void reiserfs_journal_abort (struct super_block *sb, int errno); +void reiserfs_abort (struct super_block *sb, int errno, const char *fmt, ...); int reiserfs_allocate_list_bitmaps(struct super_block *s, struct reiserfs_list_bitmap *, int) ; void add_save_link (struct reiserfs_transaction_handle * th, struct inode * inode, int truncate); -void remove_save_link (struct inode * inode, int truncate); +int remove_save_link (struct inode * inode, int truncate); /* objectid.c */ __u32 reiserfs_get_unused_objectid (struct reiserfs_transaction_handle *th); @@ -1911,8 +1914,8 @@ int reiserfs_delete_item (struct reiserf void reiserfs_delete_solid_item (struct reiserfs_transaction_handle *th, struct inode *inode, struct key * key); -void reiserfs_delete_object (struct reiserfs_transaction_handle *th, struct inode * p_s_inode); -void reiserfs_do_truncate (struct reiserfs_transaction_handle *th, +int reiserfs_delete_object (struct reiserfs_transaction_handle *th, struct inode * p_s_inode); +int reiserfs_do_truncate (struct reiserfs_transaction_handle *th, struct inode * p_s_inode, struct page *, int update_timestamps); @@ -1926,7 +1929,7 @@ void reiserfs_do_truncate (struct reiser void padd_item (char * item, int total_length, int length); /* inode.c */ -void restart_transaction(struct reiserfs_transaction_handle *th, struct inode *inode, struct path *path); +int restart_transaction(struct reiserfs_transaction_handle *th, struct inode *inode, struct path *path); void reiserfs_read_locked_inode(struct inode * inode, struct reiserfs_iget_args *args) ; int reiserfs_find_actor(struct inode * inode, void *p) ; int reiserfs_init_locked_inode(struct inode * inode, void *p) ; @@ -1941,7 +1944,7 @@ int reiserfs_encode_fh( struct dentry *d int connectable ); int reiserfs_prepare_write(struct file *, struct page *, unsigned, unsigned) ; -void reiserfs_truncate_file(struct inode *, int update_timestamps) ; +int reiserfs_truncate_file(struct inode *, int update_timestamps) ; void make_cpu_key (struct cpu_key * cpu_key, struct inode * inode, loff_t offset, int type, int key_length); void make_le_item_head (struct item_head * ih, const struct cpu_key * key, diff -puN include/linux/reiserfs_fs_sb.h~reiserfs-add-i-o-error-handling-to-journal-operations include/linux/reiserfs_fs_sb.h --- 25/include/linux/reiserfs_fs_sb.h~reiserfs-add-i-o-error-handling-to-journal-operations 2004-10-05 11:11:22.926597456 -0700 +++ 25-akpm/include/linux/reiserfs_fs_sb.h 2004-10-05 11:11:22.963591832 -0700 @@ -242,14 +242,24 @@ struct reiserfs_journal { struct reiserfs_journal_cnode *j_list_hash_table[JOURNAL_HASH_SIZE] ; /* hash table for all the real buffer heads in all the transactions */ struct list_head j_prealloc_list; /* list of inodes which have preallocated blocks */ + int j_persistent_trans; unsigned long j_max_trans_size ; unsigned long j_max_batch_size ; + int j_errno; + /* when flushing ordered buffers, throttle new ordered writers */ struct work_struct j_work; atomic_t j_async_throttle; }; +enum journal_state_bits { + J_WRITERS_BLOCKED = 1, /* set when new writers not allowed */ + J_WRITERS_QUEUED, /* set when log is full due to too many writers */ + J_ABORTED, /* set when log is aborted */ +}; + + #define JOURNAL_DESC_MAGIC "ReIsErLB" /* ick. magic string to find desc blocks in the journal */ typedef __u32 (*hashf_t) (const signed char *, int); @@ -399,6 +409,7 @@ struct reiserfs_sb_info struct dentry *xattr_root; /* root of /.reiserfs_priv/.xa */ struct rw_semaphore xattr_dir_sem; + int j_errno; }; /* Definitions of reiserfs on-disk properties: */ @@ -447,6 +458,11 @@ enum reiserfs_mount_options { REISERFS_BARRIER_NONE, REISERFS_BARRIER_FLUSH, + /* Actions on error */ + REISERFS_ERROR_PANIC, + REISERFS_ERROR_RO, + REISERFS_ERROR_CONTINUE, + REISERFS_TEST1, REISERFS_TEST2, REISERFS_TEST3, @@ -478,6 +494,10 @@ enum reiserfs_mount_options { #define reiserfs_barrier_none(s) (REISERFS_SB(s)->s_mount_opt & (1 << REISERFS_BARRIER_NONE)) #define reiserfs_barrier_flush(s) (REISERFS_SB(s)->s_mount_opt & (1 << REISERFS_BARRIER_FLUSH)) +#define reiserfs_error_panic(s) (REISERFS_SB(s)->s_mount_opt & (1 << REISERFS_ERROR_PANIC)) +#define reiserfs_error_ro(s) (REISERFS_SB(s)->s_mount_opt & (1 << REISERFS_ERROR_RO)) +#define reiserfs_error_continue(s) (REISERFS_SB(s)->s_mount_opt & (1 << REISERFS_ERROR_CONTINUE)) + void reiserfs_file_buffer (struct buffer_head * bh, int list); extern struct file_system_type reiserfs_fs_type; int reiserfs_resize(struct super_block *, unsigned long) ; @@ -502,4 +522,10 @@ static inline char *reiserfs_bdevname(st return (s == NULL) ? "Null superblock" : s -> s_id; } +#define reiserfs_is_journal_aborted(journal) (unlikely (__reiserfs_is_journal_aborted (journal))) +static inline int __reiserfs_is_journal_aborted (struct reiserfs_journal *journal) +{ + return test_bit (J_ABORTED, &journal->j_state); +} + #endif /* _LINUX_REISER_FS_SB */ _