stmf.c revision 716c180559045549271833327182dc6a266134f1
c10c16dec587a0662068f6e2991c29ed3a9db943Richard Lowe * CDDL HEADER START
c10c16dec587a0662068f6e2991c29ed3a9db943Richard Lowe * The contents of this file are subject to the terms of the
c10c16dec587a0662068f6e2991c29ed3a9db943Richard Lowe * Common Development and Distribution License (the "License").
ed22c7109fc5dd9e1b7a5d0333bdc7ad2718e2abYuri Pankov * You may not use this file except in compliance with the License.
c10c16dec587a0662068f6e2991c29ed3a9db943Richard Lowe * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
c10c16dec587a0662068f6e2991c29ed3a9db943Richard Lowe * See the License for the specific language governing permissions
c10c16dec587a0662068f6e2991c29ed3a9db943Richard Lowe * and limitations under the License.
c10c16dec587a0662068f6e2991c29ed3a9db943Richard Lowe * When distributing Covered Code, include this CDDL HEADER in each
c10c16dec587a0662068f6e2991c29ed3a9db943Richard Lowe * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
c10c16dec587a0662068f6e2991c29ed3a9db943Richard Lowe * If applicable, add the following below this CDDL HEADER, with the
c10c16dec587a0662068f6e2991c29ed3a9db943Richard Lowe * fields enclosed by brackets "[]" replaced with your own identifying
c10c16dec587a0662068f6e2991c29ed3a9db943Richard Lowe * information: Portions Copyright [yyyy] [name of copyright owner]
c10c16dec587a0662068f6e2991c29ed3a9db943Richard Lowe * CDDL HEADER END
c10c16dec587a0662068f6e2991c29ed3a9db943Richard Lowe * Copyright (c) 2008, 2010, Oracle and/or its affiliates. All rights reserved.
#include "stmf_impl.h"
#include "lun_map.h"
#include "stmf_state.h"
#include "stmf_stats.h"
* stmf_state_lock --> ilport_lock/iss_lockp --> ilu_task_lock
void **result);
char *info);
static char stmf_ctoi(char c);
void stmf_svc_init();
void stmf_check_freetask();
int target_reset);
void stmf_delete_all_ppds();
void stmf_trace_clear();
void stmf_worker_init();
void stmf_worker_mgmt();
const void *void_irport2);
static void stmf_delete_itl_kstat_by_lport(char *);
static void stmf_delete_itl_kstat_by_guid(char *);
static int stmf_itl_kstat_compare(const void*, const void*);
volatile int stmf_allow_modunload = 0;
#ifdef DEBUG
volatile int stmf_drop_task_counter = 0;
volatile int stmf_drop_buf_counter = 0;
static int trace_buf_size;
static int trace_buf_curndx;
static int stmf_i_max_nworkers;
static int stmf_i_min_nworkers;
static int stmf_worker_sel_counter = 0;
static int stmf_nworkers_accepting_cmds;
static int stmf_worker_scale_down_qd = 0;
&modldrv,
_init(void)
int ret;
if (ret)
return (ret);
trace_buf_curndx = 0;
return (ret);
_fini(void)
int ret;
return (EBUSY);
if ((!stmf_allow_modunload) &&
return (EBUSY);
return (EBUSY);
return (EBUSY);
return (EBUSY);
return (EBUSY);
if (ret) {
return (ret);
return (ret);
switch (cmd) {
case DDI_INFO_DEVT2DEVINFO:
case DDI_INFO_DEVT2INSTANCE:
*result =
return (DDI_FAILURE);
return (DDI_SUCCESS);
switch (cmd) {
case DDI_ATTACH:
return (DDI_SUCCESS);
return (DDI_FAILURE);
switch (cmd) {
case DDI_DETACH:
return (DDI_SUCCESS);
return (DDI_FAILURE);
return (EBUSY);
return (EBUSY);
int ret;
if (ret)
return (EFAULT);
goto copyin_iocdata_done;
if (ret == 0)
if (*obuf) {
if (*ibuf) {
return (ret);
int ret;
if (ret)
return (EFAULT);
if (ret)
return (EFAULT);
int ret = 0;
uint32_t n;
return (ENOTTY);
return (EPERM);
if (ret)
return (ret);
switch (cmd) {
case STMF_IOCTL_LU_LIST:
/* retrieves both registered/unregistered */
sizeof (slist_lu_t));
case STMF_IOCTL_REG_LU_LIST:
case STMF_IOCTL_VE_LU_LIST:
case STMF_IOCTL_SESSION_LIST:
(p_id[0] == 0)) {
sizeof (sioc_target_port_props_t))) {
case STMF_IOCTL_SET_LU_STATE:
ret = 0;
ret = 0;
case STMF_IOCTL_ADD_HG_ENTRY:
case STMF_IOCTL_ADD_TG_ENTRY:
case STMF_IOCTL_VALIDATE_VIEW:
&veid,
if (ret == 0 &&
case STMF_IOCTL_GET_HG_LIST:
case STMF_IOCTL_GET_TG_LIST:
grpname++;
if (!id_entry)
grp_entry++;
case STMF_IOCTL_GET_VE_LIST:
ve++;
case STMF_IOCTL_LU_VE_LIST:
ve++;
case STMF_IOCTL_LOAD_PP_DATA:
case STMF_IOCTL_GET_PP_DATA:
case STMF_IOCTL_CLEAR_PP_DATA:
case STMF_IOCTL_CLEAR_TRACE:
case STMF_IOCTL_ADD_TRACE:
case STMF_IOCTL_GET_TRACE:
i = *((int *)ibuf);
trace_buf_size)) {
if (ret == 0) {
if (obuf) {
if (ibuf) {
return (ret);
int online = 0;
int offline = 0;
int onlining = 0;
int offlining = 0;
offline++;
online++;
onlining++;
offlining++;
offline++;
online++;
onlining++;
offlining++;
if (onlining)
return (STMF_STATE_ONLINING);
return (STMF_STATE_ONLINE);
if (offlining) {
return (STMF_STATE_OFFLINING);
return (STMF_STATE_OFFLINE);
int svc_state;
return (EACCES);
return (EBUSY);
return (EINVAL);
return (EBUSY);
return (EINVAL);
return (EINVAL);
return (EINVAL);
return (EACCES);
return (STMF_SUCCESS);
return (STMF_SUCCESS);
return (STMF_SUCCESS);
return (STMF_SUCCESS);
return (NULL);
return (NULL);
return (STMF_FAILURE);
case STMF_ICM_REGISTER_LUN:
(void) stmf_ic_lu_reg(
case STMF_ICM_LUN_ACTIVE:
(void) stmf_ic_lu_reg(
case STMF_ICM_DEREGISTER_LUN:
(void) stmf_ic_lu_dereg(
case STMF_ICM_SCSI_DATA:
(void) stmf_ic_rx_scsi_data(
case STMF_ICM_SCSI_STATUS:
(void) stmf_ic_rx_scsi_status(
case STMF_ICM_STATUS:
(void) stmf_ic_rx_status(
return (STMF_FAILURE);
return (STMF_SUCCESS);
return (STMF_SUCCESS);
return (STMF_SUCCESS);
return (STMF_SUCCESS);
return (STMF_SUCCESS);
return (STMF_SUCCESS);
return (STMF_SUCCESS);
return (STMF_SUCCESS);
if (ic_xfer_done_msg) {
return (STMF_FAILURE);
return (STMF_FAILURE);
return (STMF_FAILURE);
return (STMF_SUCCESS);
return (STMF_FAILURE);
return (STMF_FAILURE);
return (STMF_FAILURE);
if (dbuf) {
if (ic_cmd_msg) {
return (ret);
int error;
return (STMF_FAILURE);
return (STMF_FAILURE);
return (STMF_FAILURE);
return (STMF_FAILURE);
return (STMF_FAILURE);
return (STMF_FAILURE);
return (STMF_FAILURE);
return (STMF_FAILURE);
return (STMF_FAILURE);
return (STMF_FAILURE);
return (STMF_FAILURE);
return (STMF_FAILURE);
return (STMF_SUCCESS);
int ret = 0;
return (EINVAL);
goto err;
if (ic_reg_port) {
if (ic_reg_lun) {
err:
return (ret);
int alloc_size;
} __istmf_t;
} __stmf_t;
int shared;
int fw_private;
} stmf_sizes[] = { { 0, 0 },
int stmf_size;
int kmem_flag;
return (NULL);
return (NULL);
return (sh);
return (ilu);
return (NULL);
return (ilport);
return (NULL);
return (STMF_FAILURE);
goto rlp_bail_out;
goto rlp_bail_out;
return (STMF_SUCCESS);
return (STMF_BUSY);
return (STMF_SUCCESS);
return (STMF_NOT_FOUND);
return (STMF_FAILURE);
goto rpp_bail_out;
goto rpp_bail_out;
return (STMF_SUCCESS);
return (STMF_BUSY);
return (STMF_SUCCESS);
return (STMF_NOT_FOUND);
int ret;
*err_ret = 0;
return (EINVAL);
return (EINVAL);
return (ENOMEM);
return (EINVAL);
return (ret);
if (ppi_token) {
goto bail_out;
goto bail_out;
((stmf_i_lu_provider_t *)
return (EINVAL);
if (ppd) {
ret = 0;
return (ret);
return (EINVAL);
NV_ENCODE_XDR)) != 0) {
goto done;
goto done;
NV_ENCODE_XDR, 0)) != 0) {
goto done;
ret = 0;
done:
return (ret);
* This array matches the Protocol Identifier in stmf_ioctl.h
return (STMF_INVALID_ARG);
return (STMF_BUSY);
if (!ilu) {
if (ic_reg_lun) {
return (STMF_SUCCESS);
return (STMF_INVALID_ARG);
return (STMF_BUSY);
return (STMF_ALREADY);
if (luid) {
((stmf_i_lu_provider_t *)
if (ic_reg_lun) {
return (STMF_SUCCESS);
return (STMF_BUSY);
return (STMF_INVALID_ARG);
if (ic_dereg_lun) {
((stmf_i_lu_provider_t *)
NULL;
return (STMF_BUSY);
return (STMF_SUCCESS);
int start_workers = 0;
return (STMF_BUSY);
return (STMF_FAILURE);
if (ic_reg_port) {
if (start_workers)
return (STMF_SUCCESS);
return (STMF_BUSY);
if (ic_dereg_port) {
return (STMF_BUSY);
return (STMF_SUCCESS);
int result;
if (result < 0) {
} else if (result > 0) {
static stmf_i_remote_port_t *
int alloc_len;
return (NULL);
return (NULL);
return (irport);
static stmf_i_remote_port_t *
return (irport);
return (NULL);
return (irport);
static stmf_i_remote_port_t *
return (irport);
* we would want to acquire stmf_state.stmf_lock before getting
* Port provider has to make sure that register/deregister session and
return (STMF_FAILURE);
return (STMF_FAILURE);
return (STMF_FAILURE);
return (STMF_FAILURE);
KM_SLEEP);
return (STMF_SUCCESS);
int found = 0;
goto try_dereg_ss_again;
if (ic_session_dereg) {
if (!found) {
if (!stay_locked)
return (iss);
return (NULL);
int ret;
if (ret < 0) {
} else if (ret > 0) {
static stmf_i_itl_kstat_t *
return (itl_kstat);
static stmf_i_itl_kstat_t *
int i, len;
return (ks_itl);
return (NULL);
lun->ident[i]);
return (ks_itl);
char *strbuf;
char *rport_alias;
char *lport_alias;
char *lu_alias;
return (STMF_SUCCESS);
return (STMF_ALLOC_FAILURE);
return (STMF_ALLOC_FAILURE);
goto itl_kstat_cleanup;
strbuf++;
strbuf++;
goto itl_kstat_cleanup;
goto itl_kstat_cleanup;
goto itl_kstat_cleanup;
return (STMF_SUCCESS);
return (STMF_ALLOC_FAILURE);
uint16_t n;
return (STMF_NOT_FOUND);
return (STMF_NOT_FOUND);
return (STMF_ALREADY);
return (STMF_ALLOC_FAILURE);
return (STMF_ALLOC_FAILURE);
return (STMF_SUCCESS);
if (nmaps == 0)
return (STMF_NOT_FOUND);
goto dereg_itl_start;
nu = 0;
if (!lm)
goto dai_scan_done;
for (i = 0; i < nu; i++) {
return (STMF_SUCCESS);
uint16_t n;
return (STMF_INVALID_ARG);
return (STMF_NOT_FOUND);
return (STMF_NOT_FOUND);
if (lun) {
return (STMF_INVALID_ARG);
return (STMF_NOT_FOUND);
return (STMF_SUCCESS);
uint16_t n;
return (STMF_NOT_FOUND);
return (ret);
return (NULL);
if (dbuf) {
return (dbuf);
return (NULL);
return (STMF_FAILURE);
return (STMF_FAILURE);
return (STMF_FAILURE);
return (STMF_FAILURE);
return (STMF_SUCCESS);
return (NULL);
struct scsi_task *
uint8_t *l;
if (!lun_map_ent) {
return (NULL);
if (*ppitask) {
if (!new_task) {
return (NULL);
l[0] = lun[0];
if (new_task) {
return (NULL);
return (NULL);
return (task);
if (!num_to_release) {
int nv;
int s = nv;
if (nv < 0)
nv = 0;
s, nv);
w = w1;
w = stmf_workers;
if (w->worker_task_tail) {
if (dbuf) {
return (STMF_ABORTED);
return (STMF_ABORTED);
#ifdef DEBUG
return (STMF_SUCCESS);
return (STMF_SUCCESS);
return (ret);
free_it = 0;
free_it = 0;
queue_it = 0;
update_queue_flags = 0;
queue_it = 0;
if (update_queue_flags) {
if (queue_it) {
if (w->worker_task_tail) {
if (++(w->worker_queue_depth) >
w->worker_max_qdepth_pu) {
if (free_it) {
ITASK_BEING_ABORTED)) == 0) {
return (STMF_ABORTED);
return (STMF_SUCCESS);
return (STMF_ABORTED);
free_it = 0;
free_it = 0;
queue_it = 0;
if (queue_it) {
if (w->worker_task_tail) {
if (free_it) {
ITASK_BEING_ABORTED)) == 0) {
ITASK_BEING_ABORTED)) == 0) {
stmf_worker_t *w;
ITASK_KNOWN_TO_LU)) == 0)) {
if (w->worker_task_tail) {
stmf_status_t, s);
switch (abort_cmd) {
case STMF_QUEUE_ABORT_LU:
case STMF_QUEUE_TASK_ABORT:
f = ITASK_KNOWN_TO_LU;
if ((old & f) != f) {
unsigned long long st;
unsigned long long st;
st = s;
st);
return (STMF_BUSY);
return (STMF_SUCCESS);
if (w->worker_task_tail) {
return (STMF_SUCCESS);
return (STMF_BUSY);
return (STMF_SUCCESS);
if (w->worker_task_tail) {
return (STMF_SUCCESS);
unsigned long long ret;
call_lu_abort = 0;
if (call_lu_abort) {
call_port_abort = 0;
if (call_port_abort) {
goto stmf_ctl_lock_exit;
goto stmf_ctl_lock_exit;
goto stmf_ctl_lock_exit;
switch (cmd) {
case STMF_CMD_LU_ONLINE:
case STMF_STATE_OFFLINE:
case STMF_STATE_ONLINE:
case STMF_STATE_ONLINING:
case STMF_STATE_OFFLINING:
goto stmf_ctl_lock_exit;
goto stmf_ctl_lock_exit;
STMF_SUCCESS) {
goto stmf_ctl_lock_exit;
case STMF_CMD_LU_OFFLINE:
case STMF_STATE_ONLINE:
case STMF_STATE_OFFLINE:
case STMF_STATE_OFFLINING:
case STMF_STATE_ONLINING:
goto stmf_ctl_lock_exit;
goto stmf_ctl_lock_exit;
STMF_SUCCESS) {
* LPORT_ONLINE/OFFLINE has nothing to do with link offline/online.
case STMF_CMD_LPORT_ONLINE:
case STMF_STATE_OFFLINE:
case STMF_STATE_ONLINE:
case STMF_STATE_ONLINING:
case STMF_STATE_OFFLINING:
goto stmf_ctl_lock_exit;
goto stmf_ctl_lock_exit;
goto stmf_ctl_lock_exit;
ddi_get_lbolt() -
goto stmf_ctl_lock_exit;
STMF_SUCCESS) {
goto stmf_ctl_lock_exit;
case STMF_CMD_LPORT_OFFLINE:
case STMF_STATE_ONLINE:
case STMF_STATE_OFFLINE:
case STMF_STATE_OFFLINING:
case STMF_STATE_ONLINING:
goto stmf_ctl_lock_exit;
goto stmf_ctl_lock_exit;
STMF_SUCCESS) {
goto stmf_ctl_lock_exit;
return (STMF_SUCCESS);
return (ret);
return (STMF_NOT_SUPPORTED);
bufsizep));
return (STMF_NOT_SUPPORTED);
uint8_t *p;
nports++;
return (NULL);
return (xd);
struct scsi_devid_desc *
return (devid);
return (rtpid);
uint8_t *p;
return (STMF_INVALID_ARG);
if (hid != 0) {
*t = BE_32(*t);
return (STMF_SUCCESS);
uint32_t m = 0;
m += sz;
n += copysz;
if (vpd_mask == 0)
p = small_buf;
p = small_buf;
case TM_ABORT_TASK:
case TM_ABORT_TASK_SET:
case TM_CLEAR_TASK_SET:
case TM_LUN_RESET:
case TM_TARGET_RESET:
case TM_TARGET_COLD_RESET:
case TM_TARGET_WARM_RESET:
!= STMF_SUCCESS) {
NULL);
int i, lf;
lf++;
if (lf == 0) {
!= STMF_SUCCESS) {
NULL);
uint32_t i;
for (i = 0; i < stmf_i_max_nworkers; i++) {
while (stmf_nworkers_cur == 0)
return (STMF_SUCCESS);
while (stmf_nworkers_cur != 0) {
return (STMF_BUSY);
for (i = 0; i < stmf_i_max_nworkers; i++) {
return (STMF_SUCCESS);
stmf_worker_t *w;
if ((w->worker_ref_count == 0) &&
thread_exit();
dec_qdepth = 0;
wait_timer = 0;
wait_delta = 0;
if (w->worker_wait_head) {
w->worker_task_head =
w->worker_wait_head;
w->worker_wait_head;
NULL;
wait_queue = 0;
abort_free = 0;
goto out_itask_flag_loop;
goto out_itask_flag_loop;
if (wait_queue) {
if (w->worker_wait_tail) {
if (wait_timer == 0) {
if (w->worker_task_tail) {
switch (curcmd) {
case ITASK_CMD_NEW_TASK:
#ifdef DEBUG
if (stmf_drop_task_counter > 0) {
if (atomic_add_32_nv(
case ITASK_CMD_DATA_XFER_DONE:
case ITASK_CMD_STATUS_DONE:
case ITASK_CMD_ABORT:
if (abort_free) {
case ITASK_CMD_POLL_LU:
if (!wait_queue) {
case ITASK_CMD_POLL_LPORT:
if (!wait_queue)
case ITASK_CMD_SEND_STATUS:
if (dec_qdepth) {
w->worker_queue_depth--;
if (w->worker_ref_count == 0)
goto stmf_worker_loop;
if (wait_timer) {
goto stmf_worker_loop;
int workers_needed;
stmf_worker_t *w;
if (stmf_nworkers_cur) {
workers_needed = 0;
if ((stmf_wm_last != 0) &&
qd = 0;
for (i = 0; i < stmf_nworkers_accepting_cmds; i++) {
if (d <= tps) {
if (stmf_worker_scale_down_timer == 0) {
w = &stmf_workers[i];
w = &stmf_workers[i];
uint8_t *p;
uint32_t s;
if (set_rel_off)
return (STMF_SUCCESS);
uint8_t *p;
switch (cdbp[0]) {
case SCMD_INQUIRY:
if (sz == 0) {
case SCMD_REPORT_LUNS:
if (ic_xfer_done_msg) {
case TM_ABORT_TASK:
case TM_ABORT_TASK_SET:
case TM_CLEAR_TASK_SET:
case TM_LUN_RESET:
case TM_TARGET_RESET:
case TM_TARGET_COLD_RESET:
case TM_TARGET_WARM_RESET:
return (STMF_ABORT_SUCCESS);
return (STMF_ABORT_SUCCESS);
case TM_ABORT_TASK:
case TM_ABORT_TASK_SET:
case TM_CLEAR_TASK_SET:
case TM_LUN_RESET:
case TM_TARGET_RESET:
case TM_TARGET_COLD_RESET:
case TM_TARGET_WARM_RESET:
return (STMF_SUCCESS);
int ntasks_pending;
if (target_reset) {
return (ret);
return (ret);
return (ret);
if (target_reset) {
return (STMF_BUSY);
!= STMF_SUCCESS) {
return (STMF_SUCCESS);
return (STMF_SUCCESS);
int not_done = 0;
if (not_done) {
!= STMF_SUCCESS) {
return (STMF_INVALID_ARG);
return (STMF_SUCCESS);
return (STMF_SUCCESS);
return (STMF_INVALID_ARG);
return (STMF_SUCCESS);
return (STMF_INVALID_ARG);
return (STMF_SUCCESS);
return (STMF_SUCCESS);
return (STMF_INVALID_ARG);
return (STMF_SUCCESS);
if (read) {
if (read) {
TASKQ_DEFAULTPRI, 0);
uint32_t i;
return (STMF_BUSY);
return (STMF_SUCCESS);
int deq;
int waitq_add = 0;
case STMF_CMD_LPORT_ONLINE:
case STMF_CMD_LPORT_OFFLINE:
case STMF_CMD_LU_ONLINE:
case STMF_CMD_LU_OFFLINE:
if (waitq_add) {
deq = 0;
case STMF_CMD_LU_ONLINE:
case STMF_CMD_LU_OFFLINE:
case STMF_CMD_LPORT_OFFLINE:
case STMF_CMD_LPORT_ONLINE:
if (deq) {
goto stmf_svc_loop;
goto stmf_svc_loop;
int stmf_level = 0;
int port_level;
int ilport_lock_held;
ILPORT_SS_GOT_INITIAL_LUNS) == 0) {
port_level = 0;
ISS_GOT_INITIAL_LUNS) == 0) {
port_level++;
stmf_level++;
ilport_lock_held = 0;
if (port_level == 0) {
if (stmf_level == 0) {
goto stmf_svc_loop;
s = sizeof (stmf_svc_req_t);
sizeof (stmf_svc_req_t)));
int len;
if (!stmf_trace_on)
ddi_get_lbolt());
trace_buf_curndx = 0;
if (!stmf_trace_on)
trace_buf_curndx = 0;
if (trace_buf_size > 0)
stmf_trace_buf[0] = 0;
void *ctl_private;
int msg = 0;
if (offline_lu) {
if (((stmf_i_lu_t *)
if (((stmf_i_local_port_t *)
if (msg) {
stmf_ctoi(char c)
static boolean_t
int ii;
char enc_char = *c++;
enc_char = *c++;
return (B_FALSE);
return (B_TRUE);
if (tptid_sz)
*tptid_sz = 0;
return (B_FALSE);
case PROTOCOL_FIBRE_CHANNEL:
return (B_FALSE);
case PROTOCOL_iSCSI:
return (B_FALSE);
return (B_FALSE);
return (B_FALSE);
return (B_FALSE);
case PROTOCOL_SRP:
return (B_FALSE);
case PROTOCOL_PARALLEL_SCSI:
case PROTOCOL_SSA:
case PROTOCOL_IEEE_1394:
case PROTOCOL_SAS:
case PROTOCOL_ADT:
case PROTOCOL_ATAPI:
return (B_FALSE);
return (B_FALSE);
if (tptid_sz)
return (B_TRUE);
return (B_FALSE);
case PROTOCOL_iSCSI:
return (B_FALSE);
case PROTOCOL_SRP:
return (B_FALSE);
case PROTOCOL_FIBRE_CHANNEL:
return (B_FALSE);
case PROTOCOL_PARALLEL_SCSI:
case PROTOCOL_SSA:
case PROTOCOL_IEEE_1394:
case PROTOCOL_SAS:
case PROTOCOL_ADT:
case PROTOCOL_ATAPI:
return (B_FALSE);
return (B_TRUE);
case PROTOCOL_FIBRE_CHANNEL:
case PROTOCOL_iSCSI:
case PROTOCOL_SRP:
case PROTOCOL_PARALLEL_SCSI:
case PROTOCOL_SSA:
case PROTOCOL_IEEE_1394:
case PROTOCOL_SAS:
case PROTOCOL_ADT:
case PROTOCOL_ATAPI:
return (rpt);
return (NULL);
return (rpt);