aboutsummaryrefslogtreecommitdiffstats
path: root/arch
diff options
context:
space:
mode:
authorFenghua Yu <fenghua.yu@intel.com>2008-04-04 14:05:59 -0400
committerTony Luck <tony.luck@intel.com>2008-04-04 14:05:59 -0400
commit2046b94e7c4fce92eb8165c2c36c6478f4927178 (patch)
tree0dbbdf17d64b521f2debcc8677368ceec8805d8c /arch
parente315c121a858499d84dc88c499046b9f10bb61ec (diff)
[IA64] Multiple outstanding ptc.g instruction support
According to SDM2.2, Itanium supports multiple outstanding ptc.g instructions. But current kernel function ia64_global_tlb_purge() uses a spinlock to serialize ptc.g instructions issued by multiple processors. This serialization might have scalability issue on a big SMP machine where many processors could purge TLB in parallel. The patch fixes this problem by issuing multiple ptc.g instructions in ia64_global_tlb_purge(). It also adds support for the "PALO" table to get a platform view of the max number of outstanding ptc.g instructions (which may be different from the processor view found from PAL_VM_SUMMARY). PALO specification can be found at: http://www.dig64.org/home/DIG64_PALO_R1_0.pdf spinaphore implementation by Matthew Wilcox. Signed-off-by: Fenghua Yu <fenghua.yu@intel.com> Signed-off-by: Tony Luck <tony.luck@intel.com>
Diffstat (limited to 'arch')
-rw-r--r--arch/ia64/kernel/efi.c46
-rw-r--r--arch/ia64/kernel/setup.c6
-rw-r--r--arch/ia64/mm/tlb.c125
3 files changed, 160 insertions, 17 deletions
diff --git a/arch/ia64/kernel/efi.c b/arch/ia64/kernel/efi.c
index 728d7247a1a6..003cd09b0732 100644
--- a/arch/ia64/kernel/efi.c
+++ b/arch/ia64/kernel/efi.c
@@ -37,6 +37,7 @@
37#include <asm/pgtable.h> 37#include <asm/pgtable.h>
38#include <asm/processor.h> 38#include <asm/processor.h>
39#include <asm/mca.h> 39#include <asm/mca.h>
40#include <asm/tlbflush.h>
40 41
41#define EFI_DEBUG 0 42#define EFI_DEBUG 0
42 43
@@ -403,6 +404,41 @@ efi_get_pal_addr (void)
403 return NULL; 404 return NULL;
404} 405}
405 406
407
408static u8 __init palo_checksum(u8 *buffer, u32 length)
409{
410 u8 sum = 0;
411 u8 *end = buffer + length;
412
413 while (buffer < end)
414 sum = (u8) (sum + *(buffer++));
415
416 return sum;
417}
418
419/*
420 * Parse and handle PALO table which is published at:
421 * http://www.dig64.org/home/DIG64_PALO_R1_0.pdf
422 */
423static void __init handle_palo(unsigned long palo_phys)
424{
425 struct palo_table *palo = __va(palo_phys);
426 u8 checksum;
427
428 if (strncmp(palo->signature, PALO_SIG, sizeof(PALO_SIG) - 1)) {
429 printk(KERN_INFO "PALO signature incorrect.\n");
430 return;
431 }
432
433 checksum = palo_checksum((u8 *)palo, palo->length);
434 if (checksum) {
435 printk(KERN_INFO "PALO checksum incorrect.\n");
436 return;
437 }
438
439 setup_ptcg_sem(palo->max_tlb_purges, 1);
440}
441
406void 442void
407efi_map_pal_code (void) 443efi_map_pal_code (void)
408{ 444{
@@ -432,6 +468,7 @@ efi_init (void)
432 u64 efi_desc_size; 468 u64 efi_desc_size;
433 char *cp, vendor[100] = "unknown"; 469 char *cp, vendor[100] = "unknown";
434 int i; 470 int i;
471 unsigned long palo_phys;
435 472
436 /* 473 /*
437 * It's too early to be able to use the standard kernel command line 474 * It's too early to be able to use the standard kernel command line
@@ -496,6 +533,8 @@ efi_init (void)
496 efi.hcdp = EFI_INVALID_TABLE_ADDR; 533 efi.hcdp = EFI_INVALID_TABLE_ADDR;
497 efi.uga = EFI_INVALID_TABLE_ADDR; 534 efi.uga = EFI_INVALID_TABLE_ADDR;
498 535
536 palo_phys = EFI_INVALID_TABLE_ADDR;
537
499 for (i = 0; i < (int) efi.systab->nr_tables; i++) { 538 for (i = 0; i < (int) efi.systab->nr_tables; i++) {
500 if (efi_guidcmp(config_tables[i].guid, MPS_TABLE_GUID) == 0) { 539 if (efi_guidcmp(config_tables[i].guid, MPS_TABLE_GUID) == 0) {
501 efi.mps = config_tables[i].table; 540 efi.mps = config_tables[i].table;
@@ -515,10 +554,17 @@ efi_init (void)
515 } else if (efi_guidcmp(config_tables[i].guid, HCDP_TABLE_GUID) == 0) { 554 } else if (efi_guidcmp(config_tables[i].guid, HCDP_TABLE_GUID) == 0) {
516 efi.hcdp = config_tables[i].table; 555 efi.hcdp = config_tables[i].table;
517 printk(" HCDP=0x%lx", config_tables[i].table); 556 printk(" HCDP=0x%lx", config_tables[i].table);
557 } else if (efi_guidcmp(config_tables[i].guid,
558 PROCESSOR_ABSTRACTION_LAYER_OVERWRITE_GUID) == 0) {
559 palo_phys = config_tables[i].table;
560 printk(" PALO=0x%lx", config_tables[i].table);
518 } 561 }
519 } 562 }
520 printk("\n"); 563 printk("\n");
521 564
565 if (palo_phys != EFI_INVALID_TABLE_ADDR)
566 handle_palo(palo_phys);
567
522 runtime = __va(efi.systab->runtime); 568 runtime = __va(efi.systab->runtime);
523 efi.get_time = phys_get_time; 569 efi.get_time = phys_get_time;
524 efi.set_time = phys_set_time; 570 efi.set_time = phys_set_time;
diff --git a/arch/ia64/kernel/setup.c b/arch/ia64/kernel/setup.c
index 4aa9eaea76c3..1cbd26340d87 100644
--- a/arch/ia64/kernel/setup.c
+++ b/arch/ia64/kernel/setup.c
@@ -59,6 +59,7 @@
59#include <asm/setup.h> 59#include <asm/setup.h>
60#include <asm/smp.h> 60#include <asm/smp.h>
61#include <asm/system.h> 61#include <asm/system.h>
62#include <asm/tlbflush.h>
62#include <asm/unistd.h> 63#include <asm/unistd.h>
63#include <asm/hpsim.h> 64#include <asm/hpsim.h>
64 65
@@ -946,9 +947,10 @@ cpu_init (void)
946#endif 947#endif
947 948
948 /* set ia64_ctx.max_rid to the maximum RID that is supported by all CPUs: */ 949 /* set ia64_ctx.max_rid to the maximum RID that is supported by all CPUs: */
949 if (ia64_pal_vm_summary(NULL, &vmi) == 0) 950 if (ia64_pal_vm_summary(NULL, &vmi) == 0) {
950 max_ctx = (1U << (vmi.pal_vm_info_2_s.rid_size - 3)) - 1; 951 max_ctx = (1U << (vmi.pal_vm_info_2_s.rid_size - 3)) - 1;
951 else { 952 setup_ptcg_sem(vmi.pal_vm_info_2_s.max_purges, 0);
953 } else {
952 printk(KERN_WARNING "cpu_init: PAL VM summary failed, assuming 18 RID bits\n"); 954 printk(KERN_WARNING "cpu_init: PAL VM summary failed, assuming 18 RID bits\n");
953 max_ctx = (1U << 15) - 1; /* use architected minimum */ 955 max_ctx = (1U << 15) - 1; /* use architected minimum */
954 } 956 }
diff --git a/arch/ia64/mm/tlb.c b/arch/ia64/mm/tlb.c
index 655da240d13c..d41d6076ed03 100644
--- a/arch/ia64/mm/tlb.c
+++ b/arch/ia64/mm/tlb.c
@@ -11,6 +11,9 @@
11 * Rohit Seth <rohit.seth@intel.com> 11 * Rohit Seth <rohit.seth@intel.com>
12 * Ken Chen <kenneth.w.chen@intel.com> 12 * Ken Chen <kenneth.w.chen@intel.com>
13 * Christophe de Dinechin <ddd@hp.com>: Avoid ptc.e on memory allocation 13 * Christophe de Dinechin <ddd@hp.com>: Avoid ptc.e on memory allocation
14 * Copyright (C) 2007 Intel Corp
15 * Fenghua Yu <fenghua.yu@intel.com>
16 * Add multiple ptc.g/ptc.ga instruction support in global tlb purge.
14 */ 17 */
15#include <linux/module.h> 18#include <linux/module.h>
16#include <linux/init.h> 19#include <linux/init.h>
@@ -26,6 +29,7 @@
26#include <asm/pal.h> 29#include <asm/pal.h>
27#include <asm/tlbflush.h> 30#include <asm/tlbflush.h>
28#include <asm/dma.h> 31#include <asm/dma.h>
32#include <asm/sal.h>
29 33
30static struct { 34static struct {
31 unsigned long mask; /* mask of supported purge page-sizes */ 35 unsigned long mask; /* mask of supported purge page-sizes */
@@ -84,14 +88,104 @@ wrap_mmu_context (struct mm_struct *mm)
84 local_flush_tlb_all(); 88 local_flush_tlb_all();
85} 89}
86 90
91/*
92 * Implement "spinaphores" ... like counting semaphores, but they
93 * spin instead of sleeping. If there are ever any other users for
94 * this primitive it can be moved up to a spinaphore.h header.
95 */
96struct spinaphore {
97 atomic_t cur;
98};
99
100static inline void spinaphore_init(struct spinaphore *ss, int val)
101{
102 atomic_set(&ss->cur, val);
103}
104
105static inline void down_spin(struct spinaphore *ss)
106{
107 while (unlikely(!atomic_add_unless(&ss->cur, -1, 0)))
108 while (atomic_read(&ss->cur) == 0)
109 cpu_relax();
110}
111
112static inline void up_spin(struct spinaphore *ss)
113{
114 atomic_add(1, &ss->cur);
115}
116
117static struct spinaphore ptcg_sem;
118static u16 nptcg = 1;
119static int need_ptcg_sem = 1;
120static int toolatetochangeptcgsem = 0;
121
122/*
123 * Maximum number of simultaneous ptc.g purges in the system can
124 * be defined by PAL_VM_SUMMARY (in which case we should take
125 * the smallest value for any cpu in the system) or by the PAL
126 * override table (in which case we should ignore the value from
127 * PAL_VM_SUMMARY).
128 *
129 * Complicating the logic here is the fact that num_possible_cpus()
130 * isn't fully setup until we start bringing cpus online.
131 */
132void
133setup_ptcg_sem(int max_purges, int from_palo)
134{
135 static int have_palo;
136 static int firstcpu = 1;
137
138 if (toolatetochangeptcgsem) {
139 BUG_ON(max_purges < nptcg);
140 return;
141 }
142
143 if (from_palo) {
144 have_palo = 1;
145
146 /* In PALO max_purges == 0 really means it! */
147 if (max_purges == 0)
148 panic("Whoa! Platform does not support global TLB purges.\n");
149 nptcg = max_purges;
150 if (nptcg == PALO_MAX_TLB_PURGES) {
151 need_ptcg_sem = 0;
152 return;
153 }
154 goto resetsema;
155 }
156 if (have_palo) {
157 if (nptcg != PALO_MAX_TLB_PURGES)
158 need_ptcg_sem = (num_possible_cpus() > nptcg);
159 return;
160 }
161
162 /* In PAL_VM_SUMMARY max_purges == 0 actually means 1 */
163 if (max_purges == 0) max_purges = 1;
164
165 if (firstcpu) {
166 nptcg = max_purges;
167 firstcpu = 0;
168 }
169 if (max_purges < nptcg)
170 nptcg = max_purges;
171 if (nptcg == PAL_MAX_PURGES) {
172 need_ptcg_sem = 0;
173 return;
174 } else
175 need_ptcg_sem = (num_possible_cpus() > nptcg);
176
177resetsema:
178 spinaphore_init(&ptcg_sem, max_purges);
179}
180
87void 181void
88ia64_global_tlb_purge (struct mm_struct *mm, unsigned long start, 182ia64_global_tlb_purge (struct mm_struct *mm, unsigned long start,
89 unsigned long end, unsigned long nbits) 183 unsigned long end, unsigned long nbits)
90{ 184{
91 static DEFINE_SPINLOCK(ptcg_lock);
92
93 struct mm_struct *active_mm = current->active_mm; 185 struct mm_struct *active_mm = current->active_mm;
94 186
187 toolatetochangeptcgsem = 1;
188
95 if (mm != active_mm) { 189 if (mm != active_mm) {
96 /* Restore region IDs for mm */ 190 /* Restore region IDs for mm */
97 if (mm && active_mm) { 191 if (mm && active_mm) {
@@ -102,19 +196,20 @@ ia64_global_tlb_purge (struct mm_struct *mm, unsigned long start,
102 } 196 }
103 } 197 }
104 198
105 /* HW requires global serialization of ptc.ga. */ 199 if (need_ptcg_sem)
106 spin_lock(&ptcg_lock); 200 down_spin(&ptcg_sem);
107 { 201
108 do { 202 do {
109 /* 203 /*
110 * Flush ALAT entries also. 204 * Flush ALAT entries also.
111 */ 205 */
112 ia64_ptcga(start, (nbits<<2)); 206 ia64_ptcga(start, (nbits << 2));
113 ia64_srlz_i(); 207 ia64_srlz_i();
114 start += (1UL << nbits); 208 start += (1UL << nbits);
115 } while (start < end); 209 } while (start < end);
116 } 210
117 spin_unlock(&ptcg_lock); 211 if (need_ptcg_sem)
212 up_spin(&ptcg_sem);
118 213
119 if (mm != active_mm) { 214 if (mm != active_mm) {
120 activate_context(active_mm); 215 activate_context(active_mm);