x86: uv2: Workaround for UV2 Hub bug (system global address format)
authorJack Steiner <steiner@sgi.com>
Tue, 20 Sep 2011 20:55:04 +0000 (13:55 -0700)
committerThomas Gleixner <tglx@linutronix.de>
Wed, 21 Sep 2011 09:23:15 +0000 (11:23 +0200)
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>
arch/x86/include/asm/uv/uv_bau.h
arch/x86/include/asm/uv/uv_hub.h
arch/x86/kernel/apic/x2apic_uv_x.c
arch/x86/platform/uv/tlb_uv.c

index 37d369859c8e54da912475ef3b9fd54132ee0212..0c767a8e000ee8c32ecd9ef222dca19b6ed7281e 100644 (file)
@@ -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)
index f26544a15214e41f418b0554c52581ce23f9aacb..54a13aaebc4006c6a55cdce0fc414e2bd563a06a 100644 (file)
  *     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_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;
 }
 
 
@@ -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 */
@@ -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)
 {
index 34b18594e72467212f0e430c329e9fa523f3aa09..cfeb978f49fd4c56fbb4e543a74ce893ed7c05a7 100644 (file)
@@ -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;
        }
index db8b915f54bc50ca23baad161d249d0382c649ab..5b552198f774eb1e6c6ab3b9ef42276706d74328 100644 (file)
@@ -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(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;
@@ -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));
@@ -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);