This patch makes tswap to do not allocate a new page, if swapped page is zero-filled, and to use ZERO_PAGE() pointer to decode it instead.
The same optimization is made in zram, and it may help VMs to reduce memory usage in some way. Signed-off-by: Kirill Tkhai <ktk...@virtuozzo.com> --- mm/tswap.c | 65 +++++++++++++++++++++++++++++++++++++++++++++++------------- 1 file changed, 51 insertions(+), 14 deletions(-) diff --git a/mm/tswap.c b/mm/tswap.c index 15f5adc2dc9..6a3cb917059 100644 --- a/mm/tswap.c +++ b/mm/tswap.c @@ -54,16 +54,20 @@ static void tswap_lru_add(struct page *page) { struct tswap_lru *lru = &tswap_lru_node[page_to_nid(page)]; - list_add_tail(&page->lru, &lru->list); - lru->nr_items++; + if (page != ZERO_PAGE(0)) { + list_add_tail(&page->lru, &lru->list); + lru->nr_items++; + } } static void tswap_lru_del(struct page *page) { struct tswap_lru *lru = &tswap_lru_node[page_to_nid(page)]; - list_del(&page->lru); - lru->nr_items--; + if (page != ZERO_PAGE(0)) { + list_del(&page->lru); + lru->nr_items--; + } } static struct page *tswap_lookup_page(swp_entry_t entry) @@ -73,7 +77,7 @@ static struct page *tswap_lookup_page(swp_entry_t entry) spin_lock(&tswap_lock); page = radix_tree_lookup(&tswap_page_tree, entry.val); spin_unlock(&tswap_lock); - BUG_ON(page && page_private(page) != entry.val); + BUG_ON(page && page != ZERO_PAGE(0) && page_private(page) != entry.val); return page; } @@ -85,7 +89,8 @@ static int tswap_insert_page(swp_entry_t entry, struct page *page) if (err) return err; - set_page_private(page, entry.val); + if (page != ZERO_PAGE(0)) + set_page_private(page, entry.val); spin_lock(&tswap_lock); err = radix_tree_insert(&tswap_page_tree, entry.val, page); if (!err) { @@ -111,7 +116,7 @@ static struct page *tswap_delete_page(swp_entry_t entry, struct page *expected) spin_unlock(&tswap_lock); if (page) { BUG_ON(expected && page != expected); - BUG_ON(page_private(page) != entry.val); + BUG_ON(page_private(page) != entry.val && page != ZERO_PAGE(0)); } return page; } @@ -274,26 +279,57 @@ static void tswap_frontswap_init(unsigned type) */ } +static bool is_zero_filled_page(struct page *page) +{ + bool zero_filled = true; + unsigned long *v; + int i; + + v = kmap_atomic(page); + for (i = 0; i < PAGE_SIZE / sizeof(*v); i++) { + if (v[i] != 0) { + zero_filled = false; + break; + } + } + kunmap_atomic(v); + return zero_filled; +} + static int tswap_frontswap_store(unsigned type, pgoff_t offset, struct page *page) { swp_entry_t entry = swp_entry(type, offset); + int zero_filled = -1, err = 0; struct page *cache_page; - int err = 0; if (!tswap_active) return -1; cache_page = tswap_lookup_page(entry); - if (cache_page) - goto copy; + if (cache_page) { + zero_filled = is_zero_filled_page(page); + /* If type of page has not changed, just reuse it */ + if (zero_filled == (cache_page == ZERO_PAGE(0))) + goto copy; + tswap_delete_page(entry, NULL); + put_page(cache_page); + } if (!(current->flags & PF_MEMCG_RECLAIM)) return -1; - cache_page = alloc_page(TSWAP_GFP_MASK | __GFP_HIGHMEM); - if (!cache_page) - return -1; + if (zero_filled == -1) + zero_filled = is_zero_filled_page(page); + + if (!zero_filled) { + cache_page = alloc_page(TSWAP_GFP_MASK | __GFP_HIGHMEM); + if (!cache_page) + return -1; + } else { + cache_page = ZERO_PAGE(0); + get_page(cache_page); + } err = tswap_insert_page(entry, cache_page); if (err) { @@ -306,7 +342,8 @@ static int tswap_frontswap_store(unsigned type, pgoff_t offset, return -1; } copy: - copy_highpage(cache_page, page); + if (cache_page != ZERO_PAGE(0)) + copy_highpage(cache_page, page); return 0; } _______________________________________________ Devel mailing list Devel@openvz.org https://lists.openvz.org/mailman/listinfo/devel