| From 6a469e4665bc158599de55d64388861d0a9f10f4 Mon Sep 17 00:00:00 2001 |
| From: Jack Steiner <steiner@sgi.com> |
| Date: Tue, 20 Sep 2011 13:55:04 -0700 |
| Subject: x86: uv2: Workaround for UV2 Hub bug (system global address format) |
| |
| From: Jack Steiner <steiner@sgi.com> |
| |
| commit 6a469e4665bc158599de55d64388861d0a9f10f4 upstream. |
| |
| This is a workaround for a UV2 hub bug that affects the format of system |
| global addresses. |
| |
| The GRU API for UV2 was inadvertently broken by a hardware change. The |
| format of the physical address used for TLB dropins and for addresses used |
| with instructions running in unmapped mode has changed. This change was |
| not documented and became apparent only when diags failed running on |
| system simulators. |
| |
| For UV1, TLB and GRU instruction physical addresses are identical to |
| socket physical addresses (although high NASID bits must be OR'ed into the |
| address). |
| |
| For UV2, socket physical addresses need to be converted. The NODE portion |
| of the physical address needs to be shifted so that the low bit is in bit |
| 39 or bit 40, depending on an MMR value. |
| |
| It is not yet clear if this bug will be fixed in a silicon respin. If it |
| is fixed, the hub revision will be incremented & the workaround disabled. |
| |
| Signed-off-by: Jack Steiner <steiner@sgi.com> |
| Cc: Ingo Molnar <mingo@elte.hu> |
| Cc: "H. Peter Anvin" <hpa@zytor.com> |
| Signed-off-by: Andrew Morton <akpm@linux-foundation.org> |
| Signed-off-by: Thomas Gleixner <tglx@linutronix.de> |
| Signed-off-by: Greg Kroah-Hartman <gregkh@suse.de> |
| |
| --- |
| arch/x86/include/asm/uv/uv_bau.h | 1 + |
| arch/x86/include/asm/uv/uv_hub.h | 37 ++++++++++++++++++++++++++++++++++--- |
| arch/x86/kernel/apic/x2apic_uv_x.c | 7 +++++-- |
| arch/x86/platform/uv/tlb_uv.c | 17 ++++++----------- |
| 4 files changed, 46 insertions(+), 16 deletions(-) |
| |
| --- a/arch/x86/include/asm/uv/uv_bau.h |
| +++ b/arch/x86/include/asm/uv/uv_bau.h |
| @@ -55,6 +55,7 @@ |
| #define UV_BAU_TUNABLES_DIR "sgi_uv" |
| #define UV_BAU_TUNABLES_FILE "bau_tunables" |
| #define WHITESPACE " \t\n" |
| +#define uv_mmask ((1UL << uv_hub_info->m_val) - 1) |
| #define uv_physnodeaddr(x) ((__pa((unsigned long)(x)) & uv_mmask)) |
| #define cpubit_isset(cpu, bau_local_cpumask) \ |
| test_bit((cpu), (bau_local_cpumask).bits) |
| --- a/arch/x86/include/asm/uv/uv_hub.h |
| +++ b/arch/x86/include/asm/uv/uv_hub.h |
| @@ -46,6 +46,13 @@ |
| * PNODE - the low N bits of the GNODE. The PNODE is the most useful variant |
| * of the nasid for socket usage. |
| * |
| + * GPA - (global physical address) a socket physical address converted |
| + * so that it can be used by the GRU as a global address. Socket |
| + * physical addresses 1) need additional NASID (node) bits added |
| + * to the high end of the address, and 2) unaliased if the |
| + * partition does not have a physical address 0. In addition, on |
| + * UV2 rev 1, GPAs need the gnode left shifted to bits 39 or 40. |
| + * |
| * |
| * NumaLink Global Physical Address Format: |
| * +--------------------------------+---------------------+ |
| @@ -141,6 +148,8 @@ struct uv_hub_info_s { |
| unsigned int gnode_extra; |
| unsigned char hub_revision; |
| unsigned char apic_pnode_shift; |
| + unsigned char m_shift; |
| + unsigned char n_lshift; |
| unsigned long gnode_upper; |
| unsigned long lowmem_remap_top; |
| unsigned long lowmem_remap_base; |
| @@ -177,6 +186,16 @@ static inline int is_uv2_hub(void) |
| return uv_hub_info->hub_revision >= UV2_HUB_REVISION_BASE; |
| } |
| |
| +static inline int is_uv2_1_hub(void) |
| +{ |
| + return uv_hub_info->hub_revision == UV2_HUB_REVISION_BASE; |
| +} |
| + |
| +static inline int is_uv2_2_hub(void) |
| +{ |
| + return uv_hub_info->hub_revision == UV2_HUB_REVISION_BASE + 1; |
| +} |
| + |
| union uvh_apicid { |
| unsigned long v; |
| struct uvh_apicid_s { |
| @@ -276,7 +295,10 @@ static inline unsigned long uv_soc_phys_ |
| { |
| if (paddr < uv_hub_info->lowmem_remap_top) |
| paddr |= uv_hub_info->lowmem_remap_base; |
| - return paddr | uv_hub_info->gnode_upper; |
| + paddr |= uv_hub_info->gnode_upper; |
| + paddr = ((paddr << uv_hub_info->m_shift) >> uv_hub_info->m_shift) | |
| + ((paddr >> uv_hub_info->m_val) << uv_hub_info->n_lshift); |
| + return paddr; |
| } |
| |
| |
| @@ -300,16 +322,19 @@ static inline unsigned long uv_gpa_to_so |
| unsigned long remap_base = uv_hub_info->lowmem_remap_base; |
| unsigned long remap_top = uv_hub_info->lowmem_remap_top; |
| |
| + gpa = ((gpa << uv_hub_info->m_shift) >> uv_hub_info->m_shift) | |
| + ((gpa >> uv_hub_info->n_lshift) << uv_hub_info->m_val); |
| + gpa = gpa & uv_hub_info->gpa_mask; |
| if (paddr >= remap_base && paddr < remap_base + remap_top) |
| paddr -= remap_base; |
| return paddr; |
| } |
| |
| |
| -/* gnode -> pnode */ |
| +/* gpa -> pnode */ |
| static inline unsigned long uv_gpa_to_gnode(unsigned long gpa) |
| { |
| - return gpa >> uv_hub_info->m_val; |
| + return gpa >> uv_hub_info->n_lshift; |
| } |
| |
| /* gpa -> pnode */ |
| @@ -320,6 +345,12 @@ static inline int uv_gpa_to_pnode(unsign |
| return uv_gpa_to_gnode(gpa) & n_mask; |
| } |
| |
| +/* gpa -> node offset*/ |
| +static inline unsigned long uv_gpa_to_offset(unsigned long gpa) |
| +{ |
| + return (gpa << uv_hub_info->m_shift) >> uv_hub_info->m_shift; |
| +} |
| + |
| /* pnode, offset --> socket virtual */ |
| static inline void *uv_pnode_offset_to_vaddr(int pnode, unsigned long offset) |
| { |
| --- a/arch/x86/kernel/apic/x2apic_uv_x.c |
| +++ b/arch/x86/kernel/apic/x2apic_uv_x.c |
| @@ -832,6 +832,10 @@ void __init uv_system_init(void) |
| uv_cpu_hub_info(cpu)->apic_pnode_shift = uvh_apicid.s.pnode_shift; |
| uv_cpu_hub_info(cpu)->hub_revision = uv_hub_info->hub_revision; |
| |
| + uv_cpu_hub_info(cpu)->m_shift = 64 - m_val; |
| + uv_cpu_hub_info(cpu)->n_lshift = is_uv2_1_hub() ? |
| + (m_val == 40 ? 40 : 39) : m_val; |
| + |
| pnode = uv_apicid_to_pnode(apicid); |
| blade = boot_pnode_to_blade(pnode); |
| lcpu = uv_blade_info[blade].nr_possible_cpus; |
| @@ -862,8 +866,7 @@ void __init uv_system_init(void) |
| if (uv_node_to_blade[nid] >= 0) |
| continue; |
| paddr = node_start_pfn(nid) << PAGE_SHIFT; |
| - paddr = uv_soc_phys_ram_to_gpa(paddr); |
| - pnode = (paddr >> m_val) & pnode_mask; |
| + pnode = uv_gpa_to_pnode(uv_soc_phys_ram_to_gpa(paddr)); |
| blade = boot_pnode_to_blade(pnode); |
| uv_node_to_blade[nid] = blade; |
| } |
| --- a/arch/x86/platform/uv/tlb_uv.c |
| +++ b/arch/x86/platform/uv/tlb_uv.c |
| @@ -115,9 +115,6 @@ early_param("nobau", setup_nobau); |
| |
| /* base pnode in this partition */ |
| static int uv_base_pnode __read_mostly; |
| -/* position of pnode (which is nasid>>1): */ |
| -static int uv_nshift __read_mostly; |
| -static unsigned long uv_mmask __read_mostly; |
| |
| static DEFINE_PER_CPU(struct ptc_stats, ptcstats); |
| static DEFINE_PER_CPU(struct bau_control, bau_control); |
| @@ -1435,7 +1432,7 @@ static void activation_descriptor_init(i |
| { |
| int i; |
| int cpu; |
| - unsigned long pa; |
| + unsigned long gpa; |
| unsigned long m; |
| unsigned long n; |
| size_t dsize; |
| @@ -1451,9 +1448,9 @@ static void activation_descriptor_init(i |
| bau_desc = kmalloc_node(dsize, GFP_KERNEL, node); |
| BUG_ON(!bau_desc); |
| |
| - pa = uv_gpa(bau_desc); /* need the real nasid*/ |
| - n = pa >> uv_nshift; |
| - m = pa & uv_mmask; |
| + gpa = uv_gpa(bau_desc); |
| + n = uv_gpa_to_gnode(gpa); |
| + m = uv_gpa_to_offset(gpa); |
| |
| /* the 14-bit pnode */ |
| write_mmr_descriptor_base(pnode, (n << UV_DESC_PSHIFT | m)); |
| @@ -1525,9 +1522,9 @@ static void pq_init(int node, int pnode) |
| bcp->queue_last = pqp + (DEST_Q_SIZE - 1); |
| } |
| /* |
| - * need the pnode of where the memory was really allocated |
| + * need the gnode of where the memory was really allocated |
| */ |
| - pn = uv_gpa(pqp) >> uv_nshift; |
| + pn = uv_gpa_to_gnode(uv_gpa(pqp)); |
| first = uv_physnodeaddr(pqp); |
| pn_first = ((unsigned long)pn << UV_PAYLOADQ_PNODE_SHIFT) | first; |
| last = uv_physnodeaddr(pqp + (DEST_Q_SIZE - 1)); |
| @@ -1837,8 +1834,6 @@ static int __init uv_bau_init(void) |
| zalloc_cpumask_var_node(mask, GFP_KERNEL, cpu_to_node(cur_cpu)); |
| } |
| |
| - uv_nshift = uv_hub_info->m_val; |
| - uv_mmask = (1UL << uv_hub_info->m_val) - 1; |
| nuvhubs = uv_num_possible_blades(); |
| spin_lock_init(&disable_lock); |
| congested_cycles = usec_2_cycles(congested_respns_us); |