Commit 6a469e46 authored by Jack Steiner's avatar Jack Steiner Committed by Thomas Gleixner

x86: uv2: Workaround for UV2 Hub bug (system global address format)

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: default avatarJack Steiner <steiner@sgi.com>
Cc: Ingo Molnar <mingo@elte.hu>
Cc: "H. Peter Anvin" <hpa@zytor.com>
Cc: <stable@kernel.org>
Signed-off-by: default avatarAndrew Morton <akpm@linux-foundation.org>
Signed-off-by: default avatarThomas Gleixner <tglx@linutronix.de>
parent 9d037a77
...@@ -55,6 +55,7 @@ ...@@ -55,6 +55,7 @@
#define UV_BAU_TUNABLES_DIR "sgi_uv" #define UV_BAU_TUNABLES_DIR "sgi_uv"
#define UV_BAU_TUNABLES_FILE "bau_tunables" #define UV_BAU_TUNABLES_FILE "bau_tunables"
#define WHITESPACE " \t\n" #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 uv_physnodeaddr(x) ((__pa((unsigned long)(x)) & uv_mmask))
#define cpubit_isset(cpu, bau_local_cpumask) \ #define cpubit_isset(cpu, bau_local_cpumask) \
test_bit((cpu), (bau_local_cpumask).bits) test_bit((cpu), (bau_local_cpumask).bits)
......
...@@ -46,6 +46,13 @@ ...@@ -46,6 +46,13 @@
* PNODE - the low N bits of the GNODE. The PNODE is the most useful variant * PNODE - the low N bits of the GNODE. The PNODE is the most useful variant
* of the nasid for socket usage. * 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: * NumaLink Global Physical Address Format:
* +--------------------------------+---------------------+ * +--------------------------------+---------------------+
...@@ -141,6 +148,8 @@ struct uv_hub_info_s { ...@@ -141,6 +148,8 @@ struct uv_hub_info_s {
unsigned int gnode_extra; unsigned int gnode_extra;
unsigned char hub_revision; unsigned char hub_revision;
unsigned char apic_pnode_shift; unsigned char apic_pnode_shift;
unsigned char m_shift;
unsigned char n_lshift;
unsigned long gnode_upper; unsigned long gnode_upper;
unsigned long lowmem_remap_top; unsigned long lowmem_remap_top;
unsigned long lowmem_remap_base; unsigned long lowmem_remap_base;
...@@ -177,6 +186,16 @@ static inline int is_uv2_hub(void) ...@@ -177,6 +186,16 @@ static inline int is_uv2_hub(void)
return uv_hub_info->hub_revision >= UV2_HUB_REVISION_BASE; 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 { union uvh_apicid {
unsigned long v; unsigned long v;
struct uvh_apicid_s { struct uvh_apicid_s {
...@@ -276,7 +295,10 @@ static inline unsigned long uv_soc_phys_ram_to_gpa(unsigned long paddr) ...@@ -276,7 +295,10 @@ static inline unsigned long uv_soc_phys_ram_to_gpa(unsigned long paddr)
{ {
if (paddr < uv_hub_info->lowmem_remap_top) if (paddr < uv_hub_info->lowmem_remap_top)
paddr |= uv_hub_info->lowmem_remap_base; 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_soc_phys_ram(unsigned long gpa) ...@@ -300,16 +322,19 @@ static inline unsigned long uv_gpa_to_soc_phys_ram(unsigned long gpa)
unsigned long remap_base = uv_hub_info->lowmem_remap_base; unsigned long remap_base = uv_hub_info->lowmem_remap_base;
unsigned long remap_top = uv_hub_info->lowmem_remap_top; 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) if (paddr >= remap_base && paddr < remap_base + remap_top)
paddr -= remap_base; paddr -= remap_base;
return paddr; return paddr;
} }
/* gnode -> pnode */ /* gpa -> pnode */
static inline unsigned long uv_gpa_to_gnode(unsigned long gpa) 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 */ /* gpa -> pnode */
...@@ -320,6 +345,12 @@ static inline int uv_gpa_to_pnode(unsigned long gpa) ...@@ -320,6 +345,12 @@ static inline int uv_gpa_to_pnode(unsigned long gpa)
return uv_gpa_to_gnode(gpa) & n_mask; 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 */ /* pnode, offset --> socket virtual */
static inline void *uv_pnode_offset_to_vaddr(int pnode, unsigned long offset) static inline void *uv_pnode_offset_to_vaddr(int pnode, unsigned long offset)
{ {
......
...@@ -832,6 +832,10 @@ void __init uv_system_init(void) ...@@ -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)->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)->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); pnode = uv_apicid_to_pnode(apicid);
blade = boot_pnode_to_blade(pnode); blade = boot_pnode_to_blade(pnode);
lcpu = uv_blade_info[blade].nr_possible_cpus; lcpu = uv_blade_info[blade].nr_possible_cpus;
...@@ -862,8 +866,7 @@ void __init uv_system_init(void) ...@@ -862,8 +866,7 @@ void __init uv_system_init(void)
if (uv_node_to_blade[nid] >= 0) if (uv_node_to_blade[nid] >= 0)
continue; continue;
paddr = node_start_pfn(nid) << PAGE_SHIFT; paddr = node_start_pfn(nid) << PAGE_SHIFT;
paddr = uv_soc_phys_ram_to_gpa(paddr); pnode = uv_gpa_to_pnode(uv_soc_phys_ram_to_gpa(paddr));
pnode = (paddr >> m_val) & pnode_mask;
blade = boot_pnode_to_blade(pnode); blade = boot_pnode_to_blade(pnode);
uv_node_to_blade[nid] = blade; uv_node_to_blade[nid] = blade;
} }
......
...@@ -115,9 +115,6 @@ early_param("nobau", setup_nobau); ...@@ -115,9 +115,6 @@ early_param("nobau", setup_nobau);
/* base pnode in this partition */ /* base pnode in this partition */
static int uv_base_pnode __read_mostly; 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 ptc_stats, ptcstats);
static DEFINE_PER_CPU(struct bau_control, bau_control); static DEFINE_PER_CPU(struct bau_control, bau_control);
...@@ -1435,7 +1432,7 @@ static void activation_descriptor_init(int node, int pnode, int base_pnode) ...@@ -1435,7 +1432,7 @@ static void activation_descriptor_init(int node, int pnode, int base_pnode)
{ {
int i; int i;
int cpu; int cpu;
unsigned long pa; unsigned long gpa;
unsigned long m; unsigned long m;
unsigned long n; unsigned long n;
size_t dsize; size_t dsize;
...@@ -1451,9 +1448,9 @@ static void activation_descriptor_init(int node, int pnode, int base_pnode) ...@@ -1451,9 +1448,9 @@ static void activation_descriptor_init(int node, int pnode, int base_pnode)
bau_desc = kmalloc_node(dsize, GFP_KERNEL, node); bau_desc = kmalloc_node(dsize, GFP_KERNEL, node);
BUG_ON(!bau_desc); BUG_ON(!bau_desc);
pa = uv_gpa(bau_desc); /* need the real nasid*/ gpa = uv_gpa(bau_desc);
n = pa >> uv_nshift; n = uv_gpa_to_gnode(gpa);
m = pa & uv_mmask; m = uv_gpa_to_offset(gpa);
/* the 14-bit pnode */ /* the 14-bit pnode */
write_mmr_descriptor_base(pnode, (n << UV_DESC_PSHIFT | m)); write_mmr_descriptor_base(pnode, (n << UV_DESC_PSHIFT | m));
...@@ -1525,9 +1522,9 @@ static void pq_init(int node, int pnode) ...@@ -1525,9 +1522,9 @@ static void pq_init(int node, int pnode)
bcp->queue_last = pqp + (DEST_Q_SIZE - 1); 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); first = uv_physnodeaddr(pqp);
pn_first = ((unsigned long)pn << UV_PAYLOADQ_PNODE_SHIFT) | first; pn_first = ((unsigned long)pn << UV_PAYLOADQ_PNODE_SHIFT) | first;
last = uv_physnodeaddr(pqp + (DEST_Q_SIZE - 1)); last = uv_physnodeaddr(pqp + (DEST_Q_SIZE - 1));
...@@ -1837,8 +1834,6 @@ static int __init uv_bau_init(void) ...@@ -1837,8 +1834,6 @@ static int __init uv_bau_init(void)
zalloc_cpumask_var_node(mask, GFP_KERNEL, cpu_to_node(cur_cpu)); 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(); nuvhubs = uv_num_possible_blades();
spin_lock_init(&disable_lock); spin_lock_init(&disable_lock);
congested_cycles = usec_2_cycles(congested_respns_us); congested_cycles = usec_2_cycles(congested_respns_us);
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment