Skip to content

Commit

Permalink
mm: numa: Add THP migration for the NUMA working set scanning fault c…
Browse files Browse the repository at this point in the history
…ase.

Note: This is very heavily based on a patch from Peter Zijlstra with
	fixes from Ingo Molnar, Hugh Dickins and Johannes Weiner.  That patch
	put a lot of migration logic into mm/huge_memory.c where it does
	not belong. This version puts tries to share some of the migration
	logic with migrate_misplaced_page.  However, it should be noted
	that now migrate.c is doing more with the pagetable manipulation
	than is preferred. The end result is barely recognisable so as
	before, the signed-offs had to be removed but will be re-added if
	the original authors are ok with it.

Add THP migration for the NUMA working set scanning fault case.

It uses the page lock to serialize. No migration pte dance is
necessary because the pte is already unmapped when we decide
to migrate.

[[email protected]: Fix memory leak on isolation failure]
[[email protected]: Fix transfer of last_nid information]
Signed-off-by: Mel Gorman <[email protected]>
  • Loading branch information
Mel Gorman committed Dec 11, 2012
1 parent 5bca230 commit b32967f
Show file tree
Hide file tree
Showing 5 changed files with 255 additions and 64 deletions.
15 changes: 15 additions & 0 deletions include/linux/migrate.h
Original file line number Diff line number Diff line change
Expand Up @@ -79,6 +79,12 @@ static inline int migrate_huge_page_move_mapping(struct address_space *mapping,
extern int migrate_misplaced_page(struct page *page, int node);
extern int migrate_misplaced_page(struct page *page, int node);
extern bool migrate_ratelimited(int node);
extern int migrate_misplaced_transhuge_page(struct mm_struct *mm,
struct vm_area_struct *vma,
pmd_t *pmd, pmd_t entry,
unsigned long address,
struct page *page, int node);

#else
static inline int migrate_misplaced_page(struct page *page, int node)
{
Expand All @@ -88,6 +94,15 @@ static inline bool migrate_ratelimited(int node)
{
return false;
}

static inline int migrate_misplaced_transhuge_page(struct mm_struct *mm,
struct vm_area_struct *vma,
pmd_t *pmd, pmd_t entry,
unsigned long address,
struct page *page, int node)
{
return -EAGAIN;
}
#endif /* CONFIG_NUMA_BALANCING */

#endif /* _LINUX_MIGRATE_H */
59 changes: 40 additions & 19 deletions mm/huge_memory.c
Original file line number Diff line number Diff line change
Expand Up @@ -600,7 +600,7 @@ static int __init setup_transparent_hugepage(char *str)
}
__setup("transparent_hugepage=", setup_transparent_hugepage);

static inline pmd_t maybe_pmd_mkwrite(pmd_t pmd, struct vm_area_struct *vma)
pmd_t maybe_pmd_mkwrite(pmd_t pmd, struct vm_area_struct *vma)
{
if (likely(vma->vm_flags & VM_WRITE))
pmd = pmd_mkwrite(pmd);
Expand Down Expand Up @@ -1023,53 +1023,74 @@ struct page *follow_trans_huge_pmd(struct vm_area_struct *vma,
int do_huge_pmd_numa_page(struct mm_struct *mm, struct vm_area_struct *vma,
unsigned long addr, pmd_t pmd, pmd_t *pmdp)
{
struct page *page = NULL;
struct page *page;
unsigned long haddr = addr & HPAGE_PMD_MASK;
int target_nid;
int current_nid = -1;
bool migrated;
bool page_locked = false;

spin_lock(&mm->page_table_lock);
if (unlikely(!pmd_same(pmd, *pmdp)))
goto out_unlock;

page = pmd_page(pmd);
get_page(page);
spin_unlock(&mm->page_table_lock);
current_nid = page_to_nid(page);
count_vm_numa_event(NUMA_HINT_FAULTS);
if (current_nid == numa_node_id())
count_vm_numa_event(NUMA_HINT_FAULTS_LOCAL);

target_nid = mpol_misplaced(page, vma, haddr);
if (target_nid == -1)
if (target_nid == -1) {
put_page(page);
goto clear_pmdnuma;
}

/*
* Due to lacking code to migrate thp pages, we'll split
* (which preserves the special PROT_NONE) and re-take the
* fault on the normal pages.
*/
split_huge_page(page);
put_page(page);

return 0;
/* Acquire the page lock to serialise THP migrations */
spin_unlock(&mm->page_table_lock);
lock_page(page);
page_locked = true;

clear_pmdnuma:
/* Confirm the PTE did not while locked */
spin_lock(&mm->page_table_lock);
if (unlikely(!pmd_same(pmd, *pmdp)))
if (unlikely(!pmd_same(pmd, *pmdp))) {
unlock_page(page);
put_page(page);
goto out_unlock;
}
spin_unlock(&mm->page_table_lock);

/* Migrate the THP to the requested node */
migrated = migrate_misplaced_transhuge_page(mm, vma,
pmdp, pmd, addr,
page, target_nid);
if (migrated)
current_nid = target_nid;
else {
spin_lock(&mm->page_table_lock);
if (unlikely(!pmd_same(pmd, *pmdp))) {
unlock_page(page);
goto out_unlock;
}
goto clear_pmdnuma;
}

task_numa_fault(current_nid, HPAGE_PMD_NR, migrated);
return 0;

clear_pmdnuma:
pmd = pmd_mknonnuma(pmd);
set_pmd_at(mm, haddr, pmdp, pmd);
VM_BUG_ON(pmd_numa(*pmdp));
update_mmu_cache_pmd(vma, addr, pmdp);
if (page_locked)
unlock_page(page);

out_unlock:
spin_unlock(&mm->page_table_lock);
if (page) {
put_page(page);
task_numa_fault(numa_node_id(), HPAGE_PMD_NR, false);
}
if (current_nid != -1)
task_numa_fault(current_nid, HPAGE_PMD_NR, migrated);
return 0;
}

Expand Down
7 changes: 5 additions & 2 deletions mm/internal.h
Original file line number Diff line number Diff line change
Expand Up @@ -212,15 +212,18 @@ static inline void mlock_migrate_page(struct page *newpage, struct page *page)
{
if (TestClearPageMlocked(page)) {
unsigned long flags;
int nr_pages = hpage_nr_pages(page);

local_irq_save(flags);
__dec_zone_page_state(page, NR_MLOCK);
__mod_zone_page_state(page_zone(page), NR_MLOCK, -nr_pages);
SetPageMlocked(newpage);
__inc_zone_page_state(newpage, NR_MLOCK);
__mod_zone_page_state(page_zone(newpage), NR_MLOCK, nr_pages);
local_irq_restore(flags);
}
}

extern pmd_t maybe_pmd_mkwrite(pmd_t pmd, struct vm_area_struct *vma);

#ifdef CONFIG_TRANSPARENT_HUGEPAGE
extern unsigned long vma_address(struct page *page,
struct vm_area_struct *vma);
Expand Down
7 changes: 5 additions & 2 deletions mm/memcontrol.c
Original file line number Diff line number Diff line change
Expand Up @@ -3288,15 +3288,18 @@ void mem_cgroup_prepare_migration(struct page *page, struct page *newpage,
struct mem_cgroup **memcgp)
{
struct mem_cgroup *memcg = NULL;
unsigned int nr_pages = 1;
struct page_cgroup *pc;
enum charge_type ctype;

*memcgp = NULL;

VM_BUG_ON(PageTransHuge(page));
if (mem_cgroup_disabled())
return;

if (PageTransHuge(page))
nr_pages <<= compound_order(page);

pc = lookup_page_cgroup(page);
lock_page_cgroup(pc);
if (PageCgroupUsed(pc)) {
Expand Down Expand Up @@ -3358,7 +3361,7 @@ void mem_cgroup_prepare_migration(struct page *page, struct page *newpage,
* charged to the res_counter since we plan on replacing the
* old one and only one page is going to be left afterwards.
*/
__mem_cgroup_commit_charge(memcg, newpage, 1, ctype, false);
__mem_cgroup_commit_charge(memcg, newpage, nr_pages, ctype, false);
}

/* remove redundant charge if migration failed*/
Expand Down
Loading

0 comments on commit b32967f

Please sign in to comment.