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 *
67a6d94f0SMike Travis * (C) Copyright 2020 Hewlett Packard Enterprise Development LP
745d9ca49SDean Nelson * Copyright (c) 2004-2008 Silicon Graphics, Inc. All Rights Reserved.
845d9ca49SDean Nelson */
945d9ca49SDean Nelson
1045d9ca49SDean Nelson /*
1145d9ca49SDean Nelson * Cross Partition Communication (XPC) partition support.
1245d9ca49SDean Nelson *
1345d9ca49SDean Nelson * This is the part of XPC that detects the presence/absence of
1445d9ca49SDean Nelson * other partitions. It provides a heartbeat and monitors the
1545d9ca49SDean Nelson * heartbeats of other partitions.
1645d9ca49SDean Nelson *
1745d9ca49SDean Nelson */
1845d9ca49SDean Nelson
19261f3b49SDean Nelson #include <linux/device.h>
20261f3b49SDean Nelson #include <linux/hardirq.h>
215a0e3ad6STejun Heo #include <linux/slab.h>
2245d9ca49SDean Nelson #include "xpc.h"
23c2c9f115SRobin Holt #include <asm/uv/uv_hub.h>
2445d9ca49SDean Nelson
2545d9ca49SDean Nelson /* XPC is exiting flag */
2645d9ca49SDean Nelson int xpc_exiting;
2745d9ca49SDean Nelson
2845d9ca49SDean Nelson /* this partition's reserved page pointers */
2945d9ca49SDean Nelson struct xpc_rsvd_page *xpc_rsvd_page;
3004de7418SDean Nelson static unsigned long *xpc_part_nasids;
3104de7418SDean Nelson unsigned long *xpc_mach_nasids;
3245d9ca49SDean Nelson
3304de7418SDean Nelson static int xpc_nasid_mask_nbytes; /* #of bytes in nasid mask */
3404de7418SDean Nelson int xpc_nasid_mask_nlongs; /* #of longs in nasid mask */
3545d9ca49SDean Nelson
36bc63d387SDean Nelson struct xpc_partition *xpc_partitions;
3745d9ca49SDean Nelson
3845d9ca49SDean Nelson /*
3945d9ca49SDean Nelson * Guarantee that the kmalloc'd memory is cacheline aligned.
4045d9ca49SDean Nelson */
4145d9ca49SDean Nelson void *
xpc_kmalloc_cacheline_aligned(size_t size,gfp_t flags,void ** base)4245d9ca49SDean Nelson xpc_kmalloc_cacheline_aligned(size_t size, gfp_t flags, void **base)
4345d9ca49SDean Nelson {
4445d9ca49SDean Nelson /* see if kmalloc will give us cachline aligned memory by default */
4545d9ca49SDean Nelson *base = kmalloc(size, flags);
462c2b94f9SDean Nelson if (*base == NULL)
4745d9ca49SDean Nelson return NULL;
482c2b94f9SDean Nelson
492c2b94f9SDean Nelson if ((u64)*base == L1_CACHE_ALIGN((u64)*base))
5045d9ca49SDean Nelson return *base;
512c2b94f9SDean Nelson
5245d9ca49SDean Nelson kfree(*base);
5345d9ca49SDean Nelson
5445d9ca49SDean Nelson /* nope, we'll have to do it ourselves */
5545d9ca49SDean Nelson *base = kmalloc(size + L1_CACHE_BYTES, flags);
562c2b94f9SDean Nelson if (*base == NULL)
5745d9ca49SDean Nelson return NULL;
582c2b94f9SDean Nelson
5945d9ca49SDean Nelson return (void *)L1_CACHE_ALIGN((u64)*base);
6045d9ca49SDean Nelson }
6145d9ca49SDean Nelson
6245d9ca49SDean Nelson /*
6345d9ca49SDean Nelson * Given a nasid, get the physical address of the partition's reserved page
6445d9ca49SDean Nelson * for that nasid. This function returns 0 on any error.
6545d9ca49SDean Nelson */
66a812dcc3SDean Nelson static unsigned long
xpc_get_rsvd_page_pa(int nasid)6745d9ca49SDean Nelson xpc_get_rsvd_page_pa(int nasid)
6845d9ca49SDean Nelson {
69908787dbSDean Nelson enum xp_retval ret;
7045d9ca49SDean Nelson u64 cookie = 0;
71a812dcc3SDean Nelson unsigned long rp_pa = nasid; /* seed with nasid */
72261f3b49SDean Nelson size_t len = 0;
73a812dcc3SDean Nelson size_t buf_len = 0;
74b0576f9eSNathan Chancellor void *buf = NULL;
7545d9ca49SDean Nelson void *buf_base = NULL;
76a7665b0aSRobin Holt enum xp_retval (*get_partition_rsvd_page_pa)
77a7665b0aSRobin Holt (void *, u64 *, unsigned long *, size_t *) =
78a7665b0aSRobin Holt xpc_arch_ops.get_partition_rsvd_page_pa;
7945d9ca49SDean Nelson
8045d9ca49SDean Nelson while (1) {
8145d9ca49SDean Nelson
825b8669dfSDean Nelson /* !!! rp_pa will need to be _gpa on UV.
835b8669dfSDean Nelson * ??? So do we save it into the architecture specific parts
845b8669dfSDean Nelson * ??? of the xpc_partition structure? Do we rename this
855b8669dfSDean Nelson * ??? function or have two versions? Rename rp_pa for UV to
865b8669dfSDean Nelson * ??? rp_gpa?
875b8669dfSDean Nelson */
88a7665b0aSRobin Holt ret = get_partition_rsvd_page_pa(buf, &cookie, &rp_pa, &len);
8945d9ca49SDean Nelson
90261f3b49SDean Nelson dev_dbg(xpc_part, "SAL returned with ret=%d, cookie=0x%016lx, "
91261f3b49SDean Nelson "address=0x%016lx, len=0x%016lx\n", ret,
92a812dcc3SDean Nelson (unsigned long)cookie, rp_pa, len);
9345d9ca49SDean Nelson
94261f3b49SDean Nelson if (ret != xpNeedMoreInfo)
9545d9ca49SDean Nelson break;
9645d9ca49SDean Nelson
97c2c9f115SRobin Holt if (len > buf_len) {
9845d9ca49SDean Nelson kfree(buf_base);
9945d9ca49SDean Nelson buf_len = L1_CACHE_ALIGN(len);
100a812dcc3SDean Nelson buf = xpc_kmalloc_cacheline_aligned(buf_len, GFP_KERNEL,
1014a3ad2ddSDean Nelson &buf_base);
10245d9ca49SDean Nelson if (buf_base == NULL) {
10345d9ca49SDean Nelson dev_err(xpc_part, "unable to kmalloc "
104a812dcc3SDean Nelson "len=0x%016lx\n", buf_len);
105261f3b49SDean Nelson ret = xpNoMemory;
10645d9ca49SDean Nelson break;
10745d9ca49SDean Nelson }
10845d9ca49SDean Nelson }
10945d9ca49SDean Nelson
110c2c9f115SRobin Holt ret = xp_remote_memcpy(xp_pa(buf), rp_pa, len);
111908787dbSDean Nelson if (ret != xpSuccess) {
112908787dbSDean Nelson dev_dbg(xpc_part, "xp_remote_memcpy failed %d\n", ret);
11345d9ca49SDean Nelson break;
11445d9ca49SDean Nelson }
11545d9ca49SDean Nelson }
11645d9ca49SDean Nelson
11745d9ca49SDean Nelson kfree(buf_base);
11845d9ca49SDean Nelson
119261f3b49SDean Nelson if (ret != xpSuccess)
12045d9ca49SDean Nelson rp_pa = 0;
1212c2b94f9SDean Nelson
122a812dcc3SDean Nelson dev_dbg(xpc_part, "reserved page at phys address 0x%016lx\n", rp_pa);
12345d9ca49SDean Nelson return rp_pa;
12445d9ca49SDean Nelson }
12545d9ca49SDean Nelson
12645d9ca49SDean Nelson /*
12745d9ca49SDean Nelson * Fill the partition reserved page with the information needed by
12845d9ca49SDean Nelson * other partitions to discover we are alive and establish initial
12945d9ca49SDean Nelson * communications.
13045d9ca49SDean Nelson */
1315b8669dfSDean Nelson int
xpc_setup_rsvd_page(void)13294bd2708SDean Nelson xpc_setup_rsvd_page(void)
13345d9ca49SDean Nelson {
1345b8669dfSDean Nelson int ret;
13545d9ca49SDean Nelson struct xpc_rsvd_page *rp;
136a812dcc3SDean Nelson unsigned long rp_pa;
13781fe7883SDean Nelson unsigned long new_ts_jiffies;
13845d9ca49SDean Nelson
13945d9ca49SDean Nelson /* get the local reserved page's address */
14045d9ca49SDean Nelson
14145d9ca49SDean Nelson preempt_disable();
142261f3b49SDean Nelson rp_pa = xpc_get_rsvd_page_pa(xp_cpu_to_nasid(smp_processor_id()));
14345d9ca49SDean Nelson preempt_enable();
14445d9ca49SDean Nelson if (rp_pa == 0) {
14545d9ca49SDean Nelson dev_err(xpc_part, "SAL failed to locate the reserved page\n");
1465b8669dfSDean Nelson return -ESRCH;
14745d9ca49SDean Nelson }
148c2c9f115SRobin Holt rp = (struct xpc_rsvd_page *)__va(xp_socket_pa(rp_pa));
14945d9ca49SDean Nelson
15094bd2708SDean Nelson if (rp->SAL_version < 3) {
15194bd2708SDean Nelson /* SAL_versions < 3 had a SAL_partid defined as a u8 */
15294bd2708SDean Nelson rp->SAL_partid &= 0xff;
15394bd2708SDean Nelson }
154261f3b49SDean Nelson BUG_ON(rp->SAL_partid != xp_partition_id);
15594bd2708SDean Nelson
15694bd2708SDean Nelson if (rp->SAL_partid < 0 || rp->SAL_partid >= xp_max_npartitions) {
15794bd2708SDean Nelson dev_err(xpc_part, "the reserved page's partid of %d is outside "
15894bd2708SDean Nelson "supported range (< 0 || >= %d)\n", rp->SAL_partid,
15994bd2708SDean Nelson xp_max_npartitions);
1605b8669dfSDean Nelson return -EINVAL;
16145d9ca49SDean Nelson }
16245d9ca49SDean Nelson
16345d9ca49SDean Nelson rp->version = XPC_RP_VERSION;
16494bd2708SDean Nelson rp->max_npartitions = xp_max_npartitions;
16545d9ca49SDean Nelson
16645d9ca49SDean Nelson /* establish the actual sizes of the nasid masks */
16745d9ca49SDean Nelson if (rp->SAL_version == 1) {
16845d9ca49SDean Nelson /* SAL_version 1 didn't set the nasids_size field */
16994bd2708SDean Nelson rp->SAL_nasids_size = 128;
17045d9ca49SDean Nelson }
17104de7418SDean Nelson xpc_nasid_mask_nbytes = rp->SAL_nasids_size;
17204de7418SDean Nelson xpc_nasid_mask_nlongs = BITS_TO_LONGS(rp->SAL_nasids_size *
17304de7418SDean Nelson BITS_PER_BYTE);
17445d9ca49SDean Nelson
17545d9ca49SDean Nelson /* setup the pointers to the various items in the reserved page */
17645d9ca49SDean Nelson xpc_part_nasids = XPC_RP_PART_NASIDS(rp);
17745d9ca49SDean Nelson xpc_mach_nasids = XPC_RP_MACH_NASIDS(rp);
17845d9ca49SDean Nelson
179a7665b0aSRobin Holt ret = xpc_arch_ops.setup_rsvd_page(rp);
1805b8669dfSDean Nelson if (ret != 0)
1815b8669dfSDean Nelson return ret;
18245d9ca49SDean Nelson
18345d9ca49SDean Nelson /*
18494bd2708SDean Nelson * Set timestamp of when reserved page was setup by XPC.
18545d9ca49SDean Nelson * This signifies to the remote partition that our reserved
18645d9ca49SDean Nelson * page is initialized.
18745d9ca49SDean Nelson */
18881fe7883SDean Nelson new_ts_jiffies = jiffies;
18981fe7883SDean Nelson if (new_ts_jiffies == 0 || new_ts_jiffies == rp->ts_jiffies)
19081fe7883SDean Nelson new_ts_jiffies++;
19181fe7883SDean Nelson rp->ts_jiffies = new_ts_jiffies;
19245d9ca49SDean Nelson
1935b8669dfSDean Nelson xpc_rsvd_page = rp;
1945b8669dfSDean Nelson return 0;
1955b8669dfSDean Nelson }
1965b8669dfSDean Nelson
1975b8669dfSDean Nelson void
xpc_teardown_rsvd_page(void)1985b8669dfSDean Nelson xpc_teardown_rsvd_page(void)
1995b8669dfSDean Nelson {
2005b8669dfSDean Nelson /* a zero timestamp indicates our rsvd page is not initialized */
2015b8669dfSDean Nelson xpc_rsvd_page->ts_jiffies = 0;
20245d9ca49SDean Nelson }
20345d9ca49SDean Nelson
20445d9ca49SDean Nelson /*
20545d9ca49SDean Nelson * Get a copy of a portion of the remote partition's rsvd page.
20645d9ca49SDean Nelson *
20745d9ca49SDean Nelson * remote_rp points to a buffer that is cacheline aligned for BTE copies and
20845d9ca49SDean Nelson * is large enough to contain a copy of their reserved page header and
20945d9ca49SDean Nelson * part_nasids mask.
21045d9ca49SDean Nelson */
21133ba3c77SDean Nelson enum xp_retval
xpc_get_remote_rp(int nasid,unsigned long * discovered_nasids,struct xpc_rsvd_page * remote_rp,unsigned long * remote_rp_pa)21204de7418SDean Nelson xpc_get_remote_rp(int nasid, unsigned long *discovered_nasids,
213a812dcc3SDean Nelson struct xpc_rsvd_page *remote_rp, unsigned long *remote_rp_pa)
21445d9ca49SDean Nelson {
21504de7418SDean Nelson int l;
216908787dbSDean Nelson enum xp_retval ret;
21745d9ca49SDean Nelson
21845d9ca49SDean Nelson /* get the reserved page's physical address */
21945d9ca49SDean Nelson
22045d9ca49SDean Nelson *remote_rp_pa = xpc_get_rsvd_page_pa(nasid);
2212c2b94f9SDean Nelson if (*remote_rp_pa == 0)
22265c17b80SDean Nelson return xpNoRsvdPageAddr;
22345d9ca49SDean Nelson
22445d9ca49SDean Nelson /* pull over the reserved page header and part_nasids mask */
225a812dcc3SDean Nelson ret = xp_remote_memcpy(xp_pa(remote_rp), *remote_rp_pa,
22604de7418SDean Nelson XPC_RP_HEADER_SIZE + xpc_nasid_mask_nbytes);
227908787dbSDean Nelson if (ret != xpSuccess)
228908787dbSDean Nelson return ret;
22945d9ca49SDean Nelson
23045d9ca49SDean Nelson if (discovered_nasids != NULL) {
23104de7418SDean Nelson unsigned long *remote_part_nasids =
23204de7418SDean Nelson XPC_RP_PART_NASIDS(remote_rp);
23345d9ca49SDean Nelson
23404de7418SDean Nelson for (l = 0; l < xpc_nasid_mask_nlongs; l++)
23504de7418SDean Nelson discovered_nasids[l] |= remote_part_nasids[l];
23645d9ca49SDean Nelson }
23745d9ca49SDean Nelson
23881fe7883SDean Nelson /* zero timestamp indicates the reserved page has not been setup */
23981fe7883SDean Nelson if (remote_rp->ts_jiffies == 0)
24094bd2708SDean Nelson return xpRsvdPageNotSet;
24194bd2708SDean Nelson
24245d9ca49SDean Nelson if (XPC_VERSION_MAJOR(remote_rp->version) !=
24345d9ca49SDean Nelson XPC_VERSION_MAJOR(XPC_RP_VERSION)) {
24465c17b80SDean Nelson return xpBadVersion;
24545d9ca49SDean Nelson }
24645d9ca49SDean Nelson
247a47d5dacSDean Nelson /* check that both remote and local partids are valid for each side */
248aaa3cd69SDean Nelson if (remote_rp->SAL_partid < 0 ||
249aaa3cd69SDean Nelson remote_rp->SAL_partid >= xp_max_npartitions ||
250261f3b49SDean Nelson remote_rp->max_npartitions <= xp_partition_id) {
25194bd2708SDean Nelson return xpInvalidPartid;
252aaa3cd69SDean Nelson }
253aaa3cd69SDean Nelson
254261f3b49SDean Nelson if (remote_rp->SAL_partid == xp_partition_id)
255aaa3cd69SDean Nelson return xpLocalPartid;
25694bd2708SDean Nelson
25765c17b80SDean Nelson return xpSuccess;
25845d9ca49SDean Nelson }
25945d9ca49SDean Nelson
26045d9ca49SDean Nelson /*
261a47d5dacSDean Nelson * See if the other side has responded to a partition deactivate request
262a47d5dacSDean Nelson * from us. Though we requested the remote partition to deactivate with regard
263a47d5dacSDean Nelson * to us, we really only need to wait for the other side to disengage from us.
26445d9ca49SDean Nelson */
__xpc_partition_disengaged(struct xpc_partition * part,bool from_timer)265*997754f1SThomas Gleixner static int __xpc_partition_disengaged(struct xpc_partition *part,
266*997754f1SThomas Gleixner bool from_timer)
26745d9ca49SDean Nelson {
26864d032baSDean Nelson short partid = XPC_PARTID(part);
26945d9ca49SDean Nelson int disengaged;
27045d9ca49SDean Nelson
271a7665b0aSRobin Holt disengaged = !xpc_arch_ops.partition_engaged(partid);
272a47d5dacSDean Nelson if (part->disengage_timeout) {
27345d9ca49SDean Nelson if (!disengaged) {
274a47d5dacSDean Nelson if (time_is_after_jiffies(part->disengage_timeout)) {
27545d9ca49SDean Nelson /* timelimit hasn't been reached yet */
27645d9ca49SDean Nelson return 0;
27745d9ca49SDean Nelson }
27845d9ca49SDean Nelson
27945d9ca49SDean Nelson /*
280a47d5dacSDean Nelson * Other side hasn't responded to our deactivate
28145d9ca49SDean Nelson * request in a timely fashion, so assume it's dead.
28245d9ca49SDean Nelson */
28345d9ca49SDean Nelson
284a47d5dacSDean Nelson dev_info(xpc_part, "deactivate request to remote "
285a47d5dacSDean Nelson "partition %d timed out\n", partid);
286a47d5dacSDean Nelson xpc_disengage_timedout = 1;
287a7665b0aSRobin Holt xpc_arch_ops.assume_partition_disengaged(partid);
28845d9ca49SDean Nelson disengaged = 1;
28945d9ca49SDean Nelson }
290a47d5dacSDean Nelson part->disengage_timeout = 0;
29145d9ca49SDean Nelson
292*997754f1SThomas Gleixner /* Cancel the timer function if not called from it */
293*997754f1SThomas Gleixner if (!from_timer)
294*997754f1SThomas Gleixner del_timer_sync(&part->disengage_timer);
29545d9ca49SDean Nelson
29683469b55SDean Nelson DBUG_ON(part->act_state != XPC_P_AS_DEACTIVATING &&
29783469b55SDean Nelson part->act_state != XPC_P_AS_INACTIVE);
29883469b55SDean Nelson if (part->act_state != XPC_P_AS_INACTIVE)
29945d9ca49SDean Nelson xpc_wakeup_channel_mgr(part);
30045d9ca49SDean Nelson
301a7665b0aSRobin Holt xpc_arch_ops.cancel_partition_deactivation_request(part);
30245d9ca49SDean Nelson }
30345d9ca49SDean Nelson return disengaged;
30445d9ca49SDean Nelson }
30545d9ca49SDean Nelson
xpc_partition_disengaged(struct xpc_partition * part)306*997754f1SThomas Gleixner int xpc_partition_disengaged(struct xpc_partition *part)
307*997754f1SThomas Gleixner {
308*997754f1SThomas Gleixner return __xpc_partition_disengaged(part, false);
309*997754f1SThomas Gleixner }
310*997754f1SThomas Gleixner
xpc_partition_disengaged_from_timer(struct xpc_partition * part)311*997754f1SThomas Gleixner int xpc_partition_disengaged_from_timer(struct xpc_partition *part)
312*997754f1SThomas Gleixner {
313*997754f1SThomas Gleixner return __xpc_partition_disengaged(part, true);
314*997754f1SThomas Gleixner }
315*997754f1SThomas Gleixner
31645d9ca49SDean Nelson /*
31745d9ca49SDean Nelson * Mark specified partition as active.
31845d9ca49SDean Nelson */
31965c17b80SDean Nelson enum xp_retval
xpc_mark_partition_active(struct xpc_partition * part)32045d9ca49SDean Nelson xpc_mark_partition_active(struct xpc_partition *part)
32145d9ca49SDean Nelson {
32245d9ca49SDean Nelson unsigned long irq_flags;
32365c17b80SDean Nelson enum xp_retval ret;
32445d9ca49SDean Nelson
32545d9ca49SDean Nelson dev_dbg(xpc_part, "setting partition %d to ACTIVE\n", XPC_PARTID(part));
32645d9ca49SDean Nelson
32745d9ca49SDean Nelson spin_lock_irqsave(&part->act_lock, irq_flags);
32883469b55SDean Nelson if (part->act_state == XPC_P_AS_ACTIVATING) {
32983469b55SDean Nelson part->act_state = XPC_P_AS_ACTIVE;
33065c17b80SDean Nelson ret = xpSuccess;
33145d9ca49SDean Nelson } else {
33265c17b80SDean Nelson DBUG_ON(part->reason == xpSuccess);
33345d9ca49SDean Nelson ret = part->reason;
33445d9ca49SDean Nelson }
33545d9ca49SDean Nelson spin_unlock_irqrestore(&part->act_lock, irq_flags);
33645d9ca49SDean Nelson
33745d9ca49SDean Nelson return ret;
33845d9ca49SDean Nelson }
33945d9ca49SDean Nelson
34045d9ca49SDean Nelson /*
341a47d5dacSDean Nelson * Start the process of deactivating the specified partition.
34245d9ca49SDean Nelson */
34345d9ca49SDean Nelson void
xpc_deactivate_partition(const int line,struct xpc_partition * part,enum xp_retval reason)34445d9ca49SDean Nelson xpc_deactivate_partition(const int line, struct xpc_partition *part,
34565c17b80SDean Nelson enum xp_retval reason)
34645d9ca49SDean Nelson {
34745d9ca49SDean Nelson unsigned long irq_flags;
34845d9ca49SDean Nelson
34945d9ca49SDean Nelson spin_lock_irqsave(&part->act_lock, irq_flags);
35045d9ca49SDean Nelson
35183469b55SDean Nelson if (part->act_state == XPC_P_AS_INACTIVE) {
35245d9ca49SDean Nelson XPC_SET_REASON(part, reason, line);
35345d9ca49SDean Nelson spin_unlock_irqrestore(&part->act_lock, irq_flags);
35465c17b80SDean Nelson if (reason == xpReactivating) {
35545d9ca49SDean Nelson /* we interrupt ourselves to reactivate partition */
356a7665b0aSRobin Holt xpc_arch_ops.request_partition_reactivation(part);
35745d9ca49SDean Nelson }
35845d9ca49SDean Nelson return;
35945d9ca49SDean Nelson }
36083469b55SDean Nelson if (part->act_state == XPC_P_AS_DEACTIVATING) {
36165c17b80SDean Nelson if ((part->reason == xpUnloading && reason != xpUnloading) ||
36265c17b80SDean Nelson reason == xpReactivating) {
36345d9ca49SDean Nelson XPC_SET_REASON(part, reason, line);
36445d9ca49SDean Nelson }
36545d9ca49SDean Nelson spin_unlock_irqrestore(&part->act_lock, irq_flags);
36645d9ca49SDean Nelson return;
36745d9ca49SDean Nelson }
36845d9ca49SDean Nelson
36983469b55SDean Nelson part->act_state = XPC_P_AS_DEACTIVATING;
37045d9ca49SDean Nelson XPC_SET_REASON(part, reason, line);
37145d9ca49SDean Nelson
37245d9ca49SDean Nelson spin_unlock_irqrestore(&part->act_lock, irq_flags);
37345d9ca49SDean Nelson
374a47d5dacSDean Nelson /* ask remote partition to deactivate with regard to us */
375a7665b0aSRobin Holt xpc_arch_ops.request_partition_deactivation(part);
37645d9ca49SDean Nelson
377a47d5dacSDean Nelson /* set a timelimit on the disengage phase of the deactivation request */
378a47d5dacSDean Nelson part->disengage_timeout = jiffies + (xpc_disengage_timelimit * HZ);
379a47d5dacSDean Nelson part->disengage_timer.expires = part->disengage_timeout;
380a47d5dacSDean Nelson add_timer(&part->disengage_timer);
38145d9ca49SDean Nelson
38245d9ca49SDean Nelson dev_dbg(xpc_part, "bringing partition %d down, reason = %d\n",
38345d9ca49SDean Nelson XPC_PARTID(part), reason);
38445d9ca49SDean Nelson
38545d9ca49SDean Nelson xpc_partition_going_down(part, reason);
38645d9ca49SDean Nelson }
38745d9ca49SDean Nelson
38845d9ca49SDean Nelson /*
38945d9ca49SDean Nelson * Mark specified partition as inactive.
39045d9ca49SDean Nelson */
39145d9ca49SDean Nelson void
xpc_mark_partition_inactive(struct xpc_partition * part)39245d9ca49SDean Nelson xpc_mark_partition_inactive(struct xpc_partition *part)
39345d9ca49SDean Nelson {
39445d9ca49SDean Nelson unsigned long irq_flags;
39545d9ca49SDean Nelson
39645d9ca49SDean Nelson dev_dbg(xpc_part, "setting partition %d to INACTIVE\n",
39745d9ca49SDean Nelson XPC_PARTID(part));
39845d9ca49SDean Nelson
39945d9ca49SDean Nelson spin_lock_irqsave(&part->act_lock, irq_flags);
40083469b55SDean Nelson part->act_state = XPC_P_AS_INACTIVE;
40145d9ca49SDean Nelson spin_unlock_irqrestore(&part->act_lock, irq_flags);
40245d9ca49SDean Nelson part->remote_rp_pa = 0;
40345d9ca49SDean Nelson }
40445d9ca49SDean Nelson
40545d9ca49SDean Nelson /*
40645d9ca49SDean Nelson * SAL has provided a partition and machine mask. The partition mask
40745d9ca49SDean Nelson * contains a bit for each even nasid in our partition. The machine
40845d9ca49SDean Nelson * mask contains a bit for each even nasid in the entire machine.
40945d9ca49SDean Nelson *
41045d9ca49SDean Nelson * Using those two bit arrays, we can determine which nasids are
41145d9ca49SDean Nelson * known in the machine. Each should also have a reserved page
41245d9ca49SDean Nelson * initialized if they are available for partitioning.
41345d9ca49SDean Nelson */
41445d9ca49SDean Nelson void
xpc_discovery(void)41545d9ca49SDean Nelson xpc_discovery(void)
41645d9ca49SDean Nelson {
41745d9ca49SDean Nelson void *remote_rp_base;
41845d9ca49SDean Nelson struct xpc_rsvd_page *remote_rp;
419a812dcc3SDean Nelson unsigned long remote_rp_pa;
42045d9ca49SDean Nelson int region;
42145d9ca49SDean Nelson int region_size;
42245d9ca49SDean Nelson int max_regions;
42345d9ca49SDean Nelson int nasid;
42404de7418SDean Nelson unsigned long *discovered_nasids;
42565c17b80SDean Nelson enum xp_retval ret;
42645d9ca49SDean Nelson
42745d9ca49SDean Nelson remote_rp = xpc_kmalloc_cacheline_aligned(XPC_RP_HEADER_SIZE +
42804de7418SDean Nelson xpc_nasid_mask_nbytes,
42945d9ca49SDean Nelson GFP_KERNEL, &remote_rp_base);
4302c2b94f9SDean Nelson if (remote_rp == NULL)
43145d9ca49SDean Nelson return;
4322c2b94f9SDean Nelson
4336396bb22SKees Cook discovered_nasids = kcalloc(xpc_nasid_mask_nlongs, sizeof(long),
43445d9ca49SDean Nelson GFP_KERNEL);
43545d9ca49SDean Nelson if (discovered_nasids == NULL) {
43645d9ca49SDean Nelson kfree(remote_rp_base);
43745d9ca49SDean Nelson return;
43845d9ca49SDean Nelson }
43945d9ca49SDean Nelson
44045d9ca49SDean Nelson /*
44145d9ca49SDean Nelson * The term 'region' in this context refers to the minimum number of
44245d9ca49SDean Nelson * nodes that can comprise an access protection grouping. The access
44345d9ca49SDean Nelson * protection is in regards to memory, IOI and IPI.
44445d9ca49SDean Nelson */
445261f3b49SDean Nelson region_size = xp_region_size;
44645d9ca49SDean Nelson
447788b66e3SMike Travis if (is_uv_system())
448c22c7aefSRobin@sgi.com max_regions = 256;
449c22c7aefSRobin@sgi.com else {
450c22c7aefSRobin@sgi.com max_regions = 64;
451c22c7aefSRobin@sgi.com
45245d9ca49SDean Nelson switch (region_size) {
45345d9ca49SDean Nelson case 128:
45445d9ca49SDean Nelson max_regions *= 2;
455df561f66SGustavo A. R. Silva fallthrough;
45645d9ca49SDean Nelson case 64:
45745d9ca49SDean Nelson max_regions *= 2;
458df561f66SGustavo A. R. Silva fallthrough;
45945d9ca49SDean Nelson case 32:
46045d9ca49SDean Nelson max_regions *= 2;
46145d9ca49SDean Nelson region_size = 16;
46245d9ca49SDean Nelson }
463c22c7aefSRobin@sgi.com }
46445d9ca49SDean Nelson
46545d9ca49SDean Nelson for (region = 0; region < max_regions; region++) {
46645d9ca49SDean Nelson
4672c2b94f9SDean Nelson if (xpc_exiting)
46845d9ca49SDean Nelson break;
46945d9ca49SDean Nelson
47045d9ca49SDean Nelson dev_dbg(xpc_part, "searching region %d\n", region);
47145d9ca49SDean Nelson
47245d9ca49SDean Nelson for (nasid = (region * region_size * 2);
4734a3ad2ddSDean Nelson nasid < ((region + 1) * region_size * 2); nasid += 2) {
47445d9ca49SDean Nelson
4752c2b94f9SDean Nelson if (xpc_exiting)
47645d9ca49SDean Nelson break;
47745d9ca49SDean Nelson
47845d9ca49SDean Nelson dev_dbg(xpc_part, "checking nasid %d\n", nasid);
47945d9ca49SDean Nelson
48004de7418SDean Nelson if (test_bit(nasid / 2, xpc_part_nasids)) {
48145d9ca49SDean Nelson dev_dbg(xpc_part, "PROM indicates Nasid %d is "
48245d9ca49SDean Nelson "part of the local partition; skipping "
48345d9ca49SDean Nelson "region\n", nasid);
48445d9ca49SDean Nelson break;
48545d9ca49SDean Nelson }
48645d9ca49SDean Nelson
48704de7418SDean Nelson if (!(test_bit(nasid / 2, xpc_mach_nasids))) {
48845d9ca49SDean Nelson dev_dbg(xpc_part, "PROM indicates Nasid %d was "
48945d9ca49SDean Nelson "not on Numa-Link network at reset\n",
49045d9ca49SDean Nelson nasid);
49145d9ca49SDean Nelson continue;
49245d9ca49SDean Nelson }
49345d9ca49SDean Nelson
49404de7418SDean Nelson if (test_bit(nasid / 2, discovered_nasids)) {
49545d9ca49SDean Nelson dev_dbg(xpc_part, "Nasid %d is part of a "
49645d9ca49SDean Nelson "partition which was previously "
49745d9ca49SDean Nelson "discovered\n", nasid);
49845d9ca49SDean Nelson continue;
49945d9ca49SDean Nelson }
50045d9ca49SDean Nelson
50133ba3c77SDean Nelson /* pull over the rsvd page header & part_nasids mask */
50245d9ca49SDean Nelson
50345d9ca49SDean Nelson ret = xpc_get_remote_rp(nasid, discovered_nasids,
50445d9ca49SDean Nelson remote_rp, &remote_rp_pa);
50565c17b80SDean Nelson if (ret != xpSuccess) {
50645d9ca49SDean Nelson dev_dbg(xpc_part, "unable to get reserved page "
50745d9ca49SDean Nelson "from nasid %d, reason=%d\n", nasid,
50845d9ca49SDean Nelson ret);
50945d9ca49SDean Nelson
51065c17b80SDean Nelson if (ret == xpLocalPartid)
51145d9ca49SDean Nelson break;
5122c2b94f9SDean Nelson
51345d9ca49SDean Nelson continue;
51445d9ca49SDean Nelson }
51545d9ca49SDean Nelson
516a7665b0aSRobin Holt xpc_arch_ops.request_partition_activation(remote_rp,
51733ba3c77SDean Nelson remote_rp_pa, nasid);
51845d9ca49SDean Nelson }
51945d9ca49SDean Nelson }
52045d9ca49SDean Nelson
52145d9ca49SDean Nelson kfree(discovered_nasids);
52245d9ca49SDean Nelson kfree(remote_rp_base);
52345d9ca49SDean Nelson }
52445d9ca49SDean Nelson
52545d9ca49SDean Nelson /*
52645d9ca49SDean Nelson * Given a partid, get the nasids owned by that partition from the
52745d9ca49SDean Nelson * remote partition's reserved page.
52845d9ca49SDean Nelson */
52965c17b80SDean Nelson enum xp_retval
xpc_initiate_partid_to_nasids(short partid,void * nasid_mask)53064d032baSDean Nelson xpc_initiate_partid_to_nasids(short partid, void *nasid_mask)
53145d9ca49SDean Nelson {
53245d9ca49SDean Nelson struct xpc_partition *part;
533a812dcc3SDean Nelson unsigned long part_nasid_pa;
53445d9ca49SDean Nelson
53545d9ca49SDean Nelson part = &xpc_partitions[partid];
5362c2b94f9SDean Nelson if (part->remote_rp_pa == 0)
53765c17b80SDean Nelson return xpPartitionDown;
53845d9ca49SDean Nelson
53904de7418SDean Nelson memset(nasid_mask, 0, xpc_nasid_mask_nbytes);
54045d9ca49SDean Nelson
541a812dcc3SDean Nelson part_nasid_pa = (unsigned long)XPC_RP_PART_NASIDS(part->remote_rp_pa);
54245d9ca49SDean Nelson
543a812dcc3SDean Nelson return xp_remote_memcpy(xp_pa(nasid_mask), part_nasid_pa,
54404de7418SDean Nelson xpc_nasid_mask_nbytes);
54545d9ca49SDean Nelson }
546