aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--include/linux/ksm.h18
-rw-r--r--mm/ksm.c83
-rw-r--r--mm/memory.c19
3 files changed, 92 insertions, 28 deletions
diff --git a/include/linux/ksm.h b/include/linux/ksm.h
index 3319a6967626..45c9b6a17bcb 100644
--- a/include/linux/ksm.h
+++ b/include/linux/ksm.h
@@ -16,9 +16,6 @@
16struct stable_node; 16struct stable_node;
17struct mem_cgroup; 17struct mem_cgroup;
18 18
19struct page *ksm_does_need_to_copy(struct page *page,
20 struct vm_area_struct *vma, unsigned long address);
21
22#ifdef CONFIG_KSM 19#ifdef CONFIG_KSM
23int ksm_madvise(struct vm_area_struct *vma, unsigned long start, 20int ksm_madvise(struct vm_area_struct *vma, unsigned long start,
24 unsigned long end, int advice, unsigned long *vm_flags); 21 unsigned long end, int advice, unsigned long *vm_flags);
@@ -73,15 +70,8 @@ static inline void set_page_stable_node(struct page *page,
73 * We'd like to make this conditional on vma->vm_flags & VM_MERGEABLE, 70 * We'd like to make this conditional on vma->vm_flags & VM_MERGEABLE,
74 * but what if the vma was unmerged while the page was swapped out? 71 * but what if the vma was unmerged while the page was swapped out?
75 */ 72 */
76static inline int ksm_might_need_to_copy(struct page *page, 73struct page *ksm_might_need_to_copy(struct page *page,
77 struct vm_area_struct *vma, unsigned long address) 74 struct vm_area_struct *vma, unsigned long address);
78{
79 struct anon_vma *anon_vma = page_anon_vma(page);
80
81 return anon_vma &&
82 (anon_vma->root != vma->anon_vma->root ||
83 page->index != linear_page_index(vma, address));
84}
85 75
86int page_referenced_ksm(struct page *page, 76int page_referenced_ksm(struct page *page,
87 struct mem_cgroup *memcg, unsigned long *vm_flags); 77 struct mem_cgroup *memcg, unsigned long *vm_flags);
@@ -113,10 +103,10 @@ static inline int ksm_madvise(struct vm_area_struct *vma, unsigned long start,
113 return 0; 103 return 0;
114} 104}
115 105
116static inline int ksm_might_need_to_copy(struct page *page, 106static inline struct page *ksm_might_need_to_copy(struct page *page,
117 struct vm_area_struct *vma, unsigned long address) 107 struct vm_area_struct *vma, unsigned long address)
118{ 108{
119 return 0; 109 return page;
120} 110}
121 111
122static inline int page_referenced_ksm(struct page *page, 112static inline int page_referenced_ksm(struct page *page,
diff --git a/mm/ksm.c b/mm/ksm.c
index e02430fb26f6..4c22cdff02ad 100644
--- a/mm/ksm.c
+++ b/mm/ksm.c
@@ -644,6 +644,57 @@ static int unmerge_ksm_pages(struct vm_area_struct *vma,
644/* 644/*
645 * Only called through the sysfs control interface: 645 * Only called through the sysfs control interface:
646 */ 646 */
647static int remove_stable_node(struct stable_node *stable_node)
648{
649 struct page *page;
650 int err;
651
652 page = get_ksm_page(stable_node, true);
653 if (!page) {
654 /*
655 * get_ksm_page did remove_node_from_stable_tree itself.
656 */
657 return 0;
658 }
659
660 if (WARN_ON_ONCE(page_mapped(page)))
661 err = -EBUSY;
662 else {
663 /*
664 * This page might be in a pagevec waiting to be freed,
665 * or it might be PageSwapCache (perhaps under writeback),
666 * or it might have been removed from swapcache a moment ago.
667 */
668 set_page_stable_node(page, NULL);
669 remove_node_from_stable_tree(stable_node);
670 err = 0;
671 }
672
673 unlock_page(page);
674 put_page(page);
675 return err;
676}
677
678static int remove_all_stable_nodes(void)
679{
680 struct stable_node *stable_node;
681 int nid;
682 int err = 0;
683
684 for (nid = 0; nid < nr_node_ids; nid++) {
685 while (root_stable_tree[nid].rb_node) {
686 stable_node = rb_entry(root_stable_tree[nid].rb_node,
687 struct stable_node, node);
688 if (remove_stable_node(stable_node)) {
689 err = -EBUSY;
690 break; /* proceed to next nid */
691 }
692 cond_resched();
693 }
694 }
695 return err;
696}
697
647static int unmerge_and_remove_all_rmap_items(void) 698static int unmerge_and_remove_all_rmap_items(void)
648{ 699{
649 struct mm_slot *mm_slot; 700 struct mm_slot *mm_slot;
@@ -691,6 +742,8 @@ static int unmerge_and_remove_all_rmap_items(void)
691 } 742 }
692 } 743 }
693 744
745 /* Clean up stable nodes, but don't worry if some are still busy */
746 remove_all_stable_nodes();
694 ksm_scan.seqnr = 0; 747 ksm_scan.seqnr = 0;
695 return 0; 748 return 0;
696 749
@@ -1586,11 +1639,19 @@ int __ksm_enter(struct mm_struct *mm)
1586 spin_lock(&ksm_mmlist_lock); 1639 spin_lock(&ksm_mmlist_lock);
1587 insert_to_mm_slots_hash(mm, mm_slot); 1640 insert_to_mm_slots_hash(mm, mm_slot);
1588 /* 1641 /*
1589 * Insert just behind the scanning cursor, to let the area settle 1642 * When KSM_RUN_MERGE (or KSM_RUN_STOP),
1643 * insert just behind the scanning cursor, to let the area settle
1590 * down a little; when fork is followed by immediate exec, we don't 1644 * down a little; when fork is followed by immediate exec, we don't
1591 * want ksmd to waste time setting up and tearing down an rmap_list. 1645 * want ksmd to waste time setting up and tearing down an rmap_list.
1646 *
1647 * But when KSM_RUN_UNMERGE, it's important to insert ahead of its
1648 * scanning cursor, otherwise KSM pages in newly forked mms will be
1649 * missed: then we might as well insert at the end of the list.
1592 */ 1650 */
1593 list_add_tail(&mm_slot->mm_list, &ksm_scan.mm_slot->mm_list); 1651 if (ksm_run & KSM_RUN_UNMERGE)
1652 list_add_tail(&mm_slot->mm_list, &ksm_mm_head.mm_list);
1653 else
1654 list_add_tail(&mm_slot->mm_list, &ksm_scan.mm_slot->mm_list);
1594 spin_unlock(&ksm_mmlist_lock); 1655 spin_unlock(&ksm_mmlist_lock);
1595 1656
1596 set_bit(MMF_VM_MERGEABLE, &mm->flags); 1657 set_bit(MMF_VM_MERGEABLE, &mm->flags);
@@ -1640,11 +1701,25 @@ void __ksm_exit(struct mm_struct *mm)
1640 } 1701 }
1641} 1702}
1642 1703
1643struct page *ksm_does_need_to_copy(struct page *page, 1704struct page *ksm_might_need_to_copy(struct page *page,
1644 struct vm_area_struct *vma, unsigned long address) 1705 struct vm_area_struct *vma, unsigned long address)
1645{ 1706{
1707 struct anon_vma *anon_vma = page_anon_vma(page);
1646 struct page *new_page; 1708 struct page *new_page;
1647 1709
1710 if (PageKsm(page)) {
1711 if (page_stable_node(page) &&
1712 !(ksm_run & KSM_RUN_UNMERGE))
1713 return page; /* no need to copy it */
1714 } else if (!anon_vma) {
1715 return page; /* no need to copy it */
1716 } else if (anon_vma->root == vma->anon_vma->root &&
1717 page->index == linear_page_index(vma, address)) {
1718 return page; /* still no need to copy it */
1719 }
1720 if (!PageUptodate(page))
1721 return page; /* let do_swap_page report the error */
1722
1648 new_page = alloc_page_vma(GFP_HIGHUSER_MOVABLE, vma, address); 1723 new_page = alloc_page_vma(GFP_HIGHUSER_MOVABLE, vma, address);
1649 if (new_page) { 1724 if (new_page) {
1650 copy_user_highpage(new_page, page, address, vma); 1725 copy_user_highpage(new_page, page, address, vma);
@@ -2024,7 +2099,7 @@ static ssize_t merge_across_nodes_store(struct kobject *kobj,
2024 2099
2025 mutex_lock(&ksm_thread_mutex); 2100 mutex_lock(&ksm_thread_mutex);
2026 if (ksm_merge_across_nodes != knob) { 2101 if (ksm_merge_across_nodes != knob) {
2027 if (ksm_pages_shared) 2102 if (ksm_pages_shared || remove_all_stable_nodes())
2028 err = -EBUSY; 2103 err = -EBUSY;
2029 else 2104 else
2030 ksm_merge_across_nodes = knob; 2105 ksm_merge_across_nodes = knob;
diff --git a/mm/memory.c b/mm/memory.c
index 054250ee4a68..7bd22a621817 100644
--- a/mm/memory.c
+++ b/mm/memory.c
@@ -2994,17 +2994,16 @@ static int do_swap_page(struct mm_struct *mm, struct vm_area_struct *vma,
2994 if (unlikely(!PageSwapCache(page) || page_private(page) != entry.val)) 2994 if (unlikely(!PageSwapCache(page) || page_private(page) != entry.val))
2995 goto out_page; 2995 goto out_page;
2996 2996
2997 if (ksm_might_need_to_copy(page, vma, address)) { 2997 swapcache = page;
2998 swapcache = page; 2998 page = ksm_might_need_to_copy(page, vma, address);
2999 page = ksm_does_need_to_copy(page, vma, address); 2999 if (unlikely(!page)) {
3000 3000 ret = VM_FAULT_OOM;
3001 if (unlikely(!page)) { 3001 page = swapcache;
3002 ret = VM_FAULT_OOM; 3002 swapcache = NULL;
3003 page = swapcache; 3003 goto out_page;
3004 swapcache = NULL;
3005 goto out_page;
3006 }
3007 } 3004 }
3005 if (page == swapcache)
3006 swapcache = NULL;
3008 3007
3009 if (mem_cgroup_try_charge_swapin(mm, page, GFP_KERNEL, &ptr)) { 3008 if (mem_cgroup_try_charge_swapin(mm, page, GFP_KERNEL, &ptr)) {
3010 ret = VM_FAULT_OOM; 3009 ret = VM_FAULT_OOM;