#include <sys/cpuvar.h>
#include <sys/conf.h>
#include <sys/file.h>
#include <sys/ddi.h>
#include <sys/sunddi.h>
#include <sys/modctl.h>
#include <sys/socket.h>
#include <sys/strsubr.h>
#include <sys/sysmacros.h>
#include <sys/socketvar.h>
#include <netinet/in.h>
#include <sys/idm/idm.h>
#include <sys/idm/idm_so.h>
#define IDM_NAME_VERSION "iSCSI Data Mover"
extern struct mod_ops mod_miscops;
extern struct mod_ops mod_miscops;
static struct modlmisc modlmisc = {
&mod_miscops,
IDM_NAME_VERSION
};
static struct modlinkage modlinkage = {
MODREV_1, (void *)&modlmisc, NULL
};
extern void idm_wd_thread(void *arg);
static int _idm_init(void);
static int _idm_fini(void);
static void idm_buf_bind_in_locked(idm_task_t *idt, idm_buf_t *buf);
static void idm_buf_bind_out_locked(idm_task_t *idt, idm_buf_t *buf);
static void idm_buf_unbind_in_locked(idm_task_t *idt, idm_buf_t *buf);
static void idm_buf_unbind_out_locked(idm_task_t *idt, idm_buf_t *buf);
static stmf_status_t idm_task_abort_one(idm_conn_t *ic, idm_task_t *idt,
idm_abort_type_t abort_type);
static void idm_task_aborted(idm_task_t *idt, idm_status_t status);
static idm_pdu_t *idm_pdu_alloc_common(uint_t hdrlen, uint_t datalen,
int sleepflag);
boolean_t idm_conn_logging = 0;
boolean_t idm_svc_logging = 0;
#ifdef DEBUG
boolean_t idm_pattern_checking = 1;
#else
boolean_t idm_pattern_checking = 0;
#endif
uint32_t idm_max_taskids = IDM_TASKIDS_MAX;
idm_transport_t idm_transport_list[] = {
{IDM_TRANSPORT_TYPE_ISER,
"/devices/ib/iser@0:iser",
NULL,
NULL,
NULL},
{IDM_TRANSPORT_TYPE_SOCKETS,
NULL,
NULL,
NULL,
NULL}
};
idm_global_t idm;
int
_init(void)
{
int rc;
if ((rc = _idm_init()) != 0) {
return (rc);
}
return (mod_install(&modlinkage));
}
int
_fini(void)
{
int rc;
if ((rc = _idm_fini()) != 0) {
return (rc);
}
if ((rc = mod_remove(&modlinkage)) != 0) {
return (rc);
}
return (rc);
}
int
_info(struct modinfo *modinfop)
{
return (mod_info(&modlinkage, modinfop));
}
idm_status_t
idm_transport_register(idm_transport_attr_t *attr)
{
ASSERT(attr->it_ops != NULL);
ASSERT(attr->it_caps != NULL);
switch (attr->type) {
case IDM_TRANSPORT_TYPE_ISER:
idm_transport_list[attr->type].it_ops = attr->it_ops;
idm_transport_list[attr->type].it_caps = attr->it_caps;
return (IDM_STATUS_SUCCESS);
default:
cmn_err(CE_NOTE, "idm: unknown transport type (0x%x) in "
"idm_transport_register", attr->type);
return (IDM_STATUS_SUCCESS);
}
}
idm_status_t
idm_ini_conn_create(idm_conn_req_t *cr, idm_conn_t **new_con)
{
idm_transport_t *it;
idm_conn_t *ic;
int rc;
it = idm_transport_lookup(cr);
retry:
ic = idm_conn_create_common(CONN_TYPE_INI, it->it_type,
&cr->icr_conn_ops);
bcopy(&cr->cr_ini_dst_addr, &ic->ic_ini_dst_addr,
sizeof (cr->cr_ini_dst_addr));
rc = it->it_ops->it_ini_conn_create(cr, ic);
if (rc != IDM_STATUS_SUCCESS) {
idm_conn_destroy_common(ic);
if (it->it_type == IDM_TRANSPORT_TYPE_ISER) {
it = &idm_transport_list[IDM_TRANSPORT_TYPE_SOCKETS];
goto retry;
}
return (IDM_STATUS_FAIL);
}
*new_con = ic;
mutex_enter(&idm.idm_global_mutex);
list_insert_tail(&idm.idm_ini_conn_list, ic);
mutex_exit(&idm.idm_global_mutex);
return (IDM_STATUS_SUCCESS);
}
void
idm_ini_conn_destroy_task(void *ic_void)
{
idm_conn_t *ic = ic_void;
ic->ic_transport_ops->it_ini_conn_destroy(ic);
idm_conn_destroy_common(ic);
}
void
idm_ini_conn_destroy(idm_conn_t *ic)
{
mutex_enter(&idm.idm_global_mutex);
list_remove(&idm.idm_ini_conn_list, ic);
mutex_exit(&idm.idm_global_mutex);
if (taskq_dispatch(idm.idm_global_taskq,
&idm_ini_conn_destroy_task, ic, TQ_SLEEP) == TASKQID_INVALID) {
cmn_err(CE_WARN,
"idm_ini_conn_destroy: Couldn't dispatch task");
}
}
idm_status_t
idm_ini_conn_connect(idm_conn_t *ic)
{
idm_status_t rc;
rc = idm_conn_sm_init(ic);
if (rc != IDM_STATUS_SUCCESS) {
return (ic->ic_conn_sm_status);
}
idm_conn_hold(ic);
idm_conn_event(ic, CE_CONNECT_REQ, (uintptr_t)NULL);
mutex_enter(&ic->ic_state_mutex);
while (!(ic->ic_state_flags & CF_LOGIN_READY) &&
!(ic->ic_state_flags & CF_ERROR)) {
cv_wait(&ic->ic_state_cv, &ic->ic_state_mutex);
}
if (ic->ic_state_flags & CF_ERROR) {
rc = ic->ic_conn_sm_status;
}
mutex_exit(&ic->ic_state_mutex);
idm_conn_rele(ic);
return (rc);
}
void
idm_ini_conn_disconnect(idm_conn_t *ic)
{
idm_conn_event(ic, CE_TRANSPORT_FAIL, (uintptr_t)NULL);
}
void
idm_ini_conn_disconnect_sync(idm_conn_t *ic)
{
mutex_enter(&ic->ic_state_mutex);
if ((ic->ic_state != CS_S9_INIT_ERROR) &&
(ic->ic_state != CS_S11_COMPLETE)) {
idm_conn_event_locked(ic, CE_TRANSPORT_FAIL, (uintptr_t)NULL,
CT_NONE);
while ((ic->ic_state != CS_S9_INIT_ERROR) &&
(ic->ic_state != CS_S11_COMPLETE))
cv_wait(&ic->ic_state_cv, &ic->ic_state_mutex);
}
mutex_exit(&ic->ic_state_mutex);
}
idm_status_t
idm_tgt_svc_create(idm_svc_req_t *sr, idm_svc_t **new_svc)
{
idm_transport_type_t type;
idm_transport_t *it;
idm_svc_t *is;
int rc;
*new_svc = NULL;
is = kmem_zalloc(sizeof (idm_svc_t), KM_SLEEP);
is->is_svc_req = *sr;
mutex_init(&is->is_mutex, NULL, MUTEX_DEFAULT, NULL);
cv_init(&is->is_cv, NULL, CV_DEFAULT, NULL);
mutex_init(&is->is_count_mutex, NULL, MUTEX_DEFAULT, NULL);
cv_init(&is->is_count_cv, NULL, CV_DEFAULT, NULL);
idm_refcnt_init(&is->is_refcnt, is);
idm_transport_setup(sr->sr_li, B_FALSE);
for (type = 0; type < IDM_TRANSPORT_NUM_TYPES; type++) {
it = &idm_transport_list[type];
if (it->it_ops == NULL) {
continue;
}
rc = it->it_ops->it_tgt_svc_create(sr, is);
if (rc != IDM_STATUS_SUCCESS) {
while (type--) {
it = &idm_transport_list[type];
if (it->it_ops == NULL) {
continue;
}
it->it_ops->it_tgt_svc_destroy(is);
}
kmem_free(is, sizeof (idm_svc_t));
return (rc);
}
}
*new_svc = is;
mutex_enter(&idm.idm_global_mutex);
list_insert_tail(&idm.idm_tgt_svc_list, is);
mutex_exit(&idm.idm_global_mutex);
return (IDM_STATUS_SUCCESS);
}
void
idm_tgt_svc_destroy(idm_svc_t *is)
{
idm_transport_type_t type;
idm_transport_t *it;
mutex_enter(&idm.idm_global_mutex);
list_remove(&idm.idm_tgt_svc_list, is);
cv_broadcast(&idm.idm_tgt_svc_cv);
mutex_exit(&idm.idm_global_mutex);
for (type = 0; type < IDM_TRANSPORT_NUM_TYPES; type++) {
it = &idm_transport_list[type];
if (it->it_ops == NULL) {
continue;
}
it->it_ops->it_tgt_svc_destroy(is);
}
idm_refcnt_destroy(&is->is_refcnt);
cv_destroy(&is->is_count_cv);
mutex_destroy(&is->is_count_mutex);
cv_destroy(&is->is_cv);
mutex_destroy(&is->is_mutex);
kmem_free(is, sizeof (idm_svc_t));
}
void
idm_tgt_svc_hold(idm_svc_t *is)
{
idm_refcnt_hold(&is->is_refcnt);
}
void
idm_tgt_svc_rele_and_destroy(idm_svc_t *is)
{
idm_refcnt_rele_and_destroy(&is->is_refcnt,
(idm_refcnt_cb_t *)&idm_tgt_svc_destroy);
}
idm_status_t
idm_tgt_svc_online(idm_svc_t *is)
{
idm_transport_type_t type, last_type;
idm_transport_t *it;
int rc = IDM_STATUS_SUCCESS;
mutex_enter(&is->is_mutex);
if (is->is_online == 0) {
for (type = 0; type < IDM_TRANSPORT_NUM_TYPES; type++) {
it = &idm_transport_list[type];
if (it->it_ops == NULL) {
continue;
}
mutex_exit(&is->is_mutex);
rc = it->it_ops->it_tgt_svc_online(is);
mutex_enter(&is->is_mutex);
if (rc != IDM_STATUS_SUCCESS) {
last_type = type;
break;
}
}
if (rc != IDM_STATUS_SUCCESS) {
for (type = 0; type < last_type; type++) {
it = &idm_transport_list[type];
if (it->it_ops == NULL) {
continue;
}
mutex_exit(&is->is_mutex);
it->it_ops->it_tgt_svc_offline(is);
mutex_enter(&is->is_mutex);
}
} else {
is->is_online = 1;
}
} else {
is->is_online++;
}
mutex_exit(&is->is_mutex);
return (rc);
}
void
idm_tgt_svc_offline(idm_svc_t *is)
{
idm_transport_type_t type;
idm_transport_t *it;
mutex_enter(&is->is_mutex);
is->is_online--;
if (is->is_online == 0) {
for (type = 0; type < IDM_TRANSPORT_NUM_TYPES; type++) {
it = &idm_transport_list[type];
if (it->it_ops == NULL) {
continue;
}
mutex_exit(&is->is_mutex);
it->it_ops->it_tgt_svc_offline(is);
mutex_enter(&is->is_mutex);
}
}
mutex_exit(&is->is_mutex);
}
idm_svc_t *
idm_tgt_svc_lookup(uint16_t port)
{
idm_svc_t *result;
retry:
mutex_enter(&idm.idm_global_mutex);
for (result = list_head(&idm.idm_tgt_svc_list);
result != NULL;
result = list_next(&idm.idm_tgt_svc_list, result)) {
if (result->is_svc_req.sr_port == port) {
if (result->is_online == 0) {
cv_wait(&idm.idm_tgt_svc_cv,
&idm.idm_global_mutex);
mutex_exit(&idm.idm_global_mutex);
goto retry;
}
idm_tgt_svc_hold(result);
mutex_exit(&idm.idm_global_mutex);
return (result);
}
}
mutex_exit(&idm.idm_global_mutex);
return (NULL);
}
kv_status_t
idm_negotiate_key_values(idm_conn_t *ic, nvlist_t *request_nvl,
nvlist_t *response_nvl, nvlist_t *negotiated_nvl)
{
ASSERT(ic->ic_transport_ops != NULL);
return (ic->ic_transport_ops->it_negotiate_key_values(ic,
request_nvl, response_nvl, negotiated_nvl));
}
void
idm_notice_key_values(idm_conn_t *ic, nvlist_t *negotiated_nvl)
{
ASSERT(ic->ic_transport_ops != NULL);
ic->ic_transport_ops->it_notice_key_values(ic, negotiated_nvl);
}
kv_status_t
idm_declare_key_values(idm_conn_t *ic, nvlist_t *config_nvl,
nvlist_t *outgoing_nvl)
{
ASSERT(ic->ic_transport_ops != NULL);
return (ic->ic_transport_ops->it_declare_key_values(ic, config_nvl,
outgoing_nvl));
}
idm_status_t
idm_buf_tx_to_ini(idm_task_t *idt, idm_buf_t *idb,
uint32_t offset, uint32_t xfer_len,
idm_buf_cb_t idb_buf_cb, void *cb_arg)
{
idm_status_t rc;
idb->idb_bufoffset = offset;
idb->idb_xfer_len = xfer_len;
idb->idb_buf_cb = idb_buf_cb;
idb->idb_cb_arg = cb_arg;
gethrestime(&idb->idb_xfer_start);
IDM_BUFPAT_CHECK(idb, xfer_len, BP_CHECK_ASSERT);
mutex_enter(&idt->idt_mutex);
switch (idt->idt_state) {
case TASK_ACTIVE:
idt->idt_tx_to_ini_start++;
idm_task_hold(idt);
idm_buf_bind_in_locked(idt, idb);
idb->idb_in_transport = B_TRUE;
rc = (*idt->idt_ic->ic_transport_ops->it_buf_tx_to_ini)
(idt, idb);
return (rc);
case TASK_SUSPENDING:
case TASK_SUSPENDED:
idm_buf_bind_in_locked(idt, idb);
mutex_exit(&idt->idt_mutex);
return (IDM_STATUS_SUCCESS);
case TASK_ABORTING:
case TASK_ABORTED:
mutex_exit(&idt->idt_mutex);
return (IDM_STATUS_SUCCESS);
default:
ASSERT(0);
break;
}
mutex_exit(&idt->idt_mutex);
return (IDM_STATUS_FAIL);
}
idm_status_t
idm_buf_rx_from_ini(idm_task_t *idt, idm_buf_t *idb,
uint32_t offset, uint32_t xfer_len,
idm_buf_cb_t idb_buf_cb, void *cb_arg)
{
idm_status_t rc;
idb->idb_bufoffset = offset;
idb->idb_xfer_len = xfer_len;
idb->idb_buf_cb = idb_buf_cb;
idb->idb_cb_arg = cb_arg;
gethrestime(&idb->idb_xfer_start);
mutex_enter(&idt->idt_mutex);
switch (idt->idt_state) {
case TASK_ACTIVE:
idt->idt_rx_from_ini_start++;
idm_task_hold(idt);
idm_buf_bind_out_locked(idt, idb);
idb->idb_in_transport = B_TRUE;
rc = (*idt->idt_ic->ic_transport_ops->it_buf_rx_from_ini)
(idt, idb);
return (rc);
case TASK_SUSPENDING:
case TASK_SUSPENDED:
case TASK_ABORTING:
case TASK_ABORTED:
idm_buf_bind_out_locked(idt, idb);
mutex_exit(&idt->idt_mutex);
return (IDM_STATUS_SUCCESS);
default:
ASSERT(0);
break;
}
mutex_exit(&idt->idt_mutex);
return (IDM_STATUS_FAIL);
}
void
idm_buf_tx_to_ini_done(idm_task_t *idt, idm_buf_t *idb, idm_status_t status)
{
ASSERT(mutex_owned(&idt->idt_mutex));
idb->idb_in_transport = B_FALSE;
idb->idb_tx_thread = B_FALSE;
idt->idt_tx_to_ini_done++;
gethrestime(&idb->idb_xfer_done);
idm_task_rele(idt);
idb->idb_status = status;
switch (idt->idt_state) {
case TASK_ACTIVE:
idt->idt_ic->ic_timestamp = ddi_get_lbolt();
idm_buf_unbind_in_locked(idt, idb);
mutex_exit(&idt->idt_mutex);
(*idb->idb_buf_cb)(idb, status);
return;
case TASK_SUSPENDING:
case TASK_SUSPENDED:
case TASK_ABORTING:
case TASK_ABORTED:
break;
default:
ASSERT(0);
}
mutex_exit(&idt->idt_mutex);
}
void
idm_buf_rx_from_ini_done(idm_task_t *idt, idm_buf_t *idb, idm_status_t status)
{
ASSERT(mutex_owned(&idt->idt_mutex));
idb->idb_in_transport = B_FALSE;
idt->idt_rx_from_ini_done++;
gethrestime(&idb->idb_xfer_done);
idm_task_rele(idt);
idb->idb_status = status;
if (status == IDM_STATUS_SUCCESS) {
IDM_BUFPAT_CHECK(idb, idb->idb_xfer_len, BP_CHECK_ASSERT);
}
switch (idt->idt_state) {
case TASK_ACTIVE:
idt->idt_ic->ic_timestamp = ddi_get_lbolt();
idm_buf_unbind_out_locked(idt, idb);
mutex_exit(&idt->idt_mutex);
(*idb->idb_buf_cb)(idb, status);
return;
case TASK_SUSPENDING:
case TASK_SUSPENDED:
case TASK_ABORTING:
case TASK_ABORTED:
break;
default:
ASSERT(0);
}
mutex_exit(&idt->idt_mutex);
}
idm_buf_t *
idm_buf_alloc(idm_conn_t *ic, void *bufptr, uint64_t buflen)
{
idm_buf_t *buf = NULL;
int rc;
ASSERT(ic != NULL);
ASSERT(idm.idm_buf_cache != NULL);
ASSERT(buflen > 0);
mutex_enter(&ic->ic_state_mutex);
if (!ic->ic_ffp) {
mutex_exit(&ic->ic_state_mutex);
return (NULL);
}
idm_conn_hold(ic);
mutex_exit(&ic->ic_state_mutex);
buf = kmem_cache_alloc(idm.idm_buf_cache, KM_NOSLEEP);
if (buf == NULL) {
idm_conn_rele(ic);
return (NULL);
}
buf->idb_ic = ic;
buf->idb_buflen = buflen;
buf->idb_exp_offset = 0;
buf->idb_bufoffset = 0;
buf->idb_xfer_len = 0;
buf->idb_magic = IDM_BUF_MAGIC;
buf->idb_in_transport = B_FALSE;
buf->idb_bufbcopy = B_FALSE;
if (bufptr == NULL) {
rc = ic->ic_transport_ops->it_buf_alloc(buf, buflen);
if (rc != 0) {
idm_conn_rele(ic);
kmem_cache_free(idm.idm_buf_cache, buf);
return (NULL);
}
buf->idb_bufalloc = B_TRUE;
} else {
buf->idb_buf = bufptr;
rc = ic->ic_transport_ops->it_buf_setup(buf);
if (rc != 0) {
idm_conn_rele(ic);
kmem_cache_free(idm.idm_buf_cache, buf);
return (NULL);
}
}
IDM_BUFPAT_SET(buf);
return (buf);
}
void
idm_buf_free(idm_buf_t *buf)
{
idm_conn_t *ic = buf->idb_ic;
buf->idb_task_binding = NULL;
if (buf->idb_bufalloc) {
ic->ic_transport_ops->it_buf_free(buf);
} else {
ic->ic_transport_ops->it_buf_teardown(buf);
}
kmem_cache_free(idm.idm_buf_cache, buf);
idm_conn_rele(ic);
}
void
idm_buf_bind_in(idm_task_t *idt, idm_buf_t *buf)
{
mutex_enter(&idt->idt_mutex);
idm_buf_bind_in_locked(idt, buf);
mutex_exit(&idt->idt_mutex);
}
static void
idm_buf_bind_in_locked(idm_task_t *idt, idm_buf_t *buf)
{
buf->idb_task_binding = idt;
buf->idb_ic = idt->idt_ic;
idm_listbuf_insert(&idt->idt_inbufv, buf);
}
void
idm_buf_bind_out(idm_task_t *idt, idm_buf_t *buf)
{
if (buf->idb_bufbcopy == B_TRUE) {
bcopy(buf->idb_bufptr, buf->idb_buf, buf->idb_buflen);
}
mutex_enter(&idt->idt_mutex);
idm_buf_bind_out_locked(idt, buf);
mutex_exit(&idt->idt_mutex);
}
static void
idm_buf_bind_out_locked(idm_task_t *idt, idm_buf_t *buf)
{
buf->idb_task_binding = idt;
buf->idb_ic = idt->idt_ic;
idm_listbuf_insert(&idt->idt_outbufv, buf);
}
void
idm_buf_unbind_in(idm_task_t *idt, idm_buf_t *buf)
{
if (buf->idb_bufbcopy == B_TRUE) {
bcopy(buf->idb_buf, buf->idb_bufptr, buf->idb_buflen);
}
mutex_enter(&idt->idt_mutex);
idm_buf_unbind_in_locked(idt, buf);
mutex_exit(&idt->idt_mutex);
}
static void
idm_buf_unbind_in_locked(idm_task_t *idt, idm_buf_t *buf)
{
list_remove(&idt->idt_inbufv, buf);
}
void
idm_buf_unbind_out(idm_task_t *idt, idm_buf_t *buf)
{
mutex_enter(&idt->idt_mutex);
idm_buf_unbind_out_locked(idt, buf);
mutex_exit(&idt->idt_mutex);
}
static void
idm_buf_unbind_out_locked(idm_task_t *idt, idm_buf_t *buf)
{
list_remove(&idt->idt_outbufv, buf);
}
idm_buf_t *
idm_buf_find(void *lbuf, size_t data_offset)
{
idm_buf_t *idb;
list_t *lst = (list_t *)lbuf;
for (idb = list_head(lst); idb != NULL; idb = list_next(lst, idb)) {
ASSERT((idb->idb_ic->ic_conn_type == CONN_TYPE_TGT) ||
(idb->idb_bufoffset == 0));
if ((data_offset >= idb->idb_bufoffset) &&
(data_offset < (idb->idb_bufoffset + idb->idb_buflen))) {
return (idb);
}
}
return (NULL);
}
void
idm_bufpat_set(idm_buf_t *idb)
{
idm_bufpat_t *bufpat;
int len, i;
len = idb->idb_buflen;
len = (len / sizeof (idm_bufpat_t)) * sizeof (idm_bufpat_t);
bufpat = idb->idb_buf;
for (i = 0; i < len; i += sizeof (idm_bufpat_t)) {
bufpat->bufpat_idb = idb;
bufpat->bufpat_bufmagic = IDM_BUF_MAGIC;
bufpat->bufpat_offset = i;
bufpat++;
}
}
boolean_t
idm_bufpat_check(idm_buf_t *idb, int check_len, idm_bufpat_check_type_t type)
{
idm_bufpat_t *bufpat;
int len, i;
len = (type == BP_CHECK_QUICK) ? sizeof (idm_bufpat_t) : check_len;
len = (len / sizeof (idm_bufpat_t)) * sizeof (idm_bufpat_t);
ASSERT(len <= idb->idb_buflen);
bufpat = idb->idb_buf;
if (!idb->idb_bufalloc)
return (B_FALSE);
for (i = 0; i < len; i += sizeof (idm_bufpat_t)) {
if (BUFPAT_MATCH(bufpat, idb)) {
IDM_CONN_LOG(CE_WARN, "idm_bufpat_check found: "
"idb %p bufpat %p "
"bufpat_idb=%p bufmagic=%08x offset=%08x",
(void *)idb, (void *)bufpat, bufpat->bufpat_idb,
bufpat->bufpat_bufmagic, bufpat->bufpat_offset);
DTRACE_PROBE2(bufpat__pattern__found,
idm_buf_t *, idb, idm_bufpat_t *, bufpat);
if (type == BP_CHECK_ASSERT) {
ASSERT(0);
}
return (B_TRUE);
}
bufpat++;
}
return (B_FALSE);
}
idm_task_t *
idm_task_alloc(idm_conn_t *ic)
{
idm_task_t *idt;
ASSERT(ic != NULL);
if (!ic->ic_ffp) {
return (NULL);
}
idt = kmem_cache_alloc(idm.idm_task_cache, KM_NOSLEEP);
if (idt == NULL) {
return (NULL);
}
ASSERT(list_is_empty(&idt->idt_inbufv));
ASSERT(list_is_empty(&idt->idt_outbufv));
mutex_enter(&ic->ic_state_mutex);
if (!ic->ic_ffp) {
mutex_exit(&ic->ic_state_mutex);
kmem_cache_free(idm.idm_task_cache, idt);
return (NULL);
}
idm_conn_hold(ic);
mutex_exit(&ic->ic_state_mutex);
idt->idt_state = TASK_IDLE;
idt->idt_ic = ic;
idt->idt_private = NULL;
idt->idt_exp_datasn = 0;
idt->idt_exp_rttsn = 0;
idt->idt_flags = 0;
return (idt);
}
void
idm_task_start(idm_task_t *idt, uintptr_t handle)
{
ASSERT(idt != NULL);
idt->idt_state = TASK_ACTIVE;
idt->idt_client_handle = handle;
idt->idt_tx_to_ini_start = idt->idt_tx_to_ini_done =
idt->idt_rx_from_ini_start = idt->idt_rx_from_ini_done =
idt->idt_tx_bytes = idt->idt_rx_bytes = 0;
}
void
idm_task_done(idm_task_t *idt)
{
ASSERT(idt != NULL);
mutex_enter(&idt->idt_mutex);
idt->idt_state = TASK_IDLE;
mutex_exit(&idt->idt_mutex);
idm_refcnt_wait_ref(&idt->idt_refcnt);
idm_refcnt_reset(&idt->idt_refcnt);
}
void
idm_task_free(idm_task_t *idt)
{
idm_conn_t *ic;
ASSERT(idt != NULL);
ASSERT(idt->idt_refcnt.ir_refcnt == 0);
ASSERT(idt->idt_state == TASK_IDLE);
ic = idt->idt_ic;
list_create(&idt->idt_inbufv, sizeof (idm_buf_t),
offsetof(idm_buf_t, idb_buflink));
list_create(&idt->idt_outbufv, sizeof (idm_buf_t),
offsetof(idm_buf_t, idb_buflink));
kmem_cache_free(idm.idm_task_cache, idt);
idm_conn_rele(ic);
}
static idm_task_t *
idm_task_find_common(idm_conn_t *ic, uint32_t itt, uint32_t ttt,
boolean_t complete)
{
uint32_t tt, client_handle;
idm_task_t *idt;
if (IDM_CONN_ISTGT(ic)) {
tt = ttt;
client_handle = itt;
} else {
tt = itt;
client_handle = ttt;
}
rw_enter(&idm.idm_taskid_table_lock, RW_READER);
if (tt >= idm.idm_taskid_max) {
rw_exit(&idm.idm_taskid_table_lock);
return (NULL);
}
idt = idm.idm_taskid_table[tt];
if (idt != NULL) {
mutex_enter(&idt->idt_mutex);
if ((idt->idt_state != TASK_ACTIVE) ||
(idt->idt_ic != ic) ||
(IDM_CONN_ISTGT(ic) &&
(idt->idt_client_handle != client_handle))) {
if ((idt->idt_ic != ic) &&
(idt->idt_state == TASK_ACTIVE) &&
(IDM_CONN_ISINI(ic) || idt->idt_client_handle ==
client_handle)) {
IDM_CONN_LOG(CE_WARN,
"idm_task_find: wrong connection %p != %p",
(void *)ic, (void *)idt->idt_ic);
}
mutex_exit(&idt->idt_mutex);
rw_exit(&idm.idm_taskid_table_lock);
return (NULL);
}
idm_task_hold(idt);
if (B_TRUE == complete)
idt->idt_state = TASK_COMPLETE;
mutex_exit(&idt->idt_mutex);
}
rw_exit(&idm.idm_taskid_table_lock);
return (idt);
}
idm_task_t *
idm_task_find(idm_conn_t *ic, uint32_t itt, uint32_t ttt)
{
return (idm_task_find_common(ic, itt, ttt, B_FALSE));
}
idm_task_t *
idm_task_find_and_complete(idm_conn_t *ic, uint32_t itt, uint32_t ttt)
{
return (idm_task_find_common(ic, itt, ttt, B_TRUE));
}
void *
idm_task_find_by_handle(idm_conn_t *ic, uintptr_t handle)
{
idm_task_t *idt = NULL;
int idx = 0;
rw_enter(&idm.idm_taskid_table_lock, RW_READER);
for (idx = 0; idx < idm.idm_taskid_max; idx++) {
idt = idm.idm_taskid_table[idx];
if (idt == NULL)
continue;
mutex_enter(&idt->idt_mutex);
if (idt->idt_state != TASK_ACTIVE) {
mutex_exit(&idt->idt_mutex);
continue;
}
if (idt->idt_client_handle == handle) {
idm_task_hold(idt);
mutex_exit(&idt->idt_mutex);
break;
}
mutex_exit(&idt->idt_mutex);
}
rw_exit(&idm.idm_taskid_table_lock);
if ((idt == NULL) || (idx == idm.idm_taskid_max))
return (NULL);
return (idt->idt_private);
}
void
idm_task_hold(idm_task_t *idt)
{
idm_refcnt_hold(&idt->idt_refcnt);
}
void
idm_task_rele(idm_task_t *idt)
{
idm_refcnt_rele(&idt->idt_refcnt);
}
stmf_status_t
idm_task_abort(idm_conn_t *ic, idm_task_t *idt, idm_abort_type_t abort_type)
{
idm_task_t *task;
int idx;
stmf_status_t s = STMF_SUCCESS;
if (idt == NULL) {
ASSERT(!ic->ic_ffp);
rw_enter(&idm.idm_taskid_table_lock, RW_READER);
for (idx = 0; idx < idm.idm_taskid_max; idx++) {
task = idm.idm_taskid_table[idx];
if (task == NULL)
continue;
mutex_enter(&task->idt_mutex);
if ((task->idt_state != TASK_IDLE) &&
(task->idt_state != TASK_COMPLETE) &&
(task->idt_ic == ic)) {
rw_exit(&idm.idm_taskid_table_lock);
s = idm_task_abort_one(ic, task, abort_type);
rw_enter(&idm.idm_taskid_table_lock, RW_READER);
} else
mutex_exit(&task->idt_mutex);
}
rw_exit(&idm.idm_taskid_table_lock);
} else {
mutex_enter(&idt->idt_mutex);
s = idm_task_abort_one(ic, idt, abort_type);
}
return (s);
}
static void
idm_task_abort_unref_cb(void *ref)
{
idm_task_t *idt = ref;
mutex_enter(&idt->idt_mutex);
switch (idt->idt_state) {
case TASK_SUSPENDING:
idt->idt_state = TASK_SUSPENDED;
mutex_exit(&idt->idt_mutex);
idm_task_aborted(idt, IDM_STATUS_SUSPENDED);
return;
case TASK_ABORTING:
idt->idt_state = TASK_ABORTED;
mutex_exit(&idt->idt_mutex);
idm_task_aborted(idt, IDM_STATUS_ABORTED);
return;
default:
mutex_exit(&idt->idt_mutex);
ASSERT(0);
break;
}
}
static stmf_status_t
idm_task_abort_one(idm_conn_t *ic, idm_task_t *idt, idm_abort_type_t abort_type)
{
stmf_status_t s = STMF_SUCCESS;
ASSERT(mutex_owned(&idt->idt_mutex));
switch (idt->idt_state) {
case TASK_ACTIVE:
switch (abort_type) {
case AT_INTERNAL_SUSPEND:
idt->idt_state = TASK_SUSPENDING;
mutex_exit(&idt->idt_mutex);
ic->ic_transport_ops->it_free_task_rsrc(idt);
idm_refcnt_async_wait_ref(&idt->idt_refcnt,
&idm_task_abort_unref_cb);
return (s);
case AT_INTERNAL_ABORT:
case AT_TASK_MGMT_ABORT:
idt->idt_state = TASK_ABORTING;
mutex_exit(&idt->idt_mutex);
ic->ic_transport_ops->it_free_task_rsrc(idt);
idm_refcnt_async_wait_ref(&idt->idt_refcnt,
&idm_task_abort_unref_cb);
return (s);
default:
ASSERT(0);
}
break;
case TASK_SUSPENDING:
switch (abort_type) {
case AT_INTERNAL_SUSPEND:
break;
case AT_INTERNAL_ABORT:
case AT_TASK_MGMT_ABORT:
idt->idt_state = TASK_ABORTING;
break;
default:
ASSERT(0);
}
break;
case TASK_SUSPENDED:
switch (abort_type) {
case AT_INTERNAL_SUSPEND:
break;
case AT_INTERNAL_ABORT:
case AT_TASK_MGMT_ABORT:
idt->idt_state = TASK_ABORTING;
mutex_exit(&idt->idt_mutex);
idm_refcnt_async_wait_ref(&idt->idt_refcnt,
&idm_task_abort_unref_cb);
return (s);
default:
ASSERT(0);
}
break;
case TASK_ABORTING:
case TASK_ABORTED:
switch (abort_type) {
case AT_INTERNAL_SUSPEND:
case AT_INTERNAL_ABORT:
case AT_TASK_MGMT_ABORT:
break;
default:
ASSERT(0);
}
break;
case TASK_COMPLETE:
idm_refcnt_wait_ref(&idt->idt_refcnt);
s = STMF_ABORT_SUCCESS;
break;
default:
ASSERT(0);
}
mutex_exit(&idt->idt_mutex);
return (s);
}
static void
idm_task_aborted(idm_task_t *idt, idm_status_t status)
{
(*idt->idt_ic->ic_conn_ops.icb_task_aborted)(idt, status);
}
void
idm_pdu_tx(idm_pdu_t *pdu)
{
idm_conn_t *ic = pdu->isp_ic;
iscsi_async_evt_hdr_t *async_evt;
mutex_enter(&ic->ic_state_mutex);
if (ic->ic_ffp && (ic->ic_pdu_events == 0)) {
mutex_exit(&ic->ic_state_mutex);
switch (IDM_PDU_OPCODE(pdu)) {
case ISCSI_OP_SCSI_RSP:
DTRACE_ISCSI_2(scsi__response, idm_conn_t *, ic,
iscsi_scsi_rsp_hdr_t *,
(iscsi_scsi_rsp_hdr_t *)pdu->isp_hdr);
idm_pdu_tx_forward(ic, pdu);
return;
case ISCSI_OP_SCSI_TASK_MGT_RSP:
DTRACE_ISCSI_2(task__response, idm_conn_t *, ic,
iscsi_text_rsp_hdr_t *,
(iscsi_text_rsp_hdr_t *)pdu->isp_hdr);
idm_pdu_tx_forward(ic, pdu);
return;
case ISCSI_OP_SCSI_DATA_RSP:
DTRACE_ISCSI_2(data__send, idm_conn_t *, ic,
iscsi_data_rsp_hdr_t *,
(iscsi_data_rsp_hdr_t *)pdu->isp_hdr);
idm_pdu_tx_forward(ic, pdu);
return;
case ISCSI_OP_RTT_RSP:
DTRACE_ISCSI_2(data__request, idm_conn_t *, ic,
iscsi_rtt_hdr_t *,
(iscsi_rtt_hdr_t *)pdu->isp_hdr);
idm_pdu_tx_forward(ic, pdu);
return;
case ISCSI_OP_NOOP_IN:
DTRACE_ISCSI_2(nop__send, idm_conn_t *, ic,
iscsi_nop_in_hdr_t *,
(iscsi_nop_in_hdr_t *)pdu->isp_hdr);
idm_pdu_tx_forward(ic, pdu);
return;
case ISCSI_OP_TEXT_RSP:
DTRACE_ISCSI_2(text__response, idm_conn_t *, ic,
iscsi_text_rsp_hdr_t *,
(iscsi_text_rsp_hdr_t *)pdu->isp_hdr);
idm_pdu_tx_forward(ic, pdu);
return;
case ISCSI_OP_TEXT_CMD:
case ISCSI_OP_NOOP_OUT:
case ISCSI_OP_SCSI_CMD:
case ISCSI_OP_SCSI_DATA:
case ISCSI_OP_SCSI_TASK_MGT_MSG:
idm_pdu_tx_forward(ic, pdu);
return;
default:
break;
}
mutex_enter(&ic->ic_state_mutex);
}
switch (IDM_PDU_OPCODE(pdu)) {
case ISCSI_OP_LOGIN_CMD:
idm_conn_tx_pdu_event(ic, CE_LOGIN_SND, (uintptr_t)pdu);
break;
case ISCSI_OP_LOGIN_RSP:
DTRACE_ISCSI_2(login__response, idm_conn_t *, ic,
iscsi_login_rsp_hdr_t *,
(iscsi_login_rsp_hdr_t *)pdu->isp_hdr);
idm_parse_login_rsp(ic, pdu, B_FALSE);
break;
case ISCSI_OP_LOGOUT_CMD:
idm_parse_logout_req(ic, pdu, B_FALSE);
break;
case ISCSI_OP_LOGOUT_RSP:
DTRACE_ISCSI_2(logout__response, idm_conn_t *, ic,
iscsi_logout_rsp_hdr_t *,
(iscsi_logout_rsp_hdr_t *)pdu->isp_hdr);
idm_parse_logout_rsp(ic, pdu, B_FALSE);
break;
case ISCSI_OP_ASYNC_EVENT:
DTRACE_ISCSI_2(async__send, idm_conn_t *, ic,
iscsi_async_evt_hdr_t *,
(iscsi_async_evt_hdr_t *)pdu->isp_hdr);
async_evt = (iscsi_async_evt_hdr_t *)pdu->isp_hdr;
switch (async_evt->async_event) {
case ISCSI_ASYNC_EVENT_REQUEST_LOGOUT:
idm_conn_tx_pdu_event(ic, CE_ASYNC_LOGOUT_SND,
(uintptr_t)pdu);
break;
case ISCSI_ASYNC_EVENT_DROPPING_CONNECTION:
idm_conn_tx_pdu_event(ic, CE_ASYNC_DROP_CONN_SND,
(uintptr_t)pdu);
break;
case ISCSI_ASYNC_EVENT_DROPPING_ALL_CONNECTIONS:
idm_conn_tx_pdu_event(ic, CE_ASYNC_DROP_ALL_CONN_SND,
(uintptr_t)pdu);
break;
case ISCSI_ASYNC_EVENT_SCSI_EVENT:
case ISCSI_ASYNC_EVENT_PARAM_NEGOTIATION:
default:
idm_conn_tx_pdu_event(ic, CE_MISC_TX,
(uintptr_t)pdu);
break;
}
break;
case ISCSI_OP_SCSI_RSP:
DTRACE_ISCSI_2(scsi__response, idm_conn_t *, ic,
iscsi_scsi_rsp_hdr_t *,
(iscsi_scsi_rsp_hdr_t *)pdu->isp_hdr);
idm_conn_tx_pdu_event(ic, CE_MISC_TX, (uintptr_t)pdu);
break;
case ISCSI_OP_SCSI_TASK_MGT_RSP:
DTRACE_ISCSI_2(task__response, idm_conn_t *, ic,
iscsi_scsi_task_mgt_rsp_hdr_t *,
(iscsi_scsi_task_mgt_rsp_hdr_t *)pdu->isp_hdr);
idm_conn_tx_pdu_event(ic, CE_MISC_TX, (uintptr_t)pdu);
break;
case ISCSI_OP_SCSI_DATA_RSP:
DTRACE_ISCSI_2(data__send, idm_conn_t *, ic,
iscsi_data_rsp_hdr_t *,
(iscsi_data_rsp_hdr_t *)pdu->isp_hdr);
idm_conn_tx_pdu_event(ic, CE_MISC_TX, (uintptr_t)pdu);
break;
case ISCSI_OP_RTT_RSP:
DTRACE_ISCSI_2(data__request, idm_conn_t *, ic,
iscsi_rtt_hdr_t *,
(iscsi_rtt_hdr_t *)pdu->isp_hdr);
idm_conn_tx_pdu_event(ic, CE_MISC_TX, (uintptr_t)pdu);
break;
case ISCSI_OP_NOOP_IN:
DTRACE_ISCSI_2(nop__send, idm_conn_t *, ic,
iscsi_nop_in_hdr_t *,
(iscsi_nop_in_hdr_t *)pdu->isp_hdr);
idm_conn_tx_pdu_event(ic, CE_MISC_TX, (uintptr_t)pdu);
break;
case ISCSI_OP_TEXT_RSP:
DTRACE_ISCSI_2(text__response, idm_conn_t *, ic,
iscsi_text_rsp_hdr_t *,
(iscsi_text_rsp_hdr_t *)pdu->isp_hdr);
idm_conn_tx_pdu_event(ic, CE_MISC_TX, (uintptr_t)pdu);
break;
case ISCSI_OP_SCSI_CMD:
case ISCSI_OP_SCSI_TASK_MGT_MSG:
case ISCSI_OP_SCSI_DATA:
case ISCSI_OP_NOOP_OUT:
case ISCSI_OP_TEXT_CMD:
case ISCSI_OP_SNACK_CMD:
case ISCSI_OP_REJECT_MSG:
default:
idm_conn_tx_pdu_event(ic, CE_MISC_TX, (uintptr_t)pdu);
break;
}
mutex_exit(&ic->ic_state_mutex);
}
static idm_pdu_t *
idm_pdu_alloc_common(uint_t hdrlen, uint_t datalen, int sleepflag)
{
idm_pdu_t *result;
result = kmem_zalloc(sizeof (idm_pdu_t) + hdrlen + datalen, sleepflag);
if (result != NULL) {
result->isp_flags |= IDM_PDU_ALLOC;
result->isp_hdr = (iscsi_hdr_t *)(result + 1);
result->isp_hdrlen = hdrlen;
result->isp_hdrbuflen = hdrlen;
result->isp_transport_hdrlen = 0;
if (datalen != 0)
result->isp_data = (uint8_t *)result->isp_hdr + hdrlen;
result->isp_datalen = datalen;
result->isp_databuflen = datalen;
result->isp_magic = IDM_PDU_MAGIC;
}
return (result);
}
idm_pdu_t *
idm_pdu_alloc(uint_t hdrlen, uint_t datalen)
{
return (idm_pdu_alloc_common(hdrlen, datalen, KM_SLEEP));
}
idm_pdu_t *
idm_pdu_alloc_nosleep(uint_t hdrlen, uint_t datalen)
{
return (idm_pdu_alloc_common(hdrlen, datalen, KM_NOSLEEP));
}
void
idm_pdu_free(idm_pdu_t *pdu)
{
ASSERT(pdu->isp_flags & IDM_PDU_ALLOC);
kmem_free(pdu,
sizeof (idm_pdu_t) + pdu->isp_hdrbuflen + pdu->isp_databuflen);
}
void
idm_pdu_init(idm_pdu_t *pdu, idm_conn_t *ic, void *private, idm_pdu_cb_t *cb)
{
ASSERT((pdu->isp_flags & IDM_PDU_ALLOC) ||
(cb != NULL));
pdu->isp_magic = IDM_PDU_MAGIC;
pdu->isp_ic = ic;
pdu->isp_private = private;
pdu->isp_callback = cb;
}
void
idm_pdu_init_hdr(idm_pdu_t *pdu, uint8_t *hdr, uint_t hdrlen)
{
pdu->isp_hdr = (iscsi_hdr_t *)((void *)hdr);
pdu->isp_hdrlen = hdrlen;
}
void
idm_pdu_init_data(idm_pdu_t *pdu, uint8_t *data, uint_t datalen)
{
pdu->isp_data = data;
pdu->isp_datalen = datalen;
}
void
idm_pdu_complete(idm_pdu_t *pdu, idm_status_t status)
{
if (pdu->isp_callback) {
pdu->isp_status = status;
(*pdu->isp_callback)(pdu, status);
} else {
idm_pdu_free(pdu);
}
}
void
idm_sm_audit_init(sm_audit_buf_t *audit_buf)
{
bzero(audit_buf, sizeof (sm_audit_buf_t));
audit_buf->sab_max_index = SM_AUDIT_BUF_MAX_REC - 1;
}
static
sm_audit_record_t *
idm_sm_audit_common(sm_audit_buf_t *audit_buf, sm_audit_record_type_t r_type,
sm_audit_sm_type_t sm_type,
int current_state)
{
sm_audit_record_t *sar;
sar = audit_buf->sab_records;
sar += audit_buf->sab_index;
audit_buf->sab_index++;
audit_buf->sab_index &= audit_buf->sab_max_index;
sar->sar_type = r_type;
gethrestime(&sar->sar_timestamp);
sar->sar_sm_type = sm_type;
sar->sar_state = current_state;
return (sar);
}
void
idm_sm_audit_event(sm_audit_buf_t *audit_buf,
sm_audit_sm_type_t sm_type, int current_state,
int event, uintptr_t event_info)
{
sm_audit_record_t *sar;
sar = idm_sm_audit_common(audit_buf, SAR_STATE_EVENT,
sm_type, current_state);
sar->sar_event = event;
sar->sar_event_info = event_info;
}
void
idm_sm_audit_state_change(sm_audit_buf_t *audit_buf,
sm_audit_sm_type_t sm_type, int current_state, int new_state)
{
sm_audit_record_t *sar;
sar = idm_sm_audit_common(audit_buf, SAR_STATE_CHANGE,
sm_type, current_state);
sar->sar_new_state = new_state;
}
void
idm_refcnt_init(idm_refcnt_t *refcnt, void *referenced_obj)
{
bzero(refcnt, sizeof (*refcnt));
idm_refcnt_reset(refcnt);
refcnt->ir_referenced_obj = referenced_obj;
bzero(&refcnt->ir_audit_buf, sizeof (refcnt_audit_buf_t));
refcnt->ir_audit_buf.anb_max_index = REFCNT_AUDIT_BUF_MAX_REC - 1;
mutex_init(&refcnt->ir_mutex, NULL, MUTEX_DEFAULT, NULL);
cv_init(&refcnt->ir_cv, NULL, CV_DEFAULT, NULL);
}
void
idm_refcnt_destroy(idm_refcnt_t *refcnt)
{
mutex_enter(&refcnt->ir_mutex);
ASSERT(refcnt->ir_refcnt == 0);
cv_destroy(&refcnt->ir_cv);
mutex_destroy(&refcnt->ir_mutex);
}
void
idm_refcnt_reset(idm_refcnt_t *refcnt)
{
refcnt->ir_waiting = REF_NOWAIT;
refcnt->ir_refcnt = 0;
}
void
idm_refcnt_hold(idm_refcnt_t *refcnt)
{
ASSERT(refcnt->ir_waiting == REF_NOWAIT);
mutex_enter(&refcnt->ir_mutex);
refcnt->ir_refcnt++;
REFCNT_AUDIT(refcnt);
mutex_exit(&refcnt->ir_mutex);
}
static void
idm_refcnt_unref_task(void *refcnt_void)
{
idm_refcnt_t *refcnt = refcnt_void;
REFCNT_AUDIT(refcnt);
(*refcnt->ir_cb)(refcnt->ir_referenced_obj);
}
void
idm_refcnt_rele(idm_refcnt_t *refcnt)
{
mutex_enter(&refcnt->ir_mutex);
ASSERT(refcnt->ir_refcnt > 0);
refcnt->ir_refcnt--;
REFCNT_AUDIT(refcnt);
if (refcnt->ir_waiting == REF_NOWAIT) {
mutex_exit(&refcnt->ir_mutex);
return;
}
if (refcnt->ir_refcnt == 0) {
if (refcnt->ir_waiting == REF_WAIT_ASYNC) {
if (taskq_dispatch(idm.idm_global_taskq,
&idm_refcnt_unref_task, refcnt, TQ_SLEEP) ==
TASKQID_INVALID) {
cmn_err(CE_WARN,
"idm_refcnt_rele: Couldn't dispatch task");
}
} else if (refcnt->ir_waiting == REF_WAIT_SYNC) {
cv_signal(&refcnt->ir_cv);
}
}
mutex_exit(&refcnt->ir_mutex);
}
void
idm_refcnt_rele_and_destroy(idm_refcnt_t *refcnt, idm_refcnt_cb_t *cb_func)
{
mutex_enter(&refcnt->ir_mutex);
ASSERT(refcnt->ir_refcnt > 0);
refcnt->ir_refcnt--;
REFCNT_AUDIT(refcnt);
if (refcnt->ir_refcnt == 0) {
refcnt->ir_cb = cb_func;
refcnt->ir_waiting = REF_WAIT_ASYNC;
if (taskq_dispatch(idm.idm_global_taskq,
&idm_refcnt_unref_task, refcnt, TQ_SLEEP) ==
TASKQID_INVALID) {
cmn_err(CE_WARN,
"idm_refcnt_rele: Couldn't dispatch task");
}
}
mutex_exit(&refcnt->ir_mutex);
}
void
idm_refcnt_wait_ref(idm_refcnt_t *refcnt)
{
mutex_enter(&refcnt->ir_mutex);
refcnt->ir_waiting = REF_WAIT_SYNC;
REFCNT_AUDIT(refcnt);
while (refcnt->ir_refcnt != 0)
cv_wait(&refcnt->ir_cv, &refcnt->ir_mutex);
mutex_exit(&refcnt->ir_mutex);
}
void
idm_refcnt_async_wait_ref(idm_refcnt_t *refcnt, idm_refcnt_cb_t *cb_func)
{
mutex_enter(&refcnt->ir_mutex);
refcnt->ir_waiting = REF_WAIT_ASYNC;
refcnt->ir_cb = cb_func;
REFCNT_AUDIT(refcnt);
if (refcnt->ir_refcnt == 0) {
if (taskq_dispatch(idm.idm_global_taskq,
&idm_refcnt_unref_task, refcnt, TQ_SLEEP) ==
TASKQID_INVALID) {
cmn_err(CE_WARN,
"idm_refcnt_async_wait_ref: "
"Couldn't dispatch task");
}
}
mutex_exit(&refcnt->ir_mutex);
}
void
idm_refcnt_destroy_unref_obj(idm_refcnt_t *refcnt,
idm_refcnt_cb_t *cb_func)
{
mutex_enter(&refcnt->ir_mutex);
if (refcnt->ir_refcnt == 0) {
mutex_exit(&refcnt->ir_mutex);
(*cb_func)(refcnt->ir_referenced_obj);
return;
}
mutex_exit(&refcnt->ir_mutex);
}
int
idm_refcnt_is_held(idm_refcnt_t *refcnt)
{
if (refcnt->ir_refcnt < 0)
return (-1);
if (refcnt->ir_refcnt == 0)
return (0);
if (refcnt->ir_waiting == REF_NOWAIT && refcnt->ir_refcnt > 0)
return (1);
return (2);
}
void
idm_conn_hold(idm_conn_t *ic)
{
idm_refcnt_hold(&ic->ic_refcnt);
}
void
idm_conn_rele(idm_conn_t *ic)
{
idm_refcnt_rele(&ic->ic_refcnt);
}
void
idm_conn_set_target_name(idm_conn_t *ic, char *target_name)
{
(void) strlcpy(ic->ic_target_name, target_name, ISCSI_MAX_NAME_LEN + 1);
}
void
idm_conn_set_initiator_name(idm_conn_t *ic, char *initiator_name)
{
(void) strlcpy(ic->ic_initiator_name, initiator_name,
ISCSI_MAX_NAME_LEN + 1);
}
void
idm_conn_set_isid(idm_conn_t *ic, uint8_t isid[ISCSI_ISID_LEN])
{
(void) snprintf(ic->ic_isid, ISCSI_MAX_ISID_LEN + 1,
"%02x%02x%02x%02x%02x%02x",
isid[0], isid[1], isid[2], isid[3], isid[4], isid[5]);
}
static int
_idm_init(void)
{
rw_init(&idm.idm_taskid_table_lock, NULL, RW_DRIVER, NULL);
mutex_init(&idm.idm_global_mutex, NULL, MUTEX_DEFAULT, NULL);
cv_init(&idm.idm_tgt_svc_cv, NULL, CV_DEFAULT, NULL);
cv_init(&idm.idm_wd_cv, NULL, CV_DEFAULT, NULL);
idm.idm_global_taskq = taskq_create("idm_global_taskq", 1, minclsyspri,
128, 16384, TASKQ_PREPOPULATE);
if (idm.idm_global_taskq == NULL) {
cv_destroy(&idm.idm_wd_cv);
cv_destroy(&idm.idm_tgt_svc_cv);
mutex_destroy(&idm.idm_global_mutex);
rw_destroy(&idm.idm_taskid_table_lock);
return (ENOMEM);
}
idm.idm_wd_thread = thread_create(NULL, 0,
idm_wd_thread, NULL, 0, &p0, TS_RUN, minclsyspri);
if (idm.idm_wd_thread == NULL) {
taskq_destroy(idm.idm_global_taskq);
cv_destroy(&idm.idm_wd_cv);
cv_destroy(&idm.idm_tgt_svc_cv);
mutex_destroy(&idm.idm_global_mutex);
rw_destroy(&idm.idm_taskid_table_lock);
return (ENOMEM);
}
mutex_enter(&idm.idm_global_mutex);
while (!idm.idm_wd_thread_running)
cv_wait(&idm.idm_wd_cv, &idm.idm_global_mutex);
mutex_exit(&idm.idm_global_mutex);
idm.idm_taskid_max = idm_max_taskids;
idm.idm_taskid_table = (idm_task_t **)
kmem_zalloc(idm.idm_taskid_max * sizeof (idm_task_t *), KM_SLEEP);
idm.idm_taskid_next = 0;
idm.idm_buf_cache = kmem_cache_create("idm_buf_cache",
sizeof (idm_buf_t), 8, NULL, NULL, NULL, NULL, NULL, KM_SLEEP);
idm.idm_task_cache = kmem_cache_create("idm_task_cache",
sizeof (idm_task_t) + IDM_TRANSPORT_HEADER_LENGTH, 8,
&idm_task_constructor, &idm_task_destructor,
NULL, NULL, NULL, KM_SLEEP);
list_create(&idm.idm_tgt_svc_list, sizeof (idm_svc_t),
offsetof(idm_svc_t, is_list_node));
list_create(&idm.idm_tgt_conn_list, sizeof (idm_conn_t),
offsetof(idm_conn_t, ic_list_node));
list_create(&idm.idm_ini_conn_list, sizeof (idm_conn_t),
offsetof(idm_conn_t, ic_list_node));
idm_so_init(&idm_transport_list[IDM_TRANSPORT_TYPE_SOCKETS]);
(void) idm_idpool_create(&idm.idm_conn_id_pool);
return (DDI_SUCCESS);
}
static int
_idm_fini(void)
{
if (!list_is_empty(&idm.idm_ini_conn_list) ||
!list_is_empty(&idm.idm_tgt_conn_list) ||
!list_is_empty(&idm.idm_tgt_svc_list)) {
return (EBUSY);
}
mutex_enter(&idm.idm_global_mutex);
idm.idm_wd_thread_running = B_FALSE;
cv_signal(&idm.idm_wd_cv);
mutex_exit(&idm.idm_global_mutex);
thread_join(idm.idm_wd_thread_did);
idm_idpool_destroy(&idm.idm_conn_id_pool);
mutex_enter(&idm.idm_global_mutex);
idm_transport_teardown();
mutex_exit(&idm.idm_global_mutex);
idm_so_fini();
list_destroy(&idm.idm_ini_conn_list);
list_destroy(&idm.idm_tgt_conn_list);
list_destroy(&idm.idm_tgt_svc_list);
kmem_cache_destroy(idm.idm_task_cache);
kmem_cache_destroy(idm.idm_buf_cache);
kmem_free(idm.idm_taskid_table,
idm.idm_taskid_max * sizeof (idm_task_t *));
mutex_destroy(&idm.idm_global_mutex);
cv_destroy(&idm.idm_wd_cv);
cv_destroy(&idm.idm_tgt_svc_cv);
rw_destroy(&idm.idm_taskid_table_lock);
return (0);
}