#include <sys/types.h>
#include <sys/conf.h>
#include <sys/ddi.h>
#include <sys/sunddi.h>
#include <sys/modctl.h>
#include <sys/bitmap.h>
#include <sys/sysmacros.h>
#include <sys/ib/adapters/hermon/hermon.h>
extern int hermon_rdma_debug;
int hermon_fmr_verbose = 0;
static int hermon_mcg_qplist_add(hermon_state_t *state, hermon_mcghdl_t mcg,
hermon_hw_mcg_qp_list_t *mcg_qplist, hermon_qphdl_t qp, uint_t *qp_found);
static int hermon_mcg_qplist_remove(hermon_mcghdl_t mcg,
hermon_hw_mcg_qp_list_t *mcg_qplist, hermon_qphdl_t qp);
static void hermon_qp_mcg_refcnt_inc(hermon_qphdl_t qp);
static void hermon_qp_mcg_refcnt_dec(hermon_qphdl_t qp);
static uint_t hermon_mcg_walk_mgid_hash(hermon_state_t *state,
uint64_t start_indx, ib_gid_t mgid, uint_t *prev_indx);
static void hermon_mcg_setup_new_hdr(hermon_mcghdl_t mcg,
hermon_hw_mcg_t *mcg_hdr, ib_gid_t mgid, hermon_rsrc_t *mcg_rsrc);
static int hermon_mcg_hash_list_remove(hermon_state_t *state, uint_t curr_indx,
uint_t prev_indx, hermon_hw_mcg_t *mcg_entry);
static int hermon_mcg_entry_invalidate(hermon_state_t *state,
hermon_hw_mcg_t *mcg_entry, uint_t indx);
static int hermon_mgid_is_valid(ib_gid_t gid);
static int hermon_mlid_is_valid(ib_lid_t lid);
static void hermon_fmr_cleanup(hermon_fmrhdl_t pool);
#define HERMON_MAX_DBR_PAGES_PER_USER 64
#define HERMON_DBR_KEY(index, page) \
(((uint64_t)index) * HERMON_MAX_DBR_PAGES_PER_USER + (page))
static hermon_udbr_page_t *
hermon_dbr_new_user_page(hermon_state_t *state, uint_t index,
uint_t page)
{
hermon_udbr_page_t *pagep;
ddi_dma_attr_t dma_attr;
uint_t cookiecnt;
int status;
hermon_umap_db_entry_t *umapdb;
ulong_t pagesize = PAGESIZE;
pagep = kmem_alloc(sizeof (*pagep), KM_SLEEP);
pagep->upg_index = page;
pagep->upg_nfree = pagesize / sizeof (hermon_dbr_t);
pagep->upg_free = kmem_zalloc(pagesize / sizeof (hermon_dbr_t) / 8,
KM_SLEEP);
pagep->upg_kvaddr = ddi_umem_alloc(pagesize, DDI_UMEM_SLEEP,
&pagep->upg_umemcookie);
pagep->upg_buf = ddi_umem_iosetup(pagep->upg_umemcookie, 0,
pagesize, B_WRITE, 0, 0, NULL, DDI_UMEM_SLEEP);
hermon_dma_attr_init(state, &dma_attr);
#ifdef __sparc
if (state->hs_cfg_profile->cp_iommu_bypass == HERMON_BINDMEM_BYPASS)
dma_attr.dma_attr_flags = DDI_DMA_FORCE_PHYSICAL;
#endif
status = ddi_dma_alloc_handle(state->hs_dip, &dma_attr,
DDI_DMA_SLEEP, NULL, &pagep->upg_dmahdl);
if (status != DDI_SUCCESS) {
IBTF_DPRINTF_L2("hermon", "hermon_new_user_page: "
"ddi_dma_buf_bind_handle failed: %d", status);
return (NULL);
}
status = ddi_dma_buf_bind_handle(pagep->upg_dmahdl,
pagep->upg_buf, DDI_DMA_RDWR | DDI_DMA_CONSISTENT,
DDI_DMA_SLEEP, NULL, &pagep->upg_dmacookie, &cookiecnt);
if (status != DDI_SUCCESS) {
IBTF_DPRINTF_L2("hermon", "hermon_dbr_new_user_page: "
"ddi_dma_buf_bind_handle failed: %d", status);
ddi_dma_free_handle(&pagep->upg_dmahdl);
return (NULL);
}
ASSERT(cookiecnt == 1);
umapdb = hermon_umap_db_alloc(state->hs_instance,
HERMON_DBR_KEY(index, page), MLNX_UMAP_DBRMEM_RSRC,
(uint64_t)(uintptr_t)pagep);
hermon_umap_db_add(umapdb);
return (pagep);
}
static int
hermon_user_dbr_alloc(hermon_state_t *state, uint_t index,
ddi_acc_handle_t *acchdl, hermon_dbr_t **vdbr, uint64_t *pdbr,
uint64_t *mapoffset)
{
hermon_user_dbr_t *udbr;
hermon_udbr_page_t *pagep;
uint_t next_page;
int dbr_index;
int i1, i2, i3, last;
uint64_t u64, mask;
mutex_enter(&state->hs_dbr_lock);
for (udbr = state->hs_user_dbr; udbr != NULL; udbr = udbr->udbr_link)
if (udbr->udbr_index == index)
break;
if (udbr == NULL) {
udbr = kmem_alloc(sizeof (*udbr), KM_SLEEP);
udbr->udbr_link = state->hs_user_dbr;
state->hs_user_dbr = udbr;
udbr->udbr_index = index;
udbr->udbr_pagep = NULL;
}
pagep = udbr->udbr_pagep;
next_page = (pagep == NULL) ? 0 : (pagep->upg_index + 1);
while (pagep != NULL)
if (pagep->upg_nfree > 0)
break;
else
pagep = pagep->upg_link;
if (pagep == NULL) {
pagep = hermon_dbr_new_user_page(state, index, next_page);
if (pagep == NULL) {
mutex_exit(&state->hs_dbr_lock);
return (DDI_FAILURE);
}
pagep->upg_link = udbr->udbr_pagep;
udbr->udbr_pagep = pagep;
}
last = PAGESIZE / sizeof (uint64_t) / 64;
mask = ~0ull;
for (i1 = 0; i1 < last; i1++)
if ((pagep->upg_free[i1] & mask) != mask)
break;
u64 = pagep->upg_free[i1];
last = sizeof (uint64_t) / sizeof (uint8_t);
for (i2 = 0, mask = 0xff; i2 < last; i2++, mask <<= 8)
if ((u64 & mask) != mask)
break;
for (i3 = 0; i3 < sizeof (uint64_t) / sizeof (uint8_t); i3++)
if ((u64 & (1ul << (i3 + 8 * i2))) == 0)
break;
pagep->upg_free[i1] |= (1ul << (i3 + 8 * i2));
dbr_index = ((i1 * sizeof (uint64_t)) + i2) * sizeof (uint64_t) + i3;
pagep->upg_nfree--;
((uint64_t *)(void *)pagep->upg_kvaddr)[dbr_index] = 0;
*mapoffset = ((HERMON_DBR_KEY(index, pagep->upg_index) <<
MLNX_UMAP_RSRC_TYPE_SHIFT) | MLNX_UMAP_DBRMEM_RSRC) << PAGESHIFT;
*vdbr = (hermon_dbr_t *)((uint64_t *)(void *)pagep->upg_kvaddr +
dbr_index);
*pdbr = pagep->upg_dmacookie.dmac_laddress + dbr_index *
sizeof (uint64_t);
mutex_exit(&state->hs_dbr_lock);
return (DDI_SUCCESS);
}
static void
hermon_user_dbr_free(hermon_state_t *state, uint_t index, hermon_dbr_t *record)
{
hermon_user_dbr_t *udbr;
hermon_udbr_page_t *pagep;
caddr_t kvaddr;
uint_t dbr_index;
uint_t max_free = PAGESIZE / sizeof (hermon_dbr_t);
int i1, i2;
dbr_index = (uintptr_t)record & PAGEOFFSET;
kvaddr = (caddr_t)record - dbr_index;
dbr_index /= sizeof (hermon_dbr_t);
mutex_enter(&state->hs_dbr_lock);
for (udbr = state->hs_user_dbr; udbr != NULL; udbr = udbr->udbr_link)
if (udbr->udbr_index == index)
break;
if (udbr == NULL) {
IBTF_DPRINTF_L2("hermon", "free user dbr: udbr struct not "
"found for index %x", index);
mutex_exit(&state->hs_dbr_lock);
return;
}
for (pagep = udbr->udbr_pagep; pagep != NULL; pagep = pagep->upg_link)
if (pagep->upg_kvaddr == kvaddr)
break;
if (pagep == NULL) {
IBTF_DPRINTF_L2("hermon", "free user dbr: pagep struct not"
" found for index %x, kvaddr %p, DBR index %x",
index, kvaddr, dbr_index);
mutex_exit(&state->hs_dbr_lock);
return;
}
if (pagep->upg_nfree >= max_free) {
IBTF_DPRINTF_L2("hermon", "free user dbr: overflow: "
"UCE index %x, DBR index %x", index, dbr_index);
mutex_exit(&state->hs_dbr_lock);
return;
}
ASSERT(dbr_index < max_free);
i1 = dbr_index / 64;
i2 = dbr_index % 64;
ASSERT((pagep->upg_free[i1] & (1ul << i2)) == (1ul << i2));
pagep->upg_free[i1] &= ~(1ul << i2);
pagep->upg_nfree++;
mutex_exit(&state->hs_dbr_lock);
}
int
hermon_dbr_page_alloc(hermon_state_t *state, hermon_dbr_info_t **dinfo)
{
int status;
ddi_dma_handle_t dma_hdl;
ddi_acc_handle_t acc_hdl;
ddi_dma_attr_t dma_attr;
ddi_dma_cookie_t cookie;
uint_t cookie_cnt;
int i;
hermon_dbr_info_t *info;
caddr_t dmaaddr;
uint64_t dmalen;
ulong_t pagesize = PAGESIZE;
info = kmem_zalloc(sizeof (hermon_dbr_info_t), KM_SLEEP);
hermon_dma_attr_init(state, &dma_attr);
dma_attr.dma_attr_align = pagesize;
dma_attr.dma_attr_sgllen = 1;
#ifdef __sparc
if (state->hs_cfg_profile->cp_iommu_bypass == HERMON_BINDMEM_BYPASS)
dma_attr.dma_attr_flags = DDI_DMA_FORCE_PHYSICAL;
#endif
status = ddi_dma_alloc_handle(state->hs_dip, &dma_attr,
DDI_DMA_SLEEP, NULL, &dma_hdl);
if (status != DDI_SUCCESS) {
kmem_free((void *)info, sizeof (hermon_dbr_info_t));
cmn_err(CE_NOTE, "dbr DMA handle alloc failed\n");
return (DDI_FAILURE);
}
status = ddi_dma_mem_alloc(dma_hdl, pagesize,
&state->hs_reg_accattr, DDI_DMA_CONSISTENT, DDI_DMA_SLEEP,
NULL, &dmaaddr, (size_t *)&dmalen, &acc_hdl);
if (status != DDI_SUCCESS) {
ddi_dma_free_handle(&dma_hdl);
cmn_err(CE_CONT, "dbr DMA mem alloc failed(status %d)", status);
kmem_free((void *)info, sizeof (hermon_dbr_info_t));
return (DDI_FAILURE);
}
status = ddi_dma_addr_bind_handle(dma_hdl, NULL,
dmaaddr, (size_t)dmalen, DDI_DMA_RDWR |
DDI_DMA_CONSISTENT, DDI_DMA_SLEEP, NULL, &cookie, &cookie_cnt);
if (status != DDI_SUCCESS) {
ddi_dma_mem_free(&acc_hdl);
ddi_dma_free_handle(&dma_hdl);
kmem_free((void *)info, sizeof (hermon_dbr_info_t));
cmn_err(CE_CONT, "dbr DMA bind handle failed (status %d)",
status);
return (DDI_FAILURE);
}
*dinfo = info;
info->dbr_dmahdl = dma_hdl;
info->dbr_acchdl = acc_hdl;
info->dbr_page = (hermon_dbr_t *)(void *)dmaaddr;
info->dbr_link = NULL;
info->dbr_paddr = cookie.dmac_laddress;
info->dbr_firstfree = 0;
info->dbr_nfree = HERMON_NUM_DBR_PER_PAGE;
for (i = 0; i < HERMON_NUM_DBR_PER_PAGE; i++) {
info->dbr_page[i] = i + 1;
}
return (DDI_SUCCESS);
}
int
hermon_dbr_alloc(hermon_state_t *state, uint_t index, ddi_acc_handle_t *acchdl,
hermon_dbr_t **vdbr, uint64_t *pdbr, uint64_t *mapoffset)
{
hermon_dbr_t *record = NULL;
hermon_dbr_info_t *info = NULL;
uint32_t idx;
int status;
if (index != state->hs_kernel_uar_index)
return (hermon_user_dbr_alloc(state, index, acchdl, vdbr, pdbr,
mapoffset));
mutex_enter(&state->hs_dbr_lock);
for (info = state->hs_kern_dbr; info != NULL; info = info->dbr_link)
if (info->dbr_nfree != 0)
break;
if (info == NULL) {
status = hermon_dbr_page_alloc(state, &info);
if (status != DDI_SUCCESS) {
mutex_exit(&state->hs_dbr_lock);
return (DDI_FAILURE);
}
info->dbr_link = state->hs_kern_dbr;
state->hs_kern_dbr = info;
}
idx = info->dbr_firstfree;
record = info->dbr_page + idx;
info->dbr_firstfree = *record;
info->dbr_nfree--;
*record = 0;
*acchdl = info->dbr_acchdl;
*vdbr = record;
*pdbr = info->dbr_paddr + idx * sizeof (hermon_dbr_t);
mutex_exit(&state->hs_dbr_lock);
return (DDI_SUCCESS);
}
void
hermon_dbr_free(hermon_state_t *state, uint_t indx, hermon_dbr_t *record)
{
hermon_dbr_t *page;
hermon_dbr_info_t *info;
if (indx != state->hs_kernel_uar_index) {
hermon_user_dbr_free(state, indx, record);
return;
}
page = (hermon_dbr_t *)(uintptr_t)((uintptr_t)record & PAGEMASK);
mutex_enter(&state->hs_dbr_lock);
for (info = state->hs_kern_dbr; info != NULL; info = info->dbr_link)
if (info->dbr_page == page)
break;
ASSERT(info != NULL);
*record = info->dbr_firstfree;
info->dbr_firstfree = record - info->dbr_page;
info->dbr_nfree++;
mutex_exit(&state->hs_dbr_lock);
}
void
hermon_dbr_kern_free(hermon_state_t *state)
{
hermon_dbr_info_t *info, *link;
hermon_user_dbr_t *udbr, *next;
hermon_udbr_page_t *pagep, *nextp;
hermon_umap_db_entry_t *umapdb;
int instance, status;
uint64_t value;
extern hermon_umap_db_t hermon_userland_rsrc_db;
mutex_enter(&state->hs_dbr_lock);
for (info = state->hs_kern_dbr; info != NULL; info = link) {
(void) ddi_dma_unbind_handle(info->dbr_dmahdl);
ddi_dma_mem_free(&info->dbr_acchdl);
ddi_dma_free_handle(&info->dbr_dmahdl);
link = info->dbr_link;
kmem_free(info, sizeof (hermon_dbr_info_t));
}
udbr = state->hs_user_dbr;
instance = state->hs_instance;
mutex_enter(&hermon_userland_rsrc_db.hdl_umapdb_lock);
while (udbr != NULL) {
pagep = udbr->udbr_pagep;
while (pagep != NULL) {
(void) ddi_dma_unbind_handle(pagep->upg_dmahdl);
ddi_dma_free_handle(&pagep->upg_dmahdl);
freerbuf(pagep->upg_buf);
ddi_umem_free(pagep->upg_umemcookie);
status = hermon_umap_db_find_nolock(instance,
HERMON_DBR_KEY(udbr->udbr_index,
pagep->upg_index), MLNX_UMAP_DBRMEM_RSRC,
&value, HERMON_UMAP_DB_REMOVE, &umapdb);
if (status == DDI_SUCCESS)
hermon_umap_db_free(umapdb);
kmem_free(pagep->upg_free,
PAGESIZE / sizeof (hermon_dbr_t) / 8);
nextp = pagep->upg_link;
kmem_free(pagep, sizeof (*pagep));
pagep = nextp;
}
next = udbr->udbr_link;
kmem_free(udbr, sizeof (*udbr));
udbr = next;
}
mutex_exit(&hermon_userland_rsrc_db.hdl_umapdb_lock);
mutex_exit(&state->hs_dbr_lock);
}
int
hermon_ah_alloc(hermon_state_t *state, hermon_pdhdl_t pd,
ibt_adds_vect_t *attr_p, hermon_ahhdl_t *ahhdl, uint_t sleepflag)
{
hermon_rsrc_t *rsrc;
hermon_hw_udav_t *udav;
hermon_ahhdl_t ah;
int status;
if (!hermon_portnum_is_valid(state, attr_p->av_port_num)) {
return (IBT_HCA_PORT_INVALID);
}
status = hermon_rsrc_alloc(state, HERMON_AHHDL, 1, sleepflag, &rsrc);
if (status != DDI_SUCCESS) {
return (IBT_INSUFF_RESOURCE);
}
ah = (hermon_ahhdl_t)rsrc->hr_addr;
_NOTE(NOW_INVISIBLE_TO_OTHER_THREADS(*ah))
hermon_pd_refcnt_inc(pd);
udav = (hermon_hw_udav_t *)kmem_zalloc(sizeof (hermon_hw_udav_t),
KM_SLEEP);
_NOTE(NOW_INVISIBLE_TO_OTHER_THREADS(*udav))
status = hermon_set_addr_path(state, attr_p,
(hermon_hw_addr_path_t *)udav, HERMON_ADDRPATH_UDAV);
if (status != DDI_SUCCESS) {
hermon_pd_refcnt_dec(pd);
hermon_rsrc_free(state, &rsrc);
return (status);
}
udav->pd = pd->pd_pdnum;
udav->sl = attr_p->av_srvl;
ah->ah_rsrcp = rsrc;
ah->ah_pdhdl = pd;
ah->ah_udav = udav;
ah->ah_save_guid = attr_p->av_dgid.gid_guid;
*ahhdl = ah;
return (DDI_SUCCESS);
}
int
hermon_ah_free(hermon_state_t *state, hermon_ahhdl_t *ahhdl, uint_t sleepflag)
{
hermon_rsrc_t *rsrc;
hermon_pdhdl_t pd;
hermon_ahhdl_t ah;
ah = *ahhdl;
mutex_enter(&ah->ah_lock);
rsrc = ah->ah_rsrcp;
pd = ah->ah_pdhdl;
mutex_exit(&ah->ah_lock);
_NOTE(NOW_INVISIBLE_TO_OTHER_THREADS(*ah))
kmem_free(ah->ah_udav, sizeof (hermon_hw_udav_t));
hermon_pd_refcnt_dec(pd);
hermon_rsrc_free(state, &rsrc);
*ahhdl = NULL;
return (DDI_SUCCESS);
}
int
hermon_ah_query(hermon_state_t *state, hermon_ahhdl_t ah, hermon_pdhdl_t *pd,
ibt_adds_vect_t *attr_p)
{
mutex_enter(&ah->ah_lock);
_NOTE(NOW_INVISIBLE_TO_OTHER_THREADS(*attr_p))
*pd = ah->ah_pdhdl;
hermon_get_addr_path(state, (hermon_hw_addr_path_t *)ah->ah_udav,
attr_p, HERMON_ADDRPATH_UDAV);
attr_p->av_dgid.gid_guid = ah->ah_save_guid;
mutex_exit(&ah->ah_lock);
return (DDI_SUCCESS);
}
int
hermon_ah_modify(hermon_state_t *state, hermon_ahhdl_t ah,
ibt_adds_vect_t *attr_p)
{
hermon_hw_udav_t old_udav;
uint64_t data_old;
int status, size, i;
if (!hermon_portnum_is_valid(state, attr_p->av_port_num)) {
return (IBT_HCA_PORT_INVALID);
}
mutex_enter(&ah->ah_lock);
bcopy(ah->ah_udav, &old_udav, sizeof (hermon_hw_udav_t));
status = hermon_set_addr_path(state, attr_p,
(hermon_hw_addr_path_t *)ah->ah_udav, HERMON_ADDRPATH_UDAV);
if (status != DDI_SUCCESS) {
mutex_exit(&ah->ah_lock);
return (status);
}
ah->ah_save_guid = attr_p->av_dgid.gid_guid;
_NOTE(NOW_INVISIBLE_TO_OTHER_THREADS(*(ah->ah_udav)))
ah->ah_udav->sl = attr_p->av_srvl;
size = sizeof (hermon_hw_udav_t) >> 3;
for (i = 0; i < size; i++) {
data_old = ((uint64_t *)&old_udav)[i];
if (i == 0) {
data_old = data_old & HERMON_UDAV_MODIFY_MASK0;
} else if (i == 1) {
data_old = data_old & HERMON_UDAV_MODIFY_MASK1;
} else {
data_old = 0;
}
((uint64_t *)ah->ah_udav)[i] |= data_old;
}
ah->ah_udav->pd = old_udav.pd;
mutex_exit(&ah->ah_lock);
return (DDI_SUCCESS);
}
int
hermon_mcg_attach(hermon_state_t *state, hermon_qphdl_t qp, ib_gid_t gid,
ib_lid_t lid)
{
hermon_rsrc_t *rsrc;
hermon_hw_mcg_t *mcg_entry;
hermon_hw_mcg_qp_list_t *mcg_entry_qplist;
hermon_mcghdl_t mcg, newmcg;
uint64_t mgid_hash;
uint32_t end_indx;
int status;
uint_t qp_found;
if (qp->qp_serv_type != HERMON_QP_UD) {
return (IBT_QP_SRV_TYPE_INVALID);
}
if (hermon_mlid_is_valid(lid) == 0) {
return (IBT_MC_MLID_INVALID);
}
if (hermon_mgid_is_valid(gid) == 0) {
return (IBT_MC_MGID_INVALID);
}
status = hermon_mgid_hash_cmd_post(state, gid.gid_prefix, gid.gid_guid,
&mgid_hash, HERMON_SLEEPFLAG_FOR_CONTEXT());
if (status != HERMON_CMD_SUCCESS) {
cmn_err(CE_CONT, "Hermon: MGID_HASH 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));
}
mutex_enter(&state->hs_mcglock);
mcg_entry = state->hs_mcgtmp;
mcg_entry_qplist = HERMON_MCG_GET_QPLIST_PTR(mcg_entry);
bzero(mcg_entry, HERMON_MCGMEM_SZ(state));
end_indx = hermon_mcg_walk_mgid_hash(state, mgid_hash, gid, NULL);
mcg = &state->hs_mcghdl[end_indx];
if ((mcg->mcg_mgid_h == 0) && (mcg->mcg_mgid_l == 0)) {
hermon_mcg_setup_new_hdr(mcg, mcg_entry, gid, NULL);
status = hermon_mcg_qplist_add(state, mcg, mcg_entry_qplist, qp,
&qp_found);
if (status != DDI_SUCCESS) {
bzero(mcg, sizeof (struct hermon_sw_mcg_list_s));
mutex_exit(&state->hs_mcglock);
return (status);
}
if (!qp_found)
mcg_entry->member_cnt = (mcg->mcg_num_qps + 1);
status = hermon_write_mgm_cmd_post(state, mcg_entry, end_indx,
HERMON_CMD_NOSLEEP_SPIN);
if (status != HERMON_CMD_SUCCESS) {
bzero(mcg, sizeof (struct hermon_sw_mcg_list_s));
mutex_exit(&state->hs_mcglock);
HERMON_WARNING(state, "failed to write MCG entry");
cmn_err(CE_CONT, "Hermon: WRITE_MGM 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));
}
if (!qp_found) {
mcg->mcg_num_qps++;
hermon_qp_mcg_refcnt_inc(qp);
}
mutex_exit(&state->hs_mcglock);
return (DDI_SUCCESS);
}
if ((mcg->mcg_mgid_h == gid.gid_prefix) &&
(mcg->mcg_mgid_l == gid.gid_guid)) {
status = hermon_read_mgm_cmd_post(state, mcg_entry, end_indx,
HERMON_CMD_NOSLEEP_SPIN);
if (status != HERMON_CMD_SUCCESS) {
mutex_exit(&state->hs_mcglock);
HERMON_WARNING(state, "failed to read MCG entry");
cmn_err(CE_CONT, "Hermon: READ_MGM 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));
}
status = hermon_mcg_qplist_add(state, mcg, mcg_entry_qplist, qp,
&qp_found);
if (status != DDI_SUCCESS) {
mutex_exit(&state->hs_mcglock);
return (status);
}
if (!qp_found)
mcg_entry->member_cnt = (mcg->mcg_num_qps + 1);
status = hermon_write_mgm_cmd_post(state, mcg_entry, end_indx,
HERMON_CMD_NOSLEEP_SPIN);
if (status != HERMON_CMD_SUCCESS) {
mutex_exit(&state->hs_mcglock);
HERMON_WARNING(state, "failed to write MCG entry");
cmn_err(CE_CONT, "Hermon: WRITE_MGM 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));
}
if (!qp_found) {
mcg->mcg_num_qps++;
hermon_qp_mcg_refcnt_inc(qp);
}
mutex_exit(&state->hs_mcglock);
return (DDI_SUCCESS);
}
status = hermon_rsrc_alloc(state, HERMON_MCG, 1, HERMON_NOSLEEP, &rsrc);
if (status != DDI_SUCCESS) {
mutex_exit(&state->hs_mcglock);
return (IBT_INSUFF_RESOURCE);
}
newmcg = &state->hs_mcghdl[rsrc->hr_indx];
hermon_mcg_setup_new_hdr(newmcg, mcg_entry, gid, rsrc);
status = hermon_mcg_qplist_add(state, newmcg, mcg_entry_qplist, qp,
&qp_found);
if (status != DDI_SUCCESS) {
bzero(newmcg, sizeof (struct hermon_sw_mcg_list_s));
hermon_rsrc_free(state, &rsrc);
mutex_exit(&state->hs_mcglock);
return (status);
}
mcg_entry->member_cnt = (newmcg->mcg_num_qps + 1);
status = hermon_write_mgm_cmd_post(state, mcg_entry, rsrc->hr_indx,
HERMON_CMD_NOSLEEP_SPIN);
if (status != HERMON_CMD_SUCCESS) {
bzero(newmcg, sizeof (struct hermon_sw_mcg_list_s));
hermon_rsrc_free(state, &rsrc);
mutex_exit(&state->hs_mcglock);
HERMON_WARNING(state, "failed to write MCG entry");
cmn_err(CE_CONT, "Hermon: WRITE_MGM 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));
}
status = hermon_read_mgm_cmd_post(state, mcg_entry, end_indx,
HERMON_CMD_NOSLEEP_SPIN);
if (status != HERMON_CMD_SUCCESS) {
bzero(newmcg, sizeof (struct hermon_sw_mcg_list_s));
hermon_rsrc_free(state, &rsrc);
mutex_exit(&state->hs_mcglock);
HERMON_WARNING(state, "failed to read MCG entry");
cmn_err(CE_CONT, "Hermon: READ_MGM 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));
}
mcg_entry->next_gid_indx = rsrc->hr_indx;
status = hermon_write_mgm_cmd_post(state, mcg_entry, end_indx,
HERMON_CMD_NOSLEEP_SPIN);
if (status != HERMON_CMD_SUCCESS) {
bzero(newmcg, sizeof (struct hermon_sw_mcg_list_s));
hermon_rsrc_free(state, &rsrc);
mutex_exit(&state->hs_mcglock);
HERMON_WARNING(state, "failed to write MCG entry");
cmn_err(CE_CONT, "Hermon: WRITE_MGM 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));
}
mcg = &state->hs_mcghdl[end_indx];
mcg->mcg_next_indx = rsrc->hr_indx;
newmcg->mcg_num_qps++;
hermon_qp_mcg_refcnt_inc(qp);
mutex_exit(&state->hs_mcglock);
return (DDI_SUCCESS);
}
int
hermon_mcg_detach(hermon_state_t *state, hermon_qphdl_t qp, ib_gid_t gid,
ib_lid_t lid)
{
hermon_hw_mcg_t *mcg_entry;
hermon_hw_mcg_qp_list_t *mcg_entry_qplist;
hermon_mcghdl_t mcg;
uint64_t mgid_hash;
uint32_t end_indx, prev_indx;
int status;
if (hermon_mlid_is_valid(lid) == 0) {
return (IBT_MC_MLID_INVALID);
}
status = hermon_mgid_hash_cmd_post(state, gid.gid_prefix, gid.gid_guid,
&mgid_hash, HERMON_SLEEPFLAG_FOR_CONTEXT());
if (status != HERMON_CMD_SUCCESS) {
cmn_err(CE_CONT, "Hermon: MGID_HASH 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));
}
mutex_enter(&state->hs_mcglock);
mcg_entry = state->hs_mcgtmp;
mcg_entry_qplist = HERMON_MCG_GET_QPLIST_PTR(mcg_entry);
end_indx = hermon_mcg_walk_mgid_hash(state, mgid_hash, gid, &prev_indx);
mcg = &state->hs_mcghdl[end_indx];
if (((mcg->mcg_mgid_h == 0) && (mcg->mcg_mgid_l == 0)) ||
((mcg->mcg_mgid_h != gid.gid_prefix) ||
(mcg->mcg_mgid_l != gid.gid_guid))) {
mutex_exit(&state->hs_mcglock);
return (IBT_MC_MGID_INVALID);
}
status = hermon_read_mgm_cmd_post(state, mcg_entry, end_indx,
HERMON_CMD_NOSLEEP_SPIN);
if (status != HERMON_CMD_SUCCESS) {
mutex_exit(&state->hs_mcglock);
HERMON_WARNING(state, "failed to read MCG entry");
cmn_err(CE_CONT, "Hermon: READ_MGM 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));
}
status = hermon_mcg_qplist_remove(mcg, mcg_entry_qplist, qp);
if (status != DDI_SUCCESS) {
mutex_exit(&state->hs_mcglock);
return (status);
}
hermon_qp_mcg_refcnt_dec(qp);
if (mcg->mcg_num_qps == 1) {
status = hermon_mcg_hash_list_remove(state, end_indx, prev_indx,
mcg_entry);
if (status != DDI_SUCCESS) {
mutex_exit(&state->hs_mcglock);
return (status);
}
} else {
mcg_entry->member_cnt = (mcg->mcg_num_qps - 1);
status = hermon_write_mgm_cmd_post(state, mcg_entry, end_indx,
HERMON_CMD_NOSLEEP_SPIN);
if (status != HERMON_CMD_SUCCESS) {
mutex_exit(&state->hs_mcglock);
HERMON_WARNING(state, "failed to write MCG entry");
cmn_err(CE_CONT, "Hermon: WRITE_MGM 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));
}
mcg->mcg_num_qps--;
}
mutex_exit(&state->hs_mcglock);
return (DDI_SUCCESS);
}
static void
hermon_qp_mcg_refcnt_inc(hermon_qphdl_t qp)
{
mutex_enter(&qp->qp_lock);
qp->qp_mcg_refcnt++;
mutex_exit(&qp->qp_lock);
}
static void
hermon_qp_mcg_refcnt_dec(hermon_qphdl_t qp)
{
mutex_enter(&qp->qp_lock);
qp->qp_mcg_refcnt--;
mutex_exit(&qp->qp_lock);
}
static int
hermon_mcg_qplist_add(hermon_state_t *state, hermon_mcghdl_t mcg,
hermon_hw_mcg_qp_list_t *mcg_qplist, hermon_qphdl_t qp,
uint_t *qp_found)
{
uint_t qplist_indx;
ASSERT(MUTEX_HELD(&state->hs_mcglock));
qplist_indx = mcg->mcg_num_qps;
if (qplist_indx >= state->hs_cfg_profile->cp_num_qp_per_mcg) {
return (IBT_HCA_MCG_QP_EXCEEDED);
}
for (qplist_indx = 0; qplist_indx < mcg->mcg_num_qps;
qplist_indx++) {
if (mcg_qplist[qplist_indx].qpn == qp->qp_qpnum) {
break;
}
}
if (qplist_indx < mcg->mcg_num_qps) {
*qp_found = 1;
} else {
mcg_qplist[qplist_indx].qpn =
(qp->qp_qpnum | HERMON_MCG_QPN_BLOCK_LB);
*qp_found = 0;
}
return (DDI_SUCCESS);
}
static int
hermon_mcg_qplist_remove(hermon_mcghdl_t mcg,
hermon_hw_mcg_qp_list_t *mcg_qplist, hermon_qphdl_t qp)
{
uint_t i, qplist_indx;
qplist_indx = mcg->mcg_num_qps;
for (i = 0; i < qplist_indx; i++) {
if (mcg_qplist[i].qpn == qp->qp_qpnum) {
mcg_qplist[i] = mcg_qplist[qplist_indx - 1];
mcg_qplist[qplist_indx - 1].qpn = 0;
return (DDI_SUCCESS);
}
}
return (IBT_QP_HDL_INVALID);
}
static uint_t
hermon_mcg_walk_mgid_hash(hermon_state_t *state, uint64_t start_indx,
ib_gid_t mgid, uint_t *p_indx)
{
hermon_mcghdl_t curr_mcghdl;
uint_t curr_indx, prev_indx;
ASSERT(MUTEX_HELD(&state->hs_mcglock));
curr_indx = (uint_t)start_indx;
prev_indx = curr_indx;
curr_mcghdl = &state->hs_mcghdl[curr_indx];
if ((curr_mcghdl->mcg_mgid_h == 0) &&
(curr_mcghdl->mcg_mgid_l == 0)) {
goto end_mgid_hash_walk;
}
if ((curr_mcghdl->mcg_mgid_h == mgid.gid_prefix) &&
(curr_mcghdl->mcg_mgid_l == mgid.gid_guid)) {
goto end_mgid_hash_walk;
}
while (curr_mcghdl->mcg_next_indx != 0) {
prev_indx = curr_indx;
curr_indx = curr_mcghdl->mcg_next_indx;
curr_mcghdl = &state->hs_mcghdl[curr_indx];
if ((curr_mcghdl->mcg_mgid_h == mgid.gid_prefix) &&
(curr_mcghdl->mcg_mgid_l == mgid.gid_guid)) {
break;
}
}
end_mgid_hash_walk:
if (p_indx != NULL) {
*p_indx = prev_indx;
}
return (curr_indx);
}
static void
hermon_mcg_setup_new_hdr(hermon_mcghdl_t mcg, hermon_hw_mcg_t *mcg_hdr,
ib_gid_t mgid, hermon_rsrc_t *mcg_rsrc)
{
mcg->mcg_mgid_h = mgid.gid_prefix;
mcg->mcg_mgid_l = mgid.gid_guid;
mcg->mcg_rsrcp = mcg_rsrc;
mcg->mcg_next_indx = 0;
mcg->mcg_num_qps = 0;
mcg_hdr->mgid_h = mgid.gid_prefix;
mcg_hdr->mgid_l = mgid.gid_guid;
mcg_hdr->next_gid_indx = 0;
}
static int
hermon_mcg_hash_list_remove(hermon_state_t *state, uint_t curr_indx,
uint_t prev_indx, hermon_hw_mcg_t *mcg_entry)
{
hermon_mcghdl_t curr_mcg, prev_mcg, next_mcg;
uint_t next_indx;
int status;
curr_mcg = &state->hs_mcghdl[curr_indx];
if (curr_indx == prev_indx) {
next_indx = curr_mcg->mcg_next_indx;
if (next_indx == 0) {
status = hermon_mcg_entry_invalidate(state, mcg_entry,
curr_indx);
bzero(curr_mcg, sizeof (struct hermon_sw_mcg_list_s));
return (status);
}
next_mcg = &state->hs_mcghdl[next_indx];
status = hermon_read_mgm_cmd_post(state, mcg_entry, next_indx,
HERMON_CMD_NOSLEEP_SPIN);
if (status != HERMON_CMD_SUCCESS) {
HERMON_WARNING(state, "failed to read MCG entry");
cmn_err(CE_CONT, "Hermon: READ_MGM 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));
}
status = hermon_write_mgm_cmd_post(state, mcg_entry, curr_indx,
HERMON_CMD_NOSLEEP_SPIN);
if (status != HERMON_CMD_SUCCESS) {
HERMON_WARNING(state, "failed to write MCG entry");
cmn_err(CE_CONT, "Hermon: WRITE_MGM 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));
}
bcopy(next_mcg, curr_mcg, sizeof (struct hermon_sw_mcg_list_s));
bzero(next_mcg, sizeof (struct hermon_sw_mcg_list_s));
hermon_rsrc_free(state, &curr_mcg->mcg_rsrcp);
return (DDI_SUCCESS);
}
status = hermon_read_mgm_cmd_post(state, mcg_entry, prev_indx,
HERMON_CMD_NOSLEEP_SPIN);
if (status != HERMON_CMD_SUCCESS) {
HERMON_WARNING(state, "failed to read MCG entry");
cmn_err(CE_CONT, "Hermon: READ_MGM 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));
}
mcg_entry->next_gid_indx = curr_mcg->mcg_next_indx;
status = hermon_write_mgm_cmd_post(state, mcg_entry, prev_indx,
HERMON_CMD_NOSLEEP_SPIN);
if (status != HERMON_CMD_SUCCESS) {
HERMON_WARNING(state, "failed to write MCG entry");
cmn_err(CE_CONT, "Hermon: WRITE_MGM 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));
}
prev_mcg = &state->hs_mcghdl[prev_indx];
prev_mcg->mcg_next_indx = curr_mcg->mcg_next_indx;
hermon_rsrc_free(state, &curr_mcg->mcg_rsrcp);
bzero(curr_mcg, sizeof (struct hermon_sw_mcg_list_s));
return (DDI_SUCCESS);
}
static int
hermon_mcg_entry_invalidate(hermon_state_t *state, hermon_hw_mcg_t *mcg_entry,
uint_t indx)
{
int status;
bzero(mcg_entry, HERMON_MCGMEM_SZ(state));
status = hermon_write_mgm_cmd_post(state, mcg_entry, indx,
HERMON_CMD_NOSLEEP_SPIN);
if (status != HERMON_CMD_SUCCESS) {
HERMON_WARNING(state, "failed to write MCG entry");
cmn_err(CE_CONT, "Hermon: WRITE_MGM 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));
}
return (DDI_SUCCESS);
}
static int
hermon_mgid_is_valid(ib_gid_t gid)
{
uint_t topbits, flags, scope;
topbits = (gid.gid_prefix >> HERMON_MCG_TOPBITS_SHIFT) &
HERMON_MCG_TOPBITS_MASK;
if (topbits != HERMON_MCG_TOPBITS) {
return (0);
}
flags = (gid.gid_prefix >> HERMON_MCG_FLAGS_SHIFT) &
HERMON_MCG_FLAGS_MASK;
if (!((flags == HERMON_MCG_FLAGS_PERM) ||
(flags == HERMON_MCG_FLAGS_NONPERM))) {
return (0);
}
scope = (gid.gid_prefix >> HERMON_MCG_SCOPE_SHIFT) &
HERMON_MCG_SCOPE_MASK;
if (!((scope == HERMON_MCG_SCOPE_LINKLOC) ||
(scope == HERMON_MCG_SCOPE_SITELOC) ||
(scope == HERMON_MCG_SCOPE_ORGLOC) ||
(scope == HERMON_MCG_SCOPE_GLOBAL))) {
return (0);
}
return (1);
}
static int
hermon_mlid_is_valid(ib_lid_t lid)
{
if ((lid < IB_LID_MC_FIRST) || (lid > IB_LID_MC_LAST)) {
return (0);
}
return (1);
}
int
hermon_pd_alloc(hermon_state_t *state, hermon_pdhdl_t *pdhdl, uint_t sleepflag)
{
hermon_rsrc_t *rsrc;
hermon_pdhdl_t pd;
int status;
status = hermon_rsrc_alloc(state, HERMON_PDHDL, 1, sleepflag, &rsrc);
if (status != DDI_SUCCESS) {
return (IBT_INSUFF_RESOURCE);
}
pd = (hermon_pdhdl_t)rsrc->hr_addr;
_NOTE(NOW_INVISIBLE_TO_OTHER_THREADS(*pd))
pd->pd_refcnt = 0;
*pdhdl = pd;
return (DDI_SUCCESS);
}
int
hermon_pd_free(hermon_state_t *state, hermon_pdhdl_t *pdhdl)
{
hermon_rsrc_t *rsrc;
hermon_pdhdl_t pd;
pd = *pdhdl;
_NOTE(NOW_INVISIBLE_TO_OTHER_THREADS(*pd))
rsrc = pd->pd_rsrcp;
if (pd->pd_refcnt != 0) {
return (IBT_PD_IN_USE);
}
hermon_rsrc_free(state, &rsrc);
*pdhdl = (hermon_pdhdl_t)NULL;
return (DDI_SUCCESS);
}
void
hermon_pd_refcnt_inc(hermon_pdhdl_t pd)
{
atomic_inc_32(&pd->pd_refcnt);
}
void
hermon_pd_refcnt_dec(hermon_pdhdl_t pd)
{
atomic_dec_32(&pd->pd_refcnt);
}
int
hermon_port_query(hermon_state_t *state, uint_t port, ibt_hca_portinfo_t *pi)
{
sm_portinfo_t portinfo;
sm_guidinfo_t guidinfo;
sm_pkey_table_t pkeytable;
ib_gid_t *sgid;
uint_t sgid_max, pkey_max, tbl_size;
int i, j, indx, status;
ib_pkey_t *pkeyp;
ib_guid_t *guidp;
_NOTE(NOW_INVISIBLE_TO_OTHER_THREADS(*pi))
_NOTE(NOW_INVISIBLE_TO_OTHER_THREADS(*state))
if (!hermon_portnum_is_valid(state, port)) {
return (IBT_HCA_PORT_INVALID);
}
pkeyp = state->hs_pkey[port - 1];
guidp = state->hs_guid[port - 1];
status = hermon_getportinfo_cmd_post(state, port,
HERMON_SLEEPFLAG_FOR_CONTEXT(), &portinfo);
if (status != HERMON_CMD_SUCCESS) {
cmn_err(CE_CONT, "Hermon: GetPortInfo (port %02d) command "
"failed: %08x\n", port, status);
if (status == HERMON_CMD_INVALID_STATUS) {
hermon_fm_ereport(state, HCA_SYS_ERR, HCA_ERR_SRV_LOST);
}
return (ibc_get_ci_failure(0));
}
pi->p_base_lid = portinfo.LID;
pi->p_qkey_violations = portinfo.Q_KeyViolations;
pi->p_pkey_violations = portinfo.P_KeyViolations;
pi->p_sm_sl = portinfo.MasterSMSL;
pi->p_sm_lid = portinfo.MasterSMLID;
pi->p_linkstate = portinfo.PortState;
pi->p_port_num = portinfo.LocalPortNum;
pi->p_phys_state = portinfo.PortPhysicalState;
pi->p_width_supported = portinfo.LinkWidthSupported;
pi->p_width_enabled = portinfo.LinkWidthEnabled;
pi->p_width_active = portinfo.LinkWidthActive;
pi->p_speed_supported = portinfo.LinkSpeedSupported;
pi->p_speed_enabled = portinfo.LinkSpeedEnabled;
pi->p_speed_active = portinfo.LinkSpeedActive;
pi->p_mtu = portinfo.MTUCap;
pi->p_lmc = portinfo.LMC;
pi->p_max_vl = portinfo.VLCap;
pi->p_subnet_timeout = portinfo.SubnetTimeOut;
pi->p_msg_sz = ((uint32_t)1 << HERMON_QP_LOG_MAX_MSGSZ);
tbl_size = state->hs_cfg_profile->cp_log_max_gidtbl;
pi->p_sgid_tbl_sz = (1 << tbl_size);
tbl_size = state->hs_cfg_profile->cp_log_max_pkeytbl;
pi->p_pkey_tbl_sz = (1 << tbl_size);
state->hs_sn_prefix[port - 1] = portinfo.GidPrefix;
if (portinfo.CapabilityMask & SM_CAP_MASK_IS_SM)
pi->p_capabilities |= IBT_PORT_CAP_SM;
if (portinfo.CapabilityMask & SM_CAP_MASK_IS_SM_DISABLED)
pi->p_capabilities |= IBT_PORT_CAP_SM_DISABLED;
if (portinfo.CapabilityMask & SM_CAP_MASK_IS_SNMP_SUPPD)
pi->p_capabilities |= IBT_PORT_CAP_SNMP_TUNNEL;
if (portinfo.CapabilityMask & SM_CAP_MASK_IS_DM_SUPPD)
pi->p_capabilities |= IBT_PORT_CAP_DM;
if (portinfo.CapabilityMask & SM_CAP_MASK_IS_VM_SUPPD)
pi->p_capabilities |= IBT_PORT_CAP_VENDOR;
if (portinfo.CapabilityMask & SM_CAP_MASK_IS_CLNT_REREG_SUPPD)
pi->p_capabilities |= IBT_PORT_CAP_CLNT_REREG;
for (i = 0; i < pi->p_sgid_tbl_sz; i += 8) {
status = hermon_getguidinfo_cmd_post(state, port, i >> 3,
HERMON_SLEEPFLAG_FOR_CONTEXT(), &guidinfo);
if (status != HERMON_CMD_SUCCESS) {
cmn_err(CE_CONT, "Hermon: GetGUIDInfo (port %02d) "
"command failed: %08x\n", port, status);
if (status == HERMON_CMD_INVALID_STATUS) {
hermon_fm_ereport(state, HCA_SYS_ERR,
HCA_ERR_SRV_LOST);
}
return (ibc_get_ci_failure(0));
}
sgid_max = min((pi->p_sgid_tbl_sz - i), 8);
for (j = 0; j < sgid_max; j++) {
indx = (i + j);
sgid = &pi->p_sgid_tbl[indx];
_NOTE(NOW_INVISIBLE_TO_OTHER_THREADS(*sgid))
sgid->gid_prefix = portinfo.GidPrefix;
guidp[indx] = sgid->gid_guid =
guidinfo.GUIDBlocks[j];
}
}
for (i = 0; i < pi->p_pkey_tbl_sz; i += 32) {
status = hermon_getpkeytable_cmd_post(state, port, i,
HERMON_SLEEPFLAG_FOR_CONTEXT(), &pkeytable);
if (status != HERMON_CMD_SUCCESS) {
cmn_err(CE_CONT, "Hermon: GetPKeyTable (port %02d) "
"command failed: %08x\n", port, status);
if (status == HERMON_CMD_INVALID_STATUS) {
hermon_fm_ereport(state, HCA_SYS_ERR,
HCA_ERR_SRV_LOST);
}
return (ibc_get_ci_failure(0));
}
pkey_max = min((pi->p_pkey_tbl_sz - i), 32);
for (j = 0; j < pkey_max; j++) {
indx = (i + j);
pkeyp[indx] = pi->p_pkey_tbl[indx] =
pkeytable.P_KeyTableBlocks[j];
}
}
return (DDI_SUCCESS);
}
int
hermon_port_modify(hermon_state_t *state, uint8_t port,
ibt_port_modify_flags_t flags, uint8_t init_type)
{
sm_portinfo_t portinfo;
uint32_t capmask;
int status;
hermon_hw_set_port_t set_port;
if ((flags & IBT_PORT_SHUTDOWN) ||
(flags & IBT_PORT_SET_INIT_TYPE)) {
return (IBT_NOT_SUPPORTED);
}
bzero(&set_port, sizeof (set_port));
if (flags & IBT_PORT_RESET_QKEY)
set_port.rqk = 1;
if (!hermon_portnum_is_valid(state, port)) {
return (IBT_HCA_PORT_INVALID);
}
status = hermon_getportinfo_cmd_post(state, port,
HERMON_SLEEPFLAG_FOR_CONTEXT(), &portinfo);
if (status != HERMON_CMD_SUCCESS) {
if (status == HERMON_CMD_INVALID_STATUS) {
hermon_fm_ereport(state, HCA_SYS_ERR, HCA_ERR_SRV_LOST);
}
return (ibc_get_ci_failure(0));
}
capmask = portinfo.CapabilityMask;
if (flags & IBT_PORT_RESET_SM)
capmask &= ~SM_CAP_MASK_IS_SM;
else if (flags & IBT_PORT_SET_SM)
capmask |= SM_CAP_MASK_IS_SM;
if (flags & IBT_PORT_RESET_SNMP)
capmask &= ~SM_CAP_MASK_IS_SNMP_SUPPD;
else if (flags & IBT_PORT_SET_SNMP)
capmask |= SM_CAP_MASK_IS_SNMP_SUPPD;
if (flags & IBT_PORT_RESET_DEVMGT)
capmask &= ~SM_CAP_MASK_IS_DM_SUPPD;
else if (flags & IBT_PORT_SET_DEVMGT)
capmask |= SM_CAP_MASK_IS_DM_SUPPD;
if (flags & IBT_PORT_RESET_VENDOR)
capmask &= ~SM_CAP_MASK_IS_VM_SUPPD;
else if (flags & IBT_PORT_SET_VENDOR)
capmask |= SM_CAP_MASK_IS_VM_SUPPD;
set_port.cap_mask = capmask;
status = hermon_set_port_cmd_post(state, &set_port, port,
HERMON_SLEEPFLAG_FOR_CONTEXT());
if (status != HERMON_CMD_SUCCESS) {
HERMON_WARNING(state, "failed to modify port capabilities");
cmn_err(CE_CONT, "Hermon: SET_IB (port %02d) command failed: "
"%08x\n", port, status);
if (status == HERMON_CMD_INVALID_STATUS) {
hermon_fm_ereport(state, HCA_SYS_ERR, HCA_ERR_SRV_LOST);
}
return (ibc_get_ci_failure(0));
}
return (DDI_SUCCESS);
}
int hermon_srate_override = -1;
int
hermon_set_addr_path(hermon_state_t *state, ibt_adds_vect_t *av,
hermon_hw_addr_path_t *path, uint_t type)
{
uint_t gidtbl_sz;
hermon_hw_udav_t *udav;
_NOTE(NOW_INVISIBLE_TO_OTHER_THREADS(*av))
_NOTE(NOW_INVISIBLE_TO_OTHER_THREADS(*path))
udav = (hermon_hw_udav_t *)(void *)path;
_NOTE(NOW_INVISIBLE_TO_OTHER_THREADS(*udav))
path->mlid = av->av_src_path;
path->rlid = av->av_dlid;
switch (av->av_srate) {
case IBT_SRATE_2:
path->max_stat_rate = 7; break;
case IBT_SRATE_10:
path->max_stat_rate = 8; break;
case IBT_SRATE_30:
path->max_stat_rate = 9; break;
case IBT_SRATE_5:
path->max_stat_rate = 10; break;
case IBT_SRATE_20:
path->max_stat_rate = 11; break;
case IBT_SRATE_40:
path->max_stat_rate = 12; break;
case IBT_SRATE_60:
path->max_stat_rate = 13; break;
case IBT_SRATE_80:
path->max_stat_rate = 14; break;
case IBT_SRATE_120:
path->max_stat_rate = 15; break;
case IBT_SRATE_NOT_SPECIFIED:
path->max_stat_rate = 0; break;
default:
return (IBT_STATIC_RATE_INVALID);
}
if (hermon_srate_override != -1)
path->max_stat_rate = hermon_srate_override;
gidtbl_sz = (1 << state->hs_queryport.log_max_gid);
if ((av->av_send_grh) && (av->av_sgid_ix > gidtbl_sz)) {
return (IBT_SGID_INVALID);
}
path->grh = av->av_send_grh;
if (type == HERMON_ADDRPATH_QP) {
path->mgid_index = av->av_sgid_ix;
} else {
path->mgid_index = ((av->av_port_num - 1) * gidtbl_sz) +
av->av_sgid_ix;
udav->portnum = av->av_port_num;
}
if ((path->grh) || (type == HERMON_ADDRPATH_QP)) {
path->flow_label = av->av_flow;
path->tclass = av->av_tclass;
path->hop_limit = av->av_hop;
bcopy(&(av->av_dgid.gid_prefix), &(path->rgid_h),
sizeof (uint64_t));
bcopy(&(av->av_dgid.gid_guid), &(path->rgid_l),
sizeof (uint64_t));
} else {
path->rgid_l = 0x2;
path->flow_label = 0;
path->tclass = 0;
path->hop_limit = 0;
path->rgid_h = 0;
}
udav->sl = (HERMON_DEF_SCHED_SELECTION & 0x3C) >> 2;
return (DDI_SUCCESS);
}
void
hermon_get_addr_path(hermon_state_t *state, hermon_hw_addr_path_t *path,
ibt_adds_vect_t *av, uint_t type)
{
uint_t gidtbl_sz;
_NOTE(NOW_INVISIBLE_TO_OTHER_THREADS(*path))
_NOTE(NOW_INVISIBLE_TO_OTHER_THREADS(*av))
av->av_src_path = path->mlid;
av->av_dlid = path->rlid;
switch (path->max_stat_rate) {
case 7:
av->av_srate = IBT_SRATE_2; break;
case 8:
av->av_srate = IBT_SRATE_10; break;
case 9:
av->av_srate = IBT_SRATE_30; break;
case 10:
av->av_srate = IBT_SRATE_5; break;
case 11:
av->av_srate = IBT_SRATE_20; break;
case 12:
av->av_srate = IBT_SRATE_40; break;
case 13:
av->av_srate = IBT_SRATE_60; break;
case 14:
av->av_srate = IBT_SRATE_80; break;
case 15:
av->av_srate = IBT_SRATE_120; break;
case 0:
av->av_srate = IBT_SRATE_NOT_SPECIFIED; break;
default:
av->av_srate = IBT_SRATE_1X;
}
av->av_send_grh = path->grh;
if (type == HERMON_ADDRPATH_QP) {
av->av_sgid_ix = path->mgid_index;
} else {
gidtbl_sz = (1 << state->hs_queryport.log_max_gid);
av->av_sgid_ix = path->mgid_index - ((av->av_port_num - 1) *
gidtbl_sz);
av->av_port_num = ((hermon_hw_udav_t *)(void *)path)->portnum;
}
av->av_flow = path->flow_label;
av->av_tclass = path->tclass;
av->av_hop = path->hop_limit;
bcopy(&(path->rgid_h), &(av->av_dgid.gid_prefix), sizeof (uint64_t));
bcopy(&(path->rgid_l), &(av->av_dgid.gid_guid), sizeof (uint64_t));
}
int
hermon_portnum_is_valid(hermon_state_t *state, uint_t portnum)
{
uint_t max_port;
max_port = state->hs_cfg_profile->cp_num_ports;
if ((portnum <= max_port) && (portnum != 0)) {
return (1);
} else {
return (0);
}
}
int
hermon_pkeyindex_is_valid(hermon_state_t *state, uint_t pkeyindx)
{
uint_t max_pkeyindx;
max_pkeyindx = 1 << state->hs_cfg_profile->cp_log_max_pkeytbl;
if (pkeyindx < max_pkeyindx) {
return (1);
} else {
return (0);
}
}
int
hermon_queue_alloc(hermon_state_t *state, hermon_qalloc_info_t *qa_info,
uint_t sleepflag)
{
ddi_dma_attr_t dma_attr;
int (*callback)(caddr_t);
uint64_t realsize, alloc_mask;
int flag, status;
_NOTE(NOW_INVISIBLE_TO_OTHER_THREADS(*qa_info))
callback = (sleepflag == HERMON_SLEEP) ? DDI_DMA_SLEEP :
DDI_DMA_DONTWAIT;
hermon_dma_attr_init(state, &dma_attr);
dma_attr.dma_attr_align = qa_info->qa_bind_align;
#ifdef __sparc
if (state->hs_cfg_profile->cp_iommu_bypass == HERMON_BINDMEM_BYPASS) {
dma_attr.dma_attr_flags = DDI_DMA_FORCE_PHYSICAL;
}
#endif
status = ddi_dma_alloc_handle(state->hs_dip, &dma_attr, callback, NULL,
&qa_info->qa_dmahdl);
if (status != DDI_SUCCESS) {
return (DDI_FAILURE);
}
alloc_mask = qa_info->qa_alloc_align - 1;
if (qa_info->qa_bind_align == qa_info->qa_alloc_align) {
realsize = qa_info->qa_size;
} else {
realsize = qa_info->qa_size + alloc_mask;
}
if (qa_info->qa_location == HERMON_QUEUE_LOCATION_NORMAL) {
status = ddi_dma_mem_alloc(qa_info->qa_dmahdl, realsize,
&state->hs_reg_accattr, DDI_DMA_CONSISTENT, callback, NULL,
(caddr_t *)&qa_info->qa_buf_real,
(size_t *)&qa_info->qa_buf_realsz, &qa_info->qa_acchdl);
if (status != DDI_SUCCESS) {
ddi_dma_free_handle(&qa_info->qa_dmahdl);
return (DDI_FAILURE);
}
qa_info->qa_buf_aligned = qa_info->qa_buf_real;
bzero(qa_info->qa_buf_real, qa_info->qa_buf_realsz);
} else {
flag = (sleepflag == HERMON_SLEEP) ? DDI_UMEM_SLEEP :
DDI_UMEM_NOSLEEP;
qa_info->qa_buf_real = ddi_umem_alloc(realsize, flag,
&qa_info->qa_umemcookie);
if (qa_info->qa_buf_real == NULL) {
ddi_dma_free_handle(&qa_info->qa_dmahdl);
return (DDI_FAILURE);
}
qa_info->qa_buf_aligned = qa_info->qa_buf_real;
}
if (qa_info->qa_bind_align != qa_info->qa_alloc_align) {
qa_info->qa_buf_aligned = (uint32_t *)(uintptr_t)(((uintptr_t)
qa_info->qa_buf_aligned + alloc_mask) & ~alloc_mask);
}
qa_info->qa_pgoffs = (uint_t)((uintptr_t)
qa_info->qa_buf_aligned & HERMON_PAGEOFFSET);
return (DDI_SUCCESS);
}
void
hermon_queue_free(hermon_qalloc_info_t *qa_info)
{
_NOTE(NOW_INVISIBLE_TO_OTHER_THREADS(*qa_info))
if (qa_info->qa_location == HERMON_QUEUE_LOCATION_NORMAL) {
ddi_dma_mem_free(&qa_info->qa_acchdl);
} else if (qa_info->qa_location == HERMON_QUEUE_LOCATION_USERLAND) {
ddi_umem_free(qa_info->qa_umemcookie);
}
ddi_dma_free_handle(&qa_info->qa_dmahdl);
}
int
hermon_create_fmr_pool(hermon_state_t *state, hermon_pdhdl_t pd,
ibt_fmr_pool_attr_t *fmr_attr, hermon_fmrhdl_t *fmrpoolp)
{
hermon_fmrhdl_t fmrpool;
hermon_fmr_list_t *fmr, *fmr_next;
hermon_mrhdl_t mr;
int status;
int sleep;
int i;
sleep = (fmr_attr->fmr_flags & IBT_MR_SLEEP) ? HERMON_SLEEP :
HERMON_NOSLEEP;
if ((sleep == HERMON_SLEEP) &&
(sleep != HERMON_SLEEPFLAG_FOR_CONTEXT())) {
return (IBT_INVALID_PARAM);
}
fmrpool = (hermon_fmrhdl_t)kmem_zalloc(sizeof (*fmrpool), sleep);
if (fmrpool == NULL) {
status = IBT_INSUFF_RESOURCE;
goto fail;
}
_NOTE(NOW_INVISIBLE_TO_OTHER_THREADS(*fmrpool))
mutex_init(&fmrpool->fmr_lock, NULL, MUTEX_DRIVER,
DDI_INTR_PRI(state->hs_intrmsi_pri));
mutex_init(&fmrpool->remap_lock, NULL, MUTEX_DRIVER,
DDI_INTR_PRI(state->hs_intrmsi_pri));
mutex_init(&fmrpool->dirty_lock, NULL, MUTEX_DRIVER,
DDI_INTR_PRI(state->hs_intrmsi_pri));
fmrpool->fmr_state = state;
fmrpool->fmr_flush_function = fmr_attr->fmr_func_hdlr;
fmrpool->fmr_flush_arg = fmr_attr->fmr_func_arg;
fmrpool->fmr_pool_size = 0;
fmrpool->fmr_max_pages = fmr_attr->fmr_max_pages_per_fmr;
fmrpool->fmr_page_sz = fmr_attr->fmr_page_sz;
fmrpool->fmr_dirty_watermark = fmr_attr->fmr_pool_size / 4;
fmrpool->fmr_dirty_len = 0;
fmrpool->fmr_remap_watermark = fmr_attr->fmr_pool_size / 32;
fmrpool->fmr_remap_len = 0;
fmrpool->fmr_flags = fmr_attr->fmr_flags;
fmrpool->fmr_stat_register = 0;
fmrpool->fmr_max_remaps = state->hs_cfg_profile->cp_fmr_max_remaps;
fmrpool->fmr_remap_gen = 1;
fmrpool->fmr_free_list_tail = &fmrpool->fmr_free_list;
fmrpool->fmr_dirty_list = NULL;
fmrpool->fmr_dirty_list_tail = &fmrpool->fmr_dirty_list;
fmrpool->fmr_remap_list = NULL;
fmrpool->fmr_remap_list_tail = &fmrpool->fmr_remap_list;
fmrpool->fmr_pool_size = fmrpool->fmr_free_len =
fmr_attr->fmr_pool_size;
for (i = 0; i < fmr_attr->fmr_pool_size; i++) {
status = hermon_mr_alloc_fmr(state, pd, fmrpool, &mr);
if (status != DDI_SUCCESS) {
goto fail2;
}
fmr = (hermon_fmr_list_t *)kmem_zalloc(
sizeof (hermon_fmr_list_t), sleep);
_NOTE(NOW_INVISIBLE_TO_OTHER_THREADS(*fmr))
fmr->fmr = mr;
fmr->fmr_remaps = 0;
fmr->fmr_remap_gen = fmrpool->fmr_remap_gen;
fmr->fmr_pool = fmrpool;
_NOTE(NOW_INVISIBLE_TO_OTHER_THREADS(*mr))
mr->mr_fmr = fmr;
if (!i)
fmrpool->fmr_free_list_tail = &fmr->fmr_next;
fmr->fmr_next = fmrpool->fmr_free_list;
fmrpool->fmr_free_list = fmr;
}
*fmrpoolp = fmrpool;
IBTF_DPRINTF_L2("fmr", "create_fmr_pool SUCCESS");
return (IBT_SUCCESS);
fail2:
for (fmr = fmrpool->fmr_free_list; fmr != NULL; fmr = fmr_next) {
_NOTE(NOW_INVISIBLE_TO_OTHER_THREADS(*fmr))
fmr_next = fmr->fmr_next;
(void) hermon_mr_dealloc_fmr(state, &fmr->fmr);
kmem_free(fmr, sizeof (hermon_fmr_list_t));
}
kmem_free(fmrpool, sizeof (*fmrpool));
fail:
*fmrpoolp = NULL;
IBTF_DPRINTF_L2("fmr", "create_fmr_pool FAILED");
if (status == DDI_FAILURE) {
return (ibc_get_ci_failure(0));
} else {
return (status);
}
}
int
hermon_destroy_fmr_pool(hermon_state_t *state, hermon_fmrhdl_t fmrpool)
{
hermon_fmr_list_t *fmr, *fmr_next;
mutex_enter(&fmrpool->fmr_lock);
hermon_fmr_cleanup(fmrpool);
for (fmr = fmrpool->fmr_free_list; fmr != NULL; fmr = fmr_next) {
fmr_next = fmr->fmr_next;
(void) hermon_mr_dealloc_fmr(state, &fmr->fmr);
kmem_free(fmr, sizeof (hermon_fmr_list_t));
--fmrpool->fmr_pool_size;
}
ASSERT(fmrpool->fmr_pool_size == 0);
mutex_exit(&fmrpool->fmr_lock);
mutex_destroy(&fmrpool->fmr_lock);
mutex_destroy(&fmrpool->dirty_lock);
mutex_destroy(&fmrpool->remap_lock);
kmem_free(fmrpool, sizeof (*fmrpool));
IBTF_DPRINTF_L2("fmr", "destroy_fmr_pool SUCCESS");
return (DDI_SUCCESS);
}
int
hermon_flush_fmr_pool(hermon_state_t *state, hermon_fmrhdl_t fmrpool)
{
mutex_enter(&fmrpool->fmr_lock);
hermon_fmr_cleanup(fmrpool);
mutex_exit(&fmrpool->fmr_lock);
return (DDI_SUCCESS);
}
int
hermon_register_physical_fmr(hermon_state_t *state, hermon_fmrhdl_t fmrpool,
ibt_pmr_attr_t *mem_pattr, hermon_mrhdl_t *mr,
ibt_pmr_desc_t *mem_desc_p)
{
hermon_fmr_list_t *fmr;
int status;
if (mem_pattr->pmr_len < 1 || (mem_pattr->pmr_num_buf >
fmrpool->fmr_max_pages)) {
return (IBT_MR_LEN_INVALID);
}
mutex_enter(&fmrpool->fmr_lock);
if (fmrpool->fmr_free_list == NULL) {
if (hermon_fmr_verbose & 2)
IBTF_DPRINTF_L2("fmr", "register needs remap");
mutex_enter(&fmrpool->remap_lock);
if (fmrpool->fmr_remap_list) {
*(fmrpool->fmr_free_list_tail) =
fmrpool->fmr_remap_list;
fmrpool->fmr_remap_list = NULL;
fmrpool->fmr_free_list_tail =
fmrpool->fmr_remap_list_tail;
fmrpool->fmr_remap_list_tail = &fmrpool->fmr_remap_list;
fmrpool->fmr_free_len += fmrpool->fmr_remap_len;
fmrpool->fmr_remap_len = 0;
}
mutex_exit(&fmrpool->remap_lock);
}
if (fmrpool->fmr_free_list == NULL) {
if (hermon_fmr_verbose & 2)
IBTF_DPRINTF_L2("fmr", "register needs cleanup");
hermon_fmr_cleanup(fmrpool);
}
fmr = fmrpool->fmr_free_list;
if (fmr == NULL) {
IBTF_DPRINTF_L2("fmr", "WARNING: no free fmr resource");
cmn_err(CE_CONT, "no free fmr resource\n");
mutex_exit(&fmrpool->fmr_lock);
return (IBT_INSUFF_RESOURCE);
}
if ((fmrpool->fmr_free_list = fmr->fmr_next) == NULL)
fmrpool->fmr_free_list_tail = &fmrpool->fmr_free_list;
fmr->fmr_next = NULL;
fmrpool->fmr_stat_register++;
mutex_exit(&fmrpool->fmr_lock);
_NOTE(NOW_INVISIBLE_TO_OTHER_THREADS(*fmr))
status = hermon_mr_register_physical_fmr(state, mem_pattr, fmr->fmr,
mem_desc_p);
if (status != DDI_SUCCESS) {
return (status);
}
_NOTE(NOW_INVISIBLE_TO_OTHER_THREADS(*fmr->fmr))
if (hermon_rdma_debug & 0x4)
IBTF_DPRINTF_L2("fmr", " reg: mr %p key %x",
fmr->fmr, fmr->fmr->mr_rkey);
_NOTE(NOW_VISIBLE_TO_OTHER_THREADS(*fmr->fmr))
if (fmr->fmr_remap_gen != fmrpool->fmr_remap_gen) {
fmr->fmr_remap_gen = fmrpool->fmr_remap_gen;
fmr->fmr_remaps = 0;
}
fmr->fmr_remaps++;
*mr = (hermon_mrhdl_t)fmr->fmr;
return (DDI_SUCCESS);
}
int
hermon_deregister_fmr(hermon_state_t *state, hermon_mrhdl_t mr)
{
hermon_fmrhdl_t fmrpool;
hermon_fmr_list_t *fmr, **fmrlast;
int len;
fmr = mr->mr_fmr;
_NOTE(NOW_INVISIBLE_TO_OTHER_THREADS(*fmr))
fmrpool = fmr->fmr_pool;
_NOTE(NOW_INVISIBLE_TO_OTHER_THREADS(*(fmr->fmr)))
*(uint8_t *)(fmr->fmr->mr_mptrsrcp->hr_addr) = 0xF0;
if (fmr->fmr_remaps <
state->hs_cfg_profile->cp_fmr_max_remaps) {
_NOTE(NOW_INVISIBLE_TO_OTHER_THREADS(*(fmr->fmr)))
if (hermon_rdma_debug & 0x4)
IBTF_DPRINTF_L2("fmr", "dereg: mr %p key %x",
fmr->fmr, fmr->fmr->mr_rkey);
_NOTE(NOW_VISIBLE_TO_OTHER_THREADS(*(fmr->fmr)))
mutex_enter(&fmrpool->remap_lock);
fmr->fmr_next = NULL;
*(fmrpool->fmr_remap_list_tail) = fmr;
fmrpool->fmr_remap_list_tail = &fmr->fmr_next;
fmrpool->fmr_remap_len++;
fmrlast = NULL;
if (fmrpool->fmr_remap_len >=
fmrpool->fmr_remap_watermark) {
fmr = fmrpool->fmr_remap_list;
fmrlast = fmrpool->fmr_remap_list_tail;
len = fmrpool->fmr_remap_len;
fmrpool->fmr_remap_len = 0;
fmrpool->fmr_remap_list = NULL;
fmrpool->fmr_remap_list_tail =
&fmrpool->fmr_remap_list;
}
mutex_exit(&fmrpool->remap_lock);
if (fmrlast) {
mutex_enter(&fmrpool->fmr_lock);
*(fmrpool->fmr_free_list_tail) = fmr;
fmrpool->fmr_free_list_tail = fmrlast;
fmrpool->fmr_free_len += len;
mutex_exit(&fmrpool->fmr_lock);
}
} else {
_NOTE(NOW_INVISIBLE_TO_OTHER_THREADS(*(fmr->fmr)))
if (hermon_rdma_debug & 0x4)
IBTF_DPRINTF_L2("fmr", "dirty: mr %p key %x",
fmr->fmr, fmr->fmr->mr_rkey);
_NOTE(NOW_VISIBLE_TO_OTHER_THREADS(*(fmr->fmr)))
mutex_enter(&fmrpool->dirty_lock);
fmr->fmr_next = NULL;
*(fmrpool->fmr_dirty_list_tail) = fmr;
fmrpool->fmr_dirty_list_tail = &fmr->fmr_next;
fmrpool->fmr_dirty_len++;
if (fmrpool->fmr_dirty_len >=
fmrpool->fmr_dirty_watermark) {
mutex_exit(&fmrpool->dirty_lock);
mutex_enter(&fmrpool->fmr_lock);
hermon_fmr_cleanup(fmrpool);
mutex_exit(&fmrpool->fmr_lock);
} else
mutex_exit(&fmrpool->dirty_lock);
}
return (DDI_SUCCESS);
}
static void
hermon_fmr_cleanup(hermon_fmrhdl_t fmrpool)
{
int status;
ASSERT(MUTEX_HELD(&fmrpool->fmr_lock));
if (fmrpool->fmr_stat_register == 0)
return;
fmrpool->fmr_stat_register = 0;
membar_producer();
if (hermon_fmr_verbose)
IBTF_DPRINTF_L2("fmr", "TPT_SYNC");
status = hermon_sync_tpt_cmd_post(fmrpool->fmr_state,
HERMON_CMD_NOSLEEP_SPIN);
if (status != HERMON_CMD_SUCCESS) {
cmn_err(CE_WARN, "fmr SYNC_TPT failed(%x)\n", status);
}
fmrpool->fmr_remap_gen++;
mutex_enter(&fmrpool->dirty_lock);
if (fmrpool->fmr_dirty_list) {
*(fmrpool->fmr_free_list_tail) = fmrpool->fmr_dirty_list;
fmrpool->fmr_dirty_list = NULL;
fmrpool->fmr_free_list_tail = fmrpool->fmr_dirty_list_tail;
fmrpool->fmr_dirty_list_tail = &fmrpool->fmr_dirty_list;
fmrpool->fmr_free_len += fmrpool->fmr_dirty_len;
fmrpool->fmr_dirty_len = 0;
}
mutex_exit(&fmrpool->dirty_lock);
mutex_enter(&fmrpool->remap_lock);
if (fmrpool->fmr_remap_list) {
*(fmrpool->fmr_free_list_tail) = fmrpool->fmr_remap_list;
fmrpool->fmr_remap_list = NULL;
fmrpool->fmr_free_list_tail = fmrpool->fmr_remap_list_tail;
fmrpool->fmr_remap_list_tail = &fmrpool->fmr_remap_list;
fmrpool->fmr_free_len += fmrpool->fmr_remap_len;
fmrpool->fmr_remap_len = 0;
}
mutex_exit(&fmrpool->remap_lock);
if (fmrpool->fmr_flush_function != NULL) {
(void) fmrpool->fmr_flush_function(
(ibc_fmr_pool_hdl_t)fmrpool,
fmrpool->fmr_flush_arg);
}
}