18c2ecf20Sopenharmony_ci/*
28c2ecf20Sopenharmony_ci * This file is subject to the terms and conditions of the GNU General Public
38c2ecf20Sopenharmony_ci * License.  See the file "COPYING" in the main directory of this archive
48c2ecf20Sopenharmony_ci * for more details.
58c2ecf20Sopenharmony_ci *
68c2ecf20Sopenharmony_ci * (C) Copyright 2020 Hewlett Packard Enterprise Development LP
78c2ecf20Sopenharmony_ci * Copyright (c) 2004-2008 Silicon Graphics, Inc.  All Rights Reserved.
88c2ecf20Sopenharmony_ci */
98c2ecf20Sopenharmony_ci
108c2ecf20Sopenharmony_ci/*
118c2ecf20Sopenharmony_ci * Cross Partition Communication (XPC) partition support.
128c2ecf20Sopenharmony_ci *
138c2ecf20Sopenharmony_ci *	This is the part of XPC that detects the presence/absence of
148c2ecf20Sopenharmony_ci *	other partitions. It provides a heartbeat and monitors the
158c2ecf20Sopenharmony_ci *	heartbeats of other partitions.
168c2ecf20Sopenharmony_ci *
178c2ecf20Sopenharmony_ci */
188c2ecf20Sopenharmony_ci
198c2ecf20Sopenharmony_ci#include <linux/device.h>
208c2ecf20Sopenharmony_ci#include <linux/hardirq.h>
218c2ecf20Sopenharmony_ci#include <linux/slab.h>
228c2ecf20Sopenharmony_ci#include "xpc.h"
238c2ecf20Sopenharmony_ci#include <asm/uv/uv_hub.h>
248c2ecf20Sopenharmony_ci
258c2ecf20Sopenharmony_ci/* XPC is exiting flag */
268c2ecf20Sopenharmony_ciint xpc_exiting;
278c2ecf20Sopenharmony_ci
288c2ecf20Sopenharmony_ci/* this partition's reserved page pointers */
298c2ecf20Sopenharmony_cistruct xpc_rsvd_page *xpc_rsvd_page;
308c2ecf20Sopenharmony_cistatic unsigned long *xpc_part_nasids;
318c2ecf20Sopenharmony_ciunsigned long *xpc_mach_nasids;
328c2ecf20Sopenharmony_ci
338c2ecf20Sopenharmony_cistatic int xpc_nasid_mask_nbytes;	/* #of bytes in nasid mask */
348c2ecf20Sopenharmony_ciint xpc_nasid_mask_nlongs;	/* #of longs in nasid mask */
358c2ecf20Sopenharmony_ci
368c2ecf20Sopenharmony_cistruct xpc_partition *xpc_partitions;
378c2ecf20Sopenharmony_ci
388c2ecf20Sopenharmony_ci/*
398c2ecf20Sopenharmony_ci * Guarantee that the kmalloc'd memory is cacheline aligned.
408c2ecf20Sopenharmony_ci */
418c2ecf20Sopenharmony_civoid *
428c2ecf20Sopenharmony_cixpc_kmalloc_cacheline_aligned(size_t size, gfp_t flags, void **base)
438c2ecf20Sopenharmony_ci{
448c2ecf20Sopenharmony_ci	/* see if kmalloc will give us cachline aligned memory by default */
458c2ecf20Sopenharmony_ci	*base = kmalloc(size, flags);
468c2ecf20Sopenharmony_ci	if (*base == NULL)
478c2ecf20Sopenharmony_ci		return NULL;
488c2ecf20Sopenharmony_ci
498c2ecf20Sopenharmony_ci	if ((u64)*base == L1_CACHE_ALIGN((u64)*base))
508c2ecf20Sopenharmony_ci		return *base;
518c2ecf20Sopenharmony_ci
528c2ecf20Sopenharmony_ci	kfree(*base);
538c2ecf20Sopenharmony_ci
548c2ecf20Sopenharmony_ci	/* nope, we'll have to do it ourselves */
558c2ecf20Sopenharmony_ci	*base = kmalloc(size + L1_CACHE_BYTES, flags);
568c2ecf20Sopenharmony_ci	if (*base == NULL)
578c2ecf20Sopenharmony_ci		return NULL;
588c2ecf20Sopenharmony_ci
598c2ecf20Sopenharmony_ci	return (void *)L1_CACHE_ALIGN((u64)*base);
608c2ecf20Sopenharmony_ci}
618c2ecf20Sopenharmony_ci
628c2ecf20Sopenharmony_ci/*
638c2ecf20Sopenharmony_ci * Given a nasid, get the physical address of the  partition's reserved page
648c2ecf20Sopenharmony_ci * for that nasid. This function returns 0 on any error.
658c2ecf20Sopenharmony_ci */
668c2ecf20Sopenharmony_cistatic unsigned long
678c2ecf20Sopenharmony_cixpc_get_rsvd_page_pa(int nasid)
688c2ecf20Sopenharmony_ci{
698c2ecf20Sopenharmony_ci	enum xp_retval ret;
708c2ecf20Sopenharmony_ci	u64 cookie = 0;
718c2ecf20Sopenharmony_ci	unsigned long rp_pa = nasid;	/* seed with nasid */
728c2ecf20Sopenharmony_ci	size_t len = 0;
738c2ecf20Sopenharmony_ci	size_t buf_len = 0;
748c2ecf20Sopenharmony_ci	void *buf = NULL;
758c2ecf20Sopenharmony_ci	void *buf_base = NULL;
768c2ecf20Sopenharmony_ci	enum xp_retval (*get_partition_rsvd_page_pa)
778c2ecf20Sopenharmony_ci		(void *, u64 *, unsigned long *, size_t *) =
788c2ecf20Sopenharmony_ci		xpc_arch_ops.get_partition_rsvd_page_pa;
798c2ecf20Sopenharmony_ci
808c2ecf20Sopenharmony_ci	while (1) {
818c2ecf20Sopenharmony_ci
828c2ecf20Sopenharmony_ci		/* !!! rp_pa will need to be _gpa on UV.
838c2ecf20Sopenharmony_ci		 * ??? So do we save it into the architecture specific parts
848c2ecf20Sopenharmony_ci		 * ??? of the xpc_partition structure? Do we rename this
858c2ecf20Sopenharmony_ci		 * ??? function or have two versions? Rename rp_pa for UV to
868c2ecf20Sopenharmony_ci		 * ??? rp_gpa?
878c2ecf20Sopenharmony_ci		 */
888c2ecf20Sopenharmony_ci		ret = get_partition_rsvd_page_pa(buf, &cookie, &rp_pa, &len);
898c2ecf20Sopenharmony_ci
908c2ecf20Sopenharmony_ci		dev_dbg(xpc_part, "SAL returned with ret=%d, cookie=0x%016lx, "
918c2ecf20Sopenharmony_ci			"address=0x%016lx, len=0x%016lx\n", ret,
928c2ecf20Sopenharmony_ci			(unsigned long)cookie, rp_pa, len);
938c2ecf20Sopenharmony_ci
948c2ecf20Sopenharmony_ci		if (ret != xpNeedMoreInfo)
958c2ecf20Sopenharmony_ci			break;
968c2ecf20Sopenharmony_ci
978c2ecf20Sopenharmony_ci		if (len > buf_len) {
988c2ecf20Sopenharmony_ci			kfree(buf_base);
998c2ecf20Sopenharmony_ci			buf_len = L1_CACHE_ALIGN(len);
1008c2ecf20Sopenharmony_ci			buf = xpc_kmalloc_cacheline_aligned(buf_len, GFP_KERNEL,
1018c2ecf20Sopenharmony_ci							    &buf_base);
1028c2ecf20Sopenharmony_ci			if (buf_base == NULL) {
1038c2ecf20Sopenharmony_ci				dev_err(xpc_part, "unable to kmalloc "
1048c2ecf20Sopenharmony_ci					"len=0x%016lx\n", buf_len);
1058c2ecf20Sopenharmony_ci				ret = xpNoMemory;
1068c2ecf20Sopenharmony_ci				break;
1078c2ecf20Sopenharmony_ci			}
1088c2ecf20Sopenharmony_ci		}
1098c2ecf20Sopenharmony_ci
1108c2ecf20Sopenharmony_ci		ret = xp_remote_memcpy(xp_pa(buf), rp_pa, len);
1118c2ecf20Sopenharmony_ci		if (ret != xpSuccess) {
1128c2ecf20Sopenharmony_ci			dev_dbg(xpc_part, "xp_remote_memcpy failed %d\n", ret);
1138c2ecf20Sopenharmony_ci			break;
1148c2ecf20Sopenharmony_ci		}
1158c2ecf20Sopenharmony_ci	}
1168c2ecf20Sopenharmony_ci
1178c2ecf20Sopenharmony_ci	kfree(buf_base);
1188c2ecf20Sopenharmony_ci
1198c2ecf20Sopenharmony_ci	if (ret != xpSuccess)
1208c2ecf20Sopenharmony_ci		rp_pa = 0;
1218c2ecf20Sopenharmony_ci
1228c2ecf20Sopenharmony_ci	dev_dbg(xpc_part, "reserved page at phys address 0x%016lx\n", rp_pa);
1238c2ecf20Sopenharmony_ci	return rp_pa;
1248c2ecf20Sopenharmony_ci}
1258c2ecf20Sopenharmony_ci
1268c2ecf20Sopenharmony_ci/*
1278c2ecf20Sopenharmony_ci * Fill the partition reserved page with the information needed by
1288c2ecf20Sopenharmony_ci * other partitions to discover we are alive and establish initial
1298c2ecf20Sopenharmony_ci * communications.
1308c2ecf20Sopenharmony_ci */
1318c2ecf20Sopenharmony_ciint
1328c2ecf20Sopenharmony_cixpc_setup_rsvd_page(void)
1338c2ecf20Sopenharmony_ci{
1348c2ecf20Sopenharmony_ci	int ret;
1358c2ecf20Sopenharmony_ci	struct xpc_rsvd_page *rp;
1368c2ecf20Sopenharmony_ci	unsigned long rp_pa;
1378c2ecf20Sopenharmony_ci	unsigned long new_ts_jiffies;
1388c2ecf20Sopenharmony_ci
1398c2ecf20Sopenharmony_ci	/* get the local reserved page's address */
1408c2ecf20Sopenharmony_ci
1418c2ecf20Sopenharmony_ci	preempt_disable();
1428c2ecf20Sopenharmony_ci	rp_pa = xpc_get_rsvd_page_pa(xp_cpu_to_nasid(smp_processor_id()));
1438c2ecf20Sopenharmony_ci	preempt_enable();
1448c2ecf20Sopenharmony_ci	if (rp_pa == 0) {
1458c2ecf20Sopenharmony_ci		dev_err(xpc_part, "SAL failed to locate the reserved page\n");
1468c2ecf20Sopenharmony_ci		return -ESRCH;
1478c2ecf20Sopenharmony_ci	}
1488c2ecf20Sopenharmony_ci	rp = (struct xpc_rsvd_page *)__va(xp_socket_pa(rp_pa));
1498c2ecf20Sopenharmony_ci
1508c2ecf20Sopenharmony_ci	if (rp->SAL_version < 3) {
1518c2ecf20Sopenharmony_ci		/* SAL_versions < 3 had a SAL_partid defined as a u8 */
1528c2ecf20Sopenharmony_ci		rp->SAL_partid &= 0xff;
1538c2ecf20Sopenharmony_ci	}
1548c2ecf20Sopenharmony_ci	BUG_ON(rp->SAL_partid != xp_partition_id);
1558c2ecf20Sopenharmony_ci
1568c2ecf20Sopenharmony_ci	if (rp->SAL_partid < 0 || rp->SAL_partid >= xp_max_npartitions) {
1578c2ecf20Sopenharmony_ci		dev_err(xpc_part, "the reserved page's partid of %d is outside "
1588c2ecf20Sopenharmony_ci			"supported range (< 0 || >= %d)\n", rp->SAL_partid,
1598c2ecf20Sopenharmony_ci			xp_max_npartitions);
1608c2ecf20Sopenharmony_ci		return -EINVAL;
1618c2ecf20Sopenharmony_ci	}
1628c2ecf20Sopenharmony_ci
1638c2ecf20Sopenharmony_ci	rp->version = XPC_RP_VERSION;
1648c2ecf20Sopenharmony_ci	rp->max_npartitions = xp_max_npartitions;
1658c2ecf20Sopenharmony_ci
1668c2ecf20Sopenharmony_ci	/* establish the actual sizes of the nasid masks */
1678c2ecf20Sopenharmony_ci	if (rp->SAL_version == 1) {
1688c2ecf20Sopenharmony_ci		/* SAL_version 1 didn't set the nasids_size field */
1698c2ecf20Sopenharmony_ci		rp->SAL_nasids_size = 128;
1708c2ecf20Sopenharmony_ci	}
1718c2ecf20Sopenharmony_ci	xpc_nasid_mask_nbytes = rp->SAL_nasids_size;
1728c2ecf20Sopenharmony_ci	xpc_nasid_mask_nlongs = BITS_TO_LONGS(rp->SAL_nasids_size *
1738c2ecf20Sopenharmony_ci					      BITS_PER_BYTE);
1748c2ecf20Sopenharmony_ci
1758c2ecf20Sopenharmony_ci	/* setup the pointers to the various items in the reserved page */
1768c2ecf20Sopenharmony_ci	xpc_part_nasids = XPC_RP_PART_NASIDS(rp);
1778c2ecf20Sopenharmony_ci	xpc_mach_nasids = XPC_RP_MACH_NASIDS(rp);
1788c2ecf20Sopenharmony_ci
1798c2ecf20Sopenharmony_ci	ret = xpc_arch_ops.setup_rsvd_page(rp);
1808c2ecf20Sopenharmony_ci	if (ret != 0)
1818c2ecf20Sopenharmony_ci		return ret;
1828c2ecf20Sopenharmony_ci
1838c2ecf20Sopenharmony_ci	/*
1848c2ecf20Sopenharmony_ci	 * Set timestamp of when reserved page was setup by XPC.
1858c2ecf20Sopenharmony_ci	 * This signifies to the remote partition that our reserved
1868c2ecf20Sopenharmony_ci	 * page is initialized.
1878c2ecf20Sopenharmony_ci	 */
1888c2ecf20Sopenharmony_ci	new_ts_jiffies = jiffies;
1898c2ecf20Sopenharmony_ci	if (new_ts_jiffies == 0 || new_ts_jiffies == rp->ts_jiffies)
1908c2ecf20Sopenharmony_ci		new_ts_jiffies++;
1918c2ecf20Sopenharmony_ci	rp->ts_jiffies = new_ts_jiffies;
1928c2ecf20Sopenharmony_ci
1938c2ecf20Sopenharmony_ci	xpc_rsvd_page = rp;
1948c2ecf20Sopenharmony_ci	return 0;
1958c2ecf20Sopenharmony_ci}
1968c2ecf20Sopenharmony_ci
1978c2ecf20Sopenharmony_civoid
1988c2ecf20Sopenharmony_cixpc_teardown_rsvd_page(void)
1998c2ecf20Sopenharmony_ci{
2008c2ecf20Sopenharmony_ci	/* a zero timestamp indicates our rsvd page is not initialized */
2018c2ecf20Sopenharmony_ci	xpc_rsvd_page->ts_jiffies = 0;
2028c2ecf20Sopenharmony_ci}
2038c2ecf20Sopenharmony_ci
2048c2ecf20Sopenharmony_ci/*
2058c2ecf20Sopenharmony_ci * Get a copy of a portion of the remote partition's rsvd page.
2068c2ecf20Sopenharmony_ci *
2078c2ecf20Sopenharmony_ci * remote_rp points to a buffer that is cacheline aligned for BTE copies and
2088c2ecf20Sopenharmony_ci * is large enough to contain a copy of their reserved page header and
2098c2ecf20Sopenharmony_ci * part_nasids mask.
2108c2ecf20Sopenharmony_ci */
2118c2ecf20Sopenharmony_cienum xp_retval
2128c2ecf20Sopenharmony_cixpc_get_remote_rp(int nasid, unsigned long *discovered_nasids,
2138c2ecf20Sopenharmony_ci		  struct xpc_rsvd_page *remote_rp, unsigned long *remote_rp_pa)
2148c2ecf20Sopenharmony_ci{
2158c2ecf20Sopenharmony_ci	int l;
2168c2ecf20Sopenharmony_ci	enum xp_retval ret;
2178c2ecf20Sopenharmony_ci
2188c2ecf20Sopenharmony_ci	/* get the reserved page's physical address */
2198c2ecf20Sopenharmony_ci
2208c2ecf20Sopenharmony_ci	*remote_rp_pa = xpc_get_rsvd_page_pa(nasid);
2218c2ecf20Sopenharmony_ci	if (*remote_rp_pa == 0)
2228c2ecf20Sopenharmony_ci		return xpNoRsvdPageAddr;
2238c2ecf20Sopenharmony_ci
2248c2ecf20Sopenharmony_ci	/* pull over the reserved page header and part_nasids mask */
2258c2ecf20Sopenharmony_ci	ret = xp_remote_memcpy(xp_pa(remote_rp), *remote_rp_pa,
2268c2ecf20Sopenharmony_ci			       XPC_RP_HEADER_SIZE + xpc_nasid_mask_nbytes);
2278c2ecf20Sopenharmony_ci	if (ret != xpSuccess)
2288c2ecf20Sopenharmony_ci		return ret;
2298c2ecf20Sopenharmony_ci
2308c2ecf20Sopenharmony_ci	if (discovered_nasids != NULL) {
2318c2ecf20Sopenharmony_ci		unsigned long *remote_part_nasids =
2328c2ecf20Sopenharmony_ci		    XPC_RP_PART_NASIDS(remote_rp);
2338c2ecf20Sopenharmony_ci
2348c2ecf20Sopenharmony_ci		for (l = 0; l < xpc_nasid_mask_nlongs; l++)
2358c2ecf20Sopenharmony_ci			discovered_nasids[l] |= remote_part_nasids[l];
2368c2ecf20Sopenharmony_ci	}
2378c2ecf20Sopenharmony_ci
2388c2ecf20Sopenharmony_ci	/* zero timestamp indicates the reserved page has not been setup */
2398c2ecf20Sopenharmony_ci	if (remote_rp->ts_jiffies == 0)
2408c2ecf20Sopenharmony_ci		return xpRsvdPageNotSet;
2418c2ecf20Sopenharmony_ci
2428c2ecf20Sopenharmony_ci	if (XPC_VERSION_MAJOR(remote_rp->version) !=
2438c2ecf20Sopenharmony_ci	    XPC_VERSION_MAJOR(XPC_RP_VERSION)) {
2448c2ecf20Sopenharmony_ci		return xpBadVersion;
2458c2ecf20Sopenharmony_ci	}
2468c2ecf20Sopenharmony_ci
2478c2ecf20Sopenharmony_ci	/* check that both remote and local partids are valid for each side */
2488c2ecf20Sopenharmony_ci	if (remote_rp->SAL_partid < 0 ||
2498c2ecf20Sopenharmony_ci	    remote_rp->SAL_partid >= xp_max_npartitions ||
2508c2ecf20Sopenharmony_ci	    remote_rp->max_npartitions <= xp_partition_id) {
2518c2ecf20Sopenharmony_ci		return xpInvalidPartid;
2528c2ecf20Sopenharmony_ci	}
2538c2ecf20Sopenharmony_ci
2548c2ecf20Sopenharmony_ci	if (remote_rp->SAL_partid == xp_partition_id)
2558c2ecf20Sopenharmony_ci		return xpLocalPartid;
2568c2ecf20Sopenharmony_ci
2578c2ecf20Sopenharmony_ci	return xpSuccess;
2588c2ecf20Sopenharmony_ci}
2598c2ecf20Sopenharmony_ci
2608c2ecf20Sopenharmony_ci/*
2618c2ecf20Sopenharmony_ci * See if the other side has responded to a partition deactivate request
2628c2ecf20Sopenharmony_ci * from us. Though we requested the remote partition to deactivate with regard
2638c2ecf20Sopenharmony_ci * to us, we really only need to wait for the other side to disengage from us.
2648c2ecf20Sopenharmony_ci */
2658c2ecf20Sopenharmony_ciint
2668c2ecf20Sopenharmony_cixpc_partition_disengaged(struct xpc_partition *part)
2678c2ecf20Sopenharmony_ci{
2688c2ecf20Sopenharmony_ci	short partid = XPC_PARTID(part);
2698c2ecf20Sopenharmony_ci	int disengaged;
2708c2ecf20Sopenharmony_ci
2718c2ecf20Sopenharmony_ci	disengaged = !xpc_arch_ops.partition_engaged(partid);
2728c2ecf20Sopenharmony_ci	if (part->disengage_timeout) {
2738c2ecf20Sopenharmony_ci		if (!disengaged) {
2748c2ecf20Sopenharmony_ci			if (time_is_after_jiffies(part->disengage_timeout)) {
2758c2ecf20Sopenharmony_ci				/* timelimit hasn't been reached yet */
2768c2ecf20Sopenharmony_ci				return 0;
2778c2ecf20Sopenharmony_ci			}
2788c2ecf20Sopenharmony_ci
2798c2ecf20Sopenharmony_ci			/*
2808c2ecf20Sopenharmony_ci			 * Other side hasn't responded to our deactivate
2818c2ecf20Sopenharmony_ci			 * request in a timely fashion, so assume it's dead.
2828c2ecf20Sopenharmony_ci			 */
2838c2ecf20Sopenharmony_ci
2848c2ecf20Sopenharmony_ci			dev_info(xpc_part, "deactivate request to remote "
2858c2ecf20Sopenharmony_ci				 "partition %d timed out\n", partid);
2868c2ecf20Sopenharmony_ci			xpc_disengage_timedout = 1;
2878c2ecf20Sopenharmony_ci			xpc_arch_ops.assume_partition_disengaged(partid);
2888c2ecf20Sopenharmony_ci			disengaged = 1;
2898c2ecf20Sopenharmony_ci		}
2908c2ecf20Sopenharmony_ci		part->disengage_timeout = 0;
2918c2ecf20Sopenharmony_ci
2928c2ecf20Sopenharmony_ci		/* cancel the timer function, provided it's not us */
2938c2ecf20Sopenharmony_ci		if (!in_interrupt())
2948c2ecf20Sopenharmony_ci			del_singleshot_timer_sync(&part->disengage_timer);
2958c2ecf20Sopenharmony_ci
2968c2ecf20Sopenharmony_ci		DBUG_ON(part->act_state != XPC_P_AS_DEACTIVATING &&
2978c2ecf20Sopenharmony_ci			part->act_state != XPC_P_AS_INACTIVE);
2988c2ecf20Sopenharmony_ci		if (part->act_state != XPC_P_AS_INACTIVE)
2998c2ecf20Sopenharmony_ci			xpc_wakeup_channel_mgr(part);
3008c2ecf20Sopenharmony_ci
3018c2ecf20Sopenharmony_ci		xpc_arch_ops.cancel_partition_deactivation_request(part);
3028c2ecf20Sopenharmony_ci	}
3038c2ecf20Sopenharmony_ci	return disengaged;
3048c2ecf20Sopenharmony_ci}
3058c2ecf20Sopenharmony_ci
3068c2ecf20Sopenharmony_ci/*
3078c2ecf20Sopenharmony_ci * Mark specified partition as active.
3088c2ecf20Sopenharmony_ci */
3098c2ecf20Sopenharmony_cienum xp_retval
3108c2ecf20Sopenharmony_cixpc_mark_partition_active(struct xpc_partition *part)
3118c2ecf20Sopenharmony_ci{
3128c2ecf20Sopenharmony_ci	unsigned long irq_flags;
3138c2ecf20Sopenharmony_ci	enum xp_retval ret;
3148c2ecf20Sopenharmony_ci
3158c2ecf20Sopenharmony_ci	dev_dbg(xpc_part, "setting partition %d to ACTIVE\n", XPC_PARTID(part));
3168c2ecf20Sopenharmony_ci
3178c2ecf20Sopenharmony_ci	spin_lock_irqsave(&part->act_lock, irq_flags);
3188c2ecf20Sopenharmony_ci	if (part->act_state == XPC_P_AS_ACTIVATING) {
3198c2ecf20Sopenharmony_ci		part->act_state = XPC_P_AS_ACTIVE;
3208c2ecf20Sopenharmony_ci		ret = xpSuccess;
3218c2ecf20Sopenharmony_ci	} else {
3228c2ecf20Sopenharmony_ci		DBUG_ON(part->reason == xpSuccess);
3238c2ecf20Sopenharmony_ci		ret = part->reason;
3248c2ecf20Sopenharmony_ci	}
3258c2ecf20Sopenharmony_ci	spin_unlock_irqrestore(&part->act_lock, irq_flags);
3268c2ecf20Sopenharmony_ci
3278c2ecf20Sopenharmony_ci	return ret;
3288c2ecf20Sopenharmony_ci}
3298c2ecf20Sopenharmony_ci
3308c2ecf20Sopenharmony_ci/*
3318c2ecf20Sopenharmony_ci * Start the process of deactivating the specified partition.
3328c2ecf20Sopenharmony_ci */
3338c2ecf20Sopenharmony_civoid
3348c2ecf20Sopenharmony_cixpc_deactivate_partition(const int line, struct xpc_partition *part,
3358c2ecf20Sopenharmony_ci			 enum xp_retval reason)
3368c2ecf20Sopenharmony_ci{
3378c2ecf20Sopenharmony_ci	unsigned long irq_flags;
3388c2ecf20Sopenharmony_ci
3398c2ecf20Sopenharmony_ci	spin_lock_irqsave(&part->act_lock, irq_flags);
3408c2ecf20Sopenharmony_ci
3418c2ecf20Sopenharmony_ci	if (part->act_state == XPC_P_AS_INACTIVE) {
3428c2ecf20Sopenharmony_ci		XPC_SET_REASON(part, reason, line);
3438c2ecf20Sopenharmony_ci		spin_unlock_irqrestore(&part->act_lock, irq_flags);
3448c2ecf20Sopenharmony_ci		if (reason == xpReactivating) {
3458c2ecf20Sopenharmony_ci			/* we interrupt ourselves to reactivate partition */
3468c2ecf20Sopenharmony_ci			xpc_arch_ops.request_partition_reactivation(part);
3478c2ecf20Sopenharmony_ci		}
3488c2ecf20Sopenharmony_ci		return;
3498c2ecf20Sopenharmony_ci	}
3508c2ecf20Sopenharmony_ci	if (part->act_state == XPC_P_AS_DEACTIVATING) {
3518c2ecf20Sopenharmony_ci		if ((part->reason == xpUnloading && reason != xpUnloading) ||
3528c2ecf20Sopenharmony_ci		    reason == xpReactivating) {
3538c2ecf20Sopenharmony_ci			XPC_SET_REASON(part, reason, line);
3548c2ecf20Sopenharmony_ci		}
3558c2ecf20Sopenharmony_ci		spin_unlock_irqrestore(&part->act_lock, irq_flags);
3568c2ecf20Sopenharmony_ci		return;
3578c2ecf20Sopenharmony_ci	}
3588c2ecf20Sopenharmony_ci
3598c2ecf20Sopenharmony_ci	part->act_state = XPC_P_AS_DEACTIVATING;
3608c2ecf20Sopenharmony_ci	XPC_SET_REASON(part, reason, line);
3618c2ecf20Sopenharmony_ci
3628c2ecf20Sopenharmony_ci	spin_unlock_irqrestore(&part->act_lock, irq_flags);
3638c2ecf20Sopenharmony_ci
3648c2ecf20Sopenharmony_ci	/* ask remote partition to deactivate with regard to us */
3658c2ecf20Sopenharmony_ci	xpc_arch_ops.request_partition_deactivation(part);
3668c2ecf20Sopenharmony_ci
3678c2ecf20Sopenharmony_ci	/* set a timelimit on the disengage phase of the deactivation request */
3688c2ecf20Sopenharmony_ci	part->disengage_timeout = jiffies + (xpc_disengage_timelimit * HZ);
3698c2ecf20Sopenharmony_ci	part->disengage_timer.expires = part->disengage_timeout;
3708c2ecf20Sopenharmony_ci	add_timer(&part->disengage_timer);
3718c2ecf20Sopenharmony_ci
3728c2ecf20Sopenharmony_ci	dev_dbg(xpc_part, "bringing partition %d down, reason = %d\n",
3738c2ecf20Sopenharmony_ci		XPC_PARTID(part), reason);
3748c2ecf20Sopenharmony_ci
3758c2ecf20Sopenharmony_ci	xpc_partition_going_down(part, reason);
3768c2ecf20Sopenharmony_ci}
3778c2ecf20Sopenharmony_ci
3788c2ecf20Sopenharmony_ci/*
3798c2ecf20Sopenharmony_ci * Mark specified partition as inactive.
3808c2ecf20Sopenharmony_ci */
3818c2ecf20Sopenharmony_civoid
3828c2ecf20Sopenharmony_cixpc_mark_partition_inactive(struct xpc_partition *part)
3838c2ecf20Sopenharmony_ci{
3848c2ecf20Sopenharmony_ci	unsigned long irq_flags;
3858c2ecf20Sopenharmony_ci
3868c2ecf20Sopenharmony_ci	dev_dbg(xpc_part, "setting partition %d to INACTIVE\n",
3878c2ecf20Sopenharmony_ci		XPC_PARTID(part));
3888c2ecf20Sopenharmony_ci
3898c2ecf20Sopenharmony_ci	spin_lock_irqsave(&part->act_lock, irq_flags);
3908c2ecf20Sopenharmony_ci	part->act_state = XPC_P_AS_INACTIVE;
3918c2ecf20Sopenharmony_ci	spin_unlock_irqrestore(&part->act_lock, irq_flags);
3928c2ecf20Sopenharmony_ci	part->remote_rp_pa = 0;
3938c2ecf20Sopenharmony_ci}
3948c2ecf20Sopenharmony_ci
3958c2ecf20Sopenharmony_ci/*
3968c2ecf20Sopenharmony_ci * SAL has provided a partition and machine mask.  The partition mask
3978c2ecf20Sopenharmony_ci * contains a bit for each even nasid in our partition.  The machine
3988c2ecf20Sopenharmony_ci * mask contains a bit for each even nasid in the entire machine.
3998c2ecf20Sopenharmony_ci *
4008c2ecf20Sopenharmony_ci * Using those two bit arrays, we can determine which nasids are
4018c2ecf20Sopenharmony_ci * known in the machine.  Each should also have a reserved page
4028c2ecf20Sopenharmony_ci * initialized if they are available for partitioning.
4038c2ecf20Sopenharmony_ci */
4048c2ecf20Sopenharmony_civoid
4058c2ecf20Sopenharmony_cixpc_discovery(void)
4068c2ecf20Sopenharmony_ci{
4078c2ecf20Sopenharmony_ci	void *remote_rp_base;
4088c2ecf20Sopenharmony_ci	struct xpc_rsvd_page *remote_rp;
4098c2ecf20Sopenharmony_ci	unsigned long remote_rp_pa;
4108c2ecf20Sopenharmony_ci	int region;
4118c2ecf20Sopenharmony_ci	int region_size;
4128c2ecf20Sopenharmony_ci	int max_regions;
4138c2ecf20Sopenharmony_ci	int nasid;
4148c2ecf20Sopenharmony_ci	unsigned long *discovered_nasids;
4158c2ecf20Sopenharmony_ci	enum xp_retval ret;
4168c2ecf20Sopenharmony_ci
4178c2ecf20Sopenharmony_ci	remote_rp = xpc_kmalloc_cacheline_aligned(XPC_RP_HEADER_SIZE +
4188c2ecf20Sopenharmony_ci						  xpc_nasid_mask_nbytes,
4198c2ecf20Sopenharmony_ci						  GFP_KERNEL, &remote_rp_base);
4208c2ecf20Sopenharmony_ci	if (remote_rp == NULL)
4218c2ecf20Sopenharmony_ci		return;
4228c2ecf20Sopenharmony_ci
4238c2ecf20Sopenharmony_ci	discovered_nasids = kcalloc(xpc_nasid_mask_nlongs, sizeof(long),
4248c2ecf20Sopenharmony_ci				    GFP_KERNEL);
4258c2ecf20Sopenharmony_ci	if (discovered_nasids == NULL) {
4268c2ecf20Sopenharmony_ci		kfree(remote_rp_base);
4278c2ecf20Sopenharmony_ci		return;
4288c2ecf20Sopenharmony_ci	}
4298c2ecf20Sopenharmony_ci
4308c2ecf20Sopenharmony_ci	/*
4318c2ecf20Sopenharmony_ci	 * The term 'region' in this context refers to the minimum number of
4328c2ecf20Sopenharmony_ci	 * nodes that can comprise an access protection grouping. The access
4338c2ecf20Sopenharmony_ci	 * protection is in regards to memory, IOI and IPI.
4348c2ecf20Sopenharmony_ci	 */
4358c2ecf20Sopenharmony_ci	region_size = xp_region_size;
4368c2ecf20Sopenharmony_ci
4378c2ecf20Sopenharmony_ci	if (is_uv_system())
4388c2ecf20Sopenharmony_ci		max_regions = 256;
4398c2ecf20Sopenharmony_ci	else {
4408c2ecf20Sopenharmony_ci		max_regions = 64;
4418c2ecf20Sopenharmony_ci
4428c2ecf20Sopenharmony_ci		switch (region_size) {
4438c2ecf20Sopenharmony_ci		case 128:
4448c2ecf20Sopenharmony_ci			max_regions *= 2;
4458c2ecf20Sopenharmony_ci			fallthrough;
4468c2ecf20Sopenharmony_ci		case 64:
4478c2ecf20Sopenharmony_ci			max_regions *= 2;
4488c2ecf20Sopenharmony_ci			fallthrough;
4498c2ecf20Sopenharmony_ci		case 32:
4508c2ecf20Sopenharmony_ci			max_regions *= 2;
4518c2ecf20Sopenharmony_ci			region_size = 16;
4528c2ecf20Sopenharmony_ci		}
4538c2ecf20Sopenharmony_ci	}
4548c2ecf20Sopenharmony_ci
4558c2ecf20Sopenharmony_ci	for (region = 0; region < max_regions; region++) {
4568c2ecf20Sopenharmony_ci
4578c2ecf20Sopenharmony_ci		if (xpc_exiting)
4588c2ecf20Sopenharmony_ci			break;
4598c2ecf20Sopenharmony_ci
4608c2ecf20Sopenharmony_ci		dev_dbg(xpc_part, "searching region %d\n", region);
4618c2ecf20Sopenharmony_ci
4628c2ecf20Sopenharmony_ci		for (nasid = (region * region_size * 2);
4638c2ecf20Sopenharmony_ci		     nasid < ((region + 1) * region_size * 2); nasid += 2) {
4648c2ecf20Sopenharmony_ci
4658c2ecf20Sopenharmony_ci			if (xpc_exiting)
4668c2ecf20Sopenharmony_ci				break;
4678c2ecf20Sopenharmony_ci
4688c2ecf20Sopenharmony_ci			dev_dbg(xpc_part, "checking nasid %d\n", nasid);
4698c2ecf20Sopenharmony_ci
4708c2ecf20Sopenharmony_ci			if (test_bit(nasid / 2, xpc_part_nasids)) {
4718c2ecf20Sopenharmony_ci				dev_dbg(xpc_part, "PROM indicates Nasid %d is "
4728c2ecf20Sopenharmony_ci					"part of the local partition; skipping "
4738c2ecf20Sopenharmony_ci					"region\n", nasid);
4748c2ecf20Sopenharmony_ci				break;
4758c2ecf20Sopenharmony_ci			}
4768c2ecf20Sopenharmony_ci
4778c2ecf20Sopenharmony_ci			if (!(test_bit(nasid / 2, xpc_mach_nasids))) {
4788c2ecf20Sopenharmony_ci				dev_dbg(xpc_part, "PROM indicates Nasid %d was "
4798c2ecf20Sopenharmony_ci					"not on Numa-Link network at reset\n",
4808c2ecf20Sopenharmony_ci					nasid);
4818c2ecf20Sopenharmony_ci				continue;
4828c2ecf20Sopenharmony_ci			}
4838c2ecf20Sopenharmony_ci
4848c2ecf20Sopenharmony_ci			if (test_bit(nasid / 2, discovered_nasids)) {
4858c2ecf20Sopenharmony_ci				dev_dbg(xpc_part, "Nasid %d is part of a "
4868c2ecf20Sopenharmony_ci					"partition which was previously "
4878c2ecf20Sopenharmony_ci					"discovered\n", nasid);
4888c2ecf20Sopenharmony_ci				continue;
4898c2ecf20Sopenharmony_ci			}
4908c2ecf20Sopenharmony_ci
4918c2ecf20Sopenharmony_ci			/* pull over the rsvd page header & part_nasids mask */
4928c2ecf20Sopenharmony_ci
4938c2ecf20Sopenharmony_ci			ret = xpc_get_remote_rp(nasid, discovered_nasids,
4948c2ecf20Sopenharmony_ci						remote_rp, &remote_rp_pa);
4958c2ecf20Sopenharmony_ci			if (ret != xpSuccess) {
4968c2ecf20Sopenharmony_ci				dev_dbg(xpc_part, "unable to get reserved page "
4978c2ecf20Sopenharmony_ci					"from nasid %d, reason=%d\n", nasid,
4988c2ecf20Sopenharmony_ci					ret);
4998c2ecf20Sopenharmony_ci
5008c2ecf20Sopenharmony_ci				if (ret == xpLocalPartid)
5018c2ecf20Sopenharmony_ci					break;
5028c2ecf20Sopenharmony_ci
5038c2ecf20Sopenharmony_ci				continue;
5048c2ecf20Sopenharmony_ci			}
5058c2ecf20Sopenharmony_ci
5068c2ecf20Sopenharmony_ci			xpc_arch_ops.request_partition_activation(remote_rp,
5078c2ecf20Sopenharmony_ci							 remote_rp_pa, nasid);
5088c2ecf20Sopenharmony_ci		}
5098c2ecf20Sopenharmony_ci	}
5108c2ecf20Sopenharmony_ci
5118c2ecf20Sopenharmony_ci	kfree(discovered_nasids);
5128c2ecf20Sopenharmony_ci	kfree(remote_rp_base);
5138c2ecf20Sopenharmony_ci}
5148c2ecf20Sopenharmony_ci
5158c2ecf20Sopenharmony_ci/*
5168c2ecf20Sopenharmony_ci * Given a partid, get the nasids owned by that partition from the
5178c2ecf20Sopenharmony_ci * remote partition's reserved page.
5188c2ecf20Sopenharmony_ci */
5198c2ecf20Sopenharmony_cienum xp_retval
5208c2ecf20Sopenharmony_cixpc_initiate_partid_to_nasids(short partid, void *nasid_mask)
5218c2ecf20Sopenharmony_ci{
5228c2ecf20Sopenharmony_ci	struct xpc_partition *part;
5238c2ecf20Sopenharmony_ci	unsigned long part_nasid_pa;
5248c2ecf20Sopenharmony_ci
5258c2ecf20Sopenharmony_ci	part = &xpc_partitions[partid];
5268c2ecf20Sopenharmony_ci	if (part->remote_rp_pa == 0)
5278c2ecf20Sopenharmony_ci		return xpPartitionDown;
5288c2ecf20Sopenharmony_ci
5298c2ecf20Sopenharmony_ci	memset(nasid_mask, 0, xpc_nasid_mask_nbytes);
5308c2ecf20Sopenharmony_ci
5318c2ecf20Sopenharmony_ci	part_nasid_pa = (unsigned long)XPC_RP_PART_NASIDS(part->remote_rp_pa);
5328c2ecf20Sopenharmony_ci
5338c2ecf20Sopenharmony_ci	return xp_remote_memcpy(xp_pa(nasid_mask), part_nasid_pa,
5348c2ecf20Sopenharmony_ci				xpc_nasid_mask_nbytes);
5358c2ecf20Sopenharmony_ci}
536