[PATCH 6/7] mm: Cleanup - Reorganize code to group handling of page
From: Tim Chen
Date: Tue May 03 2016 - 17:03:25 EST
In this patch, we reorganize the paging operations so the paging
operations of pages to the same swap device can be grouped together.
This prepares for the next patch that remove multiple pages from
the same swap cache together once they have been paged out.
The patch creates a new function handle_pgout_batch that takes
the code of handle_pgout and put a loop around handle_pgout code for
multiple pages.
Signed-off-by: Tim Chen <tim.c.chen@xxxxxxxxxxxxxxx>
---
Âmm/vmscan.c | 338 +++++++++++++++++++++++++++++++++++-------------------------
Â1 file changed, 196 insertions(+), 142 deletions(-)
diff --git a/mm/vmscan.c b/mm/vmscan.c
index fab61f1..9fc04e1 100644
--- a/mm/vmscan.c
+++ b/mm/vmscan.c
@@ -884,154 +884,218 @@ enum pg_result {
 PG_UNKNOWN,
Â};
Â
-static enum pg_result handle_pgout(struct list_head *page_list,
+static void handle_pgout_batch(struct list_head *page_list,
 struct zone *zone,
 struct scan_control *sc,
 enum ttu_flags ttu_flags,
 enum page_references references,
 bool may_enter_fs,
 bool lazyfree,
- intÂÂ*swap_ret,
- struct page *page)
+ struct page *pages[],
+ intÂÂswap_ret[],
+ int ret[],
+ int nr)
Â{
 struct address_space *mapping;
+ struct page *page;
+ int i;
Â
- mapping =ÂÂpage_mapping(page);
+ for (i = 0; i < nr; ++i) {
+ page = pages[i];
+ mapping =ÂÂpage_mapping(page);
Â
- /*
- Â* The page is mapped into the page tables of one or more
- Â* processes. Try to unmap it here.
- Â*/
- if (page_mapped(page) && mapping) {
- switch (*swap_ret = try_to_unmap(page, lazyfree ?
- (ttu_flags | TTU_BATCH_FLUSH | TTU_LZFREE) :
- (ttu_flags | TTU_BATCH_FLUSH))) {
- case SWAP_FAIL:
- return PG_ACTIVATE_LOCKED;
- case SWAP_AGAIN:
- return PG_KEEP_LOCKED;
- case SWAP_MLOCK:
- return PG_MLOCKED;
- case SWAP_LZFREE:
- goto lazyfree;
- case SWAP_SUCCESS:
- ; /* try to free the page below */
+ /* check outcome of cache addition */
+ if (!ret[i]) {
+ ret[i] = PG_ACTIVATE_LOCKED;
+ continue;
 }
- }
-
- if (PageDirty(page)) {
 /*
- Â* Only kswapd can writeback filesystem pages to
- Â* avoid risk of stack overflow but only writeback
- Â* if many dirty pages have been encountered.
+ Â* The page is mapped into the page tables of one or more
+ Â* processes. Try to unmap it here.
 Â*/
- if (page_is_file_cache(page) &&
- (!current_is_kswapd() ||
- Â!test_bit(ZONE_DIRTY, &zone->flags))) {
+ if (page_mapped(page) && mapping) {
+ switch (swap_ret[i] = try_to_unmap(page, lazyfree ?
+ (ttu_flags | TTU_BATCH_FLUSH | TTU_LZFREE) :
+ (ttu_flags | TTU_BATCH_FLUSH))) {
+ case SWAP_FAIL:
+ ret[i] = PG_ACTIVATE_LOCKED;
+ continue;
+ case SWAP_AGAIN:
+ ret[i] = PG_KEEP_LOCKED;
+ continue;
+ case SWAP_MLOCK:
+ ret[i] = PG_MLOCKED;
+ continue;
+ case SWAP_LZFREE:
+ goto lazyfree;
+ case SWAP_SUCCESS:
+ ; /* try to free the page below */
+ }
+ }
+
+ if (PageDirty(page)) {
 /*
- Â* Immediately reclaim when written back.
- Â* Similar in principal to deactivate_page()
- Â* except we already have the page isolated
- Â* and know it's dirty
+ Â* Only kswapd can writeback filesystem pages to
+ Â* avoid risk of stack overflow but only writeback
+ Â* if many dirty pages have been encountered.
 Â*/
- inc_zone_page_state(page, NR_VMSCAN_IMMEDIATE);
- SetPageReclaim(page);
-
- return PG_KEEP_LOCKED;
- }
+ if (page_is_file_cache(page) &&
+ (!current_is_kswapd() ||
+ Â!test_bit(ZONE_DIRTY, &zone->flags))) {
+ /*
+ Â* Immediately reclaim when written back.
+ Â* Similar in principal to deactivate_page()
+ Â* except we already have the page isolated
+ Â* and know it's dirty
+ Â*/
+ inc_zone_page_state(page, NR_VMSCAN_IMMEDIATE);
+ SetPageReclaim(page);
Â
- if (references == PAGEREF_RECLAIM_CLEAN)
- return PG_KEEP_LOCKED;
- if (!may_enter_fs)
- return PG_KEEP_LOCKED;
- if (!sc->may_writepage)
- return PG_KEEP_LOCKED;
+ ret[i] = PG_KEEP_LOCKED;
+ continue;
+ }
Â
- /*
- Â* Page is dirty. Flush the TLB if a writable entry
- Â* potentially exists to avoid CPU writes after IO
- Â* starts and then write it out here.
- Â*/
- try_to_unmap_flush_dirty();
- switch (pageout(page, mapping, sc)) {
- case PAGE_KEEP:
- return PG_KEEP_LOCKED;
- case PAGE_ACTIVATE:
- return PG_ACTIVATE_LOCKED;
- case PAGE_SUCCESS:
- if (PageWriteback(page))
- return PG_KEEP;
- if (PageDirty(page))
- return PG_KEEP;
+ if (references == PAGEREF_RECLAIM_CLEAN) {
+ ret[i] = PG_KEEP_LOCKED;
+ continue;
+ }
+ if (!may_enter_fs) {
+ ret[i] = PG_KEEP_LOCKED;
+ continue;
+ }
+ if (!sc->may_writepage) {
+ ret[i] = PG_KEEP_LOCKED;
+ continue;
+ }
Â
 /*
- Â* A synchronous write - probably a ramdisk.ÂÂGo
- Â* ahead and try to reclaim the page.
+ Â* Page is dirty. Flush the TLB if a writable entry
+ Â* potentially exists to avoid CPU writes after IO
+ Â* starts and then write it out here.
 Â*/
- if (!trylock_page(page))
- return PG_KEEP;
- if (PageDirty(page) || PageWriteback(page))
- return PG_KEEP_LOCKED;
- mapping = page_mapping(page);
- case PAGE_CLEAN:
- ; /* try to free the page below */
- }
- }
+ try_to_unmap_flush_dirty();
+ switch (pageout(page, mapping, sc)) {
+ case PAGE_KEEP:
+ ret[i] = PG_KEEP_LOCKED;
+ continue;
+ case PAGE_ACTIVATE:
+ ret[i] = PG_ACTIVATE_LOCKED;
+ continue;
+ case PAGE_SUCCESS:
+ if (PageWriteback(page)) {
+ ret[i] = PG_KEEP;
+ continue;
+ }
+ if (PageDirty(page)) {
+ ret[i] = PG_KEEP;
+ continue;
+ }
Â
- /*
- Â* If the page has buffers, try to free the buffer mappings
- Â* associated with this page. If we succeed we try to free
- Â* the page as well.
- Â*
- Â* We do this even if the page is PageDirty().
- Â* try_to_release_page() does not perform I/O, but it is
- Â* possible for a page to have PageDirty set, but it is actually
- Â* clean (all its buffers are clean).ÂÂThis happens if the
- Â* buffers were written out directly, with submit_bh(). ext3
- Â* will do this, as well as the blockdev mapping.
- Â* try_to_release_page() will discover that cleanness and will
- Â* drop the buffers and mark the page clean - it can be freed.
- Â*
- Â* Rarely, pages can have buffers and no ->mapping.ÂÂThese are
- Â* the pages which were not successfully invalidated in
- Â* truncate_complete_page().ÂÂWe try to drop those buffers here
- Â* and if that worked, and the page is no longer mapped into
- Â* process address space (page_count == 1) it can be freed.
- Â* Otherwise, leave the page on the LRU so it is swappable.
- Â*/
- if (page_has_private(page)) {
- if (!try_to_release_page(page, sc->gfp_mask))
- return PG_ACTIVATE_LOCKED;
- if (!mapping && page_count(page) == 1) {
- unlock_page(page);
- if (put_page_testzero(page))
- return PG_FREE;
- else {
 /*
- Â* rare race with speculative reference.
- Â* the speculative reference will free
- Â* this page shortly, so we may
- Â* increment nr_reclaimed (and
- Â* leave it off the LRU).
+ Â* A synchronous write - probably a ramdisk.ÂÂGo
+ Â* ahead and try to reclaim the page.
 Â*/
- return PG_SPECULATIVE_REF;
+ if (!trylock_page(page)) {
+ ret[i] = PG_KEEP;
+ continue;
+ }
+ if (PageDirty(page) || PageWriteback(page)) {
+ ret[i] = PG_KEEP_LOCKED;
+ continue;
+ }
+ mapping = page_mapping(page);
+ case PAGE_CLEAN:
+ ; /* try to free the page below */
 }
 }
- }
Â
+ /*
+ Â* If the page has buffers, try to free the buffer mappings
+ Â* associated with this page. If we succeed we try to free
+ Â* the page as well.
+ Â*
+ Â* We do this even if the page is PageDirty().
+ Â* try_to_release_page() does not perform I/O, but it is
+ Â* possible for a page to have PageDirty set, but it is actually
+ Â* clean (all its buffers are clean).ÂÂThis happens if the
+ Â* buffers were written out directly, with submit_bh(). ext3
+ Â* will do this, as well as the blockdev mapping.
+ Â* try_to_release_page() will discover that cleanness and will
+ Â* drop the buffers and mark the page clean - it can be freed.
+ Â*
+ Â* Rarely, pages can have buffers and no ->mapping.ÂÂThese are
+ Â* the pages which were not successfully invalidated in
+ Â* truncate_complete_page().ÂÂWe try to drop those buffers here
+ Â* and if that worked, and the page is no longer mapped into
+ Â* process address space (page_count == 1) it can be freed.
+ Â* Otherwise, leave the page on the LRU so it is swappable.
+ Â*/
+ if (page_has_private(page)) {
+ if (!try_to_release_page(page, sc->gfp_mask)) {
+ ret[i] = PG_ACTIVATE_LOCKED;
+ continue;
+ }
+ if (!mapping && page_count(page) == 1) {
+ unlock_page(page);
+ if (put_page_testzero(page)) {
+ ret[i] = PG_FREE;
+ continue;
+ } else {
+ /*
+ Â* rare race with speculative reference.
+ Â* the speculative reference will free
+ Â* this page shortly, so we may
+ Â* increment nr_reclaimed (and
+ Â* leave it off the LRU).
+ Â*/
+ ret[i] = PG_SPECULATIVE_REF;
+ continue;
+ }
+ }
+ }
Âlazyfree:
- if (!mapping || !__remove_mapping(mapping, page, true))
- return PG_KEEP_LOCKED;
+ if (!mapping || !__remove_mapping(mapping, page, true)) {
+ ret[i] = PG_KEEP_LOCKED;
+ continue;
+ }
+
+ /*
+ Â* At this point, we have no other references and there is
+ Â* no way to pick any more up (removed from LRU, removed
+ Â* from pagecache). Can use non-atomic bitops now (and
+ Â* we obviously don't have to worry about waking up a process
+ Â* waiting on the page lock, because there are no references.
+ Â*/
+ __ClearPageLocked(page);
+ ret[i] = PG_FREE;
+ }
+}
+
+static enum pg_result handle_pgout(struct list_head *page_list,
+ struct zone *zone,
+ struct scan_control *sc,
+ enum ttu_flags ttu_flags,
+ enum page_references references,
+ bool may_enter_fs,
+ bool lazyfree,
+ intÂÂ*swap_ret,
+ struct page *page)
+{
+ struct page *pages[1];
+ int ret[1];
+ int sret[1];
+
+ pages[0] = page;
Â
 /*
- Â* At this point, we have no other references and there is
- Â* no way to pick any more up (removed from LRU, removed
- Â* from pagecache). Can use non-atomic bitops now (and
- Â* we obviously don't have to worry about waking up a process
- Â* waiting on the page lock, because there are no references.
+ Â* page is in swap cache or page cache, indicate that
+ Â* by setting ret[0] to 1
 Â*/
- __ClearPageLocked(page);
- return PG_FREE;
+ ret[0] = 1;
+ handle_pgout_batch(page_list, zone, sc, ttu_flags, references,
+ may_enter_fs, lazyfree, pages, sret, ret, 1);
+ *swap_ret = sret[0];
+ return ret[0];
Â}
Â
Âstatic void pg_finish(struct page *page,
@@ -1095,14 +1159,13 @@ static unsigned long shrink_anon_page_list(struct list_head *page_list,
 bool clean)
Â{
 unsigned long nr_reclaimed = 0;
- enum pg_result pg_dispose;
 swp_entry_t swp_entries[SWAP_BATCH];
 struct page *pages[SWAP_BATCH];
 int m, i, k, ret[SWAP_BATCH];
 struct page *page;
Â
 while (n > 0) {
- int swap_ret = SWAP_SUCCESS;
+ int swap_ret[SWAP_BATCH];
Â
 m = get_swap_pages(n, swp_entries);
 if (!m)
@@ -1127,28 +1190,19 @@ static unsigned long shrink_anon_page_list(struct list_head *page_list,
 */
 add_to_swap_batch(pages, page_list, swp_entries, ret, m);
Â
- for (i = 0; i < m; ++i) {
- page = pages[i];
-
- if (!ret[i]) {
- pg_finish(page, PG_ACTIVATE_LOCKED, swap_ret,
- &nr_reclaimed, pgactivate,
- ret_pages, free_pages);
- continue;
- }
-
- if (clean)
- pg_dispose = handle_pgout(page_list, zone, sc,
- ttu_flags, PAGEREF_RECLAIM_CLEAN,
- true, true, &swap_ret, page);
- else
- pg_dispose = handle_pgout(page_list, zone, sc,
- ttu_flags, PAGEREF_RECLAIM,
- true, true, &swap_ret, page);
-
- pg_finish(page, pg_dispose, swap_ret, &nr_reclaimed,
- pgactivate, ret_pages, free_pages);
- }
+ if (clean)
+ handle_pgout_batch(page_list, zone, sc, ttu_flags,
+ PAGEREF_RECLAIM_CLEAN, true, true,
+ pages, swap_ret, ret, m);
+ else
+ handle_pgout_batch(page_list, zone, sc, ttu_flags,
+ PAGEREF_RECLAIM, true, true,
+ pages, swap_ret, ret, m);
+
+ for (i = 0; i < m; ++i)
+ pg_finish(pages[i], ret[i], swap_ret[i],
+ &nr_reclaimed, pgactivate,
+ ret_pages, free_pages);
 }
 return nr_reclaimed;
Â
--Â
2.5.5