mirror of
https://github.com/AetherDroid/android_kernel_samsung_on5xelte.git
synced 2025-10-29 23:28:52 +01:00
Fixed MTP to work with TWRP
This commit is contained in:
commit
f6dfaef42e
50820 changed files with 20846062 additions and 0 deletions
11
drivers/infiniband/hw/ipath/Kconfig
Normal file
11
drivers/infiniband/hw/ipath/Kconfig
Normal file
|
|
@ -0,0 +1,11 @@
|
|||
config INFINIBAND_IPATH
|
||||
tristate "QLogic HTX HCA support"
|
||||
depends on 64BIT && NET && HT_IRQ
|
||||
---help---
|
||||
This is a driver for the obsolete 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.
|
||||
37
drivers/infiniband/hw/ipath/Makefile
Normal file
37
drivers/infiniband/hw/ipath/Makefile
Normal file
|
|
@ -0,0 +1,37 @@
|
|||
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
|
||||
851
drivers/infiniband/hw/ipath/ipath_common.h
Normal file
851
drivers/infiniband/hw/ipath/ipath_common.h
Normal file
|
|
@ -0,0 +1,851 @@
|
|||
/*
|
||||
* 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 */
|
||||
478
drivers/infiniband/hw/ipath/ipath_cq.c
Normal file
478
drivers/infiniband/hw/ipath/ipath_cq.c
Normal file
|
|
@ -0,0 +1,478 @@
|
|||
/*
|
||||
* 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
|
||||
* @entries: the minimum size of the completion queue
|
||||
* @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, int entries, int comp_vector,
|
||||
struct ib_ucontext *context,
|
||||
struct ib_udata *udata)
|
||||
{
|
||||
struct ipath_ibdev *dev = to_idev(ibdev);
|
||||
struct ipath_cq *cq;
|
||||
struct ipath_cq_wc *wc;
|
||||
struct ib_cq *ret;
|
||||
u32 sz;
|
||||
|
||||
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;
|
||||
}
|
||||
99
drivers/infiniband/hw/ipath/ipath_debug.h
Normal file
99
drivers/infiniband/hw/ipath/ipath_debug.h
Normal file
|
|
@ -0,0 +1,99 @@
|
|||
/*
|
||||
* 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 */
|
||||
551
drivers/infiniband/hw/ipath/ipath_diag.c
Normal file
551
drivers/infiniband/hw/ipath/ipath_diag.c
Normal file
|
|
@ -0,0 +1,551 @@
|
|||
/*
|
||||
* 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;
|
||||
}
|
||||
179
drivers/infiniband/hw/ipath/ipath_dma.c
Normal file
179
drivers/infiniband/hw/ipath/ipath_dma.c
Normal file
|
|
@ -0,0 +1,179 @@
|
|||
/*
|
||||
* 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
|
||||
};
|
||||
2779
drivers/infiniband/hw/ipath/ipath_driver.c
Normal file
2779
drivers/infiniband/hw/ipath/ipath_driver.c
Normal file
File diff suppressed because it is too large
Load diff
1183
drivers/infiniband/hw/ipath/ipath_eeprom.c
Normal file
1183
drivers/infiniband/hw/ipath/ipath_eeprom.c
Normal file
File diff suppressed because it is too large
Load diff
2617
drivers/infiniband/hw/ipath/ipath_file_ops.c
Normal file
2617
drivers/infiniband/hw/ipath/ipath_file_ops.c
Normal file
File diff suppressed because it is too large
Load diff
422
drivers/infiniband/hw/ipath/ipath_fs.c
Normal file
422
drivers/infiniband/hw/ipath/ipath_fs.c
Normal file
|
|
@ -0,0 +1,422 @@
|
|||
/*
|
||||
* 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;
|
||||
|
||||
mutex_lock(&parent->d_inode->i_mutex);
|
||||
*dentry = lookup_one_len(name, parent, strlen(name));
|
||||
if (!IS_ERR(*dentry))
|
||||
error = ipathfs_mknod(parent->d_inode, *dentry,
|
||||
mode, fops, data);
|
||||
else
|
||||
error = PTR_ERR(*dentry);
|
||||
mutex_unlock(&parent->d_inode->i_mutex);
|
||||
|
||||
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 = kmalloc(count, GFP_KERNEL);
|
||||
if (!tmp) {
|
||||
ret = -ENOMEM;
|
||||
goto bail;
|
||||
}
|
||||
|
||||
if (copy_from_user(tmp, buf, count)) {
|
||||
ret = -EFAULT;
|
||||
goto bail_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 (!(d_unhashed(tmp) && tmp->d_inode)) {
|
||||
dget_dlock(tmp);
|
||||
__d_drop(tmp);
|
||||
spin_unlock(&tmp->d_lock);
|
||||
simple_unlink(parent->d_inode, 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);
|
||||
mutex_lock(&root->d_inode->i_mutex);
|
||||
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(root->d_inode, dir);
|
||||
|
||||
bail:
|
||||
mutex_unlock(&root->d_inode->i_mutex);
|
||||
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);
|
||||
}
|
||||
1940
drivers/infiniband/hw/ipath/ipath_iba6110.c
Normal file
1940
drivers/infiniband/hw/ipath/ipath_iba6110.c
Normal file
File diff suppressed because it is too large
Load diff
1066
drivers/infiniband/hw/ipath/ipath_init_chip.c
Normal file
1066
drivers/infiniband/hw/ipath/ipath_init_chip.c
Normal file
File diff suppressed because it is too large
Load diff
1273
drivers/infiniband/hw/ipath/ipath_intr.c
Normal file
1273
drivers/infiniband/hw/ipath/ipath_intr.c
Normal file
File diff suppressed because it is too large
Load diff
1378
drivers/infiniband/hw/ipath/ipath_kernel.h
Normal file
1378
drivers/infiniband/hw/ipath/ipath_kernel.h
Normal file
File diff suppressed because it is too large
Load diff
270
drivers/infiniband/hw/ipath/ipath_keys.c
Normal file
270
drivers/infiniband/hw/ipath/ipath_keys.c
Normal file
|
|
@ -0,0 +1,270 @@
|
|||
/*
|
||||
* 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;
|
||||
}
|
||||
1513
drivers/infiniband/hw/ipath/ipath_mad.c
Normal file
1513
drivers/infiniband/hw/ipath/ipath_mad.c
Normal file
File diff suppressed because it is too large
Load diff
174
drivers/infiniband/hw/ipath/ipath_mmap.c
Normal file
174
drivers/infiniband/hw/ipath/ipath_mmap.c
Normal file
|
|
@ -0,0 +1,174 @@
|
|||
/*
|
||||
* 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;
|
||||
}
|
||||
425
drivers/infiniband/hw/ipath/ipath_mr.c
Normal file
425
drivers/infiniband/hw/ipath/ipath_mr.c
Normal file
|
|
@ -0,0 +1,425 @@
|
|||
/*
|
||||
* 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;
|
||||
|
||||
/*
|
||||
* ib_reg_phys_mr() will initialize mr->ibmr except for
|
||||
* lkey and rkey.
|
||||
*/
|
||||
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_phys_mr - register a physical memory region
|
||||
* @pd: protection domain for this memory region
|
||||
* @buffer_list: pointer to the list of physical buffers to register
|
||||
* @num_phys_buf: the number of physical buffers to register
|
||||
* @iova_start: the starting address passed over IB which maps to this MR
|
||||
*
|
||||
* Returns the memory region on success, otherwise returns an errno.
|
||||
*/
|
||||
struct ib_mr *ipath_reg_phys_mr(struct ib_pd *pd,
|
||||
struct ib_phys_buf *buffer_list,
|
||||
int num_phys_buf, int acc, u64 *iova_start)
|
||||
{
|
||||
struct ipath_mr *mr;
|
||||
int n, m, i;
|
||||
struct ib_mr *ret;
|
||||
|
||||
mr = alloc_mr(num_phys_buf, &to_idev(pd->device)->lk_table);
|
||||
if (mr == NULL) {
|
||||
ret = ERR_PTR(-ENOMEM);
|
||||
goto bail;
|
||||
}
|
||||
|
||||
mr->mr.pd = pd;
|
||||
mr->mr.user_base = *iova_start;
|
||||
mr->mr.iova = *iova_start;
|
||||
mr->mr.length = 0;
|
||||
mr->mr.offset = 0;
|
||||
mr->mr.access_flags = acc;
|
||||
mr->mr.max_segs = num_phys_buf;
|
||||
mr->umem = NULL;
|
||||
|
||||
m = 0;
|
||||
n = 0;
|
||||
for (i = 0; i < num_phys_buf; i++) {
|
||||
mr->mr.map[m]->segs[n].vaddr = (void *) buffer_list[i].addr;
|
||||
mr->mr.map[m]->segs[n].length = buffer_list[i].size;
|
||||
mr->mr.length += buffer_list[i].size;
|
||||
n++;
|
||||
if (n == IPATH_SEGSZ) {
|
||||
m++;
|
||||
n = 0;
|
||||
}
|
||||
}
|
||||
|
||||
ret = &mr->ibmr;
|
||||
|
||||
bail:
|
||||
return ret;
|
||||
}
|
||||
|
||||
/**
|
||||
* 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 = umem->offset;
|
||||
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;
|
||||
}
|
||||
1080
drivers/infiniband/hw/ipath/ipath_qp.c
Normal file
1080
drivers/infiniband/hw/ipath/ipath_qp.c
Normal file
File diff suppressed because it is too large
Load diff
1969
drivers/infiniband/hw/ipath/ipath_rc.c
Normal file
1969
drivers/infiniband/hw/ipath/ipath_rc.c
Normal file
File diff suppressed because it is too large
Load diff
512
drivers/infiniband/hw/ipath/ipath_registers.h
Normal file
512
drivers/infiniband/hw/ipath/ipath_registers.h
Normal file
|
|
@ -0,0 +1,512 @@
|
|||
/*
|
||||
* 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 */
|
||||
734
drivers/infiniband/hw/ipath/ipath_ruc.c
Normal file
734
drivers/infiniband/hw/ipath/ipath_ruc.c
Normal file
|
|
@ -0,0 +1,734 @@
|
|||
/*
|
||||
* 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/sched.h>
|
||||
#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->wr.wr.rdma.remote_addr,
|
||||
wqe->wr.wr.rdma.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->wr.wr.rdma.remote_addr,
|
||||
wqe->wr.wr.rdma.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->wr.wr.atomic.remote_addr,
|
||||
wqe->wr.wr.atomic.rkey,
|
||||
IB_ACCESS_REMOTE_ATOMIC)))
|
||||
goto acc_err;
|
||||
/* Perform atomic OP and save result. */
|
||||
maddr = (atomic64_t *) qp->r_sge.sge.vaddr;
|
||||
sdata = wqe->wr.wr.atomic.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->wr.wr.atomic.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;
|
||||
}
|
||||
818
drivers/infiniband/hw/ipath/ipath_sdma.c
Normal file
818
drivers/infiniband/hw/ipath/ipath_sdma.c
Normal file
|
|
@ -0,0 +1,818 @@
|
|||
/*
|
||||
* 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;
|
||||
|
||||
init_timer(&dd->ipath_sdma_vl15_timer);
|
||||
dd->ipath_sdma_vl15_timer.function = vl15_watchdog_timeout;
|
||||
dd->ipath_sdma_vl15_timer.data = (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;
|
||||
}
|
||||
380
drivers/infiniband/hw/ipath/ipath_srq.c
Normal file
380
drivers/infiniband/hw/ipath/ipath_srq.c
Normal file
|
|
@ -0,0 +1,380 @@
|
|||
/*
|
||||
* 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;
|
||||
}
|
||||
347
drivers/infiniband/hw/ipath/ipath_stats.c
Normal file
347
drivers/infiniband/hw/ipath/ipath_stats.c
Normal file
|
|
@ -0,0 +1,347 @@
|
|||
/*
|
||||
* 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);
|
||||
}
|
||||
1238
drivers/infiniband/hw/ipath/ipath_sysfs.c
Normal file
1238
drivers/infiniband/hw/ipath/ipath_sysfs.c
Normal file
File diff suppressed because it is too large
Load diff
547
drivers/infiniband/hw/ipath/ipath_uc.c
Normal file
547
drivers/infiniband/hw/ipath/ipath_uc.c
Normal file
|
|
@ -0,0 +1,547 @@
|
|||
/*
|
||||
* 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->wr.wr.rdma.remote_addr);
|
||||
ohdr->u.rc.reth.rkey =
|
||||
cpu_to_be32(wqe->wr.wr.rdma.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;
|
||||
}
|
||||
580
drivers/infiniband/hw/ipath/ipath_ud.c
Normal file
580
drivers/infiniband/hw/ipath/ipath_ud.c
Normal file
|
|
@ -0,0 +1,580 @@
|
|||
/*
|
||||
* 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/sched.h>
|
||||
#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->wr.wr.ud.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->wr.wr.ud.remote_qkey < 0 ?
|
||||
sqp->qkey : swqe->wr.wr.ud.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->wr.wr.ud.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->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->wr.wr.ud.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->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->wr.opcode == IB_WR_SEND_WITH_IMM) {
|
||||
qp->s_hdrwords++;
|
||||
ohdr->u.ud.imm_data = wqe->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->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->wr.wr.ud.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->wr.wr.ud.remote_qkey < 0 ?
|
||||
qp->qkey : wqe->wr.wr.ud.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:;
|
||||
}
|
||||
229
drivers/infiniband/hw/ipath/ipath_user_pages.c
Normal file
229
drivers/infiniband/hw/ipath/ipath_user_pages.c
Normal file
|
|
@ -0,0 +1,229 @@
|
|||
/*
|
||||
* 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 <linux/sched.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;
|
||||
}
|
||||
875
drivers/infiniband/hw/ipath/ipath_user_sdma.c
Normal file
875
drivers/infiniband/hw/ipath/ipath_user_sdma.c
Normal file
|
|
@ -0,0 +1,875 @@
|
|||
/*
|
||||
* 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/sched.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 = addr & ~PAGE_MASK;
|
||||
|
||||
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 = addr & ~PAGE_MASK;
|
||||
|
||||
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;
|
||||
}
|
||||
|
||||
52
drivers/infiniband/hw/ipath/ipath_user_sdma.h
Normal file
52
drivers/infiniband/hw/ipath/ipath_user_sdma.h
Normal file
|
|
@ -0,0 +1,52 @@
|
|||
/*
|
||||
* 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);
|
||||
2342
drivers/infiniband/hw/ipath/ipath_verbs.c
Normal file
2342
drivers/infiniband/hw/ipath/ipath_verbs.c
Normal file
File diff suppressed because it is too large
Load diff
936
drivers/infiniband/hw/ipath/ipath_verbs.h
Normal file
936
drivers/infiniband/hw/ipath/ipath_verbs.h
Normal file
|
|
@ -0,0 +1,936 @@
|
|||
/*
|
||||
* 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 {
|
||||
struct ib_send_wr wr; /* don't use wr.sg_list */
|
||||
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,
|
||||
struct ib_wc *in_wc,
|
||||
struct ib_grh *in_grh,
|
||||
struct ib_mad *in_mad, struct ib_mad *out_mad);
|
||||
|
||||
/*
|
||||
* 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, int entries, int comp_vector,
|
||||
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_phys_mr(struct ib_pd *pd,
|
||||
struct ib_phys_buf *buffer_list,
|
||||
int num_phys_buf, int acc, u64 *iova_start);
|
||||
|
||||
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 */
|
||||
364
drivers/infiniband/hw/ipath/ipath_verbs_mcast.c
Normal file
364
drivers/infiniband/hw/ipath/ipath_verbs_mcast.c
Normal file
|
|
@ -0,0 +1,364 @@
|
|||
/*
|
||||
* 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/sched.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;
|
||||
}
|
||||
62
drivers/infiniband/hw/ipath/ipath_wc_ppc64.c
Normal file
62
drivers/infiniband/hw/ipath/ipath_wc_ppc64.c
Normal file
|
|
@ -0,0 +1,62 @@
|
|||
/*
|
||||
* 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;
|
||||
}
|
||||
|
||||
/**
|
||||
* ipath_unordered_wc - indicate whether write combining is unordered
|
||||
*
|
||||
* Because our performance depends on our ability to do write
|
||||
* combining mmio writes in the most efficient way, we need to
|
||||
* know if we are on a processor that may reorder stores when
|
||||
* write combining.
|
||||
*/
|
||||
int ipath_unordered_wc(void)
|
||||
{
|
||||
return 1;
|
||||
}
|
||||
184
drivers/infiniband/hw/ipath/ipath_wc_x86_64.c
Normal file
184
drivers/infiniband/hw/ipath/ipath_wc_x86_64.c
Normal file
|
|
@ -0,0 +1,184 @@
|
|||
/*
|
||||
* 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/mtrr.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) {
|
||||
int cookie;
|
||||
ipath_cdbg(VERBOSE, "Setting mtrr for chip to WC "
|
||||
"(addr %llx, len=0x%llx)\n",
|
||||
(unsigned long long) pioaddr,
|
||||
(unsigned long long) piolen);
|
||||
cookie = mtrr_add(pioaddr, piolen, MTRR_TYPE_WRCOMB, 0);
|
||||
if (cookie < 0) {
|
||||
{
|
||||
dev_info(&dd->pcidev->dev,
|
||||
"mtrr_add() WC for PIO bufs "
|
||||
"failed (%d)\n",
|
||||
cookie);
|
||||
ret = -EINVAL;
|
||||
}
|
||||
} else {
|
||||
ipath_cdbg(VERBOSE, "Set mtrr for chip to WC, "
|
||||
"cookie is %d\n", cookie);
|
||||
dd->ipath_wc_cookie = cookie;
|
||||
dd->ipath_wc_base = (unsigned long) pioaddr;
|
||||
dd->ipath_wc_len = (unsigned long) piolen;
|
||||
}
|
||||
}
|
||||
|
||||
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)
|
||||
{
|
||||
if (dd->ipath_wc_cookie) {
|
||||
int r;
|
||||
ipath_cdbg(VERBOSE, "undoing WCCOMB on pio buffers\n");
|
||||
r = mtrr_del(dd->ipath_wc_cookie, dd->ipath_wc_base,
|
||||
dd->ipath_wc_len);
|
||||
if (r < 0)
|
||||
dev_info(&dd->pcidev->dev,
|
||||
"mtrr_del(%lx, %lx, %lx) failed: %d\n",
|
||||
dd->ipath_wc_cookie, dd->ipath_wc_base,
|
||||
dd->ipath_wc_len, r);
|
||||
dd->ipath_wc_cookie = 0; /* even on failure */
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* ipath_unordered_wc - indicate whether write combining is ordered
|
||||
*
|
||||
* Because our performance depends on our ability to do write combining mmio
|
||||
* writes in the most efficient way, we need to know if we are on an Intel
|
||||
* or AMD x86_64 processor. AMD x86_64 processors flush WC buffers out in
|
||||
* the order completed, and so no special flushing is required to get
|
||||
* correct ordering. Intel processors, however, will flush write buffers
|
||||
* out in "random" orders, and so explicit ordering is needed at times.
|
||||
*/
|
||||
int ipath_unordered_wc(void)
|
||||
{
|
||||
return boot_cpu_data.x86_vendor != X86_VENDOR_AMD;
|
||||
}
|
||||
Loading…
Add table
Add a link
Reference in a new issue