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