18c2ecf20Sopenharmony_ci/* 28c2ecf20Sopenharmony_ci * This file is provided under a dual BSD/GPLv2 license. When using or 38c2ecf20Sopenharmony_ci * redistributing this file, you may do so under either license. 48c2ecf20Sopenharmony_ci * 58c2ecf20Sopenharmony_ci * GPL LICENSE SUMMARY 68c2ecf20Sopenharmony_ci * 78c2ecf20Sopenharmony_ci * Copyright(c) 2015 Intel Corporation. All rights reserved. 88c2ecf20Sopenharmony_ci * Copyright(c) 2017 T-Platforms. All Rights Reserved. 98c2ecf20Sopenharmony_ci * 108c2ecf20Sopenharmony_ci * This program is free software; you can redistribute it and/or modify 118c2ecf20Sopenharmony_ci * it under the terms of version 2 of the GNU General Public License as 128c2ecf20Sopenharmony_ci * published by the Free Software Foundation. 138c2ecf20Sopenharmony_ci * 148c2ecf20Sopenharmony_ci * BSD LICENSE 158c2ecf20Sopenharmony_ci * 168c2ecf20Sopenharmony_ci * Copyright(c) 2015 Intel Corporation. All rights reserved. 178c2ecf20Sopenharmony_ci * Copyright(c) 2017 T-Platforms. All Rights Reserved. 188c2ecf20Sopenharmony_ci * 198c2ecf20Sopenharmony_ci * Redistribution and use in source and binary forms, with or without 208c2ecf20Sopenharmony_ci * modification, are permitted provided that the following conditions 218c2ecf20Sopenharmony_ci * are met: 228c2ecf20Sopenharmony_ci * 238c2ecf20Sopenharmony_ci * * Redistributions of source code must retain the above copyright 248c2ecf20Sopenharmony_ci * notice, this list of conditions and the following disclaimer. 258c2ecf20Sopenharmony_ci * * Redistributions in binary form must reproduce the above copy 268c2ecf20Sopenharmony_ci * notice, this list of conditions and the following disclaimer in 278c2ecf20Sopenharmony_ci * the documentation and/or other materials provided with the 288c2ecf20Sopenharmony_ci * distribution. 298c2ecf20Sopenharmony_ci * * Neither the name of Intel Corporation nor the names of its 308c2ecf20Sopenharmony_ci * contributors may be used to endorse or promote products derived 318c2ecf20Sopenharmony_ci * from this software without specific prior written permission. 328c2ecf20Sopenharmony_ci * 338c2ecf20Sopenharmony_ci * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS 348c2ecf20Sopenharmony_ci * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT 358c2ecf20Sopenharmony_ci * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR 368c2ecf20Sopenharmony_ci * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT 378c2ecf20Sopenharmony_ci * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, 388c2ecf20Sopenharmony_ci * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT 398c2ecf20Sopenharmony_ci * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, 408c2ecf20Sopenharmony_ci * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY 418c2ecf20Sopenharmony_ci * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 428c2ecf20Sopenharmony_ci * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE 438c2ecf20Sopenharmony_ci * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 448c2ecf20Sopenharmony_ci * 458c2ecf20Sopenharmony_ci * PCIe NTB Perf Linux driver 468c2ecf20Sopenharmony_ci */ 478c2ecf20Sopenharmony_ci 488c2ecf20Sopenharmony_ci/* 498c2ecf20Sopenharmony_ci * How to use this tool, by example. 508c2ecf20Sopenharmony_ci * 518c2ecf20Sopenharmony_ci * Assuming $DBG_DIR is something like: 528c2ecf20Sopenharmony_ci * '/sys/kernel/debug/ntb_perf/0000:00:03.0' 538c2ecf20Sopenharmony_ci * Suppose aside from local device there is at least one remote device 548c2ecf20Sopenharmony_ci * connected to NTB with index 0. 558c2ecf20Sopenharmony_ci *----------------------------------------------------------------------------- 568c2ecf20Sopenharmony_ci * Eg: install driver with specified chunk/total orders and dma-enabled flag 578c2ecf20Sopenharmony_ci * 588c2ecf20Sopenharmony_ci * root@self# insmod ntb_perf.ko chunk_order=19 total_order=28 use_dma 598c2ecf20Sopenharmony_ci *----------------------------------------------------------------------------- 608c2ecf20Sopenharmony_ci * Eg: check NTB ports (index) and MW mapping information 618c2ecf20Sopenharmony_ci * 628c2ecf20Sopenharmony_ci * root@self# cat $DBG_DIR/info 638c2ecf20Sopenharmony_ci *----------------------------------------------------------------------------- 648c2ecf20Sopenharmony_ci * Eg: start performance test with peer (index 0) and get the test metrics 658c2ecf20Sopenharmony_ci * 668c2ecf20Sopenharmony_ci * root@self# echo 0 > $DBG_DIR/run 678c2ecf20Sopenharmony_ci * root@self# cat $DBG_DIR/run 688c2ecf20Sopenharmony_ci */ 698c2ecf20Sopenharmony_ci 708c2ecf20Sopenharmony_ci#include <linux/init.h> 718c2ecf20Sopenharmony_ci#include <linux/kernel.h> 728c2ecf20Sopenharmony_ci#include <linux/module.h> 738c2ecf20Sopenharmony_ci#include <linux/sched.h> 748c2ecf20Sopenharmony_ci#include <linux/wait.h> 758c2ecf20Sopenharmony_ci#include <linux/dma-mapping.h> 768c2ecf20Sopenharmony_ci#include <linux/dmaengine.h> 778c2ecf20Sopenharmony_ci#include <linux/pci.h> 788c2ecf20Sopenharmony_ci#include <linux/ktime.h> 798c2ecf20Sopenharmony_ci#include <linux/slab.h> 808c2ecf20Sopenharmony_ci#include <linux/delay.h> 818c2ecf20Sopenharmony_ci#include <linux/sizes.h> 828c2ecf20Sopenharmony_ci#include <linux/workqueue.h> 838c2ecf20Sopenharmony_ci#include <linux/debugfs.h> 848c2ecf20Sopenharmony_ci#include <linux/random.h> 858c2ecf20Sopenharmony_ci#include <linux/ntb.h> 868c2ecf20Sopenharmony_ci 878c2ecf20Sopenharmony_ci#define DRIVER_NAME "ntb_perf" 888c2ecf20Sopenharmony_ci#define DRIVER_VERSION "2.0" 898c2ecf20Sopenharmony_ci 908c2ecf20Sopenharmony_ciMODULE_LICENSE("Dual BSD/GPL"); 918c2ecf20Sopenharmony_ciMODULE_VERSION(DRIVER_VERSION); 928c2ecf20Sopenharmony_ciMODULE_AUTHOR("Dave Jiang <dave.jiang@intel.com>"); 938c2ecf20Sopenharmony_ciMODULE_DESCRIPTION("PCIe NTB Performance Measurement Tool"); 948c2ecf20Sopenharmony_ci 958c2ecf20Sopenharmony_ci#define MAX_THREADS_CNT 32 968c2ecf20Sopenharmony_ci#define DEF_THREADS_CNT 1 978c2ecf20Sopenharmony_ci#define MAX_CHUNK_SIZE SZ_1M 988c2ecf20Sopenharmony_ci#define MAX_CHUNK_ORDER 20 /* no larger than 1M */ 998c2ecf20Sopenharmony_ci 1008c2ecf20Sopenharmony_ci#define DMA_TRIES 100 1018c2ecf20Sopenharmony_ci#define DMA_MDELAY 10 1028c2ecf20Sopenharmony_ci 1038c2ecf20Sopenharmony_ci#define MSG_TRIES 1000 1048c2ecf20Sopenharmony_ci#define MSG_UDELAY_LOW 1000000 1058c2ecf20Sopenharmony_ci#define MSG_UDELAY_HIGH 2000000 1068c2ecf20Sopenharmony_ci 1078c2ecf20Sopenharmony_ci#define PERF_BUF_LEN 1024 1088c2ecf20Sopenharmony_ci 1098c2ecf20Sopenharmony_cistatic unsigned long max_mw_size; 1108c2ecf20Sopenharmony_cimodule_param(max_mw_size, ulong, 0644); 1118c2ecf20Sopenharmony_ciMODULE_PARM_DESC(max_mw_size, "Upper limit of memory window size"); 1128c2ecf20Sopenharmony_ci 1138c2ecf20Sopenharmony_cistatic unsigned char chunk_order = 19; /* 512K */ 1148c2ecf20Sopenharmony_cimodule_param(chunk_order, byte, 0644); 1158c2ecf20Sopenharmony_ciMODULE_PARM_DESC(chunk_order, "Data chunk order [2^n] to transfer"); 1168c2ecf20Sopenharmony_ci 1178c2ecf20Sopenharmony_cistatic unsigned char total_order = 30; /* 1G */ 1188c2ecf20Sopenharmony_cimodule_param(total_order, byte, 0644); 1198c2ecf20Sopenharmony_ciMODULE_PARM_DESC(total_order, "Total data order [2^n] to transfer"); 1208c2ecf20Sopenharmony_ci 1218c2ecf20Sopenharmony_cistatic bool use_dma; /* default to 0 */ 1228c2ecf20Sopenharmony_cimodule_param(use_dma, bool, 0644); 1238c2ecf20Sopenharmony_ciMODULE_PARM_DESC(use_dma, "Use DMA engine to measure performance"); 1248c2ecf20Sopenharmony_ci 1258c2ecf20Sopenharmony_ci/*============================================================================== 1268c2ecf20Sopenharmony_ci * Perf driver data definition 1278c2ecf20Sopenharmony_ci *============================================================================== 1288c2ecf20Sopenharmony_ci */ 1298c2ecf20Sopenharmony_ci 1308c2ecf20Sopenharmony_cienum perf_cmd { 1318c2ecf20Sopenharmony_ci PERF_CMD_INVAL = -1,/* invalid spad command */ 1328c2ecf20Sopenharmony_ci PERF_CMD_SSIZE = 0, /* send out buffer size */ 1338c2ecf20Sopenharmony_ci PERF_CMD_RSIZE = 1, /* recv in buffer size */ 1348c2ecf20Sopenharmony_ci PERF_CMD_SXLAT = 2, /* send in buffer xlat */ 1358c2ecf20Sopenharmony_ci PERF_CMD_RXLAT = 3, /* recv out buffer xlat */ 1368c2ecf20Sopenharmony_ci PERF_CMD_CLEAR = 4, /* clear allocated memory */ 1378c2ecf20Sopenharmony_ci PERF_STS_DONE = 5, /* init is done */ 1388c2ecf20Sopenharmony_ci PERF_STS_LNKUP = 6, /* link up state flag */ 1398c2ecf20Sopenharmony_ci}; 1408c2ecf20Sopenharmony_ci 1418c2ecf20Sopenharmony_cistruct perf_ctx; 1428c2ecf20Sopenharmony_ci 1438c2ecf20Sopenharmony_cistruct perf_peer { 1448c2ecf20Sopenharmony_ci struct perf_ctx *perf; 1458c2ecf20Sopenharmony_ci int pidx; 1468c2ecf20Sopenharmony_ci int gidx; 1478c2ecf20Sopenharmony_ci 1488c2ecf20Sopenharmony_ci /* Outbound MW params */ 1498c2ecf20Sopenharmony_ci u64 outbuf_xlat; 1508c2ecf20Sopenharmony_ci resource_size_t outbuf_size; 1518c2ecf20Sopenharmony_ci void __iomem *outbuf; 1528c2ecf20Sopenharmony_ci phys_addr_t out_phys_addr; 1538c2ecf20Sopenharmony_ci dma_addr_t dma_dst_addr; 1548c2ecf20Sopenharmony_ci /* Inbound MW params */ 1558c2ecf20Sopenharmony_ci dma_addr_t inbuf_xlat; 1568c2ecf20Sopenharmony_ci resource_size_t inbuf_size; 1578c2ecf20Sopenharmony_ci void *inbuf; 1588c2ecf20Sopenharmony_ci 1598c2ecf20Sopenharmony_ci /* NTB connection setup service */ 1608c2ecf20Sopenharmony_ci struct work_struct service; 1618c2ecf20Sopenharmony_ci unsigned long sts; 1628c2ecf20Sopenharmony_ci 1638c2ecf20Sopenharmony_ci struct completion init_comp; 1648c2ecf20Sopenharmony_ci}; 1658c2ecf20Sopenharmony_ci#define to_peer_service(__work) \ 1668c2ecf20Sopenharmony_ci container_of(__work, struct perf_peer, service) 1678c2ecf20Sopenharmony_ci 1688c2ecf20Sopenharmony_cistruct perf_thread { 1698c2ecf20Sopenharmony_ci struct perf_ctx *perf; 1708c2ecf20Sopenharmony_ci int tidx; 1718c2ecf20Sopenharmony_ci 1728c2ecf20Sopenharmony_ci /* DMA-based test sync parameters */ 1738c2ecf20Sopenharmony_ci atomic_t dma_sync; 1748c2ecf20Sopenharmony_ci wait_queue_head_t dma_wait; 1758c2ecf20Sopenharmony_ci struct dma_chan *dma_chan; 1768c2ecf20Sopenharmony_ci 1778c2ecf20Sopenharmony_ci /* Data source and measured statistics */ 1788c2ecf20Sopenharmony_ci void *src; 1798c2ecf20Sopenharmony_ci u64 copied; 1808c2ecf20Sopenharmony_ci ktime_t duration; 1818c2ecf20Sopenharmony_ci int status; 1828c2ecf20Sopenharmony_ci struct work_struct work; 1838c2ecf20Sopenharmony_ci}; 1848c2ecf20Sopenharmony_ci#define to_thread_work(__work) \ 1858c2ecf20Sopenharmony_ci container_of(__work, struct perf_thread, work) 1868c2ecf20Sopenharmony_ci 1878c2ecf20Sopenharmony_cistruct perf_ctx { 1888c2ecf20Sopenharmony_ci struct ntb_dev *ntb; 1898c2ecf20Sopenharmony_ci 1908c2ecf20Sopenharmony_ci /* Global device index and peers descriptors */ 1918c2ecf20Sopenharmony_ci int gidx; 1928c2ecf20Sopenharmony_ci int pcnt; 1938c2ecf20Sopenharmony_ci struct perf_peer *peers; 1948c2ecf20Sopenharmony_ci 1958c2ecf20Sopenharmony_ci /* Performance measuring work-threads interface */ 1968c2ecf20Sopenharmony_ci unsigned long busy_flag; 1978c2ecf20Sopenharmony_ci wait_queue_head_t twait; 1988c2ecf20Sopenharmony_ci atomic_t tsync; 1998c2ecf20Sopenharmony_ci u8 tcnt; 2008c2ecf20Sopenharmony_ci struct perf_peer *test_peer; 2018c2ecf20Sopenharmony_ci struct perf_thread threads[MAX_THREADS_CNT]; 2028c2ecf20Sopenharmony_ci 2038c2ecf20Sopenharmony_ci /* Scratchpad/Message IO operations */ 2048c2ecf20Sopenharmony_ci int (*cmd_send)(struct perf_peer *peer, enum perf_cmd cmd, u64 data); 2058c2ecf20Sopenharmony_ci int (*cmd_recv)(struct perf_ctx *perf, int *pidx, enum perf_cmd *cmd, 2068c2ecf20Sopenharmony_ci u64 *data); 2078c2ecf20Sopenharmony_ci 2088c2ecf20Sopenharmony_ci struct dentry *dbgfs_dir; 2098c2ecf20Sopenharmony_ci}; 2108c2ecf20Sopenharmony_ci 2118c2ecf20Sopenharmony_ci/* 2128c2ecf20Sopenharmony_ci * Scratchpads-base commands interface 2138c2ecf20Sopenharmony_ci */ 2148c2ecf20Sopenharmony_ci#define PERF_SPAD_CNT(_pcnt) \ 2158c2ecf20Sopenharmony_ci (3*((_pcnt) + 1)) 2168c2ecf20Sopenharmony_ci#define PERF_SPAD_CMD(_gidx) \ 2178c2ecf20Sopenharmony_ci (3*(_gidx)) 2188c2ecf20Sopenharmony_ci#define PERF_SPAD_LDATA(_gidx) \ 2198c2ecf20Sopenharmony_ci (3*(_gidx) + 1) 2208c2ecf20Sopenharmony_ci#define PERF_SPAD_HDATA(_gidx) \ 2218c2ecf20Sopenharmony_ci (3*(_gidx) + 2) 2228c2ecf20Sopenharmony_ci#define PERF_SPAD_NOTIFY(_gidx) \ 2238c2ecf20Sopenharmony_ci (BIT_ULL(_gidx)) 2248c2ecf20Sopenharmony_ci 2258c2ecf20Sopenharmony_ci/* 2268c2ecf20Sopenharmony_ci * Messages-base commands interface 2278c2ecf20Sopenharmony_ci */ 2288c2ecf20Sopenharmony_ci#define PERF_MSG_CNT 3 2298c2ecf20Sopenharmony_ci#define PERF_MSG_CMD 0 2308c2ecf20Sopenharmony_ci#define PERF_MSG_LDATA 1 2318c2ecf20Sopenharmony_ci#define PERF_MSG_HDATA 2 2328c2ecf20Sopenharmony_ci 2338c2ecf20Sopenharmony_ci/*============================================================================== 2348c2ecf20Sopenharmony_ci * Static data declarations 2358c2ecf20Sopenharmony_ci *============================================================================== 2368c2ecf20Sopenharmony_ci */ 2378c2ecf20Sopenharmony_ci 2388c2ecf20Sopenharmony_cistatic struct dentry *perf_dbgfs_topdir; 2398c2ecf20Sopenharmony_ci 2408c2ecf20Sopenharmony_cistatic struct workqueue_struct *perf_wq __read_mostly; 2418c2ecf20Sopenharmony_ci 2428c2ecf20Sopenharmony_ci/*============================================================================== 2438c2ecf20Sopenharmony_ci * NTB cross-link commands execution service 2448c2ecf20Sopenharmony_ci *============================================================================== 2458c2ecf20Sopenharmony_ci */ 2468c2ecf20Sopenharmony_ci 2478c2ecf20Sopenharmony_cistatic void perf_terminate_test(struct perf_ctx *perf); 2488c2ecf20Sopenharmony_ci 2498c2ecf20Sopenharmony_cistatic inline bool perf_link_is_up(struct perf_peer *peer) 2508c2ecf20Sopenharmony_ci{ 2518c2ecf20Sopenharmony_ci u64 link; 2528c2ecf20Sopenharmony_ci 2538c2ecf20Sopenharmony_ci link = ntb_link_is_up(peer->perf->ntb, NULL, NULL); 2548c2ecf20Sopenharmony_ci return !!(link & BIT_ULL_MASK(peer->pidx)); 2558c2ecf20Sopenharmony_ci} 2568c2ecf20Sopenharmony_ci 2578c2ecf20Sopenharmony_cistatic int perf_spad_cmd_send(struct perf_peer *peer, enum perf_cmd cmd, 2588c2ecf20Sopenharmony_ci u64 data) 2598c2ecf20Sopenharmony_ci{ 2608c2ecf20Sopenharmony_ci struct perf_ctx *perf = peer->perf; 2618c2ecf20Sopenharmony_ci int try; 2628c2ecf20Sopenharmony_ci u32 sts; 2638c2ecf20Sopenharmony_ci 2648c2ecf20Sopenharmony_ci dev_dbg(&perf->ntb->dev, "CMD send: %d 0x%llx\n", cmd, data); 2658c2ecf20Sopenharmony_ci 2668c2ecf20Sopenharmony_ci /* 2678c2ecf20Sopenharmony_ci * Perform predefined number of attempts before give up. 2688c2ecf20Sopenharmony_ci * We are sending the data to the port specific scratchpad, so 2698c2ecf20Sopenharmony_ci * to prevent a multi-port access race-condition. Additionally 2708c2ecf20Sopenharmony_ci * there is no need in local locking since only thread-safe 2718c2ecf20Sopenharmony_ci * service work is using this method. 2728c2ecf20Sopenharmony_ci */ 2738c2ecf20Sopenharmony_ci for (try = 0; try < MSG_TRIES; try++) { 2748c2ecf20Sopenharmony_ci if (!perf_link_is_up(peer)) 2758c2ecf20Sopenharmony_ci return -ENOLINK; 2768c2ecf20Sopenharmony_ci 2778c2ecf20Sopenharmony_ci sts = ntb_peer_spad_read(perf->ntb, peer->pidx, 2788c2ecf20Sopenharmony_ci PERF_SPAD_CMD(perf->gidx)); 2798c2ecf20Sopenharmony_ci if (sts != PERF_CMD_INVAL) { 2808c2ecf20Sopenharmony_ci usleep_range(MSG_UDELAY_LOW, MSG_UDELAY_HIGH); 2818c2ecf20Sopenharmony_ci continue; 2828c2ecf20Sopenharmony_ci } 2838c2ecf20Sopenharmony_ci 2848c2ecf20Sopenharmony_ci ntb_peer_spad_write(perf->ntb, peer->pidx, 2858c2ecf20Sopenharmony_ci PERF_SPAD_LDATA(perf->gidx), 2868c2ecf20Sopenharmony_ci lower_32_bits(data)); 2878c2ecf20Sopenharmony_ci ntb_peer_spad_write(perf->ntb, peer->pidx, 2888c2ecf20Sopenharmony_ci PERF_SPAD_HDATA(perf->gidx), 2898c2ecf20Sopenharmony_ci upper_32_bits(data)); 2908c2ecf20Sopenharmony_ci ntb_peer_spad_write(perf->ntb, peer->pidx, 2918c2ecf20Sopenharmony_ci PERF_SPAD_CMD(perf->gidx), 2928c2ecf20Sopenharmony_ci cmd); 2938c2ecf20Sopenharmony_ci ntb_peer_db_set(perf->ntb, PERF_SPAD_NOTIFY(peer->gidx)); 2948c2ecf20Sopenharmony_ci 2958c2ecf20Sopenharmony_ci dev_dbg(&perf->ntb->dev, "DB ring peer %#llx\n", 2968c2ecf20Sopenharmony_ci PERF_SPAD_NOTIFY(peer->gidx)); 2978c2ecf20Sopenharmony_ci 2988c2ecf20Sopenharmony_ci break; 2998c2ecf20Sopenharmony_ci } 3008c2ecf20Sopenharmony_ci 3018c2ecf20Sopenharmony_ci return try < MSG_TRIES ? 0 : -EAGAIN; 3028c2ecf20Sopenharmony_ci} 3038c2ecf20Sopenharmony_ci 3048c2ecf20Sopenharmony_cistatic int perf_spad_cmd_recv(struct perf_ctx *perf, int *pidx, 3058c2ecf20Sopenharmony_ci enum perf_cmd *cmd, u64 *data) 3068c2ecf20Sopenharmony_ci{ 3078c2ecf20Sopenharmony_ci struct perf_peer *peer; 3088c2ecf20Sopenharmony_ci u32 val; 3098c2ecf20Sopenharmony_ci 3108c2ecf20Sopenharmony_ci ntb_db_clear(perf->ntb, PERF_SPAD_NOTIFY(perf->gidx)); 3118c2ecf20Sopenharmony_ci 3128c2ecf20Sopenharmony_ci /* 3138c2ecf20Sopenharmony_ci * We start scanning all over, since cleared DB may have been set 3148c2ecf20Sopenharmony_ci * by any peer. Yes, it makes peer with smaller index being 3158c2ecf20Sopenharmony_ci * serviced with greater priority, but it's convenient for spad 3168c2ecf20Sopenharmony_ci * and message code unification and simplicity. 3178c2ecf20Sopenharmony_ci */ 3188c2ecf20Sopenharmony_ci for (*pidx = 0; *pidx < perf->pcnt; (*pidx)++) { 3198c2ecf20Sopenharmony_ci peer = &perf->peers[*pidx]; 3208c2ecf20Sopenharmony_ci 3218c2ecf20Sopenharmony_ci if (!perf_link_is_up(peer)) 3228c2ecf20Sopenharmony_ci continue; 3238c2ecf20Sopenharmony_ci 3248c2ecf20Sopenharmony_ci val = ntb_spad_read(perf->ntb, PERF_SPAD_CMD(peer->gidx)); 3258c2ecf20Sopenharmony_ci if (val == PERF_CMD_INVAL) 3268c2ecf20Sopenharmony_ci continue; 3278c2ecf20Sopenharmony_ci 3288c2ecf20Sopenharmony_ci *cmd = val; 3298c2ecf20Sopenharmony_ci 3308c2ecf20Sopenharmony_ci val = ntb_spad_read(perf->ntb, PERF_SPAD_LDATA(peer->gidx)); 3318c2ecf20Sopenharmony_ci *data = val; 3328c2ecf20Sopenharmony_ci 3338c2ecf20Sopenharmony_ci val = ntb_spad_read(perf->ntb, PERF_SPAD_HDATA(peer->gidx)); 3348c2ecf20Sopenharmony_ci *data |= (u64)val << 32; 3358c2ecf20Sopenharmony_ci 3368c2ecf20Sopenharmony_ci /* Next command can be retrieved from now */ 3378c2ecf20Sopenharmony_ci ntb_spad_write(perf->ntb, PERF_SPAD_CMD(peer->gidx), 3388c2ecf20Sopenharmony_ci PERF_CMD_INVAL); 3398c2ecf20Sopenharmony_ci 3408c2ecf20Sopenharmony_ci dev_dbg(&perf->ntb->dev, "CMD recv: %d 0x%llx\n", *cmd, *data); 3418c2ecf20Sopenharmony_ci 3428c2ecf20Sopenharmony_ci return 0; 3438c2ecf20Sopenharmony_ci } 3448c2ecf20Sopenharmony_ci 3458c2ecf20Sopenharmony_ci return -ENODATA; 3468c2ecf20Sopenharmony_ci} 3478c2ecf20Sopenharmony_ci 3488c2ecf20Sopenharmony_cistatic int perf_msg_cmd_send(struct perf_peer *peer, enum perf_cmd cmd, 3498c2ecf20Sopenharmony_ci u64 data) 3508c2ecf20Sopenharmony_ci{ 3518c2ecf20Sopenharmony_ci struct perf_ctx *perf = peer->perf; 3528c2ecf20Sopenharmony_ci int try, ret; 3538c2ecf20Sopenharmony_ci u64 outbits; 3548c2ecf20Sopenharmony_ci 3558c2ecf20Sopenharmony_ci dev_dbg(&perf->ntb->dev, "CMD send: %d 0x%llx\n", cmd, data); 3568c2ecf20Sopenharmony_ci 3578c2ecf20Sopenharmony_ci /* 3588c2ecf20Sopenharmony_ci * Perform predefined number of attempts before give up. Message 3598c2ecf20Sopenharmony_ci * registers are free of race-condition problem when accessed 3608c2ecf20Sopenharmony_ci * from different ports, so we don't need splitting registers 3618c2ecf20Sopenharmony_ci * by global device index. We also won't have local locking, 3628c2ecf20Sopenharmony_ci * since the method is used from service work only. 3638c2ecf20Sopenharmony_ci */ 3648c2ecf20Sopenharmony_ci outbits = ntb_msg_outbits(perf->ntb); 3658c2ecf20Sopenharmony_ci for (try = 0; try < MSG_TRIES; try++) { 3668c2ecf20Sopenharmony_ci if (!perf_link_is_up(peer)) 3678c2ecf20Sopenharmony_ci return -ENOLINK; 3688c2ecf20Sopenharmony_ci 3698c2ecf20Sopenharmony_ci ret = ntb_msg_clear_sts(perf->ntb, outbits); 3708c2ecf20Sopenharmony_ci if (ret) 3718c2ecf20Sopenharmony_ci return ret; 3728c2ecf20Sopenharmony_ci 3738c2ecf20Sopenharmony_ci ntb_peer_msg_write(perf->ntb, peer->pidx, PERF_MSG_LDATA, 3748c2ecf20Sopenharmony_ci lower_32_bits(data)); 3758c2ecf20Sopenharmony_ci 3768c2ecf20Sopenharmony_ci if (ntb_msg_read_sts(perf->ntb) & outbits) { 3778c2ecf20Sopenharmony_ci usleep_range(MSG_UDELAY_LOW, MSG_UDELAY_HIGH); 3788c2ecf20Sopenharmony_ci continue; 3798c2ecf20Sopenharmony_ci } 3808c2ecf20Sopenharmony_ci 3818c2ecf20Sopenharmony_ci ntb_peer_msg_write(perf->ntb, peer->pidx, PERF_MSG_HDATA, 3828c2ecf20Sopenharmony_ci upper_32_bits(data)); 3838c2ecf20Sopenharmony_ci 3848c2ecf20Sopenharmony_ci /* This call shall trigger peer message event */ 3858c2ecf20Sopenharmony_ci ntb_peer_msg_write(perf->ntb, peer->pidx, PERF_MSG_CMD, cmd); 3868c2ecf20Sopenharmony_ci 3878c2ecf20Sopenharmony_ci break; 3888c2ecf20Sopenharmony_ci } 3898c2ecf20Sopenharmony_ci 3908c2ecf20Sopenharmony_ci return try < MSG_TRIES ? 0 : -EAGAIN; 3918c2ecf20Sopenharmony_ci} 3928c2ecf20Sopenharmony_ci 3938c2ecf20Sopenharmony_cistatic int perf_msg_cmd_recv(struct perf_ctx *perf, int *pidx, 3948c2ecf20Sopenharmony_ci enum perf_cmd *cmd, u64 *data) 3958c2ecf20Sopenharmony_ci{ 3968c2ecf20Sopenharmony_ci u64 inbits; 3978c2ecf20Sopenharmony_ci u32 val; 3988c2ecf20Sopenharmony_ci 3998c2ecf20Sopenharmony_ci inbits = ntb_msg_inbits(perf->ntb); 4008c2ecf20Sopenharmony_ci 4018c2ecf20Sopenharmony_ci if (hweight64(ntb_msg_read_sts(perf->ntb) & inbits) < 3) 4028c2ecf20Sopenharmony_ci return -ENODATA; 4038c2ecf20Sopenharmony_ci 4048c2ecf20Sopenharmony_ci val = ntb_msg_read(perf->ntb, pidx, PERF_MSG_CMD); 4058c2ecf20Sopenharmony_ci *cmd = val; 4068c2ecf20Sopenharmony_ci 4078c2ecf20Sopenharmony_ci val = ntb_msg_read(perf->ntb, pidx, PERF_MSG_LDATA); 4088c2ecf20Sopenharmony_ci *data = val; 4098c2ecf20Sopenharmony_ci 4108c2ecf20Sopenharmony_ci val = ntb_msg_read(perf->ntb, pidx, PERF_MSG_HDATA); 4118c2ecf20Sopenharmony_ci *data |= (u64)val << 32; 4128c2ecf20Sopenharmony_ci 4138c2ecf20Sopenharmony_ci /* Next command can be retrieved from now */ 4148c2ecf20Sopenharmony_ci ntb_msg_clear_sts(perf->ntb, inbits); 4158c2ecf20Sopenharmony_ci 4168c2ecf20Sopenharmony_ci dev_dbg(&perf->ntb->dev, "CMD recv: %d 0x%llx\n", *cmd, *data); 4178c2ecf20Sopenharmony_ci 4188c2ecf20Sopenharmony_ci return 0; 4198c2ecf20Sopenharmony_ci} 4208c2ecf20Sopenharmony_ci 4218c2ecf20Sopenharmony_cistatic int perf_cmd_send(struct perf_peer *peer, enum perf_cmd cmd, u64 data) 4228c2ecf20Sopenharmony_ci{ 4238c2ecf20Sopenharmony_ci struct perf_ctx *perf = peer->perf; 4248c2ecf20Sopenharmony_ci 4258c2ecf20Sopenharmony_ci if (cmd == PERF_CMD_SSIZE || cmd == PERF_CMD_SXLAT) 4268c2ecf20Sopenharmony_ci return perf->cmd_send(peer, cmd, data); 4278c2ecf20Sopenharmony_ci 4288c2ecf20Sopenharmony_ci dev_err(&perf->ntb->dev, "Send invalid command\n"); 4298c2ecf20Sopenharmony_ci return -EINVAL; 4308c2ecf20Sopenharmony_ci} 4318c2ecf20Sopenharmony_ci 4328c2ecf20Sopenharmony_cistatic int perf_cmd_exec(struct perf_peer *peer, enum perf_cmd cmd) 4338c2ecf20Sopenharmony_ci{ 4348c2ecf20Sopenharmony_ci switch (cmd) { 4358c2ecf20Sopenharmony_ci case PERF_CMD_SSIZE: 4368c2ecf20Sopenharmony_ci case PERF_CMD_RSIZE: 4378c2ecf20Sopenharmony_ci case PERF_CMD_SXLAT: 4388c2ecf20Sopenharmony_ci case PERF_CMD_RXLAT: 4398c2ecf20Sopenharmony_ci case PERF_CMD_CLEAR: 4408c2ecf20Sopenharmony_ci break; 4418c2ecf20Sopenharmony_ci default: 4428c2ecf20Sopenharmony_ci dev_err(&peer->perf->ntb->dev, "Exec invalid command\n"); 4438c2ecf20Sopenharmony_ci return -EINVAL; 4448c2ecf20Sopenharmony_ci } 4458c2ecf20Sopenharmony_ci 4468c2ecf20Sopenharmony_ci /* No need of memory barrier, since bit ops have invernal lock */ 4478c2ecf20Sopenharmony_ci set_bit(cmd, &peer->sts); 4488c2ecf20Sopenharmony_ci 4498c2ecf20Sopenharmony_ci dev_dbg(&peer->perf->ntb->dev, "CMD exec: %d\n", cmd); 4508c2ecf20Sopenharmony_ci 4518c2ecf20Sopenharmony_ci (void)queue_work(system_highpri_wq, &peer->service); 4528c2ecf20Sopenharmony_ci 4538c2ecf20Sopenharmony_ci return 0; 4548c2ecf20Sopenharmony_ci} 4558c2ecf20Sopenharmony_ci 4568c2ecf20Sopenharmony_cistatic int perf_cmd_recv(struct perf_ctx *perf) 4578c2ecf20Sopenharmony_ci{ 4588c2ecf20Sopenharmony_ci struct perf_peer *peer; 4598c2ecf20Sopenharmony_ci int ret, pidx, cmd; 4608c2ecf20Sopenharmony_ci u64 data; 4618c2ecf20Sopenharmony_ci 4628c2ecf20Sopenharmony_ci while (!(ret = perf->cmd_recv(perf, &pidx, &cmd, &data))) { 4638c2ecf20Sopenharmony_ci peer = &perf->peers[pidx]; 4648c2ecf20Sopenharmony_ci 4658c2ecf20Sopenharmony_ci switch (cmd) { 4668c2ecf20Sopenharmony_ci case PERF_CMD_SSIZE: 4678c2ecf20Sopenharmony_ci peer->inbuf_size = data; 4688c2ecf20Sopenharmony_ci return perf_cmd_exec(peer, PERF_CMD_RSIZE); 4698c2ecf20Sopenharmony_ci case PERF_CMD_SXLAT: 4708c2ecf20Sopenharmony_ci peer->outbuf_xlat = data; 4718c2ecf20Sopenharmony_ci return perf_cmd_exec(peer, PERF_CMD_RXLAT); 4728c2ecf20Sopenharmony_ci default: 4738c2ecf20Sopenharmony_ci dev_err(&perf->ntb->dev, "Recv invalid command\n"); 4748c2ecf20Sopenharmony_ci return -EINVAL; 4758c2ecf20Sopenharmony_ci } 4768c2ecf20Sopenharmony_ci } 4778c2ecf20Sopenharmony_ci 4788c2ecf20Sopenharmony_ci /* Return 0 if no data left to process, otherwise an error */ 4798c2ecf20Sopenharmony_ci return ret == -ENODATA ? 0 : ret; 4808c2ecf20Sopenharmony_ci} 4818c2ecf20Sopenharmony_ci 4828c2ecf20Sopenharmony_cistatic void perf_link_event(void *ctx) 4838c2ecf20Sopenharmony_ci{ 4848c2ecf20Sopenharmony_ci struct perf_ctx *perf = ctx; 4858c2ecf20Sopenharmony_ci struct perf_peer *peer; 4868c2ecf20Sopenharmony_ci bool lnk_up; 4878c2ecf20Sopenharmony_ci int pidx; 4888c2ecf20Sopenharmony_ci 4898c2ecf20Sopenharmony_ci for (pidx = 0; pidx < perf->pcnt; pidx++) { 4908c2ecf20Sopenharmony_ci peer = &perf->peers[pidx]; 4918c2ecf20Sopenharmony_ci 4928c2ecf20Sopenharmony_ci lnk_up = perf_link_is_up(peer); 4938c2ecf20Sopenharmony_ci 4948c2ecf20Sopenharmony_ci if (lnk_up && 4958c2ecf20Sopenharmony_ci !test_and_set_bit(PERF_STS_LNKUP, &peer->sts)) { 4968c2ecf20Sopenharmony_ci perf_cmd_exec(peer, PERF_CMD_SSIZE); 4978c2ecf20Sopenharmony_ci } else if (!lnk_up && 4988c2ecf20Sopenharmony_ci test_and_clear_bit(PERF_STS_LNKUP, &peer->sts)) { 4998c2ecf20Sopenharmony_ci perf_cmd_exec(peer, PERF_CMD_CLEAR); 5008c2ecf20Sopenharmony_ci } 5018c2ecf20Sopenharmony_ci } 5028c2ecf20Sopenharmony_ci} 5038c2ecf20Sopenharmony_ci 5048c2ecf20Sopenharmony_cistatic void perf_db_event(void *ctx, int vec) 5058c2ecf20Sopenharmony_ci{ 5068c2ecf20Sopenharmony_ci struct perf_ctx *perf = ctx; 5078c2ecf20Sopenharmony_ci 5088c2ecf20Sopenharmony_ci dev_dbg(&perf->ntb->dev, "DB vec %d mask %#llx bits %#llx\n", vec, 5098c2ecf20Sopenharmony_ci ntb_db_vector_mask(perf->ntb, vec), ntb_db_read(perf->ntb)); 5108c2ecf20Sopenharmony_ci 5118c2ecf20Sopenharmony_ci /* Just receive all available commands */ 5128c2ecf20Sopenharmony_ci (void)perf_cmd_recv(perf); 5138c2ecf20Sopenharmony_ci} 5148c2ecf20Sopenharmony_ci 5158c2ecf20Sopenharmony_cistatic void perf_msg_event(void *ctx) 5168c2ecf20Sopenharmony_ci{ 5178c2ecf20Sopenharmony_ci struct perf_ctx *perf = ctx; 5188c2ecf20Sopenharmony_ci 5198c2ecf20Sopenharmony_ci dev_dbg(&perf->ntb->dev, "Msg status bits %#llx\n", 5208c2ecf20Sopenharmony_ci ntb_msg_read_sts(perf->ntb)); 5218c2ecf20Sopenharmony_ci 5228c2ecf20Sopenharmony_ci /* Messages are only sent one-by-one */ 5238c2ecf20Sopenharmony_ci (void)perf_cmd_recv(perf); 5248c2ecf20Sopenharmony_ci} 5258c2ecf20Sopenharmony_ci 5268c2ecf20Sopenharmony_cistatic const struct ntb_ctx_ops perf_ops = { 5278c2ecf20Sopenharmony_ci .link_event = perf_link_event, 5288c2ecf20Sopenharmony_ci .db_event = perf_db_event, 5298c2ecf20Sopenharmony_ci .msg_event = perf_msg_event 5308c2ecf20Sopenharmony_ci}; 5318c2ecf20Sopenharmony_ci 5328c2ecf20Sopenharmony_cistatic void perf_free_outbuf(struct perf_peer *peer) 5338c2ecf20Sopenharmony_ci{ 5348c2ecf20Sopenharmony_ci (void)ntb_peer_mw_clear_trans(peer->perf->ntb, peer->pidx, peer->gidx); 5358c2ecf20Sopenharmony_ci} 5368c2ecf20Sopenharmony_ci 5378c2ecf20Sopenharmony_cistatic int perf_setup_outbuf(struct perf_peer *peer) 5388c2ecf20Sopenharmony_ci{ 5398c2ecf20Sopenharmony_ci struct perf_ctx *perf = peer->perf; 5408c2ecf20Sopenharmony_ci int ret; 5418c2ecf20Sopenharmony_ci 5428c2ecf20Sopenharmony_ci /* Outbuf size can be unaligned due to custom max_mw_size */ 5438c2ecf20Sopenharmony_ci ret = ntb_peer_mw_set_trans(perf->ntb, peer->pidx, peer->gidx, 5448c2ecf20Sopenharmony_ci peer->outbuf_xlat, peer->outbuf_size); 5458c2ecf20Sopenharmony_ci if (ret) { 5468c2ecf20Sopenharmony_ci dev_err(&perf->ntb->dev, "Failed to set outbuf translation\n"); 5478c2ecf20Sopenharmony_ci return ret; 5488c2ecf20Sopenharmony_ci } 5498c2ecf20Sopenharmony_ci 5508c2ecf20Sopenharmony_ci /* Initialization is finally done */ 5518c2ecf20Sopenharmony_ci set_bit(PERF_STS_DONE, &peer->sts); 5528c2ecf20Sopenharmony_ci complete_all(&peer->init_comp); 5538c2ecf20Sopenharmony_ci 5548c2ecf20Sopenharmony_ci return 0; 5558c2ecf20Sopenharmony_ci} 5568c2ecf20Sopenharmony_ci 5578c2ecf20Sopenharmony_cistatic void perf_free_inbuf(struct perf_peer *peer) 5588c2ecf20Sopenharmony_ci{ 5598c2ecf20Sopenharmony_ci if (!peer->inbuf) 5608c2ecf20Sopenharmony_ci return; 5618c2ecf20Sopenharmony_ci 5628c2ecf20Sopenharmony_ci (void)ntb_mw_clear_trans(peer->perf->ntb, peer->pidx, peer->gidx); 5638c2ecf20Sopenharmony_ci dma_free_coherent(&peer->perf->ntb->pdev->dev, peer->inbuf_size, 5648c2ecf20Sopenharmony_ci peer->inbuf, peer->inbuf_xlat); 5658c2ecf20Sopenharmony_ci peer->inbuf = NULL; 5668c2ecf20Sopenharmony_ci} 5678c2ecf20Sopenharmony_ci 5688c2ecf20Sopenharmony_cistatic int perf_setup_inbuf(struct perf_peer *peer) 5698c2ecf20Sopenharmony_ci{ 5708c2ecf20Sopenharmony_ci resource_size_t xlat_align, size_align, size_max; 5718c2ecf20Sopenharmony_ci struct perf_ctx *perf = peer->perf; 5728c2ecf20Sopenharmony_ci int ret; 5738c2ecf20Sopenharmony_ci 5748c2ecf20Sopenharmony_ci /* Get inbound MW parameters */ 5758c2ecf20Sopenharmony_ci ret = ntb_mw_get_align(perf->ntb, peer->pidx, perf->gidx, 5768c2ecf20Sopenharmony_ci &xlat_align, &size_align, &size_max); 5778c2ecf20Sopenharmony_ci if (ret) { 5788c2ecf20Sopenharmony_ci dev_err(&perf->ntb->dev, "Couldn't get inbuf restrictions\n"); 5798c2ecf20Sopenharmony_ci return ret; 5808c2ecf20Sopenharmony_ci } 5818c2ecf20Sopenharmony_ci 5828c2ecf20Sopenharmony_ci if (peer->inbuf_size > size_max) { 5838c2ecf20Sopenharmony_ci dev_err(&perf->ntb->dev, "Too big inbuf size %pa > %pa\n", 5848c2ecf20Sopenharmony_ci &peer->inbuf_size, &size_max); 5858c2ecf20Sopenharmony_ci return -EINVAL; 5868c2ecf20Sopenharmony_ci } 5878c2ecf20Sopenharmony_ci 5888c2ecf20Sopenharmony_ci peer->inbuf_size = round_up(peer->inbuf_size, size_align); 5898c2ecf20Sopenharmony_ci 5908c2ecf20Sopenharmony_ci perf_free_inbuf(peer); 5918c2ecf20Sopenharmony_ci 5928c2ecf20Sopenharmony_ci peer->inbuf = dma_alloc_coherent(&perf->ntb->pdev->dev, 5938c2ecf20Sopenharmony_ci peer->inbuf_size, &peer->inbuf_xlat, 5948c2ecf20Sopenharmony_ci GFP_KERNEL); 5958c2ecf20Sopenharmony_ci if (!peer->inbuf) { 5968c2ecf20Sopenharmony_ci dev_err(&perf->ntb->dev, "Failed to alloc inbuf of %pa\n", 5978c2ecf20Sopenharmony_ci &peer->inbuf_size); 5988c2ecf20Sopenharmony_ci return -ENOMEM; 5998c2ecf20Sopenharmony_ci } 6008c2ecf20Sopenharmony_ci if (!IS_ALIGNED(peer->inbuf_xlat, xlat_align)) { 6018c2ecf20Sopenharmony_ci ret = -EINVAL; 6028c2ecf20Sopenharmony_ci dev_err(&perf->ntb->dev, "Unaligned inbuf allocated\n"); 6038c2ecf20Sopenharmony_ci goto err_free_inbuf; 6048c2ecf20Sopenharmony_ci } 6058c2ecf20Sopenharmony_ci 6068c2ecf20Sopenharmony_ci ret = ntb_mw_set_trans(perf->ntb, peer->pidx, peer->gidx, 6078c2ecf20Sopenharmony_ci peer->inbuf_xlat, peer->inbuf_size); 6088c2ecf20Sopenharmony_ci if (ret) { 6098c2ecf20Sopenharmony_ci dev_err(&perf->ntb->dev, "Failed to set inbuf translation\n"); 6108c2ecf20Sopenharmony_ci goto err_free_inbuf; 6118c2ecf20Sopenharmony_ci } 6128c2ecf20Sopenharmony_ci 6138c2ecf20Sopenharmony_ci /* 6148c2ecf20Sopenharmony_ci * We submit inbuf xlat transmission cmd for execution here to follow 6158c2ecf20Sopenharmony_ci * the code architecture, even though this method is called from service 6168c2ecf20Sopenharmony_ci * work itself so the command will be executed right after it returns. 6178c2ecf20Sopenharmony_ci */ 6188c2ecf20Sopenharmony_ci (void)perf_cmd_exec(peer, PERF_CMD_SXLAT); 6198c2ecf20Sopenharmony_ci 6208c2ecf20Sopenharmony_ci return 0; 6218c2ecf20Sopenharmony_ci 6228c2ecf20Sopenharmony_cierr_free_inbuf: 6238c2ecf20Sopenharmony_ci perf_free_inbuf(peer); 6248c2ecf20Sopenharmony_ci 6258c2ecf20Sopenharmony_ci return ret; 6268c2ecf20Sopenharmony_ci} 6278c2ecf20Sopenharmony_ci 6288c2ecf20Sopenharmony_cistatic void perf_service_work(struct work_struct *work) 6298c2ecf20Sopenharmony_ci{ 6308c2ecf20Sopenharmony_ci struct perf_peer *peer = to_peer_service(work); 6318c2ecf20Sopenharmony_ci 6328c2ecf20Sopenharmony_ci if (test_and_clear_bit(PERF_CMD_SSIZE, &peer->sts)) 6338c2ecf20Sopenharmony_ci perf_cmd_send(peer, PERF_CMD_SSIZE, peer->outbuf_size); 6348c2ecf20Sopenharmony_ci 6358c2ecf20Sopenharmony_ci if (test_and_clear_bit(PERF_CMD_RSIZE, &peer->sts)) 6368c2ecf20Sopenharmony_ci perf_setup_inbuf(peer); 6378c2ecf20Sopenharmony_ci 6388c2ecf20Sopenharmony_ci if (test_and_clear_bit(PERF_CMD_SXLAT, &peer->sts)) 6398c2ecf20Sopenharmony_ci perf_cmd_send(peer, PERF_CMD_SXLAT, peer->inbuf_xlat); 6408c2ecf20Sopenharmony_ci 6418c2ecf20Sopenharmony_ci if (test_and_clear_bit(PERF_CMD_RXLAT, &peer->sts)) 6428c2ecf20Sopenharmony_ci perf_setup_outbuf(peer); 6438c2ecf20Sopenharmony_ci 6448c2ecf20Sopenharmony_ci if (test_and_clear_bit(PERF_CMD_CLEAR, &peer->sts)) { 6458c2ecf20Sopenharmony_ci init_completion(&peer->init_comp); 6468c2ecf20Sopenharmony_ci clear_bit(PERF_STS_DONE, &peer->sts); 6478c2ecf20Sopenharmony_ci if (test_bit(0, &peer->perf->busy_flag) && 6488c2ecf20Sopenharmony_ci peer == peer->perf->test_peer) { 6498c2ecf20Sopenharmony_ci dev_warn(&peer->perf->ntb->dev, 6508c2ecf20Sopenharmony_ci "Freeing while test on-fly\n"); 6518c2ecf20Sopenharmony_ci perf_terminate_test(peer->perf); 6528c2ecf20Sopenharmony_ci } 6538c2ecf20Sopenharmony_ci perf_free_outbuf(peer); 6548c2ecf20Sopenharmony_ci perf_free_inbuf(peer); 6558c2ecf20Sopenharmony_ci } 6568c2ecf20Sopenharmony_ci} 6578c2ecf20Sopenharmony_ci 6588c2ecf20Sopenharmony_cistatic int perf_init_service(struct perf_ctx *perf) 6598c2ecf20Sopenharmony_ci{ 6608c2ecf20Sopenharmony_ci u64 mask; 6618c2ecf20Sopenharmony_ci 6628c2ecf20Sopenharmony_ci if (ntb_peer_mw_count(perf->ntb) < perf->pcnt) { 6638c2ecf20Sopenharmony_ci dev_err(&perf->ntb->dev, "Not enough memory windows\n"); 6648c2ecf20Sopenharmony_ci return -EINVAL; 6658c2ecf20Sopenharmony_ci } 6668c2ecf20Sopenharmony_ci 6678c2ecf20Sopenharmony_ci if (ntb_msg_count(perf->ntb) >= PERF_MSG_CNT) { 6688c2ecf20Sopenharmony_ci perf->cmd_send = perf_msg_cmd_send; 6698c2ecf20Sopenharmony_ci perf->cmd_recv = perf_msg_cmd_recv; 6708c2ecf20Sopenharmony_ci 6718c2ecf20Sopenharmony_ci dev_dbg(&perf->ntb->dev, "Message service initialized\n"); 6728c2ecf20Sopenharmony_ci 6738c2ecf20Sopenharmony_ci return 0; 6748c2ecf20Sopenharmony_ci } 6758c2ecf20Sopenharmony_ci 6768c2ecf20Sopenharmony_ci dev_dbg(&perf->ntb->dev, "Message service unsupported\n"); 6778c2ecf20Sopenharmony_ci 6788c2ecf20Sopenharmony_ci mask = GENMASK_ULL(perf->pcnt, 0); 6798c2ecf20Sopenharmony_ci if (ntb_spad_count(perf->ntb) >= PERF_SPAD_CNT(perf->pcnt) && 6808c2ecf20Sopenharmony_ci (ntb_db_valid_mask(perf->ntb) & mask) == mask) { 6818c2ecf20Sopenharmony_ci perf->cmd_send = perf_spad_cmd_send; 6828c2ecf20Sopenharmony_ci perf->cmd_recv = perf_spad_cmd_recv; 6838c2ecf20Sopenharmony_ci 6848c2ecf20Sopenharmony_ci dev_dbg(&perf->ntb->dev, "Scratchpad service initialized\n"); 6858c2ecf20Sopenharmony_ci 6868c2ecf20Sopenharmony_ci return 0; 6878c2ecf20Sopenharmony_ci } 6888c2ecf20Sopenharmony_ci 6898c2ecf20Sopenharmony_ci dev_dbg(&perf->ntb->dev, "Scratchpad service unsupported\n"); 6908c2ecf20Sopenharmony_ci 6918c2ecf20Sopenharmony_ci dev_err(&perf->ntb->dev, "Command services unsupported\n"); 6928c2ecf20Sopenharmony_ci 6938c2ecf20Sopenharmony_ci return -EINVAL; 6948c2ecf20Sopenharmony_ci} 6958c2ecf20Sopenharmony_ci 6968c2ecf20Sopenharmony_cistatic int perf_enable_service(struct perf_ctx *perf) 6978c2ecf20Sopenharmony_ci{ 6988c2ecf20Sopenharmony_ci u64 mask, incmd_bit; 6998c2ecf20Sopenharmony_ci int ret, sidx, scnt; 7008c2ecf20Sopenharmony_ci 7018c2ecf20Sopenharmony_ci mask = ntb_db_valid_mask(perf->ntb); 7028c2ecf20Sopenharmony_ci (void)ntb_db_set_mask(perf->ntb, mask); 7038c2ecf20Sopenharmony_ci 7048c2ecf20Sopenharmony_ci ret = ntb_set_ctx(perf->ntb, perf, &perf_ops); 7058c2ecf20Sopenharmony_ci if (ret) 7068c2ecf20Sopenharmony_ci return ret; 7078c2ecf20Sopenharmony_ci 7088c2ecf20Sopenharmony_ci if (perf->cmd_send == perf_msg_cmd_send) { 7098c2ecf20Sopenharmony_ci u64 inbits, outbits; 7108c2ecf20Sopenharmony_ci 7118c2ecf20Sopenharmony_ci inbits = ntb_msg_inbits(perf->ntb); 7128c2ecf20Sopenharmony_ci outbits = ntb_msg_outbits(perf->ntb); 7138c2ecf20Sopenharmony_ci (void)ntb_msg_set_mask(perf->ntb, inbits | outbits); 7148c2ecf20Sopenharmony_ci 7158c2ecf20Sopenharmony_ci incmd_bit = BIT_ULL(__ffs64(inbits)); 7168c2ecf20Sopenharmony_ci ret = ntb_msg_clear_mask(perf->ntb, incmd_bit); 7178c2ecf20Sopenharmony_ci 7188c2ecf20Sopenharmony_ci dev_dbg(&perf->ntb->dev, "MSG sts unmasked %#llx\n", incmd_bit); 7198c2ecf20Sopenharmony_ci } else { 7208c2ecf20Sopenharmony_ci scnt = ntb_spad_count(perf->ntb); 7218c2ecf20Sopenharmony_ci for (sidx = 0; sidx < scnt; sidx++) 7228c2ecf20Sopenharmony_ci ntb_spad_write(perf->ntb, sidx, PERF_CMD_INVAL); 7238c2ecf20Sopenharmony_ci incmd_bit = PERF_SPAD_NOTIFY(perf->gidx); 7248c2ecf20Sopenharmony_ci ret = ntb_db_clear_mask(perf->ntb, incmd_bit); 7258c2ecf20Sopenharmony_ci 7268c2ecf20Sopenharmony_ci dev_dbg(&perf->ntb->dev, "DB bits unmasked %#llx\n", incmd_bit); 7278c2ecf20Sopenharmony_ci } 7288c2ecf20Sopenharmony_ci if (ret) { 7298c2ecf20Sopenharmony_ci ntb_clear_ctx(perf->ntb); 7308c2ecf20Sopenharmony_ci return ret; 7318c2ecf20Sopenharmony_ci } 7328c2ecf20Sopenharmony_ci 7338c2ecf20Sopenharmony_ci ntb_link_enable(perf->ntb, NTB_SPEED_AUTO, NTB_WIDTH_AUTO); 7348c2ecf20Sopenharmony_ci /* Might be not necessary */ 7358c2ecf20Sopenharmony_ci ntb_link_event(perf->ntb); 7368c2ecf20Sopenharmony_ci 7378c2ecf20Sopenharmony_ci return 0; 7388c2ecf20Sopenharmony_ci} 7398c2ecf20Sopenharmony_ci 7408c2ecf20Sopenharmony_cistatic void perf_disable_service(struct perf_ctx *perf) 7418c2ecf20Sopenharmony_ci{ 7428c2ecf20Sopenharmony_ci int pidx; 7438c2ecf20Sopenharmony_ci 7448c2ecf20Sopenharmony_ci if (perf->cmd_send == perf_msg_cmd_send) { 7458c2ecf20Sopenharmony_ci u64 inbits; 7468c2ecf20Sopenharmony_ci 7478c2ecf20Sopenharmony_ci inbits = ntb_msg_inbits(perf->ntb); 7488c2ecf20Sopenharmony_ci (void)ntb_msg_set_mask(perf->ntb, inbits); 7498c2ecf20Sopenharmony_ci } else { 7508c2ecf20Sopenharmony_ci (void)ntb_db_set_mask(perf->ntb, PERF_SPAD_NOTIFY(perf->gidx)); 7518c2ecf20Sopenharmony_ci } 7528c2ecf20Sopenharmony_ci 7538c2ecf20Sopenharmony_ci ntb_clear_ctx(perf->ntb); 7548c2ecf20Sopenharmony_ci 7558c2ecf20Sopenharmony_ci for (pidx = 0; pidx < perf->pcnt; pidx++) 7568c2ecf20Sopenharmony_ci perf_cmd_exec(&perf->peers[pidx], PERF_CMD_CLEAR); 7578c2ecf20Sopenharmony_ci 7588c2ecf20Sopenharmony_ci for (pidx = 0; pidx < perf->pcnt; pidx++) 7598c2ecf20Sopenharmony_ci flush_work(&perf->peers[pidx].service); 7608c2ecf20Sopenharmony_ci 7618c2ecf20Sopenharmony_ci for (pidx = 0; pidx < perf->pcnt; pidx++) { 7628c2ecf20Sopenharmony_ci struct perf_peer *peer = &perf->peers[pidx]; 7638c2ecf20Sopenharmony_ci 7648c2ecf20Sopenharmony_ci ntb_spad_write(perf->ntb, PERF_SPAD_CMD(peer->gidx), 0); 7658c2ecf20Sopenharmony_ci } 7668c2ecf20Sopenharmony_ci 7678c2ecf20Sopenharmony_ci ntb_db_clear(perf->ntb, PERF_SPAD_NOTIFY(perf->gidx)); 7688c2ecf20Sopenharmony_ci 7698c2ecf20Sopenharmony_ci ntb_link_disable(perf->ntb); 7708c2ecf20Sopenharmony_ci} 7718c2ecf20Sopenharmony_ci 7728c2ecf20Sopenharmony_ci/*============================================================================== 7738c2ecf20Sopenharmony_ci * Performance measuring work-thread 7748c2ecf20Sopenharmony_ci *============================================================================== 7758c2ecf20Sopenharmony_ci */ 7768c2ecf20Sopenharmony_ci 7778c2ecf20Sopenharmony_cistatic void perf_dma_copy_callback(void *data) 7788c2ecf20Sopenharmony_ci{ 7798c2ecf20Sopenharmony_ci struct perf_thread *pthr = data; 7808c2ecf20Sopenharmony_ci 7818c2ecf20Sopenharmony_ci atomic_dec(&pthr->dma_sync); 7828c2ecf20Sopenharmony_ci wake_up(&pthr->dma_wait); 7838c2ecf20Sopenharmony_ci} 7848c2ecf20Sopenharmony_ci 7858c2ecf20Sopenharmony_cistatic int perf_copy_chunk(struct perf_thread *pthr, 7868c2ecf20Sopenharmony_ci void __iomem *dst, void *src, size_t len) 7878c2ecf20Sopenharmony_ci{ 7888c2ecf20Sopenharmony_ci struct dma_async_tx_descriptor *tx; 7898c2ecf20Sopenharmony_ci struct dmaengine_unmap_data *unmap; 7908c2ecf20Sopenharmony_ci struct device *dma_dev; 7918c2ecf20Sopenharmony_ci int try = 0, ret = 0; 7928c2ecf20Sopenharmony_ci struct perf_peer *peer = pthr->perf->test_peer; 7938c2ecf20Sopenharmony_ci void __iomem *vbase; 7948c2ecf20Sopenharmony_ci void __iomem *dst_vaddr; 7958c2ecf20Sopenharmony_ci dma_addr_t dst_dma_addr; 7968c2ecf20Sopenharmony_ci 7978c2ecf20Sopenharmony_ci if (!use_dma) { 7988c2ecf20Sopenharmony_ci memcpy_toio(dst, src, len); 7998c2ecf20Sopenharmony_ci goto ret_check_tsync; 8008c2ecf20Sopenharmony_ci } 8018c2ecf20Sopenharmony_ci 8028c2ecf20Sopenharmony_ci dma_dev = pthr->dma_chan->device->dev; 8038c2ecf20Sopenharmony_ci 8048c2ecf20Sopenharmony_ci if (!is_dma_copy_aligned(pthr->dma_chan->device, offset_in_page(src), 8058c2ecf20Sopenharmony_ci offset_in_page(dst), len)) 8068c2ecf20Sopenharmony_ci return -EIO; 8078c2ecf20Sopenharmony_ci 8088c2ecf20Sopenharmony_ci vbase = peer->outbuf; 8098c2ecf20Sopenharmony_ci dst_vaddr = dst; 8108c2ecf20Sopenharmony_ci dst_dma_addr = peer->dma_dst_addr + (dst_vaddr - vbase); 8118c2ecf20Sopenharmony_ci 8128c2ecf20Sopenharmony_ci unmap = dmaengine_get_unmap_data(dma_dev, 1, GFP_NOWAIT); 8138c2ecf20Sopenharmony_ci if (!unmap) 8148c2ecf20Sopenharmony_ci return -ENOMEM; 8158c2ecf20Sopenharmony_ci 8168c2ecf20Sopenharmony_ci unmap->len = len; 8178c2ecf20Sopenharmony_ci unmap->addr[0] = dma_map_page(dma_dev, virt_to_page(src), 8188c2ecf20Sopenharmony_ci offset_in_page(src), len, DMA_TO_DEVICE); 8198c2ecf20Sopenharmony_ci if (dma_mapping_error(dma_dev, unmap->addr[0])) { 8208c2ecf20Sopenharmony_ci ret = -EIO; 8218c2ecf20Sopenharmony_ci goto err_free_resource; 8228c2ecf20Sopenharmony_ci } 8238c2ecf20Sopenharmony_ci unmap->to_cnt = 1; 8248c2ecf20Sopenharmony_ci 8258c2ecf20Sopenharmony_ci do { 8268c2ecf20Sopenharmony_ci tx = dmaengine_prep_dma_memcpy(pthr->dma_chan, dst_dma_addr, 8278c2ecf20Sopenharmony_ci unmap->addr[0], len, DMA_PREP_INTERRUPT | DMA_CTRL_ACK); 8288c2ecf20Sopenharmony_ci if (!tx) 8298c2ecf20Sopenharmony_ci msleep(DMA_MDELAY); 8308c2ecf20Sopenharmony_ci } while (!tx && (try++ < DMA_TRIES)); 8318c2ecf20Sopenharmony_ci 8328c2ecf20Sopenharmony_ci if (!tx) { 8338c2ecf20Sopenharmony_ci ret = -EIO; 8348c2ecf20Sopenharmony_ci goto err_free_resource; 8358c2ecf20Sopenharmony_ci } 8368c2ecf20Sopenharmony_ci 8378c2ecf20Sopenharmony_ci tx->callback = perf_dma_copy_callback; 8388c2ecf20Sopenharmony_ci tx->callback_param = pthr; 8398c2ecf20Sopenharmony_ci dma_set_unmap(tx, unmap); 8408c2ecf20Sopenharmony_ci 8418c2ecf20Sopenharmony_ci ret = dma_submit_error(dmaengine_submit(tx)); 8428c2ecf20Sopenharmony_ci if (ret) { 8438c2ecf20Sopenharmony_ci dmaengine_unmap_put(unmap); 8448c2ecf20Sopenharmony_ci goto err_free_resource; 8458c2ecf20Sopenharmony_ci } 8468c2ecf20Sopenharmony_ci 8478c2ecf20Sopenharmony_ci dmaengine_unmap_put(unmap); 8488c2ecf20Sopenharmony_ci 8498c2ecf20Sopenharmony_ci atomic_inc(&pthr->dma_sync); 8508c2ecf20Sopenharmony_ci dma_async_issue_pending(pthr->dma_chan); 8518c2ecf20Sopenharmony_ci 8528c2ecf20Sopenharmony_ciret_check_tsync: 8538c2ecf20Sopenharmony_ci return likely(atomic_read(&pthr->perf->tsync) > 0) ? 0 : -EINTR; 8548c2ecf20Sopenharmony_ci 8558c2ecf20Sopenharmony_cierr_free_resource: 8568c2ecf20Sopenharmony_ci dmaengine_unmap_put(unmap); 8578c2ecf20Sopenharmony_ci 8588c2ecf20Sopenharmony_ci return ret; 8598c2ecf20Sopenharmony_ci} 8608c2ecf20Sopenharmony_ci 8618c2ecf20Sopenharmony_cistatic bool perf_dma_filter(struct dma_chan *chan, void *data) 8628c2ecf20Sopenharmony_ci{ 8638c2ecf20Sopenharmony_ci struct perf_ctx *perf = data; 8648c2ecf20Sopenharmony_ci int node; 8658c2ecf20Sopenharmony_ci 8668c2ecf20Sopenharmony_ci node = dev_to_node(&perf->ntb->dev); 8678c2ecf20Sopenharmony_ci 8688c2ecf20Sopenharmony_ci return node == NUMA_NO_NODE || node == dev_to_node(chan->device->dev); 8698c2ecf20Sopenharmony_ci} 8708c2ecf20Sopenharmony_ci 8718c2ecf20Sopenharmony_cistatic int perf_init_test(struct perf_thread *pthr) 8728c2ecf20Sopenharmony_ci{ 8738c2ecf20Sopenharmony_ci struct perf_ctx *perf = pthr->perf; 8748c2ecf20Sopenharmony_ci dma_cap_mask_t dma_mask; 8758c2ecf20Sopenharmony_ci struct perf_peer *peer = pthr->perf->test_peer; 8768c2ecf20Sopenharmony_ci 8778c2ecf20Sopenharmony_ci pthr->src = kmalloc_node(perf->test_peer->outbuf_size, GFP_KERNEL, 8788c2ecf20Sopenharmony_ci dev_to_node(&perf->ntb->dev)); 8798c2ecf20Sopenharmony_ci if (!pthr->src) 8808c2ecf20Sopenharmony_ci return -ENOMEM; 8818c2ecf20Sopenharmony_ci 8828c2ecf20Sopenharmony_ci get_random_bytes(pthr->src, perf->test_peer->outbuf_size); 8838c2ecf20Sopenharmony_ci 8848c2ecf20Sopenharmony_ci if (!use_dma) 8858c2ecf20Sopenharmony_ci return 0; 8868c2ecf20Sopenharmony_ci 8878c2ecf20Sopenharmony_ci dma_cap_zero(dma_mask); 8888c2ecf20Sopenharmony_ci dma_cap_set(DMA_MEMCPY, dma_mask); 8898c2ecf20Sopenharmony_ci pthr->dma_chan = dma_request_channel(dma_mask, perf_dma_filter, perf); 8908c2ecf20Sopenharmony_ci if (!pthr->dma_chan) { 8918c2ecf20Sopenharmony_ci dev_err(&perf->ntb->dev, "%d: Failed to get DMA channel\n", 8928c2ecf20Sopenharmony_ci pthr->tidx); 8938c2ecf20Sopenharmony_ci goto err_free; 8948c2ecf20Sopenharmony_ci } 8958c2ecf20Sopenharmony_ci peer->dma_dst_addr = 8968c2ecf20Sopenharmony_ci dma_map_resource(pthr->dma_chan->device->dev, 8978c2ecf20Sopenharmony_ci peer->out_phys_addr, peer->outbuf_size, 8988c2ecf20Sopenharmony_ci DMA_FROM_DEVICE, 0); 8998c2ecf20Sopenharmony_ci if (dma_mapping_error(pthr->dma_chan->device->dev, 9008c2ecf20Sopenharmony_ci peer->dma_dst_addr)) { 9018c2ecf20Sopenharmony_ci dev_err(pthr->dma_chan->device->dev, "%d: Failed to map DMA addr\n", 9028c2ecf20Sopenharmony_ci pthr->tidx); 9038c2ecf20Sopenharmony_ci peer->dma_dst_addr = 0; 9048c2ecf20Sopenharmony_ci dma_release_channel(pthr->dma_chan); 9058c2ecf20Sopenharmony_ci goto err_free; 9068c2ecf20Sopenharmony_ci } 9078c2ecf20Sopenharmony_ci dev_dbg(pthr->dma_chan->device->dev, "%d: Map MMIO %pa to DMA addr %pad\n", 9088c2ecf20Sopenharmony_ci pthr->tidx, 9098c2ecf20Sopenharmony_ci &peer->out_phys_addr, 9108c2ecf20Sopenharmony_ci &peer->dma_dst_addr); 9118c2ecf20Sopenharmony_ci 9128c2ecf20Sopenharmony_ci atomic_set(&pthr->dma_sync, 0); 9138c2ecf20Sopenharmony_ci return 0; 9148c2ecf20Sopenharmony_ci 9158c2ecf20Sopenharmony_cierr_free: 9168c2ecf20Sopenharmony_ci atomic_dec(&perf->tsync); 9178c2ecf20Sopenharmony_ci wake_up(&perf->twait); 9188c2ecf20Sopenharmony_ci kfree(pthr->src); 9198c2ecf20Sopenharmony_ci return -ENODEV; 9208c2ecf20Sopenharmony_ci} 9218c2ecf20Sopenharmony_ci 9228c2ecf20Sopenharmony_cistatic int perf_run_test(struct perf_thread *pthr) 9238c2ecf20Sopenharmony_ci{ 9248c2ecf20Sopenharmony_ci struct perf_peer *peer = pthr->perf->test_peer; 9258c2ecf20Sopenharmony_ci struct perf_ctx *perf = pthr->perf; 9268c2ecf20Sopenharmony_ci void __iomem *flt_dst, *bnd_dst; 9278c2ecf20Sopenharmony_ci u64 total_size, chunk_size; 9288c2ecf20Sopenharmony_ci void *flt_src; 9298c2ecf20Sopenharmony_ci int ret = 0; 9308c2ecf20Sopenharmony_ci 9318c2ecf20Sopenharmony_ci total_size = 1ULL << total_order; 9328c2ecf20Sopenharmony_ci chunk_size = 1ULL << chunk_order; 9338c2ecf20Sopenharmony_ci chunk_size = min_t(u64, peer->outbuf_size, chunk_size); 9348c2ecf20Sopenharmony_ci 9358c2ecf20Sopenharmony_ci flt_src = pthr->src; 9368c2ecf20Sopenharmony_ci bnd_dst = peer->outbuf + peer->outbuf_size; 9378c2ecf20Sopenharmony_ci flt_dst = peer->outbuf; 9388c2ecf20Sopenharmony_ci 9398c2ecf20Sopenharmony_ci pthr->duration = ktime_get(); 9408c2ecf20Sopenharmony_ci 9418c2ecf20Sopenharmony_ci /* Copied field is cleared on test launch stage */ 9428c2ecf20Sopenharmony_ci while (pthr->copied < total_size) { 9438c2ecf20Sopenharmony_ci ret = perf_copy_chunk(pthr, flt_dst, flt_src, chunk_size); 9448c2ecf20Sopenharmony_ci if (ret) { 9458c2ecf20Sopenharmony_ci dev_err(&perf->ntb->dev, "%d: Got error %d on test\n", 9468c2ecf20Sopenharmony_ci pthr->tidx, ret); 9478c2ecf20Sopenharmony_ci return ret; 9488c2ecf20Sopenharmony_ci } 9498c2ecf20Sopenharmony_ci 9508c2ecf20Sopenharmony_ci pthr->copied += chunk_size; 9518c2ecf20Sopenharmony_ci 9528c2ecf20Sopenharmony_ci flt_dst += chunk_size; 9538c2ecf20Sopenharmony_ci flt_src += chunk_size; 9548c2ecf20Sopenharmony_ci if (flt_dst >= bnd_dst || flt_dst < peer->outbuf) { 9558c2ecf20Sopenharmony_ci flt_dst = peer->outbuf; 9568c2ecf20Sopenharmony_ci flt_src = pthr->src; 9578c2ecf20Sopenharmony_ci } 9588c2ecf20Sopenharmony_ci 9598c2ecf20Sopenharmony_ci /* Give up CPU to give a chance for other threads to use it */ 9608c2ecf20Sopenharmony_ci schedule(); 9618c2ecf20Sopenharmony_ci } 9628c2ecf20Sopenharmony_ci 9638c2ecf20Sopenharmony_ci return 0; 9648c2ecf20Sopenharmony_ci} 9658c2ecf20Sopenharmony_ci 9668c2ecf20Sopenharmony_cistatic int perf_sync_test(struct perf_thread *pthr) 9678c2ecf20Sopenharmony_ci{ 9688c2ecf20Sopenharmony_ci struct perf_ctx *perf = pthr->perf; 9698c2ecf20Sopenharmony_ci 9708c2ecf20Sopenharmony_ci if (!use_dma) 9718c2ecf20Sopenharmony_ci goto no_dma_ret; 9728c2ecf20Sopenharmony_ci 9738c2ecf20Sopenharmony_ci wait_event(pthr->dma_wait, 9748c2ecf20Sopenharmony_ci (atomic_read(&pthr->dma_sync) == 0 || 9758c2ecf20Sopenharmony_ci atomic_read(&perf->tsync) < 0)); 9768c2ecf20Sopenharmony_ci 9778c2ecf20Sopenharmony_ci if (atomic_read(&perf->tsync) < 0) 9788c2ecf20Sopenharmony_ci return -EINTR; 9798c2ecf20Sopenharmony_ci 9808c2ecf20Sopenharmony_cino_dma_ret: 9818c2ecf20Sopenharmony_ci pthr->duration = ktime_sub(ktime_get(), pthr->duration); 9828c2ecf20Sopenharmony_ci 9838c2ecf20Sopenharmony_ci dev_dbg(&perf->ntb->dev, "%d: copied %llu bytes\n", 9848c2ecf20Sopenharmony_ci pthr->tidx, pthr->copied); 9858c2ecf20Sopenharmony_ci 9868c2ecf20Sopenharmony_ci dev_dbg(&perf->ntb->dev, "%d: lasted %llu usecs\n", 9878c2ecf20Sopenharmony_ci pthr->tidx, ktime_to_us(pthr->duration)); 9888c2ecf20Sopenharmony_ci 9898c2ecf20Sopenharmony_ci dev_dbg(&perf->ntb->dev, "%d: %llu MBytes/s\n", pthr->tidx, 9908c2ecf20Sopenharmony_ci div64_u64(pthr->copied, ktime_to_us(pthr->duration))); 9918c2ecf20Sopenharmony_ci 9928c2ecf20Sopenharmony_ci return 0; 9938c2ecf20Sopenharmony_ci} 9948c2ecf20Sopenharmony_ci 9958c2ecf20Sopenharmony_cistatic void perf_clear_test(struct perf_thread *pthr) 9968c2ecf20Sopenharmony_ci{ 9978c2ecf20Sopenharmony_ci struct perf_ctx *perf = pthr->perf; 9988c2ecf20Sopenharmony_ci 9998c2ecf20Sopenharmony_ci if (!use_dma) 10008c2ecf20Sopenharmony_ci goto no_dma_notify; 10018c2ecf20Sopenharmony_ci 10028c2ecf20Sopenharmony_ci /* 10038c2ecf20Sopenharmony_ci * If test finished without errors, termination isn't needed. 10048c2ecf20Sopenharmony_ci * We call it anyway just to be sure of the transfers completion. 10058c2ecf20Sopenharmony_ci */ 10068c2ecf20Sopenharmony_ci (void)dmaengine_terminate_sync(pthr->dma_chan); 10078c2ecf20Sopenharmony_ci if (pthr->perf->test_peer->dma_dst_addr) 10088c2ecf20Sopenharmony_ci dma_unmap_resource(pthr->dma_chan->device->dev, 10098c2ecf20Sopenharmony_ci pthr->perf->test_peer->dma_dst_addr, 10108c2ecf20Sopenharmony_ci pthr->perf->test_peer->outbuf_size, 10118c2ecf20Sopenharmony_ci DMA_FROM_DEVICE, 0); 10128c2ecf20Sopenharmony_ci 10138c2ecf20Sopenharmony_ci dma_release_channel(pthr->dma_chan); 10148c2ecf20Sopenharmony_ci 10158c2ecf20Sopenharmony_cino_dma_notify: 10168c2ecf20Sopenharmony_ci atomic_dec(&perf->tsync); 10178c2ecf20Sopenharmony_ci wake_up(&perf->twait); 10188c2ecf20Sopenharmony_ci kfree(pthr->src); 10198c2ecf20Sopenharmony_ci} 10208c2ecf20Sopenharmony_ci 10218c2ecf20Sopenharmony_cistatic void perf_thread_work(struct work_struct *work) 10228c2ecf20Sopenharmony_ci{ 10238c2ecf20Sopenharmony_ci struct perf_thread *pthr = to_thread_work(work); 10248c2ecf20Sopenharmony_ci int ret; 10258c2ecf20Sopenharmony_ci 10268c2ecf20Sopenharmony_ci /* 10278c2ecf20Sopenharmony_ci * Perform stages in compliance with use_dma flag value. 10288c2ecf20Sopenharmony_ci * Test status is changed only if error happened, otherwise 10298c2ecf20Sopenharmony_ci * status -ENODATA is kept while test is on-fly. Results 10308c2ecf20Sopenharmony_ci * synchronization is performed only if test fininshed 10318c2ecf20Sopenharmony_ci * without an error or interruption. 10328c2ecf20Sopenharmony_ci */ 10338c2ecf20Sopenharmony_ci ret = perf_init_test(pthr); 10348c2ecf20Sopenharmony_ci if (ret) { 10358c2ecf20Sopenharmony_ci pthr->status = ret; 10368c2ecf20Sopenharmony_ci return; 10378c2ecf20Sopenharmony_ci } 10388c2ecf20Sopenharmony_ci 10398c2ecf20Sopenharmony_ci ret = perf_run_test(pthr); 10408c2ecf20Sopenharmony_ci if (ret) { 10418c2ecf20Sopenharmony_ci pthr->status = ret; 10428c2ecf20Sopenharmony_ci goto err_clear_test; 10438c2ecf20Sopenharmony_ci } 10448c2ecf20Sopenharmony_ci 10458c2ecf20Sopenharmony_ci pthr->status = perf_sync_test(pthr); 10468c2ecf20Sopenharmony_ci 10478c2ecf20Sopenharmony_cierr_clear_test: 10488c2ecf20Sopenharmony_ci perf_clear_test(pthr); 10498c2ecf20Sopenharmony_ci} 10508c2ecf20Sopenharmony_ci 10518c2ecf20Sopenharmony_cistatic int perf_set_tcnt(struct perf_ctx *perf, u8 tcnt) 10528c2ecf20Sopenharmony_ci{ 10538c2ecf20Sopenharmony_ci if (tcnt == 0 || tcnt > MAX_THREADS_CNT) 10548c2ecf20Sopenharmony_ci return -EINVAL; 10558c2ecf20Sopenharmony_ci 10568c2ecf20Sopenharmony_ci if (test_and_set_bit_lock(0, &perf->busy_flag)) 10578c2ecf20Sopenharmony_ci return -EBUSY; 10588c2ecf20Sopenharmony_ci 10598c2ecf20Sopenharmony_ci perf->tcnt = tcnt; 10608c2ecf20Sopenharmony_ci 10618c2ecf20Sopenharmony_ci clear_bit_unlock(0, &perf->busy_flag); 10628c2ecf20Sopenharmony_ci 10638c2ecf20Sopenharmony_ci return 0; 10648c2ecf20Sopenharmony_ci} 10658c2ecf20Sopenharmony_ci 10668c2ecf20Sopenharmony_cistatic void perf_terminate_test(struct perf_ctx *perf) 10678c2ecf20Sopenharmony_ci{ 10688c2ecf20Sopenharmony_ci int tidx; 10698c2ecf20Sopenharmony_ci 10708c2ecf20Sopenharmony_ci atomic_set(&perf->tsync, -1); 10718c2ecf20Sopenharmony_ci wake_up(&perf->twait); 10728c2ecf20Sopenharmony_ci 10738c2ecf20Sopenharmony_ci for (tidx = 0; tidx < MAX_THREADS_CNT; tidx++) { 10748c2ecf20Sopenharmony_ci wake_up(&perf->threads[tidx].dma_wait); 10758c2ecf20Sopenharmony_ci cancel_work_sync(&perf->threads[tidx].work); 10768c2ecf20Sopenharmony_ci } 10778c2ecf20Sopenharmony_ci} 10788c2ecf20Sopenharmony_ci 10798c2ecf20Sopenharmony_cistatic int perf_submit_test(struct perf_peer *peer) 10808c2ecf20Sopenharmony_ci{ 10818c2ecf20Sopenharmony_ci struct perf_ctx *perf = peer->perf; 10828c2ecf20Sopenharmony_ci struct perf_thread *pthr; 10838c2ecf20Sopenharmony_ci int tidx, ret; 10848c2ecf20Sopenharmony_ci 10858c2ecf20Sopenharmony_ci ret = wait_for_completion_interruptible(&peer->init_comp); 10868c2ecf20Sopenharmony_ci if (ret < 0) 10878c2ecf20Sopenharmony_ci return ret; 10888c2ecf20Sopenharmony_ci 10898c2ecf20Sopenharmony_ci if (test_and_set_bit_lock(0, &perf->busy_flag)) 10908c2ecf20Sopenharmony_ci return -EBUSY; 10918c2ecf20Sopenharmony_ci 10928c2ecf20Sopenharmony_ci perf->test_peer = peer; 10938c2ecf20Sopenharmony_ci atomic_set(&perf->tsync, perf->tcnt); 10948c2ecf20Sopenharmony_ci 10958c2ecf20Sopenharmony_ci for (tidx = 0; tidx < MAX_THREADS_CNT; tidx++) { 10968c2ecf20Sopenharmony_ci pthr = &perf->threads[tidx]; 10978c2ecf20Sopenharmony_ci 10988c2ecf20Sopenharmony_ci pthr->status = -ENODATA; 10998c2ecf20Sopenharmony_ci pthr->copied = 0; 11008c2ecf20Sopenharmony_ci pthr->duration = ktime_set(0, 0); 11018c2ecf20Sopenharmony_ci if (tidx < perf->tcnt) 11028c2ecf20Sopenharmony_ci (void)queue_work(perf_wq, &pthr->work); 11038c2ecf20Sopenharmony_ci } 11048c2ecf20Sopenharmony_ci 11058c2ecf20Sopenharmony_ci ret = wait_event_interruptible(perf->twait, 11068c2ecf20Sopenharmony_ci atomic_read(&perf->tsync) <= 0); 11078c2ecf20Sopenharmony_ci if (ret == -ERESTARTSYS) { 11088c2ecf20Sopenharmony_ci perf_terminate_test(perf); 11098c2ecf20Sopenharmony_ci ret = -EINTR; 11108c2ecf20Sopenharmony_ci } 11118c2ecf20Sopenharmony_ci 11128c2ecf20Sopenharmony_ci clear_bit_unlock(0, &perf->busy_flag); 11138c2ecf20Sopenharmony_ci 11148c2ecf20Sopenharmony_ci return ret; 11158c2ecf20Sopenharmony_ci} 11168c2ecf20Sopenharmony_ci 11178c2ecf20Sopenharmony_cistatic int perf_read_stats(struct perf_ctx *perf, char *buf, 11188c2ecf20Sopenharmony_ci size_t size, ssize_t *pos) 11198c2ecf20Sopenharmony_ci{ 11208c2ecf20Sopenharmony_ci struct perf_thread *pthr; 11218c2ecf20Sopenharmony_ci int tidx; 11228c2ecf20Sopenharmony_ci 11238c2ecf20Sopenharmony_ci if (test_and_set_bit_lock(0, &perf->busy_flag)) 11248c2ecf20Sopenharmony_ci return -EBUSY; 11258c2ecf20Sopenharmony_ci 11268c2ecf20Sopenharmony_ci (*pos) += scnprintf(buf + *pos, size - *pos, 11278c2ecf20Sopenharmony_ci " Peer %d test statistics:\n", perf->test_peer->pidx); 11288c2ecf20Sopenharmony_ci 11298c2ecf20Sopenharmony_ci for (tidx = 0; tidx < MAX_THREADS_CNT; tidx++) { 11308c2ecf20Sopenharmony_ci pthr = &perf->threads[tidx]; 11318c2ecf20Sopenharmony_ci 11328c2ecf20Sopenharmony_ci if (pthr->status == -ENODATA) 11338c2ecf20Sopenharmony_ci continue; 11348c2ecf20Sopenharmony_ci 11358c2ecf20Sopenharmony_ci if (pthr->status) { 11368c2ecf20Sopenharmony_ci (*pos) += scnprintf(buf + *pos, size - *pos, 11378c2ecf20Sopenharmony_ci "%d: error status %d\n", tidx, pthr->status); 11388c2ecf20Sopenharmony_ci continue; 11398c2ecf20Sopenharmony_ci } 11408c2ecf20Sopenharmony_ci 11418c2ecf20Sopenharmony_ci (*pos) += scnprintf(buf + *pos, size - *pos, 11428c2ecf20Sopenharmony_ci "%d: copied %llu bytes in %llu usecs, %llu MBytes/s\n", 11438c2ecf20Sopenharmony_ci tidx, pthr->copied, ktime_to_us(pthr->duration), 11448c2ecf20Sopenharmony_ci div64_u64(pthr->copied, ktime_to_us(pthr->duration))); 11458c2ecf20Sopenharmony_ci } 11468c2ecf20Sopenharmony_ci 11478c2ecf20Sopenharmony_ci clear_bit_unlock(0, &perf->busy_flag); 11488c2ecf20Sopenharmony_ci 11498c2ecf20Sopenharmony_ci return 0; 11508c2ecf20Sopenharmony_ci} 11518c2ecf20Sopenharmony_ci 11528c2ecf20Sopenharmony_cistatic void perf_init_threads(struct perf_ctx *perf) 11538c2ecf20Sopenharmony_ci{ 11548c2ecf20Sopenharmony_ci struct perf_thread *pthr; 11558c2ecf20Sopenharmony_ci int tidx; 11568c2ecf20Sopenharmony_ci 11578c2ecf20Sopenharmony_ci perf->tcnt = DEF_THREADS_CNT; 11588c2ecf20Sopenharmony_ci perf->test_peer = &perf->peers[0]; 11598c2ecf20Sopenharmony_ci init_waitqueue_head(&perf->twait); 11608c2ecf20Sopenharmony_ci 11618c2ecf20Sopenharmony_ci for (tidx = 0; tidx < MAX_THREADS_CNT; tidx++) { 11628c2ecf20Sopenharmony_ci pthr = &perf->threads[tidx]; 11638c2ecf20Sopenharmony_ci 11648c2ecf20Sopenharmony_ci pthr->perf = perf; 11658c2ecf20Sopenharmony_ci pthr->tidx = tidx; 11668c2ecf20Sopenharmony_ci pthr->status = -ENODATA; 11678c2ecf20Sopenharmony_ci init_waitqueue_head(&pthr->dma_wait); 11688c2ecf20Sopenharmony_ci INIT_WORK(&pthr->work, perf_thread_work); 11698c2ecf20Sopenharmony_ci } 11708c2ecf20Sopenharmony_ci} 11718c2ecf20Sopenharmony_ci 11728c2ecf20Sopenharmony_cistatic void perf_clear_threads(struct perf_ctx *perf) 11738c2ecf20Sopenharmony_ci{ 11748c2ecf20Sopenharmony_ci perf_terminate_test(perf); 11758c2ecf20Sopenharmony_ci} 11768c2ecf20Sopenharmony_ci 11778c2ecf20Sopenharmony_ci/*============================================================================== 11788c2ecf20Sopenharmony_ci * DebugFS nodes 11798c2ecf20Sopenharmony_ci *============================================================================== 11808c2ecf20Sopenharmony_ci */ 11818c2ecf20Sopenharmony_ci 11828c2ecf20Sopenharmony_cistatic ssize_t perf_dbgfs_read_info(struct file *filep, char __user *ubuf, 11838c2ecf20Sopenharmony_ci size_t size, loff_t *offp) 11848c2ecf20Sopenharmony_ci{ 11858c2ecf20Sopenharmony_ci struct perf_ctx *perf = filep->private_data; 11868c2ecf20Sopenharmony_ci struct perf_peer *peer; 11878c2ecf20Sopenharmony_ci size_t buf_size; 11888c2ecf20Sopenharmony_ci ssize_t pos = 0; 11898c2ecf20Sopenharmony_ci int ret, pidx; 11908c2ecf20Sopenharmony_ci char *buf; 11918c2ecf20Sopenharmony_ci 11928c2ecf20Sopenharmony_ci buf_size = min_t(size_t, size, 0x1000U); 11938c2ecf20Sopenharmony_ci 11948c2ecf20Sopenharmony_ci buf = kmalloc(buf_size, GFP_KERNEL); 11958c2ecf20Sopenharmony_ci if (!buf) 11968c2ecf20Sopenharmony_ci return -ENOMEM; 11978c2ecf20Sopenharmony_ci 11988c2ecf20Sopenharmony_ci pos += scnprintf(buf + pos, buf_size - pos, 11998c2ecf20Sopenharmony_ci " Performance measuring tool info:\n\n"); 12008c2ecf20Sopenharmony_ci 12018c2ecf20Sopenharmony_ci pos += scnprintf(buf + pos, buf_size - pos, 12028c2ecf20Sopenharmony_ci "Local port %d, Global index %d\n", ntb_port_number(perf->ntb), 12038c2ecf20Sopenharmony_ci perf->gidx); 12048c2ecf20Sopenharmony_ci pos += scnprintf(buf + pos, buf_size - pos, "Test status: "); 12058c2ecf20Sopenharmony_ci if (test_bit(0, &perf->busy_flag)) { 12068c2ecf20Sopenharmony_ci pos += scnprintf(buf + pos, buf_size - pos, 12078c2ecf20Sopenharmony_ci "on-fly with port %d (%d)\n", 12088c2ecf20Sopenharmony_ci ntb_peer_port_number(perf->ntb, perf->test_peer->pidx), 12098c2ecf20Sopenharmony_ci perf->test_peer->pidx); 12108c2ecf20Sopenharmony_ci } else { 12118c2ecf20Sopenharmony_ci pos += scnprintf(buf + pos, buf_size - pos, "idle\n"); 12128c2ecf20Sopenharmony_ci } 12138c2ecf20Sopenharmony_ci 12148c2ecf20Sopenharmony_ci for (pidx = 0; pidx < perf->pcnt; pidx++) { 12158c2ecf20Sopenharmony_ci peer = &perf->peers[pidx]; 12168c2ecf20Sopenharmony_ci 12178c2ecf20Sopenharmony_ci pos += scnprintf(buf + pos, buf_size - pos, 12188c2ecf20Sopenharmony_ci "Port %d (%d), Global index %d:\n", 12198c2ecf20Sopenharmony_ci ntb_peer_port_number(perf->ntb, peer->pidx), peer->pidx, 12208c2ecf20Sopenharmony_ci peer->gidx); 12218c2ecf20Sopenharmony_ci 12228c2ecf20Sopenharmony_ci pos += scnprintf(buf + pos, buf_size - pos, 12238c2ecf20Sopenharmony_ci "\tLink status: %s\n", 12248c2ecf20Sopenharmony_ci test_bit(PERF_STS_LNKUP, &peer->sts) ? "up" : "down"); 12258c2ecf20Sopenharmony_ci 12268c2ecf20Sopenharmony_ci pos += scnprintf(buf + pos, buf_size - pos, 12278c2ecf20Sopenharmony_ci "\tOut buffer addr 0x%pK\n", peer->outbuf); 12288c2ecf20Sopenharmony_ci 12298c2ecf20Sopenharmony_ci pos += scnprintf(buf + pos, buf_size - pos, 12308c2ecf20Sopenharmony_ci "\tOut buff phys addr %pa[p]\n", &peer->out_phys_addr); 12318c2ecf20Sopenharmony_ci 12328c2ecf20Sopenharmony_ci pos += scnprintf(buf + pos, buf_size - pos, 12338c2ecf20Sopenharmony_ci "\tOut buffer size %pa\n", &peer->outbuf_size); 12348c2ecf20Sopenharmony_ci 12358c2ecf20Sopenharmony_ci pos += scnprintf(buf + pos, buf_size - pos, 12368c2ecf20Sopenharmony_ci "\tOut buffer xlat 0x%016llx[p]\n", peer->outbuf_xlat); 12378c2ecf20Sopenharmony_ci 12388c2ecf20Sopenharmony_ci if (!peer->inbuf) { 12398c2ecf20Sopenharmony_ci pos += scnprintf(buf + pos, buf_size - pos, 12408c2ecf20Sopenharmony_ci "\tIn buffer addr: unallocated\n"); 12418c2ecf20Sopenharmony_ci continue; 12428c2ecf20Sopenharmony_ci } 12438c2ecf20Sopenharmony_ci 12448c2ecf20Sopenharmony_ci pos += scnprintf(buf + pos, buf_size - pos, 12458c2ecf20Sopenharmony_ci "\tIn buffer addr 0x%pK\n", peer->inbuf); 12468c2ecf20Sopenharmony_ci 12478c2ecf20Sopenharmony_ci pos += scnprintf(buf + pos, buf_size - pos, 12488c2ecf20Sopenharmony_ci "\tIn buffer size %pa\n", &peer->inbuf_size); 12498c2ecf20Sopenharmony_ci 12508c2ecf20Sopenharmony_ci pos += scnprintf(buf + pos, buf_size - pos, 12518c2ecf20Sopenharmony_ci "\tIn buffer xlat %pad[p]\n", &peer->inbuf_xlat); 12528c2ecf20Sopenharmony_ci } 12538c2ecf20Sopenharmony_ci 12548c2ecf20Sopenharmony_ci ret = simple_read_from_buffer(ubuf, size, offp, buf, pos); 12558c2ecf20Sopenharmony_ci kfree(buf); 12568c2ecf20Sopenharmony_ci 12578c2ecf20Sopenharmony_ci return ret; 12588c2ecf20Sopenharmony_ci} 12598c2ecf20Sopenharmony_ci 12608c2ecf20Sopenharmony_cistatic const struct file_operations perf_dbgfs_info = { 12618c2ecf20Sopenharmony_ci .open = simple_open, 12628c2ecf20Sopenharmony_ci .read = perf_dbgfs_read_info 12638c2ecf20Sopenharmony_ci}; 12648c2ecf20Sopenharmony_ci 12658c2ecf20Sopenharmony_cistatic ssize_t perf_dbgfs_read_run(struct file *filep, char __user *ubuf, 12668c2ecf20Sopenharmony_ci size_t size, loff_t *offp) 12678c2ecf20Sopenharmony_ci{ 12688c2ecf20Sopenharmony_ci struct perf_ctx *perf = filep->private_data; 12698c2ecf20Sopenharmony_ci ssize_t ret, pos = 0; 12708c2ecf20Sopenharmony_ci char *buf; 12718c2ecf20Sopenharmony_ci 12728c2ecf20Sopenharmony_ci buf = kmalloc(PERF_BUF_LEN, GFP_KERNEL); 12738c2ecf20Sopenharmony_ci if (!buf) 12748c2ecf20Sopenharmony_ci return -ENOMEM; 12758c2ecf20Sopenharmony_ci 12768c2ecf20Sopenharmony_ci ret = perf_read_stats(perf, buf, PERF_BUF_LEN, &pos); 12778c2ecf20Sopenharmony_ci if (ret) 12788c2ecf20Sopenharmony_ci goto err_free; 12798c2ecf20Sopenharmony_ci 12808c2ecf20Sopenharmony_ci ret = simple_read_from_buffer(ubuf, size, offp, buf, pos); 12818c2ecf20Sopenharmony_cierr_free: 12828c2ecf20Sopenharmony_ci kfree(buf); 12838c2ecf20Sopenharmony_ci 12848c2ecf20Sopenharmony_ci return ret; 12858c2ecf20Sopenharmony_ci} 12868c2ecf20Sopenharmony_ci 12878c2ecf20Sopenharmony_cistatic ssize_t perf_dbgfs_write_run(struct file *filep, const char __user *ubuf, 12888c2ecf20Sopenharmony_ci size_t size, loff_t *offp) 12898c2ecf20Sopenharmony_ci{ 12908c2ecf20Sopenharmony_ci struct perf_ctx *perf = filep->private_data; 12918c2ecf20Sopenharmony_ci struct perf_peer *peer; 12928c2ecf20Sopenharmony_ci int pidx, ret; 12938c2ecf20Sopenharmony_ci 12948c2ecf20Sopenharmony_ci ret = kstrtoint_from_user(ubuf, size, 0, &pidx); 12958c2ecf20Sopenharmony_ci if (ret) 12968c2ecf20Sopenharmony_ci return ret; 12978c2ecf20Sopenharmony_ci 12988c2ecf20Sopenharmony_ci if (pidx < 0 || pidx >= perf->pcnt) 12998c2ecf20Sopenharmony_ci return -EINVAL; 13008c2ecf20Sopenharmony_ci 13018c2ecf20Sopenharmony_ci peer = &perf->peers[pidx]; 13028c2ecf20Sopenharmony_ci 13038c2ecf20Sopenharmony_ci ret = perf_submit_test(peer); 13048c2ecf20Sopenharmony_ci if (ret) 13058c2ecf20Sopenharmony_ci return ret; 13068c2ecf20Sopenharmony_ci 13078c2ecf20Sopenharmony_ci return size; 13088c2ecf20Sopenharmony_ci} 13098c2ecf20Sopenharmony_ci 13108c2ecf20Sopenharmony_cistatic const struct file_operations perf_dbgfs_run = { 13118c2ecf20Sopenharmony_ci .open = simple_open, 13128c2ecf20Sopenharmony_ci .read = perf_dbgfs_read_run, 13138c2ecf20Sopenharmony_ci .write = perf_dbgfs_write_run 13148c2ecf20Sopenharmony_ci}; 13158c2ecf20Sopenharmony_ci 13168c2ecf20Sopenharmony_cistatic ssize_t perf_dbgfs_read_tcnt(struct file *filep, char __user *ubuf, 13178c2ecf20Sopenharmony_ci size_t size, loff_t *offp) 13188c2ecf20Sopenharmony_ci{ 13198c2ecf20Sopenharmony_ci struct perf_ctx *perf = filep->private_data; 13208c2ecf20Sopenharmony_ci char buf[8]; 13218c2ecf20Sopenharmony_ci ssize_t pos; 13228c2ecf20Sopenharmony_ci 13238c2ecf20Sopenharmony_ci pos = scnprintf(buf, sizeof(buf), "%hhu\n", perf->tcnt); 13248c2ecf20Sopenharmony_ci 13258c2ecf20Sopenharmony_ci return simple_read_from_buffer(ubuf, size, offp, buf, pos); 13268c2ecf20Sopenharmony_ci} 13278c2ecf20Sopenharmony_ci 13288c2ecf20Sopenharmony_cistatic ssize_t perf_dbgfs_write_tcnt(struct file *filep, 13298c2ecf20Sopenharmony_ci const char __user *ubuf, 13308c2ecf20Sopenharmony_ci size_t size, loff_t *offp) 13318c2ecf20Sopenharmony_ci{ 13328c2ecf20Sopenharmony_ci struct perf_ctx *perf = filep->private_data; 13338c2ecf20Sopenharmony_ci int ret; 13348c2ecf20Sopenharmony_ci u8 val; 13358c2ecf20Sopenharmony_ci 13368c2ecf20Sopenharmony_ci ret = kstrtou8_from_user(ubuf, size, 0, &val); 13378c2ecf20Sopenharmony_ci if (ret) 13388c2ecf20Sopenharmony_ci return ret; 13398c2ecf20Sopenharmony_ci 13408c2ecf20Sopenharmony_ci ret = perf_set_tcnt(perf, val); 13418c2ecf20Sopenharmony_ci if (ret) 13428c2ecf20Sopenharmony_ci return ret; 13438c2ecf20Sopenharmony_ci 13448c2ecf20Sopenharmony_ci return size; 13458c2ecf20Sopenharmony_ci} 13468c2ecf20Sopenharmony_ci 13478c2ecf20Sopenharmony_cistatic const struct file_operations perf_dbgfs_tcnt = { 13488c2ecf20Sopenharmony_ci .open = simple_open, 13498c2ecf20Sopenharmony_ci .read = perf_dbgfs_read_tcnt, 13508c2ecf20Sopenharmony_ci .write = perf_dbgfs_write_tcnt 13518c2ecf20Sopenharmony_ci}; 13528c2ecf20Sopenharmony_ci 13538c2ecf20Sopenharmony_cistatic void perf_setup_dbgfs(struct perf_ctx *perf) 13548c2ecf20Sopenharmony_ci{ 13558c2ecf20Sopenharmony_ci struct pci_dev *pdev = perf->ntb->pdev; 13568c2ecf20Sopenharmony_ci 13578c2ecf20Sopenharmony_ci perf->dbgfs_dir = debugfs_create_dir(pci_name(pdev), perf_dbgfs_topdir); 13588c2ecf20Sopenharmony_ci if (!perf->dbgfs_dir) { 13598c2ecf20Sopenharmony_ci dev_warn(&perf->ntb->dev, "DebugFS unsupported\n"); 13608c2ecf20Sopenharmony_ci return; 13618c2ecf20Sopenharmony_ci } 13628c2ecf20Sopenharmony_ci 13638c2ecf20Sopenharmony_ci debugfs_create_file("info", 0600, perf->dbgfs_dir, perf, 13648c2ecf20Sopenharmony_ci &perf_dbgfs_info); 13658c2ecf20Sopenharmony_ci 13668c2ecf20Sopenharmony_ci debugfs_create_file("run", 0600, perf->dbgfs_dir, perf, 13678c2ecf20Sopenharmony_ci &perf_dbgfs_run); 13688c2ecf20Sopenharmony_ci 13698c2ecf20Sopenharmony_ci debugfs_create_file("threads_count", 0600, perf->dbgfs_dir, perf, 13708c2ecf20Sopenharmony_ci &perf_dbgfs_tcnt); 13718c2ecf20Sopenharmony_ci 13728c2ecf20Sopenharmony_ci /* They are made read-only for test exec safety and integrity */ 13738c2ecf20Sopenharmony_ci debugfs_create_u8("chunk_order", 0500, perf->dbgfs_dir, &chunk_order); 13748c2ecf20Sopenharmony_ci 13758c2ecf20Sopenharmony_ci debugfs_create_u8("total_order", 0500, perf->dbgfs_dir, &total_order); 13768c2ecf20Sopenharmony_ci 13778c2ecf20Sopenharmony_ci debugfs_create_bool("use_dma", 0500, perf->dbgfs_dir, &use_dma); 13788c2ecf20Sopenharmony_ci} 13798c2ecf20Sopenharmony_ci 13808c2ecf20Sopenharmony_cistatic void perf_clear_dbgfs(struct perf_ctx *perf) 13818c2ecf20Sopenharmony_ci{ 13828c2ecf20Sopenharmony_ci debugfs_remove_recursive(perf->dbgfs_dir); 13838c2ecf20Sopenharmony_ci} 13848c2ecf20Sopenharmony_ci 13858c2ecf20Sopenharmony_ci/*============================================================================== 13868c2ecf20Sopenharmony_ci * Basic driver initialization 13878c2ecf20Sopenharmony_ci *============================================================================== 13888c2ecf20Sopenharmony_ci */ 13898c2ecf20Sopenharmony_ci 13908c2ecf20Sopenharmony_cistatic struct perf_ctx *perf_create_data(struct ntb_dev *ntb) 13918c2ecf20Sopenharmony_ci{ 13928c2ecf20Sopenharmony_ci struct perf_ctx *perf; 13938c2ecf20Sopenharmony_ci 13948c2ecf20Sopenharmony_ci perf = devm_kzalloc(&ntb->dev, sizeof(*perf), GFP_KERNEL); 13958c2ecf20Sopenharmony_ci if (!perf) 13968c2ecf20Sopenharmony_ci return ERR_PTR(-ENOMEM); 13978c2ecf20Sopenharmony_ci 13988c2ecf20Sopenharmony_ci perf->pcnt = ntb_peer_port_count(ntb); 13998c2ecf20Sopenharmony_ci perf->peers = devm_kcalloc(&ntb->dev, perf->pcnt, sizeof(*perf->peers), 14008c2ecf20Sopenharmony_ci GFP_KERNEL); 14018c2ecf20Sopenharmony_ci if (!perf->peers) 14028c2ecf20Sopenharmony_ci return ERR_PTR(-ENOMEM); 14038c2ecf20Sopenharmony_ci 14048c2ecf20Sopenharmony_ci perf->ntb = ntb; 14058c2ecf20Sopenharmony_ci 14068c2ecf20Sopenharmony_ci return perf; 14078c2ecf20Sopenharmony_ci} 14088c2ecf20Sopenharmony_ci 14098c2ecf20Sopenharmony_cistatic int perf_setup_peer_mw(struct perf_peer *peer) 14108c2ecf20Sopenharmony_ci{ 14118c2ecf20Sopenharmony_ci struct perf_ctx *perf = peer->perf; 14128c2ecf20Sopenharmony_ci phys_addr_t phys_addr; 14138c2ecf20Sopenharmony_ci int ret; 14148c2ecf20Sopenharmony_ci 14158c2ecf20Sopenharmony_ci /* Get outbound MW parameters and map it */ 14168c2ecf20Sopenharmony_ci ret = ntb_peer_mw_get_addr(perf->ntb, perf->gidx, &phys_addr, 14178c2ecf20Sopenharmony_ci &peer->outbuf_size); 14188c2ecf20Sopenharmony_ci if (ret) 14198c2ecf20Sopenharmony_ci return ret; 14208c2ecf20Sopenharmony_ci 14218c2ecf20Sopenharmony_ci peer->outbuf = devm_ioremap_wc(&perf->ntb->dev, phys_addr, 14228c2ecf20Sopenharmony_ci peer->outbuf_size); 14238c2ecf20Sopenharmony_ci if (!peer->outbuf) 14248c2ecf20Sopenharmony_ci return -ENOMEM; 14258c2ecf20Sopenharmony_ci 14268c2ecf20Sopenharmony_ci peer->out_phys_addr = phys_addr; 14278c2ecf20Sopenharmony_ci 14288c2ecf20Sopenharmony_ci if (max_mw_size && peer->outbuf_size > max_mw_size) { 14298c2ecf20Sopenharmony_ci peer->outbuf_size = max_mw_size; 14308c2ecf20Sopenharmony_ci dev_warn(&peer->perf->ntb->dev, 14318c2ecf20Sopenharmony_ci "Peer %d outbuf reduced to %pa\n", peer->pidx, 14328c2ecf20Sopenharmony_ci &peer->outbuf_size); 14338c2ecf20Sopenharmony_ci } 14348c2ecf20Sopenharmony_ci 14358c2ecf20Sopenharmony_ci return 0; 14368c2ecf20Sopenharmony_ci} 14378c2ecf20Sopenharmony_ci 14388c2ecf20Sopenharmony_cistatic int perf_init_peers(struct perf_ctx *perf) 14398c2ecf20Sopenharmony_ci{ 14408c2ecf20Sopenharmony_ci struct perf_peer *peer; 14418c2ecf20Sopenharmony_ci int pidx, lport, ret; 14428c2ecf20Sopenharmony_ci 14438c2ecf20Sopenharmony_ci lport = ntb_port_number(perf->ntb); 14448c2ecf20Sopenharmony_ci perf->gidx = -1; 14458c2ecf20Sopenharmony_ci for (pidx = 0; pidx < perf->pcnt; pidx++) { 14468c2ecf20Sopenharmony_ci peer = &perf->peers[pidx]; 14478c2ecf20Sopenharmony_ci 14488c2ecf20Sopenharmony_ci peer->perf = perf; 14498c2ecf20Sopenharmony_ci peer->pidx = pidx; 14508c2ecf20Sopenharmony_ci if (lport < ntb_peer_port_number(perf->ntb, pidx)) { 14518c2ecf20Sopenharmony_ci if (perf->gidx == -1) 14528c2ecf20Sopenharmony_ci perf->gidx = pidx; 14538c2ecf20Sopenharmony_ci peer->gidx = pidx + 1; 14548c2ecf20Sopenharmony_ci } else { 14558c2ecf20Sopenharmony_ci peer->gidx = pidx; 14568c2ecf20Sopenharmony_ci } 14578c2ecf20Sopenharmony_ci INIT_WORK(&peer->service, perf_service_work); 14588c2ecf20Sopenharmony_ci init_completion(&peer->init_comp); 14598c2ecf20Sopenharmony_ci } 14608c2ecf20Sopenharmony_ci if (perf->gidx == -1) 14618c2ecf20Sopenharmony_ci perf->gidx = pidx; 14628c2ecf20Sopenharmony_ci 14638c2ecf20Sopenharmony_ci /* 14648c2ecf20Sopenharmony_ci * Hardware with only two ports may not have unique port 14658c2ecf20Sopenharmony_ci * numbers. In this case, the gidxs should all be zero. 14668c2ecf20Sopenharmony_ci */ 14678c2ecf20Sopenharmony_ci if (perf->pcnt == 1 && ntb_port_number(perf->ntb) == 0 && 14688c2ecf20Sopenharmony_ci ntb_peer_port_number(perf->ntb, 0) == 0) { 14698c2ecf20Sopenharmony_ci perf->gidx = 0; 14708c2ecf20Sopenharmony_ci perf->peers[0].gidx = 0; 14718c2ecf20Sopenharmony_ci } 14728c2ecf20Sopenharmony_ci 14738c2ecf20Sopenharmony_ci for (pidx = 0; pidx < perf->pcnt; pidx++) { 14748c2ecf20Sopenharmony_ci ret = perf_setup_peer_mw(&perf->peers[pidx]); 14758c2ecf20Sopenharmony_ci if (ret) 14768c2ecf20Sopenharmony_ci return ret; 14778c2ecf20Sopenharmony_ci } 14788c2ecf20Sopenharmony_ci 14798c2ecf20Sopenharmony_ci dev_dbg(&perf->ntb->dev, "Global port index %d\n", perf->gidx); 14808c2ecf20Sopenharmony_ci 14818c2ecf20Sopenharmony_ci return 0; 14828c2ecf20Sopenharmony_ci} 14838c2ecf20Sopenharmony_ci 14848c2ecf20Sopenharmony_cistatic int perf_probe(struct ntb_client *client, struct ntb_dev *ntb) 14858c2ecf20Sopenharmony_ci{ 14868c2ecf20Sopenharmony_ci struct perf_ctx *perf; 14878c2ecf20Sopenharmony_ci int ret; 14888c2ecf20Sopenharmony_ci 14898c2ecf20Sopenharmony_ci perf = perf_create_data(ntb); 14908c2ecf20Sopenharmony_ci if (IS_ERR(perf)) 14918c2ecf20Sopenharmony_ci return PTR_ERR(perf); 14928c2ecf20Sopenharmony_ci 14938c2ecf20Sopenharmony_ci ret = perf_init_peers(perf); 14948c2ecf20Sopenharmony_ci if (ret) 14958c2ecf20Sopenharmony_ci return ret; 14968c2ecf20Sopenharmony_ci 14978c2ecf20Sopenharmony_ci perf_init_threads(perf); 14988c2ecf20Sopenharmony_ci 14998c2ecf20Sopenharmony_ci ret = perf_init_service(perf); 15008c2ecf20Sopenharmony_ci if (ret) 15018c2ecf20Sopenharmony_ci return ret; 15028c2ecf20Sopenharmony_ci 15038c2ecf20Sopenharmony_ci ret = perf_enable_service(perf); 15048c2ecf20Sopenharmony_ci if (ret) 15058c2ecf20Sopenharmony_ci return ret; 15068c2ecf20Sopenharmony_ci 15078c2ecf20Sopenharmony_ci perf_setup_dbgfs(perf); 15088c2ecf20Sopenharmony_ci 15098c2ecf20Sopenharmony_ci return 0; 15108c2ecf20Sopenharmony_ci} 15118c2ecf20Sopenharmony_ci 15128c2ecf20Sopenharmony_cistatic void perf_remove(struct ntb_client *client, struct ntb_dev *ntb) 15138c2ecf20Sopenharmony_ci{ 15148c2ecf20Sopenharmony_ci struct perf_ctx *perf = ntb->ctx; 15158c2ecf20Sopenharmony_ci 15168c2ecf20Sopenharmony_ci perf_clear_dbgfs(perf); 15178c2ecf20Sopenharmony_ci 15188c2ecf20Sopenharmony_ci perf_disable_service(perf); 15198c2ecf20Sopenharmony_ci 15208c2ecf20Sopenharmony_ci perf_clear_threads(perf); 15218c2ecf20Sopenharmony_ci} 15228c2ecf20Sopenharmony_ci 15238c2ecf20Sopenharmony_cistatic struct ntb_client perf_client = { 15248c2ecf20Sopenharmony_ci .ops = { 15258c2ecf20Sopenharmony_ci .probe = perf_probe, 15268c2ecf20Sopenharmony_ci .remove = perf_remove 15278c2ecf20Sopenharmony_ci } 15288c2ecf20Sopenharmony_ci}; 15298c2ecf20Sopenharmony_ci 15308c2ecf20Sopenharmony_cistatic int __init perf_init(void) 15318c2ecf20Sopenharmony_ci{ 15328c2ecf20Sopenharmony_ci int ret; 15338c2ecf20Sopenharmony_ci 15348c2ecf20Sopenharmony_ci if (chunk_order > MAX_CHUNK_ORDER) { 15358c2ecf20Sopenharmony_ci chunk_order = MAX_CHUNK_ORDER; 15368c2ecf20Sopenharmony_ci pr_info("Chunk order reduced to %hhu\n", chunk_order); 15378c2ecf20Sopenharmony_ci } 15388c2ecf20Sopenharmony_ci 15398c2ecf20Sopenharmony_ci if (total_order < chunk_order) { 15408c2ecf20Sopenharmony_ci total_order = chunk_order; 15418c2ecf20Sopenharmony_ci pr_info("Total data order reduced to %hhu\n", total_order); 15428c2ecf20Sopenharmony_ci } 15438c2ecf20Sopenharmony_ci 15448c2ecf20Sopenharmony_ci perf_wq = alloc_workqueue("perf_wq", WQ_UNBOUND | WQ_SYSFS, 0); 15458c2ecf20Sopenharmony_ci if (!perf_wq) 15468c2ecf20Sopenharmony_ci return -ENOMEM; 15478c2ecf20Sopenharmony_ci 15488c2ecf20Sopenharmony_ci if (debugfs_initialized()) 15498c2ecf20Sopenharmony_ci perf_dbgfs_topdir = debugfs_create_dir(KBUILD_MODNAME, NULL); 15508c2ecf20Sopenharmony_ci 15518c2ecf20Sopenharmony_ci ret = ntb_register_client(&perf_client); 15528c2ecf20Sopenharmony_ci if (ret) { 15538c2ecf20Sopenharmony_ci debugfs_remove_recursive(perf_dbgfs_topdir); 15548c2ecf20Sopenharmony_ci destroy_workqueue(perf_wq); 15558c2ecf20Sopenharmony_ci } 15568c2ecf20Sopenharmony_ci 15578c2ecf20Sopenharmony_ci return ret; 15588c2ecf20Sopenharmony_ci} 15598c2ecf20Sopenharmony_cimodule_init(perf_init); 15608c2ecf20Sopenharmony_ci 15618c2ecf20Sopenharmony_cistatic void __exit perf_exit(void) 15628c2ecf20Sopenharmony_ci{ 15638c2ecf20Sopenharmony_ci ntb_unregister_client(&perf_client); 15648c2ecf20Sopenharmony_ci debugfs_remove_recursive(perf_dbgfs_topdir); 15658c2ecf20Sopenharmony_ci destroy_workqueue(perf_wq); 15668c2ecf20Sopenharmony_ci} 15678c2ecf20Sopenharmony_cimodule_exit(perf_exit); 1568