There could be a corner case where the first entry is non-zeromap, but a subsequent entry is zeromap. In this case, we should not return false. Additionally, the iteration of test_bit() is unnecessary and can be replaced with bitmap operations, which are more efficient. Since swap_read_folio() can't handle reading a large folio that's partially zeromap and partially non-zeromap, we've moved the code to mm/swap.h so that others, like those working on swap-in, can access it. Fixes: 0ca0c24e3211 ("mm: store zero pages to be swapped out in a bitmap") Signed-off-by: Barry Song <v-songbaohua@xxxxxxxx> --- mm/page_io.c | 27 ++++----------------------- mm/swap.h | 29 +++++++++++++++++++++++++++++ 2 files changed, 33 insertions(+), 23 deletions(-) diff --git a/mm/page_io.c b/mm/page_io.c index 4bc77d1c6bfa..46907c9dd20b 100644 --- a/mm/page_io.c +++ b/mm/page_io.c @@ -226,26 +226,6 @@ static void swap_zeromap_folio_clear(struct folio *folio) } } -/* - * Return the index of the first subpage which is not zero-filled - * according to swap_info_struct->zeromap. - * If all pages are zero-filled according to zeromap, it will return - * folio_nr_pages(folio). - */ -static unsigned int swap_zeromap_folio_test(struct folio *folio) -{ - struct swap_info_struct *sis = swp_swap_info(folio->swap); - swp_entry_t entry; - unsigned int i; - - for (i = 0; i < folio_nr_pages(folio); i++) { - entry = page_swap_entry(folio_page(folio, i)); - if (!test_bit(swp_offset(entry), sis->zeromap)) - return i; - } - return i; -} - /* * We may have stale swap cache pages in memory: notice * them here and get rid of the unnecessary final write. @@ -524,9 +504,10 @@ static void sio_read_complete(struct kiocb *iocb, long ret) static bool swap_read_folio_zeromap(struct folio *folio) { - unsigned int idx = swap_zeromap_folio_test(folio); + unsigned int nr_pages = folio_nr_pages(folio); + zeromap_stat_t stat = swap_zeromap_entries_check(folio->swap, nr_pages); - if (idx == 0) + if (stat == SWAP_ZEROMAP_NON) return false; /* @@ -534,7 +515,7 @@ static bool swap_read_folio_zeromap(struct folio *folio) * currently handled. Return true without marking the folio uptodate so * that an IO error is emitted (e.g. do_swap_page() will sigbus). */ - if (WARN_ON_ONCE(idx < folio_nr_pages(folio))) + if (WARN_ON_ONCE(stat == SWAP_ZEROMAP_PARTIAL)) return true; folio_zero_range(folio, 0, folio_size(folio)); diff --git a/mm/swap.h b/mm/swap.h index f8711ff82f84..f8e3fa061c1d 100644 --- a/mm/swap.h +++ b/mm/swap.h @@ -4,6 +4,12 @@ struct mempolicy; +typedef enum { + SWAP_ZEROMAP_NON, + SWAP_ZEROMAP_FULL, + SWAP_ZEROMAP_PARTIAL +} zeromap_stat_t; + #ifdef CONFIG_SWAP #include <linux/swapops.h> /* for swp_offset */ #include <linux/blk_types.h> /* for bio_end_io_t */ @@ -80,6 +86,24 @@ static inline unsigned int folio_swap_flags(struct folio *folio) { return swp_swap_info(folio->swap)->flags; } + +/* + * Check if nr entries are all zeromap, non-zeromap or partially zeromap + */ +static inline zeromap_stat_t swap_zeromap_entries_check(swp_entry_t entry, int nr) +{ + struct swap_info_struct *sis = swp_swap_info(entry); + unsigned long start = swp_offset(entry); + unsigned long end = start + nr; + + if (find_next_bit(sis->zeromap, end, start) == end) + return SWAP_ZEROMAP_NON; + if (find_next_zero_bit(sis->zeromap, end, start) == end) + return SWAP_ZEROMAP_FULL; + + return SWAP_ZEROMAP_PARTIAL; +} + #else /* CONFIG_SWAP */ struct swap_iocb; static inline void swap_read_folio(struct folio *folio, struct swap_iocb **plug) @@ -171,6 +195,11 @@ static inline unsigned int folio_swap_flags(struct folio *folio) { return 0; } + +static inline zeromap_stat_t swap_zeromap_entries_check(swp_entry_t entry, int nr) +{ + return SWAP_ZEROMAP_NONE; +} #endif /* CONFIG_SWAP */ #endif /* _MM_SWAP_H */ -- 2.34.1