/* * This file is subject to the terms and conditions of the GNU General Public * License. See the file "COPYING" in the main directory of this archive * for more details. * * SGI UV APIC functions (note: not an Intel compatible APIC) * * (C) Copyright 2020 Hewlett Packard Enterprise Development LP * Copyright (C) 2007-2014 Silicon Graphics, Inc. All rights reserved.
*/ #include <linux/crash_dump.h> #include <linux/cpuhotplug.h> #include <linux/cpumask.h> #include <linux/proc_fs.h> #include <linux/memory.h> #include <linux/export.h> #include <linux/pci.h> #include <linux/acpi.h> #include <linux/efi.h>
/* Running on a UV Hubbed system, determine which UV Hub Type it is */ staticint __init early_set_hub_type(void)
{ union uvh_node_id_u node_id;
/* * The NODE_ID MMR is always at offset 0. * Contains the chip part # + revision. * Node_id field started with 15 bits, * ... now 7 but upper 8 are masked to 0. * All blades/nodes have the same part # and hub revision.
*/
node_id.v = uv_early_read_mmr(UVH_NODE_ID);
uv_node_id = node_id.sx.node_id;
switch (node_id.s.part_number) {
case UV5_HUB_PART_NUMBER:
uv_min_hub_revision_id = node_id.s.revision
+ UV5_HUB_REVISION_BASE;
uv_hub_type_set(UV5); break;
/* UV4/4A only have a revision difference */ case UV4_HUB_PART_NUMBER:
uv_min_hub_revision_id = node_id.s.revision
+ UV4_HUB_REVISION_BASE - 1;
uv_hub_type_set(UV4); if (uv_min_hub_revision_id == UV4A_HUB_REVISION_BASE)
uv_hub_type_set(UV4|UV4A); break;
case UV3_HUB_PART_NUMBER: case UV3_HUB_PART_NUMBER_X:
uv_min_hub_revision_id = node_id.s.revision
+ UV3_HUB_REVISION_BASE;
uv_hub_type_set(UV3); break;
case UV2_HUB_PART_NUMBER: case UV2_HUB_PART_NUMBER_X:
uv_min_hub_revision_id = node_id.s.revision
+ UV2_HUB_REVISION_BASE - 1;
uv_hub_type_set(UV2); break;
/* Check if TSC is valid for all sockets */ switch (sync_state) { case UVH_TSC_SYNC_VALID:
state = "in sync";
mark_tsc_async_resets("UV BIOS"); break;
/* If BIOS state unknown, don't do anything */ case UVH_TSC_SYNC_UNKNOWN:
state = "unknown"; break;
/* Otherwise, BIOS indicates problem with TSC */ default:
state = "unstable";
mark_tsc_unstable("UV BIOS"); break;
}
pr_info("UV: TSC sync state from BIOS:0%d(%s)\n", sync_state, state);
}
if (is_uv2_hub() || is_uv3_hub())
uvh_apicid.v = uv_early_read_mmr(UVH_APICID);
if (sid_shift) {
uv_cpuid.apicid_shift = 0;
uv_cpuid.apicid_mask = (~(-1 << sid_shift));
uv_cpuid.socketid_shift = sid_shift;
} else {
pr_info("UV: CPU does not have valid CPUID.11\n");
}
/* Find UV arch type entry in UVsystab */ staticunsignedlong __init early_find_archtype(struct uv_systab *st)
{ int i;
for (i = 0; st->entry[i].type != UV_SYSTAB_TYPE_UNUSED; i++) { unsignedlong ptr = st->entry[i].offset;
if (!ptr) continue;
ptr += (unsignedlong)st; if (st->entry[i].type == UV_SYSTAB_TYPE_ARCH_TYPE) return ptr;
} return 0;
}
/* Validate UV arch type field in UVsystab */ staticint __init decode_arch_type(unsignedlong ptr)
{ struct uv_arch_type_entry *uv_ate = (struct uv_arch_type_entry *)ptr; int n = strlen(uv_ate->archtype);
if (n > 0 && n < sizeof(uv_ate->archtype)) {
pr_info("UV: UVarchtype received from BIOS\n");
uv_stringify(sizeof(uv_archtype), uv_archtype, uv_ate->archtype); return 1;
} return 0;
}
/* Determine if UV arch type entry might exist in UVsystab */ staticint __init early_get_arch_type(void)
{ unsignedlong uvst_physaddr, uvst_size, ptr; struct uv_systab *st;
u32 rev; int ret;
uvst_physaddr = get_uv_systab_phys(0); if (!uvst_physaddr) return 0;
st = early_memremap_ro(uvst_physaddr, sizeof(struct uv_systab)); if (!st) {
pr_err("UV: Cannot access UVsystab, remap failed\n"); return 0;
}
/* Called early to probe for the correct APIC driver */ staticint __init uv_acpi_madt_oem_check(char *_oem_id, char *_oem_table_id)
{ /* Set up early hub info fields for Node 0 */
uv_cpu_info->p_uv_hub_info = &uv_hub_info_node0;
/* If not UV, return. */ if (uv_set_system_type(_oem_id, _oem_table_id) == 0) return 0;
/* Save for display of the OEM Table ID */
uv_stringify(sizeof(oem_table_id), oem_table_id, _oem_table_id);
/* Kernel parameter to specify UV mem block size */ staticint __init parse_mem_block_size(char *ptr)
{ unsignedlong size = memparse(ptr, NULL);
/* Size will be rounded down by set_block_size() below */
mem_block_size = size; return 0;
}
early_param("uv_memblksize", parse_mem_block_size);
static __init int adj_blksize(u32 lgre)
{ unsignedlong base = (unsignedlong)lgre << UV_GAM_RANGE_SHFT; unsignedlong size;
for (size = mem_block_size; size > MIN_MEMORY_BLOCK_SIZE; size >>= 1) if (IS_ALIGNED(base, size)) break;
if (size >= mem_block_size) return 0;
mem_block_size = size; return 1;
}
static __init void set_block_size(void)
{ unsignedint order = ffs(mem_block_size);
if (order) { /* adjust for ffs return of 1..64 */
set_memory_block_size_order(order - 1);
pr_info("UV: mem_block_size set to 0x%lx\n", mem_block_size);
} else { /* bad or zero value, default to 1UL << 31 (2GB) */
pr_err("UV: mem_block_size error with 0x%lx\n", mem_block_size);
set_memory_block_size_order(31);
}
}
/* Calculate and Map MMIOH Regions */ staticvoid __init calc_mmioh_map(enum mmioh_arch index, int min_pnode, int max_pnode, int shift, unsignedlong base, int m_io, int n_io)
{ unsignedlong mmr, nasid_mask; int nasid, min_nasid, max_nasid, lnasid, mapped; int i, fi, li, n, max_io; char id[8];
/* small and large MMIOH mappings */ switch (index) { case UVY_MMIOH0:
mmr = UVH_RH10_GAM_MMIOH_REDIRECT_CONFIG0;
nasid_mask = UVYH_RH10_GAM_MMIOH_REDIRECT_CONFIG0_NASID_MASK;
n = UVH_RH10_GAM_MMIOH_REDIRECT_CONFIG0_DEPTH;
min_nasid = min_pnode;
max_nasid = max_pnode;
mapped = 1; break; case UVY_MMIOH1:
mmr = UVH_RH10_GAM_MMIOH_REDIRECT_CONFIG1;
nasid_mask = UVYH_RH10_GAM_MMIOH_REDIRECT_CONFIG1_NASID_MASK;
n = UVH_RH10_GAM_MMIOH_REDIRECT_CONFIG1_DEPTH;
min_nasid = min_pnode;
max_nasid = max_pnode;
mapped = 1; break; case UVX_MMIOH0:
mmr = UVH_RH_GAM_MMIOH_REDIRECT_CONFIG0;
nasid_mask = UVH_RH_GAM_MMIOH_REDIRECT_CONFIG0_NASID_MASK;
n = UVH_RH_GAM_MMIOH_REDIRECT_CONFIG0_DEPTH;
min_nasid = min_pnode * 2;
max_nasid = max_pnode * 2;
mapped = 1; break; case UVX_MMIOH1:
mmr = UVH_RH_GAM_MMIOH_REDIRECT_CONFIG1;
nasid_mask = UVH_RH_GAM_MMIOH_REDIRECT_CONFIG1_NASID_MASK;
n = UVH_RH_GAM_MMIOH_REDIRECT_CONFIG1_DEPTH;
min_nasid = min_pnode * 2;
max_nasid = max_pnode * 2;
mapped = 1; break; default:
pr_err("UV:%s:Invalid mapping type:%d\n", __func__, index); return;
}
/* enum values chosen so (index mod 2) is MMIOH 0/1 (low/high) */
snprintf(id, sizeof(id), "MMIOH%d", index%2);
max_io = lnasid = fi = li = -1; for (i = 0; i < n; i++) { unsignedlong m_redirect = mmr + i * 8; unsignedlong redirect = uv_read_local_mmr(m_redirect);
nasid = redirect & nasid_mask; if (i == 0)
pr_info("UV: %s redirect base 0x%lx(@0x%lx) 0x%04x\n",
id, redirect, m_redirect, nasid);
/* Invalid NASID check */ if (nasid < min_nasid || max_nasid < nasid) { /* Not an error: unused table entries get "poison" values */
pr_debug("UV:%s:Invalid NASID(%x):%x (range:%x..%x)\n",
__func__, index, nasid, min_nasid, max_nasid);
nasid = -1;
}
if (nasid == lnasid) {
li = i; /* Last entry check: */ if (i != n-1) continue;
}
/* Check if we have a cached (or last) redirect to print: */ if (lnasid != -1 || (i == n-1 && nasid != -1)) { unsignedlong addr1, addr2; int f, l;
if (lnasid == -1) {
f = l = i;
lnasid = nasid;
} else {
f = fi;
l = li;
}
addr1 = (base << shift) + f * (1ULL << m_io);
addr2 = (base << shift) + (l + 1) * (1ULL << m_io);
pr_info("UV: %s[%03d..%03d] NASID 0x%04x ADDR 0x%016lx - 0x%016lx\n",
id, fi, li, lnasid, addr1, addr2); if (max_io < l)
max_io = l;
}
fi = li = i;
lnasid = nasid;
}
/* * Called on each CPU to initialize the per_cpu UV data area. * FIXME: hotplug not supported yet
*/ void uv_cpu_init(void)
{ /* CPU 0 initialization will be done via uv_system_init. */ if (smp_processor_id() == 0) return;
/* * Given a bitmask 'bits' representing presnt blades, numbered * starting at 'base', masking off unused high bits of blade number * with 'mask', update the minimum and maximum blade numbers that we * have found. (Masking with 'mask' necessary because of BIOS * treatment of system partitioning when creating this table we are * interpreting.)
*/ staticinlinevoid blade_update_min_max(unsignedlong bits, int base, int mask, int *min, int *max)
{ int first, last;
if (!bits) return;
first = (base + __ffs(bits)) & mask;
last = (base + __fls(bits)) & mask;
if (*min > first)
*min = first; if (*max < last)
*max = last;
}
/* Set up physical blade translations from UVH_NODE_PRESENT_TABLE */ static __init void boot_init_possible_blades(struct uv_hub_info_s *hub_info)
{ unsignedlong np; int i, uv_pb = 0; int sock_min = INT_MAX, sock_max = -1, s_mask;
s_mask = (1 << uv_cpuid.n_skt) - 1;
if (UVH_NODE_PRESENT_TABLE) {
pr_info("UV: NODE_PRESENT_DEPTH = %d\n",
UVH_NODE_PRESENT_TABLE_DEPTH); for (i = 0; i < UVH_NODE_PRESENT_TABLE_DEPTH; i++) {
np = uv_read_local_mmr(UVH_NODE_PRESENT_TABLE + i * 8);
pr_info("UV: NODE_PRESENT(%d) = 0x%016lx\n", i, np);
blade_update_min_max(np, i * 64, s_mask, &sock_min, &sock_max);
}
} if (UVH_NODE_PRESENT_0) {
np = uv_read_local_mmr(UVH_NODE_PRESENT_0);
pr_info("UV: NODE_PRESENT_0 = 0x%016lx\n", np);
blade_update_min_max(np, 0, s_mask, &sock_min, &sock_max);
} if (UVH_NODE_PRESENT_1) {
np = uv_read_local_mmr(UVH_NODE_PRESENT_1);
pr_info("UV: NODE_PRESENT_1 = 0x%016lx\n", np);
blade_update_min_max(np, 64, s_mask, &sock_min, &sock_max);
}
/* Only update if we actually found some bits indicating blades present */ if (sock_max >= sock_min) {
_min_socket = sock_min;
_max_socket = sock_max;
uv_pb = sock_max - sock_min + 1;
} if (uv_possible_blades != uv_pb)
uv_possible_blades = uv_pb;
staticvoid __init free_1_to_1_table(unsignedshort **tp, char *tname, int min, int max, intmax2)
{ int i; unsignedshort *table = *tp;
if (table == NULL) return; if (max != max2) return; for (i = 0; i < max; i++) { if (i != table[i]) return;
}
kfree(table);
*tp = NULL;
pr_info("UV: %s is 1:1, conversion table removed\n", tname);
}
/* * Build Socket Tables * If the number of nodes is >1 per socket, socket to node table will * contain lowest node number on that socket.
*/ staticvoid __init build_socket_tables(void)
{ struct uv_gam_range_entry *gre = uv_gre_table; int nums, numn, nump; int i, lnid, apicid; int minsock = _min_socket; int maxsock = _max_socket; int minpnode = _min_pnode; int maxpnode = _max_pnode;
if (!gre) { if (is_uv2_hub() || is_uv3_hub()) {
pr_info("UV: No UVsystab socket table, ignoring\n"); return;
}
pr_err("UV: Error: UVsystab address translations not available!\n");
WARN_ON_ONCE(!gre); return;
}
/* Fill in pnode/node/addr conversion list values: */ 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)
_socket_to_pnode[i] = gre->pnode;
i = gre->pnode - minpnode; if (_pnode_to_socket[i] == SOCK_EMPTY)
_pnode_to_socket[i] = gre->sockid;
/* Set socket -> node values: */
lnid = NUMA_NO_NODE; for (apicid = 0; apicid < ARRAY_SIZE(__apicid_to_node); apicid++) { int nid = __apicid_to_node[apicid]; int sockid;
/* * If e.g. socket id == pnode for all pnodes, * system runs faster by removing corresponding conversion table.
*/
FREE_1_TO_1_TABLE(_socket_to_node, _min_socket, nums, numn);
FREE_1_TO_1_TABLE(_node_to_socket, _min_socket, nums, numn);
FREE_1_TO_1_TABLE(_socket_to_pnode, _min_pnode, nums, nump);
FREE_1_TO_1_TABLE(_pnode_to_socket, _min_pnode, nums, nump);
}
/* Check which reboot to use */ staticvoid check_efi_reboot(void)
{ /* If EFI reboot not available, use ACPI reboot */ if (!efi_enabled(EFI_BOOT))
reboot_type = BOOT_ACPI;
}
/* * User proc fs file handling now deprecated. * Recommend using /sys/firmware/sgi_uv/... instead.
*/ staticint __maybe_unused proc_hubbed_show(struct seq_file *file, void *data)
{
pr_notice_once("%s: using deprecated /proc/sgi_uv/hubbed, use /sys/firmware/sgi_uv/hub_type\n",
current->comm);
seq_printf(file, "0x%x\n", uv_hubbed_system); return 0;
}
if (!hub) {
pr_err("UV: Unknown/unsupported UV hub\n"); return;
}
pr_info("UV: Found %s hub\n", hub);
map_low_mmrs();
/* Get uv_systab for decoding, setup UV BIOS calls */
uv_bios_init();
/* If there's an UVsystab problem then abort UV init: */ if (decode_uv_systab() < 0) {
pr_err("UV: Mangled UVsystab format\n"); return;
}
build_socket_tables();
build_uv_gr_table();
set_block_size();
uv_init_hub_info(&hub_info); /* If UV2 or UV3 may need to get # blades from HW */ if (is_uv(UV2|UV3) && !uv_gre_table)
boot_init_possible_blades(&hub_info); else /* min/max sockets set in decode_gam_rng_tbl */
uv_possible_blades = (_max_socket - _min_socket) + 1;
/* uv_num_possible_blades() is really the hub count: */
pr_info("UV: Found %d hubs, %d nodes, %d CPUs\n", uv_num_possible_blades(), num_possible_nodes(), num_possible_cpus());
/* * __uv_hub_info_list[] is indexed by node, but there is only * one hub_info structure per blade. First, allocate one * structure per blade. Further down we create a per-node * table (__uv_hub_info_list[]) pointing to hub_info * structures for the correct blade.
*/
/* Allocate & fill new per hub info list */
new_hub = (bid == 0) ? &uv_hub_info_node0
: kzalloc_node(bytes, GFP_KERNEL, uv_blade_to_node(bid)); if (WARN_ON_ONCE(!new_hub)) { /* do not kfree() bid 0, which is statically allocated */ while (--bid > 0)
kfree(uv_hub_info_list_blade[bid]);
kfree(uv_hub_info_list_blade); return;
}
/* Use information from GAM table if available: */ if (uv_gre_table)
new_hub->pnode = uv_blade_to_pnode(bid); else/* Or fill in during CPU loop: */
new_hub->pnode = 0xffff;
/* * There is a different code path needed to initialize a UV system that does * not have a "UV HUB" (referred to as "hubless").
*/ void __init uv_system_init(void)
{ if (likely(!is_uv_system() && !is_uv_hubless(1))) return;
if (is_uv_system())
uv_system_init_hub(); else
uv_system_init_hubless();
}
apic_driver(apic_x2apic_uv_x);
Messung V0.5
¤ Dauer der Verarbeitung: 0.20 Sekunden
(vorverarbeitet)
¤
Die Informationen auf dieser Webseite wurden
nach bestem Wissen sorgfältig zusammengestellt. Es wird jedoch weder Vollständigkeit, noch Richtigkeit,
noch Qualität der bereit gestellten Informationen zugesichert.
Bemerkung:
Die farbliche Syntaxdarstellung und die Messung sind noch experimentell.