#include "iscsi.h"
#include <sys/bootprops.h>
#include <sys/sysevent/eventdefs.h>
#include <sys/sysevent/dev.h>
#define TPGT_EXT_SIZE 5
#define LUN_EXT_SIZE 10
#define ADDR_EXT_SIZE (1 + TPGT_EXT_SIZE + 1 + LUN_EXT_SIZE)
static iscsi_status_t iscsi_lun_virt_create(iscsi_sess_t *isp,
uint16_t lun_num, iscsi_lun_t *ilp, struct scsi_inquiry *inq);
static iscsi_status_t iscsi_lun_phys_create(iscsi_sess_t *isp,
uint16_t lun_num, iscsi_lun_t *ilp, struct scsi_inquiry *inq);
extern dev_info_t *scsi_vhci_dip;
extern ib_boot_prop_t *iscsiboot_prop;
iscsi_status_t
iscsi_lun_create(iscsi_sess_t *isp, uint16_t lun_num, uint8_t lun_addr_type,
struct scsi_inquiry *inq, char *guid)
{
iscsi_status_t rtn = ISCSI_STATUS_INTERNAL_ERROR;
iscsi_hba_t *ihp = NULL;
iscsi_lun_t *ilp = NULL;
iscsi_lun_t *ilp_tmp = NULL;
char *addr = NULL;
uint16_t boot_lun_num = 0;
uint64_t *lun_num_ptr = NULL;
uint32_t oid_tmp = 0;
ASSERT(isp != NULL);
ihp = isp->sess_hba;
ASSERT(ihp != NULL);
mutex_enter(&iscsi_oid_mutex);
oid_tmp = iscsi_oid++;
mutex_exit(&iscsi_oid_mutex);
rw_enter(&isp->sess_lun_list_rwlock, RW_WRITER);
for (ilp_tmp = isp->sess_lun_list; ilp_tmp != NULL;
ilp_tmp = ilp_tmp->lun_next) {
if (ilp_tmp->lun_num == lun_num) {
rw_exit(&isp->sess_lun_list_rwlock);
return (ISCSI_STATUS_SUCCESS);
}
}
addr = kmem_zalloc((strlen((char *)isp->sess_name) +
ADDR_EXT_SIZE + 1), KM_SLEEP);
(void) snprintf(addr,
(strlen((char *)isp->sess_name) +
ADDR_EXT_SIZE + 1),
"%02X%02X%s%04X,%d", isp->sess_isid[4],
isp->sess_isid[5], isp->sess_name,
isp->sess_tpgt_nego & 0xFFFF, lun_num);
ilp = kmem_zalloc(sizeof (iscsi_lun_t), KM_SLEEP);
ilp->lun_sig = ISCSI_SIG_LUN;
ilp->lun_state &= ISCSI_LUN_STATE_CLEAR;
ilp->lun_state |= ISCSI_LUN_STATE_OFFLINE;
ilp->lun_num = lun_num;
ilp->lun_addr_type = lun_addr_type;
ilp->lun_sess = isp;
ilp->lun_addr = addr;
ilp->lun_type = inq->inq_dtype & DTYPE_MASK;
ilp->lun_oid = oid_tmp;
ilp->lun_refcnt = 1;
mutex_init(&ilp->lun_mutex, NULL, MUTEX_DRIVER, NULL);
bcopy(inq->inq_vid, ilp->lun_vid, sizeof (inq->inq_vid));
bcopy(inq->inq_pid, ilp->lun_pid, sizeof (inq->inq_pid));
if (guid != NULL) {
ilp->lun_guid_size = strlen(guid) + 1;
ilp->lun_guid = kmem_zalloc(ilp->lun_guid_size, KM_SLEEP);
(void) strcpy(ilp->lun_guid, guid);
} else {
ilp->lun_guid_size = 0;
ilp->lun_guid = NULL;
}
if (isp->sess_lun_list == NULL) {
isp->sess_lun_list = ilp;
} else {
ilp->lun_next = isp->sess_lun_list;
isp->sess_lun_list = ilp;
}
if ((ihp->hba_mpxio_enabled == B_TRUE) &&
(guid != NULL)) {
rtn = iscsi_lun_virt_create(isp, lun_num, ilp, inq);
}
if (!ISCSI_SUCCESS(rtn)) {
rtn = iscsi_lun_phys_create(isp, lun_num, ilp, inq);
}
if (!ISCSI_SUCCESS(rtn)) {
if (ilp == isp->sess_lun_list) {
isp->sess_lun_list = ilp->lun_next;
} else {
for (ilp_tmp = isp->sess_lun_list; ilp_tmp;
ilp_tmp = ilp_tmp->lun_next) {
if (ilp_tmp->lun_next == ilp) {
ilp_tmp->lun_next = ilp->lun_next;
break;
}
}
}
kmem_free(ilp->lun_addr,
(strlen((char *)isp->sess_name) +
ADDR_EXT_SIZE + 1));
ilp->lun_addr = NULL;
if (ilp->lun_guid != NULL) {
kmem_free(ilp->lun_guid, ilp->lun_guid_size);
ilp->lun_guid = NULL;
}
mutex_destroy(&ilp->lun_mutex);
kmem_free(ilp, sizeof (iscsi_lun_t));
} else {
ilp->lun_state &= ISCSI_LUN_STATE_CLEAR;
ilp->lun_state |= ISCSI_LUN_STATE_ONLINE;
ilp->lun_time_online = ddi_get_time();
if (iscsiboot_prop != NULL && isp->sess_boot == B_TRUE &&
iscsiboot_prop->boot_tgt.lun_online == 0) {
lun_num_ptr =
(uint64_t *)iscsiboot_prop->boot_tgt.tgt_boot_lun;
boot_lun_num = (uint16_t)(*lun_num_ptr);
if (boot_lun_num == ilp->lun_num) {
iscsiboot_prop->boot_tgt.lun_online = 1;
}
}
}
rw_exit(&isp->sess_lun_list_rwlock);
return (rtn);
}
void
iscsi_lun_hold(iscsi_lun_t *ilp)
{
mutex_enter(&ilp->lun_mutex);
ASSERT(ilp->lun_refcnt > 0);
ilp->lun_refcnt++;
mutex_exit(&ilp->lun_mutex);
}
void
iscsi_lun_rele(iscsi_lun_t *ilp)
{
ASSERT(ilp != NULL);
mutex_enter(&ilp->lun_mutex);
ASSERT(ilp->lun_refcnt > 0);
if (--ilp->lun_refcnt == 0) {
iscsi_sess_t *isp;
isp = ilp->lun_sess;
ASSERT(isp != NULL);
kmem_free(ilp->lun_addr, (strlen((char *)isp->sess_name) +
ADDR_EXT_SIZE + 1));
if (ilp->lun_guid != NULL) {
kmem_free(ilp->lun_guid, ilp->lun_guid_size);
}
mutex_destroy(&ilp->lun_mutex);
kmem_free(ilp, sizeof (iscsi_lun_t));
} else {
mutex_exit(&ilp->lun_mutex);
}
}
static void
iscsi_lun_cmd_cancel(iscsi_lun_t *ilp)
{
iscsi_sess_t *isp;
iscsi_cmd_t *icmdp, *nicmdp;
isp = ilp->lun_sess;
rw_enter(&isp->sess_state_rwlock, RW_READER);
mutex_enter(&isp->sess_queue_pending.mutex);
for (icmdp = isp->sess_queue_pending.head;
icmdp; icmdp = nicmdp) {
nicmdp = icmdp->cmd_next;
iscsi_cmd_state_machine(icmdp, ISCSI_CMD_EVENT_E4, isp);
}
mutex_exit(&isp->sess_queue_pending.mutex);
rw_exit(&isp->sess_state_rwlock);
}
iscsi_status_t
iscsi_lun_destroy(iscsi_hba_t *ihp, iscsi_lun_t *ilp)
{
iscsi_status_t status = ISCSI_STATUS_SUCCESS;
iscsi_sess_t *isp = NULL;
iscsi_lun_t *t_ilp = NULL;
ASSERT(ilp != NULL);
isp = ilp->lun_sess;
ASSERT(isp != NULL);
iscsi_lun_cmd_cancel(ilp);
status = iscsi_lun_offline(ihp, ilp, B_TRUE);
if (ISCSI_SUCCESS(status)) {
if (isp->sess_lun_list == ilp) {
isp->sess_lun_list = ilp->lun_next;
} else {
t_ilp = isp->sess_lun_list;
while (t_ilp->lun_next != NULL) {
if (t_ilp->lun_next == ilp) {
break;
}
t_ilp = t_ilp->lun_next;
}
if (t_ilp->lun_next == ilp) {
t_ilp->lun_next = ilp->lun_next;
} else {
ASSERT(FALSE);
}
}
iscsi_lun_rele(ilp);
}
return (status);
}
static iscsi_status_t
iscsi_lun_virt_create(iscsi_sess_t *isp, uint16_t lun_num, iscsi_lun_t *ilp,
struct scsi_inquiry *inq)
{
iscsi_status_t rtn = ISCSI_STATUS_INTERNAL_ERROR;
int mdi_rtn = MDI_FAILURE;
iscsi_hba_t *ihp = NULL;
mdi_pathinfo_t *pip = NULL;
char *nodename = NULL;
char **compatible = NULL;
int ncompatible = 0;
ASSERT(isp != NULL);
ASSERT(ilp != NULL);
ihp = isp->sess_hba;
ASSERT(ihp != NULL);
scsi_hba_nodename_compatible_get(inq, "vhci",
inq->inq_dtype, NULL, &nodename, &compatible, &ncompatible);
if (nodename == NULL) {
cmn_err(CE_WARN, "iscsi driver found no compatible driver "
"for %s lun %d dtype:0x%02x", isp->sess_name, lun_num,
inq->inq_dtype);
return (ISCSI_STATUS_INTERNAL_ERROR);
}
ndi_devi_enter(scsi_vhci_dip);
mdi_rtn = mdi_pi_alloc_compatible(ihp->hba_dip, nodename,
ilp->lun_guid, ilp->lun_addr, compatible, ncompatible,
0, &pip);
if (mdi_rtn == MDI_SUCCESS) {
mdi_pi_set_phci_private(pip, (caddr_t)ilp);
if (mdi_prop_update_string(pip, MDI_GUID,
ilp->lun_guid) != DDI_SUCCESS) {
cmn_err(CE_WARN, "iscsi driver unable to create "
"property for %s lun %d (MDI_GUID)",
isp->sess_name, lun_num);
mdi_rtn = MDI_FAILURE;
goto virt_create_done;
}
if (mdi_prop_update_int(pip, TARGET_PROP,
isp->sess_oid) != DDI_SUCCESS) {
cmn_err(CE_WARN, "iscsi driver unable to create "
"property for %s lun %d (TARGET_PROP)",
isp->sess_name, lun_num);
mdi_rtn = MDI_FAILURE;
goto virt_create_done;
}
if (mdi_prop_update_int(pip, LUN_PROP,
ilp->lun_num) != DDI_SUCCESS) {
cmn_err(CE_WARN, "iscsi driver unable to create "
"property for %s lun %d (LUN_PROP)",
isp->sess_name, lun_num);
mdi_rtn = MDI_FAILURE;
goto virt_create_done;
}
if (mdi_prop_update_string_array(pip, "compatible",
compatible, ncompatible) !=
DDI_PROP_SUCCESS) {
cmn_err(CE_WARN, "iscsi driver unable to create "
"property for %s lun %d (COMPATIBLE)",
isp->sess_name, lun_num);
mdi_rtn = MDI_FAILURE;
goto virt_create_done;
}
mdi_rtn = mdi_pi_online(pip, 0);
if (mdi_rtn == MDI_NOT_SUPPORTED) {
mdi_rtn = MDI_FAILURE;
goto virt_create_done;
}
ilp->lun_pip = pip;
ilp->lun_dip = NULL;
virt_create_done:
if (pip && mdi_rtn != MDI_SUCCESS) {
ilp->lun_pip = NULL;
ilp->lun_dip = NULL;
(void) mdi_prop_remove(pip, NULL);
(void) mdi_pi_free(pip, 0);
} else {
rtn = ISCSI_STATUS_SUCCESS;
}
}
ndi_devi_exit(scsi_vhci_dip);
scsi_hba_nodename_compatible_free(nodename, compatible);
return (rtn);
}
static iscsi_status_t
iscsi_lun_phys_create(iscsi_sess_t *isp, uint16_t lun_num,
iscsi_lun_t *ilp, struct scsi_inquiry *inq)
{
iscsi_status_t rtn = ISCSI_STATUS_INTERNAL_ERROR;
int ndi_rtn = NDI_FAILURE;
iscsi_hba_t *ihp = NULL;
dev_info_t *lun_dip = NULL;
char *nodename = NULL;
char **compatible = NULL;
int ncompatible = 0;
char *scsi_binding_set = NULL;
char instance[32];
ASSERT(isp != NULL);
ASSERT(ilp != NULL);
ihp = isp->sess_hba;
ASSERT(ihp != NULL);
ASSERT(inq != NULL);
if (ddi_prop_lookup_string(DDI_DEV_T_ANY, isp->sess_hba->hba_dip,
DDI_PROP_NOTPROM | DDI_PROP_DONTPASS, "scsi-binding-set",
&scsi_binding_set) != DDI_PROP_SUCCESS) {
scsi_binding_set = NULL;
}
scsi_hba_nodename_compatible_get(inq, scsi_binding_set,
inq->inq_dtype, NULL, &nodename, &compatible, &ncompatible);
if (scsi_binding_set)
ddi_prop_free(scsi_binding_set);
if (nodename == NULL) {
cmn_err(CE_WARN, "iscsi driver found no compatible driver "
"for %s lun %d", isp->sess_name, lun_num);
return (ISCSI_STATUS_INTERNAL_ERROR);
}
ndi_devi_enter(ihp->hba_dip);
ndi_rtn = ndi_devi_alloc(ihp->hba_dip, nodename,
DEVI_SID_NODEID, &lun_dip);
if (ndi_rtn == NDI_SUCCESS) {
if (ndi_prop_update_int(DDI_DEV_T_NONE,
lun_dip, TARGET_PROP, (int)isp->sess_oid) !=
DDI_PROP_SUCCESS) {
cmn_err(CE_WARN, "iscsi driver unable to create "
"property for %s lun %d (TARGET_PROP)",
isp->sess_name, lun_num);
ndi_rtn = NDI_FAILURE;
goto phys_create_done;
}
if (ndi_prop_update_int(DDI_DEV_T_NONE,
lun_dip, LUN_PROP, (int)ilp->lun_num) !=
DDI_PROP_SUCCESS) {
cmn_err(CE_WARN, "iscsi driver unable to create "
"property for %s lun %d (LUN_PROP)",
isp->sess_name, lun_num);
ndi_rtn = NDI_FAILURE;
goto phys_create_done;
}
if (ndi_prop_update_string_array(DDI_DEV_T_NONE,
lun_dip, "compatible", compatible, ncompatible)
!= DDI_PROP_SUCCESS) {
cmn_err(CE_WARN, "iscsi driver unable to create "
"property for %s lun %d (COMPATIBLE)",
isp->sess_name, lun_num);
ndi_rtn = NDI_FAILURE;
goto phys_create_done;
}
phys_create_done:
if (ndi_rtn == NDI_SUCCESS) {
ndi_rtn = ndi_devi_online(lun_dip, 0);
}
if (ndi_rtn == NDI_SUCCESS) {
rtn = ISCSI_STATUS_SUCCESS;
(void) snprintf(instance, 32, "%d",
ddi_get_instance(lun_dip));
(void) ndi_prop_update_string(DDI_DEV_T_NONE,
lun_dip, NDI_GUID, instance);
} else {
cmn_err(CE_WARN, "iscsi driver unable to online "
"%s lun %d", isp->sess_name, lun_num);
ndi_prop_remove_all(lun_dip);
(void) ndi_devi_free(lun_dip);
}
}
ndi_devi_exit(ihp->hba_dip);
ilp->lun_dip = lun_dip;
ilp->lun_pip = NULL;
scsi_hba_nodename_compatible_free(nodename, compatible);
return (rtn);
}
void
iscsi_lun_online(iscsi_hba_t *ihp, iscsi_lun_t *ilp)
{
int rval = 0;
uint64_t *lun_num_ptr = NULL;
uint16_t boot_lun_num = 0;
iscsi_sess_t *isp = NULL;
boolean_t online = B_FALSE;
nvlist_t *attr_list = NULL;
char *pathname = NULL;
dev_info_t *lun_dip = NULL;
ASSERT(ilp != NULL);
ASSERT((ilp->lun_pip != NULL) || (ilp->lun_dip != NULL));
if (ilp->lun_pip != NULL) {
ndi_devi_enter(scsi_vhci_dip);
rval = mdi_pi_online(ilp->lun_pip, 0);
ndi_devi_exit(scsi_vhci_dip);
if (rval == MDI_SUCCESS) {
ilp->lun_state &= ISCSI_LUN_STATE_CLEAR;
ilp->lun_state |= ISCSI_LUN_STATE_ONLINE;
ilp->lun_time_online = ddi_get_time();
online = B_TRUE;
}
} else if (ilp->lun_dip != NULL) {
ndi_devi_enter(ihp->hba_dip);
rval = ndi_devi_online(ilp->lun_dip, 0);
ndi_devi_exit(ihp->hba_dip);
if (rval == NDI_SUCCESS) {
ilp->lun_state &= ISCSI_LUN_STATE_CLEAR;
ilp->lun_state |= ISCSI_LUN_STATE_ONLINE;
ilp->lun_time_online = ddi_get_time();
online = B_TRUE;
}
}
if (iscsiboot_prop != NULL &&
iscsiboot_prop->boot_tgt.lun_online == 0) {
isp = ilp->lun_sess;
if (isp->sess_boot == B_TRUE) {
lun_num_ptr =
(uint64_t *)iscsiboot_prop->boot_tgt.tgt_boot_lun;
boot_lun_num = (uint16_t)(*lun_num_ptr);
if (boot_lun_num == ilp->lun_num) {
iscsiboot_prop->boot_tgt.lun_online = 1;
}
}
}
if (online == B_TRUE && ilp->lun_type == DTYPE_DIRECT) {
if (nvlist_alloc(&attr_list, NV_UNIQUE_NAME_TYPE, KM_SLEEP) !=
DDI_SUCCESS) {
return;
}
if (ilp->lun_pip != NULL) {
lun_dip = mdi_pi_get_client(ilp->lun_pip);
} else {
lun_dip = ilp->lun_dip;
}
pathname = kmem_zalloc(MAXNAMELEN + 1, KM_SLEEP);
(void) ddi_pathname(lun_dip, pathname);
if (nvlist_add_string(attr_list, DEV_PHYS_PATH, pathname) !=
DDI_SUCCESS) {
nvlist_free(attr_list);
kmem_free(pathname, MAXNAMELEN + 1);
return;
}
iscsi_send_sysevent(ihp, EC_DEV_ADD, ESC_DISK, attr_list);
kmem_free(pathname, MAXNAMELEN + 1);
nvlist_free(attr_list);
}
}
iscsi_status_t
iscsi_lun_offline(iscsi_hba_t *ihp, iscsi_lun_t *ilp, boolean_t lun_free)
{
iscsi_status_t status = ISCSI_STATUS_SUCCESS;
dev_info_t *cdip;
char *pathname = NULL;
boolean_t offline = B_FALSE;
nvlist_t *attr_list = NULL;
ASSERT(ilp != NULL);
ASSERT((ilp->lun_pip != NULL) || (ilp->lun_dip != NULL));
if (ilp->lun_pip == NULL)
cdip = ilp->lun_dip;
else
cdip = mdi_pi_get_client(ilp->lun_pip);
if (cdip != NULL && ilp->lun_type == DTYPE_DIRECT) {
pathname = kmem_zalloc(MAXNAMELEN + 1, KM_SLEEP);
(void) ddi_pathname(cdip, pathname);
}
if (ilp->lun_pip != NULL) {
ndi_devi_enter(scsi_vhci_dip);
if (mdi_pi_offline(ilp->lun_pip, 0) == MDI_SUCCESS) {
ilp->lun_state &= ISCSI_LUN_STATE_CLEAR;
ilp->lun_state |= ISCSI_LUN_STATE_OFFLINE;
if (lun_free == B_TRUE) {
(void) mdi_prop_remove(ilp->lun_pip, NULL);
(void) mdi_pi_free(ilp->lun_pip, 0);
}
offline = B_TRUE;
} else {
status = ISCSI_STATUS_BUSY;
if (lun_free == B_FALSE) {
ilp->lun_state |= ISCSI_LUN_STATE_INVALID;
offline = B_TRUE;
}
}
ndi_devi_exit(scsi_vhci_dip);
} else {
int flags = NDI_DEVFS_CLEAN;
ndi_devi_enter(ihp->hba_dip);
if (lun_free == B_TRUE &&
(ilp->lun_state & ISCSI_LUN_STATE_ONLINE))
flags |= NDI_DEVI_REMOVE;
if (ndi_devi_offline(ilp->lun_dip, flags) != NDI_SUCCESS) {
status = ISCSI_STATUS_BUSY;
if (lun_free == B_FALSE) {
ilp->lun_state |= ISCSI_LUN_STATE_INVALID;
offline = B_TRUE;
}
} else {
ilp->lun_state &= ISCSI_LUN_STATE_CLEAR;
ilp->lun_state |= ISCSI_LUN_STATE_OFFLINE;
offline = B_TRUE;
}
ndi_devi_exit(ihp->hba_dip);
}
if (offline == B_TRUE && pathname != NULL &&
ilp->lun_type == DTYPE_DIRECT) {
if (nvlist_alloc(&attr_list, NV_UNIQUE_NAME_TYPE, KM_SLEEP) !=
DDI_SUCCESS) {
kmem_free(pathname, MAXNAMELEN + 1);
return (status);
}
if (nvlist_add_string(attr_list, DEV_PHYS_PATH, pathname) !=
DDI_SUCCESS) {
nvlist_free(attr_list);
kmem_free(pathname, MAXNAMELEN + 1);
return (status);
}
iscsi_send_sysevent(ihp, EC_DEV_REMOVE, ESC_DISK, attr_list);
nvlist_free(attr_list);
}
if (pathname != NULL) {
kmem_free(pathname, MAXNAMELEN + 1);
}
return (status);
}