d227fa7288
If a lot of QPs fall into Error state at once and the EQ of the respective HCA is too small, it might overrun, causing the eHCA driver to stop processing completion events and calling the application's completion handlers, effectively causing traffic to stop. Fix this by limiting available QPs and CQs to a customizable max count, and determining EQ size based on these counts and a worst-case assumption. Signed-off-by: Stefan Roscher <stefan.roscher@de.ibm.com> Signed-off-by: Roland Dreier <rolandd@cisco.com>
1029 lines
28 KiB
C
1029 lines
28 KiB
C
/*
|
|
* IBM eServer eHCA Infiniband device driver for Linux on POWER
|
|
*
|
|
* module start stop, hca detection
|
|
*
|
|
* Authors: Heiko J Schick <schickhj@de.ibm.com>
|
|
* Hoang-Nam Nguyen <hnguyen@de.ibm.com>
|
|
* Joachim Fenkes <fenkes@de.ibm.com>
|
|
*
|
|
* Copyright (c) 2005 IBM Corporation
|
|
*
|
|
* All rights reserved.
|
|
*
|
|
* This source code is distributed under a dual license of GPL v2.0 and OpenIB
|
|
* BSD.
|
|
*
|
|
* OpenIB BSD License
|
|
*
|
|
* 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.
|
|
*
|
|
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
|
|
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
|
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
|
|
* ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
|
|
* LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
|
|
* CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
|
|
* SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR
|
|
* BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER
|
|
* IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
|
|
* ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
|
|
* POSSIBILITY OF SUCH DAMAGE.
|
|
*/
|
|
|
|
#ifdef CONFIG_PPC_64K_PAGES
|
|
#include <linux/slab.h>
|
|
#endif
|
|
|
|
#include "ehca_classes.h"
|
|
#include "ehca_iverbs.h"
|
|
#include "ehca_mrmw.h"
|
|
#include "ehca_tools.h"
|
|
#include "hcp_if.h"
|
|
|
|
#define HCAD_VERSION "0026"
|
|
|
|
MODULE_LICENSE("Dual BSD/GPL");
|
|
MODULE_AUTHOR("Christoph Raisch <raisch@de.ibm.com>");
|
|
MODULE_DESCRIPTION("IBM eServer HCA InfiniBand Device Driver");
|
|
MODULE_VERSION(HCAD_VERSION);
|
|
|
|
static int ehca_open_aqp1 = 0;
|
|
static int ehca_hw_level = 0;
|
|
static int ehca_poll_all_eqs = 1;
|
|
|
|
int ehca_debug_level = 0;
|
|
int ehca_nr_ports = 2;
|
|
int ehca_use_hp_mr = 0;
|
|
int ehca_port_act_time = 30;
|
|
int ehca_static_rate = -1;
|
|
int ehca_scaling_code = 0;
|
|
int ehca_lock_hcalls = -1;
|
|
int ehca_max_cq = -1;
|
|
int ehca_max_qp = -1;
|
|
|
|
module_param_named(open_aqp1, ehca_open_aqp1, bool, S_IRUGO);
|
|
module_param_named(debug_level, ehca_debug_level, int, S_IRUGO);
|
|
module_param_named(hw_level, ehca_hw_level, int, S_IRUGO);
|
|
module_param_named(nr_ports, ehca_nr_ports, int, S_IRUGO);
|
|
module_param_named(use_hp_mr, ehca_use_hp_mr, bool, S_IRUGO);
|
|
module_param_named(port_act_time, ehca_port_act_time, int, S_IRUGO);
|
|
module_param_named(poll_all_eqs, ehca_poll_all_eqs, bool, S_IRUGO);
|
|
module_param_named(static_rate, ehca_static_rate, int, S_IRUGO);
|
|
module_param_named(scaling_code, ehca_scaling_code, bool, S_IRUGO);
|
|
module_param_named(lock_hcalls, ehca_lock_hcalls, bool, S_IRUGO);
|
|
module_param_named(number_of_cqs, ehca_max_cq, int, S_IRUGO);
|
|
module_param_named(number_of_qps, ehca_max_qp, int, S_IRUGO);
|
|
|
|
MODULE_PARM_DESC(open_aqp1,
|
|
"Open AQP1 on startup (default: no)");
|
|
MODULE_PARM_DESC(debug_level,
|
|
"Amount of debug output (0: none (default), 1: traces, "
|
|
"2: some dumps, 3: lots)");
|
|
MODULE_PARM_DESC(hw_level,
|
|
"Hardware level (0: autosensing (default), "
|
|
"0x10..0x14: eHCA, 0x20..0x23: eHCA2)");
|
|
MODULE_PARM_DESC(nr_ports,
|
|
"number of connected ports (-1: autodetect, 1: port one only, "
|
|
"2: two ports (default)");
|
|
MODULE_PARM_DESC(use_hp_mr,
|
|
"Use high performance MRs (default: no)");
|
|
MODULE_PARM_DESC(port_act_time,
|
|
"Time to wait for port activation (default: 30 sec)");
|
|
MODULE_PARM_DESC(poll_all_eqs,
|
|
"Poll all event queues periodically (default: yes)");
|
|
MODULE_PARM_DESC(static_rate,
|
|
"Set permanent static rate (default: no static rate)");
|
|
MODULE_PARM_DESC(scaling_code,
|
|
"Enable scaling code (default: no)");
|
|
MODULE_PARM_DESC(lock_hcalls,
|
|
"Serialize all hCalls made by the driver "
|
|
"(default: autodetect)");
|
|
MODULE_PARM_DESC(number_of_cqs,
|
|
"Max number of CQs which can be allocated "
|
|
"(default: autodetect)");
|
|
MODULE_PARM_DESC(number_of_qps,
|
|
"Max number of QPs which can be allocated "
|
|
"(default: autodetect)");
|
|
|
|
DEFINE_RWLOCK(ehca_qp_idr_lock);
|
|
DEFINE_RWLOCK(ehca_cq_idr_lock);
|
|
DEFINE_IDR(ehca_qp_idr);
|
|
DEFINE_IDR(ehca_cq_idr);
|
|
|
|
static LIST_HEAD(shca_list); /* list of all registered ehcas */
|
|
static DEFINE_SPINLOCK(shca_list_lock);
|
|
|
|
static struct timer_list poll_eqs_timer;
|
|
|
|
#ifdef CONFIG_PPC_64K_PAGES
|
|
static struct kmem_cache *ctblk_cache;
|
|
|
|
void *ehca_alloc_fw_ctrlblock(gfp_t flags)
|
|
{
|
|
void *ret = kmem_cache_zalloc(ctblk_cache, flags);
|
|
if (!ret)
|
|
ehca_gen_err("Out of memory for ctblk");
|
|
return ret;
|
|
}
|
|
|
|
void ehca_free_fw_ctrlblock(void *ptr)
|
|
{
|
|
if (ptr)
|
|
kmem_cache_free(ctblk_cache, ptr);
|
|
|
|
}
|
|
#endif
|
|
|
|
int ehca2ib_return_code(u64 ehca_rc)
|
|
{
|
|
switch (ehca_rc) {
|
|
case H_SUCCESS:
|
|
return 0;
|
|
case H_RESOURCE: /* Resource in use */
|
|
case H_BUSY:
|
|
return -EBUSY;
|
|
case H_NOT_ENOUGH_RESOURCES: /* insufficient resources */
|
|
case H_CONSTRAINED: /* resource constraint */
|
|
case H_NO_MEM:
|
|
return -ENOMEM;
|
|
default:
|
|
return -EINVAL;
|
|
}
|
|
}
|
|
|
|
static int ehca_create_slab_caches(void)
|
|
{
|
|
int ret;
|
|
|
|
ret = ehca_init_pd_cache();
|
|
if (ret) {
|
|
ehca_gen_err("Cannot create PD SLAB cache.");
|
|
return ret;
|
|
}
|
|
|
|
ret = ehca_init_cq_cache();
|
|
if (ret) {
|
|
ehca_gen_err("Cannot create CQ SLAB cache.");
|
|
goto create_slab_caches2;
|
|
}
|
|
|
|
ret = ehca_init_qp_cache();
|
|
if (ret) {
|
|
ehca_gen_err("Cannot create QP SLAB cache.");
|
|
goto create_slab_caches3;
|
|
}
|
|
|
|
ret = ehca_init_av_cache();
|
|
if (ret) {
|
|
ehca_gen_err("Cannot create AV SLAB cache.");
|
|
goto create_slab_caches4;
|
|
}
|
|
|
|
ret = ehca_init_mrmw_cache();
|
|
if (ret) {
|
|
ehca_gen_err("Cannot create MR&MW SLAB cache.");
|
|
goto create_slab_caches5;
|
|
}
|
|
|
|
ret = ehca_init_small_qp_cache();
|
|
if (ret) {
|
|
ehca_gen_err("Cannot create small queue SLAB cache.");
|
|
goto create_slab_caches6;
|
|
}
|
|
|
|
#ifdef CONFIG_PPC_64K_PAGES
|
|
ctblk_cache = kmem_cache_create("ehca_cache_ctblk",
|
|
EHCA_PAGESIZE, H_CB_ALIGNMENT,
|
|
SLAB_HWCACHE_ALIGN,
|
|
NULL);
|
|
if (!ctblk_cache) {
|
|
ehca_gen_err("Cannot create ctblk SLAB cache.");
|
|
ehca_cleanup_small_qp_cache();
|
|
goto create_slab_caches6;
|
|
}
|
|
#endif
|
|
return 0;
|
|
|
|
create_slab_caches6:
|
|
ehca_cleanup_mrmw_cache();
|
|
|
|
create_slab_caches5:
|
|
ehca_cleanup_av_cache();
|
|
|
|
create_slab_caches4:
|
|
ehca_cleanup_qp_cache();
|
|
|
|
create_slab_caches3:
|
|
ehca_cleanup_cq_cache();
|
|
|
|
create_slab_caches2:
|
|
ehca_cleanup_pd_cache();
|
|
|
|
return ret;
|
|
}
|
|
|
|
static void ehca_destroy_slab_caches(void)
|
|
{
|
|
ehca_cleanup_small_qp_cache();
|
|
ehca_cleanup_mrmw_cache();
|
|
ehca_cleanup_av_cache();
|
|
ehca_cleanup_qp_cache();
|
|
ehca_cleanup_cq_cache();
|
|
ehca_cleanup_pd_cache();
|
|
#ifdef CONFIG_PPC_64K_PAGES
|
|
if (ctblk_cache)
|
|
kmem_cache_destroy(ctblk_cache);
|
|
#endif
|
|
}
|
|
|
|
#define EHCA_HCAAVER EHCA_BMASK_IBM(32, 39)
|
|
#define EHCA_REVID EHCA_BMASK_IBM(40, 63)
|
|
|
|
static struct cap_descr {
|
|
u64 mask;
|
|
char *descr;
|
|
} hca_cap_descr[] = {
|
|
{ HCA_CAP_AH_PORT_NR_CHECK, "HCA_CAP_AH_PORT_NR_CHECK" },
|
|
{ HCA_CAP_ATOMIC, "HCA_CAP_ATOMIC" },
|
|
{ HCA_CAP_AUTO_PATH_MIG, "HCA_CAP_AUTO_PATH_MIG" },
|
|
{ HCA_CAP_BAD_P_KEY_CTR, "HCA_CAP_BAD_P_KEY_CTR" },
|
|
{ HCA_CAP_SQD_RTS_PORT_CHANGE, "HCA_CAP_SQD_RTS_PORT_CHANGE" },
|
|
{ HCA_CAP_CUR_QP_STATE_MOD, "HCA_CAP_CUR_QP_STATE_MOD" },
|
|
{ HCA_CAP_INIT_TYPE, "HCA_CAP_INIT_TYPE" },
|
|
{ HCA_CAP_PORT_ACTIVE_EVENT, "HCA_CAP_PORT_ACTIVE_EVENT" },
|
|
{ HCA_CAP_Q_KEY_VIOL_CTR, "HCA_CAP_Q_KEY_VIOL_CTR" },
|
|
{ HCA_CAP_WQE_RESIZE, "HCA_CAP_WQE_RESIZE" },
|
|
{ HCA_CAP_RAW_PACKET_MCAST, "HCA_CAP_RAW_PACKET_MCAST" },
|
|
{ HCA_CAP_SHUTDOWN_PORT, "HCA_CAP_SHUTDOWN_PORT" },
|
|
{ HCA_CAP_RC_LL_QP, "HCA_CAP_RC_LL_QP" },
|
|
{ HCA_CAP_SRQ, "HCA_CAP_SRQ" },
|
|
{ HCA_CAP_UD_LL_QP, "HCA_CAP_UD_LL_QP" },
|
|
{ HCA_CAP_RESIZE_MR, "HCA_CAP_RESIZE_MR" },
|
|
{ HCA_CAP_MINI_QP, "HCA_CAP_MINI_QP" },
|
|
{ HCA_CAP_H_ALLOC_RES_SYNC, "HCA_CAP_H_ALLOC_RES_SYNC" },
|
|
};
|
|
|
|
static int ehca_sense_attributes(struct ehca_shca *shca)
|
|
{
|
|
int i, ret = 0;
|
|
u64 h_ret;
|
|
struct hipz_query_hca *rblock;
|
|
struct hipz_query_port *port;
|
|
const char *loc_code;
|
|
|
|
static const u32 pgsize_map[] = {
|
|
HCA_CAP_MR_PGSIZE_4K, 0x1000,
|
|
HCA_CAP_MR_PGSIZE_64K, 0x10000,
|
|
HCA_CAP_MR_PGSIZE_1M, 0x100000,
|
|
HCA_CAP_MR_PGSIZE_16M, 0x1000000,
|
|
};
|
|
|
|
ehca_gen_dbg("Probing adapter %s...",
|
|
shca->ofdev->node->full_name);
|
|
loc_code = of_get_property(shca->ofdev->node, "ibm,loc-code", NULL);
|
|
if (loc_code)
|
|
ehca_gen_dbg(" ... location lode=%s", loc_code);
|
|
|
|
rblock = ehca_alloc_fw_ctrlblock(GFP_KERNEL);
|
|
if (!rblock) {
|
|
ehca_gen_err("Cannot allocate rblock memory.");
|
|
return -ENOMEM;
|
|
}
|
|
|
|
h_ret = hipz_h_query_hca(shca->ipz_hca_handle, rblock);
|
|
if (h_ret != H_SUCCESS) {
|
|
ehca_gen_err("Cannot query device properties. h_ret=%li",
|
|
h_ret);
|
|
ret = -EPERM;
|
|
goto sense_attributes1;
|
|
}
|
|
|
|
if (ehca_nr_ports == 1)
|
|
shca->num_ports = 1;
|
|
else
|
|
shca->num_ports = (u8)rblock->num_ports;
|
|
|
|
ehca_gen_dbg(" ... found %x ports", rblock->num_ports);
|
|
|
|
if (ehca_hw_level == 0) {
|
|
u32 hcaaver;
|
|
u32 revid;
|
|
|
|
hcaaver = EHCA_BMASK_GET(EHCA_HCAAVER, rblock->hw_ver);
|
|
revid = EHCA_BMASK_GET(EHCA_REVID, rblock->hw_ver);
|
|
|
|
ehca_gen_dbg(" ... hardware version=%x:%x", hcaaver, revid);
|
|
|
|
if (hcaaver == 1) {
|
|
if (revid <= 3)
|
|
shca->hw_level = 0x10 | (revid + 1);
|
|
else
|
|
shca->hw_level = 0x14;
|
|
} else if (hcaaver == 2) {
|
|
if (revid == 0)
|
|
shca->hw_level = 0x21;
|
|
else if (revid == 0x10)
|
|
shca->hw_level = 0x22;
|
|
else if (revid == 0x20 || revid == 0x21)
|
|
shca->hw_level = 0x23;
|
|
}
|
|
|
|
if (!shca->hw_level) {
|
|
ehca_gen_warn("unknown hardware version"
|
|
" - assuming default level");
|
|
shca->hw_level = 0x22;
|
|
}
|
|
} else
|
|
shca->hw_level = ehca_hw_level;
|
|
ehca_gen_dbg(" ... hardware level=%x", shca->hw_level);
|
|
|
|
shca->hca_cap = rblock->hca_cap_indicators;
|
|
ehca_gen_dbg(" ... HCA capabilities:");
|
|
for (i = 0; i < ARRAY_SIZE(hca_cap_descr); i++)
|
|
if (EHCA_BMASK_GET(hca_cap_descr[i].mask, shca->hca_cap))
|
|
ehca_gen_dbg(" %s", hca_cap_descr[i].descr);
|
|
|
|
/* Autodetect hCall locking -- the "H_ALLOC_RESOURCE synced" flag is
|
|
* a firmware property, so it's valid across all adapters
|
|
*/
|
|
if (ehca_lock_hcalls == -1)
|
|
ehca_lock_hcalls = !(shca->hca_cap & HCA_CAP_H_ALLOC_RES_SYNC);
|
|
|
|
/* translate supported MR page sizes; always support 4K */
|
|
shca->hca_cap_mr_pgsize = EHCA_PAGESIZE;
|
|
for (i = 0; i < ARRAY_SIZE(pgsize_map); i += 2)
|
|
if (rblock->memory_page_size_supported & pgsize_map[i])
|
|
shca->hca_cap_mr_pgsize |= pgsize_map[i + 1];
|
|
|
|
/* Set maximum number of CQs and QPs to calculate EQ size */
|
|
if (ehca_max_qp == -1)
|
|
ehca_max_qp = min_t(int, rblock->max_qp, EHCA_MAX_NUM_QUEUES);
|
|
else if (ehca_max_qp < 1 || ehca_max_qp > rblock->max_qp) {
|
|
ehca_gen_err("Requested number of QPs is out of range (1 - %i) "
|
|
"specified by HW", rblock->max_qp);
|
|
ret = -EINVAL;
|
|
goto sense_attributes1;
|
|
}
|
|
|
|
if (ehca_max_cq == -1)
|
|
ehca_max_cq = min_t(int, rblock->max_cq, EHCA_MAX_NUM_QUEUES);
|
|
else if (ehca_max_cq < 1 || ehca_max_cq > rblock->max_cq) {
|
|
ehca_gen_err("Requested number of CQs is out of range (1 - %i) "
|
|
"specified by HW", rblock->max_cq);
|
|
ret = -EINVAL;
|
|
goto sense_attributes1;
|
|
}
|
|
|
|
/* query max MTU from first port -- it's the same for all ports */
|
|
port = (struct hipz_query_port *)rblock;
|
|
h_ret = hipz_h_query_port(shca->ipz_hca_handle, 1, port);
|
|
if (h_ret != H_SUCCESS) {
|
|
ehca_gen_err("Cannot query port properties. h_ret=%li",
|
|
h_ret);
|
|
ret = -EPERM;
|
|
goto sense_attributes1;
|
|
}
|
|
|
|
shca->max_mtu = port->max_mtu;
|
|
|
|
sense_attributes1:
|
|
ehca_free_fw_ctrlblock(rblock);
|
|
return ret;
|
|
}
|
|
|
|
static int init_node_guid(struct ehca_shca *shca)
|
|
{
|
|
int ret = 0;
|
|
struct hipz_query_hca *rblock;
|
|
|
|
rblock = ehca_alloc_fw_ctrlblock(GFP_KERNEL);
|
|
if (!rblock) {
|
|
ehca_err(&shca->ib_device, "Can't allocate rblock memory.");
|
|
return -ENOMEM;
|
|
}
|
|
|
|
if (hipz_h_query_hca(shca->ipz_hca_handle, rblock) != H_SUCCESS) {
|
|
ehca_err(&shca->ib_device, "Can't query device properties");
|
|
ret = -EINVAL;
|
|
goto init_node_guid1;
|
|
}
|
|
|
|
memcpy(&shca->ib_device.node_guid, &rblock->node_guid, sizeof(u64));
|
|
|
|
init_node_guid1:
|
|
ehca_free_fw_ctrlblock(rblock);
|
|
return ret;
|
|
}
|
|
|
|
static int ehca_init_device(struct ehca_shca *shca)
|
|
{
|
|
int ret;
|
|
|
|
ret = init_node_guid(shca);
|
|
if (ret)
|
|
return ret;
|
|
|
|
strlcpy(shca->ib_device.name, "ehca%d", IB_DEVICE_NAME_MAX);
|
|
shca->ib_device.owner = THIS_MODULE;
|
|
|
|
shca->ib_device.uverbs_abi_ver = 8;
|
|
shca->ib_device.uverbs_cmd_mask =
|
|
(1ull << IB_USER_VERBS_CMD_GET_CONTEXT) |
|
|
(1ull << IB_USER_VERBS_CMD_QUERY_DEVICE) |
|
|
(1ull << IB_USER_VERBS_CMD_QUERY_PORT) |
|
|
(1ull << IB_USER_VERBS_CMD_ALLOC_PD) |
|
|
(1ull << IB_USER_VERBS_CMD_DEALLOC_PD) |
|
|
(1ull << IB_USER_VERBS_CMD_REG_MR) |
|
|
(1ull << IB_USER_VERBS_CMD_DEREG_MR) |
|
|
(1ull << IB_USER_VERBS_CMD_CREATE_COMP_CHANNEL) |
|
|
(1ull << IB_USER_VERBS_CMD_CREATE_CQ) |
|
|
(1ull << IB_USER_VERBS_CMD_DESTROY_CQ) |
|
|
(1ull << IB_USER_VERBS_CMD_CREATE_QP) |
|
|
(1ull << IB_USER_VERBS_CMD_MODIFY_QP) |
|
|
(1ull << IB_USER_VERBS_CMD_QUERY_QP) |
|
|
(1ull << IB_USER_VERBS_CMD_DESTROY_QP) |
|
|
(1ull << IB_USER_VERBS_CMD_ATTACH_MCAST) |
|
|
(1ull << IB_USER_VERBS_CMD_DETACH_MCAST);
|
|
|
|
shca->ib_device.node_type = RDMA_NODE_IB_CA;
|
|
shca->ib_device.phys_port_cnt = shca->num_ports;
|
|
shca->ib_device.num_comp_vectors = 1;
|
|
shca->ib_device.dma_device = &shca->ofdev->dev;
|
|
shca->ib_device.query_device = ehca_query_device;
|
|
shca->ib_device.query_port = ehca_query_port;
|
|
shca->ib_device.query_gid = ehca_query_gid;
|
|
shca->ib_device.query_pkey = ehca_query_pkey;
|
|
/* shca->in_device.modify_device = ehca_modify_device */
|
|
shca->ib_device.modify_port = ehca_modify_port;
|
|
shca->ib_device.alloc_ucontext = ehca_alloc_ucontext;
|
|
shca->ib_device.dealloc_ucontext = ehca_dealloc_ucontext;
|
|
shca->ib_device.alloc_pd = ehca_alloc_pd;
|
|
shca->ib_device.dealloc_pd = ehca_dealloc_pd;
|
|
shca->ib_device.create_ah = ehca_create_ah;
|
|
/* shca->ib_device.modify_ah = ehca_modify_ah; */
|
|
shca->ib_device.query_ah = ehca_query_ah;
|
|
shca->ib_device.destroy_ah = ehca_destroy_ah;
|
|
shca->ib_device.create_qp = ehca_create_qp;
|
|
shca->ib_device.modify_qp = ehca_modify_qp;
|
|
shca->ib_device.query_qp = ehca_query_qp;
|
|
shca->ib_device.destroy_qp = ehca_destroy_qp;
|
|
shca->ib_device.post_send = ehca_post_send;
|
|
shca->ib_device.post_recv = ehca_post_recv;
|
|
shca->ib_device.create_cq = ehca_create_cq;
|
|
shca->ib_device.destroy_cq = ehca_destroy_cq;
|
|
shca->ib_device.resize_cq = ehca_resize_cq;
|
|
shca->ib_device.poll_cq = ehca_poll_cq;
|
|
/* shca->ib_device.peek_cq = ehca_peek_cq; */
|
|
shca->ib_device.req_notify_cq = ehca_req_notify_cq;
|
|
/* shca->ib_device.req_ncomp_notif = ehca_req_ncomp_notif; */
|
|
shca->ib_device.get_dma_mr = ehca_get_dma_mr;
|
|
shca->ib_device.reg_phys_mr = ehca_reg_phys_mr;
|
|
shca->ib_device.reg_user_mr = ehca_reg_user_mr;
|
|
shca->ib_device.query_mr = ehca_query_mr;
|
|
shca->ib_device.dereg_mr = ehca_dereg_mr;
|
|
shca->ib_device.rereg_phys_mr = ehca_rereg_phys_mr;
|
|
shca->ib_device.alloc_mw = ehca_alloc_mw;
|
|
shca->ib_device.bind_mw = ehca_bind_mw;
|
|
shca->ib_device.dealloc_mw = ehca_dealloc_mw;
|
|
shca->ib_device.alloc_fmr = ehca_alloc_fmr;
|
|
shca->ib_device.map_phys_fmr = ehca_map_phys_fmr;
|
|
shca->ib_device.unmap_fmr = ehca_unmap_fmr;
|
|
shca->ib_device.dealloc_fmr = ehca_dealloc_fmr;
|
|
shca->ib_device.attach_mcast = ehca_attach_mcast;
|
|
shca->ib_device.detach_mcast = ehca_detach_mcast;
|
|
shca->ib_device.process_mad = ehca_process_mad;
|
|
shca->ib_device.mmap = ehca_mmap;
|
|
|
|
if (EHCA_BMASK_GET(HCA_CAP_SRQ, shca->hca_cap)) {
|
|
shca->ib_device.uverbs_cmd_mask |=
|
|
(1ull << IB_USER_VERBS_CMD_CREATE_SRQ) |
|
|
(1ull << IB_USER_VERBS_CMD_MODIFY_SRQ) |
|
|
(1ull << IB_USER_VERBS_CMD_QUERY_SRQ) |
|
|
(1ull << IB_USER_VERBS_CMD_DESTROY_SRQ);
|
|
|
|
shca->ib_device.create_srq = ehca_create_srq;
|
|
shca->ib_device.modify_srq = ehca_modify_srq;
|
|
shca->ib_device.query_srq = ehca_query_srq;
|
|
shca->ib_device.destroy_srq = ehca_destroy_srq;
|
|
shca->ib_device.post_srq_recv = ehca_post_srq_recv;
|
|
}
|
|
|
|
return ret;
|
|
}
|
|
|
|
static int ehca_create_aqp1(struct ehca_shca *shca, u32 port)
|
|
{
|
|
struct ehca_sport *sport = &shca->sport[port - 1];
|
|
struct ib_cq *ibcq;
|
|
struct ib_qp *ibqp;
|
|
struct ib_qp_init_attr qp_init_attr;
|
|
int ret;
|
|
|
|
if (sport->ibcq_aqp1) {
|
|
ehca_err(&shca->ib_device, "AQP1 CQ is already created.");
|
|
return -EPERM;
|
|
}
|
|
|
|
ibcq = ib_create_cq(&shca->ib_device, NULL, NULL, (void *)(-1), 10, 0);
|
|
if (IS_ERR(ibcq)) {
|
|
ehca_err(&shca->ib_device, "Cannot create AQP1 CQ.");
|
|
return PTR_ERR(ibcq);
|
|
}
|
|
sport->ibcq_aqp1 = ibcq;
|
|
|
|
if (sport->ibqp_sqp[IB_QPT_GSI]) {
|
|
ehca_err(&shca->ib_device, "AQP1 QP is already created.");
|
|
ret = -EPERM;
|
|
goto create_aqp1;
|
|
}
|
|
|
|
memset(&qp_init_attr, 0, sizeof(struct ib_qp_init_attr));
|
|
qp_init_attr.send_cq = ibcq;
|
|
qp_init_attr.recv_cq = ibcq;
|
|
qp_init_attr.sq_sig_type = IB_SIGNAL_ALL_WR;
|
|
qp_init_attr.cap.max_send_wr = 100;
|
|
qp_init_attr.cap.max_recv_wr = 100;
|
|
qp_init_attr.cap.max_send_sge = 2;
|
|
qp_init_attr.cap.max_recv_sge = 1;
|
|
qp_init_attr.qp_type = IB_QPT_GSI;
|
|
qp_init_attr.port_num = port;
|
|
qp_init_attr.qp_context = NULL;
|
|
qp_init_attr.event_handler = NULL;
|
|
qp_init_attr.srq = NULL;
|
|
|
|
ibqp = ib_create_qp(&shca->pd->ib_pd, &qp_init_attr);
|
|
if (IS_ERR(ibqp)) {
|
|
ehca_err(&shca->ib_device, "Cannot create AQP1 QP.");
|
|
ret = PTR_ERR(ibqp);
|
|
goto create_aqp1;
|
|
}
|
|
sport->ibqp_sqp[IB_QPT_GSI] = ibqp;
|
|
|
|
return 0;
|
|
|
|
create_aqp1:
|
|
ib_destroy_cq(sport->ibcq_aqp1);
|
|
return ret;
|
|
}
|
|
|
|
static int ehca_destroy_aqp1(struct ehca_sport *sport)
|
|
{
|
|
int ret;
|
|
|
|
ret = ib_destroy_qp(sport->ibqp_sqp[IB_QPT_GSI]);
|
|
if (ret) {
|
|
ehca_gen_err("Cannot destroy AQP1 QP. ret=%i", ret);
|
|
return ret;
|
|
}
|
|
|
|
ret = ib_destroy_cq(sport->ibcq_aqp1);
|
|
if (ret)
|
|
ehca_gen_err("Cannot destroy AQP1 CQ. ret=%i", ret);
|
|
|
|
return ret;
|
|
}
|
|
|
|
static ssize_t ehca_show_debug_level(struct device_driver *ddp, char *buf)
|
|
{
|
|
return snprintf(buf, PAGE_SIZE, "%d\n", ehca_debug_level);
|
|
}
|
|
|
|
static ssize_t ehca_store_debug_level(struct device_driver *ddp,
|
|
const char *buf, size_t count)
|
|
{
|
|
int value = (*buf) - '0';
|
|
if (value >= 0 && value <= 9)
|
|
ehca_debug_level = value;
|
|
return 1;
|
|
}
|
|
|
|
static DRIVER_ATTR(debug_level, S_IRUSR | S_IWUSR,
|
|
ehca_show_debug_level, ehca_store_debug_level);
|
|
|
|
static struct attribute *ehca_drv_attrs[] = {
|
|
&driver_attr_debug_level.attr,
|
|
NULL
|
|
};
|
|
|
|
static struct attribute_group ehca_drv_attr_grp = {
|
|
.attrs = ehca_drv_attrs
|
|
};
|
|
|
|
static struct attribute_group *ehca_drv_attr_groups[] = {
|
|
&ehca_drv_attr_grp,
|
|
NULL,
|
|
};
|
|
|
|
#define EHCA_RESOURCE_ATTR(name) \
|
|
static ssize_t ehca_show_##name(struct device *dev, \
|
|
struct device_attribute *attr, \
|
|
char *buf) \
|
|
{ \
|
|
struct ehca_shca *shca; \
|
|
struct hipz_query_hca *rblock; \
|
|
int data; \
|
|
\
|
|
shca = dev->driver_data; \
|
|
\
|
|
rblock = ehca_alloc_fw_ctrlblock(GFP_KERNEL); \
|
|
if (!rblock) { \
|
|
dev_err(dev, "Can't allocate rblock memory.\n"); \
|
|
return 0; \
|
|
} \
|
|
\
|
|
if (hipz_h_query_hca(shca->ipz_hca_handle, rblock) != H_SUCCESS) { \
|
|
dev_err(dev, "Can't query device properties\n"); \
|
|
ehca_free_fw_ctrlblock(rblock); \
|
|
return 0; \
|
|
} \
|
|
\
|
|
data = rblock->name; \
|
|
ehca_free_fw_ctrlblock(rblock); \
|
|
\
|
|
if ((strcmp(#name, "num_ports") == 0) && (ehca_nr_ports == 1)) \
|
|
return snprintf(buf, 256, "1\n"); \
|
|
else \
|
|
return snprintf(buf, 256, "%d\n", data); \
|
|
\
|
|
} \
|
|
static DEVICE_ATTR(name, S_IRUGO, ehca_show_##name, NULL);
|
|
|
|
EHCA_RESOURCE_ATTR(num_ports);
|
|
EHCA_RESOURCE_ATTR(hw_ver);
|
|
EHCA_RESOURCE_ATTR(max_eq);
|
|
EHCA_RESOURCE_ATTR(cur_eq);
|
|
EHCA_RESOURCE_ATTR(max_cq);
|
|
EHCA_RESOURCE_ATTR(cur_cq);
|
|
EHCA_RESOURCE_ATTR(max_qp);
|
|
EHCA_RESOURCE_ATTR(cur_qp);
|
|
EHCA_RESOURCE_ATTR(max_mr);
|
|
EHCA_RESOURCE_ATTR(cur_mr);
|
|
EHCA_RESOURCE_ATTR(max_mw);
|
|
EHCA_RESOURCE_ATTR(cur_mw);
|
|
EHCA_RESOURCE_ATTR(max_pd);
|
|
EHCA_RESOURCE_ATTR(max_ah);
|
|
|
|
static ssize_t ehca_show_adapter_handle(struct device *dev,
|
|
struct device_attribute *attr,
|
|
char *buf)
|
|
{
|
|
struct ehca_shca *shca = dev->driver_data;
|
|
|
|
return sprintf(buf, "%lx\n", shca->ipz_hca_handle.handle);
|
|
|
|
}
|
|
static DEVICE_ATTR(adapter_handle, S_IRUGO, ehca_show_adapter_handle, NULL);
|
|
|
|
static struct attribute *ehca_dev_attrs[] = {
|
|
&dev_attr_adapter_handle.attr,
|
|
&dev_attr_num_ports.attr,
|
|
&dev_attr_hw_ver.attr,
|
|
&dev_attr_max_eq.attr,
|
|
&dev_attr_cur_eq.attr,
|
|
&dev_attr_max_cq.attr,
|
|
&dev_attr_cur_cq.attr,
|
|
&dev_attr_max_qp.attr,
|
|
&dev_attr_cur_qp.attr,
|
|
&dev_attr_max_mr.attr,
|
|
&dev_attr_cur_mr.attr,
|
|
&dev_attr_max_mw.attr,
|
|
&dev_attr_cur_mw.attr,
|
|
&dev_attr_max_pd.attr,
|
|
&dev_attr_max_ah.attr,
|
|
NULL
|
|
};
|
|
|
|
static struct attribute_group ehca_dev_attr_grp = {
|
|
.attrs = ehca_dev_attrs
|
|
};
|
|
|
|
static int __devinit ehca_probe(struct of_device *dev,
|
|
const struct of_device_id *id)
|
|
{
|
|
struct ehca_shca *shca;
|
|
const u64 *handle;
|
|
struct ib_pd *ibpd;
|
|
int ret, i, eq_size;
|
|
|
|
handle = of_get_property(dev->node, "ibm,hca-handle", NULL);
|
|
if (!handle) {
|
|
ehca_gen_err("Cannot get eHCA handle for adapter: %s.",
|
|
dev->node->full_name);
|
|
return -ENODEV;
|
|
}
|
|
|
|
if (!(*handle)) {
|
|
ehca_gen_err("Wrong eHCA handle for adapter: %s.",
|
|
dev->node->full_name);
|
|
return -ENODEV;
|
|
}
|
|
|
|
shca = (struct ehca_shca *)ib_alloc_device(sizeof(*shca));
|
|
if (!shca) {
|
|
ehca_gen_err("Cannot allocate shca memory.");
|
|
return -ENOMEM;
|
|
}
|
|
mutex_init(&shca->modify_mutex);
|
|
atomic_set(&shca->num_cqs, 0);
|
|
atomic_set(&shca->num_qps, 0);
|
|
for (i = 0; i < ARRAY_SIZE(shca->sport); i++)
|
|
spin_lock_init(&shca->sport[i].mod_sqp_lock);
|
|
|
|
shca->ofdev = dev;
|
|
shca->ipz_hca_handle.handle = *handle;
|
|
dev->dev.driver_data = shca;
|
|
|
|
ret = ehca_sense_attributes(shca);
|
|
if (ret < 0) {
|
|
ehca_gen_err("Cannot sense eHCA attributes.");
|
|
goto probe1;
|
|
}
|
|
|
|
ret = ehca_init_device(shca);
|
|
if (ret) {
|
|
ehca_gen_err("Cannot init ehca device struct");
|
|
goto probe1;
|
|
}
|
|
|
|
eq_size = 2 * ehca_max_cq + 4 * ehca_max_qp;
|
|
/* create event queues */
|
|
ret = ehca_create_eq(shca, &shca->eq, EHCA_EQ, eq_size);
|
|
if (ret) {
|
|
ehca_err(&shca->ib_device, "Cannot create EQ.");
|
|
goto probe1;
|
|
}
|
|
|
|
ret = ehca_create_eq(shca, &shca->neq, EHCA_NEQ, 513);
|
|
if (ret) {
|
|
ehca_err(&shca->ib_device, "Cannot create NEQ.");
|
|
goto probe3;
|
|
}
|
|
|
|
/* create internal protection domain */
|
|
ibpd = ehca_alloc_pd(&shca->ib_device, (void *)(-1), NULL);
|
|
if (IS_ERR(ibpd)) {
|
|
ehca_err(&shca->ib_device, "Cannot create internal PD.");
|
|
ret = PTR_ERR(ibpd);
|
|
goto probe4;
|
|
}
|
|
|
|
shca->pd = container_of(ibpd, struct ehca_pd, ib_pd);
|
|
shca->pd->ib_pd.device = &shca->ib_device;
|
|
|
|
/* create internal max MR */
|
|
ret = ehca_reg_internal_maxmr(shca, shca->pd, &shca->maxmr);
|
|
|
|
if (ret) {
|
|
ehca_err(&shca->ib_device, "Cannot create internal MR ret=%i",
|
|
ret);
|
|
goto probe5;
|
|
}
|
|
|
|
ret = ib_register_device(&shca->ib_device);
|
|
if (ret) {
|
|
ehca_err(&shca->ib_device,
|
|
"ib_register_device() failed ret=%i", ret);
|
|
goto probe6;
|
|
}
|
|
|
|
/* create AQP1 for port 1 */
|
|
if (ehca_open_aqp1 == 1) {
|
|
shca->sport[0].port_state = IB_PORT_DOWN;
|
|
ret = ehca_create_aqp1(shca, 1);
|
|
if (ret) {
|
|
ehca_err(&shca->ib_device,
|
|
"Cannot create AQP1 for port 1.");
|
|
goto probe7;
|
|
}
|
|
}
|
|
|
|
/* create AQP1 for port 2 */
|
|
if ((ehca_open_aqp1 == 1) && (shca->num_ports == 2)) {
|
|
shca->sport[1].port_state = IB_PORT_DOWN;
|
|
ret = ehca_create_aqp1(shca, 2);
|
|
if (ret) {
|
|
ehca_err(&shca->ib_device,
|
|
"Cannot create AQP1 for port 2.");
|
|
goto probe8;
|
|
}
|
|
}
|
|
|
|
ret = sysfs_create_group(&dev->dev.kobj, &ehca_dev_attr_grp);
|
|
if (ret) /* only complain; we can live without attributes */
|
|
ehca_err(&shca->ib_device,
|
|
"Cannot create device attributes ret=%d", ret);
|
|
|
|
spin_lock(&shca_list_lock);
|
|
list_add(&shca->shca_list, &shca_list);
|
|
spin_unlock(&shca_list_lock);
|
|
|
|
return 0;
|
|
|
|
probe8:
|
|
ret = ehca_destroy_aqp1(&shca->sport[0]);
|
|
if (ret)
|
|
ehca_err(&shca->ib_device,
|
|
"Cannot destroy AQP1 for port 1. ret=%i", ret);
|
|
|
|
probe7:
|
|
ib_unregister_device(&shca->ib_device);
|
|
|
|
probe6:
|
|
ret = ehca_dereg_internal_maxmr(shca);
|
|
if (ret)
|
|
ehca_err(&shca->ib_device,
|
|
"Cannot destroy internal MR. ret=%x", ret);
|
|
|
|
probe5:
|
|
ret = ehca_dealloc_pd(&shca->pd->ib_pd);
|
|
if (ret)
|
|
ehca_err(&shca->ib_device,
|
|
"Cannot destroy internal PD. ret=%x", ret);
|
|
|
|
probe4:
|
|
ret = ehca_destroy_eq(shca, &shca->neq);
|
|
if (ret)
|
|
ehca_err(&shca->ib_device,
|
|
"Cannot destroy NEQ. ret=%x", ret);
|
|
|
|
probe3:
|
|
ret = ehca_destroy_eq(shca, &shca->eq);
|
|
if (ret)
|
|
ehca_err(&shca->ib_device,
|
|
"Cannot destroy EQ. ret=%x", ret);
|
|
|
|
probe1:
|
|
ib_dealloc_device(&shca->ib_device);
|
|
|
|
return -EINVAL;
|
|
}
|
|
|
|
static int __devexit ehca_remove(struct of_device *dev)
|
|
{
|
|
struct ehca_shca *shca = dev->dev.driver_data;
|
|
int ret;
|
|
|
|
sysfs_remove_group(&dev->dev.kobj, &ehca_dev_attr_grp);
|
|
|
|
if (ehca_open_aqp1 == 1) {
|
|
int i;
|
|
for (i = 0; i < shca->num_ports; i++) {
|
|
ret = ehca_destroy_aqp1(&shca->sport[i]);
|
|
if (ret)
|
|
ehca_err(&shca->ib_device,
|
|
"Cannot destroy AQP1 for port %x "
|
|
"ret=%i", ret, i);
|
|
}
|
|
}
|
|
|
|
ib_unregister_device(&shca->ib_device);
|
|
|
|
ret = ehca_dereg_internal_maxmr(shca);
|
|
if (ret)
|
|
ehca_err(&shca->ib_device,
|
|
"Cannot destroy internal MR. ret=%i", ret);
|
|
|
|
ret = ehca_dealloc_pd(&shca->pd->ib_pd);
|
|
if (ret)
|
|
ehca_err(&shca->ib_device,
|
|
"Cannot destroy internal PD. ret=%i", ret);
|
|
|
|
ret = ehca_destroy_eq(shca, &shca->eq);
|
|
if (ret)
|
|
ehca_err(&shca->ib_device, "Cannot destroy EQ. ret=%i", ret);
|
|
|
|
ret = ehca_destroy_eq(shca, &shca->neq);
|
|
if (ret)
|
|
ehca_err(&shca->ib_device, "Canot destroy NEQ. ret=%i", ret);
|
|
|
|
ib_dealloc_device(&shca->ib_device);
|
|
|
|
spin_lock(&shca_list_lock);
|
|
list_del(&shca->shca_list);
|
|
spin_unlock(&shca_list_lock);
|
|
|
|
return ret;
|
|
}
|
|
|
|
static struct of_device_id ehca_device_table[] =
|
|
{
|
|
{
|
|
.name = "lhca",
|
|
.compatible = "IBM,lhca",
|
|
},
|
|
{},
|
|
};
|
|
|
|
static struct of_platform_driver ehca_driver = {
|
|
.name = "ehca",
|
|
.match_table = ehca_device_table,
|
|
.probe = ehca_probe,
|
|
.remove = ehca_remove,
|
|
.driver = {
|
|
.groups = ehca_drv_attr_groups,
|
|
},
|
|
};
|
|
|
|
void ehca_poll_eqs(unsigned long data)
|
|
{
|
|
struct ehca_shca *shca;
|
|
|
|
spin_lock(&shca_list_lock);
|
|
list_for_each_entry(shca, &shca_list, shca_list) {
|
|
if (shca->eq.is_initialized) {
|
|
/* call deadman proc only if eq ptr does not change */
|
|
struct ehca_eq *eq = &shca->eq;
|
|
int max = 3;
|
|
volatile u64 q_ofs, q_ofs2;
|
|
u64 flags;
|
|
spin_lock_irqsave(&eq->spinlock, flags);
|
|
q_ofs = eq->ipz_queue.current_q_offset;
|
|
spin_unlock_irqrestore(&eq->spinlock, flags);
|
|
do {
|
|
spin_lock_irqsave(&eq->spinlock, flags);
|
|
q_ofs2 = eq->ipz_queue.current_q_offset;
|
|
spin_unlock_irqrestore(&eq->spinlock, flags);
|
|
max--;
|
|
} while (q_ofs == q_ofs2 && max > 0);
|
|
if (q_ofs == q_ofs2)
|
|
ehca_process_eq(shca, 0);
|
|
}
|
|
}
|
|
mod_timer(&poll_eqs_timer, round_jiffies(jiffies + HZ));
|
|
spin_unlock(&shca_list_lock);
|
|
}
|
|
|
|
static int __init ehca_module_init(void)
|
|
{
|
|
int ret;
|
|
|
|
printk(KERN_INFO "eHCA Infiniband Device Driver "
|
|
"(Version " HCAD_VERSION ")\n");
|
|
|
|
ret = ehca_create_comp_pool();
|
|
if (ret) {
|
|
ehca_gen_err("Cannot create comp pool.");
|
|
return ret;
|
|
}
|
|
|
|
ret = ehca_create_slab_caches();
|
|
if (ret) {
|
|
ehca_gen_err("Cannot create SLAB caches");
|
|
ret = -ENOMEM;
|
|
goto module_init1;
|
|
}
|
|
|
|
ret = ibmebus_register_driver(&ehca_driver);
|
|
if (ret) {
|
|
ehca_gen_err("Cannot register eHCA device driver");
|
|
ret = -EINVAL;
|
|
goto module_init2;
|
|
}
|
|
|
|
if (ehca_poll_all_eqs != 1) {
|
|
ehca_gen_err("WARNING!!!");
|
|
ehca_gen_err("It is possible to lose interrupts.");
|
|
} else {
|
|
init_timer(&poll_eqs_timer);
|
|
poll_eqs_timer.function = ehca_poll_eqs;
|
|
poll_eqs_timer.expires = jiffies + HZ;
|
|
add_timer(&poll_eqs_timer);
|
|
}
|
|
|
|
return 0;
|
|
|
|
module_init2:
|
|
ehca_destroy_slab_caches();
|
|
|
|
module_init1:
|
|
ehca_destroy_comp_pool();
|
|
return ret;
|
|
};
|
|
|
|
static void __exit ehca_module_exit(void)
|
|
{
|
|
if (ehca_poll_all_eqs == 1)
|
|
del_timer_sync(&poll_eqs_timer);
|
|
|
|
ibmebus_unregister_driver(&ehca_driver);
|
|
|
|
ehca_destroy_slab_caches();
|
|
|
|
ehca_destroy_comp_pool();
|
|
|
|
idr_destroy(&ehca_cq_idr);
|
|
idr_destroy(&ehca_qp_idr);
|
|
};
|
|
|
|
module_init(ehca_module_init);
|
|
module_exit(ehca_module_exit);
|