#include <sys/types.h>
#include <sys/conf.h>
#include <sys/ddi.h>
#include <sys/sunddi.h>
#include <sys/modctl.h>
#include <sys/ib/adapters/hermon/hermon.h>
#include <sys/ib/mgt/ibmf/ibmf.h>
#include <sys/disp.h>
static void hermon_agent_request_cb(ibmf_handle_t ibmf_handle,
ibmf_msg_t *msgp, void *args);
static void hermon_agent_handle_req(void *cb_args);
static void hermon_agent_response_cb(ibmf_handle_t ibmf_handle,
ibmf_msg_t *msgp, void *args);
static int hermon_agent_list_init(hermon_state_t *state);
static void hermon_agent_list_fini(hermon_state_t *state);
static int hermon_agent_register_all(hermon_state_t *state);
static int hermon_agent_unregister_all(hermon_state_t *state, int num_reg);
static void hermon_agent_mad_resp_handling(hermon_state_t *state,
ibmf_msg_t *msgp, uint_t port);
int
hermon_agent_handlers_init(hermon_state_t *state)
{
int status;
char *rsrc_name;
if ((state->hs_cfg_profile->cp_qp0_agents_in_fw) &&
(state->hs_cfg_profile->cp_qp1_agents_in_fw)) {
return (DDI_SUCCESS);
}
rsrc_name = (char *)kmem_zalloc(HERMON_RSRC_NAME_MAXLEN, KM_SLEEP);
HERMON_RSRC_NAME(rsrc_name, HERMON_TASKQ_NAME);
status = hermon_agent_list_init(state);
if (status != DDI_SUCCESS) {
goto agentsinit_fail;
}
state->hs_taskq_agents = ddi_taskq_create(state->hs_dip,
rsrc_name, HERMON_TASKQ_NTHREADS, TASKQ_DEFAULTPRI, 0);
if (state->hs_taskq_agents == NULL) {
hermon_agent_list_fini(state);
goto agentsinit_fail;
}
status = hermon_agent_register_all(state);
if (status != DDI_SUCCESS) {
ddi_taskq_destroy(state->hs_taskq_agents);
hermon_agent_list_fini(state);
goto agentsinit_fail;
}
kmem_free(rsrc_name, HERMON_RSRC_NAME_MAXLEN);
return (DDI_SUCCESS);
agentsinit_fail:
kmem_free(rsrc_name, HERMON_RSRC_NAME_MAXLEN);
return (status);
}
int
hermon_agent_handlers_fini(hermon_state_t *state)
{
int status;
if ((state->hs_cfg_profile->cp_qp0_agents_in_fw) &&
(state->hs_cfg_profile->cp_qp1_agents_in_fw)) {
return (DDI_SUCCESS);
}
status = hermon_agent_unregister_all(state, state->hs_num_agents);
if (status != DDI_SUCCESS) {
return (DDI_FAILURE);
}
ddi_taskq_destroy(state->hs_taskq_agents);
hermon_agent_list_fini(state);
return (DDI_SUCCESS);
}
static void
hermon_agent_request_cb(ibmf_handle_t ibmf_handle, ibmf_msg_t *msgp,
void *args)
{
hermon_agent_handler_arg_t *cb_args;
hermon_agent_list_t *curr;
hermon_state_t *state;
int status;
curr = (hermon_agent_list_t *)args;
state = curr->agl_state;
cb_args = (hermon_agent_handler_arg_t *)kmem_zalloc(
sizeof (hermon_agent_handler_arg_t), KM_NOSLEEP);
if (cb_args == NULL) {
(void) ibmf_free_msg(ibmf_handle, &msgp);
return;
}
_NOTE(NOW_INVISIBLE_TO_OTHER_THREADS(*cb_args))
cb_args->ahd_ibmfhdl = ibmf_handle;
cb_args->ahd_ibmfmsg = msgp;
cb_args->ahd_agentlist = args;
status = ddi_taskq_dispatch(state->hs_taskq_agents,
hermon_agent_handle_req, cb_args, DDI_NOSLEEP);
if (status == DDI_FAILURE) {
kmem_free(cb_args, sizeof (hermon_agent_handler_arg_t));
(void) ibmf_free_msg(ibmf_handle, &msgp);
}
}
static ib_lid_t
hermon_get_smlid(hermon_state_t *state, uint_t port)
{
sm_portinfo_t portinfo;
int status;
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);
return (0);
}
return (portinfo.MasterSMLID);
}
static ibt_port_change_t
hermon_port_change_flags(sm_portinfo_t *curpinfo, sm_portinfo_t *madpinfo)
{
int SMDisabled, ReregSuppd;
ibt_port_change_t flags = 0;
SMDisabled = curpinfo->CapabilityMask & SM_CAP_MASK_IS_SM_DISABLED;
ReregSuppd = curpinfo->CapabilityMask & SM_CAP_MASK_IS_CLNT_REREG_SUPPD;
if (curpinfo->MasterSMLID != madpinfo->MasterSMLID) {
flags |= IBT_PORT_CHANGE_SM_LID;
}
if (curpinfo->MasterSMSL != madpinfo->MasterSMSL) {
flags |= IBT_PORT_CHANGE_SM_SL;
}
if (curpinfo->SubnetTimeOut != madpinfo->SubnetTimeOut) {
flags |= IBT_PORT_CHANGE_SUB_TIMEOUT;
}
if ((madpinfo->CapabilityMask & SM_CAP_MASK_IS_SM_DISABLED)
^ SMDisabled) {
flags |= IBT_PORT_CHANGE_SM_FLAG;
}
if ((madpinfo->CapabilityMask & SM_CAP_MASK_IS_CLNT_REREG_SUPPD)
^ ReregSuppd) {
flags |= IBT_PORT_CHANGE_REREG;
}
return (flags);
}
int
hermon_set_port_capability(hermon_state_t *state, uint8_t port,
sm_portinfo_t *portinfo, ibt_port_change_t flags)
{
uint32_t capmask;
int status;
hermon_hw_set_port_t set_port;
bzero(&set_port, sizeof (set_port));
if (!hermon_portnum_is_valid(state, port)) {
return (IBT_HCA_PORT_INVALID);
}
capmask = portinfo->CapabilityMask;
if (flags & IBT_PORT_CHANGE_SM_FLAG)
capmask ^= SM_CAP_MASK_IS_SM;
if (flags & IBT_PORT_CHANGE_REREG)
capmask ^= SM_CAP_MASK_IS_CLNT_REREG_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);
return (DDI_FAILURE);
}
return (DDI_SUCCESS);
}
static void
hermon_agent_handle_req(void *cb_args)
{
hermon_agent_handler_arg_t *agent_args;
hermon_agent_list_t *curr;
ibc_async_event_t event;
ibt_async_code_t type, code;
sm_portinfo_t curpinfo, tmadpinfo;
sm_portinfo_t *madpinfop;
hermon_state_t *state;
ibmf_handle_t ibmf_handle;
ibmf_msg_t *msgp;
ibmf_msg_bufs_t *recv_msgbufp;
ibmf_msg_bufs_t *send_msgbufp;
ib_mad_hdr_t *madhdrp;
ibmf_retrans_t retrans;
uint_t port;
int status;
_NOTE(NOW_INVISIBLE_TO_OTHER_THREADS(*((sm_portinfo_t *)madpinfop)))
_NOTE(NOW_INVISIBLE_TO_OTHER_THREADS(curpinfo))
_NOTE(NOW_INVISIBLE_TO_OTHER_THREADS(tmadpinfo))
agent_args = (hermon_agent_handler_arg_t *)cb_args;
ibmf_handle = agent_args->ahd_ibmfhdl;
msgp = agent_args->ahd_ibmfmsg;
curr = agent_args->ahd_agentlist;
state = curr->agl_state;
port = curr->agl_port;
recv_msgbufp = &msgp->im_msgbufs_recv;
send_msgbufp = &msgp->im_msgbufs_send;
bcopy(recv_msgbufp, send_msgbufp, sizeof (ibmf_msg_bufs_t));
_NOTE(NOW_INVISIBLE_TO_OTHER_THREADS(msgp->im_local_addr))
if (HERMON_IS_SPECIAL_TRAP_MAD(msgp)) {
msgp->im_local_addr.ia_remote_lid =
hermon_get_smlid(state, port);
} else {
int isSMSet, isReregSuppd;
uint_t attr_id, method, mgmt_class;
madhdrp = recv_msgbufp->im_bufs_mad_hdr;
method = madhdrp->R_Method;
attr_id = b2h16(madhdrp->AttributeID);
mgmt_class = madhdrp->MgmtClass;
isSMSet = (((mgmt_class == MAD_MGMT_CLASS_SUBN_LID_ROUTED)||
(mgmt_class == MAD_MGMT_CLASS_SUBN_DIRECT_ROUTE)) &&
(method == MAD_METHOD_SET));
if (isSMSet && (attr_id == SM_PORTINFO_ATTRID) ||
(attr_id == SM_PKEY_TABLE_ATTRID) ||
(attr_id == SM_GUIDINFO_ATTRID)) {
madpinfop = recv_msgbufp->im_bufs_cl_data;
tmadpinfo = *madpinfop;
HERMON_GETPORTINFO_SWAP(&tmadpinfo);
status = hermon_getportinfo_cmd_post(state, port,
HERMON_SLEEPFLAG_FOR_CONTEXT(), &curpinfo);
if (status != HERMON_CMD_SUCCESS) {
cmn_err(CE_CONT, "Hermon: GetPortInfo "
"(port %02d) command failed: %08x\n", port,
status);
goto hermon_agent_handle_req_skip_response;
}
}
status = hermon_mad_ifc_cmd_post(state, port,
HERMON_CMD_SLEEP_NOSPIN,
(uint32_t *)recv_msgbufp->im_bufs_mad_hdr,
(uint32_t *)send_msgbufp->im_bufs_mad_hdr);
if (status != HERMON_CMD_SUCCESS) {
if ((status != HERMON_CMD_BAD_PKT) &&
(status != HERMON_CMD_INSUFF_RSRC)) {
cmn_err(CE_CONT, "Hermon: MAD_IFC (port %02d) "
"command failed: %08x\n", port, status);
}
goto hermon_agent_handle_req_skip_response;
}
if (isSMSet) {
event.ev_port_flags = 0;
type = 0;
event.ev_port = (uint8_t)port;
switch (attr_id) {
case SM_PORTINFO_ATTRID:
isReregSuppd = curpinfo.CapabilityMask &
SM_CAP_MASK_IS_CLNT_REREG_SUPPD;
madpinfop = recv_msgbufp->im_bufs_cl_data;
if (tmadpinfo.ClientRereg && isReregSuppd) {
type |= IBT_CLNT_REREG_EVENT;
}
type |= IBT_PORT_CHANGE_EVENT;
event.ev_port_flags = hermon_port_change_flags(
&curpinfo, &tmadpinfo);
if (event.ev_port_flags &
(IBT_PORT_CHANGE_REREG |
IBT_PORT_CHANGE_SM_FLAG)) {
if (hermon_set_port_capability(state,
port, &curpinfo,
event.ev_port_flags)
!= DDI_SUCCESS) {
cmn_err(CE_CONT, "HERMON: Port "
"%d capability reset "
"failed\n", port);
}
}
if ((event.ev_port_flags &
IBT_PORT_CHANGE_SM_LID) && !isReregSuppd)
type |= IBT_CLNT_REREG_EVENT;
break;
case SM_PKEY_TABLE_ATTRID:
type |= IBT_PORT_CHANGE_EVENT;
event.ev_port_flags = IBT_PORT_CHANGE_PKEY;
break;
case SM_GUIDINFO_ATTRID:
type |= IBT_PORT_CHANGE_EVENT;
event.ev_port_flags = IBT_PORT_CHANGE_SGID;
break;
default:
break;
}
while (type != 0) {
if (type & IBT_PORT_CHANGE_EVENT) {
code = IBT_PORT_CHANGE_EVENT;
type &= ~IBT_PORT_CHANGE_EVENT;
} else {
code = IBT_CLNT_REREG_EVENT;
type = 0;
}
ibc_async_handler(state->hs_ibtfpriv, code,
&event);
}
}
}
if (HERMON_IS_TRAP_REPRESS_MAD(msgp)) {
goto hermon_agent_handle_req_skip_response;
}
hermon_agent_mad_resp_handling(state, msgp, port);
status = ibmf_msg_transport(ibmf_handle, IBMF_QP_HANDLE_DEFAULT,
msgp, &retrans, hermon_agent_response_cb, state, 0);
if (status != IBMF_SUCCESS) {
goto hermon_agent_handle_req_skip_response;
}
kmem_free(agent_args, sizeof (hermon_agent_handler_arg_t));
return;
hermon_agent_handle_req_skip_response:
(void) ibmf_free_msg(ibmf_handle, &msgp);
kmem_free(agent_args, sizeof (hermon_agent_handler_arg_t));
}
static void
hermon_agent_response_cb(ibmf_handle_t ibmf_handle, ibmf_msg_t *msgp,
void *args)
{
(void) ibmf_free_msg(ibmf_handle, &msgp);
}
static int
hermon_agent_list_init(hermon_state_t *state)
{
hermon_agent_list_t *curr;
uint_t num_ports, num_agents, num_agents_per_port;
uint_t num_sma_agents = 0;
uint_t num_pma_agents = 0;
uint_t num_bma_agents = 0;
uint_t do_qp0, do_qp1;
int i, j, indx;
num_ports = state->hs_cfg_profile->cp_num_ports;
num_agents = 0;
num_agents_per_port = 0;
do_qp0 = state->hs_cfg_profile->cp_qp0_agents_in_fw;
do_qp1 = state->hs_cfg_profile->cp_qp1_agents_in_fw;
if (do_qp0 == 0) {
num_agents += (num_ports * HERMON_NUM_QP0_AGENTS_PER_PORT);
num_agents_per_port += HERMON_NUM_QP0_AGENTS_PER_PORT;
num_sma_agents = num_ports;
}
if (do_qp1 == 0) {
num_agents += (num_ports * HERMON_NUM_QP1_AGENTS_PER_PORT);
num_agents_per_port += HERMON_NUM_QP1_AGENTS_PER_PORT;
num_pma_agents = num_ports;
}
state->hs_num_agents = num_agents;
state->hs_agents = (hermon_agent_list_t *)kmem_zalloc(num_agents *
sizeof (hermon_agent_list_t), KM_SLEEP);
if (state->hs_agents == NULL) {
return (DDI_FAILURE);
}
indx = 0;
for (i = 0; i < num_agents_per_port; i++) {
for (j = 0; j < num_ports; j++) {
curr = &state->hs_agents[indx];
curr->agl_state = state;
curr->agl_port = j + 1;
if ((do_qp0 == 0) && num_sma_agents) {
curr->agl_mgmtclass = SUBN_AGENT;
num_sma_agents--;
indx++;
} else if ((do_qp1 == 0) && (num_pma_agents)) {
curr->agl_mgmtclass = PERF_AGENT;
num_pma_agents--;
indx++;
} else if ((do_qp1 == 0) && (num_bma_agents)) {
curr->agl_mgmtclass = BM_AGENT;
num_bma_agents--;
indx++;
}
}
}
return (DDI_SUCCESS);
}
static void
hermon_agent_list_fini(hermon_state_t *state)
{
kmem_free(state->hs_agents,
state->hs_num_agents * sizeof (hermon_agent_list_t));
}
static int
hermon_agent_register_all(hermon_state_t *state)
{
hermon_agent_list_t *curr;
ibmf_register_info_t ibmf_reg;
ibmf_impl_caps_t impl_caps;
ib_guid_t nodeguid;
int i, status, num_registered;
nodeguid = state->hs_ibtfinfo.hca_attr->hca_node_guid;
num_registered = 0;
if (state->hs_num_agents == 0) {
return (DDI_SUCCESS);
}
for (i = 0; i < state->hs_num_agents; i++) {
curr = &state->hs_agents[i];
ibmf_reg.ir_ci_guid = nodeguid;
ibmf_reg.ir_port_num = curr->agl_port;
ibmf_reg.ir_client_class = curr->agl_mgmtclass;
status = ibmf_register(&ibmf_reg, IBMF_VERSION, 0,
NULL, NULL, &curr->agl_ibmfhdl, &impl_caps);
if (status != IBMF_SUCCESS) {
goto agents_reg_fail;
}
status = ibmf_setup_async_cb(curr->agl_ibmfhdl,
IBMF_QP_HANDLE_DEFAULT, hermon_agent_request_cb, curr, 0);
if (status != IBMF_SUCCESS) {
(void) ibmf_unregister(&curr->agl_ibmfhdl, 0);
goto agents_reg_fail;
}
num_registered++;
}
return (DDI_SUCCESS);
agents_reg_fail:
(void) hermon_agent_unregister_all(state, num_registered);
return (DDI_FAILURE);
}
static int
hermon_agent_unregister_all(hermon_state_t *state, int num_reg)
{
hermon_agent_list_t *curr;
int i, status;
if (num_reg == 0) {
return (DDI_SUCCESS);
}
for (i = 0; i < num_reg; i++) {
curr = &state->hs_agents[i];
status = ibmf_tear_down_async_cb(curr->agl_ibmfhdl,
IBMF_QP_HANDLE_DEFAULT, 0);
if (status != IBMF_SUCCESS) {
return (DDI_FAILURE);
}
status = ibmf_unregister(&curr->agl_ibmfhdl, 0);
if (status != IBMF_SUCCESS) {
return (DDI_FAILURE);
}
}
return (DDI_SUCCESS);
}
static void
hermon_agent_mad_resp_handling(hermon_state_t *state, ibmf_msg_t *msgp,
uint_t port)
{
ib_mad_hdr_t *rmadhdrp = msgp->im_msgbufs_recv.im_bufs_mad_hdr;
ib_mad_hdr_t *smadhdrp = msgp->im_msgbufs_send.im_bufs_mad_hdr;
uint_t hop_count, hop_point;
uchar_t *resp, *ret_path;
resp = (uchar_t *)msgp->im_msgbufs_send.im_bufs_cl_data;
if (HERMON_MAD_IS_DR(rmadhdrp)) {
_NOTE(NOW_INVISIBLE_TO_OTHER_THREADS(*((sm_dr_mad_hdr_t *)rmadhdrp)))
_NOTE(NOW_INVISIBLE_TO_OTHER_THREADS(*((sm_dr_mad_hdr_t *)smadhdrp)))
HERMON_DRMAD_SET_DIRECTION(rmadhdrp);
hop_count = HERMON_DRMAD_GET_HOPCOUNT(rmadhdrp);
hop_point = HERMON_DRMAD_GET_HOPPOINTER(rmadhdrp);
if ((hop_count != 0) && ((hop_point == hop_count) ||
(hop_point == hop_count + 1))) {
ret_path = &resp[HERMON_DRMAD_RETURN_PATH_OFFSET];
ret_path[hop_point] = (uchar_t)port;
}
hop_point++;
HERMON_DRMAD_SET_HOPPOINTER(smadhdrp, (uint8_t)hop_point);
}
}