Commit 6f2fb125 authored by Jeff Mahoney's avatar Jeff Mahoney Committed by Linus Torvalds

[PATCH] ReiserFS: Add I/O error handling to journal operations

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: default avatarJeff Mahoney <jeffm@novell.com>
Signed-off-by: default avatarAndrew Morton <akpm@osdl.org>
Signed-off-by: default avatarLinus Torvalds <torvalds@osdl.org>
parent 5b7b40f5
......@@ -137,6 +137,8 @@ static int scan_bitmap_block (struct reiserfs_transaction_handle *th,
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_transaction_handle *th,
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 reiserfs_transaction_handle *th,
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 reiserfs_transaction_handle *th,
{
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 (struct reiserfs_transaction_handle *th,
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 reiserfs_transaction_handle * th,
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 reiserfs_transaction_handle *th,
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 (struct reiserfs_transaction_handle *th)
{
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);
......
......@@ -26,10 +26,13 @@ struct file_operations reiserfs_dir_operations = {
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;
}
......
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
......@@ -55,6 +55,7 @@ __u32 reiserfs_get_unused_objectid (struct reiserfs_transaction_handle *th)
__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 reiserfs_transaction_handle *th,
__u32 * map = objectid_map (s, rs);
int i = 0;
BUG_ON (!th->t_trans_id);
//return;
check_objectid_map (s, map);
......
......@@ -366,6 +366,49 @@ void reiserfs_panic (struct super_block * sb, const char * fmt, ...)
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)
{
......
......@@ -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 * s, unsigned long block_count_new)
* 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 * s, unsigned long block_count_new)
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 * s, unsigned long block_count_new)
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);
}
......@@ -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 reiserfs_transaction_handle *th,
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 reiserfs_transaction_handle *th,
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 reiserfs_transaction_handle *th,
}
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 reiserfs_transaction_handle *th, struct inod
/* 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 (struct reiserfs_transaction_handle *th,
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 reiserfs_transaction_handle *th
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 reiserfs_transaction_handle *th,
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 reiserfs_transaction_handle *th,
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 reiserfs_transaction_handle *th, struct i
/* 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 reiserfs_transaction_handle *th,
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 reiserfs_transaction_handle *th,
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 reiserfs_transaction_handle *th,
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 reiserfs_transaction_handle *th,
}
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 @@ void reiserfs_do_truncate (struct reiserfs_transaction_handle *th,
}
reiserfs_update_sd (th, p_s_inode);
out:
pathrelse(&s_search_path) ;
return err;
}
......@@ -1963,6 +1994,8 @@ int reiserfs_paste_into_item (struct reiserfs_transaction_handle *th,
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_transaction_handle *th,
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);
......
This diff is collapsed.
......@@ -34,6 +34,7 @@ int direct2indirect (struct reiserfs_transaction_handle *th, struct inode * inod
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_transaction_handle *th,
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;
......
......@@ -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_transaction_handle *, struct super_block
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 reiserfs_transaction_handle *th,
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 reiserfs_transaction_handle *th,
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 *dentry, __u32 *data, int *lenp,
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,
......
......@@ -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(struct super_block *s)
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 */
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment