From d8cc8d7c2e6ba00cb91bb988281a62c330e49cf5 Mon Sep 17 00:00:00 2001 From: Andrea Arcangeli <aarcange@redhat.com> Date: Thu, 25 Mar 2010 17:04:18 -0300 Subject: [PATCH] avoid leaving orphaned swapcache in ksm RH-Author: Andrea Arcangeli <aarcange@redhat.com> Message-id: <20100325170418.GU10659@random.random> Patchwork-id: 8132 O-Subject: [PATCH KVM RHEL5 KSM] avoid leaving orphaned swapcache in ksm Bugzilla: 575585 RH-Acked-by: Rik van Riel <riel@redhat.com> RH-Acked-by: Izik Eidus <ieidus@redhat.com> RH-Acked-by: Marcelo Tosatti <mtosatti@redhat.com> Subject: release swap cache from merged pages From: Andrea Arcangeli <aarcange@redhat.com> Remove the old page from swapcache if it was the last mapping. Signed-off-by: Andrea Arcangeli <aarcange@redhat.com> --- https://bugzilla.redhat.com/show_bug.cgi?id=575585 Signed-off-by: Eduardo Habkost <ehabkost@redhat.com> --- kernel/ksm/ksm.h | 2 ++ kernel/ksm/ksm_main.c | 40 ++++++++++++++++++++++++++++++++++++---- 2 files changed, 38 insertions(+), 4 deletions(-) diff --git a/kernel/ksm/ksm.h b/kernel/ksm/ksm.h index 91ca286..ad68c2f 100644 --- a/kernel/ksm/ksm.h +++ b/kernel/ksm/ksm.h @@ -81,4 +81,6 @@ struct ksm_kthread_info { #define KSM_SCAN _IOW(KSMIO, 0x40,\ struct ksm_user_scan) +static void (*free_page_and_swap_cache_p)(struct page *page); + #endif diff --git a/kernel/ksm/ksm_main.c b/kernel/ksm/ksm_main.c index 7032b4f..ddb977a 100644 --- a/kernel/ksm/ksm_main.c +++ b/kernel/ksm/ksm_main.c @@ -37,6 +37,7 @@ #include <linux/swap.h> #include <linux/rbtree.h> #include <linux/anon_inodes.h> +#include <linux/kprobes.h> #include <asm/tlbflush.h> @@ -148,6 +149,8 @@ static struct task_struct *kthread; static DECLARE_WAIT_QUEUE_HEAD(kthread_wait); static DECLARE_RWSEM(kthread_lock); +static void (*free_page_and_swap_cache_p)(struct page *page) = put_page; + static int ksm_slab_init(void) { int ret = -ENOMEM; @@ -1019,8 +1022,9 @@ static int cmp_and_merge_page(struct ksm_scan *ksm_scan, struct page *page) tmp_rmap_item->tree_item; } } - } - put_page(page2[0]); + free_page_and_swap_cache_p(page2[0]); + } else + put_page(page2[0]); return !ret; } if (!wait && !rmap_item) { @@ -1129,9 +1133,14 @@ static int ksm_scan_start(struct ksm_scan *ksm_scan, int scan_npages) 1, 0, 0, page, NULL); up_read(&slot->mm->mmap_sem); if (val == 1) { - if (!PageKsm(page[0])) + if (!PageKsm(page[0])) { cmp_and_merge_page(ksm_scan, page[0]); - put_page(page[0]); + if (!page_mapped(page[0])) + free_page_and_swap_cache_p(page[0]); + else + put_page(page[0]); + } else + put_page(page[0]); } } else { up_read(&slot->mm->mmap_sem); @@ -1373,10 +1382,33 @@ int kthread_ksm_scan_thread(void *nothing) return 0; } +static void __init init_free_page_and_swap_cache_p(void) +{ +#if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,18) && defined(CONFIG_KALLSYMS) && !defined(RHEL_RELEASE_CODE) + free_page_and_swap_cache_p = (void (*)(struct page *)) + kallsyms_lookup_name("free_page_and_swap_cache"); + BUG_ON(!free_page_and_swap_cache_p); +#else + struct kprobe tmp; + + memset(&tmp, 0, sizeof(struct kprobe)); + tmp.symbol_name = "free_page_and_swap_cache"; + + if (register_kprobe(&tmp)) + printk(KERN_WARNING "free_page_and_swap_cache as put_page"); + else { + free_page_and_swap_cache_p = (void (*)(struct page *)) tmp.addr; + unregister_kprobe(&tmp); + } +#endif +} + static int __init ksm_init(void) { int r; + init_free_page_and_swap_cache_p(); + r = ksm_slab_init(); if (r) goto out; -- 1.7.0.3