xref: /openbmc/linux/drivers/ntb/test/ntb_perf.c (revision 34d6f206a88c2651d216bd3487ac956a40b2ba8e)
18a7b6a77SDave Jiang /*
28a7b6a77SDave Jiang  * This file is provided under a dual BSD/GPLv2 license.  When using or
38a7b6a77SDave Jiang  *   redistributing this file, you may do so under either license.
48a7b6a77SDave Jiang  *
58a7b6a77SDave Jiang  *   GPL LICENSE SUMMARY
68a7b6a77SDave Jiang  *
78a7b6a77SDave Jiang  *   Copyright(c) 2015 Intel Corporation. All rights reserved.
85648e56dSSerge Semin  *   Copyright(c) 2017 T-Platforms. All Rights Reserved.
98a7b6a77SDave Jiang  *
108a7b6a77SDave Jiang  *   This program is free software; you can redistribute it and/or modify
118a7b6a77SDave Jiang  *   it under the terms of version 2 of the GNU General Public License as
128a7b6a77SDave Jiang  *   published by the Free Software Foundation.
138a7b6a77SDave Jiang  *
148a7b6a77SDave Jiang  *   BSD LICENSE
158a7b6a77SDave Jiang  *
168a7b6a77SDave Jiang  *   Copyright(c) 2015 Intel Corporation. All rights reserved.
175648e56dSSerge Semin  *   Copyright(c) 2017 T-Platforms. All Rights Reserved.
188a7b6a77SDave Jiang  *
198a7b6a77SDave Jiang  *   Redistribution and use in source and binary forms, with or without
208a7b6a77SDave Jiang  *   modification, are permitted provided that the following conditions
218a7b6a77SDave Jiang  *   are met:
228a7b6a77SDave Jiang  *
238a7b6a77SDave Jiang  *     * Redistributions of source code must retain the above copyright
248a7b6a77SDave Jiang  *       notice, this list of conditions and the following disclaimer.
258a7b6a77SDave Jiang  *     * Redistributions in binary form must reproduce the above copy
268a7b6a77SDave Jiang  *       notice, this list of conditions and the following disclaimer in
278a7b6a77SDave Jiang  *       the documentation and/or other materials provided with the
288a7b6a77SDave Jiang  *       distribution.
298a7b6a77SDave Jiang  *     * Neither the name of Intel Corporation nor the names of its
308a7b6a77SDave Jiang  *       contributors may be used to endorse or promote products derived
318a7b6a77SDave Jiang  *       from this software without specific prior written permission.
328a7b6a77SDave Jiang  *
338a7b6a77SDave Jiang  *   THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
348a7b6a77SDave Jiang  *   "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
358a7b6a77SDave Jiang  *   LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
368a7b6a77SDave Jiang  *   A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
378a7b6a77SDave Jiang  *   OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
388a7b6a77SDave Jiang  *   SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
398a7b6a77SDave Jiang  *   LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
408a7b6a77SDave Jiang  *   DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
418a7b6a77SDave Jiang  *   THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
428a7b6a77SDave Jiang  *   (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
438a7b6a77SDave Jiang  *   OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
448a7b6a77SDave Jiang  *
458a7b6a77SDave Jiang  * PCIe NTB Perf Linux driver
468a7b6a77SDave Jiang  */
478a7b6a77SDave Jiang 
485648e56dSSerge Semin /*
495648e56dSSerge Semin  * How to use this tool, by example.
505648e56dSSerge Semin  *
515648e56dSSerge Semin  * Assuming $DBG_DIR is something like:
525648e56dSSerge Semin  * '/sys/kernel/debug/ntb_perf/0000:00:03.0'
535648e56dSSerge Semin  * Suppose aside from local device there is at least one remote device
545648e56dSSerge Semin  * connected to NTB with index 0.
555648e56dSSerge Semin  *-----------------------------------------------------------------------------
565648e56dSSerge Semin  * Eg: install driver with specified chunk/total orders and dma-enabled flag
575648e56dSSerge Semin  *
585648e56dSSerge Semin  * root@self# insmod ntb_perf.ko chunk_order=19 total_order=28 use_dma
595648e56dSSerge Semin  *-----------------------------------------------------------------------------
605648e56dSSerge Semin  * Eg: check NTB ports (index) and MW mapping information
615648e56dSSerge Semin  *
625648e56dSSerge Semin  * root@self# cat $DBG_DIR/info
635648e56dSSerge Semin  *-----------------------------------------------------------------------------
645648e56dSSerge Semin  * Eg: start performance test with peer (index 0) and get the test metrics
655648e56dSSerge Semin  *
665648e56dSSerge Semin  * root@self# echo 0 > $DBG_DIR/run
675648e56dSSerge Semin  * root@self# cat $DBG_DIR/run
685648e56dSSerge Semin  */
695648e56dSSerge Semin 
708a7b6a77SDave Jiang #include <linux/init.h>
718a7b6a77SDave Jiang #include <linux/kernel.h>
728a7b6a77SDave Jiang #include <linux/module.h>
735648e56dSSerge Semin #include <linux/sched.h>
745648e56dSSerge Semin #include <linux/wait.h>
758a7b6a77SDave Jiang #include <linux/dma-mapping.h>
768a7b6a77SDave Jiang #include <linux/dmaengine.h>
775648e56dSSerge Semin #include <linux/pci.h>
785648e56dSSerge Semin #include <linux/ktime.h>
795648e56dSSerge Semin #include <linux/slab.h>
808a7b6a77SDave Jiang #include <linux/delay.h>
818a7b6a77SDave Jiang #include <linux/sizes.h>
825648e56dSSerge Semin #include <linux/workqueue.h>
835648e56dSSerge Semin #include <linux/debugfs.h>
845648e56dSSerge Semin #include <linux/random.h>
858a7b6a77SDave Jiang #include <linux/ntb.h>
868a7b6a77SDave Jiang 
878a7b6a77SDave Jiang #define DRIVER_NAME		"ntb_perf"
885648e56dSSerge Semin #define DRIVER_VERSION		"2.0"
898a7b6a77SDave Jiang 
900ed08f82SGreg Kroah-Hartman MODULE_LICENSE("Dual BSD/GPL");
918a7b6a77SDave Jiang MODULE_VERSION(DRIVER_VERSION);
925648e56dSSerge Semin MODULE_AUTHOR("Dave Jiang <dave.jiang@intel.com>");
935648e56dSSerge Semin MODULE_DESCRIPTION("PCIe NTB Performance Measurement Tool");
948a7b6a77SDave Jiang 
955648e56dSSerge Semin #define MAX_THREADS_CNT		32
965648e56dSSerge Semin #define DEF_THREADS_CNT		1
975648e56dSSerge Semin #define MAX_CHUNK_SIZE		SZ_1M
985648e56dSSerge Semin #define MAX_CHUNK_ORDER		20 /* no larger than 1M */
995648e56dSSerge Semin 
1005648e56dSSerge Semin #define DMA_TRIES		100
1015648e56dSSerge Semin #define DMA_MDELAY		10
1025648e56dSSerge Semin 
1038b2f0336SSanjay R Mehta #define MSG_TRIES		1000
104d7699665SSanjay R Mehta #define MSG_UDELAY_LOW		1000000
105d7699665SSanjay R Mehta #define MSG_UDELAY_HIGH		2000000
1065648e56dSSerge Semin 
1075648e56dSSerge Semin #define PERF_BUF_LEN 1024
1088a7b6a77SDave Jiang 
1094aae9777SLogan Gunthorpe static unsigned long max_mw_size;
1104aae9777SLogan Gunthorpe module_param(max_mw_size, ulong, 0644);
1115648e56dSSerge Semin MODULE_PARM_DESC(max_mw_size, "Upper limit of memory window size");
1124aae9777SLogan Gunthorpe 
1135648e56dSSerge Semin static unsigned char chunk_order = 19; /* 512K */
1145648e56dSSerge Semin module_param(chunk_order, byte, 0644);
1155648e56dSSerge Semin MODULE_PARM_DESC(chunk_order, "Data chunk order [2^n] to transfer");
1168a7b6a77SDave Jiang 
1175648e56dSSerge Semin static unsigned char total_order = 30; /* 1G */
1185648e56dSSerge Semin module_param(total_order, byte, 0644);
1195648e56dSSerge Semin MODULE_PARM_DESC(total_order, "Total data order [2^n] to transfer");
1208a7b6a77SDave Jiang 
1218a7b6a77SDave Jiang static bool use_dma; /* default to 0 */
1228a7b6a77SDave Jiang module_param(use_dma, bool, 0644);
1235648e56dSSerge Semin MODULE_PARM_DESC(use_dma, "Use DMA engine to measure performance");
1248a7b6a77SDave Jiang 
1255648e56dSSerge Semin /*==============================================================================
1265648e56dSSerge Semin  *                         Perf driver data definition
1275648e56dSSerge Semin  *==============================================================================
1285648e56dSSerge Semin  */
129e9410ff8SGary R Hook 
1305648e56dSSerge Semin enum perf_cmd {
1315648e56dSSerge Semin 	PERF_CMD_INVAL = -1,/* invalid spad command */
1325648e56dSSerge Semin 	PERF_CMD_SSIZE = 0, /* send out buffer size */
1335648e56dSSerge Semin 	PERF_CMD_RSIZE = 1, /* recv in  buffer size */
1345648e56dSSerge Semin 	PERF_CMD_SXLAT = 2, /* send in  buffer xlat */
1355648e56dSSerge Semin 	PERF_CMD_RXLAT = 3, /* recv out buffer xlat */
1365648e56dSSerge Semin 	PERF_CMD_CLEAR = 4, /* clear allocated memory */
1375648e56dSSerge Semin 	PERF_STS_DONE  = 5, /* init is done */
1385648e56dSSerge Semin 	PERF_STS_LNKUP = 6, /* link up state flag */
1398a7b6a77SDave Jiang };
1408a7b6a77SDave Jiang 
1418a7b6a77SDave Jiang struct perf_ctx;
1428a7b6a77SDave Jiang 
1435648e56dSSerge Semin struct perf_peer {
1448a7b6a77SDave Jiang 	struct perf_ctx	*perf;
1455648e56dSSerge Semin 	int pidx;
1465648e56dSSerge Semin 	int gidx;
1475648e56dSSerge Semin 
1485648e56dSSerge Semin 	/* Outbound MW params */
1495648e56dSSerge Semin 	u64 outbuf_xlat;
1505648e56dSSerge Semin 	resource_size_t outbuf_size;
1515648e56dSSerge Semin 	void __iomem *outbuf;
15299a06056SJiasen Lin 	phys_addr_t out_phys_addr;
15399a06056SJiasen Lin 	dma_addr_t dma_dst_addr;
1545648e56dSSerge Semin 	/* Inbound MW params */
1555648e56dSSerge Semin 	dma_addr_t inbuf_xlat;
1565648e56dSSerge Semin 	resource_size_t inbuf_size;
1575648e56dSSerge Semin 	void		*inbuf;
1585648e56dSSerge Semin 
1595648e56dSSerge Semin 	/* NTB connection setup service */
1605648e56dSSerge Semin 	struct work_struct	service;
1615648e56dSSerge Semin 	unsigned long		sts;
16234d8673aSLogan Gunthorpe 
16334d8673aSLogan Gunthorpe 	struct completion init_comp;
1648a7b6a77SDave Jiang };
1655648e56dSSerge Semin #define to_peer_service(__work) \
1665648e56dSSerge Semin 	container_of(__work, struct perf_peer, service)
1675648e56dSSerge Semin 
1685648e56dSSerge Semin struct perf_thread {
1695648e56dSSerge Semin 	struct perf_ctx *perf;
1705648e56dSSerge Semin 	int tidx;
1715648e56dSSerge Semin 
1725648e56dSSerge Semin 	/* DMA-based test sync parameters */
1735648e56dSSerge Semin 	atomic_t dma_sync;
1745648e56dSSerge Semin 	wait_queue_head_t dma_wait;
1755648e56dSSerge Semin 	struct dma_chan *dma_chan;
1765648e56dSSerge Semin 
1775648e56dSSerge Semin 	/* Data source and measured statistics */
1785648e56dSSerge Semin 	void *src;
1795648e56dSSerge Semin 	u64 copied;
1805648e56dSSerge Semin 	ktime_t duration;
1815648e56dSSerge Semin 	int status;
1825648e56dSSerge Semin 	struct work_struct work;
1835648e56dSSerge Semin };
1845648e56dSSerge Semin #define to_thread_work(__work) \
1855648e56dSSerge Semin 	container_of(__work, struct perf_thread, work)
1868a7b6a77SDave Jiang 
1878a7b6a77SDave Jiang struct perf_ctx {
1888a7b6a77SDave Jiang 	struct ntb_dev *ntb;
1895648e56dSSerge Semin 
1905648e56dSSerge Semin 	/* Global device index and peers descriptors */
1915648e56dSSerge Semin 	int gidx;
1925648e56dSSerge Semin 	int pcnt;
1935648e56dSSerge Semin 	struct perf_peer *peers;
1945648e56dSSerge Semin 
1955648e56dSSerge Semin 	/* Performance measuring work-threads interface */
1965648e56dSSerge Semin 	unsigned long busy_flag;
1975648e56dSSerge Semin 	wait_queue_head_t twait;
1988a7b6a77SDave Jiang 	atomic_t tsync;
1995648e56dSSerge Semin 	u8 tcnt;
2005648e56dSSerge Semin 	struct perf_peer *test_peer;
2015648e56dSSerge Semin 	struct perf_thread threads[MAX_THREADS_CNT];
2025648e56dSSerge Semin 
2035648e56dSSerge Semin 	/* Scratchpad/Message IO operations */
2045648e56dSSerge Semin 	int (*cmd_send)(struct perf_peer *peer, enum perf_cmd cmd, u64 data);
2055648e56dSSerge Semin 	int (*cmd_recv)(struct perf_ctx *perf, int *pidx, enum perf_cmd *cmd,
2065648e56dSSerge Semin 			u64 *data);
2075648e56dSSerge Semin 
2085648e56dSSerge Semin 	struct dentry *dbgfs_dir;
2098a7b6a77SDave Jiang };
2108a7b6a77SDave Jiang 
2115648e56dSSerge Semin /*
2125648e56dSSerge Semin  * Scratchpads-base commands interface
2135648e56dSSerge Semin  */
2145648e56dSSerge Semin #define PERF_SPAD_CNT(_pcnt) \
2155648e56dSSerge Semin 	(3*((_pcnt) + 1))
2165648e56dSSerge Semin #define PERF_SPAD_CMD(_gidx) \
2175648e56dSSerge Semin 	(3*(_gidx))
2185648e56dSSerge Semin #define PERF_SPAD_LDATA(_gidx) \
2195648e56dSSerge Semin 	(3*(_gidx) + 1)
2205648e56dSSerge Semin #define PERF_SPAD_HDATA(_gidx) \
2215648e56dSSerge Semin 	(3*(_gidx) + 2)
2225648e56dSSerge Semin #define PERF_SPAD_NOTIFY(_gidx) \
2235648e56dSSerge Semin 	(BIT_ULL(_gidx))
2245648e56dSSerge Semin 
2255648e56dSSerge Semin /*
2265648e56dSSerge Semin  * Messages-base commands interface
2275648e56dSSerge Semin  */
2285648e56dSSerge Semin #define PERF_MSG_CNT		3
2295648e56dSSerge Semin #define PERF_MSG_CMD		0
2305648e56dSSerge Semin #define PERF_MSG_LDATA		1
2315648e56dSSerge Semin #define PERF_MSG_HDATA		2
2325648e56dSSerge Semin 
2335648e56dSSerge Semin /*==============================================================================
2345648e56dSSerge Semin  *                           Static data declarations
2355648e56dSSerge Semin  *==============================================================================
2365648e56dSSerge Semin  */
2375648e56dSSerge Semin 
2385648e56dSSerge Semin static struct dentry *perf_dbgfs_topdir;
2395648e56dSSerge Semin 
2405648e56dSSerge Semin static struct workqueue_struct *perf_wq __read_mostly;
2415648e56dSSerge Semin 
2425648e56dSSerge Semin /*==============================================================================
2435648e56dSSerge Semin  *                  NTB cross-link commands execution service
2445648e56dSSerge Semin  *==============================================================================
2455648e56dSSerge Semin  */
2465648e56dSSerge Semin 
2475648e56dSSerge Semin static void perf_terminate_test(struct perf_ctx *perf);
2485648e56dSSerge Semin 
perf_link_is_up(struct perf_peer * peer)2495648e56dSSerge Semin static inline bool perf_link_is_up(struct perf_peer *peer)
2505648e56dSSerge Semin {
2515648e56dSSerge Semin 	u64 link;
2525648e56dSSerge Semin 
2535648e56dSSerge Semin 	link = ntb_link_is_up(peer->perf->ntb, NULL, NULL);
2545648e56dSSerge Semin 	return !!(link & BIT_ULL_MASK(peer->pidx));
2555648e56dSSerge Semin }
2565648e56dSSerge Semin 
perf_spad_cmd_send(struct perf_peer * peer,enum perf_cmd cmd,u64 data)2575648e56dSSerge Semin static int perf_spad_cmd_send(struct perf_peer *peer, enum perf_cmd cmd,
2585648e56dSSerge Semin 			      u64 data)
2595648e56dSSerge Semin {
2605648e56dSSerge Semin 	struct perf_ctx *perf = peer->perf;
2615648e56dSSerge Semin 	int try;
2625648e56dSSerge Semin 	u32 sts;
2635648e56dSSerge Semin 
2645648e56dSSerge Semin 	dev_dbg(&perf->ntb->dev, "CMD send: %d 0x%llx\n", cmd, data);
2655648e56dSSerge Semin 
2665648e56dSSerge Semin 	/*
2675648e56dSSerge Semin 	 * Perform predefined number of attempts before give up.
2685648e56dSSerge Semin 	 * We are sending the data to the port specific scratchpad, so
2695648e56dSSerge Semin 	 * to prevent a multi-port access race-condition. Additionally
2705648e56dSSerge Semin 	 * there is no need in local locking since only thread-safe
2715648e56dSSerge Semin 	 * service work is using this method.
2725648e56dSSerge Semin 	 */
2735648e56dSSerge Semin 	for (try = 0; try < MSG_TRIES; try++) {
2745648e56dSSerge Semin 		if (!perf_link_is_up(peer))
2755648e56dSSerge Semin 			return -ENOLINK;
2765648e56dSSerge Semin 
2775648e56dSSerge Semin 		sts = ntb_peer_spad_read(perf->ntb, peer->pidx,
2785648e56dSSerge Semin 					 PERF_SPAD_CMD(perf->gidx));
2793b28c987SSerge Semin 		if (sts != PERF_CMD_INVAL) {
2805648e56dSSerge Semin 			usleep_range(MSG_UDELAY_LOW, MSG_UDELAY_HIGH);
2815648e56dSSerge Semin 			continue;
2825648e56dSSerge Semin 		}
2835648e56dSSerge Semin 
2845648e56dSSerge Semin 		ntb_peer_spad_write(perf->ntb, peer->pidx,
2855648e56dSSerge Semin 				    PERF_SPAD_LDATA(perf->gidx),
2863b28c987SSerge Semin 				    lower_32_bits(data));
2875648e56dSSerge Semin 		ntb_peer_spad_write(perf->ntb, peer->pidx,
2885648e56dSSerge Semin 				    PERF_SPAD_HDATA(perf->gidx),
2893b28c987SSerge Semin 				    upper_32_bits(data));
2905648e56dSSerge Semin 		ntb_peer_spad_write(perf->ntb, peer->pidx,
2915648e56dSSerge Semin 				    PERF_SPAD_CMD(perf->gidx),
2923b28c987SSerge Semin 				    cmd);
2935648e56dSSerge Semin 		ntb_peer_db_set(perf->ntb, PERF_SPAD_NOTIFY(peer->gidx));
2945648e56dSSerge Semin 
2955648e56dSSerge Semin 		dev_dbg(&perf->ntb->dev, "DB ring peer %#llx\n",
2965648e56dSSerge Semin 			PERF_SPAD_NOTIFY(peer->gidx));
2975648e56dSSerge Semin 
2985648e56dSSerge Semin 		break;
2995648e56dSSerge Semin 	}
3005648e56dSSerge Semin 
3015648e56dSSerge Semin 	return try < MSG_TRIES ? 0 : -EAGAIN;
3025648e56dSSerge Semin }
3035648e56dSSerge Semin 
perf_spad_cmd_recv(struct perf_ctx * perf,int * pidx,enum perf_cmd * cmd,u64 * data)3045648e56dSSerge Semin static int perf_spad_cmd_recv(struct perf_ctx *perf, int *pidx,
3055648e56dSSerge Semin 			      enum perf_cmd *cmd, u64 *data)
3065648e56dSSerge Semin {
3075648e56dSSerge Semin 	struct perf_peer *peer;
3085648e56dSSerge Semin 	u32 val;
3095648e56dSSerge Semin 
3105648e56dSSerge Semin 	ntb_db_clear(perf->ntb, PERF_SPAD_NOTIFY(perf->gidx));
3115648e56dSSerge Semin 
3125648e56dSSerge Semin 	/*
3135648e56dSSerge Semin 	 * We start scanning all over, since cleared DB may have been set
3145648e56dSSerge Semin 	 * by any peer. Yes, it makes peer with smaller index being
3155648e56dSSerge Semin 	 * serviced with greater priority, but it's convenient for spad
3165648e56dSSerge Semin 	 * and message code unification and simplicity.
3175648e56dSSerge Semin 	 */
3185648e56dSSerge Semin 	for (*pidx = 0; *pidx < perf->pcnt; (*pidx)++) {
3195648e56dSSerge Semin 		peer = &perf->peers[*pidx];
3205648e56dSSerge Semin 
3215648e56dSSerge Semin 		if (!perf_link_is_up(peer))
3225648e56dSSerge Semin 			continue;
3235648e56dSSerge Semin 
3245648e56dSSerge Semin 		val = ntb_spad_read(perf->ntb, PERF_SPAD_CMD(peer->gidx));
3255648e56dSSerge Semin 		if (val == PERF_CMD_INVAL)
3265648e56dSSerge Semin 			continue;
3275648e56dSSerge Semin 
3285648e56dSSerge Semin 		*cmd = val;
3295648e56dSSerge Semin 
3305648e56dSSerge Semin 		val = ntb_spad_read(perf->ntb, PERF_SPAD_LDATA(peer->gidx));
3313b28c987SSerge Semin 		*data = val;
3325648e56dSSerge Semin 
3335648e56dSSerge Semin 		val = ntb_spad_read(perf->ntb, PERF_SPAD_HDATA(peer->gidx));
3343b28c987SSerge Semin 		*data |= (u64)val << 32;
3355648e56dSSerge Semin 
3365648e56dSSerge Semin 		/* Next command can be retrieved from now */
3375648e56dSSerge Semin 		ntb_spad_write(perf->ntb, PERF_SPAD_CMD(peer->gidx),
3383b28c987SSerge Semin 			       PERF_CMD_INVAL);
3395648e56dSSerge Semin 
3405648e56dSSerge Semin 		dev_dbg(&perf->ntb->dev, "CMD recv: %d 0x%llx\n", *cmd, *data);
3415648e56dSSerge Semin 
3425648e56dSSerge Semin 		return 0;
3435648e56dSSerge Semin 	}
3445648e56dSSerge Semin 
3455648e56dSSerge Semin 	return -ENODATA;
3465648e56dSSerge Semin }
3475648e56dSSerge Semin 
perf_msg_cmd_send(struct perf_peer * peer,enum perf_cmd cmd,u64 data)3485648e56dSSerge Semin static int perf_msg_cmd_send(struct perf_peer *peer, enum perf_cmd cmd,
3495648e56dSSerge Semin 			     u64 data)
3505648e56dSSerge Semin {
3515648e56dSSerge Semin 	struct perf_ctx *perf = peer->perf;
3525648e56dSSerge Semin 	int try, ret;
3535648e56dSSerge Semin 	u64 outbits;
3545648e56dSSerge Semin 
3555648e56dSSerge Semin 	dev_dbg(&perf->ntb->dev, "CMD send: %d 0x%llx\n", cmd, data);
3565648e56dSSerge Semin 
3575648e56dSSerge Semin 	/*
3585648e56dSSerge Semin 	 * Perform predefined number of attempts before give up. Message
3595648e56dSSerge Semin 	 * registers are free of race-condition problem when accessed
3605648e56dSSerge Semin 	 * from different ports, so we don't need splitting registers
3615648e56dSSerge Semin 	 * by global device index. We also won't have local locking,
3625648e56dSSerge Semin 	 * since the method is used from service work only.
3635648e56dSSerge Semin 	 */
3645648e56dSSerge Semin 	outbits = ntb_msg_outbits(perf->ntb);
3655648e56dSSerge Semin 	for (try = 0; try < MSG_TRIES; try++) {
3665648e56dSSerge Semin 		if (!perf_link_is_up(peer))
3675648e56dSSerge Semin 			return -ENOLINK;
3685648e56dSSerge Semin 
3695648e56dSSerge Semin 		ret = ntb_msg_clear_sts(perf->ntb, outbits);
3705648e56dSSerge Semin 		if (ret)
3715648e56dSSerge Semin 			return ret;
3725648e56dSSerge Semin 
3735648e56dSSerge Semin 		ntb_peer_msg_write(perf->ntb, peer->pidx, PERF_MSG_LDATA,
3743b28c987SSerge Semin 				   lower_32_bits(data));
3755648e56dSSerge Semin 
3765648e56dSSerge Semin 		if (ntb_msg_read_sts(perf->ntb) & outbits) {
3775648e56dSSerge Semin 			usleep_range(MSG_UDELAY_LOW, MSG_UDELAY_HIGH);
3785648e56dSSerge Semin 			continue;
3795648e56dSSerge Semin 		}
3805648e56dSSerge Semin 
3815648e56dSSerge Semin 		ntb_peer_msg_write(perf->ntb, peer->pidx, PERF_MSG_HDATA,
3823b28c987SSerge Semin 				   upper_32_bits(data));
3835648e56dSSerge Semin 
3845648e56dSSerge Semin 		/* This call shall trigger peer message event */
3853b28c987SSerge Semin 		ntb_peer_msg_write(perf->ntb, peer->pidx, PERF_MSG_CMD, cmd);
3865648e56dSSerge Semin 
3875648e56dSSerge Semin 		break;
3885648e56dSSerge Semin 	}
3895648e56dSSerge Semin 
3905648e56dSSerge Semin 	return try < MSG_TRIES ? 0 : -EAGAIN;
3915648e56dSSerge Semin }
3925648e56dSSerge Semin 
perf_msg_cmd_recv(struct perf_ctx * perf,int * pidx,enum perf_cmd * cmd,u64 * data)3935648e56dSSerge Semin static int perf_msg_cmd_recv(struct perf_ctx *perf, int *pidx,
3945648e56dSSerge Semin 			     enum perf_cmd *cmd, u64 *data)
3955648e56dSSerge Semin {
3965648e56dSSerge Semin 	u64 inbits;
3975648e56dSSerge Semin 	u32 val;
3985648e56dSSerge Semin 
3995648e56dSSerge Semin 	inbits = ntb_msg_inbits(perf->ntb);
4005648e56dSSerge Semin 
4015648e56dSSerge Semin 	if (hweight64(ntb_msg_read_sts(perf->ntb) & inbits) < 3)
4025648e56dSSerge Semin 		return -ENODATA;
4035648e56dSSerge Semin 
4045648e56dSSerge Semin 	val = ntb_msg_read(perf->ntb, pidx, PERF_MSG_CMD);
4053b28c987SSerge Semin 	*cmd = val;
4065648e56dSSerge Semin 
4075648e56dSSerge Semin 	val = ntb_msg_read(perf->ntb, pidx, PERF_MSG_LDATA);
4083b28c987SSerge Semin 	*data = val;
4095648e56dSSerge Semin 
4105648e56dSSerge Semin 	val = ntb_msg_read(perf->ntb, pidx, PERF_MSG_HDATA);
4113b28c987SSerge Semin 	*data |= (u64)val << 32;
4125648e56dSSerge Semin 
4135648e56dSSerge Semin 	/* Next command can be retrieved from now */
4145648e56dSSerge Semin 	ntb_msg_clear_sts(perf->ntb, inbits);
4155648e56dSSerge Semin 
4165648e56dSSerge Semin 	dev_dbg(&perf->ntb->dev, "CMD recv: %d 0x%llx\n", *cmd, *data);
4175648e56dSSerge Semin 
4185648e56dSSerge Semin 	return 0;
4195648e56dSSerge Semin }
4205648e56dSSerge Semin 
perf_cmd_send(struct perf_peer * peer,enum perf_cmd cmd,u64 data)4215648e56dSSerge Semin static int perf_cmd_send(struct perf_peer *peer, enum perf_cmd cmd, u64 data)
4225648e56dSSerge Semin {
4235648e56dSSerge Semin 	struct perf_ctx *perf = peer->perf;
4245648e56dSSerge Semin 
4255648e56dSSerge Semin 	if (cmd == PERF_CMD_SSIZE || cmd == PERF_CMD_SXLAT)
4265648e56dSSerge Semin 		return perf->cmd_send(peer, cmd, data);
4275648e56dSSerge Semin 
4285648e56dSSerge Semin 	dev_err(&perf->ntb->dev, "Send invalid command\n");
4295648e56dSSerge Semin 	return -EINVAL;
4305648e56dSSerge Semin }
4315648e56dSSerge Semin 
perf_cmd_exec(struct perf_peer * peer,enum perf_cmd cmd)4325648e56dSSerge Semin static int perf_cmd_exec(struct perf_peer *peer, enum perf_cmd cmd)
4335648e56dSSerge Semin {
4345648e56dSSerge Semin 	switch (cmd) {
4355648e56dSSerge Semin 	case PERF_CMD_SSIZE:
4365648e56dSSerge Semin 	case PERF_CMD_RSIZE:
4375648e56dSSerge Semin 	case PERF_CMD_SXLAT:
4385648e56dSSerge Semin 	case PERF_CMD_RXLAT:
4395648e56dSSerge Semin 	case PERF_CMD_CLEAR:
4405648e56dSSerge Semin 		break;
4415648e56dSSerge Semin 	default:
4425648e56dSSerge Semin 		dev_err(&peer->perf->ntb->dev, "Exec invalid command\n");
4435648e56dSSerge Semin 		return -EINVAL;
4445648e56dSSerge Semin 	}
4455648e56dSSerge Semin 
4465648e56dSSerge Semin 	/* No need of memory barrier, since bit ops have invernal lock */
4475648e56dSSerge Semin 	set_bit(cmd, &peer->sts);
4485648e56dSSerge Semin 
4495648e56dSSerge Semin 	dev_dbg(&peer->perf->ntb->dev, "CMD exec: %d\n", cmd);
4505648e56dSSerge Semin 
4515648e56dSSerge Semin 	(void)queue_work(system_highpri_wq, &peer->service);
4525648e56dSSerge Semin 
4535648e56dSSerge Semin 	return 0;
4545648e56dSSerge Semin }
4555648e56dSSerge Semin 
perf_cmd_recv(struct perf_ctx * perf)4565648e56dSSerge Semin static int perf_cmd_recv(struct perf_ctx *perf)
4575648e56dSSerge Semin {
4585648e56dSSerge Semin 	struct perf_peer *peer;
4595648e56dSSerge Semin 	int ret, pidx, cmd;
4605648e56dSSerge Semin 	u64 data;
4615648e56dSSerge Semin 
4625648e56dSSerge Semin 	while (!(ret = perf->cmd_recv(perf, &pidx, &cmd, &data))) {
4635648e56dSSerge Semin 		peer = &perf->peers[pidx];
4645648e56dSSerge Semin 
4655648e56dSSerge Semin 		switch (cmd) {
4665648e56dSSerge Semin 		case PERF_CMD_SSIZE:
4675648e56dSSerge Semin 			peer->inbuf_size = data;
4685648e56dSSerge Semin 			return perf_cmd_exec(peer, PERF_CMD_RSIZE);
4695648e56dSSerge Semin 		case PERF_CMD_SXLAT:
4705648e56dSSerge Semin 			peer->outbuf_xlat = data;
4715648e56dSSerge Semin 			return perf_cmd_exec(peer, PERF_CMD_RXLAT);
4725648e56dSSerge Semin 		default:
4735648e56dSSerge Semin 			dev_err(&perf->ntb->dev, "Recv invalid command\n");
4745648e56dSSerge Semin 			return -EINVAL;
4755648e56dSSerge Semin 		}
4765648e56dSSerge Semin 	}
4775648e56dSSerge Semin 
4785648e56dSSerge Semin 	/* Return 0 if no data left to process, otherwise an error */
4795648e56dSSerge Semin 	return ret == -ENODATA ? 0 : ret;
4805648e56dSSerge Semin }
4818a7b6a77SDave Jiang 
perf_link_event(void * ctx)4828a7b6a77SDave Jiang static void perf_link_event(void *ctx)
4838a7b6a77SDave Jiang {
4848a7b6a77SDave Jiang 	struct perf_ctx *perf = ctx;
4855648e56dSSerge Semin 	struct perf_peer *peer;
4865648e56dSSerge Semin 	bool lnk_up;
4875648e56dSSerge Semin 	int pidx;
4888a7b6a77SDave Jiang 
4895648e56dSSerge Semin 	for (pidx = 0; pidx < perf->pcnt; pidx++) {
4905648e56dSSerge Semin 		peer = &perf->peers[pidx];
49135539b54SLogan Gunthorpe 
4925648e56dSSerge Semin 		lnk_up = perf_link_is_up(peer);
49335539b54SLogan Gunthorpe 
4945648e56dSSerge Semin 		if (lnk_up &&
4955648e56dSSerge Semin 		    !test_and_set_bit(PERF_STS_LNKUP, &peer->sts)) {
4965648e56dSSerge Semin 			perf_cmd_exec(peer, PERF_CMD_SSIZE);
4975648e56dSSerge Semin 		} else if (!lnk_up &&
4985648e56dSSerge Semin 			   test_and_clear_bit(PERF_STS_LNKUP, &peer->sts)) {
4995648e56dSSerge Semin 			perf_cmd_exec(peer, PERF_CMD_CLEAR);
5005648e56dSSerge Semin 		}
50135539b54SLogan Gunthorpe 	}
5028a7b6a77SDave Jiang }
5038a7b6a77SDave Jiang 
perf_db_event(void * ctx,int vec)5048a7b6a77SDave Jiang static void perf_db_event(void *ctx, int vec)
5058a7b6a77SDave Jiang {
5068a7b6a77SDave Jiang 	struct perf_ctx *perf = ctx;
5078a7b6a77SDave Jiang 
5085648e56dSSerge Semin 	dev_dbg(&perf->ntb->dev, "DB vec %d mask %#llx bits %#llx\n", vec,
5095648e56dSSerge Semin 		ntb_db_vector_mask(perf->ntb, vec), ntb_db_read(perf->ntb));
5108a7b6a77SDave Jiang 
5115648e56dSSerge Semin 	/* Just receive all available commands */
5125648e56dSSerge Semin 	(void)perf_cmd_recv(perf);
5135648e56dSSerge Semin }
5145648e56dSSerge Semin 
perf_msg_event(void * ctx)5155648e56dSSerge Semin static void perf_msg_event(void *ctx)
5165648e56dSSerge Semin {
5175648e56dSSerge Semin 	struct perf_ctx *perf = ctx;
5185648e56dSSerge Semin 
5195648e56dSSerge Semin 	dev_dbg(&perf->ntb->dev, "Msg status bits %#llx\n",
5205648e56dSSerge Semin 		ntb_msg_read_sts(perf->ntb));
5215648e56dSSerge Semin 
5225648e56dSSerge Semin 	/* Messages are only sent one-by-one */
5235648e56dSSerge Semin 	(void)perf_cmd_recv(perf);
5248a7b6a77SDave Jiang }
5258a7b6a77SDave Jiang 
5268a7b6a77SDave Jiang static const struct ntb_ctx_ops perf_ops = {
5278a7b6a77SDave Jiang 	.link_event = perf_link_event,
5288a7b6a77SDave Jiang 	.db_event = perf_db_event,
5295648e56dSSerge Semin 	.msg_event = perf_msg_event
5308a7b6a77SDave Jiang };
5318a7b6a77SDave Jiang 
perf_free_outbuf(struct perf_peer * peer)5325648e56dSSerge Semin static void perf_free_outbuf(struct perf_peer *peer)
5338a7b6a77SDave Jiang {
5345648e56dSSerge Semin 	(void)ntb_peer_mw_clear_trans(peer->perf->ntb, peer->pidx, peer->gidx);
5358a7b6a77SDave Jiang }
5368a7b6a77SDave Jiang 
perf_setup_outbuf(struct perf_peer * peer)5375648e56dSSerge Semin static int perf_setup_outbuf(struct perf_peer *peer)
5388a7b6a77SDave Jiang {
5395648e56dSSerge Semin 	struct perf_ctx *perf = peer->perf;
5405648e56dSSerge Semin 	int ret;
5418a7b6a77SDave Jiang 
5425648e56dSSerge Semin 	/* Outbuf size can be unaligned due to custom max_mw_size */
5435648e56dSSerge Semin 	ret = ntb_peer_mw_set_trans(perf->ntb, peer->pidx, peer->gidx,
5445648e56dSSerge Semin 				    peer->outbuf_xlat, peer->outbuf_size);
5455648e56dSSerge Semin 	if (ret) {
5465648e56dSSerge Semin 		dev_err(&perf->ntb->dev, "Failed to set outbuf translation\n");
5475648e56dSSerge Semin 		return ret;
5488a7b6a77SDave Jiang 	}
5498a7b6a77SDave Jiang 
5505648e56dSSerge Semin 	/* Initialization is finally done */
5515648e56dSSerge Semin 	set_bit(PERF_STS_DONE, &peer->sts);
55234d8673aSLogan Gunthorpe 	complete_all(&peer->init_comp);
5535648e56dSSerge Semin 
5545648e56dSSerge Semin 	return 0;
5555648e56dSSerge Semin }
5565648e56dSSerge Semin 
perf_free_inbuf(struct perf_peer * peer)5575648e56dSSerge Semin static void perf_free_inbuf(struct perf_peer *peer)
5585648e56dSSerge Semin {
5595648e56dSSerge Semin 	if (!peer->inbuf)
5605648e56dSSerge Semin 		return;
5615648e56dSSerge Semin 
5625648e56dSSerge Semin 	(void)ntb_mw_clear_trans(peer->perf->ntb, peer->pidx, peer->gidx);
56398f4e140SSanjay R Mehta 	dma_free_coherent(&peer->perf->ntb->pdev->dev, peer->inbuf_size,
5645648e56dSSerge Semin 			  peer->inbuf, peer->inbuf_xlat);
5655648e56dSSerge Semin 	peer->inbuf = NULL;
5665648e56dSSerge Semin }
5675648e56dSSerge Semin 
perf_setup_inbuf(struct perf_peer * peer)5685648e56dSSerge Semin static int perf_setup_inbuf(struct perf_peer *peer)
5695648e56dSSerge Semin {
5705648e56dSSerge Semin 	resource_size_t xlat_align, size_align, size_max;
5715648e56dSSerge Semin 	struct perf_ctx *perf = peer->perf;
5725648e56dSSerge Semin 	int ret;
5735648e56dSSerge Semin 
5745648e56dSSerge Semin 	/* Get inbound MW parameters */
5755648e56dSSerge Semin 	ret = ntb_mw_get_align(perf->ntb, peer->pidx, perf->gidx,
5765648e56dSSerge Semin 			       &xlat_align, &size_align, &size_max);
5775648e56dSSerge Semin 	if (ret) {
5785648e56dSSerge Semin 		dev_err(&perf->ntb->dev, "Couldn't get inbuf restrictions\n");
5795648e56dSSerge Semin 		return ret;
5805648e56dSSerge Semin 	}
5815648e56dSSerge Semin 
5825648e56dSSerge Semin 	if (peer->inbuf_size > size_max) {
5835648e56dSSerge Semin 		dev_err(&perf->ntb->dev, "Too big inbuf size %pa > %pa\n",
5845648e56dSSerge Semin 			&peer->inbuf_size, &size_max);
5858a7b6a77SDave Jiang 		return -EINVAL;
5868a7b6a77SDave Jiang 	}
5878a7b6a77SDave Jiang 
5885648e56dSSerge Semin 	peer->inbuf_size = round_up(peer->inbuf_size, size_align);
5898a7b6a77SDave Jiang 
5905648e56dSSerge Semin 	perf_free_inbuf(peer);
5918a7b6a77SDave Jiang 
59298f4e140SSanjay R Mehta 	peer->inbuf = dma_alloc_coherent(&perf->ntb->pdev->dev,
59398f4e140SSanjay R Mehta 					 peer->inbuf_size, &peer->inbuf_xlat,
59498f4e140SSanjay R Mehta 					 GFP_KERNEL);
5955648e56dSSerge Semin 	if (!peer->inbuf) {
5965648e56dSSerge Semin 		dev_err(&perf->ntb->dev, "Failed to alloc inbuf of %pa\n",
5975648e56dSSerge Semin 			&peer->inbuf_size);
5985648e56dSSerge Semin 		return -ENOMEM;
5995648e56dSSerge Semin 	}
6005648e56dSSerge Semin 	if (!IS_ALIGNED(peer->inbuf_xlat, xlat_align)) {
6010097ae5fSYang Li 		ret = -EINVAL;
6025648e56dSSerge Semin 		dev_err(&perf->ntb->dev, "Unaligned inbuf allocated\n");
6035648e56dSSerge Semin 		goto err_free_inbuf;
6045648e56dSSerge Semin 	}
6058a7b6a77SDave Jiang 
6065648e56dSSerge Semin 	ret = ntb_mw_set_trans(perf->ntb, peer->pidx, peer->gidx,
6075648e56dSSerge Semin 			       peer->inbuf_xlat, peer->inbuf_size);
6085648e56dSSerge Semin 	if (ret) {
6095648e56dSSerge Semin 		dev_err(&perf->ntb->dev, "Failed to set inbuf translation\n");
6105648e56dSSerge Semin 		goto err_free_inbuf;
6115648e56dSSerge Semin 	}
6125648e56dSSerge Semin 
6135648e56dSSerge Semin 	/*
6145648e56dSSerge Semin 	 * We submit inbuf xlat transmission cmd for execution here to follow
6155648e56dSSerge Semin 	 * the code architecture, even though this method is called from service
6165648e56dSSerge Semin 	 * work itself so the command will be executed right after it returns.
6175648e56dSSerge Semin 	 */
6185648e56dSSerge Semin 	(void)perf_cmd_exec(peer, PERF_CMD_SXLAT);
6195648e56dSSerge Semin 
6205648e56dSSerge Semin 	return 0;
6215648e56dSSerge Semin 
6225648e56dSSerge Semin err_free_inbuf:
6235648e56dSSerge Semin 	perf_free_inbuf(peer);
6245648e56dSSerge Semin 
6255648e56dSSerge Semin 	return ret;
6265648e56dSSerge Semin }
6275648e56dSSerge Semin 
perf_service_work(struct work_struct * work)6285648e56dSSerge Semin static void perf_service_work(struct work_struct *work)
6295648e56dSSerge Semin {
6305648e56dSSerge Semin 	struct perf_peer *peer = to_peer_service(work);
6315648e56dSSerge Semin 
6325648e56dSSerge Semin 	if (test_and_clear_bit(PERF_CMD_SSIZE, &peer->sts))
6335648e56dSSerge Semin 		perf_cmd_send(peer, PERF_CMD_SSIZE, peer->outbuf_size);
6345648e56dSSerge Semin 
6355648e56dSSerge Semin 	if (test_and_clear_bit(PERF_CMD_RSIZE, &peer->sts))
6365648e56dSSerge Semin 		perf_setup_inbuf(peer);
6375648e56dSSerge Semin 
6385648e56dSSerge Semin 	if (test_and_clear_bit(PERF_CMD_SXLAT, &peer->sts))
6395648e56dSSerge Semin 		perf_cmd_send(peer, PERF_CMD_SXLAT, peer->inbuf_xlat);
6405648e56dSSerge Semin 
6415648e56dSSerge Semin 	if (test_and_clear_bit(PERF_CMD_RXLAT, &peer->sts))
6425648e56dSSerge Semin 		perf_setup_outbuf(peer);
6435648e56dSSerge Semin 
6445648e56dSSerge Semin 	if (test_and_clear_bit(PERF_CMD_CLEAR, &peer->sts)) {
64534d8673aSLogan Gunthorpe 		init_completion(&peer->init_comp);
6465648e56dSSerge Semin 		clear_bit(PERF_STS_DONE, &peer->sts);
6475648e56dSSerge Semin 		if (test_bit(0, &peer->perf->busy_flag) &&
6485648e56dSSerge Semin 		    peer == peer->perf->test_peer) {
6495648e56dSSerge Semin 			dev_warn(&peer->perf->ntb->dev,
6505648e56dSSerge Semin 				"Freeing while test on-fly\n");
6515648e56dSSerge Semin 			perf_terminate_test(peer->perf);
6525648e56dSSerge Semin 		}
6535648e56dSSerge Semin 		perf_free_outbuf(peer);
6545648e56dSSerge Semin 		perf_free_inbuf(peer);
6555648e56dSSerge Semin 	}
6565648e56dSSerge Semin }
6575648e56dSSerge Semin 
perf_init_service(struct perf_ctx * perf)6585648e56dSSerge Semin static int perf_init_service(struct perf_ctx *perf)
6595648e56dSSerge Semin {
6605648e56dSSerge Semin 	u64 mask;
6615648e56dSSerge Semin 
662a9c4211aSLogan Gunthorpe 	if (ntb_peer_mw_count(perf->ntb) < perf->pcnt) {
6635648e56dSSerge Semin 		dev_err(&perf->ntb->dev, "Not enough memory windows\n");
6645648e56dSSerge Semin 		return -EINVAL;
6655648e56dSSerge Semin 	}
6665648e56dSSerge Semin 
6675648e56dSSerge Semin 	if (ntb_msg_count(perf->ntb) >= PERF_MSG_CNT) {
6685648e56dSSerge Semin 		perf->cmd_send = perf_msg_cmd_send;
6695648e56dSSerge Semin 		perf->cmd_recv = perf_msg_cmd_recv;
6705648e56dSSerge Semin 
6715648e56dSSerge Semin 		dev_dbg(&perf->ntb->dev, "Message service initialized\n");
6725648e56dSSerge Semin 
6735648e56dSSerge Semin 		return 0;
6745648e56dSSerge Semin 	}
6755648e56dSSerge Semin 
6765648e56dSSerge Semin 	dev_dbg(&perf->ntb->dev, "Message service unsupported\n");
6775648e56dSSerge Semin 
6785648e56dSSerge Semin 	mask = GENMASK_ULL(perf->pcnt, 0);
6795648e56dSSerge Semin 	if (ntb_spad_count(perf->ntb) >= PERF_SPAD_CNT(perf->pcnt) &&
6805648e56dSSerge Semin 	    (ntb_db_valid_mask(perf->ntb) & mask) == mask) {
6815648e56dSSerge Semin 		perf->cmd_send = perf_spad_cmd_send;
6825648e56dSSerge Semin 		perf->cmd_recv = perf_spad_cmd_recv;
6835648e56dSSerge Semin 
6845648e56dSSerge Semin 		dev_dbg(&perf->ntb->dev, "Scratchpad service initialized\n");
6855648e56dSSerge Semin 
6865648e56dSSerge Semin 		return 0;
6875648e56dSSerge Semin 	}
6885648e56dSSerge Semin 
6895648e56dSSerge Semin 	dev_dbg(&perf->ntb->dev, "Scratchpad service unsupported\n");
6905648e56dSSerge Semin 
6915648e56dSSerge Semin 	dev_err(&perf->ntb->dev, "Command services unsupported\n");
6925648e56dSSerge Semin 
6935648e56dSSerge Semin 	return -EINVAL;
6945648e56dSSerge Semin }
6955648e56dSSerge Semin 
perf_enable_service(struct perf_ctx * perf)6965648e56dSSerge Semin static int perf_enable_service(struct perf_ctx *perf)
6975648e56dSSerge Semin {
6985648e56dSSerge Semin 	u64 mask, incmd_bit;
6995648e56dSSerge Semin 	int ret, sidx, scnt;
7005648e56dSSerge Semin 
7015648e56dSSerge Semin 	mask = ntb_db_valid_mask(perf->ntb);
7025648e56dSSerge Semin 	(void)ntb_db_set_mask(perf->ntb, mask);
7035648e56dSSerge Semin 
7045648e56dSSerge Semin 	ret = ntb_set_ctx(perf->ntb, perf, &perf_ops);
7055648e56dSSerge Semin 	if (ret)
7065648e56dSSerge Semin 		return ret;
7075648e56dSSerge Semin 
7085648e56dSSerge Semin 	if (perf->cmd_send == perf_msg_cmd_send) {
7095648e56dSSerge Semin 		u64 inbits, outbits;
7105648e56dSSerge Semin 
7115648e56dSSerge Semin 		inbits = ntb_msg_inbits(perf->ntb);
7125648e56dSSerge Semin 		outbits = ntb_msg_outbits(perf->ntb);
7135648e56dSSerge Semin 		(void)ntb_msg_set_mask(perf->ntb, inbits | outbits);
7145648e56dSSerge Semin 
7155648e56dSSerge Semin 		incmd_bit = BIT_ULL(__ffs64(inbits));
7165648e56dSSerge Semin 		ret = ntb_msg_clear_mask(perf->ntb, incmd_bit);
7175648e56dSSerge Semin 
7185648e56dSSerge Semin 		dev_dbg(&perf->ntb->dev, "MSG sts unmasked %#llx\n", incmd_bit);
7195648e56dSSerge Semin 	} else {
7205648e56dSSerge Semin 		scnt = ntb_spad_count(perf->ntb);
7215648e56dSSerge Semin 		for (sidx = 0; sidx < scnt; sidx++)
7225648e56dSSerge Semin 			ntb_spad_write(perf->ntb, sidx, PERF_CMD_INVAL);
7235648e56dSSerge Semin 		incmd_bit = PERF_SPAD_NOTIFY(perf->gidx);
7245648e56dSSerge Semin 		ret = ntb_db_clear_mask(perf->ntb, incmd_bit);
7255648e56dSSerge Semin 
7265648e56dSSerge Semin 		dev_dbg(&perf->ntb->dev, "DB bits unmasked %#llx\n", incmd_bit);
7275648e56dSSerge Semin 	}
7285648e56dSSerge Semin 	if (ret) {
7295648e56dSSerge Semin 		ntb_clear_ctx(perf->ntb);
7305648e56dSSerge Semin 		return ret;
7315648e56dSSerge Semin 	}
7325648e56dSSerge Semin 
7335648e56dSSerge Semin 	ntb_link_enable(perf->ntb, NTB_SPEED_AUTO, NTB_WIDTH_AUTO);
7345648e56dSSerge Semin 	/* Might be not necessary */
7355648e56dSSerge Semin 	ntb_link_event(perf->ntb);
7365648e56dSSerge Semin 
7375648e56dSSerge Semin 	return 0;
7385648e56dSSerge Semin }
7395648e56dSSerge Semin 
perf_disable_service(struct perf_ctx * perf)7405648e56dSSerge Semin static void perf_disable_service(struct perf_ctx *perf)
7415648e56dSSerge Semin {
7425648e56dSSerge Semin 	int pidx;
7435648e56dSSerge Semin 
7445648e56dSSerge Semin 	if (perf->cmd_send == perf_msg_cmd_send) {
7455648e56dSSerge Semin 		u64 inbits;
7465648e56dSSerge Semin 
7475648e56dSSerge Semin 		inbits = ntb_msg_inbits(perf->ntb);
7485648e56dSSerge Semin 		(void)ntb_msg_set_mask(perf->ntb, inbits);
7495648e56dSSerge Semin 	} else {
7505648e56dSSerge Semin 		(void)ntb_db_set_mask(perf->ntb, PERF_SPAD_NOTIFY(perf->gidx));
7515648e56dSSerge Semin 	}
7525648e56dSSerge Semin 
7535648e56dSSerge Semin 	ntb_clear_ctx(perf->ntb);
7545648e56dSSerge Semin 
7555648e56dSSerge Semin 	for (pidx = 0; pidx < perf->pcnt; pidx++)
7565648e56dSSerge Semin 		perf_cmd_exec(&perf->peers[pidx], PERF_CMD_CLEAR);
7575648e56dSSerge Semin 
7585648e56dSSerge Semin 	for (pidx = 0; pidx < perf->pcnt; pidx++)
7595648e56dSSerge Semin 		flush_work(&perf->peers[pidx].service);
760b1ee5998SSanjay R Mehta 
76112c023d7SSanjay R Mehta 	for (pidx = 0; pidx < perf->pcnt; pidx++) {
76212c023d7SSanjay R Mehta 		struct perf_peer *peer = &perf->peers[pidx];
76312c023d7SSanjay R Mehta 
76412c023d7SSanjay R Mehta 		ntb_spad_write(perf->ntb, PERF_SPAD_CMD(peer->gidx), 0);
76512c023d7SSanjay R Mehta 	}
76612c023d7SSanjay R Mehta 
76712c023d7SSanjay R Mehta 	ntb_db_clear(perf->ntb, PERF_SPAD_NOTIFY(perf->gidx));
76812c023d7SSanjay R Mehta 
769b1ee5998SSanjay R Mehta 	ntb_link_disable(perf->ntb);
7705648e56dSSerge Semin }
7715648e56dSSerge Semin 
7725648e56dSSerge Semin /*==============================================================================
7735648e56dSSerge Semin  *                      Performance measuring work-thread
7745648e56dSSerge Semin  *==============================================================================
7755648e56dSSerge Semin  */
7765648e56dSSerge Semin 
perf_dma_copy_callback(void * data)7775648e56dSSerge Semin static void perf_dma_copy_callback(void *data)
7785648e56dSSerge Semin {
7795648e56dSSerge Semin 	struct perf_thread *pthr = data;
7805648e56dSSerge Semin 
7815648e56dSSerge Semin 	atomic_dec(&pthr->dma_sync);
7825648e56dSSerge Semin 	wake_up(&pthr->dma_wait);
7835648e56dSSerge Semin }
7845648e56dSSerge Semin 
perf_copy_chunk(struct perf_thread * pthr,void __iomem * dst,void * src,size_t len)7855648e56dSSerge Semin static int perf_copy_chunk(struct perf_thread *pthr,
7865648e56dSSerge Semin 			   void __iomem *dst, void *src, size_t len)
7875648e56dSSerge Semin {
7885648e56dSSerge Semin 	struct dma_async_tx_descriptor *tx;
7895648e56dSSerge Semin 	struct dmaengine_unmap_data *unmap;
7905648e56dSSerge Semin 	struct device *dma_dev;
7915648e56dSSerge Semin 	int try = 0, ret = 0;
79299a06056SJiasen Lin 	struct perf_peer *peer = pthr->perf->test_peer;
79399a06056SJiasen Lin 	void __iomem *vbase;
79499a06056SJiasen Lin 	void __iomem *dst_vaddr;
79599a06056SJiasen Lin 	dma_addr_t dst_dma_addr;
7965648e56dSSerge Semin 
7975648e56dSSerge Semin 	if (!use_dma) {
7985648e56dSSerge Semin 		memcpy_toio(dst, src, len);
7995648e56dSSerge Semin 		goto ret_check_tsync;
8005648e56dSSerge Semin 	}
8015648e56dSSerge Semin 
8025648e56dSSerge Semin 	dma_dev = pthr->dma_chan->device->dev;
8035648e56dSSerge Semin 
8045648e56dSSerge Semin 	if (!is_dma_copy_aligned(pthr->dma_chan->device, offset_in_page(src),
8055648e56dSSerge Semin 				 offset_in_page(dst), len))
8065648e56dSSerge Semin 		return -EIO;
8075648e56dSSerge Semin 
80899a06056SJiasen Lin 	vbase = peer->outbuf;
80999a06056SJiasen Lin 	dst_vaddr = dst;
81099a06056SJiasen Lin 	dst_dma_addr = peer->dma_dst_addr + (dst_vaddr - vbase);
81199a06056SJiasen Lin 
8129cb8bfdfSSanjay R Mehta 	unmap = dmaengine_get_unmap_data(dma_dev, 1, GFP_NOWAIT);
8138a7b6a77SDave Jiang 	if (!unmap)
8148a7b6a77SDave Jiang 		return -ENOMEM;
8158a7b6a77SDave Jiang 
8165648e56dSSerge Semin 	unmap->len = len;
8175648e56dSSerge Semin 	unmap->addr[0] = dma_map_page(dma_dev, virt_to_page(src),
8185648e56dSSerge Semin 		offset_in_page(src), len, DMA_TO_DEVICE);
8195648e56dSSerge Semin 	if (dma_mapping_error(dma_dev, unmap->addr[0])) {
8205648e56dSSerge Semin 		ret = -EIO;
8215648e56dSSerge Semin 		goto err_free_resource;
8225648e56dSSerge Semin 	}
8238a7b6a77SDave Jiang 	unmap->to_cnt = 1;
8248a7b6a77SDave Jiang 
8258a7b6a77SDave Jiang 	do {
8269cb8bfdfSSanjay R Mehta 		tx = dmaengine_prep_dma_memcpy(pthr->dma_chan, dst_dma_addr,
8275648e56dSSerge Semin 			unmap->addr[0], len, DMA_PREP_INTERRUPT | DMA_CTRL_ACK);
8285648e56dSSerge Semin 		if (!tx)
8295648e56dSSerge Semin 			msleep(DMA_MDELAY);
8305648e56dSSerge Semin 	} while (!tx && (try++ < DMA_TRIES));
8318a7b6a77SDave Jiang 
8325648e56dSSerge Semin 	if (!tx) {
8335648e56dSSerge Semin 		ret = -EIO;
8345648e56dSSerge Semin 		goto err_free_resource;
8358a7b6a77SDave Jiang 	}
8368a7b6a77SDave Jiang 
8375648e56dSSerge Semin 	tx->callback = perf_dma_copy_callback;
8385648e56dSSerge Semin 	tx->callback_param = pthr;
8395648e56dSSerge Semin 	dma_set_unmap(tx, unmap);
8408a7b6a77SDave Jiang 
841cd20dc3cSDan Carpenter 	ret = dma_submit_error(dmaengine_submit(tx));
842cd20dc3cSDan Carpenter 	if (ret) {
8435648e56dSSerge Semin 		dmaengine_unmap_put(unmap);
8445648e56dSSerge Semin 		goto err_free_resource;
8455648e56dSSerge Semin 	}
8468a7b6a77SDave Jiang 
8479644347cSDave Jiang 	dmaengine_unmap_put(unmap);
8489644347cSDave Jiang 
8495648e56dSSerge Semin 	atomic_inc(&pthr->dma_sync);
8505648e56dSSerge Semin 	dma_async_issue_pending(pthr->dma_chan);
8518a7b6a77SDave Jiang 
8525648e56dSSerge Semin ret_check_tsync:
8535648e56dSSerge Semin 	return likely(atomic_read(&pthr->perf->tsync) > 0) ? 0 : -EINTR;
8548a7b6a77SDave Jiang 
8555648e56dSSerge Semin err_free_resource:
8568a7b6a77SDave Jiang 	dmaengine_unmap_put(unmap);
8575648e56dSSerge Semin 
8585648e56dSSerge Semin 	return ret;
8598a7b6a77SDave Jiang }
8608a7b6a77SDave Jiang 
perf_dma_filter(struct dma_chan * chan,void * data)8615648e56dSSerge Semin static bool perf_dma_filter(struct dma_chan *chan, void *data)
8628a7b6a77SDave Jiang {
8635648e56dSSerge Semin 	struct perf_ctx *perf = data;
8645648e56dSSerge Semin 	int node;
8658a7b6a77SDave Jiang 
8665648e56dSSerge Semin 	node = dev_to_node(&perf->ntb->dev);
8678a7b6a77SDave Jiang 
8685648e56dSSerge Semin 	return node == NUMA_NO_NODE || node == dev_to_node(chan->device->dev);
8698a7b6a77SDave Jiang }
870da573eaaSLogan Gunthorpe 
perf_init_test(struct perf_thread * pthr)8715648e56dSSerge Semin static int perf_init_test(struct perf_thread *pthr)
8728a7b6a77SDave Jiang {
8735648e56dSSerge Semin 	struct perf_ctx *perf = pthr->perf;
8748a7b6a77SDave Jiang 	dma_cap_mask_t dma_mask;
87599a06056SJiasen Lin 	struct perf_peer *peer = pthr->perf->test_peer;
8768a7b6a77SDave Jiang 
8775648e56dSSerge Semin 	pthr->src = kmalloc_node(perf->test_peer->outbuf_size, GFP_KERNEL,
8785648e56dSSerge Semin 				 dev_to_node(&perf->ntb->dev));
8795648e56dSSerge Semin 	if (!pthr->src)
8805648e56dSSerge Semin 		return -ENOMEM;
8815648e56dSSerge Semin 
8825648e56dSSerge Semin 	get_random_bytes(pthr->src, perf->test_peer->outbuf_size);
8835648e56dSSerge Semin 
8845648e56dSSerge Semin 	if (!use_dma)
8855648e56dSSerge Semin 		return 0;
8865648e56dSSerge Semin 
8878a7b6a77SDave Jiang 	dma_cap_zero(dma_mask);
8888a7b6a77SDave Jiang 	dma_cap_set(DMA_MEMCPY, dma_mask);
8895648e56dSSerge Semin 	pthr->dma_chan = dma_request_channel(dma_mask, perf_dma_filter, perf);
8905648e56dSSerge Semin 	if (!pthr->dma_chan) {
8915648e56dSSerge Semin 		dev_err(&perf->ntb->dev, "%d: Failed to get DMA channel\n",
8925648e56dSSerge Semin 			pthr->tidx);
89399a06056SJiasen Lin 		goto err_free;
89499a06056SJiasen Lin 	}
89599a06056SJiasen Lin 	peer->dma_dst_addr =
89699a06056SJiasen Lin 		dma_map_resource(pthr->dma_chan->device->dev,
89799a06056SJiasen Lin 				 peer->out_phys_addr, peer->outbuf_size,
89899a06056SJiasen Lin 				 DMA_FROM_DEVICE, 0);
89999a06056SJiasen Lin 	if (dma_mapping_error(pthr->dma_chan->device->dev,
90099a06056SJiasen Lin 			      peer->dma_dst_addr)) {
90199a06056SJiasen Lin 		dev_err(pthr->dma_chan->device->dev, "%d: Failed to map DMA addr\n",
90299a06056SJiasen Lin 			pthr->tidx);
90399a06056SJiasen Lin 		peer->dma_dst_addr = 0;
90499a06056SJiasen Lin 		dma_release_channel(pthr->dma_chan);
90599a06056SJiasen Lin 		goto err_free;
90699a06056SJiasen Lin 	}
90799a06056SJiasen Lin 	dev_dbg(pthr->dma_chan->device->dev, "%d: Map MMIO %pa to DMA addr %pad\n",
90899a06056SJiasen Lin 			pthr->tidx,
90999a06056SJiasen Lin 			&peer->out_phys_addr,
91099a06056SJiasen Lin 			&peer->dma_dst_addr);
91199a06056SJiasen Lin 
91299a06056SJiasen Lin 	atomic_set(&pthr->dma_sync, 0);
91399a06056SJiasen Lin 	return 0;
91499a06056SJiasen Lin 
91599a06056SJiasen Lin err_free:
9165648e56dSSerge Semin 	atomic_dec(&perf->tsync);
9175648e56dSSerge Semin 	wake_up(&perf->twait);
9185648e56dSSerge Semin 	kfree(pthr->src);
9198a7b6a77SDave Jiang 	return -ENODEV;
9208a7b6a77SDave Jiang }
9215648e56dSSerge Semin 
perf_run_test(struct perf_thread * pthr)9225648e56dSSerge Semin static int perf_run_test(struct perf_thread *pthr)
9235648e56dSSerge Semin {
9245648e56dSSerge Semin 	struct perf_peer *peer = pthr->perf->test_peer;
9255648e56dSSerge Semin 	struct perf_ctx *perf = pthr->perf;
9265648e56dSSerge Semin 	void __iomem *flt_dst, *bnd_dst;
9275648e56dSSerge Semin 	u64 total_size, chunk_size;
9285648e56dSSerge Semin 	void *flt_src;
9295648e56dSSerge Semin 	int ret = 0;
9305648e56dSSerge Semin 
9315648e56dSSerge Semin 	total_size = 1ULL << total_order;
9325648e56dSSerge Semin 	chunk_size = 1ULL << chunk_order;
9335648e56dSSerge Semin 	chunk_size = min_t(u64, peer->outbuf_size, chunk_size);
9345648e56dSSerge Semin 
9355648e56dSSerge Semin 	flt_src = pthr->src;
9365648e56dSSerge Semin 	bnd_dst = peer->outbuf + peer->outbuf_size;
9375648e56dSSerge Semin 	flt_dst = peer->outbuf;
9385648e56dSSerge Semin 
9395648e56dSSerge Semin 	pthr->duration = ktime_get();
9405648e56dSSerge Semin 
9415648e56dSSerge Semin 	/* Copied field is cleared on test launch stage */
9425648e56dSSerge Semin 	while (pthr->copied < total_size) {
9435648e56dSSerge Semin 		ret = perf_copy_chunk(pthr, flt_dst, flt_src, chunk_size);
9445648e56dSSerge Semin 		if (ret) {
9455648e56dSSerge Semin 			dev_err(&perf->ntb->dev, "%d: Got error %d on test\n",
9465648e56dSSerge Semin 				pthr->tidx, ret);
9475648e56dSSerge Semin 			return ret;
9488a7b6a77SDave Jiang 		}
9498a7b6a77SDave Jiang 
9505648e56dSSerge Semin 		pthr->copied += chunk_size;
9518a7b6a77SDave Jiang 
9525648e56dSSerge Semin 		flt_dst += chunk_size;
9535648e56dSSerge Semin 		flt_src += chunk_size;
9545648e56dSSerge Semin 		if (flt_dst >= bnd_dst || flt_dst < peer->outbuf) {
9555648e56dSSerge Semin 			flt_dst = peer->outbuf;
9565648e56dSSerge Semin 			flt_src = pthr->src;
9575648e56dSSerge Semin 		}
9588a7b6a77SDave Jiang 
9595648e56dSSerge Semin 		/* Give up CPU to give a chance for other threads to use it */
9608a7b6a77SDave Jiang 		schedule();
9615648e56dSSerge Semin 	}
9628a7b6a77SDave Jiang 
9635648e56dSSerge Semin 	return 0;
9645648e56dSSerge Semin }
9658a7b6a77SDave Jiang 
perf_sync_test(struct perf_thread * pthr)9665648e56dSSerge Semin static int perf_sync_test(struct perf_thread *pthr)
9675648e56dSSerge Semin {
9685648e56dSSerge Semin 	struct perf_ctx *perf = pthr->perf;
9698a7b6a77SDave Jiang 
9705648e56dSSerge Semin 	if (!use_dma)
9715648e56dSSerge Semin 		goto no_dma_ret;
9725648e56dSSerge Semin 
9735648e56dSSerge Semin 	wait_event(pthr->dma_wait,
9745648e56dSSerge Semin 		   (atomic_read(&pthr->dma_sync) == 0 ||
9755648e56dSSerge Semin 		    atomic_read(&perf->tsync) < 0));
9765648e56dSSerge Semin 
9775648e56dSSerge Semin 	if (atomic_read(&perf->tsync) < 0)
9785648e56dSSerge Semin 		return -EINTR;
9795648e56dSSerge Semin 
9805648e56dSSerge Semin no_dma_ret:
9815648e56dSSerge Semin 	pthr->duration = ktime_sub(ktime_get(), pthr->duration);
9825648e56dSSerge Semin 
9835648e56dSSerge Semin 	dev_dbg(&perf->ntb->dev, "%d: copied %llu bytes\n",
9845648e56dSSerge Semin 		pthr->tidx, pthr->copied);
9855648e56dSSerge Semin 
9865648e56dSSerge Semin 	dev_dbg(&perf->ntb->dev, "%d: lasted %llu usecs\n",
9875648e56dSSerge Semin 		pthr->tidx, ktime_to_us(pthr->duration));
9885648e56dSSerge Semin 
9895648e56dSSerge Semin 	dev_dbg(&perf->ntb->dev, "%d: %llu MBytes/s\n", pthr->tidx,
9905648e56dSSerge Semin 		div64_u64(pthr->copied, ktime_to_us(pthr->duration)));
9915648e56dSSerge Semin 
9925648e56dSSerge Semin 	return 0;
9935648e56dSSerge Semin }
9945648e56dSSerge Semin 
perf_clear_test(struct perf_thread * pthr)9955648e56dSSerge Semin static void perf_clear_test(struct perf_thread *pthr)
9965648e56dSSerge Semin {
9975648e56dSSerge Semin 	struct perf_ctx *perf = pthr->perf;
9985648e56dSSerge Semin 
9995648e56dSSerge Semin 	if (!use_dma)
10005648e56dSSerge Semin 		goto no_dma_notify;
10015648e56dSSerge Semin 
10025648e56dSSerge Semin 	/*
10035648e56dSSerge Semin 	 * If test finished without errors, termination isn't needed.
10045648e56dSSerge Semin 	 * We call it anyway just to be sure of the transfers completion.
10055648e56dSSerge Semin 	 */
10065648e56dSSerge Semin 	(void)dmaengine_terminate_sync(pthr->dma_chan);
100799a06056SJiasen Lin 	if (pthr->perf->test_peer->dma_dst_addr)
100899a06056SJiasen Lin 		dma_unmap_resource(pthr->dma_chan->device->dev,
100999a06056SJiasen Lin 				   pthr->perf->test_peer->dma_dst_addr,
101099a06056SJiasen Lin 				   pthr->perf->test_peer->outbuf_size,
101199a06056SJiasen Lin 				   DMA_FROM_DEVICE, 0);
1012a0348a4dSJiasen Lin 
10135648e56dSSerge Semin 	dma_release_channel(pthr->dma_chan);
10145648e56dSSerge Semin 
10155648e56dSSerge Semin no_dma_notify:
10168a7b6a77SDave Jiang 	atomic_dec(&perf->tsync);
10175648e56dSSerge Semin 	wake_up(&perf->twait);
10185648e56dSSerge Semin 	kfree(pthr->src);
10198a7b6a77SDave Jiang }
10208a7b6a77SDave Jiang 
perf_thread_work(struct work_struct * work)10215648e56dSSerge Semin static void perf_thread_work(struct work_struct *work)
10228a7b6a77SDave Jiang {
10235648e56dSSerge Semin 	struct perf_thread *pthr = to_thread_work(work);
10245648e56dSSerge Semin 	int ret;
10258a7b6a77SDave Jiang 
10265648e56dSSerge Semin 	/*
10275648e56dSSerge Semin 	 * Perform stages in compliance with use_dma flag value.
10285648e56dSSerge Semin 	 * Test status is changed only if error happened, otherwise
10295648e56dSSerge Semin 	 * status -ENODATA is kept while test is on-fly. Results
10305648e56dSSerge Semin 	 * synchronization is performed only if test fininshed
10315648e56dSSerge Semin 	 * without an error or interruption.
10325648e56dSSerge Semin 	 */
10335648e56dSSerge Semin 	ret = perf_init_test(pthr);
10345648e56dSSerge Semin 	if (ret) {
10355648e56dSSerge Semin 		pthr->status = ret;
10368a7b6a77SDave Jiang 		return;
10378a7b6a77SDave Jiang 	}
10388a7b6a77SDave Jiang 
10395648e56dSSerge Semin 	ret = perf_run_test(pthr);
10405648e56dSSerge Semin 	if (ret) {
10415648e56dSSerge Semin 		pthr->status = ret;
10425648e56dSSerge Semin 		goto err_clear_test;
10435648e56dSSerge Semin 	}
10448a7b6a77SDave Jiang 
10455648e56dSSerge Semin 	pthr->status = perf_sync_test(pthr);
10465648e56dSSerge Semin 
10475648e56dSSerge Semin err_clear_test:
10485648e56dSSerge Semin 	perf_clear_test(pthr);
10495648e56dSSerge Semin }
10505648e56dSSerge Semin 
perf_set_tcnt(struct perf_ctx * perf,u8 tcnt)10515648e56dSSerge Semin static int perf_set_tcnt(struct perf_ctx *perf, u8 tcnt)
10525648e56dSSerge Semin {
10535648e56dSSerge Semin 	if (tcnt == 0 || tcnt > MAX_THREADS_CNT)
10548a7b6a77SDave Jiang 		return -EINVAL;
10558a7b6a77SDave Jiang 
10565648e56dSSerge Semin 	if (test_and_set_bit_lock(0, &perf->busy_flag))
10575648e56dSSerge Semin 		return -EBUSY;
1058980c41c8SLogan Gunthorpe 
10595648e56dSSerge Semin 	perf->tcnt = tcnt;
10608a7b6a77SDave Jiang 
10615648e56dSSerge Semin 	clear_bit_unlock(0, &perf->busy_flag);
1062ee5f750fSDave Jiang 
10638a7b6a77SDave Jiang 	return 0;
10648a7b6a77SDave Jiang }
10658a7b6a77SDave Jiang 
perf_terminate_test(struct perf_ctx * perf)10665648e56dSSerge Semin static void perf_terminate_test(struct perf_ctx *perf)
10678a7b6a77SDave Jiang {
10685648e56dSSerge Semin 	int tidx;
10698a7b6a77SDave Jiang 
10705648e56dSSerge Semin 	atomic_set(&perf->tsync, -1);
10715648e56dSSerge Semin 	wake_up(&perf->twait);
10728a7b6a77SDave Jiang 
10735648e56dSSerge Semin 	for (tidx = 0; tidx < MAX_THREADS_CNT; tidx++) {
10745648e56dSSerge Semin 		wake_up(&perf->threads[tidx].dma_wait);
10755648e56dSSerge Semin 		cancel_work_sync(&perf->threads[tidx].work);
10765648e56dSSerge Semin 	}
10778a7b6a77SDave Jiang }
10788a7b6a77SDave Jiang 
perf_submit_test(struct perf_peer * peer)10795648e56dSSerge Semin static int perf_submit_test(struct perf_peer *peer)
10808a7b6a77SDave Jiang {
10815648e56dSSerge Semin 	struct perf_ctx *perf = peer->perf;
10825648e56dSSerge Semin 	struct perf_thread *pthr;
10835648e56dSSerge Semin 	int tidx, ret;
10848a7b6a77SDave Jiang 
108534d8673aSLogan Gunthorpe 	ret = wait_for_completion_interruptible(&peer->init_comp);
108634d8673aSLogan Gunthorpe 	if (ret < 0)
108734d8673aSLogan Gunthorpe 		return ret;
10888a7b6a77SDave Jiang 
10895648e56dSSerge Semin 	if (test_and_set_bit_lock(0, &perf->busy_flag))
10905648e56dSSerge Semin 		return -EBUSY;
10918a7b6a77SDave Jiang 
10925648e56dSSerge Semin 	perf->test_peer = peer;
10935648e56dSSerge Semin 	atomic_set(&perf->tsync, perf->tcnt);
10948a7b6a77SDave Jiang 
10955648e56dSSerge Semin 	for (tidx = 0; tidx < MAX_THREADS_CNT; tidx++) {
10965648e56dSSerge Semin 		pthr = &perf->threads[tidx];
10975648e56dSSerge Semin 
10985648e56dSSerge Semin 		pthr->status = -ENODATA;
10995648e56dSSerge Semin 		pthr->copied = 0;
11005648e56dSSerge Semin 		pthr->duration = ktime_set(0, 0);
11015648e56dSSerge Semin 		if (tidx < perf->tcnt)
11025648e56dSSerge Semin 			(void)queue_work(perf_wq, &pthr->work);
11038a7b6a77SDave Jiang 	}
11048a7b6a77SDave Jiang 
11055648e56dSSerge Semin 	ret = wait_event_interruptible(perf->twait,
11065648e56dSSerge Semin 				       atomic_read(&perf->tsync) <= 0);
11075648e56dSSerge Semin 	if (ret == -ERESTARTSYS) {
11085648e56dSSerge Semin 		perf_terminate_test(perf);
11095648e56dSSerge Semin 		ret = -EINTR;
11105648e56dSSerge Semin 	}
11115648e56dSSerge Semin 
11125648e56dSSerge Semin 	clear_bit_unlock(0, &perf->busy_flag);
11135648e56dSSerge Semin 
11145648e56dSSerge Semin 	return ret;
11155648e56dSSerge Semin }
11165648e56dSSerge Semin 
perf_read_stats(struct perf_ctx * perf,char * buf,size_t size,ssize_t * pos)11175648e56dSSerge Semin static int perf_read_stats(struct perf_ctx *perf, char *buf,
11185648e56dSSerge Semin 			   size_t size, ssize_t *pos)
11198a7b6a77SDave Jiang {
11205648e56dSSerge Semin 	struct perf_thread *pthr;
11215648e56dSSerge Semin 	int tidx;
11228a7b6a77SDave Jiang 
11235648e56dSSerge Semin 	if (test_and_set_bit_lock(0, &perf->busy_flag))
11245648e56dSSerge Semin 		return -EBUSY;
11258a7b6a77SDave Jiang 
11265648e56dSSerge Semin 	(*pos) += scnprintf(buf + *pos, size - *pos,
11275648e56dSSerge Semin 		"    Peer %d test statistics:\n", perf->test_peer->pidx);
1128da573eaaSLogan Gunthorpe 
11295648e56dSSerge Semin 	for (tidx = 0; tidx < MAX_THREADS_CNT; tidx++) {
11305648e56dSSerge Semin 		pthr = &perf->threads[tidx];
113158fd0f3bSLogan Gunthorpe 
11325648e56dSSerge Semin 		if (pthr->status == -ENODATA)
11335648e56dSSerge Semin 			continue;
113458fd0f3bSLogan Gunthorpe 
11355648e56dSSerge Semin 		if (pthr->status) {
11365648e56dSSerge Semin 			(*pos) += scnprintf(buf + *pos, size - *pos,
11375648e56dSSerge Semin 				"%d: error status %d\n", tidx, pthr->status);
113858fd0f3bSLogan Gunthorpe 			continue;
113958fd0f3bSLogan Gunthorpe 		}
114058fd0f3bSLogan Gunthorpe 
11415648e56dSSerge Semin 		(*pos) += scnprintf(buf + *pos, size - *pos,
114258fd0f3bSLogan Gunthorpe 			"%d: copied %llu bytes in %llu usecs, %llu MBytes/s\n",
11435648e56dSSerge Semin 			tidx, pthr->copied, ktime_to_us(pthr->duration),
11445648e56dSSerge Semin 			div64_u64(pthr->copied, ktime_to_us(pthr->duration)));
114558fd0f3bSLogan Gunthorpe 	}
114658fd0f3bSLogan Gunthorpe 
11475648e56dSSerge Semin 	clear_bit_unlock(0, &perf->busy_flag);
11485648e56dSSerge Semin 
11495648e56dSSerge Semin 	return 0;
11505648e56dSSerge Semin }
11515648e56dSSerge Semin 
perf_init_threads(struct perf_ctx * perf)11525648e56dSSerge Semin static void perf_init_threads(struct perf_ctx *perf)
11535648e56dSSerge Semin {
11545648e56dSSerge Semin 	struct perf_thread *pthr;
11555648e56dSSerge Semin 	int tidx;
11565648e56dSSerge Semin 
11575648e56dSSerge Semin 	perf->tcnt = DEF_THREADS_CNT;
11585648e56dSSerge Semin 	perf->test_peer = &perf->peers[0];
11595648e56dSSerge Semin 	init_waitqueue_head(&perf->twait);
11605648e56dSSerge Semin 
11615648e56dSSerge Semin 	for (tidx = 0; tidx < MAX_THREADS_CNT; tidx++) {
11625648e56dSSerge Semin 		pthr = &perf->threads[tidx];
11635648e56dSSerge Semin 
11645648e56dSSerge Semin 		pthr->perf = perf;
11655648e56dSSerge Semin 		pthr->tidx = tidx;
11665648e56dSSerge Semin 		pthr->status = -ENODATA;
11675648e56dSSerge Semin 		init_waitqueue_head(&pthr->dma_wait);
11685648e56dSSerge Semin 		INIT_WORK(&pthr->work, perf_thread_work);
11695648e56dSSerge Semin 	}
11705648e56dSSerge Semin }
11715648e56dSSerge Semin 
perf_clear_threads(struct perf_ctx * perf)11725648e56dSSerge Semin static void perf_clear_threads(struct perf_ctx *perf)
11735648e56dSSerge Semin {
11745648e56dSSerge Semin 	perf_terminate_test(perf);
11755648e56dSSerge Semin }
11765648e56dSSerge Semin 
11775648e56dSSerge Semin /*==============================================================================
11785648e56dSSerge Semin  *                               DebugFS nodes
11795648e56dSSerge Semin  *==============================================================================
11805648e56dSSerge Semin  */
11815648e56dSSerge Semin 
perf_dbgfs_read_info(struct file * filep,char __user * ubuf,size_t size,loff_t * offp)11825648e56dSSerge Semin static ssize_t perf_dbgfs_read_info(struct file *filep, char __user *ubuf,
11835648e56dSSerge Semin 				    size_t size, loff_t *offp)
11845648e56dSSerge Semin {
11855648e56dSSerge Semin 	struct perf_ctx *perf = filep->private_data;
11865648e56dSSerge Semin 	struct perf_peer *peer;
11875648e56dSSerge Semin 	size_t buf_size;
11885648e56dSSerge Semin 	ssize_t pos = 0;
11895648e56dSSerge Semin 	int ret, pidx;
11905648e56dSSerge Semin 	char *buf;
11915648e56dSSerge Semin 
11925648e56dSSerge Semin 	buf_size = min_t(size_t, size, 0x1000U);
11935648e56dSSerge Semin 
11945648e56dSSerge Semin 	buf = kmalloc(buf_size, GFP_KERNEL);
11955648e56dSSerge Semin 	if (!buf)
11965648e56dSSerge Semin 		return -ENOMEM;
11975648e56dSSerge Semin 
11985648e56dSSerge Semin 	pos += scnprintf(buf + pos, buf_size - pos,
11995648e56dSSerge Semin 		"    Performance measuring tool info:\n\n");
12005648e56dSSerge Semin 
12015648e56dSSerge Semin 	pos += scnprintf(buf + pos, buf_size - pos,
12025648e56dSSerge Semin 		"Local port %d, Global index %d\n", ntb_port_number(perf->ntb),
12035648e56dSSerge Semin 		perf->gidx);
12045648e56dSSerge Semin 	pos += scnprintf(buf + pos, buf_size - pos, "Test status: ");
12055648e56dSSerge Semin 	if (test_bit(0, &perf->busy_flag)) {
12065648e56dSSerge Semin 		pos += scnprintf(buf + pos, buf_size - pos,
12075648e56dSSerge Semin 			"on-fly with port %d (%d)\n",
12085648e56dSSerge Semin 			ntb_peer_port_number(perf->ntb, perf->test_peer->pidx),
12095648e56dSSerge Semin 			perf->test_peer->pidx);
12105648e56dSSerge Semin 	} else {
12115648e56dSSerge Semin 		pos += scnprintf(buf + pos, buf_size - pos, "idle\n");
12125648e56dSSerge Semin 	}
12135648e56dSSerge Semin 
12145648e56dSSerge Semin 	for (pidx = 0; pidx < perf->pcnt; pidx++) {
12155648e56dSSerge Semin 		peer = &perf->peers[pidx];
12165648e56dSSerge Semin 
12175648e56dSSerge Semin 		pos += scnprintf(buf + pos, buf_size - pos,
12185648e56dSSerge Semin 			"Port %d (%d), Global index %d:\n",
12195648e56dSSerge Semin 			ntb_peer_port_number(perf->ntb, peer->pidx), peer->pidx,
12205648e56dSSerge Semin 			peer->gidx);
12215648e56dSSerge Semin 
12225648e56dSSerge Semin 		pos += scnprintf(buf + pos, buf_size - pos,
12235648e56dSSerge Semin 			"\tLink status: %s\n",
12245648e56dSSerge Semin 			test_bit(PERF_STS_LNKUP, &peer->sts) ? "up" : "down");
12255648e56dSSerge Semin 
12265648e56dSSerge Semin 		pos += scnprintf(buf + pos, buf_size - pos,
12275648e56dSSerge Semin 			"\tOut buffer addr 0x%pK\n", peer->outbuf);
12285648e56dSSerge Semin 
12295648e56dSSerge Semin 		pos += scnprintf(buf + pos, buf_size - pos,
1230*fd8932cfSMax Hawking 			"\tOut buff phys addr %pap\n", &peer->out_phys_addr);
123199a06056SJiasen Lin 
123299a06056SJiasen Lin 		pos += scnprintf(buf + pos, buf_size - pos,
12335648e56dSSerge Semin 			"\tOut buffer size %pa\n", &peer->outbuf_size);
12345648e56dSSerge Semin 
12355648e56dSSerge Semin 		pos += scnprintf(buf + pos, buf_size - pos,
12365648e56dSSerge Semin 			"\tOut buffer xlat 0x%016llx[p]\n", peer->outbuf_xlat);
12375648e56dSSerge Semin 
12385648e56dSSerge Semin 		if (!peer->inbuf) {
12395648e56dSSerge Semin 			pos += scnprintf(buf + pos, buf_size - pos,
12405648e56dSSerge Semin 				"\tIn buffer addr: unallocated\n");
12415648e56dSSerge Semin 			continue;
12425648e56dSSerge Semin 		}
12435648e56dSSerge Semin 
12445648e56dSSerge Semin 		pos += scnprintf(buf + pos, buf_size - pos,
12455648e56dSSerge Semin 			"\tIn buffer addr 0x%pK\n", peer->inbuf);
12465648e56dSSerge Semin 
12475648e56dSSerge Semin 		pos += scnprintf(buf + pos, buf_size - pos,
12485648e56dSSerge Semin 			"\tIn buffer size %pa\n", &peer->inbuf_size);
12495648e56dSSerge Semin 
12505648e56dSSerge Semin 		pos += scnprintf(buf + pos, buf_size - pos,
12515648e56dSSerge Semin 			"\tIn buffer xlat %pad[p]\n", &peer->inbuf_xlat);
12525648e56dSSerge Semin 	}
12535648e56dSSerge Semin 
12545648e56dSSerge Semin 	ret = simple_read_from_buffer(ubuf, size, offp, buf, pos);
12558a7b6a77SDave Jiang 	kfree(buf);
12568a7b6a77SDave Jiang 
12578a7b6a77SDave Jiang 	return ret;
12588a7b6a77SDave Jiang }
12598a7b6a77SDave Jiang 
12605648e56dSSerge Semin static const struct file_operations perf_dbgfs_info = {
12618a7b6a77SDave Jiang 	.open = simple_open,
12625648e56dSSerge Semin 	.read = perf_dbgfs_read_info
12638a7b6a77SDave Jiang };
12648a7b6a77SDave Jiang 
perf_dbgfs_read_run(struct file * filep,char __user * ubuf,size_t size,loff_t * offp)12655648e56dSSerge Semin static ssize_t perf_dbgfs_read_run(struct file *filep, char __user *ubuf,
12665648e56dSSerge Semin 				   size_t size, loff_t *offp)
12678a7b6a77SDave Jiang {
12685648e56dSSerge Semin 	struct perf_ctx *perf = filep->private_data;
12695648e56dSSerge Semin 	ssize_t ret, pos = 0;
12705648e56dSSerge Semin 	char *buf;
12718a7b6a77SDave Jiang 
12725648e56dSSerge Semin 	buf = kmalloc(PERF_BUF_LEN, GFP_KERNEL);
12735648e56dSSerge Semin 	if (!buf)
12745648e56dSSerge Semin 		return -ENOMEM;
12758a7b6a77SDave Jiang 
12765648e56dSSerge Semin 	ret = perf_read_stats(perf, buf, PERF_BUF_LEN, &pos);
12775648e56dSSerge Semin 	if (ret)
12785648e56dSSerge Semin 		goto err_free;
12795648e56dSSerge Semin 
12805648e56dSSerge Semin 	ret = simple_read_from_buffer(ubuf, size, offp, buf, pos);
12815648e56dSSerge Semin err_free:
12825648e56dSSerge Semin 	kfree(buf);
12835648e56dSSerge Semin 
12845648e56dSSerge Semin 	return ret;
12858a7b6a77SDave Jiang }
12868a7b6a77SDave Jiang 
perf_dbgfs_write_run(struct file * filep,const char __user * ubuf,size_t size,loff_t * offp)12875648e56dSSerge Semin static ssize_t perf_dbgfs_write_run(struct file *filep, const char __user *ubuf,
12885648e56dSSerge Semin 				    size_t size, loff_t *offp)
12895648e56dSSerge Semin {
12905648e56dSSerge Semin 	struct perf_ctx *perf = filep->private_data;
12915648e56dSSerge Semin 	struct perf_peer *peer;
12925648e56dSSerge Semin 	int pidx, ret;
12938a7b6a77SDave Jiang 
12945648e56dSSerge Semin 	ret = kstrtoint_from_user(ubuf, size, 0, &pidx);
12955648e56dSSerge Semin 	if (ret)
12965648e56dSSerge Semin 		return ret;
12978a7b6a77SDave Jiang 
12985648e56dSSerge Semin 	if (pidx < 0 || pidx >= perf->pcnt)
12995648e56dSSerge Semin 		return -EINVAL;
13008a7b6a77SDave Jiang 
13015648e56dSSerge Semin 	peer = &perf->peers[pidx];
13028407dd6cSGary R Hook 
13035648e56dSSerge Semin 	ret = perf_submit_test(peer);
13045648e56dSSerge Semin 	if (ret)
13055648e56dSSerge Semin 		return ret;
13068407dd6cSGary R Hook 
13075648e56dSSerge Semin 	return size;
13085648e56dSSerge Semin }
13098407dd6cSGary R Hook 
13105648e56dSSerge Semin static const struct file_operations perf_dbgfs_run = {
13115648e56dSSerge Semin 	.open = simple_open,
13125648e56dSSerge Semin 	.read = perf_dbgfs_read_run,
13135648e56dSSerge Semin 	.write = perf_dbgfs_write_run
13145648e56dSSerge Semin };
13155648e56dSSerge Semin 
perf_dbgfs_read_tcnt(struct file * filep,char __user * ubuf,size_t size,loff_t * offp)13165648e56dSSerge Semin static ssize_t perf_dbgfs_read_tcnt(struct file *filep, char __user *ubuf,
13175648e56dSSerge Semin 				    size_t size, loff_t *offp)
13185648e56dSSerge Semin {
13195648e56dSSerge Semin 	struct perf_ctx *perf = filep->private_data;
13205648e56dSSerge Semin 	char buf[8];
13215648e56dSSerge Semin 	ssize_t pos;
13225648e56dSSerge Semin 
13235648e56dSSerge Semin 	pos = scnprintf(buf, sizeof(buf), "%hhu\n", perf->tcnt);
13245648e56dSSerge Semin 
13255648e56dSSerge Semin 	return simple_read_from_buffer(ubuf, size, offp, buf, pos);
13265648e56dSSerge Semin }
13275648e56dSSerge Semin 
perf_dbgfs_write_tcnt(struct file * filep,const char __user * ubuf,size_t size,loff_t * offp)13285648e56dSSerge Semin static ssize_t perf_dbgfs_write_tcnt(struct file *filep,
13295648e56dSSerge Semin 				     const char __user *ubuf,
13305648e56dSSerge Semin 				     size_t size, loff_t *offp)
13315648e56dSSerge Semin {
13325648e56dSSerge Semin 	struct perf_ctx *perf = filep->private_data;
13335648e56dSSerge Semin 	int ret;
13345648e56dSSerge Semin 	u8 val;
13355648e56dSSerge Semin 
13365648e56dSSerge Semin 	ret = kstrtou8_from_user(ubuf, size, 0, &val);
13375648e56dSSerge Semin 	if (ret)
13385648e56dSSerge Semin 		return ret;
13395648e56dSSerge Semin 
13405648e56dSSerge Semin 	ret = perf_set_tcnt(perf, val);
13415648e56dSSerge Semin 	if (ret)
13425648e56dSSerge Semin 		return ret;
13435648e56dSSerge Semin 
13445648e56dSSerge Semin 	return size;
13455648e56dSSerge Semin }
13465648e56dSSerge Semin 
13475648e56dSSerge Semin static const struct file_operations perf_dbgfs_tcnt = {
13485648e56dSSerge Semin 	.open = simple_open,
13495648e56dSSerge Semin 	.read = perf_dbgfs_read_tcnt,
13505648e56dSSerge Semin 	.write = perf_dbgfs_write_tcnt
13515648e56dSSerge Semin };
13525648e56dSSerge Semin 
perf_setup_dbgfs(struct perf_ctx * perf)13535648e56dSSerge Semin static void perf_setup_dbgfs(struct perf_ctx *perf)
13545648e56dSSerge Semin {
13555648e56dSSerge Semin 	struct pci_dev *pdev = perf->ntb->pdev;
13565648e56dSSerge Semin 
13575648e56dSSerge Semin 	perf->dbgfs_dir = debugfs_create_dir(pci_name(pdev), perf_dbgfs_topdir);
135845191087SMinjie Du 	if (IS_ERR(perf->dbgfs_dir)) {
13595648e56dSSerge Semin 		dev_warn(&perf->ntb->dev, "DebugFS unsupported\n");
13605648e56dSSerge Semin 		return;
13615648e56dSSerge Semin 	}
13625648e56dSSerge Semin 
13635648e56dSSerge Semin 	debugfs_create_file("info", 0600, perf->dbgfs_dir, perf,
13645648e56dSSerge Semin 			    &perf_dbgfs_info);
13655648e56dSSerge Semin 
13665648e56dSSerge Semin 	debugfs_create_file("run", 0600, perf->dbgfs_dir, perf,
13675648e56dSSerge Semin 			    &perf_dbgfs_run);
13685648e56dSSerge Semin 
13695648e56dSSerge Semin 	debugfs_create_file("threads_count", 0600, perf->dbgfs_dir, perf,
13705648e56dSSerge Semin 			    &perf_dbgfs_tcnt);
13715648e56dSSerge Semin 
13725648e56dSSerge Semin 	/* They are made read-only for test exec safety and integrity */
13735648e56dSSerge Semin 	debugfs_create_u8("chunk_order", 0500, perf->dbgfs_dir, &chunk_order);
13745648e56dSSerge Semin 
13755648e56dSSerge Semin 	debugfs_create_u8("total_order", 0500, perf->dbgfs_dir, &total_order);
13765648e56dSSerge Semin 
13775648e56dSSerge Semin 	debugfs_create_bool("use_dma", 0500, perf->dbgfs_dir, &use_dma);
13785648e56dSSerge Semin }
13795648e56dSSerge Semin 
perf_clear_dbgfs(struct perf_ctx * perf)13805648e56dSSerge Semin static void perf_clear_dbgfs(struct perf_ctx *perf)
13815648e56dSSerge Semin {
13825648e56dSSerge Semin 	debugfs_remove_recursive(perf->dbgfs_dir);
13835648e56dSSerge Semin }
13845648e56dSSerge Semin 
13855648e56dSSerge Semin /*==============================================================================
13865648e56dSSerge Semin  *                        Basic driver initialization
13875648e56dSSerge Semin  *==============================================================================
13885648e56dSSerge Semin  */
13895648e56dSSerge Semin 
perf_create_data(struct ntb_dev * ntb)13905648e56dSSerge Semin static struct perf_ctx *perf_create_data(struct ntb_dev *ntb)
13915648e56dSSerge Semin {
13925648e56dSSerge Semin 	struct perf_ctx *perf;
13935648e56dSSerge Semin 
13945648e56dSSerge Semin 	perf = devm_kzalloc(&ntb->dev, sizeof(*perf), GFP_KERNEL);
13955648e56dSSerge Semin 	if (!perf)
13965648e56dSSerge Semin 		return ERR_PTR(-ENOMEM);
13975648e56dSSerge Semin 
13985648e56dSSerge Semin 	perf->pcnt = ntb_peer_port_count(ntb);
13995648e56dSSerge Semin 	perf->peers = devm_kcalloc(&ntb->dev, perf->pcnt, sizeof(*perf->peers),
14005648e56dSSerge Semin 				  GFP_KERNEL);
14015648e56dSSerge Semin 	if (!perf->peers)
14025648e56dSSerge Semin 		return ERR_PTR(-ENOMEM);
14035648e56dSSerge Semin 
14045648e56dSSerge Semin 	perf->ntb = ntb;
14055648e56dSSerge Semin 
14065648e56dSSerge Semin 	return perf;
14075648e56dSSerge Semin }
14085648e56dSSerge Semin 
perf_setup_peer_mw(struct perf_peer * peer)14095648e56dSSerge Semin static int perf_setup_peer_mw(struct perf_peer *peer)
14105648e56dSSerge Semin {
14115648e56dSSerge Semin 	struct perf_ctx *perf = peer->perf;
14125648e56dSSerge Semin 	phys_addr_t phys_addr;
14135648e56dSSerge Semin 	int ret;
14145648e56dSSerge Semin 
14155648e56dSSerge Semin 	/* Get outbound MW parameters and map it */
1416ae89339bSSanjay R Mehta 	ret = ntb_peer_mw_get_addr(perf->ntb, perf->gidx, &phys_addr,
14175648e56dSSerge Semin 				   &peer->outbuf_size);
14185648e56dSSerge Semin 	if (ret)
14195648e56dSSerge Semin 		return ret;
14205648e56dSSerge Semin 
14215648e56dSSerge Semin 	peer->outbuf = devm_ioremap_wc(&perf->ntb->dev, phys_addr,
14225648e56dSSerge Semin 					peer->outbuf_size);
14235648e56dSSerge Semin 	if (!peer->outbuf)
14245648e56dSSerge Semin 		return -ENOMEM;
14255648e56dSSerge Semin 
142699a06056SJiasen Lin 	peer->out_phys_addr = phys_addr;
142799a06056SJiasen Lin 
14285648e56dSSerge Semin 	if (max_mw_size && peer->outbuf_size > max_mw_size) {
14295648e56dSSerge Semin 		peer->outbuf_size = max_mw_size;
14305648e56dSSerge Semin 		dev_warn(&peer->perf->ntb->dev,
14311536dc06SArnd Bergmann 			"Peer %d outbuf reduced to %pa\n", peer->pidx,
14321536dc06SArnd Bergmann 			&peer->outbuf_size);
14335648e56dSSerge Semin 	}
14348407dd6cSGary R Hook 
14358a7b6a77SDave Jiang 	return 0;
14365648e56dSSerge Semin }
143732e0f5bfSGary R Hook 
perf_init_peers(struct perf_ctx * perf)14385648e56dSSerge Semin static int perf_init_peers(struct perf_ctx *perf)
14395648e56dSSerge Semin {
14405648e56dSSerge Semin 	struct perf_peer *peer;
14415648e56dSSerge Semin 	int pidx, lport, ret;
14425648e56dSSerge Semin 
14435648e56dSSerge Semin 	lport = ntb_port_number(perf->ntb);
14445648e56dSSerge Semin 	perf->gidx = -1;
14455648e56dSSerge Semin 	for (pidx = 0; pidx < perf->pcnt; pidx++) {
14465648e56dSSerge Semin 		peer = &perf->peers[pidx];
14475648e56dSSerge Semin 
14485648e56dSSerge Semin 		peer->perf = perf;
14495648e56dSSerge Semin 		peer->pidx = pidx;
14505648e56dSSerge Semin 		if (lport < ntb_peer_port_number(perf->ntb, pidx)) {
14515648e56dSSerge Semin 			if (perf->gidx == -1)
14525648e56dSSerge Semin 				perf->gidx = pidx;
14535648e56dSSerge Semin 			peer->gidx = pidx + 1;
14545648e56dSSerge Semin 		} else {
14555648e56dSSerge Semin 			peer->gidx = pidx;
14565648e56dSSerge Semin 		}
14575648e56dSSerge Semin 		INIT_WORK(&peer->service, perf_service_work);
145834d8673aSLogan Gunthorpe 		init_completion(&peer->init_comp);
14595648e56dSSerge Semin 	}
14605648e56dSSerge Semin 	if (perf->gidx == -1)
14615648e56dSSerge Semin 		perf->gidx = pidx;
14625648e56dSSerge Semin 
1463b54369a2SLogan Gunthorpe 	/*
1464b54369a2SLogan Gunthorpe 	 * Hardware with only two ports may not have unique port
1465b54369a2SLogan Gunthorpe 	 * numbers. In this case, the gidxs should all be zero.
1466b54369a2SLogan Gunthorpe 	 */
1467b54369a2SLogan Gunthorpe 	if (perf->pcnt == 1 &&  ntb_port_number(perf->ntb) == 0 &&
1468b54369a2SLogan Gunthorpe 	    ntb_peer_port_number(perf->ntb, 0) == 0) {
1469b54369a2SLogan Gunthorpe 		perf->gidx = 0;
1470b54369a2SLogan Gunthorpe 		perf->peers[0].gidx = 0;
1471b54369a2SLogan Gunthorpe 	}
1472b54369a2SLogan Gunthorpe 
14735648e56dSSerge Semin 	for (pidx = 0; pidx < perf->pcnt; pidx++) {
14745648e56dSSerge Semin 		ret = perf_setup_peer_mw(&perf->peers[pidx]);
14755648e56dSSerge Semin 		if (ret)
14765648e56dSSerge Semin 			return ret;
14775648e56dSSerge Semin 	}
14785648e56dSSerge Semin 
14795648e56dSSerge Semin 	dev_dbg(&perf->ntb->dev, "Global port index %d\n", perf->gidx);
14805648e56dSSerge Semin 
14815648e56dSSerge Semin 	return 0;
14828a7b6a77SDave Jiang }
14838a7b6a77SDave Jiang 
perf_probe(struct ntb_client * client,struct ntb_dev * ntb)14848a7b6a77SDave Jiang static int perf_probe(struct ntb_client *client, struct ntb_dev *ntb)
14858a7b6a77SDave Jiang {
14868a7b6a77SDave Jiang 	struct perf_ctx *perf;
14875648e56dSSerge Semin 	int ret;
14888a7b6a77SDave Jiang 
14895648e56dSSerge Semin 	perf = perf_create_data(ntb);
14905648e56dSSerge Semin 	if (IS_ERR(perf))
14915648e56dSSerge Semin 		return PTR_ERR(perf);
149219645a07SLogan Gunthorpe 
14935648e56dSSerge Semin 	ret = perf_init_peers(perf);
14945648e56dSSerge Semin 	if (ret)
14955648e56dSSerge Semin 		return ret;
1496443b9a14SSerge Semin 
14975648e56dSSerge Semin 	perf_init_threads(perf);
14981e530119SSerge Semin 
14995648e56dSSerge Semin 	ret = perf_init_service(perf);
15005648e56dSSerge Semin 	if (ret)
15015648e56dSSerge Semin 		return ret;
15028a7b6a77SDave Jiang 
15035648e56dSSerge Semin 	ret = perf_enable_service(perf);
15045648e56dSSerge Semin 	if (ret)
15055648e56dSSerge Semin 		return ret;
15068a7b6a77SDave Jiang 
15075648e56dSSerge Semin 	perf_setup_dbgfs(perf);
150858fd0f3bSLogan Gunthorpe 
15098a7b6a77SDave Jiang 	return 0;
15108a7b6a77SDave Jiang }
15118a7b6a77SDave Jiang 
perf_remove(struct ntb_client * client,struct ntb_dev * ntb)15128a7b6a77SDave Jiang static void perf_remove(struct ntb_client *client, struct ntb_dev *ntb)
15138a7b6a77SDave Jiang {
15148a7b6a77SDave Jiang 	struct perf_ctx *perf = ntb->ctx;
15158a7b6a77SDave Jiang 
15165648e56dSSerge Semin 	perf_clear_dbgfs(perf);
15178a7b6a77SDave Jiang 
15185648e56dSSerge Semin 	perf_disable_service(perf);
1519da573eaaSLogan Gunthorpe 
15205648e56dSSerge Semin 	perf_clear_threads(perf);
15218a7b6a77SDave Jiang }
15228a7b6a77SDave Jiang 
15238a7b6a77SDave Jiang static struct ntb_client perf_client = {
15248a7b6a77SDave Jiang 	.ops = {
15258a7b6a77SDave Jiang 		.probe = perf_probe,
15265648e56dSSerge Semin 		.remove = perf_remove
15275648e56dSSerge Semin 	}
15288a7b6a77SDave Jiang };
15295648e56dSSerge Semin 
perf_init(void)15305648e56dSSerge Semin static int __init perf_init(void)
15315648e56dSSerge Semin {
15325648e56dSSerge Semin 	int ret;
15335648e56dSSerge Semin 
15345648e56dSSerge Semin 	if (chunk_order > MAX_CHUNK_ORDER) {
15355648e56dSSerge Semin 		chunk_order = MAX_CHUNK_ORDER;
15365648e56dSSerge Semin 		pr_info("Chunk order reduced to %hhu\n", chunk_order);
15375648e56dSSerge Semin 	}
15385648e56dSSerge Semin 
15395648e56dSSerge Semin 	if (total_order < chunk_order) {
15405648e56dSSerge Semin 		total_order = chunk_order;
15415648e56dSSerge Semin 		pr_info("Total data order reduced to %hhu\n", total_order);
15425648e56dSSerge Semin 	}
15435648e56dSSerge Semin 
15445648e56dSSerge Semin 	perf_wq = alloc_workqueue("perf_wq", WQ_UNBOUND | WQ_SYSFS, 0);
15455648e56dSSerge Semin 	if (!perf_wq)
15465648e56dSSerge Semin 		return -ENOMEM;
15475648e56dSSerge Semin 
15485648e56dSSerge Semin 	if (debugfs_initialized())
15495648e56dSSerge Semin 		perf_dbgfs_topdir = debugfs_create_dir(KBUILD_MODNAME, NULL);
15505648e56dSSerge Semin 
15515648e56dSSerge Semin 	ret = ntb_register_client(&perf_client);
15525648e56dSSerge Semin 	if (ret) {
15535648e56dSSerge Semin 		debugfs_remove_recursive(perf_dbgfs_topdir);
15545648e56dSSerge Semin 		destroy_workqueue(perf_wq);
15555648e56dSSerge Semin 	}
15565648e56dSSerge Semin 
15575648e56dSSerge Semin 	return ret;
15585648e56dSSerge Semin }
15595648e56dSSerge Semin module_init(perf_init);
15605648e56dSSerge Semin 
perf_exit(void)15615648e56dSSerge Semin static void __exit perf_exit(void)
15625648e56dSSerge Semin {
15635648e56dSSerge Semin 	ntb_unregister_client(&perf_client);
15645648e56dSSerge Semin 	debugfs_remove_recursive(perf_dbgfs_topdir);
15655648e56dSSerge Semin 	destroy_workqueue(perf_wq);
15665648e56dSSerge Semin }
15675648e56dSSerge Semin module_exit(perf_exit);
1568