aboutsummaryrefslogtreecommitdiffstats
path: root/arch/sparc/mm
diff options
context:
space:
mode:
authorDavid Miller <davem@davemloft.net>2012-10-08 19:34:22 -0400
committerLinus Torvalds <torvalds@linux-foundation.org>2012-10-09 03:23:05 -0400
commitc460bec78d9257a54bcc5f9d5fadf89f8c70dbd1 (patch)
tree5928e8f429f1f792f0972811539af0dfe1007ac1 /arch/sparc/mm
parent56a70b8c6acc73f5d9ec383d840909dd9e63c865 (diff)
sparc64: Eliminate PTE table memory wastage.
We've split up the PTE tables so that they take up half a page instead of a full page. This is in order to facilitate transparent huge page support, which works much better if our PMDs cover 4MB instead of 8MB. What we do is have a one-behind cache for PTE table allocations in the mm struct. This logic triggers only on allocations. For example, we don't try to keep track of free'd up page table blocks in the style that the s390 port does. There were only two slightly annoying aspects to this change: 1) Changing pgtable_t to be a "pte_t *". There's all of this special logic in the TLB free paths that needed adjustments, as did the PMD populate interfaces. 2) init_new_context() needs to zap the pointer, since the mm struct just gets copied from the parent on fork. Signed-off-by: David S. Miller <davem@davemloft.net> Cc: Andrea Arcangeli <aarcange@redhat.com> Cc: Johannes Weiner <hannes@cmpxchg.org> Cc: Gerald Schaefer <gerald.schaefer@de.ibm.com> Signed-off-by: Andrew Morton <akpm@linux-foundation.org> Signed-off-by: Linus Torvalds <torvalds@linux-foundation.org>
Diffstat (limited to 'arch/sparc/mm')
-rw-r--r--arch/sparc/mm/init_64.c101
-rw-r--r--arch/sparc/mm/tsb.c9
2 files changed, 110 insertions, 0 deletions
diff --git a/arch/sparc/mm/init_64.c b/arch/sparc/mm/init_64.c
index 809eecf6c797..12ef4ea60c88 100644
--- a/arch/sparc/mm/init_64.c
+++ b/arch/sparc/mm/init_64.c
@@ -2467,3 +2467,104 @@ void __flush_tlb_all(void)
2467 __asm__ __volatile__("wrpr %0, 0, %%pstate" 2467 __asm__ __volatile__("wrpr %0, 0, %%pstate"
2468 : : "r" (pstate)); 2468 : : "r" (pstate));
2469} 2469}
2470
2471static pte_t *get_from_cache(struct mm_struct *mm)
2472{
2473 struct page *page;
2474 pte_t *ret;
2475
2476 spin_lock(&mm->page_table_lock);
2477 page = mm->context.pgtable_page;
2478 ret = NULL;
2479 if (page) {
2480 void *p = page_address(page);
2481
2482 mm->context.pgtable_page = NULL;
2483
2484 ret = (pte_t *) (p + (PAGE_SIZE / 2));
2485 }
2486 spin_unlock(&mm->page_table_lock);
2487
2488 return ret;
2489}
2490
2491static struct page *__alloc_for_cache(struct mm_struct *mm)
2492{
2493 struct page *page = alloc_page(GFP_KERNEL | __GFP_NOTRACK |
2494 __GFP_REPEAT | __GFP_ZERO);
2495
2496 if (page) {
2497 spin_lock(&mm->page_table_lock);
2498 if (!mm->context.pgtable_page) {
2499 atomic_set(&page->_count, 2);
2500 mm->context.pgtable_page = page;
2501 }
2502 spin_unlock(&mm->page_table_lock);
2503 }
2504 return page;
2505}
2506
2507pte_t *pte_alloc_one_kernel(struct mm_struct *mm,
2508 unsigned long address)
2509{
2510 struct page *page;
2511 pte_t *pte;
2512
2513 pte = get_from_cache(mm);
2514 if (pte)
2515 return pte;
2516
2517 page = __alloc_for_cache(mm);
2518 if (page)
2519 pte = (pte_t *) page_address(page);
2520
2521 return pte;
2522}
2523
2524pgtable_t pte_alloc_one(struct mm_struct *mm,
2525 unsigned long address)
2526{
2527 struct page *page;
2528 pte_t *pte;
2529
2530 pte = get_from_cache(mm);
2531 if (pte)
2532 return pte;
2533
2534 page = __alloc_for_cache(mm);
2535 if (page) {
2536 pgtable_page_ctor(page);
2537 pte = (pte_t *) page_address(page);
2538 }
2539
2540 return pte;
2541}
2542
2543void pte_free_kernel(struct mm_struct *mm, pte_t *pte)
2544{
2545 struct page *page = virt_to_page(pte);
2546 if (put_page_testzero(page))
2547 free_hot_cold_page(page, 0);
2548}
2549
2550static void __pte_free(pgtable_t pte)
2551{
2552 struct page *page = virt_to_page(pte);
2553 if (put_page_testzero(page)) {
2554 pgtable_page_dtor(page);
2555 free_hot_cold_page(page, 0);
2556 }
2557}
2558
2559void pte_free(struct mm_struct *mm, pgtable_t pte)
2560{
2561 __pte_free(pte);
2562}
2563
2564void pgtable_free(void *table, bool is_page)
2565{
2566 if (is_page)
2567 __pte_free(table);
2568 else
2569 kmem_cache_free(pgtable_cache, table);
2570}
diff --git a/arch/sparc/mm/tsb.c b/arch/sparc/mm/tsb.c
index 70e50ea2a5b6..a35ee832baf3 100644
--- a/arch/sparc/mm/tsb.c
+++ b/arch/sparc/mm/tsb.c
@@ -445,6 +445,8 @@ int init_new_context(struct task_struct *tsk, struct mm_struct *mm)
445 mm->context.huge_pte_count = 0; 445 mm->context.huge_pte_count = 0;
446#endif 446#endif
447 447
448 mm->context.pgtable_page = NULL;
449
448 /* copy_mm() copies over the parent's mm_struct before calling 450 /* copy_mm() copies over the parent's mm_struct before calling
449 * us, so we need to zero out the TSB pointer or else tsb_grow() 451 * us, so we need to zero out the TSB pointer or else tsb_grow()
450 * will be confused and think there is an older TSB to free up. 452 * will be confused and think there is an older TSB to free up.
@@ -483,10 +485,17 @@ static void tsb_destroy_one(struct tsb_config *tp)
483void destroy_context(struct mm_struct *mm) 485void destroy_context(struct mm_struct *mm)
484{ 486{
485 unsigned long flags, i; 487 unsigned long flags, i;
488 struct page *page;
486 489
487 for (i = 0; i < MM_NUM_TSBS; i++) 490 for (i = 0; i < MM_NUM_TSBS; i++)
488 tsb_destroy_one(&mm->context.tsb_block[i]); 491 tsb_destroy_one(&mm->context.tsb_block[i]);
489 492
493 page = mm->context.pgtable_page;
494 if (page && put_page_testzero(page)) {
495 pgtable_page_dtor(page);
496 free_hot_cold_page(page, 0);
497 }
498
490 spin_lock_irqsave(&ctx_alloc_lock, flags); 499 spin_lock_irqsave(&ctx_alloc_lock, flags);
491 500
492 if (CTX_VALID(mm->context)) { 501 if (CTX_VALID(mm->context)) {