18c2ecf20Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-or-later
28c2ecf20Sopenharmony_ci/*
38c2ecf20Sopenharmony_ci * SN Platform GRU Driver
48c2ecf20Sopenharmony_ci *
58c2ecf20Sopenharmony_ci *              KERNEL SERVICES THAT USE THE GRU
68c2ecf20Sopenharmony_ci *
78c2ecf20Sopenharmony_ci *  Copyright (c) 2008 Silicon Graphics, Inc.  All Rights Reserved.
88c2ecf20Sopenharmony_ci */
98c2ecf20Sopenharmony_ci
108c2ecf20Sopenharmony_ci#include <linux/kernel.h>
118c2ecf20Sopenharmony_ci#include <linux/errno.h>
128c2ecf20Sopenharmony_ci#include <linux/slab.h>
138c2ecf20Sopenharmony_ci#include <linux/mm.h>
148c2ecf20Sopenharmony_ci#include <linux/spinlock.h>
158c2ecf20Sopenharmony_ci#include <linux/device.h>
168c2ecf20Sopenharmony_ci#include <linux/miscdevice.h>
178c2ecf20Sopenharmony_ci#include <linux/proc_fs.h>
188c2ecf20Sopenharmony_ci#include <linux/interrupt.h>
198c2ecf20Sopenharmony_ci#include <linux/sync_core.h>
208c2ecf20Sopenharmony_ci#include <linux/uaccess.h>
218c2ecf20Sopenharmony_ci#include <linux/delay.h>
228c2ecf20Sopenharmony_ci#include <linux/export.h>
238c2ecf20Sopenharmony_ci#include <asm/io_apic.h>
248c2ecf20Sopenharmony_ci#include "gru.h"
258c2ecf20Sopenharmony_ci#include "grulib.h"
268c2ecf20Sopenharmony_ci#include "grutables.h"
278c2ecf20Sopenharmony_ci#include "grukservices.h"
288c2ecf20Sopenharmony_ci#include "gru_instructions.h"
298c2ecf20Sopenharmony_ci#include <asm/uv/uv_hub.h>
308c2ecf20Sopenharmony_ci
318c2ecf20Sopenharmony_ci/*
328c2ecf20Sopenharmony_ci * Kernel GRU Usage
338c2ecf20Sopenharmony_ci *
348c2ecf20Sopenharmony_ci * The following is an interim algorithm for management of kernel GRU
358c2ecf20Sopenharmony_ci * resources. This will likely be replaced when we better understand the
368c2ecf20Sopenharmony_ci * kernel/user requirements.
378c2ecf20Sopenharmony_ci *
388c2ecf20Sopenharmony_ci * Blade percpu resources reserved for kernel use. These resources are
398c2ecf20Sopenharmony_ci * reserved whenever the the kernel context for the blade is loaded. Note
408c2ecf20Sopenharmony_ci * that the kernel context is not guaranteed to be always available. It is
418c2ecf20Sopenharmony_ci * loaded on demand & can be stolen by a user if the user demand exceeds the
428c2ecf20Sopenharmony_ci * kernel demand. The kernel can always reload the kernel context but
438c2ecf20Sopenharmony_ci * a SLEEP may be required!!!.
448c2ecf20Sopenharmony_ci *
458c2ecf20Sopenharmony_ci * Async Overview:
468c2ecf20Sopenharmony_ci *
478c2ecf20Sopenharmony_ci * 	Each blade has one "kernel context" that owns GRU kernel resources
488c2ecf20Sopenharmony_ci * 	located on the blade. Kernel drivers use GRU resources in this context
498c2ecf20Sopenharmony_ci * 	for sending messages, zeroing memory, etc.
508c2ecf20Sopenharmony_ci *
518c2ecf20Sopenharmony_ci * 	The kernel context is dynamically loaded on demand. If it is not in
528c2ecf20Sopenharmony_ci * 	use by the kernel, the kernel context can be unloaded & given to a user.
538c2ecf20Sopenharmony_ci * 	The kernel context will be reloaded when needed. This may require that
548c2ecf20Sopenharmony_ci * 	a context be stolen from a user.
558c2ecf20Sopenharmony_ci * 		NOTE: frequent unloading/reloading of the kernel context is
568c2ecf20Sopenharmony_ci * 		expensive. We are depending on batch schedulers, cpusets, sane
578c2ecf20Sopenharmony_ci * 		drivers or some other mechanism to prevent the need for frequent
588c2ecf20Sopenharmony_ci *	 	stealing/reloading.
598c2ecf20Sopenharmony_ci *
608c2ecf20Sopenharmony_ci * 	The kernel context consists of two parts:
618c2ecf20Sopenharmony_ci * 		- 1 CB & a few DSRs that are reserved for each cpu on the blade.
628c2ecf20Sopenharmony_ci * 		  Each cpu has it's own private resources & does not share them
638c2ecf20Sopenharmony_ci * 		  with other cpus. These resources are used serially, ie,
648c2ecf20Sopenharmony_ci * 		  locked, used & unlocked  on each call to a function in
658c2ecf20Sopenharmony_ci * 		  grukservices.
668c2ecf20Sopenharmony_ci * 		  	(Now that we have dynamic loading of kernel contexts, I
678c2ecf20Sopenharmony_ci * 		  	 may rethink this & allow sharing between cpus....)
688c2ecf20Sopenharmony_ci *
698c2ecf20Sopenharmony_ci *		- Additional resources can be reserved long term & used directly
708c2ecf20Sopenharmony_ci *		  by UV drivers located in the kernel. Drivers using these GRU
718c2ecf20Sopenharmony_ci *		  resources can use asynchronous GRU instructions that send
728c2ecf20Sopenharmony_ci *		  interrupts on completion.
738c2ecf20Sopenharmony_ci *		  	- these resources must be explicitly locked/unlocked
748c2ecf20Sopenharmony_ci *		  	- locked resources prevent (obviously) the kernel
758c2ecf20Sopenharmony_ci *		  	  context from being unloaded.
768c2ecf20Sopenharmony_ci *			- drivers using these resource directly issue their own
778c2ecf20Sopenharmony_ci *			  GRU instruction and must wait/check completion.
788c2ecf20Sopenharmony_ci *
798c2ecf20Sopenharmony_ci * 		  When these resources are reserved, the caller can optionally
808c2ecf20Sopenharmony_ci * 		  associate a wait_queue with the resources and use asynchronous
818c2ecf20Sopenharmony_ci * 		  GRU instructions. When an async GRU instruction completes, the
828c2ecf20Sopenharmony_ci * 		  driver will do a wakeup on the event.
838c2ecf20Sopenharmony_ci *
848c2ecf20Sopenharmony_ci */
858c2ecf20Sopenharmony_ci
868c2ecf20Sopenharmony_ci
878c2ecf20Sopenharmony_ci#define ASYNC_HAN_TO_BID(h)	((h) - 1)
888c2ecf20Sopenharmony_ci#define ASYNC_BID_TO_HAN(b)	((b) + 1)
898c2ecf20Sopenharmony_ci#define ASYNC_HAN_TO_BS(h)	gru_base[ASYNC_HAN_TO_BID(h)]
908c2ecf20Sopenharmony_ci
918c2ecf20Sopenharmony_ci#define GRU_NUM_KERNEL_CBR	1
928c2ecf20Sopenharmony_ci#define GRU_NUM_KERNEL_DSR_BYTES 256
938c2ecf20Sopenharmony_ci#define GRU_NUM_KERNEL_DSR_CL	(GRU_NUM_KERNEL_DSR_BYTES /		\
948c2ecf20Sopenharmony_ci					GRU_CACHE_LINE_BYTES)
958c2ecf20Sopenharmony_ci
968c2ecf20Sopenharmony_ci/* GRU instruction attributes for all instructions */
978c2ecf20Sopenharmony_ci#define IMA			IMA_CB_DELAY
988c2ecf20Sopenharmony_ci
998c2ecf20Sopenharmony_ci/* GRU cacheline size is always 64 bytes - even on arches with 128 byte lines */
1008c2ecf20Sopenharmony_ci#define __gru_cacheline_aligned__                               \
1018c2ecf20Sopenharmony_ci	__attribute__((__aligned__(GRU_CACHE_LINE_BYTES)))
1028c2ecf20Sopenharmony_ci
1038c2ecf20Sopenharmony_ci#define MAGIC	0x1234567887654321UL
1048c2ecf20Sopenharmony_ci
1058c2ecf20Sopenharmony_ci/* Default retry count for GRU errors on kernel instructions */
1068c2ecf20Sopenharmony_ci#define EXCEPTION_RETRY_LIMIT	3
1078c2ecf20Sopenharmony_ci
1088c2ecf20Sopenharmony_ci/* Status of message queue sections */
1098c2ecf20Sopenharmony_ci#define MQS_EMPTY		0
1108c2ecf20Sopenharmony_ci#define MQS_FULL		1
1118c2ecf20Sopenharmony_ci#define MQS_NOOP		2
1128c2ecf20Sopenharmony_ci
1138c2ecf20Sopenharmony_ci/*----------------- RESOURCE MANAGEMENT -------------------------------------*/
1148c2ecf20Sopenharmony_ci/* optimized for x86_64 */
1158c2ecf20Sopenharmony_cistruct message_queue {
1168c2ecf20Sopenharmony_ci	union gru_mesqhead	head __gru_cacheline_aligned__;	/* CL 0 */
1178c2ecf20Sopenharmony_ci	int			qlines;				/* DW 1 */
1188c2ecf20Sopenharmony_ci	long 			hstatus[2];
1198c2ecf20Sopenharmony_ci	void 			*next __gru_cacheline_aligned__;/* CL 1 */
1208c2ecf20Sopenharmony_ci	void 			*limit;
1218c2ecf20Sopenharmony_ci	void 			*start;
1228c2ecf20Sopenharmony_ci	void 			*start2;
1238c2ecf20Sopenharmony_ci	char			data ____cacheline_aligned;	/* CL 2 */
1248c2ecf20Sopenharmony_ci};
1258c2ecf20Sopenharmony_ci
1268c2ecf20Sopenharmony_ci/* First word in every message - used by mesq interface */
1278c2ecf20Sopenharmony_cistruct message_header {
1288c2ecf20Sopenharmony_ci	char	present;
1298c2ecf20Sopenharmony_ci	char	present2;
1308c2ecf20Sopenharmony_ci	char 	lines;
1318c2ecf20Sopenharmony_ci	char	fill;
1328c2ecf20Sopenharmony_ci};
1338c2ecf20Sopenharmony_ci
1348c2ecf20Sopenharmony_ci#define HSTATUS(mq, h)	((mq) + offsetof(struct message_queue, hstatus[h]))
1358c2ecf20Sopenharmony_ci
1368c2ecf20Sopenharmony_ci/*
1378c2ecf20Sopenharmony_ci * Reload the blade's kernel context into a GRU chiplet. Called holding
1388c2ecf20Sopenharmony_ci * the bs_kgts_sema for READ. Will steal user contexts if necessary.
1398c2ecf20Sopenharmony_ci */
1408c2ecf20Sopenharmony_cistatic void gru_load_kernel_context(struct gru_blade_state *bs, int blade_id)
1418c2ecf20Sopenharmony_ci{
1428c2ecf20Sopenharmony_ci	struct gru_state *gru;
1438c2ecf20Sopenharmony_ci	struct gru_thread_state *kgts;
1448c2ecf20Sopenharmony_ci	void *vaddr;
1458c2ecf20Sopenharmony_ci	int ctxnum, ncpus;
1468c2ecf20Sopenharmony_ci
1478c2ecf20Sopenharmony_ci	up_read(&bs->bs_kgts_sema);
1488c2ecf20Sopenharmony_ci	down_write(&bs->bs_kgts_sema);
1498c2ecf20Sopenharmony_ci
1508c2ecf20Sopenharmony_ci	if (!bs->bs_kgts) {
1518c2ecf20Sopenharmony_ci		do {
1528c2ecf20Sopenharmony_ci			bs->bs_kgts = gru_alloc_gts(NULL, 0, 0, 0, 0, 0);
1538c2ecf20Sopenharmony_ci			if (!IS_ERR(bs->bs_kgts))
1548c2ecf20Sopenharmony_ci				break;
1558c2ecf20Sopenharmony_ci			msleep(1);
1568c2ecf20Sopenharmony_ci		} while (true);
1578c2ecf20Sopenharmony_ci		bs->bs_kgts->ts_user_blade_id = blade_id;
1588c2ecf20Sopenharmony_ci	}
1598c2ecf20Sopenharmony_ci	kgts = bs->bs_kgts;
1608c2ecf20Sopenharmony_ci
1618c2ecf20Sopenharmony_ci	if (!kgts->ts_gru) {
1628c2ecf20Sopenharmony_ci		STAT(load_kernel_context);
1638c2ecf20Sopenharmony_ci		ncpus = uv_blade_nr_possible_cpus(blade_id);
1648c2ecf20Sopenharmony_ci		kgts->ts_cbr_au_count = GRU_CB_COUNT_TO_AU(
1658c2ecf20Sopenharmony_ci			GRU_NUM_KERNEL_CBR * ncpus + bs->bs_async_cbrs);
1668c2ecf20Sopenharmony_ci		kgts->ts_dsr_au_count = GRU_DS_BYTES_TO_AU(
1678c2ecf20Sopenharmony_ci			GRU_NUM_KERNEL_DSR_BYTES * ncpus +
1688c2ecf20Sopenharmony_ci				bs->bs_async_dsr_bytes);
1698c2ecf20Sopenharmony_ci		while (!gru_assign_gru_context(kgts)) {
1708c2ecf20Sopenharmony_ci			msleep(1);
1718c2ecf20Sopenharmony_ci			gru_steal_context(kgts);
1728c2ecf20Sopenharmony_ci		}
1738c2ecf20Sopenharmony_ci		gru_load_context(kgts);
1748c2ecf20Sopenharmony_ci		gru = bs->bs_kgts->ts_gru;
1758c2ecf20Sopenharmony_ci		vaddr = gru->gs_gru_base_vaddr;
1768c2ecf20Sopenharmony_ci		ctxnum = kgts->ts_ctxnum;
1778c2ecf20Sopenharmony_ci		bs->kernel_cb = get_gseg_base_address_cb(vaddr, ctxnum, 0);
1788c2ecf20Sopenharmony_ci		bs->kernel_dsr = get_gseg_base_address_ds(vaddr, ctxnum, 0);
1798c2ecf20Sopenharmony_ci	}
1808c2ecf20Sopenharmony_ci	downgrade_write(&bs->bs_kgts_sema);
1818c2ecf20Sopenharmony_ci}
1828c2ecf20Sopenharmony_ci
1838c2ecf20Sopenharmony_ci/*
1848c2ecf20Sopenharmony_ci * Free all kernel contexts that are not currently in use.
1858c2ecf20Sopenharmony_ci *   Returns 0 if all freed, else number of inuse context.
1868c2ecf20Sopenharmony_ci */
1878c2ecf20Sopenharmony_cistatic int gru_free_kernel_contexts(void)
1888c2ecf20Sopenharmony_ci{
1898c2ecf20Sopenharmony_ci	struct gru_blade_state *bs;
1908c2ecf20Sopenharmony_ci	struct gru_thread_state *kgts;
1918c2ecf20Sopenharmony_ci	int bid, ret = 0;
1928c2ecf20Sopenharmony_ci
1938c2ecf20Sopenharmony_ci	for (bid = 0; bid < GRU_MAX_BLADES; bid++) {
1948c2ecf20Sopenharmony_ci		bs = gru_base[bid];
1958c2ecf20Sopenharmony_ci		if (!bs)
1968c2ecf20Sopenharmony_ci			continue;
1978c2ecf20Sopenharmony_ci
1988c2ecf20Sopenharmony_ci		/* Ignore busy contexts. Don't want to block here.  */
1998c2ecf20Sopenharmony_ci		if (down_write_trylock(&bs->bs_kgts_sema)) {
2008c2ecf20Sopenharmony_ci			kgts = bs->bs_kgts;
2018c2ecf20Sopenharmony_ci			if (kgts && kgts->ts_gru)
2028c2ecf20Sopenharmony_ci				gru_unload_context(kgts, 0);
2038c2ecf20Sopenharmony_ci			bs->bs_kgts = NULL;
2048c2ecf20Sopenharmony_ci			up_write(&bs->bs_kgts_sema);
2058c2ecf20Sopenharmony_ci			kfree(kgts);
2068c2ecf20Sopenharmony_ci		} else {
2078c2ecf20Sopenharmony_ci			ret++;
2088c2ecf20Sopenharmony_ci		}
2098c2ecf20Sopenharmony_ci	}
2108c2ecf20Sopenharmony_ci	return ret;
2118c2ecf20Sopenharmony_ci}
2128c2ecf20Sopenharmony_ci
2138c2ecf20Sopenharmony_ci/*
2148c2ecf20Sopenharmony_ci * Lock & load the kernel context for the specified blade.
2158c2ecf20Sopenharmony_ci */
2168c2ecf20Sopenharmony_cistatic struct gru_blade_state *gru_lock_kernel_context(int blade_id)
2178c2ecf20Sopenharmony_ci{
2188c2ecf20Sopenharmony_ci	struct gru_blade_state *bs;
2198c2ecf20Sopenharmony_ci	int bid;
2208c2ecf20Sopenharmony_ci
2218c2ecf20Sopenharmony_ci	STAT(lock_kernel_context);
2228c2ecf20Sopenharmony_ciagain:
2238c2ecf20Sopenharmony_ci	bid = blade_id < 0 ? uv_numa_blade_id() : blade_id;
2248c2ecf20Sopenharmony_ci	bs = gru_base[bid];
2258c2ecf20Sopenharmony_ci
2268c2ecf20Sopenharmony_ci	/* Handle the case where migration occurred while waiting for the sema */
2278c2ecf20Sopenharmony_ci	down_read(&bs->bs_kgts_sema);
2288c2ecf20Sopenharmony_ci	if (blade_id < 0 && bid != uv_numa_blade_id()) {
2298c2ecf20Sopenharmony_ci		up_read(&bs->bs_kgts_sema);
2308c2ecf20Sopenharmony_ci		goto again;
2318c2ecf20Sopenharmony_ci	}
2328c2ecf20Sopenharmony_ci	if (!bs->bs_kgts || !bs->bs_kgts->ts_gru)
2338c2ecf20Sopenharmony_ci		gru_load_kernel_context(bs, bid);
2348c2ecf20Sopenharmony_ci	return bs;
2358c2ecf20Sopenharmony_ci
2368c2ecf20Sopenharmony_ci}
2378c2ecf20Sopenharmony_ci
2388c2ecf20Sopenharmony_ci/*
2398c2ecf20Sopenharmony_ci * Unlock the kernel context for the specified blade. Context is not
2408c2ecf20Sopenharmony_ci * unloaded but may be stolen before next use.
2418c2ecf20Sopenharmony_ci */
2428c2ecf20Sopenharmony_cistatic void gru_unlock_kernel_context(int blade_id)
2438c2ecf20Sopenharmony_ci{
2448c2ecf20Sopenharmony_ci	struct gru_blade_state *bs;
2458c2ecf20Sopenharmony_ci
2468c2ecf20Sopenharmony_ci	bs = gru_base[blade_id];
2478c2ecf20Sopenharmony_ci	up_read(&bs->bs_kgts_sema);
2488c2ecf20Sopenharmony_ci	STAT(unlock_kernel_context);
2498c2ecf20Sopenharmony_ci}
2508c2ecf20Sopenharmony_ci
2518c2ecf20Sopenharmony_ci/*
2528c2ecf20Sopenharmony_ci * Reserve & get pointers to the DSR/CBRs reserved for the current cpu.
2538c2ecf20Sopenharmony_ci * 	- returns with preemption disabled
2548c2ecf20Sopenharmony_ci */
2558c2ecf20Sopenharmony_cistatic int gru_get_cpu_resources(int dsr_bytes, void **cb, void **dsr)
2568c2ecf20Sopenharmony_ci{
2578c2ecf20Sopenharmony_ci	struct gru_blade_state *bs;
2588c2ecf20Sopenharmony_ci	int lcpu;
2598c2ecf20Sopenharmony_ci
2608c2ecf20Sopenharmony_ci	BUG_ON(dsr_bytes > GRU_NUM_KERNEL_DSR_BYTES);
2618c2ecf20Sopenharmony_ci	preempt_disable();
2628c2ecf20Sopenharmony_ci	bs = gru_lock_kernel_context(-1);
2638c2ecf20Sopenharmony_ci	lcpu = uv_blade_processor_id();
2648c2ecf20Sopenharmony_ci	*cb = bs->kernel_cb + lcpu * GRU_HANDLE_STRIDE;
2658c2ecf20Sopenharmony_ci	*dsr = bs->kernel_dsr + lcpu * GRU_NUM_KERNEL_DSR_BYTES;
2668c2ecf20Sopenharmony_ci	return 0;
2678c2ecf20Sopenharmony_ci}
2688c2ecf20Sopenharmony_ci
2698c2ecf20Sopenharmony_ci/*
2708c2ecf20Sopenharmony_ci * Free the current cpus reserved DSR/CBR resources.
2718c2ecf20Sopenharmony_ci */
2728c2ecf20Sopenharmony_cistatic void gru_free_cpu_resources(void *cb, void *dsr)
2738c2ecf20Sopenharmony_ci{
2748c2ecf20Sopenharmony_ci	gru_unlock_kernel_context(uv_numa_blade_id());
2758c2ecf20Sopenharmony_ci	preempt_enable();
2768c2ecf20Sopenharmony_ci}
2778c2ecf20Sopenharmony_ci
2788c2ecf20Sopenharmony_ci/*
2798c2ecf20Sopenharmony_ci * Reserve GRU resources to be used asynchronously.
2808c2ecf20Sopenharmony_ci *   Note: currently supports only 1 reservation per blade.
2818c2ecf20Sopenharmony_ci *
2828c2ecf20Sopenharmony_ci * 	input:
2838c2ecf20Sopenharmony_ci * 		blade_id  - blade on which resources should be reserved
2848c2ecf20Sopenharmony_ci * 		cbrs	  - number of CBRs
2858c2ecf20Sopenharmony_ci * 		dsr_bytes - number of DSR bytes needed
2868c2ecf20Sopenharmony_ci *	output:
2878c2ecf20Sopenharmony_ci *		handle to identify resource
2888c2ecf20Sopenharmony_ci *		(0 = async resources already reserved)
2898c2ecf20Sopenharmony_ci */
2908c2ecf20Sopenharmony_ciunsigned long gru_reserve_async_resources(int blade_id, int cbrs, int dsr_bytes,
2918c2ecf20Sopenharmony_ci			struct completion *cmp)
2928c2ecf20Sopenharmony_ci{
2938c2ecf20Sopenharmony_ci	struct gru_blade_state *bs;
2948c2ecf20Sopenharmony_ci	struct gru_thread_state *kgts;
2958c2ecf20Sopenharmony_ci	int ret = 0;
2968c2ecf20Sopenharmony_ci
2978c2ecf20Sopenharmony_ci	bs = gru_base[blade_id];
2988c2ecf20Sopenharmony_ci
2998c2ecf20Sopenharmony_ci	down_write(&bs->bs_kgts_sema);
3008c2ecf20Sopenharmony_ci
3018c2ecf20Sopenharmony_ci	/* Verify no resources already reserved */
3028c2ecf20Sopenharmony_ci	if (bs->bs_async_dsr_bytes + bs->bs_async_cbrs)
3038c2ecf20Sopenharmony_ci		goto done;
3048c2ecf20Sopenharmony_ci	bs->bs_async_dsr_bytes = dsr_bytes;
3058c2ecf20Sopenharmony_ci	bs->bs_async_cbrs = cbrs;
3068c2ecf20Sopenharmony_ci	bs->bs_async_wq = cmp;
3078c2ecf20Sopenharmony_ci	kgts = bs->bs_kgts;
3088c2ecf20Sopenharmony_ci
3098c2ecf20Sopenharmony_ci	/* Resources changed. Unload context if already loaded */
3108c2ecf20Sopenharmony_ci	if (kgts && kgts->ts_gru)
3118c2ecf20Sopenharmony_ci		gru_unload_context(kgts, 0);
3128c2ecf20Sopenharmony_ci	ret = ASYNC_BID_TO_HAN(blade_id);
3138c2ecf20Sopenharmony_ci
3148c2ecf20Sopenharmony_cidone:
3158c2ecf20Sopenharmony_ci	up_write(&bs->bs_kgts_sema);
3168c2ecf20Sopenharmony_ci	return ret;
3178c2ecf20Sopenharmony_ci}
3188c2ecf20Sopenharmony_ci
3198c2ecf20Sopenharmony_ci/*
3208c2ecf20Sopenharmony_ci * Release async resources previously reserved.
3218c2ecf20Sopenharmony_ci *
3228c2ecf20Sopenharmony_ci *	input:
3238c2ecf20Sopenharmony_ci *		han - handle to identify resources
3248c2ecf20Sopenharmony_ci */
3258c2ecf20Sopenharmony_civoid gru_release_async_resources(unsigned long han)
3268c2ecf20Sopenharmony_ci{
3278c2ecf20Sopenharmony_ci	struct gru_blade_state *bs = ASYNC_HAN_TO_BS(han);
3288c2ecf20Sopenharmony_ci
3298c2ecf20Sopenharmony_ci	down_write(&bs->bs_kgts_sema);
3308c2ecf20Sopenharmony_ci	bs->bs_async_dsr_bytes = 0;
3318c2ecf20Sopenharmony_ci	bs->bs_async_cbrs = 0;
3328c2ecf20Sopenharmony_ci	bs->bs_async_wq = NULL;
3338c2ecf20Sopenharmony_ci	up_write(&bs->bs_kgts_sema);
3348c2ecf20Sopenharmony_ci}
3358c2ecf20Sopenharmony_ci
3368c2ecf20Sopenharmony_ci/*
3378c2ecf20Sopenharmony_ci * Wait for async GRU instructions to complete.
3388c2ecf20Sopenharmony_ci *
3398c2ecf20Sopenharmony_ci *	input:
3408c2ecf20Sopenharmony_ci *		han - handle to identify resources
3418c2ecf20Sopenharmony_ci */
3428c2ecf20Sopenharmony_civoid gru_wait_async_cbr(unsigned long han)
3438c2ecf20Sopenharmony_ci{
3448c2ecf20Sopenharmony_ci	struct gru_blade_state *bs = ASYNC_HAN_TO_BS(han);
3458c2ecf20Sopenharmony_ci
3468c2ecf20Sopenharmony_ci	wait_for_completion(bs->bs_async_wq);
3478c2ecf20Sopenharmony_ci	mb();
3488c2ecf20Sopenharmony_ci}
3498c2ecf20Sopenharmony_ci
3508c2ecf20Sopenharmony_ci/*
3518c2ecf20Sopenharmony_ci * Lock previous reserved async GRU resources
3528c2ecf20Sopenharmony_ci *
3538c2ecf20Sopenharmony_ci *	input:
3548c2ecf20Sopenharmony_ci *		han - handle to identify resources
3558c2ecf20Sopenharmony_ci *	output:
3568c2ecf20Sopenharmony_ci *		cb  - pointer to first CBR
3578c2ecf20Sopenharmony_ci *		dsr - pointer to first DSR
3588c2ecf20Sopenharmony_ci */
3598c2ecf20Sopenharmony_civoid gru_lock_async_resource(unsigned long han,  void **cb, void **dsr)
3608c2ecf20Sopenharmony_ci{
3618c2ecf20Sopenharmony_ci	struct gru_blade_state *bs = ASYNC_HAN_TO_BS(han);
3628c2ecf20Sopenharmony_ci	int blade_id = ASYNC_HAN_TO_BID(han);
3638c2ecf20Sopenharmony_ci	int ncpus;
3648c2ecf20Sopenharmony_ci
3658c2ecf20Sopenharmony_ci	gru_lock_kernel_context(blade_id);
3668c2ecf20Sopenharmony_ci	ncpus = uv_blade_nr_possible_cpus(blade_id);
3678c2ecf20Sopenharmony_ci	if (cb)
3688c2ecf20Sopenharmony_ci		*cb = bs->kernel_cb + ncpus * GRU_HANDLE_STRIDE;
3698c2ecf20Sopenharmony_ci	if (dsr)
3708c2ecf20Sopenharmony_ci		*dsr = bs->kernel_dsr + ncpus * GRU_NUM_KERNEL_DSR_BYTES;
3718c2ecf20Sopenharmony_ci}
3728c2ecf20Sopenharmony_ci
3738c2ecf20Sopenharmony_ci/*
3748c2ecf20Sopenharmony_ci * Unlock previous reserved async GRU resources
3758c2ecf20Sopenharmony_ci *
3768c2ecf20Sopenharmony_ci *	input:
3778c2ecf20Sopenharmony_ci *		han - handle to identify resources
3788c2ecf20Sopenharmony_ci */
3798c2ecf20Sopenharmony_civoid gru_unlock_async_resource(unsigned long han)
3808c2ecf20Sopenharmony_ci{
3818c2ecf20Sopenharmony_ci	int blade_id = ASYNC_HAN_TO_BID(han);
3828c2ecf20Sopenharmony_ci
3838c2ecf20Sopenharmony_ci	gru_unlock_kernel_context(blade_id);
3848c2ecf20Sopenharmony_ci}
3858c2ecf20Sopenharmony_ci
3868c2ecf20Sopenharmony_ci/*----------------------------------------------------------------------*/
3878c2ecf20Sopenharmony_ciint gru_get_cb_exception_detail(void *cb,
3888c2ecf20Sopenharmony_ci		struct control_block_extended_exc_detail *excdet)
3898c2ecf20Sopenharmony_ci{
3908c2ecf20Sopenharmony_ci	struct gru_control_block_extended *cbe;
3918c2ecf20Sopenharmony_ci	struct gru_thread_state *kgts = NULL;
3928c2ecf20Sopenharmony_ci	unsigned long off;
3938c2ecf20Sopenharmony_ci	int cbrnum, bid;
3948c2ecf20Sopenharmony_ci
3958c2ecf20Sopenharmony_ci	/*
3968c2ecf20Sopenharmony_ci	 * Locate kgts for cb. This algorithm is SLOW but
3978c2ecf20Sopenharmony_ci	 * this function is rarely called (ie., almost never).
3988c2ecf20Sopenharmony_ci	 * Performance does not matter.
3998c2ecf20Sopenharmony_ci	 */
4008c2ecf20Sopenharmony_ci	for_each_possible_blade(bid) {
4018c2ecf20Sopenharmony_ci		if (!gru_base[bid])
4028c2ecf20Sopenharmony_ci			break;
4038c2ecf20Sopenharmony_ci		kgts = gru_base[bid]->bs_kgts;
4048c2ecf20Sopenharmony_ci		if (!kgts || !kgts->ts_gru)
4058c2ecf20Sopenharmony_ci			continue;
4068c2ecf20Sopenharmony_ci		off = cb - kgts->ts_gru->gs_gru_base_vaddr;
4078c2ecf20Sopenharmony_ci		if (off < GRU_SIZE)
4088c2ecf20Sopenharmony_ci			break;
4098c2ecf20Sopenharmony_ci		kgts = NULL;
4108c2ecf20Sopenharmony_ci	}
4118c2ecf20Sopenharmony_ci	BUG_ON(!kgts);
4128c2ecf20Sopenharmony_ci	cbrnum = thread_cbr_number(kgts, get_cb_number(cb));
4138c2ecf20Sopenharmony_ci	cbe = get_cbe(GRUBASE(cb), cbrnum);
4148c2ecf20Sopenharmony_ci	gru_flush_cache(cbe);	/* CBE not coherent */
4158c2ecf20Sopenharmony_ci	sync_core();
4168c2ecf20Sopenharmony_ci	excdet->opc = cbe->opccpy;
4178c2ecf20Sopenharmony_ci	excdet->exopc = cbe->exopccpy;
4188c2ecf20Sopenharmony_ci	excdet->ecause = cbe->ecause;
4198c2ecf20Sopenharmony_ci	excdet->exceptdet0 = cbe->idef1upd;
4208c2ecf20Sopenharmony_ci	excdet->exceptdet1 = cbe->idef3upd;
4218c2ecf20Sopenharmony_ci	gru_flush_cache(cbe);
4228c2ecf20Sopenharmony_ci	return 0;
4238c2ecf20Sopenharmony_ci}
4248c2ecf20Sopenharmony_ci
4258c2ecf20Sopenharmony_cistatic char *gru_get_cb_exception_detail_str(int ret, void *cb,
4268c2ecf20Sopenharmony_ci					     char *buf, int size)
4278c2ecf20Sopenharmony_ci{
4288c2ecf20Sopenharmony_ci	struct gru_control_block_status *gen = (void *)cb;
4298c2ecf20Sopenharmony_ci	struct control_block_extended_exc_detail excdet;
4308c2ecf20Sopenharmony_ci
4318c2ecf20Sopenharmony_ci	if (ret > 0 && gen->istatus == CBS_EXCEPTION) {
4328c2ecf20Sopenharmony_ci		gru_get_cb_exception_detail(cb, &excdet);
4338c2ecf20Sopenharmony_ci		snprintf(buf, size,
4348c2ecf20Sopenharmony_ci			"GRU:%d exception: cb %p, opc %d, exopc %d, ecause 0x%x,"
4358c2ecf20Sopenharmony_ci			"excdet0 0x%lx, excdet1 0x%x", smp_processor_id(),
4368c2ecf20Sopenharmony_ci			gen, excdet.opc, excdet.exopc, excdet.ecause,
4378c2ecf20Sopenharmony_ci			excdet.exceptdet0, excdet.exceptdet1);
4388c2ecf20Sopenharmony_ci	} else {
4398c2ecf20Sopenharmony_ci		snprintf(buf, size, "No exception");
4408c2ecf20Sopenharmony_ci	}
4418c2ecf20Sopenharmony_ci	return buf;
4428c2ecf20Sopenharmony_ci}
4438c2ecf20Sopenharmony_ci
4448c2ecf20Sopenharmony_cistatic int gru_wait_idle_or_exception(struct gru_control_block_status *gen)
4458c2ecf20Sopenharmony_ci{
4468c2ecf20Sopenharmony_ci	while (gen->istatus >= CBS_ACTIVE) {
4478c2ecf20Sopenharmony_ci		cpu_relax();
4488c2ecf20Sopenharmony_ci		barrier();
4498c2ecf20Sopenharmony_ci	}
4508c2ecf20Sopenharmony_ci	return gen->istatus;
4518c2ecf20Sopenharmony_ci}
4528c2ecf20Sopenharmony_ci
4538c2ecf20Sopenharmony_cistatic int gru_retry_exception(void *cb)
4548c2ecf20Sopenharmony_ci{
4558c2ecf20Sopenharmony_ci	struct gru_control_block_status *gen = (void *)cb;
4568c2ecf20Sopenharmony_ci	struct control_block_extended_exc_detail excdet;
4578c2ecf20Sopenharmony_ci	int retry = EXCEPTION_RETRY_LIMIT;
4588c2ecf20Sopenharmony_ci
4598c2ecf20Sopenharmony_ci	while (1)  {
4608c2ecf20Sopenharmony_ci		if (gru_wait_idle_or_exception(gen) == CBS_IDLE)
4618c2ecf20Sopenharmony_ci			return CBS_IDLE;
4628c2ecf20Sopenharmony_ci		if (gru_get_cb_message_queue_substatus(cb))
4638c2ecf20Sopenharmony_ci			return CBS_EXCEPTION;
4648c2ecf20Sopenharmony_ci		gru_get_cb_exception_detail(cb, &excdet);
4658c2ecf20Sopenharmony_ci		if ((excdet.ecause & ~EXCEPTION_RETRY_BITS) ||
4668c2ecf20Sopenharmony_ci				(excdet.cbrexecstatus & CBR_EXS_ABORT_OCC))
4678c2ecf20Sopenharmony_ci			break;
4688c2ecf20Sopenharmony_ci		if (retry-- == 0)
4698c2ecf20Sopenharmony_ci			break;
4708c2ecf20Sopenharmony_ci		gen->icmd = 1;
4718c2ecf20Sopenharmony_ci		gru_flush_cache(gen);
4728c2ecf20Sopenharmony_ci	}
4738c2ecf20Sopenharmony_ci	return CBS_EXCEPTION;
4748c2ecf20Sopenharmony_ci}
4758c2ecf20Sopenharmony_ci
4768c2ecf20Sopenharmony_ciint gru_check_status_proc(void *cb)
4778c2ecf20Sopenharmony_ci{
4788c2ecf20Sopenharmony_ci	struct gru_control_block_status *gen = (void *)cb;
4798c2ecf20Sopenharmony_ci	int ret;
4808c2ecf20Sopenharmony_ci
4818c2ecf20Sopenharmony_ci	ret = gen->istatus;
4828c2ecf20Sopenharmony_ci	if (ret == CBS_EXCEPTION)
4838c2ecf20Sopenharmony_ci		ret = gru_retry_exception(cb);
4848c2ecf20Sopenharmony_ci	rmb();
4858c2ecf20Sopenharmony_ci	return ret;
4868c2ecf20Sopenharmony_ci
4878c2ecf20Sopenharmony_ci}
4888c2ecf20Sopenharmony_ci
4898c2ecf20Sopenharmony_ciint gru_wait_proc(void *cb)
4908c2ecf20Sopenharmony_ci{
4918c2ecf20Sopenharmony_ci	struct gru_control_block_status *gen = (void *)cb;
4928c2ecf20Sopenharmony_ci	int ret;
4938c2ecf20Sopenharmony_ci
4948c2ecf20Sopenharmony_ci	ret = gru_wait_idle_or_exception(gen);
4958c2ecf20Sopenharmony_ci	if (ret == CBS_EXCEPTION)
4968c2ecf20Sopenharmony_ci		ret = gru_retry_exception(cb);
4978c2ecf20Sopenharmony_ci	rmb();
4988c2ecf20Sopenharmony_ci	return ret;
4998c2ecf20Sopenharmony_ci}
5008c2ecf20Sopenharmony_ci
5018c2ecf20Sopenharmony_cistatic void gru_abort(int ret, void *cb, char *str)
5028c2ecf20Sopenharmony_ci{
5038c2ecf20Sopenharmony_ci	char buf[GRU_EXC_STR_SIZE];
5048c2ecf20Sopenharmony_ci
5058c2ecf20Sopenharmony_ci	panic("GRU FATAL ERROR: %s - %s\n", str,
5068c2ecf20Sopenharmony_ci	      gru_get_cb_exception_detail_str(ret, cb, buf, sizeof(buf)));
5078c2ecf20Sopenharmony_ci}
5088c2ecf20Sopenharmony_ci
5098c2ecf20Sopenharmony_civoid gru_wait_abort_proc(void *cb)
5108c2ecf20Sopenharmony_ci{
5118c2ecf20Sopenharmony_ci	int ret;
5128c2ecf20Sopenharmony_ci
5138c2ecf20Sopenharmony_ci	ret = gru_wait_proc(cb);
5148c2ecf20Sopenharmony_ci	if (ret)
5158c2ecf20Sopenharmony_ci		gru_abort(ret, cb, "gru_wait_abort");
5168c2ecf20Sopenharmony_ci}
5178c2ecf20Sopenharmony_ci
5188c2ecf20Sopenharmony_ci
5198c2ecf20Sopenharmony_ci/*------------------------------ MESSAGE QUEUES -----------------------------*/
5208c2ecf20Sopenharmony_ci
5218c2ecf20Sopenharmony_ci/* Internal status . These are NOT returned to the user. */
5228c2ecf20Sopenharmony_ci#define MQIE_AGAIN		-1	/* try again */
5238c2ecf20Sopenharmony_ci
5248c2ecf20Sopenharmony_ci
5258c2ecf20Sopenharmony_ci/*
5268c2ecf20Sopenharmony_ci * Save/restore the "present" flag that is in the second line of 2-line
5278c2ecf20Sopenharmony_ci * messages
5288c2ecf20Sopenharmony_ci */
5298c2ecf20Sopenharmony_cistatic inline int get_present2(void *p)
5308c2ecf20Sopenharmony_ci{
5318c2ecf20Sopenharmony_ci	struct message_header *mhdr = p + GRU_CACHE_LINE_BYTES;
5328c2ecf20Sopenharmony_ci	return mhdr->present;
5338c2ecf20Sopenharmony_ci}
5348c2ecf20Sopenharmony_ci
5358c2ecf20Sopenharmony_cistatic inline void restore_present2(void *p, int val)
5368c2ecf20Sopenharmony_ci{
5378c2ecf20Sopenharmony_ci	struct message_header *mhdr = p + GRU_CACHE_LINE_BYTES;
5388c2ecf20Sopenharmony_ci	mhdr->present = val;
5398c2ecf20Sopenharmony_ci}
5408c2ecf20Sopenharmony_ci
5418c2ecf20Sopenharmony_ci/*
5428c2ecf20Sopenharmony_ci * Create a message queue.
5438c2ecf20Sopenharmony_ci * 	qlines - message queue size in cache lines. Includes 2-line header.
5448c2ecf20Sopenharmony_ci */
5458c2ecf20Sopenharmony_ciint gru_create_message_queue(struct gru_message_queue_desc *mqd,
5468c2ecf20Sopenharmony_ci		void *p, unsigned int bytes, int nasid, int vector, int apicid)
5478c2ecf20Sopenharmony_ci{
5488c2ecf20Sopenharmony_ci	struct message_queue *mq = p;
5498c2ecf20Sopenharmony_ci	unsigned int qlines;
5508c2ecf20Sopenharmony_ci
5518c2ecf20Sopenharmony_ci	qlines = bytes / GRU_CACHE_LINE_BYTES - 2;
5528c2ecf20Sopenharmony_ci	memset(mq, 0, bytes);
5538c2ecf20Sopenharmony_ci	mq->start = &mq->data;
5548c2ecf20Sopenharmony_ci	mq->start2 = &mq->data + (qlines / 2 - 1) * GRU_CACHE_LINE_BYTES;
5558c2ecf20Sopenharmony_ci	mq->next = &mq->data;
5568c2ecf20Sopenharmony_ci	mq->limit = &mq->data + (qlines - 2) * GRU_CACHE_LINE_BYTES;
5578c2ecf20Sopenharmony_ci	mq->qlines = qlines;
5588c2ecf20Sopenharmony_ci	mq->hstatus[0] = 0;
5598c2ecf20Sopenharmony_ci	mq->hstatus[1] = 1;
5608c2ecf20Sopenharmony_ci	mq->head = gru_mesq_head(2, qlines / 2 + 1);
5618c2ecf20Sopenharmony_ci	mqd->mq = mq;
5628c2ecf20Sopenharmony_ci	mqd->mq_gpa = uv_gpa(mq);
5638c2ecf20Sopenharmony_ci	mqd->qlines = qlines;
5648c2ecf20Sopenharmony_ci	mqd->interrupt_pnode = nasid >> 1;
5658c2ecf20Sopenharmony_ci	mqd->interrupt_vector = vector;
5668c2ecf20Sopenharmony_ci	mqd->interrupt_apicid = apicid;
5678c2ecf20Sopenharmony_ci	return 0;
5688c2ecf20Sopenharmony_ci}
5698c2ecf20Sopenharmony_ciEXPORT_SYMBOL_GPL(gru_create_message_queue);
5708c2ecf20Sopenharmony_ci
5718c2ecf20Sopenharmony_ci/*
5728c2ecf20Sopenharmony_ci * Send a NOOP message to a message queue
5738c2ecf20Sopenharmony_ci * 	Returns:
5748c2ecf20Sopenharmony_ci * 		 0 - if queue is full after the send. This is the normal case
5758c2ecf20Sopenharmony_ci * 		     but various races can change this.
5768c2ecf20Sopenharmony_ci *		-1 - if mesq sent successfully but queue not full
5778c2ecf20Sopenharmony_ci *		>0 - unexpected error. MQE_xxx returned
5788c2ecf20Sopenharmony_ci */
5798c2ecf20Sopenharmony_cistatic int send_noop_message(void *cb, struct gru_message_queue_desc *mqd,
5808c2ecf20Sopenharmony_ci				void *mesg)
5818c2ecf20Sopenharmony_ci{
5828c2ecf20Sopenharmony_ci	const struct message_header noop_header = {
5838c2ecf20Sopenharmony_ci					.present = MQS_NOOP, .lines = 1};
5848c2ecf20Sopenharmony_ci	unsigned long m;
5858c2ecf20Sopenharmony_ci	int substatus, ret;
5868c2ecf20Sopenharmony_ci	struct message_header save_mhdr, *mhdr = mesg;
5878c2ecf20Sopenharmony_ci
5888c2ecf20Sopenharmony_ci	STAT(mesq_noop);
5898c2ecf20Sopenharmony_ci	save_mhdr = *mhdr;
5908c2ecf20Sopenharmony_ci	*mhdr = noop_header;
5918c2ecf20Sopenharmony_ci	gru_mesq(cb, mqd->mq_gpa, gru_get_tri(mhdr), 1, IMA);
5928c2ecf20Sopenharmony_ci	ret = gru_wait(cb);
5938c2ecf20Sopenharmony_ci
5948c2ecf20Sopenharmony_ci	if (ret) {
5958c2ecf20Sopenharmony_ci		substatus = gru_get_cb_message_queue_substatus(cb);
5968c2ecf20Sopenharmony_ci		switch (substatus) {
5978c2ecf20Sopenharmony_ci		case CBSS_NO_ERROR:
5988c2ecf20Sopenharmony_ci			STAT(mesq_noop_unexpected_error);
5998c2ecf20Sopenharmony_ci			ret = MQE_UNEXPECTED_CB_ERR;
6008c2ecf20Sopenharmony_ci			break;
6018c2ecf20Sopenharmony_ci		case CBSS_LB_OVERFLOWED:
6028c2ecf20Sopenharmony_ci			STAT(mesq_noop_lb_overflow);
6038c2ecf20Sopenharmony_ci			ret = MQE_CONGESTION;
6048c2ecf20Sopenharmony_ci			break;
6058c2ecf20Sopenharmony_ci		case CBSS_QLIMIT_REACHED:
6068c2ecf20Sopenharmony_ci			STAT(mesq_noop_qlimit_reached);
6078c2ecf20Sopenharmony_ci			ret = 0;
6088c2ecf20Sopenharmony_ci			break;
6098c2ecf20Sopenharmony_ci		case CBSS_AMO_NACKED:
6108c2ecf20Sopenharmony_ci			STAT(mesq_noop_amo_nacked);
6118c2ecf20Sopenharmony_ci			ret = MQE_CONGESTION;
6128c2ecf20Sopenharmony_ci			break;
6138c2ecf20Sopenharmony_ci		case CBSS_PUT_NACKED:
6148c2ecf20Sopenharmony_ci			STAT(mesq_noop_put_nacked);
6158c2ecf20Sopenharmony_ci			m = mqd->mq_gpa + (gru_get_amo_value_head(cb) << 6);
6168c2ecf20Sopenharmony_ci			gru_vstore(cb, m, gru_get_tri(mesg), XTYPE_CL, 1, 1,
6178c2ecf20Sopenharmony_ci						IMA);
6188c2ecf20Sopenharmony_ci			if (gru_wait(cb) == CBS_IDLE)
6198c2ecf20Sopenharmony_ci				ret = MQIE_AGAIN;
6208c2ecf20Sopenharmony_ci			else
6218c2ecf20Sopenharmony_ci				ret = MQE_UNEXPECTED_CB_ERR;
6228c2ecf20Sopenharmony_ci			break;
6238c2ecf20Sopenharmony_ci		case CBSS_PAGE_OVERFLOW:
6248c2ecf20Sopenharmony_ci			STAT(mesq_noop_page_overflow);
6258c2ecf20Sopenharmony_ci			fallthrough;
6268c2ecf20Sopenharmony_ci		default:
6278c2ecf20Sopenharmony_ci			BUG();
6288c2ecf20Sopenharmony_ci		}
6298c2ecf20Sopenharmony_ci	}
6308c2ecf20Sopenharmony_ci	*mhdr = save_mhdr;
6318c2ecf20Sopenharmony_ci	return ret;
6328c2ecf20Sopenharmony_ci}
6338c2ecf20Sopenharmony_ci
6348c2ecf20Sopenharmony_ci/*
6358c2ecf20Sopenharmony_ci * Handle a gru_mesq full.
6368c2ecf20Sopenharmony_ci */
6378c2ecf20Sopenharmony_cistatic int send_message_queue_full(void *cb, struct gru_message_queue_desc *mqd,
6388c2ecf20Sopenharmony_ci				void *mesg, int lines)
6398c2ecf20Sopenharmony_ci{
6408c2ecf20Sopenharmony_ci	union gru_mesqhead mqh;
6418c2ecf20Sopenharmony_ci	unsigned int limit, head;
6428c2ecf20Sopenharmony_ci	unsigned long avalue;
6438c2ecf20Sopenharmony_ci	int half, qlines;
6448c2ecf20Sopenharmony_ci
6458c2ecf20Sopenharmony_ci	/* Determine if switching to first/second half of q */
6468c2ecf20Sopenharmony_ci	avalue = gru_get_amo_value(cb);
6478c2ecf20Sopenharmony_ci	head = gru_get_amo_value_head(cb);
6488c2ecf20Sopenharmony_ci	limit = gru_get_amo_value_limit(cb);
6498c2ecf20Sopenharmony_ci
6508c2ecf20Sopenharmony_ci	qlines = mqd->qlines;
6518c2ecf20Sopenharmony_ci	half = (limit != qlines);
6528c2ecf20Sopenharmony_ci
6538c2ecf20Sopenharmony_ci	if (half)
6548c2ecf20Sopenharmony_ci		mqh = gru_mesq_head(qlines / 2 + 1, qlines);
6558c2ecf20Sopenharmony_ci	else
6568c2ecf20Sopenharmony_ci		mqh = gru_mesq_head(2, qlines / 2 + 1);
6578c2ecf20Sopenharmony_ci
6588c2ecf20Sopenharmony_ci	/* Try to get lock for switching head pointer */
6598c2ecf20Sopenharmony_ci	gru_gamir(cb, EOP_IR_CLR, HSTATUS(mqd->mq_gpa, half), XTYPE_DW, IMA);
6608c2ecf20Sopenharmony_ci	if (gru_wait(cb) != CBS_IDLE)
6618c2ecf20Sopenharmony_ci		goto cberr;
6628c2ecf20Sopenharmony_ci	if (!gru_get_amo_value(cb)) {
6638c2ecf20Sopenharmony_ci		STAT(mesq_qf_locked);
6648c2ecf20Sopenharmony_ci		return MQE_QUEUE_FULL;
6658c2ecf20Sopenharmony_ci	}
6668c2ecf20Sopenharmony_ci
6678c2ecf20Sopenharmony_ci	/* Got the lock. Send optional NOP if queue not full, */
6688c2ecf20Sopenharmony_ci	if (head != limit) {
6698c2ecf20Sopenharmony_ci		if (send_noop_message(cb, mqd, mesg)) {
6708c2ecf20Sopenharmony_ci			gru_gamir(cb, EOP_IR_INC, HSTATUS(mqd->mq_gpa, half),
6718c2ecf20Sopenharmony_ci					XTYPE_DW, IMA);
6728c2ecf20Sopenharmony_ci			if (gru_wait(cb) != CBS_IDLE)
6738c2ecf20Sopenharmony_ci				goto cberr;
6748c2ecf20Sopenharmony_ci			STAT(mesq_qf_noop_not_full);
6758c2ecf20Sopenharmony_ci			return MQIE_AGAIN;
6768c2ecf20Sopenharmony_ci		}
6778c2ecf20Sopenharmony_ci		avalue++;
6788c2ecf20Sopenharmony_ci	}
6798c2ecf20Sopenharmony_ci
6808c2ecf20Sopenharmony_ci	/* Then flip queuehead to other half of queue. */
6818c2ecf20Sopenharmony_ci	gru_gamer(cb, EOP_ERR_CSWAP, mqd->mq_gpa, XTYPE_DW, mqh.val, avalue,
6828c2ecf20Sopenharmony_ci							IMA);
6838c2ecf20Sopenharmony_ci	if (gru_wait(cb) != CBS_IDLE)
6848c2ecf20Sopenharmony_ci		goto cberr;
6858c2ecf20Sopenharmony_ci
6868c2ecf20Sopenharmony_ci	/* If not successfully in swapping queue head, clear the hstatus lock */
6878c2ecf20Sopenharmony_ci	if (gru_get_amo_value(cb) != avalue) {
6888c2ecf20Sopenharmony_ci		STAT(mesq_qf_switch_head_failed);
6898c2ecf20Sopenharmony_ci		gru_gamir(cb, EOP_IR_INC, HSTATUS(mqd->mq_gpa, half), XTYPE_DW,
6908c2ecf20Sopenharmony_ci							IMA);
6918c2ecf20Sopenharmony_ci		if (gru_wait(cb) != CBS_IDLE)
6928c2ecf20Sopenharmony_ci			goto cberr;
6938c2ecf20Sopenharmony_ci	}
6948c2ecf20Sopenharmony_ci	return MQIE_AGAIN;
6958c2ecf20Sopenharmony_cicberr:
6968c2ecf20Sopenharmony_ci	STAT(mesq_qf_unexpected_error);
6978c2ecf20Sopenharmony_ci	return MQE_UNEXPECTED_CB_ERR;
6988c2ecf20Sopenharmony_ci}
6998c2ecf20Sopenharmony_ci
7008c2ecf20Sopenharmony_ci/*
7018c2ecf20Sopenharmony_ci * Handle a PUT failure. Note: if message was a 2-line message, one of the
7028c2ecf20Sopenharmony_ci * lines might have successfully have been written. Before sending the
7038c2ecf20Sopenharmony_ci * message, "present" must be cleared in BOTH lines to prevent the receiver
7048c2ecf20Sopenharmony_ci * from prematurely seeing the full message.
7058c2ecf20Sopenharmony_ci */
7068c2ecf20Sopenharmony_cistatic int send_message_put_nacked(void *cb, struct gru_message_queue_desc *mqd,
7078c2ecf20Sopenharmony_ci			void *mesg, int lines)
7088c2ecf20Sopenharmony_ci{
7098c2ecf20Sopenharmony_ci	unsigned long m;
7108c2ecf20Sopenharmony_ci	int ret, loops = 200;	/* experimentally determined */
7118c2ecf20Sopenharmony_ci
7128c2ecf20Sopenharmony_ci	m = mqd->mq_gpa + (gru_get_amo_value_head(cb) << 6);
7138c2ecf20Sopenharmony_ci	if (lines == 2) {
7148c2ecf20Sopenharmony_ci		gru_vset(cb, m, 0, XTYPE_CL, lines, 1, IMA);
7158c2ecf20Sopenharmony_ci		if (gru_wait(cb) != CBS_IDLE)
7168c2ecf20Sopenharmony_ci			return MQE_UNEXPECTED_CB_ERR;
7178c2ecf20Sopenharmony_ci	}
7188c2ecf20Sopenharmony_ci	gru_vstore(cb, m, gru_get_tri(mesg), XTYPE_CL, lines, 1, IMA);
7198c2ecf20Sopenharmony_ci	if (gru_wait(cb) != CBS_IDLE)
7208c2ecf20Sopenharmony_ci		return MQE_UNEXPECTED_CB_ERR;
7218c2ecf20Sopenharmony_ci
7228c2ecf20Sopenharmony_ci	if (!mqd->interrupt_vector)
7238c2ecf20Sopenharmony_ci		return MQE_OK;
7248c2ecf20Sopenharmony_ci
7258c2ecf20Sopenharmony_ci	/*
7268c2ecf20Sopenharmony_ci	 * Send a noop message in order to deliver a cross-partition interrupt
7278c2ecf20Sopenharmony_ci	 * to the SSI that contains the target message queue. Normally, the
7288c2ecf20Sopenharmony_ci	 * interrupt is automatically delivered by hardware following mesq
7298c2ecf20Sopenharmony_ci	 * operations, but some error conditions require explicit delivery.
7308c2ecf20Sopenharmony_ci	 * The noop message will trigger delivery. Otherwise partition failures
7318c2ecf20Sopenharmony_ci	 * could cause unrecovered errors.
7328c2ecf20Sopenharmony_ci	 */
7338c2ecf20Sopenharmony_ci	do {
7348c2ecf20Sopenharmony_ci		ret = send_noop_message(cb, mqd, mesg);
7358c2ecf20Sopenharmony_ci	} while ((ret == MQIE_AGAIN || ret == MQE_CONGESTION) && (loops-- > 0));
7368c2ecf20Sopenharmony_ci
7378c2ecf20Sopenharmony_ci	if (ret == MQIE_AGAIN || ret == MQE_CONGESTION) {
7388c2ecf20Sopenharmony_ci		/*
7398c2ecf20Sopenharmony_ci		 * Don't indicate to the app to resend the message, as it's
7408c2ecf20Sopenharmony_ci		 * already been successfully sent.  We simply send an OK
7418c2ecf20Sopenharmony_ci		 * (rather than fail the send with MQE_UNEXPECTED_CB_ERR),
7428c2ecf20Sopenharmony_ci		 * assuming that the other side is receiving enough
7438c2ecf20Sopenharmony_ci		 * interrupts to get this message processed anyway.
7448c2ecf20Sopenharmony_ci		 */
7458c2ecf20Sopenharmony_ci		ret = MQE_OK;
7468c2ecf20Sopenharmony_ci	}
7478c2ecf20Sopenharmony_ci	return ret;
7488c2ecf20Sopenharmony_ci}
7498c2ecf20Sopenharmony_ci
7508c2ecf20Sopenharmony_ci/*
7518c2ecf20Sopenharmony_ci * Handle a gru_mesq failure. Some of these failures are software recoverable
7528c2ecf20Sopenharmony_ci * or retryable.
7538c2ecf20Sopenharmony_ci */
7548c2ecf20Sopenharmony_cistatic int send_message_failure(void *cb, struct gru_message_queue_desc *mqd,
7558c2ecf20Sopenharmony_ci				void *mesg, int lines)
7568c2ecf20Sopenharmony_ci{
7578c2ecf20Sopenharmony_ci	int substatus, ret = 0;
7588c2ecf20Sopenharmony_ci
7598c2ecf20Sopenharmony_ci	substatus = gru_get_cb_message_queue_substatus(cb);
7608c2ecf20Sopenharmony_ci	switch (substatus) {
7618c2ecf20Sopenharmony_ci	case CBSS_NO_ERROR:
7628c2ecf20Sopenharmony_ci		STAT(mesq_send_unexpected_error);
7638c2ecf20Sopenharmony_ci		ret = MQE_UNEXPECTED_CB_ERR;
7648c2ecf20Sopenharmony_ci		break;
7658c2ecf20Sopenharmony_ci	case CBSS_LB_OVERFLOWED:
7668c2ecf20Sopenharmony_ci		STAT(mesq_send_lb_overflow);
7678c2ecf20Sopenharmony_ci		ret = MQE_CONGESTION;
7688c2ecf20Sopenharmony_ci		break;
7698c2ecf20Sopenharmony_ci	case CBSS_QLIMIT_REACHED:
7708c2ecf20Sopenharmony_ci		STAT(mesq_send_qlimit_reached);
7718c2ecf20Sopenharmony_ci		ret = send_message_queue_full(cb, mqd, mesg, lines);
7728c2ecf20Sopenharmony_ci		break;
7738c2ecf20Sopenharmony_ci	case CBSS_AMO_NACKED:
7748c2ecf20Sopenharmony_ci		STAT(mesq_send_amo_nacked);
7758c2ecf20Sopenharmony_ci		ret = MQE_CONGESTION;
7768c2ecf20Sopenharmony_ci		break;
7778c2ecf20Sopenharmony_ci	case CBSS_PUT_NACKED:
7788c2ecf20Sopenharmony_ci		STAT(mesq_send_put_nacked);
7798c2ecf20Sopenharmony_ci		ret = send_message_put_nacked(cb, mqd, mesg, lines);
7808c2ecf20Sopenharmony_ci		break;
7818c2ecf20Sopenharmony_ci	case CBSS_PAGE_OVERFLOW:
7828c2ecf20Sopenharmony_ci		STAT(mesq_page_overflow);
7838c2ecf20Sopenharmony_ci		fallthrough;
7848c2ecf20Sopenharmony_ci	default:
7858c2ecf20Sopenharmony_ci		BUG();
7868c2ecf20Sopenharmony_ci	}
7878c2ecf20Sopenharmony_ci	return ret;
7888c2ecf20Sopenharmony_ci}
7898c2ecf20Sopenharmony_ci
7908c2ecf20Sopenharmony_ci/*
7918c2ecf20Sopenharmony_ci * Send a message to a message queue
7928c2ecf20Sopenharmony_ci * 	mqd	message queue descriptor
7938c2ecf20Sopenharmony_ci * 	mesg	message. ust be vaddr within a GSEG
7948c2ecf20Sopenharmony_ci * 	bytes	message size (<= 2 CL)
7958c2ecf20Sopenharmony_ci */
7968c2ecf20Sopenharmony_ciint gru_send_message_gpa(struct gru_message_queue_desc *mqd, void *mesg,
7978c2ecf20Sopenharmony_ci				unsigned int bytes)
7988c2ecf20Sopenharmony_ci{
7998c2ecf20Sopenharmony_ci	struct message_header *mhdr;
8008c2ecf20Sopenharmony_ci	void *cb;
8018c2ecf20Sopenharmony_ci	void *dsr;
8028c2ecf20Sopenharmony_ci	int istatus, clines, ret;
8038c2ecf20Sopenharmony_ci
8048c2ecf20Sopenharmony_ci	STAT(mesq_send);
8058c2ecf20Sopenharmony_ci	BUG_ON(bytes < sizeof(int) || bytes > 2 * GRU_CACHE_LINE_BYTES);
8068c2ecf20Sopenharmony_ci
8078c2ecf20Sopenharmony_ci	clines = DIV_ROUND_UP(bytes, GRU_CACHE_LINE_BYTES);
8088c2ecf20Sopenharmony_ci	if (gru_get_cpu_resources(bytes, &cb, &dsr))
8098c2ecf20Sopenharmony_ci		return MQE_BUG_NO_RESOURCES;
8108c2ecf20Sopenharmony_ci	memcpy(dsr, mesg, bytes);
8118c2ecf20Sopenharmony_ci	mhdr = dsr;
8128c2ecf20Sopenharmony_ci	mhdr->present = MQS_FULL;
8138c2ecf20Sopenharmony_ci	mhdr->lines = clines;
8148c2ecf20Sopenharmony_ci	if (clines == 2) {
8158c2ecf20Sopenharmony_ci		mhdr->present2 = get_present2(mhdr);
8168c2ecf20Sopenharmony_ci		restore_present2(mhdr, MQS_FULL);
8178c2ecf20Sopenharmony_ci	}
8188c2ecf20Sopenharmony_ci
8198c2ecf20Sopenharmony_ci	do {
8208c2ecf20Sopenharmony_ci		ret = MQE_OK;
8218c2ecf20Sopenharmony_ci		gru_mesq(cb, mqd->mq_gpa, gru_get_tri(mhdr), clines, IMA);
8228c2ecf20Sopenharmony_ci		istatus = gru_wait(cb);
8238c2ecf20Sopenharmony_ci		if (istatus != CBS_IDLE)
8248c2ecf20Sopenharmony_ci			ret = send_message_failure(cb, mqd, dsr, clines);
8258c2ecf20Sopenharmony_ci	} while (ret == MQIE_AGAIN);
8268c2ecf20Sopenharmony_ci	gru_free_cpu_resources(cb, dsr);
8278c2ecf20Sopenharmony_ci
8288c2ecf20Sopenharmony_ci	if (ret)
8298c2ecf20Sopenharmony_ci		STAT(mesq_send_failed);
8308c2ecf20Sopenharmony_ci	return ret;
8318c2ecf20Sopenharmony_ci}
8328c2ecf20Sopenharmony_ciEXPORT_SYMBOL_GPL(gru_send_message_gpa);
8338c2ecf20Sopenharmony_ci
8348c2ecf20Sopenharmony_ci/*
8358c2ecf20Sopenharmony_ci * Advance the receive pointer for the queue to the next message.
8368c2ecf20Sopenharmony_ci */
8378c2ecf20Sopenharmony_civoid gru_free_message(struct gru_message_queue_desc *mqd, void *mesg)
8388c2ecf20Sopenharmony_ci{
8398c2ecf20Sopenharmony_ci	struct message_queue *mq = mqd->mq;
8408c2ecf20Sopenharmony_ci	struct message_header *mhdr = mq->next;
8418c2ecf20Sopenharmony_ci	void *next, *pnext;
8428c2ecf20Sopenharmony_ci	int half = -1;
8438c2ecf20Sopenharmony_ci	int lines = mhdr->lines;
8448c2ecf20Sopenharmony_ci
8458c2ecf20Sopenharmony_ci	if (lines == 2)
8468c2ecf20Sopenharmony_ci		restore_present2(mhdr, MQS_EMPTY);
8478c2ecf20Sopenharmony_ci	mhdr->present = MQS_EMPTY;
8488c2ecf20Sopenharmony_ci
8498c2ecf20Sopenharmony_ci	pnext = mq->next;
8508c2ecf20Sopenharmony_ci	next = pnext + GRU_CACHE_LINE_BYTES * lines;
8518c2ecf20Sopenharmony_ci	if (next == mq->limit) {
8528c2ecf20Sopenharmony_ci		next = mq->start;
8538c2ecf20Sopenharmony_ci		half = 1;
8548c2ecf20Sopenharmony_ci	} else if (pnext < mq->start2 && next >= mq->start2) {
8558c2ecf20Sopenharmony_ci		half = 0;
8568c2ecf20Sopenharmony_ci	}
8578c2ecf20Sopenharmony_ci
8588c2ecf20Sopenharmony_ci	if (half >= 0)
8598c2ecf20Sopenharmony_ci		mq->hstatus[half] = 1;
8608c2ecf20Sopenharmony_ci	mq->next = next;
8618c2ecf20Sopenharmony_ci}
8628c2ecf20Sopenharmony_ciEXPORT_SYMBOL_GPL(gru_free_message);
8638c2ecf20Sopenharmony_ci
8648c2ecf20Sopenharmony_ci/*
8658c2ecf20Sopenharmony_ci * Get next message from message queue. Return NULL if no message
8668c2ecf20Sopenharmony_ci * present. User must call next_message() to move to next message.
8678c2ecf20Sopenharmony_ci * 	rmq	message queue
8688c2ecf20Sopenharmony_ci */
8698c2ecf20Sopenharmony_civoid *gru_get_next_message(struct gru_message_queue_desc *mqd)
8708c2ecf20Sopenharmony_ci{
8718c2ecf20Sopenharmony_ci	struct message_queue *mq = mqd->mq;
8728c2ecf20Sopenharmony_ci	struct message_header *mhdr = mq->next;
8738c2ecf20Sopenharmony_ci	int present = mhdr->present;
8748c2ecf20Sopenharmony_ci
8758c2ecf20Sopenharmony_ci	/* skip NOOP messages */
8768c2ecf20Sopenharmony_ci	while (present == MQS_NOOP) {
8778c2ecf20Sopenharmony_ci		gru_free_message(mqd, mhdr);
8788c2ecf20Sopenharmony_ci		mhdr = mq->next;
8798c2ecf20Sopenharmony_ci		present = mhdr->present;
8808c2ecf20Sopenharmony_ci	}
8818c2ecf20Sopenharmony_ci
8828c2ecf20Sopenharmony_ci	/* Wait for both halves of 2 line messages */
8838c2ecf20Sopenharmony_ci	if (present == MQS_FULL && mhdr->lines == 2 &&
8848c2ecf20Sopenharmony_ci				get_present2(mhdr) == MQS_EMPTY)
8858c2ecf20Sopenharmony_ci		present = MQS_EMPTY;
8868c2ecf20Sopenharmony_ci
8878c2ecf20Sopenharmony_ci	if (!present) {
8888c2ecf20Sopenharmony_ci		STAT(mesq_receive_none);
8898c2ecf20Sopenharmony_ci		return NULL;
8908c2ecf20Sopenharmony_ci	}
8918c2ecf20Sopenharmony_ci
8928c2ecf20Sopenharmony_ci	if (mhdr->lines == 2)
8938c2ecf20Sopenharmony_ci		restore_present2(mhdr, mhdr->present2);
8948c2ecf20Sopenharmony_ci
8958c2ecf20Sopenharmony_ci	STAT(mesq_receive);
8968c2ecf20Sopenharmony_ci	return mhdr;
8978c2ecf20Sopenharmony_ci}
8988c2ecf20Sopenharmony_ciEXPORT_SYMBOL_GPL(gru_get_next_message);
8998c2ecf20Sopenharmony_ci
9008c2ecf20Sopenharmony_ci/* ---------------------- GRU DATA COPY FUNCTIONS ---------------------------*/
9018c2ecf20Sopenharmony_ci
9028c2ecf20Sopenharmony_ci/*
9038c2ecf20Sopenharmony_ci * Load a DW from a global GPA. The GPA can be a memory or MMR address.
9048c2ecf20Sopenharmony_ci */
9058c2ecf20Sopenharmony_ciint gru_read_gpa(unsigned long *value, unsigned long gpa)
9068c2ecf20Sopenharmony_ci{
9078c2ecf20Sopenharmony_ci	void *cb;
9088c2ecf20Sopenharmony_ci	void *dsr;
9098c2ecf20Sopenharmony_ci	int ret, iaa;
9108c2ecf20Sopenharmony_ci
9118c2ecf20Sopenharmony_ci	STAT(read_gpa);
9128c2ecf20Sopenharmony_ci	if (gru_get_cpu_resources(GRU_NUM_KERNEL_DSR_BYTES, &cb, &dsr))
9138c2ecf20Sopenharmony_ci		return MQE_BUG_NO_RESOURCES;
9148c2ecf20Sopenharmony_ci	iaa = gpa >> 62;
9158c2ecf20Sopenharmony_ci	gru_vload_phys(cb, gpa, gru_get_tri(dsr), iaa, IMA);
9168c2ecf20Sopenharmony_ci	ret = gru_wait(cb);
9178c2ecf20Sopenharmony_ci	if (ret == CBS_IDLE)
9188c2ecf20Sopenharmony_ci		*value = *(unsigned long *)dsr;
9198c2ecf20Sopenharmony_ci	gru_free_cpu_resources(cb, dsr);
9208c2ecf20Sopenharmony_ci	return ret;
9218c2ecf20Sopenharmony_ci}
9228c2ecf20Sopenharmony_ciEXPORT_SYMBOL_GPL(gru_read_gpa);
9238c2ecf20Sopenharmony_ci
9248c2ecf20Sopenharmony_ci
9258c2ecf20Sopenharmony_ci/*
9268c2ecf20Sopenharmony_ci * Copy a block of data using the GRU resources
9278c2ecf20Sopenharmony_ci */
9288c2ecf20Sopenharmony_ciint gru_copy_gpa(unsigned long dest_gpa, unsigned long src_gpa,
9298c2ecf20Sopenharmony_ci				unsigned int bytes)
9308c2ecf20Sopenharmony_ci{
9318c2ecf20Sopenharmony_ci	void *cb;
9328c2ecf20Sopenharmony_ci	void *dsr;
9338c2ecf20Sopenharmony_ci	int ret;
9348c2ecf20Sopenharmony_ci
9358c2ecf20Sopenharmony_ci	STAT(copy_gpa);
9368c2ecf20Sopenharmony_ci	if (gru_get_cpu_resources(GRU_NUM_KERNEL_DSR_BYTES, &cb, &dsr))
9378c2ecf20Sopenharmony_ci		return MQE_BUG_NO_RESOURCES;
9388c2ecf20Sopenharmony_ci	gru_bcopy(cb, src_gpa, dest_gpa, gru_get_tri(dsr),
9398c2ecf20Sopenharmony_ci		  XTYPE_B, bytes, GRU_NUM_KERNEL_DSR_CL, IMA);
9408c2ecf20Sopenharmony_ci	ret = gru_wait(cb);
9418c2ecf20Sopenharmony_ci	gru_free_cpu_resources(cb, dsr);
9428c2ecf20Sopenharmony_ci	return ret;
9438c2ecf20Sopenharmony_ci}
9448c2ecf20Sopenharmony_ciEXPORT_SYMBOL_GPL(gru_copy_gpa);
9458c2ecf20Sopenharmony_ci
9468c2ecf20Sopenharmony_ci/* ------------------- KERNEL QUICKTESTS RUN AT STARTUP ----------------*/
9478c2ecf20Sopenharmony_ci/* 	Temp - will delete after we gain confidence in the GRU		*/
9488c2ecf20Sopenharmony_ci
9498c2ecf20Sopenharmony_cistatic int quicktest0(unsigned long arg)
9508c2ecf20Sopenharmony_ci{
9518c2ecf20Sopenharmony_ci	unsigned long word0;
9528c2ecf20Sopenharmony_ci	unsigned long word1;
9538c2ecf20Sopenharmony_ci	void *cb;
9548c2ecf20Sopenharmony_ci	void *dsr;
9558c2ecf20Sopenharmony_ci	unsigned long *p;
9568c2ecf20Sopenharmony_ci	int ret = -EIO;
9578c2ecf20Sopenharmony_ci
9588c2ecf20Sopenharmony_ci	if (gru_get_cpu_resources(GRU_CACHE_LINE_BYTES, &cb, &dsr))
9598c2ecf20Sopenharmony_ci		return MQE_BUG_NO_RESOURCES;
9608c2ecf20Sopenharmony_ci	p = dsr;
9618c2ecf20Sopenharmony_ci	word0 = MAGIC;
9628c2ecf20Sopenharmony_ci	word1 = 0;
9638c2ecf20Sopenharmony_ci
9648c2ecf20Sopenharmony_ci	gru_vload(cb, uv_gpa(&word0), gru_get_tri(dsr), XTYPE_DW, 1, 1, IMA);
9658c2ecf20Sopenharmony_ci	if (gru_wait(cb) != CBS_IDLE) {
9668c2ecf20Sopenharmony_ci		printk(KERN_DEBUG "GRU:%d quicktest0: CBR failure 1\n", smp_processor_id());
9678c2ecf20Sopenharmony_ci		goto done;
9688c2ecf20Sopenharmony_ci	}
9698c2ecf20Sopenharmony_ci
9708c2ecf20Sopenharmony_ci	if (*p != MAGIC) {
9718c2ecf20Sopenharmony_ci		printk(KERN_DEBUG "GRU:%d quicktest0 bad magic 0x%lx\n", smp_processor_id(), *p);
9728c2ecf20Sopenharmony_ci		goto done;
9738c2ecf20Sopenharmony_ci	}
9748c2ecf20Sopenharmony_ci	gru_vstore(cb, uv_gpa(&word1), gru_get_tri(dsr), XTYPE_DW, 1, 1, IMA);
9758c2ecf20Sopenharmony_ci	if (gru_wait(cb) != CBS_IDLE) {
9768c2ecf20Sopenharmony_ci		printk(KERN_DEBUG "GRU:%d quicktest0: CBR failure 2\n", smp_processor_id());
9778c2ecf20Sopenharmony_ci		goto done;
9788c2ecf20Sopenharmony_ci	}
9798c2ecf20Sopenharmony_ci
9808c2ecf20Sopenharmony_ci	if (word0 != word1 || word1 != MAGIC) {
9818c2ecf20Sopenharmony_ci		printk(KERN_DEBUG
9828c2ecf20Sopenharmony_ci		       "GRU:%d quicktest0 err: found 0x%lx, expected 0x%lx\n",
9838c2ecf20Sopenharmony_ci		     smp_processor_id(), word1, MAGIC);
9848c2ecf20Sopenharmony_ci		goto done;
9858c2ecf20Sopenharmony_ci	}
9868c2ecf20Sopenharmony_ci	ret = 0;
9878c2ecf20Sopenharmony_ci
9888c2ecf20Sopenharmony_cidone:
9898c2ecf20Sopenharmony_ci	gru_free_cpu_resources(cb, dsr);
9908c2ecf20Sopenharmony_ci	return ret;
9918c2ecf20Sopenharmony_ci}
9928c2ecf20Sopenharmony_ci
9938c2ecf20Sopenharmony_ci#define ALIGNUP(p, q)	((void *)(((unsigned long)(p) + (q) - 1) & ~(q - 1)))
9948c2ecf20Sopenharmony_ci
9958c2ecf20Sopenharmony_cistatic int quicktest1(unsigned long arg)
9968c2ecf20Sopenharmony_ci{
9978c2ecf20Sopenharmony_ci	struct gru_message_queue_desc mqd;
9988c2ecf20Sopenharmony_ci	void *p, *mq;
9998c2ecf20Sopenharmony_ci	int i, ret = -EIO;
10008c2ecf20Sopenharmony_ci	char mes[GRU_CACHE_LINE_BYTES], *m;
10018c2ecf20Sopenharmony_ci
10028c2ecf20Sopenharmony_ci	/* Need  1K cacheline aligned that does not cross page boundary */
10038c2ecf20Sopenharmony_ci	p = kmalloc(4096, 0);
10048c2ecf20Sopenharmony_ci	if (p == NULL)
10058c2ecf20Sopenharmony_ci		return -ENOMEM;
10068c2ecf20Sopenharmony_ci	mq = ALIGNUP(p, 1024);
10078c2ecf20Sopenharmony_ci	memset(mes, 0xee, sizeof(mes));
10088c2ecf20Sopenharmony_ci
10098c2ecf20Sopenharmony_ci	gru_create_message_queue(&mqd, mq, 8 * GRU_CACHE_LINE_BYTES, 0, 0, 0);
10108c2ecf20Sopenharmony_ci	for (i = 0; i < 6; i++) {
10118c2ecf20Sopenharmony_ci		mes[8] = i;
10128c2ecf20Sopenharmony_ci		do {
10138c2ecf20Sopenharmony_ci			ret = gru_send_message_gpa(&mqd, mes, sizeof(mes));
10148c2ecf20Sopenharmony_ci		} while (ret == MQE_CONGESTION);
10158c2ecf20Sopenharmony_ci		if (ret)
10168c2ecf20Sopenharmony_ci			break;
10178c2ecf20Sopenharmony_ci	}
10188c2ecf20Sopenharmony_ci	if (ret != MQE_QUEUE_FULL || i != 4) {
10198c2ecf20Sopenharmony_ci		printk(KERN_DEBUG "GRU:%d quicktest1: unexpect status %d, i %d\n",
10208c2ecf20Sopenharmony_ci		       smp_processor_id(), ret, i);
10218c2ecf20Sopenharmony_ci		goto done;
10228c2ecf20Sopenharmony_ci	}
10238c2ecf20Sopenharmony_ci
10248c2ecf20Sopenharmony_ci	for (i = 0; i < 6; i++) {
10258c2ecf20Sopenharmony_ci		m = gru_get_next_message(&mqd);
10268c2ecf20Sopenharmony_ci		if (!m || m[8] != i)
10278c2ecf20Sopenharmony_ci			break;
10288c2ecf20Sopenharmony_ci		gru_free_message(&mqd, m);
10298c2ecf20Sopenharmony_ci	}
10308c2ecf20Sopenharmony_ci	if (i != 4) {
10318c2ecf20Sopenharmony_ci		printk(KERN_DEBUG "GRU:%d quicktest2: bad message, i %d, m %p, m8 %d\n",
10328c2ecf20Sopenharmony_ci			smp_processor_id(), i, m, m ? m[8] : -1);
10338c2ecf20Sopenharmony_ci		goto done;
10348c2ecf20Sopenharmony_ci	}
10358c2ecf20Sopenharmony_ci	ret = 0;
10368c2ecf20Sopenharmony_ci
10378c2ecf20Sopenharmony_cidone:
10388c2ecf20Sopenharmony_ci	kfree(p);
10398c2ecf20Sopenharmony_ci	return ret;
10408c2ecf20Sopenharmony_ci}
10418c2ecf20Sopenharmony_ci
10428c2ecf20Sopenharmony_cistatic int quicktest2(unsigned long arg)
10438c2ecf20Sopenharmony_ci{
10448c2ecf20Sopenharmony_ci	static DECLARE_COMPLETION(cmp);
10458c2ecf20Sopenharmony_ci	unsigned long han;
10468c2ecf20Sopenharmony_ci	int blade_id = 0;
10478c2ecf20Sopenharmony_ci	int numcb = 4;
10488c2ecf20Sopenharmony_ci	int ret = 0;
10498c2ecf20Sopenharmony_ci	unsigned long *buf;
10508c2ecf20Sopenharmony_ci	void *cb0, *cb;
10518c2ecf20Sopenharmony_ci	struct gru_control_block_status *gen;
10528c2ecf20Sopenharmony_ci	int i, k, istatus, bytes;
10538c2ecf20Sopenharmony_ci
10548c2ecf20Sopenharmony_ci	bytes = numcb * 4 * 8;
10558c2ecf20Sopenharmony_ci	buf = kmalloc(bytes, GFP_KERNEL);
10568c2ecf20Sopenharmony_ci	if (!buf)
10578c2ecf20Sopenharmony_ci		return -ENOMEM;
10588c2ecf20Sopenharmony_ci
10598c2ecf20Sopenharmony_ci	ret = -EBUSY;
10608c2ecf20Sopenharmony_ci	han = gru_reserve_async_resources(blade_id, numcb, 0, &cmp);
10618c2ecf20Sopenharmony_ci	if (!han)
10628c2ecf20Sopenharmony_ci		goto done;
10638c2ecf20Sopenharmony_ci
10648c2ecf20Sopenharmony_ci	gru_lock_async_resource(han, &cb0, NULL);
10658c2ecf20Sopenharmony_ci	memset(buf, 0xee, bytes);
10668c2ecf20Sopenharmony_ci	for (i = 0; i < numcb; i++)
10678c2ecf20Sopenharmony_ci		gru_vset(cb0 + i * GRU_HANDLE_STRIDE, uv_gpa(&buf[i * 4]), 0,
10688c2ecf20Sopenharmony_ci				XTYPE_DW, 4, 1, IMA_INTERRUPT);
10698c2ecf20Sopenharmony_ci
10708c2ecf20Sopenharmony_ci	ret = 0;
10718c2ecf20Sopenharmony_ci	k = numcb;
10728c2ecf20Sopenharmony_ci	do {
10738c2ecf20Sopenharmony_ci		gru_wait_async_cbr(han);
10748c2ecf20Sopenharmony_ci		for (i = 0; i < numcb; i++) {
10758c2ecf20Sopenharmony_ci			cb = cb0 + i * GRU_HANDLE_STRIDE;
10768c2ecf20Sopenharmony_ci			istatus = gru_check_status(cb);
10778c2ecf20Sopenharmony_ci			if (istatus != CBS_ACTIVE && istatus != CBS_CALL_OS)
10788c2ecf20Sopenharmony_ci				break;
10798c2ecf20Sopenharmony_ci		}
10808c2ecf20Sopenharmony_ci		if (i == numcb)
10818c2ecf20Sopenharmony_ci			continue;
10828c2ecf20Sopenharmony_ci		if (istatus != CBS_IDLE) {
10838c2ecf20Sopenharmony_ci			printk(KERN_DEBUG "GRU:%d quicktest2: cb %d, exception\n", smp_processor_id(), i);
10848c2ecf20Sopenharmony_ci			ret = -EFAULT;
10858c2ecf20Sopenharmony_ci		} else if (buf[4 * i] || buf[4 * i + 1] || buf[4 * i + 2] ||
10868c2ecf20Sopenharmony_ci				buf[4 * i + 3]) {
10878c2ecf20Sopenharmony_ci			printk(KERN_DEBUG "GRU:%d quicktest2:cb %d,  buf 0x%lx, 0x%lx, 0x%lx, 0x%lx\n",
10888c2ecf20Sopenharmony_ci			       smp_processor_id(), i, buf[4 * i], buf[4 * i + 1], buf[4 * i + 2], buf[4 * i + 3]);
10898c2ecf20Sopenharmony_ci			ret = -EIO;
10908c2ecf20Sopenharmony_ci		}
10918c2ecf20Sopenharmony_ci		k--;
10928c2ecf20Sopenharmony_ci		gen = cb;
10938c2ecf20Sopenharmony_ci		gen->istatus = CBS_CALL_OS; /* don't handle this CBR again */
10948c2ecf20Sopenharmony_ci	} while (k);
10958c2ecf20Sopenharmony_ci	BUG_ON(cmp.done);
10968c2ecf20Sopenharmony_ci
10978c2ecf20Sopenharmony_ci	gru_unlock_async_resource(han);
10988c2ecf20Sopenharmony_ci	gru_release_async_resources(han);
10998c2ecf20Sopenharmony_cidone:
11008c2ecf20Sopenharmony_ci	kfree(buf);
11018c2ecf20Sopenharmony_ci	return ret;
11028c2ecf20Sopenharmony_ci}
11038c2ecf20Sopenharmony_ci
11048c2ecf20Sopenharmony_ci#define BUFSIZE 200
11058c2ecf20Sopenharmony_cistatic int quicktest3(unsigned long arg)
11068c2ecf20Sopenharmony_ci{
11078c2ecf20Sopenharmony_ci	char buf1[BUFSIZE], buf2[BUFSIZE];
11088c2ecf20Sopenharmony_ci	int ret = 0;
11098c2ecf20Sopenharmony_ci
11108c2ecf20Sopenharmony_ci	memset(buf2, 0, sizeof(buf2));
11118c2ecf20Sopenharmony_ci	memset(buf1, get_cycles() & 255, sizeof(buf1));
11128c2ecf20Sopenharmony_ci	gru_copy_gpa(uv_gpa(buf2), uv_gpa(buf1), BUFSIZE);
11138c2ecf20Sopenharmony_ci	if (memcmp(buf1, buf2, BUFSIZE)) {
11148c2ecf20Sopenharmony_ci		printk(KERN_DEBUG "GRU:%d quicktest3 error\n", smp_processor_id());
11158c2ecf20Sopenharmony_ci		ret = -EIO;
11168c2ecf20Sopenharmony_ci	}
11178c2ecf20Sopenharmony_ci	return ret;
11188c2ecf20Sopenharmony_ci}
11198c2ecf20Sopenharmony_ci
11208c2ecf20Sopenharmony_ci/*
11218c2ecf20Sopenharmony_ci * Debugging only. User hook for various kernel tests
11228c2ecf20Sopenharmony_ci * of driver & gru.
11238c2ecf20Sopenharmony_ci */
11248c2ecf20Sopenharmony_ciint gru_ktest(unsigned long arg)
11258c2ecf20Sopenharmony_ci{
11268c2ecf20Sopenharmony_ci	int ret = -EINVAL;
11278c2ecf20Sopenharmony_ci
11288c2ecf20Sopenharmony_ci	switch (arg & 0xff) {
11298c2ecf20Sopenharmony_ci	case 0:
11308c2ecf20Sopenharmony_ci		ret = quicktest0(arg);
11318c2ecf20Sopenharmony_ci		break;
11328c2ecf20Sopenharmony_ci	case 1:
11338c2ecf20Sopenharmony_ci		ret = quicktest1(arg);
11348c2ecf20Sopenharmony_ci		break;
11358c2ecf20Sopenharmony_ci	case 2:
11368c2ecf20Sopenharmony_ci		ret = quicktest2(arg);
11378c2ecf20Sopenharmony_ci		break;
11388c2ecf20Sopenharmony_ci	case 3:
11398c2ecf20Sopenharmony_ci		ret = quicktest3(arg);
11408c2ecf20Sopenharmony_ci		break;
11418c2ecf20Sopenharmony_ci	case 99:
11428c2ecf20Sopenharmony_ci		ret = gru_free_kernel_contexts();
11438c2ecf20Sopenharmony_ci		break;
11448c2ecf20Sopenharmony_ci	}
11458c2ecf20Sopenharmony_ci	return ret;
11468c2ecf20Sopenharmony_ci
11478c2ecf20Sopenharmony_ci}
11488c2ecf20Sopenharmony_ci
11498c2ecf20Sopenharmony_ciint gru_kservices_init(void)
11508c2ecf20Sopenharmony_ci{
11518c2ecf20Sopenharmony_ci	return 0;
11528c2ecf20Sopenharmony_ci}
11538c2ecf20Sopenharmony_ci
11548c2ecf20Sopenharmony_civoid gru_kservices_exit(void)
11558c2ecf20Sopenharmony_ci{
11568c2ecf20Sopenharmony_ci	if (gru_free_kernel_contexts())
11578c2ecf20Sopenharmony_ci		BUG();
11588c2ecf20Sopenharmony_ci}
11598c2ecf20Sopenharmony_ci
1160