svn commit: r250079 - in head: share/man/man4 sys/amd64/conf sys/conf sys/dev/ntb sys/dev/ntb/if_ntb sys/dev/ntb/ntb_hw sys/modules sys/modules/ntb sys/modules/ntb/if_ntb sys/modules/ntb/ntb_hw

Carl Delsey carl at FreeBSD.org
Mon Apr 29 22:48:54 UTC 2013


Author: carl
Date: Mon Apr 29 22:48:53 2013
New Revision: 250079
URL: http://svnweb.freebsd.org/changeset/base/250079

Log:
  Add a new driver to support the Intel Non-Transparent Bridge(NTB).
  
  The NTB allows you to connect two systems with this device using a PCI-e
  link. The driver is made of two modules:
   - ntb_hw which is a basic hardware abstraction layer for the device.
   - if_ntb which implements the ntb network device and the communication
     protocol.
  
  The driver is limited at the moment to CPU memcpy instead of using DMA, and
  only Back-to-Back mode is supported. Also the network device isn't full
  featured yet. These changes will be coming soon. The DMA change will also
  bring in the ioat driver from the project branch it is on now.
  
  This is an initial port of the GPL/BSD Linux driver contributed by Jon Mason
  from Intel. Any bugs are my contributions.
  
  Sponsored by: Intel
  Reviewed by: jimharris, joel (man page only)
  Approved by: jimharris (mentor)

Added:
  head/share/man/man4/ntb.4   (contents, props changed)
  head/sys/dev/ntb/
  head/sys/dev/ntb/if_ntb/
  head/sys/dev/ntb/if_ntb/if_ntb.c   (contents, props changed)
  head/sys/dev/ntb/ntb_hw/
  head/sys/dev/ntb/ntb_hw/ntb_hw.c   (contents, props changed)
  head/sys/dev/ntb/ntb_hw/ntb_hw.h   (contents, props changed)
  head/sys/dev/ntb/ntb_hw/ntb_regs.h   (contents, props changed)
  head/sys/modules/ntb/
  head/sys/modules/ntb/Makefile   (contents, props changed)
  head/sys/modules/ntb/if_ntb/
  head/sys/modules/ntb/if_ntb/Makefile   (contents, props changed)
  head/sys/modules/ntb/ntb_hw/
  head/sys/modules/ntb/ntb_hw/Makefile   (contents, props changed)
Modified:
  head/share/man/man4/Makefile
  head/sys/amd64/conf/NOTES
  head/sys/conf/files.amd64
  head/sys/modules/Makefile

Modified: head/share/man/man4/Makefile
==============================================================================
--- head/share/man/man4/Makefile	Mon Apr 29 21:49:22 2013	(r250078)
+++ head/share/man/man4/Makefile	Mon Apr 29 22:48:53 2013	(r250079)
@@ -333,6 +333,7 @@ MAN=	aac.4 \
 	ng_vlan.4 \
 	nmdm.4 \
 	nsp.4 \
+	${_ntb.4} \
 	null.4 \
 	${_nvd.4} \
 	${_nve.4} \
@@ -647,6 +648,7 @@ MLINKS+=netintro.4 net.4 \
 	netintro.4 networking.4
 MLINKS+=${_nfe.4} ${_if_nfe.4}
 MLINKS+=nge.4 if_nge.4
+MLINKS+=${_ntb.4} ${_if_ntb.4} ${_ntb_hw.4}
 MLINKS+=${_nve.4} ${_if_nve.4}
 MLINKS+=${_nxge.4} ${_if_nxge.4}
 MLINKS+=patm.4 if_patm.4
@@ -784,6 +786,9 @@ MLINKS+=lindev.4 full.4
 
 .if ${MACHINE_CPUARCH} == "amd64"
 _bhyve.4=	bhyve.4
+_if_ntb.4=	if_ntb.4
+_ntb.4=		ntb.4
+_ntb_hw.4=	ntb_hw.4
 _qlxgb.4=	qlxgb.4
 _sfxge.4=	sfxge.4
 

Added: head/share/man/man4/ntb.4
==============================================================================
--- /dev/null	00:00:00 1970	(empty, because file is newly added)
+++ head/share/man/man4/ntb.4	Mon Apr 29 22:48:53 2013	(r250079)
@@ -0,0 +1,114 @@
+.\"
+.\" Copyright (c) 2013 Intel Corporation
+.\" All rights reserved.
+.\"
+.\" Redistribution and use in source and binary forms, with or without
+.\" modification, are permitted provided that the following conditions
+.\" are met:
+.\" 1. Redistributions of source code must retain the above copyright
+.\"    notice, this list of conditions, and the following disclaimer,
+.\"    without modification.
+.\" 2. Redistributions in binary form must reproduce at minimum a disclaimer
+.\"    substantially similar to the "NO WARRANTY" disclaimer below
+.\"    ("Disclaimer") and any redistribution must be conditioned upon
+.\"    including a substantially similar Disclaimer requirement for further
+.\"    binary redistribution.
+.\"
+.\" NO WARRANTY
+.\" 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 MERCHANTIBILITY AND FITNESS FOR
+.\" A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+.\" HOLDERS OR CONTRIBUTORS BE LIABLE FOR 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 DAMAGES.
+.\"
+.\" ntb driver man page.
+.\"
+.\" Author: Carl Delsey <carl at FreeBSD.org>
+.\"
+.\" $FreeBSD$
+.\"
+.Dd Apr 11, 2013
+.Dt NTB 4
+.Os
+.Sh NAME
+.Nm ntb ,
+.Nm ntb_hw ,
+.Nm if_ntb
+.Nd Intel(R) Non-Transparent Bridge driver
+.Sh SYNOPSIS
+To compile this driver into your kernel,
+place the following lines in your kernel configuration file:
+.Bd -ragged -offset indent
+.Cd "device ntb_hw"
+.Cd "device if_ntb"
+.Ed
+.Pp
+Or, to load the driver as a module at boot, place the following line in
+.Xr loader.conf 5 :
+.Bd -literal -offset indent
+if_ntb_load="YES"
+.Ed
+.Sh DESCRIPTION
+The
+.Nm
+driver provides support for the Non-Transparent Bridge (NTB) in the Intel S1200,
+Xeon E3 and Xeon E5 processor families.
+.Pp
+The NTB allows you to connect two computer systems using a PCI-e link if they
+have the correct equipment and connectors.
+.Sh CONFIGURATION
+The NTB memory windows need to be configured by the BIOS.
+If your BIOS allows you to set their size, you should set the size of both
+memory windows to 1 MiB.
+This needs to be done on both systems.
+.Pp
+Each system needs to have a different IP address assigned.
+The MAC address is randomly generated.
+Also for maximum performance, the MTU should be set to 16 kiB.
+This can be down by adding the line below to
+.Xr rc.conf 5 :
+.Bd -literal -offset indent
+ifconfig_ntb0="inet 192.168.1.10 netmask 255.255.255.0 mtu 16384"
+.Ed
+.Pp
+And on the second system :
+.Bd -literal -offset indent
+ifconfig_ntb0="inet 192.168.1.11 netmask 255.255.255.0 mtu 16384"
+.Ed
+.Pp
+If you are using the UDP protocol, you may want to increase the
+.Va net.inet.udp.maxdgram
+.Xr sysctl 8
+variable.
+.Sh SEE ALSO
+.Xr rc.conf 5 ,
+.Xr sysctl 8
+.Sh AUTHORS
+.An -nosplit
+The
+.Nm
+driver was developed by Intel and originally written by
+.An Carl Delsey Aq carl at FreeBSD.org.
+.Sh BUGS
+If the driver is unloaded, it cannot be reloaded without a system reboot.
+.Pp
+The network support is limited.
+It isn't fully configurable yet.
+It also isn't integrated into
+.Xr netgraph 4
+or
+.Xr bpf 4 .
+.Pp
+NTB to Root Port mode is not yet supported.
+.Pp
+There is no way to protect your system from malicious behavior on the other
+system once the link is brought up.
+Anyone with root or kernel access on the other system can read or write to
+any location on your system.
+In other words, only connect two systems that completely trust each other.

Modified: head/sys/amd64/conf/NOTES
==============================================================================
--- head/sys/amd64/conf/NOTES	Mon Apr 29 21:49:22 2013	(r250078)
+++ head/sys/amd64/conf/NOTES	Mon Apr 29 22:48:53 2013	(r250079)
@@ -366,6 +366,10 @@ device		iwn6000fw
 device		iwn6050fw
 device		wpifw
 
+# Intel Non-Transparent Bridge (NTB) hardware
+device		ntb_hw	# Hardware Abstraction Layer for the NTB
+device		if_ntb	# Simulated ethernet device using the NTB
+
 #
 #XXX this stores pointers in a 32bit field that is defined by the hardware
 #device	pst

Modified: head/sys/conf/files.amd64
==============================================================================
--- head/sys/conf/files.amd64	Mon Apr 29 21:49:22 2013	(r250078)
+++ head/sys/conf/files.amd64	Mon Apr 29 22:48:53 2013	(r250079)
@@ -211,6 +211,8 @@ dev/kbd/kbd.c			optional	atkbd | sc | uk
 dev/lindev/full.c		optional	lindev
 dev/lindev/lindev.c		optional	lindev
 dev/nfe/if_nfe.c		optional	nfe pci
+dev/ntb/if_ntb/if_ntb.c		optional	if_ntb
+dev/ntb/ntb_hw/ntb_hw.c		optional	if_ntb ntb_hw
 dev/nvd/nvd.c			optional	nvd nvme
 dev/nve/if_nve.c		optional	nve pci
 dev/nvme/nvme.c			optional	nvme

Added: head/sys/dev/ntb/if_ntb/if_ntb.c
==============================================================================
--- /dev/null	00:00:00 1970	(empty, because file is newly added)
+++ head/sys/dev/ntb/if_ntb/if_ntb.c	Mon Apr 29 22:48:53 2013	(r250079)
@@ -0,0 +1,1366 @@
+/*-
+ * Copyright (C) 2013 Intel Corporation
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in the
+ *    documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR 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 AUTHOR 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.
+ */
+
+#include <sys/cdefs.h>
+__FBSDID("$FreeBSD$");
+
+#include <sys/param.h>
+#include <sys/kernel.h>
+#include <sys/systm.h>
+#include <sys/bus.h>
+#include <sys/ktr.h>
+#include <sys/lock.h>
+#include <sys/malloc.h>
+#include <sys/module.h>
+#include <sys/mutex.h>
+#include <sys/queue.h>
+#include <sys/socket.h>
+#include <sys/sockio.h>
+#include <sys/taskqueue.h>
+#include <net/if.h>
+#include <net/if_media.h>
+#include <net/if_types.h>
+#include <net/if_var.h>
+#include <net/bpf.h>
+#include <net/ethernet.h>
+#include <vm/vm.h>
+#include <vm/pmap.h>
+#include <machine/bus.h>
+#include <machine/cpufunc.h>
+#include <machine/pmap.h>
+
+#include "../ntb_hw/ntb_hw.h"
+
+/*
+ * The Non-Transparent Bridge (NTB) is a device on some Intel processors that
+ * allows you to connect two systems using a PCI-e link.
+ *
+ * This module contains a protocol for sending and receiving messages, and
+ * exposes that protocol through a simulated ethernet device called ntb.
+ *
+ * NOTE: Much of the code in this module is shared with Linux. Any patches may
+ * be picked up and redistributed in Linux with a dual GPL/BSD license.
+ */
+
+/* TODO: These functions should really be part of the kernel */
+#define test_bit(pos, bitmap_addr)  (*(bitmap_addr) & 1UL << (pos))
+#define set_bit(pos, bitmap_addr)   *(bitmap_addr) |= 1UL << (pos)
+#define clear_bit(pos, bitmap_addr) *(bitmap_addr) &= ~(1UL << (pos))
+
+#define KTR_NTB KTR_SPARE3
+
+#define NTB_TRANSPORT_VERSION	3
+#define NTB_RX_MAX_PKTS		64
+#define	NTB_RXQ_SIZE		300
+
+static unsigned int transport_mtu = 0x4000 + ETHER_HDR_LEN + ETHER_CRC_LEN;
+static unsigned int max_num_clients = 1;
+
+STAILQ_HEAD(ntb_queue_list, ntb_queue_entry);
+
+struct ntb_queue_entry {
+	/* ntb_queue list reference */
+	STAILQ_ENTRY(ntb_queue_entry) entry;
+
+	/* info on data to be transfered */
+	void		*cb_data;
+	void		*buf;
+	uint64_t	len;
+	uint64_t	flags;
+};
+
+struct ntb_rx_info {
+	unsigned int entry;
+};
+
+struct ntb_transport_qp {
+	struct ntb_netdev	*transport;
+	struct ntb_softc	*ntb;
+
+	void			*cb_data;
+
+	bool			client_ready;
+	bool			qp_link;
+	uint8_t			qp_num;	/* Only 64 QP's are allowed.  0-63 */
+
+	struct ntb_rx_info	*rx_info;
+	struct ntb_rx_info	*remote_rx_info;
+
+	void (*tx_handler) (struct ntb_transport_qp *qp, void *qp_data,
+	    void *data, int len);
+	struct ntb_queue_list	tx_free_q;
+	struct mtx		ntb_tx_free_q_lock;
+	void			*tx_mw;
+	uint64_t		tx_index;
+	uint64_t		tx_max_entry;
+	uint64_t		tx_max_frame;
+
+	void (*rx_handler) (struct ntb_transport_qp *qp, void *qp_data,
+	    void *data, int len);
+	struct ntb_queue_list	rx_pend_q;
+	struct ntb_queue_list	rx_free_q;
+	struct mtx		ntb_rx_pend_q_lock;
+	struct mtx		ntb_rx_free_q_lock;
+	struct task		rx_completion_task;
+	void			*rx_buff;
+	uint64_t		rx_index;
+	uint64_t		rx_max_entry;
+	uint64_t		rx_max_frame;
+
+	void (*event_handler) (void *data, int status);
+	struct callout		link_work;
+	struct callout		queue_full;
+	struct callout		rx_full;
+
+	uint64_t		last_rx_no_buf;
+
+	/* Stats */
+	uint64_t		rx_bytes;
+	uint64_t		rx_pkts;
+	uint64_t		rx_ring_empty;
+	uint64_t		rx_err_no_buf;
+	uint64_t		rx_err_oflow;
+	uint64_t		rx_err_ver;
+	uint64_t		tx_bytes;
+	uint64_t		tx_pkts;
+	uint64_t		tx_ring_full;
+};
+
+struct ntb_queue_handlers {
+	void (*rx_handler) (struct ntb_transport_qp *qp, void *qp_data,
+	    void *data, int len);
+	void (*tx_handler) (struct ntb_transport_qp *qp, void *qp_data,
+	    void *data, int len);
+	void (*event_handler) (void *data, int status);
+};
+
+
+struct ntb_transport_mw {
+	size_t		size;
+	void		*virt_addr;
+	vm_paddr_t	dma_addr;
+};
+
+struct ntb_netdev {
+	struct ntb_softc	*ntb;
+	struct ifnet		*ifp;
+	struct ntb_transport_mw	mw[NTB_NUM_MW];
+	struct ntb_transport_qp	*qps;
+	uint64_t		max_qps;
+	uint64_t		qp_bitmap;
+	bool			transport_link;
+	struct callout		link_work;
+	struct ntb_transport_qp *qp;
+	uint64_t		bufsize;
+	u_char			eaddr[ETHER_ADDR_LEN];
+	struct mtx		tx_lock;
+	struct mtx		rx_lock;
+};
+
+static struct ntb_netdev net_softc;
+
+enum {
+	IF_NTB_DESC_DONE_FLAG = 1 << 0,
+	IF_NTB_LINK_DOWN_FLAG = 1 << 1,
+};
+
+struct ntb_payload_header {
+	uint64_t ver;
+	uint64_t len;
+	uint64_t flags;
+};
+
+enum {
+	IF_NTB_VERSION = 0,
+	IF_NTB_MW0_SZ,
+	IF_NTB_MW1_SZ,
+	IF_NTB_NUM_QPS,
+	IF_NTB_QP_LINKS,
+	IF_NTB_MAX_SPAD,
+};
+
+#define QP_TO_MW(qp)		((qp) % NTB_NUM_MW)
+#define NTB_QP_DEF_NUM_ENTRIES	100
+#define NTB_LINK_DOWN_TIMEOUT	10
+
+static int ntb_handle_module_events(struct module *m, int what, void *arg);
+static int ntb_setup_interface(void);
+static int ntb_teardown_interface(void);
+static void ntb_net_init(void *arg);
+static int ntb_ioctl(struct ifnet *ifp, u_long command, caddr_t data);
+static void ntb_start(struct ifnet *ifp);
+static void ntb_net_tx_handler(struct ntb_transport_qp *qp, void *qp_data,
+    void *data, int len);
+static void ntb_net_rx_handler(struct ntb_transport_qp *qp, void *qp_data,
+    void *data, int len);
+static void ntb_net_event_handler(void *data, int status);
+static int ntb_transport_init(struct ntb_softc *ntb);
+static void ntb_transport_free(void *transport);
+static void ntb_transport_init_queue(struct ntb_netdev *nt,
+    unsigned int qp_num);
+static void ntb_transport_free_queue(struct ntb_transport_qp *qp);
+static struct ntb_transport_qp * ntb_transport_create_queue(void *data,
+    struct ntb_softc *pdev, const struct ntb_queue_handlers *handlers);
+static void ntb_transport_link_up(struct ntb_transport_qp *qp);
+static int ntb_transport_tx_enqueue(struct ntb_transport_qp *qp, void *cb,
+    void *data, unsigned int len);
+static int ntb_process_tx(struct ntb_transport_qp *qp,
+    struct ntb_queue_entry *entry);
+static void ntb_tx_copy_task(struct ntb_transport_qp *qp,
+    struct ntb_queue_entry *entry, void *offset);
+static void ntb_qp_full(void *arg);
+static void ntb_transport_rxc_db(void *data, int db_num);
+static void ntb_rx_pendq_full(void *arg);
+static void ntb_transport_rx(struct ntb_transport_qp *qp);
+static int ntb_process_rxc(struct ntb_transport_qp *qp);
+static void ntb_rx_copy_task(struct ntb_transport_qp *qp,
+    struct ntb_queue_entry *entry, void *offset);
+static void ntb_rx_completion_task(void *arg, int pending);
+static void ntb_transport_event_callback(void *data, enum ntb_hw_event event);
+static void ntb_transport_link_work(void *arg);
+static int ntb_set_mw(struct ntb_netdev *nt, int num_mw, unsigned int size);
+static void ntb_transport_setup_qp_mw(struct ntb_netdev *nt,
+    unsigned int qp_num);
+static void ntb_qp_link_work(void *arg);
+static void ntb_transport_link_cleanup(struct ntb_netdev *nt);
+static void ntb_qp_link_down(struct ntb_transport_qp *qp);
+static void ntb_qp_link_cleanup(struct ntb_transport_qp *qp);
+static void ntb_transport_link_down(struct ntb_transport_qp *qp);
+static void ntb_send_link_down(struct ntb_transport_qp *qp);
+static void ntb_list_add(struct mtx *lock, struct ntb_queue_entry *entry,
+    struct ntb_queue_list *list);
+static struct ntb_queue_entry *ntb_list_rm(struct mtx *lock,
+    struct ntb_queue_list *list);
+static void create_random_local_eui48(u_char *eaddr);
+static unsigned int ntb_transport_max_size(struct ntb_transport_qp *qp);
+
+MALLOC_DEFINE(M_NTB_IF, "if_ntb", "ntb network driver");
+
+/* Module setup and teardown */
+static int
+ntb_handle_module_events(struct module *m, int what, void *arg)
+{
+	int err = 0;
+
+	switch (what) {
+	case MOD_LOAD:
+		err = ntb_setup_interface();
+		break;
+	case MOD_UNLOAD:
+		err = ntb_teardown_interface();
+		break;
+	default:
+		err = EOPNOTSUPP;
+		break;
+	}
+	return (err);
+}
+
+static moduledata_t ntb_transport_mod = {
+	"ntb_transport",
+	ntb_handle_module_events,
+	NULL
+};
+
+DECLARE_MODULE(ntb_transport, ntb_transport_mod, SI_SUB_KLD, SI_ORDER_ANY);
+MODULE_DEPEND(ntb_transport, ntb_hw, 1, 1, 1);
+
+static int
+ntb_setup_interface()
+{
+	struct ifnet *ifp;
+	struct ntb_queue_handlers handlers = { ntb_net_rx_handler,
+	    ntb_net_tx_handler, ntb_net_event_handler };
+
+	net_softc.ntb = devclass_get_softc(devclass_find("ntb_hw"), 0);
+	if (net_softc.ntb == NULL) {
+		printf("ntb: Can't find devclass\n");
+		return (ENXIO);
+	}
+
+	ntb_transport_init(net_softc.ntb);
+
+	ifp = net_softc.ifp = if_alloc(IFT_ETHER);
+	if (ifp == NULL) {
+		printf("ntb: cannot allocate ifnet structure\n");
+		return (ENOMEM);
+	}
+
+	net_softc.qp = ntb_transport_create_queue(ifp, net_softc.ntb,
+	    &handlers);
+	if_initname(ifp, "ntb", 0);
+	ifp->if_init = ntb_net_init;
+	ifp->if_softc = &net_softc;
+	ifp->if_flags = IFF_BROADCAST | IFF_SIMPLEX;
+	ifp->if_ioctl = ntb_ioctl;
+	ifp->if_start = ntb_start;
+	IFQ_SET_MAXLEN(&ifp->if_snd, IFQ_MAXLEN);
+	ifp->if_snd.ifq_drv_maxlen = IFQ_MAXLEN;
+	IFQ_SET_READY(&ifp->if_snd);
+	create_random_local_eui48(net_softc.eaddr);
+	ether_ifattach(ifp, net_softc.eaddr);
+	ifp->if_capabilities = IFCAP_HWCSUM | IFCAP_JUMBO_MTU;
+	ifp->if_capenable = ifp->if_capabilities;
+
+	ntb_transport_link_up(net_softc.qp);
+	net_softc.bufsize = ntb_transport_max_size(net_softc.qp) +
+	    sizeof(struct ether_header);
+	return (0);
+}
+
+static int
+ntb_teardown_interface()
+{
+	struct ifnet *ifp = net_softc.ifp;
+
+	ntb_transport_link_down(net_softc.qp);
+
+	ether_ifdetach(ifp);
+	if_free(ifp);
+	ntb_transport_free_queue(net_softc.qp);
+	ntb_transport_free(&net_softc);
+
+	return (0);
+}
+
+/* Network device interface */
+
+static void
+ntb_net_init(void *arg)
+{
+	struct ntb_netdev *ntb_softc = arg;
+	struct ifnet *ifp = ntb_softc->ifp;
+
+	ifp->if_drv_flags |= IFF_DRV_RUNNING;
+	ifp->if_drv_flags &= ~IFF_DRV_OACTIVE;
+	ifp->if_flags |= IFF_UP;
+	if_link_state_change(ifp, LINK_STATE_UP);
+}
+
+static int
+ntb_ioctl(struct ifnet *ifp, u_long command, caddr_t data)
+{
+	struct ntb_netdev *nt = ifp->if_softc;
+	struct ifreq *ifr = (struct ifreq *)data;
+	int error = 0;
+
+	switch (command) {
+	case SIOCSIFMTU:
+	    {
+		if (ifr->ifr_mtu > ntb_transport_max_size(nt->qp) -
+		    ETHER_HDR_LEN - ETHER_CRC_LEN) {
+			error = EINVAL;
+			break;
+		}
+
+		ifp->if_mtu = ifr->ifr_mtu;
+		break;
+	    }
+	default:
+		error = ether_ioctl(ifp, command, data);
+		break;
+	}
+
+	return (error);
+}
+
+
+static void
+ntb_start(struct ifnet *ifp)
+{
+	struct mbuf *m_head;
+	struct ntb_netdev *nt = ifp->if_softc;
+	int rc;
+
+	mtx_lock(&nt->tx_lock);
+	ifp->if_drv_flags &= ~IFF_DRV_OACTIVE;
+	CTR0(KTR_NTB, "TX: ntb_start");
+	while (!IFQ_DRV_IS_EMPTY(&ifp->if_snd)) {
+		IFQ_DRV_DEQUEUE(&ifp->if_snd, m_head);
+		CTR1(KTR_NTB, "TX: start mbuf %p", m_head);
+		rc = ntb_transport_tx_enqueue(nt->qp, m_head, m_head,
+			     m_length(m_head, NULL));
+		if (rc != 0) {
+			CTR1(KTR_NTB,
+			    "TX: couldn't tx mbuf %p. Returning to snd q",
+			    m_head);
+			if (rc == EAGAIN) {
+				ifp->if_drv_flags |= IFF_DRV_OACTIVE;
+				IFQ_DRV_PREPEND(&ifp->if_snd, m_head);
+				callout_reset(&nt->qp->queue_full, hz / 1000,
+				    ntb_qp_full, ifp);
+			}
+			break;
+		}
+
+	}
+	mtx_unlock(&nt->tx_lock);
+}
+
+/* Network Device Callbacks */
+static void
+ntb_net_tx_handler(struct ntb_transport_qp *qp, void *qp_data, void *data,
+    int len)
+{
+
+	m_freem(data);
+	CTR1(KTR_NTB, "TX: tx_handler freeing mbuf %p", data);
+}
+
+static void
+ntb_net_rx_handler(struct ntb_transport_qp *qp, void *qp_data, void *data,
+    int len)
+{
+	struct mbuf *m = data;
+	struct ifnet *ifp = qp_data;
+
+	CTR0(KTR_NTB, "RX: rx handler");
+	(*ifp->if_input)(ifp, m);
+}
+
+static void
+ntb_net_event_handler(void *data, int status)
+{
+
+}
+
+/* Transport Init and teardown */
+
+static int
+ntb_transport_init(struct ntb_softc *ntb)
+{
+	struct ntb_netdev *nt = &net_softc;
+	int rc, i;
+
+	nt->max_qps = max_num_clients;
+	ntb_register_transport(ntb, nt);
+	mtx_init(&nt->tx_lock, "ntb transport tx", NULL, MTX_DEF);
+	mtx_init(&nt->rx_lock, "ntb transport rx", NULL, MTX_DEF);
+
+	nt->qps = malloc(nt->max_qps * sizeof(struct ntb_transport_qp),
+			  M_NTB_IF, M_WAITOK|M_ZERO);
+
+	nt->qp_bitmap = ((uint64_t) 1 << nt->max_qps) - 1;
+
+	for (i = 0; i < nt->max_qps; i++)
+		ntb_transport_init_queue(nt, i);
+
+	callout_init(&nt->link_work, 0);
+
+	rc = ntb_register_event_callback(ntb,
+					 ntb_transport_event_callback);
+	if (rc != 0)
+		goto err;
+
+	if (ntb_query_link_status(ntb))
+		callout_reset(&nt->link_work, 0, ntb_transport_link_work, nt);
+
+	return (0);
+
+err:
+	free(nt->qps, M_NTB_IF);
+	ntb_unregister_transport(ntb);
+	return (rc);
+}
+
+static void
+ntb_transport_free(void *transport)
+{
+	struct ntb_netdev *nt = transport;
+	struct ntb_softc *ntb = nt->ntb;
+	int i;
+
+	nt->transport_link = NTB_LINK_DOWN;
+
+	callout_drain(&nt->link_work);
+
+	/* verify that all the qp's are freed */
+	for (i = 0; i < nt->max_qps; i++)
+		if (!test_bit(i, &nt->qp_bitmap))
+			ntb_transport_free_queue(&nt->qps[i]);
+
+
+	ntb_unregister_event_callback(ntb);
+
+	for (i = 0; i < NTB_NUM_MW; i++)
+		if (nt->mw[i].virt_addr != NULL)
+			contigfree(nt->mw[i].virt_addr, nt->mw[i].size,
+					  M_NTB_IF);
+
+	free(nt->qps, M_NTB_IF);
+	ntb_unregister_transport(ntb);
+}
+
+static void
+ntb_transport_init_queue(struct ntb_netdev *nt, unsigned int qp_num)
+{
+	struct ntb_transport_qp *qp;
+	unsigned int num_qps_mw, tx_size;
+	uint8_t mw_num = QP_TO_MW(qp_num);
+
+	qp = &nt->qps[qp_num];
+	qp->qp_num = qp_num;
+	qp->transport = nt;
+	qp->ntb = nt->ntb;
+	qp->qp_link = NTB_LINK_DOWN;
+	qp->client_ready = NTB_LINK_DOWN;
+	qp->event_handler = NULL;
+
+	if (nt->max_qps % NTB_NUM_MW && mw_num < nt->max_qps % NTB_NUM_MW)
+		num_qps_mw = nt->max_qps / NTB_NUM_MW + 1;
+	else
+		num_qps_mw = nt->max_qps / NTB_NUM_MW;
+
+	tx_size = (unsigned int) ntb_get_mw_size(qp->ntb, mw_num) / num_qps_mw;
+	qp->rx_info = (struct ntb_rx_info *)
+	    ((char *)ntb_get_mw_vbase(qp->ntb, mw_num) +
+	    (qp_num / NTB_NUM_MW * tx_size));
+	tx_size -= sizeof(struct ntb_rx_info);
+
+	qp->tx_mw = qp->rx_info + sizeof(struct ntb_rx_info);
+	qp->tx_max_frame = min(transport_mtu + sizeof(struct ntb_payload_header),
+	    tx_size);
+	qp->tx_max_entry = tx_size / qp->tx_max_frame;
+	qp->tx_index = 0;
+
+	callout_init(&qp->link_work, 0);
+	callout_init(&qp->queue_full, CALLOUT_MPSAFE);
+	callout_init(&qp->rx_full, CALLOUT_MPSAFE);
+
+	mtx_init(&qp->ntb_rx_pend_q_lock, "ntb rx pend q", NULL, MTX_SPIN);
+	mtx_init(&qp->ntb_rx_free_q_lock, "ntb rx free q", NULL, MTX_SPIN);
+	mtx_init(&qp->ntb_tx_free_q_lock, "ntb tx free q", NULL, MTX_SPIN);
+	TASK_INIT(&qp->rx_completion_task, 0, ntb_rx_completion_task, qp);
+
+	STAILQ_INIT(&qp->rx_pend_q);
+	STAILQ_INIT(&qp->rx_free_q);
+	STAILQ_INIT(&qp->tx_free_q);
+}
+
+static void
+ntb_transport_free_queue(struct ntb_transport_qp *qp)
+{
+	struct ntb_queue_entry *entry;
+
+	if (qp == NULL)
+		return;
+
+	callout_drain(&qp->link_work);
+
+	ntb_unregister_db_callback(qp->ntb, qp->qp_num);
+
+	while ((entry = ntb_list_rm(&qp->ntb_rx_free_q_lock, &qp->rx_free_q)))
+		free(entry, M_NTB_IF);
+
+	while ((entry = ntb_list_rm(&qp->ntb_rx_pend_q_lock, &qp->rx_pend_q)))
+		free(entry, M_NTB_IF);
+
+	while ((entry = ntb_list_rm(&qp->ntb_tx_free_q_lock, &qp->tx_free_q)))
+		free(entry, M_NTB_IF);
+
+	set_bit(qp->qp_num, &qp->transport->qp_bitmap);
+}
+
+/**
+ * ntb_transport_create_queue - Create a new NTB transport layer queue
+ * @rx_handler: receive callback function
+ * @tx_handler: transmit callback function
+ * @event_handler: event callback function
+ *
+ * Create a new NTB transport layer queue and provide the queue with a callback
+ * routine for both transmit and receive.  The receive callback routine will be
+ * used to pass up data when the transport has received it on the queue.   The
+ * transmit callback routine will be called when the transport has completed the
+ * transmission of the data on the queue and the data is ready to be freed.
+ *
+ * RETURNS: pointer to newly created ntb_queue, NULL on error.
+ */
+static struct ntb_transport_qp *
+ntb_transport_create_queue(void *data, struct ntb_softc *pdev,
+    const struct ntb_queue_handlers *handlers)
+{
+	struct ntb_queue_entry *entry;
+	struct ntb_transport_qp *qp;
+	struct ntb_netdev *nt;
+	unsigned int free_queue;
+	int rc, i;
+
+	nt = ntb_find_transport(pdev);
+	if (nt == NULL)
+		goto err;
+
+	free_queue = ffs(nt->qp_bitmap);
+	if (free_queue == 0)
+		goto err;
+
+	/* decrement free_queue to make it zero based */
+	free_queue--;
+
+	clear_bit(free_queue, &nt->qp_bitmap);
+
+	qp = &nt->qps[free_queue];
+	qp->cb_data = data;
+	qp->rx_handler = handlers->rx_handler;
+	qp->tx_handler = handlers->tx_handler;
+	qp->event_handler = handlers->event_handler;
+
+	for (i = 0; i < NTB_QP_DEF_NUM_ENTRIES; i++) {
+		entry = malloc(sizeof(struct ntb_queue_entry), M_NTB_IF,
+		    M_WAITOK|M_ZERO);
+		entry->cb_data = nt->ifp;
+		entry->buf = NULL;
+		entry->len = transport_mtu;
+		ntb_list_add(&qp->ntb_rx_pend_q_lock, entry, &qp->rx_pend_q);
+	}
+
+	for (i = 0; i < NTB_QP_DEF_NUM_ENTRIES; i++) {
+		entry = malloc(sizeof(struct ntb_queue_entry), M_NTB_IF,
+		    M_WAITOK|M_ZERO);
+		ntb_list_add(&qp->ntb_tx_free_q_lock, entry, &qp->tx_free_q);
+	}
+
+	rc = ntb_register_db_callback(qp->ntb, free_queue, qp,
+				      ntb_transport_rxc_db);
+	if (rc != 0)
+		goto err1;
+
+	return (qp);
+
+err1:
+	while ((entry = ntb_list_rm(&qp->ntb_tx_free_q_lock, &qp->tx_free_q)))
+		free(entry, M_NTB_IF);
+	while ((entry = ntb_list_rm(&qp->ntb_rx_free_q_lock, &qp->rx_free_q)))
+		free(entry, M_NTB_IF);
+	set_bit(free_queue, &nt->qp_bitmap);
+err:
+	return (NULL);
+}
+
+/**
+ * ntb_transport_link_up - Notify NTB transport of client readiness to use queue
+ * @qp: NTB transport layer queue to be enabled
+ *
+ * Notify NTB transport layer of client readiness to use queue
+ */
+static void
+ntb_transport_link_up(struct ntb_transport_qp *qp)
+{
+
+	if (qp == NULL)
+		return;
+
+	qp->client_ready = NTB_LINK_UP;
+
+	if (qp->transport->transport_link == NTB_LINK_UP)
+		callout_reset(&qp->link_work, 0, ntb_qp_link_work, qp);
+}
+
+
+
+/* Transport Tx */
+
+/**
+ * ntb_transport_tx_enqueue - Enqueue a new NTB queue entry
+ * @qp: NTB transport layer queue the entry is to be enqueued on
+ * @cb: per buffer pointer for callback function to use
+ * @data: pointer to data buffer that will be sent
+ * @len: length of the data buffer
+ *
+ * Enqueue a new transmit buffer onto the transport queue from which a NTB
+ * payload will be transmitted.  This assumes that a lock is behing held to
+ * serialize access to the qp.
+ *
+ * RETURNS: An appropriate ERRNO error value on error, or zero for success.
+ */
+static int
+ntb_transport_tx_enqueue(struct ntb_transport_qp *qp, void *cb, void *data,
+    unsigned int len)
+{
+	struct ntb_queue_entry *entry;
+	int rc;
+
+	if (qp == NULL || qp->qp_link != NTB_LINK_UP || len == 0) {
+		CTR0(KTR_NTB, "TX: link not up");
+		return (EINVAL);
+	}
+
+	entry = ntb_list_rm(&qp->ntb_tx_free_q_lock, &qp->tx_free_q);
+	if (entry == NULL) {
+		CTR0(KTR_NTB, "TX: couldn't get entry from tx_free_q");
+		return (ENOMEM);
+	}
+	CTR1(KTR_NTB, "TX: got entry %p from tx_free_q", entry);
+
+	entry->cb_data = cb;
+	entry->buf = data;
+	entry->len = len;
+	entry->flags = 0;
+
+	rc = ntb_process_tx(qp, entry);
+	if (rc != 0) {
+		ntb_list_add(&qp->ntb_tx_free_q_lock, entry, &qp->tx_free_q);
+		CTR1(KTR_NTB,
+		    "TX: process_tx failed. Returning entry %p to tx_free_q",
+		    entry);
+	}
+	return (rc);
+}
+
+static int
+ntb_process_tx(struct ntb_transport_qp *qp, struct ntb_queue_entry *entry)
+{
+	void *offset;
+
+	offset = (char *)qp->tx_mw + qp->tx_max_frame * qp->tx_index;
+	CTR3(KTR_NTB,
+	    "TX: process_tx: tx_pkts=%u, tx_index=%u, remote entry=%u",
+	    qp->tx_pkts, qp->tx_index, qp->remote_rx_info->entry);
+	if (qp->tx_index == qp->remote_rx_info->entry) {
+		CTR0(KTR_NTB, "TX: ring full");
+		qp->tx_ring_full++;
+		return (EAGAIN);
+	}
+
+	if (entry->len > qp->tx_max_frame - sizeof(struct ntb_payload_header)) {
+		if (qp->tx_handler != NULL)
+			qp->tx_handler(qp, qp->cb_data, entry->buf,
+				       EIO);
+
+		ntb_list_add(&qp->ntb_tx_free_q_lock, entry, &qp->tx_free_q);
+		CTR1(KTR_NTB,
+		    "TX: frame too big. returning entry %p to tx_free_q",
+		    entry);
+		return (0);
+	}
+	CTR2(KTR_NTB, "TX: copying entry %p to offset %p", entry, offset);
+	ntb_tx_copy_task(qp, entry, offset);
+
+	qp->tx_index++;
+	qp->tx_index %= qp->tx_max_entry;
+
+	qp->tx_pkts++;
+
+	return (0);
+}
+
+static void
+ntb_tx_copy_task(struct ntb_transport_qp *qp, struct ntb_queue_entry *entry,
+    void *offset)
+{
+	struct ntb_payload_header *hdr;
+
+	CTR2(KTR_NTB, "TX: copying %d bytes to offset %p", entry->len, offset);
+	if (entry->buf != NULL)
+		m_copydata((struct mbuf *)entry->buf, 0, entry->len, offset);
+
+	hdr = (struct ntb_payload_header *)((char *)offset + qp->tx_max_frame -
+	    sizeof(struct ntb_payload_header));
+	hdr->len = entry->len; /* TODO: replace with bus_space_write */
+	hdr->ver = qp->tx_pkts; /* TODO: replace with bus_space_write */
+	wmb();
+	/* TODO: replace with bus_space_write */
+	hdr->flags = entry->flags | IF_NTB_DESC_DONE_FLAG;
+
+	ntb_ring_sdb(qp->ntb, qp->qp_num);
+
+	/* 
+	 * The entry length can only be zero if the packet is intended to be a
+	 * "link down" or similar.  Since no payload is being sent in these
+	 * cases, there is nothing to add to the completion queue.
+	 */
+	if (entry->len > 0) {
+		qp->tx_bytes += entry->len;
+
+		if (qp->tx_handler)
+			qp->tx_handler(qp, qp->cb_data, entry->cb_data,
+				       entry->len);
+	}
+
+	CTR2(KTR_NTB,
+	    "TX: entry %p sent. hdr->ver = %d, Returning to tx_free_q", entry,
+	    hdr->ver);
+	ntb_list_add(&qp->ntb_tx_free_q_lock, entry, &qp->tx_free_q);
+}
+
+static void
+ntb_qp_full(void *arg)
+{
+
+	CTR0(KTR_NTB, "TX: qp_full callout");
+	ntb_start(arg);
+}
+
+/* Transport Rx */
+static void
+ntb_transport_rxc_db(void *data, int db_num)
+{
+	struct ntb_transport_qp *qp = data;
+
+	ntb_transport_rx(qp);
+}
+
+static void
+ntb_rx_pendq_full(void *arg)
+{
+

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


More information about the svn-src-head mailing list