Return-Path: Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S1753438AbcD1BHG (ORCPT ); Wed, 27 Apr 2016 21:07:06 -0400 Received: from relay2.sgi.com ([192.48.180.65]:46845 "EHLO relay.sgi.com" rhost-flags-OK-OK-OK-FAIL) by vger.kernel.org with ESMTP id S1752699AbcD1BGH (ORCPT ); Wed, 27 Apr 2016 21:06:07 -0400 Message-Id: <20160428005500.631332885@asylum.americas.sgi.com> References: <20160428005457.471061412@asylum.americas.sgi.com> User-Agent: quilt/0.46-1 Date: Wed, 27 Apr 2016 19:55:15 -0500 From: Mike Travis To: Ingo Molnar , "H. Peter Anvin" , Thomas Gleixner , Andrew Morton , Len Brown Cc: x86@kernel.org, linux-kernel@vger.kernel.org, uv4-kernel@sgi.com Subject: [PATCH 18/21] X86_64, UV: Build GAM reference tables Content-Disposition: inline; filename=uv4_build_socket_tables Sender: linux-kernel-owner@vger.kernel.org List-ID: X-Mailing-List: linux-kernel@vger.kernel.org Content-Length: 7147 Lines: 222 An aspect of the UV4 system architecture changes involve changing the way sockets, nodes, and pnodes are translated between one another. Decode the information from the BIOS provided EFI system table to build the needed conversion tables. Signed-off-by: Mike Travis Reviewed-by: Dimitri Sivanich Tested-by: Dimitri Sivanich Tested-by: John Estabrook Tested-by: Gary Kroening --- arch/x86/include/asm/uv/uv_hub.h | 18 ++++ arch/x86/kernel/apic/x2apic_uv_x.c | 139 +++++++++++++++++++++++++++++++++++++ 2 files changed, 155 insertions(+), 2 deletions(-) --- linux.orig/arch/x86/include/asm/uv/uv_hub.h +++ linux/arch/x86/include/asm/uv/uv_hub.h @@ -149,6 +149,9 @@ struct uv_hub_info_s { unsigned long global_mmr_base; unsigned long global_mmr_shift; unsigned long gpa_mask; + unsigned short *socket_to_node; + unsigned short *socket_to_pnode; + unsigned short *pnode_to_socket; unsigned short min_socket; unsigned short min_pnode; unsigned char hub_revision; @@ -479,10 +482,21 @@ static inline void *uv_pnode_offset_to_v return __va(((unsigned long)pnode << uv_hub_info->m_val) | offset); } -/* Extract a PNODE from an APICID (full apicid, not processor subset) */ +/* Convert socket to node */ +static inline int uv_socket_to_node(int socket) +{ + unsigned short *s2nid = uv_hub_info->socket_to_node; + + return s2nid ? s2nid[socket - uv_hub_info->min_socket] : socket; +} + +/* Extract/Convert a PNODE from an APICID (full apicid, not processor subset) */ static inline int uv_apicid_to_pnode(int apicid) { - return (apicid >> uv_hub_info->apic_pnode_shift); + int pnode = apicid >> uv_hub_info->apic_pnode_shift; + unsigned short *s2pn = uv_hub_info->socket_to_pnode; + + return s2pn ? s2pn[pnode - uv_hub_info->min_socket] : pnode; } /* Convert an apicid to the socket number on the blade */ --- linux.orig/arch/x86/kernel/apic/x2apic_uv_x.c +++ linux/arch/x86/kernel/apic/x2apic_uv_x.c @@ -278,6 +278,9 @@ static __initdata unsigned short _min_so static __initdata unsigned short _min_pnode, _max_pnode, _gr_table_len; static __initdata struct uv_gam_range_entry *uv_gre_table; static __initdata struct uv_gam_parameters *uv_gp_table; +static __initdata unsigned short *_socket_to_node; +static __initdata unsigned short *_socket_to_pnode; +static __initdata unsigned short *_pnode_to_socket; #define SOCK_EMPTY ((unsigned short)~0) extern int uv_hub_info_version(void) @@ -945,6 +948,10 @@ void __init uv_init_hub_info(struct uv_h hub_info->hub_revision = uv_hub_info->hub_revision; hub_info->pnode_mask = uv_cpuid.pnode_mask; hub_info->min_pnode = _min_pnode; + hub_info->min_socket = _min_socket; + hub_info->pnode_to_socket = _pnode_to_socket; + hub_info->socket_to_node = _socket_to_node; + hub_info->socket_to_pnode = _socket_to_pnode; hub_info->gpa_mask = mn.m_val ? (1UL << (mn.m_val + mn.n_val)) - 1 : (1UL << uv_cpuid.gpa_shift) - 1; @@ -1131,6 +1138,137 @@ static __init void boot_init_possible_bl } } +static void __init build_socket_tables(void) +{ + struct uv_gam_range_entry *gre = uv_gre_table; + int num, nump; + int cpu, i, lnid; + int minsock = _min_socket; + int maxsock = _max_socket; + int minpnode = _min_pnode; + int maxpnode = _max_pnode; + size_t bytes; + + if (!gre) { + if (is_uv1_hub() || is_uv2_hub() || is_uv3_hub()) { + pr_info("UV: No UVsystab socket table, ignoring\n"); + return; /* not required */ + } + pr_crit( + "UV: Error: UVsystab address translations not available!\n"); + BUG(); + } + + /* build socket id -> node id, pnode */ + num = maxsock - minsock + 1; + bytes = num * sizeof(_socket_to_node[0]); + _socket_to_node = kmalloc(bytes, GFP_KERNEL); + _socket_to_pnode = kmalloc(bytes, GFP_KERNEL); + + nump = maxpnode - minpnode + 1; + bytes = nump * sizeof(_pnode_to_socket[0]); + _pnode_to_socket = kmalloc(bytes, GFP_KERNEL); + BUG_ON(!_socket_to_node || !_socket_to_pnode || !_pnode_to_socket); + + for (i = 0; i < num; i++) + _socket_to_node[i] = _socket_to_pnode[i] = SOCK_EMPTY; + + for (i = 0; i < nump; i++) + _pnode_to_socket[i] = SOCK_EMPTY; + + /* fill in pnode/node/addr conversion list values */ + pr_info("UV: GAM Building socket/pnode/pxm conversion tables\n"); + for (; gre->type != UV_GAM_RANGE_TYPE_UNUSED; gre++) { + if (gre->type == UV_GAM_RANGE_TYPE_HOLE) + continue; + i = gre->sockid - minsock; + if (_socket_to_pnode[i] != SOCK_EMPTY) + continue; /* duplicate */ + _socket_to_pnode[i] = gre->pnode; + _socket_to_node[i] = gre->pxm; + + i = gre->pnode - minpnode; + _pnode_to_socket[i] = gre->sockid; + + pr_info( + "UV: sid:%02x type:%d nasid:%04x pn:%02x pxm:%2d pn2s:%2x\n", + gre->sockid, gre->type, gre->nasid, + _socket_to_pnode[gre->sockid - minsock], + _socket_to_node[gre->sockid - minsock], + _pnode_to_socket[gre->pnode - minpnode]); + } + + /* check socket -> node values */ + lnid = -1; + for_each_present_cpu(cpu) { + int nid = cpu_to_node(cpu); + int apicid, sockid; + + if (lnid == nid) + continue; + lnid = nid; + apicid = per_cpu(x86_cpu_to_apicid, cpu); + sockid = apicid >> uv_cpuid.socketid_shift; + i = sockid - minsock; + + if (nid != _socket_to_node[i]) { + pr_warn( + "UV: %02x: type:%d socket:%02x PXM:%02x != node:%2d\n", + i, sockid, gre->type, _socket_to_node[i], nid); + _socket_to_node[i] = nid; + } + } + + /* Setup physical blade to pnode translation from GAM Range Table */ + bytes = num_possible_nodes() * sizeof(_node_to_pnode[0]); + _node_to_pnode = kmalloc(bytes, GFP_KERNEL); + BUG_ON(!_node_to_pnode); + + for (lnid = 0; lnid < num_possible_nodes(); lnid++) { + unsigned short sockid; + + for (sockid = minsock; sockid <= maxsock; sockid++) { + if (lnid == _socket_to_node[sockid - minsock]) { + _node_to_pnode[lnid] = + _socket_to_pnode[sockid - minsock]; + break; + } + } + if (sockid > maxsock) { + pr_err("UV: socket for node %d not found!\n", lnid); + BUG(); + } + } + + /* + * If socket id == pnode or socket id == node for all nodes, + * system runs faster by removing corresponding conversion table. + */ + pr_info("UV: Checking socket->node/pnode for identity maps\n"); + if (minsock == 0) { + for (i = 0; i < num; i++) + if (_socket_to_node[i] == SOCK_EMPTY || + i != _socket_to_node[i]) + break; + if (i >= num) { + kfree(_socket_to_node); + _socket_to_node = NULL; + pr_info("UV: 1:1 socket_to_node table removed\n"); + } + } + if (minsock == minpnode) { + for (i = 0; i < num; i++) + if (_socket_to_pnode[i] != SOCK_EMPTY && + _socket_to_pnode[i] != i + minpnode) + break; + if (i >= num) { + kfree(_socket_to_pnode); + _socket_to_pnode = NULL; + pr_info("UV: 1:1 socket_to_pnode table removed\n"); + } + } +} + void __init uv_system_init(void) { struct uv_hub_info_s hub_info = {0}; @@ -1153,6 +1291,7 @@ void __init uv_system_init(void) uv_bios_init(); /* get uv_systab for decoding */ decode_uv_systab(); + build_socket_tables(); uv_init_hub_info(&hub_info); uv_possible_blades = num_possible_nodes(); if (!_node_to_pnode) --