#include <sys/types.h>
#include <sys/conf.h>
#include <sys/ddi.h>
#include <sys/sunddi.h>
#include <sys/modctl.h>
#include <sys/esunddi.h>
#include <sys/ib/adapters/hermon/hermon.h>
extern uint32_t hermon_kernel_data_ro;
extern uint32_t hermon_user_data_ro;
extern int hermon_rdma_debug;
static uint_t hermon_memkey_cnt = 0x00;
#define HERMON_MEMKEY_SHIFT 24
#define HERMON_MPT_SW_OWNERSHIP 0xF
#define HERMON_MPT_FREE 0x3
static int hermon_mr_common_reg(hermon_state_t *state, hermon_pdhdl_t pd,
hermon_bind_info_t *bind, hermon_mrhdl_t *mrhdl, hermon_mr_options_t *op,
hermon_mpt_rsrc_type_t mpt_type);
static int hermon_mr_common_rereg(hermon_state_t *state, hermon_mrhdl_t mr,
hermon_pdhdl_t pd, hermon_bind_info_t *bind, hermon_mrhdl_t *mrhdl_new,
hermon_mr_options_t *op);
static int hermon_mr_rereg_xlat_helper(hermon_state_t *state, hermon_mrhdl_t mr,
hermon_bind_info_t *bind, hermon_mr_options_t *op, uint64_t *mtt_addr,
uint_t sleep, uint_t *dereg_level);
static uint64_t hermon_mr_nummtt_needed(hermon_state_t *state,
hermon_bind_info_t *bind, uint_t *mtt_pgsize);
static int hermon_mr_mem_bind(hermon_state_t *state, hermon_bind_info_t *bind,
ddi_dma_handle_t dmahdl, uint_t sleep, uint_t is_buffer);
static void hermon_mr_mem_unbind(hermon_state_t *state,
hermon_bind_info_t *bind);
static int hermon_mr_fast_mtt_write(hermon_state_t *state, hermon_rsrc_t *mtt,
hermon_bind_info_t *bind, uint32_t mtt_pgsize_bits);
static int hermon_mr_fast_mtt_write_fmr(hermon_state_t *state,
hermon_rsrc_t *mtt, ibt_pmr_attr_t *mem_pattr, uint32_t mtt_pgsize_bits);
static uint_t hermon_mtt_refcnt_inc(hermon_rsrc_t *rsrc);
static uint_t hermon_mtt_refcnt_dec(hermon_rsrc_t *rsrc);
static struct umem_callback_ops hermon_umem_cbops = {
UMEM_CALLBACK_VERSION,
hermon_umap_umemlock_cb,
};
int
hermon_mr_register(hermon_state_t *state, hermon_pdhdl_t pd,
ibt_mr_attr_t *mr_attr, hermon_mrhdl_t *mrhdl, hermon_mr_options_t *op,
hermon_mpt_rsrc_type_t mpt_type)
{
hermon_bind_info_t bind;
int status;
bind.bi_type = HERMON_BINDHDL_VADDR;
bind.bi_addr = mr_attr->mr_vaddr;
bind.bi_len = mr_attr->mr_len;
bind.bi_as = mr_attr->mr_as;
bind.bi_flags = mr_attr->mr_flags;
status = hermon_mr_common_reg(state, pd, &bind, mrhdl, op,
mpt_type);
return (status);
}
int
hermon_mr_register_buf(hermon_state_t *state, hermon_pdhdl_t pd,
ibt_smr_attr_t *mr_attr, struct buf *buf, hermon_mrhdl_t *mrhdl,
hermon_mr_options_t *op, hermon_mpt_rsrc_type_t mpt_type)
{
hermon_bind_info_t bind;
int status;
bind.bi_type = HERMON_BINDHDL_BUF;
bind.bi_buf = buf;
if (mr_attr->mr_flags & IBT_MR_PHYS_IOVA) {
bind.bi_addr = mr_attr->mr_vaddr;
} else {
bind.bi_addr = (uint64_t)(uintptr_t)buf->b_un.b_addr;
}
bind.bi_as = NULL;
bind.bi_len = (uint64_t)buf->b_bcount;
bind.bi_flags = mr_attr->mr_flags;
status = hermon_mr_common_reg(state, pd, &bind, mrhdl, op, mpt_type);
return (status);
}
int
hermon_mr_register_shared(hermon_state_t *state, hermon_mrhdl_t mrhdl,
hermon_pdhdl_t pd, ibt_smr_attr_t *mr_attr, hermon_mrhdl_t *mrhdl_new)
{
hermon_rsrc_t *mpt, *mtt, *rsrc;
hermon_umap_db_entry_t *umapdb;
hermon_hw_dmpt_t mpt_entry;
hermon_mrhdl_t mr;
hermon_bind_info_t *bind;
ddi_umem_cookie_t umem_cookie;
size_t umem_len;
caddr_t umem_addr;
uint64_t mtt_addr, pgsize_msk;
uint_t sleep, mr_is_umem;
int status, umem_flags;
sleep = (mr_attr->mr_flags & IBT_MR_NOSLEEP) ? HERMON_NOSLEEP :
HERMON_SLEEP;
if ((sleep == HERMON_SLEEP) &&
(sleep != HERMON_SLEEPFLAG_FOR_CONTEXT())) {
status = IBT_INVALID_PARAM;
goto mrshared_fail;
}
hermon_pd_refcnt_inc(pd);
status = hermon_rsrc_alloc(state, HERMON_DMPT, 1, sleep, &mpt);
if (status != DDI_SUCCESS) {
status = IBT_INSUFF_RESOURCE;
goto mrshared_fail1;
}
status = hermon_rsrc_alloc(state, HERMON_MRHDL, 1, sleep, &rsrc);
if (status != DDI_SUCCESS) {
status = IBT_INSUFF_RESOURCE;
goto mrshared_fail2;
}
mr = (hermon_mrhdl_t)rsrc->hr_addr;
_NOTE(NOW_INVISIBLE_TO_OTHER_THREADS(*mr))
mr->mr_accflag = 0;
if (mr_attr->mr_flags & IBT_MR_ENABLE_WINDOW_BIND)
mr->mr_accflag |= IBT_MR_WINDOW_BIND;
if (mr_attr->mr_flags & IBT_MR_ENABLE_LOCAL_WRITE)
mr->mr_accflag |= IBT_MR_LOCAL_WRITE;
if (mr_attr->mr_flags & IBT_MR_ENABLE_REMOTE_READ)
mr->mr_accflag |= IBT_MR_REMOTE_READ;
if (mr_attr->mr_flags & IBT_MR_ENABLE_REMOTE_WRITE)
mr->mr_accflag |= IBT_MR_REMOTE_WRITE;
if (mr_attr->mr_flags & IBT_MR_ENABLE_REMOTE_ATOMIC)
mr->mr_accflag |= IBT_MR_REMOTE_ATOMIC;
mr->mr_rkey = mr->mr_lkey = hermon_mr_keycalc(mpt->hr_indx);
mutex_enter(&mrhdl->mr_lock);
if ((mrhdl->mr_is_umem) && (mrhdl->mr_umemcookie == NULL)) {
mutex_exit(&mrhdl->mr_lock);
status = IBT_MR_HDL_INVALID;
goto mrshared_fail3;
}
mr_is_umem = mrhdl->mr_is_umem;
if (mr_is_umem) {
umem_len = ptob(btopr(mrhdl->mr_bindinfo.bi_len));
umem_addr = (caddr_t)((uintptr_t)mrhdl->mr_bindinfo.bi_addr &
~PAGEOFFSET);
umem_flags = (DDI_UMEMLOCK_WRITE | DDI_UMEMLOCK_READ |
DDI_UMEMLOCK_LONGTERM);
status = umem_lockmemory(umem_addr, umem_len, umem_flags,
&umem_cookie, &hermon_umem_cbops, NULL);
if (status != 0) {
mutex_exit(&mrhdl->mr_lock);
status = IBT_INSUFF_RESOURCE;
goto mrshared_fail3;
}
umapdb = hermon_umap_db_alloc(state->hs_instance,
(uint64_t)(uintptr_t)umem_cookie, MLNX_UMAP_MRMEM_RSRC,
(uint64_t)(uintptr_t)rsrc);
if (umapdb == NULL) {
mutex_exit(&mrhdl->mr_lock);
status = IBT_INSUFF_RESOURCE;
goto mrshared_fail4;
}
}
mr->mr_mttrsrcp = mrhdl->mr_mttrsrcp;
mr->mr_logmttpgsz = mrhdl->mr_logmttpgsz;
mr->mr_bindinfo = mrhdl->mr_bindinfo;
mr->mr_mttrefcntp = mrhdl->mr_mttrefcntp;
mutex_exit(&mrhdl->mr_lock);
bind = &mr->mr_bindinfo;
_NOTE(NOW_INVISIBLE_TO_OTHER_THREADS(*bind))
mtt = mr->mr_mttrsrcp;
(void) hermon_mtt_refcnt_inc(mr->mr_mttrefcntp);
pgsize_msk = (((uint64_t)1 << mr->mr_logmttpgsz) - 1);
bind->bi_addr = ((mr_attr->mr_vaddr & ~pgsize_msk) |
(mr->mr_bindinfo.bi_addr & pgsize_msk));
bzero(&mpt_entry, sizeof (hermon_hw_dmpt_t));
mpt_entry.en_bind = (mr->mr_accflag & IBT_MR_WINDOW_BIND) ? 1 : 0;
mpt_entry.atomic = (mr->mr_accflag & IBT_MR_REMOTE_ATOMIC) ? 1 : 0;
mpt_entry.rw = (mr->mr_accflag & IBT_MR_REMOTE_WRITE) ? 1 : 0;
mpt_entry.rr = (mr->mr_accflag & IBT_MR_REMOTE_READ) ? 1 : 0;
mpt_entry.lw = (mr->mr_accflag & IBT_MR_LOCAL_WRITE) ? 1 : 0;
mpt_entry.lr = 1;
mpt_entry.reg_win = HERMON_MPT_IS_REGION;
mpt_entry.entity_sz = mr->mr_logmttpgsz;
mpt_entry.mem_key = mr->mr_lkey;
mpt_entry.pd = pd->pd_pdnum;
mpt_entry.start_addr = bind->bi_addr;
mpt_entry.reg_win_len = bind->bi_len;
mtt_addr = (mtt->hr_indx << HERMON_MTT_SIZE_SHIFT);
mpt_entry.mtt_addr_h = mtt_addr >> 32;
mpt_entry.mtt_addr_l = mtt_addr >> 3;
status = hermon_cmn_ownership_cmd_post(state, SW2HW_MPT, &mpt_entry,
sizeof (hermon_hw_dmpt_t), mpt->hr_indx, sleep);
if (status != HERMON_CMD_SUCCESS) {
cmn_err(CE_CONT, "Hermon: SW2HW_MPT command failed: %08x\n",
status);
if (status == HERMON_CMD_INVALID_STATUS) {
hermon_fm_ereport(state, HCA_SYS_ERR, HCA_ERR_SRV_LOST);
}
status = ibc_get_ci_failure(0);
goto mrshared_fail5;
}
mr->mr_mptrsrcp = mpt;
mr->mr_mttrsrcp = mtt;
mr->mr_mpt_type = HERMON_MPT_DMPT;
mr->mr_pdhdl = pd;
mr->mr_rsrcp = rsrc;
mr->mr_is_umem = mr_is_umem;
mr->mr_is_fmr = 0;
mr->mr_umemcookie = (mr_is_umem != 0) ? umem_cookie : NULL;
mr->mr_umem_cbfunc = NULL;
mr->mr_umem_cbarg1 = NULL;
mr->mr_umem_cbarg2 = NULL;
mr->mr_lkey = hermon_mr_key_swap(mr->mr_lkey);
mr->mr_rkey = hermon_mr_key_swap(mr->mr_rkey);
if (mr_is_umem) {
hermon_umap_db_add(umapdb);
}
*mrhdl_new = mr;
return (DDI_SUCCESS);
mrshared_fail5:
(void) hermon_mtt_refcnt_dec(mr->mr_mttrefcntp);
if (mr_is_umem) {
hermon_umap_db_free(umapdb);
}
mrshared_fail4:
if (mr_is_umem) {
ddi_umem_unlock(umem_cookie);
}
mrshared_fail3:
hermon_rsrc_free(state, &rsrc);
mrshared_fail2:
hermon_rsrc_free(state, &mpt);
mrshared_fail1:
hermon_pd_refcnt_dec(pd);
mrshared_fail:
return (status);
}
int
hermon_mr_alloc_fmr(hermon_state_t *state, hermon_pdhdl_t pd,
hermon_fmrhdl_t fmr_pool, hermon_mrhdl_t *mrhdl)
{
hermon_rsrc_t *mpt, *mtt, *rsrc;
hermon_hw_dmpt_t mpt_entry;
hermon_mrhdl_t mr;
hermon_bind_info_t bind;
uint64_t mtt_addr;
uint64_t nummtt;
uint_t sleep, mtt_pgsize_bits;
int status;
offset_t i;
hermon_icm_table_t *icm_table;
hermon_dma_info_t *dma_info;
uint32_t index1, index2, rindx;
sleep = (fmr_pool->fmr_flags & IBT_MR_SLEEP) ? HERMON_SLEEP :
HERMON_NOSLEEP;
if ((sleep == HERMON_SLEEP) &&
(sleep != HERMON_SLEEPFLAG_FOR_CONTEXT())) {
return (IBT_INVALID_PARAM);
}
hermon_pd_refcnt_inc(pd);
status = hermon_rsrc_alloc(state, HERMON_DMPT, 1, sleep, &mpt);
if (status != DDI_SUCCESS) {
status = IBT_INSUFF_RESOURCE;
goto fmralloc_fail1;
}
status = hermon_rsrc_alloc(state, HERMON_MRHDL, 1, sleep, &rsrc);
if (status != DDI_SUCCESS) {
status = IBT_INSUFF_RESOURCE;
goto fmralloc_fail2;
}
mr = (hermon_mrhdl_t)rsrc->hr_addr;
_NOTE(NOW_INVISIBLE_TO_OTHER_THREADS(*mr))
mr->mr_accflag = 0;
if (fmr_pool->fmr_flags & IBT_MR_ENABLE_LOCAL_WRITE)
mr->mr_accflag |= IBT_MR_LOCAL_WRITE;
if (fmr_pool->fmr_flags & IBT_MR_ENABLE_REMOTE_READ)
mr->mr_accflag |= IBT_MR_REMOTE_READ;
if (fmr_pool->fmr_flags & IBT_MR_ENABLE_REMOTE_WRITE)
mr->mr_accflag |= IBT_MR_REMOTE_WRITE;
if (fmr_pool->fmr_flags & IBT_MR_ENABLE_REMOTE_ATOMIC)
mr->mr_accflag |= IBT_MR_REMOTE_ATOMIC;
mr->mr_fmr_key = 1;
mr->mr_rkey = mr->mr_lkey = mpt->hr_indx;
bind.bi_addr = 0;
bind.bi_len = fmr_pool->fmr_max_pages << PAGESHIFT;
nummtt = hermon_mr_nummtt_needed(state, &bind, &mtt_pgsize_bits);
status = hermon_rsrc_alloc(state, HERMON_MTT, nummtt, sleep, &mtt);
if (status != DDI_SUCCESS) {
IBTF_DPRINTF_L2("FMR", "FATAL: too few MTTs");
status = IBT_INSUFF_RESOURCE;
goto fmralloc_fail3;
}
mr->mr_logmttpgsz = mtt_pgsize_bits;
bzero(&mpt_entry, sizeof (hermon_hw_dmpt_t));
mpt_entry.en_bind = 0;
mpt_entry.atomic = (mr->mr_accflag & IBT_MR_REMOTE_ATOMIC) ? 1 : 0;
mpt_entry.rw = (mr->mr_accflag & IBT_MR_REMOTE_WRITE) ? 1 : 0;
mpt_entry.rr = (mr->mr_accflag & IBT_MR_REMOTE_READ) ? 1 : 0;
mpt_entry.lw = (mr->mr_accflag & IBT_MR_LOCAL_WRITE) ? 1 : 0;
mpt_entry.lr = 1;
mpt_entry.reg_win = HERMON_MPT_IS_REGION;
mpt_entry.pd = pd->pd_pdnum;
mpt_entry.entity_sz = mr->mr_logmttpgsz;
mtt_addr = (mtt->hr_indx << HERMON_MTT_SIZE_SHIFT);
mpt_entry.fast_reg_en = 1;
mpt_entry.mtt_size = (uint_t)nummtt;
mpt_entry.mtt_addr_h = mtt_addr >> 32;
mpt_entry.mtt_addr_l = mtt_addr >> 3;
mpt_entry.mem_key = mr->mr_lkey;
mpt_entry.start_addr = 0;
mpt_entry.reg_win_len = 0;
status = hermon_cmn_ownership_cmd_post(state, SW2HW_MPT, &mpt_entry,
sizeof (hermon_hw_dmpt_t), mpt->hr_indx, sleep);
if (status != HERMON_CMD_SUCCESS) {
cmn_err(CE_CONT, "Hermon: SW2HW_MPT command failed: %08x\n",
status);
if (status == HERMON_CMD_INVALID_STATUS) {
hermon_fm_ereport(state, HCA_SYS_ERR, HCA_ERR_SRV_LOST);
}
status = ibc_get_ci_failure(0);
goto fmralloc_fail4;
}
mr->mr_mptrsrcp = mpt;
mr->mr_mttrsrcp = mtt;
mr->mr_mpt_type = HERMON_MPT_DMPT;
mr->mr_pdhdl = pd;
mr->mr_rsrcp = rsrc;
mr->mr_is_fmr = 1;
mr->mr_lkey = hermon_mr_key_swap(mr->mr_lkey);
mr->mr_rkey = hermon_mr_key_swap(mr->mr_rkey);
mr->mr_mttaddr = mtt_addr;
(void) memcpy(&mr->mr_bindinfo, &bind, sizeof (hermon_bind_info_t));
icm_table = &state->hs_icm[HERMON_DMPT];
rindx = mpt->hr_indx;
hermon_index(index1, index2, rindx, icm_table, i);
dma_info = icm_table->icm_dma[index1] + index2;
_NOTE(NOW_INVISIBLE_TO_OTHER_THREADS(*mpt))
mpt->hr_addr = (void *)((uintptr_t)(dma_info->vaddr + i * mpt->hr_len));
*mrhdl = mr;
return (DDI_SUCCESS);
fmralloc_fail4:
kmem_free(mtt, sizeof (hermon_rsrc_t) * nummtt);
fmralloc_fail3:
hermon_rsrc_free(state, &rsrc);
fmralloc_fail2:
hermon_rsrc_free(state, &mpt);
fmralloc_fail1:
hermon_pd_refcnt_dec(pd);
return (status);
}
int
hermon_mr_register_physical_fmr(hermon_state_t *state,
ibt_pmr_attr_t *mem_pattr_p, hermon_mrhdl_t mr, ibt_pmr_desc_t *mem_desc_p)
{
hermon_rsrc_t *mpt;
uint64_t *mpt_table;
int status;
uint32_t key;
mutex_enter(&mr->mr_lock);
mpt = mr->mr_mptrsrcp;
mpt_table = (uint64_t *)mpt->hr_addr;
*(uint8_t *)mpt_table = 0xF0;
membar_producer();
status = hermon_mr_fast_mtt_write_fmr(state, mr->mr_mttrsrcp,
mem_pattr_p, mr->mr_logmttpgsz);
if (status != DDI_SUCCESS) {
mutex_exit(&mr->mr_lock);
status = ibc_get_ci_failure(0);
goto fmr_reg_fail1;
}
key = mpt->hr_indx | (mr->mr_fmr_key++ << HERMON_MEMKEY_SHIFT);
mr->mr_lkey = mr->mr_rkey = hermon_mr_key_swap(key);
*(uint32_t *)&mpt_table[1] = htonl(key);
mpt_table[3] = htonll(mem_pattr_p->pmr_len);
mpt_table[2] = htonll(mem_pattr_p->pmr_iova);
*(uint32_t *)&mpt_table[4] = htonl(key);
membar_producer();
*(uint8_t *)mpt_table = 0x00;
mem_desc_p->pmd_lkey = mr->mr_lkey;
mem_desc_p->pmd_rkey = mr->mr_rkey;
mem_desc_p->pmd_iova = mem_pattr_p->pmr_iova;
mem_desc_p->pmd_phys_buf_list_sz = mem_pattr_p->pmr_len;
mr->mr_bindinfo.bi_addr = mem_pattr_p->pmr_iova;
mr->mr_bindinfo.bi_flags = mem_pattr_p->pmr_flags & IBT_MR_NONCOHERENT;
mutex_exit(&mr->mr_lock);
return (DDI_SUCCESS);
fmr_reg_fail1:
return (status);
}
int
hermon_mr_deregister(hermon_state_t *state, hermon_mrhdl_t *mrhdl, uint_t level,
uint_t sleep)
{
hermon_rsrc_t *mpt, *mtt, *rsrc, *mtt_refcnt;
hermon_umap_db_entry_t *umapdb;
hermon_pdhdl_t pd;
hermon_mrhdl_t mr;
hermon_bind_info_t *bind;
uint64_t value;
int status;
uint_t shared_mtt;
if ((sleep == HERMON_SLEEP) &&
(sleep != HERMON_SLEEPFLAG_FOR_CONTEXT())) {
status = IBT_INVALID_PARAM;
return (status);
}
mr = *mrhdl;
mutex_enter(&mr->mr_lock);
mpt = mr->mr_mptrsrcp;
mtt = mr->mr_mttrsrcp;
mtt_refcnt = mr->mr_mttrefcntp;
rsrc = mr->mr_rsrcp;
pd = mr->mr_pdhdl;
bind = &mr->mr_bindinfo;
if (mr->mr_is_fmr) {
mutex_exit(&mr->mr_lock);
return (IBT_INVALID_PARAM);
}
if ((mr->mr_is_umem) && (mr->mr_umemcookie == NULL)) {
goto mrdereg_finish_cleanup;
}
if (hermon_rdma_debug & 0x4)
IBTF_DPRINTF_L2("mr", "dereg: mr %p key %x",
mr, mr->mr_rkey);
mutex_exit(&mr->mr_lock);
if (level >= HERMON_MR_DEREG_ALL) {
if (mr->mr_mpt_type >= HERMON_MPT_DMPT) {
status = hermon_cmn_ownership_cmd_post(state, HW2SW_MPT,
NULL, 0, mpt->hr_indx, sleep);
if (status != HERMON_CMD_SUCCESS) {
if (status == HERMON_CMD_REG_BOUND) {
return (IBT_MR_IN_USE);
} else {
cmn_err(CE_CONT, "Hermon: HW2SW_MPT "
"command failed: %08x\n", status);
if (status ==
HERMON_CMD_INVALID_STATUS) {
hermon_fm_ereport(state,
HCA_SYS_ERR,
DDI_SERVICE_LOST);
}
return (IBT_INVALID_PARAM);
}
}
}
}
mutex_enter(&mr->mr_lock);
if (mr->mr_is_umem) {
status = hermon_umap_db_find(state->hs_instance,
(uint64_t)(uintptr_t)mr->mr_umemcookie,
MLNX_UMAP_MRMEM_RSRC, &value, HERMON_UMAP_DB_REMOVE,
&umapdb);
if (status == DDI_SUCCESS) {
hermon_umap_db_free(umapdb);
ddi_umem_unlock(mr->mr_umemcookie);
} else {
ddi_umem_unlock(mr->mr_umemcookie);
mr->mr_umemcookie = NULL;
}
}
if (mtt_refcnt != NULL) {
shared_mtt = hermon_mtt_refcnt_dec(mtt_refcnt);
if (!shared_mtt) {
hermon_rsrc_free(state, &mtt_refcnt);
}
if (!shared_mtt) {
if (level >= HERMON_MR_DEREG_NO_HW2SW_MPT) {
hermon_mr_mem_unbind(state, bind);
}
hermon_rsrc_free(state, &mtt);
}
}
if ((mr->mr_is_umem) && (mr->mr_umemcookie == NULL)) {
mutex_exit(&mr->mr_lock);
return (DDI_SUCCESS);
}
mrdereg_finish_cleanup:
mutex_exit(&mr->mr_lock);
hermon_rsrc_free(state, &rsrc);
if (mpt != NULL)
hermon_rsrc_free(state, &mpt);
hermon_pd_refcnt_dec(pd);
*mrhdl = NULL;
return (DDI_SUCCESS);
}
int
hermon_mr_dealloc_fmr(hermon_state_t *state, hermon_mrhdl_t *mrhdl)
{
hermon_rsrc_t *mpt, *mtt, *rsrc;
hermon_pdhdl_t pd;
hermon_mrhdl_t mr;
mr = *mrhdl;
mutex_enter(&mr->mr_lock);
mpt = mr->mr_mptrsrcp;
mtt = mr->mr_mttrsrcp;
rsrc = mr->mr_rsrcp;
pd = mr->mr_pdhdl;
mutex_exit(&mr->mr_lock);
hermon_rsrc_free(state, &mtt);
hermon_rsrc_free(state, &rsrc);
hermon_rsrc_free(state, &mpt);
hermon_pd_refcnt_dec(pd);
*mrhdl = NULL;
return (DDI_SUCCESS);
}
int
hermon_mr_query(hermon_state_t *state, hermon_mrhdl_t mr,
ibt_mr_query_attr_t *attr)
{
int status;
hermon_hw_dmpt_t mpt_entry;
uint32_t lkey;
_NOTE(NOW_INVISIBLE_TO_OTHER_THREADS(*attr))
mutex_enter(&mr->mr_lock);
if ((mr->mr_is_umem) && (mr->mr_umemcookie == NULL)) {
mutex_exit(&mr->mr_lock);
return (IBT_MR_HDL_INVALID);
}
status = hermon_cmn_query_cmd_post(state, QUERY_MPT, 0,
mr->mr_lkey >> 8, &mpt_entry, sizeof (hermon_hw_dmpt_t),
HERMON_NOSLEEP);
if (status != HERMON_CMD_SUCCESS) {
cmn_err(CE_CONT, "Hermon: QUERY_MPT failed: status %x", status);
mutex_exit(&mr->mr_lock);
return (ibc_get_ci_failure(0));
}
lkey = mpt_entry.mem_key;
mr->mr_lkey = mr->mr_rkey = (lkey >> 8) | (lkey << 24);
mr->mr_bindinfo.bi_addr = mpt_entry.start_addr;
mr->mr_bindinfo.bi_len = mpt_entry.reg_win_len;
mr->mr_accflag = (mr->mr_accflag & IBT_MR_RO_DISABLED) |
(mpt_entry.lw ? IBT_MR_LOCAL_WRITE : 0) |
(mpt_entry.rr ? IBT_MR_REMOTE_READ : 0) |
(mpt_entry.rw ? IBT_MR_REMOTE_WRITE : 0) |
(mpt_entry.atomic ? IBT_MR_REMOTE_ATOMIC : 0) |
(mpt_entry.en_bind ? IBT_MR_WINDOW_BIND : 0);
mr->mr_mttaddr = ((uint64_t)mpt_entry.mtt_addr_h << 32) |
(mpt_entry.mtt_addr_l << 3);
mr->mr_logmttpgsz = mpt_entry.entity_sz;
attr->mr_lkey_state =
(mpt_entry.status == HERMON_MPT_FREE) ? IBT_KEY_FREE :
(mpt_entry.status == HERMON_MPT_SW_OWNERSHIP) ? IBT_KEY_INVALID :
IBT_KEY_VALID;
attr->mr_phys_buf_list_sz = mpt_entry.mtt_size;
attr->mr_attr_flags = mr->mr_accflag;
attr->mr_pd = (ibt_pd_hdl_t)mr->mr_pdhdl;
attr->mr_lkey = (ibt_lkey_t)mr->mr_lkey;
attr->mr_lbounds.pb_addr = (ib_vaddr_t)mr->mr_bindinfo.bi_addr;
attr->mr_lbounds.pb_len = (size_t)mr->mr_bindinfo.bi_len;
if ((mr->mr_accflag & IBT_MR_REMOTE_READ) ||
(mr->mr_accflag & IBT_MR_REMOTE_WRITE) ||
(mr->mr_accflag & IBT_MR_REMOTE_ATOMIC)) {
attr->mr_rkey = (ibt_rkey_t)mr->mr_rkey;
attr->mr_rbounds.pb_addr = (ib_vaddr_t)mr->mr_bindinfo.bi_addr;
attr->mr_rbounds.pb_len = (size_t)mr->mr_bindinfo.bi_len;
}
attr->mr_sync_required = (mr->mr_bindinfo.bi_flags &
IBT_MR_NONCOHERENT) ? B_TRUE : B_FALSE;
mutex_exit(&mr->mr_lock);
return (DDI_SUCCESS);
}
int
hermon_mr_reregister(hermon_state_t *state, hermon_mrhdl_t mr,
hermon_pdhdl_t pd, ibt_mr_attr_t *mr_attr, hermon_mrhdl_t *mrhdl_new,
hermon_mr_options_t *op)
{
hermon_bind_info_t bind;
int status;
bind.bi_type = HERMON_BINDHDL_VADDR;
bind.bi_addr = mr_attr->mr_vaddr;
bind.bi_len = mr_attr->mr_len;
bind.bi_as = mr_attr->mr_as;
bind.bi_flags = mr_attr->mr_flags;
status = hermon_mr_common_rereg(state, mr, pd, &bind, mrhdl_new, op);
return (status);
}
int
hermon_mr_reregister_buf(hermon_state_t *state, hermon_mrhdl_t mr,
hermon_pdhdl_t pd, ibt_smr_attr_t *mr_attr, struct buf *buf,
hermon_mrhdl_t *mrhdl_new, hermon_mr_options_t *op)
{
hermon_bind_info_t bind;
int status;
bind.bi_type = HERMON_BINDHDL_BUF;
bind.bi_buf = buf;
if (mr_attr->mr_flags & IBT_MR_PHYS_IOVA) {
bind.bi_addr = mr_attr->mr_vaddr;
} else {
bind.bi_addr = (uint64_t)(uintptr_t)buf->b_un.b_addr;
}
bind.bi_len = (uint64_t)buf->b_bcount;
bind.bi_flags = mr_attr->mr_flags;
bind.bi_as = NULL;
status = hermon_mr_common_rereg(state, mr, pd, &bind, mrhdl_new, op);
return (status);
}
int
hermon_mr_sync(hermon_state_t *state, ibt_mr_sync_t *mr_segs, size_t num_segs)
{
hermon_mrhdl_t mrhdl;
uint64_t seg_vaddr, seg_len, seg_end;
uint64_t mr_start, mr_end;
uint_t type;
int status, i;
for (i = 0; i < num_segs; i++) {
mrhdl = (hermon_mrhdl_t)mr_segs[i].ms_handle;
if (mrhdl == NULL) {
status = IBT_MR_HDL_INVALID;
goto mrsync_fail;
}
mutex_enter(&mrhdl->mr_lock);
if ((mrhdl->mr_is_umem) && (mrhdl->mr_umemcookie == NULL)) {
mutex_exit(&mrhdl->mr_lock);
status = IBT_MR_HDL_INVALID;
goto mrsync_fail;
}
seg_vaddr = mr_segs[i].ms_vaddr;
seg_len = mr_segs[i].ms_len;
seg_end = seg_vaddr + seg_len - 1;
mr_start = mrhdl->mr_bindinfo.bi_addr;
mr_end = mr_start + mrhdl->mr_bindinfo.bi_len - 1;
if ((seg_vaddr < mr_start) || (seg_vaddr > mr_end)) {
mutex_exit(&mrhdl->mr_lock);
status = IBT_MR_VA_INVALID;
goto mrsync_fail;
}
if ((seg_end < mr_start) || (seg_end > mr_end)) {
mutex_exit(&mrhdl->mr_lock);
status = IBT_MR_LEN_INVALID;
goto mrsync_fail;
}
if (mr_segs[i].ms_flags & IBT_SYNC_READ) {
type = DDI_DMA_SYNC_FORDEV;
} else if (mr_segs[i].ms_flags & IBT_SYNC_WRITE) {
type = DDI_DMA_SYNC_FORCPU;
} else {
mutex_exit(&mrhdl->mr_lock);
status = IBT_INVALID_PARAM;
goto mrsync_fail;
}
(void) ddi_dma_sync(mrhdl->mr_bindinfo.bi_dmahdl,
(off_t)(seg_vaddr - mr_start), (size_t)seg_len, type);
mutex_exit(&mrhdl->mr_lock);
}
return (DDI_SUCCESS);
mrsync_fail:
return (status);
}
int
hermon_mw_alloc(hermon_state_t *state, hermon_pdhdl_t pd, ibt_mw_flags_t flags,
hermon_mwhdl_t *mwhdl)
{
hermon_rsrc_t *mpt, *rsrc;
hermon_hw_dmpt_t mpt_entry;
hermon_mwhdl_t mw;
uint_t sleep;
int status;
if (state != NULL)
return (IBT_INSUFF_RESOURCE);
sleep = (flags & IBT_MW_NOSLEEP) ? HERMON_NOSLEEP : HERMON_SLEEP;
if ((sleep == HERMON_SLEEP) &&
(sleep != HERMON_SLEEPFLAG_FOR_CONTEXT())) {
status = IBT_INVALID_PARAM;
goto mwalloc_fail;
}
hermon_pd_refcnt_inc(pd);
status = hermon_rsrc_alloc(state, HERMON_DMPT, 1, sleep, &mpt);
if (status != DDI_SUCCESS) {
status = IBT_INSUFF_RESOURCE;
goto mwalloc_fail1;
}
status = hermon_rsrc_alloc(state, HERMON_MRHDL, 1, sleep, &rsrc);
if (status != DDI_SUCCESS) {
status = IBT_INSUFF_RESOURCE;
goto mwalloc_fail2;
}
mw = (hermon_mwhdl_t)rsrc->hr_addr;
_NOTE(NOW_INVISIBLE_TO_OTHER_THREADS(*mw))
mw->mr_rkey = hermon_mr_keycalc(mpt->hr_indx);
bzero(&mpt_entry, sizeof (hermon_hw_dmpt_t));
mpt_entry.reg_win = HERMON_MPT_IS_WINDOW;
mpt_entry.mem_key = mw->mr_rkey;
mpt_entry.pd = pd->pd_pdnum;
mpt_entry.lr = 1;
status = hermon_cmn_ownership_cmd_post(state, SW2HW_MPT, &mpt_entry,
sizeof (hermon_hw_dmpt_t), mpt->hr_indx, sleep);
if (status != HERMON_CMD_SUCCESS) {
cmn_err(CE_CONT, "Hermon: SW2HW_MPT command failed: %08x\n",
status);
if (status == HERMON_CMD_INVALID_STATUS) {
hermon_fm_ereport(state, HCA_SYS_ERR, HCA_ERR_SRV_LOST);
}
status = ibc_get_ci_failure(0);
goto mwalloc_fail3;
}
mw->mr_mptrsrcp = mpt;
mw->mr_pdhdl = pd;
mw->mr_rsrcp = rsrc;
mw->mr_rkey = hermon_mr_key_swap(mw->mr_rkey);
*mwhdl = mw;
return (DDI_SUCCESS);
mwalloc_fail3:
hermon_rsrc_free(state, &rsrc);
mwalloc_fail2:
hermon_rsrc_free(state, &mpt);
mwalloc_fail1:
hermon_pd_refcnt_dec(pd);
mwalloc_fail:
return (status);
}
int
hermon_mw_free(hermon_state_t *state, hermon_mwhdl_t *mwhdl, uint_t sleep)
{
hermon_rsrc_t *mpt, *rsrc;
hermon_mwhdl_t mw;
int status;
hermon_pdhdl_t pd;
if ((sleep == HERMON_SLEEP) &&
(sleep != HERMON_SLEEPFLAG_FOR_CONTEXT())) {
status = IBT_INVALID_PARAM;
return (status);
}
mw = *mwhdl;
mutex_enter(&mw->mr_lock);
mpt = mw->mr_mptrsrcp;
rsrc = mw->mr_rsrcp;
pd = mw->mr_pdhdl;
mutex_exit(&mw->mr_lock);
_NOTE(NOW_INVISIBLE_TO_OTHER_THREADS(*mw))
status = hermon_cmn_ownership_cmd_post(state, HW2SW_MPT, NULL,
0, mpt->hr_indx, sleep);
if (status != HERMON_CMD_SUCCESS) {
cmn_err(CE_CONT, "Hermon: HW2SW_MPT command failed: %08x\n",
status);
if (status == HERMON_CMD_INVALID_STATUS) {
hermon_fm_ereport(state, HCA_SYS_ERR, HCA_ERR_SRV_LOST);
}
return (ibc_get_ci_failure(0));
}
hermon_rsrc_free(state, &rsrc);
hermon_rsrc_free(state, &mpt);
hermon_pd_refcnt_dec(pd);
*mwhdl = NULL;
return (DDI_SUCCESS);
}
uint32_t
hermon_mr_keycalc(uint32_t indx)
{
uint32_t tmp_key, tmp_indx;
_NOTE(NOW_INVISIBLE_TO_OTHER_THREADS(hermon_memkey_cnt))
tmp_key = (hermon_memkey_cnt++) << HERMON_MEMKEY_SHIFT;
tmp_indx = indx & 0xffffff;
return (tmp_key | tmp_indx);
}
uint32_t
hermon_mr_key_swap(uint32_t indx)
{
return (((indx >> 24) & 0x000000ff) | ((indx << 8) & 0xffffff00));
}
static int
hermon_mr_common_reg(hermon_state_t *state, hermon_pdhdl_t pd,
hermon_bind_info_t *bind, hermon_mrhdl_t *mrhdl, hermon_mr_options_t *op,
hermon_mpt_rsrc_type_t mpt_type)
{
hermon_rsrc_t *mpt, *mtt, *rsrc, *mtt_refcnt;
hermon_umap_db_entry_t *umapdb;
hermon_sw_refcnt_t *swrc_tmp;
hermon_hw_dmpt_t mpt_entry;
hermon_mrhdl_t mr;
ibt_mr_flags_t flags;
hermon_bind_info_t *bh;
ddi_dma_handle_t bind_dmahdl;
ddi_umem_cookie_t umem_cookie;
size_t umem_len;
caddr_t umem_addr;
uint64_t mtt_addr, max_sz;
uint_t sleep, mtt_pgsize_bits, bind_type, mr_is_umem;
int status, umem_flags, bind_override_addr;
if (op == NULL) {
bind_type = HERMON_BINDMEM_NORMAL;
bind_dmahdl = NULL;
bind_override_addr = 0;
} else {
bind_type = op->mro_bind_type;
bind_dmahdl = op->mro_bind_dmahdl;
bind_override_addr = op->mro_bind_override_addr;
}
flags = bind->bi_flags;
max_sz = ((uint64_t)1 << state->hs_cfg_profile->cp_log_max_mrw_sz);
if ((bind->bi_len == 0) || (bind->bi_len > max_sz)) {
status = IBT_MR_LEN_INVALID;
goto mrcommon_fail;
}
sleep = (flags & IBT_MR_NOSLEEP) ? HERMON_NOSLEEP: HERMON_SLEEP;
if ((sleep == HERMON_SLEEP) &&
(sleep != HERMON_SLEEPFLAG_FOR_CONTEXT())) {
status = IBT_INVALID_PARAM;
goto mrcommon_fail;
}
hermon_pd_refcnt_inc(pd);
if (mpt_type == HERMON_MPT_DMPT) {
status = hermon_rsrc_alloc(state, HERMON_DMPT, 1, sleep, &mpt);
if (status != DDI_SUCCESS) {
status = IBT_INSUFF_RESOURCE;
goto mrcommon_fail1;
}
} else {
mpt = NULL;
}
status = hermon_rsrc_alloc(state, HERMON_MRHDL, 1, sleep, &rsrc);
if (status != DDI_SUCCESS) {
status = IBT_INSUFF_RESOURCE;
goto mrcommon_fail2;
}
mr = (hermon_mrhdl_t)rsrc->hr_addr;
_NOTE(NOW_INVISIBLE_TO_OTHER_THREADS(*mr))
mr->mr_accflag = 0;
if (flags & IBT_MR_ENABLE_WINDOW_BIND)
mr->mr_accflag |= IBT_MR_WINDOW_BIND;
if (flags & IBT_MR_ENABLE_LOCAL_WRITE)
mr->mr_accflag |= IBT_MR_LOCAL_WRITE;
if (flags & IBT_MR_ENABLE_REMOTE_READ)
mr->mr_accflag |= IBT_MR_REMOTE_READ;
if (flags & IBT_MR_ENABLE_REMOTE_WRITE)
mr->mr_accflag |= IBT_MR_REMOTE_WRITE;
if (flags & IBT_MR_ENABLE_REMOTE_ATOMIC)
mr->mr_accflag |= IBT_MR_REMOTE_ATOMIC;
if (mpt)
mr->mr_rkey = mr->mr_lkey = hermon_mr_keycalc(mpt->hr_indx);
mr_is_umem = (((bind->bi_as != NULL) && (bind->bi_as != &kas)) ? 1 : 0);
if (mr_is_umem) {
umem_len = ptob(btopr(bind->bi_len +
((uintptr_t)bind->bi_addr & PAGEOFFSET)));
umem_addr = (caddr_t)((uintptr_t)bind->bi_addr & ~PAGEOFFSET);
umem_flags = (DDI_UMEMLOCK_WRITE | DDI_UMEMLOCK_READ |
DDI_UMEMLOCK_LONGTERM);
status = umem_lockmemory(umem_addr, umem_len, umem_flags,
&umem_cookie, &hermon_umem_cbops, NULL);
if (status != 0) {
status = IBT_INSUFF_RESOURCE;
goto mrcommon_fail3;
}
_NOTE(NOW_INVISIBLE_TO_OTHER_THREADS(*bind))
_NOTE(NOW_INVISIBLE_TO_OTHER_THREADS(*bind->bi_buf))
bind->bi_buf = ddi_umem_iosetup(umem_cookie, 0, umem_len,
B_WRITE, 0, 0, NULL, DDI_UMEM_SLEEP);
if (bind->bi_buf == NULL) {
status = IBT_INSUFF_RESOURCE;
goto mrcommon_fail3;
}
bind->bi_type = HERMON_BINDHDL_UBUF;
bind->bi_buf->b_flags |= B_READ;
_NOTE(NOW_VISIBLE_TO_OTHER_THREADS(*bind->bi_buf))
_NOTE(NOW_VISIBLE_TO_OTHER_THREADS(*bind))
umapdb = hermon_umap_db_alloc(state->hs_instance,
(uint64_t)(uintptr_t)umem_cookie, MLNX_UMAP_MRMEM_RSRC,
(uint64_t)(uintptr_t)rsrc);
if (umapdb == NULL) {
status = IBT_INSUFF_RESOURCE;
goto mrcommon_fail4;
}
}
bh = &mr->mr_bindinfo;
_NOTE(NOW_INVISIBLE_TO_OTHER_THREADS(*bh))
bcopy(bind, bh, sizeof (hermon_bind_info_t));
bh->bi_bypass = bind_type;
status = hermon_mr_mtt_bind(state, bh, bind_dmahdl, &mtt,
&mtt_pgsize_bits, mpt != NULL);
if (status != DDI_SUCCESS) {
bind->bi_type = bh->bi_type;
goto mrcommon_fail5;
}
mr->mr_logmttpgsz = mtt_pgsize_bits;
status = hermon_rsrc_alloc(state, HERMON_REFCNT, 1, sleep,
&mtt_refcnt);
if (status != DDI_SUCCESS) {
status = IBT_INSUFF_RESOURCE;
goto mrcommon_fail6;
}
mr->mr_mttrefcntp = mtt_refcnt;
swrc_tmp = (hermon_sw_refcnt_t *)mtt_refcnt->hr_addr;
_NOTE(NOW_INVISIBLE_TO_OTHER_THREADS(*swrc_tmp))
HERMON_MTT_REFCNT_INIT(swrc_tmp);
mtt_addr = (mtt->hr_indx << HERMON_MTT_SIZE_SHIFT);
if (mpt == NULL)
goto no_passown;
bzero(&mpt_entry, sizeof (hermon_hw_dmpt_t));
mpt_entry.status = HERMON_MPT_SW_OWNERSHIP;
mpt_entry.en_bind = (mr->mr_accflag & IBT_MR_WINDOW_BIND) ? 1 : 0;
mpt_entry.atomic = (mr->mr_accflag & IBT_MR_REMOTE_ATOMIC) ? 1 : 0;
mpt_entry.rw = (mr->mr_accflag & IBT_MR_REMOTE_WRITE) ? 1 : 0;
mpt_entry.rr = (mr->mr_accflag & IBT_MR_REMOTE_READ) ? 1 : 0;
mpt_entry.lw = (mr->mr_accflag & IBT_MR_LOCAL_WRITE) ? 1 : 0;
mpt_entry.lr = 1;
mpt_entry.phys_addr = 0;
mpt_entry.reg_win = HERMON_MPT_IS_REGION;
mpt_entry.entity_sz = mr->mr_logmttpgsz;
mpt_entry.mem_key = mr->mr_lkey;
mpt_entry.pd = pd->pd_pdnum;
mpt_entry.rem_acc_en = 0;
mpt_entry.fast_reg_en = 0;
mpt_entry.en_inval = 0;
mpt_entry.lkey = 0;
mpt_entry.win_cnt = 0;
if (bind_override_addr == 0) {
mpt_entry.start_addr = bh->bi_addr;
} else {
bh->bi_addr = bh->bi_addr & ((1 << mr->mr_logmttpgsz) - 1);
mpt_entry.start_addr = bh->bi_addr;
}
mpt_entry.reg_win_len = bh->bi_len;
mpt_entry.mtt_addr_h = mtt_addr >> 32;
mpt_entry.mtt_addr_l = mtt_addr >> 3;
mpt_entry.bnd_qp = 0;
status = hermon_cmn_ownership_cmd_post(state, SW2HW_MPT, &mpt_entry,
sizeof (hermon_hw_dmpt_t), mpt->hr_indx, sleep);
if (status != HERMON_CMD_SUCCESS) {
cmn_err(CE_CONT, "Hermon: SW2HW_MPT command failed: %08x\n",
status);
if (status == HERMON_CMD_INVALID_STATUS) {
hermon_fm_ereport(state, HCA_SYS_ERR, HCA_ERR_SRV_LOST);
}
status = ibc_get_ci_failure(0);
goto mrcommon_fail7;
}
if (hermon_rdma_debug & 0x4)
IBTF_DPRINTF_L2("mr", " reg: mr %p key %x",
mr, hermon_mr_key_swap(mr->mr_rkey));
no_passown:
mr->mr_mttaddr = mtt_addr;
mr->mr_log2_pgsz = (mr->mr_logmttpgsz - HERMON_PAGESHIFT);
mr->mr_mptrsrcp = mpt;
mr->mr_mttrsrcp = mtt;
mr->mr_pdhdl = pd;
mr->mr_rsrcp = rsrc;
mr->mr_is_umem = mr_is_umem;
mr->mr_is_fmr = 0;
mr->mr_umemcookie = (mr_is_umem != 0) ? umem_cookie : NULL;
mr->mr_umem_cbfunc = NULL;
mr->mr_umem_cbarg1 = NULL;
mr->mr_umem_cbarg2 = NULL;
mr->mr_lkey = hermon_mr_key_swap(mr->mr_lkey);
mr->mr_rkey = hermon_mr_key_swap(mr->mr_rkey);
mr->mr_mpt_type = mpt_type;
if (mr_is_umem) {
hermon_umap_db_add(umapdb);
}
*mrhdl = mr;
return (DDI_SUCCESS);
mrcommon_fail7:
hermon_rsrc_free(state, &mtt_refcnt);
mrcommon_fail6:
hermon_mr_mem_unbind(state, bh);
bind->bi_type = bh->bi_type;
mrcommon_fail5:
if (mr_is_umem) {
hermon_umap_db_free(umapdb);
}
mrcommon_fail4:
if (mr_is_umem) {
if (bind->bi_type == HERMON_BINDHDL_UBUF) {
freerbuf(bind->bi_buf);
_NOTE(NOW_INVISIBLE_TO_OTHER_THREADS(*bind))
bind->bi_type = HERMON_BINDHDL_NONE;
_NOTE(NOW_VISIBLE_TO_OTHER_THREADS(*bind))
}
ddi_umem_unlock(umem_cookie);
}
mrcommon_fail3:
hermon_rsrc_free(state, &rsrc);
mrcommon_fail2:
if (mpt != NULL)
hermon_rsrc_free(state, &mpt);
mrcommon_fail1:
hermon_pd_refcnt_dec(pd);
mrcommon_fail:
return (status);
}
int
hermon_dma_mr_register(hermon_state_t *state, hermon_pdhdl_t pd,
ibt_dmr_attr_t *mr_attr, hermon_mrhdl_t *mrhdl)
{
hermon_rsrc_t *mpt, *rsrc;
hermon_hw_dmpt_t mpt_entry;
hermon_mrhdl_t mr;
ibt_mr_flags_t flags;
uint_t sleep;
int status;
flags = mr_attr->dmr_flags;
sleep = (flags & IBT_MR_NOSLEEP) ? HERMON_NOSLEEP: HERMON_SLEEP;
if ((sleep == HERMON_SLEEP) &&
(sleep != HERMON_SLEEPFLAG_FOR_CONTEXT())) {
status = IBT_INVALID_PARAM;
goto mrcommon_fail;
}
hermon_pd_refcnt_inc(pd);
status = hermon_rsrc_alloc(state, HERMON_DMPT, 1, sleep, &mpt);
if (status != DDI_SUCCESS) {
status = IBT_INSUFF_RESOURCE;
goto mrcommon_fail1;
}
status = hermon_rsrc_alloc(state, HERMON_MRHDL, 1, sleep, &rsrc);
if (status != DDI_SUCCESS) {
status = IBT_INSUFF_RESOURCE;
goto mrcommon_fail2;
}
mr = (hermon_mrhdl_t)rsrc->hr_addr;
_NOTE(NOW_INVISIBLE_TO_OTHER_THREADS(*mr))
bzero(mr, sizeof (*mr));
mr->mr_accflag = 0;
if (flags & IBT_MR_ENABLE_WINDOW_BIND)
mr->mr_accflag |= IBT_MR_WINDOW_BIND;
if (flags & IBT_MR_ENABLE_LOCAL_WRITE)
mr->mr_accflag |= IBT_MR_LOCAL_WRITE;
if (flags & IBT_MR_ENABLE_REMOTE_READ)
mr->mr_accflag |= IBT_MR_REMOTE_READ;
if (flags & IBT_MR_ENABLE_REMOTE_WRITE)
mr->mr_accflag |= IBT_MR_REMOTE_WRITE;
if (flags & IBT_MR_ENABLE_REMOTE_ATOMIC)
mr->mr_accflag |= IBT_MR_REMOTE_ATOMIC;
if (mpt)
mr->mr_rkey = mr->mr_lkey = hermon_mr_keycalc(mpt->hr_indx);
bzero(&mpt_entry, sizeof (hermon_hw_dmpt_t));
mpt_entry.status = HERMON_MPT_SW_OWNERSHIP;
mpt_entry.en_bind = (mr->mr_accflag & IBT_MR_WINDOW_BIND) ? 1 : 0;
mpt_entry.atomic = (mr->mr_accflag & IBT_MR_REMOTE_ATOMIC) ? 1 : 0;
mpt_entry.rw = (mr->mr_accflag & IBT_MR_REMOTE_WRITE) ? 1 : 0;
mpt_entry.rr = (mr->mr_accflag & IBT_MR_REMOTE_READ) ? 1 : 0;
mpt_entry.lw = (mr->mr_accflag & IBT_MR_LOCAL_WRITE) ? 1 : 0;
mpt_entry.lr = 1;
mpt_entry.phys_addr = 1;
mpt_entry.reg_win = HERMON_MPT_IS_REGION;
mpt_entry.entity_sz = mr->mr_logmttpgsz;
mpt_entry.mem_key = mr->mr_lkey;
mpt_entry.pd = pd->pd_pdnum;
mpt_entry.rem_acc_en = 0;
mpt_entry.fast_reg_en = 0;
mpt_entry.en_inval = 0;
mpt_entry.lkey = 0;
mpt_entry.win_cnt = 0;
mpt_entry.start_addr = mr_attr->dmr_paddr;
mpt_entry.reg_win_len = mr_attr->dmr_len;
if (mr_attr->dmr_len == 0)
mpt_entry.len_b64 = 1;
mpt_entry.mtt_addr_h = 0;
mpt_entry.mtt_addr_l = 0;
mpt_entry.bnd_qp = 0;
status = hermon_cmn_ownership_cmd_post(state, SW2HW_MPT, &mpt_entry,
sizeof (hermon_hw_dmpt_t), mpt->hr_indx, sleep);
if (status != HERMON_CMD_SUCCESS) {
cmn_err(CE_CONT, "Hermon: SW2HW_MPT command failed: %08x\n",
status);
if (status == HERMON_CMD_INVALID_STATUS) {
hermon_fm_ereport(state, HCA_SYS_ERR, HCA_ERR_SRV_LOST);
}
status = ibc_get_ci_failure(0);
goto mrcommon_fail7;
}
mr->mr_mttaddr = 0;
mr->mr_log2_pgsz = 0;
mr->mr_mptrsrcp = mpt;
mr->mr_mttrsrcp = NULL;
mr->mr_pdhdl = pd;
mr->mr_rsrcp = rsrc;
mr->mr_is_umem = 0;
mr->mr_is_fmr = 0;
mr->mr_umemcookie = NULL;
mr->mr_umem_cbfunc = NULL;
mr->mr_umem_cbarg1 = NULL;
mr->mr_umem_cbarg2 = NULL;
mr->mr_lkey = hermon_mr_key_swap(mr->mr_lkey);
mr->mr_rkey = hermon_mr_key_swap(mr->mr_rkey);
mr->mr_mpt_type = HERMON_MPT_DMPT;
*mrhdl = mr;
return (DDI_SUCCESS);
mrcommon_fail7:
hermon_rsrc_free(state, &rsrc);
mrcommon_fail2:
hermon_rsrc_free(state, &mpt);
mrcommon_fail1:
hermon_pd_refcnt_dec(pd);
mrcommon_fail:
return (status);
}
int
hermon_mr_alloc_lkey(hermon_state_t *state, hermon_pdhdl_t pd,
ibt_lkey_flags_t flags, uint_t nummtt, hermon_mrhdl_t *mrhdl)
{
hermon_rsrc_t *mpt, *mtt, *rsrc, *mtt_refcnt;
hermon_sw_refcnt_t *swrc_tmp;
hermon_hw_dmpt_t mpt_entry;
hermon_mrhdl_t mr;
uint64_t mtt_addr;
uint_t sleep;
int status;
hermon_pd_refcnt_inc(pd);
sleep = (flags & IBT_KEY_NOSLEEP) ? HERMON_NOSLEEP: HERMON_SLEEP;
status = hermon_rsrc_alloc(state, HERMON_DMPT, 1, sleep, &mpt);
if (status != DDI_SUCCESS) {
status = IBT_INSUFF_RESOURCE;
goto alloclkey_fail1;
}
status = hermon_rsrc_alloc(state, HERMON_MRHDL, 1, sleep, &rsrc);
if (status != DDI_SUCCESS) {
status = IBT_INSUFF_RESOURCE;
goto alloclkey_fail2;
}
mr = (hermon_mrhdl_t)rsrc->hr_addr;
_NOTE(NOW_INVISIBLE_TO_OTHER_THREADS(*mr))
bzero(mr, sizeof (*mr));
mr->mr_bindinfo.bi_type = HERMON_BINDHDL_LKEY;
mr->mr_lkey = hermon_mr_keycalc(mpt->hr_indx);
status = hermon_rsrc_alloc(state, HERMON_MTT, nummtt, sleep, &mtt);
if (status != DDI_SUCCESS) {
status = IBT_INSUFF_RESOURCE;
goto alloclkey_fail3;
}
mr->mr_logmttpgsz = PAGESHIFT;
status = hermon_rsrc_alloc(state, HERMON_REFCNT, 1, sleep,
&mtt_refcnt);
if (status != DDI_SUCCESS) {
status = IBT_INSUFF_RESOURCE;
goto alloclkey_fail4;
}
mr->mr_mttrefcntp = mtt_refcnt;
swrc_tmp = (hermon_sw_refcnt_t *)mtt_refcnt->hr_addr;
_NOTE(NOW_INVISIBLE_TO_OTHER_THREADS(*swrc_tmp))
HERMON_MTT_REFCNT_INIT(swrc_tmp);
mtt_addr = (mtt->hr_indx << HERMON_MTT_SIZE_SHIFT);
bzero(&mpt_entry, sizeof (hermon_hw_dmpt_t));
mpt_entry.status = HERMON_MPT_FREE;
mpt_entry.lw = 1;
mpt_entry.lr = 1;
mpt_entry.reg_win = HERMON_MPT_IS_REGION;
mpt_entry.entity_sz = mr->mr_logmttpgsz;
mpt_entry.mem_key = mr->mr_lkey;
mpt_entry.pd = pd->pd_pdnum;
mpt_entry.fast_reg_en = 1;
mpt_entry.rem_acc_en = 1;
mpt_entry.en_inval = 1;
if (flags & IBT_KEY_REMOTE) {
mpt_entry.ren_inval = 1;
}
mpt_entry.mtt_size = nummtt;
mpt_entry.mtt_addr_h = mtt_addr >> 32;
mpt_entry.mtt_addr_l = mtt_addr >> 3;
status = hermon_cmn_ownership_cmd_post(state, SW2HW_MPT, &mpt_entry,
sizeof (hermon_hw_dmpt_t), mpt->hr_indx, sleep);
if (status != HERMON_CMD_SUCCESS) {
cmn_err(CE_CONT, "Hermon: alloc_lkey: SW2HW_MPT command "
"failed: %08x\n", status);
if (status == HERMON_CMD_INVALID_STATUS) {
hermon_fm_ereport(state, HCA_SYS_ERR, HCA_ERR_SRV_LOST);
}
status = ibc_get_ci_failure(0);
goto alloclkey_fail5;
}
mr->mr_accflag = IBT_MR_LOCAL_WRITE;
mr->mr_mttaddr = mtt_addr;
mr->mr_log2_pgsz = (mr->mr_logmttpgsz - HERMON_PAGESHIFT);
mr->mr_mptrsrcp = mpt;
mr->mr_mttrsrcp = mtt;
mr->mr_pdhdl = pd;
mr->mr_rsrcp = rsrc;
mr->mr_lkey = hermon_mr_key_swap(mr->mr_lkey);
mr->mr_rkey = mr->mr_lkey;
mr->mr_mpt_type = HERMON_MPT_DMPT;
*mrhdl = mr;
return (DDI_SUCCESS);
alloclkey_fail5:
hermon_rsrc_free(state, &mtt_refcnt);
alloclkey_fail4:
hermon_rsrc_free(state, &mtt);
alloclkey_fail3:
hermon_rsrc_free(state, &rsrc);
alloclkey_fail2:
hermon_rsrc_free(state, &mpt);
alloclkey_fail1:
hermon_pd_refcnt_dec(pd);
return (status);
}
int
hermon_mr_fexch_mpt_init(hermon_state_t *state, hermon_pdhdl_t pd,
uint32_t mpt_indx, uint_t nummtt, uint64_t mtt_addr, uint_t sleep)
{
hermon_hw_dmpt_t mpt_entry;
int status;
bzero(&mpt_entry, sizeof (hermon_hw_dmpt_t));
mpt_entry.status = HERMON_MPT_FREE;
mpt_entry.lw = 1;
mpt_entry.lr = 1;
mpt_entry.rw = 1;
mpt_entry.rr = 1;
mpt_entry.reg_win = HERMON_MPT_IS_REGION;
mpt_entry.entity_sz = PAGESHIFT;
mpt_entry.mem_key = mpt_indx;
mpt_entry.pd = pd->pd_pdnum;
mpt_entry.fast_reg_en = 1;
mpt_entry.rem_acc_en = 1;
mpt_entry.en_inval = 1;
mpt_entry.ren_inval = 1;
mpt_entry.mtt_size = nummtt;
mpt_entry.mtt_addr_h = mtt_addr >> 32;
mpt_entry.mtt_addr_l = mtt_addr >> 3;
status = hermon_cmn_ownership_cmd_post(state, SW2HW_MPT, &mpt_entry,
sizeof (hermon_hw_dmpt_t), mpt_indx, sleep);
if (status != HERMON_CMD_SUCCESS) {
cmn_err(CE_CONT, "Hermon: fexch_mpt_init: SW2HW_MPT command "
"failed: %08x\n", status);
if (status == HERMON_CMD_INVALID_STATUS) {
hermon_fm_ereport(state, HCA_SYS_ERR, HCA_ERR_SRV_LOST);
}
status = ibc_get_ci_failure(0);
return (status);
}
hermon_pd_refcnt_inc(pd);
return (DDI_SUCCESS);
}
int
hermon_mr_fexch_mpt_fini(hermon_state_t *state, hermon_pdhdl_t pd,
uint32_t mpt_indx, uint_t sleep)
{
int status;
status = hermon_cmn_ownership_cmd_post(state, HW2SW_MPT,
NULL, 0, mpt_indx, sleep);
if (status != DDI_SUCCESS) {
cmn_err(CE_CONT, "Hermon: fexch_mpt_fini: HW2SW_MPT command "
"failed: %08x\n", status);
if (status == HERMON_CMD_INVALID_STATUS) {
hermon_fm_ereport(state, HCA_SYS_ERR, HCA_ERR_SRV_LOST);
}
status = ibc_get_ci_failure(0);
return (status);
}
hermon_pd_refcnt_dec(pd);
return (DDI_SUCCESS);
}
int
hermon_mr_mtt_bind(hermon_state_t *state, hermon_bind_info_t *bind,
ddi_dma_handle_t bind_dmahdl, hermon_rsrc_t **mtt, uint_t *mtt_pgsize_bits,
uint_t is_buffer)
{
uint64_t nummtt;
uint_t sleep;
int status;
sleep = (bind->bi_flags & IBT_MR_NOSLEEP) ?
HERMON_NOSLEEP : HERMON_SLEEP;
if ((sleep == HERMON_SLEEP) &&
(sleep != HERMON_SLEEPFLAG_FOR_CONTEXT())) {
status = IBT_INVALID_PARAM;
goto mrmttbind_fail;
}
status = hermon_mr_mem_bind(state, bind, bind_dmahdl, sleep, is_buffer);
if (status != DDI_SUCCESS) {
status = IBT_INSUFF_RESOURCE;
goto mrmttbind_fail;
}
nummtt = hermon_mr_nummtt_needed(state, bind, mtt_pgsize_bits);
status = hermon_rsrc_alloc(state, HERMON_MTT, nummtt, sleep, mtt);
if (status != DDI_SUCCESS) {
status = IBT_INSUFF_RESOURCE;
goto mrmttbind_fail2;
}
status = hermon_mr_fast_mtt_write(state, *mtt, bind, *mtt_pgsize_bits);
if (status != DDI_SUCCESS) {
hermon_fm_ereport(state, HCA_SYS_ERR, HCA_ERR_SRV_LOST);
status = ibc_get_ci_failure(0);
goto mrmttbind_fail3;
}
return (DDI_SUCCESS);
mrmttbind_fail3:
hermon_rsrc_free(state, mtt);
mrmttbind_fail2:
hermon_mr_mem_unbind(state, bind);
mrmttbind_fail:
return (status);
}
int
hermon_mr_mtt_unbind(hermon_state_t *state, hermon_bind_info_t *bind,
hermon_rsrc_t *mtt)
{
hermon_mr_mem_unbind(state, bind);
hermon_rsrc_free(state, &mtt);
return (DDI_SUCCESS);
}
static int
hermon_mr_common_rereg(hermon_state_t *state, hermon_mrhdl_t mr,
hermon_pdhdl_t pd, hermon_bind_info_t *bind, hermon_mrhdl_t *mrhdl_new,
hermon_mr_options_t *op)
{
hermon_rsrc_t *mpt;
ibt_mr_attr_flags_t acc_flags_to_use;
ibt_mr_flags_t flags;
hermon_pdhdl_t pd_to_use;
hermon_hw_dmpt_t mpt_entry;
uint64_t mtt_addr_to_use, vaddr_to_use, len_to_use;
uint_t sleep, dereg_level;
int status;
_NOTE(NOW_INVISIBLE_TO_OTHER_THREADS(*bind))
if (mr->mr_is_umem) {
status = IBT_MR_HDL_INVALID;
goto mrrereg_fail;
}
mutex_enter(&mr->mr_lock);
mpt = mr->mr_mptrsrcp;
flags = bind->bi_flags;
sleep = (flags & IBT_MR_NOSLEEP) ? HERMON_NOSLEEP: HERMON_SLEEP;
if ((sleep == HERMON_SLEEP) &&
(sleep != HERMON_SLEEPFLAG_FOR_CONTEXT())) {
mutex_exit(&mr->mr_lock);
status = IBT_INVALID_PARAM;
goto mrrereg_fail;
}
status = hermon_cmn_ownership_cmd_post(state, HW2SW_MPT, &mpt_entry,
sizeof (hermon_hw_dmpt_t), mpt->hr_indx, HERMON_CMD_NOSLEEP_SPIN);
if (status != HERMON_CMD_SUCCESS) {
mutex_exit(&mr->mr_lock);
if (status == HERMON_CMD_REG_BOUND) {
return (IBT_MR_IN_USE);
} else {
cmn_err(CE_CONT, "Hermon: HW2SW_MPT command failed: "
"%08x\n", status);
if (status == HERMON_CMD_INVALID_STATUS) {
hermon_fm_ereport(state, HCA_SYS_ERR,
HCA_ERR_SRV_LOST);
}
if (hermon_mr_deregister(state, &mr,
HERMON_MR_DEREG_ALL, sleep) != DDI_SUCCESS) {
HERMON_WARNING(state, "failed to deregister "
"memory region");
}
return (ibc_get_ci_failure(0));
}
}
if (flags & IBT_MR_CHANGE_PD) {
if (pd == NULL) {
mutex_exit(&mr->mr_lock);
if (hermon_mr_deregister(state, &mr,
HERMON_MR_DEREG_NO_HW2SW_MPT, sleep) !=
DDI_SUCCESS) {
HERMON_WARNING(state, "failed to deregister "
"memory region");
}
status = IBT_PD_HDL_INVALID;
goto mrrereg_fail;
}
pd_to_use = pd;
} else {
pd_to_use = mr->mr_pdhdl;
}
if (flags & IBT_MR_CHANGE_ACCESS) {
if (((flags & IBT_MR_ENABLE_REMOTE_WRITE) ||
(flags & IBT_MR_ENABLE_REMOTE_ATOMIC)) &&
!(flags & IBT_MR_ENABLE_LOCAL_WRITE)) {
mutex_exit(&mr->mr_lock);
if (hermon_mr_deregister(state, &mr,
HERMON_MR_DEREG_NO_HW2SW_MPT, sleep) !=
DDI_SUCCESS) {
HERMON_WARNING(state, "failed to deregister "
"memory region");
}
status = IBT_MR_ACCESS_REQ_INVALID;
goto mrrereg_fail;
}
acc_flags_to_use = 0;
if (flags & IBT_MR_ENABLE_WINDOW_BIND)
acc_flags_to_use |= IBT_MR_WINDOW_BIND;
if (flags & IBT_MR_ENABLE_LOCAL_WRITE)
acc_flags_to_use |= IBT_MR_LOCAL_WRITE;
if (flags & IBT_MR_ENABLE_REMOTE_READ)
acc_flags_to_use |= IBT_MR_REMOTE_READ;
if (flags & IBT_MR_ENABLE_REMOTE_WRITE)
acc_flags_to_use |= IBT_MR_REMOTE_WRITE;
if (flags & IBT_MR_ENABLE_REMOTE_ATOMIC)
acc_flags_to_use |= IBT_MR_REMOTE_ATOMIC;
} else {
acc_flags_to_use = mr->mr_accflag;
}
if (flags & IBT_MR_CHANGE_TRANSLATION) {
status = hermon_mr_rereg_xlat_helper(state, mr, bind, op,
&mtt_addr_to_use, sleep, &dereg_level);
if (status != DDI_SUCCESS) {
mutex_exit(&mr->mr_lock);
if (hermon_mr_deregister(state, &mr, dereg_level,
sleep) != DDI_SUCCESS) {
HERMON_WARNING(state, "failed to deregister "
"memory region");
}
goto mrrereg_fail;
}
vaddr_to_use = mr->mr_bindinfo.bi_addr;
len_to_use = mr->mr_bindinfo.bi_len;
} else {
mtt_addr_to_use = mr->mr_mttaddr;
vaddr_to_use = mr->mr_bindinfo.bi_addr;
len_to_use = mr->mr_bindinfo.bi_len;
}
mr->mr_lkey = hermon_mr_keycalc(mpt->hr_indx);
if ((acc_flags_to_use & IBT_MR_REMOTE_READ) ||
(acc_flags_to_use & IBT_MR_REMOTE_WRITE) ||
(acc_flags_to_use & IBT_MR_REMOTE_ATOMIC)) {
mr->mr_rkey = mr->mr_lkey;
} else
mr->mr_rkey = 0;
bzero(&mpt_entry, sizeof (hermon_hw_dmpt_t));
mpt_entry.status = HERMON_MPT_SW_OWNERSHIP;
mpt_entry.en_bind = (acc_flags_to_use & IBT_MR_WINDOW_BIND) ? 1 : 0;
mpt_entry.atomic = (acc_flags_to_use & IBT_MR_REMOTE_ATOMIC) ? 1 : 0;
mpt_entry.rw = (acc_flags_to_use & IBT_MR_REMOTE_WRITE) ? 1 : 0;
mpt_entry.rr = (acc_flags_to_use & IBT_MR_REMOTE_READ) ? 1 : 0;
mpt_entry.lw = (acc_flags_to_use & IBT_MR_LOCAL_WRITE) ? 1 : 0;
mpt_entry.lr = 1;
mpt_entry.phys_addr = 0;
mpt_entry.reg_win = HERMON_MPT_IS_REGION;
mpt_entry.entity_sz = mr->mr_logmttpgsz;
mpt_entry.mem_key = mr->mr_lkey;
mpt_entry.pd = pd_to_use->pd_pdnum;
mpt_entry.start_addr = vaddr_to_use;
mpt_entry.reg_win_len = len_to_use;
mpt_entry.mtt_addr_h = mtt_addr_to_use >> 32;
mpt_entry.mtt_addr_l = mtt_addr_to_use >> 3;
status = hermon_cmn_ownership_cmd_post(state, SW2HW_MPT, &mpt_entry,
sizeof (hermon_hw_dmpt_t), mpt->hr_indx, HERMON_CMD_NOSLEEP_SPIN);
if (status != HERMON_CMD_SUCCESS) {
mutex_exit(&mr->mr_lock);
cmn_err(CE_CONT, "Hermon: SW2HW_MPT command failed: %08x\n",
status);
if (status == HERMON_CMD_INVALID_STATUS) {
hermon_fm_ereport(state, HCA_SYS_ERR, HCA_ERR_SRV_LOST);
}
if (hermon_mr_deregister(state, &mr,
HERMON_MR_DEREG_NO_HW2SW_MPT, sleep) != DDI_SUCCESS) {
HERMON_WARNING(state, "failed to deregister memory "
"region");
}
return (ibc_get_ci_failure(0));
}
if (flags & IBT_MR_CHANGE_PD) {
hermon_pd_refcnt_dec(mr->mr_pdhdl);
hermon_pd_refcnt_inc(pd);
}
mr->mr_pdhdl = pd_to_use;
mr->mr_accflag = acc_flags_to_use;
mr->mr_is_umem = 0;
mr->mr_is_fmr = 0;
mr->mr_umemcookie = NULL;
mr->mr_lkey = hermon_mr_key_swap(mr->mr_lkey);
mr->mr_rkey = hermon_mr_key_swap(mr->mr_rkey);
*mrhdl_new = mr;
mutex_exit(&mr->mr_lock);
return (DDI_SUCCESS);
mrrereg_fail:
return (status);
}
static int
hermon_mr_rereg_xlat_helper(hermon_state_t *state, hermon_mrhdl_t mr,
hermon_bind_info_t *bind, hermon_mr_options_t *op, uint64_t *mtt_addr,
uint_t sleep, uint_t *dereg_level)
{
hermon_rsrc_t *mtt, *mtt_refcnt;
hermon_sw_refcnt_t *swrc_old, *swrc_new;
ddi_dma_handle_t dmahdl;
uint64_t nummtt_needed, nummtt_in_currrsrc, max_sz;
uint_t mtt_pgsize_bits, bind_type, reuse_dmahdl;
int status;
ASSERT(MUTEX_HELD(&mr->mr_lock));
if (op == NULL) {
bind_type = HERMON_BINDMEM_NORMAL;
} else {
bind_type = op->mro_bind_type;
}
max_sz = ((uint64_t)1 << state->hs_cfg_profile->cp_log_max_mrw_sz);
if ((bind->bi_len == 0) || (bind->bi_len > max_sz)) {
*dereg_level = HERMON_MR_DEREG_NO_HW2SW_MPT;
status = IBT_MR_LEN_INVALID;
goto mrrereghelp_fail;
}
nummtt_needed = hermon_mr_nummtt_needed(state, bind, &mtt_pgsize_bits);
nummtt_in_currrsrc = mr->mr_mttrsrcp->hr_len >> HERMON_MTT_SIZE_SHIFT;
swrc_old = (hermon_sw_refcnt_t *)mr->mr_mttrefcntp->hr_addr;
if (HERMON_MTT_IS_NOT_SHARED(swrc_old) &&
(nummtt_needed <= nummtt_in_currrsrc)) {
if (HERMON_MR_REUSE_DMAHDL(mr, bind->bi_flags)) {
mr->mr_bindinfo.bi_free_dmahdl = 0;
hermon_mr_mem_unbind(state, &mr->mr_bindinfo);
dmahdl = mr->mr_bindinfo.bi_dmahdl;
reuse_dmahdl = 1;
} else {
hermon_mr_mem_unbind(state, &mr->mr_bindinfo);
dmahdl = NULL;
reuse_dmahdl = 0;
}
bind->bi_bypass = bind_type;
status = hermon_mr_mem_bind(state, bind, dmahdl, sleep, 1);
if (status != DDI_SUCCESS) {
if (reuse_dmahdl) {
ddi_dma_free_handle(&dmahdl);
}
*dereg_level = HERMON_MR_DEREG_NO_HW2SW_MPT_OR_UNBIND;
status = IBT_INSUFF_RESOURCE;
goto mrrereghelp_fail;
}
if (reuse_dmahdl) {
bind->bi_free_dmahdl = 1;
}
mtt = mr->mr_mttrsrcp;
status = hermon_mr_fast_mtt_write(state, mtt, bind,
mtt_pgsize_bits);
if (status != DDI_SUCCESS) {
hermon_mr_mem_unbind(state, bind);
*dereg_level = HERMON_MR_DEREG_NO_HW2SW_MPT_OR_UNBIND;
hermon_fm_ereport(state, HCA_SYS_ERR, HCA_ERR_SRV_LOST);
status = ibc_get_ci_failure(0);
goto mrrereghelp_fail;
}
mr->mr_bindinfo = *bind;
mr->mr_logmttpgsz = mtt_pgsize_bits;
} else {
if (!HERMON_MTT_IS_SHARED(swrc_old)) {
if (HERMON_MR_REUSE_DMAHDL(mr, bind->bi_flags)) {
mr->mr_bindinfo.bi_free_dmahdl = 0;
hermon_mr_mem_unbind(state, &mr->mr_bindinfo);
dmahdl = mr->mr_bindinfo.bi_dmahdl;
reuse_dmahdl = 1;
} else {
hermon_mr_mem_unbind(state, &mr->mr_bindinfo);
dmahdl = NULL;
reuse_dmahdl = 0;
}
} else {
dmahdl = NULL;
reuse_dmahdl = 0;
}
bind->bi_bypass = bind_type;
status = hermon_mr_mem_bind(state, bind, dmahdl, sleep, 1);
if (status != DDI_SUCCESS) {
if (reuse_dmahdl) {
ddi_dma_free_handle(&dmahdl);
}
*dereg_level = HERMON_MR_DEREG_NO_HW2SW_MPT_OR_UNBIND;
status = IBT_INSUFF_RESOURCE;
goto mrrereghelp_fail;
}
if (reuse_dmahdl) {
bind->bi_free_dmahdl = 1;
}
status = hermon_rsrc_alloc(state, HERMON_MTT, nummtt_needed,
sleep, &mtt);
if (status != DDI_SUCCESS) {
hermon_mr_mem_unbind(state, bind);
*dereg_level = HERMON_MR_DEREG_NO_HW2SW_MPT_OR_UNBIND;
status = IBT_INSUFF_RESOURCE;
goto mrrereghelp_fail;
}
if (HERMON_MTT_IS_SHARED(swrc_old)) {
status = hermon_rsrc_alloc(state, HERMON_REFCNT, 1,
sleep, &mtt_refcnt);
if (status != DDI_SUCCESS) {
hermon_mr_mem_unbind(state, bind);
hermon_rsrc_free(state, &mtt);
*dereg_level =
HERMON_MR_DEREG_NO_HW2SW_MPT_OR_UNBIND;
status = IBT_INSUFF_RESOURCE;
goto mrrereghelp_fail;
}
swrc_new = (hermon_sw_refcnt_t *)mtt_refcnt->hr_addr;
_NOTE(NOW_INVISIBLE_TO_OTHER_THREADS(*swrc_new))
HERMON_MTT_REFCNT_INIT(swrc_new);
} else {
mtt_refcnt = mr->mr_mttrefcntp;
}
status = hermon_mr_fast_mtt_write(state, mtt, bind,
mtt_pgsize_bits);
if (status != DDI_SUCCESS) {
if (HERMON_MTT_IS_SHARED(swrc_old)) {
hermon_rsrc_free(state, &mtt_refcnt);
}
hermon_mr_mem_unbind(state, bind);
hermon_rsrc_free(state, &mtt);
*dereg_level = HERMON_MR_DEREG_NO_HW2SW_MPT_OR_UNBIND;
status = IBT_INSUFF_RESOURCE;
goto mrrereghelp_fail;
}
if (HERMON_MTT_IS_SHARED(swrc_old)) {
(void) hermon_mtt_refcnt_dec(mr->mr_mttrefcntp);
} else {
hermon_rsrc_free(state, &mr->mr_mttrsrcp);
}
mr->mr_bindinfo = *bind;
mr->mr_logmttpgsz = mtt_pgsize_bits;
mr->mr_mttrsrcp = mtt;
mr->mr_mttrefcntp = mtt_refcnt;
}
*mtt_addr = mtt->hr_indx << HERMON_MTT_SIZE_SHIFT;
return (DDI_SUCCESS);
mrrereghelp_fail:
return (status);
}
static uint64_t
hermon_mr_nummtt_needed(hermon_state_t *state, hermon_bind_info_t *bind,
uint_t *mtt_pgsize_bits)
{
uint64_t pg_offset_mask;
uint64_t pg_offset, tmp_length;
*mtt_pgsize_bits = PAGESHIFT;
pg_offset_mask = ((uint64_t)1 << *mtt_pgsize_bits) - 1;
pg_offset = bind->bi_addr & pg_offset_mask;
tmp_length = pg_offset + (bind->bi_len - 1);
return ((tmp_length >> *mtt_pgsize_bits) + 1);
}
static int
hermon_mr_mem_bind(hermon_state_t *state, hermon_bind_info_t *bind,
ddi_dma_handle_t dmahdl, uint_t sleep, uint_t is_buffer)
{
ddi_dma_attr_t dma_attr;
int (*callback)(caddr_t);
int status;
ASSERT(bind->bi_type == HERMON_BINDHDL_VADDR ||
bind->bi_type == HERMON_BINDHDL_BUF ||
bind->bi_type == HERMON_BINDHDL_UBUF);
_NOTE(NOW_INVISIBLE_TO_OTHER_THREADS(*bind))
callback = (sleep == HERMON_SLEEP) ? DDI_DMA_SLEEP : DDI_DMA_DONTWAIT;
if (dmahdl == NULL) {
hermon_dma_attr_init(state, &dma_attr);
#ifdef __sparc
if (bind->bi_bypass == HERMON_BINDMEM_BYPASS) {
dma_attr.dma_attr_flags = DDI_DMA_FORCE_PHYSICAL;
}
#endif
if (is_buffer) {
if (! (bind->bi_flags & IBT_MR_DISABLE_RO)) {
if ((bind->bi_type != HERMON_BINDHDL_UBUF) &&
(hermon_kernel_data_ro ==
HERMON_RO_ENABLED)) {
dma_attr.dma_attr_flags |=
DDI_DMA_RELAXED_ORDERING;
}
if (((bind->bi_type == HERMON_BINDHDL_UBUF) &&
(hermon_user_data_ro ==
HERMON_RO_ENABLED))) {
dma_attr.dma_attr_flags |=
DDI_DMA_RELAXED_ORDERING;
}
}
}
status = ddi_dma_alloc_handle(state->hs_dip, &dma_attr,
callback, NULL, &bind->bi_dmahdl);
if (status != DDI_SUCCESS) {
return (status);
}
bind->bi_free_dmahdl = 1;
} else {
bind->bi_dmahdl = dmahdl;
bind->bi_free_dmahdl = 0;
}
if (bind->bi_type == HERMON_BINDHDL_VADDR) {
status = ddi_dma_addr_bind_handle(bind->bi_dmahdl, NULL,
(caddr_t)(uintptr_t)bind->bi_addr, bind->bi_len,
(DDI_DMA_RDWR | DDI_DMA_CONSISTENT), callback, NULL,
&bind->bi_dmacookie, &bind->bi_cookiecnt);
} else {
status = ddi_dma_buf_bind_handle(bind->bi_dmahdl,
bind->bi_buf, (DDI_DMA_RDWR | DDI_DMA_CONSISTENT), callback,
NULL, &bind->bi_dmacookie, &bind->bi_cookiecnt);
}
if (status != DDI_DMA_MAPPED) {
if (bind->bi_free_dmahdl != 0) {
ddi_dma_free_handle(&bind->bi_dmahdl);
}
return (status);
}
return (DDI_SUCCESS);
}
static void
hermon_mr_mem_unbind(hermon_state_t *state, hermon_bind_info_t *bind)
{
int status;
_NOTE(NOW_INVISIBLE_TO_OTHER_THREADS(*bind))
if (bind->bi_type == HERMON_BINDHDL_LKEY)
return;
_NOTE(NOW_INVISIBLE_TO_OTHER_THREADS(*bind))
if (bind->bi_type == HERMON_BINDHDL_UBUF) {
freerbuf(bind->bi_buf);
bind->bi_type = HERMON_BINDHDL_NONE;
}
_NOTE(NOW_VISIBLE_TO_OTHER_THREADS(*bind))
status = ddi_dma_unbind_handle(bind->bi_dmahdl);
if (status != DDI_SUCCESS) {
HERMON_WARNING(state, "failed to unbind DMA mapping");
return;
}
if (bind->bi_free_dmahdl != 0) {
ddi_dma_free_handle(&bind->bi_dmahdl);
}
}
static int
hermon_mr_fast_mtt_write(hermon_state_t *state, hermon_rsrc_t *mtt,
hermon_bind_info_t *bind, uint32_t mtt_pgsize_bits)
{
hermon_icm_table_t *icm_table;
hermon_dma_info_t *dma_info;
uint32_t index1, index2, rindx;
ddi_dma_cookie_t dmacookie;
uint_t cookie_cnt;
uint64_t *mtt_table;
uint64_t mtt_entry;
uint64_t addr, endaddr;
uint64_t pagesize;
offset_t i, start;
uint_t per_span;
int sync_needed;
pagesize = ((uint64_t)1 << mtt_pgsize_bits);
dmacookie = bind->bi_dmacookie;
cookie_cnt = bind->bi_cookiecnt;
icm_table = &state->hs_icm[HERMON_MTT];
rindx = mtt->hr_indx;
hermon_index(index1, index2, rindx, icm_table, i);
start = i;
per_span = icm_table->span;
dma_info = icm_table->icm_dma[index1] + index2;
mtt_table = (uint64_t *)(uintptr_t)dma_info->vaddr;
sync_needed = 0;
while (cookie_cnt-- > 0) {
addr = dmacookie.dmac_laddress;
endaddr = addr + (dmacookie.dmac_size - 1);
addr = addr & ~((uint64_t)pagesize - 1);
while (addr <= endaddr) {
mtt_entry = addr | HERMON_MTT_ENTRY_PRESENT;
mtt_table[i] = htonll(mtt_entry);
i++;
rindx++;
if (i == per_span) {
(void) ddi_dma_sync(dma_info->dma_hdl,
start * sizeof (hermon_hw_mtt_t),
(i - start) * sizeof (hermon_hw_mtt_t),
DDI_DMA_SYNC_FORDEV);
if ((addr + pagesize > endaddr) &&
(cookie_cnt == 0))
return (DDI_SUCCESS);
hermon_index(index1, index2, rindx, icm_table,
i);
start = i * sizeof (hermon_hw_mtt_t);
dma_info = icm_table->icm_dma[index1] + index2;
mtt_table =
(uint64_t *)(uintptr_t)dma_info->vaddr;
sync_needed = 0;
} else {
sync_needed = 1;
}
addr += pagesize;
if (addr == 0) {
static int do_once = 1;
_NOTE(SCHEME_PROTECTS_DATA("safe sharing",
do_once))
if (do_once) {
do_once = 0;
cmn_err(CE_NOTE, "probable error in "
"dma_cookie address from caller\n");
}
break;
}
}
if (cookie_cnt != 0) {
ddi_dma_nextcookie(bind->bi_dmahdl, &dmacookie);
}
}
if (sync_needed)
(void) ddi_dma_sync(dma_info->dma_hdl,
start * sizeof (hermon_hw_mtt_t),
(i - start) * sizeof (hermon_hw_mtt_t),
DDI_DMA_SYNC_FORDEV);
return (DDI_SUCCESS);
}
static int
hermon_mr_fast_mtt_write_fmr(hermon_state_t *state, hermon_rsrc_t *mtt,
ibt_pmr_attr_t *mem_pattr, uint32_t mtt_pgsize_bits)
{
hermon_icm_table_t *icm_table;
hermon_dma_info_t *dma_info;
uint32_t index1, index2, rindx;
uint64_t *mtt_table;
offset_t i, j;
uint_t per_span;
icm_table = &state->hs_icm[HERMON_MTT];
rindx = mtt->hr_indx;
hermon_index(index1, index2, rindx, icm_table, i);
per_span = icm_table->span;
dma_info = icm_table->icm_dma[index1] + index2;
mtt_table = (uint64_t *)(uintptr_t)dma_info->vaddr;
for (j = 0; j < mem_pattr->pmr_num_buf; j++) {
mtt_table[i] = mem_pattr->pmr_addr_list[j].p_laddr;
i++;
rindx++;
if (i == per_span) {
hermon_index(index1, index2, rindx, icm_table, i);
dma_info = icm_table->icm_dma[index1] + index2;
mtt_table = (uint64_t *)(uintptr_t)dma_info->vaddr;
}
}
return (DDI_SUCCESS);
}
static uint_t
hermon_mtt_refcnt_inc(hermon_rsrc_t *rsrc)
{
hermon_sw_refcnt_t *rc;
rc = (hermon_sw_refcnt_t *)rsrc->hr_addr;
return (atomic_inc_uint_nv(&rc->swrc_refcnt));
}
static uint_t
hermon_mtt_refcnt_dec(hermon_rsrc_t *rsrc)
{
hermon_sw_refcnt_t *rc;
rc = (hermon_sw_refcnt_t *)rsrc->hr_addr;
return (atomic_dec_uint_nv(&rc->swrc_refcnt));
}