#include <sys/types.h>
#include <sys/ddi.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <sys/sunddi.h>
#include <sys/ib/ibtl/ibti.h>
#include <sys/ib/ibtl/ibtl_types.h>
#include <sys/ib/clients/iser/iser.h>
extern idm_transport_ops_t iser_transport_ops;
static ibt_cm_status_t iser_ib_handle_cm_req(idm_svc_t *svc_hdl,
ibt_cm_event_t *evp, ibt_cm_return_args_t *rargsp, void *rcmp,
ibt_priv_data_len_t rcmp_len);
static ibt_cm_status_t iser_ib_handle_cm_rep(iser_state_t *statep,
ibt_cm_event_t *evp, ibt_cm_return_args_t *rargsp, void *rcmp,
ibt_priv_data_len_t rcmp_len);
static ibt_cm_status_t iser_handle_cm_conn_est(ibt_cm_event_t *evp);
static ibt_cm_status_t iser_handle_cm_conn_closed(ibt_cm_event_t *evp);
static ibt_cm_status_t iser_handle_cm_event_failure(ibt_cm_event_t *evp);
ibt_cm_status_t
iser_ib_cm_handler(void *cm_private, ibt_cm_event_t *eventp,
ibt_cm_return_args_t *ret_args, void *ret_priv_data,
ibt_priv_data_len_t ret_len_max)
{
ibt_cm_status_t ret = IBT_CM_REJECT;
switch (eventp->cm_type) {
case IBT_CM_EVENT_REQ_RCV:
ISER_LOG(CE_NOTE, "iser_ib_cm_handler: IBT_CM_EVENT_REQ_RCV");
ret = iser_ib_handle_cm_req((idm_svc_t *)cm_private, eventp,
ret_args, ret_priv_data, ret_len_max);
break;
case IBT_CM_EVENT_REP_RCV:
ISER_LOG(CE_NOTE, "iser_ib_cm_handler: IBT_CM_EVENT_REP_RCV");
ret = iser_ib_handle_cm_rep((iser_state_t *)cm_private,
eventp, ret_args, ret_priv_data, ret_len_max);
break;
case IBT_CM_EVENT_CONN_EST:
ISER_LOG(CE_NOTE, "iser_ib_cm_handler: IBT_CM_EVENT_CONN_EST");
ret = iser_handle_cm_conn_est(eventp);
break;
case IBT_CM_EVENT_CONN_CLOSED:
ISER_LOG(CE_NOTE, "iser_ib_cm_handler: "
"IBT_CM_EVENT_CONN_CLOSED");
ret = iser_handle_cm_conn_closed(eventp);
break;
case IBT_CM_EVENT_FAILURE:
ISER_LOG(CE_NOTE, "iser_ib_cm_handler: Event failure");
ret = iser_handle_cm_event_failure(eventp);
break;
case IBT_CM_EVENT_MRA_RCV:
ISER_LOG(CE_NOTE, "iser_ib_cm_handler: MRA message received");
break;
case IBT_CM_EVENT_LAP_RCV:
ISER_LOG(CE_NOTE, "iser_ib_cm_handler: LAP message received");
break;
case IBT_CM_EVENT_APR_RCV:
ISER_LOG(CE_NOTE, "iser_ib_cm_handler: APR message received");
break;
default:
ISER_LOG(CE_NOTE, "iser_ib_cm_handler: unknown event (0x%x)",
eventp->cm_type);
break;
}
return (ret);
}
static ibt_cm_status_t
iser_ib_handle_cm_req(idm_svc_t *svc_hdl, ibt_cm_event_t *evp,
ibt_cm_return_args_t *rargsp, void *rcmp, ibt_priv_data_len_t rcmp_len)
{
iser_private_data_t iser_priv_data;
ibt_ip_cm_info_t ipcm_info;
iser_chan_t *chan;
iser_conn_t *iser_conn;
int status;
bcopy((uint8_t *)evp->cm_priv_data, &iser_priv_data,
sizeof (iser_private_data_t));
status = ibt_get_ip_data(evp->cm_priv_data_len, evp->cm_priv_data,
&ipcm_info);
if (status != IBT_SUCCESS) {
return (IBT_CM_REJECT);
}
ISER_LOG(CE_NOTE, "iser_ib_handle_cm_req: ipcm_info (0x%p): src IP "
"(0x%08x) src port (0x%04x) dst IP: (0x%08x)", (void *)&ipcm_info,
ipcm_info.src_addr.un.ip4addr, ipcm_info.src_port,
ipcm_info.dst_addr.un.ip4addr);
chan = iser_ib_alloc_channel_nopathlookup(
evp->cm_event.req.req_hca_guid,
evp->cm_event.req.req_prim_hca_port);
if (chan == NULL) {
ISER_LOG(CE_NOTE, "iser_ib_handle_cm_req: failed to allocate "
"a channel from src IP (0x%08x) src port (0x%04x) "
"to dst IP: (0x%08x) on hca(%llx %d)",
ipcm_info.src_addr.un.ip4addr, ipcm_info.src_port,
ipcm_info.dst_addr.un.ip4addr,
(longlong_t)evp->cm_event.req.req_hca_guid,
evp->cm_event.req.req_prim_hca_port);
return (IBT_CM_REJECT);
}
chan->ic_localip = ipcm_info.dst_addr;
chan->ic_remoteip = ipcm_info.src_addr;
chan->ic_lport = svc_hdl->is_svc_req.sr_port;
chan->ic_rport = ipcm_info.src_port;
iser_conn = kmem_zalloc(sizeof (iser_conn_t), KM_SLEEP);
mutex_init(&iser_conn->ic_lock, NULL, MUTEX_DRIVER, NULL);
cv_init(&iser_conn->ic_stage_cv, NULL, CV_DEFAULT, NULL);
iser_conn->ic_type = ISER_CONN_TYPE_TGT;
iser_conn->ic_chan = chan;
iser_conn->ic_stage = ISER_CONN_STAGE_ALLOCATED;
iser_tgt_svc_hold((iser_svc_t *)svc_hdl->is_iser_svc);
iser_conn->ic_idms = svc_hdl;
chan->ic_conn = iser_conn;
rargsp->cm_ret.rep.cm_channel = chan->ic_chanhdl;
return (IBT_CM_ACCEPT);
}
static ibt_cm_status_t
iser_ib_handle_cm_rep(iser_state_t *statep, ibt_cm_event_t *evp,
ibt_cm_return_args_t *rargsp, void *rcmp, ibt_priv_data_len_t rcmp_len)
{
iser_ib_post_recv(evp->cm_channel);
return (IBT_CM_ACCEPT);
}
static ibt_cm_status_t
iser_handle_cm_conn_est(ibt_cm_event_t *evp)
{
iser_chan_t *iser_chan;
iser_conn_t *iser_conn;
iser_svc_t *iser_svc;
idm_status_t status;
idm_conn_t *ic;
iser_chan = (iser_chan_t *)ibt_get_chan_private(evp->cm_channel);
if (iser_chan->ic_conn->ic_type == ISER_CONN_TYPE_TGT) {
iser_conn = iser_chan->ic_conn;
iser_svc = (iser_svc_t *)iser_conn->ic_idms->is_iser_svc;
mutex_enter(&iser_conn->ic_lock);
status = idm_svc_conn_create(iser_conn->ic_idms,
IDM_TRANSPORT_TYPE_ISER, &ic);
if (status != IDM_STATUS_SUCCESS) {
ISER_LOG(CE_NOTE, "iser_handle_cm_conn_est: "
"idm_svc_conn_create_failed");
mutex_exit(&iser_conn->ic_lock);
return (IBT_CM_NO_RESOURCE);
}
iser_tgt_svc_rele(iser_svc);
idm_conn_hold(ic);
ic->ic_transport_ops = &iser_transport_ops;
ic->ic_transport_private = (void *)iser_conn;
ic->ic_transport_hdrlen = ISER_HEADER_LENGTH;
iser_conn->ic_idmc = ic;
iser_ib_conv_ibtaddr2sockaddr(&ic->ic_laddr,
&iser_conn->ic_chan->ic_localip, iser_chan->ic_lport);
iser_ib_conv_ibtaddr2sockaddr(&ic->ic_raddr,
&iser_conn->ic_chan->ic_remoteip, iser_chan->ic_rport);
idm_conn_event(ic, CE_CONNECT_ACCEPT, (uintptr_t)NULL);
iser_conn->ic_stage = ISER_CONN_STAGE_IC_CONNECTED;
mutex_exit(&iser_conn->ic_lock);
iser_ib_post_recv(iser_chan->ic_chanhdl);
}
return (IBT_CM_ACCEPT);
}
static ibt_cm_status_t
iser_handle_cm_conn_closed(ibt_cm_event_t *evp)
{
iser_chan_t *chan;
chan = (iser_chan_t *)ibt_get_chan_private(evp->cm_channel);
ISER_LOG(CE_NOTE, "iser_handle_cm_conn_closed: chan (0x%p) "
"reason (0x%x)", (void *)chan, evp->cm_event.closed);
switch (evp->cm_event.closed) {
case IBT_CM_CLOSED_DREP_RCVD:
case IBT_CM_CLOSED_ALREADY:
return (IBT_CM_ACCEPT);
case IBT_CM_CLOSED_DREQ_RCVD:
case IBT_CM_CLOSED_REJ_RCVD:
case IBT_CM_CLOSED_DREQ_TIMEOUT:
case IBT_CM_CLOSED_DUP:
case IBT_CM_CLOSED_ABORT:
case IBT_CM_CLOSED_STALE:
mutex_enter(&chan->ic_conn->ic_lock);
switch (chan->ic_conn->ic_stage) {
case ISER_CONN_STAGE_UNDEFINED:
case ISER_CONN_STAGE_CLOSED:
mutex_exit(&chan->ic_conn->ic_lock);
break;
case ISER_CONN_STAGE_ALLOCATED:
mutex_exit(&chan->ic_conn->ic_lock);
iser_internal_conn_destroy(chan->ic_conn);
break;
case ISER_CONN_STAGE_IC_DISCONNECTED:
case ISER_CONN_STAGE_IC_FREED:
case ISER_CONN_STAGE_CLOSING:
chan->ic_conn->ic_stage = ISER_CONN_STAGE_CLOSED;
mutex_exit(&chan->ic_conn->ic_lock);
break;
case ISER_CONN_STAGE_IC_CONNECTED:
case ISER_CONN_STAGE_HELLO_SENT:
case ISER_CONN_STAGE_HELLO_SENT_FAIL:
case ISER_CONN_STAGE_HELLO_WAIT:
case ISER_CONN_STAGE_HELLO_RCV:
case ISER_CONN_STAGE_HELLO_RCV_FAIL:
case ISER_CONN_STAGE_HELLOREPLY_SENT:
case ISER_CONN_STAGE_HELLOREPLY_SENT_FAIL:
case ISER_CONN_STAGE_HELLOREPLY_RCV:
case ISER_CONN_STAGE_HELLOREPLY_RCV_FAIL:
case ISER_CONN_STAGE_LOGGED_IN:
idm_conn_event(chan->ic_conn->ic_idmc,
CE_TRANSPORT_FAIL, IDM_STATUS_FAIL);
chan->ic_conn->ic_stage = ISER_CONN_STAGE_CLOSING;
mutex_exit(&chan->ic_conn->ic_lock);
break;
default:
mutex_exit(&chan->ic_conn->ic_lock);
ASSERT(0);
}
return (IBT_CM_ACCEPT);
default:
ISER_LOG(CE_NOTE, "iser_handle_cm_conn_closed: unknown closed "
"event: (0x%x)", evp->cm_event.closed);
return (IBT_CM_REJECT);
}
}
static ibt_cm_status_t
iser_handle_cm_event_failure(ibt_cm_event_t *evp)
{
iser_chan_t *chan;
chan = (iser_chan_t *)ibt_get_chan_private(evp->cm_channel);
ISER_LOG(CE_NOTE, "iser_handle_cm_event_failure: chan (0x%p): "
"code: %d msg: %d reason: %d", (void *)chan,
evp->cm_event.failed.cf_code, evp->cm_event.failed.cf_msg,
evp->cm_event.failed.cf_reason);
if ((evp->cm_channel == NULL) || (chan == NULL)) {
return (IBT_CM_ACCEPT);
}
if ((evp->cm_event.failed.cf_code != IBT_CM_FAILURE_STALE) &&
(evp->cm_event.failed.cf_msg == IBT_CM_FAILURE_REQ)) {
return (IBT_CM_ACCEPT);
}
mutex_enter(&chan->ic_conn->ic_lock);
switch (chan->ic_conn->ic_stage) {
case ISER_CONN_STAGE_UNDEFINED:
case ISER_CONN_STAGE_CLOSED:
mutex_exit(&chan->ic_conn->ic_lock);
break;
case ISER_CONN_STAGE_ALLOCATED:
mutex_exit(&chan->ic_conn->ic_lock);
iser_internal_conn_destroy(chan->ic_conn);
break;
case ISER_CONN_STAGE_IC_DISCONNECTED:
case ISER_CONN_STAGE_IC_FREED:
case ISER_CONN_STAGE_CLOSING:
chan->ic_conn->ic_stage = ISER_CONN_STAGE_CLOSED;
mutex_exit(&chan->ic_conn->ic_lock);
break;
case ISER_CONN_STAGE_IC_CONNECTED:
case ISER_CONN_STAGE_HELLO_SENT:
case ISER_CONN_STAGE_HELLO_SENT_FAIL:
case ISER_CONN_STAGE_HELLO_WAIT:
case ISER_CONN_STAGE_HELLO_RCV:
case ISER_CONN_STAGE_HELLO_RCV_FAIL:
case ISER_CONN_STAGE_HELLOREPLY_SENT:
case ISER_CONN_STAGE_HELLOREPLY_SENT_FAIL:
case ISER_CONN_STAGE_HELLOREPLY_RCV:
case ISER_CONN_STAGE_HELLOREPLY_RCV_FAIL:
case ISER_CONN_STAGE_LOGGED_IN:
idm_conn_event(chan->ic_conn->ic_idmc, CE_TRANSPORT_FAIL,
IDM_STATUS_FAIL);
chan->ic_conn->ic_stage = ISER_CONN_STAGE_CLOSING;
mutex_exit(&chan->ic_conn->ic_lock);
break;
default:
mutex_exit(&chan->ic_conn->ic_lock);
ASSERT(0);
}
return (IBT_CM_ACCEPT);
}