]>
Commit | Line | Data |
---|---|---|
df8f408d GKH |
1 | From 6a469e4665bc158599de55d64388861d0a9f10f4 Mon Sep 17 00:00:00 2001 |
2 | From: Jack Steiner <steiner@sgi.com> | |
3 | Date: Tue, 20 Sep 2011 13:55:04 -0700 | |
4 | Subject: x86: uv2: Workaround for UV2 Hub bug (system global address format) | |
5 | ||
6 | From: Jack Steiner <steiner@sgi.com> | |
7 | ||
8 | commit 6a469e4665bc158599de55d64388861d0a9f10f4 upstream. | |
9 | ||
10 | This is a workaround for a UV2 hub bug that affects the format of system | |
11 | global addresses. | |
12 | ||
13 | The GRU API for UV2 was inadvertently broken by a hardware change. The | |
14 | format of the physical address used for TLB dropins and for addresses used | |
15 | with instructions running in unmapped mode has changed. This change was | |
16 | not documented and became apparent only when diags failed running on | |
17 | system simulators. | |
18 | ||
19 | For UV1, TLB and GRU instruction physical addresses are identical to | |
20 | socket physical addresses (although high NASID bits must be OR'ed into the | |
21 | address). | |
22 | ||
23 | For UV2, socket physical addresses need to be converted. The NODE portion | |
24 | of the physical address needs to be shifted so that the low bit is in bit | |
25 | 39 or bit 40, depending on an MMR value. | |
26 | ||
27 | It is not yet clear if this bug will be fixed in a silicon respin. If it | |
28 | is fixed, the hub revision will be incremented & the workaround disabled. | |
29 | ||
30 | Signed-off-by: Jack Steiner <steiner@sgi.com> | |
31 | Cc: Ingo Molnar <mingo@elte.hu> | |
32 | Cc: "H. Peter Anvin" <hpa@zytor.com> | |
33 | Signed-off-by: Andrew Morton <akpm@linux-foundation.org> | |
34 | Signed-off-by: Thomas Gleixner <tglx@linutronix.de> | |
35 | Signed-off-by: Greg Kroah-Hartman <gregkh@suse.de> | |
36 | ||
37 | --- | |
38 | arch/x86/include/asm/uv/uv_bau.h | 1 + | |
39 | arch/x86/include/asm/uv/uv_hub.h | 37 ++++++++++++++++++++++++++++++++++--- | |
40 | arch/x86/kernel/apic/x2apic_uv_x.c | 7 +++++-- | |
41 | arch/x86/platform/uv/tlb_uv.c | 17 ++++++----------- | |
42 | 4 files changed, 46 insertions(+), 16 deletions(-) | |
43 | ||
44 | --- a/arch/x86/include/asm/uv/uv_bau.h | |
45 | +++ b/arch/x86/include/asm/uv/uv_bau.h | |
46 | @@ -55,6 +55,7 @@ | |
47 | #define UV_BAU_TUNABLES_DIR "sgi_uv" | |
48 | #define UV_BAU_TUNABLES_FILE "bau_tunables" | |
49 | #define WHITESPACE " \t\n" | |
50 | +#define uv_mmask ((1UL << uv_hub_info->m_val) - 1) | |
51 | #define uv_physnodeaddr(x) ((__pa((unsigned long)(x)) & uv_mmask)) | |
52 | #define cpubit_isset(cpu, bau_local_cpumask) \ | |
53 | test_bit((cpu), (bau_local_cpumask).bits) | |
54 | --- a/arch/x86/include/asm/uv/uv_hub.h | |
55 | +++ b/arch/x86/include/asm/uv/uv_hub.h | |
56 | @@ -46,6 +46,13 @@ | |
57 | * PNODE - the low N bits of the GNODE. The PNODE is the most useful variant | |
58 | * of the nasid for socket usage. | |
59 | * | |
60 | + * GPA - (global physical address) a socket physical address converted | |
61 | + * so that it can be used by the GRU as a global address. Socket | |
62 | + * physical addresses 1) need additional NASID (node) bits added | |
63 | + * to the high end of the address, and 2) unaliased if the | |
64 | + * partition does not have a physical address 0. In addition, on | |
65 | + * UV2 rev 1, GPAs need the gnode left shifted to bits 39 or 40. | |
66 | + * | |
67 | * | |
68 | * NumaLink Global Physical Address Format: | |
69 | * +--------------------------------+---------------------+ | |
70 | @@ -141,6 +148,8 @@ struct uv_hub_info_s { | |
71 | unsigned int gnode_extra; | |
72 | unsigned char hub_revision; | |
73 | unsigned char apic_pnode_shift; | |
74 | + unsigned char m_shift; | |
75 | + unsigned char n_lshift; | |
76 | unsigned long gnode_upper; | |
77 | unsigned long lowmem_remap_top; | |
78 | unsigned long lowmem_remap_base; | |
79 | @@ -177,6 +186,16 @@ static inline int is_uv2_hub(void) | |
80 | return uv_hub_info->hub_revision >= UV2_HUB_REVISION_BASE; | |
81 | } | |
82 | ||
83 | +static inline int is_uv2_1_hub(void) | |
84 | +{ | |
85 | + return uv_hub_info->hub_revision == UV2_HUB_REVISION_BASE; | |
86 | +} | |
87 | + | |
88 | +static inline int is_uv2_2_hub(void) | |
89 | +{ | |
90 | + return uv_hub_info->hub_revision == UV2_HUB_REVISION_BASE + 1; | |
91 | +} | |
92 | + | |
93 | union uvh_apicid { | |
94 | unsigned long v; | |
95 | struct uvh_apicid_s { | |
96 | @@ -276,7 +295,10 @@ static inline unsigned long uv_soc_phys_ | |
97 | { | |
98 | if (paddr < uv_hub_info->lowmem_remap_top) | |
99 | paddr |= uv_hub_info->lowmem_remap_base; | |
100 | - return paddr | uv_hub_info->gnode_upper; | |
101 | + paddr |= uv_hub_info->gnode_upper; | |
102 | + paddr = ((paddr << uv_hub_info->m_shift) >> uv_hub_info->m_shift) | | |
103 | + ((paddr >> uv_hub_info->m_val) << uv_hub_info->n_lshift); | |
104 | + return paddr; | |
105 | } | |
106 | ||
107 | ||
108 | @@ -300,16 +322,19 @@ static inline unsigned long uv_gpa_to_so | |
109 | unsigned long remap_base = uv_hub_info->lowmem_remap_base; | |
110 | unsigned long remap_top = uv_hub_info->lowmem_remap_top; | |
111 | ||
112 | + gpa = ((gpa << uv_hub_info->m_shift) >> uv_hub_info->m_shift) | | |
113 | + ((gpa >> uv_hub_info->n_lshift) << uv_hub_info->m_val); | |
114 | + gpa = gpa & uv_hub_info->gpa_mask; | |
115 | if (paddr >= remap_base && paddr < remap_base + remap_top) | |
116 | paddr -= remap_base; | |
117 | return paddr; | |
118 | } | |
119 | ||
120 | ||
121 | -/* gnode -> pnode */ | |
122 | +/* gpa -> pnode */ | |
123 | static inline unsigned long uv_gpa_to_gnode(unsigned long gpa) | |
124 | { | |
125 | - return gpa >> uv_hub_info->m_val; | |
126 | + return gpa >> uv_hub_info->n_lshift; | |
127 | } | |
128 | ||
129 | /* gpa -> pnode */ | |
130 | @@ -320,6 +345,12 @@ static inline int uv_gpa_to_pnode(unsign | |
131 | return uv_gpa_to_gnode(gpa) & n_mask; | |
132 | } | |
133 | ||
134 | +/* gpa -> node offset*/ | |
135 | +static inline unsigned long uv_gpa_to_offset(unsigned long gpa) | |
136 | +{ | |
137 | + return (gpa << uv_hub_info->m_shift) >> uv_hub_info->m_shift; | |
138 | +} | |
139 | + | |
140 | /* pnode, offset --> socket virtual */ | |
141 | static inline void *uv_pnode_offset_to_vaddr(int pnode, unsigned long offset) | |
142 | { | |
143 | --- a/arch/x86/kernel/apic/x2apic_uv_x.c | |
144 | +++ b/arch/x86/kernel/apic/x2apic_uv_x.c | |
145 | @@ -832,6 +832,10 @@ void __init uv_system_init(void) | |
146 | uv_cpu_hub_info(cpu)->apic_pnode_shift = uvh_apicid.s.pnode_shift; | |
147 | uv_cpu_hub_info(cpu)->hub_revision = uv_hub_info->hub_revision; | |
148 | ||
149 | + uv_cpu_hub_info(cpu)->m_shift = 64 - m_val; | |
150 | + uv_cpu_hub_info(cpu)->n_lshift = is_uv2_1_hub() ? | |
151 | + (m_val == 40 ? 40 : 39) : m_val; | |
152 | + | |
153 | pnode = uv_apicid_to_pnode(apicid); | |
154 | blade = boot_pnode_to_blade(pnode); | |
155 | lcpu = uv_blade_info[blade].nr_possible_cpus; | |
156 | @@ -862,8 +866,7 @@ void __init uv_system_init(void) | |
157 | if (uv_node_to_blade[nid] >= 0) | |
158 | continue; | |
159 | paddr = node_start_pfn(nid) << PAGE_SHIFT; | |
160 | - paddr = uv_soc_phys_ram_to_gpa(paddr); | |
161 | - pnode = (paddr >> m_val) & pnode_mask; | |
162 | + pnode = uv_gpa_to_pnode(uv_soc_phys_ram_to_gpa(paddr)); | |
163 | blade = boot_pnode_to_blade(pnode); | |
164 | uv_node_to_blade[nid] = blade; | |
165 | } | |
166 | --- a/arch/x86/platform/uv/tlb_uv.c | |
167 | +++ b/arch/x86/platform/uv/tlb_uv.c | |
168 | @@ -115,9 +115,6 @@ early_param("nobau", setup_nobau); | |
169 | ||
170 | /* base pnode in this partition */ | |
171 | static int uv_base_pnode __read_mostly; | |
172 | -/* position of pnode (which is nasid>>1): */ | |
173 | -static int uv_nshift __read_mostly; | |
174 | -static unsigned long uv_mmask __read_mostly; | |
175 | ||
176 | static DEFINE_PER_CPU(struct ptc_stats, ptcstats); | |
177 | static DEFINE_PER_CPU(struct bau_control, bau_control); | |
178 | @@ -1435,7 +1432,7 @@ static void activation_descriptor_init(i | |
179 | { | |
180 | int i; | |
181 | int cpu; | |
182 | - unsigned long pa; | |
183 | + unsigned long gpa; | |
184 | unsigned long m; | |
185 | unsigned long n; | |
186 | size_t dsize; | |
187 | @@ -1451,9 +1448,9 @@ static void activation_descriptor_init(i | |
188 | bau_desc = kmalloc_node(dsize, GFP_KERNEL, node); | |
189 | BUG_ON(!bau_desc); | |
190 | ||
191 | - pa = uv_gpa(bau_desc); /* need the real nasid*/ | |
192 | - n = pa >> uv_nshift; | |
193 | - m = pa & uv_mmask; | |
194 | + gpa = uv_gpa(bau_desc); | |
195 | + n = uv_gpa_to_gnode(gpa); | |
196 | + m = uv_gpa_to_offset(gpa); | |
197 | ||
198 | /* the 14-bit pnode */ | |
199 | write_mmr_descriptor_base(pnode, (n << UV_DESC_PSHIFT | m)); | |
200 | @@ -1525,9 +1522,9 @@ static void pq_init(int node, int pnode) | |
201 | bcp->queue_last = pqp + (DEST_Q_SIZE - 1); | |
202 | } | |
203 | /* | |
204 | - * need the pnode of where the memory was really allocated | |
205 | + * need the gnode of where the memory was really allocated | |
206 | */ | |
207 | - pn = uv_gpa(pqp) >> uv_nshift; | |
208 | + pn = uv_gpa_to_gnode(uv_gpa(pqp)); | |
209 | first = uv_physnodeaddr(pqp); | |
210 | pn_first = ((unsigned long)pn << UV_PAYLOADQ_PNODE_SHIFT) | first; | |
211 | last = uv_physnodeaddr(pqp + (DEST_Q_SIZE - 1)); | |
212 | @@ -1837,8 +1834,6 @@ static int __init uv_bau_init(void) | |
213 | zalloc_cpumask_var_node(mask, GFP_KERNEL, cpu_to_node(cur_cpu)); | |
214 | } | |
215 | ||
216 | - uv_nshift = uv_hub_info->m_val; | |
217 | - uv_mmask = (1UL << uv_hub_info->m_val) - 1; | |
218 | nuvhubs = uv_num_possible_blades(); | |
219 | spin_lock_init(&disable_lock); | |
220 | congested_cycles = usec_2_cycles(congested_respns_us); |