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