Commit 58ff407b authored by Jan Kara's avatar Jan Kara Committed by Linus Torvalds

[PATCH] Fix IO error reporting on fsync()

When IO error happens on metadata buffer, buffer is freed from memory and
later fsync() is called, filesystems like ext2 fail to report EIO.  We

solve the problem by introducing a pointer to associated address space into
the buffer_head.  When a buffer is removed from a list of metadata buffers
associated with an address space, IO error is transferred from the buffer to
the address space, so that fsync can later report it.
Signed-off-by: default avatarJan Kara <jack@suse.cz>
Signed-off-by: default avatarAndrew Morton <akpm@osdl.org>
Signed-off-by: default avatarLinus Torvalds <torvalds@osdl.org>
parent d343fce1
...@@ -452,6 +452,7 @@ static void end_buffer_async_write(struct buffer_head *bh, int uptodate) ...@@ -452,6 +452,7 @@ static void end_buffer_async_write(struct buffer_head *bh, int uptodate)
bdevname(bh->b_bdev, b)); bdevname(bh->b_bdev, b));
} }
set_bit(AS_EIO, &page->mapping->flags); set_bit(AS_EIO, &page->mapping->flags);
set_buffer_write_io_error(bh);
clear_buffer_uptodate(bh); clear_buffer_uptodate(bh);
SetPageError(page); SetPageError(page);
} }
...@@ -571,6 +572,10 @@ EXPORT_SYMBOL(mark_buffer_async_write); ...@@ -571,6 +572,10 @@ EXPORT_SYMBOL(mark_buffer_async_write);
static inline void __remove_assoc_queue(struct buffer_head *bh) static inline void __remove_assoc_queue(struct buffer_head *bh)
{ {
list_del_init(&bh->b_assoc_buffers); list_del_init(&bh->b_assoc_buffers);
WARN_ON(!bh->b_assoc_map);
if (buffer_write_io_error(bh))
set_bit(AS_EIO, &bh->b_assoc_map->flags);
bh->b_assoc_map = NULL;
} }
int inode_has_buffers(struct inode *inode) int inode_has_buffers(struct inode *inode)
...@@ -669,6 +674,7 @@ void mark_buffer_dirty_inode(struct buffer_head *bh, struct inode *inode) ...@@ -669,6 +674,7 @@ void mark_buffer_dirty_inode(struct buffer_head *bh, struct inode *inode)
spin_lock(&buffer_mapping->private_lock); spin_lock(&buffer_mapping->private_lock);
list_move_tail(&bh->b_assoc_buffers, list_move_tail(&bh->b_assoc_buffers,
&mapping->private_list); &mapping->private_list);
bh->b_assoc_map = mapping;
spin_unlock(&buffer_mapping->private_lock); spin_unlock(&buffer_mapping->private_lock);
} }
} }
...@@ -765,7 +771,7 @@ static int fsync_buffers_list(spinlock_t *lock, struct list_head *list) ...@@ -765,7 +771,7 @@ static int fsync_buffers_list(spinlock_t *lock, struct list_head *list)
spin_lock(lock); spin_lock(lock);
while (!list_empty(list)) { while (!list_empty(list)) {
bh = BH_ENTRY(list->next); bh = BH_ENTRY(list->next);
list_del_init(&bh->b_assoc_buffers); __remove_assoc_queue(bh);
if (buffer_dirty(bh) || buffer_locked(bh)) { if (buffer_dirty(bh) || buffer_locked(bh)) {
list_add(&bh->b_assoc_buffers, &tmp); list_add(&bh->b_assoc_buffers, &tmp);
if (buffer_dirty(bh)) { if (buffer_dirty(bh)) {
...@@ -786,7 +792,7 @@ static int fsync_buffers_list(spinlock_t *lock, struct list_head *list) ...@@ -786,7 +792,7 @@ static int fsync_buffers_list(spinlock_t *lock, struct list_head *list)
while (!list_empty(&tmp)) { while (!list_empty(&tmp)) {
bh = BH_ENTRY(tmp.prev); bh = BH_ENTRY(tmp.prev);
__remove_assoc_queue(bh); list_del_init(&bh->b_assoc_buffers);
get_bh(bh); get_bh(bh);
spin_unlock(lock); spin_unlock(lock);
wait_on_buffer(bh); wait_on_buffer(bh);
...@@ -1167,6 +1173,7 @@ void __bforget(struct buffer_head *bh) ...@@ -1167,6 +1173,7 @@ void __bforget(struct buffer_head *bh)
spin_lock(&buffer_mapping->private_lock); spin_lock(&buffer_mapping->private_lock);
list_del_init(&bh->b_assoc_buffers); list_del_init(&bh->b_assoc_buffers);
bh->b_assoc_map = NULL;
spin_unlock(&buffer_mapping->private_lock); spin_unlock(&buffer_mapping->private_lock);
} }
__brelse(bh); __brelse(bh);
......
...@@ -69,6 +69,8 @@ struct buffer_head { ...@@ -69,6 +69,8 @@ struct buffer_head {
bh_end_io_t *b_end_io; /* I/O completion */ bh_end_io_t *b_end_io; /* I/O completion */
void *b_private; /* reserved for b_end_io */ void *b_private; /* reserved for b_end_io */
struct list_head b_assoc_buffers; /* associated with another mapping */ struct list_head b_assoc_buffers; /* associated with another mapping */
struct address_space *b_assoc_map; /* mapping this buffer is
associated with */
atomic_t b_count; /* users using this buffer_head */ atomic_t b_count; /* users using this buffer_head */
}; };
......
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