Soft RoCE driver

Soft RoCE (RXE) - The software RoCE driver

ib_rxe implements the RDMA transport and registers to the RDMA core
device as a kernel verbs provider. It also implements the packet IO
layer. On the other hand ib_rxe registers to the Linux netdev stack
as a udp encapsulating protocol, in that case RDMA, for sending and
receiving packets over any Ethernet device.  This yields a RDMA
transport over the UDP/Ethernet network layer forming a RoCEv2
compatible device.

The configuration procedure of the Soft RoCE drivers requires
binding to any existing Ethernet network device. This is done with
/sys interface.

A userspace Soft RoCE library (librxe) provides user applications
the ability to run with Soft RoCE devices.  The use of rxe verbs ins
user space requires the inclusion of librxe as a device specifics
plug-in to libibverbs. librxe is packaged separately.

Architecture:

     +-----------------------------------------------------------+
     |                          Application                      |
     +-----------------------------------------------------------+
                            +-----------------------------------+
                            |             libibverbs            |
User                        +-----------------------------------+
                            +----------------+ +----------------+
                            | librxe         | | HW RoCE lib    |
                            +----------------+ +----------------+
+---------------------------------------------------------------+
     +--------------+                           +------------+
     | Sockets      |                           | RDMA ULP   |
     +--------------+                           +------------+
     +--------------+                  +---------------------+
     | TCP/IP       |                  | ib_core             |
     +--------------+                  +---------------------+
                             +------------+ +----------------+
Kernel                       | ib_rxe     | | HW RoCE driver |
                             +------------+ +----------------+
     +------------------------------------+
     | NIC driver                         |
     +------------------------------------+

~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
     +-----------------------------------------------------------+
     |                          Application                      |
     +-----------------------------------------------------------+
                            +-----------------------------------+
                            |             libibverbs            |
User                        +-----------------------------------+
                            +----------------+ +----------------+
                            | librxe         | | HW RoCE lib    |
                            +----------------+ +----------------+
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
     +--------------+                           +------------+
     | Sockets      |                           | RDMA ULP   |
     +--------------+                           +------------+
     +--------------+                  +---------------------+
     | TCP/IP       |                  | ib_core             |
     +--------------+                  +---------------------+
                             +------------+ +----------------+
Kernel                       | ib_rxe     | | HW RoCE driver |
                             +------------+ +----------------+
     +------------------------------------+
     | NIC driver                         |
     +------------------------------------+
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~

Soft RoCE resources:

[1[ https://github.com/SoftRoCE/librxe-dev librxe - source code in
Github
[2] https://github.com/SoftRoCE/rxe-dev/wiki/rxe-dev:-Home - Soft RoCE
Wiki page
[3] https://github.com/SoftRoCE/librxe-dev - Soft RoCE userspace library

Signed-off-by: Kamal Heib <kamalh@mellanox.com>
Signed-off-by: Amir Vadai <amirv@mellanox.com>
Signed-off-by: Moni Shoua <monis@mellanox.com>
Reviewed-by: Haggai Eran <haggaie@mellanox.com>
Signed-off-by: Doug Ledford <dledford@redhat.com>
This commit is contained in:
Moni Shoua 2016-06-16 16:45:23 +03:00 committed by Doug Ledford
parent 33688abb28
commit 8700e3e7c4
38 changed files with 13039 additions and 0 deletions

View File

@ -7444,6 +7444,15 @@ W: http://www.mellanox.com
Q: http://patchwork.ozlabs.org/project/netdev/list/
F: drivers/net/ethernet/mellanox/mlxsw/
SOFT-ROCE DRIVER (rxe)
M: Moni Shoua <monis@mellanox.com>
L: linux-rdma@vger.kernel.org
S: Supported
W: https://github.com/SoftRoCE/rxe-dev/wiki/rxe-dev:-Home
Q: http://patchwork.kernel.org/project/linux-rdma/list/
F: drivers/infiniband/hw/rxe/
F: include/uapi/rdma/rdma_user_rxe.h
MEMBARRIER SUPPORT
M: Mathieu Desnoyers <mathieu.desnoyers@efficios.com>
M: "Paul E. McKenney" <paulmck@linux.vnet.ibm.com>

View File

@ -84,6 +84,7 @@ source "drivers/infiniband/ulp/iser/Kconfig"
source "drivers/infiniband/ulp/isert/Kconfig"
source "drivers/infiniband/sw/rdmavt/Kconfig"
source "drivers/infiniband/sw/rxe/Kconfig"
source "drivers/infiniband/hw/hfi1/Kconfig"

View File

@ -1 +1,2 @@
obj-$(CONFIG_INFINIBAND_RDMAVT) += rdmavt/
obj-$(CONFIG_RDMA_RXE) += rxe/

View File

@ -0,0 +1,24 @@
config RDMA_RXE
tristate "Software RDMA over Ethernet (RoCE) driver"
depends on INET && PCI && INFINIBAND
depends on NET_UDP_TUNNEL
---help---
This driver implements the InfiniBand RDMA transport over
the Linux network stack. It enables a system with a
standard Ethernet adapter to interoperate with a RoCE
adapter or with another system running the RXE driver.
Documentation on InfiniBand and RoCE can be downloaded at
www.infinibandta.org and www.openfabrics.org. (See also
siw which is a similar software driver for iWARP.)
The driver is split into two layers, one interfaces with the
Linux RDMA stack and implements a kernel or user space
verbs API. The user space verbs API requires a support
library named librxe which is loaded by the generic user
space verbs API, libibverbs. The other layer interfaces
with the Linux network stack at layer 3.
To configure and work with soft-RoCE driver please use the
following wiki page under "configure Soft-RoCE (RXE)" section:
https://github.com/SoftRoCE/rxe-dev/wiki/rxe-dev:-Home

View File

@ -0,0 +1,24 @@
obj-$(CONFIG_RDMA_RXE) += rdma_rxe.o
rdma_rxe-y := \
rxe.o \
rxe_comp.o \
rxe_req.o \
rxe_resp.o \
rxe_recv.o \
rxe_pool.o \
rxe_queue.o \
rxe_verbs.o \
rxe_av.o \
rxe_srq.o \
rxe_qp.o \
rxe_cq.o \
rxe_mr.o \
rxe_dma.o \
rxe_opcode.o \
rxe_mmap.o \
rxe_icrc.o \
rxe_mcast.o \
rxe_task.o \
rxe_net.o \
rxe_sysfs.o

View File

@ -0,0 +1,386 @@
/*
* Copyright (c) 2016 Mellanox Technologies Ltd. All rights reserved.
* Copyright (c) 2015 System Fabric Works, 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 "rxe.h"
#include "rxe_loc.h"
MODULE_AUTHOR("Bob Pearson, Frank Zago, John Groves, Kamal Heib");
MODULE_DESCRIPTION("Soft RDMA transport");
MODULE_LICENSE("Dual BSD/GPL");
MODULE_VERSION("0.2");
/* free resources for all ports on a device */
static void rxe_cleanup_ports(struct rxe_dev *rxe)
{
kfree(rxe->port.pkey_tbl);
rxe->port.pkey_tbl = NULL;
}
/* free resources for a rxe device all objects created for this device must
* have been destroyed
*/
static void rxe_cleanup(struct rxe_dev *rxe)
{
rxe_pool_cleanup(&rxe->uc_pool);
rxe_pool_cleanup(&rxe->pd_pool);
rxe_pool_cleanup(&rxe->ah_pool);
rxe_pool_cleanup(&rxe->srq_pool);
rxe_pool_cleanup(&rxe->qp_pool);
rxe_pool_cleanup(&rxe->cq_pool);
rxe_pool_cleanup(&rxe->mr_pool);
rxe_pool_cleanup(&rxe->mw_pool);
rxe_pool_cleanup(&rxe->mc_grp_pool);
rxe_pool_cleanup(&rxe->mc_elem_pool);
rxe_cleanup_ports(rxe);
}
/* called when all references have been dropped */
void rxe_release(struct kref *kref)
{
struct rxe_dev *rxe = container_of(kref, struct rxe_dev, ref_cnt);
rxe_cleanup(rxe);
ib_dealloc_device(&rxe->ib_dev);
}
void rxe_dev_put(struct rxe_dev *rxe)
{
kref_put(&rxe->ref_cnt, rxe_release);
}
EXPORT_SYMBOL_GPL(rxe_dev_put);
/* initialize rxe device parameters */
static int rxe_init_device_param(struct rxe_dev *rxe)
{
rxe->max_inline_data = RXE_MAX_INLINE_DATA;
rxe->attr.fw_ver = RXE_FW_VER;
rxe->attr.max_mr_size = RXE_MAX_MR_SIZE;
rxe->attr.page_size_cap = RXE_PAGE_SIZE_CAP;
rxe->attr.vendor_id = RXE_VENDOR_ID;
rxe->attr.vendor_part_id = RXE_VENDOR_PART_ID;
rxe->attr.hw_ver = RXE_HW_VER;
rxe->attr.max_qp = RXE_MAX_QP;
rxe->attr.max_qp_wr = RXE_MAX_QP_WR;
rxe->attr.device_cap_flags = RXE_DEVICE_CAP_FLAGS;
rxe->attr.max_sge = RXE_MAX_SGE;
rxe->attr.max_sge_rd = RXE_MAX_SGE_RD;
rxe->attr.max_cq = RXE_MAX_CQ;
rxe->attr.max_cqe = (1 << RXE_MAX_LOG_CQE) - 1;
rxe->attr.max_mr = RXE_MAX_MR;
rxe->attr.max_pd = RXE_MAX_PD;
rxe->attr.max_qp_rd_atom = RXE_MAX_QP_RD_ATOM;
rxe->attr.max_ee_rd_atom = RXE_MAX_EE_RD_ATOM;
rxe->attr.max_res_rd_atom = RXE_MAX_RES_RD_ATOM;
rxe->attr.max_qp_init_rd_atom = RXE_MAX_QP_INIT_RD_ATOM;
rxe->attr.max_ee_init_rd_atom = RXE_MAX_EE_INIT_RD_ATOM;
rxe->attr.atomic_cap = RXE_ATOMIC_CAP;
rxe->attr.max_ee = RXE_MAX_EE;
rxe->attr.max_rdd = RXE_MAX_RDD;
rxe->attr.max_mw = RXE_MAX_MW;
rxe->attr.max_raw_ipv6_qp = RXE_MAX_RAW_IPV6_QP;
rxe->attr.max_raw_ethy_qp = RXE_MAX_RAW_ETHY_QP;
rxe->attr.max_mcast_grp = RXE_MAX_MCAST_GRP;
rxe->attr.max_mcast_qp_attach = RXE_MAX_MCAST_QP_ATTACH;
rxe->attr.max_total_mcast_qp_attach = RXE_MAX_TOT_MCAST_QP_ATTACH;
rxe->attr.max_ah = RXE_MAX_AH;
rxe->attr.max_fmr = RXE_MAX_FMR;
rxe->attr.max_map_per_fmr = RXE_MAX_MAP_PER_FMR;
rxe->attr.max_srq = RXE_MAX_SRQ;
rxe->attr.max_srq_wr = RXE_MAX_SRQ_WR;
rxe->attr.max_srq_sge = RXE_MAX_SRQ_SGE;
rxe->attr.max_fast_reg_page_list_len = RXE_MAX_FMR_PAGE_LIST_LEN;
rxe->attr.max_pkeys = RXE_MAX_PKEYS;
rxe->attr.local_ca_ack_delay = RXE_LOCAL_CA_ACK_DELAY;
rxe->max_ucontext = RXE_MAX_UCONTEXT;
return 0;
}
/* initialize port attributes */
static int rxe_init_port_param(struct rxe_port *port)
{
port->attr.state = RXE_PORT_STATE;
port->attr.max_mtu = RXE_PORT_MAX_MTU;
port->attr.active_mtu = RXE_PORT_ACTIVE_MTU;
port->attr.gid_tbl_len = RXE_PORT_GID_TBL_LEN;
port->attr.port_cap_flags = RXE_PORT_PORT_CAP_FLAGS;
port->attr.max_msg_sz = RXE_PORT_MAX_MSG_SZ;
port->attr.bad_pkey_cntr = RXE_PORT_BAD_PKEY_CNTR;
port->attr.qkey_viol_cntr = RXE_PORT_QKEY_VIOL_CNTR;
port->attr.pkey_tbl_len = RXE_PORT_PKEY_TBL_LEN;
port->attr.lid = RXE_PORT_LID;
port->attr.sm_lid = RXE_PORT_SM_LID;
port->attr.lmc = RXE_PORT_LMC;
port->attr.max_vl_num = RXE_PORT_MAX_VL_NUM;
port->attr.sm_sl = RXE_PORT_SM_SL;
port->attr.subnet_timeout = RXE_PORT_SUBNET_TIMEOUT;
port->attr.init_type_reply = RXE_PORT_INIT_TYPE_REPLY;
port->attr.active_width = RXE_PORT_ACTIVE_WIDTH;
port->attr.active_speed = RXE_PORT_ACTIVE_SPEED;
port->attr.phys_state = RXE_PORT_PHYS_STATE;
port->mtu_cap =
ib_mtu_enum_to_int(RXE_PORT_ACTIVE_MTU);
port->subnet_prefix = cpu_to_be64(RXE_PORT_SUBNET_PREFIX);
return 0;
}
/* initialize port state, note IB convention that HCA ports are always
* numbered from 1
*/
static int rxe_init_ports(struct rxe_dev *rxe)
{
struct rxe_port *port = &rxe->port;
rxe_init_port_param(port);
if (!port->attr.pkey_tbl_len || !port->attr.gid_tbl_len)
return -EINVAL;
port->pkey_tbl = kcalloc(port->attr.pkey_tbl_len,
sizeof(*port->pkey_tbl), GFP_KERNEL);
if (!port->pkey_tbl)
return -ENOMEM;
port->pkey_tbl[0] = 0xffff;
port->port_guid = rxe->ifc_ops->port_guid(rxe);
spin_lock_init(&port->port_lock);
return 0;
}
/* init pools of managed objects */
static int rxe_init_pools(struct rxe_dev *rxe)
{
int err;
err = rxe_pool_init(rxe, &rxe->uc_pool, RXE_TYPE_UC,
rxe->max_ucontext);
if (err)
goto err1;
err = rxe_pool_init(rxe, &rxe->pd_pool, RXE_TYPE_PD,
rxe->attr.max_pd);
if (err)
goto err2;
err = rxe_pool_init(rxe, &rxe->ah_pool, RXE_TYPE_AH,
rxe->attr.max_ah);
if (err)
goto err3;
err = rxe_pool_init(rxe, &rxe->srq_pool, RXE_TYPE_SRQ,
rxe->attr.max_srq);
if (err)
goto err4;
err = rxe_pool_init(rxe, &rxe->qp_pool, RXE_TYPE_QP,
rxe->attr.max_qp);
if (err)
goto err5;
err = rxe_pool_init(rxe, &rxe->cq_pool, RXE_TYPE_CQ,
rxe->attr.max_cq);
if (err)
goto err6;
err = rxe_pool_init(rxe, &rxe->mr_pool, RXE_TYPE_MR,
rxe->attr.max_mr);
if (err)
goto err7;
err = rxe_pool_init(rxe, &rxe->mw_pool, RXE_TYPE_MW,
rxe->attr.max_mw);
if (err)
goto err8;
err = rxe_pool_init(rxe, &rxe->mc_grp_pool, RXE_TYPE_MC_GRP,
rxe->attr.max_mcast_grp);
if (err)
goto err9;
err = rxe_pool_init(rxe, &rxe->mc_elem_pool, RXE_TYPE_MC_ELEM,
rxe->attr.max_total_mcast_qp_attach);
if (err)
goto err10;
return 0;
err10:
rxe_pool_cleanup(&rxe->mc_grp_pool);
err9:
rxe_pool_cleanup(&rxe->mw_pool);
err8:
rxe_pool_cleanup(&rxe->mr_pool);
err7:
rxe_pool_cleanup(&rxe->cq_pool);
err6:
rxe_pool_cleanup(&rxe->qp_pool);
err5:
rxe_pool_cleanup(&rxe->srq_pool);
err4:
rxe_pool_cleanup(&rxe->ah_pool);
err3:
rxe_pool_cleanup(&rxe->pd_pool);
err2:
rxe_pool_cleanup(&rxe->uc_pool);
err1:
return err;
}
/* initialize rxe device state */
static int rxe_init(struct rxe_dev *rxe)
{
int err;
/* init default device parameters */
rxe_init_device_param(rxe);
err = rxe_init_ports(rxe);
if (err)
goto err1;
err = rxe_init_pools(rxe);
if (err)
goto err2;
/* init pending mmap list */
spin_lock_init(&rxe->mmap_offset_lock);
spin_lock_init(&rxe->pending_lock);
INIT_LIST_HEAD(&rxe->pending_mmaps);
INIT_LIST_HEAD(&rxe->list);
mutex_init(&rxe->usdev_lock);
return 0;
err2:
rxe_cleanup_ports(rxe);
err1:
return err;
}
int rxe_set_mtu(struct rxe_dev *rxe, unsigned int ndev_mtu)
{
struct rxe_port *port = &rxe->port;
enum ib_mtu mtu;
mtu = eth_mtu_int_to_enum(ndev_mtu);
/* Make sure that new MTU in range */
mtu = mtu ? min_t(enum ib_mtu, mtu, RXE_PORT_MAX_MTU) : IB_MTU_256;
port->attr.active_mtu = mtu;
port->mtu_cap = ib_mtu_enum_to_int(mtu);
return 0;
}
EXPORT_SYMBOL(rxe_set_mtu);
/* called by ifc layer to create new rxe device.
* The caller should allocate memory for rxe by calling ib_alloc_device.
*/
int rxe_add(struct rxe_dev *rxe, unsigned int mtu)
{
int err;
kref_init(&rxe->ref_cnt);
err = rxe_init(rxe);
if (err)
goto err1;
err = rxe_set_mtu(rxe, mtu);
if (err)
goto err1;
err = rxe_register_device(rxe);
if (err)
goto err1;
return 0;
err1:
rxe_dev_put(rxe);
return err;
}
EXPORT_SYMBOL(rxe_add);
/* called by the ifc layer to remove a device */
void rxe_remove(struct rxe_dev *rxe)
{
rxe_unregister_device(rxe);
rxe_dev_put(rxe);
}
EXPORT_SYMBOL(rxe_remove);
static int __init rxe_module_init(void)
{
int err;
/* initialize slab caches for managed objects */
err = rxe_cache_init();
if (err) {
pr_err("rxe: unable to init object pools\n");
return err;
}
err = rxe_net_init();
if (err) {
pr_err("rxe: unable to init\n");
rxe_cache_exit();
return err;
}
pr_info("rxe: loaded\n");
return 0;
}
static void __exit rxe_module_exit(void)
{
rxe_remove_all();
rxe_net_exit();
rxe_cache_exit();
pr_info("rxe: unloaded\n");
}
module_init(rxe_module_init);
module_exit(rxe_module_exit);

View File

@ -0,0 +1,77 @@
/*
* Copyright (c) 2016 Mellanox Technologies Ltd. All rights reserved.
* Copyright (c) 2015 System Fabric Works, 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 RXE_H
#define RXE_H
#include <linux/module.h>
#include <linux/skbuff.h>
#include <linux/crc32.h>
#include <rdma/ib_verbs.h>
#include <rdma/ib_user_verbs.h>
#include <rdma/ib_pack.h>
#include <rdma/ib_smi.h>
#include <rdma/ib_umem.h>
#include <rdma/ib_cache.h>
#include <rdma/ib_addr.h>
#include "rxe_net.h"
#include "rxe_opcode.h"
#include "rxe_hdr.h"
#include "rxe_param.h"
#include "rxe_verbs.h"
#define RXE_UVERBS_ABI_VERSION (1)
#define IB_PHYS_STATE_LINK_UP (5)
#define IB_PHYS_STATE_LINK_DOWN (3)
#define RXE_ROCE_V2_SPORT (0xc000)
int rxe_set_mtu(struct rxe_dev *rxe, unsigned int dev_mtu);
int rxe_add(struct rxe_dev *rxe, unsigned int mtu);
void rxe_remove(struct rxe_dev *rxe);
void rxe_remove_all(void);
int rxe_rcv(struct sk_buff *skb);
void rxe_dev_put(struct rxe_dev *rxe);
struct rxe_dev *net_to_rxe(struct net_device *ndev);
struct rxe_dev *get_rxe_by_name(const char* name);
void rxe_port_up(struct rxe_dev *rxe);
void rxe_port_down(struct rxe_dev *rxe);
#endif /* RXE_H */

View File

@ -0,0 +1,98 @@
/*
* Copyright (c) 2016 Mellanox Technologies Ltd. All rights reserved.
* Copyright (c) 2015 System Fabric Works, 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 "rxe.h"
#include "rxe_loc.h"
int rxe_av_chk_attr(struct rxe_dev *rxe, struct ib_ah_attr *attr)
{
struct rxe_port *port;
if (attr->port_num != 1) {
pr_info("rxe: invalid port_num = %d\n", attr->port_num);
return -EINVAL;
}
port = &rxe->port;
if (attr->ah_flags & IB_AH_GRH) {
if (attr->grh.sgid_index > port->attr.gid_tbl_len) {
pr_info("rxe: invalid sgid index = %d\n",
attr->grh.sgid_index);
return -EINVAL;
}
}
return 0;
}
int rxe_av_from_attr(struct rxe_dev *rxe, u8 port_num,
struct rxe_av *av, struct ib_ah_attr *attr)
{
memset(av, 0, sizeof(*av));
memcpy(&av->grh, &attr->grh, sizeof(attr->grh));
av->port_num = port_num;
return 0;
}
int rxe_av_to_attr(struct rxe_dev *rxe, struct rxe_av *av,
struct ib_ah_attr *attr)
{
memcpy(&attr->grh, &av->grh, sizeof(av->grh));
attr->port_num = av->port_num;
return 0;
}
int rxe_av_fill_ip_info(struct rxe_dev *rxe,
struct rxe_av *av,
struct ib_ah_attr *attr,
struct ib_gid_attr *sgid_attr,
union ib_gid *sgid)
{
rdma_gid2ip(&av->sgid_addr._sockaddr, sgid);
rdma_gid2ip(&av->dgid_addr._sockaddr, &attr->grh.dgid);
av->network_type = ib_gid_to_network_type(sgid_attr->gid_type, sgid);
return 0;
}
struct rxe_av *rxe_get_av(struct rxe_pkt_info *pkt)
{
if (!pkt || !pkt->qp)
return NULL;
if (qp_type(pkt->qp) == IB_QPT_RC || qp_type(pkt->qp) == IB_QPT_UC)
return &pkt->qp->pri_av;
return (pkt->wqe) ? &pkt->wqe->av : NULL;
}

View File

@ -0,0 +1,734 @@
/*
* Copyright (c) 2016 Mellanox Technologies Ltd. All rights reserved.
* Copyright (c) 2015 System Fabric Works, 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/skbuff.h>
#include "rxe.h"
#include "rxe_loc.h"
#include "rxe_queue.h"
#include "rxe_task.h"
enum comp_state {
COMPST_GET_ACK,
COMPST_GET_WQE,
COMPST_COMP_WQE,
COMPST_COMP_ACK,
COMPST_CHECK_PSN,
COMPST_CHECK_ACK,
COMPST_READ,
COMPST_ATOMIC,
COMPST_WRITE_SEND,
COMPST_UPDATE_COMP,
COMPST_ERROR_RETRY,
COMPST_RNR_RETRY,
COMPST_ERROR,
COMPST_EXIT, /* We have an issue, and we want to rerun the completer */
COMPST_DONE, /* The completer finished successflly */
};
static char *comp_state_name[] = {
[COMPST_GET_ACK] = "GET ACK",
[COMPST_GET_WQE] = "GET WQE",
[COMPST_COMP_WQE] = "COMP WQE",
[COMPST_COMP_ACK] = "COMP ACK",
[COMPST_CHECK_PSN] = "CHECK PSN",
[COMPST_CHECK_ACK] = "CHECK ACK",
[COMPST_READ] = "READ",
[COMPST_ATOMIC] = "ATOMIC",
[COMPST_WRITE_SEND] = "WRITE/SEND",
[COMPST_UPDATE_COMP] = "UPDATE COMP",
[COMPST_ERROR_RETRY] = "ERROR RETRY",
[COMPST_RNR_RETRY] = "RNR RETRY",
[COMPST_ERROR] = "ERROR",
[COMPST_EXIT] = "EXIT",
[COMPST_DONE] = "DONE",
};
static unsigned long rnrnak_usec[32] = {
[IB_RNR_TIMER_655_36] = 655360,
[IB_RNR_TIMER_000_01] = 10,
[IB_RNR_TIMER_000_02] = 20,
[IB_RNR_TIMER_000_03] = 30,
[IB_RNR_TIMER_000_04] = 40,
[IB_RNR_TIMER_000_06] = 60,
[IB_RNR_TIMER_000_08] = 80,
[IB_RNR_TIMER_000_12] = 120,
[IB_RNR_TIMER_000_16] = 160,
[IB_RNR_TIMER_000_24] = 240,
[IB_RNR_TIMER_000_32] = 320,
[IB_RNR_TIMER_000_48] = 480,
[IB_RNR_TIMER_000_64] = 640,
[IB_RNR_TIMER_000_96] = 960,
[IB_RNR_TIMER_001_28] = 1280,
[IB_RNR_TIMER_001_92] = 1920,
[IB_RNR_TIMER_002_56] = 2560,
[IB_RNR_TIMER_003_84] = 3840,
[IB_RNR_TIMER_005_12] = 5120,
[IB_RNR_TIMER_007_68] = 7680,
[IB_RNR_TIMER_010_24] = 10240,
[IB_RNR_TIMER_015_36] = 15360,
[IB_RNR_TIMER_020_48] = 20480,
[IB_RNR_TIMER_030_72] = 30720,
[IB_RNR_TIMER_040_96] = 40960,
[IB_RNR_TIMER_061_44] = 61410,
[IB_RNR_TIMER_081_92] = 81920,
[IB_RNR_TIMER_122_88] = 122880,
[IB_RNR_TIMER_163_84] = 163840,
[IB_RNR_TIMER_245_76] = 245760,
[IB_RNR_TIMER_327_68] = 327680,
[IB_RNR_TIMER_491_52] = 491520,
};
static inline unsigned long rnrnak_jiffies(u8 timeout)
{
return max_t(unsigned long,
usecs_to_jiffies(rnrnak_usec[timeout]), 1);
}
static enum ib_wc_opcode wr_to_wc_opcode(enum ib_wr_opcode opcode)
{
switch (opcode) {
case IB_WR_RDMA_WRITE: return IB_WC_RDMA_WRITE;
case IB_WR_RDMA_WRITE_WITH_IMM: return IB_WC_RDMA_WRITE;
case IB_WR_SEND: return IB_WC_SEND;
case IB_WR_SEND_WITH_IMM: return IB_WC_SEND;
case IB_WR_RDMA_READ: return IB_WC_RDMA_READ;
case IB_WR_ATOMIC_CMP_AND_SWP: return IB_WC_COMP_SWAP;
case IB_WR_ATOMIC_FETCH_AND_ADD: return IB_WC_FETCH_ADD;
case IB_WR_LSO: return IB_WC_LSO;
case IB_WR_SEND_WITH_INV: return IB_WC_SEND;
case IB_WR_RDMA_READ_WITH_INV: return IB_WC_RDMA_READ;
case IB_WR_LOCAL_INV: return IB_WC_LOCAL_INV;
case IB_WR_REG_MR: return IB_WC_REG_MR;
default:
return 0xff;
}
}
void retransmit_timer(unsigned long data)
{
struct rxe_qp *qp = (struct rxe_qp *)data;
if (qp->valid) {
qp->comp.timeout = 1;
rxe_run_task(&qp->comp.task, 1);
}
}
void rxe_comp_queue_pkt(struct rxe_dev *rxe, struct rxe_qp *qp,
struct sk_buff *skb)
{
int must_sched;
skb_queue_tail(&qp->resp_pkts, skb);
must_sched = skb_queue_len(&qp->resp_pkts) > 1;
rxe_run_task(&qp->comp.task, must_sched);
}
static inline enum comp_state get_wqe(struct rxe_qp *qp,
struct rxe_pkt_info *pkt,
struct rxe_send_wqe **wqe_p)
{
struct rxe_send_wqe *wqe;
/* we come here whether or not we found a response packet to see if
* there are any posted WQEs
*/
wqe = queue_head(qp->sq.queue);
*wqe_p = wqe;
/* no WQE or requester has not started it yet */
if (!wqe || wqe->state == wqe_state_posted)
return pkt ? COMPST_DONE : COMPST_EXIT;
/* WQE does not require an ack */
if (wqe->state == wqe_state_done)
return COMPST_COMP_WQE;
/* WQE caused an error */
if (wqe->state == wqe_state_error)
return COMPST_ERROR;
/* we have a WQE, if we also have an ack check its PSN */
return pkt ? COMPST_CHECK_PSN : COMPST_EXIT;
}
static inline void reset_retry_counters(struct rxe_qp *qp)
{
qp->comp.retry_cnt = qp->attr.retry_cnt;
qp->comp.rnr_retry = qp->attr.rnr_retry;
}
static inline enum comp_state check_psn(struct rxe_qp *qp,
struct rxe_pkt_info *pkt,
struct rxe_send_wqe *wqe)
{
s32 diff;
/* check to see if response is past the oldest WQE. if it is, complete
* send/write or error read/atomic
*/
diff = psn_compare(pkt->psn, wqe->last_psn);
if (diff > 0) {
if (wqe->state == wqe_state_pending) {
if (wqe->mask & WR_ATOMIC_OR_READ_MASK)
return COMPST_ERROR_RETRY;
reset_retry_counters(qp);
return COMPST_COMP_WQE;
} else {
return COMPST_DONE;
}
}
/* compare response packet to expected response */
diff = psn_compare(pkt->psn, qp->comp.psn);
if (diff < 0) {
/* response is most likely a retried packet if it matches an
* uncompleted WQE go complete it else ignore it
*/
if (pkt->psn == wqe->last_psn)
return COMPST_COMP_ACK;
else
return COMPST_DONE;
} else if ((diff > 0) && (wqe->mask & WR_ATOMIC_OR_READ_MASK)) {
return COMPST_ERROR_RETRY;
} else {
return COMPST_CHECK_ACK;
}
}
static inline enum comp_state check_ack(struct rxe_qp *qp,
struct rxe_pkt_info *pkt,
struct rxe_send_wqe *wqe)
{
unsigned int mask = pkt->mask;
u8 syn;
/* Check the sequence only */
switch (qp->comp.opcode) {
case -1:
/* Will catch all *_ONLY cases. */
if (!(mask & RXE_START_MASK))
return COMPST_ERROR;
break;
case IB_OPCODE_RC_RDMA_READ_RESPONSE_FIRST:
case IB_OPCODE_RC_RDMA_READ_RESPONSE_MIDDLE:
if (pkt->opcode != IB_OPCODE_RC_RDMA_READ_RESPONSE_MIDDLE &&
pkt->opcode != IB_OPCODE_RC_RDMA_READ_RESPONSE_LAST) {
return COMPST_ERROR;
}
break;
default:
WARN_ON(1);
}
/* Check operation validity. */
switch (pkt->opcode) {
case IB_OPCODE_RC_RDMA_READ_RESPONSE_FIRST:
case IB_OPCODE_RC_RDMA_READ_RESPONSE_LAST:
case IB_OPCODE_RC_RDMA_READ_RESPONSE_ONLY:
syn = aeth_syn(pkt);
if ((syn & AETH_TYPE_MASK) != AETH_ACK)
return COMPST_ERROR;
/* Fall through (IB_OPCODE_RC_RDMA_READ_RESPONSE_MIDDLE
* doesn't have an AETH)
*/
case IB_OPCODE_RC_RDMA_READ_RESPONSE_MIDDLE:
if (wqe->wr.opcode != IB_WR_RDMA_READ &&
wqe->wr.opcode != IB_WR_RDMA_READ_WITH_INV) {
return COMPST_ERROR;
}
reset_retry_counters(qp);
return COMPST_READ;
case IB_OPCODE_RC_ATOMIC_ACKNOWLEDGE:
syn = aeth_syn(pkt);
if ((syn & AETH_TYPE_MASK) != AETH_ACK)
return COMPST_ERROR;
if (wqe->wr.opcode != IB_WR_ATOMIC_CMP_AND_SWP &&
wqe->wr.opcode != IB_WR_ATOMIC_FETCH_AND_ADD)
return COMPST_ERROR;
reset_retry_counters(qp);
return COMPST_ATOMIC;
case IB_OPCODE_RC_ACKNOWLEDGE:
syn = aeth_syn(pkt);
switch (syn & AETH_TYPE_MASK) {
case AETH_ACK:
reset_retry_counters(qp);
return COMPST_WRITE_SEND;
case AETH_RNR_NAK:
return COMPST_RNR_RETRY;
case AETH_NAK:
switch (syn) {
case AETH_NAK_PSN_SEQ_ERROR:
/* a nak implicitly acks all packets with psns
* before
*/
if (psn_compare(pkt->psn, qp->comp.psn) > 0) {
qp->comp.psn = pkt->psn;
if (qp->req.wait_psn) {
qp->req.wait_psn = 0;
rxe_run_task(&qp->req.task, 1);
}
}
return COMPST_ERROR_RETRY;
case AETH_NAK_INVALID_REQ:
wqe->status = IB_WC_REM_INV_REQ_ERR;
return COMPST_ERROR;
case AETH_NAK_REM_ACC_ERR:
wqe->status = IB_WC_REM_ACCESS_ERR;
return COMPST_ERROR;
case AETH_NAK_REM_OP_ERR:
wqe->status = IB_WC_REM_OP_ERR;
return COMPST_ERROR;
default:
pr_warn("unexpected nak %x\n", syn);
wqe->status = IB_WC_REM_OP_ERR;
return COMPST_ERROR;
}
default:
return COMPST_ERROR;
}
break;
default:
pr_warn("unexpected opcode\n");
}
return COMPST_ERROR;
}
static inline enum comp_state do_read(struct rxe_qp *qp,
struct rxe_pkt_info *pkt,
struct rxe_send_wqe *wqe)
{
struct rxe_dev *rxe = to_rdev(qp->ibqp.device);
int ret;
ret = copy_data(rxe, qp->pd, IB_ACCESS_LOCAL_WRITE,
&wqe->dma, payload_addr(pkt),
payload_size(pkt), to_mem_obj, NULL);
if (ret)
return COMPST_ERROR;
if (wqe->dma.resid == 0 && (pkt->mask & RXE_END_MASK))
return COMPST_COMP_ACK;
else
return COMPST_UPDATE_COMP;
}
static inline enum comp_state do_atomic(struct rxe_qp *qp,
struct rxe_pkt_info *pkt,
struct rxe_send_wqe *wqe)
{
struct rxe_dev *rxe = to_rdev(qp->ibqp.device);
int ret;
u64 atomic_orig = atmack_orig(pkt);
ret = copy_data(rxe, qp->pd, IB_ACCESS_LOCAL_WRITE,
&wqe->dma, &atomic_orig,
sizeof(u64), to_mem_obj, NULL);
if (ret)
return COMPST_ERROR;
else
return COMPST_COMP_ACK;
}
static void make_send_cqe(struct rxe_qp *qp, struct rxe_send_wqe *wqe,
struct rxe_cqe *cqe)
{
memset(cqe, 0, sizeof(*cqe));
if (!qp->is_user) {
struct ib_wc *wc = &cqe->ibwc;
wc->wr_id = wqe->wr.wr_id;
wc->status = wqe->status;
wc->opcode = wr_to_wc_opcode(wqe->wr.opcode);
if (wqe->wr.opcode == IB_WR_RDMA_WRITE_WITH_IMM ||
wqe->wr.opcode == IB_WR_SEND_WITH_IMM)
wc->wc_flags = IB_WC_WITH_IMM;
wc->byte_len = wqe->dma.length;
wc->qp = &qp->ibqp;
} else {
struct ib_uverbs_wc *uwc = &cqe->uibwc;
uwc->wr_id = wqe->wr.wr_id;
uwc->status = wqe->status;
uwc->opcode = wr_to_wc_opcode(wqe->wr.opcode);
if (wqe->wr.opcode == IB_WR_RDMA_WRITE_WITH_IMM ||
wqe->wr.opcode == IB_WR_SEND_WITH_IMM)
uwc->wc_flags = IB_WC_WITH_IMM;
uwc->byte_len = wqe->dma.length;
uwc->qp_num = qp->ibqp.qp_num;
}
}
static void do_complete(struct rxe_qp *qp, struct rxe_send_wqe *wqe)
{
struct rxe_cqe cqe;
if ((qp->sq_sig_type == IB_SIGNAL_ALL_WR) ||
(wqe->wr.send_flags & IB_SEND_SIGNALED) ||
(qp->req.state == QP_STATE_ERROR)) {
make_send_cqe(qp, wqe, &cqe);
rxe_cq_post(qp->scq, &cqe, 0);
}
advance_consumer(qp->sq.queue);
/*
* we completed something so let req run again
* if it is trying to fence
*/
if (qp->req.wait_fence) {
qp->req.wait_fence = 0;
rxe_run_task(&qp->req.task, 1);
}
}
static inline enum comp_state complete_ack(struct rxe_qp *qp,
struct rxe_pkt_info *pkt,
struct rxe_send_wqe *wqe)
{
unsigned long flags;
if (wqe->has_rd_atomic) {
wqe->has_rd_atomic = 0;
atomic_inc(&qp->req.rd_atomic);
if (qp->req.need_rd_atomic) {
qp->comp.timeout_retry = 0;
qp->req.need_rd_atomic = 0;
rxe_run_task(&qp->req.task, 1);
}
}
if (unlikely(qp->req.state == QP_STATE_DRAIN)) {
/* state_lock used by requester & completer */
spin_lock_irqsave(&qp->state_lock, flags);
if ((qp->req.state == QP_STATE_DRAIN) &&
(qp->comp.psn == qp->req.psn)) {
qp->req.state = QP_STATE_DRAINED;
spin_unlock_irqrestore(&qp->state_lock, flags);
if (qp->ibqp.event_handler) {
struct ib_event ev;
ev.device = qp->ibqp.device;
ev.element.qp = &qp->ibqp;
ev.event = IB_EVENT_SQ_DRAINED;
qp->ibqp.event_handler(&ev,
qp->ibqp.qp_context);
}
} else {
spin_unlock_irqrestore(&qp->state_lock, flags);
}
}
do_complete(qp, wqe);
if (psn_compare(pkt->psn, qp->comp.psn) >= 0)
return COMPST_UPDATE_COMP;
else
return COMPST_DONE;
}
static inline enum comp_state complete_wqe(struct rxe_qp *qp,
struct rxe_pkt_info *pkt,
struct rxe_send_wqe *wqe)
{
qp->comp.opcode = -1;
if (pkt) {
if (psn_compare(pkt->psn, qp->comp.psn) >= 0)
qp->comp.psn = (pkt->psn + 1) & BTH_PSN_MASK;
if (qp->req.wait_psn) {
qp->req.wait_psn = 0;
rxe_run_task(&qp->req.task, 1);
}
}
do_complete(qp, wqe);
return COMPST_GET_WQE;
}
int rxe_completer(void *arg)
{
struct rxe_qp *qp = (struct rxe_qp *)arg;
struct rxe_send_wqe *wqe = wqe;
struct sk_buff *skb = NULL;
struct rxe_pkt_info *pkt = NULL;
enum comp_state state;
if (!qp->valid) {
while ((skb = skb_dequeue(&qp->resp_pkts))) {
rxe_drop_ref(qp);
kfree_skb(skb);
}
skb = NULL;
pkt = NULL;
while (queue_head(qp->sq.queue))
advance_consumer(qp->sq.queue);
goto exit;
}
if (qp->req.state == QP_STATE_ERROR) {
while ((skb = skb_dequeue(&qp->resp_pkts))) {
rxe_drop_ref(qp);
kfree_skb(skb);
}
skb = NULL;
pkt = NULL;
while ((wqe = queue_head(qp->sq.queue))) {
wqe->status = IB_WC_WR_FLUSH_ERR;
do_complete(qp, wqe);
}
goto exit;
}
if (qp->req.state == QP_STATE_RESET) {
while ((skb = skb_dequeue(&qp->resp_pkts))) {
rxe_drop_ref(qp);
kfree_skb(skb);
}
skb = NULL;
pkt = NULL;
while (queue_head(qp->sq.queue))
advance_consumer(qp->sq.queue);
goto exit;
}
if (qp->comp.timeout) {
qp->comp.timeout_retry = 1;
qp->comp.timeout = 0;
} else {
qp->comp.timeout_retry = 0;
}
if (qp->req.need_retry)
goto exit;
state = COMPST_GET_ACK;
while (1) {
pr_debug("state = %s\n", comp_state_name[state]);
switch (state) {
case COMPST_GET_ACK:
skb = skb_dequeue(&qp->resp_pkts);
if (skb) {
pkt = SKB_TO_PKT(skb);
qp->comp.timeout_retry = 0;
}
state = COMPST_GET_WQE;
break;
case COMPST_GET_WQE:
state = get_wqe(qp, pkt, &wqe);
break;
case COMPST_CHECK_PSN:
state = check_psn(qp, pkt, wqe);
break;
case COMPST_CHECK_ACK:
state = check_ack(qp, pkt, wqe);
break;
case COMPST_READ:
state = do_read(qp, pkt, wqe);
break;
case COMPST_ATOMIC:
state = do_atomic(qp, pkt, wqe);
break;
case COMPST_WRITE_SEND:
if (wqe->state == wqe_state_pending &&
wqe->last_psn == pkt->psn)
state = COMPST_COMP_ACK;
else
state = COMPST_UPDATE_COMP;
break;
case COMPST_COMP_ACK:
state = complete_ack(qp, pkt, wqe);
break;
case COMPST_COMP_WQE:
state = complete_wqe(qp, pkt, wqe);
break;
case COMPST_UPDATE_COMP:
if (pkt->mask & RXE_END_MASK)
qp->comp.opcode = -1;
else
qp->comp.opcode = pkt->opcode;
if (psn_compare(pkt->psn, qp->comp.psn) >= 0)
qp->comp.psn = (pkt->psn + 1) & BTH_PSN_MASK;
if (qp->req.wait_psn) {
qp->req.wait_psn = 0;
rxe_run_task(&qp->req.task, 1);
}
state = COMPST_DONE;
break;
case COMPST_DONE:
if (pkt) {
rxe_drop_ref(pkt->qp);
kfree_skb(skb);
}
goto done;
case COMPST_EXIT:
if (qp->comp.timeout_retry && wqe) {
state = COMPST_ERROR_RETRY;
break;
}
/* re reset the timeout counter if
* (1) QP is type RC
* (2) the QP is alive
* (3) there is a packet sent by the requester that
* might be acked (we still might get spurious
* timeouts but try to keep them as few as possible)
* (4) the timeout parameter is set
*/
if ((qp_type(qp) == IB_QPT_RC) &&
(qp->req.state == QP_STATE_READY) &&
(psn_compare(qp->req.psn, qp->comp.psn) > 0) &&
qp->qp_timeout_jiffies)
mod_timer(&qp->retrans_timer,
jiffies + qp->qp_timeout_jiffies);
goto exit;
case COMPST_ERROR_RETRY:
/* we come here if the retry timer fired and we did
* not receive a response packet. try to retry the send
* queue if that makes sense and the limits have not
* been exceeded. remember that some timeouts are
* spurious since we do not reset the timer but kick
* it down the road or let it expire
*/
/* there is nothing to retry in this case */
if (!wqe || (wqe->state == wqe_state_posted))
goto exit;
if (qp->comp.retry_cnt > 0) {
if (qp->comp.retry_cnt != 7)
qp->comp.retry_cnt--;
/* no point in retrying if we have already
* seen the last ack that the requester could
* have caused
*/
if (psn_compare(qp->req.psn,
qp->comp.psn) > 0) {
/* tell the requester to retry the
* send send queue next time around
*/
qp->req.need_retry = 1;
rxe_run_task(&qp->req.task, 1);
}
goto exit;
} else {
wqe->status = IB_WC_RETRY_EXC_ERR;
state = COMPST_ERROR;
}
break;
case COMPST_RNR_RETRY:
if (qp->comp.rnr_retry > 0) {
if (qp->comp.rnr_retry != 7)
qp->comp.rnr_retry--;
qp->req.need_retry = 1;
pr_debug("set rnr nak timer\n");
mod_timer(&qp->rnr_nak_timer,
jiffies + rnrnak_jiffies(aeth_syn(pkt)
& ~AETH_TYPE_MASK));
goto exit;
} else {
wqe->status = IB_WC_RNR_RETRY_EXC_ERR;
state = COMPST_ERROR;
}
break;
case COMPST_ERROR:
do_complete(qp, wqe);
rxe_qp_error(qp);
goto exit;
}
}
exit:
/* we come here if we are done with processing and want the task to
* exit from the loop calling us
*/
return -EAGAIN;
done:
/* we come here if we have processed a packet we want the task to call
* us again to see if there is anything else to do
*/
return 0;
}

View File

@ -0,0 +1,165 @@
/*
* Copyright (c) 2016 Mellanox Technologies Ltd. All rights reserved.
* Copyright (c) 2015 System Fabric Works, 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 "rxe.h"
#include "rxe_loc.h"
#include "rxe_queue.h"
int rxe_cq_chk_attr(struct rxe_dev *rxe, struct rxe_cq *cq,
int cqe, int comp_vector, struct ib_udata *udata)
{
int count;
if (cqe <= 0) {
pr_warn("cqe(%d) <= 0\n", cqe);
goto err1;
}
if (cqe > rxe->attr.max_cqe) {
pr_warn("cqe(%d) > max_cqe(%d)\n",
cqe, rxe->attr.max_cqe);
goto err1;
}
if (cq) {
count = queue_count(cq->queue);
if (cqe < count) {
pr_warn("cqe(%d) < current # elements in queue (%d)",
cqe, count);
goto err1;
}
}
return 0;
err1:
return -EINVAL;
}
static void rxe_send_complete(unsigned long data)
{
struct rxe_cq *cq = (struct rxe_cq *)data;
cq->ibcq.comp_handler(&cq->ibcq, cq->ibcq.cq_context);
}
int rxe_cq_from_init(struct rxe_dev *rxe, struct rxe_cq *cq, int cqe,
int comp_vector, struct ib_ucontext *context,
struct ib_udata *udata)
{
int err;
cq->queue = rxe_queue_init(rxe, &cqe,
sizeof(struct rxe_cqe));
if (!cq->queue) {
pr_warn("unable to create cq\n");
return -ENOMEM;
}
err = do_mmap_info(rxe, udata, false, context, cq->queue->buf,
cq->queue->buf_size, &cq->queue->ip);
if (err) {
kvfree(cq->queue->buf);
kfree(cq->queue);
return err;
}
if (udata)
cq->is_user = 1;
tasklet_init(&cq->comp_task, rxe_send_complete, (unsigned long)cq);
spin_lock_init(&cq->cq_lock);
cq->ibcq.cqe = cqe;
return 0;
}
int rxe_cq_resize_queue(struct rxe_cq *cq, int cqe, struct ib_udata *udata)
{
int err;
err = rxe_queue_resize(cq->queue, (unsigned int *)&cqe,
sizeof(struct rxe_cqe),
cq->queue->ip ? cq->queue->ip->context : NULL,
udata, NULL, &cq->cq_lock);
if (!err)
cq->ibcq.cqe = cqe;
return err;
}
int rxe_cq_post(struct rxe_cq *cq, struct rxe_cqe *cqe, int solicited)
{
struct ib_event ev;
unsigned long flags;
spin_lock_irqsave(&cq->cq_lock, flags);
if (unlikely(queue_full(cq->queue))) {
spin_unlock_irqrestore(&cq->cq_lock, flags);
if (cq->ibcq.event_handler) {
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 -EBUSY;
}
memcpy(producer_addr(cq->queue), cqe, sizeof(*cqe));
/* make sure all changes to the CQ are written before we update the
* producer pointer
*/
smp_wmb();
advance_producer(cq->queue);
spin_unlock_irqrestore(&cq->cq_lock, flags);
if ((cq->notify == IB_CQ_NEXT_COMP) ||
(cq->notify == IB_CQ_SOLICITED && solicited)) {
cq->notify = 0;
tasklet_schedule(&cq->comp_task);
}
return 0;
}
void rxe_cq_cleanup(void *arg)
{
struct rxe_cq *cq = arg;
if (cq->queue)
rxe_queue_cleanup(cq->queue);
}

View File

@ -0,0 +1,166 @@
/*
* Copyright (c) 2016 Mellanox Technologies Ltd. All rights reserved.
* Copyright (c) 2015 System Fabric Works, 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 "rxe.h"
#include "rxe_loc.h"
#define DMA_BAD_ADDER ((u64)0)
static int rxe_mapping_error(struct ib_device *dev, u64 dma_addr)
{
return dma_addr == DMA_BAD_ADDER;
}
static u64 rxe_dma_map_single(struct ib_device *dev,
void *cpu_addr, size_t size,
enum dma_data_direction direction)
{
WARN_ON(!valid_dma_direction(direction));
return (uintptr_t)cpu_addr;
}
static void rxe_dma_unmap_single(struct ib_device *dev,
u64 addr, size_t size,
enum dma_data_direction direction)
{
WARN_ON(!valid_dma_direction(direction));
}
static u64 rxe_dma_map_page(struct ib_device *dev,
struct page *page,
unsigned long offset,
size_t size, enum dma_data_direction direction)
{
u64 addr;
WARN_ON(!valid_dma_direction(direction));
if (offset + size > PAGE_SIZE) {
addr = DMA_BAD_ADDER;
goto done;
}
addr = (uintptr_t)page_address(page);
if (addr)
addr += offset;
done:
return addr;
}
static void rxe_dma_unmap_page(struct ib_device *dev,
u64 addr, size_t size,
enum dma_data_direction direction)
{
WARN_ON(!valid_dma_direction(direction));
}
static int rxe_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;
WARN_ON(!valid_dma_direction(direction));
for_each_sg(sgl, sg, nents, i) {
addr = (uintptr_t)page_address(sg_page(sg));
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 rxe_unmap_sg(struct ib_device *dev,
struct scatterlist *sg, int nents,
enum dma_data_direction direction)
{
WARN_ON(!valid_dma_direction(direction));
}
static void rxe_sync_single_for_cpu(struct ib_device *dev,
u64 addr,
size_t size, enum dma_data_direction dir)
{
}
static void rxe_sync_single_for_device(struct ib_device *dev,
u64 addr,
size_t size, enum dma_data_direction dir)
{
}
static void *rxe_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 = (uintptr_t)addr;
return addr;
}
static void rxe_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 rxe_dma_mapping_ops = {
.mapping_error = rxe_mapping_error,
.map_single = rxe_dma_map_single,
.unmap_single = rxe_dma_unmap_single,
.map_page = rxe_dma_map_page,
.unmap_page = rxe_dma_unmap_page,
.map_sg = rxe_map_sg,
.unmap_sg = rxe_unmap_sg,
.sync_single_for_cpu = rxe_sync_single_for_cpu,
.sync_single_for_device = rxe_sync_single_for_device,
.alloc_coherent = rxe_dma_alloc_coherent,
.free_coherent = rxe_dma_free_coherent
};

View File

@ -0,0 +1,952 @@
/*
* Copyright (c) 2016 Mellanox Technologies Ltd. All rights reserved.
* Copyright (c) 2015 System Fabric Works, 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 RXE_HDR_H
#define RXE_HDR_H
/* extracted information about a packet carried in an sk_buff struct fits in
* the skbuff cb array. Must be at most 48 bytes. stored in control block of
* sk_buff for received packets.
*/
struct rxe_pkt_info {
struct rxe_dev *rxe; /* device that owns packet */
struct rxe_qp *qp; /* qp that owns packet */
struct rxe_send_wqe *wqe; /* send wqe */
u8 *hdr; /* points to bth */
u32 mask; /* useful info about pkt */
u32 psn; /* bth psn of packet */
u16 pkey_index; /* partition of pkt */
u16 paylen; /* length of bth - icrc */
u8 port_num; /* port pkt received on */
u8 opcode; /* bth opcode of packet */
u8 offset; /* bth offset from pkt->hdr */
};
/* Macros should be used only for received skb */
#define SKB_TO_PKT(skb) ((struct rxe_pkt_info *)(skb)->cb)
#define PKT_TO_SKB(pkt) container_of((void *)(pkt), struct sk_buff, cb)
/*
* IBA header types and methods
*
* Some of these are for reference and completeness only since
* rxe does not currently support RD transport
* most of this could be moved into IB core. ib_pack.h has
* part of this but is incomplete
*
* Header specific routines to insert/extract values to/from headers
* the routines that are named __hhh_(set_)fff() take a pointer to a
* hhh header and get(set) the fff field. The routines named
* hhh_(set_)fff take a packet info struct and find the
* header and field based on the opcode in the packet.
* Conversion to/from network byte order from cpu order is also done.
*/
#define RXE_ICRC_SIZE (4)
#define RXE_MAX_HDR_LENGTH (80)
/******************************************************************************
* Base Transport Header
******************************************************************************/
struct rxe_bth {
u8 opcode;
u8 flags;
__be16 pkey;
__be32 qpn;
__be32 apsn;
};
#define BTH_TVER (0)
#define BTH_DEF_PKEY (0xffff)
#define BTH_SE_MASK (0x80)
#define BTH_MIG_MASK (0x40)
#define BTH_PAD_MASK (0x30)
#define BTH_TVER_MASK (0x0f)
#define BTH_FECN_MASK (0x80000000)
#define BTH_BECN_MASK (0x40000000)
#define BTH_RESV6A_MASK (0x3f000000)
#define BTH_QPN_MASK (0x00ffffff)
#define BTH_ACK_MASK (0x80000000)
#define BTH_RESV7_MASK (0x7f000000)
#define BTH_PSN_MASK (0x00ffffff)
static inline u8 __bth_opcode(void *arg)
{
struct rxe_bth *bth = arg;
return bth->opcode;
}
static inline void __bth_set_opcode(void *arg, u8 opcode)
{
struct rxe_bth *bth = arg;
bth->opcode = opcode;
}
static inline u8 __bth_se(void *arg)
{
struct rxe_bth *bth = arg;
return 0 != (BTH_SE_MASK & bth->flags);
}
static inline void __bth_set_se(void *arg, int se)
{
struct rxe_bth *bth = arg;
if (se)
bth->flags |= BTH_SE_MASK;
else
bth->flags &= ~BTH_SE_MASK;
}
static inline u8 __bth_mig(void *arg)
{
struct rxe_bth *bth = arg;
return 0 != (BTH_MIG_MASK & bth->flags);
}
static inline void __bth_set_mig(void *arg, u8 mig)
{
struct rxe_bth *bth = arg;
if (mig)
bth->flags |= BTH_MIG_MASK;
else
bth->flags &= ~BTH_MIG_MASK;
}
static inline u8 __bth_pad(void *arg)
{
struct rxe_bth *bth = arg;
return (BTH_PAD_MASK & bth->flags) >> 4;
}
static inline void __bth_set_pad(void *arg, u8 pad)
{
struct rxe_bth *bth = arg;
bth->flags = (BTH_PAD_MASK & (pad << 4)) |
(~BTH_PAD_MASK & bth->flags);
}
static inline u8 __bth_tver(void *arg)
{
struct rxe_bth *bth = arg;
return BTH_TVER_MASK & bth->flags;
}
static inline void __bth_set_tver(void *arg, u8 tver)
{
struct rxe_bth *bth = arg;
bth->flags = (BTH_TVER_MASK & tver) |
(~BTH_TVER_MASK & bth->flags);
}
static inline u16 __bth_pkey(void *arg)
{
struct rxe_bth *bth = arg;
return be16_to_cpu(bth->pkey);
}
static inline void __bth_set_pkey(void *arg, u16 pkey)
{
struct rxe_bth *bth = arg;
bth->pkey = cpu_to_be16(pkey);
}
static inline u32 __bth_qpn(void *arg)
{
struct rxe_bth *bth = arg;
return BTH_QPN_MASK & be32_to_cpu(bth->qpn);
}
static inline void __bth_set_qpn(void *arg, u32 qpn)
{
struct rxe_bth *bth = arg;
u32 resvqpn = be32_to_cpu(bth->qpn);
bth->qpn = cpu_to_be32((BTH_QPN_MASK & qpn) |
(~BTH_QPN_MASK & resvqpn));
}
static inline int __bth_fecn(void *arg)
{
struct rxe_bth *bth = arg;
return 0 != (cpu_to_be32(BTH_FECN_MASK) & bth->qpn);
}
static inline void __bth_set_fecn(void *arg, int fecn)
{
struct rxe_bth *bth = arg;
if (fecn)
bth->qpn |= cpu_to_be32(BTH_FECN_MASK);
else
bth->qpn &= ~cpu_to_be32(BTH_FECN_MASK);
}
static inline int __bth_becn(void *arg)
{
struct rxe_bth *bth = arg;
return 0 != (cpu_to_be32(BTH_BECN_MASK) & bth->qpn);
}
static inline void __bth_set_becn(void *arg, int becn)
{
struct rxe_bth *bth = arg;
if (becn)
bth->qpn |= cpu_to_be32(BTH_BECN_MASK);
else
bth->qpn &= ~cpu_to_be32(BTH_BECN_MASK);
}
static inline u8 __bth_resv6a(void *arg)
{
struct rxe_bth *bth = arg;
return (BTH_RESV6A_MASK & be32_to_cpu(bth->qpn)) >> 24;
}
static inline void __bth_set_resv6a(void *arg)
{
struct rxe_bth *bth = arg;
bth->qpn = cpu_to_be32(~BTH_RESV6A_MASK);
}
static inline int __bth_ack(void *arg)
{
struct rxe_bth *bth = arg;
return 0 != (cpu_to_be32(BTH_ACK_MASK) & bth->apsn);
}
static inline void __bth_set_ack(void *arg, int ack)
{
struct rxe_bth *bth = arg;
if (ack)
bth->apsn |= cpu_to_be32(BTH_ACK_MASK);
else
bth->apsn &= ~cpu_to_be32(BTH_ACK_MASK);
}
static inline void __bth_set_resv7(void *arg)
{
struct rxe_bth *bth = arg;
bth->apsn &= ~cpu_to_be32(BTH_RESV7_MASK);
}
static inline u32 __bth_psn(void *arg)
{
struct rxe_bth *bth = arg;
return BTH_PSN_MASK & be32_to_cpu(bth->apsn);
}
static inline void __bth_set_psn(void *arg, u32 psn)
{
struct rxe_bth *bth = arg;
u32 apsn = be32_to_cpu(bth->apsn);
bth->apsn = cpu_to_be32((BTH_PSN_MASK & psn) |
(~BTH_PSN_MASK & apsn));
}
static inline u8 bth_opcode(struct rxe_pkt_info *pkt)
{
return __bth_opcode(pkt->hdr + pkt->offset);
}
static inline void bth_set_opcode(struct rxe_pkt_info *pkt, u8 opcode)
{
__bth_set_opcode(pkt->hdr + pkt->offset, opcode);
}
static inline u8 bth_se(struct rxe_pkt_info *pkt)
{
return __bth_se(pkt->hdr + pkt->offset);
}
static inline void bth_set_se(struct rxe_pkt_info *pkt, int se)
{
__bth_set_se(pkt->hdr + pkt->offset, se);
}
static inline u8 bth_mig(struct rxe_pkt_info *pkt)
{
return __bth_mig(pkt->hdr + pkt->offset);
}
static inline void bth_set_mig(struct rxe_pkt_info *pkt, u8 mig)
{
__bth_set_mig(pkt->hdr + pkt->offset, mig);
}
static inline u8 bth_pad(struct rxe_pkt_info *pkt)
{
return __bth_pad(pkt->hdr + pkt->offset);
}
static inline void bth_set_pad(struct rxe_pkt_info *pkt, u8 pad)
{
__bth_set_pad(pkt->hdr + pkt->offset, pad);
}
static inline u8 bth_tver(struct rxe_pkt_info *pkt)
{
return __bth_tver(pkt->hdr + pkt->offset);
}
static inline void bth_set_tver(struct rxe_pkt_info *pkt, u8 tver)
{
__bth_set_tver(pkt->hdr + pkt->offset, tver);
}
static inline u16 bth_pkey(struct rxe_pkt_info *pkt)
{
return __bth_pkey(pkt->hdr + pkt->offset);
}
static inline void bth_set_pkey(struct rxe_pkt_info *pkt, u16 pkey)
{
__bth_set_pkey(pkt->hdr + pkt->offset, pkey);
}
static inline u32 bth_qpn(struct rxe_pkt_info *pkt)
{
return __bth_qpn(pkt->hdr + pkt->offset);
}
static inline void bth_set_qpn(struct rxe_pkt_info *pkt, u32 qpn)
{
__bth_set_qpn(pkt->hdr + pkt->offset, qpn);
}
static inline int bth_fecn(struct rxe_pkt_info *pkt)
{
return __bth_fecn(pkt->hdr + pkt->offset);
}
static inline void bth_set_fecn(struct rxe_pkt_info *pkt, int fecn)
{
__bth_set_fecn(pkt->hdr + pkt->offset, fecn);
}
static inline int bth_becn(struct rxe_pkt_info *pkt)
{
return __bth_becn(pkt->hdr + pkt->offset);
}
static inline void bth_set_becn(struct rxe_pkt_info *pkt, int becn)
{
__bth_set_becn(pkt->hdr + pkt->offset, becn);
}
static inline u8 bth_resv6a(struct rxe_pkt_info *pkt)
{
return __bth_resv6a(pkt->hdr + pkt->offset);
}
static inline void bth_set_resv6a(struct rxe_pkt_info *pkt)
{
__bth_set_resv6a(pkt->hdr + pkt->offset);
}
static inline int bth_ack(struct rxe_pkt_info *pkt)
{
return __bth_ack(pkt->hdr + pkt->offset);
}
static inline void bth_set_ack(struct rxe_pkt_info *pkt, int ack)
{
__bth_set_ack(pkt->hdr + pkt->offset, ack);
}
static inline void bth_set_resv7(struct rxe_pkt_info *pkt)
{
__bth_set_resv7(pkt->hdr + pkt->offset);
}
static inline u32 bth_psn(struct rxe_pkt_info *pkt)
{
return __bth_psn(pkt->hdr + pkt->offset);
}
static inline void bth_set_psn(struct rxe_pkt_info *pkt, u32 psn)
{
__bth_set_psn(pkt->hdr + pkt->offset, psn);
}
static inline void bth_init(struct rxe_pkt_info *pkt, u8 opcode, int se,
int mig, int pad, u16 pkey, u32 qpn, int ack_req,
u32 psn)
{
struct rxe_bth *bth = (struct rxe_bth *)(pkt->hdr + pkt->offset);
bth->opcode = opcode;
bth->flags = (pad << 4) & BTH_PAD_MASK;
if (se)
bth->flags |= BTH_SE_MASK;
if (mig)
bth->flags |= BTH_MIG_MASK;
bth->pkey = cpu_to_be16(pkey);
bth->qpn = cpu_to_be32(qpn & BTH_QPN_MASK);
psn &= BTH_PSN_MASK;
if (ack_req)
psn |= BTH_ACK_MASK;
bth->apsn = cpu_to_be32(psn);
}
/******************************************************************************
* Reliable Datagram Extended Transport Header
******************************************************************************/
struct rxe_rdeth {
__be32 een;
};
#define RDETH_EEN_MASK (0x00ffffff)
static inline u8 __rdeth_een(void *arg)
{
struct rxe_rdeth *rdeth = arg;
return RDETH_EEN_MASK & be32_to_cpu(rdeth->een);
}
static inline void __rdeth_set_een(void *arg, u32 een)
{
struct rxe_rdeth *rdeth = arg;
rdeth->een = cpu_to_be32(RDETH_EEN_MASK & een);
}
static inline u8 rdeth_een(struct rxe_pkt_info *pkt)
{
return __rdeth_een(pkt->hdr + pkt->offset
+ rxe_opcode[pkt->opcode].offset[RXE_RDETH]);
}
static inline void rdeth_set_een(struct rxe_pkt_info *pkt, u32 een)
{
__rdeth_set_een(pkt->hdr + pkt->offset
+ rxe_opcode[pkt->opcode].offset[RXE_RDETH], een);
}
/******************************************************************************
* Datagram Extended Transport Header
******************************************************************************/
struct rxe_deth {
__be32 qkey;
__be32 sqp;
};
#define GSI_QKEY (0x80010000)
#define DETH_SQP_MASK (0x00ffffff)
static inline u32 __deth_qkey(void *arg)
{
struct rxe_deth *deth = arg;
return be32_to_cpu(deth->qkey);
}
static inline void __deth_set_qkey(void *arg, u32 qkey)
{
struct rxe_deth *deth = arg;
deth->qkey = cpu_to_be32(qkey);
}
static inline u32 __deth_sqp(void *arg)
{
struct rxe_deth *deth = arg;
return DETH_SQP_MASK & be32_to_cpu(deth->sqp);
}
static inline void __deth_set_sqp(void *arg, u32 sqp)
{
struct rxe_deth *deth = arg;
deth->sqp = cpu_to_be32(DETH_SQP_MASK & sqp);
}
static inline u32 deth_qkey(struct rxe_pkt_info *pkt)
{
return __deth_qkey(pkt->hdr + pkt->offset
+ rxe_opcode[pkt->opcode].offset[RXE_DETH]);
}
static inline void deth_set_qkey(struct rxe_pkt_info *pkt, u32 qkey)
{
__deth_set_qkey(pkt->hdr + pkt->offset
+ rxe_opcode[pkt->opcode].offset[RXE_DETH], qkey);
}
static inline u32 deth_sqp(struct rxe_pkt_info *pkt)
{
return __deth_sqp(pkt->hdr + pkt->offset
+ rxe_opcode[pkt->opcode].offset[RXE_DETH]);
}
static inline void deth_set_sqp(struct rxe_pkt_info *pkt, u32 sqp)
{
__deth_set_sqp(pkt->hdr + pkt->offset
+ rxe_opcode[pkt->opcode].offset[RXE_DETH], sqp);
}
/******************************************************************************
* RDMA Extended Transport Header
******************************************************************************/
struct rxe_reth {
__be64 va;
__be32 rkey;
__be32 len;
};
static inline u64 __reth_va(void *arg)
{
struct rxe_reth *reth = arg;
return be64_to_cpu(reth->va);
}
static inline void __reth_set_va(void *arg, u64 va)
{
struct rxe_reth *reth = arg;
reth->va = cpu_to_be64(va);
}
static inline u32 __reth_rkey(void *arg)
{
struct rxe_reth *reth = arg;
return be32_to_cpu(reth->rkey);
}
static inline void __reth_set_rkey(void *arg, u32 rkey)
{
struct rxe_reth *reth = arg;
reth->rkey = cpu_to_be32(rkey);
}
static inline u32 __reth_len(void *arg)
{
struct rxe_reth *reth = arg;
return be32_to_cpu(reth->len);
}
static inline void __reth_set_len(void *arg, u32 len)
{
struct rxe_reth *reth = arg;
reth->len = cpu_to_be32(len);
}
static inline u64 reth_va(struct rxe_pkt_info *pkt)
{
return __reth_va(pkt->hdr + pkt->offset
+ rxe_opcode[pkt->opcode].offset[RXE_RETH]);
}
static inline void reth_set_va(struct rxe_pkt_info *pkt, u64 va)
{
__reth_set_va(pkt->hdr + pkt->offset
+ rxe_opcode[pkt->opcode].offset[RXE_RETH], va);
}
static inline u32 reth_rkey(struct rxe_pkt_info *pkt)
{
return __reth_rkey(pkt->hdr + pkt->offset
+ rxe_opcode[pkt->opcode].offset[RXE_RETH]);
}
static inline void reth_set_rkey(struct rxe_pkt_info *pkt, u32 rkey)
{
__reth_set_rkey(pkt->hdr + pkt->offset
+ rxe_opcode[pkt->opcode].offset[RXE_RETH], rkey);
}
static inline u32 reth_len(struct rxe_pkt_info *pkt)
{
return __reth_len(pkt->hdr + pkt->offset
+ rxe_opcode[pkt->opcode].offset[RXE_RETH]);
}
static inline void reth_set_len(struct rxe_pkt_info *pkt, u32 len)
{
__reth_set_len(pkt->hdr + pkt->offset
+ rxe_opcode[pkt->opcode].offset[RXE_RETH], len);
}
/******************************************************************************
* Atomic Extended Transport Header
******************************************************************************/
struct rxe_atmeth {
__be64 va;
__be32 rkey;
__be64 swap_add;
__be64 comp;
} __attribute__((__packed__));
static inline u64 __atmeth_va(void *arg)
{
struct rxe_atmeth *atmeth = arg;
return be64_to_cpu(atmeth->va);
}
static inline void __atmeth_set_va(void *arg, u64 va)
{
struct rxe_atmeth *atmeth = arg;
atmeth->va = cpu_to_be64(va);
}
static inline u32 __atmeth_rkey(void *arg)
{
struct rxe_atmeth *atmeth = arg;
return be32_to_cpu(atmeth->rkey);
}
static inline void __atmeth_set_rkey(void *arg, u32 rkey)
{
struct rxe_atmeth *atmeth = arg;
atmeth->rkey = cpu_to_be32(rkey);
}
static inline u64 __atmeth_swap_add(void *arg)
{
struct rxe_atmeth *atmeth = arg;
return be64_to_cpu(atmeth->swap_add);
}
static inline void __atmeth_set_swap_add(void *arg, u64 swap_add)
{
struct rxe_atmeth *atmeth = arg;
atmeth->swap_add = cpu_to_be64(swap_add);
}
static inline u64 __atmeth_comp(void *arg)
{
struct rxe_atmeth *atmeth = arg;
return be64_to_cpu(atmeth->comp);
}
static inline void __atmeth_set_comp(void *arg, u64 comp)
{
struct rxe_atmeth *atmeth = arg;
atmeth->comp = cpu_to_be64(comp);
}
static inline u64 atmeth_va(struct rxe_pkt_info *pkt)
{
return __atmeth_va(pkt->hdr + pkt->offset
+ rxe_opcode[pkt->opcode].offset[RXE_ATMETH]);
}
static inline void atmeth_set_va(struct rxe_pkt_info *pkt, u64 va)
{
__atmeth_set_va(pkt->hdr + pkt->offset
+ rxe_opcode[pkt->opcode].offset[RXE_ATMETH], va);
}
static inline u32 atmeth_rkey(struct rxe_pkt_info *pkt)
{
return __atmeth_rkey(pkt->hdr + pkt->offset
+ rxe_opcode[pkt->opcode].offset[RXE_ATMETH]);
}
static inline void atmeth_set_rkey(struct rxe_pkt_info *pkt, u32 rkey)
{
__atmeth_set_rkey(pkt->hdr + pkt->offset
+ rxe_opcode[pkt->opcode].offset[RXE_ATMETH], rkey);
}
static inline u64 atmeth_swap_add(struct rxe_pkt_info *pkt)
{
return __atmeth_swap_add(pkt->hdr + pkt->offset
+ rxe_opcode[pkt->opcode].offset[RXE_ATMETH]);
}
static inline void atmeth_set_swap_add(struct rxe_pkt_info *pkt, u64 swap_add)
{
__atmeth_set_swap_add(pkt->hdr + pkt->offset
+ rxe_opcode[pkt->opcode].offset[RXE_ATMETH], swap_add);
}
static inline u64 atmeth_comp(struct rxe_pkt_info *pkt)
{
return __atmeth_comp(pkt->hdr + pkt->offset
+ rxe_opcode[pkt->opcode].offset[RXE_ATMETH]);
}
static inline void atmeth_set_comp(struct rxe_pkt_info *pkt, u64 comp)
{
__atmeth_set_comp(pkt->hdr + pkt->offset
+ rxe_opcode[pkt->opcode].offset[RXE_ATMETH], comp);
}
/******************************************************************************
* Ack Extended Transport Header
******************************************************************************/
struct rxe_aeth {
__be32 smsn;
};
#define AETH_SYN_MASK (0xff000000)
#define AETH_MSN_MASK (0x00ffffff)
enum aeth_syndrome {
AETH_TYPE_MASK = 0xe0,
AETH_ACK = 0x00,
AETH_RNR_NAK = 0x20,
AETH_RSVD = 0x40,
AETH_NAK = 0x60,
AETH_ACK_UNLIMITED = 0x1f,
AETH_NAK_PSN_SEQ_ERROR = 0x60,
AETH_NAK_INVALID_REQ = 0x61,
AETH_NAK_REM_ACC_ERR = 0x62,
AETH_NAK_REM_OP_ERR = 0x63,
AETH_NAK_INV_RD_REQ = 0x64,
};
static inline u8 __aeth_syn(void *arg)
{
struct rxe_aeth *aeth = arg;
return (AETH_SYN_MASK & be32_to_cpu(aeth->smsn)) >> 24;
}
static inline void __aeth_set_syn(void *arg, u8 syn)
{
struct rxe_aeth *aeth = arg;
u32 smsn = be32_to_cpu(aeth->smsn);
aeth->smsn = cpu_to_be32((AETH_SYN_MASK & (syn << 24)) |
(~AETH_SYN_MASK & smsn));
}
static inline u32 __aeth_msn(void *arg)
{
struct rxe_aeth *aeth = arg;
return AETH_MSN_MASK & be32_to_cpu(aeth->smsn);
}
static inline void __aeth_set_msn(void *arg, u32 msn)
{
struct rxe_aeth *aeth = arg;
u32 smsn = be32_to_cpu(aeth->smsn);
aeth->smsn = cpu_to_be32((AETH_MSN_MASK & msn) |
(~AETH_MSN_MASK & smsn));
}
static inline u8 aeth_syn(struct rxe_pkt_info *pkt)
{
return __aeth_syn(pkt->hdr + pkt->offset
+ rxe_opcode[pkt->opcode].offset[RXE_AETH]);
}
static inline void aeth_set_syn(struct rxe_pkt_info *pkt, u8 syn)
{
__aeth_set_syn(pkt->hdr + pkt->offset
+ rxe_opcode[pkt->opcode].offset[RXE_AETH], syn);
}
static inline u32 aeth_msn(struct rxe_pkt_info *pkt)
{
return __aeth_msn(pkt->hdr + pkt->offset
+ rxe_opcode[pkt->opcode].offset[RXE_AETH]);
}
static inline void aeth_set_msn(struct rxe_pkt_info *pkt, u32 msn)
{
__aeth_set_msn(pkt->hdr + pkt->offset
+ rxe_opcode[pkt->opcode].offset[RXE_AETH], msn);
}
/******************************************************************************
* Atomic Ack Extended Transport Header
******************************************************************************/
struct rxe_atmack {
__be64 orig;
};
static inline u64 __atmack_orig(void *arg)
{
struct rxe_atmack *atmack = arg;
return be64_to_cpu(atmack->orig);
}
static inline void __atmack_set_orig(void *arg, u64 orig)
{
struct rxe_atmack *atmack = arg;
atmack->orig = cpu_to_be64(orig);
}
static inline u64 atmack_orig(struct rxe_pkt_info *pkt)
{
return __atmack_orig(pkt->hdr + pkt->offset
+ rxe_opcode[pkt->opcode].offset[RXE_ATMACK]);
}
static inline void atmack_set_orig(struct rxe_pkt_info *pkt, u64 orig)
{
__atmack_set_orig(pkt->hdr + pkt->offset
+ rxe_opcode[pkt->opcode].offset[RXE_ATMACK], orig);
}
/******************************************************************************
* Immediate Extended Transport Header
******************************************************************************/
struct rxe_immdt {
__be32 imm;
};
static inline __be32 __immdt_imm(void *arg)
{
struct rxe_immdt *immdt = arg;
return immdt->imm;
}
static inline void __immdt_set_imm(void *arg, __be32 imm)
{
struct rxe_immdt *immdt = arg;
immdt->imm = imm;
}
static inline __be32 immdt_imm(struct rxe_pkt_info *pkt)
{
return __immdt_imm(pkt->hdr + pkt->offset
+ rxe_opcode[pkt->opcode].offset[RXE_IMMDT]);
}
static inline void immdt_set_imm(struct rxe_pkt_info *pkt, __be32 imm)
{
__immdt_set_imm(pkt->hdr + pkt->offset
+ rxe_opcode[pkt->opcode].offset[RXE_IMMDT], imm);
}
/******************************************************************************
* Invalidate Extended Transport Header
******************************************************************************/
struct rxe_ieth {
__be32 rkey;
};
static inline u32 __ieth_rkey(void *arg)
{
struct rxe_ieth *ieth = arg;
return be32_to_cpu(ieth->rkey);
}
static inline void __ieth_set_rkey(void *arg, u32 rkey)
{
struct rxe_ieth *ieth = arg;
ieth->rkey = cpu_to_be32(rkey);
}
static inline u32 ieth_rkey(struct rxe_pkt_info *pkt)
{
return __ieth_rkey(pkt->hdr + pkt->offset
+ rxe_opcode[pkt->opcode].offset[RXE_IETH]);
}
static inline void ieth_set_rkey(struct rxe_pkt_info *pkt, u32 rkey)
{
__ieth_set_rkey(pkt->hdr + pkt->offset
+ rxe_opcode[pkt->opcode].offset[RXE_IETH], rkey);
}
enum rxe_hdr_length {
RXE_BTH_BYTES = sizeof(struct rxe_bth),
RXE_DETH_BYTES = sizeof(struct rxe_deth),
RXE_IMMDT_BYTES = sizeof(struct rxe_immdt),
RXE_RETH_BYTES = sizeof(struct rxe_reth),
RXE_AETH_BYTES = sizeof(struct rxe_aeth),
RXE_ATMACK_BYTES = sizeof(struct rxe_atmack),
RXE_ATMETH_BYTES = sizeof(struct rxe_atmeth),
RXE_IETH_BYTES = sizeof(struct rxe_ieth),
RXE_RDETH_BYTES = sizeof(struct rxe_rdeth),
};
static inline size_t header_size(struct rxe_pkt_info *pkt)
{
return pkt->offset + rxe_opcode[pkt->opcode].length;
}
static inline void *payload_addr(struct rxe_pkt_info *pkt)
{
return pkt->hdr + pkt->offset
+ rxe_opcode[pkt->opcode].offset[RXE_PAYLOAD];
}
static inline size_t payload_size(struct rxe_pkt_info *pkt)
{
return pkt->paylen - rxe_opcode[pkt->opcode].offset[RXE_PAYLOAD]
- bth_pad(pkt) - RXE_ICRC_SIZE;
}
#endif /* RXE_HDR_H */

View File

@ -0,0 +1,96 @@
/*
* Copyright (c) 2016 Mellanox Technologies Ltd. All rights reserved.
* Copyright (c) 2015 System Fabric Works, 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 "rxe.h"
#include "rxe_loc.h"
/* Compute a partial ICRC for all the IB transport headers. */
u32 rxe_icrc_hdr(struct rxe_pkt_info *pkt, struct sk_buff *skb)
{
unsigned int bth_offset = 0;
struct iphdr *ip4h = NULL;
struct ipv6hdr *ip6h = NULL;
struct udphdr *udph;
struct rxe_bth *bth;
int crc;
int length;
int hdr_size = sizeof(struct udphdr) +
(skb->protocol == htons(ETH_P_IP) ?
sizeof(struct iphdr) : sizeof(struct ipv6hdr));
/* pseudo header buffer size is calculate using ipv6 header size since
* it is bigger than ipv4
*/
u8 pshdr[sizeof(struct udphdr) +
sizeof(struct ipv6hdr) +
RXE_BTH_BYTES];
/* This seed is the result of computing a CRC with a seed of
* 0xfffffff and 8 bytes of 0xff representing a masked LRH.
*/
crc = 0xdebb20e3;
if (skb->protocol == htons(ETH_P_IP)) { /* IPv4 */
memcpy(pshdr, ip_hdr(skb), hdr_size);
ip4h = (struct iphdr *)pshdr;
udph = (struct udphdr *)(ip4h + 1);
ip4h->ttl = 0xff;
ip4h->check = CSUM_MANGLED_0;
ip4h->tos = 0xff;
} else { /* IPv6 */
memcpy(pshdr, ipv6_hdr(skb), hdr_size);
ip6h = (struct ipv6hdr *)pshdr;
udph = (struct udphdr *)(ip6h + 1);
memset(ip6h->flow_lbl, 0xff, sizeof(ip6h->flow_lbl));
ip6h->priority = 0xf;
ip6h->hop_limit = 0xff;
}
udph->check = CSUM_MANGLED_0;
bth_offset += hdr_size;
memcpy(&pshdr[bth_offset], pkt->hdr, RXE_BTH_BYTES);
bth = (struct rxe_bth *)&pshdr[bth_offset];
/* exclude bth.resv8a */
bth->qpn |= cpu_to_be32(~BTH_QPN_MASK);
length = hdr_size + RXE_BTH_BYTES;
crc = crc32_le(crc, pshdr, length);
/* And finish to compute the CRC on the remainder of the headers. */
crc = crc32_le(crc, pkt->hdr + RXE_BTH_BYTES,
rxe_opcode[pkt->opcode].length - RXE_BTH_BYTES);
return crc;
}

View File

@ -0,0 +1,286 @@
/*
* Copyright (c) 2016 Mellanox Technologies Ltd. All rights reserved.
* Copyright (c) 2015 System Fabric Works, 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 RXE_LOC_H
#define RXE_LOC_H
/* rxe_av.c */
int rxe_av_chk_attr(struct rxe_dev *rxe, struct ib_ah_attr *attr);
int rxe_av_from_attr(struct rxe_dev *rxe, u8 port_num,
struct rxe_av *av, struct ib_ah_attr *attr);
int rxe_av_to_attr(struct rxe_dev *rxe, struct rxe_av *av,
struct ib_ah_attr *attr);
int rxe_av_fill_ip_info(struct rxe_dev *rxe,
struct rxe_av *av,
struct ib_ah_attr *attr,
struct ib_gid_attr *sgid_attr,
union ib_gid *sgid);
struct rxe_av *rxe_get_av(struct rxe_pkt_info *pkt);
/* rxe_cq.c */
int rxe_cq_chk_attr(struct rxe_dev *rxe, struct rxe_cq *cq,
int cqe, int comp_vector, struct ib_udata *udata);
int rxe_cq_from_init(struct rxe_dev *rxe, struct rxe_cq *cq, int cqe,
int comp_vector, struct ib_ucontext *context,
struct ib_udata *udata);
int rxe_cq_resize_queue(struct rxe_cq *cq, int new_cqe, struct ib_udata *udata);
int rxe_cq_post(struct rxe_cq *cq, struct rxe_cqe *cqe, int solicited);
void rxe_cq_cleanup(void *arg);
/* rxe_mcast.c */
int rxe_mcast_get_grp(struct rxe_dev *rxe, union ib_gid *mgid,
struct rxe_mc_grp **grp_p);
int rxe_mcast_add_grp_elem(struct rxe_dev *rxe, struct rxe_qp *qp,
struct rxe_mc_grp *grp);
int rxe_mcast_drop_grp_elem(struct rxe_dev *rxe, struct rxe_qp *qp,
union ib_gid *mgid);
void rxe_drop_all_mcast_groups(struct rxe_qp *qp);
void rxe_mc_cleanup(void *arg);
/* rxe_mmap.c */
struct rxe_mmap_info {
struct list_head pending_mmaps;
struct ib_ucontext *context;
struct kref ref;
void *obj;
struct mminfo info;
};
void rxe_mmap_release(struct kref *ref);
struct rxe_mmap_info *rxe_create_mmap_info(struct rxe_dev *dev,
u32 size,
struct ib_ucontext *context,
void *obj);
int rxe_mmap(struct ib_ucontext *context, struct vm_area_struct *vma);
/* rxe_mr.c */
enum copy_direction {
to_mem_obj,
from_mem_obj,
};
int rxe_mem_init_dma(struct rxe_dev *rxe, struct rxe_pd *pd,
int access, struct rxe_mem *mem);
int rxe_mem_init_user(struct rxe_dev *rxe, struct rxe_pd *pd, u64 start,
u64 length, u64 iova, int access, struct ib_udata *udata,
struct rxe_mem *mr);
int rxe_mem_init_fast(struct rxe_dev *rxe, struct rxe_pd *pd,
int max_pages, struct rxe_mem *mem);
int rxe_mem_copy(struct rxe_mem *mem, u64 iova, void *addr,
int length, enum copy_direction dir, u32 *crcp);
int copy_data(struct rxe_dev *rxe, struct rxe_pd *pd, int access,
struct rxe_dma_info *dma, void *addr, int length,
enum copy_direction dir, u32 *crcp);
void *iova_to_vaddr(struct rxe_mem *mem, u64 iova, int length);
enum lookup_type {
lookup_local,
lookup_remote,
};
struct rxe_mem *lookup_mem(struct rxe_pd *pd, int access, u32 key,
enum lookup_type type);
int mem_check_range(struct rxe_mem *mem, u64 iova, size_t length);
int rxe_mem_map_pages(struct rxe_dev *rxe, struct rxe_mem *mem,
u64 *page, int num_pages, u64 iova);
void rxe_mem_cleanup(void *arg);
int advance_dma_data(struct rxe_dma_info *dma, unsigned int length);
/* rxe_qp.c */
int rxe_qp_chk_init(struct rxe_dev *rxe, struct ib_qp_init_attr *init);
int rxe_qp_from_init(struct rxe_dev *rxe, struct rxe_qp *qp, struct rxe_pd *pd,
struct ib_qp_init_attr *init, struct ib_udata *udata,
struct ib_pd *ibpd);
int rxe_qp_to_init(struct rxe_qp *qp, struct ib_qp_init_attr *init);
int rxe_qp_chk_attr(struct rxe_dev *rxe, struct rxe_qp *qp,
struct ib_qp_attr *attr, int mask);
int rxe_qp_from_attr(struct rxe_qp *qp, struct ib_qp_attr *attr,
int mask, struct ib_udata *udata);
int rxe_qp_to_attr(struct rxe_qp *qp, struct ib_qp_attr *attr, int mask);
void rxe_qp_error(struct rxe_qp *qp);
void rxe_qp_destroy(struct rxe_qp *qp);
void rxe_qp_cleanup(void *arg);
static inline int qp_num(struct rxe_qp *qp)
{
return qp->ibqp.qp_num;
}
static inline enum ib_qp_type qp_type(struct rxe_qp *qp)
{
return qp->ibqp.qp_type;
}
static inline enum ib_qp_state qp_state(struct rxe_qp *qp)
{
return qp->attr.qp_state;
}
static inline int qp_mtu(struct rxe_qp *qp)
{
if (qp->ibqp.qp_type == IB_QPT_RC || qp->ibqp.qp_type == IB_QPT_UC)
return qp->attr.path_mtu;
else
return RXE_PORT_MAX_MTU;
}
static inline int rcv_wqe_size(int max_sge)
{
return sizeof(struct rxe_recv_wqe) +
max_sge * sizeof(struct ib_sge);
}
void free_rd_atomic_resource(struct rxe_qp *qp, struct resp_res *res);
static inline void rxe_advance_resp_resource(struct rxe_qp *qp)
{
qp->resp.res_head++;
if (unlikely(qp->resp.res_head == qp->attr.max_rd_atomic))
qp->resp.res_head = 0;
}
void retransmit_timer(unsigned long data);
void rnr_nak_timer(unsigned long data);
void dump_qp(struct rxe_qp *qp);
/* rxe_srq.c */
#define IB_SRQ_INIT_MASK (~IB_SRQ_LIMIT)
int rxe_srq_chk_attr(struct rxe_dev *rxe, struct rxe_srq *srq,
struct ib_srq_attr *attr, enum ib_srq_attr_mask mask);
int rxe_srq_from_init(struct rxe_dev *rxe, struct rxe_srq *srq,
struct ib_srq_init_attr *init,
struct ib_ucontext *context, struct ib_udata *udata);
int rxe_srq_from_attr(struct rxe_dev *rxe, struct rxe_srq *srq,
struct ib_srq_attr *attr, enum ib_srq_attr_mask mask,
struct ib_udata *udata);
extern struct ib_dma_mapping_ops rxe_dma_mapping_ops;
void rxe_release(struct kref *kref);
int rxe_completer(void *arg);
int rxe_requester(void *arg);
int rxe_responder(void *arg);
u32 rxe_icrc_hdr(struct rxe_pkt_info *pkt, struct sk_buff *skb);
void rxe_resp_queue_pkt(struct rxe_dev *rxe,
struct rxe_qp *qp, struct sk_buff *skb);
void rxe_comp_queue_pkt(struct rxe_dev *rxe,
struct rxe_qp *qp, struct sk_buff *skb);
static inline unsigned wr_opcode_mask(int opcode, struct rxe_qp *qp)
{
return rxe_wr_opcode_info[opcode].mask[qp->ibqp.qp_type];
}
static inline int rxe_xmit_packet(struct rxe_dev *rxe, struct rxe_qp *qp,
struct rxe_pkt_info *pkt, struct sk_buff *skb)
{
int err;
int is_request = pkt->mask & RXE_REQ_MASK;
if ((is_request && (qp->req.state != QP_STATE_READY)) ||
(!is_request && (qp->resp.state != QP_STATE_READY))) {
pr_info("Packet dropped. QP is not in ready state\n");
goto drop;
}
if (pkt->mask & RXE_LOOPBACK_MASK) {
memcpy(SKB_TO_PKT(skb), pkt, sizeof(*pkt));
err = rxe->ifc_ops->loopback(skb);
} else {
err = rxe->ifc_ops->send(rxe, pkt, skb);
}
if (err) {
rxe->xmit_errors++;
return err;
}
atomic_inc(&qp->skb_out);
if ((qp_type(qp) != IB_QPT_RC) &&
(pkt->mask & RXE_END_MASK)) {
pkt->wqe->state = wqe_state_done;
rxe_run_task(&qp->comp.task, 1);
}
goto done;
drop:
kfree_skb(skb);
err = 0;
done:
return err;
}
#endif /* RXE_LOC_H */

View File

@ -0,0 +1,190 @@
/*
* Copyright (c) 2016 Mellanox Technologies Ltd. All rights reserved.
* Copyright (c) 2015 System Fabric Works, 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 "rxe.h"
#include "rxe_loc.h"
int rxe_mcast_get_grp(struct rxe_dev *rxe, union ib_gid *mgid,
struct rxe_mc_grp **grp_p)
{
int err;
struct rxe_mc_grp *grp;
if (rxe->attr.max_mcast_qp_attach == 0) {
err = -EINVAL;
goto err1;
}
grp = rxe_pool_get_key(&rxe->mc_grp_pool, mgid);
if (grp)
goto done;
grp = rxe_alloc(&rxe->mc_grp_pool);
if (!grp) {
err = -ENOMEM;
goto err1;
}
INIT_LIST_HEAD(&grp->qp_list);
spin_lock_init(&grp->mcg_lock);
grp->rxe = rxe;
rxe_add_key(grp, mgid);
err = rxe->ifc_ops->mcast_add(rxe, mgid);
if (err)
goto err2;
done:
*grp_p = grp;
return 0;
err2:
rxe_drop_ref(grp);
err1:
return err;
}
int rxe_mcast_add_grp_elem(struct rxe_dev *rxe, struct rxe_qp *qp,
struct rxe_mc_grp *grp)
{
int err;
struct rxe_mc_elem *elem;
/* check to see of the qp is already a member of the group */
spin_lock_bh(&qp->grp_lock);
spin_lock_bh(&grp->mcg_lock);
list_for_each_entry(elem, &grp->qp_list, qp_list) {
if (elem->qp == qp) {
err = 0;
goto out;
}
}
if (grp->num_qp >= rxe->attr.max_mcast_qp_attach) {
err = -ENOMEM;
goto out;
}
elem = rxe_alloc(&rxe->mc_elem_pool);
if (!elem) {
err = -ENOMEM;
goto out;
}
/* each qp holds a ref on the grp */
rxe_add_ref(grp);
grp->num_qp++;
elem->qp = qp;
elem->grp = grp;
list_add(&elem->qp_list, &grp->qp_list);
list_add(&elem->grp_list, &qp->grp_list);
err = 0;
out:
spin_unlock_bh(&grp->mcg_lock);
spin_unlock_bh(&qp->grp_lock);
return err;
}
int rxe_mcast_drop_grp_elem(struct rxe_dev *rxe, struct rxe_qp *qp,
union ib_gid *mgid)
{
struct rxe_mc_grp *grp;
struct rxe_mc_elem *elem, *tmp;
grp = rxe_pool_get_key(&rxe->mc_grp_pool, mgid);
if (!grp)
goto err1;
spin_lock_bh(&qp->grp_lock);
spin_lock_bh(&grp->mcg_lock);
list_for_each_entry_safe(elem, tmp, &grp->qp_list, qp_list) {
if (elem->qp == qp) {
list_del(&elem->qp_list);
list_del(&elem->grp_list);
grp->num_qp--;
spin_unlock_bh(&grp->mcg_lock);
spin_unlock_bh(&qp->grp_lock);
rxe_drop_ref(elem);
rxe_drop_ref(grp); /* ref held by QP */
rxe_drop_ref(grp); /* ref from get_key */
return 0;
}
}
spin_unlock_bh(&grp->mcg_lock);
spin_unlock_bh(&qp->grp_lock);
rxe_drop_ref(grp); /* ref from get_key */
err1:
return -EINVAL;
}
void rxe_drop_all_mcast_groups(struct rxe_qp *qp)
{
struct rxe_mc_grp *grp;
struct rxe_mc_elem *elem;
while (1) {
spin_lock_bh(&qp->grp_lock);
if (list_empty(&qp->grp_list)) {
spin_unlock_bh(&qp->grp_lock);
break;
}
elem = list_first_entry(&qp->grp_list, struct rxe_mc_elem,
grp_list);
list_del(&elem->grp_list);
spin_unlock_bh(&qp->grp_lock);
grp = elem->grp;
spin_lock_bh(&grp->mcg_lock);
list_del(&elem->qp_list);
grp->num_qp--;
spin_unlock_bh(&grp->mcg_lock);
rxe_drop_ref(grp);
rxe_drop_ref(elem);
}
}
void rxe_mc_cleanup(void *arg)
{
struct rxe_mc_grp *grp = arg;
struct rxe_dev *rxe = grp->rxe;
rxe_drop_key(grp);
rxe->ifc_ops->mcast_delete(rxe, &grp->mgid);
}

View File

@ -0,0 +1,173 @@
/*
* Copyright (c) 2016 Mellanox Technologies Ltd. All rights reserved.
* Copyright (c) 2015 System Fabric Works, 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/vmalloc.h>
#include <linux/mm.h>
#include <linux/errno.h>
#include <asm/pgtable.h>
#include "rxe.h"
#include "rxe_loc.h"
#include "rxe_queue.h"
void rxe_mmap_release(struct kref *ref)
{
struct rxe_mmap_info *ip = container_of(ref,
struct rxe_mmap_info, ref);
struct rxe_dev *rxe = to_rdev(ip->context->device);
spin_lock_bh(&rxe->pending_lock);
if (!list_empty(&ip->pending_mmaps))
list_del(&ip->pending_mmaps);
spin_unlock_bh(&rxe->pending_lock);
vfree(ip->obj); /* buf */
kfree(ip);
}
/*
* open and close keep track of how many times the memory region is mapped,
* to avoid releasing it.
*/
static void rxe_vma_open(struct vm_area_struct *vma)
{
struct rxe_mmap_info *ip = vma->vm_private_data;
kref_get(&ip->ref);
}
static void rxe_vma_close(struct vm_area_struct *vma)
{
struct rxe_mmap_info *ip = vma->vm_private_data;
kref_put(&ip->ref, rxe_mmap_release);
}
static struct vm_operations_struct rxe_vm_ops = {
.open = rxe_vma_open,
.close = rxe_vma_close,
};
/**
* rxe_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 rxe_mmap(struct ib_ucontext *context, struct vm_area_struct *vma)
{
struct rxe_dev *rxe = to_rdev(context->device);
unsigned long offset = vma->vm_pgoff << PAGE_SHIFT;
unsigned long size = vma->vm_end - vma->vm_start;
struct rxe_mmap_info *ip, *pp;
int ret;
/*
* 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_bh(&rxe->pending_lock);
list_for_each_entry_safe(ip, pp, &rxe->pending_mmaps, pending_mmaps) {
if (context != ip->context || (__u64)offset != ip->info.offset)
continue;
/* Don't allow a mmap larger than the object. */
if (size > ip->info.size) {
pr_err("mmap region is larger than the object!\n");
spin_unlock_bh(&rxe->pending_lock);
ret = -EINVAL;
goto done;
}
goto found_it;
}
pr_warn("unable to find pending mmap info\n");
spin_unlock_bh(&rxe->pending_lock);
ret = -EINVAL;
goto done;
found_it:
list_del_init(&ip->pending_mmaps);
spin_unlock_bh(&rxe->pending_lock);
ret = remap_vmalloc_range(vma, ip->obj, 0);
if (ret) {
pr_err("rxe: err %d from remap_vmalloc_range\n", ret);
goto done;
}
vma->vm_ops = &rxe_vm_ops;
vma->vm_private_data = ip;
rxe_vma_open(vma);
done:
return ret;
}
/*
* Allocate information for rxe_mmap
*/
struct rxe_mmap_info *rxe_create_mmap_info(struct rxe_dev *rxe,
u32 size,
struct ib_ucontext *context,
void *obj)
{
struct rxe_mmap_info *ip;
ip = kmalloc(sizeof(*ip), GFP_KERNEL);
if (!ip)
return NULL;
size = PAGE_ALIGN(size);
spin_lock_bh(&rxe->mmap_offset_lock);
if (rxe->mmap_offset == 0)
rxe->mmap_offset = PAGE_SIZE;
ip->info.offset = rxe->mmap_offset;
rxe->mmap_offset += size;
spin_unlock_bh(&rxe->mmap_offset_lock);
INIT_LIST_HEAD(&ip->pending_mmaps);
ip->info.size = size;
ip->context = context;
ip->obj = obj;
kref_init(&ip->ref);
return ip;
}

View File

@ -0,0 +1,643 @@
/*
* Copyright (c) 2016 Mellanox Technologies Ltd. All rights reserved.
* Copyright (c) 2015 System Fabric Works, 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 "rxe.h"
#include "rxe_loc.h"
/*
* lfsr (linear feedback shift register) with period 255
*/
static u8 rxe_get_key(void)
{
static unsigned key = 1;
key = key << 1;
key |= (0 != (key & 0x100)) ^ (0 != (key & 0x10))
^ (0 != (key & 0x80)) ^ (0 != (key & 0x40));
key &= 0xff;
return key;
}
int mem_check_range(struct rxe_mem *mem, u64 iova, size_t length)
{
switch (mem->type) {
case RXE_MEM_TYPE_DMA:
return 0;
case RXE_MEM_TYPE_MR:
case RXE_MEM_TYPE_FMR:
return ((iova < mem->iova) ||
((iova + length) > (mem->iova + mem->length))) ?
-EFAULT : 0;
default:
return -EFAULT;
}
}
#define IB_ACCESS_REMOTE (IB_ACCESS_REMOTE_READ \
| IB_ACCESS_REMOTE_WRITE \
| IB_ACCESS_REMOTE_ATOMIC)
static void rxe_mem_init(int access, struct rxe_mem *mem)
{
u32 lkey = mem->pelem.index << 8 | rxe_get_key();
u32 rkey = (access & IB_ACCESS_REMOTE) ? lkey : 0;
if (mem->pelem.pool->type == RXE_TYPE_MR) {
mem->ibmr.lkey = lkey;
mem->ibmr.rkey = rkey;
}
mem->lkey = lkey;
mem->rkey = rkey;
mem->state = RXE_MEM_STATE_INVALID;
mem->type = RXE_MEM_TYPE_NONE;
mem->map_shift = ilog2(RXE_BUF_PER_MAP);
}
void rxe_mem_cleanup(void *arg)
{
struct rxe_mem *mem = arg;
int i;
if (mem->umem)
ib_umem_release(mem->umem);
if (mem->map) {
for (i = 0; i < mem->num_map; i++)
kfree(mem->map[i]);
kfree(mem->map);
}
}
static int rxe_mem_alloc(struct rxe_dev *rxe, struct rxe_mem *mem, int num_buf)
{
int i;
int num_map;
struct rxe_map **map = mem->map;
num_map = (num_buf + RXE_BUF_PER_MAP - 1) / RXE_BUF_PER_MAP;
mem->map = kmalloc_array(num_map, sizeof(*map), GFP_KERNEL);
if (!mem->map)
goto err1;
for (i = 0; i < num_map; i++) {
mem->map[i] = kmalloc(sizeof(**map), GFP_KERNEL);
if (!mem->map[i])
goto err2;
}
WARN_ON(!is_power_of_2(RXE_BUF_PER_MAP));
mem->map_shift = ilog2(RXE_BUF_PER_MAP);
mem->map_mask = RXE_BUF_PER_MAP - 1;
mem->num_buf = num_buf;
mem->num_map = num_map;
mem->max_buf = num_map * RXE_BUF_PER_MAP;
return 0;
err2:
for (i--; i >= 0; i--)
kfree(mem->map[i]);
kfree(mem->map);
err1:
return -ENOMEM;
}
int rxe_mem_init_dma(struct rxe_dev *rxe, struct rxe_pd *pd,
int access, struct rxe_mem *mem)
{
rxe_mem_init(access, mem);
mem->pd = pd;
mem->access = access;
mem->state = RXE_MEM_STATE_VALID;
mem->type = RXE_MEM_TYPE_DMA;
return 0;
}
int rxe_mem_init_user(struct rxe_dev *rxe, struct rxe_pd *pd, u64 start,
u64 length, u64 iova, int access, struct ib_udata *udata,
struct rxe_mem *mem)
{
int entry;
struct rxe_map **map;
struct rxe_phys_buf *buf = NULL;
struct ib_umem *umem;
struct scatterlist *sg;
int num_buf;
void *vaddr;
int err;
umem = ib_umem_get(pd->ibpd.uobject->context, start, length, access, 0);
if (IS_ERR(umem)) {
pr_warn("err %d from rxe_umem_get\n",
(int)PTR_ERR(umem));
err = -EINVAL;
goto err1;
}
mem->umem = umem;
num_buf = umem->nmap;
rxe_mem_init(access, mem);
err = rxe_mem_alloc(rxe, mem, num_buf);
if (err) {
pr_warn("err %d from rxe_mem_alloc\n", err);
ib_umem_release(umem);
goto err1;
}
WARN_ON(!is_power_of_2(umem->page_size));
mem->page_shift = ilog2(umem->page_size);
mem->page_mask = umem->page_size - 1;
num_buf = 0;
map = mem->map;
if (length > 0) {
buf = map[0]->buf;
for_each_sg(umem->sg_head.sgl, sg, umem->nmap, entry) {
vaddr = page_address(sg_page(sg));
if (!vaddr) {
pr_warn("null vaddr\n");
err = -ENOMEM;
goto err1;
}
buf->addr = (uintptr_t)vaddr;
buf->size = umem->page_size;
num_buf++;
buf++;
if (num_buf >= RXE_BUF_PER_MAP) {
map++;
buf = map[0]->buf;
num_buf = 0;
}
}
}
mem->pd = pd;
mem->umem = umem;
mem->access = access;
mem->length = length;
mem->iova = iova;
mem->va = start;
mem->offset = ib_umem_offset(umem);
mem->state = RXE_MEM_STATE_VALID;
mem->type = RXE_MEM_TYPE_MR;
return 0;
err1:
return err;
}
int rxe_mem_init_fast(struct rxe_dev *rxe, struct rxe_pd *pd,
int max_pages, struct rxe_mem *mem)
{
int err;
rxe_mem_init(0, mem);
/* In fastreg, we also set the rkey */
mem->ibmr.rkey = mem->ibmr.lkey;
err = rxe_mem_alloc(rxe, mem, max_pages);
if (err)
goto err1;
mem->pd = pd;
mem->max_buf = max_pages;
mem->state = RXE_MEM_STATE_FREE;
mem->type = RXE_MEM_TYPE_MR;
return 0;
err1:
return err;
}
static void lookup_iova(
struct rxe_mem *mem,
u64 iova,
int *m_out,
int *n_out,
size_t *offset_out)
{
size_t offset = iova - mem->iova + mem->offset;
int map_index;
int buf_index;
u64 length;
if (likely(mem->page_shift)) {
*offset_out = offset & mem->page_mask;
offset >>= mem->page_shift;
*n_out = offset & mem->map_mask;
*m_out = offset >> mem->map_shift;
} else {
map_index = 0;
buf_index = 0;
length = mem->map[map_index]->buf[buf_index].size;
while (offset >= length) {
offset -= length;
buf_index++;
if (buf_index == RXE_BUF_PER_MAP) {
map_index++;
buf_index = 0;
}
length = mem->map[map_index]->buf[buf_index].size;
}
*m_out = map_index;
*n_out = buf_index;
*offset_out = offset;
}
}
void *iova_to_vaddr(struct rxe_mem *mem, u64 iova, int length)
{
size_t offset;
int m, n;
void *addr;
if (mem->state != RXE_MEM_STATE_VALID) {
pr_warn("mem not in valid state\n");
addr = NULL;
goto out;
}
if (!mem->map) {
addr = (void *)(uintptr_t)iova;
goto out;
}
if (mem_check_range(mem, iova, length)) {
pr_warn("range violation\n");
addr = NULL;
goto out;
}
lookup_iova(mem, iova, &m, &n, &offset);
if (offset + length > mem->map[m]->buf[n].size) {
pr_warn("crosses page boundary\n");
addr = NULL;
goto out;
}
addr = (void *)(uintptr_t)mem->map[m]->buf[n].addr + offset;
out:
return addr;
}
/* copy data from a range (vaddr, vaddr+length-1) to or from
* a mem object starting at iova. Compute incremental value of
* crc32 if crcp is not zero. caller must hold a reference to mem
*/
int rxe_mem_copy(struct rxe_mem *mem, u64 iova, void *addr, int length,
enum copy_direction dir, u32 *crcp)
{
int err;
int bytes;
u8 *va;
struct rxe_map **map;
struct rxe_phys_buf *buf;
int m;
int i;
size_t offset;
u32 crc = crcp ? (*crcp) : 0;
if (mem->type == RXE_MEM_TYPE_DMA) {
u8 *src, *dest;
src = (dir == to_mem_obj) ?
addr : ((void *)(uintptr_t)iova);
dest = (dir == to_mem_obj) ?
((void *)(uintptr_t)iova) : addr;
if (crcp)
*crcp = crc32_le(*crcp, src, length);
memcpy(dest, src, length);
return 0;
}
WARN_ON(!mem->map);
err = mem_check_range(mem, iova, length);
if (err) {
err = -EFAULT;
goto err1;
}
lookup_iova(mem, iova, &m, &i, &offset);
map = mem->map + m;
buf = map[0]->buf + i;
while (length > 0) {
u8 *src, *dest;
va = (u8 *)(uintptr_t)buf->addr + offset;
src = (dir == to_mem_obj) ? addr : va;
dest = (dir == to_mem_obj) ? va : addr;
bytes = buf->size - offset;
if (bytes > length)
bytes = length;
if (crcp)
crc = crc32_le(crc, src, bytes);
memcpy(dest, src, bytes);
length -= bytes;
addr += bytes;
offset = 0;
buf++;
i++;
if (i == RXE_BUF_PER_MAP) {
i = 0;
map++;
buf = map[0]->buf;
}
}
if (crcp)
*crcp = crc;
return 0;
err1:
return err;
}
/* copy data in or out of a wqe, i.e. sg list
* under the control of a dma descriptor
*/
int copy_data(
struct rxe_dev *rxe,
struct rxe_pd *pd,
int access,
struct rxe_dma_info *dma,
void *addr,
int length,
enum copy_direction dir,
u32 *crcp)
{
int bytes;
struct rxe_sge *sge = &dma->sge[dma->cur_sge];
int offset = dma->sge_offset;
int resid = dma->resid;
struct rxe_mem *mem = NULL;
u64 iova;
int err;
if (length == 0)
return 0;
if (length > resid) {
err = -EINVAL;
goto err2;
}
if (sge->length && (offset < sge->length)) {
mem = lookup_mem(pd, access, sge->lkey, lookup_local);
if (!mem) {
err = -EINVAL;
goto err1;
}
}
while (length > 0) {
bytes = length;
if (offset >= sge->length) {
if (mem) {
rxe_drop_ref(mem);
mem = NULL;
}
sge++;
dma->cur_sge++;
offset = 0;
if (dma->cur_sge >= dma->num_sge) {
err = -ENOSPC;
goto err2;
}
if (sge->length) {
mem = lookup_mem(pd, access, sge->lkey,
lookup_local);
if (!mem) {
err = -EINVAL;
goto err1;
}
} else {
continue;
}
}
if (bytes > sge->length - offset)
bytes = sge->length - offset;
if (bytes > 0) {
iova = sge->addr + offset;
err = rxe_mem_copy(mem, iova, addr, bytes, dir, crcp);
if (err)
goto err2;
offset += bytes;
resid -= bytes;
length -= bytes;
addr += bytes;
}
}
dma->sge_offset = offset;
dma->resid = resid;
if (mem)
rxe_drop_ref(mem);
return 0;
err2:
if (mem)
rxe_drop_ref(mem);
err1:
return err;
}
int advance_dma_data(struct rxe_dma_info *dma, unsigned int length)
{
struct rxe_sge *sge = &dma->sge[dma->cur_sge];
int offset = dma->sge_offset;
int resid = dma->resid;
while (length) {
unsigned int bytes;
if (offset >= sge->length) {
sge++;
dma->cur_sge++;
offset = 0;
if (dma->cur_sge >= dma->num_sge)
return -ENOSPC;
}
bytes = length;
if (bytes > sge->length - offset)
bytes = sge->length - offset;
offset += bytes;
resid -= bytes;
length -= bytes;
}
dma->sge_offset = offset;
dma->resid = resid;
return 0;
}
/* (1) find the mem (mr or mw) corresponding to lkey/rkey
* depending on lookup_type
* (2) verify that the (qp) pd matches the mem pd
* (3) verify that the mem can support the requested access
* (4) verify that mem state is valid
*/
struct rxe_mem *lookup_mem(struct rxe_pd *pd, int access, u32 key,
enum lookup_type type)
{
struct rxe_mem *mem;
struct rxe_dev *rxe = to_rdev(pd->ibpd.device);
int index = key >> 8;
if (index >= RXE_MIN_MR_INDEX && index <= RXE_MAX_MR_INDEX) {
mem = rxe_pool_get_index(&rxe->mr_pool, index);
if (!mem)
goto err1;
} else {
goto err1;
}
if ((type == lookup_local && mem->lkey != key) ||
(type == lookup_remote && mem->rkey != key))
goto err2;
if (mem->pd != pd)
goto err2;
if (access && !(access & mem->access))
goto err2;
if (mem->state != RXE_MEM_STATE_VALID)
goto err2;
return mem;
err2:
rxe_drop_ref(mem);
err1:
return NULL;
}
int rxe_mem_map_pages(struct rxe_dev *rxe, struct rxe_mem *mem,
u64 *page, int num_pages, u64 iova)
{
int i;
int num_buf;
int err;
struct rxe_map **map;
struct rxe_phys_buf *buf;
int page_size;
if (num_pages > mem->max_buf) {
err = -EINVAL;
goto err1;
}
num_buf = 0;
page_size = 1 << mem->page_shift;
map = mem->map;
buf = map[0]->buf;
for (i = 0; i < num_pages; i++) {
buf->addr = *page++;
buf->size = page_size;
buf++;
num_buf++;
if (num_buf == RXE_BUF_PER_MAP) {
map++;
buf = map[0]->buf;
num_buf = 0;
}
}
mem->iova = iova;
mem->va = iova;
mem->length = num_pages << mem->page_shift;
mem->state = RXE_MEM_STATE_VALID;
return 0;
err1:
return err;
}

View File

@ -0,0 +1,708 @@
/*
* Copyright (c) 2016 Mellanox Technologies Ltd. All rights reserved.
* Copyright (c) 2015 System Fabric Works, 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/skbuff.h>
#include <linux/if_arp.h>
#include <linux/netdevice.h>
#include <linux/if.h>
#include <linux/if_vlan.h>
#include <net/udp_tunnel.h>
#include <net/sch_generic.h>
#include <linux/netfilter.h>
#include <rdma/ib_addr.h>
#include "rxe.h"
#include "rxe_net.h"
#include "rxe_loc.h"
static LIST_HEAD(rxe_dev_list);
static spinlock_t dev_list_lock; /* spinlock for device list */
struct rxe_dev *net_to_rxe(struct net_device *ndev)
{
struct rxe_dev *rxe;
struct rxe_dev *found = NULL;
spin_lock_bh(&dev_list_lock);
list_for_each_entry(rxe, &rxe_dev_list, list) {
if (rxe->ndev == ndev) {
found = rxe;
break;
}
}
spin_unlock_bh(&dev_list_lock);
return found;
}
struct rxe_dev *get_rxe_by_name(const char* name)
{
struct rxe_dev *rxe;
struct rxe_dev *found = NULL;
spin_lock_bh(&dev_list_lock);
list_for_each_entry(rxe, &rxe_dev_list, list) {
if (!strcmp(name, rxe->ib_dev.name)) {
found = rxe;
break;
}
}
spin_unlock_bh(&dev_list_lock);
return found;
}
struct rxe_recv_sockets recv_sockets;
static __be64 rxe_mac_to_eui64(struct net_device *ndev)
{
unsigned char *mac_addr = ndev->dev_addr;
__be64 eui64;
unsigned char *dst = (unsigned char *)&eui64;
dst[0] = mac_addr[0] ^ 2;
dst[1] = mac_addr[1];
dst[2] = mac_addr[2];
dst[3] = 0xff;
dst[4] = 0xfe;
dst[5] = mac_addr[3];
dst[6] = mac_addr[4];
dst[7] = mac_addr[5];
return eui64;
}
static __be64 node_guid(struct rxe_dev *rxe)
{
return rxe_mac_to_eui64(rxe->ndev);
}
static __be64 port_guid(struct rxe_dev *rxe)
{
return rxe_mac_to_eui64(rxe->ndev);
}
static struct device *dma_device(struct rxe_dev *rxe)
{
struct net_device *ndev;
ndev = rxe->ndev;
if (ndev->priv_flags & IFF_802_1Q_VLAN)
ndev = vlan_dev_real_dev(ndev);
return ndev->dev.parent;
}
static int mcast_add(struct rxe_dev *rxe, union ib_gid *mgid)
{
int err;
unsigned char ll_addr[ETH_ALEN];
ipv6_eth_mc_map((struct in6_addr *)mgid->raw, ll_addr);
err = dev_mc_add(rxe->ndev, ll_addr);
return err;
}
static int mcast_delete(struct rxe_dev *rxe, union ib_gid *mgid)
{
int err;
unsigned char ll_addr[ETH_ALEN];
ipv6_eth_mc_map((struct in6_addr *)mgid->raw, ll_addr);
err = dev_mc_del(rxe->ndev, ll_addr);
return err;
}
static struct dst_entry *rxe_find_route4(struct net_device *ndev,
struct in_addr *saddr,
struct in_addr *daddr)
{
struct rtable *rt;
struct flowi4 fl = { { 0 } };
memset(&fl, 0, sizeof(fl));
fl.flowi4_oif = ndev->ifindex;
memcpy(&fl.saddr, saddr, sizeof(*saddr));
memcpy(&fl.daddr, daddr, sizeof(*daddr));
fl.flowi4_proto = IPPROTO_UDP;
rt = ip_route_output_key(&init_net, &fl);
if (IS_ERR(rt)) {
pr_err_ratelimited("no route to %pI4\n", &daddr->s_addr);
return NULL;
}
return &rt->dst;
}
#if IS_ENABLED(CONFIG_IPV6)
static struct dst_entry *rxe_find_route6(struct net_device *ndev,
struct in6_addr *saddr,
struct in6_addr *daddr)
{
struct dst_entry *ndst;
struct flowi6 fl6 = { { 0 } };
memset(&fl6, 0, sizeof(fl6));
fl6.flowi6_oif = ndev->ifindex;
memcpy(&fl6.saddr, saddr, sizeof(*saddr));
memcpy(&fl6.daddr, daddr, sizeof(*daddr));
fl6.flowi6_proto = IPPROTO_UDP;
if (unlikely(ipv6_stub->ipv6_dst_lookup(sock_net(recv_sockets.sk6->sk),
recv_sockets.sk6->sk, &ndst, &fl6))) {
pr_err_ratelimited("no route to %pI6\n", daddr);
goto put;
}
if (unlikely(ndst->error)) {
pr_err("no route to %pI6\n", daddr);
goto put;
}
return ndst;
put:
dst_release(ndst);
return NULL;
}
#else
static struct dst_entry *rxe_find_route6(struct net_device *ndev,
struct in6_addr *saddr,
struct in6_addr *daddr)
{
return NULL;
}
#endif
static int rxe_udp_encap_recv(struct sock *sk, struct sk_buff *skb)
{
struct udphdr *udph;
struct net_device *ndev = skb->dev;
struct rxe_dev *rxe = net_to_rxe(ndev);
struct rxe_pkt_info *pkt = SKB_TO_PKT(skb);
if (!rxe)
goto drop;
if (skb_linearize(skb)) {
pr_err("skb_linearize failed\n");
goto drop;
}
udph = udp_hdr(skb);
pkt->rxe = rxe;
pkt->port_num = 1;
pkt->hdr = (u8 *)(udph + 1);
pkt->mask = RXE_GRH_MASK;
pkt->paylen = be16_to_cpu(udph->len) - sizeof(*udph);
return rxe_rcv(skb);
drop:
kfree_skb(skb);
return 0;
}
static struct socket *rxe_setup_udp_tunnel(struct net *net, __be16 port,
bool ipv6)
{
int err;
struct socket *sock;
struct udp_port_cfg udp_cfg;
struct udp_tunnel_sock_cfg tnl_cfg;
memset(&udp_cfg, 0, sizeof(udp_cfg));
if (ipv6) {
udp_cfg.family = AF_INET6;
udp_cfg.ipv6_v6only = 1;
} else {
udp_cfg.family = AF_INET;
}
udp_cfg.local_udp_port = port;
/* Create UDP socket */
err = udp_sock_create(net, &udp_cfg, &sock);
if (err < 0) {
pr_err("failed to create udp socket. err = %d\n", err);
return ERR_PTR(err);
}
tnl_cfg.sk_user_data = NULL;
tnl_cfg.encap_type = 1;
tnl_cfg.encap_rcv = rxe_udp_encap_recv;
tnl_cfg.encap_destroy = NULL;
/* Setup UDP tunnel */
setup_udp_tunnel_sock(net, sock, &tnl_cfg);
return sock;
}
static void rxe_release_udp_tunnel(struct socket *sk)
{
udp_tunnel_sock_release(sk);
}
static void prepare_udp_hdr(struct sk_buff *skb, __be16 src_port,
__be16 dst_port)
{
struct udphdr *udph;
__skb_push(skb, sizeof(*udph));
skb_reset_transport_header(skb);
udph = udp_hdr(skb);
udph->dest = dst_port;
udph->source = src_port;
udph->len = htons(skb->len);
udph->check = 0;
}
static void prepare_ipv4_hdr(struct dst_entry *dst, struct sk_buff *skb,
__be32 saddr, __be32 daddr, __u8 proto,
__u8 tos, __u8 ttl, __be16 df, bool xnet)
{
struct iphdr *iph;
skb_scrub_packet(skb, xnet);
skb_clear_hash(skb);
skb_dst_set(skb, dst);
memset(IPCB(skb), 0, sizeof(*IPCB(skb)));
skb_push(skb, sizeof(struct iphdr));
skb_reset_network_header(skb);
iph = ip_hdr(skb);
iph->version = IPVERSION;
iph->ihl = sizeof(struct iphdr) >> 2;
iph->frag_off = df;
iph->protocol = proto;
iph->tos = tos;
iph->daddr = daddr;
iph->saddr = saddr;
iph->ttl = ttl;
__ip_select_ident(dev_net(dst->dev), iph,
skb_shinfo(skb)->gso_segs ?: 1);
iph->tot_len = htons(skb->len);
ip_send_check(iph);
}
static void prepare_ipv6_hdr(struct dst_entry *dst, struct sk_buff *skb,
struct in6_addr *saddr, struct in6_addr *daddr,
__u8 proto, __u8 prio, __u8 ttl)
{
struct ipv6hdr *ip6h;
memset(&(IPCB(skb)->opt), 0, sizeof(IPCB(skb)->opt));
IPCB(skb)->flags &= ~(IPSKB_XFRM_TUNNEL_SIZE | IPSKB_XFRM_TRANSFORMED
| IPSKB_REROUTED);
skb_dst_set(skb, dst);
__skb_push(skb, sizeof(*ip6h));
skb_reset_network_header(skb);
ip6h = ipv6_hdr(skb);
ip6_flow_hdr(ip6h, prio, htonl(0));
ip6h->payload_len = htons(skb->len);
ip6h->nexthdr = proto;
ip6h->hop_limit = ttl;
ip6h->daddr = *daddr;
ip6h->saddr = *saddr;
ip6h->payload_len = htons(skb->len - sizeof(*ip6h));
}
static int prepare4(struct rxe_dev *rxe, struct sk_buff *skb, struct rxe_av *av)
{
struct dst_entry *dst;
bool xnet = false;
__be16 df = htons(IP_DF);
struct in_addr *saddr = &av->sgid_addr._sockaddr_in.sin_addr;
struct in_addr *daddr = &av->dgid_addr._sockaddr_in.sin_addr;
struct rxe_pkt_info *pkt = SKB_TO_PKT(skb);
dst = rxe_find_route4(rxe->ndev, saddr, daddr);
if (!dst) {
pr_err("Host not reachable\n");
return -EHOSTUNREACH;
}
if (!memcmp(saddr, daddr, sizeof(*daddr)))
pkt->mask |= RXE_LOOPBACK_MASK;
prepare_udp_hdr(skb, htons(RXE_ROCE_V2_SPORT),
htons(ROCE_V2_UDP_DPORT));
prepare_ipv4_hdr(dst, skb, saddr->s_addr, daddr->s_addr, IPPROTO_UDP,
av->grh.traffic_class, av->grh.hop_limit, df, xnet);
return 0;
}
static int prepare6(struct rxe_dev *rxe, struct sk_buff *skb, struct rxe_av *av)
{
struct dst_entry *dst;
struct in6_addr *saddr = &av->sgid_addr._sockaddr_in6.sin6_addr;
struct in6_addr *daddr = &av->dgid_addr._sockaddr_in6.sin6_addr;
struct rxe_pkt_info *pkt = SKB_TO_PKT(skb);
dst = rxe_find_route6(rxe->ndev, saddr, daddr);
if (!dst) {
pr_err("Host not reachable\n");
return -EHOSTUNREACH;
}
if (!memcmp(saddr, daddr, sizeof(*daddr)))
pkt->mask |= RXE_LOOPBACK_MASK;
prepare_udp_hdr(skb, htons(RXE_ROCE_V2_SPORT),
htons(ROCE_V2_UDP_DPORT));
prepare_ipv6_hdr(dst, skb, saddr, daddr, IPPROTO_UDP,
av->grh.traffic_class,
av->grh.hop_limit);
return 0;
}
static int prepare(struct rxe_dev *rxe, struct rxe_pkt_info *pkt,
struct sk_buff *skb, u32 *crc)
{
int err = 0;
struct rxe_av *av = rxe_get_av(pkt);
if (av->network_type == RDMA_NETWORK_IPV4)
err = prepare4(rxe, skb, av);
else if (av->network_type == RDMA_NETWORK_IPV6)
err = prepare6(rxe, skb, av);
*crc = rxe_icrc_hdr(pkt, skb);
return err;
}
static void rxe_skb_tx_dtor(struct sk_buff *skb)
{
struct sock *sk = skb->sk;
struct rxe_qp *qp = sk->sk_user_data;
int skb_out = atomic_dec_return(&qp->skb_out);
if (unlikely(qp->need_req_skb &&
skb_out < RXE_INFLIGHT_SKBS_PER_QP_LOW))
rxe_run_task(&qp->req.task, 1);
}
static int send(struct rxe_dev *rxe, struct rxe_pkt_info *pkt,
struct sk_buff *skb)
{
struct sk_buff *nskb;
struct rxe_av *av;
int err;
av = rxe_get_av(pkt);
nskb = skb_clone(skb, GFP_ATOMIC);
if (!nskb)
return -ENOMEM;
nskb->destructor = rxe_skb_tx_dtor;
nskb->sk = pkt->qp->sk->sk;
if (av->network_type == RDMA_NETWORK_IPV4) {
err = ip_local_out(dev_net(skb_dst(skb)->dev), nskb->sk, nskb);
} else if (av->network_type == RDMA_NETWORK_IPV6) {
err = ip6_local_out(dev_net(skb_dst(skb)->dev), nskb->sk, nskb);
} else {
pr_err("Unknown layer 3 protocol: %d\n", av->network_type);
kfree_skb(nskb);
return -EINVAL;
}
if (unlikely(net_xmit_eval(err))) {
pr_debug("error sending packet: %d\n", err);
return -EAGAIN;
}
kfree_skb(skb);
return 0;
}
static int loopback(struct sk_buff *skb)
{
return rxe_rcv(skb);
}
static inline int addr_same(struct rxe_dev *rxe, struct rxe_av *av)
{
return rxe->port.port_guid == av->grh.dgid.global.interface_id;
}
static struct sk_buff *init_packet(struct rxe_dev *rxe, struct rxe_av *av,
int paylen, struct rxe_pkt_info *pkt)
{
unsigned int hdr_len;
struct sk_buff *skb;
if (av->network_type == RDMA_NETWORK_IPV4)
hdr_len = ETH_HLEN + sizeof(struct udphdr) +
sizeof(struct iphdr);
else
hdr_len = ETH_HLEN + sizeof(struct udphdr) +
sizeof(struct ipv6hdr);
skb = alloc_skb(paylen + hdr_len + LL_RESERVED_SPACE(rxe->ndev),
GFP_ATOMIC);
if (unlikely(!skb))
return NULL;
skb_reserve(skb, hdr_len + LL_RESERVED_SPACE(rxe->ndev));
skb->dev = rxe->ndev;
if (av->network_type == RDMA_NETWORK_IPV4)
skb->protocol = htons(ETH_P_IP);
else
skb->protocol = htons(ETH_P_IPV6);
pkt->rxe = rxe;
pkt->port_num = 1;
pkt->hdr = skb_put(skb, paylen);
pkt->mask |= RXE_GRH_MASK;
memset(pkt->hdr, 0, paylen);
return skb;
}
/*
* this is required by rxe_cfg to match rxe devices in
* /sys/class/infiniband up with their underlying ethernet devices
*/
static char *parent_name(struct rxe_dev *rxe, unsigned int port_num)
{
return rxe->ndev->name;
}
static enum rdma_link_layer link_layer(struct rxe_dev *rxe,
unsigned int port_num)
{
return IB_LINK_LAYER_ETHERNET;
}
static struct rxe_ifc_ops ifc_ops = {
.node_guid = node_guid,
.port_guid = port_guid,
.dma_device = dma_device,
.mcast_add = mcast_add,
.mcast_delete = mcast_delete,
.prepare = prepare,
.send = send,
.loopback = loopback,
.init_packet = init_packet,
.parent_name = parent_name,
.link_layer = link_layer,
};
struct rxe_dev *rxe_net_add(struct net_device *ndev)
{
int err;
struct rxe_dev *rxe = NULL;
rxe = (struct rxe_dev *)ib_alloc_device(sizeof(*rxe));
if (!rxe)
return NULL;
rxe->ifc_ops = &ifc_ops;
rxe->ndev = ndev;
err = rxe_add(rxe, ndev->mtu);
if (err) {
ib_dealloc_device(&rxe->ib_dev);
return NULL;
}
spin_lock_bh(&dev_list_lock);
list_add_tail(&rxe_dev_list, &rxe->list);
spin_unlock_bh(&dev_list_lock);
return rxe;
}
void rxe_remove_all(void)
{
spin_lock_bh(&dev_list_lock);
while (!list_empty(&rxe_dev_list)) {
struct rxe_dev *rxe =
list_first_entry(&rxe_dev_list, struct rxe_dev, list);
list_del(&rxe->list);
spin_unlock_bh(&dev_list_lock);
rxe_remove(rxe);
spin_lock_bh(&dev_list_lock);
}
spin_unlock_bh(&dev_list_lock);
}
EXPORT_SYMBOL(rxe_remove_all);
static void rxe_port_event(struct rxe_dev *rxe,
enum ib_event_type event)
{
struct ib_event ev;
ev.device = &rxe->ib_dev;
ev.element.port_num = 1;
ev.event = event;
ib_dispatch_event(&ev);
}
/* Caller must hold net_info_lock */
void rxe_port_up(struct rxe_dev *rxe)
{
struct rxe_port *port;
port = &rxe->port;
port->attr.state = IB_PORT_ACTIVE;
port->attr.phys_state = IB_PHYS_STATE_LINK_UP;
rxe_port_event(rxe, IB_EVENT_PORT_ACTIVE);
pr_info("rxe: set %s active\n", rxe->ib_dev.name);
return;
}
/* Caller must hold net_info_lock */
void rxe_port_down(struct rxe_dev *rxe)
{
struct rxe_port *port;
port = &rxe->port;
port->attr.state = IB_PORT_DOWN;
port->attr.phys_state = IB_PHYS_STATE_LINK_DOWN;
rxe_port_event(rxe, IB_EVENT_PORT_ERR);
pr_info("rxe: set %s down\n", rxe->ib_dev.name);
return;
}
static int rxe_notify(struct notifier_block *not_blk,
unsigned long event,
void *arg)
{
struct net_device *ndev = netdev_notifier_info_to_dev(arg);
struct rxe_dev *rxe = net_to_rxe(ndev);
if (!rxe)
goto out;
switch (event) {
case NETDEV_UNREGISTER:
list_del(&rxe->list);
rxe_remove(rxe);
break;
case NETDEV_UP:
rxe_port_up(rxe);
break;
case NETDEV_DOWN:
rxe_port_down(rxe);
break;
case NETDEV_CHANGEMTU:
pr_info("rxe: %s changed mtu to %d\n", ndev->name, ndev->mtu);
rxe_set_mtu(rxe, ndev->mtu);
break;
case NETDEV_REBOOT:
case NETDEV_CHANGE:
case NETDEV_GOING_DOWN:
case NETDEV_CHANGEADDR:
case NETDEV_CHANGENAME:
case NETDEV_FEAT_CHANGE:
default:
pr_info("rxe: ignoring netdev event = %ld for %s\n",
event, ndev->name);
break;
}
out:
return NOTIFY_OK;
}
static struct notifier_block rxe_net_notifier = {
.notifier_call = rxe_notify,
};
int rxe_net_init(void)
{
int err;
spin_lock_init(&dev_list_lock);
recv_sockets.sk6 = rxe_setup_udp_tunnel(&init_net,
htons(ROCE_V2_UDP_DPORT), true);
if (IS_ERR(recv_sockets.sk6)) {
recv_sockets.sk6 = NULL;
pr_err("rxe: Failed to create IPv6 UDP tunnel\n");
return -1;
}
recv_sockets.sk4 = rxe_setup_udp_tunnel(&init_net,
htons(ROCE_V2_UDP_DPORT), false);
if (IS_ERR(recv_sockets.sk4)) {
rxe_release_udp_tunnel(recv_sockets.sk6);
recv_sockets.sk4 = NULL;
recv_sockets.sk6 = NULL;
pr_err("rxe: Failed to create IPv4 UDP tunnel\n");
return -1;
}
err = register_netdevice_notifier(&rxe_net_notifier);
if (err) {
rxe_release_udp_tunnel(recv_sockets.sk6);
rxe_release_udp_tunnel(recv_sockets.sk4);
pr_err("rxe: Failed to rigister netdev notifier\n");
}
return err;
}
void rxe_net_exit(void)
{
if (recv_sockets.sk6)
rxe_release_udp_tunnel(recv_sockets.sk6);
if (recv_sockets.sk4)
rxe_release_udp_tunnel(recv_sockets.sk4);
unregister_netdevice_notifier(&rxe_net_notifier);
}

View File

@ -0,0 +1,53 @@
/*
* Copyright (c) 2016 Mellanox Technologies Ltd. All rights reserved.
* Copyright (c) 2015 System Fabric Works, 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 RXE_NET_H
#define RXE_NET_H
#include <net/sock.h>
#include <net/if_inet6.h>
#include <linux/module.h>
struct rxe_recv_sockets {
struct socket *sk4;
struct socket *sk6;
};
extern struct rxe_recv_sockets recv_sockets;
struct rxe_dev *rxe_net_add(struct net_device *ndev);
int rxe_net_init(void);
void rxe_net_exit(void);
#endif /* RXE_NET_H */

View File

@ -0,0 +1,961 @@
/*
* Copyright (c) 2016 Mellanox Technologies Ltd. All rights reserved.
* Copyright (c) 2015 System Fabric Works, Inc. All rights reserved.
*
* This software is available to you under a choice of one of two
* licenses. You may choose to be licensed under the terms of the GNU
* General Public License (GPL) Version 2, available from the file
* COPYING in the main directory of this source tree, or the
* OpenIB.org BSD license below:
*
* Redistribution and use in source and binary forms, with or
* without modification, are permitted provided that the following
* conditions are met:
*
* - Redistributions of source code must retain the above
* copyright notice, this list of conditions and the following
* disclaimer.
*
* - Redistributions in binary form must reproduce the above
* copyright notice, this list of conditions and the following
* disclaimer in the documentation and/or other materials
* provided with the distribution.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
* NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
* BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
* ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
* CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
* SOFTWARE.
*/
#include <rdma/ib_pack.h>
#include "rxe_opcode.h"
#include "rxe_hdr.h"
/* useful information about work request opcodes and pkt opcodes in
* table form
*/
struct rxe_wr_opcode_info rxe_wr_opcode_info[] = {
[IB_WR_RDMA_WRITE] = {
.name = "IB_WR_RDMA_WRITE",
.mask = {
[IB_QPT_RC] = WR_INLINE_MASK | WR_WRITE_MASK,
[IB_QPT_UC] = WR_INLINE_MASK | WR_WRITE_MASK,
},
},
[IB_WR_RDMA_WRITE_WITH_IMM] = {
.name = "IB_WR_RDMA_WRITE_WITH_IMM",
.mask = {
[IB_QPT_RC] = WR_INLINE_MASK | WR_WRITE_MASK,
[IB_QPT_UC] = WR_INLINE_MASK | WR_WRITE_MASK,
},
},
[IB_WR_SEND] = {
.name = "IB_WR_SEND",
.mask = {
[IB_QPT_SMI] = WR_INLINE_MASK | WR_SEND_MASK,
[IB_QPT_GSI] = WR_INLINE_MASK | WR_SEND_MASK,
[IB_QPT_RC] = WR_INLINE_MASK | WR_SEND_MASK,
[IB_QPT_UC] = WR_INLINE_MASK | WR_SEND_MASK,
[IB_QPT_UD] = WR_INLINE_MASK | WR_SEND_MASK,
},
},
[IB_WR_SEND_WITH_IMM] = {
.name = "IB_WR_SEND_WITH_IMM",
.mask = {
[IB_QPT_SMI] = WR_INLINE_MASK | WR_SEND_MASK,
[IB_QPT_GSI] = WR_INLINE_MASK | WR_SEND_MASK,
[IB_QPT_RC] = WR_INLINE_MASK | WR_SEND_MASK,
[IB_QPT_UC] = WR_INLINE_MASK | WR_SEND_MASK,
[IB_QPT_UD] = WR_INLINE_MASK | WR_SEND_MASK,
},
},
[IB_WR_RDMA_READ] = {
.name = "IB_WR_RDMA_READ",
.mask = {
[IB_QPT_RC] = WR_READ_MASK,
},
},
[IB_WR_ATOMIC_CMP_AND_SWP] = {
.name = "IB_WR_ATOMIC_CMP_AND_SWP",
.mask = {
[IB_QPT_RC] = WR_ATOMIC_MASK,
},
},
[IB_WR_ATOMIC_FETCH_AND_ADD] = {
.name = "IB_WR_ATOMIC_FETCH_AND_ADD",
.mask = {
[IB_QPT_RC] = WR_ATOMIC_MASK,
},
},
[IB_WR_LSO] = {
.name = "IB_WR_LSO",
.mask = {
/* not supported */
},
},
[IB_WR_SEND_WITH_INV] = {
.name = "IB_WR_SEND_WITH_INV",
.mask = {
[IB_QPT_RC] = WR_INLINE_MASK | WR_SEND_MASK,
[IB_QPT_UC] = WR_INLINE_MASK | WR_SEND_MASK,
[IB_QPT_UD] = WR_INLINE_MASK | WR_SEND_MASK,
},
},
[IB_WR_RDMA_READ_WITH_INV] = {
.name = "IB_WR_RDMA_READ_WITH_INV",
.mask = {
[IB_QPT_RC] = WR_READ_MASK,
},
},
[IB_WR_LOCAL_INV] = {
.name = "IB_WR_LOCAL_INV",
.mask = {
[IB_QPT_RC] = WR_REG_MASK,
},
},
[IB_WR_REG_MR] = {
.name = "IB_WR_REG_MR",
.mask = {
[IB_QPT_RC] = WR_REG_MASK,
},
},
};
struct rxe_opcode_info rxe_opcode[RXE_NUM_OPCODE] = {
[IB_OPCODE_RC_SEND_FIRST] = {
.name = "IB_OPCODE_RC_SEND_FIRST",
.mask = RXE_PAYLOAD_MASK | RXE_REQ_MASK | RXE_RWR_MASK
| RXE_SEND_MASK | RXE_START_MASK,
.length = RXE_BTH_BYTES,
.offset = {
[RXE_BTH] = 0,
[RXE_PAYLOAD] = RXE_BTH_BYTES,
}
},
[IB_OPCODE_RC_SEND_MIDDLE] = {
.name = "IB_OPCODE_RC_SEND_MIDDLE]",
.mask = RXE_PAYLOAD_MASK | RXE_REQ_MASK | RXE_SEND_MASK
| RXE_MIDDLE_MASK,
.length = RXE_BTH_BYTES,
.offset = {
[RXE_BTH] = 0,
[RXE_PAYLOAD] = RXE_BTH_BYTES,
}
},
[IB_OPCODE_RC_SEND_LAST] = {
.name = "IB_OPCODE_RC_SEND_LAST",
.mask = RXE_PAYLOAD_MASK | RXE_REQ_MASK | RXE_COMP_MASK
| RXE_SEND_MASK | RXE_END_MASK,
.length = RXE_BTH_BYTES,
.offset = {
[RXE_BTH] = 0,
[RXE_PAYLOAD] = RXE_BTH_BYTES,
}
},
[IB_OPCODE_RC_SEND_LAST_WITH_IMMEDIATE] = {
.name = "IB_OPCODE_RC_SEND_LAST_WITH_IMMEDIATE",
.mask = RXE_IMMDT_MASK | RXE_PAYLOAD_MASK | RXE_REQ_MASK
| RXE_COMP_MASK | RXE_SEND_MASK | RXE_END_MASK,
.length = RXE_BTH_BYTES + RXE_IMMDT_BYTES,
.offset = {
[RXE_BTH] = 0,
[RXE_IMMDT] = RXE_BTH_BYTES,
[RXE_PAYLOAD] = RXE_BTH_BYTES
+ RXE_IMMDT_BYTES,
}
},
[IB_OPCODE_RC_SEND_ONLY] = {
.name = "IB_OPCODE_RC_SEND_ONLY",
.mask = RXE_PAYLOAD_MASK | RXE_REQ_MASK | RXE_COMP_MASK
| RXE_RWR_MASK | RXE_SEND_MASK
| RXE_START_MASK | RXE_END_MASK,
.length = RXE_BTH_BYTES,
.offset = {
[RXE_BTH] = 0,
[RXE_PAYLOAD] = RXE_BTH_BYTES,
}
},
[IB_OPCODE_RC_SEND_ONLY_WITH_IMMEDIATE] = {
.name = "IB_OPCODE_RC_SEND_ONLY_WITH_IMMEDIATE",
.mask = RXE_IMMDT_MASK | RXE_PAYLOAD_MASK | RXE_REQ_MASK
| RXE_COMP_MASK | RXE_RWR_MASK | RXE_SEND_MASK
| RXE_START_MASK | RXE_END_MASK,
.length = RXE_BTH_BYTES + RXE_IMMDT_BYTES,
.offset = {
[RXE_BTH] = 0,
[RXE_IMMDT] = RXE_BTH_BYTES,
[RXE_PAYLOAD] = RXE_BTH_BYTES
+ RXE_IMMDT_BYTES,
}
},
[IB_OPCODE_RC_RDMA_WRITE_FIRST] = {
.name = "IB_OPCODE_RC_RDMA_WRITE_FIRST",
.mask = RXE_RETH_MASK | RXE_PAYLOAD_MASK | RXE_REQ_MASK
| RXE_WRITE_MASK | RXE_START_MASK,
.length = RXE_BTH_BYTES + RXE_RETH_BYTES,
.offset = {
[RXE_BTH] = 0,
[RXE_RETH] = RXE_BTH_BYTES,
[RXE_PAYLOAD] = RXE_BTH_BYTES
+ RXE_RETH_BYTES,
}
},
[IB_OPCODE_RC_RDMA_WRITE_MIDDLE] = {
.name = "IB_OPCODE_RC_RDMA_WRITE_MIDDLE",
.mask = RXE_PAYLOAD_MASK | RXE_REQ_MASK | RXE_WRITE_MASK
| RXE_MIDDLE_MASK,
.length = RXE_BTH_BYTES,
.offset = {
[RXE_BTH] = 0,
[RXE_PAYLOAD] = RXE_BTH_BYTES,
}
},
[IB_OPCODE_RC_RDMA_WRITE_LAST] = {
.name = "IB_OPCODE_RC_RDMA_WRITE_LAST",
.mask = RXE_PAYLOAD_MASK | RXE_REQ_MASK | RXE_WRITE_MASK
| RXE_END_MASK,
.length = RXE_BTH_BYTES,
.offset = {
[RXE_BTH] = 0,
[RXE_PAYLOAD] = RXE_BTH_BYTES,
}
},
[IB_OPCODE_RC_RDMA_WRITE_LAST_WITH_IMMEDIATE] = {
.name = "IB_OPCODE_RC_RDMA_WRITE_LAST_WITH_IMMEDIATE",
.mask = RXE_IMMDT_MASK | RXE_PAYLOAD_MASK | RXE_REQ_MASK
| RXE_WRITE_MASK | RXE_COMP_MASK | RXE_RWR_MASK
| RXE_END_MASK,
.length = RXE_BTH_BYTES + RXE_IMMDT_BYTES,
.offset = {
[RXE_BTH] = 0,
[RXE_IMMDT] = RXE_BTH_BYTES,
[RXE_PAYLOAD] = RXE_BTH_BYTES
+ RXE_IMMDT_BYTES,
}
},
[IB_OPCODE_RC_RDMA_WRITE_ONLY] = {
.name = "IB_OPCODE_RC_RDMA_WRITE_ONLY",
.mask = RXE_RETH_MASK | RXE_PAYLOAD_MASK | RXE_REQ_MASK
| RXE_WRITE_MASK | RXE_START_MASK
| RXE_END_MASK,
.length = RXE_BTH_BYTES + RXE_RETH_BYTES,
.offset = {
[RXE_BTH] = 0,
[RXE_RETH] = RXE_BTH_BYTES,
[RXE_PAYLOAD] = RXE_BTH_BYTES
+ RXE_RETH_BYTES,
}
},
[IB_OPCODE_RC_RDMA_WRITE_ONLY_WITH_IMMEDIATE] = {
.name = "IB_OPCODE_RC_RDMA_WRITE_ONLY_WITH_IMMEDIATE",
.mask = RXE_RETH_MASK | RXE_IMMDT_MASK | RXE_PAYLOAD_MASK
| RXE_REQ_MASK | RXE_WRITE_MASK
| RXE_COMP_MASK | RXE_RWR_MASK
| RXE_START_MASK | RXE_END_MASK,
.length = RXE_BTH_BYTES + RXE_IMMDT_BYTES + RXE_RETH_BYTES,
.offset = {
[RXE_BTH] = 0,
[RXE_RETH] = RXE_BTH_BYTES,
[RXE_IMMDT] = RXE_BTH_BYTES
+ RXE_RETH_BYTES,
[RXE_PAYLOAD] = RXE_BTH_BYTES
+ RXE_RETH_BYTES
+ RXE_IMMDT_BYTES,
}
},
[IB_OPCODE_RC_RDMA_READ_REQUEST] = {
.name = "IB_OPCODE_RC_RDMA_READ_REQUEST",
.mask = RXE_RETH_MASK | RXE_REQ_MASK | RXE_READ_MASK
| RXE_START_MASK | RXE_END_MASK,
.length = RXE_BTH_BYTES + RXE_RETH_BYTES,
.offset = {
[RXE_BTH] = 0,
[RXE_RETH] = RXE_BTH_BYTES,
[RXE_PAYLOAD] = RXE_BTH_BYTES
+ RXE_RETH_BYTES,
}
},
[IB_OPCODE_RC_RDMA_READ_RESPONSE_FIRST] = {
.name = "IB_OPCODE_RC_RDMA_READ_RESPONSE_FIRST",
.mask = RXE_AETH_MASK | RXE_PAYLOAD_MASK | RXE_ACK_MASK
| RXE_START_MASK,
.length = RXE_BTH_BYTES + RXE_AETH_BYTES,
.offset = {
[RXE_BTH] = 0,
[RXE_AETH] = RXE_BTH_BYTES,
[RXE_PAYLOAD] = RXE_BTH_BYTES
+ RXE_AETH_BYTES,
}
},
[IB_OPCODE_RC_RDMA_READ_RESPONSE_MIDDLE] = {
.name = "IB_OPCODE_RC_RDMA_READ_RESPONSE_MIDDLE",
.mask = RXE_PAYLOAD_MASK | RXE_ACK_MASK | RXE_MIDDLE_MASK,
.length = RXE_BTH_BYTES,
.offset = {
[RXE_BTH] = 0,
[RXE_PAYLOAD] = RXE_BTH_BYTES,
}
},
[IB_OPCODE_RC_RDMA_READ_RESPONSE_LAST] = {
.name = "IB_OPCODE_RC_RDMA_READ_RESPONSE_LAST",
.mask = RXE_AETH_MASK | RXE_PAYLOAD_MASK | RXE_ACK_MASK
| RXE_END_MASK,
.length = RXE_BTH_BYTES + RXE_AETH_BYTES,
.offset = {
[RXE_BTH] = 0,
[RXE_AETH] = RXE_BTH_BYTES,
[RXE_PAYLOAD] = RXE_BTH_BYTES
+ RXE_AETH_BYTES,
}
},
[IB_OPCODE_RC_RDMA_READ_RESPONSE_ONLY] = {
.name = "IB_OPCODE_RC_RDMA_READ_RESPONSE_ONLY",
.mask = RXE_AETH_MASK | RXE_PAYLOAD_MASK | RXE_ACK_MASK
| RXE_START_MASK | RXE_END_MASK,
.length = RXE_BTH_BYTES + RXE_AETH_BYTES,
.offset = {
[RXE_BTH] = 0,
[RXE_AETH] = RXE_BTH_BYTES,
[RXE_PAYLOAD] = RXE_BTH_BYTES
+ RXE_AETH_BYTES,
}
},
[IB_OPCODE_RC_ACKNOWLEDGE] = {
.name = "IB_OPCODE_RC_ACKNOWLEDGE",
.mask = RXE_AETH_MASK | RXE_ACK_MASK | RXE_START_MASK
| RXE_END_MASK,
.length = RXE_BTH_BYTES + RXE_AETH_BYTES,
.offset = {
[RXE_BTH] = 0,
[RXE_AETH] = RXE_BTH_BYTES,
[RXE_PAYLOAD] = RXE_BTH_BYTES
+ RXE_AETH_BYTES,
}
},
[IB_OPCODE_RC_ATOMIC_ACKNOWLEDGE] = {
.name = "IB_OPCODE_RC_ATOMIC_ACKNOWLEDGE",
.mask = RXE_AETH_MASK | RXE_ATMACK_MASK | RXE_ACK_MASK
| RXE_START_MASK | RXE_END_MASK,
.length = RXE_BTH_BYTES + RXE_ATMACK_BYTES + RXE_AETH_BYTES,
.offset = {
[RXE_BTH] = 0,
[RXE_AETH] = RXE_BTH_BYTES,
[RXE_ATMACK] = RXE_BTH_BYTES
+ RXE_AETH_BYTES,
[RXE_PAYLOAD] = RXE_BTH_BYTES
+ RXE_ATMACK_BYTES + RXE_AETH_BYTES,
}
},
[IB_OPCODE_RC_COMPARE_SWAP] = {
.name = "IB_OPCODE_RC_COMPARE_SWAP",
.mask = RXE_ATMETH_MASK | RXE_REQ_MASK | RXE_ATOMIC_MASK
| RXE_START_MASK | RXE_END_MASK,
.length = RXE_BTH_BYTES + RXE_ATMETH_BYTES,
.offset = {
[RXE_BTH] = 0,
[RXE_ATMETH] = RXE_BTH_BYTES,
[RXE_PAYLOAD] = RXE_BTH_BYTES
+ RXE_ATMETH_BYTES,
}
},
[IB_OPCODE_RC_FETCH_ADD] = {
.name = "IB_OPCODE_RC_FETCH_ADD",
.mask = RXE_ATMETH_MASK | RXE_REQ_MASK | RXE_ATOMIC_MASK
| RXE_START_MASK | RXE_END_MASK,
.length = RXE_BTH_BYTES + RXE_ATMETH_BYTES,
.offset = {
[RXE_BTH] = 0,
[RXE_ATMETH] = RXE_BTH_BYTES,
[RXE_PAYLOAD] = RXE_BTH_BYTES
+ RXE_ATMETH_BYTES,
}
},
[IB_OPCODE_RC_SEND_LAST_WITH_INVALIDATE] = {
.name = "IB_OPCODE_RC_SEND_LAST_WITH_INVALIDATE",
.mask = RXE_IETH_MASK | RXE_PAYLOAD_MASK | RXE_REQ_MASK
| RXE_COMP_MASK | RXE_SEND_MASK | RXE_END_MASK,
.length = RXE_BTH_BYTES + RXE_IETH_BYTES,
.offset = {
[RXE_BTH] = 0,
[RXE_IETH] = RXE_BTH_BYTES,
[RXE_PAYLOAD] = RXE_BTH_BYTES
+ RXE_IETH_BYTES,
}
},
[IB_OPCODE_RC_SEND_ONLY_WITH_INVALIDATE] = {
.name = "IB_OPCODE_RC_SEND_ONLY_INV",
.mask = RXE_IETH_MASK | RXE_PAYLOAD_MASK | RXE_REQ_MASK
| RXE_COMP_MASK | RXE_RWR_MASK | RXE_SEND_MASK
| RXE_END_MASK,
.length = RXE_BTH_BYTES + RXE_IETH_BYTES,
.offset = {
[RXE_BTH] = 0,
[RXE_IETH] = RXE_BTH_BYTES,
[RXE_PAYLOAD] = RXE_BTH_BYTES
+ RXE_IETH_BYTES,
}
},
/* UC */
[IB_OPCODE_UC_SEND_FIRST] = {
.name = "IB_OPCODE_UC_SEND_FIRST",
.mask = RXE_PAYLOAD_MASK | RXE_REQ_MASK | RXE_RWR_MASK
| RXE_SEND_MASK | RXE_START_MASK,
.length = RXE_BTH_BYTES,
.offset = {
[RXE_BTH] = 0,
[RXE_PAYLOAD] = RXE_BTH_BYTES,
}
},
[IB_OPCODE_UC_SEND_MIDDLE] = {
.name = "IB_OPCODE_UC_SEND_MIDDLE",
.mask = RXE_PAYLOAD_MASK | RXE_REQ_MASK | RXE_SEND_MASK
| RXE_MIDDLE_MASK,
.length = RXE_BTH_BYTES,
.offset = {
[RXE_BTH] = 0,
[RXE_PAYLOAD] = RXE_BTH_BYTES,
}
},
[IB_OPCODE_UC_SEND_LAST] = {
.name = "IB_OPCODE_UC_SEND_LAST",
.mask = RXE_PAYLOAD_MASK | RXE_REQ_MASK | RXE_COMP_MASK
| RXE_SEND_MASK | RXE_END_MASK,
.length = RXE_BTH_BYTES,
.offset = {
[RXE_BTH] = 0,
[RXE_PAYLOAD] = RXE_BTH_BYTES,
}
},
[IB_OPCODE_UC_SEND_LAST_WITH_IMMEDIATE] = {
.name = "IB_OPCODE_UC_SEND_LAST_WITH_IMMEDIATE",
.mask = RXE_IMMDT_MASK | RXE_PAYLOAD_MASK | RXE_REQ_MASK
| RXE_COMP_MASK | RXE_SEND_MASK | RXE_END_MASK,
.length = RXE_BTH_BYTES + RXE_IMMDT_BYTES,
.offset = {
[RXE_BTH] = 0,
[RXE_IMMDT] = RXE_BTH_BYTES,
[RXE_PAYLOAD] = RXE_BTH_BYTES
+ RXE_IMMDT_BYTES,
}
},
[IB_OPCODE_UC_SEND_ONLY] = {
.name = "IB_OPCODE_UC_SEND_ONLY",
.mask = RXE_PAYLOAD_MASK | RXE_REQ_MASK | RXE_COMP_MASK
| RXE_RWR_MASK | RXE_SEND_MASK
| RXE_START_MASK | RXE_END_MASK,
.length = RXE_BTH_BYTES,
.offset = {
[RXE_BTH] = 0,
[RXE_PAYLOAD] = RXE_BTH_BYTES,
}
},
[IB_OPCODE_UC_SEND_ONLY_WITH_IMMEDIATE] = {
.name = "IB_OPCODE_UC_SEND_ONLY_WITH_IMMEDIATE",
.mask = RXE_IMMDT_MASK | RXE_PAYLOAD_MASK | RXE_REQ_MASK
| RXE_COMP_MASK | RXE_RWR_MASK | RXE_SEND_MASK
| RXE_START_MASK | RXE_END_MASK,
.length = RXE_BTH_BYTES + RXE_IMMDT_BYTES,
.offset = {
[RXE_BTH] = 0,
[RXE_IMMDT] = RXE_BTH_BYTES,
[RXE_PAYLOAD] = RXE_BTH_BYTES
+ RXE_IMMDT_BYTES,
}
},
[IB_OPCODE_UC_RDMA_WRITE_FIRST] = {
.name = "IB_OPCODE_UC_RDMA_WRITE_FIRST",
.mask = RXE_RETH_MASK | RXE_PAYLOAD_MASK | RXE_REQ_MASK
| RXE_WRITE_MASK | RXE_START_MASK,
.length = RXE_BTH_BYTES + RXE_RETH_BYTES,
.offset = {
[RXE_BTH] = 0,
[RXE_RETH] = RXE_BTH_BYTES,
[RXE_PAYLOAD] = RXE_BTH_BYTES
+ RXE_RETH_BYTES,
}
},
[IB_OPCODE_UC_RDMA_WRITE_MIDDLE] = {
.name = "IB_OPCODE_UC_RDMA_WRITE_MIDDLE",
.mask = RXE_PAYLOAD_MASK | RXE_REQ_MASK | RXE_WRITE_MASK
| RXE_MIDDLE_MASK,
.length = RXE_BTH_BYTES,
.offset = {
[RXE_BTH] = 0,
[RXE_PAYLOAD] = RXE_BTH_BYTES,
}
},
[IB_OPCODE_UC_RDMA_WRITE_LAST] = {
.name = "IB_OPCODE_UC_RDMA_WRITE_LAST",
.mask = RXE_PAYLOAD_MASK | RXE_REQ_MASK | RXE_WRITE_MASK
| RXE_END_MASK,
.length = RXE_BTH_BYTES,
.offset = {
[RXE_BTH] = 0,
[RXE_PAYLOAD] = RXE_BTH_BYTES,
}
},
[IB_OPCODE_UC_RDMA_WRITE_LAST_WITH_IMMEDIATE] = {
.name = "IB_OPCODE_UC_RDMA_WRITE_LAST_WITH_IMMEDIATE",
.mask = RXE_IMMDT_MASK | RXE_PAYLOAD_MASK | RXE_REQ_MASK
| RXE_WRITE_MASK | RXE_COMP_MASK | RXE_RWR_MASK
| RXE_END_MASK,
.length = RXE_BTH_BYTES + RXE_IMMDT_BYTES,
.offset = {
[RXE_BTH] = 0,
[RXE_IMMDT] = RXE_BTH_BYTES,
[RXE_PAYLOAD] = RXE_BTH_BYTES
+ RXE_IMMDT_BYTES,
}
},
[IB_OPCODE_UC_RDMA_WRITE_ONLY] = {
.name = "IB_OPCODE_UC_RDMA_WRITE_ONLY",
.mask = RXE_RETH_MASK | RXE_PAYLOAD_MASK | RXE_REQ_MASK
| RXE_WRITE_MASK | RXE_START_MASK
| RXE_END_MASK,
.length = RXE_BTH_BYTES + RXE_RETH_BYTES,
.offset = {
[RXE_BTH] = 0,
[RXE_RETH] = RXE_BTH_BYTES,
[RXE_PAYLOAD] = RXE_BTH_BYTES
+ RXE_RETH_BYTES,
}
},
[IB_OPCODE_UC_RDMA_WRITE_ONLY_WITH_IMMEDIATE] = {
.name = "IB_OPCODE_UC_RDMA_WRITE_ONLY_WITH_IMMEDIATE",
.mask = RXE_RETH_MASK | RXE_IMMDT_MASK | RXE_PAYLOAD_MASK
| RXE_REQ_MASK | RXE_WRITE_MASK
| RXE_COMP_MASK | RXE_RWR_MASK
| RXE_START_MASK | RXE_END_MASK,
.length = RXE_BTH_BYTES + RXE_IMMDT_BYTES + RXE_RETH_BYTES,
.offset = {
[RXE_BTH] = 0,
[RXE_RETH] = RXE_BTH_BYTES,
[RXE_IMMDT] = RXE_BTH_BYTES
+ RXE_RETH_BYTES,
[RXE_PAYLOAD] = RXE_BTH_BYTES
+ RXE_RETH_BYTES
+ RXE_IMMDT_BYTES,
}
},
/* RD */
[IB_OPCODE_RD_SEND_FIRST] = {
.name = "IB_OPCODE_RD_SEND_FIRST",
.mask = RXE_RDETH_MASK | RXE_DETH_MASK | RXE_PAYLOAD_MASK
| RXE_REQ_MASK | RXE_RWR_MASK | RXE_SEND_MASK
| RXE_START_MASK,
.length = RXE_BTH_BYTES + RXE_DETH_BYTES + RXE_RDETH_BYTES,
.offset = {
[RXE_BTH] = 0,
[RXE_RDETH] = RXE_BTH_BYTES,
[RXE_DETH] = RXE_BTH_BYTES
+ RXE_RDETH_BYTES,
[RXE_PAYLOAD] = RXE_BTH_BYTES
+ RXE_RDETH_BYTES
+ RXE_DETH_BYTES,
}
},
[IB_OPCODE_RD_SEND_MIDDLE] = {
.name = "IB_OPCODE_RD_SEND_MIDDLE",
.mask = RXE_RDETH_MASK | RXE_DETH_MASK | RXE_PAYLOAD_MASK
| RXE_REQ_MASK | RXE_SEND_MASK
| RXE_MIDDLE_MASK,
.length = RXE_BTH_BYTES + RXE_DETH_BYTES + RXE_RDETH_BYTES,
.offset = {
[RXE_BTH] = 0,
[RXE_RDETH] = RXE_BTH_BYTES,
[RXE_DETH] = RXE_BTH_BYTES
+ RXE_RDETH_BYTES,
[RXE_PAYLOAD] = RXE_BTH_BYTES
+ RXE_RDETH_BYTES
+ RXE_DETH_BYTES,
}
},
[IB_OPCODE_RD_SEND_LAST] = {
.name = "IB_OPCODE_RD_SEND_LAST",
.mask = RXE_RDETH_MASK | RXE_DETH_MASK | RXE_PAYLOAD_MASK
| RXE_REQ_MASK | RXE_COMP_MASK | RXE_SEND_MASK
| RXE_END_MASK,
.length = RXE_BTH_BYTES + RXE_DETH_BYTES + RXE_RDETH_BYTES,
.offset = {
[RXE_BTH] = 0,
[RXE_RDETH] = RXE_BTH_BYTES,
[RXE_DETH] = RXE_BTH_BYTES
+ RXE_RDETH_BYTES,
[RXE_PAYLOAD] = RXE_BTH_BYTES
+ RXE_RDETH_BYTES
+ RXE_DETH_BYTES,
}
},
[IB_OPCODE_RD_SEND_LAST_WITH_IMMEDIATE] = {
.name = "IB_OPCODE_RD_SEND_LAST_WITH_IMMEDIATE",
.mask = RXE_RDETH_MASK | RXE_DETH_MASK | RXE_IMMDT_MASK
| RXE_PAYLOAD_MASK | RXE_REQ_MASK
| RXE_COMP_MASK | RXE_SEND_MASK
| RXE_END_MASK,
.length = RXE_BTH_BYTES + RXE_IMMDT_BYTES + RXE_DETH_BYTES
+ RXE_RDETH_BYTES,
.offset = {
[RXE_BTH] = 0,
[RXE_RDETH] = RXE_BTH_BYTES,
[RXE_DETH] = RXE_BTH_BYTES
+ RXE_RDETH_BYTES,
[RXE_IMMDT] = RXE_BTH_BYTES
+ RXE_RDETH_BYTES
+ RXE_DETH_BYTES,
[RXE_PAYLOAD] = RXE_BTH_BYTES
+ RXE_RDETH_BYTES
+ RXE_DETH_BYTES
+ RXE_IMMDT_BYTES,
}
},
[IB_OPCODE_RD_SEND_ONLY] = {
.name = "IB_OPCODE_RD_SEND_ONLY",
.mask = RXE_RDETH_MASK | RXE_DETH_MASK | RXE_PAYLOAD_MASK
| RXE_REQ_MASK | RXE_COMP_MASK | RXE_RWR_MASK
| RXE_SEND_MASK | RXE_START_MASK | RXE_END_MASK,
.length = RXE_BTH_BYTES + RXE_DETH_BYTES + RXE_RDETH_BYTES,
.offset = {
[RXE_BTH] = 0,
[RXE_RDETH] = RXE_BTH_BYTES,
[RXE_DETH] = RXE_BTH_BYTES
+ RXE_RDETH_BYTES,
[RXE_PAYLOAD] = RXE_BTH_BYTES
+ RXE_RDETH_BYTES
+ RXE_DETH_BYTES,
}
},
[IB_OPCODE_RD_SEND_ONLY_WITH_IMMEDIATE] = {
.name = "IB_OPCODE_RD_SEND_ONLY_WITH_IMMEDIATE",
.mask = RXE_RDETH_MASK | RXE_DETH_MASK | RXE_IMMDT_MASK
| RXE_PAYLOAD_MASK | RXE_REQ_MASK
| RXE_COMP_MASK | RXE_RWR_MASK | RXE_SEND_MASK
| RXE_START_MASK | RXE_END_MASK,
.length = RXE_BTH_BYTES + RXE_IMMDT_BYTES + RXE_DETH_BYTES
+ RXE_RDETH_BYTES,
.offset = {
[RXE_BTH] = 0,
[RXE_RDETH] = RXE_BTH_BYTES,
[RXE_DETH] = RXE_BTH_BYTES
+ RXE_RDETH_BYTES,
[RXE_IMMDT] = RXE_BTH_BYTES
+ RXE_RDETH_BYTES
+ RXE_DETH_BYTES,
[RXE_PAYLOAD] = RXE_BTH_BYTES
+ RXE_RDETH_BYTES
+ RXE_DETH_BYTES
+ RXE_IMMDT_BYTES,
}
},
[IB_OPCODE_RD_RDMA_WRITE_FIRST] = {
.name = "IB_OPCODE_RD_RDMA_WRITE_FIRST",
.mask = RXE_RDETH_MASK | RXE_DETH_MASK | RXE_RETH_MASK
| RXE_PAYLOAD_MASK | RXE_REQ_MASK
| RXE_WRITE_MASK | RXE_START_MASK,
.length = RXE_BTH_BYTES + RXE_RETH_BYTES + RXE_DETH_BYTES
+ RXE_RDETH_BYTES,
.offset = {
[RXE_BTH] = 0,
[RXE_RDETH] = RXE_BTH_BYTES,
[RXE_DETH] = RXE_BTH_BYTES
+ RXE_RDETH_BYTES,
[RXE_RETH] = RXE_BTH_BYTES
+ RXE_RDETH_BYTES
+ RXE_DETH_BYTES,
[RXE_PAYLOAD] = RXE_BTH_BYTES
+ RXE_RDETH_BYTES
+ RXE_DETH_BYTES
+ RXE_RETH_BYTES,
}
},
[IB_OPCODE_RD_RDMA_WRITE_MIDDLE] = {
.name = "IB_OPCODE_RD_RDMA_WRITE_MIDDLE",
.mask = RXE_RDETH_MASK | RXE_DETH_MASK | RXE_PAYLOAD_MASK
| RXE_REQ_MASK | RXE_WRITE_MASK
| RXE_MIDDLE_MASK,
.length = RXE_BTH_BYTES + RXE_DETH_BYTES + RXE_RDETH_BYTES,
.offset = {
[RXE_BTH] = 0,
[RXE_RDETH] = RXE_BTH_BYTES,
[RXE_DETH] = RXE_BTH_BYTES
+ RXE_RDETH_BYTES,
[RXE_PAYLOAD] = RXE_BTH_BYTES
+ RXE_RDETH_BYTES
+ RXE_DETH_BYTES,
}
},
[IB_OPCODE_RD_RDMA_WRITE_LAST] = {
.name = "IB_OPCODE_RD_RDMA_WRITE_LAST",
.mask = RXE_RDETH_MASK | RXE_DETH_MASK | RXE_PAYLOAD_MASK
| RXE_REQ_MASK | RXE_WRITE_MASK
| RXE_END_MASK,
.length = RXE_BTH_BYTES + RXE_DETH_BYTES + RXE_RDETH_BYTES,
.offset = {
[RXE_BTH] = 0,
[RXE_RDETH] = RXE_BTH_BYTES,
[RXE_DETH] = RXE_BTH_BYTES
+ RXE_RDETH_BYTES,
[RXE_PAYLOAD] = RXE_BTH_BYTES
+ RXE_RDETH_BYTES
+ RXE_DETH_BYTES,
}
},
[IB_OPCODE_RD_RDMA_WRITE_LAST_WITH_IMMEDIATE] = {
.name = "IB_OPCODE_RD_RDMA_WRITE_LAST_WITH_IMMEDIATE",
.mask = RXE_RDETH_MASK | RXE_DETH_MASK | RXE_IMMDT_MASK
| RXE_PAYLOAD_MASK | RXE_REQ_MASK
| RXE_WRITE_MASK | RXE_COMP_MASK | RXE_RWR_MASK
| RXE_END_MASK,
.length = RXE_BTH_BYTES + RXE_IMMDT_BYTES + RXE_DETH_BYTES
+ RXE_RDETH_BYTES,
.offset = {
[RXE_BTH] = 0,
[RXE_RDETH] = RXE_BTH_BYTES,
[RXE_DETH] = RXE_BTH_BYTES
+ RXE_RDETH_BYTES,
[RXE_IMMDT] = RXE_BTH_BYTES
+ RXE_RDETH_BYTES
+ RXE_DETH_BYTES,
[RXE_PAYLOAD] = RXE_BTH_BYTES
+ RXE_RDETH_BYTES
+ RXE_DETH_BYTES
+ RXE_IMMDT_BYTES,
}
},
[IB_OPCODE_RD_RDMA_WRITE_ONLY] = {
.name = "IB_OPCODE_RD_RDMA_WRITE_ONLY",
.mask = RXE_RDETH_MASK | RXE_DETH_MASK | RXE_RETH_MASK
| RXE_PAYLOAD_MASK | RXE_REQ_MASK
| RXE_WRITE_MASK | RXE_START_MASK
| RXE_END_MASK,
.length = RXE_BTH_BYTES + RXE_RETH_BYTES + RXE_DETH_BYTES
+ RXE_RDETH_BYTES,
.offset = {
[RXE_BTH] = 0,
[RXE_RDETH] = RXE_BTH_BYTES,
[RXE_DETH] = RXE_BTH_BYTES
+ RXE_RDETH_BYTES,
[RXE_RETH] = RXE_BTH_BYTES
+ RXE_RDETH_BYTES
+ RXE_DETH_BYTES,
[RXE_PAYLOAD] = RXE_BTH_BYTES
+ RXE_RDETH_BYTES
+ RXE_DETH_BYTES
+ RXE_RETH_BYTES,
}
},
[IB_OPCODE_RD_RDMA_WRITE_ONLY_WITH_IMMEDIATE] = {
.name = "IB_OPCODE_RD_RDMA_WRITE_ONLY_WITH_IMMEDIATE",
.mask = RXE_RDETH_MASK | RXE_DETH_MASK | RXE_RETH_MASK
| RXE_IMMDT_MASK | RXE_PAYLOAD_MASK
| RXE_REQ_MASK | RXE_WRITE_MASK
| RXE_COMP_MASK | RXE_RWR_MASK
| RXE_START_MASK | RXE_END_MASK,
.length = RXE_BTH_BYTES + RXE_IMMDT_BYTES + RXE_RETH_BYTES
+ RXE_DETH_BYTES + RXE_RDETH_BYTES,
.offset = {
[RXE_BTH] = 0,
[RXE_RDETH] = RXE_BTH_BYTES,
[RXE_DETH] = RXE_BTH_BYTES
+ RXE_RDETH_BYTES,
[RXE_RETH] = RXE_BTH_BYTES
+ RXE_RDETH_BYTES
+ RXE_DETH_BYTES,
[RXE_IMMDT] = RXE_BTH_BYTES
+ RXE_RDETH_BYTES
+ RXE_DETH_BYTES
+ RXE_RETH_BYTES,
[RXE_PAYLOAD] = RXE_BTH_BYTES
+ RXE_RDETH_BYTES
+ RXE_DETH_BYTES
+ RXE_RETH_BYTES
+ RXE_IMMDT_BYTES,
}
},
[IB_OPCODE_RD_RDMA_READ_REQUEST] = {
.name = "IB_OPCODE_RD_RDMA_READ_REQUEST",
.mask = RXE_RDETH_MASK | RXE_DETH_MASK | RXE_RETH_MASK
| RXE_REQ_MASK | RXE_READ_MASK
| RXE_START_MASK | RXE_END_MASK,
.length = RXE_BTH_BYTES + RXE_RETH_BYTES + RXE_DETH_BYTES
+ RXE_RDETH_BYTES,
.offset = {
[RXE_BTH] = 0,
[RXE_RDETH] = RXE_BTH_BYTES,
[RXE_DETH] = RXE_BTH_BYTES
+ RXE_RDETH_BYTES,
[RXE_RETH] = RXE_BTH_BYTES
+ RXE_RDETH_BYTES
+ RXE_DETH_BYTES,
[RXE_PAYLOAD] = RXE_BTH_BYTES
+ RXE_RETH_BYTES
+ RXE_DETH_BYTES
+ RXE_RDETH_BYTES,
}
},
[IB_OPCODE_RD_RDMA_READ_RESPONSE_FIRST] = {
.name = "IB_OPCODE_RD_RDMA_READ_RESPONSE_FIRST",
.mask = RXE_RDETH_MASK | RXE_AETH_MASK
| RXE_PAYLOAD_MASK | RXE_ACK_MASK
| RXE_START_MASK,
.length = RXE_BTH_BYTES + RXE_AETH_BYTES + RXE_RDETH_BYTES,
.offset = {
[RXE_BTH] = 0,
[RXE_RDETH] = RXE_BTH_BYTES,
[RXE_AETH] = RXE_BTH_BYTES
+ RXE_RDETH_BYTES,
[RXE_PAYLOAD] = RXE_BTH_BYTES
+ RXE_RDETH_BYTES
+ RXE_AETH_BYTES,
}
},
[IB_OPCODE_RD_RDMA_READ_RESPONSE_MIDDLE] = {
.name = "IB_OPCODE_RD_RDMA_READ_RESPONSE_MIDDLE",
.mask = RXE_RDETH_MASK | RXE_PAYLOAD_MASK | RXE_ACK_MASK
| RXE_MIDDLE_MASK,
.length = RXE_BTH_BYTES + RXE_RDETH_BYTES,
.offset = {
[RXE_BTH] = 0,
[RXE_RDETH] = RXE_BTH_BYTES,
[RXE_PAYLOAD] = RXE_BTH_BYTES
+ RXE_RDETH_BYTES,
}
},
[IB_OPCODE_RD_RDMA_READ_RESPONSE_LAST] = {
.name = "IB_OPCODE_RD_RDMA_READ_RESPONSE_LAST",
.mask = RXE_RDETH_MASK | RXE_AETH_MASK | RXE_PAYLOAD_MASK
| RXE_ACK_MASK | RXE_END_MASK,
.length = RXE_BTH_BYTES + RXE_AETH_BYTES + RXE_RDETH_BYTES,
.offset = {
[RXE_BTH] = 0,
[RXE_RDETH] = RXE_BTH_BYTES,
[RXE_AETH] = RXE_BTH_BYTES
+ RXE_RDETH_BYTES,
[RXE_PAYLOAD] = RXE_BTH_BYTES
+ RXE_RDETH_BYTES
+ RXE_AETH_BYTES,
}
},
[IB_OPCODE_RD_RDMA_READ_RESPONSE_ONLY] = {
.name = "IB_OPCODE_RD_RDMA_READ_RESPONSE_ONLY",
.mask = RXE_RDETH_MASK | RXE_AETH_MASK | RXE_PAYLOAD_MASK
| RXE_ACK_MASK | RXE_START_MASK | RXE_END_MASK,
.length = RXE_BTH_BYTES + RXE_AETH_BYTES + RXE_RDETH_BYTES,
.offset = {
[RXE_BTH] = 0,
[RXE_RDETH] = RXE_BTH_BYTES,
[RXE_AETH] = RXE_BTH_BYTES
+ RXE_RDETH_BYTES,
[RXE_PAYLOAD] = RXE_BTH_BYTES
+ RXE_RDETH_BYTES
+ RXE_AETH_BYTES,
}
},
[IB_OPCODE_RD_ACKNOWLEDGE] = {
.name = "IB_OPCODE_RD_ACKNOWLEDGE",
.mask = RXE_RDETH_MASK | RXE_AETH_MASK | RXE_ACK_MASK
| RXE_START_MASK | RXE_END_MASK,
.length = RXE_BTH_BYTES + RXE_AETH_BYTES + RXE_RDETH_BYTES,
.offset = {
[RXE_BTH] = 0,
[RXE_RDETH] = RXE_BTH_BYTES,
[RXE_AETH] = RXE_BTH_BYTES
+ RXE_RDETH_BYTES,
}
},
[IB_OPCODE_RD_ATOMIC_ACKNOWLEDGE] = {
.name = "IB_OPCODE_RD_ATOMIC_ACKNOWLEDGE",
.mask = RXE_RDETH_MASK | RXE_AETH_MASK | RXE_ATMACK_MASK
| RXE_ACK_MASK | RXE_START_MASK | RXE_END_MASK,
.length = RXE_BTH_BYTES + RXE_ATMACK_BYTES + RXE_AETH_BYTES
+ RXE_RDETH_BYTES,
.offset = {
[RXE_BTH] = 0,
[RXE_RDETH] = RXE_BTH_BYTES,
[RXE_AETH] = RXE_BTH_BYTES
+ RXE_RDETH_BYTES,
[RXE_ATMACK] = RXE_BTH_BYTES
+ RXE_RDETH_BYTES
+ RXE_AETH_BYTES,
}
},
[IB_OPCODE_RD_COMPARE_SWAP] = {
.name = "RD_COMPARE_SWAP",
.mask = RXE_RDETH_MASK | RXE_DETH_MASK | RXE_ATMETH_MASK
| RXE_REQ_MASK | RXE_ATOMIC_MASK
| RXE_START_MASK | RXE_END_MASK,
.length = RXE_BTH_BYTES + RXE_ATMETH_BYTES + RXE_DETH_BYTES
+ RXE_RDETH_BYTES,
.offset = {
[RXE_BTH] = 0,
[RXE_RDETH] = RXE_BTH_BYTES,
[RXE_DETH] = RXE_BTH_BYTES
+ RXE_RDETH_BYTES,
[RXE_ATMETH] = RXE_BTH_BYTES
+ RXE_RDETH_BYTES
+ RXE_DETH_BYTES,
[RXE_PAYLOAD] = RXE_BTH_BYTES +
+ RXE_ATMETH_BYTES
+ RXE_DETH_BYTES +
+ RXE_RDETH_BYTES,
}
},
[IB_OPCODE_RD_FETCH_ADD] = {
.name = "IB_OPCODE_RD_FETCH_ADD",
.mask = RXE_RDETH_MASK | RXE_DETH_MASK | RXE_ATMETH_MASK
| RXE_REQ_MASK | RXE_ATOMIC_MASK
| RXE_START_MASK | RXE_END_MASK,
.length = RXE_BTH_BYTES + RXE_ATMETH_BYTES + RXE_DETH_BYTES
+ RXE_RDETH_BYTES,
.offset = {
[RXE_BTH] = 0,
[RXE_RDETH] = RXE_BTH_BYTES,
[RXE_DETH] = RXE_BTH_BYTES
+ RXE_RDETH_BYTES,
[RXE_ATMETH] = RXE_BTH_BYTES
+ RXE_RDETH_BYTES
+ RXE_DETH_BYTES,
[RXE_PAYLOAD] = RXE_BTH_BYTES +
+ RXE_ATMETH_BYTES
+ RXE_DETH_BYTES +
+ RXE_RDETH_BYTES,
}
},
/* UD */
[IB_OPCODE_UD_SEND_ONLY] = {
.name = "IB_OPCODE_UD_SEND_ONLY",
.mask = RXE_DETH_MASK | RXE_PAYLOAD_MASK | RXE_REQ_MASK
| RXE_COMP_MASK | RXE_RWR_MASK | RXE_SEND_MASK
| RXE_START_MASK | RXE_END_MASK,
.length = RXE_BTH_BYTES + RXE_DETH_BYTES,
.offset = {
[RXE_BTH] = 0,
[RXE_DETH] = RXE_BTH_BYTES,
[RXE_PAYLOAD] = RXE_BTH_BYTES
+ RXE_DETH_BYTES,
}
},
[IB_OPCODE_UD_SEND_ONLY_WITH_IMMEDIATE] = {
.name = "IB_OPCODE_UD_SEND_ONLY_WITH_IMMEDIATE",
.mask = RXE_DETH_MASK | RXE_IMMDT_MASK | RXE_PAYLOAD_MASK
| RXE_REQ_MASK | RXE_COMP_MASK | RXE_RWR_MASK
| RXE_SEND_MASK | RXE_START_MASK | RXE_END_MASK,
.length = RXE_BTH_BYTES + RXE_IMMDT_BYTES + RXE_DETH_BYTES,
.offset = {
[RXE_BTH] = 0,
[RXE_DETH] = RXE_BTH_BYTES,
[RXE_IMMDT] = RXE_BTH_BYTES
+ RXE_DETH_BYTES,
[RXE_PAYLOAD] = RXE_BTH_BYTES
+ RXE_DETH_BYTES
+ RXE_IMMDT_BYTES,
}
},
};

View File

@ -0,0 +1,129 @@
/*
* Copyright (c) 2016 Mellanox Technologies Ltd. All rights reserved.
* Copyright (c) 2015 System Fabric Works, 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 RXE_OPCODE_H
#define RXE_OPCODE_H
/*
* contains header bit mask definitions and header lengths
* declaration of the rxe_opcode_info struct and
* rxe_wr_opcode_info struct
*/
enum rxe_wr_mask {
WR_INLINE_MASK = BIT(0),
WR_ATOMIC_MASK = BIT(1),
WR_SEND_MASK = BIT(2),
WR_READ_MASK = BIT(3),
WR_WRITE_MASK = BIT(4),
WR_LOCAL_MASK = BIT(5),
WR_REG_MASK = BIT(6),
WR_READ_OR_WRITE_MASK = WR_READ_MASK | WR_WRITE_MASK,
WR_READ_WRITE_OR_SEND_MASK = WR_READ_OR_WRITE_MASK | WR_SEND_MASK,
WR_WRITE_OR_SEND_MASK = WR_WRITE_MASK | WR_SEND_MASK,
WR_ATOMIC_OR_READ_MASK = WR_ATOMIC_MASK | WR_READ_MASK,
};
#define WR_MAX_QPT (8)
struct rxe_wr_opcode_info {
char *name;
enum rxe_wr_mask mask[WR_MAX_QPT];
};
extern struct rxe_wr_opcode_info rxe_wr_opcode_info[];
enum rxe_hdr_type {
RXE_LRH,
RXE_GRH,
RXE_BTH,
RXE_RETH,
RXE_AETH,
RXE_ATMETH,
RXE_ATMACK,
RXE_IETH,
RXE_RDETH,
RXE_DETH,
RXE_IMMDT,
RXE_PAYLOAD,
NUM_HDR_TYPES
};
enum rxe_hdr_mask {
RXE_LRH_MASK = BIT(RXE_LRH),
RXE_GRH_MASK = BIT(RXE_GRH),
RXE_BTH_MASK = BIT(RXE_BTH),
RXE_IMMDT_MASK = BIT(RXE_IMMDT),
RXE_RETH_MASK = BIT(RXE_RETH),
RXE_AETH_MASK = BIT(RXE_AETH),
RXE_ATMETH_MASK = BIT(RXE_ATMETH),
RXE_ATMACK_MASK = BIT(RXE_ATMACK),
RXE_IETH_MASK = BIT(RXE_IETH),
RXE_RDETH_MASK = BIT(RXE_RDETH),
RXE_DETH_MASK = BIT(RXE_DETH),
RXE_PAYLOAD_MASK = BIT(RXE_PAYLOAD),
RXE_REQ_MASK = BIT(NUM_HDR_TYPES + 0),
RXE_ACK_MASK = BIT(NUM_HDR_TYPES + 1),
RXE_SEND_MASK = BIT(NUM_HDR_TYPES + 2),
RXE_WRITE_MASK = BIT(NUM_HDR_TYPES + 3),
RXE_READ_MASK = BIT(NUM_HDR_TYPES + 4),
RXE_ATOMIC_MASK = BIT(NUM_HDR_TYPES + 5),
RXE_RWR_MASK = BIT(NUM_HDR_TYPES + 6),
RXE_COMP_MASK = BIT(NUM_HDR_TYPES + 7),
RXE_START_MASK = BIT(NUM_HDR_TYPES + 8),
RXE_MIDDLE_MASK = BIT(NUM_HDR_TYPES + 9),
RXE_END_MASK = BIT(NUM_HDR_TYPES + 10),
RXE_LOOPBACK_MASK = BIT(NUM_HDR_TYPES + 12),
RXE_READ_OR_ATOMIC = (RXE_READ_MASK | RXE_ATOMIC_MASK),
RXE_WRITE_OR_SEND = (RXE_WRITE_MASK | RXE_SEND_MASK),
};
#define OPCODE_NONE (-1)
#define RXE_NUM_OPCODE 256
struct rxe_opcode_info {
char *name;
enum rxe_hdr_mask mask;
int length;
int offset[NUM_HDR_TYPES];
};
extern struct rxe_opcode_info rxe_opcode[RXE_NUM_OPCODE];
#endif /* RXE_OPCODE_H */

View File

@ -0,0 +1,172 @@
/*
* Copyright (c) 2016 Mellanox Technologies Ltd. All rights reserved.
* Copyright (c) 2015 System Fabric Works, 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 RXE_PARAM_H
#define RXE_PARAM_H
static inline enum ib_mtu rxe_mtu_int_to_enum(int mtu)
{
if (mtu < 256)
return 0;
else if (mtu < 512)
return IB_MTU_256;
else if (mtu < 1024)
return IB_MTU_512;
else if (mtu < 2048)
return IB_MTU_1024;
else if (mtu < 4096)
return IB_MTU_2048;
else
return IB_MTU_4096;
}
/* Find the IB mtu for a given network MTU. */
static inline enum ib_mtu eth_mtu_int_to_enum(int mtu)
{
mtu -= RXE_MAX_HDR_LENGTH;
return rxe_mtu_int_to_enum(mtu);
}
/* default/initial rxe device parameter settings */
enum rxe_device_param {
RXE_FW_VER = 0,
RXE_MAX_MR_SIZE = -1ull,
RXE_PAGE_SIZE_CAP = 0xfffff000,
RXE_VENDOR_ID = 0,
RXE_VENDOR_PART_ID = 0,
RXE_HW_VER = 0,
RXE_MAX_QP = 0x10000,
RXE_MAX_QP_WR = 0x4000,
RXE_MAX_INLINE_DATA = 400,
RXE_DEVICE_CAP_FLAGS = IB_DEVICE_BAD_PKEY_CNTR
| IB_DEVICE_BAD_QKEY_CNTR
| IB_DEVICE_AUTO_PATH_MIG
| IB_DEVICE_CHANGE_PHY_PORT
| IB_DEVICE_UD_AV_PORT_ENFORCE
| IB_DEVICE_PORT_ACTIVE_EVENT
| IB_DEVICE_SYS_IMAGE_GUID
| IB_DEVICE_RC_RNR_NAK_GEN
| IB_DEVICE_SRQ_RESIZE
| IB_DEVICE_MEM_MGT_EXTENSIONS,
RXE_MAX_SGE = 32,
RXE_MAX_SGE_RD = 32,
RXE_MAX_CQ = 16384,
RXE_MAX_LOG_CQE = 13,
RXE_MAX_MR = 2 * 1024,
RXE_MAX_PD = 0x7ffc,
RXE_MAX_QP_RD_ATOM = 128,
RXE_MAX_EE_RD_ATOM = 0,
RXE_MAX_RES_RD_ATOM = 0x3f000,
RXE_MAX_QP_INIT_RD_ATOM = 128,
RXE_MAX_EE_INIT_RD_ATOM = 0,
RXE_ATOMIC_CAP = 1,
RXE_MAX_EE = 0,
RXE_MAX_RDD = 0,
RXE_MAX_MW = 0,
RXE_MAX_RAW_IPV6_QP = 0,
RXE_MAX_RAW_ETHY_QP = 0,
RXE_MAX_MCAST_GRP = 8192,
RXE_MAX_MCAST_QP_ATTACH = 56,
RXE_MAX_TOT_MCAST_QP_ATTACH = 0x70000,
RXE_MAX_AH = 100,
RXE_MAX_FMR = 0,
RXE_MAX_MAP_PER_FMR = 0,
RXE_MAX_SRQ = 960,
RXE_MAX_SRQ_WR = 0x4000,
RXE_MIN_SRQ_WR = 1,
RXE_MAX_SRQ_SGE = 27,
RXE_MIN_SRQ_SGE = 1,
RXE_MAX_FMR_PAGE_LIST_LEN = 512,
RXE_MAX_PKEYS = 64,
RXE_LOCAL_CA_ACK_DELAY = 15,
RXE_MAX_UCONTEXT = 512,
RXE_NUM_PORT = 1,
RXE_NUM_COMP_VECTORS = 1,
RXE_MIN_QP_INDEX = 16,
RXE_MAX_QP_INDEX = 0x00020000,
RXE_MIN_SRQ_INDEX = 0x00020001,
RXE_MAX_SRQ_INDEX = 0x00040000,
RXE_MIN_MR_INDEX = 0x00000001,
RXE_MAX_MR_INDEX = 0x00040000,
RXE_MIN_MW_INDEX = 0x00040001,
RXE_MAX_MW_INDEX = 0x00060000,
RXE_MAX_PKT_PER_ACK = 64,
RXE_MAX_UNACKED_PSNS = 128,
/* Max inflight SKBs per queue pair */
RXE_INFLIGHT_SKBS_PER_QP_HIGH = 64,
RXE_INFLIGHT_SKBS_PER_QP_LOW = 16,
/* Delay before calling arbiter timer */
RXE_NSEC_ARB_TIMER_DELAY = 200,
};
/* default/initial rxe port parameters */
enum rxe_port_param {
RXE_PORT_STATE = IB_PORT_DOWN,
RXE_PORT_MAX_MTU = IB_MTU_4096,
RXE_PORT_ACTIVE_MTU = IB_MTU_256,
RXE_PORT_GID_TBL_LEN = 1024,
RXE_PORT_PORT_CAP_FLAGS = RDMA_CORE_CAP_PROT_ROCE_UDP_ENCAP,
RXE_PORT_MAX_MSG_SZ = 0x800000,
RXE_PORT_BAD_PKEY_CNTR = 0,
RXE_PORT_QKEY_VIOL_CNTR = 0,
RXE_PORT_LID = 0,
RXE_PORT_SM_LID = 0,
RXE_PORT_SM_SL = 0,
RXE_PORT_LMC = 0,
RXE_PORT_MAX_VL_NUM = 1,
RXE_PORT_SUBNET_TIMEOUT = 0,
RXE_PORT_INIT_TYPE_REPLY = 0,
RXE_PORT_ACTIVE_WIDTH = IB_WIDTH_1X,
RXE_PORT_ACTIVE_SPEED = 1,
RXE_PORT_PKEY_TBL_LEN = 64,
RXE_PORT_PHYS_STATE = 2,
RXE_PORT_SUBNET_PREFIX = 0xfe80000000000000ULL,
};
/* default/initial port info parameters */
enum rxe_port_info_param {
RXE_PORT_INFO_VL_CAP = 4, /* 1-8 */
RXE_PORT_INFO_MTU_CAP = 5, /* 4096 */
RXE_PORT_INFO_OPER_VL = 1, /* 1 */
};
#endif /* RXE_PARAM_H */

View File

@ -0,0 +1,502 @@
/*
* Copyright (c) 2016 Mellanox Technologies Ltd. All rights reserved.
* Copyright (c) 2015 System Fabric Works, 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 "rxe.h"
#include "rxe_loc.h"
/* info about object pools
* note that mr and mw share a single index space
* so that one can map an lkey to the correct type of object
*/
struct rxe_type_info rxe_type_info[RXE_NUM_TYPES] = {
[RXE_TYPE_UC] = {
.name = "rxe-uc",
.size = sizeof(struct rxe_ucontext),
},
[RXE_TYPE_PD] = {
.name = "rxe-pd",
.size = sizeof(struct rxe_pd),
},
[RXE_TYPE_AH] = {
.name = "rxe-ah",
.size = sizeof(struct rxe_ah),
.flags = RXE_POOL_ATOMIC,
},
[RXE_TYPE_SRQ] = {
.name = "rxe-srq",
.size = sizeof(struct rxe_srq),
.flags = RXE_POOL_INDEX,
.min_index = RXE_MIN_SRQ_INDEX,
.max_index = RXE_MAX_SRQ_INDEX,
},
[RXE_TYPE_QP] = {
.name = "rxe-qp",
.size = sizeof(struct rxe_qp),
.cleanup = rxe_qp_cleanup,
.flags = RXE_POOL_INDEX,
.min_index = RXE_MIN_QP_INDEX,
.max_index = RXE_MAX_QP_INDEX,
},
[RXE_TYPE_CQ] = {
.name = "rxe-cq",
.size = sizeof(struct rxe_cq),
.cleanup = rxe_cq_cleanup,
},
[RXE_TYPE_MR] = {
.name = "rxe-mr",
.size = sizeof(struct rxe_mem),
.cleanup = rxe_mem_cleanup,
.flags = RXE_POOL_INDEX,
.max_index = RXE_MAX_MR_INDEX,
.min_index = RXE_MIN_MR_INDEX,
},
[RXE_TYPE_MW] = {
.name = "rxe-mw",
.size = sizeof(struct rxe_mem),
.flags = RXE_POOL_INDEX,
.max_index = RXE_MAX_MW_INDEX,
.min_index = RXE_MIN_MW_INDEX,
},
[RXE_TYPE_MC_GRP] = {
.name = "rxe-mc_grp",
.size = sizeof(struct rxe_mc_grp),
.cleanup = rxe_mc_cleanup,
.flags = RXE_POOL_KEY,
.key_offset = offsetof(struct rxe_mc_grp, mgid),
.key_size = sizeof(union ib_gid),
},
[RXE_TYPE_MC_ELEM] = {
.name = "rxe-mc_elem",
.size = sizeof(struct rxe_mc_elem),
.flags = RXE_POOL_ATOMIC,
},
};
static inline char *pool_name(struct rxe_pool *pool)
{
return rxe_type_info[pool->type].name;
}
static inline struct kmem_cache *pool_cache(struct rxe_pool *pool)
{
return rxe_type_info[pool->type].cache;
}
static inline enum rxe_elem_type rxe_type(void *arg)
{
struct rxe_pool_entry *elem = arg;
return elem->pool->type;
}
int rxe_cache_init(void)
{
int err;
int i;
size_t size;
struct rxe_type_info *type;
for (i = 0; i < RXE_NUM_TYPES; i++) {
type = &rxe_type_info[i];
size = ALIGN(type->size, RXE_POOL_ALIGN);
type->cache = kmem_cache_create(type->name, size,
RXE_POOL_ALIGN,
RXE_POOL_CACHE_FLAGS, NULL);
if (!type->cache) {
pr_err("Unable to init kmem cache for %s\n",
type->name);
err = -ENOMEM;
goto err1;
}
}
return 0;
err1:
while (--i >= 0) {
kmem_cache_destroy(type->cache);
type->cache = NULL;
}
return err;
}
void rxe_cache_exit(void)
{
int i;
struct rxe_type_info *type;
for (i = 0; i < RXE_NUM_TYPES; i++) {
type = &rxe_type_info[i];
kmem_cache_destroy(type->cache);
type->cache = NULL;
}
}
static int rxe_pool_init_index(struct rxe_pool *pool, u32 max, u32 min)
{
int err = 0;
size_t size;
if ((max - min + 1) < pool->max_elem) {
pr_warn("not enough indices for max_elem\n");
err = -EINVAL;
goto out;
}
pool->max_index = max;
pool->min_index = min;
size = BITS_TO_LONGS(max - min + 1) * sizeof(long);
pool->table = kmalloc(size, GFP_KERNEL);
if (!pool->table) {
pr_warn("no memory for bit table\n");
err = -ENOMEM;
goto out;
}
pool->table_size = size;
bitmap_zero(pool->table, max - min + 1);
out:
return err;
}
int rxe_pool_init(
struct rxe_dev *rxe,
struct rxe_pool *pool,
enum rxe_elem_type type,
unsigned max_elem)
{
int err = 0;
size_t size = rxe_type_info[type].size;
memset(pool, 0, sizeof(*pool));
pool->rxe = rxe;
pool->type = type;
pool->max_elem = max_elem;
pool->elem_size = ALIGN(size, RXE_POOL_ALIGN);
pool->flags = rxe_type_info[type].flags;
pool->tree = RB_ROOT;
pool->cleanup = rxe_type_info[type].cleanup;
atomic_set(&pool->num_elem, 0);
kref_init(&pool->ref_cnt);
spin_lock_init(&pool->pool_lock);
if (rxe_type_info[type].flags & RXE_POOL_INDEX) {
err = rxe_pool_init_index(pool,
rxe_type_info[type].max_index,
rxe_type_info[type].min_index);
if (err)
goto out;
}
if (rxe_type_info[type].flags & RXE_POOL_KEY) {
pool->key_offset = rxe_type_info[type].key_offset;
pool->key_size = rxe_type_info[type].key_size;
}
pool->state = rxe_pool_valid;
out:
return err;
}
static void rxe_pool_release(struct kref *kref)
{
struct rxe_pool *pool = container_of(kref, struct rxe_pool, ref_cnt);
pool->state = rxe_pool_invalid;
kfree(pool->table);
}
static void rxe_pool_put(struct rxe_pool *pool)
{
kref_put(&pool->ref_cnt, rxe_pool_release);
}
int rxe_pool_cleanup(struct rxe_pool *pool)
{
unsigned long flags;
spin_lock_irqsave(&pool->pool_lock, flags);
pool->state = rxe_pool_invalid;
if (atomic_read(&pool->num_elem) > 0)
pr_warn("%s pool destroyed with unfree'd elem\n",
pool_name(pool));
spin_unlock_irqrestore(&pool->pool_lock, flags);
rxe_pool_put(pool);
return 0;
}
static u32 alloc_index(struct rxe_pool *pool)
{
u32 index;
u32 range = pool->max_index - pool->min_index + 1;
index = find_next_zero_bit(pool->table, range, pool->last);
if (index >= range)
index = find_first_zero_bit(pool->table, range);
set_bit(index, pool->table);
pool->last = index;
return index + pool->min_index;
}
static void insert_index(struct rxe_pool *pool, struct rxe_pool_entry *new)
{
struct rb_node **link = &pool->tree.rb_node;
struct rb_node *parent = NULL;
struct rxe_pool_entry *elem;
while (*link) {
parent = *link;
elem = rb_entry(parent, struct rxe_pool_entry, node);
if (elem->index == new->index) {
pr_warn("element already exists!\n");
goto out;
}
if (elem->index > new->index)
link = &(*link)->rb_left;
else
link = &(*link)->rb_right;
}
rb_link_node(&new->node, parent, link);
rb_insert_color(&new->node, &pool->tree);
out:
return;
}
static void insert_key(struct rxe_pool *pool, struct rxe_pool_entry *new)
{
struct rb_node **link = &pool->tree.rb_node;
struct rb_node *parent = NULL;
struct rxe_pool_entry *elem;
int cmp;
while (*link) {
parent = *link;
elem = rb_entry(parent, struct rxe_pool_entry, node);
cmp = memcmp((u8 *)elem + pool->key_offset,
(u8 *)new + pool->key_offset, pool->key_size);
if (cmp == 0) {
pr_warn("key already exists!\n");
goto out;
}
if (cmp > 0)
link = &(*link)->rb_left;
else
link = &(*link)->rb_right;
}
rb_link_node(&new->node, parent, link);
rb_insert_color(&new->node, &pool->tree);
out:
return;
}
void rxe_add_key(void *arg, void *key)
{
struct rxe_pool_entry *elem = arg;
struct rxe_pool *pool = elem->pool;
unsigned long flags;
spin_lock_irqsave(&pool->pool_lock, flags);
memcpy((u8 *)elem + pool->key_offset, key, pool->key_size);
insert_key(pool, elem);
spin_unlock_irqrestore(&pool->pool_lock, flags);
}
void rxe_drop_key(void *arg)
{
struct rxe_pool_entry *elem = arg;
struct rxe_pool *pool = elem->pool;
unsigned long flags;
spin_lock_irqsave(&pool->pool_lock, flags);
rb_erase(&elem->node, &pool->tree);
spin_unlock_irqrestore(&pool->pool_lock, flags);
}
void rxe_add_index(void *arg)
{
struct rxe_pool_entry *elem = arg;
struct rxe_pool *pool = elem->pool;
unsigned long flags;
spin_lock_irqsave(&pool->pool_lock, flags);
elem->index = alloc_index(pool);
insert_index(pool, elem);
spin_unlock_irqrestore(&pool->pool_lock, flags);
}
void rxe_drop_index(void *arg)
{
struct rxe_pool_entry *elem = arg;
struct rxe_pool *pool = elem->pool;
unsigned long flags;
spin_lock_irqsave(&pool->pool_lock, flags);
clear_bit(elem->index - pool->min_index, pool->table);
rb_erase(&elem->node, &pool->tree);
spin_unlock_irqrestore(&pool->pool_lock, flags);
}
void *rxe_alloc(struct rxe_pool *pool)
{
struct rxe_pool_entry *elem;
unsigned long flags;
might_sleep_if(!(pool->flags & RXE_POOL_ATOMIC));
spin_lock_irqsave(&pool->pool_lock, flags);
if (pool->state != rxe_pool_valid) {
spin_unlock_irqrestore(&pool->pool_lock, flags);
return NULL;
}
kref_get(&pool->ref_cnt);
spin_unlock_irqrestore(&pool->pool_lock, flags);
kref_get(&pool->rxe->ref_cnt);
if (atomic_inc_return(&pool->num_elem) > pool->max_elem) {
atomic_dec(&pool->num_elem);
rxe_dev_put(pool->rxe);
rxe_pool_put(pool);
return NULL;
}
elem = kmem_cache_zalloc(pool_cache(pool),
(pool->flags & RXE_POOL_ATOMIC) ?
GFP_ATOMIC : GFP_KERNEL);
elem->pool = pool;
kref_init(&elem->ref_cnt);
return elem;
}
void rxe_elem_release(struct kref *kref)
{
struct rxe_pool_entry *elem =
container_of(kref, struct rxe_pool_entry, ref_cnt);
struct rxe_pool *pool = elem->pool;
if (pool->cleanup)
pool->cleanup(elem);
kmem_cache_free(pool_cache(pool), elem);
atomic_dec(&pool->num_elem);
rxe_dev_put(pool->rxe);
rxe_pool_put(pool);
}
void *rxe_pool_get_index(struct rxe_pool *pool, u32 index)
{
struct rb_node *node = NULL;
struct rxe_pool_entry *elem = NULL;
unsigned long flags;
spin_lock_irqsave(&pool->pool_lock, flags);
if (pool->state != rxe_pool_valid)
goto out;
node = pool->tree.rb_node;
while (node) {
elem = rb_entry(node, struct rxe_pool_entry, node);
if (elem->index > index)
node = node->rb_left;
else if (elem->index < index)
node = node->rb_right;
else
break;
}
if (node)
kref_get(&elem->ref_cnt);
out:
spin_unlock_irqrestore(&pool->pool_lock, flags);
return node ? (void *)elem : NULL;
}
void *rxe_pool_get_key(struct rxe_pool *pool, void *key)
{
struct rb_node *node = NULL;
struct rxe_pool_entry *elem = NULL;
int cmp;
unsigned long flags;
spin_lock_irqsave(&pool->pool_lock, flags);
if (pool->state != rxe_pool_valid)
goto out;
node = pool->tree.rb_node;
while (node) {
elem = rb_entry(node, struct rxe_pool_entry, node);
cmp = memcmp((u8 *)elem + pool->key_offset,
key, pool->key_size);
if (cmp > 0)
node = node->rb_left;
else if (cmp < 0)
node = node->rb_right;
else
break;
}
if (node)
kref_get(&elem->ref_cnt);
out:
spin_unlock_irqrestore(&pool->pool_lock, flags);
return node ? ((void *)elem) : NULL;
}

View File

@ -0,0 +1,163 @@
/*
* Copyright (c) 2016 Mellanox Technologies Ltd. All rights reserved.
* Copyright (c) 2015 System Fabric Works, 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 RXE_POOL_H
#define RXE_POOL_H
#define RXE_POOL_ALIGN (16)
#define RXE_POOL_CACHE_FLAGS (0)
enum rxe_pool_flags {
RXE_POOL_ATOMIC = BIT(0),
RXE_POOL_INDEX = BIT(1),
RXE_POOL_KEY = BIT(2),
};
enum rxe_elem_type {
RXE_TYPE_UC,
RXE_TYPE_PD,
RXE_TYPE_AH,
RXE_TYPE_SRQ,
RXE_TYPE_QP,
RXE_TYPE_CQ,
RXE_TYPE_MR,
RXE_TYPE_MW,
RXE_TYPE_MC_GRP,
RXE_TYPE_MC_ELEM,
RXE_NUM_TYPES, /* keep me last */
};
struct rxe_type_info {
char *name;
size_t size;
void (*cleanup)(void *obj);
enum rxe_pool_flags flags;
u32 max_index;
u32 min_index;
size_t key_offset;
size_t key_size;
struct kmem_cache *cache;
};
extern struct rxe_type_info rxe_type_info[];
enum rxe_pool_state {
rxe_pool_invalid,
rxe_pool_valid,
};
struct rxe_pool_entry {
struct rxe_pool *pool;
struct kref ref_cnt;
struct list_head list;
/* only used if indexed or keyed */
struct rb_node node;
u32 index;
};
struct rxe_pool {
struct rxe_dev *rxe;
spinlock_t pool_lock; /* pool spinlock */
size_t elem_size;
struct kref ref_cnt;
void (*cleanup)(void *obj);
enum rxe_pool_state state;
enum rxe_pool_flags flags;
enum rxe_elem_type type;
unsigned int max_elem;
atomic_t num_elem;
/* only used if indexed or keyed */
struct rb_root tree;
unsigned long *table;
size_t table_size;
u32 max_index;
u32 min_index;
u32 last;
size_t key_offset;
size_t key_size;
};
/* initialize slab caches for managed objects */
int rxe_cache_init(void);
/* cleanup slab caches for managed objects */
void rxe_cache_exit(void);
/* initialize a pool of objects with given limit on
* number of elements. gets parameters from rxe_type_info
* pool elements will be allocated out of a slab cache
*/
int rxe_pool_init(struct rxe_dev *rxe, struct rxe_pool *pool,
enum rxe_elem_type type, u32 max_elem);
/* free resources from object pool */
int rxe_pool_cleanup(struct rxe_pool *pool);
/* allocate an object from pool */
void *rxe_alloc(struct rxe_pool *pool);
/* assign an index to an indexed object and insert object into
* pool's rb tree
*/
void rxe_add_index(void *elem);
/* drop an index and remove object from rb tree */
void rxe_drop_index(void *elem);
/* assign a key to a keyed object and insert object into
* pool's rb tree
*/
void rxe_add_key(void *elem, void *key);
/* remove elem from rb tree */
void rxe_drop_key(void *elem);
/* lookup an indexed object from index. takes a reference on object */
void *rxe_pool_get_index(struct rxe_pool *pool, u32 index);
/* lookup keyed object from key. takes a reference on the object */
void *rxe_pool_get_key(struct rxe_pool *pool, void *key);
/* cleanup an object when all references are dropped */
void rxe_elem_release(struct kref *kref);
/* take a reference on an object */
#define rxe_add_ref(elem) kref_get(&(elem)->pelem.ref_cnt)
/* drop a reference on an object */
#define rxe_drop_ref(elem) kref_put(&(elem)->pelem.ref_cnt, rxe_elem_release)
#endif /* RXE_POOL_H */

View File

@ -0,0 +1,851 @@
/*
* Copyright (c) 2016 Mellanox Technologies Ltd. All rights reserved.
* Copyright (c) 2015 System Fabric Works, 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/skbuff.h>
#include <linux/delay.h>
#include <linux/sched.h>
#include "rxe.h"
#include "rxe_loc.h"
#include "rxe_queue.h"
#include "rxe_task.h"
char *rxe_qp_state_name[] = {
[QP_STATE_RESET] = "RESET",
[QP_STATE_INIT] = "INIT",
[QP_STATE_READY] = "READY",
[QP_STATE_DRAIN] = "DRAIN",
[QP_STATE_DRAINED] = "DRAINED",
[QP_STATE_ERROR] = "ERROR",
};
static int rxe_qp_chk_cap(struct rxe_dev *rxe, struct ib_qp_cap *cap,
int has_srq)
{
if (cap->max_send_wr > rxe->attr.max_qp_wr) {
pr_warn("invalid send wr = %d > %d\n",
cap->max_send_wr, rxe->attr.max_qp_wr);
goto err1;
}
if (cap->max_send_sge > rxe->attr.max_sge) {
pr_warn("invalid send sge = %d > %d\n",
cap->max_send_sge, rxe->attr.max_sge);
goto err1;
}
if (!has_srq) {
if (cap->max_recv_wr > rxe->attr.max_qp_wr) {
pr_warn("invalid recv wr = %d > %d\n",
cap->max_recv_wr, rxe->attr.max_qp_wr);
goto err1;
}
if (cap->max_recv_sge > rxe->attr.max_sge) {
pr_warn("invalid recv sge = %d > %d\n",
cap->max_recv_sge, rxe->attr.max_sge);
goto err1;
}
}
if (cap->max_inline_data > rxe->max_inline_data) {
pr_warn("invalid max inline data = %d > %d\n",
cap->max_inline_data, rxe->max_inline_data);
goto err1;
}
return 0;
err1:
return -EINVAL;
}
int rxe_qp_chk_init(struct rxe_dev *rxe, struct ib_qp_init_attr *init)
{
struct ib_qp_cap *cap = &init->cap;
struct rxe_port *port;
int port_num = init->port_num;
if (!init->recv_cq || !init->send_cq) {
pr_warn("missing cq\n");
goto err1;
}
if (rxe_qp_chk_cap(rxe, cap, !!init->srq))
goto err1;
if (init->qp_type == IB_QPT_SMI || init->qp_type == IB_QPT_GSI) {
if (port_num != 1) {
pr_warn("invalid port = %d\n", port_num);
goto err1;
}
port = &rxe->port;
if (init->qp_type == IB_QPT_SMI && port->qp_smi_index) {
pr_warn("SMI QP exists for port %d\n", port_num);
goto err1;
}
if (init->qp_type == IB_QPT_GSI && port->qp_gsi_index) {
pr_warn("GSI QP exists for port %d\n", port_num);
goto err1;
}
}
return 0;
err1:
return -EINVAL;
}
static int alloc_rd_atomic_resources(struct rxe_qp *qp, unsigned int n)
{
qp->resp.res_head = 0;
qp->resp.res_tail = 0;
qp->resp.resources = kcalloc(n, sizeof(struct resp_res), GFP_KERNEL);
if (!qp->resp.resources)
return -ENOMEM;
return 0;
}
static void free_rd_atomic_resources(struct rxe_qp *qp)
{
if (qp->resp.resources) {
int i;
for (i = 0; i < qp->attr.max_rd_atomic; i++) {
struct resp_res *res = &qp->resp.resources[i];
free_rd_atomic_resource(qp, res);
}
kfree(qp->resp.resources);
qp->resp.resources = NULL;
}
}
void free_rd_atomic_resource(struct rxe_qp *qp, struct resp_res *res)
{
if (res->type == RXE_ATOMIC_MASK) {
rxe_drop_ref(qp);
kfree_skb(res->atomic.skb);
} else if (res->type == RXE_READ_MASK) {
if (res->read.mr)
rxe_drop_ref(res->read.mr);
}
res->type = 0;
}
static void cleanup_rd_atomic_resources(struct rxe_qp *qp)
{
int i;
struct resp_res *res;
if (qp->resp.resources) {
for (i = 0; i < qp->attr.max_rd_atomic; i++) {
res = &qp->resp.resources[i];
free_rd_atomic_resource(qp, res);
}
}
}
static void rxe_qp_init_misc(struct rxe_dev *rxe, struct rxe_qp *qp,
struct ib_qp_init_attr *init)
{
struct rxe_port *port;
u32 qpn;
qp->sq_sig_type = init->sq_sig_type;
qp->attr.path_mtu = 1;
qp->mtu = ib_mtu_enum_to_int(qp->attr.path_mtu);
qpn = qp->pelem.index;
port = &rxe->port;
switch (init->qp_type) {
case IB_QPT_SMI:
qp->ibqp.qp_num = 0;
port->qp_smi_index = qpn;
qp->attr.port_num = init->port_num;
break;
case IB_QPT_GSI:
qp->ibqp.qp_num = 1;
port->qp_gsi_index = qpn;
qp->attr.port_num = init->port_num;
break;
default:
qp->ibqp.qp_num = qpn;
break;
}
INIT_LIST_HEAD(&qp->grp_list);
skb_queue_head_init(&qp->send_pkts);
spin_lock_init(&qp->grp_lock);
spin_lock_init(&qp->state_lock);
atomic_set(&qp->ssn, 0);
atomic_set(&qp->skb_out, 0);
}
static int rxe_qp_init_req(struct rxe_dev *rxe, struct rxe_qp *qp,
struct ib_qp_init_attr *init,
struct ib_ucontext *context, struct ib_udata *udata)
{
int err;
int wqe_size;
err = sock_create_kern(&init_net, AF_INET, SOCK_DGRAM, 0, &qp->sk);
if (err < 0)
return err;
qp->sk->sk->sk_user_data = qp;
qp->sq.max_wr = init->cap.max_send_wr;
qp->sq.max_sge = init->cap.max_send_sge;
qp->sq.max_inline = init->cap.max_inline_data;
wqe_size = max_t(int, sizeof(struct rxe_send_wqe) +
qp->sq.max_sge * sizeof(struct ib_sge),
sizeof(struct rxe_send_wqe) +
qp->sq.max_inline);
qp->sq.queue = rxe_queue_init(rxe,
&qp->sq.max_wr,
wqe_size);
if (!qp->sq.queue)
return -ENOMEM;
err = do_mmap_info(rxe, udata, true,
context, qp->sq.queue->buf,
qp->sq.queue->buf_size, &qp->sq.queue->ip);
if (err) {
kvfree(qp->sq.queue->buf);
kfree(qp->sq.queue);
return err;
}
qp->req.wqe_index = producer_index(qp->sq.queue);
qp->req.state = QP_STATE_RESET;
qp->req.opcode = -1;
qp->comp.opcode = -1;
spin_lock_init(&qp->sq.sq_lock);
skb_queue_head_init(&qp->req_pkts);
rxe_init_task(rxe, &qp->req.task, qp,
rxe_requester, "req");
rxe_init_task(rxe, &qp->comp.task, qp,
rxe_completer, "comp");
init_timer(&qp->rnr_nak_timer);
qp->rnr_nak_timer.function = rnr_nak_timer;
qp->rnr_nak_timer.data = (unsigned long)qp;
init_timer(&qp->retrans_timer);
qp->retrans_timer.function = retransmit_timer;
qp->retrans_timer.data = (unsigned long)qp;
qp->qp_timeout_jiffies = 0; /* Can't be set for UD/UC in modify_qp */
return 0;
}
static int rxe_qp_init_resp(struct rxe_dev *rxe, struct rxe_qp *qp,
struct ib_qp_init_attr *init,
struct ib_ucontext *context, struct ib_udata *udata)
{
int err;
int wqe_size;
if (!qp->srq) {
qp->rq.max_wr = init->cap.max_recv_wr;
qp->rq.max_sge = init->cap.max_recv_sge;
wqe_size = rcv_wqe_size(qp->rq.max_sge);
pr_debug("max_wr = %d, max_sge = %d, wqe_size = %d\n",
qp->rq.max_wr, qp->rq.max_sge, wqe_size);
qp->rq.queue = rxe_queue_init(rxe,
&qp->rq.max_wr,
wqe_size);
if (!qp->rq.queue)
return -ENOMEM;
err = do_mmap_info(rxe, udata, false, context,
qp->rq.queue->buf,
qp->rq.queue->buf_size,
&qp->rq.queue->ip);
if (err) {
kvfree(qp->rq.queue->buf);
kfree(qp->rq.queue);
return err;
}
}
spin_lock_init(&qp->rq.producer_lock);
spin_lock_init(&qp->rq.consumer_lock);
skb_queue_head_init(&qp->resp_pkts);
rxe_init_task(rxe, &qp->resp.task, qp,
rxe_responder, "resp");
qp->resp.opcode = OPCODE_NONE;
qp->resp.msn = 0;
qp->resp.state = QP_STATE_RESET;
return 0;
}
/* called by the create qp verb */
int rxe_qp_from_init(struct rxe_dev *rxe, struct rxe_qp *qp, struct rxe_pd *pd,
struct ib_qp_init_attr *init, struct ib_udata *udata,
struct ib_pd *ibpd)
{
int err;
struct rxe_cq *rcq = to_rcq(init->recv_cq);
struct rxe_cq *scq = to_rcq(init->send_cq);
struct rxe_srq *srq = init->srq ? to_rsrq(init->srq) : NULL;
struct ib_ucontext *context = udata ? ibpd->uobject->context : NULL;
rxe_add_ref(pd);
rxe_add_ref(rcq);
rxe_add_ref(scq);
if (srq)
rxe_add_ref(srq);
qp->pd = pd;
qp->rcq = rcq;
qp->scq = scq;
qp->srq = srq;
rxe_qp_init_misc(rxe, qp, init);
err = rxe_qp_init_req(rxe, qp, init, context, udata);
if (err)
goto err1;
err = rxe_qp_init_resp(rxe, qp, init, context, udata);
if (err)
goto err2;
qp->attr.qp_state = IB_QPS_RESET;
qp->valid = 1;
return 0;
err2:
rxe_queue_cleanup(qp->sq.queue);
err1:
if (srq)
rxe_drop_ref(srq);
rxe_drop_ref(scq);
rxe_drop_ref(rcq);
rxe_drop_ref(pd);
return err;
}
/* called by the query qp verb */
int rxe_qp_to_init(struct rxe_qp *qp, struct ib_qp_init_attr *init)
{
init->event_handler = qp->ibqp.event_handler;
init->qp_context = qp->ibqp.qp_context;
init->send_cq = qp->ibqp.send_cq;
init->recv_cq = qp->ibqp.recv_cq;
init->srq = qp->ibqp.srq;
init->cap.max_send_wr = qp->sq.max_wr;
init->cap.max_send_sge = qp->sq.max_sge;
init->cap.max_inline_data = qp->sq.max_inline;
if (!qp->srq) {
init->cap.max_recv_wr = qp->rq.max_wr;
init->cap.max_recv_sge = qp->rq.max_sge;
}
init->sq_sig_type = qp->sq_sig_type;
init->qp_type = qp->ibqp.qp_type;
init->port_num = 1;
return 0;
}
/* called by the modify qp verb, this routine checks all the parameters before
* making any changes
*/
int rxe_qp_chk_attr(struct rxe_dev *rxe, struct rxe_qp *qp,
struct ib_qp_attr *attr, int mask)
{
enum ib_qp_state cur_state = (mask & IB_QP_CUR_STATE) ?
attr->cur_qp_state : qp->attr.qp_state;
enum ib_qp_state new_state = (mask & IB_QP_STATE) ?
attr->qp_state : cur_state;
if (!ib_modify_qp_is_ok(cur_state, new_state, qp_type(qp), mask,
IB_LINK_LAYER_ETHERNET)) {
pr_warn("invalid mask or state for qp\n");
goto err1;
}
if (mask & IB_QP_STATE) {
if (cur_state == IB_QPS_SQD) {
if (qp->req.state == QP_STATE_DRAIN &&
new_state != IB_QPS_ERR)
goto err1;
}
}
if (mask & IB_QP_PORT) {
if (attr->port_num != 1) {
pr_warn("invalid port %d\n", attr->port_num);
goto err1;
}
}
if (mask & IB_QP_CAP && rxe_qp_chk_cap(rxe, &attr->cap, !!qp->srq))
goto err1;
if (mask & IB_QP_AV && rxe_av_chk_attr(rxe, &attr->ah_attr))
goto err1;
if (mask & IB_QP_ALT_PATH) {
if (rxe_av_chk_attr(rxe, &attr->alt_ah_attr))
goto err1;
if (attr->alt_port_num != 1) {
pr_warn("invalid alt port %d\n", attr->alt_port_num);
goto err1;
}
if (attr->alt_timeout > 31) {
pr_warn("invalid QP alt timeout %d > 31\n",
attr->alt_timeout);
goto err1;
}
}
if (mask & IB_QP_PATH_MTU) {
struct rxe_port *port = &rxe->port;
enum ib_mtu max_mtu = port->attr.max_mtu;
enum ib_mtu mtu = attr->path_mtu;
if (mtu > max_mtu) {
pr_debug("invalid mtu (%d) > (%d)\n",
ib_mtu_enum_to_int(mtu),
ib_mtu_enum_to_int(max_mtu));
goto err1;
}
}
if (mask & IB_QP_MAX_QP_RD_ATOMIC) {
if (attr->max_rd_atomic > rxe->attr.max_qp_rd_atom) {
pr_warn("invalid max_rd_atomic %d > %d\n",
attr->max_rd_atomic,
rxe->attr.max_qp_rd_atom);
goto err1;
}
}
if (mask & IB_QP_TIMEOUT) {
if (attr->timeout > 31) {
pr_warn("invalid QP timeout %d > 31\n",
attr->timeout);
goto err1;
}
}
return 0;
err1:
return -EINVAL;
}
/* move the qp to the reset state */
static void rxe_qp_reset(struct rxe_qp *qp)
{
/* stop tasks from running */
rxe_disable_task(&qp->resp.task);
/* stop request/comp */
if (qp->sq.queue) {
if (qp_type(qp) == IB_QPT_RC)
rxe_disable_task(&qp->comp.task);
rxe_disable_task(&qp->req.task);
}
/* move qp to the reset state */
qp->req.state = QP_STATE_RESET;
qp->resp.state = QP_STATE_RESET;
/* let state machines reset themselves drain work and packet queues
* etc.
*/
__rxe_do_task(&qp->resp.task);
if (qp->sq.queue) {
__rxe_do_task(&qp->comp.task);
__rxe_do_task(&qp->req.task);
}
/* cleanup attributes */
atomic_set(&qp->ssn, 0);
qp->req.opcode = -1;
qp->req.need_retry = 0;
qp->req.noack_pkts = 0;
qp->resp.msn = 0;
qp->resp.opcode = -1;
qp->resp.drop_msg = 0;
qp->resp.goto_error = 0;
qp->resp.sent_psn_nak = 0;
if (qp->resp.mr) {
rxe_drop_ref(qp->resp.mr);
qp->resp.mr = NULL;
}
cleanup_rd_atomic_resources(qp);
/* reenable tasks */
rxe_enable_task(&qp->resp.task);
if (qp->sq.queue) {
if (qp_type(qp) == IB_QPT_RC)
rxe_enable_task(&qp->comp.task);
rxe_enable_task(&qp->req.task);
}
}
/* drain the send queue */
static void rxe_qp_drain(struct rxe_qp *qp)
{
if (qp->sq.queue) {
if (qp->req.state != QP_STATE_DRAINED) {
qp->req.state = QP_STATE_DRAIN;
if (qp_type(qp) == IB_QPT_RC)
rxe_run_task(&qp->comp.task, 1);
else
__rxe_do_task(&qp->comp.task);
rxe_run_task(&qp->req.task, 1);
}
}
}
/* move the qp to the error state */
void rxe_qp_error(struct rxe_qp *qp)
{
qp->req.state = QP_STATE_ERROR;
qp->resp.state = QP_STATE_ERROR;
/* drain work and packet queues */
rxe_run_task(&qp->resp.task, 1);
if (qp_type(qp) == IB_QPT_RC)
rxe_run_task(&qp->comp.task, 1);
else
__rxe_do_task(&qp->comp.task);
rxe_run_task(&qp->req.task, 1);
}
/* called by the modify qp verb */
int rxe_qp_from_attr(struct rxe_qp *qp, struct ib_qp_attr *attr, int mask,
struct ib_udata *udata)
{
int err;
struct rxe_dev *rxe = to_rdev(qp->ibqp.device);
union ib_gid sgid;
struct ib_gid_attr sgid_attr;
if (mask & IB_QP_MAX_QP_RD_ATOMIC) {
int max_rd_atomic = __roundup_pow_of_two(attr->max_rd_atomic);
free_rd_atomic_resources(qp);
err = alloc_rd_atomic_resources(qp, max_rd_atomic);
if (err)
return err;
qp->attr.max_rd_atomic = max_rd_atomic;
atomic_set(&qp->req.rd_atomic, max_rd_atomic);
}
if (mask & IB_QP_CUR_STATE)
qp->attr.cur_qp_state = attr->qp_state;
if (mask & IB_QP_EN_SQD_ASYNC_NOTIFY)
qp->attr.en_sqd_async_notify = attr->en_sqd_async_notify;
if (mask & IB_QP_ACCESS_FLAGS)
qp->attr.qp_access_flags = attr->qp_access_flags;
if (mask & IB_QP_PKEY_INDEX)
qp->attr.pkey_index = attr->pkey_index;
if (mask & IB_QP_PORT)
qp->attr.port_num = attr->port_num;
if (mask & IB_QP_QKEY)
qp->attr.qkey = attr->qkey;
if (mask & IB_QP_AV) {
ib_get_cached_gid(&rxe->ib_dev, 1,
attr->ah_attr.grh.sgid_index, &sgid,
&sgid_attr);
rxe_av_from_attr(rxe, attr->port_num, &qp->pri_av,
&attr->ah_attr);
rxe_av_fill_ip_info(rxe, &qp->pri_av, &attr->ah_attr,
&sgid_attr, &sgid);
if (sgid_attr.ndev)
dev_put(sgid_attr.ndev);
}
if (mask & IB_QP_ALT_PATH) {
ib_get_cached_gid(&rxe->ib_dev, 1,
attr->alt_ah_attr.grh.sgid_index, &sgid,
&sgid_attr);
rxe_av_from_attr(rxe, attr->alt_port_num, &qp->alt_av,
&attr->alt_ah_attr);
rxe_av_fill_ip_info(rxe, &qp->alt_av, &attr->alt_ah_attr,
&sgid_attr, &sgid);
if (sgid_attr.ndev)
dev_put(sgid_attr.ndev);
qp->attr.alt_port_num = attr->alt_port_num;
qp->attr.alt_pkey_index = attr->alt_pkey_index;
qp->attr.alt_timeout = attr->alt_timeout;
}
if (mask & IB_QP_PATH_MTU) {
qp->attr.path_mtu = attr->path_mtu;
qp->mtu = ib_mtu_enum_to_int(attr->path_mtu);
}
if (mask & IB_QP_TIMEOUT) {
qp->attr.timeout = attr->timeout;
if (attr->timeout == 0) {
qp->qp_timeout_jiffies = 0;
} else {
/* According to the spec, timeout = 4.096 * 2 ^ attr->timeout [us] */
int j = nsecs_to_jiffies(4096ULL << attr->timeout);
qp->qp_timeout_jiffies = j ? j : 1;
}
}
if (mask & IB_QP_RETRY_CNT) {
qp->attr.retry_cnt = attr->retry_cnt;
qp->comp.retry_cnt = attr->retry_cnt;
pr_debug("set retry count = %d\n", attr->retry_cnt);
}
if (mask & IB_QP_RNR_RETRY) {
qp->attr.rnr_retry = attr->rnr_retry;
qp->comp.rnr_retry = attr->rnr_retry;
pr_debug("set rnr retry count = %d\n", attr->rnr_retry);
}
if (mask & IB_QP_RQ_PSN) {
qp->attr.rq_psn = (attr->rq_psn & BTH_PSN_MASK);
qp->resp.psn = qp->attr.rq_psn;
pr_debug("set resp psn = 0x%x\n", qp->resp.psn);
}
if (mask & IB_QP_MIN_RNR_TIMER) {
qp->attr.min_rnr_timer = attr->min_rnr_timer;
pr_debug("set min rnr timer = 0x%x\n",
attr->min_rnr_timer);
}
if (mask & IB_QP_SQ_PSN) {
qp->attr.sq_psn = (attr->sq_psn & BTH_PSN_MASK);
qp->req.psn = qp->attr.sq_psn;
qp->comp.psn = qp->attr.sq_psn;
pr_debug("set req psn = 0x%x\n", qp->req.psn);
}
if (mask & IB_QP_MAX_DEST_RD_ATOMIC) {
qp->attr.max_dest_rd_atomic =
__roundup_pow_of_two(attr->max_dest_rd_atomic);
}
if (mask & IB_QP_PATH_MIG_STATE)
qp->attr.path_mig_state = attr->path_mig_state;
if (mask & IB_QP_DEST_QPN)
qp->attr.dest_qp_num = attr->dest_qp_num;
if (mask & IB_QP_STATE) {
qp->attr.qp_state = attr->qp_state;
switch (attr->qp_state) {
case IB_QPS_RESET:
pr_debug("qp state -> RESET\n");
rxe_qp_reset(qp);
break;
case IB_QPS_INIT:
pr_debug("qp state -> INIT\n");
qp->req.state = QP_STATE_INIT;
qp->resp.state = QP_STATE_INIT;
break;
case IB_QPS_RTR:
pr_debug("qp state -> RTR\n");
qp->resp.state = QP_STATE_READY;
break;
case IB_QPS_RTS:
pr_debug("qp state -> RTS\n");
qp->req.state = QP_STATE_READY;
break;
case IB_QPS_SQD:
pr_debug("qp state -> SQD\n");
rxe_qp_drain(qp);
break;
case IB_QPS_SQE:
pr_warn("qp state -> SQE !!?\n");
/* Not possible from modify_qp. */
break;
case IB_QPS_ERR:
pr_debug("qp state -> ERR\n");
rxe_qp_error(qp);
break;
}
}
return 0;
}
/* called by the query qp verb */
int rxe_qp_to_attr(struct rxe_qp *qp, struct ib_qp_attr *attr, int mask)
{
struct rxe_dev *rxe = to_rdev(qp->ibqp.device);
*attr = qp->attr;
attr->rq_psn = qp->resp.psn;
attr->sq_psn = qp->req.psn;
attr->cap.max_send_wr = qp->sq.max_wr;
attr->cap.max_send_sge = qp->sq.max_sge;
attr->cap.max_inline_data = qp->sq.max_inline;
if (!qp->srq) {
attr->cap.max_recv_wr = qp->rq.max_wr;
attr->cap.max_recv_sge = qp->rq.max_sge;
}
rxe_av_to_attr(rxe, &qp->pri_av, &attr->ah_attr);
rxe_av_to_attr(rxe, &qp->alt_av, &attr->alt_ah_attr);
if (qp->req.state == QP_STATE_DRAIN) {
attr->sq_draining = 1;
/* applications that get this state
* typically spin on it. yield the
* processor
*/
cond_resched();
} else {
attr->sq_draining = 0;
}
pr_debug("attr->sq_draining = %d\n", attr->sq_draining);
return 0;
}
/* called by the destroy qp verb */
void rxe_qp_destroy(struct rxe_qp *qp)
{
qp->valid = 0;
qp->qp_timeout_jiffies = 0;
rxe_cleanup_task(&qp->resp.task);
del_timer_sync(&qp->retrans_timer);
del_timer_sync(&qp->rnr_nak_timer);
rxe_cleanup_task(&qp->req.task);
if (qp_type(qp) == IB_QPT_RC)
rxe_cleanup_task(&qp->comp.task);
/* flush out any receive wr's or pending requests */
__rxe_do_task(&qp->req.task);
if (qp->sq.queue) {
__rxe_do_task(&qp->comp.task);
__rxe_do_task(&qp->req.task);
}
}
/* called when the last reference to the qp is dropped */
void rxe_qp_cleanup(void *arg)
{
struct rxe_qp *qp = arg;
rxe_drop_all_mcast_groups(qp);
if (qp->sq.queue)
rxe_queue_cleanup(qp->sq.queue);
if (qp->srq)
rxe_drop_ref(qp->srq);
if (qp->rq.queue)
rxe_queue_cleanup(qp->rq.queue);
if (qp->scq)
rxe_drop_ref(qp->scq);
if (qp->rcq)
rxe_drop_ref(qp->rcq);
if (qp->pd)
rxe_drop_ref(qp->pd);
if (qp->resp.mr) {
rxe_drop_ref(qp->resp.mr);
qp->resp.mr = NULL;
}
free_rd_atomic_resources(qp);
kernel_sock_shutdown(qp->sk, SHUT_RDWR);
}

View File

@ -0,0 +1,217 @@
/*
* Copyright (c) 2016 Mellanox Technologies Ltd. All rights reserved.
* Copyright (c) 2015 System Fabric Works, 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 retailuce 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/vmalloc.h>
#include "rxe.h"
#include "rxe_loc.h"
#include "rxe_queue.h"
int do_mmap_info(struct rxe_dev *rxe,
struct ib_udata *udata,
bool is_req,
struct ib_ucontext *context,
struct rxe_queue_buf *buf,
size_t buf_size,
struct rxe_mmap_info **ip_p)
{
int err;
u32 len, offset;
struct rxe_mmap_info *ip = NULL;
if (udata) {
if (is_req) {
len = udata->outlen - sizeof(struct mminfo);
offset = sizeof(struct mminfo);
} else {
len = udata->outlen;
offset = 0;
}
if (len < sizeof(ip->info))
goto err1;
ip = rxe_create_mmap_info(rxe, buf_size, context, buf);
if (!ip)
goto err1;
err = copy_to_user(udata->outbuf + offset, &ip->info,
sizeof(ip->info));
if (err)
goto err2;
spin_lock_bh(&rxe->pending_lock);
list_add(&ip->pending_mmaps, &rxe->pending_mmaps);
spin_unlock_bh(&rxe->pending_lock);
}
*ip_p = ip;
return 0;
err2:
kfree(ip);
err1:
return -EINVAL;
}
struct rxe_queue *rxe_queue_init(struct rxe_dev *rxe,
int *num_elem,
unsigned int elem_size)
{
struct rxe_queue *q;
size_t buf_size;
unsigned int num_slots;
/* num_elem == 0 is allowed, but uninteresting */
if (*num_elem < 0)
goto err1;
q = kmalloc(sizeof(*q), GFP_KERNEL);
if (!q)
goto err1;
q->rxe = rxe;
/* used in resize, only need to copy used part of queue */
q->elem_size = elem_size;
/* pad element up to at least a cacheline and always a power of 2 */
if (elem_size < cache_line_size())
elem_size = cache_line_size();
elem_size = roundup_pow_of_two(elem_size);
q->log2_elem_size = order_base_2(elem_size);
num_slots = *num_elem + 1;
num_slots = roundup_pow_of_two(num_slots);
q->index_mask = num_slots - 1;
buf_size = sizeof(struct rxe_queue_buf) + num_slots * elem_size;
q->buf = vmalloc_user(buf_size);
if (!q->buf)
goto err2;
q->buf->log2_elem_size = q->log2_elem_size;
q->buf->index_mask = q->index_mask;
q->buf_size = buf_size;
*num_elem = num_slots - 1;
return q;
err2:
kfree(q);
err1:
return NULL;
}
/* copies elements from original q to new q and then swaps the contents of the
* two q headers. This is so that if anyone is holding a pointer to q it will
* still work
*/
static int resize_finish(struct rxe_queue *q, struct rxe_queue *new_q,
unsigned int num_elem)
{
if (!queue_empty(q) && (num_elem < queue_count(q)))
return -EINVAL;
while (!queue_empty(q)) {
memcpy(producer_addr(new_q), consumer_addr(q),
new_q->elem_size);
advance_producer(new_q);
advance_consumer(q);
}
swap(*q, *new_q);
return 0;
}
int rxe_queue_resize(struct rxe_queue *q,
unsigned int *num_elem_p,
unsigned int elem_size,
struct ib_ucontext *context,
struct ib_udata *udata,
spinlock_t *producer_lock,
spinlock_t *consumer_lock)
{
struct rxe_queue *new_q;
unsigned int num_elem = *num_elem_p;
int err;
unsigned long flags = 0, flags1;
new_q = rxe_queue_init(q->rxe, &num_elem, elem_size);
if (!new_q)
return -ENOMEM;
err = do_mmap_info(new_q->rxe, udata, false, context, new_q->buf,
new_q->buf_size, &new_q->ip);
if (err) {
vfree(new_q->buf);
kfree(new_q);
goto err1;
}
spin_lock_irqsave(consumer_lock, flags1);
if (producer_lock) {
spin_lock_irqsave(producer_lock, flags);
err = resize_finish(q, new_q, num_elem);
spin_unlock_irqrestore(producer_lock, flags);
} else {
err = resize_finish(q, new_q, num_elem);
}
spin_unlock_irqrestore(consumer_lock, flags1);
rxe_queue_cleanup(new_q); /* new/old dep on err */
if (err)
goto err1;
*num_elem_p = num_elem;
return 0;
err1:
return err;
}
void rxe_queue_cleanup(struct rxe_queue *q)
{
if (q->ip)
kref_put(&q->ip->ref, rxe_mmap_release);
else
vfree(q->buf);
kfree(q);
}

View File

@ -0,0 +1,178 @@
/*
* Copyright (c) 2016 Mellanox Technologies Ltd. All rights reserved.
* Copyright (c) 2015 System Fabric Works, 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 RXE_QUEUE_H
#define RXE_QUEUE_H
/* implements a simple circular buffer that can optionally be
* shared between user space and the kernel and can be resized
* the requested element size is rounded up to a power of 2
* and the number of elements in the buffer is also rounded
* up to a power of 2. Since the queue is empty when the
* producer and consumer indices match the maximum capacity
* of the queue is one less than the number of element slots
*/
/* this data structure is shared between user space and kernel
* space for those cases where the queue is shared. It contains
* the producer and consumer indices. Is also contains a copy
* of the queue size parameters for user space to use but the
* kernel must use the parameters in the rxe_queue struct
* this MUST MATCH the corresponding librxe struct
* for performance reasons arrange to have producer and consumer
* pointers in separate cache lines
* the kernel should always mask the indices to avoid accessing
* memory outside of the data area
*/
struct rxe_queue_buf {
__u32 log2_elem_size;
__u32 index_mask;
__u32 pad_1[30];
__u32 producer_index;
__u32 pad_2[31];
__u32 consumer_index;
__u32 pad_3[31];
__u8 data[0];
};
struct rxe_queue {
struct rxe_dev *rxe;
struct rxe_queue_buf *buf;
struct rxe_mmap_info *ip;
size_t buf_size;
size_t elem_size;
unsigned int log2_elem_size;
unsigned int index_mask;
};
int do_mmap_info(struct rxe_dev *rxe,
struct ib_udata *udata,
bool is_req,
struct ib_ucontext *context,
struct rxe_queue_buf *buf,
size_t buf_size,
struct rxe_mmap_info **ip_p);
struct rxe_queue *rxe_queue_init(struct rxe_dev *rxe,
int *num_elem,
unsigned int elem_size);
int rxe_queue_resize(struct rxe_queue *q,
unsigned int *num_elem_p,
unsigned int elem_size,
struct ib_ucontext *context,
struct ib_udata *udata,
/* Protect producers while resizing queue */
spinlock_t *producer_lock,
/* Protect consumers while resizing queue */
spinlock_t *consumer_lock);
void rxe_queue_cleanup(struct rxe_queue *queue);
static inline int next_index(struct rxe_queue *q, int index)
{
return (index + 1) & q->buf->index_mask;
}
static inline int queue_empty(struct rxe_queue *q)
{
return ((q->buf->producer_index - q->buf->consumer_index)
& q->index_mask) == 0;
}
static inline int queue_full(struct rxe_queue *q)
{
return ((q->buf->producer_index + 1 - q->buf->consumer_index)
& q->index_mask) == 0;
}
static inline void advance_producer(struct rxe_queue *q)
{
q->buf->producer_index = (q->buf->producer_index + 1)
& q->index_mask;
}
static inline void advance_consumer(struct rxe_queue *q)
{
q->buf->consumer_index = (q->buf->consumer_index + 1)
& q->index_mask;
}
static inline void *producer_addr(struct rxe_queue *q)
{
return q->buf->data + ((q->buf->producer_index & q->index_mask)
<< q->log2_elem_size);
}
static inline void *consumer_addr(struct rxe_queue *q)
{
return q->buf->data + ((q->buf->consumer_index & q->index_mask)
<< q->log2_elem_size);
}
static inline unsigned int producer_index(struct rxe_queue *q)
{
return q->buf->producer_index;
}
static inline unsigned int consumer_index(struct rxe_queue *q)
{
return q->buf->consumer_index;
}
static inline void *addr_from_index(struct rxe_queue *q, unsigned int index)
{
return q->buf->data + ((index & q->index_mask)
<< q->buf->log2_elem_size);
}
static inline unsigned int index_from_addr(const struct rxe_queue *q,
const void *addr)
{
return (((u8 *)addr - q->buf->data) >> q->log2_elem_size)
& q->index_mask;
}
static inline unsigned int queue_count(const struct rxe_queue *q)
{
return (q->buf->producer_index - q->buf->consumer_index)
& q->index_mask;
}
static inline void *queue_head(struct rxe_queue *q)
{
return queue_empty(q) ? NULL : consumer_addr(q);
}
#endif /* RXE_QUEUE_H */

View File

@ -0,0 +1,420 @@
/*
* Copyright (c) 2016 Mellanox Technologies Ltd. All rights reserved.
* Copyright (c) 2015 System Fabric Works, 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/skbuff.h>
#include "rxe.h"
#include "rxe_loc.h"
static int check_type_state(struct rxe_dev *rxe, struct rxe_pkt_info *pkt,
struct rxe_qp *qp)
{
if (unlikely(!qp->valid))
goto err1;
switch (qp_type(qp)) {
case IB_QPT_RC:
if (unlikely((pkt->opcode & IB_OPCODE_RC) != 0)) {
pr_warn_ratelimited("bad qp type\n");
goto err1;
}
break;
case IB_QPT_UC:
if (unlikely(!(pkt->opcode & IB_OPCODE_UC))) {
pr_warn_ratelimited("bad qp type\n");
goto err1;
}
break;
case IB_QPT_UD:
case IB_QPT_SMI:
case IB_QPT_GSI:
if (unlikely(!(pkt->opcode & IB_OPCODE_UD))) {
pr_warn_ratelimited("bad qp type\n");
goto err1;
}
break;
default:
pr_warn_ratelimited("unsupported qp type\n");
goto err1;
}
if (pkt->mask & RXE_REQ_MASK) {
if (unlikely(qp->resp.state != QP_STATE_READY))
goto err1;
} else if (unlikely(qp->req.state < QP_STATE_READY ||
qp->req.state > QP_STATE_DRAINED)) {
goto err1;
}
return 0;
err1:
return -EINVAL;
}
static void set_bad_pkey_cntr(struct rxe_port *port)
{
spin_lock_bh(&port->port_lock);
port->attr.bad_pkey_cntr = min((u32)0xffff,
port->attr.bad_pkey_cntr + 1);
spin_unlock_bh(&port->port_lock);
}
static void set_qkey_viol_cntr(struct rxe_port *port)
{
spin_lock_bh(&port->port_lock);
port->attr.qkey_viol_cntr = min((u32)0xffff,
port->attr.qkey_viol_cntr + 1);
spin_unlock_bh(&port->port_lock);
}
static int check_keys(struct rxe_dev *rxe, struct rxe_pkt_info *pkt,
u32 qpn, struct rxe_qp *qp)
{
int i;
int found_pkey = 0;
struct rxe_port *port = &rxe->port;
u16 pkey = bth_pkey(pkt);
pkt->pkey_index = 0;
if (qpn == 1) {
for (i = 0; i < port->attr.pkey_tbl_len; i++) {
if (pkey_match(pkey, port->pkey_tbl[i])) {
pkt->pkey_index = i;
found_pkey = 1;
break;
}
}
if (!found_pkey) {
pr_warn_ratelimited("bad pkey = 0x%x\n", pkey);
set_bad_pkey_cntr(port);
goto err1;
}
} else if (qpn != 0) {
if (unlikely(!pkey_match(pkey,
port->pkey_tbl[qp->attr.pkey_index]
))) {
pr_warn_ratelimited("bad pkey = 0x%0x\n", pkey);
set_bad_pkey_cntr(port);
goto err1;
}
pkt->pkey_index = qp->attr.pkey_index;
}
if ((qp_type(qp) == IB_QPT_UD || qp_type(qp) == IB_QPT_GSI) &&
qpn != 0 && pkt->mask) {
u32 qkey = (qpn == 1) ? GSI_QKEY : qp->attr.qkey;
if (unlikely(deth_qkey(pkt) != qkey)) {
pr_warn_ratelimited("bad qkey, got 0x%x expected 0x%x for qpn 0x%x\n",
deth_qkey(pkt), qkey, qpn);
set_qkey_viol_cntr(port);
goto err1;
}
}
return 0;
err1:
return -EINVAL;
}
static int check_addr(struct rxe_dev *rxe, struct rxe_pkt_info *pkt,
struct rxe_qp *qp)
{
struct sk_buff *skb = PKT_TO_SKB(pkt);
if (qp_type(qp) != IB_QPT_RC && qp_type(qp) != IB_QPT_UC)
goto done;
if (unlikely(pkt->port_num != qp->attr.port_num)) {
pr_warn_ratelimited("port %d != qp port %d\n",
pkt->port_num, qp->attr.port_num);
goto err1;
}
if (skb->protocol == htons(ETH_P_IP)) {
struct in_addr *saddr =
&qp->pri_av.sgid_addr._sockaddr_in.sin_addr;
struct in_addr *daddr =
&qp->pri_av.dgid_addr._sockaddr_in.sin_addr;
if (ip_hdr(skb)->daddr != saddr->s_addr) {
pr_warn_ratelimited("dst addr %pI4 != qp source addr %pI4\n",
&ip_hdr(skb)->daddr,
&saddr->s_addr);
goto err1;
}
if (ip_hdr(skb)->saddr != daddr->s_addr) {
pr_warn_ratelimited("source addr %pI4 != qp dst addr %pI4\n",
&ip_hdr(skb)->saddr,
&daddr->s_addr);
goto err1;
}
} else if (skb->protocol == htons(ETH_P_IPV6)) {
struct in6_addr *saddr =
&qp->pri_av.sgid_addr._sockaddr_in6.sin6_addr;
struct in6_addr *daddr =
&qp->pri_av.dgid_addr._sockaddr_in6.sin6_addr;
if (memcmp(&ipv6_hdr(skb)->daddr, saddr, sizeof(*saddr))) {
pr_warn_ratelimited("dst addr %pI6 != qp source addr %pI6\n",
&ipv6_hdr(skb)->daddr, saddr);
goto err1;
}
if (memcmp(&ipv6_hdr(skb)->saddr, daddr, sizeof(*daddr))) {
pr_warn_ratelimited("source addr %pI6 != qp dst addr %pI6\n",
&ipv6_hdr(skb)->saddr, daddr);
goto err1;
}
}
done:
return 0;
err1:
return -EINVAL;
}
static int hdr_check(struct rxe_pkt_info *pkt)
{
struct rxe_dev *rxe = pkt->rxe;
struct rxe_port *port = &rxe->port;
struct rxe_qp *qp = NULL;
u32 qpn = bth_qpn(pkt);
int index;
int err;
if (unlikely(bth_tver(pkt) != BTH_TVER)) {
pr_warn_ratelimited("bad tver\n");
goto err1;
}
if (qpn != IB_MULTICAST_QPN) {
index = (qpn == 0) ? port->qp_smi_index :
((qpn == 1) ? port->qp_gsi_index : qpn);
qp = rxe_pool_get_index(&rxe->qp_pool, index);
if (unlikely(!qp)) {
pr_warn_ratelimited("no qp matches qpn 0x%x\n", qpn);
goto err1;
}
err = check_type_state(rxe, pkt, qp);
if (unlikely(err))
goto err2;
err = check_addr(rxe, pkt, qp);
if (unlikely(err))
goto err2;
err = check_keys(rxe, pkt, qpn, qp);
if (unlikely(err))
goto err2;
} else {
if (unlikely((pkt->mask & RXE_GRH_MASK) == 0)) {
pr_warn_ratelimited("no grh for mcast qpn\n");
goto err1;
}
}
pkt->qp = qp;
return 0;
err2:
if (qp)
rxe_drop_ref(qp);
err1:
return -EINVAL;
}
static inline void rxe_rcv_pkt(struct rxe_dev *rxe,
struct rxe_pkt_info *pkt,
struct sk_buff *skb)
{
if (pkt->mask & RXE_REQ_MASK)
rxe_resp_queue_pkt(rxe, pkt->qp, skb);
else
rxe_comp_queue_pkt(rxe, pkt->qp, skb);
}
static void rxe_rcv_mcast_pkt(struct rxe_dev *rxe, struct sk_buff *skb)
{
struct rxe_pkt_info *pkt = SKB_TO_PKT(skb);
struct rxe_mc_grp *mcg;
struct sk_buff *skb_copy;
struct rxe_mc_elem *mce;
struct rxe_qp *qp;
union ib_gid dgid;
int err;
if (skb->protocol == htons(ETH_P_IP))
ipv6_addr_set_v4mapped(ip_hdr(skb)->daddr,
(struct in6_addr *)&dgid);
else if (skb->protocol == htons(ETH_P_IPV6))
memcpy(&dgid, &ipv6_hdr(skb)->daddr, sizeof(dgid));
/* lookup mcast group corresponding to mgid, takes a ref */
mcg = rxe_pool_get_key(&rxe->mc_grp_pool, &dgid);
if (!mcg)
goto err1; /* mcast group not registered */
spin_lock_bh(&mcg->mcg_lock);
list_for_each_entry(mce, &mcg->qp_list, qp_list) {
qp = mce->qp;
pkt = SKB_TO_PKT(skb);
/* validate qp for incoming packet */
err = check_type_state(rxe, pkt, qp);
if (err)
continue;
err = check_keys(rxe, pkt, bth_qpn(pkt), qp);
if (err)
continue;
/* if *not* the last qp in the list
* make a copy of the skb to post to the next qp
*/
skb_copy = (mce->qp_list.next != &mcg->qp_list) ?
skb_clone(skb, GFP_KERNEL) : NULL;
pkt->qp = qp;
rxe_add_ref(qp);
rxe_rcv_pkt(rxe, pkt, skb);
skb = skb_copy;
if (!skb)
break;
}
spin_unlock_bh(&mcg->mcg_lock);
rxe_drop_ref(mcg); /* drop ref from rxe_pool_get_key. */
err1:
if (skb)
kfree_skb(skb);
}
static int rxe_match_dgid(struct rxe_dev *rxe, struct sk_buff *skb)
{
union ib_gid dgid;
union ib_gid *pdgid;
u16 index;
if (skb->protocol == htons(ETH_P_IP)) {
ipv6_addr_set_v4mapped(ip_hdr(skb)->daddr,
(struct in6_addr *)&dgid);
pdgid = &dgid;
} else {
pdgid = (union ib_gid *)&ipv6_hdr(skb)->daddr;
}
return ib_find_cached_gid_by_port(&rxe->ib_dev, pdgid,
IB_GID_TYPE_ROCE_UDP_ENCAP,
1, rxe->ndev, &index);
}
/* rxe_rcv is called from the interface driver */
int rxe_rcv(struct sk_buff *skb)
{
int err;
struct rxe_pkt_info *pkt = SKB_TO_PKT(skb);
struct rxe_dev *rxe = pkt->rxe;
__be32 *icrcp;
u32 calc_icrc, pack_icrc;
pkt->offset = 0;
if (unlikely(skb->len < pkt->offset + RXE_BTH_BYTES))
goto drop;
if (unlikely(rxe_match_dgid(rxe, skb) < 0)) {
pr_warn_ratelimited("failed matching dgid\n");
goto drop;
}
pkt->opcode = bth_opcode(pkt);
pkt->psn = bth_psn(pkt);
pkt->qp = NULL;
pkt->mask |= rxe_opcode[pkt->opcode].mask;
if (unlikely(skb->len < header_size(pkt)))
goto drop;
err = hdr_check(pkt);
if (unlikely(err))
goto drop;
/* Verify ICRC */
icrcp = (__be32 *)(pkt->hdr + pkt->paylen - RXE_ICRC_SIZE);
pack_icrc = be32_to_cpu(*icrcp);
calc_icrc = rxe_icrc_hdr(pkt, skb);
calc_icrc = crc32_le(calc_icrc, (u8 *)payload_addr(pkt), payload_size(pkt));
calc_icrc = cpu_to_be32(~calc_icrc);
if (unlikely(calc_icrc != pack_icrc)) {
char saddr[sizeof(struct in6_addr)];
if (skb->protocol == htons(ETH_P_IPV6))
sprintf(saddr, "%pI6", &ipv6_hdr(skb)->saddr);
else if (skb->protocol == htons(ETH_P_IP))
sprintf(saddr, "%pI4", &ip_hdr(skb)->saddr);
else
sprintf(saddr, "unknown");
pr_warn_ratelimited("bad ICRC from %s\n", saddr);
goto drop;
}
if (unlikely(bth_qpn(pkt) == IB_MULTICAST_QPN))
rxe_rcv_mcast_pkt(rxe, skb);
else
rxe_rcv_pkt(rxe, pkt, skb);
return 0;
drop:
if (pkt->qp)
rxe_drop_ref(pkt->qp);
kfree_skb(skb);
return 0;
}
EXPORT_SYMBOL(rxe_rcv);

View File

@ -0,0 +1,726 @@
/*
* Copyright (c) 2016 Mellanox Technologies Ltd. All rights reserved.
* Copyright (c) 2015 System Fabric Works, 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/skbuff.h>
#include "rxe.h"
#include "rxe_loc.h"
#include "rxe_queue.h"
static int next_opcode(struct rxe_qp *qp, struct rxe_send_wqe *wqe,
unsigned opcode);
static inline void retry_first_write_send(struct rxe_qp *qp,
struct rxe_send_wqe *wqe,
unsigned mask, int npsn)
{
int i;
for (i = 0; i < npsn; i++) {
int to_send = (wqe->dma.resid > qp->mtu) ?
qp->mtu : wqe->dma.resid;
qp->req.opcode = next_opcode(qp, wqe,
wqe->wr.opcode);
if (wqe->wr.send_flags & IB_SEND_INLINE) {
wqe->dma.resid -= to_send;
wqe->dma.sge_offset += to_send;
} else {
advance_dma_data(&wqe->dma, to_send);
}
if (mask & WR_WRITE_MASK)
wqe->iova += qp->mtu;
}
}
static void req_retry(struct rxe_qp *qp)
{
struct rxe_send_wqe *wqe;
unsigned int wqe_index;
unsigned int mask;
int npsn;
int first = 1;
wqe = queue_head(qp->sq.queue);
npsn = (qp->comp.psn - wqe->first_psn) & BTH_PSN_MASK;
qp->req.wqe_index = consumer_index(qp->sq.queue);
qp->req.psn = qp->comp.psn;
qp->req.opcode = -1;
for (wqe_index = consumer_index(qp->sq.queue);
wqe_index != producer_index(qp->sq.queue);
wqe_index = next_index(qp->sq.queue, wqe_index)) {
wqe = addr_from_index(qp->sq.queue, wqe_index);
mask = wr_opcode_mask(wqe->wr.opcode, qp);
if (wqe->state == wqe_state_posted)
break;
if (wqe->state == wqe_state_done)
continue;
wqe->iova = (mask & WR_ATOMIC_MASK) ?
wqe->wr.wr.atomic.remote_addr :
(mask & WR_READ_OR_WRITE_MASK) ?
wqe->wr.wr.rdma.remote_addr :
0;
if (!first || (mask & WR_READ_MASK) == 0) {
wqe->dma.resid = wqe->dma.length;
wqe->dma.cur_sge = 0;
wqe->dma.sge_offset = 0;
}
if (first) {
first = 0;
if (mask & WR_WRITE_OR_SEND_MASK)
retry_first_write_send(qp, wqe, mask, npsn);
if (mask & WR_READ_MASK)
wqe->iova += npsn * qp->mtu;
}
wqe->state = wqe_state_posted;
}
}
void rnr_nak_timer(unsigned long data)
{
struct rxe_qp *qp = (struct rxe_qp *)data;
pr_debug("rnr nak timer fired\n");
rxe_run_task(&qp->req.task, 1);
}
static struct rxe_send_wqe *req_next_wqe(struct rxe_qp *qp)
{
struct rxe_send_wqe *wqe = queue_head(qp->sq.queue);
unsigned long flags;
if (unlikely(qp->req.state == QP_STATE_DRAIN)) {
/* check to see if we are drained;
* state_lock used by requester and completer
*/
spin_lock_irqsave(&qp->state_lock, flags);
do {
if (qp->req.state != QP_STATE_DRAIN) {
/* comp just finished */
spin_unlock_irqrestore(&qp->state_lock,
flags);
break;
}
if (wqe && ((qp->req.wqe_index !=
consumer_index(qp->sq.queue)) ||
(wqe->state != wqe_state_posted))) {
/* comp not done yet */
spin_unlock_irqrestore(&qp->state_lock,
flags);
break;
}
qp->req.state = QP_STATE_DRAINED;
spin_unlock_irqrestore(&qp->state_lock, flags);
if (qp->ibqp.event_handler) {
struct ib_event ev;
ev.device = qp->ibqp.device;
ev.element.qp = &qp->ibqp;
ev.event = IB_EVENT_SQ_DRAINED;
qp->ibqp.event_handler(&ev,
qp->ibqp.qp_context);
}
} while (0);
}
if (qp->req.wqe_index == producer_index(qp->sq.queue))
return NULL;
wqe = addr_from_index(qp->sq.queue, qp->req.wqe_index);
if (unlikely((qp->req.state == QP_STATE_DRAIN ||
qp->req.state == QP_STATE_DRAINED) &&
(wqe->state != wqe_state_processing)))
return NULL;
if (unlikely((wqe->wr.send_flags & IB_SEND_FENCE) &&
(qp->req.wqe_index != consumer_index(qp->sq.queue)))) {
qp->req.wait_fence = 1;
return NULL;
}
wqe->mask = wr_opcode_mask(wqe->wr.opcode, qp);
return wqe;
}
static int next_opcode_rc(struct rxe_qp *qp, unsigned opcode, int fits)
{
switch (opcode) {
case IB_WR_RDMA_WRITE:
if (qp->req.opcode == IB_OPCODE_RC_RDMA_WRITE_FIRST ||
qp->req.opcode == IB_OPCODE_RC_RDMA_WRITE_MIDDLE)
return fits ?
IB_OPCODE_RC_RDMA_WRITE_LAST :
IB_OPCODE_RC_RDMA_WRITE_MIDDLE;
else
return fits ?
IB_OPCODE_RC_RDMA_WRITE_ONLY :
IB_OPCODE_RC_RDMA_WRITE_FIRST;
case IB_WR_RDMA_WRITE_WITH_IMM:
if (qp->req.opcode == IB_OPCODE_RC_RDMA_WRITE_FIRST ||
qp->req.opcode == IB_OPCODE_RC_RDMA_WRITE_MIDDLE)
return fits ?
IB_OPCODE_RC_RDMA_WRITE_LAST_WITH_IMMEDIATE :
IB_OPCODE_RC_RDMA_WRITE_MIDDLE;
else
return fits ?
IB_OPCODE_RC_RDMA_WRITE_ONLY_WITH_IMMEDIATE :
IB_OPCODE_RC_RDMA_WRITE_FIRST;
case IB_WR_SEND:
if (qp->req.opcode == IB_OPCODE_RC_SEND_FIRST ||
qp->req.opcode == IB_OPCODE_RC_SEND_MIDDLE)
return fits ?
IB_OPCODE_RC_SEND_LAST :
IB_OPCODE_RC_SEND_MIDDLE;
else
return fits ?
IB_OPCODE_RC_SEND_ONLY :
IB_OPCODE_RC_SEND_FIRST;
case IB_WR_SEND_WITH_IMM:
if (qp->req.opcode == IB_OPCODE_RC_SEND_FIRST ||
qp->req.opcode == IB_OPCODE_RC_SEND_MIDDLE)
return fits ?
IB_OPCODE_RC_SEND_LAST_WITH_IMMEDIATE :
IB_OPCODE_RC_SEND_MIDDLE;
else
return fits ?
IB_OPCODE_RC_SEND_ONLY_WITH_IMMEDIATE :
IB_OPCODE_RC_SEND_FIRST;
case IB_WR_RDMA_READ:
return IB_OPCODE_RC_RDMA_READ_REQUEST;
case IB_WR_ATOMIC_CMP_AND_SWP:
return IB_OPCODE_RC_COMPARE_SWAP;
case IB_WR_ATOMIC_FETCH_AND_ADD:
return IB_OPCODE_RC_FETCH_ADD;
case IB_WR_SEND_WITH_INV:
if (qp->req.opcode == IB_OPCODE_RC_SEND_FIRST ||
qp->req.opcode == IB_OPCODE_RC_SEND_MIDDLE)
return fits ? IB_OPCODE_RC_SEND_LAST_WITH_INVALIDATE :
IB_OPCODE_RC_SEND_MIDDLE;
else
return fits ? IB_OPCODE_RC_SEND_ONLY_WITH_INVALIDATE :
IB_OPCODE_RC_SEND_FIRST;
case IB_WR_REG_MR:
case IB_WR_LOCAL_INV:
return opcode;
}
return -EINVAL;
}
static int next_opcode_uc(struct rxe_qp *qp, unsigned opcode, int fits)
{
switch (opcode) {
case IB_WR_RDMA_WRITE:
if (qp->req.opcode == IB_OPCODE_UC_RDMA_WRITE_FIRST ||
qp->req.opcode == IB_OPCODE_UC_RDMA_WRITE_MIDDLE)
return fits ?
IB_OPCODE_UC_RDMA_WRITE_LAST :
IB_OPCODE_UC_RDMA_WRITE_MIDDLE;
else
return fits ?
IB_OPCODE_UC_RDMA_WRITE_ONLY :
IB_OPCODE_UC_RDMA_WRITE_FIRST;
case IB_WR_RDMA_WRITE_WITH_IMM:
if (qp->req.opcode == IB_OPCODE_UC_RDMA_WRITE_FIRST ||
qp->req.opcode == IB_OPCODE_UC_RDMA_WRITE_MIDDLE)
return fits ?
IB_OPCODE_UC_RDMA_WRITE_LAST_WITH_IMMEDIATE :
IB_OPCODE_UC_RDMA_WRITE_MIDDLE;
else
return fits ?
IB_OPCODE_UC_RDMA_WRITE_ONLY_WITH_IMMEDIATE :
IB_OPCODE_UC_RDMA_WRITE_FIRST;
case IB_WR_SEND:
if (qp->req.opcode == IB_OPCODE_UC_SEND_FIRST ||
qp->req.opcode == IB_OPCODE_UC_SEND_MIDDLE)
return fits ?
IB_OPCODE_UC_SEND_LAST :
IB_OPCODE_UC_SEND_MIDDLE;
else
return fits ?
IB_OPCODE_UC_SEND_ONLY :
IB_OPCODE_UC_SEND_FIRST;
case IB_WR_SEND_WITH_IMM:
if (qp->req.opcode == IB_OPCODE_UC_SEND_FIRST ||
qp->req.opcode == IB_OPCODE_UC_SEND_MIDDLE)
return fits ?
IB_OPCODE_UC_SEND_LAST_WITH_IMMEDIATE :
IB_OPCODE_UC_SEND_MIDDLE;
else
return fits ?
IB_OPCODE_UC_SEND_ONLY_WITH_IMMEDIATE :
IB_OPCODE_UC_SEND_FIRST;
}
return -EINVAL;
}
static int next_opcode(struct rxe_qp *qp, struct rxe_send_wqe *wqe,
unsigned opcode)
{
int fits = (wqe->dma.resid <= qp->mtu);
switch (qp_type(qp)) {
case IB_QPT_RC:
return next_opcode_rc(qp, opcode, fits);
case IB_QPT_UC:
return next_opcode_uc(qp, opcode, fits);
case IB_QPT_SMI:
case IB_QPT_UD:
case IB_QPT_GSI:
switch (opcode) {
case IB_WR_SEND:
return IB_OPCODE_UD_SEND_ONLY;
case IB_WR_SEND_WITH_IMM:
return IB_OPCODE_UD_SEND_ONLY_WITH_IMMEDIATE;
}
break;
default:
break;
}
return -EINVAL;
}
static inline int check_init_depth(struct rxe_qp *qp, struct rxe_send_wqe *wqe)
{
int depth;
if (wqe->has_rd_atomic)
return 0;
qp->req.need_rd_atomic = 1;
depth = atomic_dec_return(&qp->req.rd_atomic);
if (depth >= 0) {
qp->req.need_rd_atomic = 0;
wqe->has_rd_atomic = 1;
return 0;
}
atomic_inc(&qp->req.rd_atomic);
return -EAGAIN;
}
static inline int get_mtu(struct rxe_qp *qp, struct rxe_send_wqe *wqe)
{
struct rxe_dev *rxe = to_rdev(qp->ibqp.device);
struct rxe_port *port;
struct rxe_av *av;
if ((qp_type(qp) == IB_QPT_RC) || (qp_type(qp) == IB_QPT_UC))
return qp->mtu;
av = &wqe->av;
port = &rxe->port;
return port->mtu_cap;
}
static struct sk_buff *init_req_packet(struct rxe_qp *qp,
struct rxe_send_wqe *wqe,
int opcode, int payload,
struct rxe_pkt_info *pkt)
{
struct rxe_dev *rxe = to_rdev(qp->ibqp.device);
struct rxe_port *port = &rxe->port;
struct sk_buff *skb;
struct rxe_send_wr *ibwr = &wqe->wr;
struct rxe_av *av;
int pad = (-payload) & 0x3;
int paylen;
int solicited;
u16 pkey;
u32 qp_num;
int ack_req;
/* length from start of bth to end of icrc */
paylen = rxe_opcode[opcode].length + payload + pad + RXE_ICRC_SIZE;
/* pkt->hdr, rxe, port_num and mask are initialized in ifc
* layer
*/
pkt->opcode = opcode;
pkt->qp = qp;
pkt->psn = qp->req.psn;
pkt->mask = rxe_opcode[opcode].mask;
pkt->paylen = paylen;
pkt->offset = 0;
pkt->wqe = wqe;
/* init skb */
av = rxe_get_av(pkt);
skb = rxe->ifc_ops->init_packet(rxe, av, paylen, pkt);
if (unlikely(!skb))
return NULL;
/* init bth */
solicited = (ibwr->send_flags & IB_SEND_SOLICITED) &&
(pkt->mask & RXE_END_MASK) &&
((pkt->mask & (RXE_SEND_MASK)) ||
(pkt->mask & (RXE_WRITE_MASK | RXE_IMMDT_MASK)) ==
(RXE_WRITE_MASK | RXE_IMMDT_MASK));
pkey = (qp_type(qp) == IB_QPT_GSI) ?
port->pkey_tbl[ibwr->wr.ud.pkey_index] :
port->pkey_tbl[qp->attr.pkey_index];
qp_num = (pkt->mask & RXE_DETH_MASK) ? ibwr->wr.ud.remote_qpn :
qp->attr.dest_qp_num;
ack_req = ((pkt->mask & RXE_END_MASK) ||
(qp->req.noack_pkts++ > RXE_MAX_PKT_PER_ACK));
if (ack_req)
qp->req.noack_pkts = 0;
bth_init(pkt, pkt->opcode, solicited, 0, pad, pkey, qp_num,
ack_req, pkt->psn);
/* init optional headers */
if (pkt->mask & RXE_RETH_MASK) {
reth_set_rkey(pkt, ibwr->wr.rdma.rkey);
reth_set_va(pkt, wqe->iova);
reth_set_len(pkt, wqe->dma.length);
}
if (pkt->mask & RXE_IMMDT_MASK)
immdt_set_imm(pkt, ibwr->ex.imm_data);
if (pkt->mask & RXE_IETH_MASK)
ieth_set_rkey(pkt, ibwr->ex.invalidate_rkey);
if (pkt->mask & RXE_ATMETH_MASK) {
atmeth_set_va(pkt, wqe->iova);
if (opcode == IB_OPCODE_RC_COMPARE_SWAP ||
opcode == IB_OPCODE_RD_COMPARE_SWAP) {
atmeth_set_swap_add(pkt, ibwr->wr.atomic.swap);
atmeth_set_comp(pkt, ibwr->wr.atomic.compare_add);
} else {
atmeth_set_swap_add(pkt, ibwr->wr.atomic.compare_add);
}
atmeth_set_rkey(pkt, ibwr->wr.atomic.rkey);
}
if (pkt->mask & RXE_DETH_MASK) {
if (qp->ibqp.qp_num == 1)
deth_set_qkey(pkt, GSI_QKEY);
else
deth_set_qkey(pkt, ibwr->wr.ud.remote_qkey);
deth_set_sqp(pkt, qp->ibqp.qp_num);
}
return skb;
}
static int fill_packet(struct rxe_qp *qp, struct rxe_send_wqe *wqe,
struct rxe_pkt_info *pkt, struct sk_buff *skb,
int paylen)
{
struct rxe_dev *rxe = to_rdev(qp->ibqp.device);
u32 crc = 0;
u32 *p;
int err;
err = rxe->ifc_ops->prepare(rxe, pkt, skb, &crc);
if (err)
return err;
if (pkt->mask & RXE_WRITE_OR_SEND) {
if (wqe->wr.send_flags & IB_SEND_INLINE) {
u8 *tmp = &wqe->dma.inline_data[wqe->dma.sge_offset];
crc = crc32_le(crc, tmp, paylen);
memcpy(payload_addr(pkt), tmp, paylen);
wqe->dma.resid -= paylen;
wqe->dma.sge_offset += paylen;
} else {
err = copy_data(rxe, qp->pd, 0, &wqe->dma,
payload_addr(pkt), paylen,
from_mem_obj,
&crc);
if (err)
return err;
}
}
p = payload_addr(pkt) + paylen + bth_pad(pkt);
*p = ~crc;
return 0;
}
static void update_wqe_state(struct rxe_qp *qp,
struct rxe_send_wqe *wqe,
struct rxe_pkt_info *pkt,
enum wqe_state *prev_state)
{
enum wqe_state prev_state_ = wqe->state;
if (pkt->mask & RXE_END_MASK) {
if (qp_type(qp) == IB_QPT_RC)
wqe->state = wqe_state_pending;
} else {
wqe->state = wqe_state_processing;
}
*prev_state = prev_state_;
}
static void update_state(struct rxe_qp *qp, struct rxe_send_wqe *wqe,
struct rxe_pkt_info *pkt, int payload)
{
/* number of packets left to send including current one */
int num_pkt = (wqe->dma.resid + payload + qp->mtu - 1) / qp->mtu;
/* handle zero length packet case */
if (num_pkt == 0)
num_pkt = 1;
if (pkt->mask & RXE_START_MASK) {
wqe->first_psn = qp->req.psn;
wqe->last_psn = (qp->req.psn + num_pkt - 1) & BTH_PSN_MASK;
}
if (pkt->mask & RXE_READ_MASK)
qp->req.psn = (wqe->first_psn + num_pkt) & BTH_PSN_MASK;
else
qp->req.psn = (qp->req.psn + 1) & BTH_PSN_MASK;
qp->req.opcode = pkt->opcode;
if (pkt->mask & RXE_END_MASK)
qp->req.wqe_index = next_index(qp->sq.queue, qp->req.wqe_index);
qp->need_req_skb = 0;
if (qp->qp_timeout_jiffies && !timer_pending(&qp->retrans_timer))
mod_timer(&qp->retrans_timer,
jiffies + qp->qp_timeout_jiffies);
}
int rxe_requester(void *arg)
{
struct rxe_qp *qp = (struct rxe_qp *)arg;
struct rxe_pkt_info pkt;
struct sk_buff *skb;
struct rxe_send_wqe *wqe;
unsigned mask;
int payload;
int mtu;
int opcode;
int ret;
enum wqe_state prev_state;
next_wqe:
if (unlikely(!qp->valid || qp->req.state == QP_STATE_ERROR))
goto exit;
if (unlikely(qp->req.state == QP_STATE_RESET)) {
qp->req.wqe_index = consumer_index(qp->sq.queue);
qp->req.opcode = -1;
qp->req.need_rd_atomic = 0;
qp->req.wait_psn = 0;
qp->req.need_retry = 0;
goto exit;
}
if (unlikely(qp->req.need_retry)) {
req_retry(qp);
qp->req.need_retry = 0;
}
wqe = req_next_wqe(qp);
if (unlikely(!wqe))
goto exit;
if (wqe->mask & WR_REG_MASK) {
if (wqe->wr.opcode == IB_WR_LOCAL_INV) {
struct rxe_dev *rxe = to_rdev(qp->ibqp.device);
struct rxe_mem *rmr;
rmr = rxe_pool_get_index(&rxe->mr_pool,
wqe->wr.ex.invalidate_rkey >> 8);
if (!rmr) {
pr_err("No mr for key %#x\n", wqe->wr.ex.invalidate_rkey);
wqe->state = wqe_state_error;
wqe->status = IB_WC_MW_BIND_ERR;
goto exit;
}
rmr->state = RXE_MEM_STATE_FREE;
wqe->state = wqe_state_done;
wqe->status = IB_WC_SUCCESS;
} else if (wqe->wr.opcode == IB_WR_REG_MR) {
struct rxe_mem *rmr = to_rmr(wqe->wr.wr.reg.mr);
rmr->state = RXE_MEM_STATE_VALID;
rmr->access = wqe->wr.wr.reg.access;
rmr->lkey = wqe->wr.wr.reg.key;
rmr->rkey = wqe->wr.wr.reg.key;
wqe->state = wqe_state_done;
wqe->status = IB_WC_SUCCESS;
} else {
goto exit;
}
qp->req.wqe_index = next_index(qp->sq.queue,
qp->req.wqe_index);
goto next_wqe;
}
if (unlikely(qp_type(qp) == IB_QPT_RC &&
qp->req.psn > (qp->comp.psn + RXE_MAX_UNACKED_PSNS))) {
qp->req.wait_psn = 1;
goto exit;
}
/* Limit the number of inflight SKBs per QP */
if (unlikely(atomic_read(&qp->skb_out) >
RXE_INFLIGHT_SKBS_PER_QP_HIGH)) {
qp->need_req_skb = 1;
goto exit;
}
opcode = next_opcode(qp, wqe, wqe->wr.opcode);
if (unlikely(opcode < 0)) {
wqe->status = IB_WC_LOC_QP_OP_ERR;
goto exit;
}
mask = rxe_opcode[opcode].mask;
if (unlikely(mask & RXE_READ_OR_ATOMIC)) {
if (check_init_depth(qp, wqe))
goto exit;
}
mtu = get_mtu(qp, wqe);
payload = (mask & RXE_WRITE_OR_SEND) ? wqe->dma.resid : 0;
if (payload > mtu) {
if (qp_type(qp) == IB_QPT_UD) {
/* C10-93.1.1: If the total sum of all the buffer lengths specified for a
* UD message exceeds the MTU of the port as returned by QueryHCA, the CI
* shall not emit any packets for this message. Further, the CI shall not
* generate an error due to this condition.
*/
/* fake a successful UD send */
wqe->first_psn = qp->req.psn;
wqe->last_psn = qp->req.psn;
qp->req.psn = (qp->req.psn + 1) & BTH_PSN_MASK;
qp->req.opcode = IB_OPCODE_UD_SEND_ONLY;
qp->req.wqe_index = next_index(qp->sq.queue,
qp->req.wqe_index);
wqe->state = wqe_state_done;
wqe->status = IB_WC_SUCCESS;
goto complete;
}
payload = mtu;
}
skb = init_req_packet(qp, wqe, opcode, payload, &pkt);
if (unlikely(!skb)) {
pr_err("Failed allocating skb\n");
goto err;
}
if (fill_packet(qp, wqe, &pkt, skb, payload)) {
pr_debug("Error during fill packet\n");
goto err;
}
update_wqe_state(qp, wqe, &pkt, &prev_state);
ret = rxe_xmit_packet(to_rdev(qp->ibqp.device), qp, &pkt, skb);
if (ret) {
qp->need_req_skb = 1;
kfree_skb(skb);
wqe->state = prev_state;
if (ret == -EAGAIN) {
rxe_run_task(&qp->req.task, 1);
goto exit;
}
goto err;
}
update_state(qp, wqe, &pkt, payload);
goto next_wqe;
err:
kfree_skb(skb);
wqe->status = IB_WC_LOC_PROT_ERR;
wqe->state = wqe_state_error;
complete:
if (qp_type(qp) != IB_QPT_RC) {
while (rxe_completer(qp) == 0)
;
}
return 0;
exit:
return -EAGAIN;
}

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,193 @@
/*
* Copyright (c) 2016 Mellanox Technologies Ltd. All rights reserved.
* Copyright (c) 2015 System Fabric Works, 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 "rxe.h"
#include "rxe_loc.h"
#include "rxe_queue.h"
int rxe_srq_chk_attr(struct rxe_dev *rxe, struct rxe_srq *srq,
struct ib_srq_attr *attr, enum ib_srq_attr_mask mask)
{
if (srq && srq->error) {
pr_warn("srq in error state\n");
goto err1;
}
if (mask & IB_SRQ_MAX_WR) {
if (attr->max_wr > rxe->attr.max_srq_wr) {
pr_warn("max_wr(%d) > max_srq_wr(%d)\n",
attr->max_wr, rxe->attr.max_srq_wr);
goto err1;
}
if (attr->max_wr <= 0) {
pr_warn("max_wr(%d) <= 0\n", attr->max_wr);
goto err1;
}
if (srq && srq->limit && (attr->max_wr < srq->limit)) {
pr_warn("max_wr (%d) < srq->limit (%d)\n",
attr->max_wr, srq->limit);
goto err1;
}
if (attr->max_wr < RXE_MIN_SRQ_WR)
attr->max_wr = RXE_MIN_SRQ_WR;
}
if (mask & IB_SRQ_LIMIT) {
if (attr->srq_limit > rxe->attr.max_srq_wr) {
pr_warn("srq_limit(%d) > max_srq_wr(%d)\n",
attr->srq_limit, rxe->attr.max_srq_wr);
goto err1;
}
if (srq && (attr->srq_limit > srq->rq.queue->buf->index_mask)) {
pr_warn("srq_limit (%d) > cur limit(%d)\n",
attr->srq_limit,
srq->rq.queue->buf->index_mask);
goto err1;
}
}
if (mask == IB_SRQ_INIT_MASK) {
if (attr->max_sge > rxe->attr.max_srq_sge) {
pr_warn("max_sge(%d) > max_srq_sge(%d)\n",
attr->max_sge, rxe->attr.max_srq_sge);
goto err1;
}
if (attr->max_sge < RXE_MIN_SRQ_SGE)
attr->max_sge = RXE_MIN_SRQ_SGE;
}
return 0;
err1:
return -EINVAL;
}
int rxe_srq_from_init(struct rxe_dev *rxe, struct rxe_srq *srq,
struct ib_srq_init_attr *init,
struct ib_ucontext *context, struct ib_udata *udata)
{
int err;
int srq_wqe_size;
struct rxe_queue *q;
srq->ibsrq.event_handler = init->event_handler;
srq->ibsrq.srq_context = init->srq_context;
srq->limit = init->attr.srq_limit;
srq->srq_num = srq->pelem.index;
srq->rq.max_wr = init->attr.max_wr;
srq->rq.max_sge = init->attr.max_sge;
srq_wqe_size = rcv_wqe_size(srq->rq.max_sge);
spin_lock_init(&srq->rq.producer_lock);
spin_lock_init(&srq->rq.consumer_lock);
q = rxe_queue_init(rxe, &srq->rq.max_wr,
srq_wqe_size);
if (!q) {
pr_warn("unable to allocate queue for srq\n");
return -ENOMEM;
}
srq->rq.queue = q;
err = do_mmap_info(rxe, udata, false, context, q->buf,
q->buf_size, &q->ip);
if (err)
return err;
if (udata && udata->outlen >= sizeof(struct mminfo) + sizeof(u32)) {
if (copy_to_user(udata->outbuf + sizeof(struct mminfo),
&srq->srq_num, sizeof(u32)))
return -EFAULT;
}
return 0;
}
int rxe_srq_from_attr(struct rxe_dev *rxe, struct rxe_srq *srq,
struct ib_srq_attr *attr, enum ib_srq_attr_mask mask,
struct ib_udata *udata)
{
int err;
struct rxe_queue *q = srq->rq.queue;
struct mminfo mi = { .offset = 1, .size = 0};
if (mask & IB_SRQ_MAX_WR) {
/* Check that we can write the mminfo struct to user space */
if (udata && udata->inlen >= sizeof(__u64)) {
__u64 mi_addr;
/* Get address of user space mminfo struct */
err = ib_copy_from_udata(&mi_addr, udata,
sizeof(mi_addr));
if (err)
goto err1;
udata->outbuf = (void __user *)(unsigned long)mi_addr;
udata->outlen = sizeof(mi);
if (!access_ok(VERIFY_WRITE,
(void __user *)udata->outbuf,
udata->outlen)) {
err = -EFAULT;
goto err1;
}
}
err = rxe_queue_resize(q, (unsigned int *)&attr->max_wr,
rcv_wqe_size(srq->rq.max_sge),
srq->rq.queue->ip ?
srq->rq.queue->ip->context :
NULL,
udata, &srq->rq.producer_lock,
&srq->rq.consumer_lock);
if (err)
goto err2;
}
if (mask & IB_SRQ_LIMIT)
srq->limit = attr->srq_limit;
return 0;
err2:
rxe_queue_cleanup(q);
srq->rq.queue = NULL;
err1:
return err;
}

View File

@ -0,0 +1,157 @@
/*
* Copyright (c) 2016 Mellanox Technologies Ltd. All rights reserved.
* Copyright (c) 2015 System Fabric Works, 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 "rxe.h"
#include "rxe_net.h"
/* Copy argument and remove trailing CR. Return the new length. */
static int sanitize_arg(const char *val, char *intf, int intf_len)
{
int len;
if (!val)
return 0;
/* Remove newline. */
for (len = 0; len < intf_len - 1 && val[len] && val[len] != '\n'; len++)
intf[len] = val[len];
intf[len] = 0;
if (len == 0 || (val[len] != 0 && val[len] != '\n'))
return 0;
return len;
}
static void rxe_set_port_state(struct net_device *ndev)
{
struct rxe_dev *rxe = net_to_rxe(ndev);
bool is_up = netif_running(ndev) && netif_carrier_ok(ndev);
if (!rxe)
goto out;
if (is_up)
rxe_port_up(rxe);
else
rxe_port_down(rxe); /* down for unknown state */
out:
return;
}
static int rxe_param_set_add(const char *val, const struct kernel_param *kp)
{
int len;
int err = 0;
char intf[32];
struct net_device *ndev = NULL;
struct rxe_dev *rxe;
len = sanitize_arg(val, intf, sizeof(intf));
if (!len) {
pr_err("rxe: add: invalid interface name\n");
err = -EINVAL;
goto err;
}
ndev = dev_get_by_name(&init_net, intf);
if (!ndev) {
pr_err("interface %s not found\n", intf);
err = -EINVAL;
goto err;
}
if (net_to_rxe(ndev)) {
pr_err("rxe: already configured on %s\n", intf);
err = -EINVAL;
goto err;
}
rxe = rxe_net_add(ndev);
if (!rxe) {
pr_err("rxe: failed to add %s\n", intf);
err = -EINVAL;
goto err;
}
rxe_set_port_state(ndev);
pr_info("rxe: added %s to %s\n", rxe->ib_dev.name, intf);
err:
if (ndev)
dev_put(ndev);
return err;
}
static int rxe_param_set_remove(const char *val, const struct kernel_param *kp)
{
int len;
char intf[32];
struct rxe_dev *rxe;
len = sanitize_arg(val, intf, sizeof(intf));
if (!len) {
pr_err("rxe: add: invalid interface name\n");
return -EINVAL;
}
if (strncmp("all", intf, len) == 0) {
pr_info("rxe_sys: remove all");
rxe_remove_all();
return 0;
}
rxe = get_rxe_by_name(intf);
if (!rxe) {
pr_err("rxe: not configured on %s\n", intf);
return -EINVAL;
}
list_del(&rxe->list);
rxe_remove(rxe);
return 0;
}
static const struct kernel_param_ops rxe_add_ops = {
.set = rxe_param_set_add,
};
static const struct kernel_param_ops rxe_remove_ops = {
.set = rxe_param_set_remove,
};
module_param_cb(add, &rxe_add_ops, NULL, 0200);
MODULE_PARM_DESC(add, "Create RXE device over network interface");
module_param_cb(remove, &rxe_remove_ops, NULL, 0200);
MODULE_PARM_DESC(remove, "Remove RXE device over network interface");

View File

@ -0,0 +1,154 @@
/*
* Copyright (c) 2016 Mellanox Technologies Ltd. All rights reserved.
* Copyright (c) 2015 System Fabric Works, 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/kernel.h>
#include <linux/interrupt.h>
#include <linux/hardirq.h>
#include "rxe_task.h"
int __rxe_do_task(struct rxe_task *task)
{
int ret;
while ((ret = task->func(task->arg)) == 0)
;
task->ret = ret;
return ret;
}
/*
* this locking is due to a potential race where
* a second caller finds the task already running
* but looks just after the last call to func
*/
void rxe_do_task(unsigned long data)
{
int cont;
int ret;
unsigned long flags;
struct rxe_task *task = (struct rxe_task *)data;
spin_lock_irqsave(&task->state_lock, flags);
switch (task->state) {
case TASK_STATE_START:
task->state = TASK_STATE_BUSY;
spin_unlock_irqrestore(&task->state_lock, flags);
break;
case TASK_STATE_BUSY:
task->state = TASK_STATE_ARMED;
/* fall through to */
case TASK_STATE_ARMED:
spin_unlock_irqrestore(&task->state_lock, flags);
return;
default:
spin_unlock_irqrestore(&task->state_lock, flags);
pr_warn("bad state = %d in rxe_do_task\n", task->state);
return;
}
do {
cont = 0;
ret = task->func(task->arg);
spin_lock_irqsave(&task->state_lock, flags);
switch (task->state) {
case TASK_STATE_BUSY:
if (ret)
task->state = TASK_STATE_START;
else
cont = 1;
break;
/* soneone tried to run the task since the last time we called
* func, so we will call one more time regardless of the
* return value
*/
case TASK_STATE_ARMED:
task->state = TASK_STATE_BUSY;
cont = 1;
break;
default:
pr_warn("bad state = %d in rxe_do_task\n",
task->state);
}
spin_unlock_irqrestore(&task->state_lock, flags);
} while (cont);
task->ret = ret;
}
int rxe_init_task(void *obj, struct rxe_task *task,
void *arg, int (*func)(void *), char *name)
{
task->obj = obj;
task->arg = arg;
task->func = func;
snprintf(task->name, sizeof(task->name), "%s", name);
tasklet_init(&task->tasklet, rxe_do_task, (unsigned long)task);
task->state = TASK_STATE_START;
spin_lock_init(&task->state_lock);
return 0;
}
void rxe_cleanup_task(struct rxe_task *task)
{
tasklet_kill(&task->tasklet);
}
void rxe_run_task(struct rxe_task *task, int sched)
{
if (sched)
tasklet_schedule(&task->tasklet);
else
rxe_do_task((unsigned long)task);
}
void rxe_disable_task(struct rxe_task *task)
{
tasklet_disable(&task->tasklet);
}
void rxe_enable_task(struct rxe_task *task)
{
tasklet_enable(&task->tasklet);
}

View File

@ -0,0 +1,95 @@
/*
* Copyright (c) 2016 Mellanox Technologies Ltd. All rights reserved.
* Copyright (c) 2015 System Fabric Works, 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 RXE_TASK_H
#define RXE_TASK_H
enum {
TASK_STATE_START = 0,
TASK_STATE_BUSY = 1,
TASK_STATE_ARMED = 2,
};
/*
* data structure to describe a 'task' which is a short
* function that returns 0 as long as it needs to be
* called again.
*/
struct rxe_task {
void *obj;
struct tasklet_struct tasklet;
int state;
spinlock_t state_lock; /* spinlock for task state */
void *arg;
int (*func)(void *arg);
int ret;
char name[16];
};
/*
* init rxe_task structure
* arg => parameter to pass to fcn
* fcn => function to call until it returns != 0
*/
int rxe_init_task(void *obj, struct rxe_task *task,
void *arg, int (*func)(void *), char *name);
/* cleanup task */
void rxe_cleanup_task(struct rxe_task *task);
/*
* raw call to func in loop without any checking
* can call when tasklets are disabled
*/
int __rxe_do_task(struct rxe_task *task);
/*
* common function called by any of the main tasklets
* If there is any chance that there is additional
* work to do someone must reschedule the task before
* leaving
*/
void rxe_do_task(unsigned long data);
/* run a task, else schedule it to run as a tasklet, The decision
* to run or schedule tasklet is based on the parameter sched.
*/
void rxe_run_task(struct rxe_task *task, int sched);
/* keep a task from scheduling */
void rxe_disable_task(struct rxe_task *task);
/* allow task to run */
void rxe_enable_task(struct rxe_task *task);
#endif /* RXE_TASK_H */

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,480 @@
/*
* Copyright (c) 2016 Mellanox Technologies Ltd. All rights reserved.
* Copyright (c) 2015 System Fabric Works, 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 RXE_VERBS_H
#define RXE_VERBS_H
#include <linux/interrupt.h>
#include <rdma/rdma_user_rxe.h>
#include "rxe_pool.h"
#include "rxe_task.h"
static inline int pkey_match(u16 key1, u16 key2)
{
return (((key1 & 0x7fff) != 0) &&
((key1 & 0x7fff) == (key2 & 0x7fff)) &&
((key1 & 0x8000) || (key2 & 0x8000))) ? 1 : 0;
}
/* Return >0 if psn_a > psn_b
* 0 if psn_a == psn_b
* <0 if psn_a < psn_b
*/
static inline int psn_compare(u32 psn_a, u32 psn_b)
{
s32 diff;
diff = (psn_a - psn_b) << 8;
return diff;
}
struct rxe_ucontext {
struct rxe_pool_entry pelem;
struct ib_ucontext ibuc;
};
struct rxe_pd {
struct rxe_pool_entry pelem;
struct ib_pd ibpd;
};
struct rxe_ah {
struct rxe_pool_entry pelem;
struct ib_ah ibah;
struct rxe_pd *pd;
struct rxe_av av;
};
struct rxe_cqe {
union {
struct ib_wc ibwc;
struct ib_uverbs_wc uibwc;
};
};
struct rxe_cq {
struct rxe_pool_entry pelem;
struct ib_cq ibcq;
struct rxe_queue *queue;
spinlock_t cq_lock;
u8 notify;
int is_user;
struct tasklet_struct comp_task;
};
enum wqe_state {
wqe_state_posted,
wqe_state_processing,
wqe_state_pending,
wqe_state_done,
wqe_state_error,
};
struct rxe_sq {
int max_wr;
int max_sge;
int max_inline;
spinlock_t sq_lock; /* guard queue */
struct rxe_queue *queue;
};
struct rxe_rq {
int max_wr;
int max_sge;
spinlock_t producer_lock; /* guard queue producer */
spinlock_t consumer_lock; /* guard queue consumer */
struct rxe_queue *queue;
};
struct rxe_srq {
struct rxe_pool_entry pelem;
struct ib_srq ibsrq;
struct rxe_pd *pd;
struct rxe_rq rq;
u32 srq_num;
int limit;
int error;
};
enum rxe_qp_state {
QP_STATE_RESET,
QP_STATE_INIT,
QP_STATE_READY,
QP_STATE_DRAIN, /* req only */
QP_STATE_DRAINED, /* req only */
QP_STATE_ERROR
};
extern char *rxe_qp_state_name[];
struct rxe_req_info {
enum rxe_qp_state state;
int wqe_index;
u32 psn;
int opcode;
atomic_t rd_atomic;
int wait_fence;
int need_rd_atomic;
int wait_psn;
int need_retry;
int noack_pkts;
struct rxe_task task;
};
struct rxe_comp_info {
u32 psn;
int opcode;
int timeout;
int timeout_retry;
u32 retry_cnt;
u32 rnr_retry;
struct rxe_task task;
};
enum rdatm_res_state {
rdatm_res_state_next,
rdatm_res_state_new,
rdatm_res_state_replay,
};
struct resp_res {
int type;
u32 first_psn;
u32 last_psn;
u32 cur_psn;
enum rdatm_res_state state;
union {
struct {
struct sk_buff *skb;
} atomic;
struct {
struct rxe_mem *mr;
u64 va_org;
u32 rkey;
u32 length;
u64 va;
u32 resid;
} read;
};
};
struct rxe_resp_info {
enum rxe_qp_state state;
u32 msn;
u32 psn;
int opcode;
int drop_msg;
int goto_error;
int sent_psn_nak;
enum ib_wc_status status;
u8 aeth_syndrome;
/* Receive only */
struct rxe_recv_wqe *wqe;
/* RDMA read / atomic only */
u64 va;
struct rxe_mem *mr;
u32 resid;
u32 rkey;
u64 atomic_orig;
/* SRQ only */
struct {
struct rxe_recv_wqe wqe;
struct ib_sge sge[RXE_MAX_SGE];
} srq_wqe;
/* Responder resources. It's a circular list where the oldest
* resource is dropped first.
*/
struct resp_res *resources;
unsigned int res_head;
unsigned int res_tail;
struct resp_res *res;
struct rxe_task task;
};
struct rxe_qp {
struct rxe_pool_entry pelem;
struct ib_qp ibqp;
struct ib_qp_attr attr;
unsigned int valid;
unsigned int mtu;
int is_user;
struct rxe_pd *pd;
struct rxe_srq *srq;
struct rxe_cq *scq;
struct rxe_cq *rcq;
enum ib_sig_type sq_sig_type;
struct rxe_sq sq;
struct rxe_rq rq;
struct socket *sk;
struct rxe_av pri_av;
struct rxe_av alt_av;
/* list of mcast groups qp has joined (for cleanup) */
struct list_head grp_list;
spinlock_t grp_lock; /* guard grp_list */
struct sk_buff_head req_pkts;
struct sk_buff_head resp_pkts;
struct sk_buff_head send_pkts;
struct rxe_req_info req;
struct rxe_comp_info comp;
struct rxe_resp_info resp;
atomic_t ssn;
atomic_t skb_out;
int need_req_skb;
/* Timer for retranmitting packet when ACKs have been lost. RC
* only. The requester sets it when it is not already
* started. The responder resets it whenever an ack is
* received.
*/
struct timer_list retrans_timer;
u64 qp_timeout_jiffies;
/* Timer for handling RNR NAKS. */
struct timer_list rnr_nak_timer;
spinlock_t state_lock; /* guard requester and completer */
};
enum rxe_mem_state {
RXE_MEM_STATE_ZOMBIE,
RXE_MEM_STATE_INVALID,
RXE_MEM_STATE_FREE,
RXE_MEM_STATE_VALID,
};
enum rxe_mem_type {
RXE_MEM_TYPE_NONE,
RXE_MEM_TYPE_DMA,
RXE_MEM_TYPE_MR,
RXE_MEM_TYPE_FMR,
RXE_MEM_TYPE_MW,
};
#define RXE_BUF_PER_MAP (PAGE_SIZE / sizeof(struct rxe_phys_buf))
struct rxe_phys_buf {
u64 addr;
u64 size;
};
struct rxe_map {
struct rxe_phys_buf buf[RXE_BUF_PER_MAP];
};
struct rxe_mem {
struct rxe_pool_entry pelem;
union {
struct ib_mr ibmr;
struct ib_mw ibmw;
};
struct rxe_pd *pd;
struct ib_umem *umem;
u32 lkey;
u32 rkey;
enum rxe_mem_state state;
enum rxe_mem_type type;
u64 va;
u64 iova;
size_t length;
u32 offset;
int access;
int page_shift;
int page_mask;
int map_shift;
int map_mask;
u32 num_buf;
u32 nbuf;
u32 max_buf;
u32 num_map;
struct rxe_map **map;
};
struct rxe_mc_grp {
struct rxe_pool_entry pelem;
spinlock_t mcg_lock; /* guard group */
struct rxe_dev *rxe;
struct list_head qp_list;
union ib_gid mgid;
int num_qp;
u32 qkey;
u16 pkey;
};
struct rxe_mc_elem {
struct rxe_pool_entry pelem;
struct list_head qp_list;
struct list_head grp_list;
struct rxe_qp *qp;
struct rxe_mc_grp *grp;
};
struct rxe_port {
struct ib_port_attr attr;
u16 *pkey_tbl;
__be64 port_guid;
__be64 subnet_prefix;
spinlock_t port_lock; /* guard port */
unsigned int mtu_cap;
/* special QPs */
u32 qp_smi_index;
u32 qp_gsi_index;
};
/* callbacks from rdma_rxe to network interface layer */
struct rxe_ifc_ops {
void (*release)(struct rxe_dev *rxe);
__be64 (*node_guid)(struct rxe_dev *rxe);
__be64 (*port_guid)(struct rxe_dev *rxe);
struct device *(*dma_device)(struct rxe_dev *rxe);
int (*mcast_add)(struct rxe_dev *rxe, union ib_gid *mgid);
int (*mcast_delete)(struct rxe_dev *rxe, union ib_gid *mgid);
int (*prepare)(struct rxe_dev *rxe, struct rxe_pkt_info *pkt,
struct sk_buff *skb, u32 *crc);
int (*send)(struct rxe_dev *rxe, struct rxe_pkt_info *pkt,
struct sk_buff *skb);
int (*loopback)(struct sk_buff *skb);
struct sk_buff *(*init_packet)(struct rxe_dev *rxe, struct rxe_av *av,
int paylen, struct rxe_pkt_info *pkt);
char *(*parent_name)(struct rxe_dev *rxe, unsigned int port_num);
enum rdma_link_layer (*link_layer)(struct rxe_dev *rxe,
unsigned int port_num);
};
struct rxe_dev {
struct ib_device ib_dev;
struct ib_device_attr attr;
int max_ucontext;
int max_inline_data;
struct kref ref_cnt;
struct mutex usdev_lock;
struct rxe_ifc_ops *ifc_ops;
struct net_device *ndev;
int xmit_errors;
struct rxe_pool uc_pool;
struct rxe_pool pd_pool;
struct rxe_pool ah_pool;
struct rxe_pool srq_pool;
struct rxe_pool qp_pool;
struct rxe_pool cq_pool;
struct rxe_pool mr_pool;
struct rxe_pool mw_pool;
struct rxe_pool mc_grp_pool;
struct rxe_pool mc_elem_pool;
spinlock_t pending_lock; /* guard pending_mmaps */
struct list_head pending_mmaps;
spinlock_t mmap_offset_lock; /* guard mmap_offset */
int mmap_offset;
struct rxe_port port;
struct list_head list;
};
static inline struct rxe_dev *to_rdev(struct ib_device *dev)
{
return dev ? container_of(dev, struct rxe_dev, ib_dev) : NULL;
}
static inline struct rxe_ucontext *to_ruc(struct ib_ucontext *uc)
{
return uc ? container_of(uc, struct rxe_ucontext, ibuc) : NULL;
}
static inline struct rxe_pd *to_rpd(struct ib_pd *pd)
{
return pd ? container_of(pd, struct rxe_pd, ibpd) : NULL;
}
static inline struct rxe_ah *to_rah(struct ib_ah *ah)
{
return ah ? container_of(ah, struct rxe_ah, ibah) : NULL;
}
static inline struct rxe_srq *to_rsrq(struct ib_srq *srq)
{
return srq ? container_of(srq, struct rxe_srq, ibsrq) : NULL;
}
static inline struct rxe_qp *to_rqp(struct ib_qp *qp)
{
return qp ? container_of(qp, struct rxe_qp, ibqp) : NULL;
}
static inline struct rxe_cq *to_rcq(struct ib_cq *cq)
{
return cq ? container_of(cq, struct rxe_cq, ibcq) : NULL;
}
static inline struct rxe_mem *to_rmr(struct ib_mr *mr)
{
return mr ? container_of(mr, struct rxe_mem, ibmr) : NULL;
}
static inline struct rxe_mem *to_rmw(struct ib_mw *mw)
{
return mw ? container_of(mw, struct rxe_mem, ibmw) : NULL;
}
int rxe_register_device(struct rxe_dev *rxe);
int rxe_unregister_device(struct rxe_dev *rxe);
void rxe_mc_cleanup(void *arg);
#endif /* RXE_VERBS_H */

View File

@ -6,3 +6,4 @@ header-y += ib_user_verbs.h
header-y += rdma_netlink.h
header-y += rdma_user_cm.h
header-y += hfi/
header-y += rdma_user_rxe.h

View File

@ -0,0 +1,144 @@
/*
* Copyright (c) 2016 Mellanox Technologies Ltd. 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 RDMA_USER_RXE_H
#define RDMA_USER_RXE_H
#include <linux/types.h>
union rxe_gid {
__u8 raw[16];
struct {
__be64 subnet_prefix;
__be64 interface_id;
} global;
};
struct rxe_global_route {
union rxe_gid dgid;
__u32 flow_label;
__u8 sgid_index;
__u8 hop_limit;
__u8 traffic_class;
};
struct rxe_av {
__u8 port_num;
__u8 network_type;
struct rxe_global_route grh;
union {
struct sockaddr _sockaddr;
struct sockaddr_in _sockaddr_in;
struct sockaddr_in6 _sockaddr_in6;
} sgid_addr, dgid_addr;
};
struct rxe_send_wr {
__u64 wr_id;
__u32 num_sge;
__u32 opcode;
__u32 send_flags;
union {
__be32 imm_data;
__u32 invalidate_rkey;
} ex;
union {
struct {
__u64 remote_addr;
__u32 rkey;
} rdma;
struct {
__u64 remote_addr;
__u64 compare_add;
__u64 swap;
__u32 rkey;
} atomic;
struct {
__u32 remote_qpn;
__u32 remote_qkey;
__u16 pkey_index;
} ud;
struct {
struct ib_mr *mr;
__u32 key;
int access;
} reg;
} wr;
};
struct rxe_sge {
__u64 addr;
__u32 length;
__u32 lkey;
};
struct mminfo {
__u64 offset;
__u32 size;
__u32 pad;
};
struct rxe_dma_info {
__u32 length;
__u32 resid;
__u32 cur_sge;
__u32 num_sge;
__u32 sge_offset;
union {
__u8 inline_data[0];
struct rxe_sge sge[0];
};
};
struct rxe_send_wqe {
struct rxe_send_wr wr;
struct rxe_av av;
__u32 status;
__u32 state;
__u64 iova;
__u32 mask;
__u32 first_psn;
__u32 last_psn;
__u32 ack_length;
__u32 ssn;
__u32 has_rd_atomic;
struct rxe_dma_info dma;
};
struct rxe_recv_wqe {
__u64 wr_id;
__u32 num_sge;
__u32 padding;
struct rxe_dma_info dma;
};
#endif /* RDMA_USER_RXE_H */