Skip to content

Commit 1b151e2

Browse files
Matthew Wilcox (Oracle)axboe
Matthew Wilcox (Oracle)
authored andcommitted
block: Remove special-casing of compound pages
The special casing was originally added in pre-git history; reproducing the commit log here: > commit a318a92567d77 > Author: Andrew Morton <akpm@osdl.org> > Date: Sun Sep 21 01:42:22 2003 -0700 > > [PATCH] Speed up direct-io hugetlbpage handling > > This patch short-circuits all the direct-io page dirtying logic for > higher-order pages. Without this, we pointlessly bounce BIOs up to > keventd all the time. In the last twenty years, compound pages have become used for more than just hugetlb. Rewrite these functions to operate on folios instead of pages and remove the special case for hugetlbfs; I don't think it's needed any more (and if it is, we can put it back in as a call to folio_test_hugetlb()). This was found by inspection; as far as I can tell, this bug can lead to pages used as the destination of a direct I/O read not being marked as dirty. If those pages are then reclaimed by the MM without being dirtied for some other reason, they won't be written out. Then when they're faulted back in, they will not contain the data they should. It'll take a pretty unusual setup to produce this problem with several races all going the wrong way. This problem predates the folio work; it could for example have been triggered by mmaping a THP in tmpfs and using that as the target of an O_DIRECT read. Fixes: 800d8c6 ("shmem: add huge pages support") Cc: <stable@vger.kernel.org> Signed-off-by: Matthew Wilcox (Oracle) <willy@infradead.org> Signed-off-by: Jens Axboe <axboe@kernel.dk>
1 parent 847c5bc commit 1b151e2

File tree

1 file changed

+24
-22
lines changed

1 file changed

+24
-22
lines changed

block/bio.c

+24-22
Original file line numberDiff line numberDiff line change
@@ -1145,13 +1145,22 @@ EXPORT_SYMBOL(bio_add_folio);
11451145

11461146
void __bio_release_pages(struct bio *bio, bool mark_dirty)
11471147
{
1148-
struct bvec_iter_all iter_all;
1149-
struct bio_vec *bvec;
1148+
struct folio_iter fi;
1149+
1150+
bio_for_each_folio_all(fi, bio) {
1151+
struct page *page;
1152+
size_t done = 0;
11501153

1151-
bio_for_each_segment_all(bvec, bio, iter_all) {
1152-
if (mark_dirty && !PageCompound(bvec->bv_page))
1153-
set_page_dirty_lock(bvec->bv_page);
1154-
bio_release_page(bio, bvec->bv_page);
1154+
if (mark_dirty) {
1155+
folio_lock(fi.folio);
1156+
folio_mark_dirty(fi.folio);
1157+
folio_unlock(fi.folio);
1158+
}
1159+
page = folio_page(fi.folio, fi.offset / PAGE_SIZE);
1160+
do {
1161+
bio_release_page(bio, page++);
1162+
done += PAGE_SIZE;
1163+
} while (done < fi.length);
11551164
}
11561165
}
11571166
EXPORT_SYMBOL_GPL(__bio_release_pages);
@@ -1439,18 +1448,12 @@ EXPORT_SYMBOL(bio_free_pages);
14391448
* bio_set_pages_dirty() and bio_check_pages_dirty() are support functions
14401449
* for performing direct-IO in BIOs.
14411450
*
1442-
* The problem is that we cannot run set_page_dirty() from interrupt context
1451+
* The problem is that we cannot run folio_mark_dirty() from interrupt context
14431452
* because the required locks are not interrupt-safe. So what we can do is to
14441453
* mark the pages dirty _before_ performing IO. And in interrupt context,
14451454
* check that the pages are still dirty. If so, fine. If not, redirty them
14461455
* in process context.
14471456
*
1448-
* We special-case compound pages here: normally this means reads into hugetlb
1449-
* pages. The logic in here doesn't really work right for compound pages
1450-
* because the VM does not uniformly chase down the head page in all cases.
1451-
* But dirtiness of compound pages is pretty meaningless anyway: the VM doesn't
1452-
* handle them at all. So we skip compound pages here at an early stage.
1453-
*
14541457
* Note that this code is very hard to test under normal circumstances because
14551458
* direct-io pins the pages with get_user_pages(). This makes
14561459
* is_page_cache_freeable return false, and the VM will not clean the pages.
@@ -1466,12 +1469,12 @@ EXPORT_SYMBOL(bio_free_pages);
14661469
*/
14671470
void bio_set_pages_dirty(struct bio *bio)
14681471
{
1469-
struct bio_vec *bvec;
1470-
struct bvec_iter_all iter_all;
1472+
struct folio_iter fi;
14711473

1472-
bio_for_each_segment_all(bvec, bio, iter_all) {
1473-
if (!PageCompound(bvec->bv_page))
1474-
set_page_dirty_lock(bvec->bv_page);
1474+
bio_for_each_folio_all(fi, bio) {
1475+
folio_lock(fi.folio);
1476+
folio_mark_dirty(fi.folio);
1477+
folio_unlock(fi.folio);
14751478
}
14761479
}
14771480
EXPORT_SYMBOL_GPL(bio_set_pages_dirty);
@@ -1515,12 +1518,11 @@ static void bio_dirty_fn(struct work_struct *work)
15151518

15161519
void bio_check_pages_dirty(struct bio *bio)
15171520
{
1518-
struct bio_vec *bvec;
1521+
struct folio_iter fi;
15191522
unsigned long flags;
1520-
struct bvec_iter_all iter_all;
15211523

1522-
bio_for_each_segment_all(bvec, bio, iter_all) {
1523-
if (!PageDirty(bvec->bv_page) && !PageCompound(bvec->bv_page))
1524+
bio_for_each_folio_all(fi, bio) {
1525+
if (!folio_test_dirty(fi.folio))
15241526
goto defer;
15251527
}
15261528

0 commit comments

Comments
 (0)