#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>
int hermon_should_panic = 0;
#define hermon_cq_update_ci_doorbell(cq) \
\
HERMON_UAR_DB_RECORD_WRITE(cq->cq_arm_ci_vdbr, \
cq->cq_consindx & 0x00FFFFFF)
static int hermon_cq_arm_doorbell(hermon_state_t *state, hermon_cqhdl_t cq,
uint_t cmd);
static void hermon_arm_cq_dbr_init(hermon_dbr_t *cq_arm_dbr);
static void hermon_cq_cqe_consume(hermon_state_t *state, hermon_cqhdl_t cq,
hermon_hw_cqe_t *cqe, ibt_wc_t *wc);
static void hermon_cq_errcqe_consume(hermon_state_t *state, hermon_cqhdl_t cq,
hermon_hw_cqe_t *cqe, ibt_wc_t *wc);
int
hermon_cq_alloc(hermon_state_t *state, ibt_cq_hdl_t ibt_cqhdl,
ibt_cq_attr_t *cq_attr, uint_t *actual_size, hermon_cqhdl_t *cqhdl,
uint_t sleepflag)
{
hermon_rsrc_t *cqc, *rsrc;
hermon_umap_db_entry_t *umapdb;
hermon_hw_cqc_t cqc_entry;
hermon_cqhdl_t cq;
ibt_mr_attr_t mr_attr;
hermon_mr_options_t op;
hermon_pdhdl_t pd;
hermon_mrhdl_t mr;
hermon_hw_cqe_t *buf;
uint64_t value;
uint32_t log_cq_size, uarpg;
uint_t cq_is_umap;
uint32_t status, flag;
hermon_cq_sched_t *cq_schedp;
_NOTE(NOW_INVISIBLE_TO_OTHER_THREADS(*cq_attr))
cq_is_umap = (cq_attr->cq_flags & IBT_CQ_USER_MAP) ? 1 : 0;
if (cq_is_umap) {
status = hermon_umap_db_find(state->hs_instance, ddi_get_pid(),
MLNX_UMAP_UARPG_RSRC, &value, 0, NULL);
if (status != DDI_SUCCESS) {
status = IBT_INVALID_PARAM;
goto cqalloc_fail;
}
uarpg = ((hermon_rsrc_t *)(uintptr_t)value)->hr_indx;
} else {
uarpg = state->hs_kernel_uar_index;
}
pd = state->hs_pdhdl_internal;
hermon_pd_refcnt_inc(pd);
status = hermon_rsrc_alloc(state, HERMON_CQC, 1, sleepflag, &cqc);
if (status != DDI_SUCCESS) {
status = IBT_INSUFF_RESOURCE;
goto cqalloc_fail1;
}
status = hermon_rsrc_alloc(state, HERMON_CQHDL, 1, sleepflag, &rsrc);
if (status != DDI_SUCCESS) {
status = IBT_INSUFF_RESOURCE;
goto cqalloc_fail2;
}
cq = (hermon_cqhdl_t)rsrc->hr_addr;
_NOTE(NOW_INVISIBLE_TO_OTHER_THREADS(*cq))
cq->cq_is_umap = cq_is_umap;
cq->cq_cqnum = cqc->hr_indx;
cq->cq_intmod_count = 0;
cq->cq_intmod_usec = 0;
if (cq->cq_is_umap) {
umapdb = hermon_umap_db_alloc(state->hs_instance, cq->cq_cqnum,
MLNX_UMAP_CQMEM_RSRC, (uint64_t)(uintptr_t)rsrc);
if (umapdb == NULL) {
status = IBT_INSUFF_RESOURCE;
goto cqalloc_fail3;
}
}
status = hermon_dbr_alloc(state, uarpg, &cq->cq_arm_ci_dbr_acchdl,
&cq->cq_arm_ci_vdbr, &cq->cq_arm_ci_pdbr, &cq->cq_dbr_mapoffset);
if (status != DDI_SUCCESS) {
status = IBT_INSUFF_RESOURCE;
goto cqalloc_fail4;
}
cq_attr->cq_size = max(cq_attr->cq_size, HERMON_CQ_MIN_SIZE);
log_cq_size = highbit(cq_attr->cq_size);
if (log_cq_size > state->hs_cfg_profile->cp_log_max_cq_sz) {
status = IBT_HCA_CQ_EXCEEDED;
goto cqalloc_fail4a;
}
cq->cq_cqinfo.qa_size = (1 << log_cq_size) * sizeof (hermon_hw_cqe_t);
cq->cq_cqinfo.qa_alloc_align = PAGESIZE;
cq->cq_cqinfo.qa_bind_align = PAGESIZE;
if (cq->cq_is_umap) {
cq->cq_cqinfo.qa_location = HERMON_QUEUE_LOCATION_USERLAND;
} else {
cq->cq_cqinfo.qa_location = HERMON_QUEUE_LOCATION_NORMAL;
hermon_arm_cq_dbr_init(cq->cq_arm_ci_vdbr);
}
status = hermon_queue_alloc(state, &cq->cq_cqinfo, sleepflag);
if (status != DDI_SUCCESS) {
status = IBT_INSUFF_RESOURCE;
goto cqalloc_fail4;
}
buf = (hermon_hw_cqe_t *)cq->cq_cqinfo.qa_buf_aligned;
_NOTE(NOW_INVISIBLE_TO_OTHER_THREADS(*buf))
flag = (sleepflag == HERMON_SLEEP) ? IBT_MR_SLEEP : IBT_MR_NOSLEEP;
mr_attr.mr_vaddr = (uint64_t)(uintptr_t)buf;
mr_attr.mr_len = cq->cq_cqinfo.qa_size;
mr_attr.mr_as = NULL;
mr_attr.mr_flags = flag | IBT_MR_ENABLE_LOCAL_WRITE;
op.mro_bind_type = state->hs_cfg_profile->cp_iommu_bypass;
op.mro_bind_dmahdl = cq->cq_cqinfo.qa_dmahdl;
op.mro_bind_override_addr = 0;
status = hermon_mr_register(state, pd, &mr_attr, &mr, &op,
HERMON_CQ_CMPT);
if (status != DDI_SUCCESS) {
status = IBT_INSUFF_RESOURCE;
goto cqalloc_fail5;
}
_NOTE(NOW_INVISIBLE_TO_OTHER_THREADS(*mr))
cq->cq_erreqnum = HERMON_CQ_ERREQNUM_GET(state);
if (cq_attr->cq_flags & IBT_CQ_HID) {
if (!HERMON_HID_VALID(state, cq_attr->cq_hid)) {
IBTF_DPRINTF_L2("CQalloc", "bad handler id 0x%x",
cq_attr->cq_hid);
status = IBT_INVALID_PARAM;
goto cqalloc_fail5;
}
cq->cq_eqnum = HERMON_HID_TO_EQNUM(state, cq_attr->cq_hid);
IBTF_DPRINTF_L2("cqalloc", "hid: eqn %d", cq->cq_eqnum);
} else {
cq_schedp = (hermon_cq_sched_t *)cq_attr->cq_sched;
if (cq_schedp == NULL) {
cq_schedp = &state->hs_cq_sched_default;
} else if (cq_schedp != &state->hs_cq_sched_default) {
int i;
hermon_cq_sched_t *tmp;
tmp = state->hs_cq_sched_array;
for (i = 0; i < state->hs_cq_sched_array_size; i++)
if (cq_schedp == &tmp[i])
break;
if (i >= state->hs_cq_sched_array_size) {
cmn_err(CE_CONT, "!Invalid cq_sched argument: "
"ignored\n");
cq_schedp = &state->hs_cq_sched_default;
}
}
cq->cq_eqnum = HERMON_HID_TO_EQNUM(state,
HERMON_CQSCHED_NEXT_HID(cq_schedp));
IBTF_DPRINTF_L2("cqalloc", "sched: first-1 %d, len %d, "
"eqn %d", cq_schedp->cqs_start_hid - 1,
cq_schedp->cqs_len, cq->cq_eqnum);
}
bzero(&cqc_entry, sizeof (hermon_hw_cqc_t));
cqc_entry.state = HERMON_CQ_DISARMED;
cqc_entry.pg_offs = cq->cq_cqinfo.qa_pgoffs >> 5;
cqc_entry.log_cq_sz = log_cq_size;
cqc_entry.usr_page = uarpg;
cqc_entry.c_eqn = cq->cq_eqnum;
cqc_entry.log2_pgsz = mr->mr_log2_pgsz;
cqc_entry.mtt_base_addh = (uint32_t)((mr->mr_mttaddr >> 32) & 0xFF);
cqc_entry.mtt_base_addl = mr->mr_mttaddr >> 3;
cqc_entry.dbr_addrh = (uint32_t)((uint64_t)cq->cq_arm_ci_pdbr >> 32);
cqc_entry.dbr_addrl = (uint32_t)((uint64_t)cq->cq_arm_ci_pdbr >> 3);
status = hermon_cmn_ownership_cmd_post(state, SW2HW_CQ, &cqc_entry,
sizeof (hermon_hw_cqc_t), cq->cq_cqnum, sleepflag);
if (status != HERMON_CMD_SUCCESS) {
cmn_err(CE_CONT, "Hermon: SW2HW_CQ 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 cqalloc_fail6;
}
cq->cq_resize_hdl = 0;
cq->cq_cqcrsrcp = cqc;
cq->cq_rsrcp = rsrc;
cq->cq_consindx = 0;
cq->cq_buf = buf;
cq->cq_bufsz = (1 << log_cq_size);
cq->cq_log_cqsz = log_cq_size;
cq->cq_mrhdl = mr;
cq->cq_refcnt = 0;
cq->cq_is_special = 0;
cq->cq_uarpg = uarpg;
cq->cq_umap_dhp = (devmap_cookie_t)NULL;
avl_create(&cq->cq_wrid_wqhdr_avl_tree, hermon_wrid_workq_compare,
sizeof (struct hermon_workq_avl_s),
offsetof(struct hermon_workq_avl_s, wqa_link));
cq->cq_hdlrarg = (void *)ibt_cqhdl;
hermon_icm_set_num_to_hdl(state, HERMON_CQC, cqc->hr_indx, cq);
if (cq->cq_is_umap) {
hermon_umap_db_add(umapdb);
}
if (actual_size != NULL) {
*actual_size = (1 << log_cq_size) - 1;
}
*cqhdl = cq;
return (DDI_SUCCESS);
cqalloc_fail6:
if (hermon_mr_deregister(state, &mr, HERMON_MR_DEREG_ALL,
sleepflag) != DDI_SUCCESS) {
HERMON_WARNING(state, "failed to deregister CQ memory");
}
cqalloc_fail5:
hermon_queue_free(&cq->cq_cqinfo);
cqalloc_fail4a:
hermon_dbr_free(state, uarpg, cq->cq_arm_ci_vdbr);
cqalloc_fail4:
if (cq_is_umap) {
hermon_umap_db_free(umapdb);
}
cqalloc_fail3:
hermon_rsrc_free(state, &rsrc);
cqalloc_fail2:
hermon_rsrc_free(state, &cqc);
cqalloc_fail1:
hermon_pd_refcnt_dec(pd);
cqalloc_fail:
return (status);
}
int
hermon_cq_free(hermon_state_t *state, hermon_cqhdl_t *cqhdl, uint_t sleepflag)
{
hermon_rsrc_t *cqc, *rsrc;
hermon_umap_db_entry_t *umapdb;
hermon_hw_cqc_t cqc_entry;
hermon_pdhdl_t pd;
hermon_mrhdl_t mr;
hermon_cqhdl_t cq, resize;
uint32_t cqnum;
uint64_t value;
uint_t maxprot;
int status;
cq = *cqhdl;
mutex_enter(&cq->cq_lock);
cqc = cq->cq_cqcrsrcp;
rsrc = cq->cq_rsrcp;
pd = state->hs_pdhdl_internal;
mr = cq->cq_mrhdl;
cqnum = cq->cq_cqnum;
resize = cq->cq_resize_hdl;
if (cq->cq_refcnt != 0) {
mutex_exit(&cq->cq_lock);
return (IBT_CQ_BUSY);
}
if (cq->cq_is_umap) {
status = hermon_umap_db_find(state->hs_instance, cqnum,
MLNX_UMAP_CQMEM_RSRC, &value, HERMON_UMAP_DB_REMOVE,
&umapdb);
if (status != DDI_SUCCESS) {
mutex_exit(&cq->cq_lock);
HERMON_WARNING(state, "failed to find in database");
return (ibc_get_ci_failure(0));
}
hermon_umap_db_free(umapdb);
if (cq->cq_umap_dhp != NULL) {
maxprot = (PROT_READ | PROT_WRITE | PROT_USER);
status = devmap_devmem_remap(cq->cq_umap_dhp,
state->hs_dip, 0, 0, cq->cq_cqinfo.qa_size,
maxprot, DEVMAP_MAPPING_INVALID, NULL);
if (status != DDI_SUCCESS) {
mutex_exit(&cq->cq_lock);
HERMON_WARNING(state, "failed in CQ memory "
"devmap_devmem_remap()");
return (ibc_get_ci_failure(0));
}
cq->cq_umap_dhp = (devmap_cookie_t)NULL;
}
}
hermon_icm_set_num_to_hdl(state, HERMON_CQC, cqc->hr_indx, NULL);
mutex_exit(&cq->cq_lock);
_NOTE(NOW_INVISIBLE_TO_OTHER_THREADS(*cq))
status = hermon_cmn_ownership_cmd_post(state, HW2SW_CQ, &cqc_entry,
sizeof (hermon_hw_cqc_t), cqnum, sleepflag);
if (status != HERMON_CMD_SUCCESS) {
HERMON_WARNING(state, "failed to reclaim CQC ownership");
cmn_err(CE_CONT, "Hermon: HW2SW_CQ 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_mr_deregister(state, &mr, HERMON_MR_DEREG_ALL,
sleepflag);
if (status != DDI_SUCCESS) {
HERMON_WARNING(state, "failed to deregister CQ memory");
return (ibc_get_ci_failure(0));
}
if (resize) {
mr = resize->cq_mrhdl;
status = hermon_mr_deregister(state, &mr, HERMON_MR_DEREG_ALL,
sleepflag);
if (status != DDI_SUCCESS) {
HERMON_WARNING(state, "failed to deregister resize CQ "
"memory");
return (ibc_get_ci_failure(0));
}
}
hermon_queue_free(&cq->cq_cqinfo);
if (resize) {
hermon_queue_free(&resize->cq_cqinfo);
kmem_free(resize, sizeof (struct hermon_sw_cq_s));
}
hermon_dbr_free(state, cq->cq_uarpg, cq->cq_arm_ci_vdbr);
hermon_rsrc_free(state, &rsrc);
hermon_rsrc_free(state, &cqc);
hermon_pd_refcnt_dec(pd);
*cqhdl = NULL;
return (DDI_SUCCESS);
}
int
hermon_cq_resize(hermon_state_t *state, hermon_cqhdl_t cq, uint_t req_size,
uint_t *actual_size, uint_t sleepflag)
{
hermon_hw_cqc_t cqc_entry;
hermon_cqhdl_t resize_hdl;
hermon_qalloc_info_t new_cqinfo;
ibt_mr_attr_t mr_attr;
hermon_mr_options_t op;
hermon_pdhdl_t pd;
hermon_mrhdl_t mr;
hermon_hw_cqe_t *buf;
uint32_t new_prod_indx;
uint_t log_cq_size;
int status, flag;
if (cq->cq_resize_hdl != 0) {
status = IBT_CQ_BUSY;
goto cqresize_fail;
}
pd = state->hs_pdhdl_internal;
req_size = max(req_size, HERMON_CQ_MIN_SIZE);
log_cq_size = highbit(req_size);
if (log_cq_size > state->hs_cfg_profile->cp_log_max_cq_sz) {
status = IBT_HCA_CQ_EXCEEDED;
goto cqresize_fail;
}
resize_hdl = kmem_zalloc(sizeof (struct hermon_sw_cq_s), KM_SLEEP);
new_cqinfo.qa_size = (1 << log_cq_size) * sizeof (hermon_hw_cqe_t);
new_cqinfo.qa_alloc_align = PAGESIZE;
new_cqinfo.qa_bind_align = PAGESIZE;
if (cq->cq_is_umap) {
new_cqinfo.qa_location = HERMON_QUEUE_LOCATION_USERLAND;
} else {
new_cqinfo.qa_location = HERMON_QUEUE_LOCATION_NORMAL;
}
status = hermon_queue_alloc(state, &new_cqinfo, sleepflag);
if (status != DDI_SUCCESS) {
kmem_free(resize_hdl, sizeof (struct hermon_sw_cq_s));
status = IBT_INSUFF_RESOURCE;
goto cqresize_fail;
}
buf = (hermon_hw_cqe_t *)new_cqinfo.qa_buf_aligned;
_NOTE(NOW_INVISIBLE_TO_OTHER_THREADS(*buf))
flag = (sleepflag == HERMON_SLEEP) ? IBT_MR_SLEEP : IBT_MR_NOSLEEP;
mr_attr.mr_vaddr = (uint64_t)(uintptr_t)buf;
mr_attr.mr_len = new_cqinfo.qa_size;
mr_attr.mr_as = NULL;
mr_attr.mr_flags = flag | IBT_MR_ENABLE_LOCAL_WRITE;
op.mro_bind_type = state->hs_cfg_profile->cp_iommu_bypass;
op.mro_bind_dmahdl = new_cqinfo.qa_dmahdl;
op.mro_bind_override_addr = 0;
status = hermon_mr_register(state, pd, &mr_attr, &mr, &op,
HERMON_CQ_CMPT);
if (status != DDI_SUCCESS) {
hermon_queue_free(&new_cqinfo);
kmem_free(resize_hdl, sizeof (struct hermon_sw_cq_s));
status = IBT_INSUFF_RESOURCE;
goto cqresize_fail;
}
_NOTE(NOW_INVISIBLE_TO_OTHER_THREADS(*mr))
mutex_enter(&cq->cq_lock);
bzero(&cqc_entry, sizeof (hermon_hw_cqc_t));
cqc_entry.log_cq_sz = log_cq_size;
cqc_entry.pg_offs = new_cqinfo.qa_pgoffs >> 5;
cqc_entry.log2_pgsz = mr->mr_log2_pgsz;
cqc_entry.mtt_base_addh = (uint32_t)((mr->mr_mttaddr >> 32) & 0xFF);
cqc_entry.mtt_base_addl = mr->mr_mttaddr >> 3;
status = hermon_resize_cq_cmd_post(state, &cqc_entry, cq->cq_cqnum,
&new_prod_indx, HERMON_CMD_NOSLEEP_SPIN);
if (status != HERMON_CMD_SUCCESS) {
mutex_exit(&cq->cq_lock);
if (hermon_mr_deregister(state, &mr, HERMON_MR_DEREG_ALL,
sleepflag) != DDI_SUCCESS) {
HERMON_WARNING(state, "failed to deregister CQ memory");
}
kmem_free(resize_hdl, sizeof (struct hermon_sw_cq_s));
hermon_queue_free(&new_cqinfo);
if (status == HERMON_CMD_BAD_SIZE) {
return (IBT_CQ_SZ_INSUFFICIENT);
} else {
cmn_err(CE_CONT, "Hermon: RESIZE_CQ 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));
}
}
resize_hdl->cq_buf = buf;
resize_hdl->cq_bufsz = (1 << log_cq_size);
resize_hdl->cq_mrhdl = mr;
resize_hdl->cq_log_cqsz = log_cq_size;
bcopy(&new_cqinfo, &(resize_hdl->cq_cqinfo),
sizeof (struct hermon_qalloc_info_s));
cq->cq_resize_hdl = resize_hdl;
mutex_exit(&cq->cq_lock);
if (actual_size != NULL) {
*actual_size = (1 << log_cq_size) - 1;
}
return (DDI_SUCCESS);
cqresize_fail:
return (status);
}
int
hermon_cq_modify(hermon_state_t *state, hermon_cqhdl_t cq,
uint_t count, uint_t usec, ibt_cq_handler_id_t hid, uint_t sleepflag)
{
int status;
hermon_hw_cqc_t cqc_entry;
mutex_enter(&cq->cq_lock);
if (count != cq->cq_intmod_count ||
usec != cq->cq_intmod_usec) {
bzero(&cqc_entry, sizeof (hermon_hw_cqc_t));
cqc_entry.cq_max_cnt = count;
cqc_entry.cq_period = usec;
status = hermon_modify_cq_cmd_post(state, &cqc_entry,
cq->cq_cqnum, MODIFY_MODERATION_CQ, sleepflag);
if (status != HERMON_CMD_SUCCESS) {
mutex_exit(&cq->cq_lock);
cmn_err(CE_CONT, "Hermon: MODIFY_MODERATION_CQ "
"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));
}
cq->cq_intmod_count = count;
cq->cq_intmod_usec = usec;
}
if (hid && (hid - 1 != cq->cq_eqnum)) {
bzero(&cqc_entry, sizeof (hermon_hw_cqc_t));
cqc_entry.c_eqn = HERMON_HID_TO_EQNUM(state, hid);
status = hermon_modify_cq_cmd_post(state, &cqc_entry,
cq->cq_cqnum, MODIFY_EQN, sleepflag);
if (status != HERMON_CMD_SUCCESS) {
mutex_exit(&cq->cq_lock);
cmn_err(CE_CONT, "Hermon: MODIFY_EQN 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));
}
cq->cq_eqnum = hid - 1;
}
mutex_exit(&cq->cq_lock);
return (DDI_SUCCESS);
}
int
hermon_cq_notify(hermon_state_t *state, hermon_cqhdl_t cq,
ibt_cq_notify_flags_t flags)
{
uint_t cmd;
ibt_status_t status;
if (flags == IBT_NEXT_COMPLETION) {
cmd = HERMON_CQDB_NOTIFY_CQ;
} else if (flags == IBT_NEXT_SOLICITED) {
cmd = HERMON_CQDB_NOTIFY_CQ_SOLICIT;
} else {
return (IBT_CQ_NOTIFY_TYPE_INVALID);
}
status = hermon_cq_arm_doorbell(state, cq, cmd);
return (status);
}
int
hermon_cq_poll(hermon_state_t *state, hermon_cqhdl_t cq, ibt_wc_t *wc_p,
uint_t num_wc, uint_t *num_polled)
{
hermon_hw_cqe_t *cqe;
uint_t opcode;
uint32_t cons_indx, wrap_around_mask, shift, mask;
uint32_t polled_cnt, spec_op = 0;
int status;
if (cq->cq_is_umap) {
return (IBT_CQ_HDL_INVALID);
}
mutex_enter(&cq->cq_lock);
cons_indx = cq->cq_consindx;
shift = cq->cq_log_cqsz;
mask = cq->cq_bufsz;
wrap_around_mask = (cq->cq_bufsz - 1);
cqe = &cq->cq_buf[cons_indx & wrap_around_mask];
polled_cnt = 0;
while (HERMON_CQE_OWNER_IS_SW(cq, cqe, cons_indx, shift, mask)) {
if (cq->cq_resize_hdl != 0) {
opcode = HERMON_CQE_OPCODE_GET(cq, cqe);
if (opcode == HERMON_CQE_RCV_RESIZE_CODE) {
hermon_cq_resize_helper(state, cq);
cons_indx = (cons_indx + 1);
spec_op = 1;
wrap_around_mask = (cq->cq_bufsz - 1);
cqe = &cq->cq_buf[cons_indx & wrap_around_mask];
continue;
}
}
hermon_cq_cqe_consume(state, cq, cqe, &wc_p[polled_cnt++]);
cons_indx = (cons_indx + 1);
cqe = &cq->cq_buf[cons_indx & wrap_around_mask];
if (polled_cnt >= num_wc) {
break;
}
}
if ((polled_cnt != 0) && (cq->cq_consindx != cons_indx)) {
cq->cq_consindx = cons_indx;
hermon_cq_update_ci_doorbell(cq);
} else if (polled_cnt == 0) {
if (spec_op != 0) {
cq->cq_consindx = cons_indx;
hermon_cq_update_ci_doorbell(cq);
}
}
mutex_exit(&cq->cq_lock);
if (num_polled != NULL) {
*num_polled = polled_cnt;
}
if (polled_cnt == 0) {
status = IBT_CQ_EMPTY;
} else {
status = DDI_SUCCESS;
}
if (ddi_in_panic() != 0) {
(void) hermon_isr((caddr_t)state, (caddr_t)NULL);
}
return (status);
}
static void
hermon_arm_cq_dbr_init(hermon_dbr_t *cq_arm_dbr)
{
uint32_t *target;
target = (uint32_t *)cq_arm_dbr + 1;
*target = htonl(1 << HERMON_CQDB_CMDSN_SHIFT);
}
static void
hermon_arm_cq_dbr_update(hermon_dbr_t *cq_arm_dbr)
{
uint32_t tmp, cmp, new;
uint32_t old_cmd_sn, new_cmd_sn;
uint32_t *target;
int retries = 0;
target = (uint32_t *)cq_arm_dbr + 1;
retry:
cmp = *target;
tmp = htonl(cmp);
old_cmd_sn = tmp & (0x3 << HERMON_CQDB_CMDSN_SHIFT);
new_cmd_sn = (old_cmd_sn + (0x1 << HERMON_CQDB_CMDSN_SHIFT)) &
(0x3 << HERMON_CQDB_CMDSN_SHIFT);
new = htonl((tmp & ~(0x37 << HERMON_CQDB_CMD_SHIFT)) | new_cmd_sn);
tmp = atomic_cas_32(target, cmp, new);
if (tmp != cmp) {
drv_usecwait(retries & 0xff);
if (++retries > 100000) {
cmn_err(CE_CONT, "cas failed in hermon\n");
retries = 0;
}
goto retry;
}
}
int
hermon_cq_handler(hermon_state_t *state, hermon_eqhdl_t eq,
hermon_hw_eqe_t *eqe)
{
hermon_cqhdl_t cq;
uint_t cqnum;
cqnum = HERMON_EQE_CQNUM_GET(eq, eqe);
cq = hermon_cqhdl_from_cqnum(state, cqnum);
if ((cq != NULL) && (cq->cq_cqnum == cqnum) &&
(state->hs_ibtfpriv != NULL)) {
hermon_arm_cq_dbr_update(cq->cq_arm_ci_vdbr);
HERMON_DO_IBTF_CQ_CALLB(state, cq);
}
return (DDI_SUCCESS);
}
int
hermon_cq_err_handler(hermon_state_t *state, hermon_eqhdl_t eq,
hermon_hw_eqe_t *eqe)
{
hermon_cqhdl_t cq;
uint_t cqnum;
ibc_async_event_t event;
ibt_async_code_t type;
HERMON_FMANOTE(state, HERMON_FMA_OVERRUN);
cqnum = HERMON_EQE_CQNUM_GET(eq, eqe);
cq = hermon_cqhdl_from_cqnum(state, cqnum);
if ((cq != NULL) && (cq->cq_cqnum == cqnum) &&
(state->hs_ibtfpriv != NULL)) {
event.ev_cq_hdl = (ibt_cq_hdl_t)cq->cq_hdlrarg;
type = IBT_ERROR_CQ;
HERMON_DO_IBTF_ASYNC_CALLB(state, type, &event);
}
return (DDI_SUCCESS);
}
int
hermon_cq_refcnt_inc(hermon_cqhdl_t cq, uint_t is_special)
{
mutex_enter(&cq->cq_lock);
if (cq->cq_refcnt == 0) {
cq->cq_is_special = is_special;
} else {
if (cq->cq_is_special != is_special) {
mutex_exit(&cq->cq_lock);
return (DDI_FAILURE);
}
}
cq->cq_refcnt++;
mutex_exit(&cq->cq_lock);
return (DDI_SUCCESS);
}
void
hermon_cq_refcnt_dec(hermon_cqhdl_t cq)
{
mutex_enter(&cq->cq_lock);
cq->cq_refcnt--;
mutex_exit(&cq->cq_lock);
}
static int
hermon_cq_arm_doorbell(hermon_state_t *state, hermon_cqhdl_t cq, uint_t cq_cmd)
{
uint32_t cq_num;
uint32_t *target;
uint32_t old_cmd, cmp, new, tmp, cmd_sn;
ddi_acc_handle_t uarhdl = hermon_get_uarhdl(state);
hermon_pio_init(fm_loop_cnt, fm_status, fm_test_num);
cq_num = cq->cq_cqnum;
target = (uint32_t *)cq->cq_arm_ci_vdbr + 1;
hermon_pio_start(state, uarhdl, pio_error, fm_loop_cnt, fm_status,
fm_test_num);
retry:
cmp = *target;
tmp = htonl(cmp);
old_cmd = tmp & (0x7 << HERMON_CQDB_CMD_SHIFT);
cmd_sn = tmp & (0x3 << HERMON_CQDB_CMDSN_SHIFT);
if (cq_cmd == HERMON_CQDB_NOTIFY_CQ) {
if (old_cmd != HERMON_CQDB_NOTIFY_CQ) {
cmd_sn |= (HERMON_CQDB_NOTIFY_CQ <<
HERMON_CQDB_CMD_SHIFT);
new = htonl(cmd_sn | (cq->cq_consindx & 0xFFFFFF));
tmp = atomic_cas_32(target, cmp, new);
if (tmp != cmp)
goto retry;
HERMON_UAR_DOORBELL(state, uarhdl, (uint64_t *)(void *)
&state->hs_uar->cq, (((uint64_t)cmd_sn | cq_num) <<
32) | (cq->cq_consindx & 0xFFFFFF));
}
} else {
ASSERT(cq_cmd == HERMON_CQDB_NOTIFY_CQ_SOLICIT);
if (old_cmd != HERMON_CQDB_NOTIFY_CQ &&
old_cmd != HERMON_CQDB_NOTIFY_CQ_SOLICIT) {
cmd_sn |= (HERMON_CQDB_NOTIFY_CQ_SOLICIT <<
HERMON_CQDB_CMD_SHIFT);
new = htonl(cmd_sn | (cq->cq_consindx & 0xFFFFFF));
tmp = atomic_cas_32(target, cmp, new);
if (tmp != cmp)
goto retry;
HERMON_UAR_DOORBELL(state, uarhdl, (uint64_t *)(void *)
&state->hs_uar->cq, (((uint64_t)cmd_sn | cq_num) <<
32) | (cq->cq_consindx & 0xFFFFFF));
}
}
hermon_pio_end(state, uarhdl, pio_error, fm_loop_cnt, fm_status,
fm_test_num);
return (IBT_SUCCESS);
pio_error:
hermon_fm_ereport(state, HCA_SYS_ERR, HCA_ERR_SRV_LOST);
return (ibc_get_ci_failure(0));
}
hermon_cqhdl_t
hermon_cqhdl_from_cqnum(hermon_state_t *state, uint_t cqnum)
{
uint_t cqindx, cqmask;
cqmask = (1 << state->hs_cfg_profile->cp_log_num_cq) - 1;
cqindx = cqnum & cqmask;
return (hermon_icm_num_to_hdl(state, HERMON_CQC, cqindx));
}
static void
hermon_cq_cqe_consume(hermon_state_t *state, hermon_cqhdl_t cq,
hermon_hw_cqe_t *cqe, ibt_wc_t *wc)
{
uint_t opcode, qpnum, qp1_indx;
ibt_wc_flags_t flags;
ibt_wrc_opcode_t type;
opcode = HERMON_CQE_OPCODE_GET(cq, cqe);
if ((opcode == HERMON_CQE_SEND_ERR_OPCODE) ||
(opcode == HERMON_CQE_RECV_ERR_OPCODE)) {
hermon_cq_errcqe_consume(state, cq, cqe, wc);
return;
}
wc->wc_id = hermon_wrid_get_entry(cq, cqe);
flags = IBT_WC_NO_FLAGS;
_NOTE(NOW_INVISIBLE_TO_OTHER_THREADS(state->hs_fcoib_may_be_running))
if (HERMON_CQE_SENDRECV_GET(cq, cqe) != HERMON_COMPLETION_RECV) {
switch (opcode) {
case HERMON_CQE_SND_RDMAWR_IMM:
case HERMON_CQE_SND_RDMAWR:
type = IBT_WRC_RDMAW;
break;
case HERMON_CQE_SND_SEND_INV:
case HERMON_CQE_SND_SEND_IMM:
case HERMON_CQE_SND_SEND:
type = IBT_WRC_SEND;
break;
case HERMON_CQE_SND_LSO:
type = IBT_WRC_SEND_LSO;
break;
case HERMON_CQE_SND_RDMARD:
type = IBT_WRC_RDMAR;
break;
case HERMON_CQE_SND_ATOMIC_CS:
type = IBT_WRC_CSWAP;
break;
case HERMON_CQE_SND_ATOMIC_FA:
type = IBT_WRC_FADD;
break;
case HERMON_CQE_SND_BIND_MW:
type = IBT_WRC_BIND;
break;
case HERMON_CQE_SND_FRWR:
type = IBT_WRC_FAST_REG_PMR;
break;
case HERMON_CQE_SND_LCL_INV:
type = IBT_WRC_LOCAL_INVALIDATE;
break;
default:
HERMON_WARNING(state, "unknown send CQE type");
wc->wc_status = IBT_WC_LOCAL_QP_OP_ERR;
return;
}
} else if ((state->hs_fcoib_may_be_running == B_TRUE) &&
hermon_fcoib_is_fexch_qpn(state, HERMON_CQE_QPNUM_GET(cq, cqe))) {
type = IBT_WRC_RECV;
if (HERMON_CQE_FEXCH_DIFE(cq, cqe))
flags |= IBT_WC_DIF_ERROR;
wc->wc_bytes_xfer = HERMON_CQE_BYTECNT_GET(cq, cqe);
wc->wc_fexch_seq_cnt = HERMON_CQE_FEXCH_SEQ_CNT(cq, cqe);
wc->wc_fexch_tx_bytes_xfer = HERMON_CQE_FEXCH_TX_BYTES(cq, cqe);
wc->wc_fexch_rx_bytes_xfer = HERMON_CQE_FEXCH_RX_BYTES(cq, cqe);
wc->wc_fexch_seq_id = HERMON_CQE_FEXCH_SEQ_ID(cq, cqe);
wc->wc_detail = HERMON_CQE_FEXCH_DETAIL(cq, cqe) &
IBT_WC_DETAIL_FC_MATCH_MASK;
wc->wc_rkey = HERMON_CQE_IMM_ETH_PKEY_CRED_GET(cq, cqe);
flags |= IBT_WC_FEXCH_FMT | IBT_WC_RKEY_INVALIDATED;
} else {
wc->wc_sl = HERMON_CQE_SL_GET(cq, cqe);
wc->wc_qpn = HERMON_CQE_DQPN_GET(cq, cqe);
wc->wc_slid = HERMON_CQE_DLID_GET(cq, cqe);
wc->wc_immed_data =
HERMON_CQE_IMM_ETH_PKEY_CRED_GET(cq, cqe);
wc->wc_ethertype = (wc->wc_immed_data & 0xFFFF);
wc->wc_pkey_ix = (wc->wc_immed_data &
((1 << state->hs_queryport.log_max_pkey) - 1));
wc->wc_path_bits = HERMON_CQE_PATHBITS_GET(cq, cqe);
wc->wc_bytes_xfer = HERMON_CQE_BYTECNT_GET(cq, cqe);
if (HERMON_CQE_GRH_GET(cq, cqe) != 0) {
flags |= IBT_WC_GRH_PRESENT;
}
switch (opcode) {
case HERMON_CQE_RCV_SEND_IMM:
qpnum = HERMON_CQE_QPNUM_GET(cq, cqe);
qp1_indx = state->hs_spec_qp1->hr_indx;
if ((qpnum < qp1_indx) || (qpnum > qp1_indx + 1)) {
flags |= IBT_WC_IMMED_DATA_PRESENT;
}
case HERMON_CQE_RCV_SEND:
type = IBT_WRC_RECV;
if (HERMON_CQE_IS_IPOK(cq, cqe)) {
wc->wc_cksum = HERMON_CQE_CKSUM(cq, cqe);
flags |= IBT_WC_CKSUM_OK;
wc->wc_detail = IBT_WC_DETAIL_ALL_FLAGS_MASK &
HERMON_CQE_IPOIB_STATUS(cq, cqe);
}
break;
case HERMON_CQE_RCV_SEND_INV:
type = IBT_WRC_RECV;
flags |= IBT_WC_RKEY_INVALIDATED;
wc->wc_rkey = wc->wc_immed_data;
break;
case HERMON_CQE_RCV_RDMAWR_IMM:
flags |= IBT_WC_IMMED_DATA_PRESENT;
type = IBT_WRC_RECV_RDMAWI;
break;
default:
HERMON_WARNING(state, "unknown recv CQE type");
wc->wc_status = IBT_WC_LOCAL_QP_OP_ERR;
return;
}
}
wc->wc_type = type;
wc->wc_flags = flags;
wc->wc_status = IBT_WC_SUCCESS;
}
static void
hermon_cq_errcqe_consume(hermon_state_t *state, hermon_cqhdl_t cq,
hermon_hw_cqe_t *cqe, ibt_wc_t *wc)
{
uint32_t imm_eth_pkey_cred;
uint_t status;
ibt_wc_status_t ibt_status;
wc->wc_id = hermon_wrid_get_entry(cq, cqe);
imm_eth_pkey_cred = HERMON_CQE_ERROR_SYNDROME_GET(cq, cqe);
status = imm_eth_pkey_cred;
if (status != HERMON_CQE_WR_FLUSHED_ERR)
IBTF_DPRINTF_L2("CQE ERR", "cqe %p QPN %x indx %x status 0x%x "
"vendor syndrome %x", cqe, HERMON_CQE_QPNUM_GET(cq, cqe),
HERMON_CQE_WQECNTR_GET(cq, cqe), status,
HERMON_CQE_ERROR_VENDOR_SYNDROME_GET(cq, cqe));
switch (status) {
case HERMON_CQE_LOC_LEN_ERR:
HERMON_WARNING(state, HERMON_FMA_LOCLEN);
ibt_status = IBT_WC_LOCAL_LEN_ERR;
break;
case HERMON_CQE_LOC_OP_ERR:
HERMON_WARNING(state, HERMON_FMA_LOCQPOP);
ibt_status = IBT_WC_LOCAL_QP_OP_ERR;
break;
case HERMON_CQE_LOC_PROT_ERR:
HERMON_WARNING(state, HERMON_FMA_LOCPROT);
ibt_status = IBT_WC_LOCAL_PROTECT_ERR;
IBTF_DPRINTF_L2("ERRCQE", "is at %p", cqe);
if (hermon_should_panic) {
cmn_err(CE_PANIC, "Hermon intentional PANIC - "
"Local Protection Error\n");
}
break;
case HERMON_CQE_WR_FLUSHED_ERR:
ibt_status = IBT_WC_WR_FLUSHED_ERR;
break;
case HERMON_CQE_MW_BIND_ERR:
HERMON_WARNING(state, HERMON_FMA_MWBIND);
ibt_status = IBT_WC_MEM_WIN_BIND_ERR;
break;
case HERMON_CQE_BAD_RESPONSE_ERR:
HERMON_WARNING(state, HERMON_FMA_RESP);
ibt_status = IBT_WC_BAD_RESPONSE_ERR;
break;
case HERMON_CQE_LOCAL_ACCESS_ERR:
HERMON_WARNING(state, HERMON_FMA_LOCACC);
ibt_status = IBT_WC_LOCAL_ACCESS_ERR;
break;
case HERMON_CQE_REM_INV_REQ_ERR:
HERMON_WARNING(state, HERMON_FMA_REMREQ);
ibt_status = IBT_WC_REMOTE_INVALID_REQ_ERR;
break;
case HERMON_CQE_REM_ACC_ERR:
HERMON_WARNING(state, HERMON_FMA_REMACC);
ibt_status = IBT_WC_REMOTE_ACCESS_ERR;
break;
case HERMON_CQE_REM_OP_ERR:
HERMON_WARNING(state, HERMON_FMA_REMOP);
ibt_status = IBT_WC_REMOTE_OP_ERR;
break;
case HERMON_CQE_TRANS_TO_ERR:
HERMON_WARNING(state, HERMON_FMA_XPORTCNT);
ibt_status = IBT_WC_TRANS_TIMEOUT_ERR;
break;
case HERMON_CQE_RNRNAK_TO_ERR:
HERMON_WARNING(state, HERMON_FMA_RNRCNT);
ibt_status = IBT_WC_RNR_NAK_TIMEOUT_ERR;
break;
default:
HERMON_WARNING(state, "unknown error CQE status");
HERMON_FMANOTE(state, HERMON_FMA_UNKN);
ibt_status = IBT_WC_LOCAL_QP_OP_ERR;
break;
}
wc->wc_status = ibt_status;
}
void
hermon_cq_resize_helper(hermon_state_t *state, hermon_cqhdl_t cq)
{
hermon_cqhdl_t resize_hdl;
int status;
ASSERT(MUTEX_HELD(&cq->cq_lock));
resize_hdl = cq->cq_resize_hdl;
status = hermon_mr_deregister(state, &cq->cq_mrhdl, HERMON_MR_DEREG_ALL,
HERMON_SLEEP);
if (status != DDI_SUCCESS) {
HERMON_WARNING(state, "failed to deregister old CQ memory");
}
hermon_queue_free(&cq->cq_cqinfo);
cq->cq_buf = resize_hdl->cq_buf;
cq->cq_mrhdl = resize_hdl->cq_mrhdl;
cq->cq_bufsz = resize_hdl->cq_bufsz;
cq->cq_log_cqsz = resize_hdl->cq_log_cqsz;
cq->cq_umap_dhp = cq->cq_resize_hdl->cq_umap_dhp;
cq->cq_resize_hdl = 0;
bcopy(&resize_hdl->cq_cqinfo, &cq->cq_cqinfo,
sizeof (struct hermon_qalloc_info_s));
kmem_free(resize_hdl, sizeof (struct hermon_sw_cq_s));
}
void
hermon_cq_entries_flush(hermon_state_t *state, hermon_qphdl_t qp)
{
hermon_cqhdl_t cq;
hermon_hw_cqe_t *cqe, *next_cqe;
hermon_srqhdl_t srq;
hermon_workq_hdr_t *wq;
uint32_t cons_indx, tail_cons_indx, wrap_around_mask;
uint32_t new_indx, check_indx, qpnum;
uint32_t shift, mask;
int outstanding_cqes;
qpnum = qp->qp_qpnum;
if ((srq = qp->qp_srqhdl) != NULL)
wq = qp->qp_srqhdl->srq_wq_wqhdr;
else
wq = NULL;
cq = qp->qp_rq_cqhdl;
if (cq == NULL) {
cq = qp->qp_sq_cqhdl;
}
do_send_cq:
if (cq == NULL)
return;
cons_indx = cq->cq_consindx;
shift = cq->cq_log_cqsz;
mask = cq->cq_bufsz;
wrap_around_mask = mask - 1;
cqe = &cq->cq_buf[cons_indx & wrap_around_mask];
outstanding_cqes = 0;
tail_cons_indx = cons_indx;
while (HERMON_CQE_OWNER_IS_SW(cq, cqe, tail_cons_indx, shift, mask)) {
outstanding_cqes++;
tail_cons_indx++;
cqe = &cq->cq_buf[tail_cons_indx & wrap_around_mask];
}
check_indx = new_indx = (tail_cons_indx - 1);
while (--outstanding_cqes >= 0) {
cqe = &cq->cq_buf[check_indx & wrap_around_mask];
if (qpnum == HERMON_CQE_QPNUM_GET(cq, cqe)) {
if (srq && (HERMON_CQE_SENDRECV_GET(cq, cqe) ==
HERMON_COMPLETION_RECV)) {
uint64_t *desc;
int indx;
indx = HERMON_CQE_WQEADDRSZ_GET(cq, cqe) &
wq->wq_mask;
desc = HERMON_SRQ_WQE_ADDR(srq, wq->wq_tail);
((uint16_t *)desc)[1] = htons(indx);
wq->wq_tail = indx;
}
} else {
if (check_indx != new_indx) {
next_cqe =
&cq->cq_buf[new_indx & wrap_around_mask];
bcopy(cqe, next_cqe, sizeof (hermon_hw_cqe_t));
}
new_indx--;
}
check_indx--;
}
cons_indx = (new_indx + 1);
if (cq->cq_consindx != cons_indx) {
cq->cq_consindx = cons_indx;
hermon_cq_update_ci_doorbell(cq);
}
if (cq != qp->qp_sq_cqhdl) {
cq = qp->qp_sq_cqhdl;
goto do_send_cq;
}
}
static int
hermon_get_cq_sched_list(hermon_state_t *state)
{
char **listp, ulp_prop[HERMON_CQH_MAX + 4];
uint_t nlist, i, j, ndata;
int *data;
size_t len;
hermon_cq_sched_t *cq_schedp;
if (ddi_prop_lookup_string_array(DDI_DEV_T_ANY, state->hs_dip,
DDI_PROP_DONTPASS, "cqh-group-list", &listp, &nlist) !=
DDI_PROP_SUCCESS)
return (0);
state->hs_cq_sched_array_size = nlist;
state->hs_cq_sched_array = cq_schedp = kmem_zalloc(nlist *
sizeof (hermon_cq_sched_t), KM_SLEEP);
for (i = 0; i < nlist; i++) {
if ((len = strlen(listp[i])) >= HERMON_CQH_MAX) {
cmn_err(CE_CONT, "'cqh' property name too long\n");
goto game_over;
}
for (j = 0; j < i; j++) {
if (strcmp(listp[j], listp[i]) == 0) {
cmn_err(CE_CONT, "Duplicate 'cqh' property\n");
goto game_over;
}
}
(void) strncpy(cq_schedp[i].cqs_name, listp[i], HERMON_CQH_MAX);
ulp_prop[0] = 'c';
ulp_prop[1] = 'q';
ulp_prop[2] = 'h';
ulp_prop[3] = '-';
(void) strncpy(ulp_prop + 4, listp[i], len + 1);
if (ddi_prop_lookup_int_array(DDI_DEV_T_ANY, state->hs_dip,
DDI_PROP_DONTPASS, ulp_prop, &data, &ndata) !=
DDI_PROP_SUCCESS) {
cmn_err(CE_CONT, "property '%s' not found\n", ulp_prop);
goto game_over;
}
if (ndata != 2) {
cmn_err(CE_CONT, "property '%s' does not "
"have 2 integers\n", ulp_prop);
goto game_over_free_data;
}
cq_schedp[i].cqs_desired = data[0];
cq_schedp[i].cqs_minimum = data[1];
cq_schedp[i].cqs_refcnt = 0;
ddi_prop_free(data);
}
if (ddi_prop_lookup_int_array(DDI_DEV_T_ANY, state->hs_dip,
DDI_PROP_DONTPASS, "cqh-default", &data, &ndata) !=
DDI_PROP_SUCCESS) {
cmn_err(CE_CONT, "property 'cqh-default' not found\n");
goto game_over;
}
if (ndata != 2) {
cmn_err(CE_CONT, "property 'cqh-default' does not "
"have 2 integers\n");
goto game_over_free_data;
}
cq_schedp = &state->hs_cq_sched_default;
cq_schedp->cqs_desired = data[0];
cq_schedp->cqs_minimum = data[1];
cq_schedp->cqs_refcnt = 0;
ddi_prop_free(data);
ddi_prop_free(listp);
return (1);
game_over_free_data:
ddi_prop_free(data);
game_over:
cmn_err(CE_CONT, "Error in 'cqh' properties in hermon.conf\n");
cmn_err(CE_CONT, "completion handler groups not being used\n");
kmem_free(cq_schedp, nlist * sizeof (hermon_cq_sched_t));
state->hs_cq_sched_array_size = 0;
ddi_prop_free(listp);
return (0);
}
int
hermon_cq_sched_init(hermon_state_t *state)
{
hermon_cq_sched_t *cq_schedp, *defp;
int i, desired, array_size;
mutex_init(&state->hs_cq_sched_lock, NULL, MUTEX_DRIVER,
DDI_INTR_PRI(state->hs_intrmsi_pri));
mutex_enter(&state->hs_cq_sched_lock);
state->hs_cq_sched_array = NULL;
defp = &state->hs_cq_sched_default;
defp->cqs_start_hid = 1;
defp->cqs_len = state->hs_intrmsi_allocd;
defp->cqs_next_alloc = defp->cqs_len - 1;
(void) strncpy(defp->cqs_name, "default", 8);
if (hermon_get_cq_sched_list(state) == 0)
goto done;
desired = defp->cqs_desired;
if (desired <= 0)
goto done;
cq_schedp = state->hs_cq_sched_array;
array_size = state->hs_cq_sched_array_size;
for (i = 0; i < array_size; i++)
desired += cq_schedp[i].cqs_desired;
if (desired > state->hs_intrmsi_allocd) {
cmn_err(CE_CONT, "#interrupts allocated (%d) is less than "
"the #interrupts desired (%d)\n",
state->hs_intrmsi_allocd, desired);
cmn_err(CE_CONT, "completion handler groups not being used\n");
goto done;
}
for (i = 0; i < array_size; i++) {
desired = cq_schedp[i].cqs_desired;
cq_schedp[i].cqs_start_hid = defp->cqs_start_hid;
cq_schedp[i].cqs_len = desired;
cq_schedp[i].cqs_next_alloc = desired - 1;
defp->cqs_len -= desired;
defp->cqs_start_hid += desired;
}
state->hs_cq_sched_default.cqs_next_alloc =
state->hs_cq_sched_default.cqs_len - 1;
done:
mutex_exit(&state->hs_cq_sched_lock);
return (IBT_SUCCESS);
}
void
hermon_cq_sched_fini(hermon_state_t *state)
{
mutex_enter(&state->hs_cq_sched_lock);
if (state->hs_cq_sched_array_size) {
kmem_free(state->hs_cq_sched_array, sizeof (hermon_cq_sched_t) *
state->hs_cq_sched_array_size);
state->hs_cq_sched_array_size = 0;
state->hs_cq_sched_array = NULL;
}
mutex_exit(&state->hs_cq_sched_lock);
mutex_destroy(&state->hs_cq_sched_lock);
}
int
hermon_cq_sched_alloc(hermon_state_t *state, ibt_cq_sched_attr_t *attr,
hermon_cq_sched_t **cq_sched_pp)
{
hermon_cq_sched_t *cq_schedp;
int i;
char *name;
ibt_cq_sched_flags_t flags;
flags = attr->cqs_flags;
if ((flags & (IBT_CQS_SCHED_GROUP | IBT_CQS_EXACT_SCHED_GROUP)) == 0) {
*cq_sched_pp = NULL;
return (IBT_SUCCESS);
}
name = attr->cqs_pool_name;
mutex_enter(&state->hs_cq_sched_lock);
cq_schedp = state->hs_cq_sched_array;
for (i = 0; i < state->hs_cq_sched_array_size; i++, cq_schedp++) {
if (strcmp(name, cq_schedp->cqs_name) == 0) {
if (cq_schedp->cqs_len != 0)
cq_schedp->cqs_refcnt++;
break;
}
}
if ((i == state->hs_cq_sched_array_size) ||
(cq_schedp->cqs_len == 0))
cq_schedp = NULL;
mutex_exit(&state->hs_cq_sched_lock);
*cq_sched_pp = cq_schedp;
if ((cq_schedp == NULL) &&
(attr->cqs_flags & IBT_CQS_EXACT_SCHED_GROUP))
return (IBT_CQ_NO_SCHED_GROUP);
else
return (IBT_SUCCESS);
}
int
hermon_cq_sched_free(hermon_state_t *state, hermon_cq_sched_t *cq_schedp)
{
if (cq_schedp != NULL) {
mutex_enter(&state->hs_cq_sched_lock);
if (cq_schedp->cqs_refcnt == 0)
HERMON_WARNING(state, "cq_sched free underflow\n");
else
cq_schedp->cqs_refcnt--;
mutex_exit(&state->hs_cq_sched_lock);
}
return (IBT_SUCCESS);
}