162306a36Sopenharmony_ci/*
262306a36Sopenharmony_ci * This file is subject to the terms and conditions of the GNU General Public
362306a36Sopenharmony_ci * License.  See the file "COPYING" in the main directory of this archive
462306a36Sopenharmony_ci * for more details.
562306a36Sopenharmony_ci *
662306a36Sopenharmony_ci * (C) Copyright 2020 Hewlett Packard Enterprise Development LP
762306a36Sopenharmony_ci * Copyright (c) 2004-2008 Silicon Graphics, Inc.  All Rights Reserved.
862306a36Sopenharmony_ci */
962306a36Sopenharmony_ci
1062306a36Sopenharmony_ci/*
1162306a36Sopenharmony_ci * Cross Partition Communication (XPC) partition support.
1262306a36Sopenharmony_ci *
1362306a36Sopenharmony_ci *	This is the part of XPC that detects the presence/absence of
1462306a36Sopenharmony_ci *	other partitions. It provides a heartbeat and monitors the
1562306a36Sopenharmony_ci *	heartbeats of other partitions.
1662306a36Sopenharmony_ci *
1762306a36Sopenharmony_ci */
1862306a36Sopenharmony_ci
1962306a36Sopenharmony_ci#include <linux/device.h>
2062306a36Sopenharmony_ci#include <linux/hardirq.h>
2162306a36Sopenharmony_ci#include <linux/slab.h>
2262306a36Sopenharmony_ci#include "xpc.h"
2362306a36Sopenharmony_ci#include <asm/uv/uv_hub.h>
2462306a36Sopenharmony_ci
2562306a36Sopenharmony_ci/* XPC is exiting flag */
2662306a36Sopenharmony_ciint xpc_exiting;
2762306a36Sopenharmony_ci
2862306a36Sopenharmony_ci/* this partition's reserved page pointers */
2962306a36Sopenharmony_cistruct xpc_rsvd_page *xpc_rsvd_page;
3062306a36Sopenharmony_cistatic unsigned long *xpc_part_nasids;
3162306a36Sopenharmony_ciunsigned long *xpc_mach_nasids;
3262306a36Sopenharmony_ci
3362306a36Sopenharmony_cistatic int xpc_nasid_mask_nbytes;	/* #of bytes in nasid mask */
3462306a36Sopenharmony_ciint xpc_nasid_mask_nlongs;	/* #of longs in nasid mask */
3562306a36Sopenharmony_ci
3662306a36Sopenharmony_cistruct xpc_partition *xpc_partitions;
3762306a36Sopenharmony_ci
3862306a36Sopenharmony_ci/*
3962306a36Sopenharmony_ci * Guarantee that the kmalloc'd memory is cacheline aligned.
4062306a36Sopenharmony_ci */
4162306a36Sopenharmony_civoid *
4262306a36Sopenharmony_cixpc_kmalloc_cacheline_aligned(size_t size, gfp_t flags, void **base)
4362306a36Sopenharmony_ci{
4462306a36Sopenharmony_ci	/* see if kmalloc will give us cachline aligned memory by default */
4562306a36Sopenharmony_ci	*base = kmalloc(size, flags);
4662306a36Sopenharmony_ci	if (*base == NULL)
4762306a36Sopenharmony_ci		return NULL;
4862306a36Sopenharmony_ci
4962306a36Sopenharmony_ci	if ((u64)*base == L1_CACHE_ALIGN((u64)*base))
5062306a36Sopenharmony_ci		return *base;
5162306a36Sopenharmony_ci
5262306a36Sopenharmony_ci	kfree(*base);
5362306a36Sopenharmony_ci
5462306a36Sopenharmony_ci	/* nope, we'll have to do it ourselves */
5562306a36Sopenharmony_ci	*base = kmalloc(size + L1_CACHE_BYTES, flags);
5662306a36Sopenharmony_ci	if (*base == NULL)
5762306a36Sopenharmony_ci		return NULL;
5862306a36Sopenharmony_ci
5962306a36Sopenharmony_ci	return (void *)L1_CACHE_ALIGN((u64)*base);
6062306a36Sopenharmony_ci}
6162306a36Sopenharmony_ci
6262306a36Sopenharmony_ci/*
6362306a36Sopenharmony_ci * Given a nasid, get the physical address of the  partition's reserved page
6462306a36Sopenharmony_ci * for that nasid. This function returns 0 on any error.
6562306a36Sopenharmony_ci */
6662306a36Sopenharmony_cistatic unsigned long
6762306a36Sopenharmony_cixpc_get_rsvd_page_pa(int nasid)
6862306a36Sopenharmony_ci{
6962306a36Sopenharmony_ci	enum xp_retval ret;
7062306a36Sopenharmony_ci	u64 cookie = 0;
7162306a36Sopenharmony_ci	unsigned long rp_pa = nasid;	/* seed with nasid */
7262306a36Sopenharmony_ci	size_t len = 0;
7362306a36Sopenharmony_ci	size_t buf_len = 0;
7462306a36Sopenharmony_ci	void *buf = NULL;
7562306a36Sopenharmony_ci	void *buf_base = NULL;
7662306a36Sopenharmony_ci	enum xp_retval (*get_partition_rsvd_page_pa)
7762306a36Sopenharmony_ci		(void *, u64 *, unsigned long *, size_t *) =
7862306a36Sopenharmony_ci		xpc_arch_ops.get_partition_rsvd_page_pa;
7962306a36Sopenharmony_ci
8062306a36Sopenharmony_ci	while (1) {
8162306a36Sopenharmony_ci
8262306a36Sopenharmony_ci		/* !!! rp_pa will need to be _gpa on UV.
8362306a36Sopenharmony_ci		 * ??? So do we save it into the architecture specific parts
8462306a36Sopenharmony_ci		 * ??? of the xpc_partition structure? Do we rename this
8562306a36Sopenharmony_ci		 * ??? function or have two versions? Rename rp_pa for UV to
8662306a36Sopenharmony_ci		 * ??? rp_gpa?
8762306a36Sopenharmony_ci		 */
8862306a36Sopenharmony_ci		ret = get_partition_rsvd_page_pa(buf, &cookie, &rp_pa, &len);
8962306a36Sopenharmony_ci
9062306a36Sopenharmony_ci		dev_dbg(xpc_part, "SAL returned with ret=%d, cookie=0x%016lx, "
9162306a36Sopenharmony_ci			"address=0x%016lx, len=0x%016lx\n", ret,
9262306a36Sopenharmony_ci			(unsigned long)cookie, rp_pa, len);
9362306a36Sopenharmony_ci
9462306a36Sopenharmony_ci		if (ret != xpNeedMoreInfo)
9562306a36Sopenharmony_ci			break;
9662306a36Sopenharmony_ci
9762306a36Sopenharmony_ci		if (len > buf_len) {
9862306a36Sopenharmony_ci			kfree(buf_base);
9962306a36Sopenharmony_ci			buf_len = L1_CACHE_ALIGN(len);
10062306a36Sopenharmony_ci			buf = xpc_kmalloc_cacheline_aligned(buf_len, GFP_KERNEL,
10162306a36Sopenharmony_ci							    &buf_base);
10262306a36Sopenharmony_ci			if (buf_base == NULL) {
10362306a36Sopenharmony_ci				dev_err(xpc_part, "unable to kmalloc "
10462306a36Sopenharmony_ci					"len=0x%016lx\n", buf_len);
10562306a36Sopenharmony_ci				ret = xpNoMemory;
10662306a36Sopenharmony_ci				break;
10762306a36Sopenharmony_ci			}
10862306a36Sopenharmony_ci		}
10962306a36Sopenharmony_ci
11062306a36Sopenharmony_ci		ret = xp_remote_memcpy(xp_pa(buf), rp_pa, len);
11162306a36Sopenharmony_ci		if (ret != xpSuccess) {
11262306a36Sopenharmony_ci			dev_dbg(xpc_part, "xp_remote_memcpy failed %d\n", ret);
11362306a36Sopenharmony_ci			break;
11462306a36Sopenharmony_ci		}
11562306a36Sopenharmony_ci	}
11662306a36Sopenharmony_ci
11762306a36Sopenharmony_ci	kfree(buf_base);
11862306a36Sopenharmony_ci
11962306a36Sopenharmony_ci	if (ret != xpSuccess)
12062306a36Sopenharmony_ci		rp_pa = 0;
12162306a36Sopenharmony_ci
12262306a36Sopenharmony_ci	dev_dbg(xpc_part, "reserved page at phys address 0x%016lx\n", rp_pa);
12362306a36Sopenharmony_ci	return rp_pa;
12462306a36Sopenharmony_ci}
12562306a36Sopenharmony_ci
12662306a36Sopenharmony_ci/*
12762306a36Sopenharmony_ci * Fill the partition reserved page with the information needed by
12862306a36Sopenharmony_ci * other partitions to discover we are alive and establish initial
12962306a36Sopenharmony_ci * communications.
13062306a36Sopenharmony_ci */
13162306a36Sopenharmony_ciint
13262306a36Sopenharmony_cixpc_setup_rsvd_page(void)
13362306a36Sopenharmony_ci{
13462306a36Sopenharmony_ci	int ret;
13562306a36Sopenharmony_ci	struct xpc_rsvd_page *rp;
13662306a36Sopenharmony_ci	unsigned long rp_pa;
13762306a36Sopenharmony_ci	unsigned long new_ts_jiffies;
13862306a36Sopenharmony_ci
13962306a36Sopenharmony_ci	/* get the local reserved page's address */
14062306a36Sopenharmony_ci
14162306a36Sopenharmony_ci	preempt_disable();
14262306a36Sopenharmony_ci	rp_pa = xpc_get_rsvd_page_pa(xp_cpu_to_nasid(smp_processor_id()));
14362306a36Sopenharmony_ci	preempt_enable();
14462306a36Sopenharmony_ci	if (rp_pa == 0) {
14562306a36Sopenharmony_ci		dev_err(xpc_part, "SAL failed to locate the reserved page\n");
14662306a36Sopenharmony_ci		return -ESRCH;
14762306a36Sopenharmony_ci	}
14862306a36Sopenharmony_ci	rp = (struct xpc_rsvd_page *)__va(xp_socket_pa(rp_pa));
14962306a36Sopenharmony_ci
15062306a36Sopenharmony_ci	if (rp->SAL_version < 3) {
15162306a36Sopenharmony_ci		/* SAL_versions < 3 had a SAL_partid defined as a u8 */
15262306a36Sopenharmony_ci		rp->SAL_partid &= 0xff;
15362306a36Sopenharmony_ci	}
15462306a36Sopenharmony_ci	BUG_ON(rp->SAL_partid != xp_partition_id);
15562306a36Sopenharmony_ci
15662306a36Sopenharmony_ci	if (rp->SAL_partid < 0 || rp->SAL_partid >= xp_max_npartitions) {
15762306a36Sopenharmony_ci		dev_err(xpc_part, "the reserved page's partid of %d is outside "
15862306a36Sopenharmony_ci			"supported range (< 0 || >= %d)\n", rp->SAL_partid,
15962306a36Sopenharmony_ci			xp_max_npartitions);
16062306a36Sopenharmony_ci		return -EINVAL;
16162306a36Sopenharmony_ci	}
16262306a36Sopenharmony_ci
16362306a36Sopenharmony_ci	rp->version = XPC_RP_VERSION;
16462306a36Sopenharmony_ci	rp->max_npartitions = xp_max_npartitions;
16562306a36Sopenharmony_ci
16662306a36Sopenharmony_ci	/* establish the actual sizes of the nasid masks */
16762306a36Sopenharmony_ci	if (rp->SAL_version == 1) {
16862306a36Sopenharmony_ci		/* SAL_version 1 didn't set the nasids_size field */
16962306a36Sopenharmony_ci		rp->SAL_nasids_size = 128;
17062306a36Sopenharmony_ci	}
17162306a36Sopenharmony_ci	xpc_nasid_mask_nbytes = rp->SAL_nasids_size;
17262306a36Sopenharmony_ci	xpc_nasid_mask_nlongs = BITS_TO_LONGS(rp->SAL_nasids_size *
17362306a36Sopenharmony_ci					      BITS_PER_BYTE);
17462306a36Sopenharmony_ci
17562306a36Sopenharmony_ci	/* setup the pointers to the various items in the reserved page */
17662306a36Sopenharmony_ci	xpc_part_nasids = XPC_RP_PART_NASIDS(rp);
17762306a36Sopenharmony_ci	xpc_mach_nasids = XPC_RP_MACH_NASIDS(rp);
17862306a36Sopenharmony_ci
17962306a36Sopenharmony_ci	ret = xpc_arch_ops.setup_rsvd_page(rp);
18062306a36Sopenharmony_ci	if (ret != 0)
18162306a36Sopenharmony_ci		return ret;
18262306a36Sopenharmony_ci
18362306a36Sopenharmony_ci	/*
18462306a36Sopenharmony_ci	 * Set timestamp of when reserved page was setup by XPC.
18562306a36Sopenharmony_ci	 * This signifies to the remote partition that our reserved
18662306a36Sopenharmony_ci	 * page is initialized.
18762306a36Sopenharmony_ci	 */
18862306a36Sopenharmony_ci	new_ts_jiffies = jiffies;
18962306a36Sopenharmony_ci	if (new_ts_jiffies == 0 || new_ts_jiffies == rp->ts_jiffies)
19062306a36Sopenharmony_ci		new_ts_jiffies++;
19162306a36Sopenharmony_ci	rp->ts_jiffies = new_ts_jiffies;
19262306a36Sopenharmony_ci
19362306a36Sopenharmony_ci	xpc_rsvd_page = rp;
19462306a36Sopenharmony_ci	return 0;
19562306a36Sopenharmony_ci}
19662306a36Sopenharmony_ci
19762306a36Sopenharmony_civoid
19862306a36Sopenharmony_cixpc_teardown_rsvd_page(void)
19962306a36Sopenharmony_ci{
20062306a36Sopenharmony_ci	/* a zero timestamp indicates our rsvd page is not initialized */
20162306a36Sopenharmony_ci	xpc_rsvd_page->ts_jiffies = 0;
20262306a36Sopenharmony_ci}
20362306a36Sopenharmony_ci
20462306a36Sopenharmony_ci/*
20562306a36Sopenharmony_ci * Get a copy of a portion of the remote partition's rsvd page.
20662306a36Sopenharmony_ci *
20762306a36Sopenharmony_ci * remote_rp points to a buffer that is cacheline aligned for BTE copies and
20862306a36Sopenharmony_ci * is large enough to contain a copy of their reserved page header and
20962306a36Sopenharmony_ci * part_nasids mask.
21062306a36Sopenharmony_ci */
21162306a36Sopenharmony_cienum xp_retval
21262306a36Sopenharmony_cixpc_get_remote_rp(int nasid, unsigned long *discovered_nasids,
21362306a36Sopenharmony_ci		  struct xpc_rsvd_page *remote_rp, unsigned long *remote_rp_pa)
21462306a36Sopenharmony_ci{
21562306a36Sopenharmony_ci	int l;
21662306a36Sopenharmony_ci	enum xp_retval ret;
21762306a36Sopenharmony_ci
21862306a36Sopenharmony_ci	/* get the reserved page's physical address */
21962306a36Sopenharmony_ci
22062306a36Sopenharmony_ci	*remote_rp_pa = xpc_get_rsvd_page_pa(nasid);
22162306a36Sopenharmony_ci	if (*remote_rp_pa == 0)
22262306a36Sopenharmony_ci		return xpNoRsvdPageAddr;
22362306a36Sopenharmony_ci
22462306a36Sopenharmony_ci	/* pull over the reserved page header and part_nasids mask */
22562306a36Sopenharmony_ci	ret = xp_remote_memcpy(xp_pa(remote_rp), *remote_rp_pa,
22662306a36Sopenharmony_ci			       XPC_RP_HEADER_SIZE + xpc_nasid_mask_nbytes);
22762306a36Sopenharmony_ci	if (ret != xpSuccess)
22862306a36Sopenharmony_ci		return ret;
22962306a36Sopenharmony_ci
23062306a36Sopenharmony_ci	if (discovered_nasids != NULL) {
23162306a36Sopenharmony_ci		unsigned long *remote_part_nasids =
23262306a36Sopenharmony_ci		    XPC_RP_PART_NASIDS(remote_rp);
23362306a36Sopenharmony_ci
23462306a36Sopenharmony_ci		for (l = 0; l < xpc_nasid_mask_nlongs; l++)
23562306a36Sopenharmony_ci			discovered_nasids[l] |= remote_part_nasids[l];
23662306a36Sopenharmony_ci	}
23762306a36Sopenharmony_ci
23862306a36Sopenharmony_ci	/* zero timestamp indicates the reserved page has not been setup */
23962306a36Sopenharmony_ci	if (remote_rp->ts_jiffies == 0)
24062306a36Sopenharmony_ci		return xpRsvdPageNotSet;
24162306a36Sopenharmony_ci
24262306a36Sopenharmony_ci	if (XPC_VERSION_MAJOR(remote_rp->version) !=
24362306a36Sopenharmony_ci	    XPC_VERSION_MAJOR(XPC_RP_VERSION)) {
24462306a36Sopenharmony_ci		return xpBadVersion;
24562306a36Sopenharmony_ci	}
24662306a36Sopenharmony_ci
24762306a36Sopenharmony_ci	/* check that both remote and local partids are valid for each side */
24862306a36Sopenharmony_ci	if (remote_rp->SAL_partid < 0 ||
24962306a36Sopenharmony_ci	    remote_rp->SAL_partid >= xp_max_npartitions ||
25062306a36Sopenharmony_ci	    remote_rp->max_npartitions <= xp_partition_id) {
25162306a36Sopenharmony_ci		return xpInvalidPartid;
25262306a36Sopenharmony_ci	}
25362306a36Sopenharmony_ci
25462306a36Sopenharmony_ci	if (remote_rp->SAL_partid == xp_partition_id)
25562306a36Sopenharmony_ci		return xpLocalPartid;
25662306a36Sopenharmony_ci
25762306a36Sopenharmony_ci	return xpSuccess;
25862306a36Sopenharmony_ci}
25962306a36Sopenharmony_ci
26062306a36Sopenharmony_ci/*
26162306a36Sopenharmony_ci * See if the other side has responded to a partition deactivate request
26262306a36Sopenharmony_ci * from us. Though we requested the remote partition to deactivate with regard
26362306a36Sopenharmony_ci * to us, we really only need to wait for the other side to disengage from us.
26462306a36Sopenharmony_ci */
26562306a36Sopenharmony_cistatic int __xpc_partition_disengaged(struct xpc_partition *part,
26662306a36Sopenharmony_ci				      bool from_timer)
26762306a36Sopenharmony_ci{
26862306a36Sopenharmony_ci	short partid = XPC_PARTID(part);
26962306a36Sopenharmony_ci	int disengaged;
27062306a36Sopenharmony_ci
27162306a36Sopenharmony_ci	disengaged = !xpc_arch_ops.partition_engaged(partid);
27262306a36Sopenharmony_ci	if (part->disengage_timeout) {
27362306a36Sopenharmony_ci		if (!disengaged) {
27462306a36Sopenharmony_ci			if (time_is_after_jiffies(part->disengage_timeout)) {
27562306a36Sopenharmony_ci				/* timelimit hasn't been reached yet */
27662306a36Sopenharmony_ci				return 0;
27762306a36Sopenharmony_ci			}
27862306a36Sopenharmony_ci
27962306a36Sopenharmony_ci			/*
28062306a36Sopenharmony_ci			 * Other side hasn't responded to our deactivate
28162306a36Sopenharmony_ci			 * request in a timely fashion, so assume it's dead.
28262306a36Sopenharmony_ci			 */
28362306a36Sopenharmony_ci
28462306a36Sopenharmony_ci			dev_info(xpc_part, "deactivate request to remote "
28562306a36Sopenharmony_ci				 "partition %d timed out\n", partid);
28662306a36Sopenharmony_ci			xpc_disengage_timedout = 1;
28762306a36Sopenharmony_ci			xpc_arch_ops.assume_partition_disengaged(partid);
28862306a36Sopenharmony_ci			disengaged = 1;
28962306a36Sopenharmony_ci		}
29062306a36Sopenharmony_ci		part->disengage_timeout = 0;
29162306a36Sopenharmony_ci
29262306a36Sopenharmony_ci		/* Cancel the timer function if not called from it */
29362306a36Sopenharmony_ci		if (!from_timer)
29462306a36Sopenharmony_ci			del_timer_sync(&part->disengage_timer);
29562306a36Sopenharmony_ci
29662306a36Sopenharmony_ci		DBUG_ON(part->act_state != XPC_P_AS_DEACTIVATING &&
29762306a36Sopenharmony_ci			part->act_state != XPC_P_AS_INACTIVE);
29862306a36Sopenharmony_ci		if (part->act_state != XPC_P_AS_INACTIVE)
29962306a36Sopenharmony_ci			xpc_wakeup_channel_mgr(part);
30062306a36Sopenharmony_ci
30162306a36Sopenharmony_ci		xpc_arch_ops.cancel_partition_deactivation_request(part);
30262306a36Sopenharmony_ci	}
30362306a36Sopenharmony_ci	return disengaged;
30462306a36Sopenharmony_ci}
30562306a36Sopenharmony_ci
30662306a36Sopenharmony_ciint xpc_partition_disengaged(struct xpc_partition *part)
30762306a36Sopenharmony_ci{
30862306a36Sopenharmony_ci	return __xpc_partition_disengaged(part, false);
30962306a36Sopenharmony_ci}
31062306a36Sopenharmony_ci
31162306a36Sopenharmony_ciint xpc_partition_disengaged_from_timer(struct xpc_partition *part)
31262306a36Sopenharmony_ci{
31362306a36Sopenharmony_ci	return __xpc_partition_disengaged(part, true);
31462306a36Sopenharmony_ci}
31562306a36Sopenharmony_ci
31662306a36Sopenharmony_ci/*
31762306a36Sopenharmony_ci * Mark specified partition as active.
31862306a36Sopenharmony_ci */
31962306a36Sopenharmony_cienum xp_retval
32062306a36Sopenharmony_cixpc_mark_partition_active(struct xpc_partition *part)
32162306a36Sopenharmony_ci{
32262306a36Sopenharmony_ci	unsigned long irq_flags;
32362306a36Sopenharmony_ci	enum xp_retval ret;
32462306a36Sopenharmony_ci
32562306a36Sopenharmony_ci	dev_dbg(xpc_part, "setting partition %d to ACTIVE\n", XPC_PARTID(part));
32662306a36Sopenharmony_ci
32762306a36Sopenharmony_ci	spin_lock_irqsave(&part->act_lock, irq_flags);
32862306a36Sopenharmony_ci	if (part->act_state == XPC_P_AS_ACTIVATING) {
32962306a36Sopenharmony_ci		part->act_state = XPC_P_AS_ACTIVE;
33062306a36Sopenharmony_ci		ret = xpSuccess;
33162306a36Sopenharmony_ci	} else {
33262306a36Sopenharmony_ci		DBUG_ON(part->reason == xpSuccess);
33362306a36Sopenharmony_ci		ret = part->reason;
33462306a36Sopenharmony_ci	}
33562306a36Sopenharmony_ci	spin_unlock_irqrestore(&part->act_lock, irq_flags);
33662306a36Sopenharmony_ci
33762306a36Sopenharmony_ci	return ret;
33862306a36Sopenharmony_ci}
33962306a36Sopenharmony_ci
34062306a36Sopenharmony_ci/*
34162306a36Sopenharmony_ci * Start the process of deactivating the specified partition.
34262306a36Sopenharmony_ci */
34362306a36Sopenharmony_civoid
34462306a36Sopenharmony_cixpc_deactivate_partition(const int line, struct xpc_partition *part,
34562306a36Sopenharmony_ci			 enum xp_retval reason)
34662306a36Sopenharmony_ci{
34762306a36Sopenharmony_ci	unsigned long irq_flags;
34862306a36Sopenharmony_ci
34962306a36Sopenharmony_ci	spin_lock_irqsave(&part->act_lock, irq_flags);
35062306a36Sopenharmony_ci
35162306a36Sopenharmony_ci	if (part->act_state == XPC_P_AS_INACTIVE) {
35262306a36Sopenharmony_ci		XPC_SET_REASON(part, reason, line);
35362306a36Sopenharmony_ci		spin_unlock_irqrestore(&part->act_lock, irq_flags);
35462306a36Sopenharmony_ci		if (reason == xpReactivating) {
35562306a36Sopenharmony_ci			/* we interrupt ourselves to reactivate partition */
35662306a36Sopenharmony_ci			xpc_arch_ops.request_partition_reactivation(part);
35762306a36Sopenharmony_ci		}
35862306a36Sopenharmony_ci		return;
35962306a36Sopenharmony_ci	}
36062306a36Sopenharmony_ci	if (part->act_state == XPC_P_AS_DEACTIVATING) {
36162306a36Sopenharmony_ci		if ((part->reason == xpUnloading && reason != xpUnloading) ||
36262306a36Sopenharmony_ci		    reason == xpReactivating) {
36362306a36Sopenharmony_ci			XPC_SET_REASON(part, reason, line);
36462306a36Sopenharmony_ci		}
36562306a36Sopenharmony_ci		spin_unlock_irqrestore(&part->act_lock, irq_flags);
36662306a36Sopenharmony_ci		return;
36762306a36Sopenharmony_ci	}
36862306a36Sopenharmony_ci
36962306a36Sopenharmony_ci	part->act_state = XPC_P_AS_DEACTIVATING;
37062306a36Sopenharmony_ci	XPC_SET_REASON(part, reason, line);
37162306a36Sopenharmony_ci
37262306a36Sopenharmony_ci	spin_unlock_irqrestore(&part->act_lock, irq_flags);
37362306a36Sopenharmony_ci
37462306a36Sopenharmony_ci	/* ask remote partition to deactivate with regard to us */
37562306a36Sopenharmony_ci	xpc_arch_ops.request_partition_deactivation(part);
37662306a36Sopenharmony_ci
37762306a36Sopenharmony_ci	/* set a timelimit on the disengage phase of the deactivation request */
37862306a36Sopenharmony_ci	part->disengage_timeout = jiffies + (xpc_disengage_timelimit * HZ);
37962306a36Sopenharmony_ci	part->disengage_timer.expires = part->disengage_timeout;
38062306a36Sopenharmony_ci	add_timer(&part->disengage_timer);
38162306a36Sopenharmony_ci
38262306a36Sopenharmony_ci	dev_dbg(xpc_part, "bringing partition %d down, reason = %d\n",
38362306a36Sopenharmony_ci		XPC_PARTID(part), reason);
38462306a36Sopenharmony_ci
38562306a36Sopenharmony_ci	xpc_partition_going_down(part, reason);
38662306a36Sopenharmony_ci}
38762306a36Sopenharmony_ci
38862306a36Sopenharmony_ci/*
38962306a36Sopenharmony_ci * Mark specified partition as inactive.
39062306a36Sopenharmony_ci */
39162306a36Sopenharmony_civoid
39262306a36Sopenharmony_cixpc_mark_partition_inactive(struct xpc_partition *part)
39362306a36Sopenharmony_ci{
39462306a36Sopenharmony_ci	unsigned long irq_flags;
39562306a36Sopenharmony_ci
39662306a36Sopenharmony_ci	dev_dbg(xpc_part, "setting partition %d to INACTIVE\n",
39762306a36Sopenharmony_ci		XPC_PARTID(part));
39862306a36Sopenharmony_ci
39962306a36Sopenharmony_ci	spin_lock_irqsave(&part->act_lock, irq_flags);
40062306a36Sopenharmony_ci	part->act_state = XPC_P_AS_INACTIVE;
40162306a36Sopenharmony_ci	spin_unlock_irqrestore(&part->act_lock, irq_flags);
40262306a36Sopenharmony_ci	part->remote_rp_pa = 0;
40362306a36Sopenharmony_ci}
40462306a36Sopenharmony_ci
40562306a36Sopenharmony_ci/*
40662306a36Sopenharmony_ci * SAL has provided a partition and machine mask.  The partition mask
40762306a36Sopenharmony_ci * contains a bit for each even nasid in our partition.  The machine
40862306a36Sopenharmony_ci * mask contains a bit for each even nasid in the entire machine.
40962306a36Sopenharmony_ci *
41062306a36Sopenharmony_ci * Using those two bit arrays, we can determine which nasids are
41162306a36Sopenharmony_ci * known in the machine.  Each should also have a reserved page
41262306a36Sopenharmony_ci * initialized if they are available for partitioning.
41362306a36Sopenharmony_ci */
41462306a36Sopenharmony_civoid
41562306a36Sopenharmony_cixpc_discovery(void)
41662306a36Sopenharmony_ci{
41762306a36Sopenharmony_ci	void *remote_rp_base;
41862306a36Sopenharmony_ci	struct xpc_rsvd_page *remote_rp;
41962306a36Sopenharmony_ci	unsigned long remote_rp_pa;
42062306a36Sopenharmony_ci	int region;
42162306a36Sopenharmony_ci	int region_size;
42262306a36Sopenharmony_ci	int max_regions;
42362306a36Sopenharmony_ci	int nasid;
42462306a36Sopenharmony_ci	unsigned long *discovered_nasids;
42562306a36Sopenharmony_ci	enum xp_retval ret;
42662306a36Sopenharmony_ci
42762306a36Sopenharmony_ci	remote_rp = xpc_kmalloc_cacheline_aligned(XPC_RP_HEADER_SIZE +
42862306a36Sopenharmony_ci						  xpc_nasid_mask_nbytes,
42962306a36Sopenharmony_ci						  GFP_KERNEL, &remote_rp_base);
43062306a36Sopenharmony_ci	if (remote_rp == NULL)
43162306a36Sopenharmony_ci		return;
43262306a36Sopenharmony_ci
43362306a36Sopenharmony_ci	discovered_nasids = kcalloc(xpc_nasid_mask_nlongs, sizeof(long),
43462306a36Sopenharmony_ci				    GFP_KERNEL);
43562306a36Sopenharmony_ci	if (discovered_nasids == NULL) {
43662306a36Sopenharmony_ci		kfree(remote_rp_base);
43762306a36Sopenharmony_ci		return;
43862306a36Sopenharmony_ci	}
43962306a36Sopenharmony_ci
44062306a36Sopenharmony_ci	/*
44162306a36Sopenharmony_ci	 * The term 'region' in this context refers to the minimum number of
44262306a36Sopenharmony_ci	 * nodes that can comprise an access protection grouping. The access
44362306a36Sopenharmony_ci	 * protection is in regards to memory, IOI and IPI.
44462306a36Sopenharmony_ci	 */
44562306a36Sopenharmony_ci	region_size = xp_region_size;
44662306a36Sopenharmony_ci
44762306a36Sopenharmony_ci	if (is_uv_system())
44862306a36Sopenharmony_ci		max_regions = 256;
44962306a36Sopenharmony_ci	else {
45062306a36Sopenharmony_ci		max_regions = 64;
45162306a36Sopenharmony_ci
45262306a36Sopenharmony_ci		switch (region_size) {
45362306a36Sopenharmony_ci		case 128:
45462306a36Sopenharmony_ci			max_regions *= 2;
45562306a36Sopenharmony_ci			fallthrough;
45662306a36Sopenharmony_ci		case 64:
45762306a36Sopenharmony_ci			max_regions *= 2;
45862306a36Sopenharmony_ci			fallthrough;
45962306a36Sopenharmony_ci		case 32:
46062306a36Sopenharmony_ci			max_regions *= 2;
46162306a36Sopenharmony_ci			region_size = 16;
46262306a36Sopenharmony_ci		}
46362306a36Sopenharmony_ci	}
46462306a36Sopenharmony_ci
46562306a36Sopenharmony_ci	for (region = 0; region < max_regions; region++) {
46662306a36Sopenharmony_ci
46762306a36Sopenharmony_ci		if (xpc_exiting)
46862306a36Sopenharmony_ci			break;
46962306a36Sopenharmony_ci
47062306a36Sopenharmony_ci		dev_dbg(xpc_part, "searching region %d\n", region);
47162306a36Sopenharmony_ci
47262306a36Sopenharmony_ci		for (nasid = (region * region_size * 2);
47362306a36Sopenharmony_ci		     nasid < ((region + 1) * region_size * 2); nasid += 2) {
47462306a36Sopenharmony_ci
47562306a36Sopenharmony_ci			if (xpc_exiting)
47662306a36Sopenharmony_ci				break;
47762306a36Sopenharmony_ci
47862306a36Sopenharmony_ci			dev_dbg(xpc_part, "checking nasid %d\n", nasid);
47962306a36Sopenharmony_ci
48062306a36Sopenharmony_ci			if (test_bit(nasid / 2, xpc_part_nasids)) {
48162306a36Sopenharmony_ci				dev_dbg(xpc_part, "PROM indicates Nasid %d is "
48262306a36Sopenharmony_ci					"part of the local partition; skipping "
48362306a36Sopenharmony_ci					"region\n", nasid);
48462306a36Sopenharmony_ci				break;
48562306a36Sopenharmony_ci			}
48662306a36Sopenharmony_ci
48762306a36Sopenharmony_ci			if (!(test_bit(nasid / 2, xpc_mach_nasids))) {
48862306a36Sopenharmony_ci				dev_dbg(xpc_part, "PROM indicates Nasid %d was "
48962306a36Sopenharmony_ci					"not on Numa-Link network at reset\n",
49062306a36Sopenharmony_ci					nasid);
49162306a36Sopenharmony_ci				continue;
49262306a36Sopenharmony_ci			}
49362306a36Sopenharmony_ci
49462306a36Sopenharmony_ci			if (test_bit(nasid / 2, discovered_nasids)) {
49562306a36Sopenharmony_ci				dev_dbg(xpc_part, "Nasid %d is part of a "
49662306a36Sopenharmony_ci					"partition which was previously "
49762306a36Sopenharmony_ci					"discovered\n", nasid);
49862306a36Sopenharmony_ci				continue;
49962306a36Sopenharmony_ci			}
50062306a36Sopenharmony_ci
50162306a36Sopenharmony_ci			/* pull over the rsvd page header & part_nasids mask */
50262306a36Sopenharmony_ci
50362306a36Sopenharmony_ci			ret = xpc_get_remote_rp(nasid, discovered_nasids,
50462306a36Sopenharmony_ci						remote_rp, &remote_rp_pa);
50562306a36Sopenharmony_ci			if (ret != xpSuccess) {
50662306a36Sopenharmony_ci				dev_dbg(xpc_part, "unable to get reserved page "
50762306a36Sopenharmony_ci					"from nasid %d, reason=%d\n", nasid,
50862306a36Sopenharmony_ci					ret);
50962306a36Sopenharmony_ci
51062306a36Sopenharmony_ci				if (ret == xpLocalPartid)
51162306a36Sopenharmony_ci					break;
51262306a36Sopenharmony_ci
51362306a36Sopenharmony_ci				continue;
51462306a36Sopenharmony_ci			}
51562306a36Sopenharmony_ci
51662306a36Sopenharmony_ci			xpc_arch_ops.request_partition_activation(remote_rp,
51762306a36Sopenharmony_ci							 remote_rp_pa, nasid);
51862306a36Sopenharmony_ci		}
51962306a36Sopenharmony_ci	}
52062306a36Sopenharmony_ci
52162306a36Sopenharmony_ci	kfree(discovered_nasids);
52262306a36Sopenharmony_ci	kfree(remote_rp_base);
52362306a36Sopenharmony_ci}
52462306a36Sopenharmony_ci
52562306a36Sopenharmony_ci/*
52662306a36Sopenharmony_ci * Given a partid, get the nasids owned by that partition from the
52762306a36Sopenharmony_ci * remote partition's reserved page.
52862306a36Sopenharmony_ci */
52962306a36Sopenharmony_cienum xp_retval
53062306a36Sopenharmony_cixpc_initiate_partid_to_nasids(short partid, void *nasid_mask)
53162306a36Sopenharmony_ci{
53262306a36Sopenharmony_ci	struct xpc_partition *part;
53362306a36Sopenharmony_ci	unsigned long part_nasid_pa;
53462306a36Sopenharmony_ci
53562306a36Sopenharmony_ci	part = &xpc_partitions[partid];
53662306a36Sopenharmony_ci	if (part->remote_rp_pa == 0)
53762306a36Sopenharmony_ci		return xpPartitionDown;
53862306a36Sopenharmony_ci
53962306a36Sopenharmony_ci	memset(nasid_mask, 0, xpc_nasid_mask_nbytes);
54062306a36Sopenharmony_ci
54162306a36Sopenharmony_ci	part_nasid_pa = (unsigned long)XPC_RP_PART_NASIDS(part->remote_rp_pa);
54262306a36Sopenharmony_ci
54362306a36Sopenharmony_ci	return xp_remote_memcpy(xp_pa(nasid_mask), part_nasid_pa,
54462306a36Sopenharmony_ci				xpc_nasid_mask_nbytes);
54562306a36Sopenharmony_ci}
546