|
@@ -2623,7 +2623,7 @@ static int do_swap_page(struct mm_struct *mm, struct vm_area_struct *vma,
|
|
|
unsigned int flags, pte_t orig_pte)
|
|
|
{
|
|
|
spinlock_t *ptl;
|
|
|
- struct page *page;
|
|
|
+ struct page *page, *swapcache = NULL;
|
|
|
swp_entry_t entry;
|
|
|
pte_t pte;
|
|
|
struct mem_cgroup *ptr = NULL;
|
|
@@ -2679,10 +2679,23 @@ static int do_swap_page(struct mm_struct *mm, struct vm_area_struct *vma,
|
|
|
lock_page(page);
|
|
|
delayacct_clear_flag(DELAYACCT_PF_SWAPIN);
|
|
|
|
|
|
- page = ksm_might_need_to_copy(page, vma, address);
|
|
|
- if (!page) {
|
|
|
- ret = VM_FAULT_OOM;
|
|
|
- goto out;
|
|
|
+ /*
|
|
|
+ * Make sure try_to_free_swap didn't release the swapcache
|
|
|
+ * from under us. The page pin isn't enough to prevent that.
|
|
|
+ */
|
|
|
+ if (unlikely(!PageSwapCache(page)))
|
|
|
+ goto out_page;
|
|
|
+
|
|
|
+ if (ksm_might_need_to_copy(page, vma, address)) {
|
|
|
+ swapcache = page;
|
|
|
+ page = ksm_does_need_to_copy(page, vma, address);
|
|
|
+
|
|
|
+ if (unlikely(!page)) {
|
|
|
+ ret = VM_FAULT_OOM;
|
|
|
+ page = swapcache;
|
|
|
+ swapcache = NULL;
|
|
|
+ goto out_page;
|
|
|
+ }
|
|
|
}
|
|
|
|
|
|
if (mem_cgroup_try_charge_swapin(mm, page, GFP_KERNEL, &ptr)) {
|
|
@@ -2735,6 +2748,18 @@ static int do_swap_page(struct mm_struct *mm, struct vm_area_struct *vma,
|
|
|
if (vm_swap_full() || (vma->vm_flags & VM_LOCKED) || PageMlocked(page))
|
|
|
try_to_free_swap(page);
|
|
|
unlock_page(page);
|
|
|
+ if (swapcache) {
|
|
|
+ /*
|
|
|
+ * Hold the lock to avoid the swap entry to be reused
|
|
|
+ * until we take the PT lock for the pte_same() check
|
|
|
+ * (to avoid false positives from pte_same). For
|
|
|
+ * further safety release the lock after the swap_free
|
|
|
+ * so that the swap count won't change under a
|
|
|
+ * parallel locked swapcache.
|
|
|
+ */
|
|
|
+ unlock_page(swapcache);
|
|
|
+ page_cache_release(swapcache);
|
|
|
+ }
|
|
|
|
|
|
if (flags & FAULT_FLAG_WRITE) {
|
|
|
ret |= do_wp_page(mm, vma, address, page_table, pmd, ptl, pte);
|
|
@@ -2756,6 +2781,10 @@ out_page:
|
|
|
unlock_page(page);
|
|
|
out_release:
|
|
|
page_cache_release(page);
|
|
|
+ if (swapcache) {
|
|
|
+ unlock_page(swapcache);
|
|
|
+ page_cache_release(swapcache);
|
|
|
+ }
|
|
|
return ret;
|
|
|
}
|
|
|
|