[openib-general] [PATCH 08/13] [RFC] ipath core last bit

Roland Dreier rolandd at cisco.com
Fri Dec 16 15:48:55 PST 2005


Last piece of ipath LLD

---

 drivers/infiniband/hw/ipath/ipath_layer.c | 1155 +++++++++++++++++++++++++++++
 1 files changed, 1155 insertions(+), 0 deletions(-)
 create mode 100644 drivers/infiniband/hw/ipath/ipath_layer.c

978ded82c9b5a4bca4e55f36d20ef4a585c50f38
diff --git a/drivers/infiniband/hw/ipath/ipath_layer.c b/drivers/infiniband/hw/ipath/ipath_layer.c
new file mode 100644
index 0000000..6a60851
--- /dev/null
+++ b/drivers/infiniband/hw/ipath/ipath_layer.c
@@ -0,0 +1,1155 @@
+/*
+ * Copyright (c) 2003, 2004, 2005. PathScale, Inc. All rights reserved.
+ *
+ * This software is available to you under a choice of one of two
+ * licenses.  You may choose to be licensed under the terms of the GNU
+ * General Public License (GPL) Version 2, available from the file
+ * COPYING in the main directory of this source tree, or the
+ * OpenIB.org BSD license below:
+ *
+ *     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
+ *        copyright notice, this list of conditions and the following
+ *        disclaimer in the documentation and/or other materials
+ *        provided with the distribution.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+ * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+ * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+ * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
+ * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
+ * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
+ * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+ * SOFTWARE.
+ *
+ * Patent licenses, if any, provided herein do not apply to
+ * combinations of this program with other software, or any other
+ * product whatsoever.
+ *
+ * $Id: ipath_layer.c 4365 2005-12-10 00:04:16Z rjwalsh $
+ */
+
+/*
+ * These are the routines used by layered drivers, currently just the
+ * layered ethernet driver and verbs layer.
+ */
+
+#include <linux/pci.h>
+
+#include "ipath_kernel.h"
+#include "ips_common.h"
+#include "ipath_layer.h"
+
+/* unit number is already validated in ipath_ioctl() */
+int ipath_kset_linkstate(uint32_t arg)
+{
+	ipath_type unit = 0xffff & (arg >> 16);
+	uint32_t lstate;
+	ipath_devdata *dd;
+
+	if (unit >= infinipath_max ||
+	    !(devdata[unit].ipath_flags & IPATH_INITTED)) {
+		_IPATH_DBG("Invalid unit %u\n", unit);
+		return -ENODEV;
+	}
+
+	dd = &devdata[unit];
+	arg &= 0xffff;
+	if (arg != IPATH_IB_LINKDOWN && arg != IPATH_IB_LINKARM &&
+	    arg != IPATH_IB_LINKACTIVE) {
+		_IPATH_DBG("Unknown linkstate 0x%x requested\n", arg);
+		return -EINVAL;
+	}
+	if (arg == IPATH_IB_LINKDOWN) {
+		ipath_down_link(unit);	/* really moving it to idle */
+		lstate = IPATH_LINKDOWN | IPATH_LINK_SLEEPING;
+	} else if (arg == IPATH_IB_LINKARM) {
+		if (!(dd->ipath_flags &
+		      (IPATH_LINKINIT | IPATH_LINKARMED | IPATH_LINKDOWN |
+		       IPATH_LINK_SLEEPING | IPATH_LINKACTIVE)))
+			_IPATH_DBG
+			    ("don't know current state (flags 0x%x), try anyway\n",
+			     dd->ipath_flags);
+		ipath_set_ib_lstate(unit, INFINIPATH_IBCC_LINKCMD_ARMED);
+		lstate = IPATH_LINKARMED;
+	} else {
+		int tryarmed = 0;
+		/*
+		 * because we sometimes go to ARMED, but then back to 0x11
+		 * (initialized) before the SMA asks us to move to ACTIVE,
+		 * we will try to advance state to ARMED here, if necessary
+		 */
+		if (!(dd->ipath_flags &
+		      (IPATH_LINKINIT | IPATH_LINKARMED | IPATH_LINKDOWN |
+		       IPATH_LINK_SLEEPING | IPATH_LINKACTIVE))) {
+			/* this one is just paranoia */
+			_IPATH_DBG
+			    ("don't know current state (flags 0x%x), try anyway\n",
+			     dd->ipath_flags);
+			tryarmed = 1;
+
+		}
+		if (!(dd->ipath_flags & (IPATH_LINKARMED | IPATH_LINKACTIVE)))
+			tryarmed = 1;
+		if (tryarmed) {
+			ipath_set_ib_lstate(unit,
+					    INFINIPATH_IBCC_LINKCMD_ARMED);
+			/*
+			 * give it up to 2 seconds to get to ARMED or
+			 * ACTIVE; continue afterwards even if we fail
+			 */
+			if (ipath_wait_linkstate
+			    (unit, IPATH_LINKARMED | IPATH_LINKACTIVE, 2000))
+				_IPATH_VDBG
+				    ("try for active, even though didn't get to ARMED\n");
+		}
+
+		ipath_set_ib_lstate(unit, INFINIPATH_IBCC_LINKCMD_ACTIVE);
+		lstate = IPATH_LINKACTIVE;
+	}
+	return ipath_wait_linkstate(unit, lstate, 5000);
+}
+
+/*
+ * we can handle "any" incoming size, the issue here is whether we
+ * need to restrict our outgoing size.   For now, we don't do any
+ * sanity checking on this, and we don't deal with what happens to
+ * programs that are already running when the size changes.
+ * unit number is already validated in ipath_ioctl()
+ * NOTE: changing the MTU will usually cause the IBC to go back to
+ * link initialize (0x11) state...
+ */
+int ipath_kset_mtu(uint32_t arg)
+{
+	unsigned unit = (arg >> 16) & 0xffff;
+	uint32_t piosize;
+	int changed = 0;
+
+	if (unit >= infinipath_max ||
+	    !(devdata[unit].ipath_flags & IPATH_INITTED)) {
+		_IPATH_DBG("Invalid unit %u\n", unit);
+		return -ENODEV;
+	}
+
+	arg &= 0xffff;
+	/*
+	 * mtu is IB data payload max.  It's the largest power of 2 less
+	 * than piosize (or even larger, since it only really controls the
+	 * largest we can receive; we can send the max of the mtu and piosize).
+	 * We check that it's one of the valid IB sizes.
+	 */
+	if (arg != 256 && arg != 512 && arg != 1024 && arg != 2048 &&
+	    arg != 4096) {
+		_IPATH_DBG("Trying to set invalid mtu %u, failing\n", arg);
+		return -EINVAL;
+	}
+	if (devdata[unit].ipath_ibmtu == arg) {
+		return 0;	/* same as current */
+	}
+
+	piosize = devdata[unit].ipath_ibmaxlen;
+	devdata[unit].ipath_ibmtu = arg;
+
+	/*
+	 * the 128 is the max IB header size allowed for in our pio send buffers
+	 * If we are reducing the MTU below that, this doesn't completely make
+	 * sense, but it's OK.
+	 */
+	if (arg >= (piosize - 128)) {
+		/* hasn't been changed */
+		if (piosize == devdata[unit].ipath_init_ibmaxlen)
+			_IPATH_VDBG
+			    ("mtu 0x%x >= ibmaxlen hardware max, nothing to do\n",
+			     arg);
+		else {
+			_IPATH_VDBG
+			    ("mtu 0x%x restores ibmaxlen to full amount 0x%x\n",
+			     arg, piosize);
+			devdata[unit].ipath_ibmaxlen = piosize;
+			changed = 1;
+		}
+	} else if ((arg + 128) == devdata[unit].ipath_ibmaxlen)
+		_IPATH_VDBG("ibmaxlen %x same as current, no change\n", arg);
+	else {
+		piosize = arg + 128;
+		_IPATH_VDBG("ibmaxlen was 0x%x, setting to 0x%x (mtu 0x%x)\n",
+			    devdata[unit].ipath_ibmaxlen, piosize, arg);
+		devdata[unit].ipath_ibmaxlen = piosize;
+		changed = 1;
+	}
+
+	if (changed) {
+		/*
+		 * set the IBC maxpktlength to the size of our pio
+		 * buffers in words
+		 */
+		uint64_t ibc = devdata[unit].ipath_ibcctrl;
+		ibc &= ~(INFINIPATH_IBCC_MAXPKTLEN_MASK <<
+			 INFINIPATH_IBCC_MAXPKTLEN_SHIFT);
+
+		piosize = piosize - 2 * sizeof(uint32_t);	/* ignore pbc */
+		devdata[unit].ipath_ibmaxlen = piosize;
+		piosize /= sizeof(uint32_t);	/* in words */
+		/*
+		 * for ICRC, which we only send in diag test pkt mode, and we
+		 * don't need to worry about that for mtu
+		 */
+		piosize += 1;
+
+		ibc |= piosize << INFINIPATH_IBCC_MAXPKTLEN_SHIFT;
+		devdata[unit].ipath_ibcctrl = ibc;
+		ipath_kput_kreg(unit, kr_ibcctrl, devdata[unit].ipath_ibcctrl);
+	}
+	return 0;
+}
+
+void ipath_set_sps_lid(const ipath_type unit, uint32_t arg)
+{
+	if (unit >= infinipath_max ||
+	    !(devdata[unit].ipath_flags & IPATH_INITTED)) {
+		_IPATH_DBG("Invalid unit %u\n", unit);
+		return;
+	}
+
+	ipath_stats.sps_lid[unit] = devdata[unit].ipath_lid = arg;
+	if (devdata[unit].ipath_layer.l_intr)
+		devdata[unit].ipath_layer.l_intr(unit, IPATH_LAYER_INT_LID);
+}
+
+/* XXX - need to inform anyone who cares this just happened. */
+int ipath_layer_set_guid(const ipath_type device, uint64_t guid)
+{
+	if (device >= infinipath_max ||
+	    !(devdata[device].ipath_flags & IPATH_INITTED)) {
+		_IPATH_DBG("Invalid unit %u\n", device);
+		return -ENODEV;
+	}
+	devdata[device].ipath_guid = guid;
+	return 0;
+}
+
+uint64_t ipath_layer_get_guid(const ipath_type device)
+{
+	if (device >= infinipath_max ||
+	    !(devdata[device].ipath_flags & IPATH_INITTED)) {
+		_IPATH_DBG("Invalid unit %u\n", device);
+		return 0;
+	}
+	return devdata[device].ipath_guid;
+}
+
+uint32_t ipath_layer_get_nguid(const ipath_type device)
+{
+	if (device >= infinipath_max ||
+	    !(devdata[device].ipath_flags & IPATH_INITTED)) {
+		_IPATH_DBG("Invalid unit %u\n", device);
+		return 0;
+	}
+	return devdata[device].ipath_nguid;
+}
+
+int ipath_layer_query_device(const ipath_type device, uint32_t * vendor,
+			     uint32_t * boardrev, uint32_t * majrev,
+			     uint32_t * minrev)
+{
+	if (device >= infinipath_max ||
+	    !(devdata[device].ipath_flags & IPATH_INITTED)) {
+		_IPATH_DBG("Invalid unit %u\n", device);
+		return -ENODEV;
+	}
+
+	*vendor = devdata[device].ipath_vendorid;
+	*boardrev = devdata[device].ipath_boardrev;
+	*majrev = devdata[device].ipath_majrev;
+	*minrev = devdata[device].ipath_minrev;
+
+	return 0;
+}
+
+uint32_t ipath_layer_get_flags(const ipath_type device)
+{
+	if (device >= infinipath_max ||
+	    !(devdata[device].ipath_flags & IPATH_INITTED)) {
+		_IPATH_DBG("Invalid unit %u\n", device);
+		return 0;
+	}
+
+	return devdata[device].ipath_flags;
+}
+
+struct device *ipath_layer_get_pcidev(const ipath_type device)
+{
+	if (device >= infinipath_max ||
+	    !(devdata[device].ipath_flags & IPATH_INITTED)) {
+		_IPATH_DBG("Invalid unit %u\n", device);
+		return NULL;
+	}
+
+	return &(devdata[device].pcidev->dev);
+}
+
+uint16_t ipath_layer_get_deviceid(const ipath_type device)
+{
+	if (device >= infinipath_max ||
+	    !(devdata[device].ipath_flags & IPATH_INITTED)) {
+		_IPATH_DBG("Invalid unit %u\n", device);
+		return 0;
+	}
+
+	return devdata[device].ipath_deviceid;
+}
+
+uint64_t ipath_layer_get_lastibcstat(const ipath_type device)
+{
+	if (device >= infinipath_max ||
+	    !(devdata[device].ipath_flags & IPATH_INITTED)) {
+		_IPATH_DBG("Invalid unit %u\n", device);
+		return 0;
+	}
+
+	return devdata[device].ipath_lastibcstat;
+}
+
+uint32_t ipath_layer_get_ibmtu(const ipath_type device)
+{
+	if (device >= infinipath_max ||
+	    !(devdata[device].ipath_flags & IPATH_INITTED)) {
+		_IPATH_DBG("Invalid unit %u\n", device);
+		return 0;
+	}
+
+	return devdata[device].ipath_ibmtu;
+}
+
+int ipath_layer_register(const ipath_type device,
+			 int (*l_intr) (const ipath_type, uint32_t),
+			 int (*l_rcv) (const ipath_type, void *,
+				       struct sk_buff *), uint16_t l_rcv_opcode,
+			 int (*l_rcv_lid) (const ipath_type, void *),
+			 uint16_t l_rcv_lid_opcode)
+{
+	int ret = 0;
+
+	if (device >= infinipath_max) {
+		_IPATH_DBG("Invalid unit %u\n", device);
+		return 1;
+	}
+	if (!(devdata[device].ipath_flags & IPATH_INITTED)) {
+		_IPATH_VDBG("%s not yet initialized, failing\n",
+			    ipath_get_unit_name(device));
+		return 1;
+	}
+
+	_IPATH_VDBG("intr %p rx %p, rx_lid %p\n", l_intr, l_rcv, l_rcv_lid);
+	if (devdata[device].ipath_layer.l_intr
+	    || devdata[device].ipath_layer.l_rcv) {
+		_IPATH_DBG
+		    ("Layered device already registered on unit %u, failing\n",
+		     device);
+		return 1;
+	}
+
+	if(!(*devdata[device].ipath_statusp & IPATH_STATUS_SMA))
+		*devdata[device].ipath_statusp |= IPATH_STATUS_OIB_SMA;
+	devdata[device].ipath_layer.l_intr = l_intr;
+	devdata[device].ipath_layer.l_rcv = l_rcv;
+	devdata[device].ipath_layer.l_rcv_lid = l_rcv_lid;
+	devdata[device].ipath_layer.l_rcv_opcode = l_rcv_opcode;
+	devdata[device].ipath_layer.l_rcv_lid_opcode = l_rcv_lid_opcode;
+
+	return ret;
+}
+
+static void ipath_verbs_timer(unsigned long t)
+{
+	/*
+	 * If port 0 receive packet interrupts are not availabile,
+	 * check the receive queue.
+	 */
+	if (!(devdata[t].ipath_flags & IPATH_GPIO_INTR))
+		ipath_kreceive(t);
+
+	/* Handle verbs layer timeouts. */
+	if (devdata[t].verbs_layer.l_timer_cb)
+		devdata[t].verbs_layer.l_timer_cb(t);
+
+	mod_timer(&devdata[t].verbs_layer.l_timer, jiffies + 1);
+}
+
+/* Verbs layer registration. */
+int ipath_verbs_register(const ipath_type device,
+			  int (*l_piobufavail) (const ipath_type device),
+			  void (*l_rcv) (const ipath_type device, void *rhdr,
+					 void *data, u32 tlen),
+			  void (*l_timer_cb) (const ipath_type device))
+{
+	if (device >= infinipath_max) {
+		_IPATH_DBG("Invalid unit %u\n", device);
+		return 0;
+	}
+	if (!(devdata[device].ipath_flags & IPATH_INITTED)) {
+		_IPATH_VDBG("%s not yet initialized, failing\n",
+			    ipath_get_unit_name(device));
+		return 0;
+	}
+
+	_IPATH_VDBG("piobufavail %p rx %p\n", l_piobufavail, l_rcv);
+	if (devdata[device].verbs_layer.l_piobufavail ||
+	    devdata[device].verbs_layer.l_rcv) {
+		_IPATH_DBG("Verbs layer already registered on unit %u, "
+			   "failing\n", device);
+		return 0;
+	}
+
+	devdata[device].verbs_layer.l_piobufavail = l_piobufavail;
+	devdata[device].verbs_layer.l_rcv = l_rcv;
+	devdata[device].verbs_layer.l_timer_cb = l_timer_cb;
+	devdata[device].verbs_layer.l_flags = 0;
+
+	return 1;
+}
+
+void ipath_verbs_unregister(const ipath_type device)
+{
+	if (device >= infinipath_max) {
+		_IPATH_DBG("Invalid unit %u\n", device);
+		return;
+	}
+	if (!(devdata[device].ipath_flags & IPATH_INITTED)) {
+		_IPATH_VDBG("%s not yet initialized, failing\n",
+			    ipath_get_unit_name(device));
+		return;
+	}
+
+	*devdata[device].ipath_statusp &= ~IPATH_STATUS_OIB_SMA;
+	devdata[device].verbs_layer.l_piobufavail = NULL;
+	devdata[device].verbs_layer.l_rcv = NULL;
+	devdata[device].verbs_layer.l_timer_cb = NULL;
+	devdata[device].verbs_layer.l_flags = 0;
+}
+
+int ipath_layer_open(const ipath_type device, uint32_t * pktmax)
+{
+	int ret = 0;
+	uint32_t intval = 0;
+
+	if (device >= infinipath_max) {
+		_IPATH_DBG("Invalid unit %u\n", device);
+		return 1;
+	}
+	if (!devdata[device].ipath_layer.l_intr
+	    || !devdata[device].ipath_layer.l_rcv) {
+		_IPATH_DBG("layer not registered, failing\n");
+		return 1;
+	}
+
+	if ((ret =
+	     ipath_setrcvhdrsize(device, NUM_OF_EKSTRA_WORDS_IN_HEADER_QUEUE)))
+		return ret;
+
+	*pktmax = devdata[device].ipath_ibmaxlen;
+
+	if (*devdata[device].ipath_statusp & IPATH_STATUS_IB_READY)
+		intval |= IPATH_LAYER_INT_IF_UP;
+	if (ipath_stats.sps_lid[device])
+		intval |= IPATH_LAYER_INT_LID;
+	if (ipath_stats.sps_mlid[device])
+		intval |= IPATH_LAYER_INT_BCAST;
+	/*
+	 * do this on open, in case low level is already up and
+	 * just layered driver was reloaded, etc.
+	 */
+	if (intval)
+		devdata[device].ipath_layer.l_intr(device, intval);
+
+	return ret;
+}
+
+int16_t ipath_layer_get_lid(const ipath_type device)
+{
+	if (device >= infinipath_max) {
+		_IPATH_DBG("Invalid unit %u\n", device);
+		return 0;
+	}
+
+	_IPATH_VDBG("returning mylid 0x%x for layered dev %d\n",
+		    devdata[device].ipath_lid, device);
+	return devdata[device].ipath_lid;
+}
+
+/*
+ * get the MAC address.  This is the EUID-64 OUI octets (top 3), then
+ * skip the next 2 (which should both be zero or 0xff).
+ * The returned MAC is in network order
+ * mac points to at least 6 bytes of buffer
+ * returns 0 on error (to be consistent with get_lid and get_bcast
+ * return 1 on success
+ * We assume that by the time the LID is set, that the GUID is as valid
+ * as it's ever going to be, rather than adding yet another status bit.
+ */
+
+int ipath_layer_get_mac(const ipath_type device, uint8_t * mac)
+{
+	uint8_t *guid;
+
+	if (device >= infinipath_max) {
+		_IPATH_DBG("Invalid unit %u, failing\n", device);
+		return 0;
+	}
+	guid = (uint8_t *) & devdata[device].ipath_guid;
+
+	mac[0] = guid[0];
+	mac[1] = guid[1];
+	mac[2] = guid[2];
+	mac[3] = guid[5];
+	mac[4] = guid[6];
+	mac[5] = guid[7];
+	if((guid[3] || guid[4]) && !(guid[3] == 0xff && guid[4] == 0xff))
+		_IPATH_DBG("Warning, guid bytes 3 and 4 not 0 or 0xffff: %x %x\n",
+			guid[3], guid[4]);
+	_IPATH_VDBG("Returning %x:%x:%x:%x:%x:%x\n",
+		    mac[0], mac[1], mac[2], mac[3], mac[4], mac[5]);
+	return 1;
+}
+
+int16_t ipath_layer_get_bcast(const ipath_type device)
+{
+	if (device >= infinipath_max) {
+		_IPATH_DBG("Invalid unit %u, failing\n", device);
+		return 0;
+	}
+
+	_IPATH_VDBG("returning broadcast LID 0x%x for unit %u\n",
+		    devdata[device].ipath_mlid, device);
+	return devdata[device].ipath_mlid;
+}
+
+int ipath_layer_get_num_of_dev(void)
+{
+	return infinipath_max;
+}
+
+int ipath_layer_get_cr_errpkey(const ipath_type device)
+{
+	return ipath_kget_creg32(device, cr_errpkey);
+}
+
+void ipath_layer_close(const ipath_type device)
+{
+	if (device >= infinipath_max) {
+		_IPATH_DBG("Invalid unit %u\n", device);
+		return;
+	}
+	if (!devdata[device].ipath_layer.l_intr
+	    || !devdata[device].ipath_layer.l_rcv) {
+		/* normal if not all chips are present */
+		_IPATH_VDBG("layer close without open\n");
+	} else {
+		devdata[device].ipath_layer.l_intr = NULL;
+		devdata[device].ipath_layer.l_rcv = NULL;
+		devdata[device].ipath_layer.l_rcv_lid = NULL;
+		devdata[device].ipath_layer.l_rcv_opcode = 0;
+		devdata[device].ipath_layer.l_rcv_lid_opcode = 0;
+	}
+}
+
+static inline void copy_aligned(uint32_t *piobuf, struct ipath_sge_state *ss,
+				uint32_t length)
+{
+	struct ipath_sge *sge = &ss->sge;
+
+	while (length) {
+		u32 len = sge->length;
+		u32 w;
+
+		BUG_ON(len == 0);
+		if (len > length)
+			len = length;
+		/* Need to round up for the last dword in the packet. */
+		w = (len + 3) >> 2;
+		ipath_dwordcpy(piobuf, sge->vaddr, w);
+		piobuf += w;
+		sge->vaddr += len;
+		sge->length -= len;
+		sge->sge_length -= len;
+		if (sge->sge_length == 0) {
+			if (--ss->num_sge)
+				*sge = *ss->sg_list++;
+		} else if (sge->length == 0 && sge->mr != NULL) {
+			if (++sge->n >= IPATH_SEGSZ) {
+				if (++sge->m >= sge->mr->mapsz)
+					break;
+				sge->n = 0;
+			}
+			sge->vaddr = sge->mr->map[sge->m]->segs[sge->n].vaddr;
+			sge->length = sge->mr->map[sge->m]->segs[sge->n].length;
+		}
+		length -= len;
+	}
+}
+
+static inline void copy_unaligned(uint32_t *piobuf, struct ipath_sge_state *ss,
+				  uint32_t length)
+{
+	struct ipath_sge *sge = &ss->sge;
+	union {
+		u8 wbuf[4];
+		u32 w;
+	} u;
+	int extra = 0;
+
+	while (length) {
+		u32 len = sge->length;
+
+		BUG_ON(len == 0);
+		if (len > length)
+			len = length;
+		length -= len;
+		while (len) {
+			u.wbuf[extra++] = *(u8 *) sge->vaddr;
+			sge->vaddr++;
+			sge->length--;
+			sge->sge_length--;
+			if (extra >= 4) {
+				*piobuf++ = u.w;
+				extra = 0;
+			}
+			len--;
+		}
+		if (sge->sge_length == 0) {
+			if (--ss->num_sge)
+				*sge = *ss->sg_list++;
+		} else if (sge->length == 0) {
+			if (++sge->n >= IPATH_SEGSZ) {
+				if (++sge->m >= sge->mr->mapsz)
+					break;
+				sge->n = 0;
+			}
+			sge->vaddr = sge->mr->map[sge->m]->segs[sge->n].vaddr;
+			sge->length = sge->mr->map[sge->m]->segs[sge->n].length;
+		}
+	}
+	if (extra) {
+		while (extra < 4)
+			u.wbuf[extra++] = 0;
+		*piobuf = u.w;
+	}
+}
+
+/*
+ * This is like ipath_send_smapkt() in that we need to be able to send
+ * packets after the chip is initialized (MADs) but also like
+ * ipath_layer_send() since its used by the verbs layer.
+ */
+int ipath_verbs_send(const ipath_type device, uint32_t hdrwords,
+		     uint32_t *hdr, uint32_t len, struct ipath_sge_state *ss)
+{
+	ipath_devdata *dd = &devdata[device];
+	int whichpb;
+	uint32_t *piobuf, plen;
+	uint64_t pboff;
+
+	if (device >= infinipath_max ||
+	    !(dd->ipath_flags & IPATH_PRESENT) || !dd->ipath_kregbase) {
+		_IPATH_DBG("illegal unit %u\n", device);
+		return -ENODEV;
+	}
+	if (!(dd->ipath_flags & IPATH_INITTED)) {
+		/* no hardware, freeze, etc. */
+		_IPATH_DBG("unit %u not usable\n", device);
+		return -ENODEV;
+	}
+	/* +1 is for the qword padding of pbc */
+	plen = hdrwords + ((len + 3) >> 2) + 1;
+	if ((plen << 2) > dd->ipath_ibmaxlen) {
+		_IPATH_DBG("packet len 0x%x too long, failing\n", plen);
+		return -EINVAL;
+	}
+
+	/* Get a PIO buffer to use. */
+	if ((whichpb = ipath_getpiobuf(device)) < 0)
+		return whichpb;
+
+	pboff = dd->ipath_piobufbase;
+	piobuf = (uint32_t *) (((char *)(dd->ipath_kregbase)) + pboff
+			       + whichpb * dd->ipath_palign);
+	_IPATH_EPDBG("0x%x+1w pio%d\n", plen - 1, whichpb);
+
+	/* Write len to control qword, no flags. */
+	*((uint64_t *) piobuf) = (uint64_t) plen;
+	piobuf += 2;
+	ipath_dwordcpy(piobuf, hdr, hdrwords);
+	if (len == 0)
+		return 0;
+	piobuf += hdrwords;
+	/*
+	 * If we really wanted to check everything, we would have to
+	 * check that each segment starts on a dword boundary and is
+	 * a dword multiple in length.
+	 * Since there can be lots of segments, we only check for a simple
+	 * common case where the amount to copy is contained in one segment.
+	 */
+	if (ss->sge.length == len)
+		copy_aligned(piobuf, ss, len);
+	else
+		copy_unaligned(piobuf, ss, len);
+	return 0;
+}
+
+void ipath_layer_snapshot_counters(const ipath_type device, u64 * swords,
+				   u64 * rwords, u64 * spkts, u64 * rpkts)
+{
+	if (device >= infinipath_max ||
+	    !(devdata[device].ipath_flags & IPATH_PRESENT)) {
+		_IPATH_DBG("illegal unit %u\n", device);
+		return;
+	}
+	if (!(devdata[device].ipath_flags & IPATH_INITTED)) {
+		/* no hardware, freeze, etc. */
+		_IPATH_DBG("unit %u not usable\n", device);
+		return;
+	}
+	*swords = ipath_snap_cntr(device, cr_wordsendcnt);
+	*rwords = ipath_snap_cntr(device, cr_wordrcvcnt);
+	*spkts = ipath_snap_cntr(device, cr_pktsendcnt);
+	*rpkts = ipath_snap_cntr(device, cr_pktrcvcnt);
+}
+
+/*
+ * Return the counters needed by recv_pma_get_portcounters().
+ */
+void ipath_layer_get_counters(const ipath_type device,
+			      struct ipath_layer_counters *cntrs)
+{
+	if (device >= infinipath_max ||
+	    !(devdata[device].ipath_flags & IPATH_PRESENT)) {
+		_IPATH_DBG("illegal unit %u\n", device);
+		return;
+	}
+	if (!(devdata[device].ipath_flags & IPATH_INITTED)) {
+		/* no hardware, freeze, etc. */
+		_IPATH_DBG("unit %u not usable\n", device);
+		return;
+	}
+	cntrs->symbol_error_counter =
+		ipath_snap_cntr(device, cr_ibsymbolerrcnt);
+	cntrs->link_error_recovery_counter =
+		ipath_snap_cntr(device, cr_iblinkerrrecovcnt);
+	cntrs->link_downed_counter = ipath_snap_cntr(device, cr_iblinkdowncnt);
+	cntrs->port_rcv_errors = ipath_snap_cntr(device, cr_err_rlencnt) +
+		ipath_snap_cntr(device, cr_invalidrlencnt) +
+		ipath_snap_cntr(device, cr_erricrccnt) +
+		ipath_snap_cntr(device, cr_errvcrccnt) +
+		ipath_snap_cntr(device, cr_badformatcnt);
+	cntrs->port_rcv_remphys_errors = ipath_snap_cntr(device, cr_rcvebpcnt);
+	cntrs->port_xmit_discards = ipath_snap_cntr(device, cr_unsupvlcnt);
+	cntrs->port_xmit_data = ipath_snap_cntr(device, cr_wordsendcnt);
+	cntrs->port_rcv_data = ipath_snap_cntr(device, cr_wordrcvcnt);
+	cntrs->port_xmit_packets = ipath_snap_cntr(device, cr_pktsendcnt);
+	cntrs->port_rcv_packets = ipath_snap_cntr(device, cr_pktrcvcnt);
+}
+
+void ipath_layer_want_buffer(const ipath_type device)
+{
+	atomic_set_mask(INFINIPATH_S_PIOINTBUFAVAIL,
+			&devdata[device].ipath_sendctrl);
+	ipath_kput_kreg(device, kr_sendctrl, devdata[device].ipath_sendctrl);
+}
+
+int ipath_layer_send(const ipath_type device, void *hdr, void *data,
+		     uint32_t datawords)
+{
+	int ret = 0, whichpb;
+	uint32_t *piobuf, plen;
+	uint16_t vlsllnh;
+	uint64_t pboff;
+
+	if (device >= infinipath_max) {
+		_IPATH_DBG("Invalid unit %u, failing\n", device);
+		return -EINVAL;
+	}
+	if (!(devdata[device].ipath_flags & IPATH_RCVHDRSZ_SET)) {
+		_IPATH_DBG("send while not open\n");
+		ret = -EINVAL;
+	} else
+	    if ((devdata[device].ipath_flags & (IPATH_LINKUNK | IPATH_LINKDOWN))
+		|| devdata[device].ipath_lid == 0) {
+		/* lid check is for when sma hasn't yet configured */
+		ret = -ENETDOWN;
+		_IPATH_VDBG("send while not ready, mylid=%u, flags=0x%x\n",
+			    devdata[device].ipath_lid,
+			    devdata[device].ipath_flags);
+	}
+	/* +1 is for the qword padding of pbc */
+	plen = (sizeof(ips_message_header_typ) >> 2) + datawords + 1;
+	if (plen > (devdata[device].ipath_ibmaxlen >> 2)) {
+		_IPATH_DBG("packet len 0x%x too long, failing\n", plen);
+		ret = -EINVAL;
+	}
+	vlsllnh = *((uint16_t *) hdr);
+	if (vlsllnh != htons(IPS_LRH_BTH)) {
+		_IPATH_DBG("Warning: lrh[0] wrong (%x, not %x); not sending\n",
+			   vlsllnh, htons(IPS_LRH_BTH));
+		ret = -EINVAL;
+	}
+	if (ret)
+		goto done;
+
+	/* Get a PIO buffer to use. */
+	if ((whichpb = ipath_getpiobuf(device)) < 0) {
+		ret = whichpb;
+		goto done;
+	}
+
+	pboff = devdata[device].ipath_piobufbase;
+	piobuf =
+	    (uint32_t *) (((char *)(devdata[device].ipath_kregbase)) + pboff +
+			  whichpb * devdata[device].ipath_palign);
+	_IPATH_EPDBG("0x%x+1w pio%d\n", plen - 1, whichpb);
+
+	/* len to control qword, no flags */
+	*((uint64_t *) piobuf) = (uint64_t) plen;
+	piobuf += 2;
+	ipath_dwordcpy(piobuf, hdr, (sizeof(ips_message_header_typ) >> 2));
+	piobuf += (sizeof(ips_message_header_typ) >> 2);
+	ipath_dwordcpy(piobuf, data, datawords);
+
+	ipath_stats.sps_ether_spkts++;	/* another ether packet sent */
+
+done:
+	return ret;
+}
+
+void ipath_layer_set_piointbufavail_int(const ipath_type device)
+{
+	if (device >= infinipath_max) {
+		_IPATH_DBG("Invalid unit %u\n", device);
+		return;
+	}
+
+	atomic_set_mask(INFINIPATH_S_PIOINTBUFAVAIL,
+			&devdata[device].ipath_sendctrl);
+
+	ipath_kput_kreg(device, kr_sendctrl, devdata[device].ipath_sendctrl);
+}
+
+void ipath_layer_enable_timer(const ipath_type device)
+{
+	if (device >= infinipath_max) {
+		_IPATH_DBG("Invalid unit %u\n", device);
+		return;
+	}
+
+	/*
+	 * HT-400 has a design flaw where the chip and kernel idea
+	 * of the tail register don't always agree, and therefore we won't
+	 * get an interrupt on the next packet received.
+	 * If the board supports per packet receive interrupts, use it.
+	 * Otherwise, the timer function periodically checks for packets
+	 * to cover this case.
+	 * Either way, the timer is needed for verbs layer related
+	 * processing.
+	 */
+	if (devdata[device].ipath_flags & IPATH_GPIO_INTR) {
+		ipath_kput_kreg(device, kr_debugportselect, 0x2074076542310UL);
+		/* Enable GPIO bit 2 interrupt */
+		ipath_kput_kreg(device, kr_gpio_mask, (uint64_t)(1 << 2));
+	}
+
+	init_timer(&devdata[device].verbs_layer.l_timer);
+	devdata[device].verbs_layer.l_timer.function = ipath_verbs_timer;
+	devdata[device].verbs_layer.l_timer.data = (unsigned long)device;
+	devdata[device].verbs_layer.l_timer.expires = jiffies + 1;
+	add_timer(&devdata[device].verbs_layer.l_timer);
+}
+
+void ipath_layer_disable_timer(const ipath_type device)
+{
+	if (device >= infinipath_max) {
+		_IPATH_DBG("Invalid unit %u\n", device);
+		return;
+	}
+
+	/* Disable GPIO bit 2 interrupt */
+	if (devdata[device].ipath_flags & IPATH_GPIO_INTR)
+		ipath_kput_kreg(device, kr_gpio_mask, 0);
+
+	del_timer_sync(&devdata[device].verbs_layer.l_timer);
+}
+
+/*
+ * Get the verbs layer flags.
+ */
+unsigned ipath_verbs_get_flags(const ipath_type device)
+{
+	if (device >= infinipath_max) {
+		_IPATH_DBG("Invalid unit %u\n", device);
+		return 0;
+	}
+
+	return devdata[device].verbs_layer.l_flags;
+}
+
+/*
+ * Set the verbs layer flags.
+ */
+void ipath_verbs_set_flags(const ipath_type device, unsigned flags)
+{
+	ipath_type s;
+
+	if (device >= infinipath_max) {
+		_IPATH_DBG("Invalid unit %u\n", device);
+		return;
+	}
+
+	devdata[device].verbs_layer.l_flags = flags;
+
+	for (s = 0; s < infinipath_max; s++) {
+		if (!(devdata[s].ipath_flags & IPATH_INITTED))
+			continue;
+		if ((flags & IPATH_VERBS_KERNEL_SMA) &&
+		    !(*devdata[s].ipath_statusp & IPATH_STATUS_SMA)) {
+			*devdata[s].ipath_statusp |= IPATH_STATUS_OIB_SMA;
+		} else {
+			*devdata[s].ipath_statusp &= ~IPATH_STATUS_OIB_SMA;
+		}
+	}
+}
+
+/*
+ * Return the size of the PKEY table for port 0.
+ */
+unsigned ipath_layer_get_npkeys(const ipath_type device)
+{
+	if (device >= infinipath_max) {
+		_IPATH_DBG("Invalid unit %u\n", device);
+		return 0;
+	}
+
+	return ARRAY_SIZE(devdata[device].ipath_pd[0]->port_pkeys);
+}
+
+/*
+ * Return the indexed PKEY from the port 0 PKEY table.
+ */
+unsigned ipath_layer_get_pkey(const ipath_type device, unsigned index)
+{
+	if (device >= infinipath_max) {
+		_IPATH_DBG("Invalid unit %u\n", device);
+		return 0;
+	}
+	if (index >= ARRAY_SIZE(devdata[device].ipath_pd[0]->port_pkeys))
+		return 0;
+
+	return devdata[device].ipath_pd[0]->port_pkeys[index];
+}
+
+/*
+ * Return the PKEY table for port 0.
+ */
+void ipath_layer_get_pkeys(const ipath_type device, uint16_t *pkeys)
+{
+	struct _ipath_portdata *pd;
+
+	if (device >= infinipath_max) {
+		_IPATH_DBG("Invalid unit %u\n", device);
+		return;
+	}
+
+	pd = devdata[device].ipath_pd[0];
+	memcpy(pkeys, pd->port_pkeys, sizeof(pd->port_pkeys));
+}
+
+/*
+ * Decrecment the reference count for the given PKEY.
+ * Return true if this was the last reference and the hardware table entry
+ * needs to be changed.
+ */
+static inline int rm_pkey(ipath_devdata *dd, uint16_t key)
+{
+	int i;
+
+	for (i = 0; i < ARRAY_SIZE(dd->ipath_pkeys); i++) {
+		if (dd->ipath_pkeys[i] != key)
+			continue;
+		if (atomic_dec_and_test(&dd->ipath_pkeyrefs[i])) {
+			dd->ipath_pkeys[i] = 0;
+			return 1;
+		}
+		break;
+	}
+	return 0;
+}
+
+/*
+ * Add the given PKEY to the hardware table.
+ * Return an error code if unable to add the entry, zero if no change,
+ * or 1 if the hardware PKEY register needs to be updated.
+ */
+static inline int add_pkey(ipath_devdata *dd, uint16_t key)
+{
+	int i;
+	uint16_t lkey = key & 0x7FFF;
+	int any = 0;
+
+	for (i = 0; i < ARRAY_SIZE(dd->ipath_pkeys); i++) {
+		if (!dd->ipath_pkeys[i]) {
+			any++;
+			continue;
+		}
+		/* If it matches exactly, try to increment the ref count */
+		if (dd->ipath_pkeys[i] == key) {
+			if (atomic_inc_return(&dd->ipath_pkeyrefs[i]) > 1)
+				return 0;
+			/* Lost the race. Look for an empty slot below. */
+			atomic_dec(&dd->ipath_pkeyrefs[i]);
+			any++;
+		}
+		/*
+		 * It makes no sense to have both the limited and unlimited
+		 * PKEY set at the same time since the unlimited one will
+		 * disable the limited one.
+		 */
+		if ((dd->ipath_pkeys[i] & 0x7FFF) == lkey)
+			return -EEXIST;
+	}
+	if (!any)
+		return -EBUSY;
+	for (i = 0; i < ARRAY_SIZE(dd->ipath_pkeys); i++) {
+		if (!dd->ipath_pkeys[i] &&
+		    atomic_inc_return(&dd->ipath_pkeyrefs[i]) == 1) {
+			/* for ipathstats, etc. */
+			ipath_stats.sps_pkeys[i] = lkey;
+			dd->ipath_pkeys[i] = key;
+			return 1;
+		}
+	}
+	return -EBUSY;
+}
+
+/*
+ * Set the PKEY table for port 0.
+ */
+int ipath_layer_set_pkeys(const ipath_type device, uint16_t *pkeys)
+{
+	ipath_portdata *pd;
+	ipath_devdata *dd;
+	int i;
+	int changed = 0;
+
+	if (device >= infinipath_max) {
+		_IPATH_DBG("Invalid unit %u\n", device);
+		return -EINVAL;
+	}
+
+	dd = &devdata[device];
+	pd = dd->ipath_pd[0];
+
+	for (i = 0; i > ARRAY_SIZE(pd->port_pkeys); i++) {
+		uint16_t key = pkeys[i];
+		uint16_t okey = pd->port_pkeys[i];
+
+		if (key == okey)
+			continue;
+		/*
+		 * The value of this PKEY table entry is changing.
+		 * Remove the old entry in the hardware's array of PKEYs.
+		 */
+		if (okey & 0x7FFF)
+			changed |= rm_pkey(dd, okey);
+		if (key & 0x7FFF) {
+			int ret = add_pkey(dd, key);
+
+			if (ret < 0)
+				key = 0;
+			else
+				changed |= ret;
+		}
+		pd->port_pkeys[i] = key;
+	}
+	if (changed) {
+		uint64_t pkey;
+
+		pkey = (uint64_t) dd->ipath_pkeys[0] |
+		    ((uint64_t) dd->ipath_pkeys[1] << 16) |
+		    ((uint64_t) dd->ipath_pkeys[2] << 32) |
+		    ((uint64_t) dd->ipath_pkeys[3] << 48);
+		_IPATH_VDBG("p0 new pkey reg %llx\n", pkey);
+		ipath_kput_kreg(pd->port_unit, kr_partitionkey, pkey);
+	}
+	return 0;
+}
+
+/*
+ * Registers that vary with the chip implementation constants (port)
+ * use this routine.
+ */
+uint64_t ipath_kget_kreg64_port(const ipath_type stype, ipath_kreg regno,
+				unsigned port)
+{
+	ipath_kreg tmp =
+	    (port < devdata[stype].ipath_portcnt && regno == kr_rcvhdraddr) ?
+	    regno + port :
+	    ((port < devdata[stype].ipath_portcnt
+	      && regno == kr_rcvhdrtailaddr) ? regno + port : __kr_invalid);
+	return ipath_kget_kreg64(stype, tmp);
+}
+
+/*
+ * Registers that vary with the chip implementation constants (port)
+ * use this routine.
+ */
+void ipath_kput_kreg_port(const ipath_type stype, ipath_kreg regno,
+			  unsigned port, uint64_t value)
+{
+	ipath_kreg tmp =
+	    (port < devdata[stype].ipath_portcnt && regno == kr_rcvhdraddr) ?
+	    regno + port :
+	    ((port < devdata[stype].ipath_portcnt
+	      && regno == kr_rcvhdrtailaddr) ? regno + port : __kr_invalid);
+	ipath_kput_kreg(stype, tmp, value);
+}
+
+EXPORT_SYMBOL(ipath_kset_linkstate);
+EXPORT_SYMBOL(ipath_kset_mtu);
+EXPORT_SYMBOL(ipath_layer_close);
+EXPORT_SYMBOL(ipath_layer_get_bcast);
+EXPORT_SYMBOL(ipath_layer_get_cr_errpkey);
+EXPORT_SYMBOL(ipath_layer_get_deviceid);
+EXPORT_SYMBOL(ipath_layer_get_flags);
+EXPORT_SYMBOL(ipath_layer_get_guid);
+EXPORT_SYMBOL(ipath_layer_get_ibmtu);
+EXPORT_SYMBOL(ipath_layer_get_lastibcstat);
+EXPORT_SYMBOL(ipath_layer_get_lid);
+EXPORT_SYMBOL(ipath_layer_get_mac);
+EXPORT_SYMBOL(ipath_layer_get_nguid);
+EXPORT_SYMBOL(ipath_layer_get_num_of_dev);
+EXPORT_SYMBOL(ipath_layer_get_pcidev);
+EXPORT_SYMBOL(ipath_layer_open);
+EXPORT_SYMBOL(ipath_layer_query_device);
+EXPORT_SYMBOL(ipath_layer_register);
+EXPORT_SYMBOL(ipath_layer_send);
+EXPORT_SYMBOL(ipath_layer_set_guid);
+EXPORT_SYMBOL(ipath_layer_set_piointbufavail_int);
+EXPORT_SYMBOL(ipath_layer_snapshot_counters);
+EXPORT_SYMBOL(ipath_layer_get_counters);
+EXPORT_SYMBOL(ipath_layer_want_buffer);
+EXPORT_SYMBOL(ipath_verbs_register);
+EXPORT_SYMBOL(ipath_verbs_send);
+EXPORT_SYMBOL(ipath_verbs_unregister);
+EXPORT_SYMBOL(ipath_set_sps_lid);
+EXPORT_SYMBOL(ipath_layer_enable_timer);
+EXPORT_SYMBOL(ipath_layer_disable_timer);
+EXPORT_SYMBOL(ipath_verbs_get_flags);
+EXPORT_SYMBOL(ipath_verbs_set_flags);
+EXPORT_SYMBOL(ipath_layer_get_npkeys);
+EXPORT_SYMBOL(ipath_layer_get_pkey);
+EXPORT_SYMBOL(ipath_layer_get_pkeys);
+EXPORT_SYMBOL(ipath_layer_set_pkeys);
-- 
0.99.9n



More information about the general mailing list