aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorDmitry Monakhov <dmonakhov@openvz.org>2012-09-28 23:36:25 -0400
committerTheodore Ts'o <tytso@mit.edu>2012-09-28 23:36:25 -0400
commit82e54229118785badffb4ef5ba4803df25fe007f (patch)
tree270d0afb27dce342b7508cd05bb0db45cdad089d
parente27f41e1b789e60e7d8cc9c81fd93ca49ef31f13 (diff)
ext4: fix unwritten counter leakage
ext4_set_io_unwritten_flag() will increment i_unwritten counter, so once we mark end_io with EXT4_END_IO_UNWRITTEN we have to revert it back on error path. - add missed error checks to prevent counter leakage - ext4_end_io_nolock() will clear EXT4_END_IO_UNWRITTEN flag to signal that conversion finished. - add BUG_ON to ext4_free_end_io() to prevent similar leakage in future. Visible effect of this bug is that unaligned aio_stress may deadlock Reviewed-by: Jan Kara <jack@suse.cz> Signed-off-by: Dmitry Monakhov <dmonakhov@openvz.org> Signed-off-by: "Theodore Ts'o" <tytso@mit.edu>
-rw-r--r--fs/ext4/extents.c21
-rw-r--r--fs/ext4/page-io.c6
2 files changed, 19 insertions, 8 deletions
diff --git a/fs/ext4/extents.c b/fs/ext4/extents.c
index a1f56c3e773b..54a94426ef7b 100644
--- a/fs/ext4/extents.c
+++ b/fs/ext4/extents.c
@@ -3633,6 +3633,8 @@ ext4_ext_handle_uninitialized_extents(handle_t *handle, struct inode *inode,
3633 if ((flags & EXT4_GET_BLOCKS_PRE_IO)) { 3633 if ((flags & EXT4_GET_BLOCKS_PRE_IO)) {
3634 ret = ext4_split_unwritten_extents(handle, inode, map, 3634 ret = ext4_split_unwritten_extents(handle, inode, map,
3635 path, flags); 3635 path, flags);
3636 if (ret <= 0)
3637 goto out;
3636 /* 3638 /*
3637 * Flag the inode(non aio case) or end_io struct (aio case) 3639 * Flag the inode(non aio case) or end_io struct (aio case)
3638 * that this IO needs to conversion to written when IO is 3640 * that this IO needs to conversion to written when IO is
@@ -3878,6 +3880,7 @@ int ext4_ext_map_blocks(handle_t *handle, struct inode *inode,
3878 struct ext4_allocation_request ar; 3880 struct ext4_allocation_request ar;
3879 ext4_io_end_t *io = ext4_inode_aio(inode); 3881 ext4_io_end_t *io = ext4_inode_aio(inode);
3880 ext4_lblk_t cluster_offset; 3882 ext4_lblk_t cluster_offset;
3883 int set_unwritten = 0;
3881 3884
3882 ext_debug("blocks %u/%u requested for inode %lu\n", 3885 ext_debug("blocks %u/%u requested for inode %lu\n",
3883 map->m_lblk, map->m_len, inode->i_ino); 3886 map->m_lblk, map->m_len, inode->i_ino);
@@ -4100,13 +4103,8 @@ got_allocated_blocks:
4100 * For non asycn direct IO case, flag the inode state 4103 * For non asycn direct IO case, flag the inode state
4101 * that we need to perform conversion when IO is done. 4104 * that we need to perform conversion when IO is done.
4102 */ 4105 */
4103 if ((flags & EXT4_GET_BLOCKS_PRE_IO)) { 4106 if ((flags & EXT4_GET_BLOCKS_PRE_IO))
4104 if (io) 4107 set_unwritten = 1;
4105 ext4_set_io_unwritten_flag(inode, io);
4106 else
4107 ext4_set_inode_state(inode,
4108 EXT4_STATE_DIO_UNWRITTEN);
4109 }
4110 if (ext4_should_dioread_nolock(inode)) 4108 if (ext4_should_dioread_nolock(inode))
4111 map->m_flags |= EXT4_MAP_UNINIT; 4109 map->m_flags |= EXT4_MAP_UNINIT;
4112 } 4110 }
@@ -4118,6 +4116,15 @@ got_allocated_blocks:
4118 if (!err) 4116 if (!err)
4119 err = ext4_ext_insert_extent(handle, inode, path, 4117 err = ext4_ext_insert_extent(handle, inode, path,
4120 &newex, flags); 4118 &newex, flags);
4119
4120 if (!err && set_unwritten) {
4121 if (io)
4122 ext4_set_io_unwritten_flag(inode, io);
4123 else
4124 ext4_set_inode_state(inode,
4125 EXT4_STATE_DIO_UNWRITTEN);
4126 }
4127
4121 if (err && free_on_err) { 4128 if (err && free_on_err) {
4122 int fb_flags = flags & EXT4_GET_BLOCKS_DELALLOC_RESERVE ? 4129 int fb_flags = flags & EXT4_GET_BLOCKS_DELALLOC_RESERVE ?
4123 EXT4_FREE_BLOCKS_NO_QUOT_UPDATE : 0; 4130 EXT4_FREE_BLOCKS_NO_QUOT_UPDATE : 0;
diff --git a/fs/ext4/page-io.c b/fs/ext4/page-io.c
index de77e31cc119..997002218228 100644
--- a/fs/ext4/page-io.c
+++ b/fs/ext4/page-io.c
@@ -71,6 +71,8 @@ void ext4_free_io_end(ext4_io_end_t *io)
71 int i; 71 int i;
72 72
73 BUG_ON(!io); 73 BUG_ON(!io);
74 BUG_ON(io->flag & EXT4_IO_END_UNWRITTEN);
75
74 if (io->page) 76 if (io->page)
75 put_page(io->page); 77 put_page(io->page);
76 for (i = 0; i < io->num_io_pages; i++) 78 for (i = 0; i < io->num_io_pages; i++)
@@ -94,6 +96,8 @@ int ext4_end_io_nolock(ext4_io_end_t *io)
94 ssize_t size = io->size; 96 ssize_t size = io->size;
95 int ret = 0; 97 int ret = 0;
96 98
99 BUG_ON(!(io->flag & EXT4_IO_END_UNWRITTEN));
100
97 ext4_debug("ext4_end_io_nolock: io 0x%p from inode %lu,list->next 0x%p," 101 ext4_debug("ext4_end_io_nolock: io 0x%p from inode %lu,list->next 0x%p,"
98 "list->prev 0x%p\n", 102 "list->prev 0x%p\n",
99 io, inode->i_ino, io->list.next, io->list.prev); 103 io, inode->i_ino, io->list.next, io->list.prev);
@@ -106,7 +110,7 @@ int ext4_end_io_nolock(ext4_io_end_t *io)
106 "(inode %lu, offset %llu, size %zd, error %d)", 110 "(inode %lu, offset %llu, size %zd, error %d)",
107 inode->i_ino, offset, size, ret); 111 inode->i_ino, offset, size, ret);
108 } 112 }
109 113 io->flag &= ~EXT4_IO_END_UNWRITTEN;
110 if (io->iocb) 114 if (io->iocb)
111 aio_complete(io->iocb, io->result, 0); 115 aio_complete(io->iocb, io->result, 0);
112 116