#include <sys/bootprops.h>
#include "iscsi.h"
#include "persistent.h"
#include "iscsi_targetparam.h"
#define ISCSI_SESS_ENUM_TIMEOUT_DEFAULT 60
#define SCSI_INQUIRY_PQUAL_MASK 0xE0
boolean_t iscsi_sess_logging = B_FALSE;
typedef struct replun_data {
boolean_t lun_valid;
boolean_t lun_found;
uint16_t lun_num;
uint8_t lun_addr_type;
} replun_data_t;
int iscsi_sess_enum_timeout = ISCSI_SESS_ENUM_TIMEOUT_DEFAULT;
int iscsi_sess_max_delay = ISCSI_DEFAULT_MAX_STORM_DELAY;
static const char *iscsi_sess_enum_warn_msgs[] = {
"completed",
"partially successful",
"IO failures",
"submitted",
"unable to submit the enumeration",
"session is gone",
"test unit ready failed"
};
static iscsi_sess_t *iscsi_sess_alloc(iscsi_hba_t *ihp, iscsi_sess_type_t type);
static char *iscsi_sess_event_str(iscsi_sess_event_t event);
static iscsi_status_t iscsi_sess_threads_create(iscsi_sess_t *isp);
static void iscsi_sess_flush(iscsi_sess_t *isp);
static void iscsi_sess_offline_luns(iscsi_sess_t *isp);
static iscsi_status_t retrieve_lundata(uint32_t lun_count, unsigned char *buf,
iscsi_sess_t *isp, uint16_t *lun_data, uint8_t *lun_addr_type);
static void iscsi_sess_state_free(iscsi_sess_t *isp,
iscsi_sess_event_t event, uint32_t event_count);
static void iscsi_sess_state_logged_in(iscsi_sess_t *isp,
iscsi_sess_event_t event, uint32_t event_count);
static void iscsi_sess_state_failed(iscsi_sess_t *isp,
iscsi_sess_event_t event, uint32_t event_count);
static void iscsi_sess_state_in_flush(iscsi_sess_t *isp,
iscsi_sess_event_t event, uint32_t event_count);
static void iscsi_sess_state_flushed(iscsi_sess_t *isp,
iscsi_sess_event_t event, uint32_t event_count);
static void iscsi_sess_enumeration(void *arg);
static iscsi_status_t iscsi_sess_testunitready(iscsi_sess_t *isp,
uint32_t event_count);
static iscsi_status_t iscsi_sess_reportluns(iscsi_sess_t *isp,
uint32_t event_count);
static void iscsi_sess_inquiry(iscsi_sess_t *isp, uint16_t lun_num,
uint8_t lun_addr_type, uint32_t event_count, iscsi_lun_t *ilp);
static void iscsi_sess_update_busy_luns(iscsi_sess_t *isp, boolean_t clear);
static void iscsi_sess_enum_warn(iscsi_sess_t *isp, iscsi_enum_result_t r);
iscsi_sess_t *
iscsi_sess_create(iscsi_hba_t *ihp, iSCSIDiscoveryMethod_t method,
struct sockaddr *addr_dsc, char *target_name, int tpgt, uchar_t isid_lsb,
iscsi_sess_type_t type, uint32_t *oid)
{
iscsi_sess_t *isp = NULL;
int len = 0;
char *tq_name;
char *th_name;
iscsi_status_t status;
len = strlen(target_name);
clean_failed_sess:
if (isp != NULL) {
(void) iscsi_sess_destroy(isp);
}
for (isp = ihp->hba_sess_list; isp; isp = isp->sess_next) {
if ((strcmp((char *)isp->sess_name, target_name) == 0) &&
(isp->sess_isid[5] == isid_lsb)) {
if (isp->sess_tpgt_conf == tpgt) {
*oid = isp->sess_oid;
if (isp->sess_wd_thread != NULL &&
isp->sess_ic_thread != NULL) {
return (isp);
}
if (isp->sess_wd_thread == NULL) {
th_name = kmem_zalloc(
ISCSI_TH_MAX_NAME_LEN, KM_SLEEP);
if (snprintf(th_name,
(ISCSI_TH_MAX_NAME_LEN - 1),
ISCSI_SESS_WD_NAME_FORMAT,
ihp->hba_oid, isp->sess_oid) <
ISCSI_TH_MAX_NAME_LEN) {
isp->sess_wd_thread =
iscsi_thread_create(
ihp->hba_dip,
th_name,
iscsi_wd_thread,
isp);
(void) iscsi_thread_start(
isp->sess_wd_thread);
}
kmem_free(th_name,
ISCSI_TH_MAX_NAME_LEN);
if (isp->sess_wd_thread == NULL) {
goto clean_failed_sess;
}
}
if (isp->sess_ic_thread == NULL) {
status = iscsi_sess_threads_create(isp);
if (status != ISCSI_STATUS_SUCCESS) {
goto clean_failed_sess;
}
}
return (isp);
}
if ((((isp->sess_tpgt_conf == ISCSI_DEFAULT_TPGT) &&
(tpgt != ISCSI_DEFAULT_TPGT)) ||
((isp->sess_tpgt_conf != ISCSI_DEFAULT_TPGT) &&
(tpgt == ISCSI_DEFAULT_TPGT)))) {
return (NULL);
}
}
}
isp = (iscsi_sess_t *)kmem_zalloc(sizeof (iscsi_sess_t), KM_SLEEP);
if (strncmp(target_name, SENDTARGETS_DISCOVERY,
strlen(SENDTARGETS_DISCOVERY))) {
isp->sess_target_oid = iscsi_targetparam_get_oid(
(uchar_t *)target_name);
}
if (method & iSCSIDiscoveryMethodBoot) {
isp->sess_boot = B_TRUE;
} else {
isp->sess_boot = B_FALSE;
}
method = method & ~(iSCSIDiscoveryMethodBoot);
isp->sess_discovered_by = method;
if (addr_dsc == NULL) {
bzero(&isp->sess_discovered_addr,
sizeof (isp->sess_discovered_addr));
} else {
bcopy(addr_dsc, &isp->sess_discovered_addr,
SIZEOF_SOCKADDR(addr_dsc));
}
mutex_enter(&iscsi_oid_mutex);
isp->sess_oid = iscsi_oid++;
*oid = isp->sess_oid;
mutex_exit(&iscsi_oid_mutex);
isp->sess_name_length = 0;
isp->sess_sig = ISCSI_SIG_SESS;
isp->sess_state = ISCSI_SESS_STATE_FREE;
rw_init(&isp->sess_state_rwlock, NULL, RW_DRIVER, NULL);
mutex_init(&isp->sess_reset_mutex, NULL, MUTEX_DRIVER, NULL);
isp->sess_hba = ihp;
isp->sess_isid[0] = ISCSI_SUN_ISID_0;
isp->sess_isid[1] = ISCSI_SUN_ISID_1;
isp->sess_isid[2] = ISCSI_SUN_ISID_2;
isp->sess_isid[3] = ISCSI_SUN_ISID_3;
isp->sess_isid[4] = 0;
isp->sess_isid[5] = isid_lsb;
isp->sess_cmdsn = 1;
isp->sess_expcmdsn = 1;
isp->sess_maxcmdsn = 1;
isp->sess_last_err = NoError;
isp->sess_tsid = 0;
isp->sess_type = type;
isp->sess_reset_in_progress = B_FALSE;
isp->sess_boot_nic_reset = B_FALSE;
idm_sm_audit_init(&isp->sess_state_audit);
bcopy(&ihp->hba_params, &isp->sess_params,
sizeof (iscsi_login_params_t));
bcopy((char *)target_name, isp->sess_name, len);
isp->sess_name_length = len;
isp->sess_tpgt_conf = tpgt;
isp->sess_tpgt_nego = ISCSI_DEFAULT_TPGT;
iscsi_init_queue(&isp->sess_queue_pending);
iscsi_init_queue(&isp->sess_queue_completion);
isp->sess_lun_list = NULL;
rw_init(&isp->sess_lun_list_rwlock, NULL, RW_DRIVER, NULL);
isp->sess_conn_act = NULL;
isp->sess_conn_list = NULL;
rw_init(&isp->sess_conn_list_rwlock, NULL, RW_DRIVER, NULL);
mutex_init(&isp->sess_cmdsn_mutex, NULL, MUTEX_DRIVER, NULL);
tq_name = kmem_zalloc(ISCSI_TH_MAX_NAME_LEN, KM_SLEEP);
if (snprintf(tq_name, (ISCSI_TH_MAX_NAME_LEN - 1),
ISCSI_SESS_LOGIN_TASKQ_NAME_FORMAT, ihp->hba_oid, isp->sess_oid) <
ISCSI_TH_MAX_NAME_LEN) {
isp->sess_login_taskq = ddi_taskq_create(ihp->hba_dip,
tq_name, 1, TASKQ_DEFAULTPRI, 0);
}
if (isp->sess_login_taskq == NULL) {
kmem_free(tq_name, ISCSI_TH_MAX_NAME_LEN);
goto iscsi_sess_cleanup2;
}
if (snprintf(tq_name, (ISCSI_TH_MAX_NAME_LEN - 1),
ISCSI_SESS_ENUM_TASKQ_NAME_FORMAT, ihp->hba_oid, isp->sess_oid) <
ISCSI_TH_MAX_NAME_LEN) {
isp->sess_enum_taskq = ddi_taskq_create(ihp->hba_dip,
tq_name, 1, TASKQ_DEFAULTPRI, 0);
}
kmem_free(tq_name, ISCSI_TH_MAX_NAME_LEN);
if (isp->sess_enum_taskq == NULL) {
goto iscsi_sess_cleanup1;
}
th_name = kmem_zalloc(ISCSI_TH_MAX_NAME_LEN, KM_SLEEP);
if (snprintf(th_name, (ISCSI_TH_MAX_NAME_LEN - 1),
ISCSI_SESS_WD_NAME_FORMAT, ihp->hba_oid, isp->sess_oid) <
ISCSI_TH_MAX_NAME_LEN) {
isp->sess_wd_thread = iscsi_thread_create(ihp->hba_dip,
th_name, iscsi_wd_thread, isp);
(void) iscsi_thread_start(isp->sess_wd_thread);
}
kmem_free(th_name, ISCSI_TH_MAX_NAME_LEN);
if (isp->sess_wd_thread == NULL) {
goto iscsi_sess_cleanup0;
}
status = iscsi_sess_threads_create(isp);
if (status != ISCSI_STATUS_SUCCESS) {
goto iscsi_sess_cleanup1;
}
if (ihp->hba_sess_list == NULL) {
ihp->hba_sess_list = isp;
} else {
isp->sess_next = ihp->hba_sess_list;
ihp->hba_sess_list = isp;
}
KSTAT_INC_HBA_CNTR_SESS(ihp);
(void) iscsi_sess_kstat_init(isp);
if (type == ISCSI_SESS_TYPE_NORMAL) {
isp->sess_enum_status = ISCSI_SESS_ENUM_FREE;
isp->sess_enum_result = ISCSI_SESS_ENUM_COMPLETE;
isp->sess_enum_result_count = 0;
mutex_init(&isp->sess_enum_lock, NULL, MUTEX_DRIVER, NULL);
cv_init(&isp->sess_enum_cv, NULL, CV_DRIVER, NULL);
}
mutex_init(&isp->sess_state_wmutex, NULL, MUTEX_DRIVER, NULL);
cv_init(&isp->sess_state_wcv, NULL, CV_DRIVER, NULL);
isp->sess_state_hasw = B_FALSE;
isp->sess_state_event_count = 0;
return (isp);
iscsi_sess_cleanup0:
ddi_taskq_destroy(isp->sess_enum_taskq);
iscsi_sess_cleanup1:
ddi_taskq_destroy(isp->sess_login_taskq);
iscsi_sess_cleanup2:
if (isp->sess_wd_thread != NULL) {
iscsi_thread_destroy(isp->sess_wd_thread);
isp->sess_wd_thread = NULL;
}
if (isp->sess_ic_thread != NULL) {
iscsi_thread_destroy(isp->sess_ic_thread);
isp->sess_ic_thread = NULL;
}
mutex_destroy(&isp->sess_cmdsn_mutex);
rw_destroy(&isp->sess_conn_list_rwlock);
rw_destroy(&isp->sess_lun_list_rwlock);
iscsi_destroy_queue(&isp->sess_queue_completion);
iscsi_destroy_queue(&isp->sess_queue_pending);
rw_destroy(&isp->sess_state_rwlock);
mutex_destroy(&isp->sess_reset_mutex);
kmem_free(isp, sizeof (iscsi_sess_t));
return (NULL);
}
int
iscsi_sess_get(uint32_t oid, iscsi_hba_t *ihp, iscsi_sess_t **ispp)
{
int rval = 0;
iscsi_sess_t *isp = NULL;
ASSERT(ihp != NULL);
ASSERT(ispp != NULL);
for (isp = ihp->hba_sess_list; isp; isp = isp->sess_next) {
if (isp->sess_oid == oid) {
break;
}
}
if (isp != NULL) {
*ispp = isp;
} else {
rval = EFAULT;
}
return (rval);
}
void
iscsi_sess_online(void *arg)
{
iscsi_sess_t *isp;
iscsi_hba_t *ihp;
iscsi_conn_t *icp;
int idx;
uint32_t event_count;
isp = (iscsi_sess_t *)arg;
ASSERT(isp != NULL);
ihp = isp->sess_hba;
ASSERT(ihp != NULL);
if (ddi_get_lbolt() < isp->sess_failure_lbolt +
SEC_TO_TICK(isp->sess_storm_delay)) {
return;
}
icp = isp->sess_conn_act;
if (icp == NULL) {
icp = isp->sess_conn_list;
for (idx = 0; idx < (isp->sess_isid[5] %
isp->sess_conn_next_cid); idx++) {
ASSERT(icp->conn_next != NULL);
icp = icp->conn_next;
}
isp->sess_conn_act = icp;
}
if (icp == NULL) {
cmn_err(CE_NOTE, "iscsi session(%d) - "
"no connection assigned", isp->sess_oid);
return;
}
mutex_enter(&icp->conn_state_mutex);
if (icp->conn_state == ISCSI_CONN_STATE_FREE) {
while (icp != NULL) {
if (iscsi_conn_online(icp) == ISCSI_STATUS_SUCCESS) {
mutex_exit(&icp->conn_state_mutex);
break;
} else {
mutex_exit(&icp->conn_state_mutex);
icp = icp->conn_next;
if (icp != NULL) {
mutex_enter(&icp->conn_state_mutex);
}
}
}
isp->sess_conn_act = icp;
if (icp == NULL) {
isp->sess_failure_lbolt = ddi_get_lbolt();
if (isp->sess_storm_delay == 0) {
isp->sess_storm_delay++;
} else {
if ((isp->sess_storm_delay * 2) <
iscsi_sess_max_delay) {
isp->sess_storm_delay =
isp->sess_storm_delay * 2;
} else {
isp->sess_storm_delay =
iscsi_sess_max_delay;
}
}
} else {
isp->sess_storm_delay = 0;
isp->sess_failure_lbolt = 0;
}
} else if (icp->conn_state == ISCSI_CONN_STATE_LOGGED_IN) {
mutex_exit(&icp->conn_state_mutex);
event_count = atomic_inc_32_nv(&isp->sess_state_event_count);
iscsi_sess_enter_state_zone(isp);
iscsi_sess_state_machine(isp,
ISCSI_SESS_EVENT_N1, event_count);
iscsi_sess_exit_state_zone(isp);
} else {
mutex_exit(&icp->conn_state_mutex);
}
}
iscsi_status_t
iscsi_sess_destroy(iscsi_sess_t *isp)
{
iscsi_status_t rval = ISCSI_STATUS_SUCCESS;
iscsi_status_t tmprval = ISCSI_STATUS_SUCCESS;
iscsi_hba_t *ihp;
iscsi_sess_t *t_isp;
iscsi_lun_t *ilp;
iscsi_conn_t *icp;
ASSERT(isp != NULL);
ihp = isp->sess_hba;
ASSERT(ihp != NULL);
rw_enter(&isp->sess_lun_list_rwlock, RW_WRITER);
ilp = isp->sess_lun_list;
while (ilp != NULL) {
iscsi_lun_t *ilp_next = ilp->lun_next;
tmprval = iscsi_lun_destroy(ihp, ilp);
if (!ISCSI_SUCCESS(tmprval)) {
rval = tmprval;
}
ilp = ilp_next;
}
rw_exit(&isp->sess_lun_list_rwlock);
if (!ISCSI_SUCCESS(rval)) {
return (rval);
}
rw_enter(&isp->sess_conn_list_rwlock, RW_WRITER);
icp = isp->sess_conn_list;
while (icp != NULL) {
rval = iscsi_conn_offline(icp);
if (ISCSI_SUCCESS(rval)) {
icp = icp->conn_next;
} else {
rw_exit(&isp->sess_conn_list_rwlock);
return (rval);
}
}
rw_exit(&isp->sess_conn_list_rwlock);
ASSERT(isp->sess_state == ISCSI_SESS_STATE_FREE ||
isp->sess_state == ISCSI_SESS_STATE_FAILED);
if (isp->sess_wd_thread) {
iscsi_thread_destroy(isp->sess_wd_thread);
isp->sess_wd_thread = NULL;
}
rw_enter(&isp->sess_conn_list_rwlock, RW_WRITER);
icp = isp->sess_conn_list;
while (icp != NULL) {
rval = iscsi_conn_destroy(icp);
if (!ISCSI_SUCCESS(rval)) {
rw_exit(&isp->sess_conn_list_rwlock);
return (rval);
}
icp = isp->sess_conn_list;
}
rw_exit(&isp->sess_conn_list_rwlock);
if (isp->sess_ic_thread != NULL) {
iscsi_thread_destroy(isp->sess_ic_thread);
isp->sess_ic_thread = NULL;
}
ddi_taskq_destroy(isp->sess_enum_taskq);
ddi_taskq_destroy(isp->sess_login_taskq);
iscsi_destroy_queue(&isp->sess_queue_pending);
iscsi_destroy_queue(&isp->sess_queue_completion);
if (ihp->hba_sess_list == isp) {
ihp->hba_sess_list = isp->sess_next;
} else {
t_isp = ihp->hba_sess_list;
while (t_isp->sess_next != NULL) {
if (t_isp->sess_next == isp) {
break;
}
t_isp = t_isp->sess_next;
}
if (t_isp->sess_next == isp) {
t_isp->sess_next = isp->sess_next;
} else {
ASSERT(FALSE);
}
}
if (isp->sess_type == ISCSI_SESS_TYPE_NORMAL) {
mutex_enter(&isp->sess_enum_lock);
while (isp->sess_enum_result_count > 0) {
cv_wait(&isp->sess_enum_cv, &isp->sess_enum_lock);
}
mutex_exit(&isp->sess_enum_lock);
}
(void) iscsi_sess_kstat_term(isp);
rw_destroy(&isp->sess_lun_list_rwlock);
rw_destroy(&isp->sess_conn_list_rwlock);
mutex_destroy(&isp->sess_cmdsn_mutex);
rw_destroy(&isp->sess_state_rwlock);
mutex_destroy(&isp->sess_reset_mutex);
if (isp->sess_type == ISCSI_SESS_TYPE_NORMAL) {
mutex_destroy(&isp->sess_enum_lock);
cv_destroy(&isp->sess_enum_cv);
}
mutex_destroy(&isp->sess_state_wmutex);
cv_destroy(&isp->sess_state_wcv);
kmem_free(isp, sizeof (iscsi_sess_t));
return (rval);
}
extern ib_boot_prop_t *iscsiboot_prop;
boolean_t
iscsi_sess_set_auth(iscsi_sess_t *isp)
{
char *init_name;
iscsi_chap_props_t *chap = NULL;
iscsi_auth_props_t *auth = NULL;
uchar_t *tmp = NULL;
if (isp == (iscsi_sess_t *)NULL) {
return (B_FALSE);
}
if (isp->sess_hba == (iscsi_hba_t *)NULL) {
return (B_FALSE);
}
init_name = (char *)isp->sess_hba->hba_name;
bzero(&isp->sess_auth, sizeof (iscsi_auth_t));
if (isp->sess_boot == B_FALSE) {
auth = (iscsi_auth_props_t *)kmem_zalloc
(sizeof (iscsi_auth_props_t), KM_SLEEP);
if (persistent_auth_get((char *)isp->sess_name, auth)
!= B_TRUE) {
bzero(auth, sizeof (*auth));
if (persistent_auth_get(init_name, auth) != B_TRUE) {
bzero(auth, sizeof (*auth));
auth->a_auth_method = authMethodNone;
}
auth->a_bi_auth = B_FALSE;
}
chap = (iscsi_chap_props_t *)kmem_zalloc
(sizeof (iscsi_chap_props_t), KM_SLEEP);
if (persistent_chap_get((char *)isp->sess_name, chap)
== B_FALSE) {
int name_len = strlen((char *)isp->sess_name);
bcopy((char *)isp->sess_name, chap->c_user, name_len);
chap->c_user_len = name_len;
(void) (persistent_chap_set((char *)isp->sess_name,
chap));
bzero(chap, sizeof (*chap));
}
if (auth->a_auth_method & authMethodCHAP) {
if (persistent_chap_get(init_name, chap) == B_FALSE) {
kmem_free(chap, sizeof (iscsi_chap_props_t));
isp->sess_auth.password_length = 0;
kmem_free(auth, sizeof (iscsi_auth_props_t));
return (B_FALSE);
}
bcopy(chap->c_user, isp->sess_auth.username,
sizeof (chap->c_user));
bcopy(chap->c_secret, isp->sess_auth.password,
sizeof (chap->c_secret));
isp->sess_auth.password_length = chap->c_secret_len;
} else {
isp->sess_auth.password_length = 0;
}
if (auth->a_auth_method & authMethodCHAP &&
auth->a_bi_auth == B_TRUE) {
isp->sess_auth.bidirectional_auth = 1;
bzero(chap, sizeof (*chap));
if (persistent_chap_get((char *)isp->sess_name, chap)
== B_TRUE) {
bcopy(chap->c_secret,
isp->sess_auth.password_in,
sizeof (chap->c_secret));
bcopy(chap->c_user, isp->sess_auth.username_in,
strlen((char *)chap->c_user));
isp->sess_auth.password_length_in =
chap->c_secret_len;
} else {
}
} else {
isp->sess_auth.bidirectional_auth = 0;
}
if (auth != NULL) {
kmem_free(auth, sizeof (iscsi_auth_props_t));
}
if (chap != NULL) {
kmem_free(chap, sizeof (iscsi_chap_props_t));
}
} else {
if (iscsiboot_prop == NULL) {
return (B_FALSE);
}
if (iscsiboot_prop->boot_init.ini_chap_sec == NULL) {
return (B_FALSE);
}
(void) bcopy(iscsiboot_prop->boot_init.ini_chap_sec,
isp->sess_auth.password,
strlen((char *)iscsiboot_prop->boot_init.ini_chap_sec));
if (iscsiboot_prop->boot_init.ini_chap_name == NULL) {
(void) bcopy(init_name, isp->sess_auth.username,
strlen(init_name));
} else {
tmp = iscsiboot_prop->boot_init.ini_chap_name;
(void) bcopy(tmp,
isp->sess_auth.username, strlen((char *)tmp));
}
isp->sess_auth.password_length =
strlen((char *)iscsiboot_prop->boot_init.ini_chap_sec);
if (iscsiboot_prop->boot_tgt.tgt_chap_sec != NULL) {
tmp = iscsiboot_prop->boot_tgt.tgt_chap_sec;
(void) bcopy(tmp,
isp->sess_auth.password_in, strlen((char *)tmp));
if (iscsiboot_prop->boot_tgt.tgt_chap_name == NULL) {
(void) bcopy(isp->sess_name,
isp->sess_auth.username_in,
isp->sess_name_length);
} else {
tmp = iscsiboot_prop->boot_tgt.tgt_chap_name;
(void) bcopy(tmp,
isp->sess_auth.username_in,
strlen((char *)tmp));
}
tmp = iscsiboot_prop->boot_tgt.tgt_chap_sec;
isp->sess_auth.password_length_in =
strlen((char *)tmp);
isp->sess_auth.bidirectional_auth = 1;
}
}
if ((isp->sess_auth.password_length != 0) ||
(isp->sess_auth.password_length_in != 0)) {
isp->sess_auth.num_auth_buffers = 5;
isp->sess_auth.auth_buffers[0].address =
&(isp->sess_auth.auth_client_block);
isp->sess_auth.auth_buffers[0].length =
sizeof (isp->sess_auth.auth_client_block);
isp->sess_auth.auth_buffers[1].address =
&(isp->sess_auth.auth_recv_string_block);
isp->sess_auth.auth_buffers[1].length =
sizeof (isp->sess_auth.auth_recv_string_block);
isp->sess_auth.auth_buffers[2].address =
&(isp->sess_auth.auth_send_string_block);
isp->sess_auth.auth_buffers[2].length =
sizeof (isp->sess_auth.auth_send_string_block);
isp->sess_auth.auth_buffers[3].address =
&(isp->sess_auth.auth_recv_binary_block);
isp->sess_auth.auth_buffers[3].length =
sizeof (isp->sess_auth.auth_recv_binary_block);
isp->sess_auth.auth_buffers[4].address =
&(isp->sess_auth.auth_send_binary_block);
isp->sess_auth.auth_buffers[4].length =
sizeof (isp->sess_auth.auth_send_binary_block);
}
return (B_TRUE);
}
iscsi_status_t
iscsi_sess_reserve_scsi_itt(iscsi_cmd_t *icmdp)
{
idm_task_t *itp;
iscsi_conn_t *icp = icmdp->cmd_conn;
itp = idm_task_alloc(icp->conn_ic);
if (itp == NULL)
return (ISCSI_STATUS_INTERNAL_ERROR);
itp->idt_private = icmdp;
icmdp->cmd_itp = itp;
icmdp->cmd_itt = itp->idt_tt;
return (ISCSI_STATUS_SUCCESS);
}
void
iscsi_sess_release_scsi_itt(iscsi_cmd_t *icmdp)
{
idm_task_free(icmdp->cmd_itp);
}
iscsi_status_t
iscsi_sess_reserve_itt(iscsi_sess_t *isp, iscsi_cmd_t *icmdp)
{
if (isp->sess_cmd_table_count >= ISCSI_CMD_TABLE_SIZE) {
return (ISCSI_STATUS_ITT_TABLE_FULL);
}
if (isp->sess_itt < IDM_TASKIDS_MAX)
isp->sess_itt = IDM_TASKIDS_MAX;
while (isp->sess_cmd_table[isp->sess_itt %
ISCSI_CMD_TABLE_SIZE] != NULL) {
isp->sess_itt++;
}
icmdp->cmd_itt = isp->sess_itt;
isp->sess_cmd_table[isp->sess_itt %
ISCSI_CMD_TABLE_SIZE] = icmdp;
isp->sess_cmd_table_count++;
isp->sess_itt++;
return (ISCSI_STATUS_SUCCESS);
}
void
iscsi_sess_release_itt(iscsi_sess_t *isp, iscsi_cmd_t *icmdp)
{
int hash_index = (icmdp->cmd_itt % ISCSI_CMD_TABLE_SIZE);
ASSERT(isp->sess_cmd_table[hash_index] != NULL);
isp->sess_cmd_table[hash_index] = NULL;
isp->sess_cmd_table_count--;
}
void
iscsi_sess_redrive_io(iscsi_sess_t *isp)
{
iscsi_conn_t *icp;
ASSERT(isp != NULL);
icp = isp->sess_conn_list;
while (icp != NULL) {
if (ISCSI_CONN_STATE_FULL_FEATURE(
icp->conn_state)) {
(void) iscsi_thread_send_wakeup(
icp->conn_tx_thread);
}
icp = icp->conn_next;
}
}
void
iscsi_sess_state_machine(iscsi_sess_t *isp, iscsi_sess_event_t event,
uint32_t event_count)
{
ASSERT(isp != NULL);
ASSERT(rw_read_locked(&isp->sess_state_rwlock) == 0);
DTRACE_PROBE3(event, iscsi_sess_t *, isp,
char *, iscsi_sess_state_str(isp->sess_state),
char *, iscsi_sess_event_str(event));
idm_sm_audit_event(&isp->sess_state_audit,
SAS_ISCSI_SESS, isp->sess_state, event, 0);
isp->sess_prev_state = isp->sess_state;
isp->sess_state_lbolt = ddi_get_lbolt();
ISCSI_SESS_LOG(CE_NOTE,
"DEBUG: sess_state: isp: %p state: %d event: %d event count: %d",
(void *)isp, isp->sess_state, event, event_count);
switch (isp->sess_state) {
case ISCSI_SESS_STATE_FREE:
iscsi_sess_state_free(isp, event, event_count);
break;
case ISCSI_SESS_STATE_LOGGED_IN:
iscsi_sess_state_logged_in(isp, event, event_count);
break;
case ISCSI_SESS_STATE_FAILED:
iscsi_sess_state_failed(isp, event, event_count);
break;
case ISCSI_SESS_STATE_IN_FLUSH:
iscsi_sess_state_in_flush(isp, event, event_count);
break;
case ISCSI_SESS_STATE_FLUSHED:
iscsi_sess_state_flushed(isp, event, event_count);
break;
default:
ASSERT(FALSE);
}
if (isp->sess_prev_state != isp->sess_state) {
idm_sm_audit_state_change(&isp->sess_state_audit,
SAS_ISCSI_SESS, isp->sess_prev_state, isp->sess_state);
}
}
char *
iscsi_sess_state_str(iscsi_sess_state_t state)
{
switch (state) {
case ISCSI_SESS_STATE_FREE:
return ("free");
case ISCSI_SESS_STATE_LOGGED_IN:
return ("logged_in");
case ISCSI_SESS_STATE_FAILED:
return ("failed");
case ISCSI_SESS_STATE_IN_FLUSH:
return ("in_flush");
case ISCSI_SESS_STATE_FLUSHED:
return ("flushed");
default:
return ("unknown");
}
}
static void
iscsi_sess_state_free(iscsi_sess_t *isp, iscsi_sess_event_t event,
uint32_t event_count)
{
iscsi_hba_t *ihp;
iscsi_enum_result_t enum_result;
ASSERT(isp != NULL);
ihp = isp->sess_hba;
ASSERT(ihp != NULL);
ASSERT(isp->sess_state == ISCSI_SESS_STATE_FREE);
switch (event) {
case ISCSI_SESS_EVENT_N1:
isp->sess_state = ISCSI_SESS_STATE_LOGGED_IN;
rw_downgrade(&isp->sess_state_rwlock);
if (isp->sess_type == ISCSI_SESS_TYPE_NORMAL) {
cmn_err(CE_NOTE,
"!iscsi session(%u) %s online\n",
isp->sess_oid, isp->sess_name);
enum_result =
iscsi_sess_enum_request(isp, B_TRUE,
event_count);
if (enum_result == ISCSI_SESS_ENUM_SUBMITTED) {
enum_result =
iscsi_sess_enum_query(isp);
}
if (enum_result != ISCSI_SESS_ENUM_COMPLETE) {
iscsi_sess_enum_warn(isp, enum_result);
}
}
break;
case ISCSI_SESS_EVENT_N5:
break;
case ISCSI_SESS_EVENT_N6:
case ISCSI_SESS_EVENT_N7:
break;
default:
ASSERT(FALSE);
}
}
static void
iscsi_sess_state_logged_in(iscsi_sess_t *isp, iscsi_sess_event_t event,
uint32_t event_count)
{
iscsi_enum_result_t enum_result;
ASSERT(isp != NULL);
ASSERT(isp->sess_state == ISCSI_SESS_STATE_LOGGED_IN);
switch (event) {
case ISCSI_SESS_EVENT_N1:
if (isp->sess_type == ISCSI_SESS_TYPE_NORMAL) {
rw_downgrade(&isp->sess_state_rwlock);
enum_result =
iscsi_sess_enum_request(isp, B_TRUE, event_count);
if (enum_result == ISCSI_SESS_ENUM_SUBMITTED) {
enum_result = iscsi_sess_enum_query(isp);
}
if (enum_result != ISCSI_SESS_ENUM_COMPLETE) {
iscsi_sess_enum_warn(isp, enum_result);
}
}
break;
case ISCSI_SESS_EVENT_N3:
case ISCSI_SESS_EVENT_N5:
if (event == ISCSI_SESS_EVENT_N3) {
isp->sess_state = ISCSI_SESS_STATE_FREE;
} else {
isp->sess_state = ISCSI_SESS_STATE_FAILED;
}
rw_downgrade(&isp->sess_state_rwlock);
isp->sess_tpgt_nego = ISCSI_DEFAULT_TPGT;
iscsi_sess_flush(isp);
if (event == ISCSI_SESS_EVENT_N3) {
if (isp->sess_type == ISCSI_SESS_TYPE_NORMAL) {
cmn_err(CE_NOTE,
"!iscsi session(%u) %s offline\n",
isp->sess_oid, isp->sess_name);
}
iscsi_sess_offline_luns(isp);
}
mutex_enter(&isp->sess_reset_mutex);
isp->sess_reset_in_progress = B_FALSE;
mutex_exit(&isp->sess_reset_mutex);
iscsi_sess_update_busy_luns(isp, B_TRUE);
break;
case ISCSI_SESS_EVENT_N6:
break;
case ISCSI_SESS_EVENT_N7:
isp->sess_state = ISCSI_SESS_STATE_IN_FLUSH;
break;
default:
ASSERT(FALSE);
}
}
static void
iscsi_sess_state_failed(iscsi_sess_t *isp, iscsi_sess_event_t event,
uint32_t event_count)
{
iscsi_hba_t *ihp;
iscsi_enum_result_t enum_result;
ASSERT(isp != NULL);
ihp = isp->sess_hba;
ASSERT(ihp != NULL);
ASSERT(isp->sess_state == ISCSI_SESS_STATE_FAILED);
switch (event) {
case ISCSI_SESS_EVENT_N1:
isp->sess_state = ISCSI_SESS_STATE_LOGGED_IN;
rw_downgrade(&isp->sess_state_rwlock);
if (isp->sess_type == ISCSI_SESS_TYPE_NORMAL) {
enum_result =
iscsi_sess_enum_request(isp, B_TRUE,
event_count);
if (enum_result == ISCSI_SESS_ENUM_SUBMITTED) {
enum_result =
iscsi_sess_enum_query(isp);
}
if (enum_result != ISCSI_SESS_ENUM_COMPLETE) {
iscsi_sess_enum_warn(isp, enum_result);
}
}
break;
case ISCSI_SESS_EVENT_N5:
break;
case ISCSI_SESS_EVENT_N6:
isp->sess_state = ISCSI_SESS_STATE_FREE;
if (isp->sess_type == ISCSI_SESS_TYPE_NORMAL) {
cmn_err(CE_NOTE, "!iscsi session(%u) %s offline\n",
isp->sess_oid, isp->sess_name);
}
rw_downgrade(&isp->sess_state_rwlock);
iscsi_sess_offline_luns(isp);
break;
case ISCSI_SESS_EVENT_N7:
break;
default:
ASSERT(FALSE);
}
}
static void
iscsi_sess_state_in_flush(iscsi_sess_t *isp, iscsi_sess_event_t event,
uint32_t event_count)
{
ASSERT(isp != NULL);
ASSERT(isp->sess_state == ISCSI_SESS_STATE_IN_FLUSH);
switch (event) {
case ISCSI_SESS_EVENT_N1:
break;
case ISCSI_SESS_EVENT_N3:
case ISCSI_SESS_EVENT_N5:
if (event == ISCSI_SESS_EVENT_N3) {
isp->sess_state = ISCSI_SESS_STATE_FREE;
} else {
isp->sess_state = ISCSI_SESS_STATE_FLUSHED;
}
rw_downgrade(&isp->sess_state_rwlock);
isp->sess_tpgt_nego = ISCSI_DEFAULT_TPGT;
iscsi_sess_flush(isp);
if (event == ISCSI_SESS_EVENT_N3) {
if (isp->sess_type == ISCSI_SESS_TYPE_NORMAL) {
cmn_err(CE_NOTE,
"!iscsi session(%u) %s offline\n",
isp->sess_oid, isp->sess_name);
}
iscsi_sess_offline_luns(isp);
}
mutex_enter(&isp->sess_reset_mutex);
isp->sess_reset_in_progress = B_FALSE;
mutex_exit(&isp->sess_reset_mutex);
iscsi_sess_update_busy_luns(isp, B_TRUE);
break;
case ISCSI_SESS_EVENT_N6:
break;
case ISCSI_SESS_EVENT_N7:
break;
default:
ASSERT(FALSE);
}
}
static void
iscsi_sess_state_flushed(iscsi_sess_t *isp, iscsi_sess_event_t event,
uint32_t event_count)
{
iscsi_hba_t *ihp;
iscsi_enum_result_t enum_result;
ASSERT(isp != NULL);
ASSERT(isp->sess_state == ISCSI_SESS_STATE_FLUSHED);
ihp = isp->sess_hba;
ASSERT(ihp != NULL);
switch (event) {
case ISCSI_SESS_EVENT_N1:
isp->sess_state = ISCSI_SESS_STATE_LOGGED_IN;
rw_downgrade(&isp->sess_state_rwlock);
if (isp->sess_type == ISCSI_SESS_TYPE_NORMAL) {
enum_result =
iscsi_sess_enum_request(isp, B_TRUE,
event_count);
if (enum_result == ISCSI_SESS_ENUM_SUBMITTED) {
enum_result =
iscsi_sess_enum_query(isp);
}
if (enum_result != ISCSI_SESS_ENUM_COMPLETE) {
iscsi_sess_enum_warn(isp, enum_result);
}
}
break;
case ISCSI_SESS_EVENT_N6:
isp->sess_state = ISCSI_SESS_STATE_FREE;
rw_downgrade(&isp->sess_state_rwlock);
if (isp->sess_type == ISCSI_SESS_TYPE_NORMAL) {
cmn_err(CE_NOTE, "!iscsi session(%u) %s offline\n",
isp->sess_oid, isp->sess_name);
}
iscsi_sess_offline_luns(isp);
break;
case ISCSI_SESS_EVENT_N7:
break;
default:
ASSERT(FALSE);
}
}
static char *
iscsi_sess_event_str(iscsi_sess_event_t event)
{
switch (event) {
case ISCSI_SESS_EVENT_N1:
return ("N1");
case ISCSI_SESS_EVENT_N3:
return ("N3");
case ISCSI_SESS_EVENT_N5:
return ("N5");
case ISCSI_SESS_EVENT_N6:
return ("N6");
case ISCSI_SESS_EVENT_N7:
return ("N7");
default:
return ("unknown");
}
}
static iscsi_status_t
iscsi_sess_threads_create(iscsi_sess_t *isp)
{
iscsi_hba_t *ihp;
char th_name[ISCSI_TH_MAX_NAME_LEN];
ASSERT(isp != NULL);
ihp = isp->sess_hba;
ASSERT(ihp != NULL);
if (snprintf(th_name, sizeof (th_name) - 1,
ISCSI_SESS_IOTH_NAME_FORMAT, ihp->hba_oid,
isp->sess_oid) >= sizeof (th_name)) {
return (ISCSI_STATUS_INTERNAL_ERROR);
}
isp->sess_ic_thread = iscsi_thread_create(ihp->hba_dip,
th_name, iscsi_ic_thread, isp);
if (isp->sess_ic_thread == NULL) {
return (ISCSI_STATUS_INTERNAL_ERROR);
}
(void) iscsi_thread_start(isp->sess_ic_thread);
return (ISCSI_STATUS_SUCCESS);
}
static void
iscsi_sess_enumeration(void *arg)
{
iscsi_task_t *itp = (iscsi_task_t *)arg;
iscsi_sess_t *isp;
iscsi_status_t rval = ISCSI_STATUS_SUCCESS;
iscsi_enum_result_t enum_result = ISCSI_SESS_ENUM_COMPLETE;
uint32_t event_count = itp->t_event_count;
ASSERT(itp != NULL);
isp = (iscsi_sess_t *)itp->t_arg;
ASSERT(isp != NULL);
rval = iscsi_sess_testunitready(isp, event_count);
if (ISCSI_SUCCESS(rval)) {
rval = iscsi_sess_reportluns(isp, event_count);
if (!ISCSI_SUCCESS(rval)) {
if (isp->sess_lun_list == NULL) {
iscsi_sess_inquiry(isp, 0, 0, event_count,
NULL);
}
}
} else {
enum_result = ISCSI_SESS_ENUM_TUR_FAIL;
}
kmem_free(itp, sizeof (iscsi_task_t));
mutex_enter(&isp->sess_enum_lock);
if (isp->sess_enum_result_count != 0) {
isp->sess_enum_status = ISCSI_SESS_ENUM_DONE;
} else {
isp->sess_enum_status = ISCSI_SESS_ENUM_FREE;
}
isp->sess_enum_result = enum_result;
cv_broadcast(&isp->sess_enum_cv);
mutex_exit(&isp->sess_enum_lock);
}
static iscsi_status_t
iscsi_sess_testunitready(iscsi_sess_t *isp, uint32_t event_count)
{
iscsi_status_t rval = ISCSI_STATUS_SUCCESS;
int retries = 0;
struct uscsi_cmd ucmd;
char cdb[CDB_GROUP0];
ASSERT(isp != NULL);
while ((retries++ < 3) &&
(isp->sess_state_event_count == event_count)) {
bzero(&cdb[0], CDB_GROUP0);
bzero(&ucmd, sizeof (struct uscsi_cmd));
ucmd.uscsi_timeout = iscsi_sess_enum_timeout;
ucmd.uscsi_cdb = &cdb[0];
ucmd.uscsi_cdblen = CDB_GROUP0;
rval = iscsi_handle_passthru(isp, 0, &ucmd);
if (ISCSI_SUCCESS(rval)) {
break;
}
}
return (rval);
}
#define SCSI_REPORTLUNS_ADDRESS_SIZE 8
#define SCSI_REPORTLUNS_ADDRESS_MASK 0xC0
#define SCSI_REPORTLUNS_ADDRESS_PERIPHERAL 0x00
#define SCSI_REPORTLUNS_ADDRESS_FLAT_SPACE 0x40
#define SCSI_REPORTLUNS_ADDRESS_LOGICAL_UNIT 0x80
#define SCSI_REPORTLUNS_ADDRESS_EXTENDED_UNIT 0xC0
#define SCSI_REPORTLUNS_ADDRESS_LOGICAL_UNIT_2B 0x00
#define SCSI_REPORTLUNS_ADDRESS_LOGICAL_UNIT_4B 0x01
#define SCSI_REPORTLUNS_ADDRESS_LOGICAL_UNIT_6B 0x10
#define SCSI_REPORTLUNS_ADDRESS_LOGICAL_UNIT_8B 0x20
#define SCSI_REPORTLUNS_ADDRESS_LOGICAL_UNIT_SIZE 0x30
static iscsi_status_t
iscsi_sess_reportluns(iscsi_sess_t *isp, uint32_t event_count)
{
iscsi_status_t rval = ISCSI_STATUS_SUCCESS;
iscsi_hba_t *ihp;
struct uscsi_cmd ucmd;
unsigned char cdb[CDB_GROUP5];
unsigned char *buf = NULL;
int buf_len = sizeof (struct scsi_inquiry);
uint32_t lun_list_length = 0;
uint16_t lun_num = 0;
uint8_t lun_addr_type = 0;
uint32_t lun_count = 0;
uint32_t lun_start = 0;
uint32_t lun_total = 0;
int retries = 0;
iscsi_lun_t *ilp_next;
iscsi_lun_t *ilp = NULL;
replun_data_t *saved_replun_ptr = NULL;
ASSERT(isp != NULL);
ihp = isp->sess_hba;
ASSERT(ihp != NULL);
while ((retries++ < 3) &&
(isp->sess_state_event_count == event_count)) {
if (buf == NULL) {
buf = kmem_zalloc(buf_len, KM_SLEEP);
}
bzero(&cdb, CDB_GROUP5);
cdb[0] = SCMD_REPORT_LUNS;
cdb[6] = (buf_len & 0xff000000) >> 24;
cdb[7] = (buf_len & 0x00ff0000) >> 16;
cdb[8] = (buf_len & 0x0000ff00) >> 8;
cdb[9] = (buf_len & 0x000000ff);
bzero(&ucmd, sizeof (struct uscsi_cmd));
ucmd.uscsi_flags = USCSI_READ;
ucmd.uscsi_timeout = iscsi_sess_enum_timeout;
ucmd.uscsi_cdb = (char *)&cdb[0];
ucmd.uscsi_cdblen = CDB_GROUP5;
ucmd.uscsi_bufaddr = (char *)buf;
ucmd.uscsi_buflen = buf_len;
rval = iscsi_handle_passthru(isp, 0, &ucmd);
if (ISCSI_SUCCESS(rval) &&
(ucmd.uscsi_status != STATUS_GOOD)) {
rval = ISCSI_STATUS_USCSI_FAILED;
}
if (ISCSI_SUCCESS(rval)) {
lun_list_length = htonl(*(uint32_t *)buf);
if (buf_len >= lun_list_length +
SCSI_REPORTLUNS_ADDRESS_SIZE) {
break;
}
kmem_free(buf, buf_len);
buf = NULL;
buf_len = lun_list_length +
SCSI_REPORTLUNS_ADDRESS_SIZE;
} else {
retries++;
}
}
if (isp->sess_state_event_count != event_count) {
if (buf != NULL) {
kmem_free(buf, buf_len);
buf = NULL;
}
return (rval);
}
if (!ISCSI_SUCCESS(rval)) {
kmem_free(buf, buf_len);
return (rval);
}
lun_total = lun_list_length / SCSI_REPORTLUNS_ADDRESS_SIZE;
saved_replun_ptr = kmem_zalloc(lun_total * sizeof (replun_data_t),
KM_SLEEP);
lun_start = 0;
rw_enter(&isp->sess_lun_list_rwlock, RW_WRITER);
for (ilp = isp->sess_lun_list; ilp; ilp = ilp_next) {
if (isp->sess_state_event_count != event_count)
break;
ilp_next = ilp->lun_next;
for (lun_count = lun_start; lun_count < lun_total;
lun_count++) {
if (lun_count == lun_start &&
saved_replun_ptr[lun_start].lun_found) {
lun_start++;
continue;
}
if ((saved_replun_ptr[lun_count].lun_valid
== B_TRUE) &&
(saved_replun_ptr[lun_count].lun_num
== ilp->lun_num)) {
rw_exit(&isp->sess_lun_list_rwlock);
iscsi_sess_inquiry(isp, ilp->lun_num,
saved_replun_ptr[lun_count].lun_addr_type,
event_count, ilp);
rw_enter(&isp->sess_lun_list_rwlock,
RW_WRITER);
saved_replun_ptr[lun_count].lun_found
= B_TRUE;
break;
} else {
if (retrieve_lundata(lun_count, buf, isp,
&lun_num, &lun_addr_type) !=
ISCSI_STATUS_SUCCESS) {
continue;
}
saved_replun_ptr[lun_count].lun_valid = B_TRUE;
saved_replun_ptr[lun_count].lun_num = lun_num;
saved_replun_ptr[lun_count].lun_addr_type =
lun_addr_type;
if (ilp->lun_num == lun_num) {
rw_exit(&isp->sess_lun_list_rwlock);
iscsi_sess_inquiry(isp, lun_num,
lun_addr_type, event_count, ilp);
rw_enter(&isp->sess_lun_list_rwlock,
RW_WRITER);
saved_replun_ptr[lun_count].lun_found
= B_TRUE;
break;
}
}
}
if (lun_count == lun_total) {
DTRACE_PROBE2(
sess_reportluns_lun_no_longer_exists,
int, ilp->lun_num, int, ilp->lun_state);
(void) iscsi_lun_destroy(ihp, ilp);
}
}
rw_exit(&isp->sess_lun_list_rwlock);
for (lun_count = lun_start; lun_count < lun_total; lun_count++) {
if (saved_replun_ptr[lun_count].lun_valid == B_FALSE) {
if (retrieve_lundata(lun_count, buf, isp,
&lun_num, &lun_addr_type) != ISCSI_STATUS_SUCCESS) {
continue;
}
} else {
if (saved_replun_ptr[lun_count].lun_found == B_TRUE) {
continue;
}
lun_num = saved_replun_ptr[lun_count].lun_num;
lun_addr_type =
saved_replun_ptr[lun_count].lun_addr_type;
}
rw_enter(&isp->sess_lun_list_rwlock, RW_READER);
for (ilp = isp->sess_lun_list; ilp; ilp = ilp->lun_next) {
if (ilp->lun_num == lun_num) {
break;
}
}
rw_exit(&isp->sess_lun_list_rwlock);
if (ilp == NULL) {
iscsi_sess_inquiry(isp, lun_num, lun_addr_type,
event_count, NULL);
} else {
cmn_err(CE_NOTE,
"!Duplicate Lun Number(%d) recieved from "
"Target(%s)", lun_num, isp->sess_name);
}
}
if (buf != NULL) {
kmem_free(buf, buf_len);
}
kmem_free(saved_replun_ptr, lun_total * sizeof (replun_data_t));
return (rval);
}
#define ISCSI_MAX_INQUIRY_BUF_SIZE 0xFF
#define ISCSI_MAX_INQUIRY_RETRIES 3
static void
iscsi_sess_inquiry(iscsi_sess_t *isp, uint16_t lun_num, uint8_t lun_addr_type,
uint32_t event_count, iscsi_lun_t *ilp)
{
iscsi_status_t rval;
struct uscsi_cmd ucmd;
uchar_t cdb[CDB_GROUP0];
uchar_t *inq;
size_t inq_len;
uchar_t *inq83;
size_t inq83_len;
int retries;
ddi_devid_t devid;
char *guid = NULL;
iscsi_hba_t *ihp;
iscsi_status_t status = ISCSI_STATUS_SUCCESS;
boolean_t inq_ready = B_FALSE;
boolean_t inq83_ready = B_FALSE;
boolean_t nochange = B_FALSE;
uchar_t lun_type;
ASSERT(isp != NULL);
ihp = isp->sess_hba;
ASSERT(ihp != NULL);
inq = kmem_zalloc(ISCSI_MAX_INQUIRY_BUF_SIZE, KM_SLEEP);
inq83 = kmem_zalloc(ISCSI_MAX_INQUIRY_BUF_SIZE, KM_SLEEP);
if (ilp == NULL) {
goto sess_inq;
}
if (ilp->lun_addr_type != lun_addr_type) {
goto offline_old;
}
goto sess_inq;
offline_old:
if (isp->sess_state_event_count != event_count) {
goto inq_done;
}
status = iscsi_lun_destroy(ihp, ilp);
if (status != ISCSI_STATUS_SUCCESS) {
cmn_err(CE_WARN, "iscsi session(%u) is unable to offline"
" obsolete logical unit %d", isp->sess_oid, lun_num);
goto inq_done;
}
ilp = NULL;
sess_inq:
if (inq_ready == B_TRUE) {
goto sess_inq83;
}
bzero(&cdb, CDB_GROUP0);
cdb[0] = SCMD_INQUIRY;
cdb[4] = ISCSI_MAX_INQUIRY_BUF_SIZE;
bzero(&ucmd, sizeof (struct uscsi_cmd));
ucmd.uscsi_flags = USCSI_READ;
ucmd.uscsi_timeout = iscsi_sess_enum_timeout;
ucmd.uscsi_cdb = (char *)&cdb[0];
ucmd.uscsi_cdblen = CDB_GROUP0;
ucmd.uscsi_bufaddr = (char *)inq;
ucmd.uscsi_buflen = ISCSI_MAX_INQUIRY_BUF_SIZE;
retries = 0;
while ((retries++ < ISCSI_MAX_INQUIRY_RETRIES) &&
(isp->sess_state_event_count == event_count)) {
rval = iscsi_handle_passthru(isp, lun_num, &ucmd);
if (ISCSI_SUCCESS(rval) &&
(ucmd.uscsi_status != STATUS_GOOD)) {
rval = ISCSI_STATUS_USCSI_FAILED;
}
if (ISCSI_SUCCESS(rval)) {
inq_len = ISCSI_MAX_INQUIRY_BUF_SIZE - ucmd.uscsi_resid;
break;
}
}
if (!ISCSI_SUCCESS(rval)) {
cmn_err(CE_NOTE, "iscsi session(%u) unable to enumerate "
"logical unit - inquiry failed lun %d",
isp->sess_oid, lun_num);
goto inq_done;
}
inq_ready = B_TRUE;
sess_inq83:
if ((inq[0] & SCSI_INQUIRY_PQUAL_MASK) != 0x00) {
if (ilp != NULL) {
goto offline_old;
}
goto inq_done;
}
lun_type = ((struct scsi_inquiry *)inq)->inq_dtype & DTYPE_MASK;
if ((ilp != NULL) && (ilp->lun_type != lun_type)) {
goto offline_old;
}
if (inq83_ready == B_TRUE) {
goto guid_ready;
}
bzero(&cdb, CDB_GROUP0);
cdb[0] = SCMD_INQUIRY;
cdb[1] = 0x01;
cdb[2] = 0x83;
cdb[4] = ISCSI_MAX_INQUIRY_BUF_SIZE;
ucmd.uscsi_flags = USCSI_READ;
ucmd.uscsi_timeout = iscsi_sess_enum_timeout;
ucmd.uscsi_cdb = (char *)&cdb[0];
ucmd.uscsi_cdblen = CDB_GROUP0;
ucmd.uscsi_bufaddr = (char *)inq83;
ucmd.uscsi_buflen = ISCSI_MAX_INQUIRY_BUF_SIZE;
retries = 0;
while ((retries++ < ISCSI_MAX_INQUIRY_RETRIES) &&
(isp->sess_state_event_count == event_count)) {
rval = iscsi_handle_passthru(isp, lun_num, &ucmd);
if (ISCSI_SUCCESS(rval) &&
(ucmd.uscsi_status != STATUS_GOOD)) {
rval = ISCSI_STATUS_USCSI_FAILED;
}
if (ISCSI_SUCCESS(rval)) {
inq83_len = ISCSI_MAX_INQUIRY_BUF_SIZE -
ucmd.uscsi_resid;
break;
}
}
if (ISCSI_SUCCESS(rval)) {
if (ddi_devid_scsi_encode(
DEVID_SCSI_ENCODE_VERSION_LATEST, NULL,
inq, inq_len, NULL, 0, inq83, inq83_len, &devid) ==
DDI_SUCCESS) {
guid = ddi_devid_to_guid(devid);
ddi_devid_free(devid);
}
}
inq83_ready = B_TRUE;
guid_ready:
if (ilp != NULL) {
if ((guid == NULL) && (ilp->lun_guid == NULL)) {
nochange = B_TRUE;
}
if ((guid != NULL) && (ilp->lun_guid != NULL) &&
((strlen(guid) + 1) == ilp->lun_guid_size) &&
(bcmp(guid, ilp->lun_guid, ilp->lun_guid_size) == 0)) {
nochange = B_TRUE;
}
if (nochange != B_TRUE) {
goto offline_old;
}
if (ilp->lun_state & (ISCSI_LUN_STATE_OFFLINE |
ISCSI_LUN_STATE_INVALID)) {
if (isp->sess_state_event_count == event_count) {
(void) iscsi_lun_online(ihp, ilp);
}
}
} else {
if (isp->sess_state_event_count == event_count) {
(void) iscsi_lun_create(isp, lun_num, lun_addr_type,
(struct scsi_inquiry *)inq, guid);
}
}
inq_done:
if (guid != NULL) {
ddi_devid_free_guid(guid);
}
kmem_free(inq, ISCSI_MAX_INQUIRY_BUF_SIZE);
kmem_free(inq83, ISCSI_MAX_INQUIRY_BUF_SIZE);
}
static iscsi_status_t
retrieve_lundata(uint32_t lun_count, unsigned char *buf, iscsi_sess_t *isp,
uint16_t *lun_num, uint8_t *lun_addr_type)
{
uint32_t lun_idx = 0;
ASSERT(lun_num != NULL);
ASSERT(lun_addr_type != NULL);
lun_idx = (lun_count + 1) * SCSI_REPORTLUNS_ADDRESS_SIZE;
switch (buf[lun_idx] & SCSI_REPORTLUNS_ADDRESS_MASK) {
case SCSI_REPORTLUNS_ADDRESS_PERIPHERAL:
case SCSI_REPORTLUNS_ADDRESS_LOGICAL_UNIT:
case SCSI_REPORTLUNS_ADDRESS_FLAT_SPACE:
*lun_addr_type = (buf[lun_idx] &
SCSI_REPORTLUNS_ADDRESS_MASK) >> 6;
*lun_num = (buf[lun_idx] & 0x3F) << 8;
*lun_num |= buf[lun_idx + 1];
return (ISCSI_STATUS_SUCCESS);
default:
cmn_err(CE_NOTE, "iscsi session(%u) unable "
"to enumerate logical units - report "
"luns returned an unsupported format",
isp->sess_oid);
break;
}
return (ISCSI_STATUS_INTERNAL_ERROR);
}
static void
iscsi_sess_flush(iscsi_sess_t *isp)
{
iscsi_cmd_t *icmdp;
ASSERT(isp != NULL);
ASSERT(isp->sess_state != ISCSI_SESS_STATE_LOGGED_IN);
mutex_enter(&isp->sess_queue_pending.mutex);
icmdp = isp->sess_queue_pending.head;
while (icmdp != NULL) {
if (isp->sess_state == ISCSI_SESS_STATE_FAILED) {
mutex_enter(&icmdp->cmd_mutex);
if (icmdp->cmd_type == ISCSI_CMD_TYPE_SCSI) {
icmdp->cmd_un.scsi.pkt_stat |= STAT_ABORTED;
}
mutex_exit(&icmdp->cmd_mutex);
}
iscsi_cmd_state_machine(icmdp,
ISCSI_CMD_EVENT_E7, isp);
icmdp = isp->sess_queue_pending.head;
}
mutex_exit(&isp->sess_queue_pending.mutex);
}
static void
iscsi_sess_offline_luns(iscsi_sess_t *isp)
{
iscsi_lun_t *ilp;
iscsi_hba_t *ihp;
ASSERT(isp != NULL);
ASSERT(isp->sess_state != ISCSI_SESS_STATE_LOGGED_IN);
ihp = isp->sess_hba;
ASSERT(ihp != NULL);
rw_enter(&isp->sess_lun_list_rwlock, RW_READER);
ilp = isp->sess_lun_list;
while (ilp != NULL) {
(void) iscsi_lun_offline(ihp, ilp, B_FALSE);
ilp = ilp->lun_next;
}
rw_exit(&isp->sess_lun_list_rwlock);
}
int
iscsi_sess_get_by_target(uint32_t target_oid, iscsi_hba_t *ihp,
iscsi_sess_t **ispp)
{
int rval = 0;
iscsi_sess_t *isp = NULL;
ASSERT(ihp != NULL);
ASSERT(ispp != NULL);
for (isp = ihp->hba_sess_list; isp; isp = isp->sess_next) {
if (isp->sess_target_oid == target_oid) {
break;
}
}
if (isp != NULL) {
*ispp = isp;
} else {
rval = EFAULT;
}
return (rval);
}
static void
iscsi_sess_update_busy_luns(iscsi_sess_t *isp, boolean_t clear)
{
iscsi_lun_t *ilp;
iscsi_hba_t *ihp;
ASSERT(isp != NULL);
ihp = isp->sess_hba;
ASSERT(ihp != NULL);
rw_enter(&isp->sess_lun_list_rwlock, RW_WRITER);
ilp = isp->sess_lun_list;
while (ilp != NULL) {
if (clear == B_TRUE) {
ilp->lun_state &= ~ISCSI_LUN_STATE_BUSY;
} else {
ilp->lun_state |= ISCSI_LUN_STATE_BUSY;
}
ilp = ilp->lun_next;
}
rw_exit(&isp->sess_lun_list_rwlock);
}
iscsi_enum_result_t
iscsi_sess_enum_request(iscsi_sess_t *isp, boolean_t wait, uint32_t event_count)
{
iscsi_task_t *itp;
itp = kmem_zalloc(sizeof (iscsi_task_t), KM_SLEEP);
itp->t_arg = isp;
itp->t_event_count = event_count;
mutex_enter(&isp->sess_enum_lock);
while ((isp->sess_enum_status != ISCSI_SESS_ENUM_FREE) &&
(isp->sess_enum_status != ISCSI_SESS_ENUM_INPROG)) {
cv_wait(&isp->sess_enum_cv, &isp->sess_enum_lock);
}
if (isp->sess_enum_status == ISCSI_SESS_ENUM_INPROG) {
if (wait == B_TRUE) {
isp->sess_enum_result_count ++;
}
mutex_exit(&isp->sess_enum_lock);
kmem_free(itp, sizeof (iscsi_task_t));
return (ISCSI_SESS_ENUM_SUBMITTED);
}
ASSERT(isp->sess_enum_status == ISCSI_SESS_ENUM_FREE);
ASSERT(isp->sess_enum_result_count == 0);
isp->sess_enum_status = ISCSI_SESS_ENUM_INPROG;
if (ddi_taskq_dispatch(isp->sess_enum_taskq,
iscsi_sess_enumeration, itp, DDI_SLEEP) != DDI_SUCCESS) {
isp->sess_enum_status = ISCSI_SESS_ENUM_FREE;
mutex_exit(&isp->sess_enum_lock);
kmem_free(itp, sizeof (iscsi_task_t));
return (ISCSI_SESS_ENUM_SUBFAIL);
}
if (wait == B_TRUE) {
isp->sess_enum_result_count ++;
}
mutex_exit(&isp->sess_enum_lock);
return (ISCSI_SESS_ENUM_SUBMITTED);
}
iscsi_enum_result_t
iscsi_sess_enum_query(iscsi_sess_t *isp)
{
iscsi_enum_result_t ret = ISCSI_SESS_ENUM_IOFAIL;
mutex_enter(&isp->sess_enum_lock);
while (isp->sess_enum_status != ISCSI_SESS_ENUM_DONE) {
cv_wait(&isp->sess_enum_cv, &isp->sess_enum_lock);
}
ret = isp->sess_enum_result;
isp->sess_enum_result_count --;
if (isp->sess_enum_result_count == 0) {
isp->sess_enum_status = ISCSI_SESS_ENUM_FREE;
cv_broadcast(&isp->sess_enum_cv);
}
mutex_exit(&isp->sess_enum_lock);
return (ret);
}
static void
iscsi_sess_enum_warn(iscsi_sess_t *isp, iscsi_enum_result_t r)
{
cmn_err(CE_WARN, "iscsi session (%u) enumeration fails - %s",
isp->sess_oid, iscsi_sess_enum_warn_msgs[r]);
}
void
iscsi_sess_enter_state_zone(iscsi_sess_t *isp)
{
mutex_enter(&isp->sess_state_wmutex);
while (isp->sess_state_hasw == B_TRUE) {
cv_wait(&isp->sess_state_wcv, &isp->sess_state_wmutex);
}
isp->sess_state_hasw = B_TRUE;
mutex_exit(&isp->sess_state_wmutex);
rw_enter(&isp->sess_state_rwlock, RW_WRITER);
}
void
iscsi_sess_exit_state_zone(iscsi_sess_t *isp)
{
rw_exit(&isp->sess_state_rwlock);
mutex_enter(&isp->sess_state_wmutex);
isp->sess_state_hasw = B_FALSE;
cv_signal(&isp->sess_state_wcv);
mutex_exit(&isp->sess_state_wmutex);
}