/* * iser_ib_cm_handler() */ 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: /* Not supported */ ISER_LOG(CE_NOTE, "iser_ib_cm_handler: MRA message received"); break; case IBT_CM_EVENT_LAP_RCV: /* Not supported */ ISER_LOG(CE_NOTE, "iser_ib_cm_handler: LAP message received"); break; case IBT_CM_EVENT_APR_RCV: /* Not supported */ 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); }
/* * iser_tgt_svc_create() * Establish the CM service for inbound iSER service requests on the port * indicated by sr->sr_port. * idm_svc_req_t contains the service parameters. */ idm_status_t iser_tgt_svc_create(idm_svc_req_t *sr, idm_svc_t *is) { iser_svc_t *iser_svc; int rc; iser_svc = kmem_zalloc(sizeof (iser_svc_t), KM_SLEEP); is->is_iser_svc = (void *)iser_svc; idm_refcnt_init(&iser_svc->is_refcnt, iser_svc); list_create(&iser_svc->is_sbindlist, sizeof (iser_sbind_t), offsetof(iser_sbind_t, is_list_node)); iser_svc->is_svcid = ibt_get_ip_sid(IPPROTO_TCP, sr->sr_port); /* * Register an iSER target service for the requested port * and set the iser_svc structure in the idm_svc handle. */ rc = iser_register_service(is); if (rc != DDI_SUCCESS) { ISER_LOG(CE_NOTE, "iser_tgt_svc_create: iser_register_service " "failed on port (%d): rc (0x%x)", sr->sr_port, rc); (void) ibt_release_ip_sid(iser_svc->is_svcid); list_destroy(&iser_svc->is_sbindlist); idm_refcnt_destroy(&iser_svc->is_refcnt); kmem_free(iser_svc, sizeof (iser_svc_t)); return (IDM_STATUS_FAIL); } return (IDM_STATUS_SUCCESS); }
/* * iser_ini_conn_connect() * Establish the connection referred to by the handle previously allocated via * iser_ini_conn_create(). */ static idm_status_t iser_ini_conn_connect(idm_conn_t *ic) { iser_conn_t *iser_conn; iser_status_t status; iser_conn = (iser_conn_t *)ic->ic_transport_private; status = iser_channel_open(iser_conn->ic_chan); if (status != ISER_STATUS_SUCCESS) { ISER_LOG(CE_WARN, "iser: failed to open channel"); return (IDM_STATUS_FAIL); } /* * Set the local and remote addresses in the idm conn handle. */ iser_ib_conv_ibtaddr2sockaddr(&ic->ic_laddr, &iser_conn->ic_chan->ic_localip, iser_conn->ic_chan->ic_lport); iser_ib_conv_ibtaddr2sockaddr(&ic->ic_raddr, &iser_conn->ic_chan->ic_remoteip, iser_conn->ic_chan->ic_rport); mutex_enter(&iser_conn->ic_lock); /* Hold a reference on the IDM connection handle */ idm_conn_hold(ic); iser_conn->ic_stage = ISER_CONN_STAGE_IC_CONNECTED; mutex_exit(&iser_conn->ic_lock); return (IDM_STATUS_SUCCESS); }
/* ARGSUSED */ static idm_status_t iser_buf_alloc(idm_buf_t *idb, uint64_t buflen) { iser_conn_t *iser_conn; iser_hca_t *iser_hca; iser_buf_t *iser_buf; if (buflen > ISER_DEFAULT_BUFLEN) { return (IDM_STATUS_FAIL); } iser_conn = (iser_conn_t *)idb->idb_ic->ic_transport_private; iser_hca = iser_conn->ic_chan->ic_hca; /* * Allocate a buffer from this HCA's cache. Once initialized, these * will remain allocated and registered (see above). */ iser_buf = kmem_cache_alloc(iser_hca->iser_buf_cache, KM_NOSLEEP); if (iser_buf == NULL) { ISER_LOG(CE_NOTE, "iser_buf_alloc: alloc failed"); return (IDM_STATUS_FAIL); } /* Set the allocated data buffer pointer in the IDM buf handle */ idb->idb_buf = iser_buf->buf; /* Set the private buf and reg handles in the IDM buf handle */ idb->idb_buf_private = (void *)iser_buf; idb->idb_reg_private = (void *)iser_buf->iser_mr; return (IDM_STATUS_SUCCESS); }
/* * iser_buf_tx_from_ini() transmits data from the initiator into the buffer * in idb to fulfill SCSI Write commands. An iser_xfer routine is invoked * to implement the RDMA operations. * * Caller holds idt->idt_mutex. */ static idm_status_t iser_buf_rx_from_ini(idm_task_t *idt, idm_buf_t *idb) { iser_status_t iser_status; idm_status_t idm_status = IDM_STATUS_SUCCESS; ASSERT(mutex_owned(&idt->idt_mutex)); iser_status = iser_xfer_buf_from_ini(idt, idb); if (iser_status != ISER_STATUS_SUCCESS) { ISER_LOG(CE_WARN, "iser_buf_rx_from_ini: failed " "iser_xfer_buf_from_ini: idt (0x%p) idb (0x%p)", (void *) idt, (void *) idb); idm_buf_rx_from_ini_done(idt, idb, IDM_STATUS_ABORTED); return (IDM_STATUS_FAIL); } /* * iSCSIt's Data Completion Notify callback is invoked from * the Work Request Send completion Handler */ mutex_exit(&idt->idt_mutex); return (idm_status); }
/* * iser_ini_conn_create() * Allocate an iSER initiator connection context */ static idm_status_t iser_ini_conn_create(idm_conn_req_t *cr, idm_conn_t *ic) { iser_chan_t *iser_chan = NULL; iser_conn_t *iser_conn; /* Allocate and set up a connection handle */ iser_conn = kmem_zalloc(sizeof (iser_conn_t), KM_SLEEP); mutex_init(&iser_conn->ic_lock, NULL, MUTEX_DRIVER, NULL); /* Allocate and open a channel to the target node */ iser_chan = iser_channel_alloc(NULL, &cr->cr_ini_dst_addr); if (iser_chan == NULL) { ISER_LOG(CE_WARN, "iser: failed to allocate channel"); mutex_destroy(&iser_conn->ic_lock); kmem_free(iser_conn, sizeof (iser_conn_t)); return (IDM_STATUS_FAIL); } /* * The local IP and remote IP are filled in iser_channel_alloc. The * remote port needs to be filled in from idm_conn_req_t. The local * port is irrelevant. Internal representation of the port in the * IDM sockaddr structure is in network byte order. IBT expects the * port in host byte order. */ switch (cr->cr_ini_dst_addr.sin.sa_family) { case AF_INET: iser_chan->ic_rport = ntohs(cr->cr_ini_dst_addr.sin4.sin_port); break; case AF_INET6: iser_chan->ic_rport = ntohs(cr->cr_ini_dst_addr.sin6.sin6_port); break; default: iser_chan->ic_rport = ISCSI_LISTEN_PORT; } iser_chan->ic_lport = 0; cv_init(&iser_conn->ic_stage_cv, NULL, CV_DEFAULT, NULL); iser_conn->ic_type = ISER_CONN_TYPE_INI; iser_conn->ic_stage = ISER_CONN_STAGE_ALLOCATED; iser_conn->ic_chan = iser_chan; iser_conn->ic_idmc = ic; /* * Set a pointer to the iser_conn in the iser_chan for easy * access during CM event handling */ iser_chan->ic_conn = iser_conn; /* Set the iSER conn handle in the IDM conn private handle */ ic->ic_transport_private = (void *)iser_conn; /* Set the transport header length */ ic->ic_transport_hdrlen = ISER_HEADER_LENGTH; return (IDM_STATUS_SUCCESS); }
int iser_idm_register() { idm_transport_attr_t attr; idm_status_t status; attr.type = IDM_TRANSPORT_TYPE_ISER; attr.it_ops = &iser_transport_ops; attr.it_caps = &iser_transport_caps; status = idm_transport_register(&attr); if (status != IDM_STATUS_SUCCESS) { ISER_LOG(CE_WARN, "Failed to register iSER transport with IDM"); return (DDI_FAILURE); } ISER_LOG(CE_NOTE, "Registered iSER transport with IDM"); return (DDI_SUCCESS); }
/* * iser_tgt_svc_online() * Bind the CM service allocated via iser_tgt_svc_create(). */ static idm_status_t iser_tgt_svc_online(idm_svc_t *is) { iser_status_t status; mutex_enter(&is->is_mutex); /* * Pass the IDM service handle as the client private data for * later use. */ status = iser_bind_service(is); if (status != ISER_STATUS_SUCCESS) { ISER_LOG(CE_NOTE, "iser_tgt_svc_online: failed bind service"); mutex_exit(&is->is_mutex); return (IDM_STATUS_FAIL); } mutex_exit(&is->is_mutex); return (IDM_STATUS_SUCCESS); }
/* * iser_pdu_tx() transmits a Control PDU via the iSER channel. We pull the * channel out of the idm_conn_t passed in, and pass it and the pdu to the * iser_xfer routine. */ static void iser_pdu_tx(idm_conn_t *ic, idm_pdu_t *pdu) { iser_conn_t *iser_conn; iser_status_t iser_status; iser_conn = (iser_conn_t *)ic->ic_transport_private; iser_status = iser_xfer_ctrlpdu(iser_conn->ic_chan, pdu); if (iser_status != ISER_STATUS_SUCCESS) { ISER_LOG(CE_WARN, "iser_pdu_tx: failed iser_xfer_ctrlpdu: " "ic (0x%p) pdu (0x%p)", (void *) ic, (void *) pdu); /* Fail this PDU transmission */ idm_pdu_complete(pdu, IDM_STATUS_FAIL); } /* * We successfully posted this PDU for transmission. * The completion handler will invoke idm_pdu_complete() * with the completion status. See iser_cq.c for more * information. */ }
/* * Handle EVENT FAILURE */ 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)) { /* channel not established yet */ 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)) { /* * This end is active, just ignore, ibt_open_rc_channel() * caller will take care of cleanup. */ return (IBT_CM_ACCEPT); } /* handle depending upon our connection state */ mutex_enter(&chan->ic_conn->ic_lock); switch (chan->ic_conn->ic_stage) { case ISER_CONN_STAGE_UNDEFINED: case ISER_CONN_STAGE_CLOSED: /* do nothing, just drop the lock */ mutex_exit(&chan->ic_conn->ic_lock); break; case ISER_CONN_STAGE_ALLOCATED: /* * We blew up or were offlined during connection * establishment. Teardown the iSER conn and chan * handles. */ 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: /* update to CLOSED, then drop the lock */ 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: /* fail the transport and move the conn to CLOSING */ 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); } /* accept the event */ 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: /* we requested a disconnect */ case IBT_CM_CLOSED_ALREADY: /* duplicate close */ /* ignore these */ return (IBT_CM_ACCEPT); case IBT_CM_CLOSED_DREQ_RCVD: /* request to close the channel */ case IBT_CM_CLOSED_REJ_RCVD: /* reject after conn establishment */ case IBT_CM_CLOSED_DREQ_TIMEOUT: /* our close request timed out */ case IBT_CM_CLOSED_DUP: /* duplicate close request */ case IBT_CM_CLOSED_ABORT: /* aborted connection establishment */ case IBT_CM_CLOSED_STALE: /* stale / unref connection */ /* handle these depending upon our connection state */ mutex_enter(&chan->ic_conn->ic_lock); switch (chan->ic_conn->ic_stage) { case ISER_CONN_STAGE_UNDEFINED: case ISER_CONN_STAGE_CLOSED: /* do nothing, just drop the lock */ mutex_exit(&chan->ic_conn->ic_lock); break; case ISER_CONN_STAGE_ALLOCATED: /* * We blew up or were offlined during connection * establishment. Teardown the iSER conn and chan * handles. */ 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: /* we're down, set CLOSED */ 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: /* for all other stages, fail the transport */ 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); } /* accept the event */ return (IBT_CM_ACCEPT); default: /* unknown event */ 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_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); /* * An ibt_open_rc_channel() comes in as a IBT_CM_EVENT_REQ_RCV on the * iSER-IB target, upon which the target sends a Response, accepting * the request. This comes in as a IBT_CM_EVENT_REP_RCV on the iSER-IB * initiator, which then sends an RTU. Upon getting this RTU from the * iSER-IB initiator, the IBT_CM_EVENT_CONN_EST event is generated on * the target. Then subsequently an IBT_CM_EVENT_CONN_EST event is * generated on the initiator. * * Our new connection has been established on the target. If we are * receiving this event on the target side, the iser_channel can be * used as it is already populated. On the target side, an IDM * connection is then allocated and the IDM layer is notified. * If we are on the initiator we needn't do anything, since we * already have the IDM linkage in place for this connection. */ 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) { /* * No IDM rsrcs or something equally Bad. * Return non-SUCCESS to IBCM. He'll give * us a CONN_CLOSED, which we'll handle * below. */ 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); } /* We no longer need the hold on the iSER service handle */ iser_tgt_svc_rele(iser_svc); /* Hold a reference on the IDM connection handle */ idm_conn_hold(ic); /* Set the transport ops and conn on the idm_conn handle */ 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; /* * Set the local and remote addresses in the idm conn handle. */ 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); /* * Kick the state machine. At CS_S3_XPT_UP the state machine * will notify the client (target) about the new connection. */ idm_conn_event(ic, CE_CONNECT_ACCEPT, NULL); iser_conn->ic_stage = ISER_CONN_STAGE_IC_CONNECTED; mutex_exit(&iser_conn->ic_lock); /* * Post work requests on the receive queue */ iser_ib_post_recv(iser_chan->ic_chanhdl); } return (IBT_CM_ACCEPT); }
/* ARGSUSED */ 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; /* * CM private data brings IP information * Private data received is a stream of bytes and may not be properly * aligned. So, bcopy the data onto the stack before accessing it. */ bcopy((uint8_t *)evp->cm_priv_data, &iser_priv_data, sizeof (iser_private_data_t)); /* extract the CM IP info */ 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); /* Allocate a channel to establish the new connection */ 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); } /* Set the local and remote ip */ chan->ic_localip = ipcm_info.dst_addr; chan->ic_remoteip = ipcm_info.src_addr; /* Set the local and remote port numbers on the channel handle */ chan->ic_lport = svc_hdl->is_svc_req.sr_port; chan->ic_rport = ipcm_info.src_port; /* Allocate the iser_conn_t for the IDM svc binding */ iser_conn = kmem_zalloc(sizeof (iser_conn_t), KM_SLEEP); /* Set up the iser_conn attributes */ 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; /* Hold a reference to the iSER service handle */ iser_tgt_svc_hold((iser_svc_t *)svc_hdl->is_iser_svc); iser_conn->ic_idms = svc_hdl; /* * Now set a pointer to the iser_conn in the iser_chan for * access during CM event handling */ chan->ic_conn = iser_conn; rargsp->cm_ret.rep.cm_channel = chan->ic_chanhdl; return (IBT_CM_ACCEPT); }