#include <sys/ddi.h>
#include <sys/sunddi.h>
#include <sys/socket.h>
#include <sys/sysmacros.h>
#include <sys/ib/clients/iser/iser.h>
#include <sys/ib/clients/iser/iser_idm.h>
static void iser_pdu_tx(idm_conn_t *ic, idm_pdu_t *pdu);
static idm_status_t iser_buf_tx_to_ini(idm_task_t *idt, idm_buf_t *idb);
static idm_status_t iser_buf_rx_from_ini(idm_task_t *idt, idm_buf_t *idb);
static idm_status_t iser_tgt_enable_datamover(idm_conn_t *ic);
static idm_status_t iser_ini_enable_datamover(idm_conn_t *ic);
static void iser_notice_key_values(struct idm_conn_s *ic,
nvlist_t *negotiated_nvl);
static kv_status_t iser_declare_key_values(struct idm_conn_s *ic,
nvlist_t *config_nvl, nvlist_t *outgoing_nvl);
static idm_status_t iser_free_task_rsrcs(idm_task_t *idt);
static kv_status_t iser_negotiate_key_values(idm_conn_t *ic,
nvlist_t *request_nvl, nvlist_t *response_nvl, nvlist_t *negotiated_nvl);
static kv_status_t iser_handle_numerical(nvpair_t *nvp, uint64_t value,
const idm_kv_xlate_t *ikvx, uint64_t min_value, uint64_t max_value,
uint64_t iser_max_value, nvlist_t *request_nvl, nvlist_t *response_nvl,
nvlist_t *negotiated_nvl);
static kv_status_t iser_handle_boolean(nvpair_t *nvp, boolean_t value,
const idm_kv_xlate_t *ikvx, boolean_t iser_value, nvlist_t *request_nvl,
nvlist_t *response_nvl, nvlist_t *negotiated_nvl);
static kv_status_t iser_handle_key(nvpair_t *nvp, const idm_kv_xlate_t *ikvx,
nvlist_t *request_nvl, nvlist_t *response_nvl, nvlist_t *negotiated_nvl);
static kv_status_t iser_process_request_nvlist(nvlist_t *request_nvl,
nvlist_t *response_nvl, nvlist_t *negotiated_nvl);
static boolean_t iser_conn_is_capable(idm_conn_req_t *ic,
idm_transport_caps_t *caps);
static idm_status_t iser_buf_alloc(idm_buf_t *idb, uint64_t buflen);
static idm_status_t iser_buf_setup(idm_buf_t *idb);
static void iser_buf_teardown(idm_buf_t *idb);
static void iser_buf_free(idm_buf_t *idb);
static void iser_tgt_svc_destroy(struct idm_svc_s *is);
static idm_status_t iser_tgt_svc_online(struct idm_svc_s *is);
static void iser_tgt_svc_offline(struct idm_svc_s *is);
static idm_status_t iser_tgt_conn_connect(struct idm_conn_s *ic);
static idm_status_t iser_ini_conn_create(idm_conn_req_t *cr,
struct idm_conn_s *ic);
static void iser_conn_destroy(struct idm_conn_s *ic);
static idm_status_t iser_ini_conn_connect(struct idm_conn_s *ic);
static void iser_conn_disconnect(struct idm_conn_s *ic);
idm_transport_ops_t iser_transport_ops = {
&iser_pdu_tx,
&iser_buf_tx_to_ini,
&iser_buf_rx_from_ini,
NULL,
NULL,
NULL,
NULL,
NULL,
&iser_tgt_enable_datamover,
&iser_ini_enable_datamover,
NULL,
&iser_free_task_rsrcs,
&iser_negotiate_key_values,
&iser_notice_key_values,
&iser_conn_is_capable,
&iser_buf_alloc,
&iser_buf_free,
&iser_buf_setup,
&iser_buf_teardown,
&iser_tgt_svc_create,
&iser_tgt_svc_destroy,
&iser_tgt_svc_online,
&iser_tgt_svc_offline,
&iser_conn_destroy,
&iser_tgt_conn_connect,
&iser_conn_disconnect,
&iser_ini_conn_create,
&iser_conn_destroy,
&iser_ini_conn_connect,
&iser_conn_disconnect,
&iser_declare_key_values
};
idm_transport_caps_t iser_transport_caps = {
0
};
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);
}
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;
iser_conn = kmem_zalloc(sizeof (iser_conn_t), KM_SLEEP);
mutex_init(&iser_conn->ic_lock, NULL, MUTEX_DRIVER, NULL);
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);
}
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;
iser_chan->ic_conn = iser_conn;
ic->ic_transport_private = (void *)iser_conn;
ic->ic_transport_hdrlen = ISER_HEADER_LENGTH;
return (IDM_STATUS_SUCCESS);
}
void
iser_internal_conn_destroy(iser_conn_t *ic)
{
mutex_enter(&ic->ic_lock);
iser_channel_free(ic->ic_chan);
if ((ic->ic_type == ISER_CONN_TYPE_TGT) &&
(ic->ic_stage == ISER_CONN_STAGE_ALLOCATED)) {
iser_tgt_svc_rele(ic->ic_idms->is_iser_svc);
}
cv_destroy(&ic->ic_stage_cv);
mutex_exit(&ic->ic_lock);
mutex_destroy(&ic->ic_lock);
kmem_free(ic, sizeof (iser_conn_t));
}
static void
iser_conn_destroy(idm_conn_t *ic)
{
iser_conn_t *iser_conn;
iser_conn = (iser_conn_t *)ic->ic_transport_private;
iser_internal_conn_destroy(iser_conn);
ic->ic_transport_private = NULL;
}
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);
}
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);
idm_conn_hold(ic);
iser_conn->ic_stage = ISER_CONN_STAGE_IC_CONNECTED;
mutex_exit(&iser_conn->ic_lock);
return (IDM_STATUS_SUCCESS);
}
static void
iser_conn_disconnect(idm_conn_t *ic)
{
iser_conn_t *iser_conn;
iser_conn = (iser_conn_t *)ic->ic_transport_private;
mutex_enter(&iser_conn->ic_lock);
iser_conn->ic_stage = ISER_CONN_STAGE_CLOSING;
mutex_exit(&iser_conn->ic_lock);
iser_channel_close(iser_conn->ic_chan);
mutex_enter(&iser_conn->ic_lock);
idm_conn_rele(iser_conn->ic_idmc);
iser_conn->ic_stage = ISER_CONN_STAGE_CLOSED;
mutex_exit(&iser_conn->ic_lock);
}
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);
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);
}
void
iser_tgt_svc_hold(iser_svc_t *is)
{
idm_refcnt_hold(&is->is_refcnt);
}
void
iser_tgt_svc_rele(iser_svc_t *is)
{
idm_refcnt_rele(&is->is_refcnt);
}
static void
iser_tgt_svc_destroy(idm_svc_t *is)
{
iser_svc_t *iser_svc;
iser_svc = (iser_svc_t *)is->is_iser_svc;
iser_deregister_service(is);
idm_refcnt_wait_ref(&iser_svc->is_refcnt);
list_destroy(&iser_svc->is_sbindlist);
idm_refcnt_destroy(&iser_svc->is_refcnt);
kmem_free(iser_svc, sizeof (iser_svc_t));
}
static idm_status_t
iser_tgt_svc_online(idm_svc_t *is)
{
iser_status_t status;
mutex_enter(&is->is_mutex);
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);
}
static void
iser_tgt_svc_offline(idm_svc_t *is)
{
mutex_enter(&is->is_mutex);
iser_unbind_service(is);
mutex_exit(&is->is_mutex);
}
static idm_status_t
iser_tgt_conn_connect(idm_conn_t *ic)
{
return (IDM_STATUS_SUCCESS);
}
static idm_status_t
iser_tgt_enable_datamover(idm_conn_t *ic)
{
iser_conn_t *iser_conn;
iser_conn = (iser_conn_t *)ic->ic_transport_private;
mutex_enter(&iser_conn->ic_lock);
iser_conn->ic_stage = ISER_CONN_STAGE_LOGGED_IN;
mutex_exit(&iser_conn->ic_lock);
return (IDM_STATUS_SUCCESS);
}
static idm_status_t
iser_ini_enable_datamover(idm_conn_t *ic)
{
iser_conn_t *iser_conn;
clock_t delay;
int status;
iser_conn = (iser_conn_t *)ic->ic_transport_private;
mutex_enter(&iser_conn->ic_lock);
iser_conn->ic_stage = ISER_CONN_STAGE_HELLO_SENT;
mutex_exit(&iser_conn->ic_lock);
status = iser_xfer_hello_msg(iser_conn->ic_chan);
if (status != ISER_STATUS_SUCCESS) {
mutex_enter(&iser_conn->ic_lock);
iser_conn->ic_stage = ISER_CONN_STAGE_HELLO_SENT_FAIL;
mutex_exit(&iser_conn->ic_lock);
return (IDM_STATUS_FAIL);
}
delay = ddi_get_lbolt() + drv_usectohz(500000);
mutex_enter(&iser_conn->ic_lock);
while ((iser_conn->ic_stage != ISER_CONN_STAGE_HELLOREPLY_RCV) &&
(ddi_get_lbolt() < delay)) {
(void) cv_timedwait(&iser_conn->ic_stage_cv,
&iser_conn->ic_lock, delay);
}
switch (iser_conn->ic_stage) {
case ISER_CONN_STAGE_HELLOREPLY_RCV:
iser_conn->ic_stage = ISER_CONN_STAGE_LOGGED_IN;
mutex_exit(&iser_conn->ic_lock);
return (IDM_STATUS_SUCCESS);
default:
iser_conn->ic_stage = ISER_CONN_STAGE_HELLOREPLY_RCV_FAIL;
mutex_exit(&iser_conn->ic_lock);
return (IDM_STATUS_FAIL);
}
}
idm_status_t
iser_free_task_rsrcs(idm_task_t *idt)
{
return (IDM_STATUS_SUCCESS);
}
static kv_status_t
iser_negotiate_key_values(idm_conn_t *ic, nvlist_t *request_nvl,
nvlist_t *response_nvl, nvlist_t *negotiated_nvl)
{
kv_status_t kvrc = KV_HANDLED;
kvrc = iser_process_request_nvlist(request_nvl, response_nvl,
negotiated_nvl);
ic->ic_rdma_extensions = B_TRUE;
return (kvrc);
}
static kv_status_t
iser_process_request_nvlist(nvlist_t *request_nvl, nvlist_t *response_nvl,
nvlist_t *negotiated_nvl)
{
const idm_kv_xlate_t *ikvx;
char *nvp_name;
nvpair_t *nvp;
nvpair_t *next_nvp;
kv_status_t kvrc = KV_HANDLED;
boolean_t transit = B_TRUE;
nvp = nvlist_next_nvpair(request_nvl, NULL);
while (nvp != NULL) {
next_nvp = nvlist_next_nvpair(request_nvl, nvp);
nvp_name = nvpair_name(nvp);
ikvx = idm_lookup_kv_xlate(nvp_name, strlen(nvp_name));
kvrc = iser_handle_key(nvp, ikvx, request_nvl, response_nvl,
negotiated_nvl);
if (kvrc != KV_HANDLED) {
if (kvrc == KV_HANDLED_NO_TRANSIT) {
transit = B_FALSE;
} else {
break;
}
}
nvp = next_nvp;
}
if ((kvrc == KV_HANDLED) && (transit == B_FALSE)) {
kvrc = KV_HANDLED_NO_TRANSIT;
}
return (kvrc);
}
static kv_status_t
iser_handle_key(nvpair_t *nvp, const idm_kv_xlate_t *ikvx,
nvlist_t *request_nvl, nvlist_t *response_nvl, nvlist_t *negotiated_nvl)
{
kv_status_t kvrc = KV_UNHANDLED;
boolean_t bool_val;
uint64_t num_val;
int nvrc;
switch (ikvx->ik_key_id) {
case KI_RDMA_EXTENSIONS:
case KI_IMMEDIATE_DATA:
nvrc = nvpair_value_boolean_value(nvp, &bool_val);
ASSERT(nvrc == 0);
break;
case KI_INITIATOR_RECV_DATA_SEGMENT_LENGTH:
case KI_TARGET_RECV_DATA_SEGMENT_LENGTH:
case KI_MAX_OUTSTANDING_UNEXPECTED_PDUS:
nvrc = nvpair_value_uint64(nvp, &num_val);
ASSERT(nvrc == 0);
break;
default:
break;
}
switch (ikvx->ik_key_id) {
case KI_RDMA_EXTENSIONS:
kvrc = iser_handle_boolean(nvp, bool_val, ikvx, B_TRUE,
request_nvl, response_nvl, negotiated_nvl);
break;
case KI_TARGET_RECV_DATA_SEGMENT_LENGTH:
kvrc = iser_handle_numerical(nvp, num_val, ikvx,
ISER_TARGET_RECV_DATA_SEGMENT_LENGTH_MIN,
ISER_TARGET_RECV_DATA_SEGMENT_LENGTH_MAX,
ISER_TARGET_RECV_DATA_SEGMENT_LENGTH_IMPL_MAX,
request_nvl, response_nvl, negotiated_nvl);
break;
case KI_INITIATOR_RECV_DATA_SEGMENT_LENGTH:
kvrc = iser_handle_numerical(nvp, num_val, ikvx,
ISER_INITIATOR_RECV_DATA_SEGMENT_LENGTH_MIN,
ISER_INITIATOR_RECV_DATA_SEGMENT_LENGTH_MAX,
ISER_INITIATOR_RECV_DATA_SEGMENT_LENGTH_IMPL_MAX,
request_nvl, response_nvl, negotiated_nvl);
break;
case KI_IMMEDIATE_DATA:
kvrc = iser_handle_boolean(nvp, bool_val, ikvx, B_FALSE,
request_nvl, response_nvl, negotiated_nvl);
break;
case KI_MAX_OUTSTANDING_UNEXPECTED_PDUS:
kvrc = iser_handle_numerical(nvp, num_val, ikvx,
ISER_MAX_OUTSTANDING_UNEXPECTED_PDUS_MIN,
ISER_MAX_OUTSTANDING_UNEXPECTED_PDUS_MAX,
ISER_MAX_OUTSTANDING_UNEXPECTED_PDUS_IMPL_MAX,
request_nvl, response_nvl, negotiated_nvl);
break;
default:
kvrc = KV_HANDLED;
break;
}
return (kvrc);
}
static kv_status_t
iser_handle_boolean(nvpair_t *nvp, boolean_t value, const idm_kv_xlate_t *ikvx,
boolean_t iser_value, nvlist_t *request_nvl, nvlist_t *response_nvl,
nvlist_t *negotiated_nvl)
{
kv_status_t kvrc = KV_UNHANDLED;
int nvrc;
boolean_t respond = B_FALSE;
if (value != iser_value) {
value = iser_value;
nvrc = nvlist_add_boolean_value(negotiated_nvl,
ikvx->ik_key_name, value);
if (nvrc == 0) {
kvrc = KV_HANDLED_NO_TRANSIT;
respond = B_TRUE;
}
} else {
nvrc = nvlist_add_nvpair(negotiated_nvl, nvp);
respond = (ikvx->ik_declarative == B_FALSE);
}
if (nvrc == 0 && respond) {
nvrc = nvlist_add_boolean_value(response_nvl,
ikvx->ik_key_name, value);
(void) nvlist_remove_all(request_nvl, ikvx->ik_key_name);
}
if (kvrc == KV_HANDLED_NO_TRANSIT) {
return (kvrc);
}
return (idm_nvstat_to_kvstat(nvrc));
}
static kv_status_t
iser_handle_numerical(nvpair_t *nvp, uint64_t value, const idm_kv_xlate_t *ikvx,
uint64_t min_value, uint64_t max_value, uint64_t iser_max_value,
nvlist_t *request_nvl, nvlist_t *response_nvl, nvlist_t *negotiated_nvl)
{
kv_status_t kvrc = KV_UNHANDLED;
int nvrc;
boolean_t respond = B_FALSE;
if ((value < min_value) || (value > max_value)) {
kvrc = KV_VALUE_ERROR;
} else {
if (value > iser_max_value) {
value = iser_max_value;
nvrc = nvlist_add_uint64(negotiated_nvl,
ikvx->ik_key_name, value);
if (nvrc == 0) {
kvrc = KV_HANDLED_NO_TRANSIT;
respond = B_TRUE;
}
} else {
nvrc = nvlist_add_nvpair(negotiated_nvl, nvp);
respond = (ikvx->ik_declarative == B_FALSE);
}
if (nvrc == 0 && respond) {
nvrc = nvlist_add_uint64(response_nvl,
ikvx->ik_key_name, value);
(void) nvlist_remove_all(request_nvl,
ikvx->ik_key_name);
}
}
if (kvrc == KV_HANDLED_NO_TRANSIT) {
return (kvrc);
}
return (idm_nvstat_to_kvstat(nvrc));
}
static kv_status_t
iser_declare_key_values(idm_conn_t *ic, nvlist_t *config_nvl,
nvlist_t *outgoing_nvl)
{
kv_status_t kvrc;
int nvrc = 0;
int rc;
uint64_t uint64_val;
if ((rc = nvlist_lookup_uint64(config_nvl,
ISER_KV_KEY_NAME_MAX_OUTSTANDING_PDU, &uint64_val)) != ENOENT) {
ASSERT(rc == 0);
if (outgoing_nvl) {
nvrc = nvlist_add_uint64(outgoing_nvl,
ISER_KV_KEY_NAME_MAX_OUTSTANDING_PDU, uint64_val);
}
}
kvrc = idm_nvstat_to_kvstat(nvrc);
return (kvrc);
}
static void
iser_notice_key_values(idm_conn_t *ic, nvlist_t *negotiated_nvl)
{
iser_conn_t *iser_conn;
boolean_t boolean_val;
uint64_t uint64_val;
int nvrc;
char *digest_choice_string;
iser_conn = (iser_conn_t *)ic->ic_transport_private;
if ((nvrc = nvlist_lookup_string(negotiated_nvl,
"HeaderDigest", &digest_choice_string)) != ENOENT) {
ASSERT(nvrc == 0);
iser_conn->ic_op_params.op_header_digest = B_FALSE;
}
if ((nvrc = nvlist_lookup_string(negotiated_nvl,
"DataDigest", &digest_choice_string)) != ENOENT) {
ASSERT(nvrc == 0);
iser_conn->ic_op_params.op_data_digest = B_FALSE;
}
if ((nvrc = nvlist_lookup_boolean_value(negotiated_nvl,
"RDMAExtensions", &boolean_val)) != ENOENT) {
ASSERT(nvrc == 0);
iser_conn->ic_op_params.op_rdma_extensions = boolean_val;
}
if ((nvrc = nvlist_lookup_boolean_value(negotiated_nvl,
"OFMarker", &boolean_val)) != ENOENT) {
ASSERT(nvrc == 0);
iser_conn->ic_op_params.op_ofmarker = B_FALSE;
}
if ((nvrc = nvlist_lookup_boolean_value(negotiated_nvl,
"IFMarker", &boolean_val)) != ENOENT) {
ASSERT(nvrc == 0);
iser_conn->ic_op_params.op_ifmarker = B_FALSE;
}
if ((nvrc = nvlist_lookup_uint64(negotiated_nvl,
"TargetRecvDataSegmentLength", &uint64_val)) != ENOENT) {
ASSERT(nvrc == 0);
iser_conn->ic_op_params.op_target_recv_data_segment_length =
uint64_val;
}
if ((nvrc = nvlist_lookup_uint64(negotiated_nvl,
"InitiatorRecvDataSegmentLength", &uint64_val)) != ENOENT) {
ASSERT(nvrc == 0);
iser_conn->ic_op_params.op_initiator_recv_data_segment_length =
uint64_val;
}
if ((nvrc = nvlist_lookup_uint64(negotiated_nvl,
"MaxOutstandingUnexpectedPDUs", &uint64_val)) != ENOENT) {
ASSERT(nvrc == 0);
iser_conn->ic_op_params.op_max_outstanding_unexpected_pdus =
uint64_val;
}
#ifdef ISER_DEBUG
ASSERT(iser_conn->ic_op_params.op_rdma_extensions == B_TRUE);
ASSERT(iser_conn->ic_op_params.op_header_digest == B_FALSE);
ASSERT(iser_conn->ic_op_params.op_data_digest == B_FALSE);
ASSERT(iser_conn->ic_op_params.op_ofmarker == B_FALSE);
ASSERT(iser_conn->ic_op_params.op_ifmarker == B_FALSE);
#endif
}
static boolean_t
iser_conn_is_capable(idm_conn_req_t *cr, idm_transport_caps_t *caps)
{
return (iser_path_exists(NULL, &cr->cr_ini_dst_addr));
}
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);
idm_pdu_complete(pdu, IDM_STATUS_FAIL);
}
}
static idm_status_t
iser_buf_tx_to_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_to_ini(idt, idb);
if (iser_status != ISER_STATUS_SUCCESS) {
ISER_LOG(CE_WARN, "iser_buf_tx_to_ini: failed "
"iser_xfer_buf_to_ini: idt (0x%p) idb (0x%p)",
(void *) idt, (void *) idb);
idm_buf_tx_to_ini_done(idt, idb, IDM_STATUS_ABORTED);
return (IDM_STATUS_FAIL);
}
mutex_exit(&idt->idt_mutex);
return (idm_status);
}
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);
}
mutex_exit(&idt->idt_mutex);
return (idm_status);
}
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;
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);
}
idb->idb_buf = iser_buf->buf;
idb->idb_buf_private = (void *)iser_buf;
idb->idb_reg_private = (void *)iser_buf->iser_mr;
return (IDM_STATUS_SUCCESS);
}
static void
iser_buf_free(idm_buf_t *buf)
{
iser_buf_t *iser_buf;
iser_buf = buf->idb_buf_private;
kmem_cache_free(iser_buf->cache, iser_buf);
}
static idm_status_t
iser_buf_setup(idm_buf_t *idb)
{
iser_conn_t *iser_conn;
iser_chan_t *iser_chan;
iser_hca_t *iser_hca;
iser_buf_t *iser_buf;
int status;
ASSERT(idb->idb_buf != NULL);
iser_conn = (iser_conn_t *)idb->idb_ic->ic_transport_private;
ASSERT(iser_conn != NULL);
iser_hca = iser_conn->ic_chan->ic_hca;
iser_chan = iser_conn->ic_chan;
ASSERT(iser_chan != NULL);
if (idb->idb_buflen < ISER_BCOPY_THRESHOLD) {
iser_buf =
kmem_cache_alloc(iser_hca->iser_buf_cache, KM_NOSLEEP);
if (iser_buf == NULL) {
status = iser_reg_rdma_mem(iser_chan->ic_hca, idb);
idb->idb_bufalloc = B_FALSE;
return (status);
}
idb->idb_bufptr = idb->idb_buf;
idb->idb_bufbcopy = B_TRUE;
idb->idb_buf = iser_buf->buf;
idb->idb_buf_private = (void *)iser_buf;
idb->idb_reg_private = (void *)iser_buf->iser_mr;
idb->idb_bufalloc = B_TRUE;
return (IDM_STATUS_SUCCESS);
} else {
status = iser_reg_rdma_mem(iser_chan->ic_hca, idb);
idb->idb_bufalloc = B_FALSE;
return (status);
}
}
static void
iser_buf_teardown(idm_buf_t *idb)
{
iser_conn_t *iser_conn;
iser_conn = (iser_conn_t *)idb->idb_ic->ic_transport_private;
iser_dereg_rdma_mem(iser_conn->ic_chan->ic_hca, idb);
}