diff options
| author | Namhoon Kim <namhoonk@cs.unc.edu> | 2016-09-09 16:18:12 -0400 |
|---|---|---|
| committer | Namhoon Kim <namhoonk@cs.unc.edu> | 2016-09-09 16:18:12 -0400 |
| commit | 805c547ee3cdc2ef6a5f7556fdf449ced2e48680 (patch) | |
| tree | abeae409739a576291942281f9acf56725d73a1a | |
| parent | 418e60bb6948a4cf6eb7c737bea21c2314619c73 (diff) | |
TODO: Fix condition checks in replicate_page_move_mapping()wip-shared-lib
| -rw-r--r-- | include/linux/migrate.h | 8 | ||||
| -rw-r--r-- | include/litmus/replicate_lib.h | 6 | ||||
| -rw-r--r-- | kernel/sched/litmus.c | 5 | ||||
| -rw-r--r-- | litmus/litmus.c | 60 | ||||
| -rw-r--r-- | litmus/replicate_lib.c | 2 | ||||
| -rw-r--r-- | mm/migrate.c | 552 |
6 files changed, 618 insertions, 15 deletions
diff --git a/include/linux/migrate.h b/include/linux/migrate.h index cac1c0904d5f..b16047b82472 100644 --- a/include/linux/migrate.h +++ b/include/linux/migrate.h | |||
| @@ -33,6 +33,8 @@ extern int migrate_page(struct address_space *, | |||
| 33 | struct page *, struct page *, enum migrate_mode); | 33 | struct page *, struct page *, enum migrate_mode); |
| 34 | extern int migrate_pages(struct list_head *l, new_page_t new, free_page_t free, | 34 | extern int migrate_pages(struct list_head *l, new_page_t new, free_page_t free, |
| 35 | unsigned long private, enum migrate_mode mode, int reason); | 35 | unsigned long private, enum migrate_mode mode, int reason); |
| 36 | extern int replicate_pages(struct list_head *l, new_page_t new, free_page_t free, | ||
| 37 | unsigned long private, enum migrate_mode mode, int reason); | ||
| 36 | 38 | ||
| 37 | extern int migrate_prep(void); | 39 | extern int migrate_prep(void); |
| 38 | extern int migrate_prep_local(void); | 40 | extern int migrate_prep_local(void); |
| @@ -50,7 +52,11 @@ static inline int migrate_pages(struct list_head *l, new_page_t new, | |||
| 50 | free_page_t free, unsigned long private, enum migrate_mode mode, | 52 | free_page_t free, unsigned long private, enum migrate_mode mode, |
| 51 | int reason) | 53 | int reason) |
| 52 | { return -ENOSYS; } | 54 | { return -ENOSYS; } |
| 53 | 55 | static inline int replicate_pages(struct list_head *l, new_page_t new, | |
| 56 | free_page_t free, unsigned long private, enum migrate_mode mode, | ||
| 57 | int reason) | ||
| 58 | { return -ENOSYS; } | ||
| 59 | |||
| 54 | static inline int migrate_prep(void) { return -ENOSYS; } | 60 | static inline int migrate_prep(void) { return -ENOSYS; } |
| 55 | static inline int migrate_prep_local(void) { return -ENOSYS; } | 61 | static inline int migrate_prep_local(void) { return -ENOSYS; } |
| 56 | 62 | ||
diff --git a/include/litmus/replicate_lib.h b/include/litmus/replicate_lib.h index af2af36b6b79..480ce4631529 100644 --- a/include/litmus/replicate_lib.h +++ b/include/litmus/replicate_lib.h | |||
| @@ -7,8 +7,12 @@ | |||
| 7 | 7 | ||
| 8 | struct shared_lib_page { | 8 | struct shared_lib_page { |
| 9 | struct page *p_page; | 9 | struct page *p_page; |
| 10 | unsigned long pfn; | 10 | struct page *r_page; |
| 11 | unsigned long p_pfn; | ||
| 12 | unsigned long r_pfn; | ||
| 11 | struct list_head list; | 13 | struct list_head list; |
| 12 | }; | 14 | }; |
| 13 | 15 | ||
| 16 | extern struct list_head shared_lib_pages; | ||
| 17 | |||
| 14 | #endif | 18 | #endif |
diff --git a/kernel/sched/litmus.c b/kernel/sched/litmus.c index 9d58690cf51a..cd36358cee75 100644 --- a/kernel/sched/litmus.c +++ b/kernel/sched/litmus.c | |||
| @@ -20,8 +20,9 @@ static void update_time_litmus(struct rq *rq, struct task_struct *p) | |||
| 20 | /* task counter */ | 20 | /* task counter */ |
| 21 | p->se.sum_exec_runtime += delta; | 21 | p->se.sum_exec_runtime += delta; |
| 22 | if (delta) { | 22 | if (delta) { |
| 23 | TRACE_TASK(p, "charged %llu exec time (total:%llu, rem:%llu)\n", | 23 | //TRACE_TASK(p, "charged %llu exec time (total:%llu, rem:%llu)\n", |
| 24 | delta, p->rt_param.job_params.exec_time, budget_remaining(p)); | 24 | //delta, p->rt_param.job_params.exec_time, budget_remaining(p)); |
| 25 | ; | ||
| 25 | } | 26 | } |
| 26 | /* sched_clock() */ | 27 | /* sched_clock() */ |
| 27 | p->se.exec_start = rq->clock; | 28 | p->se.exec_start = rq->clock; |
diff --git a/litmus/litmus.c b/litmus/litmus.c index 402c495f62c6..8e7f5e2e68df 100644 --- a/litmus/litmus.c +++ b/litmus/litmus.c | |||
| @@ -353,6 +353,10 @@ extern void putback_movable_page(struct page *page); | |||
| 353 | extern struct page *new_alloc_page(struct page *page, unsigned long node, int **x); | 353 | extern struct page *new_alloc_page(struct page *page, unsigned long node, int **x); |
| 354 | 354 | ||
| 355 | DECLARE_PER_CPU(struct list_head, shared_lib_page_list); | 355 | DECLARE_PER_CPU(struct list_head, shared_lib_page_list); |
| 356 | #define INVALID_PFN (0xffffffff) | ||
| 357 | LIST_HEAD(shared_lib_pages); | ||
| 358 | //struct list_head shared_lib_pages = LIST_HEAD_INIT(shared_lib_pages); | ||
| 359 | EXPORT_SYMBOL(shared_lib_pages); | ||
| 356 | 360 | ||
| 357 | asmlinkage long sys_set_page_color(int cpu) | 361 | asmlinkage long sys_set_page_color(int cpu) |
| 358 | { | 362 | { |
| @@ -366,8 +370,8 @@ asmlinkage long sys_set_page_color(int cpu) | |||
| 366 | //struct list_head *shared_pagelist = this_cpu_ptr(&shared_lib_page_list); | 370 | //struct list_head *shared_pagelist = this_cpu_ptr(&shared_lib_page_list); |
| 367 | 371 | ||
| 368 | LIST_HEAD(pagelist); | 372 | LIST_HEAD(pagelist); |
| 369 | LIST_HEAD(shared_pagelist); | 373 | LIST_HEAD(task_shared_pagelist); |
| 370 | 374 | ||
| 371 | migrate_prep(); | 375 | migrate_prep(); |
| 372 | 376 | ||
| 373 | rcu_read_lock(); | 377 | rcu_read_lock(); |
| @@ -408,10 +412,36 @@ asmlinkage long sys_set_page_color(int cpu) | |||
| 408 | 412 | ||
| 409 | if (page_count(old_page) > 2 && vma_itr->vm_file != NULL && !(vma_itr->vm_flags&VM_WRITE)) { | 413 | if (page_count(old_page) > 2 && vma_itr->vm_file != NULL && !(vma_itr->vm_flags&VM_WRITE)) { |
| 410 | struct shared_lib_page *lib_page; | 414 | struct shared_lib_page *lib_page; |
| 411 | lib_page = kmalloc(sizeof(struct shared_lib_page), GFP_KERNEL); | 415 | int is_exist = 0; |
| 412 | lib_page->p_page = old_page; | 416 | |
| 413 | lib_page->pfn = page_to_pfn(old_page); | 417 | /* update PSL list */ |
| 414 | list_add_tail(&lib_page->list, &shared_pagelist); | 418 | /* check if this page is in the PSL list */ |
| 419 | rcu_read_lock(); | ||
| 420 | list_for_each_entry(lib_page, &shared_lib_pages, list) | ||
| 421 | { | ||
| 422 | if (page_to_pfn(old_page) == lib_page->p_pfn) { | ||
| 423 | is_exist = 1; | ||
| 424 | break; | ||
| 425 | } | ||
| 426 | } | ||
| 427 | rcu_read_unlock(); | ||
| 428 | |||
| 429 | if (is_exist == 0) { | ||
| 430 | lib_page = kmalloc(sizeof(struct shared_lib_page), GFP_KERNEL); | ||
| 431 | lib_page->p_page = old_page; | ||
| 432 | lib_page->r_page = NULL; | ||
| 433 | lib_page->p_pfn = page_to_pfn(old_page); | ||
| 434 | lib_page->r_pfn = INVALID_PFN; | ||
| 435 | list_add_tail(&lib_page->list, &shared_lib_pages); | ||
| 436 | TRACE_TASK(current, "NEW PAGE %ld ADDED.\n", lib_page->p_pfn); | ||
| 437 | } | ||
| 438 | else { | ||
| 439 | TRACE_TASK(current, "FOUND PAGE %ld in the list.\n", lib_page->p_pfn); | ||
| 440 | } | ||
| 441 | |||
| 442 | /* add to task_shared_pagelist */ | ||
| 443 | list_add_tail(&old_page->lru, &task_shared_pagelist); | ||
| 444 | |||
| 415 | nr_shared_pages++; | 445 | nr_shared_pages++; |
| 416 | TRACE_TASK(current, "SHARED\n"); | 446 | TRACE_TASK(current, "SHARED\n"); |
| 417 | } | 447 | } |
| @@ -428,7 +458,7 @@ asmlinkage long sys_set_page_color(int cpu) | |||
| 428 | } | 458 | } |
| 429 | //printk(KERN_INFO "PRIVATE _mapcount = %d, _count = %d\n", page_mapcount(old_page), page_count(old_page)); | 459 | //printk(KERN_INFO "PRIVATE _mapcount = %d, _count = %d\n", page_mapcount(old_page), page_count(old_page)); |
| 430 | put_page(old_page); | 460 | put_page(old_page); |
| 431 | TRACE_TASK(current, "PRIVATE\n"); | 461 | //TRACE_TASK(current, "PRIVATE\n"); |
| 432 | } | 462 | } |
| 433 | } | 463 | } |
| 434 | TRACE_TASK(current, "PAGES_IN_VMA = %d size = %d KB\n", pages_in_vma, pages_in_vma*4); | 464 | TRACE_TASK(current, "PAGES_IN_VMA = %d size = %d KB\n", pages_in_vma, pages_in_vma*4); |
| @@ -454,13 +484,21 @@ asmlinkage long sys_set_page_color(int cpu) | |||
| 454 | if (!list_empty(&pagelist)) { | 484 | if (!list_empty(&pagelist)) { |
| 455 | ret = migrate_pages(&pagelist, new_alloc_page, NULL, node, MIGRATE_SYNC, MR_SYSCALL); | 485 | ret = migrate_pages(&pagelist, new_alloc_page, NULL, node, MIGRATE_SYNC, MR_SYSCALL); |
| 456 | TRACE_TASK(current, "%ld pages not migrated.\n", ret); | 486 | TRACE_TASK(current, "%ld pages not migrated.\n", ret); |
| 457 | printk(KERN_INFO "%ld pages not migrated.\n", ret); | ||
| 458 | nr_not_migrated = ret; | 487 | nr_not_migrated = ret; |
| 459 | if (ret) { | 488 | if (ret) { |
| 460 | putback_movable_pages(&pagelist); | 489 | putback_movable_pages(&pagelist); |
| 461 | } | 490 | } |
| 462 | } | 491 | } |
| 463 | 492 | ||
| 493 | if (!list_empty(&task_shared_pagelist)) { | ||
| 494 | ret = replicate_pages(&task_shared_pagelist, new_alloc_page, NULL, node, MIGRATE_SYNC, MR_SYSCALL); | ||
| 495 | TRACE_TASK(current, "%ld shared pages not migrated.\n", ret); | ||
| 496 | nr_not_migrated += ret; | ||
| 497 | if (ret) { | ||
| 498 | putback_movable_pages(&task_shared_pagelist); | ||
| 499 | } | ||
| 500 | } | ||
| 501 | |||
| 464 | /* handle sigpage and litmus ctrl_page */ | 502 | /* handle sigpage and litmus ctrl_page */ |
| 465 | /* vma_itr = current->mm->mmap; | 503 | /* vma_itr = current->mm->mmap; |
| 466 | while (vma_itr != NULL) { | 504 | while (vma_itr != NULL) { |
| @@ -480,14 +518,14 @@ asmlinkage long sys_set_page_color(int cpu) | |||
| 480 | 518 | ||
| 481 | flush_cache(1); | 519 | flush_cache(1); |
| 482 | /* for debug START */ | 520 | /* for debug START */ |
| 483 | TRACE_TASK(current, "SHARED PAGES\n"); | 521 | TRACE_TASK(current, "PSL PAGES\n"); |
| 484 | { | 522 | { |
| 485 | struct shared_lib_page *lpage; | 523 | struct shared_lib_page *lpage; |
| 486 | 524 | ||
| 487 | rcu_read_lock(); | 525 | rcu_read_lock(); |
| 488 | list_for_each_entry(lpage, &shared_pagelist, list) | 526 | list_for_each_entry(lpage, &shared_lib_pages, list) |
| 489 | { | 527 | { |
| 490 | TRACE_TASK(current, "PFN = %ld\n", lpage->pfn); | 528 | TRACE_TASK(current, "p_PFN = %ld r_PFN = %ld\n", lpage->p_pfn, lpage->r_pfn); |
| 491 | } | 529 | } |
| 492 | rcu_read_unlock(); | 530 | rcu_read_unlock(); |
| 493 | } | 531 | } |
diff --git a/litmus/replicate_lib.c b/litmus/replicate_lib.c index 7aa240058ef5..cfc525809412 100644 --- a/litmus/replicate_lib.c +++ b/litmus/replicate_lib.c | |||
| @@ -25,6 +25,8 @@ DEFINE_PER_CPU(struct list_head, shared_lib_page_list); | |||
| 25 | #define shared_lib_pages_for(cpu_id) (&per_cpu(shared_lib_page_list, cpu_id)) | 25 | #define shared_lib_pages_for(cpu_id) (&per_cpu(shared_lib_page_list, cpu_id)) |
| 26 | #define local_shared_lib_pages() (this_cpu_ptr(&shared_lib_page_list)) | 26 | #define local_shared_lib_pages() (this_cpu_ptr(&shared_lib_page_list)) |
| 27 | 27 | ||
| 28 | #define INVALID_PFN (0xffffffff) | ||
| 29 | |||
| 28 | static int __init litmus_replicate_lib_init(void) | 30 | static int __init litmus_replicate_lib_init(void) |
| 29 | { | 31 | { |
| 30 | int cpu, ret = 0; | 32 | int cpu, ret = 0; |
diff --git a/mm/migrate.c b/mm/migrate.c index f53838fe3dfe..c88f881f2daa 100644 --- a/mm/migrate.c +++ b/mm/migrate.c | |||
| @@ -38,6 +38,8 @@ | |||
| 38 | #include <linux/balloon_compaction.h> | 38 | #include <linux/balloon_compaction.h> |
| 39 | #include <linux/mmu_notifier.h> | 39 | #include <linux/mmu_notifier.h> |
| 40 | 40 | ||
| 41 | #include <litmus/litmus.h> // for TRACE_TASK | ||
| 42 | |||
| 41 | #include <asm/tlbflush.h> | 43 | #include <asm/tlbflush.h> |
| 42 | 44 | ||
| 43 | #define CREATE_TRACE_POINTS | 45 | #define CREATE_TRACE_POINTS |
| @@ -391,6 +393,106 @@ int migrate_page_move_mapping(struct address_space *mapping, | |||
| 391 | } | 393 | } |
| 392 | 394 | ||
| 393 | /* | 395 | /* |
| 396 | * Replace the page in the mapping. | ||
| 397 | * | ||
| 398 | * The number of remaining references must be: | ||
| 399 | * 1 for anonymous pages without a mapping | ||
| 400 | * 2 for pages with a mapping | ||
| 401 | * 3 for pages with a mapping and PagePrivate/PagePrivate2 set. | ||
| 402 | */ | ||
| 403 | int replicate_page_move_mapping(struct address_space *mapping, | ||
| 404 | struct page *newpage, struct page *page, | ||
| 405 | struct buffer_head *head, enum migrate_mode mode, | ||
| 406 | int extra_count) | ||
| 407 | { | ||
| 408 | int expected_count = 1 + extra_count; | ||
| 409 | void **pslot; | ||
| 410 | |||
| 411 | // if (!mapping) { | ||
| 412 | /* Anonymous page without mapping */ | ||
| 413 | // if (page_count(page) != expected_count) | ||
| 414 | // return -EAGAIN; | ||
| 415 | // return MIGRATEPAGE_SUCCESS; | ||
| 416 | // } | ||
| 417 | |||
| 418 | TRACE_TASK(current, "page has mapping.\n"); | ||
| 419 | spin_lock_irq(&mapping->tree_lock); | ||
| 420 | |||
| 421 | pslot = radix_tree_lookup_slot(&mapping->page_tree, | ||
| 422 | page_index(page)); | ||
| 423 | |||
| 424 | expected_count += 1 + page_has_private(page); | ||
| 425 | |||
| 426 | TRACE_TASK(current, "page_count(page) = %d, expected_count = %d, page_has_private? %d\n", page_count(page), expected_count, page_has_private(page)); | ||
| 427 | |||
| 428 | if (page_count(page) != expected_count || | ||
| 429 | radix_tree_deref_slot_protected(pslot, &mapping->tree_lock) != page) { | ||
| 430 | spin_unlock_irq(&mapping->tree_lock); | ||
| 431 | TRACE_TASK(current, "1\n"); | ||
| 432 | return -EAGAIN; | ||
| 433 | } | ||
| 434 | |||
| 435 | if (!page_freeze_refs(page, expected_count)) { | ||
| 436 | spin_unlock_irq(&mapping->tree_lock); | ||
| 437 | TRACE_TASK(current, "2\n"); | ||
| 438 | return -EAGAIN; | ||
| 439 | } | ||
| 440 | |||
| 441 | /* | ||
| 442 | * In the async migration case of moving a page with buffers, lock the | ||
| 443 | * buffers using trylock before the mapping is moved. If the mapping | ||
| 444 | * was moved, we later failed to lock the buffers and could not move | ||
| 445 | * the mapping back due to an elevated page count, we would have to | ||
| 446 | * block waiting on other references to be dropped. | ||
| 447 | */ | ||
| 448 | /* if (mode == MIGRATE_ASYNC && head && | ||
| 449 | !buffer_migrate_lock_buffers(head, mode)) { | ||
| 450 | page_unfreeze_refs(page, expected_count); | ||
| 451 | spin_unlock_irq(&mapping->tree_lock); | ||
| 452 | TRACE_TASK(current, "3\n"); | ||
| 453 | return -EAGAIN; | ||
| 454 | } | ||
| 455 | */ | ||
| 456 | /* | ||
| 457 | * Now we know that no one else is looking at the page. | ||
| 458 | */ | ||
| 459 | get_page(newpage); /* add cache reference */ | ||
| 460 | if (PageSwapCache(page)) { | ||
| 461 | SetPageSwapCache(newpage); | ||
| 462 | set_page_private(newpage, page_private(page)); | ||
| 463 | } | ||
| 464 | |||
| 465 | radix_tree_replace_slot(pslot, newpage); | ||
| 466 | |||
| 467 | /* | ||
| 468 | * Drop cache reference from old page by unfreezing | ||
| 469 | * to one less reference. | ||
| 470 | * We know this isn't the last reference. | ||
| 471 | */ | ||
| 472 | page_unfreeze_refs(page, expected_count - 1); | ||
| 473 | |||
| 474 | /* | ||
| 475 | * If moved to a different zone then also account | ||
| 476 | * the page for that zone. Other VM counters will be | ||
| 477 | * taken care of when we establish references to the | ||
| 478 | * new page and drop references to the old page. | ||
| 479 | * | ||
| 480 | * Note that anonymous pages are accounted for | ||
| 481 | * via NR_FILE_PAGES and NR_ANON_PAGES if they | ||
| 482 | * are mapped to swap space. | ||
| 483 | */ | ||
| 484 | __dec_zone_page_state(page, NR_FILE_PAGES); | ||
| 485 | __inc_zone_page_state(newpage, NR_FILE_PAGES); | ||
| 486 | if (!PageSwapCache(page) && PageSwapBacked(page)) { | ||
| 487 | __dec_zone_page_state(page, NR_SHMEM); | ||
| 488 | __inc_zone_page_state(newpage, NR_SHMEM); | ||
| 489 | } | ||
| 490 | spin_unlock_irq(&mapping->tree_lock); | ||
| 491 | |||
| 492 | return MIGRATEPAGE_SUCCESS; | ||
| 493 | } | ||
| 494 | |||
| 495 | /* | ||
| 394 | * The expected number of remaining references is the same as that | 496 | * The expected number of remaining references is the same as that |
| 395 | * of migrate_page_move_mapping(). | 497 | * of migrate_page_move_mapping(). |
| 396 | */ | 498 | */ |
| @@ -578,6 +680,23 @@ int migrate_page(struct address_space *mapping, | |||
| 578 | } | 680 | } |
| 579 | EXPORT_SYMBOL(migrate_page); | 681 | EXPORT_SYMBOL(migrate_page); |
| 580 | 682 | ||
| 683 | int replicate_page(struct address_space *mapping, | ||
| 684 | struct page *newpage, struct page *page, | ||
| 685 | enum migrate_mode mode) | ||
| 686 | { | ||
| 687 | int rc, extra_count = 0; | ||
| 688 | |||
| 689 | BUG_ON(PageWriteback(page)); /* Writeback must be complete */ | ||
| 690 | //extra_count = page_count(page) - 2; | ||
| 691 | rc = replicate_page_move_mapping(mapping, newpage, page, NULL, mode, extra_count); | ||
| 692 | TRACE_TASK(current, "replicate_page_move_mapping returned %d\n", rc); | ||
| 693 | if (rc != MIGRATEPAGE_SUCCESS) | ||
| 694 | return rc; | ||
| 695 | |||
| 696 | migrate_page_copy(newpage, page); | ||
| 697 | return MIGRATEPAGE_SUCCESS; | ||
| 698 | } | ||
| 699 | |||
| 581 | #ifdef CONFIG_BLOCK | 700 | #ifdef CONFIG_BLOCK |
| 582 | /* | 701 | /* |
| 583 | * Migration function for pages with buffers. This function can only be used | 702 | * Migration function for pages with buffers. This function can only be used |
| @@ -638,6 +757,60 @@ int buffer_migrate_page(struct address_space *mapping, | |||
| 638 | EXPORT_SYMBOL(buffer_migrate_page); | 757 | EXPORT_SYMBOL(buffer_migrate_page); |
| 639 | #endif | 758 | #endif |
| 640 | 759 | ||
| 760 | int replicate_buffer_page(struct address_space *mapping, | ||
| 761 | struct page *newpage, struct page *page, enum migrate_mode mode) | ||
| 762 | { | ||
| 763 | struct buffer_head *bh, *head; | ||
| 764 | int rc; | ||
| 765 | |||
| 766 | if (!page_has_buffers(page)) { | ||
| 767 | TRACE_TASK(current, "page does not have buffers\n"); | ||
| 768 | return replicate_page(mapping, newpage, page, mode); | ||
| 769 | } | ||
| 770 | |||
| 771 | head = page_buffers(page); | ||
| 772 | |||
| 773 | rc = migrate_page_move_mapping(mapping, newpage, page, head, mode, 0); | ||
| 774 | |||
| 775 | if (rc != MIGRATEPAGE_SUCCESS) | ||
| 776 | return rc; | ||
| 777 | |||
| 778 | /* | ||
| 779 | * In the async case, migrate_page_move_mapping locked the buffers | ||
| 780 | * with an IRQ-safe spinlock held. In the sync case, the buffers | ||
| 781 | * need to be locked now | ||
| 782 | */ | ||
| 783 | if (mode != MIGRATE_ASYNC) | ||
| 784 | BUG_ON(!buffer_migrate_lock_buffers(head, mode)); | ||
| 785 | |||
| 786 | ClearPagePrivate(page); | ||
| 787 | set_page_private(newpage, page_private(page)); | ||
| 788 | set_page_private(page, 0); | ||
| 789 | put_page(page); | ||
| 790 | get_page(newpage); | ||
| 791 | |||
| 792 | bh = head; | ||
| 793 | do { | ||
| 794 | set_bh_page(bh, newpage, bh_offset(bh)); | ||
| 795 | bh = bh->b_this_page; | ||
| 796 | |||
| 797 | } while (bh != head); | ||
| 798 | |||
| 799 | SetPagePrivate(newpage); | ||
| 800 | |||
| 801 | migrate_page_copy(newpage, page); | ||
| 802 | |||
| 803 | bh = head; | ||
| 804 | do { | ||
| 805 | unlock_buffer(bh); | ||
| 806 | put_bh(bh); | ||
| 807 | bh = bh->b_this_page; | ||
| 808 | |||
| 809 | } while (bh != head); | ||
| 810 | |||
| 811 | return MIGRATEPAGE_SUCCESS; | ||
| 812 | } | ||
| 813 | |||
| 641 | /* | 814 | /* |
| 642 | * Writeback a page to clean the dirty state | 815 | * Writeback a page to clean the dirty state |
| 643 | */ | 816 | */ |
| @@ -763,6 +936,74 @@ static int move_to_new_page(struct page *newpage, struct page *page, | |||
| 763 | return rc; | 936 | return rc; |
| 764 | } | 937 | } |
| 765 | 938 | ||
| 939 | /* | ||
| 940 | * Copy a page to a newly allocated page | ||
| 941 | * The page is locked and all ptes have been successfully removed. | ||
| 942 | * | ||
| 943 | * The new page will have replaced the old page if this function | ||
| 944 | * is successful. | ||
| 945 | * | ||
| 946 | * Return value: | ||
| 947 | * < 0 - error code | ||
| 948 | * MIGRATEPAGE_SUCCESS - success | ||
| 949 | */ | ||
| 950 | static int copy_to_new_page(struct page *newpage, struct page *page, | ||
| 951 | int page_was_mapped, enum migrate_mode mode, | ||
| 952 | int has_replica) | ||
| 953 | { | ||
| 954 | struct address_space *mapping; | ||
| 955 | int rc; | ||
| 956 | |||
| 957 | /* | ||
| 958 | * Block others from accessing the page when we get around to | ||
| 959 | * establishing additional references. We are the only one | ||
| 960 | * holding a reference to the new page at this point. | ||
| 961 | */ | ||
| 962 | if (!trylock_page(newpage)) | ||
| 963 | BUG(); | ||
| 964 | |||
| 965 | /* Prepare mapping for the new page.*/ | ||
| 966 | newpage->index = page->index; | ||
| 967 | newpage->mapping = page->mapping; | ||
| 968 | if (PageSwapBacked(page)) | ||
| 969 | SetPageSwapBacked(newpage); | ||
| 970 | |||
| 971 | mapping = page_mapping(page); | ||
| 972 | if (!mapping) { | ||
| 973 | rc = migrate_page(mapping, newpage, page, mode); | ||
| 974 | } | ||
| 975 | else if (mapping->a_ops->migratepage) { | ||
| 976 | TRACE_TASK(current, "ops migration callback\n"); | ||
| 977 | /* | ||
| 978 | * Most pages have a mapping and most filesystems provide a | ||
| 979 | * migratepage callback. Anonymous pages are part of swap | ||
| 980 | * space which also has its own migratepage callback. This | ||
| 981 | * is the most common path for page migration. | ||
| 982 | */ | ||
| 983 | //rc = mapping->a_ops->migratepage(mapping, | ||
| 984 | // newpage, page, mode); | ||
| 985 | rc = replicate_buffer_page(mapping, newpage, page, mode); | ||
| 986 | } | ||
| 987 | else { | ||
| 988 | TRACE_TASK(current, "fallback function\n"); | ||
| 989 | rc = fallback_migrate_page(mapping, newpage, page, mode); | ||
| 990 | } | ||
| 991 | |||
| 992 | if (rc != MIGRATEPAGE_SUCCESS) { | ||
| 993 | newpage->mapping = NULL; | ||
| 994 | } else { | ||
| 995 | mem_cgroup_migrate(page, newpage, false); | ||
| 996 | if (page_was_mapped) | ||
| 997 | remove_migration_ptes(page, newpage); | ||
| 998 | page->mapping = NULL; | ||
| 999 | } | ||
| 1000 | |||
| 1001 | unlock_page(newpage); | ||
| 1002 | |||
| 1003 | return rc; | ||
| 1004 | } | ||
| 1005 | |||
| 1006 | |||
| 766 | static int __unmap_and_move(struct page *page, struct page *newpage, | 1007 | static int __unmap_and_move(struct page *page, struct page *newpage, |
| 767 | int force, enum migrate_mode mode) | 1008 | int force, enum migrate_mode mode) |
| 768 | { | 1009 | { |
| @@ -901,6 +1142,146 @@ out: | |||
| 901 | return rc; | 1142 | return rc; |
| 902 | } | 1143 | } |
| 903 | 1144 | ||
| 1145 | static int __unmap_and_copy(struct page *page, struct page *newpage, | ||
| 1146 | int force, enum migrate_mode mode, int has_replica) | ||
| 1147 | { | ||
| 1148 | int rc = -EAGAIN; | ||
| 1149 | int page_was_mapped = 0; | ||
| 1150 | struct anon_vma *anon_vma = NULL; | ||
| 1151 | |||
| 1152 | if (!trylock_page(page)) { | ||
| 1153 | if (!force || mode == MIGRATE_ASYNC) | ||
| 1154 | goto out; | ||
| 1155 | |||
| 1156 | /* | ||
| 1157 | * It's not safe for direct compaction to call lock_page. | ||
| 1158 | * For example, during page readahead pages are added locked | ||
| 1159 | * to the LRU. Later, when the IO completes the pages are | ||
| 1160 | * marked uptodate and unlocked. However, the queueing | ||
| 1161 | * could be merging multiple pages for one bio (e.g. | ||
| 1162 | * mpage_readpages). If an allocation happens for the | ||
| 1163 | * second or third page, the process can end up locking | ||
| 1164 | * the same page twice and deadlocking. Rather than | ||
| 1165 | * trying to be clever about what pages can be locked, | ||
| 1166 | * avoid the use of lock_page for direct compaction | ||
| 1167 | * altogether. | ||
| 1168 | */ | ||
| 1169 | if (current->flags & PF_MEMALLOC) | ||
| 1170 | goto out; | ||
| 1171 | |||
| 1172 | lock_page(page); | ||
| 1173 | } | ||
| 1174 | |||
| 1175 | if (PageWriteback(page)) { | ||
| 1176 | /* | ||
| 1177 | * Only in the case of a full synchronous migration is it | ||
| 1178 | * necessary to wait for PageWriteback. In the async case, | ||
| 1179 | * the retry loop is too short and in the sync-light case, | ||
| 1180 | * the overhead of stalling is too much | ||
| 1181 | */ | ||
| 1182 | if (mode != MIGRATE_SYNC) { | ||
| 1183 | rc = -EBUSY; | ||
| 1184 | goto out_unlock; | ||
| 1185 | } | ||
| 1186 | if (!force) | ||
| 1187 | goto out_unlock; | ||
| 1188 | wait_on_page_writeback(page); | ||
| 1189 | } | ||
| 1190 | /* | ||
| 1191 | * By try_to_unmap(), page->mapcount goes down to 0 here. In this case, | ||
| 1192 | * we cannot notice that anon_vma is freed while we migrates a page. | ||
| 1193 | * This get_anon_vma() delays freeing anon_vma pointer until the end | ||
| 1194 | * of migration. File cache pages are no problem because of page_lock() | ||
| 1195 | * File Caches may use write_page() or lock_page() in migration, then, | ||
| 1196 | * just care Anon page here. | ||
| 1197 | */ | ||
| 1198 | if (PageAnon(page) && !PageKsm(page)) { | ||
| 1199 | /* | ||
| 1200 | * Only page_lock_anon_vma_read() understands the subtleties of | ||
| 1201 | * getting a hold on an anon_vma from outside one of its mms. | ||
| 1202 | */ | ||
| 1203 | anon_vma = page_get_anon_vma(page); | ||
| 1204 | if (anon_vma) { | ||
| 1205 | /* | ||
| 1206 | * Anon page | ||
| 1207 | */ | ||
| 1208 | } else if (PageSwapCache(page)) { | ||
| 1209 | /* | ||
| 1210 | * We cannot be sure that the anon_vma of an unmapped | ||
| 1211 | * swapcache page is safe to use because we don't | ||
| 1212 | * know in advance if the VMA that this page belonged | ||
| 1213 | * to still exists. If the VMA and others sharing the | ||
| 1214 | * data have been freed, then the anon_vma could | ||
| 1215 | * already be invalid. | ||
| 1216 | * | ||
| 1217 | * To avoid this possibility, swapcache pages get | ||
| 1218 | * migrated but are not remapped when migration | ||
| 1219 | * completes | ||
| 1220 | */ | ||
| 1221 | } else { | ||
| 1222 | goto out_unlock; | ||
| 1223 | } | ||
| 1224 | } | ||
| 1225 | |||
| 1226 | if (unlikely(isolated_balloon_page(page))) { | ||
| 1227 | /* | ||
| 1228 | * A ballooned page does not need any special attention from | ||
| 1229 | * physical to virtual reverse mapping procedures. | ||
| 1230 | * Skip any attempt to unmap PTEs or to remap swap cache, | ||
| 1231 | * in order to avoid burning cycles at rmap level, and perform | ||
| 1232 | * the page migration right away (proteced by page lock). | ||
| 1233 | */ | ||
| 1234 | rc = balloon_page_migrate(newpage, page, mode); | ||
| 1235 | goto out_unlock; | ||
| 1236 | } | ||
| 1237 | |||
| 1238 | /* | ||
| 1239 | * Corner case handling: | ||
| 1240 | * 1. When a new swap-cache page is read into, it is added to the LRU | ||
| 1241 | * and treated as swapcache but it has no rmap yet. | ||
| 1242 | * Calling try_to_unmap() against a page->mapping==NULL page will | ||
| 1243 | * trigger a BUG. So handle it here. | ||
| 1244 | * 2. An orphaned page (see truncate_complete_page) might have | ||
| 1245 | * fs-private metadata. The page can be picked up due to memory | ||
| 1246 | * offlining. Everywhere else except page reclaim, the page is | ||
| 1247 | * invisible to the vm, so the page can not be migrated. So try to | ||
| 1248 | * free the metadata, so the page can be freed. | ||
| 1249 | */ | ||
| 1250 | if (!page->mapping) { | ||
| 1251 | VM_BUG_ON_PAGE(PageAnon(page), page); | ||
| 1252 | if (page_has_private(page)) { | ||
| 1253 | try_to_free_buffers(page); | ||
| 1254 | goto out_unlock; | ||
| 1255 | } | ||
| 1256 | goto skip_unmap; | ||
| 1257 | } | ||
| 1258 | |||
| 1259 | /* Establish migration ptes or remove ptes */ | ||
| 1260 | if (page_mapped(page)) { | ||
| 1261 | try_to_unmap(page, | ||
| 1262 | TTU_MIGRATION|TTU_IGNORE_MLOCK|TTU_IGNORE_ACCESS); | ||
| 1263 | page_was_mapped = 1; | ||
| 1264 | } | ||
| 1265 | |||
| 1266 | skip_unmap: | ||
| 1267 | if (!page_mapped(page)) { | ||
| 1268 | TRACE_TASK(current, "Call copy_to_new_page\n"); | ||
| 1269 | rc = copy_to_new_page(newpage, page, page_was_mapped, mode, has_replica); | ||
| 1270 | } | ||
| 1271 | |||
| 1272 | if (rc && page_was_mapped) | ||
| 1273 | remove_migration_ptes(page, page); | ||
| 1274 | |||
| 1275 | /* Drop an anon_vma reference if we took one */ | ||
| 1276 | if (anon_vma) | ||
| 1277 | put_anon_vma(anon_vma); | ||
| 1278 | |||
| 1279 | out_unlock: | ||
| 1280 | unlock_page(page); | ||
| 1281 | out: | ||
| 1282 | return rc; | ||
| 1283 | } | ||
| 1284 | |||
| 904 | /* | 1285 | /* |
| 905 | * gcc 4.7 and 4.8 on arm get an ICEs when inlining unmap_and_move(). Work | 1286 | * gcc 4.7 and 4.8 on arm get an ICEs when inlining unmap_and_move(). Work |
| 906 | * around it. | 1287 | * around it. |
| @@ -976,6 +1357,97 @@ out: | |||
| 976 | } | 1357 | } |
| 977 | 1358 | ||
| 978 | /* | 1359 | /* |
| 1360 | * Obtain the lock on page, remove all ptes. | ||
| 1361 | * 1) If r_pfn == INVALID_PFN, then copy the page to the newly allocated page in newpage. | ||
| 1362 | * 2) If r_pfn != INVALID_PFN, then unmap and modify ptes. | ||
| 1363 | */ | ||
| 1364 | #include <litmus/replicate_lib.h> | ||
| 1365 | |||
| 1366 | static ICE_noinline int unmap_and_copy(new_page_t get_new_page, | ||
| 1367 | free_page_t put_new_page, | ||
| 1368 | unsigned long private, struct page *page, | ||
| 1369 | int force, enum migrate_mode mode) | ||
| 1370 | { | ||
| 1371 | int rc = 0; | ||
| 1372 | int *result = NULL; | ||
| 1373 | struct page *newpage; | ||
| 1374 | struct shared_lib_page *lib_page; | ||
| 1375 | int is_exist_in_psl = 0, has_replica = 0; | ||
| 1376 | |||
| 1377 | /* check if this page is in the PSL list */ | ||
| 1378 | rcu_read_lock(); | ||
| 1379 | list_for_each_entry(lib_page, &shared_lib_pages, list) | ||
| 1380 | { | ||
| 1381 | if (page_to_pfn(page) == lib_page->p_pfn) { | ||
| 1382 | is_exist_in_psl = 1; | ||
| 1383 | break; | ||
| 1384 | } | ||
| 1385 | } | ||
| 1386 | rcu_read_unlock(); | ||
| 1387 | |||
| 1388 | if (is_exist_in_psl) | ||
| 1389 | TRACE_TASK(current, "Page %ld exists in PSL list\n", lib_page->p_pfn); | ||
| 1390 | |||
| 1391 | if (lib_page->r_page == NULL) { | ||
| 1392 | newpage = get_new_page(page, private, &result); | ||
| 1393 | if (!newpage) | ||
| 1394 | return -ENOMEM; | ||
| 1395 | } | ||
| 1396 | else { | ||
| 1397 | newpage = lib_page->r_page; | ||
| 1398 | has_replica = 1; | ||
| 1399 | } | ||
| 1400 | |||
| 1401 | if (page_count(page) == 1) { | ||
| 1402 | /* page was freed from under us. So we are done. */ | ||
| 1403 | TRACE_TASK(current, "page %ld _count == 1\n", page_to_pfn(page)); | ||
| 1404 | goto out; | ||
| 1405 | } | ||
| 1406 | |||
| 1407 | if (unlikely(PageTransHuge(page))) | ||
| 1408 | if (unlikely(split_huge_page(page))) | ||
| 1409 | goto out; | ||
| 1410 | |||
| 1411 | rc = __unmap_and_copy(page, newpage, force, mode, has_replica); | ||
| 1412 | |||
| 1413 | out: | ||
| 1414 | if (rc != -EAGAIN) { | ||
| 1415 | /* | ||
| 1416 | * A page that has been migrated has all references | ||
| 1417 | * removed and will be freed. A page that has not been | ||
| 1418 | * migrated will have kepts its references and be | ||
| 1419 | * restored. | ||
| 1420 | */ | ||
| 1421 | list_del(&page->lru); | ||
| 1422 | dec_zone_page_state(page, NR_ISOLATED_ANON + | ||
| 1423 | page_is_file_cache(page)); | ||
| 1424 | putback_lru_page(page); | ||
| 1425 | } | ||
| 1426 | |||
| 1427 | /* | ||
| 1428 | * If migration was not successful and there's a freeing callback, use | ||
| 1429 | * it. Otherwise, putback_lru_page() will drop the reference grabbed | ||
| 1430 | * during isolation. | ||
| 1431 | */ | ||
| 1432 | if (rc != MIGRATEPAGE_SUCCESS && put_new_page) { | ||
| 1433 | ClearPageSwapBacked(newpage); | ||
| 1434 | put_new_page(newpage, private); | ||
| 1435 | } else if (unlikely(__is_movable_balloon_page(newpage))) { | ||
| 1436 | /* drop our reference, page already in the balloon */ | ||
| 1437 | put_page(newpage); | ||
| 1438 | } else | ||
| 1439 | putback_lru_page(newpage); | ||
| 1440 | |||
| 1441 | if (result) { | ||
| 1442 | if (rc) | ||
| 1443 | *result = rc; | ||
| 1444 | else | ||
| 1445 | *result = page_to_nid(newpage); | ||
| 1446 | } | ||
| 1447 | return rc; | ||
| 1448 | } | ||
| 1449 | |||
| 1450 | /* | ||
| 979 | * Counterpart of unmap_and_move_page() for hugepage migration. | 1451 | * Counterpart of unmap_and_move_page() for hugepage migration. |
| 980 | * | 1452 | * |
| 981 | * This function doesn't wait the completion of hugepage I/O | 1453 | * This function doesn't wait the completion of hugepage I/O |
| @@ -1159,6 +1631,86 @@ out: | |||
| 1159 | return rc; | 1631 | return rc; |
| 1160 | } | 1632 | } |
| 1161 | 1633 | ||
| 1634 | /* | ||
| 1635 | * replicate_pages - replicate the pages specified in a list | ||
| 1636 | * | ||
| 1637 | * @from: The list of pages to be migrated. | ||
| 1638 | * @get_new_page: The function used to allocate free pages to be used | ||
| 1639 | * if there is no replicated page. | ||
| 1640 | * @put_new_page: The function used to free target pages if migration | ||
| 1641 | * fails, or NULL if no special handling is necessary. | ||
| 1642 | * @private: Private data to be passed on to get_new_page() | ||
| 1643 | * @mode: The migration mode that specifies the constraints for | ||
| 1644 | * page migration, if any. | ||
| 1645 | * @reason: The reason for page migration. | ||
| 1646 | * | ||
| 1647 | * The function returns after 10 attempts or if no pages are movable any more | ||
| 1648 | * because the list has become empty or no retryable pages exist any more. | ||
| 1649 | * The caller should call putback_lru_pages() to return pages to the LRU | ||
| 1650 | * or free list only if ret != 0. | ||
| 1651 | * | ||
| 1652 | * Returns the number of pages that were not migrated, or an error code. | ||
| 1653 | */ | ||
| 1654 | int replicate_pages(struct list_head *from, new_page_t get_new_page, | ||
| 1655 | free_page_t put_new_page, unsigned long private, | ||
| 1656 | enum migrate_mode mode, int reason) | ||
| 1657 | { | ||
| 1658 | int retry = 1; | ||
| 1659 | int nr_failed = 0; | ||
| 1660 | int nr_succeeded = 0; | ||
| 1661 | int pass = 0; | ||
| 1662 | struct page *page; | ||
| 1663 | struct page *page2; | ||
| 1664 | int swapwrite = current->flags & PF_SWAPWRITE; | ||
| 1665 | int rc; | ||
| 1666 | |||
| 1667 | if (!swapwrite) | ||
| 1668 | current->flags |= PF_SWAPWRITE; | ||
| 1669 | |||
| 1670 | for(pass = 0; pass < 10 && retry; pass++) { | ||
| 1671 | retry = 0; | ||
| 1672 | |||
| 1673 | list_for_each_entry_safe(page, page2, from, lru) { | ||
| 1674 | cond_resched(); | ||
| 1675 | |||
| 1676 | rc = unmap_and_copy(get_new_page, put_new_page, private, page, pass > 2, mode); | ||
| 1677 | TRACE_TASK(current, "rc = %d\n", rc); | ||
| 1678 | |||
| 1679 | switch(rc) { | ||
| 1680 | case -ENOMEM: | ||
| 1681 | goto out; | ||
| 1682 | case -EAGAIN: | ||
| 1683 | retry++; | ||
| 1684 | break; | ||
| 1685 | case MIGRATEPAGE_SUCCESS: | ||
| 1686 | nr_succeeded++; | ||
| 1687 | break; | ||
| 1688 | default: | ||
| 1689 | /* | ||
| 1690 | * Permanent failure (-EBUSY, -ENOSYS, etc.): | ||
| 1691 | * unlike -EAGAIN case, the failed page is | ||
| 1692 | * removed from migration page list and not | ||
| 1693 | * retried in the next outer loop. | ||
| 1694 | */ | ||
| 1695 | nr_failed++; | ||
| 1696 | break; | ||
| 1697 | } | ||
| 1698 | } | ||
| 1699 | } | ||
| 1700 | rc = nr_failed + retry; | ||
| 1701 | out: | ||
| 1702 | if (nr_succeeded) | ||
| 1703 | count_vm_events(PGMIGRATE_SUCCESS, nr_succeeded); | ||
| 1704 | if (nr_failed) | ||
| 1705 | count_vm_events(PGMIGRATE_FAIL, nr_failed); | ||
| 1706 | trace_mm_migrate_pages(nr_succeeded, nr_failed, mode, reason); | ||
| 1707 | |||
| 1708 | if (!swapwrite) | ||
| 1709 | current->flags &= ~PF_SWAPWRITE; | ||
| 1710 | |||
| 1711 | return rc; | ||
| 1712 | } | ||
| 1713 | |||
| 1162 | #ifdef CONFIG_NUMA | 1714 | #ifdef CONFIG_NUMA |
| 1163 | /* | 1715 | /* |
| 1164 | * Move a list of individual pages | 1716 | * Move a list of individual pages |
