Commit 83668e71 authored by Theodore Ts'o's avatar Theodore Ts'o

ext4: fix potential race when freeing ext4_io_page structures

Use an atomic_t and make sure we don't free the structure while we
might still be submitting I/O for that page.
Signed-off-by: default avatar"Theodore Ts'o" <tytso@mit.edu>
parent f7ad6d2e
...@@ -177,7 +177,7 @@ struct mpage_da_data { ...@@ -177,7 +177,7 @@ struct mpage_da_data {
struct ext4_io_page { struct ext4_io_page {
struct page *p_page; struct page *p_page;
int p_count; atomic_t p_count;
}; };
#define MAX_IO_PAGES 128 #define MAX_IO_PAGES 128
......
...@@ -67,6 +67,15 @@ void ext4_ioend_wait(struct inode *inode) ...@@ -67,6 +67,15 @@ void ext4_ioend_wait(struct inode *inode)
wait_event(*wq, (atomic_read(&EXT4_I(inode)->i_ioend_count) == 0)); wait_event(*wq, (atomic_read(&EXT4_I(inode)->i_ioend_count) == 0));
} }
static void put_io_page(struct ext4_io_page *io_page)
{
if (atomic_dec_and_test(&io_page->p_count)) {
end_page_writeback(io_page->p_page);
put_page(io_page->p_page);
kmem_cache_free(io_page_cachep, io_page);
}
}
void ext4_free_io_end(ext4_io_end_t *io) void ext4_free_io_end(ext4_io_end_t *io)
{ {
int i; int i;
...@@ -75,15 +84,8 @@ void ext4_free_io_end(ext4_io_end_t *io) ...@@ -75,15 +84,8 @@ void ext4_free_io_end(ext4_io_end_t *io)
BUG_ON(!io); BUG_ON(!io);
if (io->page) if (io->page)
put_page(io->page); put_page(io->page);
for (i = 0; i < io->num_io_pages; i++) { for (i = 0; i < io->num_io_pages; i++)
if (--io->pages[i]->p_count == 0) { put_io_page(io->pages[i]);
struct page *page = io->pages[i]->p_page;
end_page_writeback(page);
put_page(page);
kmem_cache_free(io_page_cachep, io->pages[i]);
}
}
io->num_io_pages = 0; io->num_io_pages = 0;
wq = to_ioend_wq(io->inode); wq = to_ioend_wq(io->inode);
if (atomic_dec_and_test(&EXT4_I(io->inode)->i_ioend_count) && if (atomic_dec_and_test(&EXT4_I(io->inode)->i_ioend_count) &&
...@@ -235,13 +237,7 @@ static void ext4_end_bio(struct bio *bio, int error) ...@@ -235,13 +237,7 @@ static void ext4_end_bio(struct bio *bio, int error)
} while (bh != head); } while (bh != head);
} }
if (--io_end->pages[i]->p_count == 0) { put_io_page(io_end->pages[i]);
struct page *page = io_end->pages[i]->p_page;
end_page_writeback(page);
put_page(page);
kmem_cache_free(io_page_cachep, io_end->pages[i]);
}
/* /*
* If this is a partial write which happened to make * If this is a partial write which happened to make
...@@ -369,7 +365,7 @@ static int io_submit_add_bh(struct ext4_io_submit *io, ...@@ -369,7 +365,7 @@ static int io_submit_add_bh(struct ext4_io_submit *io,
if ((io_end->num_io_pages == 0) || if ((io_end->num_io_pages == 0) ||
(io_end->pages[io_end->num_io_pages-1] != io_page)) { (io_end->pages[io_end->num_io_pages-1] != io_page)) {
io_end->pages[io_end->num_io_pages++] = io_page; io_end->pages[io_end->num_io_pages++] = io_page;
io_page->p_count++; atomic_inc(&io_page->p_count);
} }
return 0; return 0;
} }
...@@ -398,7 +394,7 @@ int ext4_bio_write_page(struct ext4_io_submit *io, ...@@ -398,7 +394,7 @@ int ext4_bio_write_page(struct ext4_io_submit *io,
return -ENOMEM; return -ENOMEM;
} }
io_page->p_page = page; io_page->p_page = page;
io_page->p_count = 0; atomic_set(&io_page->p_count, 1);
get_page(page); get_page(page);
for (bh = head = page_buffers(page), block_start = 0; for (bh = head = page_buffers(page), block_start = 0;
...@@ -430,10 +426,6 @@ int ext4_bio_write_page(struct ext4_io_submit *io, ...@@ -430,10 +426,6 @@ int ext4_bio_write_page(struct ext4_io_submit *io,
* PageWriteback bit from the page to prevent the system from * PageWriteback bit from the page to prevent the system from
* wedging later on. * wedging later on.
*/ */
if (io_page->p_count == 0) { put_io_page(io_page);
put_page(page);
end_page_writeback(page);
kmem_cache_free(io_page_cachep, io_page);
}
return ret; return ret;
} }
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