115 lines
2.9 KiB
C
115 lines
2.9 KiB
C
#ifndef _LINUX_MM_PTE_H
|
|
#define _LINUX_MM_PTE_H
|
|
|
|
#include <linux/swapops.h>
|
|
#include <linux/rmap.h>
|
|
|
|
static inline unsigned long
|
|
copy_one_pte(struct mm_struct *dst_mm, struct mm_struct *src_mm,
|
|
pte_t *dst_pte, pte_t *src_pte, struct vm_area_struct *vma,
|
|
unsigned long addr, int *rss)
|
|
{
|
|
unsigned long vm_flags = vma->vm_flags;
|
|
pte_t pte = *src_pte;
|
|
struct page *page;
|
|
|
|
/* pte contains position in swap or file, so copy. */
|
|
if (unlikely(!pte_present(pte))) {
|
|
swp_entry_t entry = pte_to_swp_entry(pte);
|
|
|
|
if (likely(!non_swap_entry(entry))) {
|
|
if (swap_duplicate(entry) < 0)
|
|
return entry.val;
|
|
|
|
/* make sure dst_mm is on swapoff's mmlist. */
|
|
if (unlikely(list_empty(&dst_mm->mmlist))) {
|
|
spin_lock(&mmlist_lock);
|
|
if (list_empty(&dst_mm->mmlist))
|
|
list_add(&dst_mm->mmlist,
|
|
&src_mm->mmlist);
|
|
spin_unlock(&mmlist_lock);
|
|
}
|
|
rss[MM_SWAPENTS]++;
|
|
} else if (is_migration_entry(entry)) {
|
|
page = migration_entry_to_page(entry);
|
|
|
|
rss[mm_counter(page)]++;
|
|
|
|
if (is_write_migration_entry(entry) &&
|
|
is_cow_mapping(vm_flags)) {
|
|
/*
|
|
* COW mappings require pages in both
|
|
* parent and child to be set to read.
|
|
*/
|
|
make_migration_entry_read(&entry);
|
|
pte = swp_entry_to_pte(entry);
|
|
if (pte_swp_soft_dirty(*src_pte))
|
|
pte = pte_swp_mksoft_dirty(pte);
|
|
set_pte_at(src_mm, addr, src_pte, pte);
|
|
}
|
|
} else if (is_device_private_entry(entry)) {
|
|
page = device_private_entry_to_page(entry);
|
|
|
|
/*
|
|
* Update rss count even for unaddressable pages, as
|
|
* they should treated just like normal pages in this
|
|
* respect.
|
|
*
|
|
* We will likely want to have some new rss counters
|
|
* for unaddressable pages, at some point. But for now
|
|
* keep things as they are.
|
|
*/
|
|
get_page(page);
|
|
rss[mm_counter(page)]++;
|
|
page_dup_rmap(page, false);
|
|
|
|
/*
|
|
* We do not preserve soft-dirty information, because so
|
|
* far, checkpoint/restore is the only feature that
|
|
* requires that. And checkpoint/restore does not work
|
|
* when a device driver is involved (you cannot easily
|
|
* save and restore device driver state).
|
|
*/
|
|
if (is_write_device_private_entry(entry) &&
|
|
is_cow_mapping(vm_flags)) {
|
|
make_device_private_entry_read(&entry);
|
|
pte = swp_entry_to_pte(entry);
|
|
set_pte_at(src_mm, addr, src_pte, pte);
|
|
}
|
|
}
|
|
goto out_set_pte;
|
|
}
|
|
|
|
/*
|
|
* If it's a COW mapping, write protect it both
|
|
* in the parent and the child
|
|
*/
|
|
if (is_cow_mapping(vm_flags) && pte_write(pte)) {
|
|
ptep_set_wrprotect(src_mm, addr, src_pte);
|
|
pte = pte_wrprotect(pte);
|
|
}
|
|
|
|
/*
|
|
* If it's a shared mapping, mark it clean in
|
|
* the child
|
|
*/
|
|
if (vm_flags & VM_SHARED)
|
|
pte = pte_mkclean(pte);
|
|
pte = pte_mkold(pte);
|
|
|
|
page = vm_normal_page(vma, addr, pte);
|
|
if (page) {
|
|
get_page(page);
|
|
page_dup_rmap(page, false);
|
|
rss[mm_counter(page)]++;
|
|
} else if (pte_devmap(pte)) {
|
|
page = pte_page(pte);
|
|
}
|
|
|
|
out_set_pte:
|
|
set_pte_at(dst_mm, addr, dst_pte, pte);
|
|
return 0;
|
|
}
|
|
|
|
#endif
|