mirror of
https://git.kernel.org/pub/scm/linux/kernel/git/stable/linux.git
synced 2025-01-11 23:50:25 +00:00
[XFS] Fix a race in xfs_submit_ioend() where we can be completing I/O for
a page while we are still submitting other buffers on the same page for I/O. SGI-PV: 948197 SGI-Modid: xfs-linux-melb:xfs-kern:25004a Signed-off-by: David Chinner <dgc@sgi.com> Signed-off-by: Nathan Scott <nathans@sgi.com>
This commit is contained in:
parent
2664b25051
commit
d88992f660
@ -336,24 +336,47 @@ static inline int bio_add_buffer(struct bio *bio, struct buffer_head *bh)
|
|||||||
}
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Submit all of the bios for all of the ioends we have saved up,
|
* Submit all of the bios for all of the ioends we have saved up, covering the
|
||||||
* covering the initial writepage page and also any probed pages.
|
* initial writepage page and also any probed pages.
|
||||||
|
*
|
||||||
|
* Because we may have multiple ioends spanning a page, we need to start
|
||||||
|
* writeback on all the buffers before we submit them for I/O. If we mark the
|
||||||
|
* buffers as we got, then we can end up with a page that only has buffers
|
||||||
|
* marked async write and I/O complete on can occur before we mark the other
|
||||||
|
* buffers async write.
|
||||||
|
*
|
||||||
|
* The end result of this is that we trip a bug in end_page_writeback() because
|
||||||
|
* we call it twice for the one page as the code in end_buffer_async_write()
|
||||||
|
* assumes that all buffers on the page are started at the same time.
|
||||||
|
*
|
||||||
|
* The fix is two passes across the ioend list - one to start writeback on the
|
||||||
|
* bufferheads, and then the second one submit them for I/O.
|
||||||
*/
|
*/
|
||||||
STATIC void
|
STATIC void
|
||||||
xfs_submit_ioend(
|
xfs_submit_ioend(
|
||||||
xfs_ioend_t *ioend)
|
xfs_ioend_t *ioend)
|
||||||
{
|
{
|
||||||
|
xfs_ioend_t *head = ioend;
|
||||||
xfs_ioend_t *next;
|
xfs_ioend_t *next;
|
||||||
struct buffer_head *bh;
|
struct buffer_head *bh;
|
||||||
struct bio *bio;
|
struct bio *bio;
|
||||||
sector_t lastblock = 0;
|
sector_t lastblock = 0;
|
||||||
|
|
||||||
|
/* Pass 1 - start writeback */
|
||||||
|
do {
|
||||||
|
next = ioend->io_list;
|
||||||
|
for (bh = ioend->io_buffer_head; bh; bh = bh->b_private) {
|
||||||
|
xfs_start_buffer_writeback(bh);
|
||||||
|
}
|
||||||
|
} while ((ioend = next) != NULL);
|
||||||
|
|
||||||
|
/* Pass 2 - submit I/O */
|
||||||
|
ioend = head;
|
||||||
do {
|
do {
|
||||||
next = ioend->io_list;
|
next = ioend->io_list;
|
||||||
bio = NULL;
|
bio = NULL;
|
||||||
|
|
||||||
for (bh = ioend->io_buffer_head; bh; bh = bh->b_private) {
|
for (bh = ioend->io_buffer_head; bh; bh = bh->b_private) {
|
||||||
xfs_start_buffer_writeback(bh);
|
|
||||||
|
|
||||||
if (!bio) {
|
if (!bio) {
|
||||||
retry:
|
retry:
|
||||||
|
Loading…
x
Reference in New Issue
Block a user