Skip to content

Commit 8353a81

Browse files
Christoph Hellwigdjwong
authored andcommitted
xfs: open code end_buffer_async_write in xfs_finish_page_writeback
Our loop in xfs_finish_page_writeback, which iterates over all buffer heads in a page and then calls end_buffer_async_write, which also iterates over all buffers in the page to check if any I/O is in flight is not only inefficient, but also potentially dangerous as end_buffer_async_write can cause the page and all buffers to be freed. Replace it with a single loop that does the work of end_buffer_async_write on a per-page basis. Signed-off-by: Christoph Hellwig <[email protected]> Reviewed-by: Brian Foster <[email protected]> Reviewed-by: Darrick J. Wong <[email protected]> Signed-off-by: Darrick J. Wong <[email protected]>
1 parent dd60687 commit 8353a81

File tree

1 file changed

+47
-24
lines changed

1 file changed

+47
-24
lines changed

fs/xfs/xfs_aops.c

Lines changed: 47 additions & 24 deletions
Original file line numberDiff line numberDiff line change
@@ -85,41 +85,56 @@ xfs_find_bdev_for_inode(
8585
* associated buffer_heads, paying attention to the start and end offsets that
8686
* we need to process on the page.
8787
*
88-
* Landmine Warning: bh->b_end_io() will call end_page_writeback() on the last
89-
* buffer in the IO. Once it does this, it is unsafe to access the bufferhead or
90-
* the page at all, as we may be racing with memory reclaim and it can free both
91-
* the bufferhead chain and the page as it will see the page as clean and
92-
* unused.
88+
* Note that we open code the action in end_buffer_async_write here so that we
89+
* only have to iterate over the buffers attached to the page once. This is not
90+
* only more efficient, but also ensures that we only calls end_page_writeback
91+
* at the end of the iteration, and thus avoids the pitfall of having the page
92+
* and buffers potentially freed after every call to end_buffer_async_write.
9393
*/
9494
static void
9595
xfs_finish_page_writeback(
9696
struct inode *inode,
9797
struct bio_vec *bvec,
9898
int error)
9999
{
100-
unsigned int end = bvec->bv_offset + bvec->bv_len - 1;
101-
struct buffer_head *head, *bh, *next;
100+
struct buffer_head *head = page_buffers(bvec->bv_page), *bh = head;
101+
bool busy = false;
102102
unsigned int off = 0;
103-
unsigned int bsize;
103+
unsigned long flags;
104104

105105
ASSERT(bvec->bv_offset < PAGE_SIZE);
106106
ASSERT((bvec->bv_offset & (i_blocksize(inode) - 1)) == 0);
107-
ASSERT(end < PAGE_SIZE);
107+
ASSERT(bvec->bv_offset + bvec->bv_len <= PAGE_SIZE);
108108
ASSERT((bvec->bv_len & (i_blocksize(inode) - 1)) == 0);
109109

110-
bh = head = page_buffers(bvec->bv_page);
111-
112-
bsize = bh->b_size;
110+
local_irq_save(flags);
111+
bit_spin_lock(BH_Uptodate_Lock, &head->b_state);
113112
do {
114-
if (off > end)
115-
break;
116-
next = bh->b_this_page;
117-
if (off < bvec->bv_offset)
118-
goto next_bh;
119-
bh->b_end_io(bh, !error);
120-
next_bh:
121-
off += bsize;
122-
} while ((bh = next) != head);
113+
if (off >= bvec->bv_offset &&
114+
off < bvec->bv_offset + bvec->bv_len) {
115+
ASSERT(buffer_async_write(bh));
116+
ASSERT(bh->b_end_io == NULL);
117+
118+
if (error) {
119+
mark_buffer_write_io_error(bh);
120+
clear_buffer_uptodate(bh);
121+
SetPageError(bvec->bv_page);
122+
} else {
123+
set_buffer_uptodate(bh);
124+
}
125+
clear_buffer_async_write(bh);
126+
unlock_buffer(bh);
127+
} else if (buffer_async_write(bh)) {
128+
ASSERT(buffer_locked(bh));
129+
busy = true;
130+
}
131+
off += bh->b_size;
132+
} while ((bh = bh->b_this_page) != head);
133+
bit_spin_unlock(BH_Uptodate_Lock, &head->b_state);
134+
local_irq_restore(flags);
135+
136+
if (!busy)
137+
end_page_writeback(bvec->bv_page);
123138
}
124139

125140
/*
@@ -133,8 +148,10 @@ xfs_destroy_ioend(
133148
int error)
134149
{
135150
struct inode *inode = ioend->io_inode;
136-
struct bio *last = ioend->io_bio;
137-
struct bio *bio, *next;
151+
struct bio *bio = &ioend->io_inline_bio;
152+
struct bio *last = ioend->io_bio, *next;
153+
u64 start = bio->bi_iter.bi_sector;
154+
bool quiet = bio_flagged(bio, BIO_QUIET);
138155

139156
for (bio = &ioend->io_inline_bio; bio; bio = next) {
140157
struct bio_vec *bvec;
@@ -155,6 +172,11 @@ xfs_destroy_ioend(
155172

156173
bio_put(bio);
157174
}
175+
176+
if (unlikely(error && !quiet)) {
177+
xfs_err_ratelimited(XFS_I(inode)->i_mount,
178+
"writeback error on sector %llu", start);
179+
}
158180
}
159181

160182
/*
@@ -423,7 +445,8 @@ xfs_start_buffer_writeback(
423445
ASSERT(!buffer_delay(bh));
424446
ASSERT(!buffer_unwritten(bh));
425447

426-
mark_buffer_async_write(bh);
448+
bh->b_end_io = NULL;
449+
set_buffer_async_write(bh);
427450
set_buffer_uptodate(bh);
428451
clear_buffer_dirty(bh);
429452
}

0 commit comments

Comments
 (0)