Skip to content

Commit

Permalink
Merge tag 'folio-5.18f' of git://git.infradead.org/users/willy/pagecache
Browse files Browse the repository at this point in the history
Pull folio fixes from Matthew Wilcox:
 "Two folio fixes for 5.18.

  Darrick and Brian have done amazing work debugging the race I created
  in the folio BIO iterator. The readahead problem was deterministic, so
  easy to fix.

   - Fix a race when we were calling folio_next() in the BIO folio iter
     without holding a reference, meaning the folio could be split or
     freed, and we'd jump to the next page instead of the intended next
     folio.

   - Fix readahead creating single-page folios instead of the intended
     large folios when doing reads that are not a power of two in size"

* tag 'folio-5.18f' of git://git.infradead.org/users/willy/pagecache:
  mm/readahead: Fix readahead with large folios
  block: Do not call folio_next() on an unreferenced folio
  • Loading branch information
torvalds committed May 5, 2022
2 parents f47c960 + b9ff43d commit fe27d18
Show file tree
Hide file tree
Showing 2 changed files with 13 additions and 7 deletions.
5 changes: 4 additions & 1 deletion include/linux/bio.h
Original file line number Diff line number Diff line change
Expand Up @@ -269,6 +269,7 @@ struct folio_iter {
size_t offset;
size_t length;
/* private: for use by the iterator */
struct folio *_next;
size_t _seg_count;
int _i;
};
Expand All @@ -283,16 +284,18 @@ static inline void bio_first_folio(struct folio_iter *fi, struct bio *bio,
PAGE_SIZE * (bvec->bv_page - &fi->folio->page);
fi->_seg_count = bvec->bv_len;
fi->length = min(folio_size(fi->folio) - fi->offset, fi->_seg_count);
fi->_next = folio_next(fi->folio);
fi->_i = i;
}

static inline void bio_next_folio(struct folio_iter *fi, struct bio *bio)
{
fi->_seg_count -= fi->length;
if (fi->_seg_count) {
fi->folio = folio_next(fi->folio);
fi->folio = fi->_next;
fi->offset = 0;
fi->length = min(folio_size(fi->folio), fi->_seg_count);
fi->_next = folio_next(fi->folio);
} else if (fi->_i + 1 < bio->bi_vcnt) {
bio_first_folio(fi, bio, fi->_i + 1);
} else {
Expand Down
15 changes: 9 additions & 6 deletions mm/readahead.c
Original file line number Diff line number Diff line change
Expand Up @@ -474,7 +474,8 @@ static inline int ra_alloc_folio(struct readahead_control *ractl, pgoff_t index,

if (!folio)
return -ENOMEM;
if (mark - index < (1UL << order))
mark = round_up(mark, 1UL << order);
if (index == mark)
folio_set_readahead(folio);
err = filemap_add_folio(ractl->mapping, folio, index, gfp);
if (err)
Expand Down Expand Up @@ -555,8 +556,9 @@ static void ondemand_readahead(struct readahead_control *ractl,
struct file_ra_state *ra = ractl->ra;
unsigned long max_pages = ra->ra_pages;
unsigned long add_pages;
unsigned long index = readahead_index(ractl);
pgoff_t prev_index;
pgoff_t index = readahead_index(ractl);
pgoff_t expected, prev_index;
unsigned int order = folio ? folio_order(folio) : 0;

/*
* If the request exceeds the readahead window, allow the read to
Expand All @@ -575,8 +577,9 @@ static void ondemand_readahead(struct readahead_control *ractl,
* It's the expected callback index, assume sequential access.
* Ramp up sizes, and push forward the readahead window.
*/
if ((index == (ra->start + ra->size - ra->async_size) ||
index == (ra->start + ra->size))) {
expected = round_up(ra->start + ra->size - ra->async_size,
1UL << order);
if (index == expected || index == (ra->start + ra->size)) {
ra->start += ra->size;
ra->size = get_next_ra_size(ra, max_pages);
ra->async_size = ra->size;
Expand Down Expand Up @@ -662,7 +665,7 @@ static void ondemand_readahead(struct readahead_control *ractl,
}

ractl->_index = ra->start;
page_cache_ra_order(ractl, ra, folio ? folio_order(folio) : 0);
page_cache_ra_order(ractl, ra, order);
}

void page_cache_sync_ra(struct readahead_control *ractl,
Expand Down

0 comments on commit fe27d18

Please sign in to comment.