/* * Memory Migration functionality - linux/mm/migration.c * * Copyright (C) 2006 Silicon Graphics, Inc., Christoph Lameter * * Page migration was first developed in the context of the memory hotplug * project. The main authors of the migration code are: * * IWAMOTO Toshihiro <iwamoto@valinux.co.jp> * Hirokazu Takahashi <taka@valinux.co.jp> * Dave Hansen <haveblue@us.ibm.com> * Christoph Lameter */ #include <linux/migrate.h>#include <linux/export.h>#include <linux/swap.h>#include <linux/swapops.h>#include <linux/pagemap.h>#include <linux/buffer_head.h>#include <linux/mm_inline.h>#include <linux/nsproxy.h>#include <linux/pagevec.h>#include <linux/ksm.h>#include <linux/rmap.h>#include <linux/topology.h>#include <linux/cpu.h>#include <linux/cpuset.h>#include <linux/writeback.h>#include <linux/mempolicy.h>#include <linux/vmalloc.h>#include <linux/security.h>#include <linux/memcontrol.h>#include <linux/syscalls.h>#include <linux/hugetlb.h>#include <linux/hugetlb_cgroup.h>#include <linux/gfp.h>#include <linux/balloon_compaction.h>#include <linux/mmu_notifier.h> #include <asm/tlbflush.h> #define CREATE_TRACE_POINTS#include <trace/events/migrate.h> #include "internal.h" /* * migrate_prep() needs to be called before we start compiling a list of pages * to be migrated using isolate_lru_page(). If scheduling work on other CPUs is * undesirable, use migrate_prep_local() */int migrate_prep(void){ /* * Clear the LRU lists so pages can be isolated. * Note that pages may be moved off the LRU after we have * drained them. Those pages will fail to migrate like other * pages that may be busy. */ lru_add_drain_all(); return 0;} /* Do the necessary work of migrate_prep but not if it involves other CPUs */int migrate_prep_local(void){ lru_add_drain(); return 0;} /* * Add isolated pages on the list back to the LRU under page lock * to avoid leaking evictable pages back onto unevictable list. */void putback_lru_pages(struct list_head *l){ struct page *page; struct page *page2; list_for_each_entry_safe(page, page2, l, lru) { list_del(&page->lru); dec_zone_page_state(page, NR_ISOLATED_ANON + page_is_file_cache(page)); putback_lru_page(page); }} /* * Put previously isolated pages back onto the appropriate lists * from where they were once taken off for compaction/migration. * * This function shall be used instead of putback_lru_pages(), * whenever the isolated pageset has been built by isolate_migratepages_range() */void putback_movable_pages(struct list_head *l){ struct page *page; struct page *page2; list_for_each_entry_safe(page, page2, l, lru) { if (unlikely(PageHuge(page))) { putback_active_hugepage(page); continue; } list_del(&page->lru); dec_zone_page_state(page, NR_ISOLATED_ANON + page_is_file_cache(page)); if (unlikely(isolated_balloon_page(page))) balloon_page_putback(page); else putback_lru_page(page); }} /* * Restore a potential migration pte to a working pte entry */static int remove_migration_pte(struct page *new, struct vm_area_struct *vma, unsigned long addr, void *old){ struct mm_struct *mm = vma->vm_mm; swp_entry_t entry; pmd_t *pmd; pte_t *ptep, pte; spinlock_t *ptl; if (unlikely(PageHuge(new))) { ptep = huge_pte_offset(mm, addr); if (!ptep) goto out; ptl = huge_pte_lockptr(hstate_vma(vma), mm, ptep); } else { pmd = mm_find_pmd(mm, addr); if (!pmd) goto out; if (pmd_trans_huge(*pmd)) goto out; ptep = pte_offset_map(pmd, addr); /* * Peek to check is_swap_pte() before taking ptlock? No, we * can race mremap's move_ptes(), which skips anon_vma lock. */ ptl = pte_lockptr(mm, pmd); } spin_lock(ptl); pte = *ptep; if (!is_swap_pte(pte)) goto unlock; entry = pte_to_swp_entry(pte); if (!is_migration_entry(entry) || migration_entry_to_page(entry) != old) goto unlock; get_page(new); pte = pte_mkold(mk_pte(new, vma->vm_page_prot)); if (pte_swp_soft_dirty(*ptep)) pte = pte_mksoft_dirty(pte); if (is_write_migration_entry(entry)) pte = pte_mkwrite(pte);#ifdef CONFIG_HUGETLB_PAGE if (PageHuge(new)) { pte = pte_mkhuge(pte); pte = arch_make_huge_pte(pte, vma, new, 0); }#endif flush_dcache_page(new); set_pte_at(mm, addr, ptep, pte); if (PageHuge(new)) { if (PageAnon(new)) hugepage_add_anon_rmap(new, vma, addr); else page_dup_rmap(new); } else if (PageAnon(new)) page_add_anon_rmap(new, vma, addr); else page_add_file_rmap(new); /* No need to invalidate - it was non-present before */ update_mmu_cache(vma, addr, ptep);unlock: pte_unmap_unlock(ptep, ptl);out: return SWAP_AGAIN;} /* * Get rid of all migration entries and replace them by * references to the indicated page. */static void remove_migration_ptes(struct page *old, struct page *new){ rmap_walk(new, remove_migration_pte, old);} /* * Something used the pte of a page under migration. We need to * get to the page and wait until migration is finished. * When we return from this function the fault will be retried. */static void __migration_entry_wait(struct mm_struct *mm, pte_t *ptep, spinlock_t *ptl){ pte_t pte; swp_entry_t entry; struct page *page; spin_lock(ptl); pte = *ptep; if (!is_swap_pte(pte)) goto out; entry = pte_to_swp_entry(pte); if (!is_migration_entry(entry)) goto out; page = migration_entry_to_page(entry); /* * Once radix-tree replacement of page migration started, page_count * *must* be zero. And, we don't want to call wait_on_page_locked() * against a page without get_page(). * So, we use get_page_unless_zero(), here. Even failed, page fault * will occur again. */ if (!get_page_unless_zero(page)) goto out; pte_unmap_unlock(ptep, ptl); wait_on_page_locked(page); put_page(page); return;out: pte_unmap_unlock(ptep, ptl);} void migration_entry_wait(struct mm_struct *mm, pmd_t *pmd, unsigned long address){ spinlock_t *ptl = pte_lockptr(mm, pmd); pte_t *ptep = pte_offset_map(pmd, address); __migration_entry_wait(mm, ptep, ptl);} void migration_entry_wait_huge(struct vm_area_struct *vma, struct mm_struct *mm, pte_t *pte){ spinlock_t *ptl = huge_pte_lockptr(hstate_vma(vma), mm, pte); __migration_entry_wait(mm, pte, ptl);} #ifdef CONFIG_BLOCK/* Returns true if all buffers are successfully locked */static bool buffer_migrate_lock_buffers(struct buffer_head *head, enum migrate_mode mode){ struct buffer_head *bh = head; /* Simple case, sync compaction */ if (mode != MIGRATE_ASYNC) { do { get_bh(bh); lock_buffer(bh); bh = bh->b_this_page; } while (bh != head); return true; } /* async case, we cannot block on lock_buffer so use trylock_buffer */ do { get_bh(bh); if (!trylock_buffer(bh)) { /* * We failed to lock the buffer and cannot stall in * async migration. Release the taken locks */ struct buffer_head *failed_bh = bh; put_bh(failed_bh); bh = head; while (bh != failed_bh) { unlock_buffer(bh); put_bh(bh); bh = bh->b_this_page; } return false; } bh = bh->b_this_page; } while (bh != head); return true;}#elsestatic inline bool buffer_migrate_lock_buffers(struct buffer_head *head, enum migrate_mode mode){ return true;}#endif /* CONFIG_BLOCK */ /* * Replace the page in the mapping. * * The number of remaining references must be: * 1 for anonymous pages without a mapping * 2 for pages with a mapping * 3 for pages with a mapping and PagePrivate/PagePrivate2 set. */int migrate_page_move_mapping(struct address_space *mapping, struct page *newpage, struct page *page, struct buffer_head *head, enum migrate_mode mode, int extra_count){ int expected_count = 1 + extra_count; void **pslot; if (!mapping) { /* Anonymous page without mapping */ if (page_count(page) != expected_count) return -EAGAIN; return MIGRATEPAGE_SUCCESS; } spin_lock_irq(&mapping->tree_lock); pslot = radix_tree_lookup_slot(&mapping->page_tree, page_index(page)); expected_count += 1 + page_has_private(page); if (page_count(page) != expected_count || radix_tree_deref_slot_protected(pslot, &mapping->tree_lock) != page) { spin_unlock_irq(&mapping->tree_lock); return -EAGAIN; } if (!page_freeze_refs(page, expected_count)) { spin_unlock_irq(&mapping->tree_lock); return -EAGAIN; } /* * In the async migration case of moving a page with buffers, lock the * buffers using trylock before the mapping is moved. If the mapping * was moved, we later failed to lock the buffers and could not move * the mapping back due to an elevated page count, we would have to * block waiting on other references to be dropped. */ if (mode == MIGRATE_ASYNC && head && !buffer_migrate_lock_buffers(head, mode)) { page_unfreeze_refs(page, expected_count); spin_unlock_irq(&mapping->tree_lock); return -EAGAIN; } /* * Now we know that no one else is looking at the page. */ get_page(newpage); /* add cache reference */ if (PageSwapCache(page)) { SetPageSwapCache(newpage); set_page_private(newpage, page_private(page)); } radix_tree_replace_slot(pslot, newpage); /* * Drop cache reference from old page by unfreezing * to one less reference. * We know this isn't the last reference. */ page_unfreeze_refs(page, expected_count - 1); /* * If moved to a different zone then also account * the page for that zone. Other VM counters will be * taken care of when we establish references to the * new page and drop references to the old page. * * Note that anonymous pages are accounted for * via NR_FILE_PAGES and NR_ANON_PAGES if they * are mapped to swap space. */ __dec_zone_page_state(page, NR_FILE_PAGES); __inc_zone_page_state(newpage, NR_FILE_PAGES); if (!PageSwapCache(page) && PageSwapBacked(page)) { __dec_zone_page_state(page, NR_SHMEM); __inc_zone_page_state(newpage, NR_SHMEM); } spin_unlock_irq(&mapping->tree_lock); return MIGRATEPAGE_SUCCESS;} /* * The expected number of remaining references is the same as that * of migrate_page_move_mapping(). */int migrate_huge_page_move_mapping(struct address_space *mapping, struct page *newpage, struct page *page){ int expected_count; void **pslot; if (!mapping) { if (page_count(page) != 1) return -EAGAIN; return MIGRATEPAGE_SUCCESS; } spin_lock_irq(&mapping->tree_lock); pslot = radix_tree_lookup_slot(&mapping->page_tree, page_index(page)); expected_count = 2 + page_has_private(page); if (page_count(page) != expected_count || radix_tree_deref_slot_protected(pslot, &mapping->tree_lock) != page) { spin_unlock_irq(&mapping->tree_lock); return -EAGAIN; } if (!page_freeze_refs(page, expected_count)) { spin_unlock_irq(&mapping->tree_lock); return -EAGAIN; } get_page(newpage); radix_tree_replace_slot(pslot, newpage); page_unfreeze_refs(page, expected_count - 1); spin_unlock_irq(&mapping->tree_lock); return MIGRATEPAGE_SUCCESS;} /* * Gigantic pages are so large that we do not guarantee that page++ pointer * arithmetic will work across the entire page. We need something more * specialized. */static void __copy_gigantic_page(struct page *dst, struct page *src, int nr_pages){ int i; struct page *dst_base = dst; struct page *src_base = src; for (i = 0; i < nr_pages; ) { cond_resched(); copy_highpage(dst, src); i++; dst = mem_map_next(dst, dst_base, i); src = mem_map_next(src, src_base, i); }} static void copy_huge_page(struct page *dst, struct page *src){ int i; int nr_pages; if (PageHuge(src)) { /* hugetlbfs page */ struct hstate *h = page_hstate(src); nr_pages = pages_per_huge_page(h); if (unlikely(nr_pages > MAX_ORDER_NR_PAGES)) { __copy_gigantic_page(dst, src, nr_pages); return; } } else { /* thp page */ BUG_ON(!PageTransHuge(src)); nr_pages = hpage_nr_pages(src); } for (i = 0; i < nr_pages; i++) { cond_resched(); copy_highpage(dst + i, src + i); }} /* * Copy the page to its new location */void migrate_page_copy(struct page *newpage, struct page *page){ int cpupid; Lines 1–500 of 1881. Load more
/* * Memory Migration functionality - linux/mm/migration.c * * Copyright (C) 2006 Silicon Graphics, Inc., Christoph Lameter * * Page migration was first developed in the context of the memory hotplug * project. The main authors of the migration code are: * * IWAMOTO Toshihiro <iwamoto@valinux.co.jp> * Hirokazu Takahashi <taka@valinux.co.jp> * Dave Hansen <haveblue@us.ibm.com> * Christoph Lameter */ #include <linux/migrate.h> #include <linux/export.h> #include <linux/swap.h> #include <linux/swapops.h> #include <linux/pagemap.h> #include <linux/buffer_head.h> #include <linux/mm_inline.h> #include <linux/nsproxy.h> #include <linux/pagevec.h> #include <linux/ksm.h> #include <linux/rmap.h> #include <linux/topology.h> #include <linux/cpu.h> #include <linux/cpuset.h> #include <linux/writeback.h> #include <linux/mempolicy.h> #include <linux/vmalloc.h> #include <linux/security.h> #include <linux/memcontrol.h> #include <linux/syscalls.h> #include <linux/hugetlb.h> #include <linux/hugetlb_cgroup.h> #include <linux/gfp.h> #include <linux/balloon_compaction.h> #include <linux/mmu_notifier.h> #include <asm/tlbflush.h> #define CREATE_TRACE_POINTS #include <trace/events/migrate.h> #include "internal.h" /* * migrate_prep() needs to be called before we start compiling a list of pages * to be migrated using isolate_lru_page(). If scheduling work on other CPUs is * undesirable, use migrate_prep_local() */ int migrate_prep(void) { /* * Clear the LRU lists so pages can be isolated. * Note that pages may be moved off the LRU after we have * drained them. Those pages will fail to migrate like other * pages that may be busy. */ lru_add_drain_all(); return 0; } /* Do the necessary work of migrate_prep but not if it involves other CPUs */ int migrate_prep_local(void) { lru_add_drain(); return 0; } /* * Add isolated pages on the list back to the LRU under page lock * to avoid leaking evictable pages back onto unevictable list. */ void putback_lru_pages(struct list_head *l) { struct page *page; struct page *page2; list_for_each_entry_safe(page, page2, l, lru) { list_del(&page->lru); dec_zone_page_state(page, NR_ISOLATED_ANON + page_is_file_cache(page)); putback_lru_page(page); } } /* * Put previously isolated pages back onto the appropriate lists * from where they were once taken off for compaction/migration. * * This function shall be used instead of putback_lru_pages(), * whenever the isolated pageset has been built by isolate_migratepages_range() */ void putback_movable_pages(struct list_head *l) { struct page *page; struct page *page2; list_for_each_entry_safe(page, page2, l, lru) { if (unlikely(PageHuge(page))) { putback_active_hugepage(page); continue; } list_del(&page->lru); dec_zone_page_state(page, NR_ISOLATED_ANON + page_is_file_cache(page)); if (unlikely(isolated_balloon_page(page))) balloon_page_putback(page); else putback_lru_page(page); } } /* * Restore a potential migration pte to a working pte entry */ static int remove_migration_pte(struct page *new, struct vm_area_struct *vma, unsigned long addr, void *old) { struct mm_struct *mm = vma->vm_mm; swp_entry_t entry; pmd_t *pmd; pte_t *ptep, pte; spinlock_t *ptl; if (unlikely(PageHuge(new))) { ptep = huge_pte_offset(mm, addr); if (!ptep) goto out; ptl = huge_pte_lockptr(hstate_vma(vma), mm, ptep); } else { pmd = mm_find_pmd(mm, addr); if (!pmd) goto out; if (pmd_trans_huge(*pmd)) goto out; ptep = pte_offset_map(pmd, addr); /* * Peek to check is_swap_pte() before taking ptlock? No, we * can race mremap's move_ptes(), which skips anon_vma lock. */ ptl = pte_lockptr(mm, pmd); } spin_lock(ptl); pte = *ptep; if (!is_swap_pte(pte)) goto unlock; entry = pte_to_swp_entry(pte); if (!is_migration_entry(entry) || migration_entry_to_page(entry) != old) goto unlock; get_page(new); pte = pte_mkold(mk_pte(new, vma->vm_page_prot)); if (pte_swp_soft_dirty(*ptep)) pte = pte_mksoft_dirty(pte); if (is_write_migration_entry(entry)) pte = pte_mkwrite(pte); #ifdef CONFIG_HUGETLB_PAGE if (PageHuge(new)) { pte = pte_mkhuge(pte); pte = arch_make_huge_pte(pte, vma, new, 0); } #endif flush_dcache_page(new); set_pte_at(mm, addr, ptep, pte); if (PageHuge(new)) { if (PageAnon(new)) hugepage_add_anon_rmap(new, vma, addr); else page_dup_rmap(new); } else if (PageAnon(new)) page_add_anon_rmap(new, vma, addr); else page_add_file_rmap(new); /* No need to invalidate - it was non-present before */ update_mmu_cache(vma, addr, ptep); unlock: pte_unmap_unlock(ptep, ptl); out: return SWAP_AGAIN; } /* * Get rid of all migration entries and replace them by * references to the indicated page. */ static void remove_migration_ptes(struct page *old, struct page *new) { rmap_walk(new, remove_migration_pte, old); } /* * Something used the pte of a page under migration. We need to * get to the page and wait until migration is finished. * When we return from this function the fault will be retried. */ static void __migration_entry_wait(struct mm_struct *mm, pte_t *ptep, spinlock_t *ptl) { pte_t pte; swp_entry_t entry; struct page *page; spin_lock(ptl); pte = *ptep; if (!is_swap_pte(pte)) goto out; entry = pte_to_swp_entry(pte); if (!is_migration_entry(entry)) goto out; page = migration_entry_to_page(entry); /* * Once radix-tree replacement of page migration started, page_count * *must* be zero. And, we don't want to call wait_on_page_locked() * against a page without get_page(). * So, we use get_page_unless_zero(), here. Even failed, page fault * will occur again. */ if (!get_page_unless_zero(page)) goto out; pte_unmap_unlock(ptep, ptl); wait_on_page_locked(page); put_page(page); return; out: pte_unmap_unlock(ptep, ptl); } void migration_entry_wait(struct mm_struct *mm, pmd_t *pmd, unsigned long address) { spinlock_t *ptl = pte_lockptr(mm, pmd); pte_t *ptep = pte_offset_map(pmd, address); __migration_entry_wait(mm, ptep, ptl); } void migration_entry_wait_huge(struct vm_area_struct *vma, struct mm_struct *mm, pte_t *pte) { spinlock_t *ptl = huge_pte_lockptr(hstate_vma(vma), mm, pte); __migration_entry_wait(mm, pte, ptl); } #ifdef CONFIG_BLOCK /* Returns true if all buffers are successfully locked */ static bool buffer_migrate_lock_buffers(struct buffer_head *head, enum migrate_mode mode) { struct buffer_head *bh = head; /* Simple case, sync compaction */ if (mode != MIGRATE_ASYNC) { do { get_bh(bh); lock_buffer(bh); bh = bh->b_this_page; } while (bh != head); return true; } /* async case, we cannot block on lock_buffer so use trylock_buffer */ do { get_bh(bh); if (!trylock_buffer(bh)) { /* * We failed to lock the buffer and cannot stall in * async migration. Release the taken locks */ struct buffer_head *failed_bh = bh; put_bh(failed_bh); bh = head; while (bh != failed_bh) { unlock_buffer(bh); put_bh(bh); bh = bh->b_this_page; } return false; } bh = bh->b_this_page; } while (bh != head); return true; } #else static inline bool buffer_migrate_lock_buffers(struct buffer_head *head, enum migrate_mode mode) { return true; } #endif /* CONFIG_BLOCK */ /* * Replace the page in the mapping. * * The number of remaining references must be: * 1 for anonymous pages without a mapping * 2 for pages with a mapping * 3 for pages with a mapping and PagePrivate/PagePrivate2 set. */ int migrate_page_move_mapping(struct address_space *mapping, struct page *newpage, struct page *page, struct buffer_head *head, enum migrate_mode mode, int extra_count) { int expected_count = 1 + extra_count; void **pslot; if (!mapping) { /* Anonymous page without mapping */ if (page_count(page) != expected_count) return -EAGAIN; return MIGRATEPAGE_SUCCESS; } spin_lock_irq(&mapping->tree_lock); pslot = radix_tree_lookup_slot(&mapping->page_tree, page_index(page)); expected_count += 1 + page_has_private(page); if (page_count(page) != expected_count || radix_tree_deref_slot_protected(pslot, &mapping->tree_lock) != page) { spin_unlock_irq(&mapping->tree_lock); return -EAGAIN; } if (!page_freeze_refs(page, expected_count)) { spin_unlock_irq(&mapping->tree_lock); return -EAGAIN; } /* * In the async migration case of moving a page with buffers, lock the * buffers using trylock before the mapping is moved. If the mapping * was moved, we later failed to lock the buffers and could not move * the mapping back due to an elevated page count, we would have to * block waiting on other references to be dropped. */ if (mode == MIGRATE_ASYNC && head && !buffer_migrate_lock_buffers(head, mode)) { page_unfreeze_refs(page, expected_count); spin_unlock_irq(&mapping->tree_lock); return -EAGAIN; } /* * Now we know that no one else is looking at the page. */ get_page(newpage); /* add cache reference */ if (PageSwapCache(page)) { SetPageSwapCache(newpage); set_page_private(newpage, page_private(page)); } radix_tree_replace_slot(pslot, newpage); /* * Drop cache reference from old page by unfreezing * to one less reference. * We know this isn't the last reference. */ page_unfreeze_refs(page, expected_count - 1); /* * If moved to a different zone then also account * the page for that zone. Other VM counters will be * taken care of when we establish references to the * new page and drop references to the old page. * * Note that anonymous pages are accounted for * via NR_FILE_PAGES and NR_ANON_PAGES if they * are mapped to swap space. */ __dec_zone_page_state(page, NR_FILE_PAGES); __inc_zone_page_state(newpage, NR_FILE_PAGES); if (!PageSwapCache(page) && PageSwapBacked(page)) { __dec_zone_page_state(page, NR_SHMEM); __inc_zone_page_state(newpage, NR_SHMEM); } spin_unlock_irq(&mapping->tree_lock); return MIGRATEPAGE_SUCCESS; } /* * The expected number of remaining references is the same as that * of migrate_page_move_mapping(). */ int migrate_huge_page_move_mapping(struct address_space *mapping, struct page *newpage, struct page *page) { int expected_count; void **pslot; if (!mapping) { if (page_count(page) != 1) return -EAGAIN; return MIGRATEPAGE_SUCCESS; } spin_lock_irq(&mapping->tree_lock); pslot = radix_tree_lookup_slot(&mapping->page_tree, page_index(page)); expected_count = 2 + page_has_private(page); if (page_count(page) != expected_count || radix_tree_deref_slot_protected(pslot, &mapping->tree_lock) != page) { spin_unlock_irq(&mapping->tree_lock); return -EAGAIN; } if (!page_freeze_refs(page, expected_count)) { spin_unlock_irq(&mapping->tree_lock); return -EAGAIN; } get_page(newpage); radix_tree_replace_slot(pslot, newpage); page_unfreeze_refs(page, expected_count - 1); spin_unlock_irq(&mapping->tree_lock); return MIGRATEPAGE_SUCCESS; } /* * Gigantic pages are so large that we do not guarantee that page++ pointer * arithmetic will work across the entire page. We need something more * specialized. */ static void __copy_gigantic_page(struct page *dst, struct page *src, int nr_pages) { int i; struct page *dst_base = dst; struct page *src_base = src; for (i = 0; i < nr_pages; ) { cond_resched(); copy_highpage(dst, src); i++; dst = mem_map_next(dst, dst_base, i); src = mem_map_next(src, src_base, i); } } static void copy_huge_page(struct page *dst, struct page *src) { int i; int nr_pages; if (PageHuge(src)) { /* hugetlbfs page */ struct hstate *h = page_hstate(src); nr_pages = pages_per_huge_page(h); if (unlikely(nr_pages > MAX_ORDER_NR_PAGES)) { __copy_gigantic_page(dst, src, nr_pages); return; } } else { /* thp page */ BUG_ON(!PageTransHuge(src)); nr_pages = hpage_nr_pages(src); } for (i = 0; i < nr_pages; i++) { cond_resched(); copy_highpage(dst + i, src + i); } } /* * Copy the page to its new location */ void migrate_page_copy(struct page *newpage, struct page *page) { int cpupid;