stmf.c revision aab83bb83be7342f6cfccaed8d5fe0b2f404855d
9512fe850e98fdd448c638ca63fdd92a8a510255ahl * CDDL HEADER START
9512fe850e98fdd448c638ca63fdd92a8a510255ahl * The contents of this file are subject to the terms of the
9512fe850e98fdd448c638ca63fdd92a8a510255ahl * Common Development and Distribution License (the "License").
9512fe850e98fdd448c638ca63fdd92a8a510255ahl * You may not use this file except in compliance with the License.
9512fe850e98fdd448c638ca63fdd92a8a510255ahl * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
9512fe850e98fdd448c638ca63fdd92a8a510255ahl * See the License for the specific language governing permissions
9512fe850e98fdd448c638ca63fdd92a8a510255ahl * and limitations under the License.
9512fe850e98fdd448c638ca63fdd92a8a510255ahl * When distributing Covered Code, include this CDDL HEADER in each
9512fe850e98fdd448c638ca63fdd92a8a510255ahl * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
9512fe850e98fdd448c638ca63fdd92a8a510255ahl * If applicable, add the following below this CDDL HEADER, with the
9512fe850e98fdd448c638ca63fdd92a8a510255ahl * fields enclosed by brackets "[]" replaced with your own identifying
9512fe850e98fdd448c638ca63fdd92a8a510255ahl * information: Portions Copyright [yyyy] [name of copyright owner]
9512fe850e98fdd448c638ca63fdd92a8a510255ahl * CDDL HEADER END
9512fe850e98fdd448c638ca63fdd92a8a510255ahl * Copyright (c) 2008, 2010, Oracle and/or its affiliates. All rights reserved.
9512fe850e98fdd448c638ca63fdd92a8a510255ahl * Copyright 2012, Nexenta Systems, Inc. All rights reserved.
9512fe850e98fdd448c638ca63fdd92a8a510255ahl * Copyright (c) 2013 by Delphix. All rights reserved.
9512fe850e98fdd448c638ca63fdd92a8a510255ahl * Copyright (c) 2013 by Saso Kiselkov. 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
struct stmf_svc_clocks;
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);
volatile int stmf_allow_modunload = 0;
#ifdef DEBUG
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);
uint16_t n;
return (STMF_NOT_FOUND);
return (STMF_NOT_FOUND);
return (STMF_ALREADY);
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_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) {
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);
TASKQ_DEFAULTPRI, 0);
uint32_t i;
return (STMF_BUSY);
return (STMF_SUCCESS);
struct stmf_svc_clocks {
case STMF_CMD_LPORT_ONLINE:
case STMF_CMD_LPORT_OFFLINE:
case STMF_CMD_LU_ONLINE:
case STMF_CMD_LU_OFFLINE:
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)
if (stmf_io_deadman_enabled) {
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);