Skip to content

Commit

Permalink
x86: uv2: Workaround for UV2 Hub bug (system global address format)
Browse files Browse the repository at this point in the history
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>
Cc: <stable@kernel.org>
Signed-off-by: Andrew Morton <akpm@linux-foundation.org>
Signed-off-by: Thomas Gleixner <tglx@linutronix.de>
  • Loading branch information
Jack Steiner authored and Thomas Gleixner committed Sep 21, 2011
1 parent 9d037a7 commit 6a469e4
Show file tree
Hide file tree
Showing 4 changed files with 46 additions and 16 deletions.
1 change: 1 addition & 0 deletions arch/x86/include/asm/uv/uv_bau.h
Original file line number Diff line number Diff line change
Expand Up @@ -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)
Expand Down
37 changes: 34 additions & 3 deletions arch/x86/include/asm/uv/uv_hub.h
Original file line number Diff line number Diff line change
Expand Up @@ -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:
* +--------------------------------+---------------------+
Expand Down Expand Up @@ -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;
Expand Down Expand Up @@ -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 {
Expand Down Expand Up @@ -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)
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;
}


Expand All @@ -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_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 */
Expand All @@ -320,6 +345,12 @@ static inline int uv_gpa_to_pnode(unsigned long gpa)
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)
{
Expand Down
7 changes: 5 additions & 2 deletions arch/x86/kernel/apic/x2apic_uv_x.c
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand Down Expand Up @@ -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;
}
Expand Down
17 changes: 6 additions & 11 deletions arch/x86/platform/uv/tlb_uv.c
Original file line number Diff line number Diff line change
Expand Up @@ -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);
Expand Down Expand Up @@ -1435,7 +1432,7 @@ static void activation_descriptor_init(int node, int pnode, int base_pnode)
{
int i;
int cpu;
unsigned long pa;
unsigned long gpa;
unsigned long m;
unsigned long n;
size_t dsize;
Expand All @@ -1451,9 +1448,9 @@ static void activation_descriptor_init(int node, int pnode, int base_pnode)
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));
Expand Down Expand Up @@ -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));
Expand Down Expand Up @@ -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);
Expand Down

0 comments on commit 6a469e4

Please sign in to comment.