forked from luck/tmp_suning_uos_patched
staging/rdma: remove deprecated ipath driver
This driver was moved to staging for eventual deletion. Time to complete that task. Signed-off-by: Doug Ledford <dledford@redhat.com>
This commit is contained in:
parent
e581d111da
commit
b85d9905a7
|
@ -5795,12 +5795,6 @@ M: Juanjo Ciarlante <jjciarla@raiz.uncu.edu.ar>
|
|||
S: Maintained
|
||||
F: net/ipv4/netfilter/ipt_MASQUERADE.c
|
||||
|
||||
IPATH DRIVER
|
||||
M: Mike Marciniszyn <infinipath@intel.com>
|
||||
L: linux-rdma@vger.kernel.org
|
||||
S: Maintained
|
||||
F: drivers/staging/rdma/ipath/
|
||||
|
||||
IPMI SUBSYSTEM
|
||||
M: Corey Minyard <minyard@acm.org>
|
||||
L: openipmi-developer@lists.sourceforge.net (moderated for non-subscribers)
|
||||
|
|
|
@ -24,6 +24,4 @@ if STAGING_RDMA
|
|||
|
||||
source "drivers/staging/rdma/hfi1/Kconfig"
|
||||
|
||||
source "drivers/staging/rdma/ipath/Kconfig"
|
||||
|
||||
endif
|
||||
|
|
|
@ -1,3 +1,2 @@
|
|||
# Entries for RDMA_STAGING tree
|
||||
obj-$(CONFIG_INFINIBAND_HFI1) += hfi1/
|
||||
obj-$(CONFIG_INFINIBAND_IPATH) += ipath/
|
||||
|
|
|
@ -1,16 +0,0 @@
|
|||
config INFINIBAND_IPATH
|
||||
tristate "QLogic HTX HCA support"
|
||||
depends on 64BIT && NET && HT_IRQ
|
||||
---help---
|
||||
This is a driver for the deprecated QLogic Hyper-Transport
|
||||
IB host channel adapter (model QHT7140),
|
||||
including InfiniBand verbs support. This driver allows these
|
||||
devices to be used with both kernel upper level protocols such
|
||||
as IP-over-InfiniBand as well as with userspace applications
|
||||
(in conjunction with InfiniBand userspace access).
|
||||
For QLogic PCIe QLE based cards, use the QIB driver instead.
|
||||
|
||||
If you have this hardware you will need to boot with PAT disabled
|
||||
on your x86-64 systems, use the nopat kernel parameter.
|
||||
|
||||
Note that this driver will soon be removed entirely from the kernel.
|
|
@ -1,37 +0,0 @@
|
|||
ccflags-y := -DIPATH_IDSTR='"QLogic kernel.org driver"' \
|
||||
-DIPATH_KERN_TYPE=0
|
||||
|
||||
obj-$(CONFIG_INFINIBAND_IPATH) += ib_ipath.o
|
||||
|
||||
ib_ipath-y := \
|
||||
ipath_cq.o \
|
||||
ipath_diag.o \
|
||||
ipath_dma.o \
|
||||
ipath_driver.o \
|
||||
ipath_eeprom.o \
|
||||
ipath_file_ops.o \
|
||||
ipath_fs.o \
|
||||
ipath_init_chip.o \
|
||||
ipath_intr.o \
|
||||
ipath_keys.o \
|
||||
ipath_mad.o \
|
||||
ipath_mmap.o \
|
||||
ipath_mr.o \
|
||||
ipath_qp.o \
|
||||
ipath_rc.o \
|
||||
ipath_ruc.o \
|
||||
ipath_sdma.o \
|
||||
ipath_srq.o \
|
||||
ipath_stats.o \
|
||||
ipath_sysfs.o \
|
||||
ipath_uc.o \
|
||||
ipath_ud.o \
|
||||
ipath_user_pages.o \
|
||||
ipath_user_sdma.o \
|
||||
ipath_verbs_mcast.o \
|
||||
ipath_verbs.o
|
||||
|
||||
ib_ipath-$(CONFIG_HT_IRQ) += ipath_iba6110.o
|
||||
|
||||
ib_ipath-$(CONFIG_X86_64) += ipath_wc_x86_64.o
|
||||
ib_ipath-$(CONFIG_PPC64) += ipath_wc_ppc64.o
|
|
@ -1,5 +0,0 @@
|
|||
The ipath driver has been moved to staging in preparation for its removal in a
|
||||
few releases. The driver will be deleted during the 4.6 merge window.
|
||||
|
||||
Contact Dennis Dalessandro <dennis.dalessandro@intel.com> and
|
||||
Cc: linux-rdma@vger.kernel.org
|
|
@ -1,851 +0,0 @@
|
|||
/*
|
||||
* Copyright (c) 2006, 2007, 2008 QLogic Corporation. All rights reserved.
|
||||
* Copyright (c) 2003, 2004, 2005, 2006 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.
|
||||
*/
|
||||
|
||||
#ifndef _IPATH_COMMON_H
|
||||
#define _IPATH_COMMON_H
|
||||
|
||||
/*
|
||||
* This file contains defines, structures, etc. that are used
|
||||
* to communicate between kernel and user code.
|
||||
*/
|
||||
|
||||
|
||||
/* This is the IEEE-assigned OUI for QLogic Inc. InfiniPath */
|
||||
#define IPATH_SRC_OUI_1 0x00
|
||||
#define IPATH_SRC_OUI_2 0x11
|
||||
#define IPATH_SRC_OUI_3 0x75
|
||||
|
||||
/* version of protocol header (known to chip also). In the long run,
|
||||
* we should be able to generate and accept a range of version numbers;
|
||||
* for now we only accept one, and it's compiled in.
|
||||
*/
|
||||
#define IPS_PROTO_VERSION 2
|
||||
|
||||
/*
|
||||
* These are compile time constants that you may want to enable or disable
|
||||
* if you are trying to debug problems with code or performance.
|
||||
* IPATH_VERBOSE_TRACING define as 1 if you want additional tracing in
|
||||
* fastpath code
|
||||
* IPATH_TRACE_REGWRITES define as 1 if you want register writes to be
|
||||
* traced in faspath code
|
||||
* _IPATH_TRACING define as 0 if you want to remove all tracing in a
|
||||
* compilation unit
|
||||
* _IPATH_DEBUGGING define as 0 if you want to remove debug prints
|
||||
*/
|
||||
|
||||
/*
|
||||
* The value in the BTH QP field that InfiniPath uses to differentiate
|
||||
* an infinipath protocol IB packet vs standard IB transport
|
||||
*/
|
||||
#define IPATH_KD_QP 0x656b79
|
||||
|
||||
/*
|
||||
* valid states passed to ipath_set_linkstate() user call
|
||||
*/
|
||||
#define IPATH_IB_LINKDOWN 0
|
||||
#define IPATH_IB_LINKARM 1
|
||||
#define IPATH_IB_LINKACTIVE 2
|
||||
#define IPATH_IB_LINKDOWN_ONLY 3
|
||||
#define IPATH_IB_LINKDOWN_SLEEP 4
|
||||
#define IPATH_IB_LINKDOWN_DISABLE 5
|
||||
#define IPATH_IB_LINK_LOOPBACK 6 /* enable local loopback */
|
||||
#define IPATH_IB_LINK_EXTERNAL 7 /* normal, disable local loopback */
|
||||
#define IPATH_IB_LINK_NO_HRTBT 8 /* disable Heartbeat, e.g. for loopback */
|
||||
#define IPATH_IB_LINK_HRTBT 9 /* enable heartbeat, normal, non-loopback */
|
||||
|
||||
/*
|
||||
* These 3 values (SDR and DDR may be ORed for auto-speed
|
||||
* negotiation) are used for the 3rd argument to path_f_set_ib_cfg
|
||||
* with cmd IPATH_IB_CFG_SPD_ENB, by direct calls or via sysfs. They
|
||||
* are also the the possible values for ipath_link_speed_enabled and active
|
||||
* The values were chosen to match values used within the IB spec.
|
||||
*/
|
||||
#define IPATH_IB_SDR 1
|
||||
#define IPATH_IB_DDR 2
|
||||
|
||||
/*
|
||||
* stats maintained by the driver. For now, at least, this is global
|
||||
* to all minor devices.
|
||||
*/
|
||||
struct infinipath_stats {
|
||||
/* number of interrupts taken */
|
||||
__u64 sps_ints;
|
||||
/* number of interrupts for errors */
|
||||
__u64 sps_errints;
|
||||
/* number of errors from chip (not incl. packet errors or CRC) */
|
||||
__u64 sps_errs;
|
||||
/* number of packet errors from chip other than CRC */
|
||||
__u64 sps_pkterrs;
|
||||
/* number of packets with CRC errors (ICRC and VCRC) */
|
||||
__u64 sps_crcerrs;
|
||||
/* number of hardware errors reported (parity, etc.) */
|
||||
__u64 sps_hwerrs;
|
||||
/* number of times IB link changed state unexpectedly */
|
||||
__u64 sps_iblink;
|
||||
__u64 sps_unused; /* was fastrcvint, no longer implemented */
|
||||
/* number of kernel (port0) packets received */
|
||||
__u64 sps_port0pkts;
|
||||
/* number of "ethernet" packets sent by driver */
|
||||
__u64 sps_ether_spkts;
|
||||
/* number of "ethernet" packets received by driver */
|
||||
__u64 sps_ether_rpkts;
|
||||
/* number of SMA packets sent by driver. Obsolete. */
|
||||
__u64 sps_sma_spkts;
|
||||
/* number of SMA packets received by driver. Obsolete. */
|
||||
__u64 sps_sma_rpkts;
|
||||
/* number of times all ports rcvhdrq was full and packet dropped */
|
||||
__u64 sps_hdrqfull;
|
||||
/* number of times all ports egrtid was full and packet dropped */
|
||||
__u64 sps_etidfull;
|
||||
/*
|
||||
* number of times we tried to send from driver, but no pio buffers
|
||||
* avail
|
||||
*/
|
||||
__u64 sps_nopiobufs;
|
||||
/* number of ports currently open */
|
||||
__u64 sps_ports;
|
||||
/* list of pkeys (other than default) accepted (0 means not set) */
|
||||
__u16 sps_pkeys[4];
|
||||
__u16 sps_unused16[4]; /* available; maintaining compatible layout */
|
||||
/* number of user ports per chip (not IB ports) */
|
||||
__u32 sps_nports;
|
||||
/* not our interrupt, or already handled */
|
||||
__u32 sps_nullintr;
|
||||
/* max number of packets handled per receive call */
|
||||
__u32 sps_maxpkts_call;
|
||||
/* avg number of packets handled per receive call */
|
||||
__u32 sps_avgpkts_call;
|
||||
/* total number of pages locked */
|
||||
__u64 sps_pagelocks;
|
||||
/* total number of pages unlocked */
|
||||
__u64 sps_pageunlocks;
|
||||
/*
|
||||
* Number of packets dropped in kernel other than errors (ether
|
||||
* packets if ipath not configured, etc.)
|
||||
*/
|
||||
__u64 sps_krdrops;
|
||||
__u64 sps_txeparity; /* PIO buffer parity error, recovered */
|
||||
/* pad for future growth */
|
||||
__u64 __sps_pad[45];
|
||||
};
|
||||
|
||||
/*
|
||||
* These are the status bits readable (in ascii form, 64bit value)
|
||||
* from the "status" sysfs file.
|
||||
*/
|
||||
#define IPATH_STATUS_INITTED 0x1 /* basic initialization done */
|
||||
#define IPATH_STATUS_DISABLED 0x2 /* hardware disabled */
|
||||
/* Device has been disabled via admin request */
|
||||
#define IPATH_STATUS_ADMIN_DISABLED 0x4
|
||||
/* Chip has been found and initted */
|
||||
#define IPATH_STATUS_CHIP_PRESENT 0x20
|
||||
/* IB link is at ACTIVE, usable for data traffic */
|
||||
#define IPATH_STATUS_IB_READY 0x40
|
||||
/* link is configured, LID, MTU, etc. have been set */
|
||||
#define IPATH_STATUS_IB_CONF 0x80
|
||||
/* no link established, probably no cable */
|
||||
#define IPATH_STATUS_IB_NOCABLE 0x100
|
||||
/* A Fatal hardware error has occurred. */
|
||||
#define IPATH_STATUS_HWERROR 0x200
|
||||
|
||||
/*
|
||||
* The list of usermode accessible registers. Also see Reg_* later in file.
|
||||
*/
|
||||
typedef enum _ipath_ureg {
|
||||
/* (RO) DMA RcvHdr to be used next. */
|
||||
ur_rcvhdrtail = 0,
|
||||
/* (RW) RcvHdr entry to be processed next by host. */
|
||||
ur_rcvhdrhead = 1,
|
||||
/* (RO) Index of next Eager index to use. */
|
||||
ur_rcvegrindextail = 2,
|
||||
/* (RW) Eager TID to be processed next */
|
||||
ur_rcvegrindexhead = 3,
|
||||
/* For internal use only; max register number. */
|
||||
_IPATH_UregMax
|
||||
} ipath_ureg;
|
||||
|
||||
/* bit values for spi_runtime_flags */
|
||||
#define IPATH_RUNTIME_HT 0x1
|
||||
#define IPATH_RUNTIME_PCIE 0x2
|
||||
#define IPATH_RUNTIME_FORCE_WC_ORDER 0x4
|
||||
#define IPATH_RUNTIME_RCVHDR_COPY 0x8
|
||||
#define IPATH_RUNTIME_MASTER 0x10
|
||||
#define IPATH_RUNTIME_NODMA_RTAIL 0x80
|
||||
#define IPATH_RUNTIME_SDMA 0x200
|
||||
#define IPATH_RUNTIME_FORCE_PIOAVAIL 0x400
|
||||
#define IPATH_RUNTIME_PIO_REGSWAPPED 0x800
|
||||
|
||||
/*
|
||||
* This structure is returned by ipath_userinit() immediately after
|
||||
* open to get implementation-specific info, and info specific to this
|
||||
* instance.
|
||||
*
|
||||
* This struct must have explict pad fields where type sizes
|
||||
* may result in different alignments between 32 and 64 bit
|
||||
* programs, since the 64 bit * bit kernel requires the user code
|
||||
* to have matching offsets
|
||||
*/
|
||||
struct ipath_base_info {
|
||||
/* version of hardware, for feature checking. */
|
||||
__u32 spi_hw_version;
|
||||
/* version of software, for feature checking. */
|
||||
__u32 spi_sw_version;
|
||||
/* InfiniPath port assigned, goes into sent packets */
|
||||
__u16 spi_port;
|
||||
__u16 spi_subport;
|
||||
/*
|
||||
* IB MTU, packets IB data must be less than this.
|
||||
* The MTU is in bytes, and will be a multiple of 4 bytes.
|
||||
*/
|
||||
__u32 spi_mtu;
|
||||
/*
|
||||
* Size of a PIO buffer. Any given packet's total size must be less
|
||||
* than this (in words). Included is the starting control word, so
|
||||
* if 513 is returned, then total pkt size is 512 words or less.
|
||||
*/
|
||||
__u32 spi_piosize;
|
||||
/* size of the TID cache in infinipath, in entries */
|
||||
__u32 spi_tidcnt;
|
||||
/* size of the TID Eager list in infinipath, in entries */
|
||||
__u32 spi_tidegrcnt;
|
||||
/* size of a single receive header queue entry in words. */
|
||||
__u32 spi_rcvhdrent_size;
|
||||
/*
|
||||
* Count of receive header queue entries allocated.
|
||||
* This may be less than the spu_rcvhdrcnt passed in!.
|
||||
*/
|
||||
__u32 spi_rcvhdr_cnt;
|
||||
|
||||
/* per-chip and other runtime features bitmap (IPATH_RUNTIME_*) */
|
||||
__u32 spi_runtime_flags;
|
||||
|
||||
/* address where receive buffer queue is mapped into */
|
||||
__u64 spi_rcvhdr_base;
|
||||
|
||||
/* user program. */
|
||||
|
||||
/* base address of eager TID receive buffers. */
|
||||
__u64 spi_rcv_egrbufs;
|
||||
|
||||
/* Allocated by initialization code, not by protocol. */
|
||||
|
||||
/*
|
||||
* Size of each TID buffer in host memory, starting at
|
||||
* spi_rcv_egrbufs. The buffers are virtually contiguous.
|
||||
*/
|
||||
__u32 spi_rcv_egrbufsize;
|
||||
/*
|
||||
* The special QP (queue pair) value that identifies an infinipath
|
||||
* protocol packet from standard IB packets. More, probably much
|
||||
* more, to be added.
|
||||
*/
|
||||
__u32 spi_qpair;
|
||||
|
||||
/*
|
||||
* User register base for init code, not to be used directly by
|
||||
* protocol or applications.
|
||||
*/
|
||||
__u64 __spi_uregbase;
|
||||
/*
|
||||
* Maximum buffer size in bytes that can be used in a single TID
|
||||
* entry (assuming the buffer is aligned to this boundary). This is
|
||||
* the minimum of what the hardware and software support Guaranteed
|
||||
* to be a power of 2.
|
||||
*/
|
||||
__u32 spi_tid_maxsize;
|
||||
/*
|
||||
* alignment of each pio send buffer (byte count
|
||||
* to add to spi_piobufbase to get to second buffer)
|
||||
*/
|
||||
__u32 spi_pioalign;
|
||||
/*
|
||||
* The index of the first pio buffer available to this process;
|
||||
* needed to do lookup in spi_pioavailaddr; not added to
|
||||
* spi_piobufbase.
|
||||
*/
|
||||
__u32 spi_pioindex;
|
||||
/* number of buffers mapped for this process */
|
||||
__u32 spi_piocnt;
|
||||
|
||||
/*
|
||||
* Base address of writeonly pio buffers for this process.
|
||||
* Each buffer has spi_piosize words, and is aligned on spi_pioalign
|
||||
* boundaries. spi_piocnt buffers are mapped from this address
|
||||
*/
|
||||
__u64 spi_piobufbase;
|
||||
|
||||
/*
|
||||
* Base address of readonly memory copy of the pioavail registers.
|
||||
* There are 2 bits for each buffer.
|
||||
*/
|
||||
__u64 spi_pioavailaddr;
|
||||
|
||||
/*
|
||||
* Address where driver updates a copy of the interface and driver
|
||||
* status (IPATH_STATUS_*) as a 64 bit value. It's followed by a
|
||||
* string indicating hardware error, if there was one.
|
||||
*/
|
||||
__u64 spi_status;
|
||||
|
||||
/* number of chip ports available to user processes */
|
||||
__u32 spi_nports;
|
||||
/* unit number of chip we are using */
|
||||
__u32 spi_unit;
|
||||
/* num bufs in each contiguous set */
|
||||
__u32 spi_rcv_egrperchunk;
|
||||
/* size in bytes of each contiguous set */
|
||||
__u32 spi_rcv_egrchunksize;
|
||||
/* total size of mmap to cover full rcvegrbuffers */
|
||||
__u32 spi_rcv_egrbuftotlen;
|
||||
__u32 spi_filler_for_align;
|
||||
/* address of readonly memory copy of the rcvhdrq tail register. */
|
||||
__u64 spi_rcvhdr_tailaddr;
|
||||
|
||||
/* shared memory pages for subports if port is shared */
|
||||
__u64 spi_subport_uregbase;
|
||||
__u64 spi_subport_rcvegrbuf;
|
||||
__u64 spi_subport_rcvhdr_base;
|
||||
|
||||
/* shared memory page for hardware port if it is shared */
|
||||
__u64 spi_port_uregbase;
|
||||
__u64 spi_port_rcvegrbuf;
|
||||
__u64 spi_port_rcvhdr_base;
|
||||
__u64 spi_port_rcvhdr_tailaddr;
|
||||
|
||||
} __attribute__ ((aligned(8)));
|
||||
|
||||
|
||||
/*
|
||||
* This version number is given to the driver by the user code during
|
||||
* initialization in the spu_userversion field of ipath_user_info, so
|
||||
* the driver can check for compatibility with user code.
|
||||
*
|
||||
* The major version changes when data structures
|
||||
* change in an incompatible way. The driver must be the same or higher
|
||||
* for initialization to succeed. In some cases, a higher version
|
||||
* driver will not interoperate with older software, and initialization
|
||||
* will return an error.
|
||||
*/
|
||||
#define IPATH_USER_SWMAJOR 1
|
||||
|
||||
/*
|
||||
* Minor version differences are always compatible
|
||||
* a within a major version, however if user software is larger
|
||||
* than driver software, some new features and/or structure fields
|
||||
* may not be implemented; the user code must deal with this if it
|
||||
* cares, or it must abort after initialization reports the difference.
|
||||
*/
|
||||
#define IPATH_USER_SWMINOR 6
|
||||
|
||||
#define IPATH_USER_SWVERSION ((IPATH_USER_SWMAJOR<<16) | IPATH_USER_SWMINOR)
|
||||
|
||||
#define IPATH_KERN_TYPE 0
|
||||
|
||||
/*
|
||||
* Similarly, this is the kernel version going back to the user. It's
|
||||
* slightly different, in that we want to tell if the driver was built as
|
||||
* part of a QLogic release, or from the driver from openfabrics.org,
|
||||
* kernel.org, or a standard distribution, for support reasons.
|
||||
* The high bit is 0 for non-QLogic and 1 for QLogic-built/supplied.
|
||||
*
|
||||
* It's returned by the driver to the user code during initialization in the
|
||||
* spi_sw_version field of ipath_base_info, so the user code can in turn
|
||||
* check for compatibility with the kernel.
|
||||
*/
|
||||
#define IPATH_KERN_SWVERSION ((IPATH_KERN_TYPE<<31) | IPATH_USER_SWVERSION)
|
||||
|
||||
/*
|
||||
* This structure is passed to ipath_userinit() to tell the driver where
|
||||
* user code buffers are, sizes, etc. The offsets and sizes of the
|
||||
* fields must remain unchanged, for binary compatibility. It can
|
||||
* be extended, if userversion is changed so user code can tell, if needed
|
||||
*/
|
||||
struct ipath_user_info {
|
||||
/*
|
||||
* version of user software, to detect compatibility issues.
|
||||
* Should be set to IPATH_USER_SWVERSION.
|
||||
*/
|
||||
__u32 spu_userversion;
|
||||
|
||||
/* desired number of receive header queue entries */
|
||||
__u32 spu_rcvhdrcnt;
|
||||
|
||||
/* size of struct base_info to write to */
|
||||
__u32 spu_base_info_size;
|
||||
|
||||
/*
|
||||
* number of words in KD protocol header
|
||||
* This tells InfiniPath how many words to copy to rcvhdrq. If 0,
|
||||
* kernel uses a default. Once set, attempts to set any other value
|
||||
* are an error (EAGAIN) until driver is reloaded.
|
||||
*/
|
||||
__u32 spu_rcvhdrsize;
|
||||
|
||||
/*
|
||||
* If two or more processes wish to share a port, each process
|
||||
* must set the spu_subport_cnt and spu_subport_id to the same
|
||||
* values. The only restriction on the spu_subport_id is that
|
||||
* it be unique for a given node.
|
||||
*/
|
||||
__u16 spu_subport_cnt;
|
||||
__u16 spu_subport_id;
|
||||
|
||||
__u32 spu_unused; /* kept for compatible layout */
|
||||
|
||||
/*
|
||||
* address of struct base_info to write to
|
||||
*/
|
||||
__u64 spu_base_info;
|
||||
|
||||
} __attribute__ ((aligned(8)));
|
||||
|
||||
/* User commands. */
|
||||
|
||||
#define IPATH_CMD_MIN 16
|
||||
|
||||
#define __IPATH_CMD_USER_INIT 16 /* old set up userspace (for old user code) */
|
||||
#define IPATH_CMD_PORT_INFO 17 /* find out what resources we got */
|
||||
#define IPATH_CMD_RECV_CTRL 18 /* control receipt of packets */
|
||||
#define IPATH_CMD_TID_UPDATE 19 /* update expected TID entries */
|
||||
#define IPATH_CMD_TID_FREE 20 /* free expected TID entries */
|
||||
#define IPATH_CMD_SET_PART_KEY 21 /* add partition key */
|
||||
#define __IPATH_CMD_SLAVE_INFO 22 /* return info on slave processes (for old user code) */
|
||||
#define IPATH_CMD_ASSIGN_PORT 23 /* allocate HCA and port */
|
||||
#define IPATH_CMD_USER_INIT 24 /* set up userspace */
|
||||
#define IPATH_CMD_UNUSED_1 25
|
||||
#define IPATH_CMD_UNUSED_2 26
|
||||
#define IPATH_CMD_PIOAVAILUPD 27 /* force an update of PIOAvail reg */
|
||||
#define IPATH_CMD_POLL_TYPE 28 /* set the kind of polling we want */
|
||||
#define IPATH_CMD_ARMLAUNCH_CTRL 29 /* armlaunch detection control */
|
||||
/* 30 is unused */
|
||||
#define IPATH_CMD_SDMA_INFLIGHT 31 /* sdma inflight counter request */
|
||||
#define IPATH_CMD_SDMA_COMPLETE 32 /* sdma completion counter request */
|
||||
|
||||
/*
|
||||
* Poll types
|
||||
*/
|
||||
#define IPATH_POLL_TYPE_URGENT 0x01
|
||||
#define IPATH_POLL_TYPE_OVERFLOW 0x02
|
||||
|
||||
struct ipath_port_info {
|
||||
__u32 num_active; /* number of active units */
|
||||
__u32 unit; /* unit (chip) assigned to caller */
|
||||
__u16 port; /* port on unit assigned to caller */
|
||||
__u16 subport; /* subport on unit assigned to caller */
|
||||
__u16 num_ports; /* number of ports available on unit */
|
||||
__u16 num_subports; /* number of subports opened on port */
|
||||
};
|
||||
|
||||
struct ipath_tid_info {
|
||||
__u32 tidcnt;
|
||||
/* make structure same size in 32 and 64 bit */
|
||||
__u32 tid__unused;
|
||||
/* virtual address of first page in transfer */
|
||||
__u64 tidvaddr;
|
||||
/* pointer (same size 32/64 bit) to __u16 tid array */
|
||||
__u64 tidlist;
|
||||
|
||||
/*
|
||||
* pointer (same size 32/64 bit) to bitmap of TIDs used
|
||||
* for this call; checked for being large enough at open
|
||||
*/
|
||||
__u64 tidmap;
|
||||
};
|
||||
|
||||
struct ipath_cmd {
|
||||
__u32 type; /* command type */
|
||||
union {
|
||||
struct ipath_tid_info tid_info;
|
||||
struct ipath_user_info user_info;
|
||||
|
||||
/*
|
||||
* address in userspace where we should put the sdma
|
||||
* inflight counter
|
||||
*/
|
||||
__u64 sdma_inflight;
|
||||
/*
|
||||
* address in userspace where we should put the sdma
|
||||
* completion counter
|
||||
*/
|
||||
__u64 sdma_complete;
|
||||
/* address in userspace of struct ipath_port_info to
|
||||
write result to */
|
||||
__u64 port_info;
|
||||
/* enable/disable receipt of packets */
|
||||
__u32 recv_ctrl;
|
||||
/* enable/disable armlaunch errors (non-zero to enable) */
|
||||
__u32 armlaunch_ctrl;
|
||||
/* partition key to set */
|
||||
__u16 part_key;
|
||||
/* user address of __u32 bitmask of active slaves */
|
||||
__u64 slave_mask_addr;
|
||||
/* type of polling we want */
|
||||
__u16 poll_type;
|
||||
} cmd;
|
||||
};
|
||||
|
||||
struct ipath_iovec {
|
||||
/* Pointer to data, but same size 32 and 64 bit */
|
||||
__u64 iov_base;
|
||||
|
||||
/*
|
||||
* Length of data; don't need 64 bits, but want
|
||||
* ipath_sendpkt to remain same size as before 32 bit changes, so...
|
||||
*/
|
||||
__u64 iov_len;
|
||||
};
|
||||
|
||||
/*
|
||||
* Describes a single packet for send. Each packet can have one or more
|
||||
* buffers, but the total length (exclusive of IB headers) must be less
|
||||
* than the MTU, and if using the PIO method, entire packet length,
|
||||
* including IB headers, must be less than the ipath_piosize value (words).
|
||||
* Use of this necessitates including sys/uio.h
|
||||
*/
|
||||
struct __ipath_sendpkt {
|
||||
__u32 sps_flags; /* flags for packet (TBD) */
|
||||
__u32 sps_cnt; /* number of entries to use in sps_iov */
|
||||
/* array of iov's describing packet. TEMPORARY */
|
||||
struct ipath_iovec sps_iov[4];
|
||||
};
|
||||
|
||||
/*
|
||||
* diagnostics can send a packet by "writing" one of the following
|
||||
* two structs to diag data special file
|
||||
* The first is the legacy version for backward compatibility
|
||||
*/
|
||||
struct ipath_diag_pkt {
|
||||
__u32 unit;
|
||||
__u64 data;
|
||||
__u32 len;
|
||||
};
|
||||
|
||||
/* The second diag_pkt struct is the expanded version that allows
|
||||
* more control over the packet, specifically, by allowing a custom
|
||||
* pbc (+ static rate) qword, so that special modes and deliberate
|
||||
* changes to CRCs can be used. The elements were also re-ordered
|
||||
* for better alignment and to avoid padding issues.
|
||||
*/
|
||||
struct ipath_diag_xpkt {
|
||||
__u64 data;
|
||||
__u64 pbc_wd;
|
||||
__u32 unit;
|
||||
__u32 len;
|
||||
};
|
||||
|
||||
/*
|
||||
* Data layout in I2C flash (for GUID, etc.)
|
||||
* All fields are little-endian binary unless otherwise stated
|
||||
*/
|
||||
#define IPATH_FLASH_VERSION 2
|
||||
struct ipath_flash {
|
||||
/* flash layout version (IPATH_FLASH_VERSION) */
|
||||
__u8 if_fversion;
|
||||
/* checksum protecting if_length bytes */
|
||||
__u8 if_csum;
|
||||
/*
|
||||
* valid length (in use, protected by if_csum), including
|
||||
* if_fversion and if_csum themselves)
|
||||
*/
|
||||
__u8 if_length;
|
||||
/* the GUID, in network order */
|
||||
__u8 if_guid[8];
|
||||
/* number of GUIDs to use, starting from if_guid */
|
||||
__u8 if_numguid;
|
||||
/* the (last 10 characters of) board serial number, in ASCII */
|
||||
char if_serial[12];
|
||||
/* board mfg date (YYYYMMDD ASCII) */
|
||||
char if_mfgdate[8];
|
||||
/* last board rework/test date (YYYYMMDD ASCII) */
|
||||
char if_testdate[8];
|
||||
/* logging of error counts, TBD */
|
||||
__u8 if_errcntp[4];
|
||||
/* powered on hours, updated at driver unload */
|
||||
__u8 if_powerhour[2];
|
||||
/* ASCII free-form comment field */
|
||||
char if_comment[32];
|
||||
/* Backwards compatible prefix for longer QLogic Serial Numbers */
|
||||
char if_sprefix[4];
|
||||
/* 82 bytes used, min flash size is 128 bytes */
|
||||
__u8 if_future[46];
|
||||
};
|
||||
|
||||
/*
|
||||
* These are the counters implemented in the chip, and are listed in order.
|
||||
* The InterCaps naming is taken straight from the chip spec.
|
||||
*/
|
||||
struct infinipath_counters {
|
||||
__u64 LBIntCnt;
|
||||
__u64 LBFlowStallCnt;
|
||||
__u64 TxSDmaDescCnt; /* was Reserved1 */
|
||||
__u64 TxUnsupVLErrCnt;
|
||||
__u64 TxDataPktCnt;
|
||||
__u64 TxFlowPktCnt;
|
||||
__u64 TxDwordCnt;
|
||||
__u64 TxLenErrCnt;
|
||||
__u64 TxMaxMinLenErrCnt;
|
||||
__u64 TxUnderrunCnt;
|
||||
__u64 TxFlowStallCnt;
|
||||
__u64 TxDroppedPktCnt;
|
||||
__u64 RxDroppedPktCnt;
|
||||
__u64 RxDataPktCnt;
|
||||
__u64 RxFlowPktCnt;
|
||||
__u64 RxDwordCnt;
|
||||
__u64 RxLenErrCnt;
|
||||
__u64 RxMaxMinLenErrCnt;
|
||||
__u64 RxICRCErrCnt;
|
||||
__u64 RxVCRCErrCnt;
|
||||
__u64 RxFlowCtrlErrCnt;
|
||||
__u64 RxBadFormatCnt;
|
||||
__u64 RxLinkProblemCnt;
|
||||
__u64 RxEBPCnt;
|
||||
__u64 RxLPCRCErrCnt;
|
||||
__u64 RxBufOvflCnt;
|
||||
__u64 RxTIDFullErrCnt;
|
||||
__u64 RxTIDValidErrCnt;
|
||||
__u64 RxPKeyMismatchCnt;
|
||||
__u64 RxP0HdrEgrOvflCnt;
|
||||
__u64 RxP1HdrEgrOvflCnt;
|
||||
__u64 RxP2HdrEgrOvflCnt;
|
||||
__u64 RxP3HdrEgrOvflCnt;
|
||||
__u64 RxP4HdrEgrOvflCnt;
|
||||
__u64 RxP5HdrEgrOvflCnt;
|
||||
__u64 RxP6HdrEgrOvflCnt;
|
||||
__u64 RxP7HdrEgrOvflCnt;
|
||||
__u64 RxP8HdrEgrOvflCnt;
|
||||
__u64 RxP9HdrEgrOvflCnt; /* was Reserved6 */
|
||||
__u64 RxP10HdrEgrOvflCnt; /* was Reserved7 */
|
||||
__u64 RxP11HdrEgrOvflCnt; /* new for IBA7220 */
|
||||
__u64 RxP12HdrEgrOvflCnt; /* new for IBA7220 */
|
||||
__u64 RxP13HdrEgrOvflCnt; /* new for IBA7220 */
|
||||
__u64 RxP14HdrEgrOvflCnt; /* new for IBA7220 */
|
||||
__u64 RxP15HdrEgrOvflCnt; /* new for IBA7220 */
|
||||
__u64 RxP16HdrEgrOvflCnt; /* new for IBA7220 */
|
||||
__u64 IBStatusChangeCnt;
|
||||
__u64 IBLinkErrRecoveryCnt;
|
||||
__u64 IBLinkDownedCnt;
|
||||
__u64 IBSymbolErrCnt;
|
||||
/* The following are new for IBA7220 */
|
||||
__u64 RxVL15DroppedPktCnt;
|
||||
__u64 RxOtherLocalPhyErrCnt;
|
||||
__u64 PcieRetryBufDiagQwordCnt;
|
||||
__u64 ExcessBufferOvflCnt;
|
||||
__u64 LocalLinkIntegrityErrCnt;
|
||||
__u64 RxVlErrCnt;
|
||||
__u64 RxDlidFltrCnt;
|
||||
};
|
||||
|
||||
/*
|
||||
* The next set of defines are for packet headers, and chip register
|
||||
* and memory bits that are visible to and/or used by user-mode software
|
||||
* The other bits that are used only by the driver or diags are in
|
||||
* ipath_registers.h
|
||||
*/
|
||||
|
||||
/* RcvHdrFlags bits */
|
||||
#define INFINIPATH_RHF_LENGTH_MASK 0x7FF
|
||||
#define INFINIPATH_RHF_LENGTH_SHIFT 0
|
||||
#define INFINIPATH_RHF_RCVTYPE_MASK 0x7
|
||||
#define INFINIPATH_RHF_RCVTYPE_SHIFT 11
|
||||
#define INFINIPATH_RHF_EGRINDEX_MASK 0xFFF
|
||||
#define INFINIPATH_RHF_EGRINDEX_SHIFT 16
|
||||
#define INFINIPATH_RHF_SEQ_MASK 0xF
|
||||
#define INFINIPATH_RHF_SEQ_SHIFT 0
|
||||
#define INFINIPATH_RHF_HDRQ_OFFSET_MASK 0x7FF
|
||||
#define INFINIPATH_RHF_HDRQ_OFFSET_SHIFT 4
|
||||
#define INFINIPATH_RHF_H_ICRCERR 0x80000000
|
||||
#define INFINIPATH_RHF_H_VCRCERR 0x40000000
|
||||
#define INFINIPATH_RHF_H_PARITYERR 0x20000000
|
||||
#define INFINIPATH_RHF_H_LENERR 0x10000000
|
||||
#define INFINIPATH_RHF_H_MTUERR 0x08000000
|
||||
#define INFINIPATH_RHF_H_IHDRERR 0x04000000
|
||||
#define INFINIPATH_RHF_H_TIDERR 0x02000000
|
||||
#define INFINIPATH_RHF_H_MKERR 0x01000000
|
||||
#define INFINIPATH_RHF_H_IBERR 0x00800000
|
||||
#define INFINIPATH_RHF_H_ERR_MASK 0xFF800000
|
||||
#define INFINIPATH_RHF_L_USE_EGR 0x80000000
|
||||
#define INFINIPATH_RHF_L_SWA 0x00008000
|
||||
#define INFINIPATH_RHF_L_SWB 0x00004000
|
||||
|
||||
/* infinipath header fields */
|
||||
#define INFINIPATH_I_VERS_MASK 0xF
|
||||
#define INFINIPATH_I_VERS_SHIFT 28
|
||||
#define INFINIPATH_I_PORT_MASK 0xF
|
||||
#define INFINIPATH_I_PORT_SHIFT 24
|
||||
#define INFINIPATH_I_TID_MASK 0x7FF
|
||||
#define INFINIPATH_I_TID_SHIFT 13
|
||||
#define INFINIPATH_I_OFFSET_MASK 0x1FFF
|
||||
#define INFINIPATH_I_OFFSET_SHIFT 0
|
||||
|
||||
/* K_PktFlags bits */
|
||||
#define INFINIPATH_KPF_INTR 0x1
|
||||
#define INFINIPATH_KPF_SUBPORT_MASK 0x3
|
||||
#define INFINIPATH_KPF_SUBPORT_SHIFT 1
|
||||
|
||||
#define INFINIPATH_MAX_SUBPORT 4
|
||||
|
||||
/* SendPIO per-buffer control */
|
||||
#define INFINIPATH_SP_TEST 0x40
|
||||
#define INFINIPATH_SP_TESTEBP 0x20
|
||||
#define INFINIPATH_SP_TRIGGER_SHIFT 15
|
||||
|
||||
/* SendPIOAvail bits */
|
||||
#define INFINIPATH_SENDPIOAVAIL_BUSY_SHIFT 1
|
||||
#define INFINIPATH_SENDPIOAVAIL_CHECK_SHIFT 0
|
||||
|
||||
/* infinipath header format */
|
||||
struct ipath_header {
|
||||
/*
|
||||
* Version - 4 bits, Port - 4 bits, TID - 10 bits and Offset -
|
||||
* 14 bits before ECO change ~28 Dec 03. After that, Vers 4,
|
||||
* Port 4, TID 11, offset 13.
|
||||
*/
|
||||
__le32 ver_port_tid_offset;
|
||||
__le16 chksum;
|
||||
__le16 pkt_flags;
|
||||
};
|
||||
|
||||
/* infinipath user message header format.
|
||||
* This structure contains the first 4 fields common to all protocols
|
||||
* that employ infinipath.
|
||||
*/
|
||||
struct ipath_message_header {
|
||||
__be16 lrh[4];
|
||||
__be32 bth[3];
|
||||
/* fields below this point are in host byte order */
|
||||
struct ipath_header iph;
|
||||
__u8 sub_opcode;
|
||||
};
|
||||
|
||||
/* infinipath ethernet header format */
|
||||
struct ether_header {
|
||||
__be16 lrh[4];
|
||||
__be32 bth[3];
|
||||
struct ipath_header iph;
|
||||
__u8 sub_opcode;
|
||||
__u8 cmd;
|
||||
__be16 lid;
|
||||
__u16 mac[3];
|
||||
__u8 frag_num;
|
||||
__u8 seq_num;
|
||||
__le32 len;
|
||||
/* MUST be of word size due to PIO write requirements */
|
||||
__le32 csum;
|
||||
__le16 csum_offset;
|
||||
__le16 flags;
|
||||
__u16 first_2_bytes;
|
||||
__u8 unused[2]; /* currently unused */
|
||||
};
|
||||
|
||||
|
||||
/* IB - LRH header consts */
|
||||
#define IPATH_LRH_GRH 0x0003 /* 1. word of IB LRH - next header: GRH */
|
||||
#define IPATH_LRH_BTH 0x0002 /* 1. word of IB LRH - next header: BTH */
|
||||
|
||||
/* misc. */
|
||||
#define SIZE_OF_CRC 1
|
||||
|
||||
#define IPATH_DEFAULT_P_KEY 0xFFFF
|
||||
#define IPATH_PERMISSIVE_LID 0xFFFF
|
||||
#define IPATH_AETH_CREDIT_SHIFT 24
|
||||
#define IPATH_AETH_CREDIT_MASK 0x1F
|
||||
#define IPATH_AETH_CREDIT_INVAL 0x1F
|
||||
#define IPATH_PSN_MASK 0xFFFFFF
|
||||
#define IPATH_MSN_MASK 0xFFFFFF
|
||||
#define IPATH_QPN_MASK 0xFFFFFF
|
||||
#define IPATH_MULTICAST_LID_BASE 0xC000
|
||||
#define IPATH_EAGER_TID_ID INFINIPATH_I_TID_MASK
|
||||
#define IPATH_MULTICAST_QPN 0xFFFFFF
|
||||
|
||||
/* Receive Header Queue: receive type (from infinipath) */
|
||||
#define RCVHQ_RCV_TYPE_EXPECTED 0
|
||||
#define RCVHQ_RCV_TYPE_EAGER 1
|
||||
#define RCVHQ_RCV_TYPE_NON_KD 2
|
||||
#define RCVHQ_RCV_TYPE_ERROR 3
|
||||
|
||||
|
||||
/* sub OpCodes - ith4x */
|
||||
#define IPATH_ITH4X_OPCODE_ENCAP 0x81
|
||||
#define IPATH_ITH4X_OPCODE_LID_ARP 0x82
|
||||
|
||||
#define IPATH_HEADER_QUEUE_WORDS 9
|
||||
|
||||
/* functions for extracting fields from rcvhdrq entries for the driver.
|
||||
*/
|
||||
static inline __u32 ipath_hdrget_err_flags(const __le32 * rbuf)
|
||||
{
|
||||
return __le32_to_cpu(rbuf[1]) & INFINIPATH_RHF_H_ERR_MASK;
|
||||
}
|
||||
|
||||
static inline __u32 ipath_hdrget_rcv_type(const __le32 * rbuf)
|
||||
{
|
||||
return (__le32_to_cpu(rbuf[0]) >> INFINIPATH_RHF_RCVTYPE_SHIFT)
|
||||
& INFINIPATH_RHF_RCVTYPE_MASK;
|
||||
}
|
||||
|
||||
static inline __u32 ipath_hdrget_length_in_bytes(const __le32 * rbuf)
|
||||
{
|
||||
return ((__le32_to_cpu(rbuf[0]) >> INFINIPATH_RHF_LENGTH_SHIFT)
|
||||
& INFINIPATH_RHF_LENGTH_MASK) << 2;
|
||||
}
|
||||
|
||||
static inline __u32 ipath_hdrget_index(const __le32 * rbuf)
|
||||
{
|
||||
return (__le32_to_cpu(rbuf[0]) >> INFINIPATH_RHF_EGRINDEX_SHIFT)
|
||||
& INFINIPATH_RHF_EGRINDEX_MASK;
|
||||
}
|
||||
|
||||
static inline __u32 ipath_hdrget_seq(const __le32 *rbuf)
|
||||
{
|
||||
return (__le32_to_cpu(rbuf[1]) >> INFINIPATH_RHF_SEQ_SHIFT)
|
||||
& INFINIPATH_RHF_SEQ_MASK;
|
||||
}
|
||||
|
||||
static inline __u32 ipath_hdrget_offset(const __le32 *rbuf)
|
||||
{
|
||||
return (__le32_to_cpu(rbuf[1]) >> INFINIPATH_RHF_HDRQ_OFFSET_SHIFT)
|
||||
& INFINIPATH_RHF_HDRQ_OFFSET_MASK;
|
||||
}
|
||||
|
||||
static inline __u32 ipath_hdrget_use_egr_buf(const __le32 *rbuf)
|
||||
{
|
||||
return __le32_to_cpu(rbuf[0]) & INFINIPATH_RHF_L_USE_EGR;
|
||||
}
|
||||
|
||||
static inline __u32 ipath_hdrget_ipath_ver(__le32 hdrword)
|
||||
{
|
||||
return (__le32_to_cpu(hdrword) >> INFINIPATH_I_VERS_SHIFT)
|
||||
& INFINIPATH_I_VERS_MASK;
|
||||
}
|
||||
|
||||
#endif /* _IPATH_COMMON_H */
|
|
@ -1,483 +0,0 @@
|
|||
/*
|
||||
* Copyright (c) 2006, 2007 QLogic Corporation. All rights reserved.
|
||||
* Copyright (c) 2005, 2006 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.
|
||||
*/
|
||||
|
||||
#include <linux/err.h>
|
||||
#include <linux/slab.h>
|
||||
#include <linux/vmalloc.h>
|
||||
|
||||
#include "ipath_verbs.h"
|
||||
|
||||
/**
|
||||
* ipath_cq_enter - add a new entry to the completion queue
|
||||
* @cq: completion queue
|
||||
* @entry: work completion entry to add
|
||||
* @sig: true if @entry is a solicitated entry
|
||||
*
|
||||
* This may be called with qp->s_lock held.
|
||||
*/
|
||||
void ipath_cq_enter(struct ipath_cq *cq, struct ib_wc *entry, int solicited)
|
||||
{
|
||||
struct ipath_cq_wc *wc;
|
||||
unsigned long flags;
|
||||
u32 head;
|
||||
u32 next;
|
||||
|
||||
spin_lock_irqsave(&cq->lock, flags);
|
||||
|
||||
/*
|
||||
* Note that the head pointer might be writable by user processes.
|
||||
* Take care to verify it is a sane value.
|
||||
*/
|
||||
wc = cq->queue;
|
||||
head = wc->head;
|
||||
if (head >= (unsigned) cq->ibcq.cqe) {
|
||||
head = cq->ibcq.cqe;
|
||||
next = 0;
|
||||
} else
|
||||
next = head + 1;
|
||||
if (unlikely(next == wc->tail)) {
|
||||
spin_unlock_irqrestore(&cq->lock, flags);
|
||||
if (cq->ibcq.event_handler) {
|
||||
struct ib_event ev;
|
||||
|
||||
ev.device = cq->ibcq.device;
|
||||
ev.element.cq = &cq->ibcq;
|
||||
ev.event = IB_EVENT_CQ_ERR;
|
||||
cq->ibcq.event_handler(&ev, cq->ibcq.cq_context);
|
||||
}
|
||||
return;
|
||||
}
|
||||
if (cq->ip) {
|
||||
wc->uqueue[head].wr_id = entry->wr_id;
|
||||
wc->uqueue[head].status = entry->status;
|
||||
wc->uqueue[head].opcode = entry->opcode;
|
||||
wc->uqueue[head].vendor_err = entry->vendor_err;
|
||||
wc->uqueue[head].byte_len = entry->byte_len;
|
||||
wc->uqueue[head].ex.imm_data = (__u32 __force) entry->ex.imm_data;
|
||||
wc->uqueue[head].qp_num = entry->qp->qp_num;
|
||||
wc->uqueue[head].src_qp = entry->src_qp;
|
||||
wc->uqueue[head].wc_flags = entry->wc_flags;
|
||||
wc->uqueue[head].pkey_index = entry->pkey_index;
|
||||
wc->uqueue[head].slid = entry->slid;
|
||||
wc->uqueue[head].sl = entry->sl;
|
||||
wc->uqueue[head].dlid_path_bits = entry->dlid_path_bits;
|
||||
wc->uqueue[head].port_num = entry->port_num;
|
||||
/* Make sure entry is written before the head index. */
|
||||
smp_wmb();
|
||||
} else
|
||||
wc->kqueue[head] = *entry;
|
||||
wc->head = next;
|
||||
|
||||
if (cq->notify == IB_CQ_NEXT_COMP ||
|
||||
(cq->notify == IB_CQ_SOLICITED && solicited)) {
|
||||
cq->notify = IB_CQ_NONE;
|
||||
cq->triggered++;
|
||||
/*
|
||||
* This will cause send_complete() to be called in
|
||||
* another thread.
|
||||
*/
|
||||
tasklet_hi_schedule(&cq->comptask);
|
||||
}
|
||||
|
||||
spin_unlock_irqrestore(&cq->lock, flags);
|
||||
|
||||
if (entry->status != IB_WC_SUCCESS)
|
||||
to_idev(cq->ibcq.device)->n_wqe_errs++;
|
||||
}
|
||||
|
||||
/**
|
||||
* ipath_poll_cq - poll for work completion entries
|
||||
* @ibcq: the completion queue to poll
|
||||
* @num_entries: the maximum number of entries to return
|
||||
* @entry: pointer to array where work completions are placed
|
||||
*
|
||||
* Returns the number of completion entries polled.
|
||||
*
|
||||
* This may be called from interrupt context. Also called by ib_poll_cq()
|
||||
* in the generic verbs code.
|
||||
*/
|
||||
int ipath_poll_cq(struct ib_cq *ibcq, int num_entries, struct ib_wc *entry)
|
||||
{
|
||||
struct ipath_cq *cq = to_icq(ibcq);
|
||||
struct ipath_cq_wc *wc;
|
||||
unsigned long flags;
|
||||
int npolled;
|
||||
u32 tail;
|
||||
|
||||
/* The kernel can only poll a kernel completion queue */
|
||||
if (cq->ip) {
|
||||
npolled = -EINVAL;
|
||||
goto bail;
|
||||
}
|
||||
|
||||
spin_lock_irqsave(&cq->lock, flags);
|
||||
|
||||
wc = cq->queue;
|
||||
tail = wc->tail;
|
||||
if (tail > (u32) cq->ibcq.cqe)
|
||||
tail = (u32) cq->ibcq.cqe;
|
||||
for (npolled = 0; npolled < num_entries; ++npolled, ++entry) {
|
||||
if (tail == wc->head)
|
||||
break;
|
||||
/* The kernel doesn't need a RMB since it has the lock. */
|
||||
*entry = wc->kqueue[tail];
|
||||
if (tail >= cq->ibcq.cqe)
|
||||
tail = 0;
|
||||
else
|
||||
tail++;
|
||||
}
|
||||
wc->tail = tail;
|
||||
|
||||
spin_unlock_irqrestore(&cq->lock, flags);
|
||||
|
||||
bail:
|
||||
return npolled;
|
||||
}
|
||||
|
||||
static void send_complete(unsigned long data)
|
||||
{
|
||||
struct ipath_cq *cq = (struct ipath_cq *)data;
|
||||
|
||||
/*
|
||||
* The completion handler will most likely rearm the notification
|
||||
* and poll for all pending entries. If a new completion entry
|
||||
* is added while we are in this routine, tasklet_hi_schedule()
|
||||
* won't call us again until we return so we check triggered to
|
||||
* see if we need to call the handler again.
|
||||
*/
|
||||
for (;;) {
|
||||
u8 triggered = cq->triggered;
|
||||
|
||||
cq->ibcq.comp_handler(&cq->ibcq, cq->ibcq.cq_context);
|
||||
|
||||
if (cq->triggered == triggered)
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* ipath_create_cq - create a completion queue
|
||||
* @ibdev: the device this completion queue is attached to
|
||||
* @attr: creation attributes
|
||||
* @context: unused by the InfiniPath driver
|
||||
* @udata: unused by the InfiniPath driver
|
||||
*
|
||||
* Returns a pointer to the completion queue or negative errno values
|
||||
* for failure.
|
||||
*
|
||||
* Called by ib_create_cq() in the generic verbs code.
|
||||
*/
|
||||
struct ib_cq *ipath_create_cq(struct ib_device *ibdev,
|
||||
const struct ib_cq_init_attr *attr,
|
||||
struct ib_ucontext *context,
|
||||
struct ib_udata *udata)
|
||||
{
|
||||
int entries = attr->cqe;
|
||||
struct ipath_ibdev *dev = to_idev(ibdev);
|
||||
struct ipath_cq *cq;
|
||||
struct ipath_cq_wc *wc;
|
||||
struct ib_cq *ret;
|
||||
u32 sz;
|
||||
|
||||
if (attr->flags)
|
||||
return ERR_PTR(-EINVAL);
|
||||
|
||||
if (entries < 1 || entries > ib_ipath_max_cqes) {
|
||||
ret = ERR_PTR(-EINVAL);
|
||||
goto done;
|
||||
}
|
||||
|
||||
/* Allocate the completion queue structure. */
|
||||
cq = kmalloc(sizeof(*cq), GFP_KERNEL);
|
||||
if (!cq) {
|
||||
ret = ERR_PTR(-ENOMEM);
|
||||
goto done;
|
||||
}
|
||||
|
||||
/*
|
||||
* Allocate the completion queue entries and head/tail pointers.
|
||||
* This is allocated separately so that it can be resized and
|
||||
* also mapped into user space.
|
||||
* We need to use vmalloc() in order to support mmap and large
|
||||
* numbers of entries.
|
||||
*/
|
||||
sz = sizeof(*wc);
|
||||
if (udata && udata->outlen >= sizeof(__u64))
|
||||
sz += sizeof(struct ib_uverbs_wc) * (entries + 1);
|
||||
else
|
||||
sz += sizeof(struct ib_wc) * (entries + 1);
|
||||
wc = vmalloc_user(sz);
|
||||
if (!wc) {
|
||||
ret = ERR_PTR(-ENOMEM);
|
||||
goto bail_cq;
|
||||
}
|
||||
|
||||
/*
|
||||
* Return the address of the WC as the offset to mmap.
|
||||
* See ipath_mmap() for details.
|
||||
*/
|
||||
if (udata && udata->outlen >= sizeof(__u64)) {
|
||||
int err;
|
||||
|
||||
cq->ip = ipath_create_mmap_info(dev, sz, context, wc);
|
||||
if (!cq->ip) {
|
||||
ret = ERR_PTR(-ENOMEM);
|
||||
goto bail_wc;
|
||||
}
|
||||
|
||||
err = ib_copy_to_udata(udata, &cq->ip->offset,
|
||||
sizeof(cq->ip->offset));
|
||||
if (err) {
|
||||
ret = ERR_PTR(err);
|
||||
goto bail_ip;
|
||||
}
|
||||
} else
|
||||
cq->ip = NULL;
|
||||
|
||||
spin_lock(&dev->n_cqs_lock);
|
||||
if (dev->n_cqs_allocated == ib_ipath_max_cqs) {
|
||||
spin_unlock(&dev->n_cqs_lock);
|
||||
ret = ERR_PTR(-ENOMEM);
|
||||
goto bail_ip;
|
||||
}
|
||||
|
||||
dev->n_cqs_allocated++;
|
||||
spin_unlock(&dev->n_cqs_lock);
|
||||
|
||||
if (cq->ip) {
|
||||
spin_lock_irq(&dev->pending_lock);
|
||||
list_add(&cq->ip->pending_mmaps, &dev->pending_mmaps);
|
||||
spin_unlock_irq(&dev->pending_lock);
|
||||
}
|
||||
|
||||
/*
|
||||
* ib_create_cq() will initialize cq->ibcq except for cq->ibcq.cqe.
|
||||
* The number of entries should be >= the number requested or return
|
||||
* an error.
|
||||
*/
|
||||
cq->ibcq.cqe = entries;
|
||||
cq->notify = IB_CQ_NONE;
|
||||
cq->triggered = 0;
|
||||
spin_lock_init(&cq->lock);
|
||||
tasklet_init(&cq->comptask, send_complete, (unsigned long)cq);
|
||||
wc->head = 0;
|
||||
wc->tail = 0;
|
||||
cq->queue = wc;
|
||||
|
||||
ret = &cq->ibcq;
|
||||
|
||||
goto done;
|
||||
|
||||
bail_ip:
|
||||
kfree(cq->ip);
|
||||
bail_wc:
|
||||
vfree(wc);
|
||||
bail_cq:
|
||||
kfree(cq);
|
||||
done:
|
||||
return ret;
|
||||
}
|
||||
|
||||
/**
|
||||
* ipath_destroy_cq - destroy a completion queue
|
||||
* @ibcq: the completion queue to destroy.
|
||||
*
|
||||
* Returns 0 for success.
|
||||
*
|
||||
* Called by ib_destroy_cq() in the generic verbs code.
|
||||
*/
|
||||
int ipath_destroy_cq(struct ib_cq *ibcq)
|
||||
{
|
||||
struct ipath_ibdev *dev = to_idev(ibcq->device);
|
||||
struct ipath_cq *cq = to_icq(ibcq);
|
||||
|
||||
tasklet_kill(&cq->comptask);
|
||||
spin_lock(&dev->n_cqs_lock);
|
||||
dev->n_cqs_allocated--;
|
||||
spin_unlock(&dev->n_cqs_lock);
|
||||
if (cq->ip)
|
||||
kref_put(&cq->ip->ref, ipath_release_mmap_info);
|
||||
else
|
||||
vfree(cq->queue);
|
||||
kfree(cq);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* ipath_req_notify_cq - change the notification type for a completion queue
|
||||
* @ibcq: the completion queue
|
||||
* @notify_flags: the type of notification to request
|
||||
*
|
||||
* Returns 0 for success.
|
||||
*
|
||||
* This may be called from interrupt context. Also called by
|
||||
* ib_req_notify_cq() in the generic verbs code.
|
||||
*/
|
||||
int ipath_req_notify_cq(struct ib_cq *ibcq, enum ib_cq_notify_flags notify_flags)
|
||||
{
|
||||
struct ipath_cq *cq = to_icq(ibcq);
|
||||
unsigned long flags;
|
||||
int ret = 0;
|
||||
|
||||
spin_lock_irqsave(&cq->lock, flags);
|
||||
/*
|
||||
* Don't change IB_CQ_NEXT_COMP to IB_CQ_SOLICITED but allow
|
||||
* any other transitions (see C11-31 and C11-32 in ch. 11.4.2.2).
|
||||
*/
|
||||
if (cq->notify != IB_CQ_NEXT_COMP)
|
||||
cq->notify = notify_flags & IB_CQ_SOLICITED_MASK;
|
||||
|
||||
if ((notify_flags & IB_CQ_REPORT_MISSED_EVENTS) &&
|
||||
cq->queue->head != cq->queue->tail)
|
||||
ret = 1;
|
||||
|
||||
spin_unlock_irqrestore(&cq->lock, flags);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
/**
|
||||
* ipath_resize_cq - change the size of the CQ
|
||||
* @ibcq: the completion queue
|
||||
*
|
||||
* Returns 0 for success.
|
||||
*/
|
||||
int ipath_resize_cq(struct ib_cq *ibcq, int cqe, struct ib_udata *udata)
|
||||
{
|
||||
struct ipath_cq *cq = to_icq(ibcq);
|
||||
struct ipath_cq_wc *old_wc;
|
||||
struct ipath_cq_wc *wc;
|
||||
u32 head, tail, n;
|
||||
int ret;
|
||||
u32 sz;
|
||||
|
||||
if (cqe < 1 || cqe > ib_ipath_max_cqes) {
|
||||
ret = -EINVAL;
|
||||
goto bail;
|
||||
}
|
||||
|
||||
/*
|
||||
* Need to use vmalloc() if we want to support large #s of entries.
|
||||
*/
|
||||
sz = sizeof(*wc);
|
||||
if (udata && udata->outlen >= sizeof(__u64))
|
||||
sz += sizeof(struct ib_uverbs_wc) * (cqe + 1);
|
||||
else
|
||||
sz += sizeof(struct ib_wc) * (cqe + 1);
|
||||
wc = vmalloc_user(sz);
|
||||
if (!wc) {
|
||||
ret = -ENOMEM;
|
||||
goto bail;
|
||||
}
|
||||
|
||||
/* Check that we can write the offset to mmap. */
|
||||
if (udata && udata->outlen >= sizeof(__u64)) {
|
||||
__u64 offset = 0;
|
||||
|
||||
ret = ib_copy_to_udata(udata, &offset, sizeof(offset));
|
||||
if (ret)
|
||||
goto bail_free;
|
||||
}
|
||||
|
||||
spin_lock_irq(&cq->lock);
|
||||
/*
|
||||
* Make sure head and tail are sane since they
|
||||
* might be user writable.
|
||||
*/
|
||||
old_wc = cq->queue;
|
||||
head = old_wc->head;
|
||||
if (head > (u32) cq->ibcq.cqe)
|
||||
head = (u32) cq->ibcq.cqe;
|
||||
tail = old_wc->tail;
|
||||
if (tail > (u32) cq->ibcq.cqe)
|
||||
tail = (u32) cq->ibcq.cqe;
|
||||
if (head < tail)
|
||||
n = cq->ibcq.cqe + 1 + head - tail;
|
||||
else
|
||||
n = head - tail;
|
||||
if (unlikely((u32)cqe < n)) {
|
||||
ret = -EINVAL;
|
||||
goto bail_unlock;
|
||||
}
|
||||
for (n = 0; tail != head; n++) {
|
||||
if (cq->ip)
|
||||
wc->uqueue[n] = old_wc->uqueue[tail];
|
||||
else
|
||||
wc->kqueue[n] = old_wc->kqueue[tail];
|
||||
if (tail == (u32) cq->ibcq.cqe)
|
||||
tail = 0;
|
||||
else
|
||||
tail++;
|
||||
}
|
||||
cq->ibcq.cqe = cqe;
|
||||
wc->head = n;
|
||||
wc->tail = 0;
|
||||
cq->queue = wc;
|
||||
spin_unlock_irq(&cq->lock);
|
||||
|
||||
vfree(old_wc);
|
||||
|
||||
if (cq->ip) {
|
||||
struct ipath_ibdev *dev = to_idev(ibcq->device);
|
||||
struct ipath_mmap_info *ip = cq->ip;
|
||||
|
||||
ipath_update_mmap_info(dev, ip, sz, wc);
|
||||
|
||||
/*
|
||||
* Return the offset to mmap.
|
||||
* See ipath_mmap() for details.
|
||||
*/
|
||||
if (udata && udata->outlen >= sizeof(__u64)) {
|
||||
ret = ib_copy_to_udata(udata, &ip->offset,
|
||||
sizeof(ip->offset));
|
||||
if (ret)
|
||||
goto bail;
|
||||
}
|
||||
|
||||
spin_lock_irq(&dev->pending_lock);
|
||||
if (list_empty(&ip->pending_mmaps))
|
||||
list_add(&ip->pending_mmaps, &dev->pending_mmaps);
|
||||
spin_unlock_irq(&dev->pending_lock);
|
||||
}
|
||||
|
||||
ret = 0;
|
||||
goto bail;
|
||||
|
||||
bail_unlock:
|
||||
spin_unlock_irq(&cq->lock);
|
||||
bail_free:
|
||||
vfree(wc);
|
||||
bail:
|
||||
return ret;
|
||||
}
|
|
@ -1,99 +0,0 @@
|
|||
/*
|
||||
* Copyright (c) 2006, 2007 QLogic Corporation. All rights reserved.
|
||||
* Copyright (c) 2003, 2004, 2005, 2006 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.
|
||||
*/
|
||||
|
||||
#ifndef _IPATH_DEBUG_H
|
||||
#define _IPATH_DEBUG_H
|
||||
|
||||
#ifndef _IPATH_DEBUGGING /* debugging enabled or not */
|
||||
#define _IPATH_DEBUGGING 1
|
||||
#endif
|
||||
|
||||
#if _IPATH_DEBUGGING
|
||||
|
||||
/*
|
||||
* Mask values for debugging. The scheme allows us to compile out any
|
||||
* of the debug tracing stuff, and if compiled in, to enable or disable
|
||||
* dynamically. This can be set at modprobe time also:
|
||||
* modprobe infinipath.ko infinipath_debug=7
|
||||
*/
|
||||
|
||||
#define __IPATH_INFO 0x1 /* generic low verbosity stuff */
|
||||
#define __IPATH_DBG 0x2 /* generic debug */
|
||||
#define __IPATH_TRSAMPLE 0x8 /* generate trace buffer sample entries */
|
||||
/* leave some low verbosity spots open */
|
||||
#define __IPATH_VERBDBG 0x40 /* very verbose debug */
|
||||
#define __IPATH_PKTDBG 0x80 /* print packet data */
|
||||
/* print process startup (init)/exit messages */
|
||||
#define __IPATH_PROCDBG 0x100
|
||||
/* print mmap/fault stuff, not using VDBG any more */
|
||||
#define __IPATH_MMDBG 0x200
|
||||
#define __IPATH_ERRPKTDBG 0x400
|
||||
#define __IPATH_USER_SEND 0x1000 /* use user mode send */
|
||||
#define __IPATH_KERNEL_SEND 0x2000 /* use kernel mode send */
|
||||
#define __IPATH_EPKTDBG 0x4000 /* print ethernet packet data */
|
||||
#define __IPATH_IPATHDBG 0x10000 /* Ethernet (IPATH) gen debug */
|
||||
#define __IPATH_IPATHWARN 0x20000 /* Ethernet (IPATH) warnings */
|
||||
#define __IPATH_IPATHERR 0x40000 /* Ethernet (IPATH) errors */
|
||||
#define __IPATH_IPATHPD 0x80000 /* Ethernet (IPATH) packet dump */
|
||||
#define __IPATH_IPATHTABLE 0x100000 /* Ethernet (IPATH) table dump */
|
||||
#define __IPATH_LINKVERBDBG 0x200000 /* very verbose linkchange debug */
|
||||
|
||||
#else /* _IPATH_DEBUGGING */
|
||||
|
||||
/*
|
||||
* define all of these even with debugging off, for the few places that do
|
||||
* if(infinipath_debug & _IPATH_xyzzy), but in a way that will make the
|
||||
* compiler eliminate the code
|
||||
*/
|
||||
|
||||
#define __IPATH_INFO 0x0 /* generic low verbosity stuff */
|
||||
#define __IPATH_DBG 0x0 /* generic debug */
|
||||
#define __IPATH_TRSAMPLE 0x0 /* generate trace buffer sample entries */
|
||||
#define __IPATH_VERBDBG 0x0 /* very verbose debug */
|
||||
#define __IPATH_PKTDBG 0x0 /* print packet data */
|
||||
#define __IPATH_PROCDBG 0x0 /* process startup (init)/exit messages */
|
||||
/* print mmap/fault stuff, not using VDBG any more */
|
||||
#define __IPATH_MMDBG 0x0
|
||||
#define __IPATH_EPKTDBG 0x0 /* print ethernet packet data */
|
||||
#define __IPATH_IPATHDBG 0x0 /* Ethernet (IPATH) table dump on */
|
||||
#define __IPATH_IPATHWARN 0x0 /* Ethernet (IPATH) warnings on */
|
||||
#define __IPATH_IPATHERR 0x0 /* Ethernet (IPATH) errors on */
|
||||
#define __IPATH_IPATHPD 0x0 /* Ethernet (IPATH) packet dump on */
|
||||
#define __IPATH_IPATHTABLE 0x0 /* Ethernet (IPATH) packet dump on */
|
||||
#define __IPATH_LINKVERBDBG 0x0 /* very verbose linkchange debug */
|
||||
|
||||
#endif /* _IPATH_DEBUGGING */
|
||||
|
||||
#define __IPATH_VERBOSEDBG __IPATH_VERBDBG
|
||||
|
||||
#endif /* _IPATH_DEBUG_H */
|
|
@ -1,551 +0,0 @@
|
|||
/*
|
||||
* Copyright (c) 2006, 2007, 2008 QLogic Corporation. All rights reserved.
|
||||
* Copyright (c) 2003, 2004, 2005, 2006 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.
|
||||
*/
|
||||
|
||||
/*
|
||||
* This file contains support for diagnostic functions. It is accessed by
|
||||
* opening the ipath_diag device, normally minor number 129. Diagnostic use
|
||||
* of the InfiniPath chip may render the chip or board unusable until the
|
||||
* driver is unloaded, or in some cases, until the system is rebooted.
|
||||
*
|
||||
* Accesses to the chip through this interface are not similar to going
|
||||
* through the /sys/bus/pci resource mmap interface.
|
||||
*/
|
||||
|
||||
#include <linux/io.h>
|
||||
#include <linux/pci.h>
|
||||
#include <linux/vmalloc.h>
|
||||
#include <linux/fs.h>
|
||||
#include <linux/export.h>
|
||||
#include <asm/uaccess.h>
|
||||
|
||||
#include "ipath_kernel.h"
|
||||
#include "ipath_common.h"
|
||||
|
||||
int ipath_diag_inuse;
|
||||
static int diag_set_link;
|
||||
|
||||
static int ipath_diag_open(struct inode *in, struct file *fp);
|
||||
static int ipath_diag_release(struct inode *in, struct file *fp);
|
||||
static ssize_t ipath_diag_read(struct file *fp, char __user *data,
|
||||
size_t count, loff_t *off);
|
||||
static ssize_t ipath_diag_write(struct file *fp, const char __user *data,
|
||||
size_t count, loff_t *off);
|
||||
|
||||
static const struct file_operations diag_file_ops = {
|
||||
.owner = THIS_MODULE,
|
||||
.write = ipath_diag_write,
|
||||
.read = ipath_diag_read,
|
||||
.open = ipath_diag_open,
|
||||
.release = ipath_diag_release,
|
||||
.llseek = default_llseek,
|
||||
};
|
||||
|
||||
static ssize_t ipath_diagpkt_write(struct file *fp,
|
||||
const char __user *data,
|
||||
size_t count, loff_t *off);
|
||||
|
||||
static const struct file_operations diagpkt_file_ops = {
|
||||
.owner = THIS_MODULE,
|
||||
.write = ipath_diagpkt_write,
|
||||
.llseek = noop_llseek,
|
||||
};
|
||||
|
||||
static atomic_t diagpkt_count = ATOMIC_INIT(0);
|
||||
static struct cdev *diagpkt_cdev;
|
||||
static struct device *diagpkt_dev;
|
||||
|
||||
int ipath_diag_add(struct ipath_devdata *dd)
|
||||
{
|
||||
char name[16];
|
||||
int ret = 0;
|
||||
|
||||
if (atomic_inc_return(&diagpkt_count) == 1) {
|
||||
ret = ipath_cdev_init(IPATH_DIAGPKT_MINOR,
|
||||
"ipath_diagpkt", &diagpkt_file_ops,
|
||||
&diagpkt_cdev, &diagpkt_dev);
|
||||
|
||||
if (ret) {
|
||||
ipath_dev_err(dd, "Couldn't create ipath_diagpkt "
|
||||
"device: %d", ret);
|
||||
goto done;
|
||||
}
|
||||
}
|
||||
|
||||
snprintf(name, sizeof(name), "ipath_diag%d", dd->ipath_unit);
|
||||
|
||||
ret = ipath_cdev_init(IPATH_DIAG_MINOR_BASE + dd->ipath_unit, name,
|
||||
&diag_file_ops, &dd->diag_cdev,
|
||||
&dd->diag_dev);
|
||||
if (ret)
|
||||
ipath_dev_err(dd, "Couldn't create %s device: %d",
|
||||
name, ret);
|
||||
|
||||
done:
|
||||
return ret;
|
||||
}
|
||||
|
||||
void ipath_diag_remove(struct ipath_devdata *dd)
|
||||
{
|
||||
if (atomic_dec_and_test(&diagpkt_count))
|
||||
ipath_cdev_cleanup(&diagpkt_cdev, &diagpkt_dev);
|
||||
|
||||
ipath_cdev_cleanup(&dd->diag_cdev, &dd->diag_dev);
|
||||
}
|
||||
|
||||
/**
|
||||
* ipath_read_umem64 - read a 64-bit quantity from the chip into user space
|
||||
* @dd: the infinipath device
|
||||
* @uaddr: the location to store the data in user memory
|
||||
* @caddr: the source chip address (full pointer, not offset)
|
||||
* @count: number of bytes to copy (multiple of 32 bits)
|
||||
*
|
||||
* This function also localizes all chip memory accesses.
|
||||
* The copy should be written such that we read full cacheline packets
|
||||
* from the chip. This is usually used for a single qword
|
||||
*
|
||||
* NOTE: This assumes the chip address is 64-bit aligned.
|
||||
*/
|
||||
static int ipath_read_umem64(struct ipath_devdata *dd, void __user *uaddr,
|
||||
const void __iomem *caddr, size_t count)
|
||||
{
|
||||
const u64 __iomem *reg_addr = caddr;
|
||||
const u64 __iomem *reg_end = reg_addr + (count / sizeof(u64));
|
||||
int ret;
|
||||
|
||||
/* not very efficient, but it works for now */
|
||||
if (reg_addr < dd->ipath_kregbase || reg_end > dd->ipath_kregend) {
|
||||
ret = -EINVAL;
|
||||
goto bail;
|
||||
}
|
||||
while (reg_addr < reg_end) {
|
||||
u64 data = readq(reg_addr);
|
||||
if (copy_to_user(uaddr, &data, sizeof(u64))) {
|
||||
ret = -EFAULT;
|
||||
goto bail;
|
||||
}
|
||||
reg_addr++;
|
||||
uaddr += sizeof(u64);
|
||||
}
|
||||
ret = 0;
|
||||
bail:
|
||||
return ret;
|
||||
}
|
||||
|
||||
/**
|
||||
* ipath_write_umem64 - write a 64-bit quantity to the chip from user space
|
||||
* @dd: the infinipath device
|
||||
* @caddr: the destination chip address (full pointer, not offset)
|
||||
* @uaddr: the source of the data in user memory
|
||||
* @count: the number of bytes to copy (multiple of 32 bits)
|
||||
*
|
||||
* This is usually used for a single qword
|
||||
* NOTE: This assumes the chip address is 64-bit aligned.
|
||||
*/
|
||||
|
||||
static int ipath_write_umem64(struct ipath_devdata *dd, void __iomem *caddr,
|
||||
const void __user *uaddr, size_t count)
|
||||
{
|
||||
u64 __iomem *reg_addr = caddr;
|
||||
const u64 __iomem *reg_end = reg_addr + (count / sizeof(u64));
|
||||
int ret;
|
||||
|
||||
/* not very efficient, but it works for now */
|
||||
if (reg_addr < dd->ipath_kregbase || reg_end > dd->ipath_kregend) {
|
||||
ret = -EINVAL;
|
||||
goto bail;
|
||||
}
|
||||
while (reg_addr < reg_end) {
|
||||
u64 data;
|
||||
if (copy_from_user(&data, uaddr, sizeof(data))) {
|
||||
ret = -EFAULT;
|
||||
goto bail;
|
||||
}
|
||||
writeq(data, reg_addr);
|
||||
|
||||
reg_addr++;
|
||||
uaddr += sizeof(u64);
|
||||
}
|
||||
ret = 0;
|
||||
bail:
|
||||
return ret;
|
||||
}
|
||||
|
||||
/**
|
||||
* ipath_read_umem32 - read a 32-bit quantity from the chip into user space
|
||||
* @dd: the infinipath device
|
||||
* @uaddr: the location to store the data in user memory
|
||||
* @caddr: the source chip address (full pointer, not offset)
|
||||
* @count: number of bytes to copy
|
||||
*
|
||||
* read 32 bit values, not 64 bit; for memories that only
|
||||
* support 32 bit reads; usually a single dword.
|
||||
*/
|
||||
static int ipath_read_umem32(struct ipath_devdata *dd, void __user *uaddr,
|
||||
const void __iomem *caddr, size_t count)
|
||||
{
|
||||
const u32 __iomem *reg_addr = caddr;
|
||||
const u32 __iomem *reg_end = reg_addr + (count / sizeof(u32));
|
||||
int ret;
|
||||
|
||||
if (reg_addr < (u32 __iomem *) dd->ipath_kregbase ||
|
||||
reg_end > (u32 __iomem *) dd->ipath_kregend) {
|
||||
ret = -EINVAL;
|
||||
goto bail;
|
||||
}
|
||||
/* not very efficient, but it works for now */
|
||||
while (reg_addr < reg_end) {
|
||||
u32 data = readl(reg_addr);
|
||||
if (copy_to_user(uaddr, &data, sizeof(data))) {
|
||||
ret = -EFAULT;
|
||||
goto bail;
|
||||
}
|
||||
|
||||
reg_addr++;
|
||||
uaddr += sizeof(u32);
|
||||
|
||||
}
|
||||
ret = 0;
|
||||
bail:
|
||||
return ret;
|
||||
}
|
||||
|
||||
/**
|
||||
* ipath_write_umem32 - write a 32-bit quantity to the chip from user space
|
||||
* @dd: the infinipath device
|
||||
* @caddr: the destination chip address (full pointer, not offset)
|
||||
* @uaddr: the source of the data in user memory
|
||||
* @count: number of bytes to copy
|
||||
*
|
||||
* write 32 bit values, not 64 bit; for memories that only
|
||||
* support 32 bit write; usually a single dword.
|
||||
*/
|
||||
|
||||
static int ipath_write_umem32(struct ipath_devdata *dd, void __iomem *caddr,
|
||||
const void __user *uaddr, size_t count)
|
||||
{
|
||||
u32 __iomem *reg_addr = caddr;
|
||||
const u32 __iomem *reg_end = reg_addr + (count / sizeof(u32));
|
||||
int ret;
|
||||
|
||||
if (reg_addr < (u32 __iomem *) dd->ipath_kregbase ||
|
||||
reg_end > (u32 __iomem *) dd->ipath_kregend) {
|
||||
ret = -EINVAL;
|
||||
goto bail;
|
||||
}
|
||||
while (reg_addr < reg_end) {
|
||||
u32 data;
|
||||
if (copy_from_user(&data, uaddr, sizeof(data))) {
|
||||
ret = -EFAULT;
|
||||
goto bail;
|
||||
}
|
||||
writel(data, reg_addr);
|
||||
|
||||
reg_addr++;
|
||||
uaddr += sizeof(u32);
|
||||
}
|
||||
ret = 0;
|
||||
bail:
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int ipath_diag_open(struct inode *in, struct file *fp)
|
||||
{
|
||||
int unit = iminor(in) - IPATH_DIAG_MINOR_BASE;
|
||||
struct ipath_devdata *dd;
|
||||
int ret;
|
||||
|
||||
mutex_lock(&ipath_mutex);
|
||||
|
||||
if (ipath_diag_inuse) {
|
||||
ret = -EBUSY;
|
||||
goto bail;
|
||||
}
|
||||
|
||||
dd = ipath_lookup(unit);
|
||||
|
||||
if (dd == NULL || !(dd->ipath_flags & IPATH_PRESENT) ||
|
||||
!dd->ipath_kregbase) {
|
||||
ret = -ENODEV;
|
||||
goto bail;
|
||||
}
|
||||
|
||||
fp->private_data = dd;
|
||||
ipath_diag_inuse = -2;
|
||||
diag_set_link = 0;
|
||||
ret = 0;
|
||||
|
||||
/* Only expose a way to reset the device if we
|
||||
make it into diag mode. */
|
||||
ipath_expose_reset(&dd->pcidev->dev);
|
||||
|
||||
bail:
|
||||
mutex_unlock(&ipath_mutex);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
/**
|
||||
* ipath_diagpkt_write - write an IB packet
|
||||
* @fp: the diag data device file pointer
|
||||
* @data: ipath_diag_pkt structure saying where to get the packet
|
||||
* @count: size of data to write
|
||||
* @off: unused by this code
|
||||
*/
|
||||
static ssize_t ipath_diagpkt_write(struct file *fp,
|
||||
const char __user *data,
|
||||
size_t count, loff_t *off)
|
||||
{
|
||||
u32 __iomem *piobuf;
|
||||
u32 plen, pbufn, maxlen_reserve;
|
||||
struct ipath_diag_pkt odp;
|
||||
struct ipath_diag_xpkt dp;
|
||||
u32 *tmpbuf = NULL;
|
||||
struct ipath_devdata *dd;
|
||||
ssize_t ret = 0;
|
||||
u64 val;
|
||||
u32 l_state, lt_state; /* LinkState, LinkTrainingState */
|
||||
|
||||
|
||||
if (count == sizeof(dp)) {
|
||||
if (copy_from_user(&dp, data, sizeof(dp))) {
|
||||
ret = -EFAULT;
|
||||
goto bail;
|
||||
}
|
||||
} else if (count == sizeof(odp)) {
|
||||
if (copy_from_user(&odp, data, sizeof(odp))) {
|
||||
ret = -EFAULT;
|
||||
goto bail;
|
||||
}
|
||||
dp.len = odp.len;
|
||||
dp.unit = odp.unit;
|
||||
dp.data = odp.data;
|
||||
dp.pbc_wd = 0;
|
||||
} else {
|
||||
ret = -EINVAL;
|
||||
goto bail;
|
||||
}
|
||||
|
||||
/* send count must be an exact number of dwords */
|
||||
if (dp.len & 3) {
|
||||
ret = -EINVAL;
|
||||
goto bail;
|
||||
}
|
||||
|
||||
plen = dp.len >> 2;
|
||||
|
||||
dd = ipath_lookup(dp.unit);
|
||||
if (!dd || !(dd->ipath_flags & IPATH_PRESENT) ||
|
||||
!dd->ipath_kregbase) {
|
||||
ipath_cdbg(VERBOSE, "illegal unit %u for diag data send\n",
|
||||
dp.unit);
|
||||
ret = -ENODEV;
|
||||
goto bail;
|
||||
}
|
||||
|
||||
if (ipath_diag_inuse && !diag_set_link &&
|
||||
!(dd->ipath_flags & IPATH_LINKACTIVE)) {
|
||||
diag_set_link = 1;
|
||||
ipath_cdbg(VERBOSE, "Trying to set to set link active for "
|
||||
"diag pkt\n");
|
||||
ipath_set_linkstate(dd, IPATH_IB_LINKARM);
|
||||
ipath_set_linkstate(dd, IPATH_IB_LINKACTIVE);
|
||||
}
|
||||
|
||||
if (!(dd->ipath_flags & IPATH_INITTED)) {
|
||||
/* no hardware, freeze, etc. */
|
||||
ipath_cdbg(VERBOSE, "unit %u not usable\n", dd->ipath_unit);
|
||||
ret = -ENODEV;
|
||||
goto bail;
|
||||
}
|
||||
/*
|
||||
* Want to skip check for l_state if using custom PBC,
|
||||
* because we might be trying to force an SM packet out.
|
||||
* first-cut, skip _all_ state checking in that case.
|
||||
*/
|
||||
val = ipath_ib_state(dd, dd->ipath_lastibcstat);
|
||||
lt_state = ipath_ib_linktrstate(dd, dd->ipath_lastibcstat);
|
||||
l_state = ipath_ib_linkstate(dd, dd->ipath_lastibcstat);
|
||||
if (!dp.pbc_wd && (lt_state != INFINIPATH_IBCS_LT_STATE_LINKUP ||
|
||||
(val != dd->ib_init && val != dd->ib_arm &&
|
||||
val != dd->ib_active))) {
|
||||
ipath_cdbg(VERBOSE, "unit %u not ready (state %llx)\n",
|
||||
dd->ipath_unit, (unsigned long long) val);
|
||||
ret = -EINVAL;
|
||||
goto bail;
|
||||
}
|
||||
|
||||
/*
|
||||
* need total length before first word written, plus 2 Dwords. One Dword
|
||||
* is for padding so we get the full user data when not aligned on
|
||||
* a word boundary. The other Dword is to make sure we have room for the
|
||||
* ICRC which gets tacked on later.
|
||||
*/
|
||||
maxlen_reserve = 2 * sizeof(u32);
|
||||
if (dp.len > dd->ipath_ibmaxlen - maxlen_reserve) {
|
||||
ipath_dbg("Pkt len 0x%x > ibmaxlen %x\n",
|
||||
dp.len, dd->ipath_ibmaxlen);
|
||||
ret = -EINVAL;
|
||||
goto bail;
|
||||
}
|
||||
|
||||
plen = sizeof(u32) + dp.len;
|
||||
|
||||
tmpbuf = vmalloc(plen);
|
||||
if (!tmpbuf) {
|
||||
dev_info(&dd->pcidev->dev, "Unable to allocate tmp buffer, "
|
||||
"failing\n");
|
||||
ret = -ENOMEM;
|
||||
goto bail;
|
||||
}
|
||||
|
||||
if (copy_from_user(tmpbuf,
|
||||
(const void __user *) (unsigned long) dp.data,
|
||||
dp.len)) {
|
||||
ret = -EFAULT;
|
||||
goto bail;
|
||||
}
|
||||
|
||||
plen >>= 2; /* in dwords */
|
||||
|
||||
piobuf = ipath_getpiobuf(dd, plen, &pbufn);
|
||||
if (!piobuf) {
|
||||
ipath_cdbg(VERBOSE, "No PIO buffers avail unit for %u\n",
|
||||
dd->ipath_unit);
|
||||
ret = -EBUSY;
|
||||
goto bail;
|
||||
}
|
||||
/* disarm it just to be extra sure */
|
||||
ipath_disarm_piobufs(dd, pbufn, 1);
|
||||
|
||||
if (ipath_debug & __IPATH_PKTDBG)
|
||||
ipath_cdbg(VERBOSE, "unit %u 0x%x+1w pio%d\n",
|
||||
dd->ipath_unit, plen - 1, pbufn);
|
||||
|
||||
if (dp.pbc_wd == 0)
|
||||
dp.pbc_wd = plen;
|
||||
writeq(dp.pbc_wd, piobuf);
|
||||
/*
|
||||
* Copy all by the trigger word, then flush, so it's written
|
||||
* to chip before trigger word, then write trigger word, then
|
||||
* flush again, so packet is sent.
|
||||
*/
|
||||
if (dd->ipath_flags & IPATH_PIO_FLUSH_WC) {
|
||||
ipath_flush_wc();
|
||||
__iowrite32_copy(piobuf + 2, tmpbuf, plen - 1);
|
||||
ipath_flush_wc();
|
||||
__raw_writel(tmpbuf[plen - 1], piobuf + plen + 1);
|
||||
} else
|
||||
__iowrite32_copy(piobuf + 2, tmpbuf, plen);
|
||||
|
||||
ipath_flush_wc();
|
||||
|
||||
ret = sizeof(dp);
|
||||
|
||||
bail:
|
||||
vfree(tmpbuf);
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int ipath_diag_release(struct inode *in, struct file *fp)
|
||||
{
|
||||
mutex_lock(&ipath_mutex);
|
||||
ipath_diag_inuse = 0;
|
||||
fp->private_data = NULL;
|
||||
mutex_unlock(&ipath_mutex);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static ssize_t ipath_diag_read(struct file *fp, char __user *data,
|
||||
size_t count, loff_t *off)
|
||||
{
|
||||
struct ipath_devdata *dd = fp->private_data;
|
||||
void __iomem *kreg_base;
|
||||
ssize_t ret;
|
||||
|
||||
kreg_base = dd->ipath_kregbase;
|
||||
|
||||
if (count == 0)
|
||||
ret = 0;
|
||||
else if ((count % 4) || (*off % 4))
|
||||
/* address or length is not 32-bit aligned, hence invalid */
|
||||
ret = -EINVAL;
|
||||
else if (ipath_diag_inuse < 1 && (*off || count != 8))
|
||||
ret = -EINVAL; /* prevent cat /dev/ipath_diag* */
|
||||
else if ((count % 8) || (*off % 8))
|
||||
/* address or length not 64-bit aligned; do 32-bit reads */
|
||||
ret = ipath_read_umem32(dd, data, kreg_base + *off, count);
|
||||
else
|
||||
ret = ipath_read_umem64(dd, data, kreg_base + *off, count);
|
||||
|
||||
if (ret >= 0) {
|
||||
*off += count;
|
||||
ret = count;
|
||||
if (ipath_diag_inuse == -2)
|
||||
ipath_diag_inuse++;
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static ssize_t ipath_diag_write(struct file *fp, const char __user *data,
|
||||
size_t count, loff_t *off)
|
||||
{
|
||||
struct ipath_devdata *dd = fp->private_data;
|
||||
void __iomem *kreg_base;
|
||||
ssize_t ret;
|
||||
|
||||
kreg_base = dd->ipath_kregbase;
|
||||
|
||||
if (count == 0)
|
||||
ret = 0;
|
||||
else if ((count % 4) || (*off % 4))
|
||||
/* address or length is not 32-bit aligned, hence invalid */
|
||||
ret = -EINVAL;
|
||||
else if ((ipath_diag_inuse == -1 && (*off || count != 8)) ||
|
||||
ipath_diag_inuse == -2) /* read qw off 0, write qw off 0 */
|
||||
ret = -EINVAL; /* before any other write allowed */
|
||||
else if ((count % 8) || (*off % 8))
|
||||
/* address or length not 64-bit aligned; do 32-bit writes */
|
||||
ret = ipath_write_umem32(dd, kreg_base + *off, data, count);
|
||||
else
|
||||
ret = ipath_write_umem64(dd, kreg_base + *off, data, count);
|
||||
|
||||
if (ret >= 0) {
|
||||
*off += count;
|
||||
ret = count;
|
||||
if (ipath_diag_inuse == -1)
|
||||
ipath_diag_inuse = 1; /* all read/write OK now */
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
|
@ -1,179 +0,0 @@
|
|||
/*
|
||||
* Copyright (c) 2006 QLogic, Corporation. 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.
|
||||
*/
|
||||
|
||||
#include <linux/scatterlist.h>
|
||||
#include <linux/gfp.h>
|
||||
#include <rdma/ib_verbs.h>
|
||||
|
||||
#include "ipath_verbs.h"
|
||||
|
||||
#define BAD_DMA_ADDRESS ((u64) 0)
|
||||
|
||||
/*
|
||||
* The following functions implement driver specific replacements
|
||||
* for the ib_dma_*() functions.
|
||||
*
|
||||
* These functions return kernel virtual addresses instead of
|
||||
* device bus addresses since the driver uses the CPU to copy
|
||||
* data instead of using hardware DMA.
|
||||
*/
|
||||
|
||||
static int ipath_mapping_error(struct ib_device *dev, u64 dma_addr)
|
||||
{
|
||||
return dma_addr == BAD_DMA_ADDRESS;
|
||||
}
|
||||
|
||||
static u64 ipath_dma_map_single(struct ib_device *dev,
|
||||
void *cpu_addr, size_t size,
|
||||
enum dma_data_direction direction)
|
||||
{
|
||||
BUG_ON(!valid_dma_direction(direction));
|
||||
return (u64) cpu_addr;
|
||||
}
|
||||
|
||||
static void ipath_dma_unmap_single(struct ib_device *dev,
|
||||
u64 addr, size_t size,
|
||||
enum dma_data_direction direction)
|
||||
{
|
||||
BUG_ON(!valid_dma_direction(direction));
|
||||
}
|
||||
|
||||
static u64 ipath_dma_map_page(struct ib_device *dev,
|
||||
struct page *page,
|
||||
unsigned long offset,
|
||||
size_t size,
|
||||
enum dma_data_direction direction)
|
||||
{
|
||||
u64 addr;
|
||||
|
||||
BUG_ON(!valid_dma_direction(direction));
|
||||
|
||||
if (offset + size > PAGE_SIZE) {
|
||||
addr = BAD_DMA_ADDRESS;
|
||||
goto done;
|
||||
}
|
||||
|
||||
addr = (u64) page_address(page);
|
||||
if (addr)
|
||||
addr += offset;
|
||||
/* TODO: handle highmem pages */
|
||||
|
||||
done:
|
||||
return addr;
|
||||
}
|
||||
|
||||
static void ipath_dma_unmap_page(struct ib_device *dev,
|
||||
u64 addr, size_t size,
|
||||
enum dma_data_direction direction)
|
||||
{
|
||||
BUG_ON(!valid_dma_direction(direction));
|
||||
}
|
||||
|
||||
static int ipath_map_sg(struct ib_device *dev, struct scatterlist *sgl,
|
||||
int nents, enum dma_data_direction direction)
|
||||
{
|
||||
struct scatterlist *sg;
|
||||
u64 addr;
|
||||
int i;
|
||||
int ret = nents;
|
||||
|
||||
BUG_ON(!valid_dma_direction(direction));
|
||||
|
||||
for_each_sg(sgl, sg, nents, i) {
|
||||
addr = (u64) page_address(sg_page(sg));
|
||||
/* TODO: handle highmem pages */
|
||||
if (!addr) {
|
||||
ret = 0;
|
||||
break;
|
||||
}
|
||||
sg->dma_address = addr + sg->offset;
|
||||
#ifdef CONFIG_NEED_SG_DMA_LENGTH
|
||||
sg->dma_length = sg->length;
|
||||
#endif
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
|
||||
static void ipath_unmap_sg(struct ib_device *dev,
|
||||
struct scatterlist *sg, int nents,
|
||||
enum dma_data_direction direction)
|
||||
{
|
||||
BUG_ON(!valid_dma_direction(direction));
|
||||
}
|
||||
|
||||
static void ipath_sync_single_for_cpu(struct ib_device *dev,
|
||||
u64 addr,
|
||||
size_t size,
|
||||
enum dma_data_direction dir)
|
||||
{
|
||||
}
|
||||
|
||||
static void ipath_sync_single_for_device(struct ib_device *dev,
|
||||
u64 addr,
|
||||
size_t size,
|
||||
enum dma_data_direction dir)
|
||||
{
|
||||
}
|
||||
|
||||
static void *ipath_dma_alloc_coherent(struct ib_device *dev, size_t size,
|
||||
u64 *dma_handle, gfp_t flag)
|
||||
{
|
||||
struct page *p;
|
||||
void *addr = NULL;
|
||||
|
||||
p = alloc_pages(flag, get_order(size));
|
||||
if (p)
|
||||
addr = page_address(p);
|
||||
if (dma_handle)
|
||||
*dma_handle = (u64) addr;
|
||||
return addr;
|
||||
}
|
||||
|
||||
static void ipath_dma_free_coherent(struct ib_device *dev, size_t size,
|
||||
void *cpu_addr, u64 dma_handle)
|
||||
{
|
||||
free_pages((unsigned long) cpu_addr, get_order(size));
|
||||
}
|
||||
|
||||
struct ib_dma_mapping_ops ipath_dma_mapping_ops = {
|
||||
.mapping_error = ipath_mapping_error,
|
||||
.map_single = ipath_dma_map_single,
|
||||
.unmap_single = ipath_dma_unmap_single,
|
||||
.map_page = ipath_dma_map_page,
|
||||
.unmap_page = ipath_dma_unmap_page,
|
||||
.map_sg = ipath_map_sg,
|
||||
.unmap_sg = ipath_unmap_sg,
|
||||
.sync_single_for_cpu = ipath_sync_single_for_cpu,
|
||||
.sync_single_for_device = ipath_sync_single_for_device,
|
||||
.alloc_coherent = ipath_dma_alloc_coherent,
|
||||
.free_coherent = ipath_dma_free_coherent
|
||||
};
|
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
|
@ -1,415 +0,0 @@
|
|||
/*
|
||||
* Copyright (c) 2006, 2007 QLogic Corporation. All rights reserved.
|
||||
* Copyright (c) 2006 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.
|
||||
*/
|
||||
|
||||
#include <linux/module.h>
|
||||
#include <linux/fs.h>
|
||||
#include <linux/mount.h>
|
||||
#include <linux/pagemap.h>
|
||||
#include <linux/init.h>
|
||||
#include <linux/namei.h>
|
||||
#include <linux/slab.h>
|
||||
|
||||
#include "ipath_kernel.h"
|
||||
|
||||
#define IPATHFS_MAGIC 0x726a77
|
||||
|
||||
static struct super_block *ipath_super;
|
||||
|
||||
static int ipathfs_mknod(struct inode *dir, struct dentry *dentry,
|
||||
umode_t mode, const struct file_operations *fops,
|
||||
void *data)
|
||||
{
|
||||
int error;
|
||||
struct inode *inode = new_inode(dir->i_sb);
|
||||
|
||||
if (!inode) {
|
||||
error = -EPERM;
|
||||
goto bail;
|
||||
}
|
||||
|
||||
inode->i_ino = get_next_ino();
|
||||
inode->i_mode = mode;
|
||||
inode->i_atime = inode->i_mtime = inode->i_ctime = CURRENT_TIME;
|
||||
inode->i_private = data;
|
||||
if (S_ISDIR(mode)) {
|
||||
inode->i_op = &simple_dir_inode_operations;
|
||||
inc_nlink(inode);
|
||||
inc_nlink(dir);
|
||||
}
|
||||
|
||||
inode->i_fop = fops;
|
||||
|
||||
d_instantiate(dentry, inode);
|
||||
error = 0;
|
||||
|
||||
bail:
|
||||
return error;
|
||||
}
|
||||
|
||||
static int create_file(const char *name, umode_t mode,
|
||||
struct dentry *parent, struct dentry **dentry,
|
||||
const struct file_operations *fops, void *data)
|
||||
{
|
||||
int error;
|
||||
|
||||
inode_lock(d_inode(parent));
|
||||
*dentry = lookup_one_len(name, parent, strlen(name));
|
||||
if (!IS_ERR(*dentry))
|
||||
error = ipathfs_mknod(d_inode(parent), *dentry,
|
||||
mode, fops, data);
|
||||
else
|
||||
error = PTR_ERR(*dentry);
|
||||
inode_unlock(d_inode(parent));
|
||||
|
||||
return error;
|
||||
}
|
||||
|
||||
static ssize_t atomic_stats_read(struct file *file, char __user *buf,
|
||||
size_t count, loff_t *ppos)
|
||||
{
|
||||
return simple_read_from_buffer(buf, count, ppos, &ipath_stats,
|
||||
sizeof ipath_stats);
|
||||
}
|
||||
|
||||
static const struct file_operations atomic_stats_ops = {
|
||||
.read = atomic_stats_read,
|
||||
.llseek = default_llseek,
|
||||
};
|
||||
|
||||
static ssize_t atomic_counters_read(struct file *file, char __user *buf,
|
||||
size_t count, loff_t *ppos)
|
||||
{
|
||||
struct infinipath_counters counters;
|
||||
struct ipath_devdata *dd;
|
||||
|
||||
dd = file_inode(file)->i_private;
|
||||
dd->ipath_f_read_counters(dd, &counters);
|
||||
|
||||
return simple_read_from_buffer(buf, count, ppos, &counters,
|
||||
sizeof counters);
|
||||
}
|
||||
|
||||
static const struct file_operations atomic_counters_ops = {
|
||||
.read = atomic_counters_read,
|
||||
.llseek = default_llseek,
|
||||
};
|
||||
|
||||
static ssize_t flash_read(struct file *file, char __user *buf,
|
||||
size_t count, loff_t *ppos)
|
||||
{
|
||||
struct ipath_devdata *dd;
|
||||
ssize_t ret;
|
||||
loff_t pos;
|
||||
char *tmp;
|
||||
|
||||
pos = *ppos;
|
||||
|
||||
if ( pos < 0) {
|
||||
ret = -EINVAL;
|
||||
goto bail;
|
||||
}
|
||||
|
||||
if (pos >= sizeof(struct ipath_flash)) {
|
||||
ret = 0;
|
||||
goto bail;
|
||||
}
|
||||
|
||||
if (count > sizeof(struct ipath_flash) - pos)
|
||||
count = sizeof(struct ipath_flash) - pos;
|
||||
|
||||
tmp = kmalloc(count, GFP_KERNEL);
|
||||
if (!tmp) {
|
||||
ret = -ENOMEM;
|
||||
goto bail;
|
||||
}
|
||||
|
||||
dd = file_inode(file)->i_private;
|
||||
if (ipath_eeprom_read(dd, pos, tmp, count)) {
|
||||
ipath_dev_err(dd, "failed to read from flash\n");
|
||||
ret = -ENXIO;
|
||||
goto bail_tmp;
|
||||
}
|
||||
|
||||
if (copy_to_user(buf, tmp, count)) {
|
||||
ret = -EFAULT;
|
||||
goto bail_tmp;
|
||||
}
|
||||
|
||||
*ppos = pos + count;
|
||||
ret = count;
|
||||
|
||||
bail_tmp:
|
||||
kfree(tmp);
|
||||
|
||||
bail:
|
||||
return ret;
|
||||
}
|
||||
|
||||
static ssize_t flash_write(struct file *file, const char __user *buf,
|
||||
size_t count, loff_t *ppos)
|
||||
{
|
||||
struct ipath_devdata *dd;
|
||||
ssize_t ret;
|
||||
loff_t pos;
|
||||
char *tmp;
|
||||
|
||||
pos = *ppos;
|
||||
|
||||
if (pos != 0) {
|
||||
ret = -EINVAL;
|
||||
goto bail;
|
||||
}
|
||||
|
||||
if (count != sizeof(struct ipath_flash)) {
|
||||
ret = -EINVAL;
|
||||
goto bail;
|
||||
}
|
||||
|
||||
tmp = memdup_user(buf, count);
|
||||
if (IS_ERR(tmp))
|
||||
return PTR_ERR(tmp);
|
||||
|
||||
dd = file_inode(file)->i_private;
|
||||
if (ipath_eeprom_write(dd, pos, tmp, count)) {
|
||||
ret = -ENXIO;
|
||||
ipath_dev_err(dd, "failed to write to flash\n");
|
||||
goto bail_tmp;
|
||||
}
|
||||
|
||||
*ppos = pos + count;
|
||||
ret = count;
|
||||
|
||||
bail_tmp:
|
||||
kfree(tmp);
|
||||
|
||||
bail:
|
||||
return ret;
|
||||
}
|
||||
|
||||
static const struct file_operations flash_ops = {
|
||||
.read = flash_read,
|
||||
.write = flash_write,
|
||||
.llseek = default_llseek,
|
||||
};
|
||||
|
||||
static int create_device_files(struct super_block *sb,
|
||||
struct ipath_devdata *dd)
|
||||
{
|
||||
struct dentry *dir, *tmp;
|
||||
char unit[10];
|
||||
int ret;
|
||||
|
||||
snprintf(unit, sizeof unit, "%02d", dd->ipath_unit);
|
||||
ret = create_file(unit, S_IFDIR|S_IRUGO|S_IXUGO, sb->s_root, &dir,
|
||||
&simple_dir_operations, dd);
|
||||
if (ret) {
|
||||
printk(KERN_ERR "create_file(%s) failed: %d\n", unit, ret);
|
||||
goto bail;
|
||||
}
|
||||
|
||||
ret = create_file("atomic_counters", S_IFREG|S_IRUGO, dir, &tmp,
|
||||
&atomic_counters_ops, dd);
|
||||
if (ret) {
|
||||
printk(KERN_ERR "create_file(%s/atomic_counters) "
|
||||
"failed: %d\n", unit, ret);
|
||||
goto bail;
|
||||
}
|
||||
|
||||
ret = create_file("flash", S_IFREG|S_IWUSR|S_IRUGO, dir, &tmp,
|
||||
&flash_ops, dd);
|
||||
if (ret) {
|
||||
printk(KERN_ERR "create_file(%s/flash) "
|
||||
"failed: %d\n", unit, ret);
|
||||
goto bail;
|
||||
}
|
||||
|
||||
bail:
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int remove_file(struct dentry *parent, char *name)
|
||||
{
|
||||
struct dentry *tmp;
|
||||
int ret;
|
||||
|
||||
tmp = lookup_one_len(name, parent, strlen(name));
|
||||
|
||||
if (IS_ERR(tmp)) {
|
||||
ret = PTR_ERR(tmp);
|
||||
goto bail;
|
||||
}
|
||||
|
||||
spin_lock(&tmp->d_lock);
|
||||
if (simple_positive(tmp)) {
|
||||
dget_dlock(tmp);
|
||||
__d_drop(tmp);
|
||||
spin_unlock(&tmp->d_lock);
|
||||
simple_unlink(d_inode(parent), tmp);
|
||||
} else
|
||||
spin_unlock(&tmp->d_lock);
|
||||
|
||||
ret = 0;
|
||||
bail:
|
||||
/*
|
||||
* We don't expect clients to care about the return value, but
|
||||
* it's there if they need it.
|
||||
*/
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int remove_device_files(struct super_block *sb,
|
||||
struct ipath_devdata *dd)
|
||||
{
|
||||
struct dentry *dir, *root;
|
||||
char unit[10];
|
||||
int ret;
|
||||
|
||||
root = dget(sb->s_root);
|
||||
inode_lock(d_inode(root));
|
||||
snprintf(unit, sizeof unit, "%02d", dd->ipath_unit);
|
||||
dir = lookup_one_len(unit, root, strlen(unit));
|
||||
|
||||
if (IS_ERR(dir)) {
|
||||
ret = PTR_ERR(dir);
|
||||
printk(KERN_ERR "Lookup of %s failed\n", unit);
|
||||
goto bail;
|
||||
}
|
||||
|
||||
remove_file(dir, "flash");
|
||||
remove_file(dir, "atomic_counters");
|
||||
d_delete(dir);
|
||||
ret = simple_rmdir(d_inode(root), dir);
|
||||
|
||||
bail:
|
||||
inode_unlock(d_inode(root));
|
||||
dput(root);
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int ipathfs_fill_super(struct super_block *sb, void *data,
|
||||
int silent)
|
||||
{
|
||||
struct ipath_devdata *dd, *tmp;
|
||||
unsigned long flags;
|
||||
int ret;
|
||||
|
||||
static struct tree_descr files[] = {
|
||||
[2] = {"atomic_stats", &atomic_stats_ops, S_IRUGO},
|
||||
{""},
|
||||
};
|
||||
|
||||
ret = simple_fill_super(sb, IPATHFS_MAGIC, files);
|
||||
if (ret) {
|
||||
printk(KERN_ERR "simple_fill_super failed: %d\n", ret);
|
||||
goto bail;
|
||||
}
|
||||
|
||||
spin_lock_irqsave(&ipath_devs_lock, flags);
|
||||
|
||||
list_for_each_entry_safe(dd, tmp, &ipath_dev_list, ipath_list) {
|
||||
spin_unlock_irqrestore(&ipath_devs_lock, flags);
|
||||
ret = create_device_files(sb, dd);
|
||||
if (ret)
|
||||
goto bail;
|
||||
spin_lock_irqsave(&ipath_devs_lock, flags);
|
||||
}
|
||||
|
||||
spin_unlock_irqrestore(&ipath_devs_lock, flags);
|
||||
|
||||
bail:
|
||||
return ret;
|
||||
}
|
||||
|
||||
static struct dentry *ipathfs_mount(struct file_system_type *fs_type,
|
||||
int flags, const char *dev_name, void *data)
|
||||
{
|
||||
struct dentry *ret;
|
||||
ret = mount_single(fs_type, flags, data, ipathfs_fill_super);
|
||||
if (!IS_ERR(ret))
|
||||
ipath_super = ret->d_sb;
|
||||
return ret;
|
||||
}
|
||||
|
||||
static void ipathfs_kill_super(struct super_block *s)
|
||||
{
|
||||
kill_litter_super(s);
|
||||
ipath_super = NULL;
|
||||
}
|
||||
|
||||
int ipathfs_add_device(struct ipath_devdata *dd)
|
||||
{
|
||||
int ret;
|
||||
|
||||
if (ipath_super == NULL) {
|
||||
ret = 0;
|
||||
goto bail;
|
||||
}
|
||||
|
||||
ret = create_device_files(ipath_super, dd);
|
||||
|
||||
bail:
|
||||
return ret;
|
||||
}
|
||||
|
||||
int ipathfs_remove_device(struct ipath_devdata *dd)
|
||||
{
|
||||
int ret;
|
||||
|
||||
if (ipath_super == NULL) {
|
||||
ret = 0;
|
||||
goto bail;
|
||||
}
|
||||
|
||||
ret = remove_device_files(ipath_super, dd);
|
||||
|
||||
bail:
|
||||
return ret;
|
||||
}
|
||||
|
||||
static struct file_system_type ipathfs_fs_type = {
|
||||
.owner = THIS_MODULE,
|
||||
.name = "ipathfs",
|
||||
.mount = ipathfs_mount,
|
||||
.kill_sb = ipathfs_kill_super,
|
||||
};
|
||||
MODULE_ALIAS_FS("ipathfs");
|
||||
|
||||
int __init ipath_init_ipathfs(void)
|
||||
{
|
||||
return register_filesystem(&ipathfs_fs_type);
|
||||
}
|
||||
|
||||
void __exit ipath_exit_ipathfs(void)
|
||||
{
|
||||
unregister_filesystem(&ipathfs_fs_type);
|
||||
}
|
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
|
@ -1,270 +0,0 @@
|
|||
/*
|
||||
* Copyright (c) 2006, 2007 QLogic Corporation. All rights reserved.
|
||||
* Copyright (c) 2005, 2006 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.
|
||||
*/
|
||||
|
||||
#include <asm/io.h>
|
||||
|
||||
#include "ipath_verbs.h"
|
||||
#include "ipath_kernel.h"
|
||||
|
||||
/**
|
||||
* ipath_alloc_lkey - allocate an lkey
|
||||
* @rkt: lkey table in which to allocate the lkey
|
||||
* @mr: memory region that this lkey protects
|
||||
*
|
||||
* Returns 1 if successful, otherwise returns 0.
|
||||
*/
|
||||
|
||||
int ipath_alloc_lkey(struct ipath_lkey_table *rkt, struct ipath_mregion *mr)
|
||||
{
|
||||
unsigned long flags;
|
||||
u32 r;
|
||||
u32 n;
|
||||
int ret;
|
||||
|
||||
spin_lock_irqsave(&rkt->lock, flags);
|
||||
|
||||
/* Find the next available LKEY */
|
||||
r = n = rkt->next;
|
||||
for (;;) {
|
||||
if (rkt->table[r] == NULL)
|
||||
break;
|
||||
r = (r + 1) & (rkt->max - 1);
|
||||
if (r == n) {
|
||||
spin_unlock_irqrestore(&rkt->lock, flags);
|
||||
ipath_dbg("LKEY table full\n");
|
||||
ret = 0;
|
||||
goto bail;
|
||||
}
|
||||
}
|
||||
rkt->next = (r + 1) & (rkt->max - 1);
|
||||
/*
|
||||
* Make sure lkey is never zero which is reserved to indicate an
|
||||
* unrestricted LKEY.
|
||||
*/
|
||||
rkt->gen++;
|
||||
mr->lkey = (r << (32 - ib_ipath_lkey_table_size)) |
|
||||
((((1 << (24 - ib_ipath_lkey_table_size)) - 1) & rkt->gen)
|
||||
<< 8);
|
||||
if (mr->lkey == 0) {
|
||||
mr->lkey |= 1 << 8;
|
||||
rkt->gen++;
|
||||
}
|
||||
rkt->table[r] = mr;
|
||||
spin_unlock_irqrestore(&rkt->lock, flags);
|
||||
|
||||
ret = 1;
|
||||
|
||||
bail:
|
||||
return ret;
|
||||
}
|
||||
|
||||
/**
|
||||
* ipath_free_lkey - free an lkey
|
||||
* @rkt: table from which to free the lkey
|
||||
* @lkey: lkey id to free
|
||||
*/
|
||||
void ipath_free_lkey(struct ipath_lkey_table *rkt, u32 lkey)
|
||||
{
|
||||
unsigned long flags;
|
||||
u32 r;
|
||||
|
||||
if (lkey == 0)
|
||||
return;
|
||||
r = lkey >> (32 - ib_ipath_lkey_table_size);
|
||||
spin_lock_irqsave(&rkt->lock, flags);
|
||||
rkt->table[r] = NULL;
|
||||
spin_unlock_irqrestore(&rkt->lock, flags);
|
||||
}
|
||||
|
||||
/**
|
||||
* ipath_lkey_ok - check IB SGE for validity and initialize
|
||||
* @rkt: table containing lkey to check SGE against
|
||||
* @isge: outgoing internal SGE
|
||||
* @sge: SGE to check
|
||||
* @acc: access flags
|
||||
*
|
||||
* Return 1 if valid and successful, otherwise returns 0.
|
||||
*
|
||||
* Check the IB SGE for validity and initialize our internal version
|
||||
* of it.
|
||||
*/
|
||||
int ipath_lkey_ok(struct ipath_qp *qp, struct ipath_sge *isge,
|
||||
struct ib_sge *sge, int acc)
|
||||
{
|
||||
struct ipath_lkey_table *rkt = &to_idev(qp->ibqp.device)->lk_table;
|
||||
struct ipath_mregion *mr;
|
||||
unsigned n, m;
|
||||
size_t off;
|
||||
int ret;
|
||||
|
||||
/*
|
||||
* We use LKEY == zero for kernel virtual addresses
|
||||
* (see ipath_get_dma_mr and ipath_dma.c).
|
||||
*/
|
||||
if (sge->lkey == 0) {
|
||||
/* always a kernel port, no locking needed */
|
||||
struct ipath_pd *pd = to_ipd(qp->ibqp.pd);
|
||||
|
||||
if (pd->user) {
|
||||
ret = 0;
|
||||
goto bail;
|
||||
}
|
||||
isge->mr = NULL;
|
||||
isge->vaddr = (void *) sge->addr;
|
||||
isge->length = sge->length;
|
||||
isge->sge_length = sge->length;
|
||||
ret = 1;
|
||||
goto bail;
|
||||
}
|
||||
mr = rkt->table[(sge->lkey >> (32 - ib_ipath_lkey_table_size))];
|
||||
if (unlikely(mr == NULL || mr->lkey != sge->lkey ||
|
||||
qp->ibqp.pd != mr->pd)) {
|
||||
ret = 0;
|
||||
goto bail;
|
||||
}
|
||||
|
||||
off = sge->addr - mr->user_base;
|
||||
if (unlikely(sge->addr < mr->user_base ||
|
||||
off + sge->length > mr->length ||
|
||||
(mr->access_flags & acc) != acc)) {
|
||||
ret = 0;
|
||||
goto bail;
|
||||
}
|
||||
|
||||
off += mr->offset;
|
||||
m = 0;
|
||||
n = 0;
|
||||
while (off >= mr->map[m]->segs[n].length) {
|
||||
off -= mr->map[m]->segs[n].length;
|
||||
n++;
|
||||
if (n >= IPATH_SEGSZ) {
|
||||
m++;
|
||||
n = 0;
|
||||
}
|
||||
}
|
||||
isge->mr = mr;
|
||||
isge->vaddr = mr->map[m]->segs[n].vaddr + off;
|
||||
isge->length = mr->map[m]->segs[n].length - off;
|
||||
isge->sge_length = sge->length;
|
||||
isge->m = m;
|
||||
isge->n = n;
|
||||
|
||||
ret = 1;
|
||||
|
||||
bail:
|
||||
return ret;
|
||||
}
|
||||
|
||||
/**
|
||||
* ipath_rkey_ok - check the IB virtual address, length, and RKEY
|
||||
* @dev: infiniband device
|
||||
* @ss: SGE state
|
||||
* @len: length of data
|
||||
* @vaddr: virtual address to place data
|
||||
* @rkey: rkey to check
|
||||
* @acc: access flags
|
||||
*
|
||||
* Return 1 if successful, otherwise 0.
|
||||
*/
|
||||
int ipath_rkey_ok(struct ipath_qp *qp, struct ipath_sge_state *ss,
|
||||
u32 len, u64 vaddr, u32 rkey, int acc)
|
||||
{
|
||||
struct ipath_ibdev *dev = to_idev(qp->ibqp.device);
|
||||
struct ipath_lkey_table *rkt = &dev->lk_table;
|
||||
struct ipath_sge *sge = &ss->sge;
|
||||
struct ipath_mregion *mr;
|
||||
unsigned n, m;
|
||||
size_t off;
|
||||
int ret;
|
||||
|
||||
/*
|
||||
* We use RKEY == zero for kernel virtual addresses
|
||||
* (see ipath_get_dma_mr and ipath_dma.c).
|
||||
*/
|
||||
if (rkey == 0) {
|
||||
/* always a kernel port, no locking needed */
|
||||
struct ipath_pd *pd = to_ipd(qp->ibqp.pd);
|
||||
|
||||
if (pd->user) {
|
||||
ret = 0;
|
||||
goto bail;
|
||||
}
|
||||
sge->mr = NULL;
|
||||
sge->vaddr = (void *) vaddr;
|
||||
sge->length = len;
|
||||
sge->sge_length = len;
|
||||
ss->sg_list = NULL;
|
||||
ss->num_sge = 1;
|
||||
ret = 1;
|
||||
goto bail;
|
||||
}
|
||||
|
||||
mr = rkt->table[(rkey >> (32 - ib_ipath_lkey_table_size))];
|
||||
if (unlikely(mr == NULL || mr->lkey != rkey ||
|
||||
qp->ibqp.pd != mr->pd)) {
|
||||
ret = 0;
|
||||
goto bail;
|
||||
}
|
||||
|
||||
off = vaddr - mr->iova;
|
||||
if (unlikely(vaddr < mr->iova || off + len > mr->length ||
|
||||
(mr->access_flags & acc) == 0)) {
|
||||
ret = 0;
|
||||
goto bail;
|
||||
}
|
||||
|
||||
off += mr->offset;
|
||||
m = 0;
|
||||
n = 0;
|
||||
while (off >= mr->map[m]->segs[n].length) {
|
||||
off -= mr->map[m]->segs[n].length;
|
||||
n++;
|
||||
if (n >= IPATH_SEGSZ) {
|
||||
m++;
|
||||
n = 0;
|
||||
}
|
||||
}
|
||||
sge->mr = mr;
|
||||
sge->vaddr = mr->map[m]->segs[n].vaddr + off;
|
||||
sge->length = mr->map[m]->segs[n].length - off;
|
||||
sge->sge_length = len;
|
||||
sge->m = m;
|
||||
sge->n = n;
|
||||
ss->sg_list = NULL;
|
||||
ss->num_sge = 1;
|
||||
|
||||
ret = 1;
|
||||
|
||||
bail:
|
||||
return ret;
|
||||
}
|
File diff suppressed because it is too large
Load Diff
|
@ -1,174 +0,0 @@
|
|||
/*
|
||||
* Copyright (c) 2006, 2007 QLogic Corporation. 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.
|
||||
*/
|
||||
|
||||
#include <linux/module.h>
|
||||
#include <linux/vmalloc.h>
|
||||
#include <linux/slab.h>
|
||||
#include <linux/mm.h>
|
||||
#include <linux/errno.h>
|
||||
#include <asm/pgtable.h>
|
||||
|
||||
#include "ipath_verbs.h"
|
||||
|
||||
/**
|
||||
* ipath_release_mmap_info - free mmap info structure
|
||||
* @ref: a pointer to the kref within struct ipath_mmap_info
|
||||
*/
|
||||
void ipath_release_mmap_info(struct kref *ref)
|
||||
{
|
||||
struct ipath_mmap_info *ip =
|
||||
container_of(ref, struct ipath_mmap_info, ref);
|
||||
struct ipath_ibdev *dev = to_idev(ip->context->device);
|
||||
|
||||
spin_lock_irq(&dev->pending_lock);
|
||||
list_del(&ip->pending_mmaps);
|
||||
spin_unlock_irq(&dev->pending_lock);
|
||||
|
||||
vfree(ip->obj);
|
||||
kfree(ip);
|
||||
}
|
||||
|
||||
/*
|
||||
* open and close keep track of how many times the CQ is mapped,
|
||||
* to avoid releasing it.
|
||||
*/
|
||||
static void ipath_vma_open(struct vm_area_struct *vma)
|
||||
{
|
||||
struct ipath_mmap_info *ip = vma->vm_private_data;
|
||||
|
||||
kref_get(&ip->ref);
|
||||
}
|
||||
|
||||
static void ipath_vma_close(struct vm_area_struct *vma)
|
||||
{
|
||||
struct ipath_mmap_info *ip = vma->vm_private_data;
|
||||
|
||||
kref_put(&ip->ref, ipath_release_mmap_info);
|
||||
}
|
||||
|
||||
static const struct vm_operations_struct ipath_vm_ops = {
|
||||
.open = ipath_vma_open,
|
||||
.close = ipath_vma_close,
|
||||
};
|
||||
|
||||
/**
|
||||
* ipath_mmap - create a new mmap region
|
||||
* @context: the IB user context of the process making the mmap() call
|
||||
* @vma: the VMA to be initialized
|
||||
* Return zero if the mmap is OK. Otherwise, return an errno.
|
||||
*/
|
||||
int ipath_mmap(struct ib_ucontext *context, struct vm_area_struct *vma)
|
||||
{
|
||||
struct ipath_ibdev *dev = to_idev(context->device);
|
||||
unsigned long offset = vma->vm_pgoff << PAGE_SHIFT;
|
||||
unsigned long size = vma->vm_end - vma->vm_start;
|
||||
struct ipath_mmap_info *ip, *pp;
|
||||
int ret = -EINVAL;
|
||||
|
||||
/*
|
||||
* Search the device's list of objects waiting for a mmap call.
|
||||
* Normally, this list is very short since a call to create a
|
||||
* CQ, QP, or SRQ is soon followed by a call to mmap().
|
||||
*/
|
||||
spin_lock_irq(&dev->pending_lock);
|
||||
list_for_each_entry_safe(ip, pp, &dev->pending_mmaps,
|
||||
pending_mmaps) {
|
||||
/* Only the creator is allowed to mmap the object */
|
||||
if (context != ip->context || (__u64) offset != ip->offset)
|
||||
continue;
|
||||
/* Don't allow a mmap larger than the object. */
|
||||
if (size > ip->size)
|
||||
break;
|
||||
|
||||
list_del_init(&ip->pending_mmaps);
|
||||
spin_unlock_irq(&dev->pending_lock);
|
||||
|
||||
ret = remap_vmalloc_range(vma, ip->obj, 0);
|
||||
if (ret)
|
||||
goto done;
|
||||
vma->vm_ops = &ipath_vm_ops;
|
||||
vma->vm_private_data = ip;
|
||||
ipath_vma_open(vma);
|
||||
goto done;
|
||||
}
|
||||
spin_unlock_irq(&dev->pending_lock);
|
||||
done:
|
||||
return ret;
|
||||
}
|
||||
|
||||
/*
|
||||
* Allocate information for ipath_mmap
|
||||
*/
|
||||
struct ipath_mmap_info *ipath_create_mmap_info(struct ipath_ibdev *dev,
|
||||
u32 size,
|
||||
struct ib_ucontext *context,
|
||||
void *obj) {
|
||||
struct ipath_mmap_info *ip;
|
||||
|
||||
ip = kmalloc(sizeof *ip, GFP_KERNEL);
|
||||
if (!ip)
|
||||
goto bail;
|
||||
|
||||
size = PAGE_ALIGN(size);
|
||||
|
||||
spin_lock_irq(&dev->mmap_offset_lock);
|
||||
if (dev->mmap_offset == 0)
|
||||
dev->mmap_offset = PAGE_SIZE;
|
||||
ip->offset = dev->mmap_offset;
|
||||
dev->mmap_offset += size;
|
||||
spin_unlock_irq(&dev->mmap_offset_lock);
|
||||
|
||||
INIT_LIST_HEAD(&ip->pending_mmaps);
|
||||
ip->size = size;
|
||||
ip->context = context;
|
||||
ip->obj = obj;
|
||||
kref_init(&ip->ref);
|
||||
|
||||
bail:
|
||||
return ip;
|
||||
}
|
||||
|
||||
void ipath_update_mmap_info(struct ipath_ibdev *dev,
|
||||
struct ipath_mmap_info *ip,
|
||||
u32 size, void *obj) {
|
||||
size = PAGE_ALIGN(size);
|
||||
|
||||
spin_lock_irq(&dev->mmap_offset_lock);
|
||||
if (dev->mmap_offset == 0)
|
||||
dev->mmap_offset = PAGE_SIZE;
|
||||
ip->offset = dev->mmap_offset;
|
||||
dev->mmap_offset += size;
|
||||
spin_unlock_irq(&dev->mmap_offset_lock);
|
||||
|
||||
ip->size = size;
|
||||
ip->obj = obj;
|
||||
}
|
|
@ -1,370 +0,0 @@
|
|||
/*
|
||||
* Copyright (c) 2006, 2007 QLogic Corporation. All rights reserved.
|
||||
* Copyright (c) 2005, 2006 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.
|
||||
*/
|
||||
|
||||
#include <linux/slab.h>
|
||||
|
||||
#include <rdma/ib_umem.h>
|
||||
#include <rdma/ib_pack.h>
|
||||
#include <rdma/ib_smi.h>
|
||||
|
||||
#include "ipath_verbs.h"
|
||||
|
||||
/* Fast memory region */
|
||||
struct ipath_fmr {
|
||||
struct ib_fmr ibfmr;
|
||||
u8 page_shift;
|
||||
struct ipath_mregion mr; /* must be last */
|
||||
};
|
||||
|
||||
static inline struct ipath_fmr *to_ifmr(struct ib_fmr *ibfmr)
|
||||
{
|
||||
return container_of(ibfmr, struct ipath_fmr, ibfmr);
|
||||
}
|
||||
|
||||
/**
|
||||
* ipath_get_dma_mr - get a DMA memory region
|
||||
* @pd: protection domain for this memory region
|
||||
* @acc: access flags
|
||||
*
|
||||
* Returns the memory region on success, otherwise returns an errno.
|
||||
* Note that all DMA addresses should be created via the
|
||||
* struct ib_dma_mapping_ops functions (see ipath_dma.c).
|
||||
*/
|
||||
struct ib_mr *ipath_get_dma_mr(struct ib_pd *pd, int acc)
|
||||
{
|
||||
struct ipath_mr *mr;
|
||||
struct ib_mr *ret;
|
||||
|
||||
mr = kzalloc(sizeof *mr, GFP_KERNEL);
|
||||
if (!mr) {
|
||||
ret = ERR_PTR(-ENOMEM);
|
||||
goto bail;
|
||||
}
|
||||
|
||||
mr->mr.access_flags = acc;
|
||||
ret = &mr->ibmr;
|
||||
|
||||
bail:
|
||||
return ret;
|
||||
}
|
||||
|
||||
static struct ipath_mr *alloc_mr(int count,
|
||||
struct ipath_lkey_table *lk_table)
|
||||
{
|
||||
struct ipath_mr *mr;
|
||||
int m, i = 0;
|
||||
|
||||
/* Allocate struct plus pointers to first level page tables. */
|
||||
m = (count + IPATH_SEGSZ - 1) / IPATH_SEGSZ;
|
||||
mr = kmalloc(sizeof *mr + m * sizeof mr->mr.map[0], GFP_KERNEL);
|
||||
if (!mr)
|
||||
goto done;
|
||||
|
||||
/* Allocate first level page tables. */
|
||||
for (; i < m; i++) {
|
||||
mr->mr.map[i] = kmalloc(sizeof *mr->mr.map[0], GFP_KERNEL);
|
||||
if (!mr->mr.map[i])
|
||||
goto bail;
|
||||
}
|
||||
mr->mr.mapsz = m;
|
||||
|
||||
if (!ipath_alloc_lkey(lk_table, &mr->mr))
|
||||
goto bail;
|
||||
mr->ibmr.rkey = mr->ibmr.lkey = mr->mr.lkey;
|
||||
|
||||
goto done;
|
||||
|
||||
bail:
|
||||
while (i) {
|
||||
i--;
|
||||
kfree(mr->mr.map[i]);
|
||||
}
|
||||
kfree(mr);
|
||||
mr = NULL;
|
||||
|
||||
done:
|
||||
return mr;
|
||||
}
|
||||
|
||||
/**
|
||||
* ipath_reg_user_mr - register a userspace memory region
|
||||
* @pd: protection domain for this memory region
|
||||
* @start: starting userspace address
|
||||
* @length: length of region to register
|
||||
* @virt_addr: virtual address to use (from HCA's point of view)
|
||||
* @mr_access_flags: access flags for this memory region
|
||||
* @udata: unused by the InfiniPath driver
|
||||
*
|
||||
* Returns the memory region on success, otherwise returns an errno.
|
||||
*/
|
||||
struct ib_mr *ipath_reg_user_mr(struct ib_pd *pd, u64 start, u64 length,
|
||||
u64 virt_addr, int mr_access_flags,
|
||||
struct ib_udata *udata)
|
||||
{
|
||||
struct ipath_mr *mr;
|
||||
struct ib_umem *umem;
|
||||
int n, m, entry;
|
||||
struct scatterlist *sg;
|
||||
struct ib_mr *ret;
|
||||
|
||||
if (length == 0) {
|
||||
ret = ERR_PTR(-EINVAL);
|
||||
goto bail;
|
||||
}
|
||||
|
||||
umem = ib_umem_get(pd->uobject->context, start, length,
|
||||
mr_access_flags, 0);
|
||||
if (IS_ERR(umem))
|
||||
return (void *) umem;
|
||||
|
||||
n = umem->nmap;
|
||||
mr = alloc_mr(n, &to_idev(pd->device)->lk_table);
|
||||
if (!mr) {
|
||||
ret = ERR_PTR(-ENOMEM);
|
||||
ib_umem_release(umem);
|
||||
goto bail;
|
||||
}
|
||||
|
||||
mr->mr.pd = pd;
|
||||
mr->mr.user_base = start;
|
||||
mr->mr.iova = virt_addr;
|
||||
mr->mr.length = length;
|
||||
mr->mr.offset = ib_umem_offset(umem);
|
||||
mr->mr.access_flags = mr_access_flags;
|
||||
mr->mr.max_segs = n;
|
||||
mr->umem = umem;
|
||||
|
||||
m = 0;
|
||||
n = 0;
|
||||
for_each_sg(umem->sg_head.sgl, sg, umem->nmap, entry) {
|
||||
void *vaddr;
|
||||
|
||||
vaddr = page_address(sg_page(sg));
|
||||
if (!vaddr) {
|
||||
ret = ERR_PTR(-EINVAL);
|
||||
goto bail;
|
||||
}
|
||||
mr->mr.map[m]->segs[n].vaddr = vaddr;
|
||||
mr->mr.map[m]->segs[n].length = umem->page_size;
|
||||
n++;
|
||||
if (n == IPATH_SEGSZ) {
|
||||
m++;
|
||||
n = 0;
|
||||
}
|
||||
}
|
||||
ret = &mr->ibmr;
|
||||
|
||||
bail:
|
||||
return ret;
|
||||
}
|
||||
|
||||
/**
|
||||
* ipath_dereg_mr - unregister and free a memory region
|
||||
* @ibmr: the memory region to free
|
||||
*
|
||||
* Returns 0 on success.
|
||||
*
|
||||
* Note that this is called to free MRs created by ipath_get_dma_mr()
|
||||
* or ipath_reg_user_mr().
|
||||
*/
|
||||
int ipath_dereg_mr(struct ib_mr *ibmr)
|
||||
{
|
||||
struct ipath_mr *mr = to_imr(ibmr);
|
||||
int i;
|
||||
|
||||
ipath_free_lkey(&to_idev(ibmr->device)->lk_table, ibmr->lkey);
|
||||
i = mr->mr.mapsz;
|
||||
while (i) {
|
||||
i--;
|
||||
kfree(mr->mr.map[i]);
|
||||
}
|
||||
|
||||
if (mr->umem)
|
||||
ib_umem_release(mr->umem);
|
||||
|
||||
kfree(mr);
|
||||
return 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* ipath_alloc_fmr - allocate a fast memory region
|
||||
* @pd: the protection domain for this memory region
|
||||
* @mr_access_flags: access flags for this memory region
|
||||
* @fmr_attr: fast memory region attributes
|
||||
*
|
||||
* Returns the memory region on success, otherwise returns an errno.
|
||||
*/
|
||||
struct ib_fmr *ipath_alloc_fmr(struct ib_pd *pd, int mr_access_flags,
|
||||
struct ib_fmr_attr *fmr_attr)
|
||||
{
|
||||
struct ipath_fmr *fmr;
|
||||
int m, i = 0;
|
||||
struct ib_fmr *ret;
|
||||
|
||||
/* Allocate struct plus pointers to first level page tables. */
|
||||
m = (fmr_attr->max_pages + IPATH_SEGSZ - 1) / IPATH_SEGSZ;
|
||||
fmr = kmalloc(sizeof *fmr + m * sizeof fmr->mr.map[0], GFP_KERNEL);
|
||||
if (!fmr)
|
||||
goto bail;
|
||||
|
||||
/* Allocate first level page tables. */
|
||||
for (; i < m; i++) {
|
||||
fmr->mr.map[i] = kmalloc(sizeof *fmr->mr.map[0],
|
||||
GFP_KERNEL);
|
||||
if (!fmr->mr.map[i])
|
||||
goto bail;
|
||||
}
|
||||
fmr->mr.mapsz = m;
|
||||
|
||||
/*
|
||||
* ib_alloc_fmr() will initialize fmr->ibfmr except for lkey &
|
||||
* rkey.
|
||||
*/
|
||||
if (!ipath_alloc_lkey(&to_idev(pd->device)->lk_table, &fmr->mr))
|
||||
goto bail;
|
||||
fmr->ibfmr.rkey = fmr->ibfmr.lkey = fmr->mr.lkey;
|
||||
/*
|
||||
* Resources are allocated but no valid mapping (RKEY can't be
|
||||
* used).
|
||||
*/
|
||||
fmr->mr.pd = pd;
|
||||
fmr->mr.user_base = 0;
|
||||
fmr->mr.iova = 0;
|
||||
fmr->mr.length = 0;
|
||||
fmr->mr.offset = 0;
|
||||
fmr->mr.access_flags = mr_access_flags;
|
||||
fmr->mr.max_segs = fmr_attr->max_pages;
|
||||
fmr->page_shift = fmr_attr->page_shift;
|
||||
|
||||
ret = &fmr->ibfmr;
|
||||
goto done;
|
||||
|
||||
bail:
|
||||
while (i)
|
||||
kfree(fmr->mr.map[--i]);
|
||||
kfree(fmr);
|
||||
ret = ERR_PTR(-ENOMEM);
|
||||
|
||||
done:
|
||||
return ret;
|
||||
}
|
||||
|
||||
/**
|
||||
* ipath_map_phys_fmr - set up a fast memory region
|
||||
* @ibmfr: the fast memory region to set up
|
||||
* @page_list: the list of pages to associate with the fast memory region
|
||||
* @list_len: the number of pages to associate with the fast memory region
|
||||
* @iova: the virtual address of the start of the fast memory region
|
||||
*
|
||||
* This may be called from interrupt context.
|
||||
*/
|
||||
|
||||
int ipath_map_phys_fmr(struct ib_fmr *ibfmr, u64 * page_list,
|
||||
int list_len, u64 iova)
|
||||
{
|
||||
struct ipath_fmr *fmr = to_ifmr(ibfmr);
|
||||
struct ipath_lkey_table *rkt;
|
||||
unsigned long flags;
|
||||
int m, n, i;
|
||||
u32 ps;
|
||||
int ret;
|
||||
|
||||
if (list_len > fmr->mr.max_segs) {
|
||||
ret = -EINVAL;
|
||||
goto bail;
|
||||
}
|
||||
rkt = &to_idev(ibfmr->device)->lk_table;
|
||||
spin_lock_irqsave(&rkt->lock, flags);
|
||||
fmr->mr.user_base = iova;
|
||||
fmr->mr.iova = iova;
|
||||
ps = 1 << fmr->page_shift;
|
||||
fmr->mr.length = list_len * ps;
|
||||
m = 0;
|
||||
n = 0;
|
||||
ps = 1 << fmr->page_shift;
|
||||
for (i = 0; i < list_len; i++) {
|
||||
fmr->mr.map[m]->segs[n].vaddr = (void *) page_list[i];
|
||||
fmr->mr.map[m]->segs[n].length = ps;
|
||||
if (++n == IPATH_SEGSZ) {
|
||||
m++;
|
||||
n = 0;
|
||||
}
|
||||
}
|
||||
spin_unlock_irqrestore(&rkt->lock, flags);
|
||||
ret = 0;
|
||||
|
||||
bail:
|
||||
return ret;
|
||||
}
|
||||
|
||||
/**
|
||||
* ipath_unmap_fmr - unmap fast memory regions
|
||||
* @fmr_list: the list of fast memory regions to unmap
|
||||
*
|
||||
* Returns 0 on success.
|
||||
*/
|
||||
int ipath_unmap_fmr(struct list_head *fmr_list)
|
||||
{
|
||||
struct ipath_fmr *fmr;
|
||||
struct ipath_lkey_table *rkt;
|
||||
unsigned long flags;
|
||||
|
||||
list_for_each_entry(fmr, fmr_list, ibfmr.list) {
|
||||
rkt = &to_idev(fmr->ibfmr.device)->lk_table;
|
||||
spin_lock_irqsave(&rkt->lock, flags);
|
||||
fmr->mr.user_base = 0;
|
||||
fmr->mr.iova = 0;
|
||||
fmr->mr.length = 0;
|
||||
spin_unlock_irqrestore(&rkt->lock, flags);
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* ipath_dealloc_fmr - deallocate a fast memory region
|
||||
* @ibfmr: the fast memory region to deallocate
|
||||
*
|
||||
* Returns 0 on success.
|
||||
*/
|
||||
int ipath_dealloc_fmr(struct ib_fmr *ibfmr)
|
||||
{
|
||||
struct ipath_fmr *fmr = to_ifmr(ibfmr);
|
||||
int i;
|
||||
|
||||
ipath_free_lkey(&to_idev(ibfmr->device)->lk_table, ibfmr->lkey);
|
||||
i = fmr->mr.mapsz;
|
||||
while (i)
|
||||
kfree(fmr->mr.map[--i]);
|
||||
kfree(fmr);
|
||||
return 0;
|
||||
}
|
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
|
@ -1,512 +0,0 @@
|
|||
/*
|
||||
* Copyright (c) 2006, 2007 QLogic Corporation. All rights reserved.
|
||||
* Copyright (c) 2003, 2004, 2005, 2006 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.
|
||||
*/
|
||||
|
||||
#ifndef _IPATH_REGISTERS_H
|
||||
#define _IPATH_REGISTERS_H
|
||||
|
||||
/*
|
||||
* This file should only be included by kernel source, and by the diags. It
|
||||
* defines the registers, and their contents, for InfiniPath chips.
|
||||
*/
|
||||
|
||||
/*
|
||||
* These are the InfiniPath register and buffer bit definitions,
|
||||
* that are visible to software, and needed only by the kernel
|
||||
* and diag code. A few, that are visible to protocol and user
|
||||
* code are in ipath_common.h. Some bits are specific
|
||||
* to a given chip implementation, and have been moved to the
|
||||
* chip-specific source file
|
||||
*/
|
||||
|
||||
/* kr_revision bits */
|
||||
#define INFINIPATH_R_CHIPREVMINOR_MASK 0xFF
|
||||
#define INFINIPATH_R_CHIPREVMINOR_SHIFT 0
|
||||
#define INFINIPATH_R_CHIPREVMAJOR_MASK 0xFF
|
||||
#define INFINIPATH_R_CHIPREVMAJOR_SHIFT 8
|
||||
#define INFINIPATH_R_ARCH_MASK 0xFF
|
||||
#define INFINIPATH_R_ARCH_SHIFT 16
|
||||
#define INFINIPATH_R_SOFTWARE_MASK 0xFF
|
||||
#define INFINIPATH_R_SOFTWARE_SHIFT 24
|
||||
#define INFINIPATH_R_BOARDID_MASK 0xFF
|
||||
#define INFINIPATH_R_BOARDID_SHIFT 32
|
||||
|
||||
/* kr_control bits */
|
||||
#define INFINIPATH_C_FREEZEMODE 0x00000002
|
||||
#define INFINIPATH_C_LINKENABLE 0x00000004
|
||||
|
||||
/* kr_sendctrl bits */
|
||||
#define INFINIPATH_S_DISARMPIOBUF_SHIFT 16
|
||||
#define INFINIPATH_S_UPDTHRESH_SHIFT 24
|
||||
#define INFINIPATH_S_UPDTHRESH_MASK 0x1f
|
||||
|
||||
#define IPATH_S_ABORT 0
|
||||
#define IPATH_S_PIOINTBUFAVAIL 1
|
||||
#define IPATH_S_PIOBUFAVAILUPD 2
|
||||
#define IPATH_S_PIOENABLE 3
|
||||
#define IPATH_S_SDMAINTENABLE 9
|
||||
#define IPATH_S_SDMASINGLEDESCRIPTOR 10
|
||||
#define IPATH_S_SDMAENABLE 11
|
||||
#define IPATH_S_SDMAHALT 12
|
||||
#define IPATH_S_DISARM 31
|
||||
|
||||
#define INFINIPATH_S_ABORT (1U << IPATH_S_ABORT)
|
||||
#define INFINIPATH_S_PIOINTBUFAVAIL (1U << IPATH_S_PIOINTBUFAVAIL)
|
||||
#define INFINIPATH_S_PIOBUFAVAILUPD (1U << IPATH_S_PIOBUFAVAILUPD)
|
||||
#define INFINIPATH_S_PIOENABLE (1U << IPATH_S_PIOENABLE)
|
||||
#define INFINIPATH_S_SDMAINTENABLE (1U << IPATH_S_SDMAINTENABLE)
|
||||
#define INFINIPATH_S_SDMASINGLEDESCRIPTOR \
|
||||
(1U << IPATH_S_SDMASINGLEDESCRIPTOR)
|
||||
#define INFINIPATH_S_SDMAENABLE (1U << IPATH_S_SDMAENABLE)
|
||||
#define INFINIPATH_S_SDMAHALT (1U << IPATH_S_SDMAHALT)
|
||||
#define INFINIPATH_S_DISARM (1U << IPATH_S_DISARM)
|
||||
|
||||
/* kr_rcvctrl bits that are the same on multiple chips */
|
||||
#define INFINIPATH_R_PORTENABLE_SHIFT 0
|
||||
#define INFINIPATH_R_QPMAP_ENABLE (1ULL << 38)
|
||||
|
||||
/* kr_intstatus, kr_intclear, kr_intmask bits */
|
||||
#define INFINIPATH_I_SDMAINT 0x8000000000000000ULL
|
||||
#define INFINIPATH_I_SDMADISABLED 0x4000000000000000ULL
|
||||
#define INFINIPATH_I_ERROR 0x0000000080000000ULL
|
||||
#define INFINIPATH_I_SPIOSENT 0x0000000040000000ULL
|
||||
#define INFINIPATH_I_SPIOBUFAVAIL 0x0000000020000000ULL
|
||||
#define INFINIPATH_I_GPIO 0x0000000010000000ULL
|
||||
#define INFINIPATH_I_JINT 0x0000000004000000ULL
|
||||
|
||||
/* kr_errorstatus, kr_errorclear, kr_errormask bits */
|
||||
#define INFINIPATH_E_RFORMATERR 0x0000000000000001ULL
|
||||
#define INFINIPATH_E_RVCRC 0x0000000000000002ULL
|
||||
#define INFINIPATH_E_RICRC 0x0000000000000004ULL
|
||||
#define INFINIPATH_E_RMINPKTLEN 0x0000000000000008ULL
|
||||
#define INFINIPATH_E_RMAXPKTLEN 0x0000000000000010ULL
|
||||
#define INFINIPATH_E_RLONGPKTLEN 0x0000000000000020ULL
|
||||
#define INFINIPATH_E_RSHORTPKTLEN 0x0000000000000040ULL
|
||||
#define INFINIPATH_E_RUNEXPCHAR 0x0000000000000080ULL
|
||||
#define INFINIPATH_E_RUNSUPVL 0x0000000000000100ULL
|
||||
#define INFINIPATH_E_REBP 0x0000000000000200ULL
|
||||
#define INFINIPATH_E_RIBFLOW 0x0000000000000400ULL
|
||||
#define INFINIPATH_E_RBADVERSION 0x0000000000000800ULL
|
||||
#define INFINIPATH_E_RRCVEGRFULL 0x0000000000001000ULL
|
||||
#define INFINIPATH_E_RRCVHDRFULL 0x0000000000002000ULL
|
||||
#define INFINIPATH_E_RBADTID 0x0000000000004000ULL
|
||||
#define INFINIPATH_E_RHDRLEN 0x0000000000008000ULL
|
||||
#define INFINIPATH_E_RHDR 0x0000000000010000ULL
|
||||
#define INFINIPATH_E_RIBLOSTLINK 0x0000000000020000ULL
|
||||
#define INFINIPATH_E_SENDSPECIALTRIGGER 0x0000000008000000ULL
|
||||
#define INFINIPATH_E_SDMADISABLED 0x0000000010000000ULL
|
||||
#define INFINIPATH_E_SMINPKTLEN 0x0000000020000000ULL
|
||||
#define INFINIPATH_E_SMAXPKTLEN 0x0000000040000000ULL
|
||||
#define INFINIPATH_E_SUNDERRUN 0x0000000080000000ULL
|
||||
#define INFINIPATH_E_SPKTLEN 0x0000000100000000ULL
|
||||
#define INFINIPATH_E_SDROPPEDSMPPKT 0x0000000200000000ULL
|
||||
#define INFINIPATH_E_SDROPPEDDATAPKT 0x0000000400000000ULL
|
||||
#define INFINIPATH_E_SPIOARMLAUNCH 0x0000000800000000ULL
|
||||
#define INFINIPATH_E_SUNEXPERRPKTNUM 0x0000001000000000ULL
|
||||
#define INFINIPATH_E_SUNSUPVL 0x0000002000000000ULL
|
||||
#define INFINIPATH_E_SENDBUFMISUSE 0x0000004000000000ULL
|
||||
#define INFINIPATH_E_SDMAGENMISMATCH 0x0000008000000000ULL
|
||||
#define INFINIPATH_E_SDMAOUTOFBOUND 0x0000010000000000ULL
|
||||
#define INFINIPATH_E_SDMATAILOUTOFBOUND 0x0000020000000000ULL
|
||||
#define INFINIPATH_E_SDMABASE 0x0000040000000000ULL
|
||||
#define INFINIPATH_E_SDMA1STDESC 0x0000080000000000ULL
|
||||
#define INFINIPATH_E_SDMARPYTAG 0x0000100000000000ULL
|
||||
#define INFINIPATH_E_SDMADWEN 0x0000200000000000ULL
|
||||
#define INFINIPATH_E_SDMAMISSINGDW 0x0000400000000000ULL
|
||||
#define INFINIPATH_E_SDMAUNEXPDATA 0x0000800000000000ULL
|
||||
#define INFINIPATH_E_IBSTATUSCHANGED 0x0001000000000000ULL
|
||||
#define INFINIPATH_E_INVALIDADDR 0x0002000000000000ULL
|
||||
#define INFINIPATH_E_RESET 0x0004000000000000ULL
|
||||
#define INFINIPATH_E_HARDWARE 0x0008000000000000ULL
|
||||
#define INFINIPATH_E_SDMADESCADDRMISALIGN 0x0010000000000000ULL
|
||||
#define INFINIPATH_E_INVALIDEEPCMD 0x0020000000000000ULL
|
||||
|
||||
/*
|
||||
* this is used to print "common" packet errors only when the
|
||||
* __IPATH_ERRPKTDBG bit is set in ipath_debug.
|
||||
*/
|
||||
#define INFINIPATH_E_PKTERRS ( INFINIPATH_E_SPKTLEN \
|
||||
| INFINIPATH_E_SDROPPEDDATAPKT | INFINIPATH_E_RVCRC \
|
||||
| INFINIPATH_E_RICRC | INFINIPATH_E_RSHORTPKTLEN \
|
||||
| INFINIPATH_E_REBP )
|
||||
|
||||
/* Convenience for decoding Send DMA errors */
|
||||
#define INFINIPATH_E_SDMAERRS ( \
|
||||
INFINIPATH_E_SDMAGENMISMATCH | INFINIPATH_E_SDMAOUTOFBOUND | \
|
||||
INFINIPATH_E_SDMATAILOUTOFBOUND | INFINIPATH_E_SDMABASE | \
|
||||
INFINIPATH_E_SDMA1STDESC | INFINIPATH_E_SDMARPYTAG | \
|
||||
INFINIPATH_E_SDMADWEN | INFINIPATH_E_SDMAMISSINGDW | \
|
||||
INFINIPATH_E_SDMAUNEXPDATA | \
|
||||
INFINIPATH_E_SDMADESCADDRMISALIGN | \
|
||||
INFINIPATH_E_SDMADISABLED | \
|
||||
INFINIPATH_E_SENDBUFMISUSE)
|
||||
|
||||
/* kr_hwerrclear, kr_hwerrmask, kr_hwerrstatus, bits */
|
||||
/* TXEMEMPARITYERR bit 0: PIObuf, 1: PIOpbc, 2: launchfifo
|
||||
* RXEMEMPARITYERR bit 0: rcvbuf, 1: lookupq, 2: expTID, 3: eagerTID
|
||||
* bit 4: flag buffer, 5: datainfo, 6: header info */
|
||||
#define INFINIPATH_HWE_TXEMEMPARITYERR_MASK 0xFULL
|
||||
#define INFINIPATH_HWE_TXEMEMPARITYERR_SHIFT 40
|
||||
#define INFINIPATH_HWE_RXEMEMPARITYERR_MASK 0x7FULL
|
||||
#define INFINIPATH_HWE_RXEMEMPARITYERR_SHIFT 44
|
||||
#define INFINIPATH_HWE_IBCBUSTOSPCPARITYERR 0x4000000000000000ULL
|
||||
#define INFINIPATH_HWE_IBCBUSFRSPCPARITYERR 0x8000000000000000ULL
|
||||
/* txe mem parity errors (shift by INFINIPATH_HWE_TXEMEMPARITYERR_SHIFT) */
|
||||
#define INFINIPATH_HWE_TXEMEMPARITYERR_PIOBUF 0x1ULL
|
||||
#define INFINIPATH_HWE_TXEMEMPARITYERR_PIOPBC 0x2ULL
|
||||
#define INFINIPATH_HWE_TXEMEMPARITYERR_PIOLAUNCHFIFO 0x4ULL
|
||||
/* rxe mem parity errors (shift by INFINIPATH_HWE_RXEMEMPARITYERR_SHIFT) */
|
||||
#define INFINIPATH_HWE_RXEMEMPARITYERR_RCVBUF 0x01ULL
|
||||
#define INFINIPATH_HWE_RXEMEMPARITYERR_LOOKUPQ 0x02ULL
|
||||
#define INFINIPATH_HWE_RXEMEMPARITYERR_EXPTID 0x04ULL
|
||||
#define INFINIPATH_HWE_RXEMEMPARITYERR_EAGERTID 0x08ULL
|
||||
#define INFINIPATH_HWE_RXEMEMPARITYERR_FLAGBUF 0x10ULL
|
||||
#define INFINIPATH_HWE_RXEMEMPARITYERR_DATAINFO 0x20ULL
|
||||
#define INFINIPATH_HWE_RXEMEMPARITYERR_HDRINFO 0x40ULL
|
||||
/* waldo specific -- find the rest in ipath_6110.c */
|
||||
#define INFINIPATH_HWE_RXDSYNCMEMPARITYERR 0x0000000400000000ULL
|
||||
/* 6120/7220 specific -- find the rest in ipath_6120.c and ipath_7220.c */
|
||||
#define INFINIPATH_HWE_MEMBISTFAILED 0x0040000000000000ULL
|
||||
|
||||
/* kr_hwdiagctrl bits */
|
||||
#define INFINIPATH_DC_FORCETXEMEMPARITYERR_MASK 0xFULL
|
||||
#define INFINIPATH_DC_FORCETXEMEMPARITYERR_SHIFT 40
|
||||
#define INFINIPATH_DC_FORCERXEMEMPARITYERR_MASK 0x7FULL
|
||||
#define INFINIPATH_DC_FORCERXEMEMPARITYERR_SHIFT 44
|
||||
#define INFINIPATH_DC_FORCERXDSYNCMEMPARITYERR 0x0000000400000000ULL
|
||||
#define INFINIPATH_DC_COUNTERDISABLE 0x1000000000000000ULL
|
||||
#define INFINIPATH_DC_COUNTERWREN 0x2000000000000000ULL
|
||||
#define INFINIPATH_DC_FORCEIBCBUSTOSPCPARITYERR 0x4000000000000000ULL
|
||||
#define INFINIPATH_DC_FORCEIBCBUSFRSPCPARITYERR 0x8000000000000000ULL
|
||||
|
||||
/* kr_ibcctrl bits */
|
||||
#define INFINIPATH_IBCC_FLOWCTRLPERIOD_MASK 0xFFULL
|
||||
#define INFINIPATH_IBCC_FLOWCTRLPERIOD_SHIFT 0
|
||||
#define INFINIPATH_IBCC_FLOWCTRLWATERMARK_MASK 0xFFULL
|
||||
#define INFINIPATH_IBCC_FLOWCTRLWATERMARK_SHIFT 8
|
||||
#define INFINIPATH_IBCC_LINKINITCMD_MASK 0x3ULL
|
||||
#define INFINIPATH_IBCC_LINKINITCMD_DISABLE 1
|
||||
/* cycle through TS1/TS2 till OK */
|
||||
#define INFINIPATH_IBCC_LINKINITCMD_POLL 2
|
||||
/* wait for TS1, then go on */
|
||||
#define INFINIPATH_IBCC_LINKINITCMD_SLEEP 3
|
||||
#define INFINIPATH_IBCC_LINKINITCMD_SHIFT 16
|
||||
#define INFINIPATH_IBCC_LINKCMD_MASK 0x3ULL
|
||||
#define INFINIPATH_IBCC_LINKCMD_DOWN 1 /* move to 0x11 */
|
||||
#define INFINIPATH_IBCC_LINKCMD_ARMED 2 /* move to 0x21 */
|
||||
#define INFINIPATH_IBCC_LINKCMD_ACTIVE 3 /* move to 0x31 */
|
||||
#define INFINIPATH_IBCC_LINKCMD_SHIFT 18
|
||||
#define INFINIPATH_IBCC_MAXPKTLEN_MASK 0x7FFULL
|
||||
#define INFINIPATH_IBCC_MAXPKTLEN_SHIFT 20
|
||||
#define INFINIPATH_IBCC_PHYERRTHRESHOLD_MASK 0xFULL
|
||||
#define INFINIPATH_IBCC_PHYERRTHRESHOLD_SHIFT 32
|
||||
#define INFINIPATH_IBCC_OVERRUNTHRESHOLD_MASK 0xFULL
|
||||
#define INFINIPATH_IBCC_OVERRUNTHRESHOLD_SHIFT 36
|
||||
#define INFINIPATH_IBCC_CREDITSCALE_MASK 0x7ULL
|
||||
#define INFINIPATH_IBCC_CREDITSCALE_SHIFT 40
|
||||
#define INFINIPATH_IBCC_LOOPBACK 0x8000000000000000ULL
|
||||
#define INFINIPATH_IBCC_LINKDOWNDEFAULTSTATE 0x4000000000000000ULL
|
||||
|
||||
/* kr_ibcstatus bits */
|
||||
#define INFINIPATH_IBCS_LINKTRAININGSTATE_SHIFT 0
|
||||
#define INFINIPATH_IBCS_LINKSTATE_MASK 0x7
|
||||
|
||||
#define INFINIPATH_IBCS_TXREADY 0x40000000
|
||||
#define INFINIPATH_IBCS_TXCREDITOK 0x80000000
|
||||
/* link training states (shift by
|
||||
INFINIPATH_IBCS_LINKTRAININGSTATE_SHIFT) */
|
||||
#define INFINIPATH_IBCS_LT_STATE_DISABLED 0x00
|
||||
#define INFINIPATH_IBCS_LT_STATE_LINKUP 0x01
|
||||
#define INFINIPATH_IBCS_LT_STATE_POLLACTIVE 0x02
|
||||
#define INFINIPATH_IBCS_LT_STATE_POLLQUIET 0x03
|
||||
#define INFINIPATH_IBCS_LT_STATE_SLEEPDELAY 0x04
|
||||
#define INFINIPATH_IBCS_LT_STATE_SLEEPQUIET 0x05
|
||||
#define INFINIPATH_IBCS_LT_STATE_CFGDEBOUNCE 0x08
|
||||
#define INFINIPATH_IBCS_LT_STATE_CFGRCVFCFG 0x09
|
||||
#define INFINIPATH_IBCS_LT_STATE_CFGWAITRMT 0x0a
|
||||
#define INFINIPATH_IBCS_LT_STATE_CFGIDLE 0x0b
|
||||
#define INFINIPATH_IBCS_LT_STATE_RECOVERRETRAIN 0x0c
|
||||
#define INFINIPATH_IBCS_LT_STATE_RECOVERWAITRMT 0x0e
|
||||
#define INFINIPATH_IBCS_LT_STATE_RECOVERIDLE 0x0f
|
||||
/* link state machine states (shift by ibcs_ls_shift) */
|
||||
#define INFINIPATH_IBCS_L_STATE_DOWN 0x0
|
||||
#define INFINIPATH_IBCS_L_STATE_INIT 0x1
|
||||
#define INFINIPATH_IBCS_L_STATE_ARM 0x2
|
||||
#define INFINIPATH_IBCS_L_STATE_ACTIVE 0x3
|
||||
#define INFINIPATH_IBCS_L_STATE_ACT_DEFER 0x4
|
||||
|
||||
|
||||
/* kr_extstatus bits */
|
||||
#define INFINIPATH_EXTS_SERDESPLLLOCK 0x1
|
||||
#define INFINIPATH_EXTS_GPIOIN_MASK 0xFFFFULL
|
||||
#define INFINIPATH_EXTS_GPIOIN_SHIFT 48
|
||||
|
||||
/* kr_extctrl bits */
|
||||
#define INFINIPATH_EXTC_GPIOINVERT_MASK 0xFFFFULL
|
||||
#define INFINIPATH_EXTC_GPIOINVERT_SHIFT 32
|
||||
#define INFINIPATH_EXTC_GPIOOE_MASK 0xFFFFULL
|
||||
#define INFINIPATH_EXTC_GPIOOE_SHIFT 48
|
||||
#define INFINIPATH_EXTC_SERDESENABLE 0x80000000ULL
|
||||
#define INFINIPATH_EXTC_SERDESCONNECT 0x40000000ULL
|
||||
#define INFINIPATH_EXTC_SERDESENTRUNKING 0x20000000ULL
|
||||
#define INFINIPATH_EXTC_SERDESDISRXFIFO 0x10000000ULL
|
||||
#define INFINIPATH_EXTC_SERDESENPLPBK1 0x08000000ULL
|
||||
#define INFINIPATH_EXTC_SERDESENPLPBK2 0x04000000ULL
|
||||
#define INFINIPATH_EXTC_SERDESENENCDEC 0x02000000ULL
|
||||
#define INFINIPATH_EXTC_LED1SECPORT_ON 0x00000020ULL
|
||||
#define INFINIPATH_EXTC_LED2SECPORT_ON 0x00000010ULL
|
||||
#define INFINIPATH_EXTC_LED1PRIPORT_ON 0x00000008ULL
|
||||
#define INFINIPATH_EXTC_LED2PRIPORT_ON 0x00000004ULL
|
||||
#define INFINIPATH_EXTC_LEDGBLOK_ON 0x00000002ULL
|
||||
#define INFINIPATH_EXTC_LEDGBLERR_OFF 0x00000001ULL
|
||||
|
||||
/* kr_partitionkey bits */
|
||||
#define INFINIPATH_PKEY_SIZE 16
|
||||
#define INFINIPATH_PKEY_MASK 0xFFFF
|
||||
#define INFINIPATH_PKEY_DEFAULT_PKEY 0xFFFF
|
||||
|
||||
/* kr_serdesconfig0 bits */
|
||||
#define INFINIPATH_SERDC0_RESET_MASK 0xfULL /* overal reset bits */
|
||||
#define INFINIPATH_SERDC0_RESET_PLL 0x10000000ULL /* pll reset */
|
||||
/* tx idle enables (per lane) */
|
||||
#define INFINIPATH_SERDC0_TXIDLE 0xF000ULL
|
||||
/* rx detect enables (per lane) */
|
||||
#define INFINIPATH_SERDC0_RXDETECT_EN 0xF0000ULL
|
||||
/* L1 Power down; use with RXDETECT, Otherwise not used on IB side */
|
||||
#define INFINIPATH_SERDC0_L1PWR_DN 0xF0ULL
|
||||
|
||||
/* common kr_xgxsconfig bits (or safe in all, even if not implemented) */
|
||||
#define INFINIPATH_XGXS_RX_POL_SHIFT 19
|
||||
#define INFINIPATH_XGXS_RX_POL_MASK 0xfULL
|
||||
|
||||
|
||||
/*
|
||||
* IPATH_PIO_MAXIBHDR is the max IB header size allowed for in our
|
||||
* PIO send buffers. This is well beyond anything currently
|
||||
* defined in the InfiniBand spec.
|
||||
*/
|
||||
#define IPATH_PIO_MAXIBHDR 128
|
||||
|
||||
typedef u64 ipath_err_t;
|
||||
|
||||
/* The following change with the type of device, so
|
||||
* need to be part of the ipath_devdata struct, or
|
||||
* we could have problems plugging in devices of
|
||||
* different types (e.g. one HT, one PCIE)
|
||||
* in one system, to be managed by one driver.
|
||||
* On the other hand, this file is may also be included
|
||||
* by other code, so leave the declarations here
|
||||
* temporarily. Minor footprint issue if common-model
|
||||
* linker used, none if C89+ linker used.
|
||||
*/
|
||||
|
||||
/* mask of defined bits for various registers */
|
||||
extern u64 infinipath_i_bitsextant;
|
||||
extern ipath_err_t infinipath_e_bitsextant, infinipath_hwe_bitsextant;
|
||||
|
||||
/* masks that are different in various chips, or only exist in some chips */
|
||||
extern u32 infinipath_i_rcvavail_mask, infinipath_i_rcvurg_mask;
|
||||
|
||||
/*
|
||||
* These are the infinipath general register numbers (not offsets).
|
||||
* The kernel registers are used directly, those beyond the kernel
|
||||
* registers are calculated from one of the base registers. The use of
|
||||
* an integer type doesn't allow type-checking as thorough as, say,
|
||||
* an enum but allows for better hiding of chip differences.
|
||||
*/
|
||||
typedef const u16 ipath_kreg, /* infinipath general registers */
|
||||
ipath_creg, /* infinipath counter registers */
|
||||
ipath_sreg; /* kernel-only, infinipath send registers */
|
||||
|
||||
/*
|
||||
* These are the chip registers common to all infinipath chips, and
|
||||
* used both by the kernel and the diagnostics or other user code.
|
||||
* They are all implemented such that 64 bit accesses work.
|
||||
* Some implement no more than 32 bits. Because 64 bit reads
|
||||
* require 2 HT cmds on opteron, we access those with 32 bit
|
||||
* reads for efficiency (they are written as 64 bits, since
|
||||
* the extra 32 bits are nearly free on writes, and it slightly reduces
|
||||
* complexity). The rest are all accessed as 64 bits.
|
||||
*/
|
||||
struct ipath_kregs {
|
||||
/* These are the 32 bit group */
|
||||
ipath_kreg kr_control;
|
||||
ipath_kreg kr_counterregbase;
|
||||
ipath_kreg kr_intmask;
|
||||
ipath_kreg kr_intstatus;
|
||||
ipath_kreg kr_pagealign;
|
||||
ipath_kreg kr_portcnt;
|
||||
ipath_kreg kr_rcvtidbase;
|
||||
ipath_kreg kr_rcvtidcnt;
|
||||
ipath_kreg kr_rcvegrbase;
|
||||
ipath_kreg kr_rcvegrcnt;
|
||||
ipath_kreg kr_scratch;
|
||||
ipath_kreg kr_sendctrl;
|
||||
ipath_kreg kr_sendpiobufbase;
|
||||
ipath_kreg kr_sendpiobufcnt;
|
||||
ipath_kreg kr_sendpiosize;
|
||||
ipath_kreg kr_sendregbase;
|
||||
ipath_kreg kr_userregbase;
|
||||
/* These are the 64 bit group */
|
||||
ipath_kreg kr_debugport;
|
||||
ipath_kreg kr_debugportselect;
|
||||
ipath_kreg kr_errorclear;
|
||||
ipath_kreg kr_errormask;
|
||||
ipath_kreg kr_errorstatus;
|
||||
ipath_kreg kr_extctrl;
|
||||
ipath_kreg kr_extstatus;
|
||||
ipath_kreg kr_gpio_clear;
|
||||
ipath_kreg kr_gpio_mask;
|
||||
ipath_kreg kr_gpio_out;
|
||||
ipath_kreg kr_gpio_status;
|
||||
ipath_kreg kr_hwdiagctrl;
|
||||
ipath_kreg kr_hwerrclear;
|
||||
ipath_kreg kr_hwerrmask;
|
||||
ipath_kreg kr_hwerrstatus;
|
||||
ipath_kreg kr_ibcctrl;
|
||||
ipath_kreg kr_ibcstatus;
|
||||
ipath_kreg kr_intblocked;
|
||||
ipath_kreg kr_intclear;
|
||||
ipath_kreg kr_interruptconfig;
|
||||
ipath_kreg kr_mdio;
|
||||
ipath_kreg kr_partitionkey;
|
||||
ipath_kreg kr_rcvbthqp;
|
||||
ipath_kreg kr_rcvbufbase;
|
||||
ipath_kreg kr_rcvbufsize;
|
||||
ipath_kreg kr_rcvctrl;
|
||||
ipath_kreg kr_rcvhdrcnt;
|
||||
ipath_kreg kr_rcvhdrentsize;
|
||||
ipath_kreg kr_rcvhdrsize;
|
||||
ipath_kreg kr_rcvintmembase;
|
||||
ipath_kreg kr_rcvintmemsize;
|
||||
ipath_kreg kr_revision;
|
||||
ipath_kreg kr_sendbuffererror;
|
||||
ipath_kreg kr_sendpioavailaddr;
|
||||
ipath_kreg kr_serdesconfig0;
|
||||
ipath_kreg kr_serdesconfig1;
|
||||
ipath_kreg kr_serdesstatus;
|
||||
ipath_kreg kr_txintmembase;
|
||||
ipath_kreg kr_txintmemsize;
|
||||
ipath_kreg kr_xgxsconfig;
|
||||
ipath_kreg kr_ibpllcfg;
|
||||
/* use these two (and the following N ports) only with
|
||||
* ipath_k*_kreg64_port(); not *kreg64() */
|
||||
ipath_kreg kr_rcvhdraddr;
|
||||
ipath_kreg kr_rcvhdrtailaddr;
|
||||
|
||||
/* remaining registers are not present on all types of infinipath
|
||||
chips */
|
||||
ipath_kreg kr_rcvpktledcnt;
|
||||
ipath_kreg kr_pcierbuftestreg0;
|
||||
ipath_kreg kr_pcierbuftestreg1;
|
||||
ipath_kreg kr_pcieq0serdesconfig0;
|
||||
ipath_kreg kr_pcieq0serdesconfig1;
|
||||
ipath_kreg kr_pcieq0serdesstatus;
|
||||
ipath_kreg kr_pcieq1serdesconfig0;
|
||||
ipath_kreg kr_pcieq1serdesconfig1;
|
||||
ipath_kreg kr_pcieq1serdesstatus;
|
||||
ipath_kreg kr_hrtbt_guid;
|
||||
ipath_kreg kr_ibcddrctrl;
|
||||
ipath_kreg kr_ibcddrstatus;
|
||||
ipath_kreg kr_jintreload;
|
||||
|
||||
/* send dma related regs */
|
||||
ipath_kreg kr_senddmabase;
|
||||
ipath_kreg kr_senddmalengen;
|
||||
ipath_kreg kr_senddmatail;
|
||||
ipath_kreg kr_senddmahead;
|
||||
ipath_kreg kr_senddmaheadaddr;
|
||||
ipath_kreg kr_senddmabufmask0;
|
||||
ipath_kreg kr_senddmabufmask1;
|
||||
ipath_kreg kr_senddmabufmask2;
|
||||
ipath_kreg kr_senddmastatus;
|
||||
|
||||
/* SerDes related regs (IBA7220-only) */
|
||||
ipath_kreg kr_ibserdesctrl;
|
||||
ipath_kreg kr_ib_epbacc;
|
||||
ipath_kreg kr_ib_epbtrans;
|
||||
ipath_kreg kr_pcie_epbacc;
|
||||
ipath_kreg kr_pcie_epbtrans;
|
||||
ipath_kreg kr_ib_ddsrxeq;
|
||||
};
|
||||
|
||||
struct ipath_cregs {
|
||||
ipath_creg cr_badformatcnt;
|
||||
ipath_creg cr_erricrccnt;
|
||||
ipath_creg cr_errlinkcnt;
|
||||
ipath_creg cr_errlpcrccnt;
|
||||
ipath_creg cr_errpkey;
|
||||
ipath_creg cr_errrcvflowctrlcnt;
|
||||
ipath_creg cr_err_rlencnt;
|
||||
ipath_creg cr_errslencnt;
|
||||
ipath_creg cr_errtidfull;
|
||||
ipath_creg cr_errtidvalid;
|
||||
ipath_creg cr_errvcrccnt;
|
||||
ipath_creg cr_ibstatuschange;
|
||||
ipath_creg cr_intcnt;
|
||||
ipath_creg cr_invalidrlencnt;
|
||||
ipath_creg cr_invalidslencnt;
|
||||
ipath_creg cr_lbflowstallcnt;
|
||||
ipath_creg cr_iblinkdowncnt;
|
||||
ipath_creg cr_iblinkerrrecovcnt;
|
||||
ipath_creg cr_ibsymbolerrcnt;
|
||||
ipath_creg cr_pktrcvcnt;
|
||||
ipath_creg cr_pktrcvflowctrlcnt;
|
||||
ipath_creg cr_pktsendcnt;
|
||||
ipath_creg cr_pktsendflowcnt;
|
||||
ipath_creg cr_portovflcnt;
|
||||
ipath_creg cr_rcvebpcnt;
|
||||
ipath_creg cr_rcvovflcnt;
|
||||
ipath_creg cr_rxdroppktcnt;
|
||||
ipath_creg cr_senddropped;
|
||||
ipath_creg cr_sendstallcnt;
|
||||
ipath_creg cr_sendunderruncnt;
|
||||
ipath_creg cr_unsupvlcnt;
|
||||
ipath_creg cr_wordrcvcnt;
|
||||
ipath_creg cr_wordsendcnt;
|
||||
ipath_creg cr_vl15droppedpktcnt;
|
||||
ipath_creg cr_rxotherlocalphyerrcnt;
|
||||
ipath_creg cr_excessbufferovflcnt;
|
||||
ipath_creg cr_locallinkintegrityerrcnt;
|
||||
ipath_creg cr_rxvlerrcnt;
|
||||
ipath_creg cr_rxdlidfltrcnt;
|
||||
ipath_creg cr_psstat;
|
||||
ipath_creg cr_psstart;
|
||||
ipath_creg cr_psinterval;
|
||||
ipath_creg cr_psrcvdatacount;
|
||||
ipath_creg cr_psrcvpktscount;
|
||||
ipath_creg cr_psxmitdatacount;
|
||||
ipath_creg cr_psxmitpktscount;
|
||||
ipath_creg cr_psxmitwaitcount;
|
||||
};
|
||||
|
||||
#endif /* _IPATH_REGISTERS_H */
|
|
@ -1,733 +0,0 @@
|
|||
/*
|
||||
* Copyright (c) 2006, 2007, 2008 QLogic Corporation. All rights reserved.
|
||||
* Copyright (c) 2005, 2006 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.
|
||||
*/
|
||||
|
||||
#include <linux/spinlock.h>
|
||||
|
||||
#include "ipath_verbs.h"
|
||||
#include "ipath_kernel.h"
|
||||
|
||||
/*
|
||||
* Convert the AETH RNR timeout code into the number of milliseconds.
|
||||
*/
|
||||
const u32 ib_ipath_rnr_table[32] = {
|
||||
656, /* 0 */
|
||||
1, /* 1 */
|
||||
1, /* 2 */
|
||||
1, /* 3 */
|
||||
1, /* 4 */
|
||||
1, /* 5 */
|
||||
1, /* 6 */
|
||||
1, /* 7 */
|
||||
1, /* 8 */
|
||||
1, /* 9 */
|
||||
1, /* A */
|
||||
1, /* B */
|
||||
1, /* C */
|
||||
1, /* D */
|
||||
2, /* E */
|
||||
2, /* F */
|
||||
3, /* 10 */
|
||||
4, /* 11 */
|
||||
6, /* 12 */
|
||||
8, /* 13 */
|
||||
11, /* 14 */
|
||||
16, /* 15 */
|
||||
21, /* 16 */
|
||||
31, /* 17 */
|
||||
41, /* 18 */
|
||||
62, /* 19 */
|
||||
82, /* 1A */
|
||||
123, /* 1B */
|
||||
164, /* 1C */
|
||||
246, /* 1D */
|
||||
328, /* 1E */
|
||||
492 /* 1F */
|
||||
};
|
||||
|
||||
/**
|
||||
* ipath_insert_rnr_queue - put QP on the RNR timeout list for the device
|
||||
* @qp: the QP
|
||||
*
|
||||
* Called with the QP s_lock held and interrupts disabled.
|
||||
* XXX Use a simple list for now. We might need a priority
|
||||
* queue if we have lots of QPs waiting for RNR timeouts
|
||||
* but that should be rare.
|
||||
*/
|
||||
void ipath_insert_rnr_queue(struct ipath_qp *qp)
|
||||
{
|
||||
struct ipath_ibdev *dev = to_idev(qp->ibqp.device);
|
||||
|
||||
/* We already did a spin_lock_irqsave(), so just use spin_lock */
|
||||
spin_lock(&dev->pending_lock);
|
||||
if (list_empty(&dev->rnrwait))
|
||||
list_add(&qp->timerwait, &dev->rnrwait);
|
||||
else {
|
||||
struct list_head *l = &dev->rnrwait;
|
||||
struct ipath_qp *nqp = list_entry(l->next, struct ipath_qp,
|
||||
timerwait);
|
||||
|
||||
while (qp->s_rnr_timeout >= nqp->s_rnr_timeout) {
|
||||
qp->s_rnr_timeout -= nqp->s_rnr_timeout;
|
||||
l = l->next;
|
||||
if (l->next == &dev->rnrwait) {
|
||||
nqp = NULL;
|
||||
break;
|
||||
}
|
||||
nqp = list_entry(l->next, struct ipath_qp,
|
||||
timerwait);
|
||||
}
|
||||
if (nqp)
|
||||
nqp->s_rnr_timeout -= qp->s_rnr_timeout;
|
||||
list_add(&qp->timerwait, l);
|
||||
}
|
||||
spin_unlock(&dev->pending_lock);
|
||||
}
|
||||
|
||||
/**
|
||||
* ipath_init_sge - Validate a RWQE and fill in the SGE state
|
||||
* @qp: the QP
|
||||
*
|
||||
* Return 1 if OK.
|
||||
*/
|
||||
int ipath_init_sge(struct ipath_qp *qp, struct ipath_rwqe *wqe,
|
||||
u32 *lengthp, struct ipath_sge_state *ss)
|
||||
{
|
||||
int i, j, ret;
|
||||
struct ib_wc wc;
|
||||
|
||||
*lengthp = 0;
|
||||
for (i = j = 0; i < wqe->num_sge; i++) {
|
||||
if (wqe->sg_list[i].length == 0)
|
||||
continue;
|
||||
/* Check LKEY */
|
||||
if (!ipath_lkey_ok(qp, j ? &ss->sg_list[j - 1] : &ss->sge,
|
||||
&wqe->sg_list[i], IB_ACCESS_LOCAL_WRITE))
|
||||
goto bad_lkey;
|
||||
*lengthp += wqe->sg_list[i].length;
|
||||
j++;
|
||||
}
|
||||
ss->num_sge = j;
|
||||
ret = 1;
|
||||
goto bail;
|
||||
|
||||
bad_lkey:
|
||||
memset(&wc, 0, sizeof(wc));
|
||||
wc.wr_id = wqe->wr_id;
|
||||
wc.status = IB_WC_LOC_PROT_ERR;
|
||||
wc.opcode = IB_WC_RECV;
|
||||
wc.qp = &qp->ibqp;
|
||||
/* Signal solicited completion event. */
|
||||
ipath_cq_enter(to_icq(qp->ibqp.recv_cq), &wc, 1);
|
||||
ret = 0;
|
||||
bail:
|
||||
return ret;
|
||||
}
|
||||
|
||||
/**
|
||||
* ipath_get_rwqe - copy the next RWQE into the QP's RWQE
|
||||
* @qp: the QP
|
||||
* @wr_id_only: update qp->r_wr_id only, not qp->r_sge
|
||||
*
|
||||
* Return 0 if no RWQE is available, otherwise return 1.
|
||||
*
|
||||
* Can be called from interrupt level.
|
||||
*/
|
||||
int ipath_get_rwqe(struct ipath_qp *qp, int wr_id_only)
|
||||
{
|
||||
unsigned long flags;
|
||||
struct ipath_rq *rq;
|
||||
struct ipath_rwq *wq;
|
||||
struct ipath_srq *srq;
|
||||
struct ipath_rwqe *wqe;
|
||||
void (*handler)(struct ib_event *, void *);
|
||||
u32 tail;
|
||||
int ret;
|
||||
|
||||
if (qp->ibqp.srq) {
|
||||
srq = to_isrq(qp->ibqp.srq);
|
||||
handler = srq->ibsrq.event_handler;
|
||||
rq = &srq->rq;
|
||||
} else {
|
||||
srq = NULL;
|
||||
handler = NULL;
|
||||
rq = &qp->r_rq;
|
||||
}
|
||||
|
||||
spin_lock_irqsave(&rq->lock, flags);
|
||||
if (!(ib_ipath_state_ops[qp->state] & IPATH_PROCESS_RECV_OK)) {
|
||||
ret = 0;
|
||||
goto unlock;
|
||||
}
|
||||
|
||||
wq = rq->wq;
|
||||
tail = wq->tail;
|
||||
/* Validate tail before using it since it is user writable. */
|
||||
if (tail >= rq->size)
|
||||
tail = 0;
|
||||
do {
|
||||
if (unlikely(tail == wq->head)) {
|
||||
ret = 0;
|
||||
goto unlock;
|
||||
}
|
||||
/* Make sure entry is read after head index is read. */
|
||||
smp_rmb();
|
||||
wqe = get_rwqe_ptr(rq, tail);
|
||||
if (++tail >= rq->size)
|
||||
tail = 0;
|
||||
if (wr_id_only)
|
||||
break;
|
||||
qp->r_sge.sg_list = qp->r_sg_list;
|
||||
} while (!ipath_init_sge(qp, wqe, &qp->r_len, &qp->r_sge));
|
||||
qp->r_wr_id = wqe->wr_id;
|
||||
wq->tail = tail;
|
||||
|
||||
ret = 1;
|
||||
set_bit(IPATH_R_WRID_VALID, &qp->r_aflags);
|
||||
if (handler) {
|
||||
u32 n;
|
||||
|
||||
/*
|
||||
* validate head pointer value and compute
|
||||
* the number of remaining WQEs.
|
||||
*/
|
||||
n = wq->head;
|
||||
if (n >= rq->size)
|
||||
n = 0;
|
||||
if (n < tail)
|
||||
n += rq->size - tail;
|
||||
else
|
||||
n -= tail;
|
||||
if (n < srq->limit) {
|
||||
struct ib_event ev;
|
||||
|
||||
srq->limit = 0;
|
||||
spin_unlock_irqrestore(&rq->lock, flags);
|
||||
ev.device = qp->ibqp.device;
|
||||
ev.element.srq = qp->ibqp.srq;
|
||||
ev.event = IB_EVENT_SRQ_LIMIT_REACHED;
|
||||
handler(&ev, srq->ibsrq.srq_context);
|
||||
goto bail;
|
||||
}
|
||||
}
|
||||
unlock:
|
||||
spin_unlock_irqrestore(&rq->lock, flags);
|
||||
bail:
|
||||
return ret;
|
||||
}
|
||||
|
||||
/**
|
||||
* ipath_ruc_loopback - handle UC and RC lookback requests
|
||||
* @sqp: the sending QP
|
||||
*
|
||||
* This is called from ipath_do_send() to
|
||||
* forward a WQE addressed to the same HCA.
|
||||
* Note that although we are single threaded due to the tasklet, we still
|
||||
* have to protect against post_send(). We don't have to worry about
|
||||
* receive interrupts since this is a connected protocol and all packets
|
||||
* will pass through here.
|
||||
*/
|
||||
static void ipath_ruc_loopback(struct ipath_qp *sqp)
|
||||
{
|
||||
struct ipath_ibdev *dev = to_idev(sqp->ibqp.device);
|
||||
struct ipath_qp *qp;
|
||||
struct ipath_swqe *wqe;
|
||||
struct ipath_sge *sge;
|
||||
unsigned long flags;
|
||||
struct ib_wc wc;
|
||||
u64 sdata;
|
||||
atomic64_t *maddr;
|
||||
enum ib_wc_status send_status;
|
||||
|
||||
/*
|
||||
* Note that we check the responder QP state after
|
||||
* checking the requester's state.
|
||||
*/
|
||||
qp = ipath_lookup_qpn(&dev->qp_table, sqp->remote_qpn);
|
||||
|
||||
spin_lock_irqsave(&sqp->s_lock, flags);
|
||||
|
||||
/* Return if we are already busy processing a work request. */
|
||||
if ((sqp->s_flags & (IPATH_S_BUSY | IPATH_S_ANY_WAIT)) ||
|
||||
!(ib_ipath_state_ops[sqp->state] & IPATH_PROCESS_OR_FLUSH_SEND))
|
||||
goto unlock;
|
||||
|
||||
sqp->s_flags |= IPATH_S_BUSY;
|
||||
|
||||
again:
|
||||
if (sqp->s_last == sqp->s_head)
|
||||
goto clr_busy;
|
||||
wqe = get_swqe_ptr(sqp, sqp->s_last);
|
||||
|
||||
/* Return if it is not OK to start a new work reqeust. */
|
||||
if (!(ib_ipath_state_ops[sqp->state] & IPATH_PROCESS_NEXT_SEND_OK)) {
|
||||
if (!(ib_ipath_state_ops[sqp->state] & IPATH_FLUSH_SEND))
|
||||
goto clr_busy;
|
||||
/* We are in the error state, flush the work request. */
|
||||
send_status = IB_WC_WR_FLUSH_ERR;
|
||||
goto flush_send;
|
||||
}
|
||||
|
||||
/*
|
||||
* We can rely on the entry not changing without the s_lock
|
||||
* being held until we update s_last.
|
||||
* We increment s_cur to indicate s_last is in progress.
|
||||
*/
|
||||
if (sqp->s_last == sqp->s_cur) {
|
||||
if (++sqp->s_cur >= sqp->s_size)
|
||||
sqp->s_cur = 0;
|
||||
}
|
||||
spin_unlock_irqrestore(&sqp->s_lock, flags);
|
||||
|
||||
if (!qp || !(ib_ipath_state_ops[qp->state] & IPATH_PROCESS_RECV_OK)) {
|
||||
dev->n_pkt_drops++;
|
||||
/*
|
||||
* For RC, the requester would timeout and retry so
|
||||
* shortcut the timeouts and just signal too many retries.
|
||||
*/
|
||||
if (sqp->ibqp.qp_type == IB_QPT_RC)
|
||||
send_status = IB_WC_RETRY_EXC_ERR;
|
||||
else
|
||||
send_status = IB_WC_SUCCESS;
|
||||
goto serr;
|
||||
}
|
||||
|
||||
memset(&wc, 0, sizeof wc);
|
||||
send_status = IB_WC_SUCCESS;
|
||||
|
||||
sqp->s_sge.sge = wqe->sg_list[0];
|
||||
sqp->s_sge.sg_list = wqe->sg_list + 1;
|
||||
sqp->s_sge.num_sge = wqe->wr.num_sge;
|
||||
sqp->s_len = wqe->length;
|
||||
switch (wqe->wr.opcode) {
|
||||
case IB_WR_SEND_WITH_IMM:
|
||||
wc.wc_flags = IB_WC_WITH_IMM;
|
||||
wc.ex.imm_data = wqe->wr.ex.imm_data;
|
||||
/* FALLTHROUGH */
|
||||
case IB_WR_SEND:
|
||||
if (!ipath_get_rwqe(qp, 0))
|
||||
goto rnr_nak;
|
||||
break;
|
||||
|
||||
case IB_WR_RDMA_WRITE_WITH_IMM:
|
||||
if (unlikely(!(qp->qp_access_flags & IB_ACCESS_REMOTE_WRITE)))
|
||||
goto inv_err;
|
||||
wc.wc_flags = IB_WC_WITH_IMM;
|
||||
wc.ex.imm_data = wqe->wr.ex.imm_data;
|
||||
if (!ipath_get_rwqe(qp, 1))
|
||||
goto rnr_nak;
|
||||
/* FALLTHROUGH */
|
||||
case IB_WR_RDMA_WRITE:
|
||||
if (unlikely(!(qp->qp_access_flags & IB_ACCESS_REMOTE_WRITE)))
|
||||
goto inv_err;
|
||||
if (wqe->length == 0)
|
||||
break;
|
||||
if (unlikely(!ipath_rkey_ok(qp, &qp->r_sge, wqe->length,
|
||||
wqe->rdma_wr.remote_addr,
|
||||
wqe->rdma_wr.rkey,
|
||||
IB_ACCESS_REMOTE_WRITE)))
|
||||
goto acc_err;
|
||||
break;
|
||||
|
||||
case IB_WR_RDMA_READ:
|
||||
if (unlikely(!(qp->qp_access_flags & IB_ACCESS_REMOTE_READ)))
|
||||
goto inv_err;
|
||||
if (unlikely(!ipath_rkey_ok(qp, &sqp->s_sge, wqe->length,
|
||||
wqe->rdma_wr.remote_addr,
|
||||
wqe->rdma_wr.rkey,
|
||||
IB_ACCESS_REMOTE_READ)))
|
||||
goto acc_err;
|
||||
qp->r_sge.sge = wqe->sg_list[0];
|
||||
qp->r_sge.sg_list = wqe->sg_list + 1;
|
||||
qp->r_sge.num_sge = wqe->wr.num_sge;
|
||||
break;
|
||||
|
||||
case IB_WR_ATOMIC_CMP_AND_SWP:
|
||||
case IB_WR_ATOMIC_FETCH_AND_ADD:
|
||||
if (unlikely(!(qp->qp_access_flags & IB_ACCESS_REMOTE_ATOMIC)))
|
||||
goto inv_err;
|
||||
if (unlikely(!ipath_rkey_ok(qp, &qp->r_sge, sizeof(u64),
|
||||
wqe->atomic_wr.remote_addr,
|
||||
wqe->atomic_wr.rkey,
|
||||
IB_ACCESS_REMOTE_ATOMIC)))
|
||||
goto acc_err;
|
||||
/* Perform atomic OP and save result. */
|
||||
maddr = (atomic64_t *) qp->r_sge.sge.vaddr;
|
||||
sdata = wqe->atomic_wr.compare_add;
|
||||
*(u64 *) sqp->s_sge.sge.vaddr =
|
||||
(wqe->wr.opcode == IB_WR_ATOMIC_FETCH_AND_ADD) ?
|
||||
(u64) atomic64_add_return(sdata, maddr) - sdata :
|
||||
(u64) cmpxchg((u64 *) qp->r_sge.sge.vaddr,
|
||||
sdata, wqe->atomic_wr.swap);
|
||||
goto send_comp;
|
||||
|
||||
default:
|
||||
send_status = IB_WC_LOC_QP_OP_ERR;
|
||||
goto serr;
|
||||
}
|
||||
|
||||
sge = &sqp->s_sge.sge;
|
||||
while (sqp->s_len) {
|
||||
u32 len = sqp->s_len;
|
||||
|
||||
if (len > sge->length)
|
||||
len = sge->length;
|
||||
if (len > sge->sge_length)
|
||||
len = sge->sge_length;
|
||||
BUG_ON(len == 0);
|
||||
ipath_copy_sge(&qp->r_sge, sge->vaddr, len);
|
||||
sge->vaddr += len;
|
||||
sge->length -= len;
|
||||
sge->sge_length -= len;
|
||||
if (sge->sge_length == 0) {
|
||||
if (--sqp->s_sge.num_sge)
|
||||
*sge = *sqp->s_sge.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;
|
||||
}
|
||||
sqp->s_len -= len;
|
||||
}
|
||||
|
||||
if (!test_and_clear_bit(IPATH_R_WRID_VALID, &qp->r_aflags))
|
||||
goto send_comp;
|
||||
|
||||
if (wqe->wr.opcode == IB_WR_RDMA_WRITE_WITH_IMM)
|
||||
wc.opcode = IB_WC_RECV_RDMA_WITH_IMM;
|
||||
else
|
||||
wc.opcode = IB_WC_RECV;
|
||||
wc.wr_id = qp->r_wr_id;
|
||||
wc.status = IB_WC_SUCCESS;
|
||||
wc.byte_len = wqe->length;
|
||||
wc.qp = &qp->ibqp;
|
||||
wc.src_qp = qp->remote_qpn;
|
||||
wc.slid = qp->remote_ah_attr.dlid;
|
||||
wc.sl = qp->remote_ah_attr.sl;
|
||||
wc.port_num = 1;
|
||||
/* Signal completion event if the solicited bit is set. */
|
||||
ipath_cq_enter(to_icq(qp->ibqp.recv_cq), &wc,
|
||||
wqe->wr.send_flags & IB_SEND_SOLICITED);
|
||||
|
||||
send_comp:
|
||||
spin_lock_irqsave(&sqp->s_lock, flags);
|
||||
flush_send:
|
||||
sqp->s_rnr_retry = sqp->s_rnr_retry_cnt;
|
||||
ipath_send_complete(sqp, wqe, send_status);
|
||||
goto again;
|
||||
|
||||
rnr_nak:
|
||||
/* Handle RNR NAK */
|
||||
if (qp->ibqp.qp_type == IB_QPT_UC)
|
||||
goto send_comp;
|
||||
/*
|
||||
* Note: we don't need the s_lock held since the BUSY flag
|
||||
* makes this single threaded.
|
||||
*/
|
||||
if (sqp->s_rnr_retry == 0) {
|
||||
send_status = IB_WC_RNR_RETRY_EXC_ERR;
|
||||
goto serr;
|
||||
}
|
||||
if (sqp->s_rnr_retry_cnt < 7)
|
||||
sqp->s_rnr_retry--;
|
||||
spin_lock_irqsave(&sqp->s_lock, flags);
|
||||
if (!(ib_ipath_state_ops[sqp->state] & IPATH_PROCESS_RECV_OK))
|
||||
goto clr_busy;
|
||||
sqp->s_flags |= IPATH_S_WAITING;
|
||||
dev->n_rnr_naks++;
|
||||
sqp->s_rnr_timeout = ib_ipath_rnr_table[qp->r_min_rnr_timer];
|
||||
ipath_insert_rnr_queue(sqp);
|
||||
goto clr_busy;
|
||||
|
||||
inv_err:
|
||||
send_status = IB_WC_REM_INV_REQ_ERR;
|
||||
wc.status = IB_WC_LOC_QP_OP_ERR;
|
||||
goto err;
|
||||
|
||||
acc_err:
|
||||
send_status = IB_WC_REM_ACCESS_ERR;
|
||||
wc.status = IB_WC_LOC_PROT_ERR;
|
||||
err:
|
||||
/* responder goes to error state */
|
||||
ipath_rc_error(qp, wc.status);
|
||||
|
||||
serr:
|
||||
spin_lock_irqsave(&sqp->s_lock, flags);
|
||||
ipath_send_complete(sqp, wqe, send_status);
|
||||
if (sqp->ibqp.qp_type == IB_QPT_RC) {
|
||||
int lastwqe = ipath_error_qp(sqp, IB_WC_WR_FLUSH_ERR);
|
||||
|
||||
sqp->s_flags &= ~IPATH_S_BUSY;
|
||||
spin_unlock_irqrestore(&sqp->s_lock, flags);
|
||||
if (lastwqe) {
|
||||
struct ib_event ev;
|
||||
|
||||
ev.device = sqp->ibqp.device;
|
||||
ev.element.qp = &sqp->ibqp;
|
||||
ev.event = IB_EVENT_QP_LAST_WQE_REACHED;
|
||||
sqp->ibqp.event_handler(&ev, sqp->ibqp.qp_context);
|
||||
}
|
||||
goto done;
|
||||
}
|
||||
clr_busy:
|
||||
sqp->s_flags &= ~IPATH_S_BUSY;
|
||||
unlock:
|
||||
spin_unlock_irqrestore(&sqp->s_lock, flags);
|
||||
done:
|
||||
if (qp && atomic_dec_and_test(&qp->refcount))
|
||||
wake_up(&qp->wait);
|
||||
}
|
||||
|
||||
static void want_buffer(struct ipath_devdata *dd, struct ipath_qp *qp)
|
||||
{
|
||||
if (!(dd->ipath_flags & IPATH_HAS_SEND_DMA) ||
|
||||
qp->ibqp.qp_type == IB_QPT_SMI) {
|
||||
unsigned long flags;
|
||||
|
||||
spin_lock_irqsave(&dd->ipath_sendctrl_lock, flags);
|
||||
dd->ipath_sendctrl |= INFINIPATH_S_PIOINTBUFAVAIL;
|
||||
ipath_write_kreg(dd, dd->ipath_kregs->kr_sendctrl,
|
||||
dd->ipath_sendctrl);
|
||||
ipath_read_kreg64(dd, dd->ipath_kregs->kr_scratch);
|
||||
spin_unlock_irqrestore(&dd->ipath_sendctrl_lock, flags);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* ipath_no_bufs_available - tell the layer driver we need buffers
|
||||
* @qp: the QP that caused the problem
|
||||
* @dev: the device we ran out of buffers on
|
||||
*
|
||||
* Called when we run out of PIO buffers.
|
||||
* If we are now in the error state, return zero to flush the
|
||||
* send work request.
|
||||
*/
|
||||
static int ipath_no_bufs_available(struct ipath_qp *qp,
|
||||
struct ipath_ibdev *dev)
|
||||
{
|
||||
unsigned long flags;
|
||||
int ret = 1;
|
||||
|
||||
/*
|
||||
* Note that as soon as want_buffer() is called and
|
||||
* possibly before it returns, ipath_ib_piobufavail()
|
||||
* could be called. Therefore, put QP on the piowait list before
|
||||
* enabling the PIO avail interrupt.
|
||||
*/
|
||||
spin_lock_irqsave(&qp->s_lock, flags);
|
||||
if (ib_ipath_state_ops[qp->state] & IPATH_PROCESS_SEND_OK) {
|
||||
dev->n_piowait++;
|
||||
qp->s_flags |= IPATH_S_WAITING;
|
||||
qp->s_flags &= ~IPATH_S_BUSY;
|
||||
spin_lock(&dev->pending_lock);
|
||||
if (list_empty(&qp->piowait))
|
||||
list_add_tail(&qp->piowait, &dev->piowait);
|
||||
spin_unlock(&dev->pending_lock);
|
||||
} else
|
||||
ret = 0;
|
||||
spin_unlock_irqrestore(&qp->s_lock, flags);
|
||||
if (ret)
|
||||
want_buffer(dev->dd, qp);
|
||||
return ret;
|
||||
}
|
||||
|
||||
/**
|
||||
* ipath_make_grh - construct a GRH header
|
||||
* @dev: a pointer to the ipath device
|
||||
* @hdr: a pointer to the GRH header being constructed
|
||||
* @grh: the global route address to send to
|
||||
* @hwords: the number of 32 bit words of header being sent
|
||||
* @nwords: the number of 32 bit words of data being sent
|
||||
*
|
||||
* Return the size of the header in 32 bit words.
|
||||
*/
|
||||
u32 ipath_make_grh(struct ipath_ibdev *dev, struct ib_grh *hdr,
|
||||
struct ib_global_route *grh, u32 hwords, u32 nwords)
|
||||
{
|
||||
hdr->version_tclass_flow =
|
||||
cpu_to_be32((6 << 28) |
|
||||
(grh->traffic_class << 20) |
|
||||
grh->flow_label);
|
||||
hdr->paylen = cpu_to_be16((hwords - 2 + nwords + SIZE_OF_CRC) << 2);
|
||||
/* next_hdr is defined by C8-7 in ch. 8.4.1 */
|
||||
hdr->next_hdr = 0x1B;
|
||||
hdr->hop_limit = grh->hop_limit;
|
||||
/* The SGID is 32-bit aligned. */
|
||||
hdr->sgid.global.subnet_prefix = dev->gid_prefix;
|
||||
hdr->sgid.global.interface_id = dev->dd->ipath_guid;
|
||||
hdr->dgid = grh->dgid;
|
||||
|
||||
/* GRH header size in 32-bit words. */
|
||||
return sizeof(struct ib_grh) / sizeof(u32);
|
||||
}
|
||||
|
||||
void ipath_make_ruc_header(struct ipath_ibdev *dev, struct ipath_qp *qp,
|
||||
struct ipath_other_headers *ohdr,
|
||||
u32 bth0, u32 bth2)
|
||||
{
|
||||
u16 lrh0;
|
||||
u32 nwords;
|
||||
u32 extra_bytes;
|
||||
|
||||
/* Construct the header. */
|
||||
extra_bytes = -qp->s_cur_size & 3;
|
||||
nwords = (qp->s_cur_size + extra_bytes) >> 2;
|
||||
lrh0 = IPATH_LRH_BTH;
|
||||
if (unlikely(qp->remote_ah_attr.ah_flags & IB_AH_GRH)) {
|
||||
qp->s_hdrwords += ipath_make_grh(dev, &qp->s_hdr.u.l.grh,
|
||||
&qp->remote_ah_attr.grh,
|
||||
qp->s_hdrwords, nwords);
|
||||
lrh0 = IPATH_LRH_GRH;
|
||||
}
|
||||
lrh0 |= qp->remote_ah_attr.sl << 4;
|
||||
qp->s_hdr.lrh[0] = cpu_to_be16(lrh0);
|
||||
qp->s_hdr.lrh[1] = cpu_to_be16(qp->remote_ah_attr.dlid);
|
||||
qp->s_hdr.lrh[2] = cpu_to_be16(qp->s_hdrwords + nwords + SIZE_OF_CRC);
|
||||
qp->s_hdr.lrh[3] = cpu_to_be16(dev->dd->ipath_lid |
|
||||
qp->remote_ah_attr.src_path_bits);
|
||||
bth0 |= ipath_get_pkey(dev->dd, qp->s_pkey_index);
|
||||
bth0 |= extra_bytes << 20;
|
||||
ohdr->bth[0] = cpu_to_be32(bth0 | (1 << 22));
|
||||
ohdr->bth[1] = cpu_to_be32(qp->remote_qpn);
|
||||
ohdr->bth[2] = cpu_to_be32(bth2);
|
||||
}
|
||||
|
||||
/**
|
||||
* ipath_do_send - perform a send on a QP
|
||||
* @data: contains a pointer to the QP
|
||||
*
|
||||
* Process entries in the send work queue until credit or queue is
|
||||
* exhausted. Only allow one CPU to send a packet per QP (tasklet).
|
||||
* Otherwise, two threads could send packets out of order.
|
||||
*/
|
||||
void ipath_do_send(unsigned long data)
|
||||
{
|
||||
struct ipath_qp *qp = (struct ipath_qp *)data;
|
||||
struct ipath_ibdev *dev = to_idev(qp->ibqp.device);
|
||||
int (*make_req)(struct ipath_qp *qp);
|
||||
unsigned long flags;
|
||||
|
||||
if ((qp->ibqp.qp_type == IB_QPT_RC ||
|
||||
qp->ibqp.qp_type == IB_QPT_UC) &&
|
||||
qp->remote_ah_attr.dlid == dev->dd->ipath_lid) {
|
||||
ipath_ruc_loopback(qp);
|
||||
goto bail;
|
||||
}
|
||||
|
||||
if (qp->ibqp.qp_type == IB_QPT_RC)
|
||||
make_req = ipath_make_rc_req;
|
||||
else if (qp->ibqp.qp_type == IB_QPT_UC)
|
||||
make_req = ipath_make_uc_req;
|
||||
else
|
||||
make_req = ipath_make_ud_req;
|
||||
|
||||
spin_lock_irqsave(&qp->s_lock, flags);
|
||||
|
||||
/* Return if we are already busy processing a work request. */
|
||||
if ((qp->s_flags & (IPATH_S_BUSY | IPATH_S_ANY_WAIT)) ||
|
||||
!(ib_ipath_state_ops[qp->state] & IPATH_PROCESS_OR_FLUSH_SEND)) {
|
||||
spin_unlock_irqrestore(&qp->s_lock, flags);
|
||||
goto bail;
|
||||
}
|
||||
|
||||
qp->s_flags |= IPATH_S_BUSY;
|
||||
|
||||
spin_unlock_irqrestore(&qp->s_lock, flags);
|
||||
|
||||
again:
|
||||
/* Check for a constructed packet to be sent. */
|
||||
if (qp->s_hdrwords != 0) {
|
||||
/*
|
||||
* If no PIO bufs are available, return. An interrupt will
|
||||
* call ipath_ib_piobufavail() when one is available.
|
||||
*/
|
||||
if (ipath_verbs_send(qp, &qp->s_hdr, qp->s_hdrwords,
|
||||
qp->s_cur_sge, qp->s_cur_size)) {
|
||||
if (ipath_no_bufs_available(qp, dev))
|
||||
goto bail;
|
||||
}
|
||||
dev->n_unicast_xmit++;
|
||||
/* Record that we sent the packet and s_hdr is empty. */
|
||||
qp->s_hdrwords = 0;
|
||||
}
|
||||
|
||||
if (make_req(qp))
|
||||
goto again;
|
||||
|
||||
bail:;
|
||||
}
|
||||
|
||||
/*
|
||||
* This should be called with s_lock held.
|
||||
*/
|
||||
void ipath_send_complete(struct ipath_qp *qp, struct ipath_swqe *wqe,
|
||||
enum ib_wc_status status)
|
||||
{
|
||||
u32 old_last, last;
|
||||
|
||||
if (!(ib_ipath_state_ops[qp->state] & IPATH_PROCESS_OR_FLUSH_SEND))
|
||||
return;
|
||||
|
||||
/* See ch. 11.2.4.1 and 10.7.3.1 */
|
||||
if (!(qp->s_flags & IPATH_S_SIGNAL_REQ_WR) ||
|
||||
(wqe->wr.send_flags & IB_SEND_SIGNALED) ||
|
||||
status != IB_WC_SUCCESS) {
|
||||
struct ib_wc wc;
|
||||
|
||||
memset(&wc, 0, sizeof wc);
|
||||
wc.wr_id = wqe->wr.wr_id;
|
||||
wc.status = status;
|
||||
wc.opcode = ib_ipath_wc_opcode[wqe->wr.opcode];
|
||||
wc.qp = &qp->ibqp;
|
||||
if (status == IB_WC_SUCCESS)
|
||||
wc.byte_len = wqe->length;
|
||||
ipath_cq_enter(to_icq(qp->ibqp.send_cq), &wc,
|
||||
status != IB_WC_SUCCESS);
|
||||
}
|
||||
|
||||
old_last = last = qp->s_last;
|
||||
if (++last >= qp->s_size)
|
||||
last = 0;
|
||||
qp->s_last = last;
|
||||
if (qp->s_cur == old_last)
|
||||
qp->s_cur = last;
|
||||
if (qp->s_tail == old_last)
|
||||
qp->s_tail = last;
|
||||
if (qp->state == IB_QPS_SQD && last == qp->s_cur)
|
||||
qp->s_draining = 0;
|
||||
}
|
|
@ -1,818 +0,0 @@
|
|||
/*
|
||||
* Copyright (c) 2007, 2008 QLogic Corporation. 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.
|
||||
*/
|
||||
|
||||
#include <linux/spinlock.h>
|
||||
#include <linux/gfp.h>
|
||||
|
||||
#include "ipath_kernel.h"
|
||||
#include "ipath_verbs.h"
|
||||
#include "ipath_common.h"
|
||||
|
||||
#define SDMA_DESCQ_SZ PAGE_SIZE /* 256 entries per 4KB page */
|
||||
|
||||
static void vl15_watchdog_enq(struct ipath_devdata *dd)
|
||||
{
|
||||
/* ipath_sdma_lock must already be held */
|
||||
if (atomic_inc_return(&dd->ipath_sdma_vl15_count) == 1) {
|
||||
unsigned long interval = (HZ + 19) / 20;
|
||||
dd->ipath_sdma_vl15_timer.expires = jiffies + interval;
|
||||
add_timer(&dd->ipath_sdma_vl15_timer);
|
||||
}
|
||||
}
|
||||
|
||||
static void vl15_watchdog_deq(struct ipath_devdata *dd)
|
||||
{
|
||||
/* ipath_sdma_lock must already be held */
|
||||
if (atomic_dec_return(&dd->ipath_sdma_vl15_count) != 0) {
|
||||
unsigned long interval = (HZ + 19) / 20;
|
||||
mod_timer(&dd->ipath_sdma_vl15_timer, jiffies + interval);
|
||||
} else {
|
||||
del_timer(&dd->ipath_sdma_vl15_timer);
|
||||
}
|
||||
}
|
||||
|
||||
static void vl15_watchdog_timeout(unsigned long opaque)
|
||||
{
|
||||
struct ipath_devdata *dd = (struct ipath_devdata *)opaque;
|
||||
|
||||
if (atomic_read(&dd->ipath_sdma_vl15_count) != 0) {
|
||||
ipath_dbg("vl15 watchdog timeout - clearing\n");
|
||||
ipath_cancel_sends(dd, 1);
|
||||
ipath_hol_down(dd);
|
||||
} else {
|
||||
ipath_dbg("vl15 watchdog timeout - "
|
||||
"condition already cleared\n");
|
||||
}
|
||||
}
|
||||
|
||||
static void unmap_desc(struct ipath_devdata *dd, unsigned head)
|
||||
{
|
||||
__le64 *descqp = &dd->ipath_sdma_descq[head].qw[0];
|
||||
u64 desc[2];
|
||||
dma_addr_t addr;
|
||||
size_t len;
|
||||
|
||||
desc[0] = le64_to_cpu(descqp[0]);
|
||||
desc[1] = le64_to_cpu(descqp[1]);
|
||||
|
||||
addr = (desc[1] << 32) | (desc[0] >> 32);
|
||||
len = (desc[0] >> 14) & (0x7ffULL << 2);
|
||||
dma_unmap_single(&dd->pcidev->dev, addr, len, DMA_TO_DEVICE);
|
||||
}
|
||||
|
||||
/*
|
||||
* ipath_sdma_lock should be locked before calling this.
|
||||
*/
|
||||
int ipath_sdma_make_progress(struct ipath_devdata *dd)
|
||||
{
|
||||
struct list_head *lp = NULL;
|
||||
struct ipath_sdma_txreq *txp = NULL;
|
||||
u16 dmahead;
|
||||
u16 start_idx = 0;
|
||||
int progress = 0;
|
||||
|
||||
if (!list_empty(&dd->ipath_sdma_activelist)) {
|
||||
lp = dd->ipath_sdma_activelist.next;
|
||||
txp = list_entry(lp, struct ipath_sdma_txreq, list);
|
||||
start_idx = txp->start_idx;
|
||||
}
|
||||
|
||||
/*
|
||||
* Read the SDMA head register in order to know that the
|
||||
* interrupt clear has been written to the chip.
|
||||
* Otherwise, we may not get an interrupt for the last
|
||||
* descriptor in the queue.
|
||||
*/
|
||||
dmahead = (u16)ipath_read_kreg32(dd, dd->ipath_kregs->kr_senddmahead);
|
||||
/* sanity check return value for error handling (chip reset, etc.) */
|
||||
if (dmahead >= dd->ipath_sdma_descq_cnt)
|
||||
goto done;
|
||||
|
||||
while (dd->ipath_sdma_descq_head != dmahead) {
|
||||
if (txp && txp->flags & IPATH_SDMA_TXREQ_F_FREEDESC &&
|
||||
dd->ipath_sdma_descq_head == start_idx) {
|
||||
unmap_desc(dd, dd->ipath_sdma_descq_head);
|
||||
start_idx++;
|
||||
if (start_idx == dd->ipath_sdma_descq_cnt)
|
||||
start_idx = 0;
|
||||
}
|
||||
|
||||
/* increment free count and head */
|
||||
dd->ipath_sdma_descq_removed++;
|
||||
if (++dd->ipath_sdma_descq_head == dd->ipath_sdma_descq_cnt)
|
||||
dd->ipath_sdma_descq_head = 0;
|
||||
|
||||
if (txp && txp->next_descq_idx == dd->ipath_sdma_descq_head) {
|
||||
/* move to notify list */
|
||||
if (txp->flags & IPATH_SDMA_TXREQ_F_VL15)
|
||||
vl15_watchdog_deq(dd);
|
||||
list_move_tail(lp, &dd->ipath_sdma_notifylist);
|
||||
if (!list_empty(&dd->ipath_sdma_activelist)) {
|
||||
lp = dd->ipath_sdma_activelist.next;
|
||||
txp = list_entry(lp, struct ipath_sdma_txreq,
|
||||
list);
|
||||
start_idx = txp->start_idx;
|
||||
} else {
|
||||
lp = NULL;
|
||||
txp = NULL;
|
||||
}
|
||||
}
|
||||
progress = 1;
|
||||
}
|
||||
|
||||
if (progress)
|
||||
tasklet_hi_schedule(&dd->ipath_sdma_notify_task);
|
||||
|
||||
done:
|
||||
return progress;
|
||||
}
|
||||
|
||||
static void ipath_sdma_notify(struct ipath_devdata *dd, struct list_head *list)
|
||||
{
|
||||
struct ipath_sdma_txreq *txp, *txp_next;
|
||||
|
||||
list_for_each_entry_safe(txp, txp_next, list, list) {
|
||||
list_del_init(&txp->list);
|
||||
|
||||
if (txp->callback)
|
||||
(*txp->callback)(txp->callback_cookie,
|
||||
txp->callback_status);
|
||||
}
|
||||
}
|
||||
|
||||
static void sdma_notify_taskbody(struct ipath_devdata *dd)
|
||||
{
|
||||
unsigned long flags;
|
||||
struct list_head list;
|
||||
|
||||
INIT_LIST_HEAD(&list);
|
||||
|
||||
spin_lock_irqsave(&dd->ipath_sdma_lock, flags);
|
||||
|
||||
list_splice_init(&dd->ipath_sdma_notifylist, &list);
|
||||
|
||||
spin_unlock_irqrestore(&dd->ipath_sdma_lock, flags);
|
||||
|
||||
ipath_sdma_notify(dd, &list);
|
||||
|
||||
/*
|
||||
* The IB verbs layer needs to see the callback before getting
|
||||
* the call to ipath_ib_piobufavail() because the callback
|
||||
* handles releasing resources the next send will need.
|
||||
* Otherwise, we could do these calls in
|
||||
* ipath_sdma_make_progress().
|
||||
*/
|
||||
ipath_ib_piobufavail(dd->verbs_dev);
|
||||
}
|
||||
|
||||
static void sdma_notify_task(unsigned long opaque)
|
||||
{
|
||||
struct ipath_devdata *dd = (struct ipath_devdata *)opaque;
|
||||
|
||||
if (!test_bit(IPATH_SDMA_SHUTDOWN, &dd->ipath_sdma_status))
|
||||
sdma_notify_taskbody(dd);
|
||||
}
|
||||
|
||||
static void dump_sdma_state(struct ipath_devdata *dd)
|
||||
{
|
||||
unsigned long reg;
|
||||
|
||||
reg = ipath_read_kreg64(dd, dd->ipath_kregs->kr_senddmastatus);
|
||||
ipath_cdbg(VERBOSE, "kr_senddmastatus: 0x%016lx\n", reg);
|
||||
|
||||
reg = ipath_read_kreg64(dd, dd->ipath_kregs->kr_sendctrl);
|
||||
ipath_cdbg(VERBOSE, "kr_sendctrl: 0x%016lx\n", reg);
|
||||
|
||||
reg = ipath_read_kreg64(dd, dd->ipath_kregs->kr_senddmabufmask0);
|
||||
ipath_cdbg(VERBOSE, "kr_senddmabufmask0: 0x%016lx\n", reg);
|
||||
|
||||
reg = ipath_read_kreg64(dd, dd->ipath_kregs->kr_senddmabufmask1);
|
||||
ipath_cdbg(VERBOSE, "kr_senddmabufmask1: 0x%016lx\n", reg);
|
||||
|
||||
reg = ipath_read_kreg64(dd, dd->ipath_kregs->kr_senddmabufmask2);
|
||||
ipath_cdbg(VERBOSE, "kr_senddmabufmask2: 0x%016lx\n", reg);
|
||||
|
||||
reg = ipath_read_kreg64(dd, dd->ipath_kregs->kr_senddmatail);
|
||||
ipath_cdbg(VERBOSE, "kr_senddmatail: 0x%016lx\n", reg);
|
||||
|
||||
reg = ipath_read_kreg64(dd, dd->ipath_kregs->kr_senddmahead);
|
||||
ipath_cdbg(VERBOSE, "kr_senddmahead: 0x%016lx\n", reg);
|
||||
}
|
||||
|
||||
static void sdma_abort_task(unsigned long opaque)
|
||||
{
|
||||
struct ipath_devdata *dd = (struct ipath_devdata *) opaque;
|
||||
u64 status;
|
||||
unsigned long flags;
|
||||
|
||||
if (test_bit(IPATH_SDMA_SHUTDOWN, &dd->ipath_sdma_status))
|
||||
return;
|
||||
|
||||
spin_lock_irqsave(&dd->ipath_sdma_lock, flags);
|
||||
|
||||
status = dd->ipath_sdma_status & IPATH_SDMA_ABORT_MASK;
|
||||
|
||||
/* nothing to do */
|
||||
if (status == IPATH_SDMA_ABORT_NONE)
|
||||
goto unlock;
|
||||
|
||||
/* ipath_sdma_abort() is done, waiting for interrupt */
|
||||
if (status == IPATH_SDMA_ABORT_DISARMED) {
|
||||
if (time_before(jiffies, dd->ipath_sdma_abort_intr_timeout))
|
||||
goto resched_noprint;
|
||||
/* give up, intr got lost somewhere */
|
||||
ipath_dbg("give up waiting for SDMADISABLED intr\n");
|
||||
__set_bit(IPATH_SDMA_DISABLED, &dd->ipath_sdma_status);
|
||||
status = IPATH_SDMA_ABORT_ABORTED;
|
||||
}
|
||||
|
||||
/* everything is stopped, time to clean up and restart */
|
||||
if (status == IPATH_SDMA_ABORT_ABORTED) {
|
||||
struct ipath_sdma_txreq *txp, *txpnext;
|
||||
u64 hwstatus;
|
||||
int notify = 0;
|
||||
|
||||
hwstatus = ipath_read_kreg64(dd,
|
||||
dd->ipath_kregs->kr_senddmastatus);
|
||||
|
||||
if ((hwstatus & (IPATH_SDMA_STATUS_SCORE_BOARD_DRAIN_IN_PROG |
|
||||
IPATH_SDMA_STATUS_ABORT_IN_PROG |
|
||||
IPATH_SDMA_STATUS_INTERNAL_SDMA_ENABLE)) ||
|
||||
!(hwstatus & IPATH_SDMA_STATUS_SCB_EMPTY)) {
|
||||
if (dd->ipath_sdma_reset_wait > 0) {
|
||||
/* not done shutting down sdma */
|
||||
--dd->ipath_sdma_reset_wait;
|
||||
goto resched;
|
||||
}
|
||||
ipath_cdbg(VERBOSE, "gave up waiting for quiescent "
|
||||
"status after SDMA reset, continuing\n");
|
||||
dump_sdma_state(dd);
|
||||
}
|
||||
|
||||
/* dequeue all "sent" requests */
|
||||
list_for_each_entry_safe(txp, txpnext,
|
||||
&dd->ipath_sdma_activelist, list) {
|
||||
txp->callback_status = IPATH_SDMA_TXREQ_S_ABORTED;
|
||||
if (txp->flags & IPATH_SDMA_TXREQ_F_VL15)
|
||||
vl15_watchdog_deq(dd);
|
||||
list_move_tail(&txp->list, &dd->ipath_sdma_notifylist);
|
||||
notify = 1;
|
||||
}
|
||||
if (notify)
|
||||
tasklet_hi_schedule(&dd->ipath_sdma_notify_task);
|
||||
|
||||
/* reset our notion of head and tail */
|
||||
dd->ipath_sdma_descq_tail = 0;
|
||||
dd->ipath_sdma_descq_head = 0;
|
||||
dd->ipath_sdma_head_dma[0] = 0;
|
||||
dd->ipath_sdma_generation = 0;
|
||||
dd->ipath_sdma_descq_removed = dd->ipath_sdma_descq_added;
|
||||
|
||||
/* Reset SendDmaLenGen */
|
||||
ipath_write_kreg(dd, dd->ipath_kregs->kr_senddmalengen,
|
||||
(u64) dd->ipath_sdma_descq_cnt | (1ULL << 18));
|
||||
|
||||
/* done with sdma state for a bit */
|
||||
spin_unlock_irqrestore(&dd->ipath_sdma_lock, flags);
|
||||
|
||||
/*
|
||||
* Don't restart sdma here (with the exception
|
||||
* below). Wait until link is up to ACTIVE. VL15 MADs
|
||||
* used to bring the link up use PIO, and multiple link
|
||||
* transitions otherwise cause the sdma engine to be
|
||||
* stopped and started multiple times.
|
||||
* The disable is done here, including the shadow,
|
||||
* so the state is kept consistent.
|
||||
* See ipath_restart_sdma() for the actual starting
|
||||
* of sdma.
|
||||
*/
|
||||
spin_lock_irqsave(&dd->ipath_sendctrl_lock, flags);
|
||||
dd->ipath_sendctrl &= ~INFINIPATH_S_SDMAENABLE;
|
||||
ipath_write_kreg(dd, dd->ipath_kregs->kr_sendctrl,
|
||||
dd->ipath_sendctrl);
|
||||
ipath_read_kreg64(dd, dd->ipath_kregs->kr_scratch);
|
||||
spin_unlock_irqrestore(&dd->ipath_sendctrl_lock, flags);
|
||||
|
||||
/* make sure I see next message */
|
||||
dd->ipath_sdma_abort_jiffies = 0;
|
||||
|
||||
/*
|
||||
* Not everything that takes SDMA offline is a link
|
||||
* status change. If the link was up, restart SDMA.
|
||||
*/
|
||||
if (dd->ipath_flags & IPATH_LINKACTIVE)
|
||||
ipath_restart_sdma(dd);
|
||||
|
||||
goto done;
|
||||
}
|
||||
|
||||
resched:
|
||||
/*
|
||||
* for now, keep spinning
|
||||
* JAG - this is bad to just have default be a loop without
|
||||
* state change
|
||||
*/
|
||||
if (time_after(jiffies, dd->ipath_sdma_abort_jiffies)) {
|
||||
ipath_dbg("looping with status 0x%08lx\n",
|
||||
dd->ipath_sdma_status);
|
||||
dd->ipath_sdma_abort_jiffies = jiffies + 5 * HZ;
|
||||
}
|
||||
resched_noprint:
|
||||
spin_unlock_irqrestore(&dd->ipath_sdma_lock, flags);
|
||||
if (!test_bit(IPATH_SDMA_SHUTDOWN, &dd->ipath_sdma_status))
|
||||
tasklet_hi_schedule(&dd->ipath_sdma_abort_task);
|
||||
return;
|
||||
|
||||
unlock:
|
||||
spin_unlock_irqrestore(&dd->ipath_sdma_lock, flags);
|
||||
done:
|
||||
return;
|
||||
}
|
||||
|
||||
/*
|
||||
* This is called from interrupt context.
|
||||
*/
|
||||
void ipath_sdma_intr(struct ipath_devdata *dd)
|
||||
{
|
||||
unsigned long flags;
|
||||
|
||||
spin_lock_irqsave(&dd->ipath_sdma_lock, flags);
|
||||
|
||||
(void) ipath_sdma_make_progress(dd);
|
||||
|
||||
spin_unlock_irqrestore(&dd->ipath_sdma_lock, flags);
|
||||
}
|
||||
|
||||
static int alloc_sdma(struct ipath_devdata *dd)
|
||||
{
|
||||
int ret = 0;
|
||||
|
||||
/* Allocate memory for SendDMA descriptor FIFO */
|
||||
dd->ipath_sdma_descq = dma_alloc_coherent(&dd->pcidev->dev,
|
||||
SDMA_DESCQ_SZ, &dd->ipath_sdma_descq_phys, GFP_KERNEL);
|
||||
|
||||
if (!dd->ipath_sdma_descq) {
|
||||
ipath_dev_err(dd, "failed to allocate SendDMA descriptor "
|
||||
"FIFO memory\n");
|
||||
ret = -ENOMEM;
|
||||
goto done;
|
||||
}
|
||||
|
||||
dd->ipath_sdma_descq_cnt =
|
||||
SDMA_DESCQ_SZ / sizeof(struct ipath_sdma_desc);
|
||||
|
||||
/* Allocate memory for DMA of head register to memory */
|
||||
dd->ipath_sdma_head_dma = dma_alloc_coherent(&dd->pcidev->dev,
|
||||
PAGE_SIZE, &dd->ipath_sdma_head_phys, GFP_KERNEL);
|
||||
if (!dd->ipath_sdma_head_dma) {
|
||||
ipath_dev_err(dd, "failed to allocate SendDMA head memory\n");
|
||||
ret = -ENOMEM;
|
||||
goto cleanup_descq;
|
||||
}
|
||||
dd->ipath_sdma_head_dma[0] = 0;
|
||||
|
||||
setup_timer(&dd->ipath_sdma_vl15_timer, vl15_watchdog_timeout,
|
||||
(unsigned long)dd);
|
||||
|
||||
atomic_set(&dd->ipath_sdma_vl15_count, 0);
|
||||
|
||||
goto done;
|
||||
|
||||
cleanup_descq:
|
||||
dma_free_coherent(&dd->pcidev->dev, SDMA_DESCQ_SZ,
|
||||
(void *)dd->ipath_sdma_descq, dd->ipath_sdma_descq_phys);
|
||||
dd->ipath_sdma_descq = NULL;
|
||||
dd->ipath_sdma_descq_phys = 0;
|
||||
done:
|
||||
return ret;
|
||||
}
|
||||
|
||||
int setup_sdma(struct ipath_devdata *dd)
|
||||
{
|
||||
int ret = 0;
|
||||
unsigned i, n;
|
||||
u64 tmp64;
|
||||
u64 senddmabufmask[3] = { 0 };
|
||||
unsigned long flags;
|
||||
|
||||
ret = alloc_sdma(dd);
|
||||
if (ret)
|
||||
goto done;
|
||||
|
||||
if (!dd->ipath_sdma_descq) {
|
||||
ipath_dev_err(dd, "SendDMA memory not allocated\n");
|
||||
goto done;
|
||||
}
|
||||
|
||||
/*
|
||||
* Set initial status as if we had been up, then gone down.
|
||||
* This lets initial start on transition to ACTIVE be the
|
||||
* same as restart after link flap.
|
||||
*/
|
||||
dd->ipath_sdma_status = IPATH_SDMA_ABORT_ABORTED;
|
||||
dd->ipath_sdma_abort_jiffies = 0;
|
||||
dd->ipath_sdma_generation = 0;
|
||||
dd->ipath_sdma_descq_tail = 0;
|
||||
dd->ipath_sdma_descq_head = 0;
|
||||
dd->ipath_sdma_descq_removed = 0;
|
||||
dd->ipath_sdma_descq_added = 0;
|
||||
|
||||
/* Set SendDmaBase */
|
||||
ipath_write_kreg(dd, dd->ipath_kregs->kr_senddmabase,
|
||||
dd->ipath_sdma_descq_phys);
|
||||
/* Set SendDmaLenGen */
|
||||
tmp64 = dd->ipath_sdma_descq_cnt;
|
||||
tmp64 |= 1<<18; /* enable generation checking */
|
||||
ipath_write_kreg(dd, dd->ipath_kregs->kr_senddmalengen, tmp64);
|
||||
/* Set SendDmaTail */
|
||||
ipath_write_kreg(dd, dd->ipath_kregs->kr_senddmatail,
|
||||
dd->ipath_sdma_descq_tail);
|
||||
/* Set SendDmaHeadAddr */
|
||||
ipath_write_kreg(dd, dd->ipath_kregs->kr_senddmaheadaddr,
|
||||
dd->ipath_sdma_head_phys);
|
||||
|
||||
/*
|
||||
* Reserve all the former "kernel" piobufs, using high number range
|
||||
* so we get as many 4K buffers as possible
|
||||
*/
|
||||
n = dd->ipath_piobcnt2k + dd->ipath_piobcnt4k;
|
||||
i = dd->ipath_lastport_piobuf + dd->ipath_pioreserved;
|
||||
ipath_chg_pioavailkernel(dd, i, n - i , 0);
|
||||
for (; i < n; ++i) {
|
||||
unsigned word = i / 64;
|
||||
unsigned bit = i & 63;
|
||||
BUG_ON(word >= 3);
|
||||
senddmabufmask[word] |= 1ULL << bit;
|
||||
}
|
||||
ipath_write_kreg(dd, dd->ipath_kregs->kr_senddmabufmask0,
|
||||
senddmabufmask[0]);
|
||||
ipath_write_kreg(dd, dd->ipath_kregs->kr_senddmabufmask1,
|
||||
senddmabufmask[1]);
|
||||
ipath_write_kreg(dd, dd->ipath_kregs->kr_senddmabufmask2,
|
||||
senddmabufmask[2]);
|
||||
|
||||
INIT_LIST_HEAD(&dd->ipath_sdma_activelist);
|
||||
INIT_LIST_HEAD(&dd->ipath_sdma_notifylist);
|
||||
|
||||
tasklet_init(&dd->ipath_sdma_notify_task, sdma_notify_task,
|
||||
(unsigned long) dd);
|
||||
tasklet_init(&dd->ipath_sdma_abort_task, sdma_abort_task,
|
||||
(unsigned long) dd);
|
||||
|
||||
/*
|
||||
* No use to turn on SDMA here, as link is probably not ACTIVE
|
||||
* Just mark it RUNNING and enable the interrupt, and let the
|
||||
* ipath_restart_sdma() on link transition to ACTIVE actually
|
||||
* enable it.
|
||||
*/
|
||||
spin_lock_irqsave(&dd->ipath_sendctrl_lock, flags);
|
||||
dd->ipath_sendctrl |= INFINIPATH_S_SDMAINTENABLE;
|
||||
ipath_write_kreg(dd, dd->ipath_kregs->kr_sendctrl, dd->ipath_sendctrl);
|
||||
ipath_read_kreg64(dd, dd->ipath_kregs->kr_scratch);
|
||||
__set_bit(IPATH_SDMA_RUNNING, &dd->ipath_sdma_status);
|
||||
spin_unlock_irqrestore(&dd->ipath_sendctrl_lock, flags);
|
||||
|
||||
done:
|
||||
return ret;
|
||||
}
|
||||
|
||||
void teardown_sdma(struct ipath_devdata *dd)
|
||||
{
|
||||
struct ipath_sdma_txreq *txp, *txpnext;
|
||||
unsigned long flags;
|
||||
dma_addr_t sdma_head_phys = 0;
|
||||
dma_addr_t sdma_descq_phys = 0;
|
||||
void *sdma_descq = NULL;
|
||||
void *sdma_head_dma = NULL;
|
||||
|
||||
spin_lock_irqsave(&dd->ipath_sdma_lock, flags);
|
||||
__clear_bit(IPATH_SDMA_RUNNING, &dd->ipath_sdma_status);
|
||||
__set_bit(IPATH_SDMA_ABORTING, &dd->ipath_sdma_status);
|
||||
__set_bit(IPATH_SDMA_SHUTDOWN, &dd->ipath_sdma_status);
|
||||
spin_unlock_irqrestore(&dd->ipath_sdma_lock, flags);
|
||||
|
||||
tasklet_kill(&dd->ipath_sdma_abort_task);
|
||||
tasklet_kill(&dd->ipath_sdma_notify_task);
|
||||
|
||||
/* turn off sdma */
|
||||
spin_lock_irqsave(&dd->ipath_sendctrl_lock, flags);
|
||||
dd->ipath_sendctrl &= ~INFINIPATH_S_SDMAENABLE;
|
||||
ipath_write_kreg(dd, dd->ipath_kregs->kr_sendctrl,
|
||||
dd->ipath_sendctrl);
|
||||
ipath_read_kreg64(dd, dd->ipath_kregs->kr_scratch);
|
||||
spin_unlock_irqrestore(&dd->ipath_sendctrl_lock, flags);
|
||||
|
||||
spin_lock_irqsave(&dd->ipath_sdma_lock, flags);
|
||||
/* dequeue all "sent" requests */
|
||||
list_for_each_entry_safe(txp, txpnext, &dd->ipath_sdma_activelist,
|
||||
list) {
|
||||
txp->callback_status = IPATH_SDMA_TXREQ_S_SHUTDOWN;
|
||||
if (txp->flags & IPATH_SDMA_TXREQ_F_VL15)
|
||||
vl15_watchdog_deq(dd);
|
||||
list_move_tail(&txp->list, &dd->ipath_sdma_notifylist);
|
||||
}
|
||||
spin_unlock_irqrestore(&dd->ipath_sdma_lock, flags);
|
||||
|
||||
sdma_notify_taskbody(dd);
|
||||
|
||||
del_timer_sync(&dd->ipath_sdma_vl15_timer);
|
||||
|
||||
spin_lock_irqsave(&dd->ipath_sdma_lock, flags);
|
||||
|
||||
dd->ipath_sdma_abort_jiffies = 0;
|
||||
|
||||
ipath_write_kreg(dd, dd->ipath_kregs->kr_senddmabase, 0);
|
||||
ipath_write_kreg(dd, dd->ipath_kregs->kr_senddmalengen, 0);
|
||||
ipath_write_kreg(dd, dd->ipath_kregs->kr_senddmatail, 0);
|
||||
ipath_write_kreg(dd, dd->ipath_kregs->kr_senddmaheadaddr, 0);
|
||||
ipath_write_kreg(dd, dd->ipath_kregs->kr_senddmabufmask0, 0);
|
||||
ipath_write_kreg(dd, dd->ipath_kregs->kr_senddmabufmask1, 0);
|
||||
ipath_write_kreg(dd, dd->ipath_kregs->kr_senddmabufmask2, 0);
|
||||
|
||||
if (dd->ipath_sdma_head_dma) {
|
||||
sdma_head_dma = (void *) dd->ipath_sdma_head_dma;
|
||||
sdma_head_phys = dd->ipath_sdma_head_phys;
|
||||
dd->ipath_sdma_head_dma = NULL;
|
||||
dd->ipath_sdma_head_phys = 0;
|
||||
}
|
||||
|
||||
if (dd->ipath_sdma_descq) {
|
||||
sdma_descq = dd->ipath_sdma_descq;
|
||||
sdma_descq_phys = dd->ipath_sdma_descq_phys;
|
||||
dd->ipath_sdma_descq = NULL;
|
||||
dd->ipath_sdma_descq_phys = 0;
|
||||
}
|
||||
|
||||
spin_unlock_irqrestore(&dd->ipath_sdma_lock, flags);
|
||||
|
||||
if (sdma_head_dma)
|
||||
dma_free_coherent(&dd->pcidev->dev, PAGE_SIZE,
|
||||
sdma_head_dma, sdma_head_phys);
|
||||
|
||||
if (sdma_descq)
|
||||
dma_free_coherent(&dd->pcidev->dev, SDMA_DESCQ_SZ,
|
||||
sdma_descq, sdma_descq_phys);
|
||||
}
|
||||
|
||||
/*
|
||||
* [Re]start SDMA, if we use it, and it's not already OK.
|
||||
* This is called on transition to link ACTIVE, either the first or
|
||||
* subsequent times.
|
||||
*/
|
||||
void ipath_restart_sdma(struct ipath_devdata *dd)
|
||||
{
|
||||
unsigned long flags;
|
||||
int needed = 1;
|
||||
|
||||
if (!(dd->ipath_flags & IPATH_HAS_SEND_DMA))
|
||||
goto bail;
|
||||
|
||||
/*
|
||||
* First, make sure we should, which is to say,
|
||||
* check that we are "RUNNING" (not in teardown)
|
||||
* and not "SHUTDOWN"
|
||||
*/
|
||||
spin_lock_irqsave(&dd->ipath_sdma_lock, flags);
|
||||
if (!test_bit(IPATH_SDMA_RUNNING, &dd->ipath_sdma_status)
|
||||
|| test_bit(IPATH_SDMA_SHUTDOWN, &dd->ipath_sdma_status))
|
||||
needed = 0;
|
||||
else {
|
||||
__clear_bit(IPATH_SDMA_DISABLED, &dd->ipath_sdma_status);
|
||||
__clear_bit(IPATH_SDMA_DISARMED, &dd->ipath_sdma_status);
|
||||
__clear_bit(IPATH_SDMA_ABORTING, &dd->ipath_sdma_status);
|
||||
}
|
||||
spin_unlock_irqrestore(&dd->ipath_sdma_lock, flags);
|
||||
if (!needed) {
|
||||
ipath_dbg("invalid attempt to restart SDMA, status 0x%08lx\n",
|
||||
dd->ipath_sdma_status);
|
||||
goto bail;
|
||||
}
|
||||
spin_lock_irqsave(&dd->ipath_sendctrl_lock, flags);
|
||||
/*
|
||||
* First clear, just to be safe. Enable is only done
|
||||
* in chip on 0->1 transition
|
||||
*/
|
||||
dd->ipath_sendctrl &= ~INFINIPATH_S_SDMAENABLE;
|
||||
ipath_write_kreg(dd, dd->ipath_kregs->kr_sendctrl, dd->ipath_sendctrl);
|
||||
ipath_read_kreg64(dd, dd->ipath_kregs->kr_scratch);
|
||||
dd->ipath_sendctrl |= INFINIPATH_S_SDMAENABLE;
|
||||
ipath_write_kreg(dd, dd->ipath_kregs->kr_sendctrl, dd->ipath_sendctrl);
|
||||
ipath_read_kreg64(dd, dd->ipath_kregs->kr_scratch);
|
||||
spin_unlock_irqrestore(&dd->ipath_sendctrl_lock, flags);
|
||||
|
||||
/* notify upper layers */
|
||||
ipath_ib_piobufavail(dd->verbs_dev);
|
||||
|
||||
bail:
|
||||
return;
|
||||
}
|
||||
|
||||
static inline void make_sdma_desc(struct ipath_devdata *dd,
|
||||
u64 *sdmadesc, u64 addr, u64 dwlen, u64 dwoffset)
|
||||
{
|
||||
WARN_ON(addr & 3);
|
||||
/* SDmaPhyAddr[47:32] */
|
||||
sdmadesc[1] = addr >> 32;
|
||||
/* SDmaPhyAddr[31:0] */
|
||||
sdmadesc[0] = (addr & 0xfffffffcULL) << 32;
|
||||
/* SDmaGeneration[1:0] */
|
||||
sdmadesc[0] |= (dd->ipath_sdma_generation & 3ULL) << 30;
|
||||
/* SDmaDwordCount[10:0] */
|
||||
sdmadesc[0] |= (dwlen & 0x7ffULL) << 16;
|
||||
/* SDmaBufOffset[12:2] */
|
||||
sdmadesc[0] |= dwoffset & 0x7ffULL;
|
||||
}
|
||||
|
||||
/*
|
||||
* This function queues one IB packet onto the send DMA queue per call.
|
||||
* The caller is responsible for checking:
|
||||
* 1) The number of send DMA descriptor entries is less than the size of
|
||||
* the descriptor queue.
|
||||
* 2) The IB SGE addresses and lengths are 32-bit aligned
|
||||
* (except possibly the last SGE's length)
|
||||
* 3) The SGE addresses are suitable for passing to dma_map_single().
|
||||
*/
|
||||
int ipath_sdma_verbs_send(struct ipath_devdata *dd,
|
||||
struct ipath_sge_state *ss, u32 dwords,
|
||||
struct ipath_verbs_txreq *tx)
|
||||
{
|
||||
|
||||
unsigned long flags;
|
||||
struct ipath_sge *sge;
|
||||
int ret = 0;
|
||||
u16 tail;
|
||||
__le64 *descqp;
|
||||
u64 sdmadesc[2];
|
||||
u32 dwoffset;
|
||||
dma_addr_t addr;
|
||||
|
||||
if ((tx->map_len + (dwords<<2)) > dd->ipath_ibmaxlen) {
|
||||
ipath_dbg("packet size %X > ibmax %X, fail\n",
|
||||
tx->map_len + (dwords<<2), dd->ipath_ibmaxlen);
|
||||
ret = -EMSGSIZE;
|
||||
goto fail;
|
||||
}
|
||||
|
||||
spin_lock_irqsave(&dd->ipath_sdma_lock, flags);
|
||||
|
||||
retry:
|
||||
if (unlikely(test_bit(IPATH_SDMA_ABORTING, &dd->ipath_sdma_status))) {
|
||||
ret = -EBUSY;
|
||||
goto unlock;
|
||||
}
|
||||
|
||||
if (tx->txreq.sg_count > ipath_sdma_descq_freecnt(dd)) {
|
||||
if (ipath_sdma_make_progress(dd))
|
||||
goto retry;
|
||||
ret = -ENOBUFS;
|
||||
goto unlock;
|
||||
}
|
||||
|
||||
addr = dma_map_single(&dd->pcidev->dev, tx->txreq.map_addr,
|
||||
tx->map_len, DMA_TO_DEVICE);
|
||||
if (dma_mapping_error(&dd->pcidev->dev, addr))
|
||||
goto ioerr;
|
||||
|
||||
dwoffset = tx->map_len >> 2;
|
||||
make_sdma_desc(dd, sdmadesc, (u64) addr, dwoffset, 0);
|
||||
|
||||
/* SDmaFirstDesc */
|
||||
sdmadesc[0] |= 1ULL << 12;
|
||||
if (tx->txreq.flags & IPATH_SDMA_TXREQ_F_USELARGEBUF)
|
||||
sdmadesc[0] |= 1ULL << 14; /* SDmaUseLargeBuf */
|
||||
|
||||
/* write to the descq */
|
||||
tail = dd->ipath_sdma_descq_tail;
|
||||
descqp = &dd->ipath_sdma_descq[tail].qw[0];
|
||||
*descqp++ = cpu_to_le64(sdmadesc[0]);
|
||||
*descqp++ = cpu_to_le64(sdmadesc[1]);
|
||||
|
||||
if (tx->txreq.flags & IPATH_SDMA_TXREQ_F_FREEDESC)
|
||||
tx->txreq.start_idx = tail;
|
||||
|
||||
/* increment the tail */
|
||||
if (++tail == dd->ipath_sdma_descq_cnt) {
|
||||
tail = 0;
|
||||
descqp = &dd->ipath_sdma_descq[0].qw[0];
|
||||
++dd->ipath_sdma_generation;
|
||||
}
|
||||
|
||||
sge = &ss->sge;
|
||||
while (dwords) {
|
||||
u32 dw;
|
||||
u32 len;
|
||||
|
||||
len = dwords << 2;
|
||||
if (len > sge->length)
|
||||
len = sge->length;
|
||||
if (len > sge->sge_length)
|
||||
len = sge->sge_length;
|
||||
BUG_ON(len == 0);
|
||||
dw = (len + 3) >> 2;
|
||||
addr = dma_map_single(&dd->pcidev->dev, sge->vaddr, dw << 2,
|
||||
DMA_TO_DEVICE);
|
||||
if (dma_mapping_error(&dd->pcidev->dev, addr))
|
||||
goto unmap;
|
||||
make_sdma_desc(dd, sdmadesc, (u64) addr, dw, dwoffset);
|
||||
/* SDmaUseLargeBuf has to be set in every descriptor */
|
||||
if (tx->txreq.flags & IPATH_SDMA_TXREQ_F_USELARGEBUF)
|
||||
sdmadesc[0] |= 1ULL << 14;
|
||||
/* write to the descq */
|
||||
*descqp++ = cpu_to_le64(sdmadesc[0]);
|
||||
*descqp++ = cpu_to_le64(sdmadesc[1]);
|
||||
|
||||
/* increment the tail */
|
||||
if (++tail == dd->ipath_sdma_descq_cnt) {
|
||||
tail = 0;
|
||||
descqp = &dd->ipath_sdma_descq[0].qw[0];
|
||||
++dd->ipath_sdma_generation;
|
||||
}
|
||||
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;
|
||||
}
|
||||
|
||||
dwoffset += dw;
|
||||
dwords -= dw;
|
||||
}
|
||||
|
||||
if (!tail)
|
||||
descqp = &dd->ipath_sdma_descq[dd->ipath_sdma_descq_cnt].qw[0];
|
||||
descqp -= 2;
|
||||
/* SDmaLastDesc */
|
||||
descqp[0] |= cpu_to_le64(1ULL << 11);
|
||||
if (tx->txreq.flags & IPATH_SDMA_TXREQ_F_INTREQ) {
|
||||
/* SDmaIntReq */
|
||||
descqp[0] |= cpu_to_le64(1ULL << 15);
|
||||
}
|
||||
|
||||
/* Commit writes to memory and advance the tail on the chip */
|
||||
wmb();
|
||||
ipath_write_kreg(dd, dd->ipath_kregs->kr_senddmatail, tail);
|
||||
|
||||
tx->txreq.next_descq_idx = tail;
|
||||
tx->txreq.callback_status = IPATH_SDMA_TXREQ_S_OK;
|
||||
dd->ipath_sdma_descq_tail = tail;
|
||||
dd->ipath_sdma_descq_added += tx->txreq.sg_count;
|
||||
list_add_tail(&tx->txreq.list, &dd->ipath_sdma_activelist);
|
||||
if (tx->txreq.flags & IPATH_SDMA_TXREQ_F_VL15)
|
||||
vl15_watchdog_enq(dd);
|
||||
goto unlock;
|
||||
|
||||
unmap:
|
||||
while (tail != dd->ipath_sdma_descq_tail) {
|
||||
if (!tail)
|
||||
tail = dd->ipath_sdma_descq_cnt - 1;
|
||||
else
|
||||
tail--;
|
||||
unmap_desc(dd, tail);
|
||||
}
|
||||
ioerr:
|
||||
ret = -EIO;
|
||||
unlock:
|
||||
spin_unlock_irqrestore(&dd->ipath_sdma_lock, flags);
|
||||
fail:
|
||||
return ret;
|
||||
}
|
|
@ -1,380 +0,0 @@
|
|||
/*
|
||||
* Copyright (c) 2006, 2007, 2008 QLogic Corporation. All rights reserved.
|
||||
* Copyright (c) 2005, 2006 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.
|
||||
*/
|
||||
|
||||
#include <linux/err.h>
|
||||
#include <linux/slab.h>
|
||||
#include <linux/vmalloc.h>
|
||||
|
||||
#include "ipath_verbs.h"
|
||||
|
||||
/**
|
||||
* ipath_post_srq_receive - post a receive on a shared receive queue
|
||||
* @ibsrq: the SRQ to post the receive on
|
||||
* @wr: the list of work requests to post
|
||||
* @bad_wr: the first WR to cause a problem is put here
|
||||
*
|
||||
* This may be called from interrupt context.
|
||||
*/
|
||||
int ipath_post_srq_receive(struct ib_srq *ibsrq, struct ib_recv_wr *wr,
|
||||
struct ib_recv_wr **bad_wr)
|
||||
{
|
||||
struct ipath_srq *srq = to_isrq(ibsrq);
|
||||
struct ipath_rwq *wq;
|
||||
unsigned long flags;
|
||||
int ret;
|
||||
|
||||
for (; wr; wr = wr->next) {
|
||||
struct ipath_rwqe *wqe;
|
||||
u32 next;
|
||||
int i;
|
||||
|
||||
if ((unsigned) wr->num_sge > srq->rq.max_sge) {
|
||||
*bad_wr = wr;
|
||||
ret = -EINVAL;
|
||||
goto bail;
|
||||
}
|
||||
|
||||
spin_lock_irqsave(&srq->rq.lock, flags);
|
||||
wq = srq->rq.wq;
|
||||
next = wq->head + 1;
|
||||
if (next >= srq->rq.size)
|
||||
next = 0;
|
||||
if (next == wq->tail) {
|
||||
spin_unlock_irqrestore(&srq->rq.lock, flags);
|
||||
*bad_wr = wr;
|
||||
ret = -ENOMEM;
|
||||
goto bail;
|
||||
}
|
||||
|
||||
wqe = get_rwqe_ptr(&srq->rq, wq->head);
|
||||
wqe->wr_id = wr->wr_id;
|
||||
wqe->num_sge = wr->num_sge;
|
||||
for (i = 0; i < wr->num_sge; i++)
|
||||
wqe->sg_list[i] = wr->sg_list[i];
|
||||
/* Make sure queue entry is written before the head index. */
|
||||
smp_wmb();
|
||||
wq->head = next;
|
||||
spin_unlock_irqrestore(&srq->rq.lock, flags);
|
||||
}
|
||||
ret = 0;
|
||||
|
||||
bail:
|
||||
return ret;
|
||||
}
|
||||
|
||||
/**
|
||||
* ipath_create_srq - create a shared receive queue
|
||||
* @ibpd: the protection domain of the SRQ to create
|
||||
* @srq_init_attr: the attributes of the SRQ
|
||||
* @udata: data from libipathverbs when creating a user SRQ
|
||||
*/
|
||||
struct ib_srq *ipath_create_srq(struct ib_pd *ibpd,
|
||||
struct ib_srq_init_attr *srq_init_attr,
|
||||
struct ib_udata *udata)
|
||||
{
|
||||
struct ipath_ibdev *dev = to_idev(ibpd->device);
|
||||
struct ipath_srq *srq;
|
||||
u32 sz;
|
||||
struct ib_srq *ret;
|
||||
|
||||
if (srq_init_attr->srq_type != IB_SRQT_BASIC) {
|
||||
ret = ERR_PTR(-ENOSYS);
|
||||
goto done;
|
||||
}
|
||||
|
||||
if (srq_init_attr->attr.max_wr == 0) {
|
||||
ret = ERR_PTR(-EINVAL);
|
||||
goto done;
|
||||
}
|
||||
|
||||
if ((srq_init_attr->attr.max_sge > ib_ipath_max_srq_sges) ||
|
||||
(srq_init_attr->attr.max_wr > ib_ipath_max_srq_wrs)) {
|
||||
ret = ERR_PTR(-EINVAL);
|
||||
goto done;
|
||||
}
|
||||
|
||||
srq = kmalloc(sizeof(*srq), GFP_KERNEL);
|
||||
if (!srq) {
|
||||
ret = ERR_PTR(-ENOMEM);
|
||||
goto done;
|
||||
}
|
||||
|
||||
/*
|
||||
* Need to use vmalloc() if we want to support large #s of entries.
|
||||
*/
|
||||
srq->rq.size = srq_init_attr->attr.max_wr + 1;
|
||||
srq->rq.max_sge = srq_init_attr->attr.max_sge;
|
||||
sz = sizeof(struct ib_sge) * srq->rq.max_sge +
|
||||
sizeof(struct ipath_rwqe);
|
||||
srq->rq.wq = vmalloc_user(sizeof(struct ipath_rwq) + srq->rq.size * sz);
|
||||
if (!srq->rq.wq) {
|
||||
ret = ERR_PTR(-ENOMEM);
|
||||
goto bail_srq;
|
||||
}
|
||||
|
||||
/*
|
||||
* Return the address of the RWQ as the offset to mmap.
|
||||
* See ipath_mmap() for details.
|
||||
*/
|
||||
if (udata && udata->outlen >= sizeof(__u64)) {
|
||||
int err;
|
||||
u32 s = sizeof(struct ipath_rwq) + srq->rq.size * sz;
|
||||
|
||||
srq->ip =
|
||||
ipath_create_mmap_info(dev, s,
|
||||
ibpd->uobject->context,
|
||||
srq->rq.wq);
|
||||
if (!srq->ip) {
|
||||
ret = ERR_PTR(-ENOMEM);
|
||||
goto bail_wq;
|
||||
}
|
||||
|
||||
err = ib_copy_to_udata(udata, &srq->ip->offset,
|
||||
sizeof(srq->ip->offset));
|
||||
if (err) {
|
||||
ret = ERR_PTR(err);
|
||||
goto bail_ip;
|
||||
}
|
||||
} else
|
||||
srq->ip = NULL;
|
||||
|
||||
/*
|
||||
* ib_create_srq() will initialize srq->ibsrq.
|
||||
*/
|
||||
spin_lock_init(&srq->rq.lock);
|
||||
srq->rq.wq->head = 0;
|
||||
srq->rq.wq->tail = 0;
|
||||
srq->limit = srq_init_attr->attr.srq_limit;
|
||||
|
||||
spin_lock(&dev->n_srqs_lock);
|
||||
if (dev->n_srqs_allocated == ib_ipath_max_srqs) {
|
||||
spin_unlock(&dev->n_srqs_lock);
|
||||
ret = ERR_PTR(-ENOMEM);
|
||||
goto bail_ip;
|
||||
}
|
||||
|
||||
dev->n_srqs_allocated++;
|
||||
spin_unlock(&dev->n_srqs_lock);
|
||||
|
||||
if (srq->ip) {
|
||||
spin_lock_irq(&dev->pending_lock);
|
||||
list_add(&srq->ip->pending_mmaps, &dev->pending_mmaps);
|
||||
spin_unlock_irq(&dev->pending_lock);
|
||||
}
|
||||
|
||||
ret = &srq->ibsrq;
|
||||
goto done;
|
||||
|
||||
bail_ip:
|
||||
kfree(srq->ip);
|
||||
bail_wq:
|
||||
vfree(srq->rq.wq);
|
||||
bail_srq:
|
||||
kfree(srq);
|
||||
done:
|
||||
return ret;
|
||||
}
|
||||
|
||||
/**
|
||||
* ipath_modify_srq - modify a shared receive queue
|
||||
* @ibsrq: the SRQ to modify
|
||||
* @attr: the new attributes of the SRQ
|
||||
* @attr_mask: indicates which attributes to modify
|
||||
* @udata: user data for ipathverbs.so
|
||||
*/
|
||||
int ipath_modify_srq(struct ib_srq *ibsrq, struct ib_srq_attr *attr,
|
||||
enum ib_srq_attr_mask attr_mask,
|
||||
struct ib_udata *udata)
|
||||
{
|
||||
struct ipath_srq *srq = to_isrq(ibsrq);
|
||||
struct ipath_rwq *wq;
|
||||
int ret = 0;
|
||||
|
||||
if (attr_mask & IB_SRQ_MAX_WR) {
|
||||
struct ipath_rwq *owq;
|
||||
struct ipath_rwqe *p;
|
||||
u32 sz, size, n, head, tail;
|
||||
|
||||
/* Check that the requested sizes are below the limits. */
|
||||
if ((attr->max_wr > ib_ipath_max_srq_wrs) ||
|
||||
((attr_mask & IB_SRQ_LIMIT) ?
|
||||
attr->srq_limit : srq->limit) > attr->max_wr) {
|
||||
ret = -EINVAL;
|
||||
goto bail;
|
||||
}
|
||||
|
||||
sz = sizeof(struct ipath_rwqe) +
|
||||
srq->rq.max_sge * sizeof(struct ib_sge);
|
||||
size = attr->max_wr + 1;
|
||||
wq = vmalloc_user(sizeof(struct ipath_rwq) + size * sz);
|
||||
if (!wq) {
|
||||
ret = -ENOMEM;
|
||||
goto bail;
|
||||
}
|
||||
|
||||
/* Check that we can write the offset to mmap. */
|
||||
if (udata && udata->inlen >= sizeof(__u64)) {
|
||||
__u64 offset_addr;
|
||||
__u64 offset = 0;
|
||||
|
||||
ret = ib_copy_from_udata(&offset_addr, udata,
|
||||
sizeof(offset_addr));
|
||||
if (ret)
|
||||
goto bail_free;
|
||||
udata->outbuf =
|
||||
(void __user *) (unsigned long) offset_addr;
|
||||
ret = ib_copy_to_udata(udata, &offset,
|
||||
sizeof(offset));
|
||||
if (ret)
|
||||
goto bail_free;
|
||||
}
|
||||
|
||||
spin_lock_irq(&srq->rq.lock);
|
||||
/*
|
||||
* validate head pointer value and compute
|
||||
* the number of remaining WQEs.
|
||||
*/
|
||||
owq = srq->rq.wq;
|
||||
head = owq->head;
|
||||
if (head >= srq->rq.size)
|
||||
head = 0;
|
||||
tail = owq->tail;
|
||||
if (tail >= srq->rq.size)
|
||||
tail = 0;
|
||||
n = head;
|
||||
if (n < tail)
|
||||
n += srq->rq.size - tail;
|
||||
else
|
||||
n -= tail;
|
||||
if (size <= n) {
|
||||
ret = -EINVAL;
|
||||
goto bail_unlock;
|
||||
}
|
||||
n = 0;
|
||||
p = wq->wq;
|
||||
while (tail != head) {
|
||||
struct ipath_rwqe *wqe;
|
||||
int i;
|
||||
|
||||
wqe = get_rwqe_ptr(&srq->rq, tail);
|
||||
p->wr_id = wqe->wr_id;
|
||||
p->num_sge = wqe->num_sge;
|
||||
for (i = 0; i < wqe->num_sge; i++)
|
||||
p->sg_list[i] = wqe->sg_list[i];
|
||||
n++;
|
||||
p = (struct ipath_rwqe *)((char *) p + sz);
|
||||
if (++tail >= srq->rq.size)
|
||||
tail = 0;
|
||||
}
|
||||
srq->rq.wq = wq;
|
||||
srq->rq.size = size;
|
||||
wq->head = n;
|
||||
wq->tail = 0;
|
||||
if (attr_mask & IB_SRQ_LIMIT)
|
||||
srq->limit = attr->srq_limit;
|
||||
spin_unlock_irq(&srq->rq.lock);
|
||||
|
||||
vfree(owq);
|
||||
|
||||
if (srq->ip) {
|
||||
struct ipath_mmap_info *ip = srq->ip;
|
||||
struct ipath_ibdev *dev = to_idev(srq->ibsrq.device);
|
||||
u32 s = sizeof(struct ipath_rwq) + size * sz;
|
||||
|
||||
ipath_update_mmap_info(dev, ip, s, wq);
|
||||
|
||||
/*
|
||||
* Return the offset to mmap.
|
||||
* See ipath_mmap() for details.
|
||||
*/
|
||||
if (udata && udata->inlen >= sizeof(__u64)) {
|
||||
ret = ib_copy_to_udata(udata, &ip->offset,
|
||||
sizeof(ip->offset));
|
||||
if (ret)
|
||||
goto bail;
|
||||
}
|
||||
|
||||
spin_lock_irq(&dev->pending_lock);
|
||||
if (list_empty(&ip->pending_mmaps))
|
||||
list_add(&ip->pending_mmaps,
|
||||
&dev->pending_mmaps);
|
||||
spin_unlock_irq(&dev->pending_lock);
|
||||
}
|
||||
} else if (attr_mask & IB_SRQ_LIMIT) {
|
||||
spin_lock_irq(&srq->rq.lock);
|
||||
if (attr->srq_limit >= srq->rq.size)
|
||||
ret = -EINVAL;
|
||||
else
|
||||
srq->limit = attr->srq_limit;
|
||||
spin_unlock_irq(&srq->rq.lock);
|
||||
}
|
||||
goto bail;
|
||||
|
||||
bail_unlock:
|
||||
spin_unlock_irq(&srq->rq.lock);
|
||||
bail_free:
|
||||
vfree(wq);
|
||||
bail:
|
||||
return ret;
|
||||
}
|
||||
|
||||
int ipath_query_srq(struct ib_srq *ibsrq, struct ib_srq_attr *attr)
|
||||
{
|
||||
struct ipath_srq *srq = to_isrq(ibsrq);
|
||||
|
||||
attr->max_wr = srq->rq.size - 1;
|
||||
attr->max_sge = srq->rq.max_sge;
|
||||
attr->srq_limit = srq->limit;
|
||||
return 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* ipath_destroy_srq - destroy a shared receive queue
|
||||
* @ibsrq: the SRQ to destroy
|
||||
*/
|
||||
int ipath_destroy_srq(struct ib_srq *ibsrq)
|
||||
{
|
||||
struct ipath_srq *srq = to_isrq(ibsrq);
|
||||
struct ipath_ibdev *dev = to_idev(ibsrq->device);
|
||||
|
||||
spin_lock(&dev->n_srqs_lock);
|
||||
dev->n_srqs_allocated--;
|
||||
spin_unlock(&dev->n_srqs_lock);
|
||||
if (srq->ip)
|
||||
kref_put(&srq->ip->ref, ipath_release_mmap_info);
|
||||
else
|
||||
vfree(srq->rq.wq);
|
||||
kfree(srq);
|
||||
|
||||
return 0;
|
||||
}
|
|
@ -1,347 +0,0 @@
|
|||
/*
|
||||
* Copyright (c) 2006, 2007, 2008 QLogic Corporation. All rights reserved.
|
||||
* Copyright (c) 2003, 2004, 2005, 2006 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.
|
||||
*/
|
||||
|
||||
#include "ipath_kernel.h"
|
||||
|
||||
struct infinipath_stats ipath_stats;
|
||||
|
||||
/**
|
||||
* ipath_snap_cntr - snapshot a chip counter
|
||||
* @dd: the infinipath device
|
||||
* @creg: the counter to snapshot
|
||||
*
|
||||
* called from add_timer and user counter read calls, to deal with
|
||||
* counters that wrap in "human time". The words sent and received, and
|
||||
* the packets sent and received are all that we worry about. For now,
|
||||
* at least, we don't worry about error counters, because if they wrap
|
||||
* that quickly, we probably don't care. We may eventually just make this
|
||||
* handle all the counters. word counters can wrap in about 20 seconds
|
||||
* of full bandwidth traffic, packet counters in a few hours.
|
||||
*/
|
||||
|
||||
u64 ipath_snap_cntr(struct ipath_devdata *dd, ipath_creg creg)
|
||||
{
|
||||
u32 val, reg64 = 0;
|
||||
u64 val64;
|
||||
unsigned long t0, t1;
|
||||
u64 ret;
|
||||
|
||||
t0 = jiffies;
|
||||
/* If fast increment counters are only 32 bits, snapshot them,
|
||||
* and maintain them as 64bit values in the driver */
|
||||
if (!(dd->ipath_flags & IPATH_32BITCOUNTERS) &&
|
||||
(creg == dd->ipath_cregs->cr_wordsendcnt ||
|
||||
creg == dd->ipath_cregs->cr_wordrcvcnt ||
|
||||
creg == dd->ipath_cregs->cr_pktsendcnt ||
|
||||
creg == dd->ipath_cregs->cr_pktrcvcnt)) {
|
||||
val64 = ipath_read_creg(dd, creg);
|
||||
val = val64 == ~0ULL ? ~0U : 0;
|
||||
reg64 = 1;
|
||||
} else /* val64 just to keep gcc quiet... */
|
||||
val64 = val = ipath_read_creg32(dd, creg);
|
||||
/*
|
||||
* See if a second has passed. This is just a way to detect things
|
||||
* that are quite broken. Normally this should take just a few
|
||||
* cycles (the check is for long enough that we don't care if we get
|
||||
* pre-empted.) An Opteron HT O read timeout is 4 seconds with
|
||||
* normal NB values
|
||||
*/
|
||||
t1 = jiffies;
|
||||
if (time_before(t0 + HZ, t1) && val == -1) {
|
||||
ipath_dev_err(dd, "Error! Read counter 0x%x timed out\n",
|
||||
creg);
|
||||
ret = 0ULL;
|
||||
goto bail;
|
||||
}
|
||||
if (reg64) {
|
||||
ret = val64;
|
||||
goto bail;
|
||||
}
|
||||
|
||||
if (creg == dd->ipath_cregs->cr_wordsendcnt) {
|
||||
if (val != dd->ipath_lastsword) {
|
||||
dd->ipath_sword += val - dd->ipath_lastsword;
|
||||
dd->ipath_lastsword = val;
|
||||
}
|
||||
val64 = dd->ipath_sword;
|
||||
} else if (creg == dd->ipath_cregs->cr_wordrcvcnt) {
|
||||
if (val != dd->ipath_lastrword) {
|
||||
dd->ipath_rword += val - dd->ipath_lastrword;
|
||||
dd->ipath_lastrword = val;
|
||||
}
|
||||
val64 = dd->ipath_rword;
|
||||
} else if (creg == dd->ipath_cregs->cr_pktsendcnt) {
|
||||
if (val != dd->ipath_lastspkts) {
|
||||
dd->ipath_spkts += val - dd->ipath_lastspkts;
|
||||
dd->ipath_lastspkts = val;
|
||||
}
|
||||
val64 = dd->ipath_spkts;
|
||||
} else if (creg == dd->ipath_cregs->cr_pktrcvcnt) {
|
||||
if (val != dd->ipath_lastrpkts) {
|
||||
dd->ipath_rpkts += val - dd->ipath_lastrpkts;
|
||||
dd->ipath_lastrpkts = val;
|
||||
}
|
||||
val64 = dd->ipath_rpkts;
|
||||
} else if (creg == dd->ipath_cregs->cr_ibsymbolerrcnt) {
|
||||
if (dd->ibdeltainprog)
|
||||
val64 -= val64 - dd->ibsymsnap;
|
||||
val64 -= dd->ibsymdelta;
|
||||
} else if (creg == dd->ipath_cregs->cr_iblinkerrrecovcnt) {
|
||||
if (dd->ibdeltainprog)
|
||||
val64 -= val64 - dd->iblnkerrsnap;
|
||||
val64 -= dd->iblnkerrdelta;
|
||||
} else
|
||||
val64 = (u64) val;
|
||||
|
||||
ret = val64;
|
||||
|
||||
bail:
|
||||
return ret;
|
||||
}
|
||||
|
||||
/**
|
||||
* ipath_qcheck - print delta of egrfull/hdrqfull errors for kernel ports
|
||||
* @dd: the infinipath device
|
||||
*
|
||||
* print the delta of egrfull/hdrqfull errors for kernel ports no more than
|
||||
* every 5 seconds. User processes are printed at close, but kernel doesn't
|
||||
* close, so... Separate routine so may call from other places someday, and
|
||||
* so function name when printed by _IPATH_INFO is meaningfull
|
||||
*/
|
||||
static void ipath_qcheck(struct ipath_devdata *dd)
|
||||
{
|
||||
static u64 last_tot_hdrqfull;
|
||||
struct ipath_portdata *pd = dd->ipath_pd[0];
|
||||
size_t blen = 0;
|
||||
char buf[128];
|
||||
u32 hdrqtail;
|
||||
|
||||
*buf = 0;
|
||||
if (pd->port_hdrqfull != dd->ipath_p0_hdrqfull) {
|
||||
blen = snprintf(buf, sizeof buf, "port 0 hdrqfull %u",
|
||||
pd->port_hdrqfull -
|
||||
dd->ipath_p0_hdrqfull);
|
||||
dd->ipath_p0_hdrqfull = pd->port_hdrqfull;
|
||||
}
|
||||
if (ipath_stats.sps_etidfull != dd->ipath_last_tidfull) {
|
||||
blen += snprintf(buf + blen, sizeof buf - blen,
|
||||
"%srcvegrfull %llu",
|
||||
blen ? ", " : "",
|
||||
(unsigned long long)
|
||||
(ipath_stats.sps_etidfull -
|
||||
dd->ipath_last_tidfull));
|
||||
dd->ipath_last_tidfull = ipath_stats.sps_etidfull;
|
||||
}
|
||||
|
||||
/*
|
||||
* this is actually the number of hdrq full interrupts, not actual
|
||||
* events, but at the moment that's mostly what I'm interested in.
|
||||
* Actual count, etc. is in the counters, if needed. For production
|
||||
* users this won't ordinarily be printed.
|
||||
*/
|
||||
|
||||
if ((ipath_debug & (__IPATH_PKTDBG | __IPATH_DBG)) &&
|
||||
ipath_stats.sps_hdrqfull != last_tot_hdrqfull) {
|
||||
blen += snprintf(buf + blen, sizeof buf - blen,
|
||||
"%shdrqfull %llu (all ports)",
|
||||
blen ? ", " : "",
|
||||
(unsigned long long)
|
||||
(ipath_stats.sps_hdrqfull -
|
||||
last_tot_hdrqfull));
|
||||
last_tot_hdrqfull = ipath_stats.sps_hdrqfull;
|
||||
}
|
||||
if (blen)
|
||||
ipath_dbg("%s\n", buf);
|
||||
|
||||
hdrqtail = ipath_get_hdrqtail(pd);
|
||||
if (pd->port_head != hdrqtail) {
|
||||
if (dd->ipath_lastport0rcv_cnt ==
|
||||
ipath_stats.sps_port0pkts) {
|
||||
ipath_cdbg(PKT, "missing rcv interrupts? "
|
||||
"port0 hd=%x tl=%x; port0pkts %llx; write"
|
||||
" hd (w/intr)\n",
|
||||
pd->port_head, hdrqtail,
|
||||
(unsigned long long)
|
||||
ipath_stats.sps_port0pkts);
|
||||
ipath_write_ureg(dd, ur_rcvhdrhead, hdrqtail |
|
||||
dd->ipath_rhdrhead_intr_off, pd->port_port);
|
||||
}
|
||||
dd->ipath_lastport0rcv_cnt = ipath_stats.sps_port0pkts;
|
||||
}
|
||||
}
|
||||
|
||||
static void ipath_chk_errormask(struct ipath_devdata *dd)
|
||||
{
|
||||
static u32 fixed;
|
||||
u32 ctrl;
|
||||
unsigned long errormask;
|
||||
unsigned long hwerrs;
|
||||
|
||||
if (!dd->ipath_errormask || !(dd->ipath_flags & IPATH_INITTED))
|
||||
return;
|
||||
|
||||
errormask = ipath_read_kreg64(dd, dd->ipath_kregs->kr_errormask);
|
||||
|
||||
if (errormask == dd->ipath_errormask)
|
||||
return;
|
||||
fixed++;
|
||||
|
||||
hwerrs = ipath_read_kreg64(dd, dd->ipath_kregs->kr_hwerrstatus);
|
||||
ctrl = ipath_read_kreg32(dd, dd->ipath_kregs->kr_control);
|
||||
|
||||
ipath_write_kreg(dd, dd->ipath_kregs->kr_errormask,
|
||||
dd->ipath_errormask);
|
||||
|
||||
if ((hwerrs & dd->ipath_hwerrmask) ||
|
||||
(ctrl & INFINIPATH_C_FREEZEMODE)) {
|
||||
/* force re-interrupt of pending events, just in case */
|
||||
ipath_write_kreg(dd, dd->ipath_kregs->kr_hwerrclear, 0ULL);
|
||||
ipath_write_kreg(dd, dd->ipath_kregs->kr_errorclear, 0ULL);
|
||||
ipath_write_kreg(dd, dd->ipath_kregs->kr_intclear, 0ULL);
|
||||
dev_info(&dd->pcidev->dev,
|
||||
"errormask fixed(%u) %lx -> %lx, ctrl %x hwerr %lx\n",
|
||||
fixed, errormask, (unsigned long)dd->ipath_errormask,
|
||||
ctrl, hwerrs);
|
||||
} else
|
||||
ipath_dbg("errormask fixed(%u) %lx -> %lx, no freeze\n",
|
||||
fixed, errormask,
|
||||
(unsigned long)dd->ipath_errormask);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* ipath_get_faststats - get word counters from chip before they overflow
|
||||
* @opaque - contains a pointer to the infinipath device ipath_devdata
|
||||
*
|
||||
* called from add_timer
|
||||
*/
|
||||
void ipath_get_faststats(unsigned long opaque)
|
||||
{
|
||||
struct ipath_devdata *dd = (struct ipath_devdata *) opaque;
|
||||
int i;
|
||||
static unsigned cnt;
|
||||
unsigned long flags;
|
||||
u64 traffic_wds;
|
||||
|
||||
/*
|
||||
* don't access the chip while running diags, or memory diags can
|
||||
* fail
|
||||
*/
|
||||
if (!dd->ipath_kregbase || !(dd->ipath_flags & IPATH_INITTED) ||
|
||||
ipath_diag_inuse)
|
||||
/* but re-arm the timer, for diags case; won't hurt other */
|
||||
goto done;
|
||||
|
||||
/*
|
||||
* We now try to maintain a "active timer", based on traffic
|
||||
* exceeding a threshold, so we need to check the word-counts
|
||||
* even if they are 64-bit.
|
||||
*/
|
||||
traffic_wds = ipath_snap_cntr(dd, dd->ipath_cregs->cr_wordsendcnt) +
|
||||
ipath_snap_cntr(dd, dd->ipath_cregs->cr_wordrcvcnt);
|
||||
spin_lock_irqsave(&dd->ipath_eep_st_lock, flags);
|
||||
traffic_wds -= dd->ipath_traffic_wds;
|
||||
dd->ipath_traffic_wds += traffic_wds;
|
||||
if (traffic_wds >= IPATH_TRAFFIC_ACTIVE_THRESHOLD)
|
||||
atomic_add(5, &dd->ipath_active_time); /* S/B #define */
|
||||
spin_unlock_irqrestore(&dd->ipath_eep_st_lock, flags);
|
||||
|
||||
if (dd->ipath_flags & IPATH_32BITCOUNTERS) {
|
||||
ipath_snap_cntr(dd, dd->ipath_cregs->cr_pktsendcnt);
|
||||
ipath_snap_cntr(dd, dd->ipath_cregs->cr_pktrcvcnt);
|
||||
}
|
||||
|
||||
ipath_qcheck(dd);
|
||||
|
||||
/*
|
||||
* deal with repeat error suppression. Doesn't really matter if
|
||||
* last error was almost a full interval ago, or just a few usecs
|
||||
* ago; still won't get more than 2 per interval. We may want
|
||||
* longer intervals for this eventually, could do with mod, counter
|
||||
* or separate timer. Also see code in ipath_handle_errors() and
|
||||
* ipath_handle_hwerrors().
|
||||
*/
|
||||
|
||||
if (dd->ipath_lasterror)
|
||||
dd->ipath_lasterror = 0;
|
||||
if (dd->ipath_lasthwerror)
|
||||
dd->ipath_lasthwerror = 0;
|
||||
if (dd->ipath_maskederrs
|
||||
&& time_after(jiffies, dd->ipath_unmasktime)) {
|
||||
char ebuf[256];
|
||||
int iserr;
|
||||
iserr = ipath_decode_err(dd, ebuf, sizeof ebuf,
|
||||
dd->ipath_maskederrs);
|
||||
if (dd->ipath_maskederrs &
|
||||
~(INFINIPATH_E_RRCVEGRFULL | INFINIPATH_E_RRCVHDRFULL |
|
||||
INFINIPATH_E_PKTERRS))
|
||||
ipath_dev_err(dd, "Re-enabling masked errors "
|
||||
"(%s)\n", ebuf);
|
||||
else {
|
||||
/*
|
||||
* rcvegrfull and rcvhdrqfull are "normal", for some
|
||||
* types of processes (mostly benchmarks) that send
|
||||
* huge numbers of messages, while not processing
|
||||
* them. So only complain about these at debug
|
||||
* level.
|
||||
*/
|
||||
if (iserr)
|
||||
ipath_dbg(
|
||||
"Re-enabling queue full errors (%s)\n",
|
||||
ebuf);
|
||||
else
|
||||
ipath_cdbg(ERRPKT, "Re-enabling packet"
|
||||
" problem interrupt (%s)\n", ebuf);
|
||||
}
|
||||
|
||||
/* re-enable masked errors */
|
||||
dd->ipath_errormask |= dd->ipath_maskederrs;
|
||||
ipath_write_kreg(dd, dd->ipath_kregs->kr_errormask,
|
||||
dd->ipath_errormask);
|
||||
dd->ipath_maskederrs = 0;
|
||||
}
|
||||
|
||||
/* limit qfull messages to ~one per minute per port */
|
||||
if ((++cnt & 0x10)) {
|
||||
for (i = (int) dd->ipath_cfgports; --i >= 0; ) {
|
||||
struct ipath_portdata *pd = dd->ipath_pd[i];
|
||||
|
||||
if (pd && pd->port_lastrcvhdrqtail != -1)
|
||||
pd->port_lastrcvhdrqtail = -1;
|
||||
}
|
||||
}
|
||||
|
||||
ipath_chk_errormask(dd);
|
||||
done:
|
||||
mod_timer(&dd->ipath_stats_timer, jiffies + HZ * 5);
|
||||
}
|
File diff suppressed because it is too large
Load Diff
|
@ -1,547 +0,0 @@
|
|||
/*
|
||||
* Copyright (c) 2006, 2007, 2008 QLogic Corporation. All rights reserved.
|
||||
* Copyright (c) 2005, 2006 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.
|
||||
*/
|
||||
|
||||
#include "ipath_verbs.h"
|
||||
#include "ipath_kernel.h"
|
||||
|
||||
/* cut down ridiculously long IB macro names */
|
||||
#define OP(x) IB_OPCODE_UC_##x
|
||||
|
||||
/**
|
||||
* ipath_make_uc_req - construct a request packet (SEND, RDMA write)
|
||||
* @qp: a pointer to the QP
|
||||
*
|
||||
* Return 1 if constructed; otherwise, return 0.
|
||||
*/
|
||||
int ipath_make_uc_req(struct ipath_qp *qp)
|
||||
{
|
||||
struct ipath_other_headers *ohdr;
|
||||
struct ipath_swqe *wqe;
|
||||
unsigned long flags;
|
||||
u32 hwords;
|
||||
u32 bth0;
|
||||
u32 len;
|
||||
u32 pmtu = ib_mtu_enum_to_int(qp->path_mtu);
|
||||
int ret = 0;
|
||||
|
||||
spin_lock_irqsave(&qp->s_lock, flags);
|
||||
|
||||
if (!(ib_ipath_state_ops[qp->state] & IPATH_PROCESS_SEND_OK)) {
|
||||
if (!(ib_ipath_state_ops[qp->state] & IPATH_FLUSH_SEND))
|
||||
goto bail;
|
||||
/* We are in the error state, flush the work request. */
|
||||
if (qp->s_last == qp->s_head)
|
||||
goto bail;
|
||||
/* If DMAs are in progress, we can't flush immediately. */
|
||||
if (atomic_read(&qp->s_dma_busy)) {
|
||||
qp->s_flags |= IPATH_S_WAIT_DMA;
|
||||
goto bail;
|
||||
}
|
||||
wqe = get_swqe_ptr(qp, qp->s_last);
|
||||
ipath_send_complete(qp, wqe, IB_WC_WR_FLUSH_ERR);
|
||||
goto done;
|
||||
}
|
||||
|
||||
ohdr = &qp->s_hdr.u.oth;
|
||||
if (qp->remote_ah_attr.ah_flags & IB_AH_GRH)
|
||||
ohdr = &qp->s_hdr.u.l.oth;
|
||||
|
||||
/* header size in 32-bit words LRH+BTH = (8+12)/4. */
|
||||
hwords = 5;
|
||||
bth0 = 1 << 22; /* Set M bit */
|
||||
|
||||
/* Get the next send request. */
|
||||
wqe = get_swqe_ptr(qp, qp->s_cur);
|
||||
qp->s_wqe = NULL;
|
||||
switch (qp->s_state) {
|
||||
default:
|
||||
if (!(ib_ipath_state_ops[qp->state] &
|
||||
IPATH_PROCESS_NEXT_SEND_OK))
|
||||
goto bail;
|
||||
/* Check if send work queue is empty. */
|
||||
if (qp->s_cur == qp->s_head)
|
||||
goto bail;
|
||||
/*
|
||||
* Start a new request.
|
||||
*/
|
||||
qp->s_psn = wqe->psn = qp->s_next_psn;
|
||||
qp->s_sge.sge = wqe->sg_list[0];
|
||||
qp->s_sge.sg_list = wqe->sg_list + 1;
|
||||
qp->s_sge.num_sge = wqe->wr.num_sge;
|
||||
qp->s_len = len = wqe->length;
|
||||
switch (wqe->wr.opcode) {
|
||||
case IB_WR_SEND:
|
||||
case IB_WR_SEND_WITH_IMM:
|
||||
if (len > pmtu) {
|
||||
qp->s_state = OP(SEND_FIRST);
|
||||
len = pmtu;
|
||||
break;
|
||||
}
|
||||
if (wqe->wr.opcode == IB_WR_SEND)
|
||||
qp->s_state = OP(SEND_ONLY);
|
||||
else {
|
||||
qp->s_state =
|
||||
OP(SEND_ONLY_WITH_IMMEDIATE);
|
||||
/* Immediate data comes after the BTH */
|
||||
ohdr->u.imm_data = wqe->wr.ex.imm_data;
|
||||
hwords += 1;
|
||||
}
|
||||
if (wqe->wr.send_flags & IB_SEND_SOLICITED)
|
||||
bth0 |= 1 << 23;
|
||||
qp->s_wqe = wqe;
|
||||
if (++qp->s_cur >= qp->s_size)
|
||||
qp->s_cur = 0;
|
||||
break;
|
||||
|
||||
case IB_WR_RDMA_WRITE:
|
||||
case IB_WR_RDMA_WRITE_WITH_IMM:
|
||||
ohdr->u.rc.reth.vaddr =
|
||||
cpu_to_be64(wqe->rdma_wr.remote_addr);
|
||||
ohdr->u.rc.reth.rkey =
|
||||
cpu_to_be32(wqe->rdma_wr.rkey);
|
||||
ohdr->u.rc.reth.length = cpu_to_be32(len);
|
||||
hwords += sizeof(struct ib_reth) / 4;
|
||||
if (len > pmtu) {
|
||||
qp->s_state = OP(RDMA_WRITE_FIRST);
|
||||
len = pmtu;
|
||||
break;
|
||||
}
|
||||
if (wqe->wr.opcode == IB_WR_RDMA_WRITE)
|
||||
qp->s_state = OP(RDMA_WRITE_ONLY);
|
||||
else {
|
||||
qp->s_state =
|
||||
OP(RDMA_WRITE_ONLY_WITH_IMMEDIATE);
|
||||
/* Immediate data comes after the RETH */
|
||||
ohdr->u.rc.imm_data = wqe->wr.ex.imm_data;
|
||||
hwords += 1;
|
||||
if (wqe->wr.send_flags & IB_SEND_SOLICITED)
|
||||
bth0 |= 1 << 23;
|
||||
}
|
||||
qp->s_wqe = wqe;
|
||||
if (++qp->s_cur >= qp->s_size)
|
||||
qp->s_cur = 0;
|
||||
break;
|
||||
|
||||
default:
|
||||
goto bail;
|
||||
}
|
||||
break;
|
||||
|
||||
case OP(SEND_FIRST):
|
||||
qp->s_state = OP(SEND_MIDDLE);
|
||||
/* FALLTHROUGH */
|
||||
case OP(SEND_MIDDLE):
|
||||
len = qp->s_len;
|
||||
if (len > pmtu) {
|
||||
len = pmtu;
|
||||
break;
|
||||
}
|
||||
if (wqe->wr.opcode == IB_WR_SEND)
|
||||
qp->s_state = OP(SEND_LAST);
|
||||
else {
|
||||
qp->s_state = OP(SEND_LAST_WITH_IMMEDIATE);
|
||||
/* Immediate data comes after the BTH */
|
||||
ohdr->u.imm_data = wqe->wr.ex.imm_data;
|
||||
hwords += 1;
|
||||
}
|
||||
if (wqe->wr.send_flags & IB_SEND_SOLICITED)
|
||||
bth0 |= 1 << 23;
|
||||
qp->s_wqe = wqe;
|
||||
if (++qp->s_cur >= qp->s_size)
|
||||
qp->s_cur = 0;
|
||||
break;
|
||||
|
||||
case OP(RDMA_WRITE_FIRST):
|
||||
qp->s_state = OP(RDMA_WRITE_MIDDLE);
|
||||
/* FALLTHROUGH */
|
||||
case OP(RDMA_WRITE_MIDDLE):
|
||||
len = qp->s_len;
|
||||
if (len > pmtu) {
|
||||
len = pmtu;
|
||||
break;
|
||||
}
|
||||
if (wqe->wr.opcode == IB_WR_RDMA_WRITE)
|
||||
qp->s_state = OP(RDMA_WRITE_LAST);
|
||||
else {
|
||||
qp->s_state =
|
||||
OP(RDMA_WRITE_LAST_WITH_IMMEDIATE);
|
||||
/* Immediate data comes after the BTH */
|
||||
ohdr->u.imm_data = wqe->wr.ex.imm_data;
|
||||
hwords += 1;
|
||||
if (wqe->wr.send_flags & IB_SEND_SOLICITED)
|
||||
bth0 |= 1 << 23;
|
||||
}
|
||||
qp->s_wqe = wqe;
|
||||
if (++qp->s_cur >= qp->s_size)
|
||||
qp->s_cur = 0;
|
||||
break;
|
||||
}
|
||||
qp->s_len -= len;
|
||||
qp->s_hdrwords = hwords;
|
||||
qp->s_cur_sge = &qp->s_sge;
|
||||
qp->s_cur_size = len;
|
||||
ipath_make_ruc_header(to_idev(qp->ibqp.device),
|
||||
qp, ohdr, bth0 | (qp->s_state << 24),
|
||||
qp->s_next_psn++ & IPATH_PSN_MASK);
|
||||
done:
|
||||
ret = 1;
|
||||
goto unlock;
|
||||
|
||||
bail:
|
||||
qp->s_flags &= ~IPATH_S_BUSY;
|
||||
unlock:
|
||||
spin_unlock_irqrestore(&qp->s_lock, flags);
|
||||
return ret;
|
||||
}
|
||||
|
||||
/**
|
||||
* ipath_uc_rcv - handle an incoming UC packet
|
||||
* @dev: the device the packet came in on
|
||||
* @hdr: the header of the packet
|
||||
* @has_grh: true if the packet has a GRH
|
||||
* @data: the packet data
|
||||
* @tlen: the length of the packet
|
||||
* @qp: the QP for this packet.
|
||||
*
|
||||
* This is called from ipath_qp_rcv() to process an incoming UC packet
|
||||
* for the given QP.
|
||||
* Called at interrupt level.
|
||||
*/
|
||||
void ipath_uc_rcv(struct ipath_ibdev *dev, struct ipath_ib_header *hdr,
|
||||
int has_grh, void *data, u32 tlen, struct ipath_qp *qp)
|
||||
{
|
||||
struct ipath_other_headers *ohdr;
|
||||
int opcode;
|
||||
u32 hdrsize;
|
||||
u32 psn;
|
||||
u32 pad;
|
||||
struct ib_wc wc;
|
||||
u32 pmtu = ib_mtu_enum_to_int(qp->path_mtu);
|
||||
struct ib_reth *reth;
|
||||
int header_in_data;
|
||||
|
||||
/* Validate the SLID. See Ch. 9.6.1.5 */
|
||||
if (unlikely(be16_to_cpu(hdr->lrh[3]) != qp->remote_ah_attr.dlid))
|
||||
goto done;
|
||||
|
||||
/* Check for GRH */
|
||||
if (!has_grh) {
|
||||
ohdr = &hdr->u.oth;
|
||||
hdrsize = 8 + 12; /* LRH + BTH */
|
||||
psn = be32_to_cpu(ohdr->bth[2]);
|
||||
header_in_data = 0;
|
||||
} else {
|
||||
ohdr = &hdr->u.l.oth;
|
||||
hdrsize = 8 + 40 + 12; /* LRH + GRH + BTH */
|
||||
/*
|
||||
* The header with GRH is 60 bytes and the
|
||||
* core driver sets the eager header buffer
|
||||
* size to 56 bytes so the last 4 bytes of
|
||||
* the BTH header (PSN) is in the data buffer.
|
||||
*/
|
||||
header_in_data = dev->dd->ipath_rcvhdrentsize == 16;
|
||||
if (header_in_data) {
|
||||
psn = be32_to_cpu(((__be32 *) data)[0]);
|
||||
data += sizeof(__be32);
|
||||
} else
|
||||
psn = be32_to_cpu(ohdr->bth[2]);
|
||||
}
|
||||
/*
|
||||
* The opcode is in the low byte when its in network order
|
||||
* (top byte when in host order).
|
||||
*/
|
||||
opcode = be32_to_cpu(ohdr->bth[0]) >> 24;
|
||||
|
||||
memset(&wc, 0, sizeof wc);
|
||||
|
||||
/* Compare the PSN verses the expected PSN. */
|
||||
if (unlikely(ipath_cmp24(psn, qp->r_psn) != 0)) {
|
||||
/*
|
||||
* Handle a sequence error.
|
||||
* Silently drop any current message.
|
||||
*/
|
||||
qp->r_psn = psn;
|
||||
inv:
|
||||
qp->r_state = OP(SEND_LAST);
|
||||
switch (opcode) {
|
||||
case OP(SEND_FIRST):
|
||||
case OP(SEND_ONLY):
|
||||
case OP(SEND_ONLY_WITH_IMMEDIATE):
|
||||
goto send_first;
|
||||
|
||||
case OP(RDMA_WRITE_FIRST):
|
||||
case OP(RDMA_WRITE_ONLY):
|
||||
case OP(RDMA_WRITE_ONLY_WITH_IMMEDIATE):
|
||||
goto rdma_first;
|
||||
|
||||
default:
|
||||
dev->n_pkt_drops++;
|
||||
goto done;
|
||||
}
|
||||
}
|
||||
|
||||
/* Check for opcode sequence errors. */
|
||||
switch (qp->r_state) {
|
||||
case OP(SEND_FIRST):
|
||||
case OP(SEND_MIDDLE):
|
||||
if (opcode == OP(SEND_MIDDLE) ||
|
||||
opcode == OP(SEND_LAST) ||
|
||||
opcode == OP(SEND_LAST_WITH_IMMEDIATE))
|
||||
break;
|
||||
goto inv;
|
||||
|
||||
case OP(RDMA_WRITE_FIRST):
|
||||
case OP(RDMA_WRITE_MIDDLE):
|
||||
if (opcode == OP(RDMA_WRITE_MIDDLE) ||
|
||||
opcode == OP(RDMA_WRITE_LAST) ||
|
||||
opcode == OP(RDMA_WRITE_LAST_WITH_IMMEDIATE))
|
||||
break;
|
||||
goto inv;
|
||||
|
||||
default:
|
||||
if (opcode == OP(SEND_FIRST) ||
|
||||
opcode == OP(SEND_ONLY) ||
|
||||
opcode == OP(SEND_ONLY_WITH_IMMEDIATE) ||
|
||||
opcode == OP(RDMA_WRITE_FIRST) ||
|
||||
opcode == OP(RDMA_WRITE_ONLY) ||
|
||||
opcode == OP(RDMA_WRITE_ONLY_WITH_IMMEDIATE))
|
||||
break;
|
||||
goto inv;
|
||||
}
|
||||
|
||||
/* OK, process the packet. */
|
||||
switch (opcode) {
|
||||
case OP(SEND_FIRST):
|
||||
case OP(SEND_ONLY):
|
||||
case OP(SEND_ONLY_WITH_IMMEDIATE):
|
||||
send_first:
|
||||
if (qp->r_flags & IPATH_R_REUSE_SGE) {
|
||||
qp->r_flags &= ~IPATH_R_REUSE_SGE;
|
||||
qp->r_sge = qp->s_rdma_read_sge;
|
||||
} else if (!ipath_get_rwqe(qp, 0)) {
|
||||
dev->n_pkt_drops++;
|
||||
goto done;
|
||||
}
|
||||
/* Save the WQE so we can reuse it in case of an error. */
|
||||
qp->s_rdma_read_sge = qp->r_sge;
|
||||
qp->r_rcv_len = 0;
|
||||
if (opcode == OP(SEND_ONLY))
|
||||
goto send_last;
|
||||
else if (opcode == OP(SEND_ONLY_WITH_IMMEDIATE))
|
||||
goto send_last_imm;
|
||||
/* FALLTHROUGH */
|
||||
case OP(SEND_MIDDLE):
|
||||
/* Check for invalid length PMTU or posted rwqe len. */
|
||||
if (unlikely(tlen != (hdrsize + pmtu + 4))) {
|
||||
qp->r_flags |= IPATH_R_REUSE_SGE;
|
||||
dev->n_pkt_drops++;
|
||||
goto done;
|
||||
}
|
||||
qp->r_rcv_len += pmtu;
|
||||
if (unlikely(qp->r_rcv_len > qp->r_len)) {
|
||||
qp->r_flags |= IPATH_R_REUSE_SGE;
|
||||
dev->n_pkt_drops++;
|
||||
goto done;
|
||||
}
|
||||
ipath_copy_sge(&qp->r_sge, data, pmtu);
|
||||
break;
|
||||
|
||||
case OP(SEND_LAST_WITH_IMMEDIATE):
|
||||
send_last_imm:
|
||||
if (header_in_data) {
|
||||
wc.ex.imm_data = *(__be32 *) data;
|
||||
data += sizeof(__be32);
|
||||
} else {
|
||||
/* Immediate data comes after BTH */
|
||||
wc.ex.imm_data = ohdr->u.imm_data;
|
||||
}
|
||||
hdrsize += 4;
|
||||
wc.wc_flags = IB_WC_WITH_IMM;
|
||||
/* FALLTHROUGH */
|
||||
case OP(SEND_LAST):
|
||||
send_last:
|
||||
/* Get the number of bytes the message was padded by. */
|
||||
pad = (be32_to_cpu(ohdr->bth[0]) >> 20) & 3;
|
||||
/* Check for invalid length. */
|
||||
/* XXX LAST len should be >= 1 */
|
||||
if (unlikely(tlen < (hdrsize + pad + 4))) {
|
||||
qp->r_flags |= IPATH_R_REUSE_SGE;
|
||||
dev->n_pkt_drops++;
|
||||
goto done;
|
||||
}
|
||||
/* Don't count the CRC. */
|
||||
tlen -= (hdrsize + pad + 4);
|
||||
wc.byte_len = tlen + qp->r_rcv_len;
|
||||
if (unlikely(wc.byte_len > qp->r_len)) {
|
||||
qp->r_flags |= IPATH_R_REUSE_SGE;
|
||||
dev->n_pkt_drops++;
|
||||
goto done;
|
||||
}
|
||||
wc.opcode = IB_WC_RECV;
|
||||
last_imm:
|
||||
ipath_copy_sge(&qp->r_sge, data, tlen);
|
||||
wc.wr_id = qp->r_wr_id;
|
||||
wc.status = IB_WC_SUCCESS;
|
||||
wc.qp = &qp->ibqp;
|
||||
wc.src_qp = qp->remote_qpn;
|
||||
wc.slid = qp->remote_ah_attr.dlid;
|
||||
wc.sl = qp->remote_ah_attr.sl;
|
||||
/* Signal completion event if the solicited bit is set. */
|
||||
ipath_cq_enter(to_icq(qp->ibqp.recv_cq), &wc,
|
||||
(ohdr->bth[0] &
|
||||
cpu_to_be32(1 << 23)) != 0);
|
||||
break;
|
||||
|
||||
case OP(RDMA_WRITE_FIRST):
|
||||
case OP(RDMA_WRITE_ONLY):
|
||||
case OP(RDMA_WRITE_ONLY_WITH_IMMEDIATE): /* consume RWQE */
|
||||
rdma_first:
|
||||
/* RETH comes after BTH */
|
||||
if (!header_in_data)
|
||||
reth = &ohdr->u.rc.reth;
|
||||
else {
|
||||
reth = (struct ib_reth *)data;
|
||||
data += sizeof(*reth);
|
||||
}
|
||||
hdrsize += sizeof(*reth);
|
||||
qp->r_len = be32_to_cpu(reth->length);
|
||||
qp->r_rcv_len = 0;
|
||||
if (qp->r_len != 0) {
|
||||
u32 rkey = be32_to_cpu(reth->rkey);
|
||||
u64 vaddr = be64_to_cpu(reth->vaddr);
|
||||
int ok;
|
||||
|
||||
/* Check rkey */
|
||||
ok = ipath_rkey_ok(qp, &qp->r_sge, qp->r_len,
|
||||
vaddr, rkey,
|
||||
IB_ACCESS_REMOTE_WRITE);
|
||||
if (unlikely(!ok)) {
|
||||
dev->n_pkt_drops++;
|
||||
goto done;
|
||||
}
|
||||
} else {
|
||||
qp->r_sge.sg_list = NULL;
|
||||
qp->r_sge.sge.mr = NULL;
|
||||
qp->r_sge.sge.vaddr = NULL;
|
||||
qp->r_sge.sge.length = 0;
|
||||
qp->r_sge.sge.sge_length = 0;
|
||||
}
|
||||
if (unlikely(!(qp->qp_access_flags &
|
||||
IB_ACCESS_REMOTE_WRITE))) {
|
||||
dev->n_pkt_drops++;
|
||||
goto done;
|
||||
}
|
||||
if (opcode == OP(RDMA_WRITE_ONLY))
|
||||
goto rdma_last;
|
||||
else if (opcode == OP(RDMA_WRITE_ONLY_WITH_IMMEDIATE))
|
||||
goto rdma_last_imm;
|
||||
/* FALLTHROUGH */
|
||||
case OP(RDMA_WRITE_MIDDLE):
|
||||
/* Check for invalid length PMTU or posted rwqe len. */
|
||||
if (unlikely(tlen != (hdrsize + pmtu + 4))) {
|
||||
dev->n_pkt_drops++;
|
||||
goto done;
|
||||
}
|
||||
qp->r_rcv_len += pmtu;
|
||||
if (unlikely(qp->r_rcv_len > qp->r_len)) {
|
||||
dev->n_pkt_drops++;
|
||||
goto done;
|
||||
}
|
||||
ipath_copy_sge(&qp->r_sge, data, pmtu);
|
||||
break;
|
||||
|
||||
case OP(RDMA_WRITE_LAST_WITH_IMMEDIATE):
|
||||
rdma_last_imm:
|
||||
if (header_in_data) {
|
||||
wc.ex.imm_data = *(__be32 *) data;
|
||||
data += sizeof(__be32);
|
||||
} else {
|
||||
/* Immediate data comes after BTH */
|
||||
wc.ex.imm_data = ohdr->u.imm_data;
|
||||
}
|
||||
hdrsize += 4;
|
||||
wc.wc_flags = IB_WC_WITH_IMM;
|
||||
|
||||
/* Get the number of bytes the message was padded by. */
|
||||
pad = (be32_to_cpu(ohdr->bth[0]) >> 20) & 3;
|
||||
/* Check for invalid length. */
|
||||
/* XXX LAST len should be >= 1 */
|
||||
if (unlikely(tlen < (hdrsize + pad + 4))) {
|
||||
dev->n_pkt_drops++;
|
||||
goto done;
|
||||
}
|
||||
/* Don't count the CRC. */
|
||||
tlen -= (hdrsize + pad + 4);
|
||||
if (unlikely(tlen + qp->r_rcv_len != qp->r_len)) {
|
||||
dev->n_pkt_drops++;
|
||||
goto done;
|
||||
}
|
||||
if (qp->r_flags & IPATH_R_REUSE_SGE)
|
||||
qp->r_flags &= ~IPATH_R_REUSE_SGE;
|
||||
else if (!ipath_get_rwqe(qp, 1)) {
|
||||
dev->n_pkt_drops++;
|
||||
goto done;
|
||||
}
|
||||
wc.byte_len = qp->r_len;
|
||||
wc.opcode = IB_WC_RECV_RDMA_WITH_IMM;
|
||||
goto last_imm;
|
||||
|
||||
case OP(RDMA_WRITE_LAST):
|
||||
rdma_last:
|
||||
/* Get the number of bytes the message was padded by. */
|
||||
pad = (be32_to_cpu(ohdr->bth[0]) >> 20) & 3;
|
||||
/* Check for invalid length. */
|
||||
/* XXX LAST len should be >= 1 */
|
||||
if (unlikely(tlen < (hdrsize + pad + 4))) {
|
||||
dev->n_pkt_drops++;
|
||||
goto done;
|
||||
}
|
||||
/* Don't count the CRC. */
|
||||
tlen -= (hdrsize + pad + 4);
|
||||
if (unlikely(tlen + qp->r_rcv_len != qp->r_len)) {
|
||||
dev->n_pkt_drops++;
|
||||
goto done;
|
||||
}
|
||||
ipath_copy_sge(&qp->r_sge, data, tlen);
|
||||
break;
|
||||
|
||||
default:
|
||||
/* Drop packet for unknown opcodes. */
|
||||
dev->n_pkt_drops++;
|
||||
goto done;
|
||||
}
|
||||
qp->r_psn++;
|
||||
qp->r_state = opcode;
|
||||
done:
|
||||
return;
|
||||
}
|
|
@ -1,579 +0,0 @@
|
|||
/*
|
||||
* Copyright (c) 2006, 2007, 2008 QLogic Corporation. All rights reserved.
|
||||
* Copyright (c) 2005, 2006 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.
|
||||
*/
|
||||
|
||||
#include <rdma/ib_smi.h>
|
||||
|
||||
#include "ipath_verbs.h"
|
||||
#include "ipath_kernel.h"
|
||||
|
||||
/**
|
||||
* ipath_ud_loopback - handle send on loopback QPs
|
||||
* @sqp: the sending QP
|
||||
* @swqe: the send work request
|
||||
*
|
||||
* This is called from ipath_make_ud_req() to forward a WQE addressed
|
||||
* to the same HCA.
|
||||
* Note that the receive interrupt handler may be calling ipath_ud_rcv()
|
||||
* while this is being called.
|
||||
*/
|
||||
static void ipath_ud_loopback(struct ipath_qp *sqp, struct ipath_swqe *swqe)
|
||||
{
|
||||
struct ipath_ibdev *dev = to_idev(sqp->ibqp.device);
|
||||
struct ipath_qp *qp;
|
||||
struct ib_ah_attr *ah_attr;
|
||||
unsigned long flags;
|
||||
struct ipath_rq *rq;
|
||||
struct ipath_srq *srq;
|
||||
struct ipath_sge_state rsge;
|
||||
struct ipath_sge *sge;
|
||||
struct ipath_rwq *wq;
|
||||
struct ipath_rwqe *wqe;
|
||||
void (*handler)(struct ib_event *, void *);
|
||||
struct ib_wc wc;
|
||||
u32 tail;
|
||||
u32 rlen;
|
||||
u32 length;
|
||||
|
||||
qp = ipath_lookup_qpn(&dev->qp_table, swqe->ud_wr.remote_qpn);
|
||||
if (!qp || !(ib_ipath_state_ops[qp->state] & IPATH_PROCESS_RECV_OK)) {
|
||||
dev->n_pkt_drops++;
|
||||
goto done;
|
||||
}
|
||||
|
||||
/*
|
||||
* Check that the qkey matches (except for QP0, see 9.6.1.4.1).
|
||||
* Qkeys with the high order bit set mean use the
|
||||
* qkey from the QP context instead of the WR (see 10.2.5).
|
||||
*/
|
||||
if (unlikely(qp->ibqp.qp_num &&
|
||||
((int) swqe->ud_wr.remote_qkey < 0 ?
|
||||
sqp->qkey : swqe->ud_wr.remote_qkey) != qp->qkey)) {
|
||||
/* XXX OK to lose a count once in a while. */
|
||||
dev->qkey_violations++;
|
||||
dev->n_pkt_drops++;
|
||||
goto drop;
|
||||
}
|
||||
|
||||
/*
|
||||
* A GRH is expected to precede the data even if not
|
||||
* present on the wire.
|
||||
*/
|
||||
length = swqe->length;
|
||||
memset(&wc, 0, sizeof wc);
|
||||
wc.byte_len = length + sizeof(struct ib_grh);
|
||||
|
||||
if (swqe->wr.opcode == IB_WR_SEND_WITH_IMM) {
|
||||
wc.wc_flags = IB_WC_WITH_IMM;
|
||||
wc.ex.imm_data = swqe->wr.ex.imm_data;
|
||||
}
|
||||
|
||||
/*
|
||||
* This would be a lot simpler if we could call ipath_get_rwqe()
|
||||
* but that uses state that the receive interrupt handler uses
|
||||
* so we would need to lock out receive interrupts while doing
|
||||
* local loopback.
|
||||
*/
|
||||
if (qp->ibqp.srq) {
|
||||
srq = to_isrq(qp->ibqp.srq);
|
||||
handler = srq->ibsrq.event_handler;
|
||||
rq = &srq->rq;
|
||||
} else {
|
||||
srq = NULL;
|
||||
handler = NULL;
|
||||
rq = &qp->r_rq;
|
||||
}
|
||||
|
||||
/*
|
||||
* Get the next work request entry to find where to put the data.
|
||||
* Note that it is safe to drop the lock after changing rq->tail
|
||||
* since ipath_post_receive() won't fill the empty slot.
|
||||
*/
|
||||
spin_lock_irqsave(&rq->lock, flags);
|
||||
wq = rq->wq;
|
||||
tail = wq->tail;
|
||||
/* Validate tail before using it since it is user writable. */
|
||||
if (tail >= rq->size)
|
||||
tail = 0;
|
||||
if (unlikely(tail == wq->head)) {
|
||||
spin_unlock_irqrestore(&rq->lock, flags);
|
||||
dev->n_pkt_drops++;
|
||||
goto drop;
|
||||
}
|
||||
wqe = get_rwqe_ptr(rq, tail);
|
||||
rsge.sg_list = qp->r_ud_sg_list;
|
||||
if (!ipath_init_sge(qp, wqe, &rlen, &rsge)) {
|
||||
spin_unlock_irqrestore(&rq->lock, flags);
|
||||
dev->n_pkt_drops++;
|
||||
goto drop;
|
||||
}
|
||||
/* Silently drop packets which are too big. */
|
||||
if (wc.byte_len > rlen) {
|
||||
spin_unlock_irqrestore(&rq->lock, flags);
|
||||
dev->n_pkt_drops++;
|
||||
goto drop;
|
||||
}
|
||||
if (++tail >= rq->size)
|
||||
tail = 0;
|
||||
wq->tail = tail;
|
||||
wc.wr_id = wqe->wr_id;
|
||||
if (handler) {
|
||||
u32 n;
|
||||
|
||||
/*
|
||||
* validate head pointer value and compute
|
||||
* the number of remaining WQEs.
|
||||
*/
|
||||
n = wq->head;
|
||||
if (n >= rq->size)
|
||||
n = 0;
|
||||
if (n < tail)
|
||||
n += rq->size - tail;
|
||||
else
|
||||
n -= tail;
|
||||
if (n < srq->limit) {
|
||||
struct ib_event ev;
|
||||
|
||||
srq->limit = 0;
|
||||
spin_unlock_irqrestore(&rq->lock, flags);
|
||||
ev.device = qp->ibqp.device;
|
||||
ev.element.srq = qp->ibqp.srq;
|
||||
ev.event = IB_EVENT_SRQ_LIMIT_REACHED;
|
||||
handler(&ev, srq->ibsrq.srq_context);
|
||||
} else
|
||||
spin_unlock_irqrestore(&rq->lock, flags);
|
||||
} else
|
||||
spin_unlock_irqrestore(&rq->lock, flags);
|
||||
|
||||
ah_attr = &to_iah(swqe->ud_wr.ah)->attr;
|
||||
if (ah_attr->ah_flags & IB_AH_GRH) {
|
||||
ipath_copy_sge(&rsge, &ah_attr->grh, sizeof(struct ib_grh));
|
||||
wc.wc_flags |= IB_WC_GRH;
|
||||
} else
|
||||
ipath_skip_sge(&rsge, sizeof(struct ib_grh));
|
||||
sge = swqe->sg_list;
|
||||
while (length) {
|
||||
u32 len = sge->length;
|
||||
|
||||
if (len > length)
|
||||
len = length;
|
||||
if (len > sge->sge_length)
|
||||
len = sge->sge_length;
|
||||
BUG_ON(len == 0);
|
||||
ipath_copy_sge(&rsge, sge->vaddr, len);
|
||||
sge->vaddr += len;
|
||||
sge->length -= len;
|
||||
sge->sge_length -= len;
|
||||
if (sge->sge_length == 0) {
|
||||
if (--swqe->wr.num_sge)
|
||||
sge++;
|
||||
} 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;
|
||||
}
|
||||
wc.status = IB_WC_SUCCESS;
|
||||
wc.opcode = IB_WC_RECV;
|
||||
wc.qp = &qp->ibqp;
|
||||
wc.src_qp = sqp->ibqp.qp_num;
|
||||
/* XXX do we know which pkey matched? Only needed for GSI. */
|
||||
wc.pkey_index = 0;
|
||||
wc.slid = dev->dd->ipath_lid |
|
||||
(ah_attr->src_path_bits &
|
||||
((1 << dev->dd->ipath_lmc) - 1));
|
||||
wc.sl = ah_attr->sl;
|
||||
wc.dlid_path_bits =
|
||||
ah_attr->dlid & ((1 << dev->dd->ipath_lmc) - 1);
|
||||
wc.port_num = 1;
|
||||
/* Signal completion event if the solicited bit is set. */
|
||||
ipath_cq_enter(to_icq(qp->ibqp.recv_cq), &wc,
|
||||
swqe->ud_wr.wr.send_flags & IB_SEND_SOLICITED);
|
||||
drop:
|
||||
if (atomic_dec_and_test(&qp->refcount))
|
||||
wake_up(&qp->wait);
|
||||
done:;
|
||||
}
|
||||
|
||||
/**
|
||||
* ipath_make_ud_req - construct a UD request packet
|
||||
* @qp: the QP
|
||||
*
|
||||
* Return 1 if constructed; otherwise, return 0.
|
||||
*/
|
||||
int ipath_make_ud_req(struct ipath_qp *qp)
|
||||
{
|
||||
struct ipath_ibdev *dev = to_idev(qp->ibqp.device);
|
||||
struct ipath_other_headers *ohdr;
|
||||
struct ib_ah_attr *ah_attr;
|
||||
struct ipath_swqe *wqe;
|
||||
unsigned long flags;
|
||||
u32 nwords;
|
||||
u32 extra_bytes;
|
||||
u32 bth0;
|
||||
u16 lrh0;
|
||||
u16 lid;
|
||||
int ret = 0;
|
||||
int next_cur;
|
||||
|
||||
spin_lock_irqsave(&qp->s_lock, flags);
|
||||
|
||||
if (!(ib_ipath_state_ops[qp->state] & IPATH_PROCESS_NEXT_SEND_OK)) {
|
||||
if (!(ib_ipath_state_ops[qp->state] & IPATH_FLUSH_SEND))
|
||||
goto bail;
|
||||
/* We are in the error state, flush the work request. */
|
||||
if (qp->s_last == qp->s_head)
|
||||
goto bail;
|
||||
/* If DMAs are in progress, we can't flush immediately. */
|
||||
if (atomic_read(&qp->s_dma_busy)) {
|
||||
qp->s_flags |= IPATH_S_WAIT_DMA;
|
||||
goto bail;
|
||||
}
|
||||
wqe = get_swqe_ptr(qp, qp->s_last);
|
||||
ipath_send_complete(qp, wqe, IB_WC_WR_FLUSH_ERR);
|
||||
goto done;
|
||||
}
|
||||
|
||||
if (qp->s_cur == qp->s_head)
|
||||
goto bail;
|
||||
|
||||
wqe = get_swqe_ptr(qp, qp->s_cur);
|
||||
next_cur = qp->s_cur + 1;
|
||||
if (next_cur >= qp->s_size)
|
||||
next_cur = 0;
|
||||
|
||||
/* Construct the header. */
|
||||
ah_attr = &to_iah(wqe->ud_wr.ah)->attr;
|
||||
if (ah_attr->dlid >= IPATH_MULTICAST_LID_BASE) {
|
||||
if (ah_attr->dlid != IPATH_PERMISSIVE_LID)
|
||||
dev->n_multicast_xmit++;
|
||||
else
|
||||
dev->n_unicast_xmit++;
|
||||
} else {
|
||||
dev->n_unicast_xmit++;
|
||||
lid = ah_attr->dlid & ~((1 << dev->dd->ipath_lmc) - 1);
|
||||
if (unlikely(lid == dev->dd->ipath_lid)) {
|
||||
/*
|
||||
* If DMAs are in progress, we can't generate
|
||||
* a completion for the loopback packet since
|
||||
* it would be out of order.
|
||||
* XXX Instead of waiting, we could queue a
|
||||
* zero length descriptor so we get a callback.
|
||||
*/
|
||||
if (atomic_read(&qp->s_dma_busy)) {
|
||||
qp->s_flags |= IPATH_S_WAIT_DMA;
|
||||
goto bail;
|
||||
}
|
||||
qp->s_cur = next_cur;
|
||||
spin_unlock_irqrestore(&qp->s_lock, flags);
|
||||
ipath_ud_loopback(qp, wqe);
|
||||
spin_lock_irqsave(&qp->s_lock, flags);
|
||||
ipath_send_complete(qp, wqe, IB_WC_SUCCESS);
|
||||
goto done;
|
||||
}
|
||||
}
|
||||
|
||||
qp->s_cur = next_cur;
|
||||
extra_bytes = -wqe->length & 3;
|
||||
nwords = (wqe->length + extra_bytes) >> 2;
|
||||
|
||||
/* header size in 32-bit words LRH+BTH+DETH = (8+12+8)/4. */
|
||||
qp->s_hdrwords = 7;
|
||||
qp->s_cur_size = wqe->length;
|
||||
qp->s_cur_sge = &qp->s_sge;
|
||||
qp->s_dmult = ah_attr->static_rate;
|
||||
qp->s_wqe = wqe;
|
||||
qp->s_sge.sge = wqe->sg_list[0];
|
||||
qp->s_sge.sg_list = wqe->sg_list + 1;
|
||||
qp->s_sge.num_sge = wqe->ud_wr.wr.num_sge;
|
||||
|
||||
if (ah_attr->ah_flags & IB_AH_GRH) {
|
||||
/* Header size in 32-bit words. */
|
||||
qp->s_hdrwords += ipath_make_grh(dev, &qp->s_hdr.u.l.grh,
|
||||
&ah_attr->grh,
|
||||
qp->s_hdrwords, nwords);
|
||||
lrh0 = IPATH_LRH_GRH;
|
||||
ohdr = &qp->s_hdr.u.l.oth;
|
||||
/*
|
||||
* Don't worry about sending to locally attached multicast
|
||||
* QPs. It is unspecified by the spec. what happens.
|
||||
*/
|
||||
} else {
|
||||
/* Header size in 32-bit words. */
|
||||
lrh0 = IPATH_LRH_BTH;
|
||||
ohdr = &qp->s_hdr.u.oth;
|
||||
}
|
||||
if (wqe->ud_wr.wr.opcode == IB_WR_SEND_WITH_IMM) {
|
||||
qp->s_hdrwords++;
|
||||
ohdr->u.ud.imm_data = wqe->ud_wr.wr.ex.imm_data;
|
||||
bth0 = IB_OPCODE_UD_SEND_ONLY_WITH_IMMEDIATE << 24;
|
||||
} else
|
||||
bth0 = IB_OPCODE_UD_SEND_ONLY << 24;
|
||||
lrh0 |= ah_attr->sl << 4;
|
||||
if (qp->ibqp.qp_type == IB_QPT_SMI)
|
||||
lrh0 |= 0xF000; /* Set VL (see ch. 13.5.3.1) */
|
||||
qp->s_hdr.lrh[0] = cpu_to_be16(lrh0);
|
||||
qp->s_hdr.lrh[1] = cpu_to_be16(ah_attr->dlid); /* DEST LID */
|
||||
qp->s_hdr.lrh[2] = cpu_to_be16(qp->s_hdrwords + nwords +
|
||||
SIZE_OF_CRC);
|
||||
lid = dev->dd->ipath_lid;
|
||||
if (lid) {
|
||||
lid |= ah_attr->src_path_bits &
|
||||
((1 << dev->dd->ipath_lmc) - 1);
|
||||
qp->s_hdr.lrh[3] = cpu_to_be16(lid);
|
||||
} else
|
||||
qp->s_hdr.lrh[3] = IB_LID_PERMISSIVE;
|
||||
if (wqe->ud_wr.wr.send_flags & IB_SEND_SOLICITED)
|
||||
bth0 |= 1 << 23;
|
||||
bth0 |= extra_bytes << 20;
|
||||
bth0 |= qp->ibqp.qp_type == IB_QPT_SMI ? IPATH_DEFAULT_P_KEY :
|
||||
ipath_get_pkey(dev->dd, qp->s_pkey_index);
|
||||
ohdr->bth[0] = cpu_to_be32(bth0);
|
||||
/*
|
||||
* Use the multicast QP if the destination LID is a multicast LID.
|
||||
*/
|
||||
ohdr->bth[1] = ah_attr->dlid >= IPATH_MULTICAST_LID_BASE &&
|
||||
ah_attr->dlid != IPATH_PERMISSIVE_LID ?
|
||||
cpu_to_be32(IPATH_MULTICAST_QPN) :
|
||||
cpu_to_be32(wqe->ud_wr.remote_qpn);
|
||||
ohdr->bth[2] = cpu_to_be32(qp->s_next_psn++ & IPATH_PSN_MASK);
|
||||
/*
|
||||
* Qkeys with the high order bit set mean use the
|
||||
* qkey from the QP context instead of the WR (see 10.2.5).
|
||||
*/
|
||||
ohdr->u.ud.deth[0] = cpu_to_be32((int)wqe->ud_wr.remote_qkey < 0 ?
|
||||
qp->qkey : wqe->ud_wr.remote_qkey);
|
||||
ohdr->u.ud.deth[1] = cpu_to_be32(qp->ibqp.qp_num);
|
||||
|
||||
done:
|
||||
ret = 1;
|
||||
goto unlock;
|
||||
|
||||
bail:
|
||||
qp->s_flags &= ~IPATH_S_BUSY;
|
||||
unlock:
|
||||
spin_unlock_irqrestore(&qp->s_lock, flags);
|
||||
return ret;
|
||||
}
|
||||
|
||||
/**
|
||||
* ipath_ud_rcv - receive an incoming UD packet
|
||||
* @dev: the device the packet came in on
|
||||
* @hdr: the packet header
|
||||
* @has_grh: true if the packet has a GRH
|
||||
* @data: the packet data
|
||||
* @tlen: the packet length
|
||||
* @qp: the QP the packet came on
|
||||
*
|
||||
* This is called from ipath_qp_rcv() to process an incoming UD packet
|
||||
* for the given QP.
|
||||
* Called at interrupt level.
|
||||
*/
|
||||
void ipath_ud_rcv(struct ipath_ibdev *dev, struct ipath_ib_header *hdr,
|
||||
int has_grh, void *data, u32 tlen, struct ipath_qp *qp)
|
||||
{
|
||||
struct ipath_other_headers *ohdr;
|
||||
int opcode;
|
||||
u32 hdrsize;
|
||||
u32 pad;
|
||||
struct ib_wc wc;
|
||||
u32 qkey;
|
||||
u32 src_qp;
|
||||
u16 dlid;
|
||||
int header_in_data;
|
||||
|
||||
/* Check for GRH */
|
||||
if (!has_grh) {
|
||||
ohdr = &hdr->u.oth;
|
||||
hdrsize = 8 + 12 + 8; /* LRH + BTH + DETH */
|
||||
qkey = be32_to_cpu(ohdr->u.ud.deth[0]);
|
||||
src_qp = be32_to_cpu(ohdr->u.ud.deth[1]);
|
||||
header_in_data = 0;
|
||||
} else {
|
||||
ohdr = &hdr->u.l.oth;
|
||||
hdrsize = 8 + 40 + 12 + 8; /* LRH + GRH + BTH + DETH */
|
||||
/*
|
||||
* The header with GRH is 68 bytes and the core driver sets
|
||||
* the eager header buffer size to 56 bytes so the last 12
|
||||
* bytes of the IB header is in the data buffer.
|
||||
*/
|
||||
header_in_data = dev->dd->ipath_rcvhdrentsize == 16;
|
||||
if (header_in_data) {
|
||||
qkey = be32_to_cpu(((__be32 *) data)[1]);
|
||||
src_qp = be32_to_cpu(((__be32 *) data)[2]);
|
||||
data += 12;
|
||||
} else {
|
||||
qkey = be32_to_cpu(ohdr->u.ud.deth[0]);
|
||||
src_qp = be32_to_cpu(ohdr->u.ud.deth[1]);
|
||||
}
|
||||
}
|
||||
src_qp &= IPATH_QPN_MASK;
|
||||
|
||||
/*
|
||||
* Check that the permissive LID is only used on QP0
|
||||
* and the QKEY matches (see 9.6.1.4.1 and 9.6.1.5.1).
|
||||
*/
|
||||
if (qp->ibqp.qp_num) {
|
||||
if (unlikely(hdr->lrh[1] == IB_LID_PERMISSIVE ||
|
||||
hdr->lrh[3] == IB_LID_PERMISSIVE)) {
|
||||
dev->n_pkt_drops++;
|
||||
goto bail;
|
||||
}
|
||||
if (unlikely(qkey != qp->qkey)) {
|
||||
/* XXX OK to lose a count once in a while. */
|
||||
dev->qkey_violations++;
|
||||
dev->n_pkt_drops++;
|
||||
goto bail;
|
||||
}
|
||||
} else if (hdr->lrh[1] == IB_LID_PERMISSIVE ||
|
||||
hdr->lrh[3] == IB_LID_PERMISSIVE) {
|
||||
struct ib_smp *smp = (struct ib_smp *) data;
|
||||
|
||||
if (smp->mgmt_class != IB_MGMT_CLASS_SUBN_DIRECTED_ROUTE) {
|
||||
dev->n_pkt_drops++;
|
||||
goto bail;
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* The opcode is in the low byte when its in network order
|
||||
* (top byte when in host order).
|
||||
*/
|
||||
opcode = be32_to_cpu(ohdr->bth[0]) >> 24;
|
||||
if (qp->ibqp.qp_num > 1 &&
|
||||
opcode == IB_OPCODE_UD_SEND_ONLY_WITH_IMMEDIATE) {
|
||||
if (header_in_data) {
|
||||
wc.ex.imm_data = *(__be32 *) data;
|
||||
data += sizeof(__be32);
|
||||
} else
|
||||
wc.ex.imm_data = ohdr->u.ud.imm_data;
|
||||
wc.wc_flags = IB_WC_WITH_IMM;
|
||||
hdrsize += sizeof(u32);
|
||||
} else if (opcode == IB_OPCODE_UD_SEND_ONLY) {
|
||||
wc.ex.imm_data = 0;
|
||||
wc.wc_flags = 0;
|
||||
} else {
|
||||
dev->n_pkt_drops++;
|
||||
goto bail;
|
||||
}
|
||||
|
||||
/* Get the number of bytes the message was padded by. */
|
||||
pad = (be32_to_cpu(ohdr->bth[0]) >> 20) & 3;
|
||||
if (unlikely(tlen < (hdrsize + pad + 4))) {
|
||||
/* Drop incomplete packets. */
|
||||
dev->n_pkt_drops++;
|
||||
goto bail;
|
||||
}
|
||||
tlen -= hdrsize + pad + 4;
|
||||
|
||||
/* Drop invalid MAD packets (see 13.5.3.1). */
|
||||
if (unlikely((qp->ibqp.qp_num == 0 &&
|
||||
(tlen != 256 ||
|
||||
(be16_to_cpu(hdr->lrh[0]) >> 12) != 15)) ||
|
||||
(qp->ibqp.qp_num == 1 &&
|
||||
(tlen != 256 ||
|
||||
(be16_to_cpu(hdr->lrh[0]) >> 12) == 15)))) {
|
||||
dev->n_pkt_drops++;
|
||||
goto bail;
|
||||
}
|
||||
|
||||
/*
|
||||
* A GRH is expected to precede the data even if not
|
||||
* present on the wire.
|
||||
*/
|
||||
wc.byte_len = tlen + sizeof(struct ib_grh);
|
||||
|
||||
/*
|
||||
* Get the next work request entry to find where to put the data.
|
||||
*/
|
||||
if (qp->r_flags & IPATH_R_REUSE_SGE)
|
||||
qp->r_flags &= ~IPATH_R_REUSE_SGE;
|
||||
else if (!ipath_get_rwqe(qp, 0)) {
|
||||
/*
|
||||
* Count VL15 packets dropped due to no receive buffer.
|
||||
* Otherwise, count them as buffer overruns since usually,
|
||||
* the HW will be able to receive packets even if there are
|
||||
* no QPs with posted receive buffers.
|
||||
*/
|
||||
if (qp->ibqp.qp_num == 0)
|
||||
dev->n_vl15_dropped++;
|
||||
else
|
||||
dev->rcv_errors++;
|
||||
goto bail;
|
||||
}
|
||||
/* Silently drop packets which are too big. */
|
||||
if (wc.byte_len > qp->r_len) {
|
||||
qp->r_flags |= IPATH_R_REUSE_SGE;
|
||||
dev->n_pkt_drops++;
|
||||
goto bail;
|
||||
}
|
||||
if (has_grh) {
|
||||
ipath_copy_sge(&qp->r_sge, &hdr->u.l.grh,
|
||||
sizeof(struct ib_grh));
|
||||
wc.wc_flags |= IB_WC_GRH;
|
||||
} else
|
||||
ipath_skip_sge(&qp->r_sge, sizeof(struct ib_grh));
|
||||
ipath_copy_sge(&qp->r_sge, data,
|
||||
wc.byte_len - sizeof(struct ib_grh));
|
||||
if (!test_and_clear_bit(IPATH_R_WRID_VALID, &qp->r_aflags))
|
||||
goto bail;
|
||||
wc.wr_id = qp->r_wr_id;
|
||||
wc.status = IB_WC_SUCCESS;
|
||||
wc.opcode = IB_WC_RECV;
|
||||
wc.vendor_err = 0;
|
||||
wc.qp = &qp->ibqp;
|
||||
wc.src_qp = src_qp;
|
||||
/* XXX do we know which pkey matched? Only needed for GSI. */
|
||||
wc.pkey_index = 0;
|
||||
wc.slid = be16_to_cpu(hdr->lrh[3]);
|
||||
wc.sl = (be16_to_cpu(hdr->lrh[0]) >> 4) & 0xF;
|
||||
dlid = be16_to_cpu(hdr->lrh[1]);
|
||||
/*
|
||||
* Save the LMC lower bits if the destination LID is a unicast LID.
|
||||
*/
|
||||
wc.dlid_path_bits = dlid >= IPATH_MULTICAST_LID_BASE ? 0 :
|
||||
dlid & ((1 << dev->dd->ipath_lmc) - 1);
|
||||
wc.port_num = 1;
|
||||
/* Signal completion event if the solicited bit is set. */
|
||||
ipath_cq_enter(to_icq(qp->ibqp.recv_cq), &wc,
|
||||
(ohdr->bth[0] &
|
||||
cpu_to_be32(1 << 23)) != 0);
|
||||
|
||||
bail:;
|
||||
}
|
|
@ -1,228 +0,0 @@
|
|||
/*
|
||||
* Copyright (c) 2006, 2007 QLogic Corporation. All rights reserved.
|
||||
* Copyright (c) 2003, 2004, 2005, 2006 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.
|
||||
*/
|
||||
|
||||
#include <linux/mm.h>
|
||||
#include <linux/device.h>
|
||||
#include <linux/slab.h>
|
||||
|
||||
#include "ipath_kernel.h"
|
||||
|
||||
static void __ipath_release_user_pages(struct page **p, size_t num_pages,
|
||||
int dirty)
|
||||
{
|
||||
size_t i;
|
||||
|
||||
for (i = 0; i < num_pages; i++) {
|
||||
ipath_cdbg(MM, "%lu/%lu put_page %p\n", (unsigned long) i,
|
||||
(unsigned long) num_pages, p[i]);
|
||||
if (dirty)
|
||||
set_page_dirty_lock(p[i]);
|
||||
put_page(p[i]);
|
||||
}
|
||||
}
|
||||
|
||||
/* call with current->mm->mmap_sem held */
|
||||
static int __ipath_get_user_pages(unsigned long start_page, size_t num_pages,
|
||||
struct page **p)
|
||||
{
|
||||
unsigned long lock_limit;
|
||||
size_t got;
|
||||
int ret;
|
||||
|
||||
lock_limit = rlimit(RLIMIT_MEMLOCK) >> PAGE_SHIFT;
|
||||
|
||||
if (num_pages > lock_limit) {
|
||||
ret = -ENOMEM;
|
||||
goto bail;
|
||||
}
|
||||
|
||||
ipath_cdbg(VERBOSE, "pin %lx pages from vaddr %lx\n",
|
||||
(unsigned long) num_pages, start_page);
|
||||
|
||||
for (got = 0; got < num_pages; got += ret) {
|
||||
ret = get_user_pages(current, current->mm,
|
||||
start_page + got * PAGE_SIZE,
|
||||
num_pages - got, 1, 1,
|
||||
p + got, NULL);
|
||||
if (ret < 0)
|
||||
goto bail_release;
|
||||
}
|
||||
|
||||
current->mm->pinned_vm += num_pages;
|
||||
|
||||
ret = 0;
|
||||
goto bail;
|
||||
|
||||
bail_release:
|
||||
__ipath_release_user_pages(p, got, 0);
|
||||
bail:
|
||||
return ret;
|
||||
}
|
||||
|
||||
/**
|
||||
* ipath_map_page - a safety wrapper around pci_map_page()
|
||||
*
|
||||
* A dma_addr of all 0's is interpreted by the chip as "disabled".
|
||||
* Unfortunately, it can also be a valid dma_addr returned on some
|
||||
* architectures.
|
||||
*
|
||||
* The powerpc iommu assigns dma_addrs in ascending order, so we don't
|
||||
* have to bother with retries or mapping a dummy page to insure we
|
||||
* don't just get the same mapping again.
|
||||
*
|
||||
* I'm sure we won't be so lucky with other iommu's, so FIXME.
|
||||
*/
|
||||
dma_addr_t ipath_map_page(struct pci_dev *hwdev, struct page *page,
|
||||
unsigned long offset, size_t size, int direction)
|
||||
{
|
||||
dma_addr_t phys;
|
||||
|
||||
phys = pci_map_page(hwdev, page, offset, size, direction);
|
||||
|
||||
if (phys == 0) {
|
||||
pci_unmap_page(hwdev, phys, size, direction);
|
||||
phys = pci_map_page(hwdev, page, offset, size, direction);
|
||||
/*
|
||||
* FIXME: If we get 0 again, we should keep this page,
|
||||
* map another, then free the 0 page.
|
||||
*/
|
||||
}
|
||||
|
||||
return phys;
|
||||
}
|
||||
|
||||
/**
|
||||
* ipath_map_single - a safety wrapper around pci_map_single()
|
||||
*
|
||||
* Same idea as ipath_map_page().
|
||||
*/
|
||||
dma_addr_t ipath_map_single(struct pci_dev *hwdev, void *ptr, size_t size,
|
||||
int direction)
|
||||
{
|
||||
dma_addr_t phys;
|
||||
|
||||
phys = pci_map_single(hwdev, ptr, size, direction);
|
||||
|
||||
if (phys == 0) {
|
||||
pci_unmap_single(hwdev, phys, size, direction);
|
||||
phys = pci_map_single(hwdev, ptr, size, direction);
|
||||
/*
|
||||
* FIXME: If we get 0 again, we should keep this page,
|
||||
* map another, then free the 0 page.
|
||||
*/
|
||||
}
|
||||
|
||||
return phys;
|
||||
}
|
||||
|
||||
/**
|
||||
* ipath_get_user_pages - lock user pages into memory
|
||||
* @start_page: the start page
|
||||
* @num_pages: the number of pages
|
||||
* @p: the output page structures
|
||||
*
|
||||
* This function takes a given start page (page aligned user virtual
|
||||
* address) and pins it and the following specified number of pages. For
|
||||
* now, num_pages is always 1, but that will probably change at some point
|
||||
* (because caller is doing expected sends on a single virtually contiguous
|
||||
* buffer, so we can do all pages at once).
|
||||
*/
|
||||
int ipath_get_user_pages(unsigned long start_page, size_t num_pages,
|
||||
struct page **p)
|
||||
{
|
||||
int ret;
|
||||
|
||||
down_write(¤t->mm->mmap_sem);
|
||||
|
||||
ret = __ipath_get_user_pages(start_page, num_pages, p);
|
||||
|
||||
up_write(¤t->mm->mmap_sem);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
void ipath_release_user_pages(struct page **p, size_t num_pages)
|
||||
{
|
||||
down_write(¤t->mm->mmap_sem);
|
||||
|
||||
__ipath_release_user_pages(p, num_pages, 1);
|
||||
|
||||
current->mm->pinned_vm -= num_pages;
|
||||
|
||||
up_write(¤t->mm->mmap_sem);
|
||||
}
|
||||
|
||||
struct ipath_user_pages_work {
|
||||
struct work_struct work;
|
||||
struct mm_struct *mm;
|
||||
unsigned long num_pages;
|
||||
};
|
||||
|
||||
static void user_pages_account(struct work_struct *_work)
|
||||
{
|
||||
struct ipath_user_pages_work *work =
|
||||
container_of(_work, struct ipath_user_pages_work, work);
|
||||
|
||||
down_write(&work->mm->mmap_sem);
|
||||
work->mm->pinned_vm -= work->num_pages;
|
||||
up_write(&work->mm->mmap_sem);
|
||||
mmput(work->mm);
|
||||
kfree(work);
|
||||
}
|
||||
|
||||
void ipath_release_user_pages_on_close(struct page **p, size_t num_pages)
|
||||
{
|
||||
struct ipath_user_pages_work *work;
|
||||
struct mm_struct *mm;
|
||||
|
||||
__ipath_release_user_pages(p, num_pages, 1);
|
||||
|
||||
mm = get_task_mm(current);
|
||||
if (!mm)
|
||||
return;
|
||||
|
||||
work = kmalloc(sizeof(*work), GFP_KERNEL);
|
||||
if (!work)
|
||||
goto bail_mm;
|
||||
|
||||
INIT_WORK(&work->work, user_pages_account);
|
||||
work->mm = mm;
|
||||
work->num_pages = num_pages;
|
||||
|
||||
queue_work(ib_wq, &work->work);
|
||||
return;
|
||||
|
||||
bail_mm:
|
||||
mmput(mm);
|
||||
return;
|
||||
}
|
|
@ -1,874 +0,0 @@
|
|||
/*
|
||||
* Copyright (c) 2007, 2008 QLogic Corporation. 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.
|
||||
*/
|
||||
#include <linux/mm.h>
|
||||
#include <linux/types.h>
|
||||
#include <linux/device.h>
|
||||
#include <linux/dmapool.h>
|
||||
#include <linux/slab.h>
|
||||
#include <linux/list.h>
|
||||
#include <linux/highmem.h>
|
||||
#include <linux/io.h>
|
||||
#include <linux/uio.h>
|
||||
#include <linux/rbtree.h>
|
||||
#include <linux/spinlock.h>
|
||||
#include <linux/delay.h>
|
||||
|
||||
#include "ipath_kernel.h"
|
||||
#include "ipath_user_sdma.h"
|
||||
|
||||
/* minimum size of header */
|
||||
#define IPATH_USER_SDMA_MIN_HEADER_LENGTH 64
|
||||
/* expected size of headers (for dma_pool) */
|
||||
#define IPATH_USER_SDMA_EXP_HEADER_LENGTH 64
|
||||
/* length mask in PBC (lower 11 bits) */
|
||||
#define IPATH_PBC_LENGTH_MASK ((1 << 11) - 1)
|
||||
|
||||
struct ipath_user_sdma_pkt {
|
||||
u8 naddr; /* dimension of addr (1..3) ... */
|
||||
u32 counter; /* sdma pkts queued counter for this entry */
|
||||
u64 added; /* global descq number of entries */
|
||||
|
||||
struct {
|
||||
u32 offset; /* offset for kvaddr, addr */
|
||||
u32 length; /* length in page */
|
||||
u8 put_page; /* should we put_page? */
|
||||
u8 dma_mapped; /* is page dma_mapped? */
|
||||
struct page *page; /* may be NULL (coherent mem) */
|
||||
void *kvaddr; /* FIXME: only for pio hack */
|
||||
dma_addr_t addr;
|
||||
} addr[4]; /* max pages, any more and we coalesce */
|
||||
struct list_head list; /* list element */
|
||||
};
|
||||
|
||||
struct ipath_user_sdma_queue {
|
||||
/*
|
||||
* pkts sent to dma engine are queued on this
|
||||
* list head. the type of the elements of this
|
||||
* list are struct ipath_user_sdma_pkt...
|
||||
*/
|
||||
struct list_head sent;
|
||||
|
||||
/* headers with expected length are allocated from here... */
|
||||
char header_cache_name[64];
|
||||
struct dma_pool *header_cache;
|
||||
|
||||
/* packets are allocated from the slab cache... */
|
||||
char pkt_slab_name[64];
|
||||
struct kmem_cache *pkt_slab;
|
||||
|
||||
/* as packets go on the queued queue, they are counted... */
|
||||
u32 counter;
|
||||
u32 sent_counter;
|
||||
|
||||
/* dma page table */
|
||||
struct rb_root dma_pages_root;
|
||||
|
||||
/* protect everything above... */
|
||||
struct mutex lock;
|
||||
};
|
||||
|
||||
struct ipath_user_sdma_queue *
|
||||
ipath_user_sdma_queue_create(struct device *dev, int unit, int port, int sport)
|
||||
{
|
||||
struct ipath_user_sdma_queue *pq =
|
||||
kmalloc(sizeof(struct ipath_user_sdma_queue), GFP_KERNEL);
|
||||
|
||||
if (!pq)
|
||||
goto done;
|
||||
|
||||
pq->counter = 0;
|
||||
pq->sent_counter = 0;
|
||||
INIT_LIST_HEAD(&pq->sent);
|
||||
|
||||
mutex_init(&pq->lock);
|
||||
|
||||
snprintf(pq->pkt_slab_name, sizeof(pq->pkt_slab_name),
|
||||
"ipath-user-sdma-pkts-%u-%02u.%02u", unit, port, sport);
|
||||
pq->pkt_slab = kmem_cache_create(pq->pkt_slab_name,
|
||||
sizeof(struct ipath_user_sdma_pkt),
|
||||
0, 0, NULL);
|
||||
|
||||
if (!pq->pkt_slab)
|
||||
goto err_kfree;
|
||||
|
||||
snprintf(pq->header_cache_name, sizeof(pq->header_cache_name),
|
||||
"ipath-user-sdma-headers-%u-%02u.%02u", unit, port, sport);
|
||||
pq->header_cache = dma_pool_create(pq->header_cache_name,
|
||||
dev,
|
||||
IPATH_USER_SDMA_EXP_HEADER_LENGTH,
|
||||
4, 0);
|
||||
if (!pq->header_cache)
|
||||
goto err_slab;
|
||||
|
||||
pq->dma_pages_root = RB_ROOT;
|
||||
|
||||
goto done;
|
||||
|
||||
err_slab:
|
||||
kmem_cache_destroy(pq->pkt_slab);
|
||||
err_kfree:
|
||||
kfree(pq);
|
||||
pq = NULL;
|
||||
|
||||
done:
|
||||
return pq;
|
||||
}
|
||||
|
||||
static void ipath_user_sdma_init_frag(struct ipath_user_sdma_pkt *pkt,
|
||||
int i, size_t offset, size_t len,
|
||||
int put_page, int dma_mapped,
|
||||
struct page *page,
|
||||
void *kvaddr, dma_addr_t dma_addr)
|
||||
{
|
||||
pkt->addr[i].offset = offset;
|
||||
pkt->addr[i].length = len;
|
||||
pkt->addr[i].put_page = put_page;
|
||||
pkt->addr[i].dma_mapped = dma_mapped;
|
||||
pkt->addr[i].page = page;
|
||||
pkt->addr[i].kvaddr = kvaddr;
|
||||
pkt->addr[i].addr = dma_addr;
|
||||
}
|
||||
|
||||
static void ipath_user_sdma_init_header(struct ipath_user_sdma_pkt *pkt,
|
||||
u32 counter, size_t offset,
|
||||
size_t len, int dma_mapped,
|
||||
struct page *page,
|
||||
void *kvaddr, dma_addr_t dma_addr)
|
||||
{
|
||||
pkt->naddr = 1;
|
||||
pkt->counter = counter;
|
||||
ipath_user_sdma_init_frag(pkt, 0, offset, len, 0, dma_mapped, page,
|
||||
kvaddr, dma_addr);
|
||||
}
|
||||
|
||||
/* we've too many pages in the iovec, coalesce to a single page */
|
||||
static int ipath_user_sdma_coalesce(const struct ipath_devdata *dd,
|
||||
struct ipath_user_sdma_pkt *pkt,
|
||||
const struct iovec *iov,
|
||||
unsigned long niov) {
|
||||
int ret = 0;
|
||||
struct page *page = alloc_page(GFP_KERNEL);
|
||||
void *mpage_save;
|
||||
char *mpage;
|
||||
int i;
|
||||
int len = 0;
|
||||
dma_addr_t dma_addr;
|
||||
|
||||
if (!page) {
|
||||
ret = -ENOMEM;
|
||||
goto done;
|
||||
}
|
||||
|
||||
mpage = kmap(page);
|
||||
mpage_save = mpage;
|
||||
for (i = 0; i < niov; i++) {
|
||||
int cfur;
|
||||
|
||||
cfur = copy_from_user(mpage,
|
||||
iov[i].iov_base, iov[i].iov_len);
|
||||
if (cfur) {
|
||||
ret = -EFAULT;
|
||||
goto free_unmap;
|
||||
}
|
||||
|
||||
mpage += iov[i].iov_len;
|
||||
len += iov[i].iov_len;
|
||||
}
|
||||
|
||||
dma_addr = dma_map_page(&dd->pcidev->dev, page, 0, len,
|
||||
DMA_TO_DEVICE);
|
||||
if (dma_mapping_error(&dd->pcidev->dev, dma_addr)) {
|
||||
ret = -ENOMEM;
|
||||
goto free_unmap;
|
||||
}
|
||||
|
||||
ipath_user_sdma_init_frag(pkt, 1, 0, len, 0, 1, page, mpage_save,
|
||||
dma_addr);
|
||||
pkt->naddr = 2;
|
||||
|
||||
goto done;
|
||||
|
||||
free_unmap:
|
||||
kunmap(page);
|
||||
__free_page(page);
|
||||
done:
|
||||
return ret;
|
||||
}
|
||||
|
||||
/* how many pages in this iovec element? */
|
||||
static int ipath_user_sdma_num_pages(const struct iovec *iov)
|
||||
{
|
||||
const unsigned long addr = (unsigned long) iov->iov_base;
|
||||
const unsigned long len = iov->iov_len;
|
||||
const unsigned long spage = addr & PAGE_MASK;
|
||||
const unsigned long epage = (addr + len - 1) & PAGE_MASK;
|
||||
|
||||
return 1 + ((epage - spage) >> PAGE_SHIFT);
|
||||
}
|
||||
|
||||
/* truncate length to page boundary */
|
||||
static int ipath_user_sdma_page_length(unsigned long addr, unsigned long len)
|
||||
{
|
||||
const unsigned long offset = offset_in_page(addr);
|
||||
|
||||
return ((offset + len) > PAGE_SIZE) ? (PAGE_SIZE - offset) : len;
|
||||
}
|
||||
|
||||
static void ipath_user_sdma_free_pkt_frag(struct device *dev,
|
||||
struct ipath_user_sdma_queue *pq,
|
||||
struct ipath_user_sdma_pkt *pkt,
|
||||
int frag)
|
||||
{
|
||||
const int i = frag;
|
||||
|
||||
if (pkt->addr[i].page) {
|
||||
if (pkt->addr[i].dma_mapped)
|
||||
dma_unmap_page(dev,
|
||||
pkt->addr[i].addr,
|
||||
pkt->addr[i].length,
|
||||
DMA_TO_DEVICE);
|
||||
|
||||
if (pkt->addr[i].kvaddr)
|
||||
kunmap(pkt->addr[i].page);
|
||||
|
||||
if (pkt->addr[i].put_page)
|
||||
put_page(pkt->addr[i].page);
|
||||
else
|
||||
__free_page(pkt->addr[i].page);
|
||||
} else if (pkt->addr[i].kvaddr)
|
||||
/* free coherent mem from cache... */
|
||||
dma_pool_free(pq->header_cache,
|
||||
pkt->addr[i].kvaddr, pkt->addr[i].addr);
|
||||
}
|
||||
|
||||
/* return number of pages pinned... */
|
||||
static int ipath_user_sdma_pin_pages(const struct ipath_devdata *dd,
|
||||
struct ipath_user_sdma_pkt *pkt,
|
||||
unsigned long addr, int tlen, int npages)
|
||||
{
|
||||
struct page *pages[2];
|
||||
int j;
|
||||
int ret;
|
||||
|
||||
ret = get_user_pages_fast(addr, npages, 0, pages);
|
||||
if (ret != npages) {
|
||||
int i;
|
||||
|
||||
for (i = 0; i < ret; i++)
|
||||
put_page(pages[i]);
|
||||
|
||||
ret = -ENOMEM;
|
||||
goto done;
|
||||
}
|
||||
|
||||
for (j = 0; j < npages; j++) {
|
||||
/* map the pages... */
|
||||
const int flen =
|
||||
ipath_user_sdma_page_length(addr, tlen);
|
||||
dma_addr_t dma_addr =
|
||||
dma_map_page(&dd->pcidev->dev,
|
||||
pages[j], 0, flen, DMA_TO_DEVICE);
|
||||
unsigned long fofs = offset_in_page(addr);
|
||||
|
||||
if (dma_mapping_error(&dd->pcidev->dev, dma_addr)) {
|
||||
ret = -ENOMEM;
|
||||
goto done;
|
||||
}
|
||||
|
||||
ipath_user_sdma_init_frag(pkt, pkt->naddr, fofs, flen, 1, 1,
|
||||
pages[j], kmap(pages[j]),
|
||||
dma_addr);
|
||||
|
||||
pkt->naddr++;
|
||||
addr += flen;
|
||||
tlen -= flen;
|
||||
}
|
||||
|
||||
done:
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int ipath_user_sdma_pin_pkt(const struct ipath_devdata *dd,
|
||||
struct ipath_user_sdma_queue *pq,
|
||||
struct ipath_user_sdma_pkt *pkt,
|
||||
const struct iovec *iov,
|
||||
unsigned long niov)
|
||||
{
|
||||
int ret = 0;
|
||||
unsigned long idx;
|
||||
|
||||
for (idx = 0; idx < niov; idx++) {
|
||||
const int npages = ipath_user_sdma_num_pages(iov + idx);
|
||||
const unsigned long addr = (unsigned long) iov[idx].iov_base;
|
||||
|
||||
ret = ipath_user_sdma_pin_pages(dd, pkt,
|
||||
addr, iov[idx].iov_len,
|
||||
npages);
|
||||
if (ret < 0)
|
||||
goto free_pkt;
|
||||
}
|
||||
|
||||
goto done;
|
||||
|
||||
free_pkt:
|
||||
for (idx = 0; idx < pkt->naddr; idx++)
|
||||
ipath_user_sdma_free_pkt_frag(&dd->pcidev->dev, pq, pkt, idx);
|
||||
|
||||
done:
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int ipath_user_sdma_init_payload(const struct ipath_devdata *dd,
|
||||
struct ipath_user_sdma_queue *pq,
|
||||
struct ipath_user_sdma_pkt *pkt,
|
||||
const struct iovec *iov,
|
||||
unsigned long niov, int npages)
|
||||
{
|
||||
int ret = 0;
|
||||
|
||||
if (npages >= ARRAY_SIZE(pkt->addr))
|
||||
ret = ipath_user_sdma_coalesce(dd, pkt, iov, niov);
|
||||
else
|
||||
ret = ipath_user_sdma_pin_pkt(dd, pq, pkt, iov, niov);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
/* free a packet list -- return counter value of last packet */
|
||||
static void ipath_user_sdma_free_pkt_list(struct device *dev,
|
||||
struct ipath_user_sdma_queue *pq,
|
||||
struct list_head *list)
|
||||
{
|
||||
struct ipath_user_sdma_pkt *pkt, *pkt_next;
|
||||
|
||||
list_for_each_entry_safe(pkt, pkt_next, list, list) {
|
||||
int i;
|
||||
|
||||
for (i = 0; i < pkt->naddr; i++)
|
||||
ipath_user_sdma_free_pkt_frag(dev, pq, pkt, i);
|
||||
|
||||
kmem_cache_free(pq->pkt_slab, pkt);
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* copy headers, coalesce etc -- pq->lock must be held
|
||||
*
|
||||
* we queue all the packets to list, returning the
|
||||
* number of bytes total. list must be empty initially,
|
||||
* as, if there is an error we clean it...
|
||||
*/
|
||||
static int ipath_user_sdma_queue_pkts(const struct ipath_devdata *dd,
|
||||
struct ipath_user_sdma_queue *pq,
|
||||
struct list_head *list,
|
||||
const struct iovec *iov,
|
||||
unsigned long niov,
|
||||
int maxpkts)
|
||||
{
|
||||
unsigned long idx = 0;
|
||||
int ret = 0;
|
||||
int npkts = 0;
|
||||
struct page *page = NULL;
|
||||
__le32 *pbc;
|
||||
dma_addr_t dma_addr;
|
||||
struct ipath_user_sdma_pkt *pkt = NULL;
|
||||
size_t len;
|
||||
size_t nw;
|
||||
u32 counter = pq->counter;
|
||||
int dma_mapped = 0;
|
||||
|
||||
while (idx < niov && npkts < maxpkts) {
|
||||
const unsigned long addr = (unsigned long) iov[idx].iov_base;
|
||||
const unsigned long idx_save = idx;
|
||||
unsigned pktnw;
|
||||
unsigned pktnwc;
|
||||
int nfrags = 0;
|
||||
int npages = 0;
|
||||
int cfur;
|
||||
|
||||
dma_mapped = 0;
|
||||
len = iov[idx].iov_len;
|
||||
nw = len >> 2;
|
||||
page = NULL;
|
||||
|
||||
pkt = kmem_cache_alloc(pq->pkt_slab, GFP_KERNEL);
|
||||
if (!pkt) {
|
||||
ret = -ENOMEM;
|
||||
goto free_list;
|
||||
}
|
||||
|
||||
if (len < IPATH_USER_SDMA_MIN_HEADER_LENGTH ||
|
||||
len > PAGE_SIZE || len & 3 || addr & 3) {
|
||||
ret = -EINVAL;
|
||||
goto free_pkt;
|
||||
}
|
||||
|
||||
if (len == IPATH_USER_SDMA_EXP_HEADER_LENGTH)
|
||||
pbc = dma_pool_alloc(pq->header_cache, GFP_KERNEL,
|
||||
&dma_addr);
|
||||
else
|
||||
pbc = NULL;
|
||||
|
||||
if (!pbc) {
|
||||
page = alloc_page(GFP_KERNEL);
|
||||
if (!page) {
|
||||
ret = -ENOMEM;
|
||||
goto free_pkt;
|
||||
}
|
||||
pbc = kmap(page);
|
||||
}
|
||||
|
||||
cfur = copy_from_user(pbc, iov[idx].iov_base, len);
|
||||
if (cfur) {
|
||||
ret = -EFAULT;
|
||||
goto free_pbc;
|
||||
}
|
||||
|
||||
/*
|
||||
* this assignment is a bit strange. it's because the
|
||||
* the pbc counts the number of 32 bit words in the full
|
||||
* packet _except_ the first word of the pbc itself...
|
||||
*/
|
||||
pktnwc = nw - 1;
|
||||
|
||||
/*
|
||||
* pktnw computation yields the number of 32 bit words
|
||||
* that the caller has indicated in the PBC. note that
|
||||
* this is one less than the total number of words that
|
||||
* goes to the send DMA engine as the first 32 bit word
|
||||
* of the PBC itself is not counted. Armed with this count,
|
||||
* we can verify that the packet is consistent with the
|
||||
* iovec lengths.
|
||||
*/
|
||||
pktnw = le32_to_cpu(*pbc) & IPATH_PBC_LENGTH_MASK;
|
||||
if (pktnw < pktnwc || pktnw > pktnwc + (PAGE_SIZE >> 2)) {
|
||||
ret = -EINVAL;
|
||||
goto free_pbc;
|
||||
}
|
||||
|
||||
|
||||
idx++;
|
||||
while (pktnwc < pktnw && idx < niov) {
|
||||
const size_t slen = iov[idx].iov_len;
|
||||
const unsigned long faddr =
|
||||
(unsigned long) iov[idx].iov_base;
|
||||
|
||||
if (slen & 3 || faddr & 3 || !slen ||
|
||||
slen > PAGE_SIZE) {
|
||||
ret = -EINVAL;
|
||||
goto free_pbc;
|
||||
}
|
||||
|
||||
npages++;
|
||||
if ((faddr & PAGE_MASK) !=
|
||||
((faddr + slen - 1) & PAGE_MASK))
|
||||
npages++;
|
||||
|
||||
pktnwc += slen >> 2;
|
||||
idx++;
|
||||
nfrags++;
|
||||
}
|
||||
|
||||
if (pktnwc != pktnw) {
|
||||
ret = -EINVAL;
|
||||
goto free_pbc;
|
||||
}
|
||||
|
||||
if (page) {
|
||||
dma_addr = dma_map_page(&dd->pcidev->dev,
|
||||
page, 0, len, DMA_TO_DEVICE);
|
||||
if (dma_mapping_error(&dd->pcidev->dev, dma_addr)) {
|
||||
ret = -ENOMEM;
|
||||
goto free_pbc;
|
||||
}
|
||||
|
||||
dma_mapped = 1;
|
||||
}
|
||||
|
||||
ipath_user_sdma_init_header(pkt, counter, 0, len, dma_mapped,
|
||||
page, pbc, dma_addr);
|
||||
|
||||
if (nfrags) {
|
||||
ret = ipath_user_sdma_init_payload(dd, pq, pkt,
|
||||
iov + idx_save + 1,
|
||||
nfrags, npages);
|
||||
if (ret < 0)
|
||||
goto free_pbc_dma;
|
||||
}
|
||||
|
||||
counter++;
|
||||
npkts++;
|
||||
|
||||
list_add_tail(&pkt->list, list);
|
||||
}
|
||||
|
||||
ret = idx;
|
||||
goto done;
|
||||
|
||||
free_pbc_dma:
|
||||
if (dma_mapped)
|
||||
dma_unmap_page(&dd->pcidev->dev, dma_addr, len, DMA_TO_DEVICE);
|
||||
free_pbc:
|
||||
if (page) {
|
||||
kunmap(page);
|
||||
__free_page(page);
|
||||
} else
|
||||
dma_pool_free(pq->header_cache, pbc, dma_addr);
|
||||
free_pkt:
|
||||
kmem_cache_free(pq->pkt_slab, pkt);
|
||||
free_list:
|
||||
ipath_user_sdma_free_pkt_list(&dd->pcidev->dev, pq, list);
|
||||
done:
|
||||
return ret;
|
||||
}
|
||||
|
||||
static void ipath_user_sdma_set_complete_counter(struct ipath_user_sdma_queue *pq,
|
||||
u32 c)
|
||||
{
|
||||
pq->sent_counter = c;
|
||||
}
|
||||
|
||||
/* try to clean out queue -- needs pq->lock */
|
||||
static int ipath_user_sdma_queue_clean(const struct ipath_devdata *dd,
|
||||
struct ipath_user_sdma_queue *pq)
|
||||
{
|
||||
struct list_head free_list;
|
||||
struct ipath_user_sdma_pkt *pkt;
|
||||
struct ipath_user_sdma_pkt *pkt_prev;
|
||||
int ret = 0;
|
||||
|
||||
INIT_LIST_HEAD(&free_list);
|
||||
|
||||
list_for_each_entry_safe(pkt, pkt_prev, &pq->sent, list) {
|
||||
s64 descd = dd->ipath_sdma_descq_removed - pkt->added;
|
||||
|
||||
if (descd < 0)
|
||||
break;
|
||||
|
||||
list_move_tail(&pkt->list, &free_list);
|
||||
|
||||
/* one more packet cleaned */
|
||||
ret++;
|
||||
}
|
||||
|
||||
if (!list_empty(&free_list)) {
|
||||
u32 counter;
|
||||
|
||||
pkt = list_entry(free_list.prev,
|
||||
struct ipath_user_sdma_pkt, list);
|
||||
counter = pkt->counter;
|
||||
|
||||
ipath_user_sdma_free_pkt_list(&dd->pcidev->dev, pq, &free_list);
|
||||
ipath_user_sdma_set_complete_counter(pq, counter);
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
void ipath_user_sdma_queue_destroy(struct ipath_user_sdma_queue *pq)
|
||||
{
|
||||
if (!pq)
|
||||
return;
|
||||
|
||||
kmem_cache_destroy(pq->pkt_slab);
|
||||
dma_pool_destroy(pq->header_cache);
|
||||
kfree(pq);
|
||||
}
|
||||
|
||||
/* clean descriptor queue, returns > 0 if some elements cleaned */
|
||||
static int ipath_user_sdma_hwqueue_clean(struct ipath_devdata *dd)
|
||||
{
|
||||
int ret;
|
||||
unsigned long flags;
|
||||
|
||||
spin_lock_irqsave(&dd->ipath_sdma_lock, flags);
|
||||
ret = ipath_sdma_make_progress(dd);
|
||||
spin_unlock_irqrestore(&dd->ipath_sdma_lock, flags);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
/* we're in close, drain packets so that we can cleanup successfully... */
|
||||
void ipath_user_sdma_queue_drain(struct ipath_devdata *dd,
|
||||
struct ipath_user_sdma_queue *pq)
|
||||
{
|
||||
int i;
|
||||
|
||||
if (!pq)
|
||||
return;
|
||||
|
||||
for (i = 0; i < 100; i++) {
|
||||
mutex_lock(&pq->lock);
|
||||
if (list_empty(&pq->sent)) {
|
||||
mutex_unlock(&pq->lock);
|
||||
break;
|
||||
}
|
||||
ipath_user_sdma_hwqueue_clean(dd);
|
||||
ipath_user_sdma_queue_clean(dd, pq);
|
||||
mutex_unlock(&pq->lock);
|
||||
msleep(10);
|
||||
}
|
||||
|
||||
if (!list_empty(&pq->sent)) {
|
||||
struct list_head free_list;
|
||||
|
||||
printk(KERN_INFO "drain: lists not empty: forcing!\n");
|
||||
INIT_LIST_HEAD(&free_list);
|
||||
mutex_lock(&pq->lock);
|
||||
list_splice_init(&pq->sent, &free_list);
|
||||
ipath_user_sdma_free_pkt_list(&dd->pcidev->dev, pq, &free_list);
|
||||
mutex_unlock(&pq->lock);
|
||||
}
|
||||
}
|
||||
|
||||
static inline __le64 ipath_sdma_make_desc0(struct ipath_devdata *dd,
|
||||
u64 addr, u64 dwlen, u64 dwoffset)
|
||||
{
|
||||
return cpu_to_le64(/* SDmaPhyAddr[31:0] */
|
||||
((addr & 0xfffffffcULL) << 32) |
|
||||
/* SDmaGeneration[1:0] */
|
||||
((dd->ipath_sdma_generation & 3ULL) << 30) |
|
||||
/* SDmaDwordCount[10:0] */
|
||||
((dwlen & 0x7ffULL) << 16) |
|
||||
/* SDmaBufOffset[12:2] */
|
||||
(dwoffset & 0x7ffULL));
|
||||
}
|
||||
|
||||
static inline __le64 ipath_sdma_make_first_desc0(__le64 descq)
|
||||
{
|
||||
return descq | cpu_to_le64(1ULL << 12);
|
||||
}
|
||||
|
||||
static inline __le64 ipath_sdma_make_last_desc0(__le64 descq)
|
||||
{
|
||||
/* last */ /* dma head */
|
||||
return descq | cpu_to_le64(1ULL << 11 | 1ULL << 13);
|
||||
}
|
||||
|
||||
static inline __le64 ipath_sdma_make_desc1(u64 addr)
|
||||
{
|
||||
/* SDmaPhyAddr[47:32] */
|
||||
return cpu_to_le64(addr >> 32);
|
||||
}
|
||||
|
||||
static void ipath_user_sdma_send_frag(struct ipath_devdata *dd,
|
||||
struct ipath_user_sdma_pkt *pkt, int idx,
|
||||
unsigned ofs, u16 tail)
|
||||
{
|
||||
const u64 addr = (u64) pkt->addr[idx].addr +
|
||||
(u64) pkt->addr[idx].offset;
|
||||
const u64 dwlen = (u64) pkt->addr[idx].length / 4;
|
||||
__le64 *descqp;
|
||||
__le64 descq0;
|
||||
|
||||
descqp = &dd->ipath_sdma_descq[tail].qw[0];
|
||||
|
||||
descq0 = ipath_sdma_make_desc0(dd, addr, dwlen, ofs);
|
||||
if (idx == 0)
|
||||
descq0 = ipath_sdma_make_first_desc0(descq0);
|
||||
if (idx == pkt->naddr - 1)
|
||||
descq0 = ipath_sdma_make_last_desc0(descq0);
|
||||
|
||||
descqp[0] = descq0;
|
||||
descqp[1] = ipath_sdma_make_desc1(addr);
|
||||
}
|
||||
|
||||
/* pq->lock must be held, get packets on the wire... */
|
||||
static int ipath_user_sdma_push_pkts(struct ipath_devdata *dd,
|
||||
struct ipath_user_sdma_queue *pq,
|
||||
struct list_head *pktlist)
|
||||
{
|
||||
int ret = 0;
|
||||
unsigned long flags;
|
||||
u16 tail;
|
||||
|
||||
if (list_empty(pktlist))
|
||||
return 0;
|
||||
|
||||
if (unlikely(!(dd->ipath_flags & IPATH_LINKACTIVE)))
|
||||
return -ECOMM;
|
||||
|
||||
spin_lock_irqsave(&dd->ipath_sdma_lock, flags);
|
||||
|
||||
if (unlikely(dd->ipath_sdma_status & IPATH_SDMA_ABORT_MASK)) {
|
||||
ret = -ECOMM;
|
||||
goto unlock;
|
||||
}
|
||||
|
||||
tail = dd->ipath_sdma_descq_tail;
|
||||
while (!list_empty(pktlist)) {
|
||||
struct ipath_user_sdma_pkt *pkt =
|
||||
list_entry(pktlist->next, struct ipath_user_sdma_pkt,
|
||||
list);
|
||||
int i;
|
||||
unsigned ofs = 0;
|
||||
u16 dtail = tail;
|
||||
|
||||
if (pkt->naddr > ipath_sdma_descq_freecnt(dd))
|
||||
goto unlock_check_tail;
|
||||
|
||||
for (i = 0; i < pkt->naddr; i++) {
|
||||
ipath_user_sdma_send_frag(dd, pkt, i, ofs, tail);
|
||||
ofs += pkt->addr[i].length >> 2;
|
||||
|
||||
if (++tail == dd->ipath_sdma_descq_cnt) {
|
||||
tail = 0;
|
||||
++dd->ipath_sdma_generation;
|
||||
}
|
||||
}
|
||||
|
||||
if ((ofs<<2) > dd->ipath_ibmaxlen) {
|
||||
ipath_dbg("packet size %X > ibmax %X, fail\n",
|
||||
ofs<<2, dd->ipath_ibmaxlen);
|
||||
ret = -EMSGSIZE;
|
||||
goto unlock;
|
||||
}
|
||||
|
||||
/*
|
||||
* if the packet is >= 2KB mtu equivalent, we have to use
|
||||
* the large buffers, and have to mark each descriptor as
|
||||
* part of a large buffer packet.
|
||||
*/
|
||||
if (ofs >= IPATH_SMALLBUF_DWORDS) {
|
||||
for (i = 0; i < pkt->naddr; i++) {
|
||||
dd->ipath_sdma_descq[dtail].qw[0] |=
|
||||
cpu_to_le64(1ULL << 14);
|
||||
if (++dtail == dd->ipath_sdma_descq_cnt)
|
||||
dtail = 0;
|
||||
}
|
||||
}
|
||||
|
||||
dd->ipath_sdma_descq_added += pkt->naddr;
|
||||
pkt->added = dd->ipath_sdma_descq_added;
|
||||
list_move_tail(&pkt->list, &pq->sent);
|
||||
ret++;
|
||||
}
|
||||
|
||||
unlock_check_tail:
|
||||
/* advance the tail on the chip if necessary */
|
||||
if (dd->ipath_sdma_descq_tail != tail) {
|
||||
wmb();
|
||||
ipath_write_kreg(dd, dd->ipath_kregs->kr_senddmatail, tail);
|
||||
dd->ipath_sdma_descq_tail = tail;
|
||||
}
|
||||
|
||||
unlock:
|
||||
spin_unlock_irqrestore(&dd->ipath_sdma_lock, flags);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
int ipath_user_sdma_writev(struct ipath_devdata *dd,
|
||||
struct ipath_user_sdma_queue *pq,
|
||||
const struct iovec *iov,
|
||||
unsigned long dim)
|
||||
{
|
||||
int ret = 0;
|
||||
struct list_head list;
|
||||
int npkts = 0;
|
||||
|
||||
INIT_LIST_HEAD(&list);
|
||||
|
||||
mutex_lock(&pq->lock);
|
||||
|
||||
if (dd->ipath_sdma_descq_added != dd->ipath_sdma_descq_removed) {
|
||||
ipath_user_sdma_hwqueue_clean(dd);
|
||||
ipath_user_sdma_queue_clean(dd, pq);
|
||||
}
|
||||
|
||||
while (dim) {
|
||||
const int mxp = 8;
|
||||
|
||||
ret = ipath_user_sdma_queue_pkts(dd, pq, &list, iov, dim, mxp);
|
||||
if (ret <= 0)
|
||||
goto done_unlock;
|
||||
else {
|
||||
dim -= ret;
|
||||
iov += ret;
|
||||
}
|
||||
|
||||
/* force packets onto the sdma hw queue... */
|
||||
if (!list_empty(&list)) {
|
||||
/*
|
||||
* lazily clean hw queue. the 4 is a guess of about
|
||||
* how many sdma descriptors a packet will take (it
|
||||
* doesn't have to be perfect).
|
||||
*/
|
||||
if (ipath_sdma_descq_freecnt(dd) < ret * 4) {
|
||||
ipath_user_sdma_hwqueue_clean(dd);
|
||||
ipath_user_sdma_queue_clean(dd, pq);
|
||||
}
|
||||
|
||||
ret = ipath_user_sdma_push_pkts(dd, pq, &list);
|
||||
if (ret < 0)
|
||||
goto done_unlock;
|
||||
else {
|
||||
npkts += ret;
|
||||
pq->counter += ret;
|
||||
|
||||
if (!list_empty(&list))
|
||||
goto done_unlock;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
done_unlock:
|
||||
if (!list_empty(&list))
|
||||
ipath_user_sdma_free_pkt_list(&dd->pcidev->dev, pq, &list);
|
||||
mutex_unlock(&pq->lock);
|
||||
|
||||
return (ret < 0) ? ret : npkts;
|
||||
}
|
||||
|
||||
int ipath_user_sdma_make_progress(struct ipath_devdata *dd,
|
||||
struct ipath_user_sdma_queue *pq)
|
||||
{
|
||||
int ret = 0;
|
||||
|
||||
mutex_lock(&pq->lock);
|
||||
ipath_user_sdma_hwqueue_clean(dd);
|
||||
ret = ipath_user_sdma_queue_clean(dd, pq);
|
||||
mutex_unlock(&pq->lock);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
u32 ipath_user_sdma_complete_counter(const struct ipath_user_sdma_queue *pq)
|
||||
{
|
||||
return pq->sent_counter;
|
||||
}
|
||||
|
||||
u32 ipath_user_sdma_inflight_counter(struct ipath_user_sdma_queue *pq)
|
||||
{
|
||||
return pq->counter;
|
||||
}
|
||||
|
|
@ -1,52 +0,0 @@
|
|||
/*
|
||||
* Copyright (c) 2007, 2008 QLogic Corporation. 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.
|
||||
*/
|
||||
#include <linux/device.h>
|
||||
|
||||
struct ipath_user_sdma_queue;
|
||||
|
||||
struct ipath_user_sdma_queue *
|
||||
ipath_user_sdma_queue_create(struct device *dev, int unit, int port, int sport);
|
||||
void ipath_user_sdma_queue_destroy(struct ipath_user_sdma_queue *pq);
|
||||
|
||||
int ipath_user_sdma_writev(struct ipath_devdata *dd,
|
||||
struct ipath_user_sdma_queue *pq,
|
||||
const struct iovec *iov,
|
||||
unsigned long dim);
|
||||
|
||||
int ipath_user_sdma_make_progress(struct ipath_devdata *dd,
|
||||
struct ipath_user_sdma_queue *pq);
|
||||
|
||||
void ipath_user_sdma_queue_drain(struct ipath_devdata *dd,
|
||||
struct ipath_user_sdma_queue *pq);
|
||||
|
||||
u32 ipath_user_sdma_complete_counter(const struct ipath_user_sdma_queue *pq);
|
||||
u32 ipath_user_sdma_inflight_counter(struct ipath_user_sdma_queue *pq);
|
File diff suppressed because it is too large
Load Diff
|
@ -1,941 +0,0 @@
|
|||
/*
|
||||
* Copyright (c) 2006, 2007, 2008 QLogic Corporation. All rights reserved.
|
||||
* Copyright (c) 2005, 2006 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.
|
||||
*/
|
||||
|
||||
#ifndef IPATH_VERBS_H
|
||||
#define IPATH_VERBS_H
|
||||
|
||||
#include <linux/types.h>
|
||||
#include <linux/spinlock.h>
|
||||
#include <linux/kernel.h>
|
||||
#include <linux/interrupt.h>
|
||||
#include <linux/kref.h>
|
||||
#include <rdma/ib_pack.h>
|
||||
#include <rdma/ib_user_verbs.h>
|
||||
|
||||
#include "ipath_kernel.h"
|
||||
|
||||
#define IPATH_MAX_RDMA_ATOMIC 4
|
||||
|
||||
#define QPN_MAX (1 << 24)
|
||||
#define QPNMAP_ENTRIES (QPN_MAX / PAGE_SIZE / BITS_PER_BYTE)
|
||||
|
||||
/*
|
||||
* Increment this value if any changes that break userspace ABI
|
||||
* compatibility are made.
|
||||
*/
|
||||
#define IPATH_UVERBS_ABI_VERSION 2
|
||||
|
||||
/*
|
||||
* Define an ib_cq_notify value that is not valid so we know when CQ
|
||||
* notifications are armed.
|
||||
*/
|
||||
#define IB_CQ_NONE (IB_CQ_NEXT_COMP + 1)
|
||||
|
||||
/* AETH NAK opcode values */
|
||||
#define IB_RNR_NAK 0x20
|
||||
#define IB_NAK_PSN_ERROR 0x60
|
||||
#define IB_NAK_INVALID_REQUEST 0x61
|
||||
#define IB_NAK_REMOTE_ACCESS_ERROR 0x62
|
||||
#define IB_NAK_REMOTE_OPERATIONAL_ERROR 0x63
|
||||
#define IB_NAK_INVALID_RD_REQUEST 0x64
|
||||
|
||||
/* Flags for checking QP state (see ib_ipath_state_ops[]) */
|
||||
#define IPATH_POST_SEND_OK 0x01
|
||||
#define IPATH_POST_RECV_OK 0x02
|
||||
#define IPATH_PROCESS_RECV_OK 0x04
|
||||
#define IPATH_PROCESS_SEND_OK 0x08
|
||||
#define IPATH_PROCESS_NEXT_SEND_OK 0x10
|
||||
#define IPATH_FLUSH_SEND 0x20
|
||||
#define IPATH_FLUSH_RECV 0x40
|
||||
#define IPATH_PROCESS_OR_FLUSH_SEND \
|
||||
(IPATH_PROCESS_SEND_OK | IPATH_FLUSH_SEND)
|
||||
|
||||
/* IB Performance Manager status values */
|
||||
#define IB_PMA_SAMPLE_STATUS_DONE 0x00
|
||||
#define IB_PMA_SAMPLE_STATUS_STARTED 0x01
|
||||
#define IB_PMA_SAMPLE_STATUS_RUNNING 0x02
|
||||
|
||||
/* Mandatory IB performance counter select values. */
|
||||
#define IB_PMA_PORT_XMIT_DATA cpu_to_be16(0x0001)
|
||||
#define IB_PMA_PORT_RCV_DATA cpu_to_be16(0x0002)
|
||||
#define IB_PMA_PORT_XMIT_PKTS cpu_to_be16(0x0003)
|
||||
#define IB_PMA_PORT_RCV_PKTS cpu_to_be16(0x0004)
|
||||
#define IB_PMA_PORT_XMIT_WAIT cpu_to_be16(0x0005)
|
||||
|
||||
struct ib_reth {
|
||||
__be64 vaddr;
|
||||
__be32 rkey;
|
||||
__be32 length;
|
||||
} __attribute__ ((packed));
|
||||
|
||||
struct ib_atomic_eth {
|
||||
__be32 vaddr[2]; /* unaligned so access as 2 32-bit words */
|
||||
__be32 rkey;
|
||||
__be64 swap_data;
|
||||
__be64 compare_data;
|
||||
} __attribute__ ((packed));
|
||||
|
||||
struct ipath_other_headers {
|
||||
__be32 bth[3];
|
||||
union {
|
||||
struct {
|
||||
__be32 deth[2];
|
||||
__be32 imm_data;
|
||||
} ud;
|
||||
struct {
|
||||
struct ib_reth reth;
|
||||
__be32 imm_data;
|
||||
} rc;
|
||||
struct {
|
||||
__be32 aeth;
|
||||
__be32 atomic_ack_eth[2];
|
||||
} at;
|
||||
__be32 imm_data;
|
||||
__be32 aeth;
|
||||
struct ib_atomic_eth atomic_eth;
|
||||
} u;
|
||||
} __attribute__ ((packed));
|
||||
|
||||
/*
|
||||
* Note that UD packets with a GRH header are 8+40+12+8 = 68 bytes
|
||||
* long (72 w/ imm_data). Only the first 56 bytes of the IB header
|
||||
* will be in the eager header buffer. The remaining 12 or 16 bytes
|
||||
* are in the data buffer.
|
||||
*/
|
||||
struct ipath_ib_header {
|
||||
__be16 lrh[4];
|
||||
union {
|
||||
struct {
|
||||
struct ib_grh grh;
|
||||
struct ipath_other_headers oth;
|
||||
} l;
|
||||
struct ipath_other_headers oth;
|
||||
} u;
|
||||
} __attribute__ ((packed));
|
||||
|
||||
struct ipath_pio_header {
|
||||
__le32 pbc[2];
|
||||
struct ipath_ib_header hdr;
|
||||
} __attribute__ ((packed));
|
||||
|
||||
/*
|
||||
* There is one struct ipath_mcast for each multicast GID.
|
||||
* All attached QPs are then stored as a list of
|
||||
* struct ipath_mcast_qp.
|
||||
*/
|
||||
struct ipath_mcast_qp {
|
||||
struct list_head list;
|
||||
struct ipath_qp *qp;
|
||||
};
|
||||
|
||||
struct ipath_mcast {
|
||||
struct rb_node rb_node;
|
||||
union ib_gid mgid;
|
||||
struct list_head qp_list;
|
||||
wait_queue_head_t wait;
|
||||
atomic_t refcount;
|
||||
int n_attached;
|
||||
};
|
||||
|
||||
/* Protection domain */
|
||||
struct ipath_pd {
|
||||
struct ib_pd ibpd;
|
||||
int user; /* non-zero if created from user space */
|
||||
};
|
||||
|
||||
/* Address Handle */
|
||||
struct ipath_ah {
|
||||
struct ib_ah ibah;
|
||||
struct ib_ah_attr attr;
|
||||
};
|
||||
|
||||
/*
|
||||
* This structure is used by ipath_mmap() to validate an offset
|
||||
* when an mmap() request is made. The vm_area_struct then uses
|
||||
* this as its vm_private_data.
|
||||
*/
|
||||
struct ipath_mmap_info {
|
||||
struct list_head pending_mmaps;
|
||||
struct ib_ucontext *context;
|
||||
void *obj;
|
||||
__u64 offset;
|
||||
struct kref ref;
|
||||
unsigned size;
|
||||
};
|
||||
|
||||
/*
|
||||
* This structure is used to contain the head pointer, tail pointer,
|
||||
* and completion queue entries as a single memory allocation so
|
||||
* it can be mmap'ed into user space.
|
||||
*/
|
||||
struct ipath_cq_wc {
|
||||
u32 head; /* index of next entry to fill */
|
||||
u32 tail; /* index of next ib_poll_cq() entry */
|
||||
union {
|
||||
/* these are actually size ibcq.cqe + 1 */
|
||||
struct ib_uverbs_wc uqueue[0];
|
||||
struct ib_wc kqueue[0];
|
||||
};
|
||||
};
|
||||
|
||||
/*
|
||||
* The completion queue structure.
|
||||
*/
|
||||
struct ipath_cq {
|
||||
struct ib_cq ibcq;
|
||||
struct tasklet_struct comptask;
|
||||
spinlock_t lock;
|
||||
u8 notify;
|
||||
u8 triggered;
|
||||
struct ipath_cq_wc *queue;
|
||||
struct ipath_mmap_info *ip;
|
||||
};
|
||||
|
||||
/*
|
||||
* A segment is a linear region of low physical memory.
|
||||
* XXX Maybe we should use phys addr here and kmap()/kunmap().
|
||||
* Used by the verbs layer.
|
||||
*/
|
||||
struct ipath_seg {
|
||||
void *vaddr;
|
||||
size_t length;
|
||||
};
|
||||
|
||||
/* The number of ipath_segs that fit in a page. */
|
||||
#define IPATH_SEGSZ (PAGE_SIZE / sizeof (struct ipath_seg))
|
||||
|
||||
struct ipath_segarray {
|
||||
struct ipath_seg segs[IPATH_SEGSZ];
|
||||
};
|
||||
|
||||
struct ipath_mregion {
|
||||
struct ib_pd *pd; /* shares refcnt of ibmr.pd */
|
||||
u64 user_base; /* User's address for this region */
|
||||
u64 iova; /* IB start address of this region */
|
||||
size_t length;
|
||||
u32 lkey;
|
||||
u32 offset; /* offset (bytes) to start of region */
|
||||
int access_flags;
|
||||
u32 max_segs; /* number of ipath_segs in all the arrays */
|
||||
u32 mapsz; /* size of the map array */
|
||||
struct ipath_segarray *map[0]; /* the segments */
|
||||
};
|
||||
|
||||
/*
|
||||
* These keep track of the copy progress within a memory region.
|
||||
* Used by the verbs layer.
|
||||
*/
|
||||
struct ipath_sge {
|
||||
struct ipath_mregion *mr;
|
||||
void *vaddr; /* kernel virtual address of segment */
|
||||
u32 sge_length; /* length of the SGE */
|
||||
u32 length; /* remaining length of the segment */
|
||||
u16 m; /* current index: mr->map[m] */
|
||||
u16 n; /* current index: mr->map[m]->segs[n] */
|
||||
};
|
||||
|
||||
/* Memory region */
|
||||
struct ipath_mr {
|
||||
struct ib_mr ibmr;
|
||||
struct ib_umem *umem;
|
||||
struct ipath_mregion mr; /* must be last */
|
||||
};
|
||||
|
||||
/*
|
||||
* Send work request queue entry.
|
||||
* The size of the sg_list is determined when the QP is created and stored
|
||||
* in qp->s_max_sge.
|
||||
*/
|
||||
struct ipath_swqe {
|
||||
union {
|
||||
struct ib_send_wr wr; /* don't use wr.sg_list */
|
||||
struct ib_ud_wr ud_wr;
|
||||
struct ib_rdma_wr rdma_wr;
|
||||
struct ib_atomic_wr atomic_wr;
|
||||
};
|
||||
|
||||
u32 psn; /* first packet sequence number */
|
||||
u32 lpsn; /* last packet sequence number */
|
||||
u32 ssn; /* send sequence number */
|
||||
u32 length; /* total length of data in sg_list */
|
||||
struct ipath_sge sg_list[0];
|
||||
};
|
||||
|
||||
/*
|
||||
* Receive work request queue entry.
|
||||
* The size of the sg_list is determined when the QP (or SRQ) is created
|
||||
* and stored in qp->r_rq.max_sge (or srq->rq.max_sge).
|
||||
*/
|
||||
struct ipath_rwqe {
|
||||
u64 wr_id;
|
||||
u8 num_sge;
|
||||
struct ib_sge sg_list[0];
|
||||
};
|
||||
|
||||
/*
|
||||
* This structure is used to contain the head pointer, tail pointer,
|
||||
* and receive work queue entries as a single memory allocation so
|
||||
* it can be mmap'ed into user space.
|
||||
* Note that the wq array elements are variable size so you can't
|
||||
* just index into the array to get the N'th element;
|
||||
* use get_rwqe_ptr() instead.
|
||||
*/
|
||||
struct ipath_rwq {
|
||||
u32 head; /* new work requests posted to the head */
|
||||
u32 tail; /* receives pull requests from here. */
|
||||
struct ipath_rwqe wq[0];
|
||||
};
|
||||
|
||||
struct ipath_rq {
|
||||
struct ipath_rwq *wq;
|
||||
spinlock_t lock;
|
||||
u32 size; /* size of RWQE array */
|
||||
u8 max_sge;
|
||||
};
|
||||
|
||||
struct ipath_srq {
|
||||
struct ib_srq ibsrq;
|
||||
struct ipath_rq rq;
|
||||
struct ipath_mmap_info *ip;
|
||||
/* send signal when number of RWQEs < limit */
|
||||
u32 limit;
|
||||
};
|
||||
|
||||
struct ipath_sge_state {
|
||||
struct ipath_sge *sg_list; /* next SGE to be used if any */
|
||||
struct ipath_sge sge; /* progress state for the current SGE */
|
||||
u8 num_sge;
|
||||
u8 static_rate;
|
||||
};
|
||||
|
||||
/*
|
||||
* This structure holds the information that the send tasklet needs
|
||||
* to send a RDMA read response or atomic operation.
|
||||
*/
|
||||
struct ipath_ack_entry {
|
||||
u8 opcode;
|
||||
u8 sent;
|
||||
u32 psn;
|
||||
union {
|
||||
struct ipath_sge_state rdma_sge;
|
||||
u64 atomic_data;
|
||||
};
|
||||
};
|
||||
|
||||
/*
|
||||
* Variables prefixed with s_ are for the requester (sender).
|
||||
* Variables prefixed with r_ are for the responder (receiver).
|
||||
* Variables prefixed with ack_ are for responder replies.
|
||||
*
|
||||
* Common variables are protected by both r_rq.lock and s_lock in that order
|
||||
* which only happens in modify_qp() or changing the QP 'state'.
|
||||
*/
|
||||
struct ipath_qp {
|
||||
struct ib_qp ibqp;
|
||||
struct ipath_qp *next; /* link list for QPN hash table */
|
||||
struct ipath_qp *timer_next; /* link list for ipath_ib_timer() */
|
||||
struct ipath_qp *pio_next; /* link for ipath_ib_piobufavail() */
|
||||
struct list_head piowait; /* link for wait PIO buf */
|
||||
struct list_head timerwait; /* link for waiting for timeouts */
|
||||
struct ib_ah_attr remote_ah_attr;
|
||||
struct ipath_ib_header s_hdr; /* next packet header to send */
|
||||
atomic_t refcount;
|
||||
wait_queue_head_t wait;
|
||||
wait_queue_head_t wait_dma;
|
||||
struct tasklet_struct s_task;
|
||||
struct ipath_mmap_info *ip;
|
||||
struct ipath_sge_state *s_cur_sge;
|
||||
struct ipath_verbs_txreq *s_tx;
|
||||
struct ipath_sge_state s_sge; /* current send request data */
|
||||
struct ipath_ack_entry s_ack_queue[IPATH_MAX_RDMA_ATOMIC + 1];
|
||||
struct ipath_sge_state s_ack_rdma_sge;
|
||||
struct ipath_sge_state s_rdma_read_sge;
|
||||
struct ipath_sge_state r_sge; /* current receive data */
|
||||
spinlock_t s_lock;
|
||||
atomic_t s_dma_busy;
|
||||
u16 s_pkt_delay;
|
||||
u16 s_hdrwords; /* size of s_hdr in 32 bit words */
|
||||
u32 s_cur_size; /* size of send packet in bytes */
|
||||
u32 s_len; /* total length of s_sge */
|
||||
u32 s_rdma_read_len; /* total length of s_rdma_read_sge */
|
||||
u32 s_next_psn; /* PSN for next request */
|
||||
u32 s_last_psn; /* last response PSN processed */
|
||||
u32 s_psn; /* current packet sequence number */
|
||||
u32 s_ack_rdma_psn; /* PSN for sending RDMA read responses */
|
||||
u32 s_ack_psn; /* PSN for acking sends and RDMA writes */
|
||||
u32 s_rnr_timeout; /* number of milliseconds for RNR timeout */
|
||||
u32 r_ack_psn; /* PSN for next ACK or atomic ACK */
|
||||
u64 r_wr_id; /* ID for current receive WQE */
|
||||
unsigned long r_aflags;
|
||||
u32 r_len; /* total length of r_sge */
|
||||
u32 r_rcv_len; /* receive data len processed */
|
||||
u32 r_psn; /* expected rcv packet sequence number */
|
||||
u32 r_msn; /* message sequence number */
|
||||
u8 state; /* QP state */
|
||||
u8 s_state; /* opcode of last packet sent */
|
||||
u8 s_ack_state; /* opcode of packet to ACK */
|
||||
u8 s_nak_state; /* non-zero if NAK is pending */
|
||||
u8 r_state; /* opcode of last packet received */
|
||||
u8 r_nak_state; /* non-zero if NAK is pending */
|
||||
u8 r_min_rnr_timer; /* retry timeout value for RNR NAKs */
|
||||
u8 r_flags;
|
||||
u8 r_max_rd_atomic; /* max number of RDMA read/atomic to receive */
|
||||
u8 r_head_ack_queue; /* index into s_ack_queue[] */
|
||||
u8 qp_access_flags;
|
||||
u8 s_max_sge; /* size of s_wq->sg_list */
|
||||
u8 s_retry_cnt; /* number of times to retry */
|
||||
u8 s_rnr_retry_cnt;
|
||||
u8 s_retry; /* requester retry counter */
|
||||
u8 s_rnr_retry; /* requester RNR retry counter */
|
||||
u8 s_pkey_index; /* PKEY index to use */
|
||||
u8 s_max_rd_atomic; /* max number of RDMA read/atomic to send */
|
||||
u8 s_num_rd_atomic; /* number of RDMA read/atomic pending */
|
||||
u8 s_tail_ack_queue; /* index into s_ack_queue[] */
|
||||
u8 s_flags;
|
||||
u8 s_dmult;
|
||||
u8 s_draining;
|
||||
u8 timeout; /* Timeout for this QP */
|
||||
enum ib_mtu path_mtu;
|
||||
u32 remote_qpn;
|
||||
u32 qkey; /* QKEY for this QP (for UD or RD) */
|
||||
u32 s_size; /* send work queue size */
|
||||
u32 s_head; /* new entries added here */
|
||||
u32 s_tail; /* next entry to process */
|
||||
u32 s_cur; /* current work queue entry */
|
||||
u32 s_last; /* last un-ACK'ed entry */
|
||||
u32 s_ssn; /* SSN of tail entry */
|
||||
u32 s_lsn; /* limit sequence number (credit) */
|
||||
struct ipath_swqe *s_wq; /* send work queue */
|
||||
struct ipath_swqe *s_wqe;
|
||||
struct ipath_sge *r_ud_sg_list;
|
||||
struct ipath_rq r_rq; /* receive work queue */
|
||||
struct ipath_sge r_sg_list[0]; /* verified SGEs */
|
||||
};
|
||||
|
||||
/*
|
||||
* Atomic bit definitions for r_aflags.
|
||||
*/
|
||||
#define IPATH_R_WRID_VALID 0
|
||||
|
||||
/*
|
||||
* Bit definitions for r_flags.
|
||||
*/
|
||||
#define IPATH_R_REUSE_SGE 0x01
|
||||
#define IPATH_R_RDMAR_SEQ 0x02
|
||||
|
||||
/*
|
||||
* Bit definitions for s_flags.
|
||||
*
|
||||
* IPATH_S_FENCE_PENDING - waiting for all prior RDMA read or atomic SWQEs
|
||||
* before processing the next SWQE
|
||||
* IPATH_S_RDMAR_PENDING - waiting for any RDMA read or atomic SWQEs
|
||||
* before processing the next SWQE
|
||||
* IPATH_S_WAITING - waiting for RNR timeout or send buffer available.
|
||||
* IPATH_S_WAIT_SSN_CREDIT - waiting for RC credits to process next SWQE
|
||||
* IPATH_S_WAIT_DMA - waiting for send DMA queue to drain before generating
|
||||
* next send completion entry not via send DMA.
|
||||
*/
|
||||
#define IPATH_S_SIGNAL_REQ_WR 0x01
|
||||
#define IPATH_S_FENCE_PENDING 0x02
|
||||
#define IPATH_S_RDMAR_PENDING 0x04
|
||||
#define IPATH_S_ACK_PENDING 0x08
|
||||
#define IPATH_S_BUSY 0x10
|
||||
#define IPATH_S_WAITING 0x20
|
||||
#define IPATH_S_WAIT_SSN_CREDIT 0x40
|
||||
#define IPATH_S_WAIT_DMA 0x80
|
||||
|
||||
#define IPATH_S_ANY_WAIT (IPATH_S_FENCE_PENDING | IPATH_S_RDMAR_PENDING | \
|
||||
IPATH_S_WAITING | IPATH_S_WAIT_SSN_CREDIT | IPATH_S_WAIT_DMA)
|
||||
|
||||
#define IPATH_PSN_CREDIT 512
|
||||
|
||||
/*
|
||||
* Since struct ipath_swqe is not a fixed size, we can't simply index into
|
||||
* struct ipath_qp.s_wq. This function does the array index computation.
|
||||
*/
|
||||
static inline struct ipath_swqe *get_swqe_ptr(struct ipath_qp *qp,
|
||||
unsigned n)
|
||||
{
|
||||
return (struct ipath_swqe *)((char *)qp->s_wq +
|
||||
(sizeof(struct ipath_swqe) +
|
||||
qp->s_max_sge *
|
||||
sizeof(struct ipath_sge)) * n);
|
||||
}
|
||||
|
||||
/*
|
||||
* Since struct ipath_rwqe is not a fixed size, we can't simply index into
|
||||
* struct ipath_rwq.wq. This function does the array index computation.
|
||||
*/
|
||||
static inline struct ipath_rwqe *get_rwqe_ptr(struct ipath_rq *rq,
|
||||
unsigned n)
|
||||
{
|
||||
return (struct ipath_rwqe *)
|
||||
((char *) rq->wq->wq +
|
||||
(sizeof(struct ipath_rwqe) +
|
||||
rq->max_sge * sizeof(struct ib_sge)) * n);
|
||||
}
|
||||
|
||||
/*
|
||||
* QPN-map pages start out as NULL, they get allocated upon
|
||||
* first use and are never deallocated. This way,
|
||||
* large bitmaps are not allocated unless large numbers of QPs are used.
|
||||
*/
|
||||
struct qpn_map {
|
||||
atomic_t n_free;
|
||||
void *page;
|
||||
};
|
||||
|
||||
struct ipath_qp_table {
|
||||
spinlock_t lock;
|
||||
u32 last; /* last QP number allocated */
|
||||
u32 max; /* size of the hash table */
|
||||
u32 nmaps; /* size of the map table */
|
||||
struct ipath_qp **table;
|
||||
/* bit map of free numbers */
|
||||
struct qpn_map map[QPNMAP_ENTRIES];
|
||||
};
|
||||
|
||||
struct ipath_lkey_table {
|
||||
spinlock_t lock;
|
||||
u32 next; /* next unused index (speeds search) */
|
||||
u32 gen; /* generation count */
|
||||
u32 max; /* size of the table */
|
||||
struct ipath_mregion **table;
|
||||
};
|
||||
|
||||
struct ipath_opcode_stats {
|
||||
u64 n_packets; /* number of packets */
|
||||
u64 n_bytes; /* total number of bytes */
|
||||
};
|
||||
|
||||
struct ipath_ibdev {
|
||||
struct ib_device ibdev;
|
||||
struct ipath_devdata *dd;
|
||||
struct list_head pending_mmaps;
|
||||
spinlock_t mmap_offset_lock;
|
||||
u32 mmap_offset;
|
||||
int ib_unit; /* This is the device number */
|
||||
u16 sm_lid; /* in host order */
|
||||
u8 sm_sl;
|
||||
u8 mkeyprot;
|
||||
/* non-zero when timer is set */
|
||||
unsigned long mkey_lease_timeout;
|
||||
|
||||
/* The following fields are really per port. */
|
||||
struct ipath_qp_table qp_table;
|
||||
struct ipath_lkey_table lk_table;
|
||||
struct list_head pending[3]; /* FIFO of QPs waiting for ACKs */
|
||||
struct list_head piowait; /* list for wait PIO buf */
|
||||
struct list_head txreq_free;
|
||||
void *txreq_bufs;
|
||||
/* list of QPs waiting for RNR timer */
|
||||
struct list_head rnrwait;
|
||||
spinlock_t pending_lock;
|
||||
__be64 sys_image_guid; /* in network order */
|
||||
__be64 gid_prefix; /* in network order */
|
||||
__be64 mkey;
|
||||
|
||||
u32 n_pds_allocated; /* number of PDs allocated for device */
|
||||
spinlock_t n_pds_lock;
|
||||
u32 n_ahs_allocated; /* number of AHs allocated for device */
|
||||
spinlock_t n_ahs_lock;
|
||||
u32 n_cqs_allocated; /* number of CQs allocated for device */
|
||||
spinlock_t n_cqs_lock;
|
||||
u32 n_qps_allocated; /* number of QPs allocated for device */
|
||||
spinlock_t n_qps_lock;
|
||||
u32 n_srqs_allocated; /* number of SRQs allocated for device */
|
||||
spinlock_t n_srqs_lock;
|
||||
u32 n_mcast_grps_allocated; /* number of mcast groups allocated */
|
||||
spinlock_t n_mcast_grps_lock;
|
||||
|
||||
u64 ipath_sword; /* total dwords sent (sample result) */
|
||||
u64 ipath_rword; /* total dwords received (sample result) */
|
||||
u64 ipath_spkts; /* total packets sent (sample result) */
|
||||
u64 ipath_rpkts; /* total packets received (sample result) */
|
||||
/* # of ticks no data sent (sample result) */
|
||||
u64 ipath_xmit_wait;
|
||||
u64 rcv_errors; /* # of packets with SW detected rcv errs */
|
||||
u64 n_unicast_xmit; /* total unicast packets sent */
|
||||
u64 n_unicast_rcv; /* total unicast packets received */
|
||||
u64 n_multicast_xmit; /* total multicast packets sent */
|
||||
u64 n_multicast_rcv; /* total multicast packets received */
|
||||
u64 z_symbol_error_counter; /* starting count for PMA */
|
||||
u64 z_link_error_recovery_counter; /* starting count for PMA */
|
||||
u64 z_link_downed_counter; /* starting count for PMA */
|
||||
u64 z_port_rcv_errors; /* starting count for PMA */
|
||||
u64 z_port_rcv_remphys_errors; /* starting count for PMA */
|
||||
u64 z_port_xmit_discards; /* starting count for PMA */
|
||||
u64 z_port_xmit_data; /* starting count for PMA */
|
||||
u64 z_port_rcv_data; /* starting count for PMA */
|
||||
u64 z_port_xmit_packets; /* starting count for PMA */
|
||||
u64 z_port_rcv_packets; /* starting count for PMA */
|
||||
u32 z_pkey_violations; /* starting count for PMA */
|
||||
u32 z_local_link_integrity_errors; /* starting count for PMA */
|
||||
u32 z_excessive_buffer_overrun_errors; /* starting count for PMA */
|
||||
u32 z_vl15_dropped; /* starting count for PMA */
|
||||
u32 n_rc_resends;
|
||||
u32 n_rc_acks;
|
||||
u32 n_rc_qacks;
|
||||
u32 n_seq_naks;
|
||||
u32 n_rdma_seq;
|
||||
u32 n_rnr_naks;
|
||||
u32 n_other_naks;
|
||||
u32 n_timeouts;
|
||||
u32 n_pkt_drops;
|
||||
u32 n_vl15_dropped;
|
||||
u32 n_wqe_errs;
|
||||
u32 n_rdma_dup_busy;
|
||||
u32 n_piowait;
|
||||
u32 n_unaligned;
|
||||
u32 port_cap_flags;
|
||||
u32 pma_sample_start;
|
||||
u32 pma_sample_interval;
|
||||
__be16 pma_counter_select[5];
|
||||
u16 pma_tag;
|
||||
u16 qkey_violations;
|
||||
u16 mkey_violations;
|
||||
u16 mkey_lease_period;
|
||||
u16 pending_index; /* which pending queue is active */
|
||||
u8 pma_sample_status;
|
||||
u8 subnet_timeout;
|
||||
u8 vl_high_limit;
|
||||
struct ipath_opcode_stats opstats[128];
|
||||
};
|
||||
|
||||
struct ipath_verbs_counters {
|
||||
u64 symbol_error_counter;
|
||||
u64 link_error_recovery_counter;
|
||||
u64 link_downed_counter;
|
||||
u64 port_rcv_errors;
|
||||
u64 port_rcv_remphys_errors;
|
||||
u64 port_xmit_discards;
|
||||
u64 port_xmit_data;
|
||||
u64 port_rcv_data;
|
||||
u64 port_xmit_packets;
|
||||
u64 port_rcv_packets;
|
||||
u32 local_link_integrity_errors;
|
||||
u32 excessive_buffer_overrun_errors;
|
||||
u32 vl15_dropped;
|
||||
};
|
||||
|
||||
struct ipath_verbs_txreq {
|
||||
struct ipath_qp *qp;
|
||||
struct ipath_swqe *wqe;
|
||||
u32 map_len;
|
||||
u32 len;
|
||||
struct ipath_sge_state *ss;
|
||||
struct ipath_pio_header hdr;
|
||||
struct ipath_sdma_txreq txreq;
|
||||
};
|
||||
|
||||
static inline struct ipath_mr *to_imr(struct ib_mr *ibmr)
|
||||
{
|
||||
return container_of(ibmr, struct ipath_mr, ibmr);
|
||||
}
|
||||
|
||||
static inline struct ipath_pd *to_ipd(struct ib_pd *ibpd)
|
||||
{
|
||||
return container_of(ibpd, struct ipath_pd, ibpd);
|
||||
}
|
||||
|
||||
static inline struct ipath_ah *to_iah(struct ib_ah *ibah)
|
||||
{
|
||||
return container_of(ibah, struct ipath_ah, ibah);
|
||||
}
|
||||
|
||||
static inline struct ipath_cq *to_icq(struct ib_cq *ibcq)
|
||||
{
|
||||
return container_of(ibcq, struct ipath_cq, ibcq);
|
||||
}
|
||||
|
||||
static inline struct ipath_srq *to_isrq(struct ib_srq *ibsrq)
|
||||
{
|
||||
return container_of(ibsrq, struct ipath_srq, ibsrq);
|
||||
}
|
||||
|
||||
static inline struct ipath_qp *to_iqp(struct ib_qp *ibqp)
|
||||
{
|
||||
return container_of(ibqp, struct ipath_qp, ibqp);
|
||||
}
|
||||
|
||||
static inline struct ipath_ibdev *to_idev(struct ib_device *ibdev)
|
||||
{
|
||||
return container_of(ibdev, struct ipath_ibdev, ibdev);
|
||||
}
|
||||
|
||||
/*
|
||||
* This must be called with s_lock held.
|
||||
*/
|
||||
static inline void ipath_schedule_send(struct ipath_qp *qp)
|
||||
{
|
||||
if (qp->s_flags & IPATH_S_ANY_WAIT)
|
||||
qp->s_flags &= ~IPATH_S_ANY_WAIT;
|
||||
if (!(qp->s_flags & IPATH_S_BUSY))
|
||||
tasklet_hi_schedule(&qp->s_task);
|
||||
}
|
||||
|
||||
int ipath_process_mad(struct ib_device *ibdev,
|
||||
int mad_flags,
|
||||
u8 port_num,
|
||||
const struct ib_wc *in_wc,
|
||||
const struct ib_grh *in_grh,
|
||||
const struct ib_mad_hdr *in, size_t in_mad_size,
|
||||
struct ib_mad_hdr *out, size_t *out_mad_size,
|
||||
u16 *out_mad_pkey_index);
|
||||
|
||||
/*
|
||||
* Compare the lower 24 bits of the two values.
|
||||
* Returns an integer <, ==, or > than zero.
|
||||
*/
|
||||
static inline int ipath_cmp24(u32 a, u32 b)
|
||||
{
|
||||
return (((int) a) - ((int) b)) << 8;
|
||||
}
|
||||
|
||||
struct ipath_mcast *ipath_mcast_find(union ib_gid *mgid);
|
||||
|
||||
int ipath_snapshot_counters(struct ipath_devdata *dd, u64 *swords,
|
||||
u64 *rwords, u64 *spkts, u64 *rpkts,
|
||||
u64 *xmit_wait);
|
||||
|
||||
int ipath_get_counters(struct ipath_devdata *dd,
|
||||
struct ipath_verbs_counters *cntrs);
|
||||
|
||||
int ipath_multicast_attach(struct ib_qp *ibqp, union ib_gid *gid, u16 lid);
|
||||
|
||||
int ipath_multicast_detach(struct ib_qp *ibqp, union ib_gid *gid, u16 lid);
|
||||
|
||||
int ipath_mcast_tree_empty(void);
|
||||
|
||||
__be32 ipath_compute_aeth(struct ipath_qp *qp);
|
||||
|
||||
struct ipath_qp *ipath_lookup_qpn(struct ipath_qp_table *qpt, u32 qpn);
|
||||
|
||||
struct ib_qp *ipath_create_qp(struct ib_pd *ibpd,
|
||||
struct ib_qp_init_attr *init_attr,
|
||||
struct ib_udata *udata);
|
||||
|
||||
int ipath_destroy_qp(struct ib_qp *ibqp);
|
||||
|
||||
int ipath_error_qp(struct ipath_qp *qp, enum ib_wc_status err);
|
||||
|
||||
int ipath_modify_qp(struct ib_qp *ibqp, struct ib_qp_attr *attr,
|
||||
int attr_mask, struct ib_udata *udata);
|
||||
|
||||
int ipath_query_qp(struct ib_qp *ibqp, struct ib_qp_attr *attr,
|
||||
int attr_mask, struct ib_qp_init_attr *init_attr);
|
||||
|
||||
unsigned ipath_free_all_qps(struct ipath_qp_table *qpt);
|
||||
|
||||
int ipath_init_qp_table(struct ipath_ibdev *idev, int size);
|
||||
|
||||
void ipath_get_credit(struct ipath_qp *qp, u32 aeth);
|
||||
|
||||
unsigned ipath_ib_rate_to_mult(enum ib_rate rate);
|
||||
|
||||
int ipath_verbs_send(struct ipath_qp *qp, struct ipath_ib_header *hdr,
|
||||
u32 hdrwords, struct ipath_sge_state *ss, u32 len);
|
||||
|
||||
void ipath_copy_sge(struct ipath_sge_state *ss, void *data, u32 length);
|
||||
|
||||
void ipath_skip_sge(struct ipath_sge_state *ss, u32 length);
|
||||
|
||||
void ipath_uc_rcv(struct ipath_ibdev *dev, struct ipath_ib_header *hdr,
|
||||
int has_grh, void *data, u32 tlen, struct ipath_qp *qp);
|
||||
|
||||
void ipath_rc_rcv(struct ipath_ibdev *dev, struct ipath_ib_header *hdr,
|
||||
int has_grh, void *data, u32 tlen, struct ipath_qp *qp);
|
||||
|
||||
void ipath_restart_rc(struct ipath_qp *qp, u32 psn);
|
||||
|
||||
void ipath_rc_error(struct ipath_qp *qp, enum ib_wc_status err);
|
||||
|
||||
int ipath_post_ud_send(struct ipath_qp *qp, struct ib_send_wr *wr);
|
||||
|
||||
void ipath_ud_rcv(struct ipath_ibdev *dev, struct ipath_ib_header *hdr,
|
||||
int has_grh, void *data, u32 tlen, struct ipath_qp *qp);
|
||||
|
||||
int ipath_alloc_lkey(struct ipath_lkey_table *rkt,
|
||||
struct ipath_mregion *mr);
|
||||
|
||||
void ipath_free_lkey(struct ipath_lkey_table *rkt, u32 lkey);
|
||||
|
||||
int ipath_lkey_ok(struct ipath_qp *qp, struct ipath_sge *isge,
|
||||
struct ib_sge *sge, int acc);
|
||||
|
||||
int ipath_rkey_ok(struct ipath_qp *qp, struct ipath_sge_state *ss,
|
||||
u32 len, u64 vaddr, u32 rkey, int acc);
|
||||
|
||||
int ipath_post_srq_receive(struct ib_srq *ibsrq, struct ib_recv_wr *wr,
|
||||
struct ib_recv_wr **bad_wr);
|
||||
|
||||
struct ib_srq *ipath_create_srq(struct ib_pd *ibpd,
|
||||
struct ib_srq_init_attr *srq_init_attr,
|
||||
struct ib_udata *udata);
|
||||
|
||||
int ipath_modify_srq(struct ib_srq *ibsrq, struct ib_srq_attr *attr,
|
||||
enum ib_srq_attr_mask attr_mask,
|
||||
struct ib_udata *udata);
|
||||
|
||||
int ipath_query_srq(struct ib_srq *ibsrq, struct ib_srq_attr *attr);
|
||||
|
||||
int ipath_destroy_srq(struct ib_srq *ibsrq);
|
||||
|
||||
void ipath_cq_enter(struct ipath_cq *cq, struct ib_wc *entry, int sig);
|
||||
|
||||
int ipath_poll_cq(struct ib_cq *ibcq, int num_entries, struct ib_wc *entry);
|
||||
|
||||
struct ib_cq *ipath_create_cq(struct ib_device *ibdev,
|
||||
const struct ib_cq_init_attr *attr,
|
||||
struct ib_ucontext *context,
|
||||
struct ib_udata *udata);
|
||||
|
||||
int ipath_destroy_cq(struct ib_cq *ibcq);
|
||||
|
||||
int ipath_req_notify_cq(struct ib_cq *ibcq, enum ib_cq_notify_flags notify_flags);
|
||||
|
||||
int ipath_resize_cq(struct ib_cq *ibcq, int cqe, struct ib_udata *udata);
|
||||
|
||||
struct ib_mr *ipath_get_dma_mr(struct ib_pd *pd, int acc);
|
||||
|
||||
struct ib_mr *ipath_reg_user_mr(struct ib_pd *pd, u64 start, u64 length,
|
||||
u64 virt_addr, int mr_access_flags,
|
||||
struct ib_udata *udata);
|
||||
|
||||
int ipath_dereg_mr(struct ib_mr *ibmr);
|
||||
|
||||
struct ib_fmr *ipath_alloc_fmr(struct ib_pd *pd, int mr_access_flags,
|
||||
struct ib_fmr_attr *fmr_attr);
|
||||
|
||||
int ipath_map_phys_fmr(struct ib_fmr *ibfmr, u64 * page_list,
|
||||
int list_len, u64 iova);
|
||||
|
||||
int ipath_unmap_fmr(struct list_head *fmr_list);
|
||||
|
||||
int ipath_dealloc_fmr(struct ib_fmr *ibfmr);
|
||||
|
||||
void ipath_release_mmap_info(struct kref *ref);
|
||||
|
||||
struct ipath_mmap_info *ipath_create_mmap_info(struct ipath_ibdev *dev,
|
||||
u32 size,
|
||||
struct ib_ucontext *context,
|
||||
void *obj);
|
||||
|
||||
void ipath_update_mmap_info(struct ipath_ibdev *dev,
|
||||
struct ipath_mmap_info *ip,
|
||||
u32 size, void *obj);
|
||||
|
||||
int ipath_mmap(struct ib_ucontext *context, struct vm_area_struct *vma);
|
||||
|
||||
void ipath_insert_rnr_queue(struct ipath_qp *qp);
|
||||
|
||||
int ipath_init_sge(struct ipath_qp *qp, struct ipath_rwqe *wqe,
|
||||
u32 *lengthp, struct ipath_sge_state *ss);
|
||||
|
||||
int ipath_get_rwqe(struct ipath_qp *qp, int wr_id_only);
|
||||
|
||||
u32 ipath_make_grh(struct ipath_ibdev *dev, struct ib_grh *hdr,
|
||||
struct ib_global_route *grh, u32 hwords, u32 nwords);
|
||||
|
||||
void ipath_make_ruc_header(struct ipath_ibdev *dev, struct ipath_qp *qp,
|
||||
struct ipath_other_headers *ohdr,
|
||||
u32 bth0, u32 bth2);
|
||||
|
||||
void ipath_do_send(unsigned long data);
|
||||
|
||||
void ipath_send_complete(struct ipath_qp *qp, struct ipath_swqe *wqe,
|
||||
enum ib_wc_status status);
|
||||
|
||||
int ipath_make_rc_req(struct ipath_qp *qp);
|
||||
|
||||
int ipath_make_uc_req(struct ipath_qp *qp);
|
||||
|
||||
int ipath_make_ud_req(struct ipath_qp *qp);
|
||||
|
||||
int ipath_register_ib_device(struct ipath_devdata *);
|
||||
|
||||
void ipath_unregister_ib_device(struct ipath_ibdev *);
|
||||
|
||||
void ipath_ib_rcv(struct ipath_ibdev *, void *, void *, u32);
|
||||
|
||||
int ipath_ib_piobufavail(struct ipath_ibdev *);
|
||||
|
||||
unsigned ipath_get_npkeys(struct ipath_devdata *);
|
||||
|
||||
u32 ipath_get_cr_errpkey(struct ipath_devdata *);
|
||||
|
||||
unsigned ipath_get_pkey(struct ipath_devdata *, unsigned);
|
||||
|
||||
extern const enum ib_wc_opcode ib_ipath_wc_opcode[];
|
||||
|
||||
/*
|
||||
* Below converts HCA-specific LinkTrainingState to IB PhysPortState
|
||||
* values.
|
||||
*/
|
||||
extern const u8 ipath_cvt_physportstate[];
|
||||
#define IB_PHYSPORTSTATE_SLEEP 1
|
||||
#define IB_PHYSPORTSTATE_POLL 2
|
||||
#define IB_PHYSPORTSTATE_DISABLED 3
|
||||
#define IB_PHYSPORTSTATE_CFG_TRAIN 4
|
||||
#define IB_PHYSPORTSTATE_LINKUP 5
|
||||
#define IB_PHYSPORTSTATE_LINK_ERR_RECOVER 6
|
||||
|
||||
extern const int ib_ipath_state_ops[];
|
||||
|
||||
extern unsigned int ib_ipath_lkey_table_size;
|
||||
|
||||
extern unsigned int ib_ipath_max_cqes;
|
||||
|
||||
extern unsigned int ib_ipath_max_cqs;
|
||||
|
||||
extern unsigned int ib_ipath_max_qp_wrs;
|
||||
|
||||
extern unsigned int ib_ipath_max_qps;
|
||||
|
||||
extern unsigned int ib_ipath_max_sges;
|
||||
|
||||
extern unsigned int ib_ipath_max_mcast_grps;
|
||||
|
||||
extern unsigned int ib_ipath_max_mcast_qp_attached;
|
||||
|
||||
extern unsigned int ib_ipath_max_srqs;
|
||||
|
||||
extern unsigned int ib_ipath_max_srq_sges;
|
||||
|
||||
extern unsigned int ib_ipath_max_srq_wrs;
|
||||
|
||||
extern const u32 ib_ipath_rnr_table[];
|
||||
|
||||
extern struct ib_dma_mapping_ops ipath_dma_mapping_ops;
|
||||
|
||||
#endif /* IPATH_VERBS_H */
|
|
@ -1,363 +0,0 @@
|
|||
/*
|
||||
* Copyright (c) 2006, 2007 QLogic Corporation. All rights reserved.
|
||||
* Copyright (c) 2005, 2006 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.
|
||||
*/
|
||||
|
||||
#include <linux/rculist.h>
|
||||
#include <linux/slab.h>
|
||||
|
||||
#include "ipath_verbs.h"
|
||||
|
||||
/*
|
||||
* Global table of GID to attached QPs.
|
||||
* The table is global to all ipath devices since a send from one QP/device
|
||||
* needs to be locally routed to any locally attached QPs on the same
|
||||
* or different device.
|
||||
*/
|
||||
static struct rb_root mcast_tree;
|
||||
static DEFINE_SPINLOCK(mcast_lock);
|
||||
|
||||
/**
|
||||
* ipath_mcast_qp_alloc - alloc a struct to link a QP to mcast GID struct
|
||||
* @qp: the QP to link
|
||||
*/
|
||||
static struct ipath_mcast_qp *ipath_mcast_qp_alloc(struct ipath_qp *qp)
|
||||
{
|
||||
struct ipath_mcast_qp *mqp;
|
||||
|
||||
mqp = kmalloc(sizeof *mqp, GFP_KERNEL);
|
||||
if (!mqp)
|
||||
goto bail;
|
||||
|
||||
mqp->qp = qp;
|
||||
atomic_inc(&qp->refcount);
|
||||
|
||||
bail:
|
||||
return mqp;
|
||||
}
|
||||
|
||||
static void ipath_mcast_qp_free(struct ipath_mcast_qp *mqp)
|
||||
{
|
||||
struct ipath_qp *qp = mqp->qp;
|
||||
|
||||
/* Notify ipath_destroy_qp() if it is waiting. */
|
||||
if (atomic_dec_and_test(&qp->refcount))
|
||||
wake_up(&qp->wait);
|
||||
|
||||
kfree(mqp);
|
||||
}
|
||||
|
||||
/**
|
||||
* ipath_mcast_alloc - allocate the multicast GID structure
|
||||
* @mgid: the multicast GID
|
||||
*
|
||||
* A list of QPs will be attached to this structure.
|
||||
*/
|
||||
static struct ipath_mcast *ipath_mcast_alloc(union ib_gid *mgid)
|
||||
{
|
||||
struct ipath_mcast *mcast;
|
||||
|
||||
mcast = kmalloc(sizeof *mcast, GFP_KERNEL);
|
||||
if (!mcast)
|
||||
goto bail;
|
||||
|
||||
mcast->mgid = *mgid;
|
||||
INIT_LIST_HEAD(&mcast->qp_list);
|
||||
init_waitqueue_head(&mcast->wait);
|
||||
atomic_set(&mcast->refcount, 0);
|
||||
mcast->n_attached = 0;
|
||||
|
||||
bail:
|
||||
return mcast;
|
||||
}
|
||||
|
||||
static void ipath_mcast_free(struct ipath_mcast *mcast)
|
||||
{
|
||||
struct ipath_mcast_qp *p, *tmp;
|
||||
|
||||
list_for_each_entry_safe(p, tmp, &mcast->qp_list, list)
|
||||
ipath_mcast_qp_free(p);
|
||||
|
||||
kfree(mcast);
|
||||
}
|
||||
|
||||
/**
|
||||
* ipath_mcast_find - search the global table for the given multicast GID
|
||||
* @mgid: the multicast GID to search for
|
||||
*
|
||||
* Returns NULL if not found.
|
||||
*
|
||||
* The caller is responsible for decrementing the reference count if found.
|
||||
*/
|
||||
struct ipath_mcast *ipath_mcast_find(union ib_gid *mgid)
|
||||
{
|
||||
struct rb_node *n;
|
||||
unsigned long flags;
|
||||
struct ipath_mcast *mcast;
|
||||
|
||||
spin_lock_irqsave(&mcast_lock, flags);
|
||||
n = mcast_tree.rb_node;
|
||||
while (n) {
|
||||
int ret;
|
||||
|
||||
mcast = rb_entry(n, struct ipath_mcast, rb_node);
|
||||
|
||||
ret = memcmp(mgid->raw, mcast->mgid.raw,
|
||||
sizeof(union ib_gid));
|
||||
if (ret < 0)
|
||||
n = n->rb_left;
|
||||
else if (ret > 0)
|
||||
n = n->rb_right;
|
||||
else {
|
||||
atomic_inc(&mcast->refcount);
|
||||
spin_unlock_irqrestore(&mcast_lock, flags);
|
||||
goto bail;
|
||||
}
|
||||
}
|
||||
spin_unlock_irqrestore(&mcast_lock, flags);
|
||||
|
||||
mcast = NULL;
|
||||
|
||||
bail:
|
||||
return mcast;
|
||||
}
|
||||
|
||||
/**
|
||||
* ipath_mcast_add - insert mcast GID into table and attach QP struct
|
||||
* @mcast: the mcast GID table
|
||||
* @mqp: the QP to attach
|
||||
*
|
||||
* Return zero if both were added. Return EEXIST if the GID was already in
|
||||
* the table but the QP was added. Return ESRCH if the QP was already
|
||||
* attached and neither structure was added.
|
||||
*/
|
||||
static int ipath_mcast_add(struct ipath_ibdev *dev,
|
||||
struct ipath_mcast *mcast,
|
||||
struct ipath_mcast_qp *mqp)
|
||||
{
|
||||
struct rb_node **n = &mcast_tree.rb_node;
|
||||
struct rb_node *pn = NULL;
|
||||
int ret;
|
||||
|
||||
spin_lock_irq(&mcast_lock);
|
||||
|
||||
while (*n) {
|
||||
struct ipath_mcast *tmcast;
|
||||
struct ipath_mcast_qp *p;
|
||||
|
||||
pn = *n;
|
||||
tmcast = rb_entry(pn, struct ipath_mcast, rb_node);
|
||||
|
||||
ret = memcmp(mcast->mgid.raw, tmcast->mgid.raw,
|
||||
sizeof(union ib_gid));
|
||||
if (ret < 0) {
|
||||
n = &pn->rb_left;
|
||||
continue;
|
||||
}
|
||||
if (ret > 0) {
|
||||
n = &pn->rb_right;
|
||||
continue;
|
||||
}
|
||||
|
||||
/* Search the QP list to see if this is already there. */
|
||||
list_for_each_entry_rcu(p, &tmcast->qp_list, list) {
|
||||
if (p->qp == mqp->qp) {
|
||||
ret = ESRCH;
|
||||
goto bail;
|
||||
}
|
||||
}
|
||||
if (tmcast->n_attached == ib_ipath_max_mcast_qp_attached) {
|
||||
ret = ENOMEM;
|
||||
goto bail;
|
||||
}
|
||||
|
||||
tmcast->n_attached++;
|
||||
|
||||
list_add_tail_rcu(&mqp->list, &tmcast->qp_list);
|
||||
ret = EEXIST;
|
||||
goto bail;
|
||||
}
|
||||
|
||||
spin_lock(&dev->n_mcast_grps_lock);
|
||||
if (dev->n_mcast_grps_allocated == ib_ipath_max_mcast_grps) {
|
||||
spin_unlock(&dev->n_mcast_grps_lock);
|
||||
ret = ENOMEM;
|
||||
goto bail;
|
||||
}
|
||||
|
||||
dev->n_mcast_grps_allocated++;
|
||||
spin_unlock(&dev->n_mcast_grps_lock);
|
||||
|
||||
mcast->n_attached++;
|
||||
|
||||
list_add_tail_rcu(&mqp->list, &mcast->qp_list);
|
||||
|
||||
atomic_inc(&mcast->refcount);
|
||||
rb_link_node(&mcast->rb_node, pn, n);
|
||||
rb_insert_color(&mcast->rb_node, &mcast_tree);
|
||||
|
||||
ret = 0;
|
||||
|
||||
bail:
|
||||
spin_unlock_irq(&mcast_lock);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
int ipath_multicast_attach(struct ib_qp *ibqp, union ib_gid *gid, u16 lid)
|
||||
{
|
||||
struct ipath_qp *qp = to_iqp(ibqp);
|
||||
struct ipath_ibdev *dev = to_idev(ibqp->device);
|
||||
struct ipath_mcast *mcast;
|
||||
struct ipath_mcast_qp *mqp;
|
||||
int ret;
|
||||
|
||||
/*
|
||||
* Allocate data structures since its better to do this outside of
|
||||
* spin locks and it will most likely be needed.
|
||||
*/
|
||||
mcast = ipath_mcast_alloc(gid);
|
||||
if (mcast == NULL) {
|
||||
ret = -ENOMEM;
|
||||
goto bail;
|
||||
}
|
||||
mqp = ipath_mcast_qp_alloc(qp);
|
||||
if (mqp == NULL) {
|
||||
ipath_mcast_free(mcast);
|
||||
ret = -ENOMEM;
|
||||
goto bail;
|
||||
}
|
||||
switch (ipath_mcast_add(dev, mcast, mqp)) {
|
||||
case ESRCH:
|
||||
/* Neither was used: can't attach the same QP twice. */
|
||||
ipath_mcast_qp_free(mqp);
|
||||
ipath_mcast_free(mcast);
|
||||
ret = -EINVAL;
|
||||
goto bail;
|
||||
case EEXIST: /* The mcast wasn't used */
|
||||
ipath_mcast_free(mcast);
|
||||
break;
|
||||
case ENOMEM:
|
||||
/* Exceeded the maximum number of mcast groups. */
|
||||
ipath_mcast_qp_free(mqp);
|
||||
ipath_mcast_free(mcast);
|
||||
ret = -ENOMEM;
|
||||
goto bail;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
ret = 0;
|
||||
|
||||
bail:
|
||||
return ret;
|
||||
}
|
||||
|
||||
int ipath_multicast_detach(struct ib_qp *ibqp, union ib_gid *gid, u16 lid)
|
||||
{
|
||||
struct ipath_qp *qp = to_iqp(ibqp);
|
||||
struct ipath_ibdev *dev = to_idev(ibqp->device);
|
||||
struct ipath_mcast *mcast = NULL;
|
||||
struct ipath_mcast_qp *p, *tmp;
|
||||
struct rb_node *n;
|
||||
int last = 0;
|
||||
int ret;
|
||||
|
||||
spin_lock_irq(&mcast_lock);
|
||||
|
||||
/* Find the GID in the mcast table. */
|
||||
n = mcast_tree.rb_node;
|
||||
while (1) {
|
||||
if (n == NULL) {
|
||||
spin_unlock_irq(&mcast_lock);
|
||||
ret = -EINVAL;
|
||||
goto bail;
|
||||
}
|
||||
|
||||
mcast = rb_entry(n, struct ipath_mcast, rb_node);
|
||||
ret = memcmp(gid->raw, mcast->mgid.raw,
|
||||
sizeof(union ib_gid));
|
||||
if (ret < 0)
|
||||
n = n->rb_left;
|
||||
else if (ret > 0)
|
||||
n = n->rb_right;
|
||||
else
|
||||
break;
|
||||
}
|
||||
|
||||
/* Search the QP list. */
|
||||
list_for_each_entry_safe(p, tmp, &mcast->qp_list, list) {
|
||||
if (p->qp != qp)
|
||||
continue;
|
||||
/*
|
||||
* We found it, so remove it, but don't poison the forward
|
||||
* link until we are sure there are no list walkers.
|
||||
*/
|
||||
list_del_rcu(&p->list);
|
||||
mcast->n_attached--;
|
||||
|
||||
/* If this was the last attached QP, remove the GID too. */
|
||||
if (list_empty(&mcast->qp_list)) {
|
||||
rb_erase(&mcast->rb_node, &mcast_tree);
|
||||
last = 1;
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
spin_unlock_irq(&mcast_lock);
|
||||
|
||||
if (p) {
|
||||
/*
|
||||
* Wait for any list walkers to finish before freeing the
|
||||
* list element.
|
||||
*/
|
||||
wait_event(mcast->wait, atomic_read(&mcast->refcount) <= 1);
|
||||
ipath_mcast_qp_free(p);
|
||||
}
|
||||
if (last) {
|
||||
atomic_dec(&mcast->refcount);
|
||||
wait_event(mcast->wait, !atomic_read(&mcast->refcount));
|
||||
ipath_mcast_free(mcast);
|
||||
spin_lock_irq(&dev->n_mcast_grps_lock);
|
||||
dev->n_mcast_grps_allocated--;
|
||||
spin_unlock_irq(&dev->n_mcast_grps_lock);
|
||||
}
|
||||
|
||||
ret = 0;
|
||||
|
||||
bail:
|
||||
return ret;
|
||||
}
|
||||
|
||||
int ipath_mcast_tree_empty(void)
|
||||
{
|
||||
return mcast_tree.rb_node == NULL;
|
||||
}
|
|
@ -1,49 +0,0 @@
|
|||
/*
|
||||
* Copyright (c) 2006, 2007 QLogic Corporation. 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.
|
||||
*/
|
||||
|
||||
/*
|
||||
* This file is conditionally built on PowerPC only. Otherwise weak symbol
|
||||
* versions of the functions exported from here are used.
|
||||
*/
|
||||
|
||||
#include "ipath_kernel.h"
|
||||
|
||||
/**
|
||||
* ipath_enable_wc - enable write combining for MMIO writes to the device
|
||||
* @dd: infinipath device
|
||||
*
|
||||
* Nothing to do on PowerPC, so just return without error.
|
||||
*/
|
||||
int ipath_enable_wc(struct ipath_devdata *dd)
|
||||
{
|
||||
return 0;
|
||||
}
|
|
@ -1,144 +0,0 @@
|
|||
/*
|
||||
* Copyright (c) 2006, 2007 QLogic Corporation. All rights reserved.
|
||||
* Copyright (c) 2003, 2004, 2005, 2006 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.
|
||||
*/
|
||||
|
||||
/*
|
||||
* This file is conditionally built on x86_64 only. Otherwise weak symbol
|
||||
* versions of the functions exported from here are used.
|
||||
*/
|
||||
|
||||
#include <linux/pci.h>
|
||||
#include <asm/processor.h>
|
||||
|
||||
#include "ipath_kernel.h"
|
||||
|
||||
/**
|
||||
* ipath_enable_wc - enable write combining for MMIO writes to the device
|
||||
* @dd: infinipath device
|
||||
*
|
||||
* This routine is x86_64-specific; it twiddles the CPU's MTRRs to enable
|
||||
* write combining.
|
||||
*/
|
||||
int ipath_enable_wc(struct ipath_devdata *dd)
|
||||
{
|
||||
int ret = 0;
|
||||
u64 pioaddr, piolen;
|
||||
unsigned bits;
|
||||
const unsigned long addr = pci_resource_start(dd->pcidev, 0);
|
||||
const size_t len = pci_resource_len(dd->pcidev, 0);
|
||||
|
||||
/*
|
||||
* Set the PIO buffers to be WCCOMB, so we get HT bursts to the
|
||||
* chip. Linux (possibly the hardware) requires it to be on a power
|
||||
* of 2 address matching the length (which has to be a power of 2).
|
||||
* For rev1, that means the base address, for rev2, it will be just
|
||||
* the PIO buffers themselves.
|
||||
* For chips with two sets of buffers, the calculations are
|
||||
* somewhat more complicated; we need to sum, and the piobufbase
|
||||
* register has both offsets, 2K in low 32 bits, 4K in high 32 bits.
|
||||
* The buffers are still packed, so a single range covers both.
|
||||
*/
|
||||
if (dd->ipath_piobcnt2k && dd->ipath_piobcnt4k) { /* 2 sizes */
|
||||
unsigned long pio2kbase, pio4kbase;
|
||||
pio2kbase = dd->ipath_piobufbase & 0xffffffffUL;
|
||||
pio4kbase = (dd->ipath_piobufbase >> 32) & 0xffffffffUL;
|
||||
if (pio2kbase < pio4kbase) { /* all, for now */
|
||||
pioaddr = addr + pio2kbase;
|
||||
piolen = pio4kbase - pio2kbase +
|
||||
dd->ipath_piobcnt4k * dd->ipath_4kalign;
|
||||
} else {
|
||||
pioaddr = addr + pio4kbase;
|
||||
piolen = pio2kbase - pio4kbase +
|
||||
dd->ipath_piobcnt2k * dd->ipath_palign;
|
||||
}
|
||||
} else { /* single buffer size (2K, currently) */
|
||||
pioaddr = addr + dd->ipath_piobufbase;
|
||||
piolen = dd->ipath_piobcnt2k * dd->ipath_palign +
|
||||
dd->ipath_piobcnt4k * dd->ipath_4kalign;
|
||||
}
|
||||
|
||||
for (bits = 0; !(piolen & (1ULL << bits)); bits++)
|
||||
/* do nothing */ ;
|
||||
|
||||
if (piolen != (1ULL << bits)) {
|
||||
piolen >>= bits;
|
||||
while (piolen >>= 1)
|
||||
bits++;
|
||||
piolen = 1ULL << (bits + 1);
|
||||
}
|
||||
if (pioaddr & (piolen - 1)) {
|
||||
u64 atmp;
|
||||
ipath_dbg("pioaddr %llx not on right boundary for size "
|
||||
"%llx, fixing\n",
|
||||
(unsigned long long) pioaddr,
|
||||
(unsigned long long) piolen);
|
||||
atmp = pioaddr & ~(piolen - 1);
|
||||
if (atmp < addr || (atmp + piolen) > (addr + len)) {
|
||||
ipath_dev_err(dd, "No way to align address/size "
|
||||
"(%llx/%llx), no WC mtrr\n",
|
||||
(unsigned long long) atmp,
|
||||
(unsigned long long) piolen << 1);
|
||||
ret = -ENODEV;
|
||||
} else {
|
||||
ipath_dbg("changing WC base from %llx to %llx, "
|
||||
"len from %llx to %llx\n",
|
||||
(unsigned long long) pioaddr,
|
||||
(unsigned long long) atmp,
|
||||
(unsigned long long) piolen,
|
||||
(unsigned long long) piolen << 1);
|
||||
pioaddr = atmp;
|
||||
piolen <<= 1;
|
||||
}
|
||||
}
|
||||
|
||||
if (!ret) {
|
||||
dd->wc_cookie = arch_phys_wc_add(pioaddr, piolen);
|
||||
if (dd->wc_cookie < 0) {
|
||||
ipath_dev_err(dd, "Seting mtrr failed on PIO buffers\n");
|
||||
ret = -ENODEV;
|
||||
} else if (dd->wc_cookie == 0)
|
||||
ipath_cdbg(VERBOSE, "Set mtrr for chip to WC not needed\n");
|
||||
else
|
||||
ipath_cdbg(VERBOSE, "Set mtrr for chip to WC\n");
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
/**
|
||||
* ipath_disable_wc - disable write combining for MMIO writes to the device
|
||||
* @dd: infinipath device
|
||||
*/
|
||||
void ipath_disable_wc(struct ipath_devdata *dd)
|
||||
{
|
||||
arch_phys_wc_del(dd->wc_cookie);
|
||||
}
|
Loading…
Reference in New Issue
Block a user