Skip to content

Commit 170f37d

Browse files
author
Matthew Wilcox (Oracle)
committed
block: Do not call folio_next() on an unreferenced folio
It is unsafe to call folio_next() on a folio unless you hold a reference on it that prevents it from being split or freed. After returning from the iterator, iomap calls folio_end_writeback() which may drop the last reference to the page, or allow the page to be split. If that happens, the iterator will not advance far enough through the bio_vec, leading to assertion failures like the BUG() in folio_end_writeback() that checks we're not trying to end writeback on a page not currently under writeback. Other assertion failures were also seen, but they're all explained by this one bug. Fix the bug by remembering where the next folio starts before returning from the iterator. There are other ways of fixing this bug, but this seems the simplest. Reported-by: Darrick J. Wong <djwong@kernel.org> Tested-by: Darrick J. Wong <djwong@kernel.org> Reported-by: Brian Foster <bfoster@redhat.com> Tested-by: Brian Foster <bfoster@redhat.com> Signed-off-by: Matthew Wilcox (Oracle) <willy@infradead.org>
1 parent a7391ad commit 170f37d

1 file changed

Lines changed: 4 additions & 1 deletion

File tree

include/linux/bio.h

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -269,6 +269,7 @@ struct folio_iter {
269269
size_t offset;
270270
size_t length;
271271
/* private: for use by the iterator */
272+
struct folio *_next;
272273
size_t _seg_count;
273274
int _i;
274275
};
@@ -283,16 +284,18 @@ static inline void bio_first_folio(struct folio_iter *fi, struct bio *bio,
283284
PAGE_SIZE * (bvec->bv_page - &fi->folio->page);
284285
fi->_seg_count = bvec->bv_len;
285286
fi->length = min(folio_size(fi->folio) - fi->offset, fi->_seg_count);
287+
fi->_next = folio_next(fi->folio);
286288
fi->_i = i;
287289
}
288290

289291
static inline void bio_next_folio(struct folio_iter *fi, struct bio *bio)
290292
{
291293
fi->_seg_count -= fi->length;
292294
if (fi->_seg_count) {
293-
fi->folio = folio_next(fi->folio);
295+
fi->folio = fi->_next;
294296
fi->offset = 0;
295297
fi->length = min(folio_size(fi->folio), fi->_seg_count);
298+
fi->_next = folio_next(fi->folio);
296299
} else if (fi->_i + 1 < bio->bi_vcnt) {
297300
bio_first_folio(fi, bio, fi->_i + 1);
298301
} else {

0 commit comments

Comments
 (0)