xpc_partition.c
来自「底层驱动开发」· C语言 代码 · 共 987 行 · 第 1/2 页
C
987 行
/* * 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. * * Copyright (c) 2004-2005 Silicon Graphics, Inc. All Rights Reserved. *//* * Cross Partition Communication (XPC) partition support. * * This is the part of XPC that detects the presence/absence of * other partitions. It provides a heartbeat and monitors the * heartbeats of other partitions. * */#include <linux/kernel.h>#include <linux/sysctl.h>#include <linux/cache.h>#include <linux/mmzone.h>#include <linux/nodemask.h>#include <asm/uncached.h>#include <asm/sn/bte.h>#include <asm/sn/intr.h>#include <asm/sn/sn_sal.h>#include <asm/sn/nodepda.h>#include <asm/sn/addrs.h>#include "xpc.h"/* XPC is exiting flag */int xpc_exiting;/* SH_IPI_ACCESS shub register value on startup */static u64 xpc_sh1_IPI_access;static u64 xpc_sh2_IPI_access0;static u64 xpc_sh2_IPI_access1;static u64 xpc_sh2_IPI_access2;static u64 xpc_sh2_IPI_access3;/* original protection values for each node */u64 xpc_prot_vec[MAX_COMPACT_NODES];/* this partition's reserved page */struct xpc_rsvd_page *xpc_rsvd_page;/* this partition's XPC variables (within the reserved page) */struct xpc_vars *xpc_vars;struct xpc_vars_part *xpc_vars_part;/* * For performance reasons, each entry of xpc_partitions[] is cacheline * aligned. And xpc_partitions[] is padded with an additional entry at the * end so that the last legitimate entry doesn't share its cacheline with * another variable. */struct xpc_partition xpc_partitions[XP_MAX_PARTITIONS + 1];/* * Generic buffer used to store a local copy of the remote partitions * reserved page or XPC variables. * * xpc_discovery runs only once and is a seperate thread that is * very likely going to be processing in parallel with receiving * interrupts. */char ____cacheline_aligned xpc_remote_copy_buffer[XPC_RSVD_PAGE_ALIGNED_SIZE];/* systune related variables */int xpc_hb_interval = XPC_HB_DEFAULT_INTERVAL;int xpc_hb_check_interval = XPC_HB_CHECK_DEFAULT_TIMEOUT;/* * Given a nasid, get the physical address of the partition's reserved page * for that nasid. This function returns 0 on any error. */static u64xpc_get_rsvd_page_pa(int nasid, u64 buf, u64 buf_size){ bte_result_t bte_res; s64 status; u64 cookie = 0; u64 rp_pa = nasid; /* seed with nasid */ u64 len = 0; while (1) { status = sn_partition_reserved_page_pa(buf, &cookie, &rp_pa, &len); dev_dbg(xpc_part, "SAL returned with status=%li, cookie=" "0x%016lx, address=0x%016lx, len=0x%016lx\n", status, cookie, rp_pa, len); if (status != SALRET_MORE_PASSES) { break; } if (len > buf_size) { dev_err(xpc_part, "len (=0x%016lx) > buf_size\n", len); status = SALRET_ERROR; break; } bte_res = xp_bte_copy(rp_pa, ia64_tpa(buf), buf_size, (BTE_NOTIFY | BTE_WACQUIRE), NULL); if (bte_res != BTE_SUCCESS) { dev_dbg(xpc_part, "xp_bte_copy failed %i\n", bte_res); status = SALRET_ERROR; break; } } if (status != SALRET_OK) { rp_pa = 0; } dev_dbg(xpc_part, "reserved page at phys address 0x%016lx\n", rp_pa); return rp_pa;}/* * Fill the partition reserved page with the information needed by * other partitions to discover we are alive and establish initial * communications. */struct xpc_rsvd_page *xpc_rsvd_page_init(void){ struct xpc_rsvd_page *rp; AMO_t *amos_page; u64 rp_pa, next_cl, nasid_array = 0; int i, ret; /* get the local reserved page's address */ rp_pa = xpc_get_rsvd_page_pa(cnodeid_to_nasid(0), (u64) xpc_remote_copy_buffer, XPC_RSVD_PAGE_ALIGNED_SIZE); if (rp_pa == 0) { dev_err(xpc_part, "SAL failed to locate the reserved page\n"); return NULL; } rp = (struct xpc_rsvd_page *) __va(rp_pa); if (rp->partid != sn_partition_id) { dev_err(xpc_part, "the reserved page's partid of %d should be " "%d\n", rp->partid, sn_partition_id); return NULL; } rp->version = XPC_RP_VERSION; /* * Place the XPC variables on the cache line following the * reserved page structure. */ next_cl = (u64) rp + XPC_RSVD_PAGE_ALIGNED_SIZE; xpc_vars = (struct xpc_vars *) next_cl; /* * Before clearing xpc_vars, see if a page of AMOs had been previously * allocated. If not we'll need to allocate one and set permissions * so that cross-partition AMOs are allowed. * * The allocated AMO page needs MCA reporting to remain disabled after * XPC has unloaded. To make this work, we keep a copy of the pointer * to this page (i.e., amos_page) in the struct xpc_vars structure, * which is pointed to by the reserved page, and re-use that saved copy * on subsequent loads of XPC. This AMO page is never freed, and its * memory protections are never restricted. */ if ((amos_page = xpc_vars->amos_page) == NULL) { amos_page = (AMO_t *) TO_AMO(uncached_alloc_page(0)); if (amos_page == NULL) { dev_err(xpc_part, "can't allocate page of AMOs\n"); return NULL; } /* * Open up AMO-R/W to cpu. This is done for Shub 1.1 systems * when xpc_allow_IPI_ops() is called via xpc_hb_init(). */ if (!enable_shub_wars_1_1()) { ret = sn_change_memprotect(ia64_tpa((u64) amos_page), PAGE_SIZE, SN_MEMPROT_ACCESS_CLASS_1, &nasid_array); if (ret != 0) { dev_err(xpc_part, "can't change memory " "protections\n"); uncached_free_page(__IA64_UNCACHED_OFFSET | TO_PHYS((u64) amos_page)); return NULL; } } } else if (!IS_AMO_ADDRESS((u64) amos_page)) { /* * EFI's XPBOOT can also set amos_page in the reserved page, * but it happens to leave it as an uncached physical address * and we need it to be an uncached virtual, so we'll have to * convert it. */ if (!IS_AMO_PHYS_ADDRESS((u64) amos_page)) { dev_err(xpc_part, "previously used amos_page address " "is bad = 0x%p\n", (void *) amos_page); return NULL; } amos_page = (AMO_t *) TO_AMO((u64) amos_page); } memset(xpc_vars, 0, sizeof(struct xpc_vars)); /* * Place the XPC per partition specific variables on the cache line * following the XPC variables structure. */ next_cl += XPC_VARS_ALIGNED_SIZE; memset((u64 *) next_cl, 0, sizeof(struct xpc_vars_part) * XP_MAX_PARTITIONS); xpc_vars_part = (struct xpc_vars_part *) next_cl; xpc_vars->vars_part_pa = __pa(next_cl); xpc_vars->version = XPC_V_VERSION; xpc_vars->act_nasid = cpuid_to_nasid(0); xpc_vars->act_phys_cpuid = cpu_physical_id(0); xpc_vars->amos_page = amos_page; /* save for next load of XPC */ /* * Initialize the activation related AMO variables. */ xpc_vars->act_amos = xpc_IPI_init(XP_MAX_PARTITIONS); for (i = 1; i < XP_NASID_MASK_WORDS; i++) { xpc_IPI_init(i + XP_MAX_PARTITIONS); } /* export AMO page's physical address to other partitions */ xpc_vars->amos_page_pa = ia64_tpa((u64) xpc_vars->amos_page); /* * This signifies to the remote partition that our reserved * page is initialized. */ rp->vars_pa = __pa(xpc_vars); return rp;}/* * Change protections to allow IPI operations (and AMO operations on * Shub 1.1 systems). */voidxpc_allow_IPI_ops(void){ int node; int nasid; // >>> Change SH_IPI_ACCESS code to use SAL call once it is available. if (is_shub2()) { xpc_sh2_IPI_access0 = (u64) HUB_L((u64 *) LOCAL_MMR_ADDR(SH2_IPI_ACCESS0)); xpc_sh2_IPI_access1 = (u64) HUB_L((u64 *) LOCAL_MMR_ADDR(SH2_IPI_ACCESS1)); xpc_sh2_IPI_access2 = (u64) HUB_L((u64 *) LOCAL_MMR_ADDR(SH2_IPI_ACCESS2)); xpc_sh2_IPI_access3 = (u64) HUB_L((u64 *) LOCAL_MMR_ADDR(SH2_IPI_ACCESS3)); for_each_online_node(node) { nasid = cnodeid_to_nasid(node); HUB_S((u64 *) GLOBAL_MMR_ADDR(nasid, SH2_IPI_ACCESS0), -1UL); HUB_S((u64 *) GLOBAL_MMR_ADDR(nasid, SH2_IPI_ACCESS1), -1UL); HUB_S((u64 *) GLOBAL_MMR_ADDR(nasid, SH2_IPI_ACCESS2), -1UL); HUB_S((u64 *) GLOBAL_MMR_ADDR(nasid, SH2_IPI_ACCESS3), -1UL); } } else { xpc_sh1_IPI_access = (u64) HUB_L((u64 *) LOCAL_MMR_ADDR(SH1_IPI_ACCESS)); for_each_online_node(node) { nasid = cnodeid_to_nasid(node); HUB_S((u64 *) GLOBAL_MMR_ADDR(nasid, SH1_IPI_ACCESS), -1UL); /* * Since the BIST collides with memory operations on * SHUB 1.1 sn_change_memprotect() cannot be used. */ if (enable_shub_wars_1_1()) { /* open up everything */ xpc_prot_vec[node] = (u64) HUB_L((u64 *) GLOBAL_MMR_ADDR(nasid, SH1_MD_DQLP_MMR_DIR_PRIVEC0)); HUB_S((u64 *) GLOBAL_MMR_ADDR(nasid, SH1_MD_DQLP_MMR_DIR_PRIVEC0), -1UL); HUB_S((u64 *) GLOBAL_MMR_ADDR(nasid, SH1_MD_DQRP_MMR_DIR_PRIVEC0), -1UL); } } }}/* * Restrict protections to disallow IPI operations (and AMO operations on * Shub 1.1 systems). */voidxpc_restrict_IPI_ops(void){ int node; int nasid; // >>> Change SH_IPI_ACCESS code to use SAL call once it is available. if (is_shub2()) { for_each_online_node(node) { nasid = cnodeid_to_nasid(node); HUB_S((u64 *) GLOBAL_MMR_ADDR(nasid, SH2_IPI_ACCESS0), xpc_sh2_IPI_access0); HUB_S((u64 *) GLOBAL_MMR_ADDR(nasid, SH2_IPI_ACCESS1), xpc_sh2_IPI_access1); HUB_S((u64 *) GLOBAL_MMR_ADDR(nasid, SH2_IPI_ACCESS2), xpc_sh2_IPI_access2); HUB_S((u64 *) GLOBAL_MMR_ADDR(nasid, SH2_IPI_ACCESS3), xpc_sh2_IPI_access3); } } else { for_each_online_node(node) { nasid = cnodeid_to_nasid(node); HUB_S((u64 *) GLOBAL_MMR_ADDR(nasid, SH1_IPI_ACCESS), xpc_sh1_IPI_access); if (enable_shub_wars_1_1()) { HUB_S((u64 *) GLOBAL_MMR_ADDR(nasid, SH1_MD_DQLP_MMR_DIR_PRIVEC0), xpc_prot_vec[node]); HUB_S((u64 *) GLOBAL_MMR_ADDR(nasid, SH1_MD_DQRP_MMR_DIR_PRIVEC0), xpc_prot_vec[node]); } } }}/* * At periodic intervals, scan through all active partitions and ensure * their heartbeat is still active. If not, the partition is deactivated. */voidxpc_check_remote_hb(void){ struct xpc_vars *remote_vars; struct xpc_partition *part; partid_t partid; bte_result_t bres; remote_vars = (struct xpc_vars *) xpc_remote_copy_buffer; for (partid = 1; partid < XP_MAX_PARTITIONS; partid++) { if (partid == sn_partition_id) { continue; } part = &xpc_partitions[partid]; if (part->act_state == XPC_P_INACTIVE || part->act_state == XPC_P_DEACTIVATING) { continue; } /* pull the remote_hb cache line */ bres = xp_bte_copy(part->remote_vars_pa, ia64_tpa((u64) remote_vars), XPC_VARS_ALIGNED_SIZE, (BTE_NOTIFY | BTE_WACQUIRE), NULL); if (bres != BTE_SUCCESS) { XPC_DEACTIVATE_PARTITION(part, xpc_map_bte_errors(bres)); continue; } dev_dbg(xpc_part, "partid = %d, heartbeat = %ld, last_heartbeat" " = %ld, kdb_status = %ld, HB_mask = 0x%lx\n", partid, remote_vars->heartbeat, part->last_heartbeat, remote_vars->kdb_status, remote_vars->heartbeating_to_mask); if (((remote_vars->heartbeat == part->last_heartbeat) && (remote_vars->kdb_status == 0)) || !XPC_HB_ALLOWED(sn_partition_id, remote_vars)) { XPC_DEACTIVATE_PARTITION(part, xpcNoHeartbeat); continue; } part->last_heartbeat = remote_vars->heartbeat; }}/* * Get a copy of the remote partition's rsvd page. * * remote_rp points to a buffer that is cacheline aligned for BTE copies and * assumed to be of size XPC_RSVD_PAGE_ALIGNED_SIZE. */static enum xpc_retvalxpc_get_remote_rp(int nasid, u64 *discovered_nasids, struct xpc_rsvd_page *remote_rp, u64 *remote_rsvd_page_pa){ int bres, i; /* get the reserved page's physical address */ *remote_rsvd_page_pa = xpc_get_rsvd_page_pa(nasid, (u64) remote_rp, XPC_RSVD_PAGE_ALIGNED_SIZE); if (*remote_rsvd_page_pa == 0) { return xpcNoRsvdPageAddr; } /* pull over the reserved page structure */ bres = xp_bte_copy(*remote_rsvd_page_pa, ia64_tpa((u64) remote_rp), XPC_RSVD_PAGE_ALIGNED_SIZE, (BTE_NOTIFY | BTE_WACQUIRE), NULL); if (bres != BTE_SUCCESS) { return xpc_map_bte_errors(bres); } if (discovered_nasids != NULL) { for (i = 0; i < XP_NASID_MASK_WORDS; i++) { discovered_nasids[i] |= remote_rp->part_nasids[i]; } } /* check that the partid is for another partition */ if (remote_rp->partid < 1 || remote_rp->partid > (XP_MAX_PARTITIONS - 1)) { return xpcInvalidPartid; } if (remote_rp->partid == sn_partition_id) { return xpcLocalPartid; } if (XPC_VERSION_MAJOR(remote_rp->version) != XPC_VERSION_MAJOR(XPC_RP_VERSION)) { return xpcBadVersion; } return xpcSuccess;}/* * Get a copy of the remote partition's XPC variables. * * remote_vars points to a buffer that is cacheline aligned for BTE copies and
⌨️ 快捷键说明
复制代码Ctrl + C
搜索代码Ctrl + F
全屏模式F11
增大字号Ctrl + =
减小字号Ctrl + -
显示快捷键?