This commit was manufactured by cvs2svn to create branch 'vserver'.
[linux-2.6.git] / arch / sparc64 / mm / tlb.c
1 /* arch/sparc64/mm/tlb.c
2  *
3  * Copyright (C) 2004 David S. Miller <davem@redhat.com>
4  */
5
6 #include <linux/kernel.h>
7 #include <linux/init.h>
8 #include <linux/percpu.h>
9 #include <linux/mm.h>
10 #include <linux/swap.h>
11
12 #include <asm/pgtable.h>
13 #include <asm/pgalloc.h>
14 #include <asm/tlbflush.h>
15 #include <asm/cacheflush.h>
16 #include <asm/mmu_context.h>
17 #include <asm/tlb.h>
18
19 /* Heavily inspired by the ppc64 code.  */
20
21 DEFINE_PER_CPU(struct mmu_gather, mmu_gathers) =
22         { NULL, 0, 0, 0, 0, 0, { 0 }, { NULL }, };
23
24 void flush_tlb_pending(void)
25 {
26         struct mmu_gather *mp = &__get_cpu_var(mmu_gathers);
27
28         if (mp->tlb_nr) {
29                 unsigned long context = mp->mm->context;
30
31                 if (CTX_VALID(context)) {
32 #ifdef CONFIG_SMP
33                         smp_flush_tlb_pending(mp->mm, mp->tlb_nr,
34                                               &mp->vaddrs[0]);
35 #else
36                         __flush_tlb_pending(CTX_HWBITS(context), mp->tlb_nr,
37                                             &mp->vaddrs[0]);
38 #endif
39                 }
40                 mp->tlb_nr = 0;
41         }
42 }
43
44 void tlb_batch_add(pte_t *ptep, pte_t orig)
45 {
46         struct mmu_gather *mp = &__get_cpu_var(mmu_gathers);
47         struct page *ptepage;
48         struct mm_struct *mm;
49         unsigned long vaddr, nr;
50
51         ptepage = virt_to_page(ptep);
52         mm = (struct mm_struct *) ptepage->mapping;
53
54         /* It is more efficient to let flush_tlb_kernel_range()
55          * handle these cases.
56          */
57         if (mm == &init_mm)
58                 return;
59
60         vaddr = ptepage->index +
61                 (((unsigned long)ptep & ~PAGE_MASK) * PTRS_PER_PTE);
62         if (pte_exec(orig))
63                 vaddr |= 0x1UL;
64
65         if (pte_dirty(orig)) {
66                 unsigned long paddr, pfn = pte_pfn(orig);
67                 struct address_space *mapping;
68                 struct page *page;
69
70                 if (!pfn_valid(pfn))
71                         goto no_cache_flush;
72
73                 page = pfn_to_page(pfn);
74                 if (PageReserved(page))
75                         goto no_cache_flush;
76
77                 /* A real file page? */
78                 mapping = page_mapping(page);
79                 if (!mapping)
80                         goto no_cache_flush;
81
82                 paddr = (unsigned long) page_address(page);
83                 if ((paddr ^ vaddr) & (1 << 13))
84                         flush_dcache_page_all(mm, page);
85         }
86
87 no_cache_flush:
88         if (mp->tlb_frozen)
89                 return;
90
91         nr = mp->tlb_nr;
92
93         if (unlikely(nr != 0 && mm != mp->mm)) {
94                 flush_tlb_pending();
95                 nr = 0;
96         }
97
98         if (nr == 0)
99                 mp->mm = mm;
100
101         mp->vaddrs[nr] = vaddr;
102         mp->tlb_nr = ++nr;
103         if (nr >= TLB_BATCH_NR)
104                 flush_tlb_pending();
105 }
106
107 void flush_tlb_pgtables(struct mm_struct *mm, unsigned long start, unsigned long end)
108 {
109         struct mmu_gather *mp = &__get_cpu_var(mmu_gathers);
110         unsigned long nr = mp->tlb_nr;
111         long s = start, e = end, vpte_base;
112
113         if (mp->tlb_frozen)
114                 return;
115
116         /* Nobody should call us with start below VM hole and end above.
117          * See if it is really true.
118          */
119         BUG_ON(s > e);
120
121 #if 0
122         /* Currently free_pgtables guarantees this.  */
123         s &= PMD_MASK;
124         e = (e + PMD_SIZE - 1) & PMD_MASK;
125 #endif
126         vpte_base = (tlb_type == spitfire ?
127                      VPTE_BASE_SPITFIRE :
128                      VPTE_BASE_CHEETAH);
129
130         if (unlikely(nr != 0 && mm != mp->mm)) {
131                 flush_tlb_pending();
132                 nr = 0;
133         }
134
135         if (nr == 0)
136                 mp->mm = mm;
137
138         start = vpte_base + (s >> (PAGE_SHIFT - 3));
139         end = vpte_base + (e >> (PAGE_SHIFT - 3));
140         while (start < end) {
141                 mp->vaddrs[nr] = start;
142                 mp->tlb_nr = ++nr;
143                 if (nr >= TLB_BATCH_NR) {
144                         flush_tlb_pending();
145                         nr = 0;
146                 }
147                 start += PAGE_SIZE;
148         }
149         if (nr)
150                 flush_tlb_pending();
151 }
152
153 unsigned long __ptrs_per_pmd(void)
154 {
155         if (test_thread_flag(TIF_32BIT))
156                 return (1UL << (32 - (PAGE_SHIFT-3) - PAGE_SHIFT));
157         return REAL_PTRS_PER_PMD;
158 }