xref: /openbmc/linux/drivers/misc/sgi-xp/xpc_partition.c (revision b0576f9ecb5c51e9932531d23c447b2739261841)
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 
18261f3b49SDean Nelson #include <linux/device.h>
19261f3b49SDean Nelson #include <linux/hardirq.h>
205a0e3ad6STejun Heo #include <linux/slab.h>
2145d9ca49SDean Nelson #include "xpc.h"
22c2c9f115SRobin Holt #include <asm/uv/uv_hub.h>
2345d9ca49SDean Nelson 
2445d9ca49SDean Nelson /* XPC is exiting flag */
2545d9ca49SDean Nelson int xpc_exiting;
2645d9ca49SDean Nelson 
2745d9ca49SDean Nelson /* this partition's reserved page pointers */
2845d9ca49SDean Nelson struct xpc_rsvd_page *xpc_rsvd_page;
2904de7418SDean Nelson static unsigned long *xpc_part_nasids;
3004de7418SDean Nelson unsigned long *xpc_mach_nasids;
3145d9ca49SDean Nelson 
3204de7418SDean Nelson static int xpc_nasid_mask_nbytes;	/* #of bytes in nasid mask */
3304de7418SDean Nelson int xpc_nasid_mask_nlongs;	/* #of longs in nasid mask */
3445d9ca49SDean Nelson 
35bc63d387SDean Nelson struct xpc_partition *xpc_partitions;
3645d9ca49SDean Nelson 
3745d9ca49SDean Nelson /*
3845d9ca49SDean Nelson  * Guarantee that the kmalloc'd memory is cacheline aligned.
3945d9ca49SDean Nelson  */
4045d9ca49SDean Nelson void *
4145d9ca49SDean Nelson xpc_kmalloc_cacheline_aligned(size_t size, gfp_t flags, void **base)
4245d9ca49SDean Nelson {
4345d9ca49SDean Nelson 	/* see if kmalloc will give us cachline aligned memory by default */
4445d9ca49SDean Nelson 	*base = kmalloc(size, flags);
452c2b94f9SDean Nelson 	if (*base == NULL)
4645d9ca49SDean Nelson 		return NULL;
472c2b94f9SDean Nelson 
482c2b94f9SDean Nelson 	if ((u64)*base == L1_CACHE_ALIGN((u64)*base))
4945d9ca49SDean Nelson 		return *base;
502c2b94f9SDean Nelson 
5145d9ca49SDean Nelson 	kfree(*base);
5245d9ca49SDean Nelson 
5345d9ca49SDean Nelson 	/* nope, we'll have to do it ourselves */
5445d9ca49SDean Nelson 	*base = kmalloc(size + L1_CACHE_BYTES, flags);
552c2b94f9SDean Nelson 	if (*base == NULL)
5645d9ca49SDean Nelson 		return NULL;
572c2b94f9SDean Nelson 
5845d9ca49SDean Nelson 	return (void *)L1_CACHE_ALIGN((u64)*base);
5945d9ca49SDean Nelson }
6045d9ca49SDean Nelson 
6145d9ca49SDean Nelson /*
6245d9ca49SDean Nelson  * Given a nasid, get the physical address of the  partition's reserved page
6345d9ca49SDean Nelson  * for that nasid. This function returns 0 on any error.
6445d9ca49SDean Nelson  */
65a812dcc3SDean Nelson static unsigned long
6645d9ca49SDean Nelson xpc_get_rsvd_page_pa(int nasid)
6745d9ca49SDean Nelson {
68908787dbSDean Nelson 	enum xp_retval ret;
6945d9ca49SDean Nelson 	u64 cookie = 0;
70a812dcc3SDean Nelson 	unsigned long rp_pa = nasid;	/* seed with nasid */
71261f3b49SDean Nelson 	size_t len = 0;
72a812dcc3SDean Nelson 	size_t buf_len = 0;
73*b0576f9eSNathan Chancellor 	void *buf = NULL;
7445d9ca49SDean Nelson 	void *buf_base = NULL;
75a7665b0aSRobin Holt 	enum xp_retval (*get_partition_rsvd_page_pa)
76a7665b0aSRobin Holt 		(void *, u64 *, unsigned long *, size_t *) =
77a7665b0aSRobin Holt 		xpc_arch_ops.get_partition_rsvd_page_pa;
7845d9ca49SDean Nelson 
7945d9ca49SDean Nelson 	while (1) {
8045d9ca49SDean Nelson 
815b8669dfSDean Nelson 		/* !!! rp_pa will need to be _gpa on UV.
825b8669dfSDean Nelson 		 * ??? So do we save it into the architecture specific parts
835b8669dfSDean Nelson 		 * ??? of the xpc_partition structure? Do we rename this
845b8669dfSDean Nelson 		 * ??? function or have two versions? Rename rp_pa for UV to
855b8669dfSDean Nelson 		 * ??? rp_gpa?
865b8669dfSDean Nelson 		 */
87a7665b0aSRobin Holt 		ret = get_partition_rsvd_page_pa(buf, &cookie, &rp_pa, &len);
8845d9ca49SDean Nelson 
89261f3b49SDean Nelson 		dev_dbg(xpc_part, "SAL returned with ret=%d, cookie=0x%016lx, "
90261f3b49SDean Nelson 			"address=0x%016lx, len=0x%016lx\n", ret,
91a812dcc3SDean Nelson 			(unsigned long)cookie, rp_pa, len);
9245d9ca49SDean Nelson 
93261f3b49SDean Nelson 		if (ret != xpNeedMoreInfo)
9445d9ca49SDean Nelson 			break;
9545d9ca49SDean Nelson 
96ea57f80cSDean Nelson 		/* !!! L1_CACHE_ALIGN() is only a sn2-bte_copy requirement */
97c2c9f115SRobin Holt 		if (is_shub())
98c2c9f115SRobin Holt 			len = L1_CACHE_ALIGN(len);
99c2c9f115SRobin Holt 
100c2c9f115SRobin Holt 		if (len > buf_len) {
10145d9ca49SDean Nelson 			kfree(buf_base);
10245d9ca49SDean Nelson 			buf_len = L1_CACHE_ALIGN(len);
103a812dcc3SDean Nelson 			buf = xpc_kmalloc_cacheline_aligned(buf_len, GFP_KERNEL,
1044a3ad2ddSDean Nelson 							    &buf_base);
10545d9ca49SDean Nelson 			if (buf_base == NULL) {
10645d9ca49SDean Nelson 				dev_err(xpc_part, "unable to kmalloc "
107a812dcc3SDean Nelson 					"len=0x%016lx\n", buf_len);
108261f3b49SDean Nelson 				ret = xpNoMemory;
10945d9ca49SDean Nelson 				break;
11045d9ca49SDean Nelson 			}
11145d9ca49SDean Nelson 		}
11245d9ca49SDean Nelson 
113c2c9f115SRobin Holt 		ret = xp_remote_memcpy(xp_pa(buf), rp_pa, len);
114908787dbSDean Nelson 		if (ret != xpSuccess) {
115908787dbSDean Nelson 			dev_dbg(xpc_part, "xp_remote_memcpy failed %d\n", ret);
11645d9ca49SDean Nelson 			break;
11745d9ca49SDean Nelson 		}
11845d9ca49SDean Nelson 	}
11945d9ca49SDean Nelson 
12045d9ca49SDean Nelson 	kfree(buf_base);
12145d9ca49SDean Nelson 
122261f3b49SDean Nelson 	if (ret != xpSuccess)
12345d9ca49SDean Nelson 		rp_pa = 0;
1242c2b94f9SDean Nelson 
125a812dcc3SDean Nelson 	dev_dbg(xpc_part, "reserved page at phys address 0x%016lx\n", rp_pa);
12645d9ca49SDean Nelson 	return rp_pa;
12745d9ca49SDean Nelson }
12845d9ca49SDean Nelson 
12945d9ca49SDean Nelson /*
13045d9ca49SDean Nelson  * Fill the partition reserved page with the information needed by
13145d9ca49SDean Nelson  * other partitions to discover we are alive and establish initial
13245d9ca49SDean Nelson  * communications.
13345d9ca49SDean Nelson  */
1345b8669dfSDean Nelson int
13594bd2708SDean Nelson xpc_setup_rsvd_page(void)
13645d9ca49SDean Nelson {
1375b8669dfSDean Nelson 	int ret;
13845d9ca49SDean Nelson 	struct xpc_rsvd_page *rp;
139a812dcc3SDean Nelson 	unsigned long rp_pa;
14081fe7883SDean Nelson 	unsigned long new_ts_jiffies;
14145d9ca49SDean Nelson 
14245d9ca49SDean Nelson 	/* get the local reserved page's address */
14345d9ca49SDean Nelson 
14445d9ca49SDean Nelson 	preempt_disable();
145261f3b49SDean Nelson 	rp_pa = xpc_get_rsvd_page_pa(xp_cpu_to_nasid(smp_processor_id()));
14645d9ca49SDean Nelson 	preempt_enable();
14745d9ca49SDean Nelson 	if (rp_pa == 0) {
14845d9ca49SDean Nelson 		dev_err(xpc_part, "SAL failed to locate the reserved page\n");
1495b8669dfSDean Nelson 		return -ESRCH;
15045d9ca49SDean Nelson 	}
151c2c9f115SRobin Holt 	rp = (struct xpc_rsvd_page *)__va(xp_socket_pa(rp_pa));
15245d9ca49SDean Nelson 
15394bd2708SDean Nelson 	if (rp->SAL_version < 3) {
15494bd2708SDean Nelson 		/* SAL_versions < 3 had a SAL_partid defined as a u8 */
15594bd2708SDean Nelson 		rp->SAL_partid &= 0xff;
15694bd2708SDean Nelson 	}
157261f3b49SDean Nelson 	BUG_ON(rp->SAL_partid != xp_partition_id);
15894bd2708SDean Nelson 
15994bd2708SDean Nelson 	if (rp->SAL_partid < 0 || rp->SAL_partid >= xp_max_npartitions) {
16094bd2708SDean Nelson 		dev_err(xpc_part, "the reserved page's partid of %d is outside "
16194bd2708SDean Nelson 			"supported range (< 0 || >= %d)\n", rp->SAL_partid,
16294bd2708SDean Nelson 			xp_max_npartitions);
1635b8669dfSDean Nelson 		return -EINVAL;
16445d9ca49SDean Nelson 	}
16545d9ca49SDean Nelson 
16645d9ca49SDean Nelson 	rp->version = XPC_RP_VERSION;
16794bd2708SDean Nelson 	rp->max_npartitions = xp_max_npartitions;
16845d9ca49SDean Nelson 
16945d9ca49SDean Nelson 	/* establish the actual sizes of the nasid masks */
17045d9ca49SDean Nelson 	if (rp->SAL_version == 1) {
17145d9ca49SDean Nelson 		/* SAL_version 1 didn't set the nasids_size field */
17294bd2708SDean Nelson 		rp->SAL_nasids_size = 128;
17345d9ca49SDean Nelson 	}
17404de7418SDean Nelson 	xpc_nasid_mask_nbytes = rp->SAL_nasids_size;
17504de7418SDean Nelson 	xpc_nasid_mask_nlongs = BITS_TO_LONGS(rp->SAL_nasids_size *
17604de7418SDean Nelson 					      BITS_PER_BYTE);
17745d9ca49SDean Nelson 
17845d9ca49SDean Nelson 	/* setup the pointers to the various items in the reserved page */
17945d9ca49SDean Nelson 	xpc_part_nasids = XPC_RP_PART_NASIDS(rp);
18045d9ca49SDean Nelson 	xpc_mach_nasids = XPC_RP_MACH_NASIDS(rp);
18145d9ca49SDean Nelson 
182a7665b0aSRobin Holt 	ret = xpc_arch_ops.setup_rsvd_page(rp);
1835b8669dfSDean Nelson 	if (ret != 0)
1845b8669dfSDean Nelson 		return ret;
18545d9ca49SDean Nelson 
18645d9ca49SDean Nelson 	/*
18794bd2708SDean Nelson 	 * Set timestamp of when reserved page was setup by XPC.
18845d9ca49SDean Nelson 	 * This signifies to the remote partition that our reserved
18945d9ca49SDean Nelson 	 * page is initialized.
19045d9ca49SDean Nelson 	 */
19181fe7883SDean Nelson 	new_ts_jiffies = jiffies;
19281fe7883SDean Nelson 	if (new_ts_jiffies == 0 || new_ts_jiffies == rp->ts_jiffies)
19381fe7883SDean Nelson 		new_ts_jiffies++;
19481fe7883SDean Nelson 	rp->ts_jiffies = new_ts_jiffies;
19545d9ca49SDean Nelson 
1965b8669dfSDean Nelson 	xpc_rsvd_page = rp;
1975b8669dfSDean Nelson 	return 0;
1985b8669dfSDean Nelson }
1995b8669dfSDean Nelson 
2005b8669dfSDean Nelson void
2015b8669dfSDean Nelson xpc_teardown_rsvd_page(void)
2025b8669dfSDean Nelson {
2035b8669dfSDean Nelson 	/* a zero timestamp indicates our rsvd page is not initialized */
2045b8669dfSDean Nelson 	xpc_rsvd_page->ts_jiffies = 0;
20545d9ca49SDean Nelson }
20645d9ca49SDean Nelson 
20745d9ca49SDean Nelson /*
20845d9ca49SDean Nelson  * Get a copy of a portion of the remote partition's rsvd page.
20945d9ca49SDean Nelson  *
21045d9ca49SDean Nelson  * remote_rp points to a buffer that is cacheline aligned for BTE copies and
21145d9ca49SDean Nelson  * is large enough to contain a copy of their reserved page header and
21245d9ca49SDean Nelson  * part_nasids mask.
21345d9ca49SDean Nelson  */
21433ba3c77SDean Nelson enum xp_retval
21504de7418SDean Nelson xpc_get_remote_rp(int nasid, unsigned long *discovered_nasids,
216a812dcc3SDean Nelson 		  struct xpc_rsvd_page *remote_rp, unsigned long *remote_rp_pa)
21745d9ca49SDean Nelson {
21804de7418SDean Nelson 	int l;
219908787dbSDean Nelson 	enum xp_retval ret;
22045d9ca49SDean Nelson 
22145d9ca49SDean Nelson 	/* get the reserved page's physical address */
22245d9ca49SDean Nelson 
22345d9ca49SDean Nelson 	*remote_rp_pa = xpc_get_rsvd_page_pa(nasid);
2242c2b94f9SDean Nelson 	if (*remote_rp_pa == 0)
22565c17b80SDean Nelson 		return xpNoRsvdPageAddr;
22645d9ca49SDean Nelson 
22745d9ca49SDean Nelson 	/* pull over the reserved page header and part_nasids mask */
228a812dcc3SDean Nelson 	ret = xp_remote_memcpy(xp_pa(remote_rp), *remote_rp_pa,
22904de7418SDean Nelson 			       XPC_RP_HEADER_SIZE + xpc_nasid_mask_nbytes);
230908787dbSDean Nelson 	if (ret != xpSuccess)
231908787dbSDean Nelson 		return ret;
23245d9ca49SDean Nelson 
23345d9ca49SDean Nelson 	if (discovered_nasids != NULL) {
23404de7418SDean Nelson 		unsigned long *remote_part_nasids =
23504de7418SDean Nelson 		    XPC_RP_PART_NASIDS(remote_rp);
23645d9ca49SDean Nelson 
23704de7418SDean Nelson 		for (l = 0; l < xpc_nasid_mask_nlongs; l++)
23804de7418SDean Nelson 			discovered_nasids[l] |= remote_part_nasids[l];
23945d9ca49SDean Nelson 	}
24045d9ca49SDean Nelson 
24181fe7883SDean Nelson 	/* zero timestamp indicates the reserved page has not been setup */
24281fe7883SDean Nelson 	if (remote_rp->ts_jiffies == 0)
24394bd2708SDean Nelson 		return xpRsvdPageNotSet;
24494bd2708SDean Nelson 
24545d9ca49SDean Nelson 	if (XPC_VERSION_MAJOR(remote_rp->version) !=
24645d9ca49SDean Nelson 	    XPC_VERSION_MAJOR(XPC_RP_VERSION)) {
24765c17b80SDean Nelson 		return xpBadVersion;
24845d9ca49SDean Nelson 	}
24945d9ca49SDean Nelson 
250a47d5dacSDean Nelson 	/* check that both remote and local partids are valid for each side */
251aaa3cd69SDean Nelson 	if (remote_rp->SAL_partid < 0 ||
252aaa3cd69SDean Nelson 	    remote_rp->SAL_partid >= xp_max_npartitions ||
253261f3b49SDean Nelson 	    remote_rp->max_npartitions <= xp_partition_id) {
25494bd2708SDean Nelson 		return xpInvalidPartid;
255aaa3cd69SDean Nelson 	}
256aaa3cd69SDean Nelson 
257261f3b49SDean Nelson 	if (remote_rp->SAL_partid == xp_partition_id)
258aaa3cd69SDean Nelson 		return xpLocalPartid;
25994bd2708SDean Nelson 
26065c17b80SDean Nelson 	return xpSuccess;
26145d9ca49SDean Nelson }
26245d9ca49SDean Nelson 
26345d9ca49SDean Nelson /*
264a47d5dacSDean Nelson  * See if the other side has responded to a partition deactivate request
265a47d5dacSDean Nelson  * from us. Though we requested the remote partition to deactivate with regard
266a47d5dacSDean Nelson  * to us, we really only need to wait for the other side to disengage from us.
26745d9ca49SDean Nelson  */
26845d9ca49SDean Nelson int
26945d9ca49SDean Nelson xpc_partition_disengaged(struct xpc_partition *part)
27045d9ca49SDean Nelson {
27164d032baSDean Nelson 	short partid = XPC_PARTID(part);
27245d9ca49SDean Nelson 	int disengaged;
27345d9ca49SDean Nelson 
274a7665b0aSRobin Holt 	disengaged = !xpc_arch_ops.partition_engaged(partid);
275a47d5dacSDean Nelson 	if (part->disengage_timeout) {
27645d9ca49SDean Nelson 		if (!disengaged) {
277a47d5dacSDean Nelson 			if (time_is_after_jiffies(part->disengage_timeout)) {
27845d9ca49SDean Nelson 				/* timelimit hasn't been reached yet */
27945d9ca49SDean Nelson 				return 0;
28045d9ca49SDean Nelson 			}
28145d9ca49SDean Nelson 
28245d9ca49SDean Nelson 			/*
283a47d5dacSDean Nelson 			 * Other side hasn't responded to our deactivate
28445d9ca49SDean Nelson 			 * request in a timely fashion, so assume it's dead.
28545d9ca49SDean Nelson 			 */
28645d9ca49SDean Nelson 
287a47d5dacSDean Nelson 			dev_info(xpc_part, "deactivate request to remote "
288a47d5dacSDean Nelson 				 "partition %d timed out\n", partid);
289a47d5dacSDean Nelson 			xpc_disengage_timedout = 1;
290a7665b0aSRobin Holt 			xpc_arch_ops.assume_partition_disengaged(partid);
29145d9ca49SDean Nelson 			disengaged = 1;
29245d9ca49SDean Nelson 		}
293a47d5dacSDean Nelson 		part->disengage_timeout = 0;
29445d9ca49SDean Nelson 
29545d9ca49SDean Nelson 		/* cancel the timer function, provided it's not us */
296a47d5dacSDean Nelson 		if (!in_interrupt())
297a47d5dacSDean Nelson 			del_singleshot_timer_sync(&part->disengage_timer);
29845d9ca49SDean Nelson 
29983469b55SDean Nelson 		DBUG_ON(part->act_state != XPC_P_AS_DEACTIVATING &&
30083469b55SDean Nelson 			part->act_state != XPC_P_AS_INACTIVE);
30183469b55SDean Nelson 		if (part->act_state != XPC_P_AS_INACTIVE)
30245d9ca49SDean Nelson 			xpc_wakeup_channel_mgr(part);
30345d9ca49SDean Nelson 
304a7665b0aSRobin Holt 		xpc_arch_ops.cancel_partition_deactivation_request(part);
30545d9ca49SDean Nelson 	}
30645d9ca49SDean Nelson 	return disengaged;
30745d9ca49SDean Nelson }
30845d9ca49SDean Nelson 
30945d9ca49SDean Nelson /*
31045d9ca49SDean Nelson  * Mark specified partition as active.
31145d9ca49SDean Nelson  */
31265c17b80SDean Nelson enum xp_retval
31345d9ca49SDean Nelson xpc_mark_partition_active(struct xpc_partition *part)
31445d9ca49SDean Nelson {
31545d9ca49SDean Nelson 	unsigned long irq_flags;
31665c17b80SDean Nelson 	enum xp_retval ret;
31745d9ca49SDean Nelson 
31845d9ca49SDean Nelson 	dev_dbg(xpc_part, "setting partition %d to ACTIVE\n", XPC_PARTID(part));
31945d9ca49SDean Nelson 
32045d9ca49SDean Nelson 	spin_lock_irqsave(&part->act_lock, irq_flags);
32183469b55SDean Nelson 	if (part->act_state == XPC_P_AS_ACTIVATING) {
32283469b55SDean Nelson 		part->act_state = XPC_P_AS_ACTIVE;
32365c17b80SDean Nelson 		ret = xpSuccess;
32445d9ca49SDean Nelson 	} else {
32565c17b80SDean Nelson 		DBUG_ON(part->reason == xpSuccess);
32645d9ca49SDean Nelson 		ret = part->reason;
32745d9ca49SDean Nelson 	}
32845d9ca49SDean Nelson 	spin_unlock_irqrestore(&part->act_lock, irq_flags);
32945d9ca49SDean Nelson 
33045d9ca49SDean Nelson 	return ret;
33145d9ca49SDean Nelson }
33245d9ca49SDean Nelson 
33345d9ca49SDean Nelson /*
334a47d5dacSDean Nelson  * Start the process of deactivating the specified partition.
33545d9ca49SDean Nelson  */
33645d9ca49SDean Nelson void
33745d9ca49SDean Nelson xpc_deactivate_partition(const int line, struct xpc_partition *part,
33865c17b80SDean Nelson 			 enum xp_retval reason)
33945d9ca49SDean Nelson {
34045d9ca49SDean Nelson 	unsigned long irq_flags;
34145d9ca49SDean Nelson 
34245d9ca49SDean Nelson 	spin_lock_irqsave(&part->act_lock, irq_flags);
34345d9ca49SDean Nelson 
34483469b55SDean Nelson 	if (part->act_state == XPC_P_AS_INACTIVE) {
34545d9ca49SDean Nelson 		XPC_SET_REASON(part, reason, line);
34645d9ca49SDean Nelson 		spin_unlock_irqrestore(&part->act_lock, irq_flags);
34765c17b80SDean Nelson 		if (reason == xpReactivating) {
34845d9ca49SDean Nelson 			/* we interrupt ourselves to reactivate partition */
349a7665b0aSRobin Holt 			xpc_arch_ops.request_partition_reactivation(part);
35045d9ca49SDean Nelson 		}
35145d9ca49SDean Nelson 		return;
35245d9ca49SDean Nelson 	}
35383469b55SDean Nelson 	if (part->act_state == XPC_P_AS_DEACTIVATING) {
35465c17b80SDean Nelson 		if ((part->reason == xpUnloading && reason != xpUnloading) ||
35565c17b80SDean Nelson 		    reason == xpReactivating) {
35645d9ca49SDean Nelson 			XPC_SET_REASON(part, reason, line);
35745d9ca49SDean Nelson 		}
35845d9ca49SDean Nelson 		spin_unlock_irqrestore(&part->act_lock, irq_flags);
35945d9ca49SDean Nelson 		return;
36045d9ca49SDean Nelson 	}
36145d9ca49SDean Nelson 
36283469b55SDean Nelson 	part->act_state = XPC_P_AS_DEACTIVATING;
36345d9ca49SDean Nelson 	XPC_SET_REASON(part, reason, line);
36445d9ca49SDean Nelson 
36545d9ca49SDean Nelson 	spin_unlock_irqrestore(&part->act_lock, irq_flags);
36645d9ca49SDean Nelson 
367a47d5dacSDean Nelson 	/* ask remote partition to deactivate with regard to us */
368a7665b0aSRobin Holt 	xpc_arch_ops.request_partition_deactivation(part);
36945d9ca49SDean Nelson 
370a47d5dacSDean Nelson 	/* set a timelimit on the disengage phase of the deactivation request */
371a47d5dacSDean Nelson 	part->disengage_timeout = jiffies + (xpc_disengage_timelimit * HZ);
372a47d5dacSDean Nelson 	part->disengage_timer.expires = part->disengage_timeout;
373a47d5dacSDean Nelson 	add_timer(&part->disengage_timer);
37445d9ca49SDean Nelson 
37545d9ca49SDean Nelson 	dev_dbg(xpc_part, "bringing partition %d down, reason = %d\n",
37645d9ca49SDean Nelson 		XPC_PARTID(part), reason);
37745d9ca49SDean Nelson 
37845d9ca49SDean Nelson 	xpc_partition_going_down(part, reason);
37945d9ca49SDean Nelson }
38045d9ca49SDean Nelson 
38145d9ca49SDean Nelson /*
38245d9ca49SDean Nelson  * Mark specified partition as inactive.
38345d9ca49SDean Nelson  */
38445d9ca49SDean Nelson void
38545d9ca49SDean Nelson xpc_mark_partition_inactive(struct xpc_partition *part)
38645d9ca49SDean Nelson {
38745d9ca49SDean Nelson 	unsigned long irq_flags;
38845d9ca49SDean Nelson 
38945d9ca49SDean Nelson 	dev_dbg(xpc_part, "setting partition %d to INACTIVE\n",
39045d9ca49SDean Nelson 		XPC_PARTID(part));
39145d9ca49SDean Nelson 
39245d9ca49SDean Nelson 	spin_lock_irqsave(&part->act_lock, irq_flags);
39383469b55SDean Nelson 	part->act_state = XPC_P_AS_INACTIVE;
39445d9ca49SDean Nelson 	spin_unlock_irqrestore(&part->act_lock, irq_flags);
39545d9ca49SDean Nelson 	part->remote_rp_pa = 0;
39645d9ca49SDean Nelson }
39745d9ca49SDean Nelson 
39845d9ca49SDean Nelson /*
39945d9ca49SDean Nelson  * SAL has provided a partition and machine mask.  The partition mask
40045d9ca49SDean Nelson  * contains a bit for each even nasid in our partition.  The machine
40145d9ca49SDean Nelson  * mask contains a bit for each even nasid in the entire machine.
40245d9ca49SDean Nelson  *
40345d9ca49SDean Nelson  * Using those two bit arrays, we can determine which nasids are
40445d9ca49SDean Nelson  * known in the machine.  Each should also have a reserved page
40545d9ca49SDean Nelson  * initialized if they are available for partitioning.
40645d9ca49SDean Nelson  */
40745d9ca49SDean Nelson void
40845d9ca49SDean Nelson xpc_discovery(void)
40945d9ca49SDean Nelson {
41045d9ca49SDean Nelson 	void *remote_rp_base;
41145d9ca49SDean Nelson 	struct xpc_rsvd_page *remote_rp;
412a812dcc3SDean Nelson 	unsigned long remote_rp_pa;
41345d9ca49SDean Nelson 	int region;
41445d9ca49SDean Nelson 	int region_size;
41545d9ca49SDean Nelson 	int max_regions;
41645d9ca49SDean Nelson 	int nasid;
41704de7418SDean Nelson 	unsigned long *discovered_nasids;
41865c17b80SDean Nelson 	enum xp_retval ret;
41945d9ca49SDean Nelson 
42045d9ca49SDean Nelson 	remote_rp = xpc_kmalloc_cacheline_aligned(XPC_RP_HEADER_SIZE +
42104de7418SDean Nelson 						  xpc_nasid_mask_nbytes,
42245d9ca49SDean Nelson 						  GFP_KERNEL, &remote_rp_base);
4232c2b94f9SDean Nelson 	if (remote_rp == NULL)
42445d9ca49SDean Nelson 		return;
4252c2b94f9SDean Nelson 
4266396bb22SKees Cook 	discovered_nasids = kcalloc(xpc_nasid_mask_nlongs, sizeof(long),
42745d9ca49SDean Nelson 				    GFP_KERNEL);
42845d9ca49SDean Nelson 	if (discovered_nasids == NULL) {
42945d9ca49SDean Nelson 		kfree(remote_rp_base);
43045d9ca49SDean Nelson 		return;
43145d9ca49SDean Nelson 	}
43245d9ca49SDean Nelson 
43345d9ca49SDean Nelson 	/*
43445d9ca49SDean Nelson 	 * The term 'region' in this context refers to the minimum number of
43545d9ca49SDean Nelson 	 * nodes that can comprise an access protection grouping. The access
43645d9ca49SDean Nelson 	 * protection is in regards to memory, IOI and IPI.
43745d9ca49SDean Nelson 	 */
438261f3b49SDean Nelson 	region_size = xp_region_size;
43945d9ca49SDean Nelson 
440c22c7aefSRobin@sgi.com 	if (is_uv())
441c22c7aefSRobin@sgi.com 		max_regions = 256;
442c22c7aefSRobin@sgi.com 	else {
443c22c7aefSRobin@sgi.com 		max_regions = 64;
444c22c7aefSRobin@sgi.com 
44545d9ca49SDean Nelson 		switch (region_size) {
44645d9ca49SDean Nelson 		case 128:
44745d9ca49SDean Nelson 			max_regions *= 2;
448f1a54cddSGustavo A. R. Silva 			/* fall through */
44945d9ca49SDean Nelson 		case 64:
45045d9ca49SDean Nelson 			max_regions *= 2;
451f1a54cddSGustavo A. R. Silva 			/* fall through */
45245d9ca49SDean Nelson 		case 32:
45345d9ca49SDean Nelson 			max_regions *= 2;
45445d9ca49SDean Nelson 			region_size = 16;
45545d9ca49SDean Nelson 			DBUG_ON(!is_shub2());
45645d9ca49SDean Nelson 		}
457c22c7aefSRobin@sgi.com 	}
45845d9ca49SDean Nelson 
45945d9ca49SDean Nelson 	for (region = 0; region < max_regions; region++) {
46045d9ca49SDean Nelson 
4612c2b94f9SDean Nelson 		if (xpc_exiting)
46245d9ca49SDean Nelson 			break;
46345d9ca49SDean Nelson 
46445d9ca49SDean Nelson 		dev_dbg(xpc_part, "searching region %d\n", region);
46545d9ca49SDean Nelson 
46645d9ca49SDean Nelson 		for (nasid = (region * region_size * 2);
4674a3ad2ddSDean Nelson 		     nasid < ((region + 1) * region_size * 2); nasid += 2) {
46845d9ca49SDean Nelson 
4692c2b94f9SDean Nelson 			if (xpc_exiting)
47045d9ca49SDean Nelson 				break;
47145d9ca49SDean Nelson 
47245d9ca49SDean Nelson 			dev_dbg(xpc_part, "checking nasid %d\n", nasid);
47345d9ca49SDean Nelson 
47404de7418SDean Nelson 			if (test_bit(nasid / 2, xpc_part_nasids)) {
47545d9ca49SDean Nelson 				dev_dbg(xpc_part, "PROM indicates Nasid %d is "
47645d9ca49SDean Nelson 					"part of the local partition; skipping "
47745d9ca49SDean Nelson 					"region\n", nasid);
47845d9ca49SDean Nelson 				break;
47945d9ca49SDean Nelson 			}
48045d9ca49SDean Nelson 
48104de7418SDean Nelson 			if (!(test_bit(nasid / 2, xpc_mach_nasids))) {
48245d9ca49SDean Nelson 				dev_dbg(xpc_part, "PROM indicates Nasid %d was "
48345d9ca49SDean Nelson 					"not on Numa-Link network at reset\n",
48445d9ca49SDean Nelson 					nasid);
48545d9ca49SDean Nelson 				continue;
48645d9ca49SDean Nelson 			}
48745d9ca49SDean Nelson 
48804de7418SDean Nelson 			if (test_bit(nasid / 2, discovered_nasids)) {
48945d9ca49SDean Nelson 				dev_dbg(xpc_part, "Nasid %d is part of a "
49045d9ca49SDean Nelson 					"partition which was previously "
49145d9ca49SDean Nelson 					"discovered\n", nasid);
49245d9ca49SDean Nelson 				continue;
49345d9ca49SDean Nelson 			}
49445d9ca49SDean Nelson 
49533ba3c77SDean Nelson 			/* pull over the rsvd page header & part_nasids mask */
49645d9ca49SDean Nelson 
49745d9ca49SDean Nelson 			ret = xpc_get_remote_rp(nasid, discovered_nasids,
49845d9ca49SDean Nelson 						remote_rp, &remote_rp_pa);
49965c17b80SDean Nelson 			if (ret != xpSuccess) {
50045d9ca49SDean Nelson 				dev_dbg(xpc_part, "unable to get reserved page "
50145d9ca49SDean Nelson 					"from nasid %d, reason=%d\n", nasid,
50245d9ca49SDean Nelson 					ret);
50345d9ca49SDean Nelson 
50465c17b80SDean Nelson 				if (ret == xpLocalPartid)
50545d9ca49SDean Nelson 					break;
5062c2b94f9SDean Nelson 
50745d9ca49SDean Nelson 				continue;
50845d9ca49SDean Nelson 			}
50945d9ca49SDean Nelson 
510a7665b0aSRobin Holt 			xpc_arch_ops.request_partition_activation(remote_rp,
51133ba3c77SDean Nelson 							 remote_rp_pa, nasid);
51245d9ca49SDean Nelson 		}
51345d9ca49SDean Nelson 	}
51445d9ca49SDean Nelson 
51545d9ca49SDean Nelson 	kfree(discovered_nasids);
51645d9ca49SDean Nelson 	kfree(remote_rp_base);
51745d9ca49SDean Nelson }
51845d9ca49SDean Nelson 
51945d9ca49SDean Nelson /*
52045d9ca49SDean Nelson  * Given a partid, get the nasids owned by that partition from the
52145d9ca49SDean Nelson  * remote partition's reserved page.
52245d9ca49SDean Nelson  */
52365c17b80SDean Nelson enum xp_retval
52464d032baSDean Nelson xpc_initiate_partid_to_nasids(short partid, void *nasid_mask)
52545d9ca49SDean Nelson {
52645d9ca49SDean Nelson 	struct xpc_partition *part;
527a812dcc3SDean Nelson 	unsigned long part_nasid_pa;
52845d9ca49SDean Nelson 
52945d9ca49SDean Nelson 	part = &xpc_partitions[partid];
5302c2b94f9SDean Nelson 	if (part->remote_rp_pa == 0)
53165c17b80SDean Nelson 		return xpPartitionDown;
53245d9ca49SDean Nelson 
53304de7418SDean Nelson 	memset(nasid_mask, 0, xpc_nasid_mask_nbytes);
53445d9ca49SDean Nelson 
535a812dcc3SDean Nelson 	part_nasid_pa = (unsigned long)XPC_RP_PART_NASIDS(part->remote_rp_pa);
53645d9ca49SDean Nelson 
537a812dcc3SDean Nelson 	return xp_remote_memcpy(xp_pa(nasid_mask), part_nasid_pa,
53804de7418SDean Nelson 				xpc_nasid_mask_nbytes);
53945d9ca49SDean Nelson }
540