Skip site navigation (1)Skip section navigation (2)
Date:      Fri, 16 Aug 2019 20:14:37 +0000 (UTC)
From:      Alexander Motin <mav@FreeBSD.org>
To:        src-committers@freebsd.org, svn-src-all@freebsd.org, svn-src-head@freebsd.org
Subject:   svn commit: r351137 - in head/sys: conf dev/ntb/test modules/ntb
Message-ID:  <201908162014.x7GKEb8L006145@repo.freebsd.org>

next in thread | raw e-mail | index | archive | help
Author: mav
Date: Fri Aug 16 20:14:37 2019
New Revision: 351137
URL: https://svnweb.freebsd.org/changeset/base/351137

Log:
  NTB Tool: Test driver for NTB hardware drivers.
  
  NTB Tool driver is meant for testing NTB hardware driver functionalities,
  such as doorbell interrupts, link events, scratchpad registers and memory
  windows.  This is a port of ntb_tool driver from Linux.  It has been
  verified on top of AMD and PLX NTB HW drivers.
  
  Submitted by:	Arpan Palit <arpan.palit@amd.com>
  Cleaned up by:	mav
  MFC after:	2 weeks
  Relnotes:	yes
  Differential Revision:	https://reviews.freebsd.org/D18819

Added:
  head/sys/dev/ntb/test/
  head/sys/dev/ntb/test/ntb_tool.c   (contents, props changed)
Modified:
  head/sys/conf/files.amd64
  head/sys/conf/files.i386
  head/sys/modules/ntb/Makefile

Modified: head/sys/conf/files.amd64
==============================================================================
--- head/sys/conf/files.amd64	Fri Aug 16 20:09:20 2019	(r351136)
+++ head/sys/conf/files.amd64	Fri Aug 16 20:14:37 2019	(r351137)
@@ -225,6 +225,7 @@ dev/ntb/ntb_if.m		optional	ntb | ntb_transport | if_nt
 dev/ntb/ntb_hw/ntb_hw_amd.c	optional	ntb_hw_amd | ntb_hw
 dev/ntb/ntb_hw/ntb_hw_intel.c	optional	ntb_hw_intel | ntb_hw
 dev/ntb/ntb_hw/ntb_hw_plx.c	optional	ntb_hw_plx | ntb_hw
+dev/ntb/test/ntb_tool.c		optional	ntb_tool
 dev/nvram/nvram.c		optional	nvram isa
 dev/random/ivy.c		optional	rdrand_rng !random_loadable
 dev/random/nehemiah.c		optional	padlock_rng !random_loadable

Modified: head/sys/conf/files.i386
==============================================================================
--- head/sys/conf/files.i386	Fri Aug 16 20:09:20 2019	(r351136)
+++ head/sys/conf/files.i386	Fri Aug 16 20:14:37 2019	(r351137)
@@ -156,6 +156,7 @@ dev/ntb/ntb_if.m		optional ntb | ntb_transport | if_nt
 dev/ntb/ntb_hw/ntb_hw_amd.c	optional ntb_hw_amd | ntb_hw
 dev/ntb/ntb_hw/ntb_hw_intel.c	optional ntb_hw_intel | ntb_hw
 dev/ntb/ntb_hw/ntb_hw_plx.c	optional ntb_hw_plx | ntb_hw
+dev/ntb/test/ntb_tool.c		optional ntb_tool
 dev/nvram/nvram.c		optional	nvram isa
 dev/ofw/ofwpci.c		optional fdt pci
 dev/pcf/pcf_isa.c		optional pcf

Added: head/sys/dev/ntb/test/ntb_tool.c
==============================================================================
--- /dev/null	00:00:00 1970	(empty, because file is newly added)
+++ head/sys/dev/ntb/test/ntb_tool.c	Fri Aug 16 20:14:37 2019	(r351137)
@@ -0,0 +1,1516 @@
+/*-
+ * This file is provided under a dual BSD/GPLv2 license.  When using or
+ * redistributing this file, you may do so under either license.
+ *
+ * GPL LICENSE SUMMARY
+ *
+ * Copyright (c) 2019 Advanced Micro Devices, Inc. All Rights Reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of version 2 of the GNU General Public License as
+ * published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * General Public License for more details.
+ *
+ * BSD LICENSE
+ *
+ * Copyright (c) 2019 Advanced Micro Devices, Inc. All Rights Reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ *   * Redistributions of source code must retain the above copyright
+ *     notice, this list of conditions and the following disclaimer.
+ *   * Redistributions in binary form must reproduce the above copy
+ *     notice, this list of conditions and the following disclaimer in
+ *     the documentation and/or other materials provided with the
+ *     distribution.
+ *   * Neither the name of Advanced Micro Devices, Inc nor the names of its
+ *     contributors may be used to endorse or promote products derived
+ *     from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+ * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+ * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+ * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ *
+ * PCIe NTB Debugging Tool FreeBSD driver
+ */
+
+/*
+ * How to use this tool, by example.
+ *
+ * List of sysctl for ntb_tool driver.
+ * root@local# sysctl -a | grep ntb_tool
+ * dev.ntb_tool.0.peer0.spad7: 0x0
+ * dev.ntb_tool.0.peer0.spad6: 0x0
+ * dev.ntb_tool.0.peer0.spad5: 0x0
+ * dev.ntb_tool.0.peer0.spad4: 0x0
+ * dev.ntb_tool.0.peer0.spad3: 0x0
+ * dev.ntb_tool.0.peer0.spad2: 0x0
+ * dev.ntb_tool.0.peer0.spad1: 0x0
+ * dev.ntb_tool.0.peer0.spad0: 0x0
+ * dev.ntb_tool.0.peer0.mw_trans2:
+ * dev.ntb_tool.0.peer0.mw_trans1:
+ * dev.ntb_tool.0.peer0.mw_trans0:
+ * dev.ntb_tool.0.peer0.peer_mw2:
+ * dev.ntb_tool.0.peer0.peer_mw1:
+ * dev.ntb_tool.0.peer0.peer_mw0:
+ * dev.ntb_tool.0.peer0.mw2:
+ * dev.ntb_tool.0.peer0.mw1:
+ * dev.ntb_tool.0.peer0.mw0:
+ * dev.ntb_tool.0.peer0.link_event: 0x0
+ * dev.ntb_tool.0.peer0.link: Y
+ * dev.ntb_tool.0.peer0.port: 1
+ * dev.ntb_tool.0.spad7: 0x0
+ * dev.ntb_tool.0.spad6: 0x0
+ * dev.ntb_tool.0.spad5: 0x0
+ * dev.ntb_tool.0.spad4: 0x0
+ * dev.ntb_tool.0.spad3: 0x0
+ * dev.ntb_tool.0.spad2: 0x0
+ * dev.ntb_tool.0.spad1: 0x0
+ * dev.ntb_tool.0.spad0: 0x0
+ * dev.ntb_tool.0.db: 0x0
+ * dev.ntb_tool.0.db_event: 0x0
+ * dev.ntb_tool.0.db_mask: 0xffff
+ * dev.ntb_tool.0.db_valid_mask: 0xffff
+ * dev.ntb_tool.0.peer_db: 0x0
+ * dev.ntb_tool.0.peer_db_mask: 0xffff
+ * dev.ntb_tool.0.link: Y
+ * dev.ntb_tool.0.port: 0
+ *
+ * The above example list shows
+ * 1) three memory windows,
+ * 1) eight scratchpad registers.
+ * 3) doorbell config.
+ * 4) link config.
+ * 2) One peer.
+ *
+ * Based on the underlined ntb_hw driver config & connection topology, these
+ * things might differ.
+ *-----------------------------------------------------------------------------
+ * Eg: check local/peer port information.
+ *
+ * # Get local device port number
+ * root@local# sysctl dev.ntb_tool.0.port
+ *
+ * # Check peer device port number
+ * root@local# sysctl dev.ntb_tool.0.peer0.port
+ *-----------------------------------------------------------------------------
+ * Eg: NTB link tests
+ *
+ * # Set local link up/down
+ * root@local# sysctl dev.ntb_tool.0.link=Y
+ * root@local# sysctl dev.ntb_tool.0.link=N
+ *
+ * # Check if link with peer device is up/down:
+ * root@local# sysctl dev.ntb_tool.0.peer0.link
+ *
+ * # Poll until the link specified as up/down. For up, value needs to be set
+ * depends on peer index, i.e., for peer0 it is 0x1 and for down, value needs
+ * to be set as 0x0.
+ * root@local# sysctl dev.ntb_tool.0.peer0.link_event=0x1
+ * root@local# sysctl dev.ntb_tool.0.peer0.link_event=0x0
+ *-----------------------------------------------------------------------------
+ * Eg: Doorbell registers tests
+ *
+ * # clear/get local doorbell
+ * root@local# sysctl dev.ntb_tool.0.db="c 0x1"
+ * root@local# sysctl dev.ntb_tool.0.db
+ *
+ * # Set/clear/get local doorbell mask
+ * root@local# sysctl dev.ntb_tool.0.db_mask="s 0x1"
+ * root@local# sysctl dev.ntb_tool.0.db_mask="c 0x1"
+ * root@local# sysctl dev.ntb_tool.0.db_mask
+ *
+ * # Ring/clear/get peer doorbell
+ * root@local# sysctl dev.ntb_tool.0.peer_db="s 0x1"
+ * root@local# sysctl dev.ntb_tool.0.peer_db="c 0x1"
+ * root@local# sysctl dev.ntb_tool.0.peer_db
+ *
+ * # Set/clear/get peer doorbell mask (functionality is absent)
+ * root@local# sysctl dev.ntb_tool.0.peer_db_mask="s 0x1"
+ * root@local# sysctl dev.ntb_tool.0.peer_db_mask="c 0x1"
+ * root@local# sysctl dev.ntb_tool.0.peer_db_mask
+ *
+ * # Poll until local doorbell is set with the specified db bits
+ * root@local# dev.ntb_tool.0.db_event=0x1
+ *-----------------------------------------------------------------------------
+ * Eg: Scratchpad registers tests
+ *
+ * # Write/read to/from local scratchpad register #0
+ * root@local# sysctl dev.ntb_tool.0.spad0=0x1023457
+ * root@local# sysctl dev.ntb_tool.0.spad0
+ *
+ * # Write/read to/from peer scratchpad register #0
+ * root@local# sysctl dev.ntb_tool.0.peer0.spad0=0x01020304
+ * root@local# sysctl dev.ntb_tool.0.peer0.spad0
+ *-----------------------------------------------------------------------------
+ * Eg: Memory windows tests (need to configure local mw_trans on both sides)
+ *
+ * # Create inbound memory window buffer of specified size/get its dma address
+ * root@local# sysctl dev.ntb_tool.0.peer0.mw_trans0=16384
+ * root@local# sysctl dev.ntb_tool.0.peer0.mw_trans0
+ *
+ * # Write/read data to/from inbound memory window with specific pattern/random
+ * data.
+ * root@local# sysctl dev.ntb_tool.0.peer0.mw0="W offset 0 nbytes 100 pattern ab"
+ * root@local# sysctl dev.ntb_tool.0.peer0.mw0="R offset 0 nbytes 100"
+ *
+ * # Write/read data to/from outbound memory window on the local device with
+ * specific pattern/random (on peer device)
+ * root@local# sysctl dev.ntb_tool.0.peer0.peer_mw0="W offset 0 nbytes 100 pattern ab"
+ * root@local# sysctl dev.ntb_tool.0.peer0.peer_mw0="R offset 0 nbytes 100"
+ *-----------------------------------------------------------------------------
+ * NOTE: *Message registers are not supported*
+ *-----------------------------------------------------------------------------
+ *
+ * contact information:
+ * Arpan Palit <arpan.palit@amd.com>
+ *
+ */
+
+#include <sys/cdefs.h>
+__FBSDID("$FreeBSD$");
+
+#include <sys/param.h>
+#include <sys/bus.h>
+#include <sys/kernel.h>
+#include <sys/module.h>
+#include <sys/mbuf.h>
+#include <sys/sysctl.h>
+#include <sys/sbuf.h>
+
+#include <machine/bus.h>
+
+#include <vm/vm.h>
+
+#include "../ntb.h"
+
+/* Buffer length for User input */
+#define	TOOL_BUF_LEN 48
+/* Memory window default command read and write offset. */
+#define	DEFAULT_MW_OFF  0
+/* Memory window default size and also max command read size. */
+#define	DEFAULT_MW_SIZE 1024
+
+MALLOC_DEFINE(M_NTB_TOOL, "ntb_tool", "ntb_tool driver memory allocation");
+
+/*
+ * Memory windows descriptor structure
+ */
+struct tool_mw {
+	struct tool_ctx    *tc;
+	int                widx;
+	int                pidx;
+
+	/* Rx buff is off virt_addr / dma_base */
+	bus_addr_t         dma_base;
+	caddr_t            virt_addr;
+	bus_dmamap_t       dma_map;
+	bus_dma_tag_t      dma_tag;
+
+	/* Tx buff is off vbase / phys_addr */
+	caddr_t            mm_base;
+	vm_paddr_t         phys_addr;
+	bus_addr_t         addr_limit;
+	size_t             phys_size;
+	size_t             xlat_align;
+	size_t             xlat_align_size;
+
+	/* Memory window configured size and limits */
+	size_t             size;
+	ssize_t            mw_buf_size;
+	ssize_t            mw_buf_offset;
+	ssize_t            mw_peer_buf_size;
+	ssize_t            mw_peer_buf_offset;
+
+	/* options to handle sysctl out */
+	int                mw_cmd_rw;
+	int                mw_peer_cmd_rw;
+};
+
+struct tool_spad {
+	int                sidx;
+	int                pidx;
+	struct tool_ctx    *tc;
+};
+
+struct tool_peer {
+	int                 pidx;
+	struct tool_ctx     *tc;
+	int                 inmw_cnt;
+	struct tool_mw      *inmws;
+	int                 outspad_cnt;
+	struct tool_spad    *outspads;
+	unsigned int        port_no;
+};
+
+struct tool_ctx {
+	device_t            dev;
+	struct callout      link_event_timer;
+	struct callout      db_event_timer;
+	int                 peer_cnt;
+	struct tool_peer    *peers;
+	int                 inmsg_cnt;
+	struct tool_msg     *inmsgs;
+	int                 inspad_cnt;
+	struct tool_spad    *inspads;
+	unsigned int        unsafe;
+
+	/* sysctl read out variables */
+	char                link_status;
+	uint64_t            link_bits;
+	uint64_t            link_mask;
+	uint64_t            db_valid_mask;
+	uint64_t            db_mask_val;
+	uint64_t            db_event_val;
+	uint64_t            peer_db_val;
+	uint64_t            peer_db_mask_val;
+	unsigned int        port_no;
+};
+
+/* structure to save dma_addr after dma load */
+struct ntb_tool_load_cb_args {
+	bus_addr_t addr;
+	int error;
+};
+
+/*
+ * NTB events handlers
+ */
+static void
+tool_link_event(void *ctx)
+{
+	struct tool_ctx *tc = ctx;
+	enum ntb_speed speed = 0;
+	enum ntb_width width = 0;
+	int up = 0;
+
+	up = ntb_link_is_up(tc->dev, &speed, &width);
+	if (up)
+		tc->link_status = 'Y';
+	else
+		tc->link_status = 'N';
+
+	device_printf(tc->dev, "link is %s speed %d width %d\n",
+	    up ? "up" : "down", speed, width);
+}
+
+static void
+tool_db_event(void *ctx, uint32_t vec)
+{
+	struct tool_ctx *tc = ctx;
+	uint64_t db_bits, db_mask;
+
+	db_mask = ntb_db_vector_mask(tc->dev, vec);
+	db_bits = ntb_db_read(tc->dev);
+
+	device_printf(tc->dev, "doorbell vec %d mask %#llx bits %#llx\n",
+	    vec, (unsigned long long)db_mask, (unsigned long long)db_bits);
+}
+
+static const struct ntb_ctx_ops tool_ops = {
+	.link_event = tool_link_event,
+	.db_event = tool_db_event,
+};
+
+/*
+ * Callout event methods
+ */
+static void
+tool_link_event_handler(void *arg)
+{
+	struct tool_ctx *tc = (struct tool_ctx *)arg;
+	uint64_t val;
+
+	val = ntb_link_is_up(tc->dev, NULL, NULL) & tc->link_mask;
+
+	if (val == tc->link_bits) {
+		device_printf(tc->dev, "link_event successful for link val="
+		    "0x%jx\n", tc->link_bits);
+		tc->link_bits = 0x0;
+		tc->link_mask = 0x0;
+	} else
+		callout_reset(&tc->link_event_timer, 1, tool_link_event_handler, tc);
+}
+
+static void
+tool_db_event_handler(void *arg)
+{
+	struct tool_ctx *tc = (struct tool_ctx *)arg;
+	uint64_t db_bits;
+
+	db_bits = ntb_db_read(tc->dev);
+
+	if (db_bits == tc->db_event_val) {
+		device_printf(tc->dev, "db_event successful for db val=0x%jx\n",
+		    tc->db_event_val);
+		tc->db_event_val = 0x0;
+	} else
+		callout_reset(&tc->db_event_timer, 1, tool_db_event_handler, tc);
+}
+
+/*
+ * Common read/write methods
+ */
+static inline int
+get_ubuf(struct sysctl_req *req, char *ubuf)
+{
+	int rc;
+
+	if (req->newlen >= TOOL_BUF_LEN)
+		return (EINVAL);
+
+	rc = SYSCTL_IN(req, ubuf, req->newlen);
+	if (rc)
+		return (rc);
+	ubuf[req->newlen] = '\0';
+
+	return (0);
+}
+
+static int
+read_out(struct sysctl_req *req, uint64_t val)
+{
+	char ubuf[16];
+
+	memset((void *)ubuf, 0, sizeof(ubuf));
+	snprintf(ubuf, sizeof(ubuf), "0x%jx", val);
+
+	return SYSCTL_OUT(req, ubuf, sizeof(ubuf));
+}
+
+static int
+tool_fn_read(struct tool_ctx *tc, struct sysctl_req *req,
+    uint64_t (*fn_read)(device_t ), uint64_t val)
+{
+	if (fn_read == NULL)
+		return read_out(req, val);
+	else if (fn_read)
+		return read_out(req, (uint64_t)fn_read(tc->dev));
+	else
+		return (EINVAL);
+}
+
+static int
+tool_fn_write(struct tool_ctx *tc, struct sysctl_oid *oidp,
+    struct sysctl_req *req, char *ubuf, uint64_t *val, bool db_mask_sflag,
+    void (*fn_set)(device_t , uint64_t), void (*fn_clear)(device_t , uint64_t))
+{
+	uint64_t db_valid_mask = tc->db_valid_mask;
+	uint64_t bits;
+	char cmd;
+
+	if (fn_set == NULL && fn_clear == NULL) {
+		device_printf(tc->dev, "ERR: Set & Clear both are not supported\n");
+		return (EINVAL);
+	}
+
+	if (tc->db_valid_mask == 0)
+		db_valid_mask = tc->db_valid_mask = ntb_db_valid_mask(tc->dev);
+
+	bits = 0;
+	sscanf(ubuf, "%c %jx", &cmd, &bits);
+	if (cmd == 's') {
+		if ((bits | db_valid_mask) > db_valid_mask) {
+			device_printf(tc->dev, "0x%jx value is not supported\n", bits);
+			return (EINVAL);
+		}
+		if (fn_set)
+			fn_set(tc->dev, bits);
+		else
+			return (EINVAL);
+		if (val)
+			*val |= bits;
+	} else if (cmd == 'c') {
+		if ((bits | db_valid_mask) > db_valid_mask) {
+			device_printf(tc->dev, "0x%jx value is not supported\n", bits);
+			return (EINVAL);
+		}
+		if (fn_clear)
+			fn_clear(tc->dev, bits);
+		if (val)
+			*val &= ~bits;
+	} else {
+		device_printf(tc->dev, "Wrong Write\n");
+		return (EINVAL);
+	}
+
+	return (0);
+}
+
+static int
+parse_mw_buf(char *buf, char *cmd, ssize_t *offset, ssize_t *buf_size,
+    uint64_t *pattern, bool *s_pflag)
+{
+	char op1[8], op2[8], op3[8];
+	uint64_t val1, val2, val3;
+	bool vs1, vs2, vs3;
+	int rc = 0;
+
+	vs1 = vs2 = vs3 = false;
+	sscanf(buf, "%c %s %jx %s %jx %s %jx",
+	    cmd, op1, &val1, op2, &val2, op3, &val3);
+
+	if (*cmd != 'W' && *cmd != 'R')
+		return (EINVAL);
+
+	if (!strcmp(op1, "offset")) {
+		*offset = val1 ? val1 : DEFAULT_MW_OFF;
+		vs1 = true;
+	} else if (!strcmp(op1, "nbytes")) {
+		*buf_size = val1 ? val1: DEFAULT_MW_SIZE;
+		vs2 = true;
+	} else if (!strcmp(op1, "pattern")) {
+		*pattern = val1;
+		vs3 = true;
+	}
+
+	if (!vs1 && !strcmp(op2, "offset")) {
+		*offset = val2 ? val2 : DEFAULT_MW_OFF;
+		vs1 = true;
+	} else if (!vs2 && !strcmp(op2, "nbytes")) {
+		*buf_size = val2 ? val2: DEFAULT_MW_SIZE;
+		vs2 = true;
+	} else if (!vs3 && !strcmp(op2, "pattern")) {
+		*pattern = val2;
+		vs3 = true;
+	}
+
+	if (!vs1 && !strcmp(op3, "offset")) {
+		*offset = val3 ? val3 : DEFAULT_MW_OFF;
+	} else if (!vs2 && !strcmp(op3, "nbytes")) {
+		*buf_size = val3 ? val3: DEFAULT_MW_SIZE;
+	} else if (!vs3 && !strcmp(op3, "pattern")) {
+		*pattern = val3;
+		vs3 = true;
+	}
+
+	*s_pflag = vs3;
+	if (vs3 && *cmd == 'R')
+		printf("NTB_TOOL_WARN: pattern is not supported with read "
+		    "command\n");
+
+	return (rc);
+}
+
+static int
+tool_mw_read_fn(struct sysctl_req *req, struct tool_mw *inmw, char *read_addr,
+    int *cmd_op, ssize_t buf_off, ssize_t buf_size, char *type)
+{
+	ssize_t index, size;
+	struct sbuf *sb;
+	int i, loop, rc;
+	char *tmp;
+
+	/* The below check is made to ignore sysctl read call. */
+	if (*cmd_op == 0)
+		return (0);
+
+	/* Proceeds only when command R/W is requested using sysctl. */
+	index = buf_off;
+	tmp = read_addr;
+	tmp += index;
+	loop = ((buf_size == 0) || (buf_size > DEFAULT_MW_SIZE)) ?
+	    DEFAULT_MW_SIZE : buf_size;
+	/*
+	 * 256 bytes of extra buffer has been allocated to print details like
+	 * summary, size, notes, i.e., excluding data part.
+	 */
+	size = loop + 256;
+	sb = sbuf_new_for_sysctl(NULL, NULL, size, req);
+	if (sb == NULL) {
+		rc = sb->s_error;
+		return (rc);
+	}
+
+	if (!strcmp(type, "mw"))
+		sbuf_printf(sb, "\nConfigured MW size\t: %zu\n", inmw->size);
+	else if (!strcmp(type, "peer_mw"))
+		sbuf_printf(sb, "\nConfigured Peer MW size\t: %zu\n",
+		    inmw->size);
+	sbuf_printf(sb, "R/W size\t\t: %zi\nR/W Offset\t\t: %zi\n\nData\n----"
+	    "->", buf_size, buf_off);
+
+	/*
+	 * Data will be read based on MW size provided by the user using nbytes,
+	 * which is limited to 1024 bytes if user req bigger size to read, check
+	 * above loop calculation which is limiting or setting the MW read size.
+	 * Below for loop prints data where in each line contains 32 bytes data
+	 * and after each 8 bytes of data we used four spaces which ensures one
+	 * data block.
+	 */
+	for (i = 0 ; i < loop; i++) {
+		if ((i % 32) == 0) {
+			sbuf_printf(sb, "\n%08zx:", index);
+			index += 32;
+		}
+		if ((i % 8) == 0)
+			sbuf_printf(sb, "    ");
+		sbuf_printf(sb, "%02hhx", *(tmp+i));
+	}
+	if (buf_size > DEFAULT_MW_SIZE)
+		sbuf_printf(sb, "\n\nNOTE: Truncating read size %zi->1024 "
+		    "bytes\n", buf_size);
+
+	/* cmd_op is set to zero after completion of each R/W command. */
+	*cmd_op -= 1;
+	rc = sbuf_finish(sb);
+	sbuf_delete(sb);
+
+	return (rc);
+}
+
+static int
+tool_mw_write_fn(struct sysctl_oid *oidp, struct sysctl_req *req,
+    struct tool_mw *inmw, char *ubuf, caddr_t write_buf, int *cmd_op,
+    ssize_t *buf_offset, ssize_t *buf_size)
+{
+	ssize_t data_buf_size;
+	uint64_t pattern = 0;
+	bool s_pflag = false;
+	void *data_buf;
+	char cmd;
+	int rc;
+
+	if (!write_buf)
+		return (ENXIO);
+
+	/* buf_offset and buf_size set to default in case user does not req */
+	*buf_offset = DEFAULT_MW_OFF;
+	*buf_size = DEFAULT_MW_SIZE;
+	rc = parse_mw_buf(ubuf, &cmd, buf_offset, buf_size, &pattern, &s_pflag);
+	if (rc) {
+		device_printf(inmw->tc->dev, "Wrong Command \"%c\" provided\n",
+		    cmd);
+		return (rc);
+	}
+
+	/* Check for req size and buffer limit */
+	if ((*buf_offset + *buf_size) > inmw->size) {
+		device_printf(inmw->tc->dev, "%s: configured mw size :%zi and "
+		    "requested size :%zi.\n", __func__, inmw->size,
+		    (*buf_offset + *buf_size));
+		*buf_offset = DEFAULT_MW_OFF;
+		*buf_size = DEFAULT_MW_SIZE;
+		rc = EINVAL;
+		goto out;
+	}
+
+	if (cmd == 'R')
+		goto read_out;
+	else if (cmd == 'W')
+		goto write;
+	else
+		goto out;
+
+write:
+	data_buf_size = *buf_size;
+	data_buf = malloc(data_buf_size, M_NTB_TOOL, M_WAITOK | M_ZERO);
+	if (!data_buf) {
+		rc = ENOMEM;
+		goto out;
+	}
+
+	if (s_pflag)
+		memset(data_buf, pattern, data_buf_size);
+	else
+		arc4rand(data_buf, data_buf_size, 1);
+
+	memcpy(write_buf + *buf_offset, data_buf, data_buf_size);
+
+	free(data_buf, M_NTB_TOOL);
+
+read_out:
+	/* cmd_op value is set to two as sysctl read call executes twice */
+	*cmd_op = 2;
+out:
+	return (rc);
+}
+
+/*
+ * Port sysctl read/write methods
+ */
+static int
+sysctl_peer_port_number(SYSCTL_HANDLER_ARGS)
+{
+	struct tool_ctx *tc = (struct tool_ctx *)arg1;
+	int rc, pidx = arg2, peer_port;
+
+	peer_port = ntb_peer_port_number(tc->dev, pidx);
+	rc = sysctl_handle_int(oidp, &peer_port, 0, req);
+	if (rc)
+		device_printf(tc->dev, "Peer port sysctl set failed with err="
+		    "(%d).\n", rc);
+	else
+		tc->peers[pidx].port_no = peer_port;
+
+	return (rc);
+}
+
+static int
+sysctl_local_port_number(SYSCTL_HANDLER_ARGS)
+{
+	struct tool_ctx *tc = (struct tool_ctx *)arg1;
+	int rc, local_port;
+
+	local_port = ntb_port_number(tc->dev);
+	rc = sysctl_handle_int(oidp, &local_port, 0, req);
+	if (rc)
+		device_printf(tc->dev, "Local port sysctl set failed with err="
+		    "(%d).\n", rc);
+	else
+		tc->port_no = local_port;
+
+	return (rc);
+}
+
+static int
+tool_init_peers(struct tool_ctx *tc)
+{
+	int pidx;
+
+	tc->peer_cnt = ntb_peer_port_count(tc->dev);
+	tc->peers = malloc(tc->peer_cnt * sizeof(*tc->peers), M_NTB_TOOL,
+	    M_WAITOK | M_ZERO);
+	if (tc->peers == NULL)
+		return (ENOMEM);
+	for (pidx = 0; pidx < tc->peer_cnt; pidx++) {
+		tc->peers[pidx].pidx = pidx;
+		tc->peers[pidx].tc = tc;
+	}
+
+	return (0);
+}
+
+static void
+tool_clear_peers(struct tool_ctx *tc)
+{
+
+	free(tc->peers, M_NTB_TOOL);
+}
+
+/*
+ * Link state sysctl read/write methods
+ */
+static int
+sysctl_link_handle(SYSCTL_HANDLER_ARGS)
+{
+	struct tool_ctx *tc = (struct tool_ctx *)arg1;
+	char buf[TOOL_BUF_LEN];
+	int rc;
+
+	if (req->newptr == NULL) {
+		snprintf(buf, 2, "%c", tc->link_status);
+
+		return SYSCTL_OUT(req, buf, 2);
+	}
+
+	rc = get_ubuf(req, buf);
+	if (rc)
+		return (rc);
+
+	if (buf[0] == 'Y')
+		rc = ntb_link_enable(tc->dev, NTB_SPEED_AUTO, NTB_WIDTH_AUTO);
+	else if (buf[0] == 'N')
+		rc = ntb_link_disable(tc->dev);
+	else
+		rc = EINVAL;
+
+	sscanf(buf, "%c", &tc->link_status);
+
+	return (0);
+}
+
+static int
+sysctl_peer_link_handle(SYSCTL_HANDLER_ARGS)
+{
+	struct tool_ctx *tc = (struct tool_ctx *)arg1;
+	int up = 0, pidx = arg2;
+	char buf[TOOL_BUF_LEN];
+
+	if (req->newptr)
+		return (0);
+
+	up = ntb_link_is_up(tc->dev, NULL, NULL);
+	memset((void *)buf, 0, TOOL_BUF_LEN);
+	if (up & (1UL << pidx))
+		buf[0] = 'Y';
+	else
+		buf[0] = 'N';
+
+	return SYSCTL_OUT(req, buf, sizeof(buf));
+}
+
+static int
+sysctl_peer_link_event_handle(SYSCTL_HANDLER_ARGS)
+{
+	struct tool_ctx *tc = (struct tool_ctx *)arg1;
+	char buf[TOOL_BUF_LEN];
+	int rc, pidx = arg2;
+	uint64_t bits;
+
+	if (req->newptr == NULL)
+		return read_out(req, tc->link_bits);
+
+	rc = get_ubuf(req, buf);
+	if (rc)
+		return (rc);
+
+	sscanf(buf, "0x%jx", &bits);
+	tc->link_bits = bits;
+	tc->link_mask = (1ULL << ((pidx) % 64));
+
+	callout_reset(&tc->link_event_timer, 1, tool_link_event_handler, tc);
+	return (0);
+}
+
+/*
+ * Memory windows read/write/setting methods
+ */
+static void
+ntb_tool_load_cb(void *arg, bus_dma_segment_t *segs, int nsegs, int error)
+{
+	struct ntb_tool_load_cb_args *cba = (struct ntb_tool_load_cb_args *)arg;
+
+	if (!(cba->error = error))
+		cba->addr = segs[0].ds_addr;
+}
+
+static int
+sysctl_mw_handle(SYSCTL_HANDLER_ARGS)
+{
+	struct tool_mw *inmw = (struct tool_mw *)arg1;
+	char buf[TOOL_BUF_LEN];
+	int rc;
+
+	if (req->newptr == NULL)
+		return tool_mw_read_fn(req, inmw, (char *)inmw->mm_base,
+		    &inmw->mw_cmd_rw, inmw->mw_buf_offset, inmw->mw_buf_size,
+		    "mw");
+
+	rc = get_ubuf(req, buf);
+	if (!rc)
+		return tool_mw_write_fn(oidp, req, inmw, buf, inmw->mm_base,
+		    &inmw->mw_cmd_rw, &inmw->mw_buf_offset, &inmw->mw_buf_size);
+
+	return (rc);
+}
+
+static int
+tool_setup_mw(struct tool_ctx *tc, unsigned int pidx, unsigned int widx,
+    size_t req_size)
+{
+	struct tool_mw *inmw = &tc->peers[pidx].inmws[widx];
+	struct ntb_tool_load_cb_args cba;
+	int rc;
+
+	if (req_size == 0)
+		inmw->size = roundup(inmw->phys_size, inmw->xlat_align_size);
+	else
+		inmw->size = roundup(req_size, inmw->xlat_align_size);
+
+	device_printf(tc->dev, "mw_size %zi req_size %zi buff %zi\n",
+	    inmw->phys_size, req_size, inmw->size);
+
+	if (bus_dma_tag_create(bus_get_dma_tag(tc->dev), inmw->xlat_align, 0,
+	    inmw->addr_limit, BUS_SPACE_MAXADDR, NULL, NULL, inmw->size, 1,
+	    inmw->size, 0, NULL, NULL, &inmw->dma_tag)) {
+		device_printf(tc->dev, "Unable to create MW tag of size "
+		    "%zu/%zu\n", inmw->phys_size, inmw->size);
+		rc = ENOMEM;
+		goto err_free_dma_var;
+	}
+
+	if (bus_dmamem_alloc(inmw->dma_tag, (void **)&inmw->virt_addr,
+	    BUS_DMA_WAITOK | BUS_DMA_ZERO, &inmw->dma_map)) {
+		device_printf(tc->dev, "Unable to allocate MW buffer of size "
+		    "%zu/%zu\n", inmw->phys_size, inmw->size);
+		rc = ENOMEM;
+		goto err_free_tag_rem;
+	}
+
+	if (bus_dmamap_load(inmw->dma_tag, inmw->dma_map, inmw->virt_addr,
+	    inmw->size, ntb_tool_load_cb, &cba, BUS_DMA_NOWAIT) || cba.error) {
+		device_printf(tc->dev, "Unable to load MW buffer of size "
+		    "%zu/%zu\n", inmw->phys_size, inmw->size);
+		rc = ENOMEM;
+		goto err_free_dma;
+	}
+	inmw->dma_base = cba.addr;
+
+	rc = ntb_mw_set_trans(tc->dev, widx, inmw->dma_base, inmw->size);
+	if (rc)
+		goto err_free_mw;
+
+	return (0);
+
+err_free_mw:
+	bus_dmamap_unload(inmw->dma_tag, inmw->dma_map);
+
+err_free_dma:
+	bus_dmamem_free(inmw->dma_tag, inmw->virt_addr, inmw->dma_map);
+
+err_free_tag_rem:
+	bus_dma_tag_destroy(inmw->dma_tag);
+
+err_free_dma_var:
+	inmw->size = 0;
+	inmw->virt_addr = 0;
+	inmw->dma_base = 0;
+	inmw->dma_tag = 0;
+	inmw->dma_map = 0;
+
+	return (rc);
+}
+
+static void
+tool_free_mw(struct tool_ctx *tc, int pidx, int widx)
+{
+	struct tool_mw *inmw = &tc->peers[pidx].inmws[widx];
+
+	if (inmw->dma_base)
+		ntb_mw_clear_trans(tc->dev, widx);
+
+	if (inmw->virt_addr && inmw->dma_tag) {
+		bus_dmamap_unload(inmw->dma_tag, inmw->dma_map);
+		bus_dmamem_free(inmw->dma_tag, inmw->virt_addr, inmw->dma_map);
+		bus_dma_tag_destroy(inmw->dma_tag);
+	}
+
+	inmw->virt_addr = 0;
+	inmw->dma_base = 0;
+	inmw->dma_tag = 0;
+	inmw->dma_map = 0;
+	inmw->mm_base = 0;
+	inmw->size = 0;
+}
+
+static int
+tool_mw_trans_read(struct tool_mw *inmw, struct sysctl_req *req)
+{
+	ssize_t buf_size = 512;
+	struct sbuf *sb;
+	int rc = 0;
+
+	sb = sbuf_new_for_sysctl(NULL, NULL, buf_size, req);
+	if (sb == NULL) {
+		rc = sb->s_error;
+		return (rc);
+	}
+
+	sbuf_printf(sb, "\nInbound MW     \t%d\n", inmw->widx);
+	sbuf_printf(sb, "Port           \t%d (%d)\n",
+	    ntb_peer_port_number(inmw->tc->dev, inmw->pidx), inmw->pidx);
+	sbuf_printf(sb, "Window Address \t%p\n", inmw->mm_base);
+	sbuf_printf(sb, "DMA Address    \t0x%016llx\n", (long long)inmw->dma_base);
+	sbuf_printf(sb, "Window Size    \t0x%016zx[p]\n", inmw->size);
+	sbuf_printf(sb, "Alignment      \t0x%016zx[p]\n", inmw->xlat_align);
+	sbuf_printf(sb, "Size Alignment \t0x%016zx[p]\n",
+	    inmw->xlat_align_size);
+	sbuf_printf(sb, "Size Max       \t0x%016zx[p]\n", inmw->phys_size);
+
+	rc = sbuf_finish(sb);
+	sbuf_delete(sb);
+
+	return (rc);
+}
+
+static int
+tool_mw_trans_write(struct sysctl_oid *oidp, struct sysctl_req *req,
+    struct tool_mw *inmw, size_t wsize)
+{
+	struct tool_ctx *tc = inmw->tc;
+	int rc = 0;
+
+	if (wsize == 0)
+		return (EINVAL);
+
+	/* No need to re-setup mw */
+	if (inmw->size == wsize)
+		return (0);
+
+	/* free mw dma buffer */
+	if (inmw->size)
+		tool_free_mw(tc, inmw->pidx, inmw->widx);
+
+	rc = tool_setup_mw(tc, inmw->pidx, inmw->widx, wsize);
+
+	return (rc);
+}
+
+static int
+sysctl_mw_trans_handler(SYSCTL_HANDLER_ARGS)
+{
+	struct tool_mw *inmw = (struct tool_mw *)arg1;
+	char buf[TOOL_BUF_LEN];
+	ssize_t wsize;
+	int rc;
+
+	if (req->newptr == NULL)
+		return tool_mw_trans_read(inmw, req);
+
+	rc = get_ubuf(req, buf);
+	if (rc == 0) {
+		sscanf(buf, "%zi", &wsize);
+		return tool_mw_trans_write(oidp, req, inmw, wsize);
+	}
+
+	return (rc);
+}
+
+static int
+sysctl_peer_mw_handle(SYSCTL_HANDLER_ARGS)
+{
+	struct tool_mw *inmw = (struct tool_mw *)arg1;

*** DIFF OUTPUT TRUNCATED AT 1000 LINES ***



Want to link to this message? Use this URL: <https://mail-archive.FreeBSD.org/cgi/mid.cgi?201908162014.x7GKEb8L006145>