#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/sdt.h>
#include <sys/ib/ibtl/ibti.h>
#include <sys/ib/ibtl/ibtl_types.h>
#include <sys/ib/clients/iser/iser.h>
static void iser_msg_handle(iser_chan_t *chan, iser_msg_t *msg);
int iser_iscsihdr_handle(iser_chan_t *chan, iser_msg_t *msg);
static int iser_ib_poll_send_completions(ibt_cq_hdl_t cq_hdl,
iser_chan_t *iser_chan);
static int iser_ib_poll_recv_completions(ibt_cq_hdl_t cq_hdl,
iser_chan_t *iser_chan);
void
iser_ib_sendcq_handler(ibt_cq_hdl_t cq_hdl, void *arg)
{
iser_chan_t *iser_chan;
ibt_status_t status;
iser_chan = (iser_chan_t *)arg;
do {
status = iser_ib_poll_send_completions(cq_hdl, iser_chan);
} while (status == IBT_SUCCESS);
if (status == IBT_CQ_EMPTY) {
status = ibt_enable_cq_notify(cq_hdl, IBT_NEXT_COMPLETION);
if (status != IBT_SUCCESS) {
ISER_LOG(CE_NOTE, "iser_ib_sendcq_handler: "
"ibt_enable_cq_notify error (%d)", status);
return;
}
do {
status = iser_ib_poll_send_completions(
cq_hdl, iser_chan);
} while (status == IBT_SUCCESS);
}
}
static int
iser_ib_poll_send_completions(ibt_cq_hdl_t cq_hdl, iser_chan_t *iser_chan)
{
ibt_wc_t wc[ISER_IB_SCQ_POLL_MAX];
ibt_wrid_t wrid;
idm_buf_t *idb = NULL;
idm_task_t *idt = NULL;
iser_wr_t *wr = NULL;
int i;
uint_t npoll = 0;
ibt_status_t status;
iser_conn_t *iser_conn;
idm_status_t idm_status;
iser_mr_t *mr;
iser_conn = iser_chan->ic_conn;
status = ibt_poll_cq(cq_hdl, wc, ISER_IB_SCQ_POLL_MAX, &npoll);
if (status != IBT_SUCCESS) {
if (status != IBT_CQ_EMPTY) {
ISER_LOG(CE_NOTE, "iser_ib_sendcq_handler: ibt_poll_cq "
"unexpected error (%d)", status);
}
return (status);
}
for (i = 0; i < npoll; i++) {
DTRACE_PROBE3(iser__send__cqe, iser_chan_t *, iser_chan,
ibt_wc_t *, &wc[i], ibt_wc_status_t, wc[i].wc_status);
wrid = wc[i].wc_id;
mutex_enter(&iser_chan->ic_sq_post_lock);
iser_chan->ic_sq_post_count--;
mutex_exit(&iser_chan->ic_sq_post_lock);
wr = (iser_wr_t *)(uintptr_t)wrid;
ASSERT(wr != NULL);
idm_status = (wc[i].wc_status == IBT_WC_SUCCESS) ?
IDM_STATUS_SUCCESS : IDM_STATUS_FAIL;
if (wc[i].wc_status != IBT_WC_SUCCESS) {
if (wr->iw_msg != NULL) {
iser_msg_free(wr->iw_msg);
}
if (wr->iw_pdu != NULL) {
idm_pdu_complete(wr->iw_pdu, idm_status);
}
if (wr->iw_buf != NULL) {
idb = wr->iw_buf;
mr = ((iser_buf_t *)
idb->idb_buf_private)->iser_mr;
#ifdef DEBUG
bcopy(&wc[i],
&((iser_buf_t *)idb->idb_buf_private)->
buf_wc, sizeof (ibt_wc_t));
#endif
idt = idb->idb_task_binding;
mutex_enter(&idt->idt_mutex);
if (wr->iw_type == ISER_WR_RDMAW) {
DTRACE_ISCSI_8(xfer__done,
idm_conn_t *, idt->idt_ic,
uintptr_t, idb->idb_buf,
uint32_t, idb->idb_bufoffset,
uint64_t, mr->is_mrva, uint32_t, 0,
uint32_t, mr->is_mrrkey,
uint32_t, idb->idb_xfer_len,
int, XFER_BUF_TX_TO_INI);
idm_buf_tx_to_ini_done(idt, idb,
IDM_STATUS_FAIL);
} else {
DTRACE_ISCSI_8(xfer__done,
idm_conn_t *, idt->idt_ic,
uintptr_t, idb->idb_buf,
uint32_t, idb->idb_bufoffset,
uint64_t, mr->is_mrva, uint32_t, 0,
uint32_t, mr->is_mrrkey,
uint32_t, idb->idb_xfer_len,
int, XFER_BUF_RX_FROM_INI);
idm_buf_rx_from_ini_done(idt, idb,
IDM_STATUS_FAIL);
}
}
iser_wr_free(wr);
mutex_enter(&iser_conn->ic_lock);
switch (iser_conn->ic_stage) {
case ISER_CONN_STAGE_IC_DISCONNECTED:
case ISER_CONN_STAGE_IC_FREED:
case ISER_CONN_STAGE_CLOSING:
case ISER_CONN_STAGE_CLOSED:
break;
default:
idm_conn_event(iser_conn->ic_idmc,
CE_TRANSPORT_FAIL, idm_status);
iser_conn->ic_stage = ISER_CONN_STAGE_CLOSING;
}
mutex_exit(&iser_conn->ic_lock);
continue;
}
switch (wr->iw_type) {
case ISER_WR_SEND:
ASSERT(wr->iw_msg != NULL);
iser_msg_free(wr->iw_msg);
if (wr->iw_pdu == NULL) {
mutex_enter(&iser_conn->ic_lock);
if (iser_conn->ic_stage ==
ISER_CONN_STAGE_HELLOREPLY_SENT) {
iser_conn->ic_stage =
ISER_CONN_STAGE_LOGGED_IN;
}
mutex_exit(&iser_conn->ic_lock);
} else {
idm_pdu_complete(wr->iw_pdu, idm_status);
}
iser_wr_free(wr);
break;
case ISER_WR_RDMAW:
case ISER_WR_RDMAR:
idb = wr->iw_buf;
mr = ((iser_buf_t *)idb->idb_buf_private)->iser_mr;
#ifdef DEBUG
bcopy(&wc[i],
&((iser_buf_t *)idb->idb_buf_private)->buf_wc,
sizeof (ibt_wc_t));
#endif
idt = idb->idb_task_binding;
mutex_enter(&idt->idt_mutex);
if (wr->iw_type == ISER_WR_RDMAW) {
DTRACE_ISCSI_8(xfer__done,
idm_conn_t *, idt->idt_ic,
uintptr_t, idb->idb_buf,
uint32_t, idb->idb_bufoffset,
uint64_t, mr->is_mrva, uint32_t, 0,
uint32_t, mr->is_mrrkey,
uint32_t, idb->idb_xfer_len,
int, XFER_BUF_TX_TO_INI);
idm_buf_tx_to_ini_done(idt, idb, idm_status);
} else {
DTRACE_ISCSI_8(xfer__done,
idm_conn_t *, idt->idt_ic,
uintptr_t, idb->idb_buf,
uint32_t, idb->idb_bufoffset,
uint64_t, mr->is_mrva, uint32_t, 0,
uint32_t, mr->is_mrrkey,
uint32_t, idb->idb_xfer_len,
int, XFER_BUF_RX_FROM_INI);
idm_buf_rx_from_ini_done(idt, idb, idm_status);
}
iser_wr_free(wr);
break;
default:
ASSERT(0);
break;
}
}
return (status);
}
void
iser_ib_recvcq_handler(ibt_cq_hdl_t cq_hdl, void *arg)
{
iser_chan_t *iser_chan;
ibt_status_t status;
iser_chan = (iser_chan_t *)arg;
do {
status = iser_ib_poll_recv_completions(cq_hdl, iser_chan);
} while (status == IBT_SUCCESS);
if (status == IBT_CQ_EMPTY) {
status = ibt_enable_cq_notify(cq_hdl, IBT_NEXT_COMPLETION);
if (status != IBT_SUCCESS) {
ISER_LOG(CE_NOTE, "iser_ib_recvcq_handler: "
"ibt_enable_cq_notify error (%d)", status);
return;
}
do {
status = iser_ib_poll_recv_completions(
cq_hdl, iser_chan);
} while (status == IBT_SUCCESS);
}
}
static int
iser_ib_poll_recv_completions(ibt_cq_hdl_t cq_hdl, iser_chan_t *iser_chan)
{
ibt_wc_t wc;
iser_msg_t *msg;
iser_qp_t *iser_qp;
int status;
iser_qp = &(iser_chan->ic_qp);
bzero(&wc, sizeof (ibt_wc_t));
status = ibt_poll_cq(cq_hdl, &wc, 1, NULL);
if (status == IBT_CQ_EMPTY) {
return (status);
}
if (status != IBT_SUCCESS) {
ISER_LOG(CE_NOTE, "iser_ib_poll_recv_completions: "
"ibt_poll_cq error (%d)", status);
mutex_enter(&iser_qp->qp_lock);
iser_qp->rq_level--;
mutex_exit(&iser_qp->qp_lock);
if ((msg = (iser_msg_t *)(uintptr_t)wc.wc_id) != NULL) {
iser_msg_free(msg);
}
return (status);
}
msg = (iser_msg_t *)(uintptr_t)wc.wc_id;
ASSERT(msg != NULL);
mutex_enter(&iser_chan->ic_conn->ic_lock);
mutex_enter(&iser_qp->qp_lock);
iser_qp->rq_level--;
if ((iser_qp->rq_taskqpending == B_FALSE) &&
(iser_qp->rq_level <= iser_qp->rq_lwm) &&
(iser_chan->ic_conn->ic_stage >= ISER_CONN_STAGE_IC_CONNECTED) &&
(iser_chan->ic_conn->ic_stage <= ISER_CONN_STAGE_LOGGED_IN)) {
iser_qp->rq_taskqpending = B_TRUE;
mutex_exit(&iser_qp->qp_lock);
status = iser_ib_post_recv_async(iser_chan->ic_chanhdl);
if (status != DDI_SUCCESS) {
ISER_LOG(CE_NOTE, "iser_ib_poll_recv_completions: "
"task dispatch failed");
mutex_enter(&iser_qp->qp_lock);
iser_qp->rq_taskqpending = B_FALSE;
mutex_exit(&iser_qp->qp_lock);
}
} else {
mutex_exit(&iser_qp->qp_lock);
}
DTRACE_PROBE3(iser__recv__cqe, iser_chan_t *, iser_chan,
ibt_wc_t *, &wc, ibt_wc_status_t, wc.wc_status);
if (wc.wc_status != IBT_WC_SUCCESS) {
switch (iser_chan->ic_conn->ic_stage) {
case ISER_CONN_STAGE_IC_DISCONNECTED:
case ISER_CONN_STAGE_IC_FREED:
case ISER_CONN_STAGE_CLOSING:
case ISER_CONN_STAGE_CLOSED:
break;
default:
idm_conn_event(iser_chan->ic_conn->ic_idmc,
CE_TRANSPORT_FAIL, IDM_STATUS_FAIL);
iser_chan->ic_conn->ic_stage =
ISER_CONN_STAGE_CLOSING;
}
mutex_exit(&iser_chan->ic_conn->ic_lock);
iser_msg_free(msg);
return (DDI_SUCCESS);
} else {
mutex_exit(&iser_chan->ic_conn->ic_lock);
iser_msg_handle(iser_chan, msg);
return (DDI_SUCCESS);
}
}
static void
iser_msg_handle(iser_chan_t *chan, iser_msg_t *msg)
{
int opcode;
iser_ctrl_hdr_t *hdr = NULL;
iser_conn_t *iser_conn = chan->ic_conn;
int status;
hdr = (iser_ctrl_hdr_t *)(uintptr_t)msg->msg_ds.ds_va;
ASSERT(hdr != NULL);
opcode = hdr->opcode;
if (opcode == ISER_OPCODE_CTRL_TYPE_PDU) {
status = iser_iscsihdr_handle(chan, msg);
if (status != DDI_SUCCESS) {
ISER_LOG(CE_NOTE, "iser_msg_handle: failed "
"iser_iscsihdr_handle");
iser_msg_free(msg);
idm_conn_event(iser_conn->ic_idmc,
CE_TRANSPORT_FAIL, IDM_STATUS_FAIL);
}
} else if (opcode == ISER_OPCODE_HELLO_MSG) {
ASSERT(opcode != ISER_OPCODE_HELLO_MSG);
if (iser_conn->ic_type != ISER_CONN_TYPE_TGT) {
idm_conn_event(iser_conn->ic_idmc,
CE_TRANSPORT_FAIL, IDM_STATUS_FAIL);
}
iser_hello_hdr_t *hello_hdr = (iser_hello_hdr_t *)hdr;
ISER_LOG(CE_NOTE, "received Hello message: opcode[%d], "
"maxver[%d], minver[%d], iser_ird[%d], msg (0x%p)",
hello_hdr->opcode, hello_hdr->maxver, hello_hdr->minver,
ntohs(hello_hdr->iser_ird), (void *)msg);
mutex_enter(&iser_conn->ic_lock);
if (iser_conn->ic_stage != ISER_CONN_STAGE_HELLO_WAIT) {
idm_conn_event(iser_conn->ic_idmc,
CE_TRANSPORT_FAIL, IDM_STATUS_FAIL);
}
iser_conn->ic_stage = ISER_CONN_STAGE_HELLOREPLY_SENT;
mutex_exit(&iser_conn->ic_lock);
status = iser_xfer_helloreply_msg(chan);
if (status != ISER_STATUS_SUCCESS) {
mutex_enter(&iser_conn->ic_lock);
iser_conn->ic_stage =
ISER_CONN_STAGE_HELLOREPLY_SENT_FAIL;
mutex_exit(&iser_conn->ic_lock);
idm_conn_event(iser_conn->ic_idmc,
CE_TRANSPORT_FAIL, status);
}
iser_msg_free(msg);
} else if (opcode == ISER_OPCODE_HELLOREPLY_MSG) {
ASSERT(opcode != ISER_OPCODE_HELLOREPLY_MSG);
if (iser_conn->ic_type != ISER_CONN_TYPE_INI) {
idm_conn_event(iser_conn->ic_idmc,
CE_TRANSPORT_FAIL, status);
}
iser_helloreply_hdr_t *hello_hdr = (iser_helloreply_hdr_t *)hdr;
ISER_LOG(CE_NOTE, "received Hello Reply message: opcode[%d], "
"maxver[%d], curver[%d], iser_ord[%d], msg (0x%p)",
hello_hdr->opcode, hello_hdr->maxver, hello_hdr->curver,
ntohs(hello_hdr->iser_ord), (void *)msg);
iser_msg_free(msg);
mutex_enter(&iser_conn->ic_lock);
iser_conn->ic_stage = ISER_CONN_STAGE_HELLOREPLY_RCV;
cv_signal(&iser_conn->ic_stage_cv);
mutex_exit(&iser_conn->ic_lock);
} else {
ISER_LOG(CE_NOTE, "iser_msg_handle: unsupported opcode (0x%x): "
"terminating session on IDM handle (0x%p)", opcode,
(void *) iser_conn->ic_idmc);
iser_msg_free(msg);
idm_conn_event(iser_conn->ic_idmc, CE_TRANSPORT_FAIL,
IDM_STATUS_FAIL);
}
}
#define IDM_PDU_OPCODE(PDU) \
((PDU)->isp_hdr->opcode & ISCSI_OPCODE_MASK)
static uint32_t
n2h24(uchar_t *ptr)
{
return ((ptr[0] << 16) | (ptr[1] << 8) | ptr[2]);
}
static void
iser_rx_pdu_cb(idm_pdu_t *pdu, idm_status_t status)
{
iser_msg_free((iser_msg_t *)pdu->isp_transport_private);
idm_pdu_free(pdu);
}
int
iser_iscsihdr_handle(iser_chan_t *chan, iser_msg_t *msg)
{
idm_pdu_t *pdu;
uint8_t *iser_hdrp;
uint8_t *iscsi_hdrp;
iscsi_hdr_t *bhs;
pdu = idm_pdu_alloc_nosleep(sizeof (iscsi_hdr_t), 0);
pdu->isp_ic = chan->ic_conn->ic_idmc;
ASSERT(pdu->isp_ic != NULL);
pdu->isp_transport_private = (void *)msg;
iser_hdrp = (uint8_t *)(uintptr_t)msg->msg_ds.ds_va;
if (iser_hdrp == NULL) {
ISER_LOG(CE_NOTE, "iser_iscsihdr_handle: iser_hdrp is NULL");
idm_pdu_free(pdu);
return (ISER_STATUS_FAIL);
}
pdu->isp_transport_hdr = (void *)iser_hdrp;
pdu->isp_transport_hdrlen = ISER_HEADER_LENGTH;
iscsi_hdrp = ((uint8_t *)(uintptr_t)msg->msg_ds.ds_va) +
ISER_HEADER_LENGTH;
if (iscsi_hdrp == NULL) {
ISER_LOG(CE_NOTE, "iser_iscsihdr_handle: iscsi_hdrp is NULL");
idm_pdu_free(pdu);
return (ISER_STATUS_FAIL);
}
pdu->isp_hdr = (iscsi_hdr_t *)(uintptr_t)iscsi_hdrp;
bhs = pdu->isp_hdr;
pdu->isp_hdrlen = sizeof (iscsi_hdr_t) +
(bhs->hlength * sizeof (uint32_t));
pdu->isp_datalen = n2h24(bhs->dlength);
pdu->isp_callback = iser_rx_pdu_cb;
if (pdu->isp_datalen) {
pdu->isp_data = ((uint8_t *)(uintptr_t)pdu->isp_hdr) +
pdu->isp_hdrlen;
}
idm_pdu_rx(pdu->isp_ic, pdu);
return (DDI_SUCCESS);
}