diff options
-rw-r--r-- | include/linux/ksm.h | 18 | ||||
-rw-r--r-- | mm/ksm.c | 83 | ||||
-rw-r--r-- | mm/memory.c | 19 |
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 @@ | |||
16 | struct stable_node; | 16 | struct stable_node; |
17 | struct mem_cgroup; | 17 | struct mem_cgroup; |
18 | 18 | ||
19 | struct 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 |
23 | int ksm_madvise(struct vm_area_struct *vma, unsigned long start, | 20 | int 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 | */ |
76 | static inline int ksm_might_need_to_copy(struct page *page, | 73 | struct 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 | ||
86 | int page_referenced_ksm(struct page *page, | 76 | int 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 | ||
116 | static inline int ksm_might_need_to_copy(struct page *page, | 106 | static 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 | ||
122 | static inline int page_referenced_ksm(struct page *page, | 112 | static inline int page_referenced_ksm(struct page *page, |
@@ -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 | */ |
647 | static 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 | |||
678 | static 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 | |||
647 | static int unmerge_and_remove_all_rmap_items(void) | 698 | static 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 | ||
1643 | struct page *ksm_does_need_to_copy(struct page *page, | 1704 | struct 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; |