xref: /openbmc/linux/drivers/misc/sgi-xp/xpc_partition.c (revision aaa3cd694c0c4ae534e8aafdf4227e395c57d6bd)
145d9ca49SDean Nelson /*
245d9ca49SDean Nelson  * This file is subject to the terms and conditions of the GNU General Public
345d9ca49SDean Nelson  * License.  See the file "COPYING" in the main directory of this archive
445d9ca49SDean Nelson  * for more details.
545d9ca49SDean Nelson  *
645d9ca49SDean Nelson  * Copyright (c) 2004-2008 Silicon Graphics, Inc.  All Rights Reserved.
745d9ca49SDean Nelson  */
845d9ca49SDean Nelson 
945d9ca49SDean Nelson /*
1045d9ca49SDean Nelson  * Cross Partition Communication (XPC) partition support.
1145d9ca49SDean Nelson  *
1245d9ca49SDean Nelson  *	This is the part of XPC that detects the presence/absence of
1345d9ca49SDean Nelson  *	other partitions. It provides a heartbeat and monitors the
1445d9ca49SDean Nelson  *	heartbeats of other partitions.
1545d9ca49SDean Nelson  *
1645d9ca49SDean Nelson  */
1745d9ca49SDean Nelson 
1845d9ca49SDean Nelson #include <linux/kernel.h>
1945d9ca49SDean Nelson #include <linux/sysctl.h>
2045d9ca49SDean Nelson #include <linux/cache.h>
2145d9ca49SDean Nelson #include <linux/mmzone.h>
2245d9ca49SDean Nelson #include <linux/nodemask.h>
2345d9ca49SDean Nelson #include <asm/sn/intr.h>
2445d9ca49SDean Nelson #include <asm/sn/sn_sal.h>
2545d9ca49SDean Nelson #include <asm/sn/nodepda.h>
2645d9ca49SDean Nelson #include <asm/sn/addrs.h>
2745d9ca49SDean Nelson #include "xpc.h"
2845d9ca49SDean Nelson 
2945d9ca49SDean Nelson /* XPC is exiting flag */
3045d9ca49SDean Nelson int xpc_exiting;
3145d9ca49SDean Nelson 
3245d9ca49SDean Nelson /* SH_IPI_ACCESS shub register value on startup */
3345d9ca49SDean Nelson static u64 xpc_sh1_IPI_access;
3445d9ca49SDean Nelson static u64 xpc_sh2_IPI_access0;
3545d9ca49SDean Nelson static u64 xpc_sh2_IPI_access1;
3645d9ca49SDean Nelson static u64 xpc_sh2_IPI_access2;
3745d9ca49SDean Nelson static u64 xpc_sh2_IPI_access3;
3845d9ca49SDean Nelson 
3945d9ca49SDean Nelson /* original protection values for each node */
4045d9ca49SDean Nelson u64 xpc_prot_vec[MAX_NUMNODES];
4145d9ca49SDean Nelson 
4245d9ca49SDean Nelson /* this partition's reserved page pointers */
4345d9ca49SDean Nelson struct xpc_rsvd_page *xpc_rsvd_page;
4445d9ca49SDean Nelson static u64 *xpc_part_nasids;
4533ba3c77SDean Nelson u64 *xpc_mach_nasids;
4645d9ca49SDean Nelson 
4794bd2708SDean Nelson /* >>> next two variables should be 'xpc_' if they remain here */
4894bd2708SDean Nelson static int xp_sizeof_nasid_mask;	/* actual size in bytes of nasid mask */
4994bd2708SDean Nelson int xp_nasid_mask_words;	/* actual size in words of nasid mask */
5045d9ca49SDean Nelson 
51bc63d387SDean Nelson struct xpc_partition *xpc_partitions;
5245d9ca49SDean Nelson 
5345d9ca49SDean Nelson /*
5445d9ca49SDean Nelson  * Generic buffer used to store a local copy of portions of a remote
5545d9ca49SDean Nelson  * partition's reserved page (either its header and part_nasids mask,
5645d9ca49SDean Nelson  * or its vars).
5745d9ca49SDean Nelson  */
5845d9ca49SDean Nelson char *xpc_remote_copy_buffer;
5945d9ca49SDean Nelson void *xpc_remote_copy_buffer_base;
6045d9ca49SDean Nelson 
6145d9ca49SDean Nelson /*
6245d9ca49SDean Nelson  * Guarantee that the kmalloc'd memory is cacheline aligned.
6345d9ca49SDean Nelson  */
6445d9ca49SDean Nelson void *
6545d9ca49SDean Nelson xpc_kmalloc_cacheline_aligned(size_t size, gfp_t flags, void **base)
6645d9ca49SDean Nelson {
6745d9ca49SDean Nelson 	/* see if kmalloc will give us cachline aligned memory by default */
6845d9ca49SDean Nelson 	*base = kmalloc(size, flags);
692c2b94f9SDean Nelson 	if (*base == NULL)
7045d9ca49SDean Nelson 		return NULL;
712c2b94f9SDean Nelson 
722c2b94f9SDean Nelson 	if ((u64)*base == L1_CACHE_ALIGN((u64)*base))
7345d9ca49SDean Nelson 		return *base;
742c2b94f9SDean Nelson 
7545d9ca49SDean Nelson 	kfree(*base);
7645d9ca49SDean Nelson 
7745d9ca49SDean Nelson 	/* nope, we'll have to do it ourselves */
7845d9ca49SDean Nelson 	*base = kmalloc(size + L1_CACHE_BYTES, flags);
792c2b94f9SDean Nelson 	if (*base == NULL)
8045d9ca49SDean Nelson 		return NULL;
812c2b94f9SDean Nelson 
8245d9ca49SDean Nelson 	return (void *)L1_CACHE_ALIGN((u64)*base);
8345d9ca49SDean Nelson }
8445d9ca49SDean Nelson 
8545d9ca49SDean Nelson /*
8645d9ca49SDean Nelson  * Given a nasid, get the physical address of the  partition's reserved page
8745d9ca49SDean Nelson  * for that nasid. This function returns 0 on any error.
8845d9ca49SDean Nelson  */
8945d9ca49SDean Nelson static u64
9045d9ca49SDean Nelson xpc_get_rsvd_page_pa(int nasid)
9145d9ca49SDean Nelson {
92908787dbSDean Nelson 	enum xp_retval ret;
9345d9ca49SDean Nelson 	s64 status;
9445d9ca49SDean Nelson 	u64 cookie = 0;
9545d9ca49SDean Nelson 	u64 rp_pa = nasid;	/* seed with nasid */
9645d9ca49SDean Nelson 	u64 len = 0;
9745d9ca49SDean Nelson 	u64 buf = buf;
9845d9ca49SDean Nelson 	u64 buf_len = 0;
9945d9ca49SDean Nelson 	void *buf_base = NULL;
10045d9ca49SDean Nelson 
10145d9ca49SDean Nelson 	while (1) {
10245d9ca49SDean Nelson 
10345d9ca49SDean Nelson 		status = sn_partition_reserved_page_pa(buf, &cookie, &rp_pa,
10445d9ca49SDean Nelson 						       &len);
10545d9ca49SDean Nelson 
10645d9ca49SDean Nelson 		dev_dbg(xpc_part, "SAL returned with status=%li, cookie="
10745d9ca49SDean Nelson 			"0x%016lx, address=0x%016lx, len=0x%016lx\n",
10845d9ca49SDean Nelson 			status, cookie, rp_pa, len);
10945d9ca49SDean Nelson 
1102c2b94f9SDean Nelson 		if (status != SALRET_MORE_PASSES)
11145d9ca49SDean Nelson 			break;
11245d9ca49SDean Nelson 
113908787dbSDean Nelson 		/* >>> L1_CACHE_ALIGN() is only a sn2-bte_copy requirement */
11445d9ca49SDean Nelson 		if (L1_CACHE_ALIGN(len) > buf_len) {
11545d9ca49SDean Nelson 			kfree(buf_base);
11645d9ca49SDean Nelson 			buf_len = L1_CACHE_ALIGN(len);
11745d9ca49SDean Nelson 			buf = (u64)xpc_kmalloc_cacheline_aligned(buf_len,
1184a3ad2ddSDean Nelson 								 GFP_KERNEL,
1194a3ad2ddSDean Nelson 								 &buf_base);
12045d9ca49SDean Nelson 			if (buf_base == NULL) {
12145d9ca49SDean Nelson 				dev_err(xpc_part, "unable to kmalloc "
12245d9ca49SDean Nelson 					"len=0x%016lx\n", buf_len);
12345d9ca49SDean Nelson 				status = SALRET_ERROR;
12445d9ca49SDean Nelson 				break;
12545d9ca49SDean Nelson 			}
12645d9ca49SDean Nelson 		}
12745d9ca49SDean Nelson 
128908787dbSDean Nelson 		ret = xp_remote_memcpy((void *)buf, (void *)rp_pa, buf_len);
129908787dbSDean Nelson 		if (ret != xpSuccess) {
130908787dbSDean Nelson 			dev_dbg(xpc_part, "xp_remote_memcpy failed %d\n", ret);
13145d9ca49SDean Nelson 			status = SALRET_ERROR;
13245d9ca49SDean Nelson 			break;
13345d9ca49SDean Nelson 		}
13445d9ca49SDean Nelson 	}
13545d9ca49SDean Nelson 
13645d9ca49SDean Nelson 	kfree(buf_base);
13745d9ca49SDean Nelson 
1382c2b94f9SDean Nelson 	if (status != SALRET_OK)
13945d9ca49SDean Nelson 		rp_pa = 0;
1402c2b94f9SDean Nelson 
14145d9ca49SDean Nelson 	dev_dbg(xpc_part, "reserved page at phys address 0x%016lx\n", rp_pa);
14245d9ca49SDean Nelson 	return rp_pa;
14345d9ca49SDean Nelson }
14445d9ca49SDean Nelson 
14545d9ca49SDean Nelson /*
14645d9ca49SDean Nelson  * Fill the partition reserved page with the information needed by
14745d9ca49SDean Nelson  * other partitions to discover we are alive and establish initial
14845d9ca49SDean Nelson  * communications.
14945d9ca49SDean Nelson  */
15045d9ca49SDean Nelson struct xpc_rsvd_page *
15194bd2708SDean Nelson xpc_setup_rsvd_page(void)
15245d9ca49SDean Nelson {
15345d9ca49SDean Nelson 	struct xpc_rsvd_page *rp;
15494bd2708SDean Nelson 	u64 rp_pa;
155*aaa3cd69SDean Nelson 	unsigned long new_stamp;
15645d9ca49SDean Nelson 
15745d9ca49SDean Nelson 	/* get the local reserved page's address */
15845d9ca49SDean Nelson 
15945d9ca49SDean Nelson 	preempt_disable();
16045d9ca49SDean Nelson 	rp_pa = xpc_get_rsvd_page_pa(cpuid_to_nasid(smp_processor_id()));
16145d9ca49SDean Nelson 	preempt_enable();
16245d9ca49SDean Nelson 	if (rp_pa == 0) {
16345d9ca49SDean Nelson 		dev_err(xpc_part, "SAL failed to locate the reserved page\n");
16445d9ca49SDean Nelson 		return NULL;
16545d9ca49SDean Nelson 	}
16645d9ca49SDean Nelson 	rp = (struct xpc_rsvd_page *)__va(rp_pa);
16745d9ca49SDean Nelson 
16894bd2708SDean Nelson 	if (rp->SAL_version < 3) {
16994bd2708SDean Nelson 		/* SAL_versions < 3 had a SAL_partid defined as a u8 */
17094bd2708SDean Nelson 		rp->SAL_partid &= 0xff;
17194bd2708SDean Nelson 	}
17294bd2708SDean Nelson 	BUG_ON(rp->SAL_partid != sn_partition_id);
17394bd2708SDean Nelson 
17494bd2708SDean Nelson 	if (rp->SAL_partid < 0 || rp->SAL_partid >= xp_max_npartitions) {
17594bd2708SDean Nelson 		dev_err(xpc_part, "the reserved page's partid of %d is outside "
17694bd2708SDean Nelson 			"supported range (< 0 || >= %d)\n", rp->SAL_partid,
17794bd2708SDean Nelson 			xp_max_npartitions);
17845d9ca49SDean Nelson 		return NULL;
17945d9ca49SDean Nelson 	}
18045d9ca49SDean Nelson 
18145d9ca49SDean Nelson 	rp->version = XPC_RP_VERSION;
18294bd2708SDean Nelson 	rp->max_npartitions = xp_max_npartitions;
18345d9ca49SDean Nelson 
18445d9ca49SDean Nelson 	/* establish the actual sizes of the nasid masks */
18545d9ca49SDean Nelson 	if (rp->SAL_version == 1) {
18645d9ca49SDean Nelson 		/* SAL_version 1 didn't set the nasids_size field */
18794bd2708SDean Nelson 		rp->SAL_nasids_size = 128;
18845d9ca49SDean Nelson 	}
18994bd2708SDean Nelson 	xp_sizeof_nasid_mask = rp->SAL_nasids_size;
19094bd2708SDean Nelson 	xp_nasid_mask_words = DIV_ROUND_UP(xp_sizeof_nasid_mask,
19194bd2708SDean Nelson 					   BYTES_PER_WORD);
19245d9ca49SDean Nelson 
19345d9ca49SDean Nelson 	/* setup the pointers to the various items in the reserved page */
19445d9ca49SDean Nelson 	xpc_part_nasids = XPC_RP_PART_NASIDS(rp);
19545d9ca49SDean Nelson 	xpc_mach_nasids = XPC_RP_MACH_NASIDS(rp);
19645d9ca49SDean Nelson 
19794bd2708SDean Nelson 	if (xpc_rsvd_page_init(rp) != xpSuccess)
19845d9ca49SDean Nelson 		return NULL;
19945d9ca49SDean Nelson 
20045d9ca49SDean Nelson 	/*
20194bd2708SDean Nelson 	 * Set timestamp of when reserved page was setup by XPC.
20245d9ca49SDean Nelson 	 * This signifies to the remote partition that our reserved
20345d9ca49SDean Nelson 	 * page is initialized.
20445d9ca49SDean Nelson 	 */
205*aaa3cd69SDean Nelson 	new_stamp = jiffies;
206*aaa3cd69SDean Nelson 	if (new_stamp == 0 || new_stamp == rp->stamp)
207*aaa3cd69SDean Nelson 		new_stamp++;
208*aaa3cd69SDean Nelson 	rp->stamp = new_stamp;
20945d9ca49SDean Nelson 
21045d9ca49SDean Nelson 	return rp;
21145d9ca49SDean Nelson }
21245d9ca49SDean Nelson 
21345d9ca49SDean Nelson /*
21445d9ca49SDean Nelson  * Change protections to allow IPI operations (and AMO operations on
21545d9ca49SDean Nelson  * Shub 1.1 systems).
21645d9ca49SDean Nelson  */
21745d9ca49SDean Nelson void
21845d9ca49SDean Nelson xpc_allow_IPI_ops(void)
21945d9ca49SDean Nelson {
22045d9ca49SDean Nelson 	int node;
22145d9ca49SDean Nelson 	int nasid;
22245d9ca49SDean Nelson 
2232c2b94f9SDean Nelson 	/* >>> Change SH_IPI_ACCESS code to use SAL call once it is available */
22445d9ca49SDean Nelson 
22545d9ca49SDean Nelson 	if (is_shub2()) {
22645d9ca49SDean Nelson 		xpc_sh2_IPI_access0 =
22745d9ca49SDean Nelson 		    (u64)HUB_L((u64 *)LOCAL_MMR_ADDR(SH2_IPI_ACCESS0));
22845d9ca49SDean Nelson 		xpc_sh2_IPI_access1 =
22945d9ca49SDean Nelson 		    (u64)HUB_L((u64 *)LOCAL_MMR_ADDR(SH2_IPI_ACCESS1));
23045d9ca49SDean Nelson 		xpc_sh2_IPI_access2 =
23145d9ca49SDean Nelson 		    (u64)HUB_L((u64 *)LOCAL_MMR_ADDR(SH2_IPI_ACCESS2));
23245d9ca49SDean Nelson 		xpc_sh2_IPI_access3 =
23345d9ca49SDean Nelson 		    (u64)HUB_L((u64 *)LOCAL_MMR_ADDR(SH2_IPI_ACCESS3));
23445d9ca49SDean Nelson 
23545d9ca49SDean Nelson 		for_each_online_node(node) {
23645d9ca49SDean Nelson 			nasid = cnodeid_to_nasid(node);
23745d9ca49SDean Nelson 			HUB_S((u64 *)GLOBAL_MMR_ADDR(nasid, SH2_IPI_ACCESS0),
23845d9ca49SDean Nelson 			      -1UL);
23945d9ca49SDean Nelson 			HUB_S((u64 *)GLOBAL_MMR_ADDR(nasid, SH2_IPI_ACCESS1),
24045d9ca49SDean Nelson 			      -1UL);
24145d9ca49SDean Nelson 			HUB_S((u64 *)GLOBAL_MMR_ADDR(nasid, SH2_IPI_ACCESS2),
24245d9ca49SDean Nelson 			      -1UL);
24345d9ca49SDean Nelson 			HUB_S((u64 *)GLOBAL_MMR_ADDR(nasid, SH2_IPI_ACCESS3),
24445d9ca49SDean Nelson 			      -1UL);
24545d9ca49SDean Nelson 		}
24645d9ca49SDean Nelson 
24745d9ca49SDean Nelson 	} else {
24845d9ca49SDean Nelson 		xpc_sh1_IPI_access =
24945d9ca49SDean Nelson 		    (u64)HUB_L((u64 *)LOCAL_MMR_ADDR(SH1_IPI_ACCESS));
25045d9ca49SDean Nelson 
25145d9ca49SDean Nelson 		for_each_online_node(node) {
25245d9ca49SDean Nelson 			nasid = cnodeid_to_nasid(node);
25345d9ca49SDean Nelson 			HUB_S((u64 *)GLOBAL_MMR_ADDR(nasid, SH1_IPI_ACCESS),
25445d9ca49SDean Nelson 			      -1UL);
25545d9ca49SDean Nelson 
25645d9ca49SDean Nelson 			/*
25745d9ca49SDean Nelson 			 * Since the BIST collides with memory operations on
25845d9ca49SDean Nelson 			 * SHUB 1.1 sn_change_memprotect() cannot be used.
25945d9ca49SDean Nelson 			 */
26045d9ca49SDean Nelson 			if (enable_shub_wars_1_1()) {
26145d9ca49SDean Nelson 				/* open up everything */
26245d9ca49SDean Nelson 				xpc_prot_vec[node] = (u64)HUB_L((u64 *)
2634a3ad2ddSDean Nelson 								GLOBAL_MMR_ADDR
2644a3ad2ddSDean Nelson 								(nasid,
26545d9ca49SDean Nelson 						  SH1_MD_DQLP_MMR_DIR_PRIVEC0));
2664a3ad2ddSDean Nelson 				HUB_S((u64 *)
2674a3ad2ddSDean Nelson 				      GLOBAL_MMR_ADDR(nasid,
26845d9ca49SDean Nelson 						   SH1_MD_DQLP_MMR_DIR_PRIVEC0),
26945d9ca49SDean Nelson 				      -1UL);
2704a3ad2ddSDean Nelson 				HUB_S((u64 *)
2714a3ad2ddSDean Nelson 				      GLOBAL_MMR_ADDR(nasid,
27245d9ca49SDean Nelson 						   SH1_MD_DQRP_MMR_DIR_PRIVEC0),
27345d9ca49SDean Nelson 				      -1UL);
27445d9ca49SDean Nelson 			}
27545d9ca49SDean Nelson 		}
27645d9ca49SDean Nelson 	}
27745d9ca49SDean Nelson }
27845d9ca49SDean Nelson 
27945d9ca49SDean Nelson /*
28045d9ca49SDean Nelson  * Restrict protections to disallow IPI operations (and AMO operations on
28145d9ca49SDean Nelson  * Shub 1.1 systems).
28245d9ca49SDean Nelson  */
28345d9ca49SDean Nelson void
28445d9ca49SDean Nelson xpc_restrict_IPI_ops(void)
28545d9ca49SDean Nelson {
28645d9ca49SDean Nelson 	int node;
28745d9ca49SDean Nelson 	int nasid;
28845d9ca49SDean Nelson 
2892c2b94f9SDean Nelson 	/* >>> Change SH_IPI_ACCESS code to use SAL call once it is available */
29045d9ca49SDean Nelson 
29145d9ca49SDean Nelson 	if (is_shub2()) {
29245d9ca49SDean Nelson 
29345d9ca49SDean Nelson 		for_each_online_node(node) {
29445d9ca49SDean Nelson 			nasid = cnodeid_to_nasid(node);
29545d9ca49SDean Nelson 			HUB_S((u64 *)GLOBAL_MMR_ADDR(nasid, SH2_IPI_ACCESS0),
29645d9ca49SDean Nelson 			      xpc_sh2_IPI_access0);
29745d9ca49SDean Nelson 			HUB_S((u64 *)GLOBAL_MMR_ADDR(nasid, SH2_IPI_ACCESS1),
29845d9ca49SDean Nelson 			      xpc_sh2_IPI_access1);
29945d9ca49SDean Nelson 			HUB_S((u64 *)GLOBAL_MMR_ADDR(nasid, SH2_IPI_ACCESS2),
30045d9ca49SDean Nelson 			      xpc_sh2_IPI_access2);
30145d9ca49SDean Nelson 			HUB_S((u64 *)GLOBAL_MMR_ADDR(nasid, SH2_IPI_ACCESS3),
30245d9ca49SDean Nelson 			      xpc_sh2_IPI_access3);
30345d9ca49SDean Nelson 		}
30445d9ca49SDean Nelson 
30545d9ca49SDean Nelson 	} else {
30645d9ca49SDean Nelson 
30745d9ca49SDean Nelson 		for_each_online_node(node) {
30845d9ca49SDean Nelson 			nasid = cnodeid_to_nasid(node);
30945d9ca49SDean Nelson 			HUB_S((u64 *)GLOBAL_MMR_ADDR(nasid, SH1_IPI_ACCESS),
31045d9ca49SDean Nelson 			      xpc_sh1_IPI_access);
31145d9ca49SDean Nelson 
31245d9ca49SDean Nelson 			if (enable_shub_wars_1_1()) {
31345d9ca49SDean Nelson 				HUB_S((u64 *)GLOBAL_MMR_ADDR(nasid,
31445d9ca49SDean Nelson 						   SH1_MD_DQLP_MMR_DIR_PRIVEC0),
31545d9ca49SDean Nelson 				      xpc_prot_vec[node]);
31645d9ca49SDean Nelson 				HUB_S((u64 *)GLOBAL_MMR_ADDR(nasid,
31745d9ca49SDean Nelson 						   SH1_MD_DQRP_MMR_DIR_PRIVEC0),
31845d9ca49SDean Nelson 				      xpc_prot_vec[node]);
31945d9ca49SDean Nelson 			}
32045d9ca49SDean Nelson 		}
32145d9ca49SDean Nelson 	}
32245d9ca49SDean Nelson }
32345d9ca49SDean Nelson 
32445d9ca49SDean Nelson /*
32545d9ca49SDean Nelson  * Get a copy of a portion of the remote partition's rsvd page.
32645d9ca49SDean Nelson  *
32745d9ca49SDean Nelson  * remote_rp points to a buffer that is cacheline aligned for BTE copies and
32845d9ca49SDean Nelson  * is large enough to contain a copy of their reserved page header and
32945d9ca49SDean Nelson  * part_nasids mask.
33045d9ca49SDean Nelson  */
33133ba3c77SDean Nelson enum xp_retval
33245d9ca49SDean Nelson xpc_get_remote_rp(int nasid, u64 *discovered_nasids,
33345d9ca49SDean Nelson 		  struct xpc_rsvd_page *remote_rp, u64 *remote_rp_pa)
33445d9ca49SDean Nelson {
335908787dbSDean Nelson 	int i;
336908787dbSDean Nelson 	enum xp_retval ret;
33745d9ca49SDean Nelson 
33845d9ca49SDean Nelson 	/* get the reserved page's physical address */
33945d9ca49SDean Nelson 
34045d9ca49SDean Nelson 	*remote_rp_pa = xpc_get_rsvd_page_pa(nasid);
3412c2b94f9SDean Nelson 	if (*remote_rp_pa == 0)
34265c17b80SDean Nelson 		return xpNoRsvdPageAddr;
34345d9ca49SDean Nelson 
34445d9ca49SDean Nelson 	/* pull over the reserved page header and part_nasids mask */
345908787dbSDean Nelson 	ret = xp_remote_memcpy(remote_rp, (void *)*remote_rp_pa,
34694bd2708SDean Nelson 			       XPC_RP_HEADER_SIZE + xp_sizeof_nasid_mask);
347908787dbSDean Nelson 	if (ret != xpSuccess)
348908787dbSDean Nelson 		return ret;
34945d9ca49SDean Nelson 
35045d9ca49SDean Nelson 	if (discovered_nasids != NULL) {
35145d9ca49SDean Nelson 		u64 *remote_part_nasids = XPC_RP_PART_NASIDS(remote_rp);
35245d9ca49SDean Nelson 
3532c2b94f9SDean Nelson 		for (i = 0; i < xp_nasid_mask_words; i++)
35445d9ca49SDean Nelson 			discovered_nasids[i] |= remote_part_nasids[i];
35545d9ca49SDean Nelson 	}
35645d9ca49SDean Nelson 
357*aaa3cd69SDean Nelson 	/* see if the reserved page has been set up by XPC */
358*aaa3cd69SDean Nelson 	if (remote_rp->stamp == 0)
35994bd2708SDean Nelson 		return xpRsvdPageNotSet;
36094bd2708SDean Nelson 
36145d9ca49SDean Nelson 	if (XPC_VERSION_MAJOR(remote_rp->version) !=
36245d9ca49SDean Nelson 	    XPC_VERSION_MAJOR(XPC_RP_VERSION)) {
36365c17b80SDean Nelson 		return xpBadVersion;
36445d9ca49SDean Nelson 	}
36545d9ca49SDean Nelson 
366*aaa3cd69SDean Nelson 	/* check that both local and remote partids are valid for each side */
367*aaa3cd69SDean Nelson 	if (remote_rp->SAL_partid < 0 ||
368*aaa3cd69SDean Nelson 	    remote_rp->SAL_partid >= xp_max_npartitions ||
369*aaa3cd69SDean Nelson 	    remote_rp->max_npartitions <= sn_partition_id) {
37094bd2708SDean Nelson 		return xpInvalidPartid;
371*aaa3cd69SDean Nelson 	}
372*aaa3cd69SDean Nelson 
373*aaa3cd69SDean Nelson 	if (remote_rp->SAL_partid == sn_partition_id)
374*aaa3cd69SDean Nelson 		return xpLocalPartid;
37594bd2708SDean Nelson 
37665c17b80SDean Nelson 	return xpSuccess;
37745d9ca49SDean Nelson }
37845d9ca49SDean Nelson 
37945d9ca49SDean Nelson /*
38045d9ca49SDean Nelson  * See if the other side has responded to a partition disengage request
38145d9ca49SDean Nelson  * from us.
38245d9ca49SDean Nelson  */
38345d9ca49SDean Nelson int
38445d9ca49SDean Nelson xpc_partition_disengaged(struct xpc_partition *part)
38545d9ca49SDean Nelson {
38664d032baSDean Nelson 	short partid = XPC_PARTID(part);
38745d9ca49SDean Nelson 	int disengaged;
38845d9ca49SDean Nelson 
38945d9ca49SDean Nelson 	disengaged = (xpc_partition_engaged(1UL << partid) == 0);
39045d9ca49SDean Nelson 	if (part->disengage_request_timeout) {
39145d9ca49SDean Nelson 		if (!disengaged) {
392*aaa3cd69SDean Nelson 			if (time_is_after_jiffies(part->
393*aaa3cd69SDean Nelson 						  disengage_request_timeout)) {
39445d9ca49SDean Nelson 				/* timelimit hasn't been reached yet */
39545d9ca49SDean Nelson 				return 0;
39645d9ca49SDean Nelson 			}
39745d9ca49SDean Nelson 
39845d9ca49SDean Nelson 			/*
39945d9ca49SDean Nelson 			 * Other side hasn't responded to our disengage
40045d9ca49SDean Nelson 			 * request in a timely fashion, so assume it's dead.
40145d9ca49SDean Nelson 			 */
40245d9ca49SDean Nelson 
40345d9ca49SDean Nelson 			dev_info(xpc_part, "disengage from remote partition %d "
40445d9ca49SDean Nelson 				 "timed out\n", partid);
40545d9ca49SDean Nelson 			xpc_disengage_request_timedout = 1;
40645d9ca49SDean Nelson 			xpc_clear_partition_engaged(1UL << partid);
40745d9ca49SDean Nelson 			disengaged = 1;
40845d9ca49SDean Nelson 		}
40945d9ca49SDean Nelson 		part->disengage_request_timeout = 0;
41045d9ca49SDean Nelson 
41145d9ca49SDean Nelson 		/* cancel the timer function, provided it's not us */
41245d9ca49SDean Nelson 		if (!in_interrupt()) {
41345d9ca49SDean Nelson 			del_singleshot_timer_sync(&part->
41445d9ca49SDean Nelson 						  disengage_request_timer);
41545d9ca49SDean Nelson 		}
41645d9ca49SDean Nelson 
41745d9ca49SDean Nelson 		DBUG_ON(part->act_state != XPC_P_DEACTIVATING &&
41845d9ca49SDean Nelson 			part->act_state != XPC_P_INACTIVE);
4192c2b94f9SDean Nelson 		if (part->act_state != XPC_P_INACTIVE)
42045d9ca49SDean Nelson 			xpc_wakeup_channel_mgr(part);
42145d9ca49SDean Nelson 
4222c2b94f9SDean Nelson 		if (XPC_SUPPORTS_DISENGAGE_REQUEST(part->remote_vars_version))
42345d9ca49SDean Nelson 			xpc_cancel_partition_disengage_request(part);
42445d9ca49SDean Nelson 	}
42545d9ca49SDean Nelson 	return disengaged;
42645d9ca49SDean Nelson }
42745d9ca49SDean Nelson 
42845d9ca49SDean Nelson /*
42945d9ca49SDean Nelson  * Mark specified partition as active.
43045d9ca49SDean Nelson  */
43165c17b80SDean Nelson enum xp_retval
43245d9ca49SDean Nelson xpc_mark_partition_active(struct xpc_partition *part)
43345d9ca49SDean Nelson {
43445d9ca49SDean Nelson 	unsigned long irq_flags;
43565c17b80SDean Nelson 	enum xp_retval ret;
43645d9ca49SDean Nelson 
43745d9ca49SDean Nelson 	dev_dbg(xpc_part, "setting partition %d to ACTIVE\n", XPC_PARTID(part));
43845d9ca49SDean Nelson 
43945d9ca49SDean Nelson 	spin_lock_irqsave(&part->act_lock, irq_flags);
44045d9ca49SDean Nelson 	if (part->act_state == XPC_P_ACTIVATING) {
44145d9ca49SDean Nelson 		part->act_state = XPC_P_ACTIVE;
44265c17b80SDean Nelson 		ret = xpSuccess;
44345d9ca49SDean Nelson 	} else {
44465c17b80SDean Nelson 		DBUG_ON(part->reason == xpSuccess);
44545d9ca49SDean Nelson 		ret = part->reason;
44645d9ca49SDean Nelson 	}
44745d9ca49SDean Nelson 	spin_unlock_irqrestore(&part->act_lock, irq_flags);
44845d9ca49SDean Nelson 
44945d9ca49SDean Nelson 	return ret;
45045d9ca49SDean Nelson }
45145d9ca49SDean Nelson 
45245d9ca49SDean Nelson /*
45345d9ca49SDean Nelson  * Notify XPC that the partition is down.
45445d9ca49SDean Nelson  */
45545d9ca49SDean Nelson void
45645d9ca49SDean Nelson xpc_deactivate_partition(const int line, struct xpc_partition *part,
45765c17b80SDean Nelson 			 enum xp_retval reason)
45845d9ca49SDean Nelson {
45945d9ca49SDean Nelson 	unsigned long irq_flags;
46045d9ca49SDean Nelson 
46145d9ca49SDean Nelson 	spin_lock_irqsave(&part->act_lock, irq_flags);
46245d9ca49SDean Nelson 
46345d9ca49SDean Nelson 	if (part->act_state == XPC_P_INACTIVE) {
46445d9ca49SDean Nelson 		XPC_SET_REASON(part, reason, line);
46545d9ca49SDean Nelson 		spin_unlock_irqrestore(&part->act_lock, irq_flags);
46665c17b80SDean Nelson 		if (reason == xpReactivating) {
46745d9ca49SDean Nelson 			/* we interrupt ourselves to reactivate partition */
46833ba3c77SDean Nelson 			xpc_IPI_send_local_reactivate(part->reactivate_nasid);
46945d9ca49SDean Nelson 		}
47045d9ca49SDean Nelson 		return;
47145d9ca49SDean Nelson 	}
47245d9ca49SDean Nelson 	if (part->act_state == XPC_P_DEACTIVATING) {
47365c17b80SDean Nelson 		if ((part->reason == xpUnloading && reason != xpUnloading) ||
47465c17b80SDean Nelson 		    reason == xpReactivating) {
47545d9ca49SDean Nelson 			XPC_SET_REASON(part, reason, line);
47645d9ca49SDean Nelson 		}
47745d9ca49SDean Nelson 		spin_unlock_irqrestore(&part->act_lock, irq_flags);
47845d9ca49SDean Nelson 		return;
47945d9ca49SDean Nelson 	}
48045d9ca49SDean Nelson 
48145d9ca49SDean Nelson 	part->act_state = XPC_P_DEACTIVATING;
48245d9ca49SDean Nelson 	XPC_SET_REASON(part, reason, line);
48345d9ca49SDean Nelson 
48445d9ca49SDean Nelson 	spin_unlock_irqrestore(&part->act_lock, irq_flags);
48545d9ca49SDean Nelson 
48645d9ca49SDean Nelson 	if (XPC_SUPPORTS_DISENGAGE_REQUEST(part->remote_vars_version)) {
48745d9ca49SDean Nelson 		xpc_request_partition_disengage(part);
48845d9ca49SDean Nelson 		xpc_IPI_send_disengage(part);
48945d9ca49SDean Nelson 
49045d9ca49SDean Nelson 		/* set a timelimit on the disengage request */
49145d9ca49SDean Nelson 		part->disengage_request_timeout = jiffies +
49245d9ca49SDean Nelson 		    (xpc_disengage_request_timelimit * HZ);
49345d9ca49SDean Nelson 		part->disengage_request_timer.expires =
49445d9ca49SDean Nelson 		    part->disengage_request_timeout;
49545d9ca49SDean Nelson 		add_timer(&part->disengage_request_timer);
49645d9ca49SDean Nelson 	}
49745d9ca49SDean Nelson 
49845d9ca49SDean Nelson 	dev_dbg(xpc_part, "bringing partition %d down, reason = %d\n",
49945d9ca49SDean Nelson 		XPC_PARTID(part), reason);
50045d9ca49SDean Nelson 
50145d9ca49SDean Nelson 	xpc_partition_going_down(part, reason);
50245d9ca49SDean Nelson }
50345d9ca49SDean Nelson 
50445d9ca49SDean Nelson /*
50545d9ca49SDean Nelson  * Mark specified partition as inactive.
50645d9ca49SDean Nelson  */
50745d9ca49SDean Nelson void
50845d9ca49SDean Nelson xpc_mark_partition_inactive(struct xpc_partition *part)
50945d9ca49SDean Nelson {
51045d9ca49SDean Nelson 	unsigned long irq_flags;
51145d9ca49SDean Nelson 
51245d9ca49SDean Nelson 	dev_dbg(xpc_part, "setting partition %d to INACTIVE\n",
51345d9ca49SDean Nelson 		XPC_PARTID(part));
51445d9ca49SDean Nelson 
51545d9ca49SDean Nelson 	spin_lock_irqsave(&part->act_lock, irq_flags);
51645d9ca49SDean Nelson 	part->act_state = XPC_P_INACTIVE;
51745d9ca49SDean Nelson 	spin_unlock_irqrestore(&part->act_lock, irq_flags);
51845d9ca49SDean Nelson 	part->remote_rp_pa = 0;
51945d9ca49SDean Nelson }
52045d9ca49SDean Nelson 
52145d9ca49SDean Nelson /*
52245d9ca49SDean Nelson  * SAL has provided a partition and machine mask.  The partition mask
52345d9ca49SDean Nelson  * contains a bit for each even nasid in our partition.  The machine
52445d9ca49SDean Nelson  * mask contains a bit for each even nasid in the entire machine.
52545d9ca49SDean Nelson  *
52645d9ca49SDean Nelson  * Using those two bit arrays, we can determine which nasids are
52745d9ca49SDean Nelson  * known in the machine.  Each should also have a reserved page
52845d9ca49SDean Nelson  * initialized if they are available for partitioning.
52945d9ca49SDean Nelson  */
53045d9ca49SDean Nelson void
53145d9ca49SDean Nelson xpc_discovery(void)
53245d9ca49SDean Nelson {
53345d9ca49SDean Nelson 	void *remote_rp_base;
53445d9ca49SDean Nelson 	struct xpc_rsvd_page *remote_rp;
53545d9ca49SDean Nelson 	u64 remote_rp_pa;
53645d9ca49SDean Nelson 	int region;
53745d9ca49SDean Nelson 	int region_size;
53845d9ca49SDean Nelson 	int max_regions;
53945d9ca49SDean Nelson 	int nasid;
54045d9ca49SDean Nelson 	struct xpc_rsvd_page *rp;
54145d9ca49SDean Nelson 	u64 *discovered_nasids;
54265c17b80SDean Nelson 	enum xp_retval ret;
54345d9ca49SDean Nelson 
54445d9ca49SDean Nelson 	remote_rp = xpc_kmalloc_cacheline_aligned(XPC_RP_HEADER_SIZE +
54594bd2708SDean Nelson 						  xp_sizeof_nasid_mask,
54645d9ca49SDean Nelson 						  GFP_KERNEL, &remote_rp_base);
5472c2b94f9SDean Nelson 	if (remote_rp == NULL)
54845d9ca49SDean Nelson 		return;
5492c2b94f9SDean Nelson 
55045d9ca49SDean Nelson 	discovered_nasids = kzalloc(sizeof(u64) * xp_nasid_mask_words,
55145d9ca49SDean Nelson 				    GFP_KERNEL);
55245d9ca49SDean Nelson 	if (discovered_nasids == NULL) {
55345d9ca49SDean Nelson 		kfree(remote_rp_base);
55445d9ca49SDean Nelson 		return;
55545d9ca49SDean Nelson 	}
55645d9ca49SDean Nelson 
55745d9ca49SDean Nelson 	rp = (struct xpc_rsvd_page *)xpc_rsvd_page;
55845d9ca49SDean Nelson 
55945d9ca49SDean Nelson 	/*
56045d9ca49SDean Nelson 	 * The term 'region' in this context refers to the minimum number of
56145d9ca49SDean Nelson 	 * nodes that can comprise an access protection grouping. The access
56245d9ca49SDean Nelson 	 * protection is in regards to memory, IOI and IPI.
56345d9ca49SDean Nelson 	 */
56445d9ca49SDean Nelson 	max_regions = 64;
56545d9ca49SDean Nelson 	region_size = sn_region_size;
56645d9ca49SDean Nelson 
56745d9ca49SDean Nelson 	switch (region_size) {
56845d9ca49SDean Nelson 	case 128:
56945d9ca49SDean Nelson 		max_regions *= 2;
57045d9ca49SDean Nelson 	case 64:
57145d9ca49SDean Nelson 		max_regions *= 2;
57245d9ca49SDean Nelson 	case 32:
57345d9ca49SDean Nelson 		max_regions *= 2;
57445d9ca49SDean Nelson 		region_size = 16;
57545d9ca49SDean Nelson 		DBUG_ON(!is_shub2());
57645d9ca49SDean Nelson 	}
57745d9ca49SDean Nelson 
57845d9ca49SDean Nelson 	for (region = 0; region < max_regions; region++) {
57945d9ca49SDean Nelson 
5802c2b94f9SDean Nelson 		if (xpc_exiting)
58145d9ca49SDean Nelson 			break;
58245d9ca49SDean Nelson 
58345d9ca49SDean Nelson 		dev_dbg(xpc_part, "searching region %d\n", region);
58445d9ca49SDean Nelson 
58545d9ca49SDean Nelson 		for (nasid = (region * region_size * 2);
5864a3ad2ddSDean Nelson 		     nasid < ((region + 1) * region_size * 2); nasid += 2) {
58745d9ca49SDean Nelson 
5882c2b94f9SDean Nelson 			if (xpc_exiting)
58945d9ca49SDean Nelson 				break;
59045d9ca49SDean Nelson 
59145d9ca49SDean Nelson 			dev_dbg(xpc_part, "checking nasid %d\n", nasid);
59245d9ca49SDean Nelson 
59345d9ca49SDean Nelson 			if (XPC_NASID_IN_ARRAY(nasid, xpc_part_nasids)) {
59445d9ca49SDean Nelson 				dev_dbg(xpc_part, "PROM indicates Nasid %d is "
59545d9ca49SDean Nelson 					"part of the local partition; skipping "
59645d9ca49SDean Nelson 					"region\n", nasid);
59745d9ca49SDean Nelson 				break;
59845d9ca49SDean Nelson 			}
59945d9ca49SDean Nelson 
60045d9ca49SDean Nelson 			if (!(XPC_NASID_IN_ARRAY(nasid, xpc_mach_nasids))) {
60145d9ca49SDean Nelson 				dev_dbg(xpc_part, "PROM indicates Nasid %d was "
60245d9ca49SDean Nelson 					"not on Numa-Link network at reset\n",
60345d9ca49SDean Nelson 					nasid);
60445d9ca49SDean Nelson 				continue;
60545d9ca49SDean Nelson 			}
60645d9ca49SDean Nelson 
60745d9ca49SDean Nelson 			if (XPC_NASID_IN_ARRAY(nasid, discovered_nasids)) {
60845d9ca49SDean Nelson 				dev_dbg(xpc_part, "Nasid %d is part of a "
60945d9ca49SDean Nelson 					"partition which was previously "
61045d9ca49SDean Nelson 					"discovered\n", nasid);
61145d9ca49SDean Nelson 				continue;
61245d9ca49SDean Nelson 			}
61345d9ca49SDean Nelson 
61433ba3c77SDean Nelson 			/* pull over the rsvd page header & part_nasids mask */
61545d9ca49SDean Nelson 
61645d9ca49SDean Nelson 			ret = xpc_get_remote_rp(nasid, discovered_nasids,
61745d9ca49SDean Nelson 						remote_rp, &remote_rp_pa);
61865c17b80SDean Nelson 			if (ret != xpSuccess) {
61945d9ca49SDean Nelson 				dev_dbg(xpc_part, "unable to get reserved page "
62045d9ca49SDean Nelson 					"from nasid %d, reason=%d\n", nasid,
62145d9ca49SDean Nelson 					ret);
62245d9ca49SDean Nelson 
62365c17b80SDean Nelson 				if (ret == xpLocalPartid)
62445d9ca49SDean Nelson 					break;
6252c2b94f9SDean Nelson 
62645d9ca49SDean Nelson 				continue;
62745d9ca49SDean Nelson 			}
62845d9ca49SDean Nelson 
62933ba3c77SDean Nelson 			xpc_initiate_partition_activation(remote_rp,
63033ba3c77SDean Nelson 							  remote_rp_pa, nasid);
63145d9ca49SDean Nelson 		}
63245d9ca49SDean Nelson 	}
63345d9ca49SDean Nelson 
63445d9ca49SDean Nelson 	kfree(discovered_nasids);
63545d9ca49SDean Nelson 	kfree(remote_rp_base);
63645d9ca49SDean Nelson }
63745d9ca49SDean Nelson 
63845d9ca49SDean Nelson /*
63945d9ca49SDean Nelson  * Given a partid, get the nasids owned by that partition from the
64045d9ca49SDean Nelson  * remote partition's reserved page.
64145d9ca49SDean Nelson  */
64265c17b80SDean Nelson enum xp_retval
64364d032baSDean Nelson xpc_initiate_partid_to_nasids(short partid, void *nasid_mask)
64445d9ca49SDean Nelson {
64545d9ca49SDean Nelson 	struct xpc_partition *part;
64645d9ca49SDean Nelson 	u64 part_nasid_pa;
64745d9ca49SDean Nelson 
64845d9ca49SDean Nelson 	part = &xpc_partitions[partid];
6492c2b94f9SDean Nelson 	if (part->remote_rp_pa == 0)
65065c17b80SDean Nelson 		return xpPartitionDown;
65145d9ca49SDean Nelson 
65245d9ca49SDean Nelson 	memset(nasid_mask, 0, XP_NASID_MASK_BYTES);
65345d9ca49SDean Nelson 
65445d9ca49SDean Nelson 	part_nasid_pa = (u64)XPC_RP_PART_NASIDS(part->remote_rp_pa);
65545d9ca49SDean Nelson 
656908787dbSDean Nelson 	return xp_remote_memcpy(nasid_mask, (void *)part_nasid_pa,
65794bd2708SDean Nelson 				xp_sizeof_nasid_mask);
65845d9ca49SDean Nelson }
659