/*
* CDDL HEADER START
*
* The contents of this file are subject to the terms of the
* Common Development and Distribution License (the "License").
* You may not use this file except in compliance with the License.
*
* You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
* See the License for the specific language governing permissions
* and limitations under the License.
*
* When distributing Covered Code, include this CDDL HEADER in each
* file and include the License file at usr/src/OPENSOLARIS.LICENSE.
* If applicable, add the following below this CDDL HEADER, with the
* fields enclosed by brackets "[]" replaced with your own identifying
* information: Portions Copyright [yyyy] [name of copyright owner]
*
* CDDL HEADER END
*/
/*
* Copyright 2009 Sun Microsystems, Inc. All rights reserved.
* Use is subject to license terms.
*/
/*
* generic scsi device watch
*/
#define SWDEBUG
#endif
/*
* debug goodies
*/
#ifdef SWDEBUG
static int swdebug = 0;
#else /* SWDEBUG */
#define swdebug (0)
#define DEBUGGING (0)
#endif
/*
* Includes, Declarations and Local Data
*/
/*
* macro for filling in lun value for scsi-1 support
*/
}
/*
* all info resides in the scsi watch structure
*
* the monitoring is performed by one separate thread which works
* from a linked list of scsi_watch_request packets
*/
static struct scsi_watch {
/* and this structure */
/* of request structures */
/* for watch thread */
/* processed by the watch */
/* thread which is being */
/* blocked */
} sw;
#if !defined(lint)
#endif
/*
* Values for sw_state
*/
#define SW_RUNNING 0
/*
* values for sw_flags
*/
struct scsi_watch_request {
/* request synchronously */
};
/*
* values for swr flags
*/
#if !defined(lint)
#endif
/*
* values for sw_what
*/
static void scsi_watch_thread(void);
/*
* setup, called from _init(), the thread is created when we need it
* and exits when there is nothing to do anymore and everything has been
* cleaned up (ie. resources deallocated)
*/
void
{
/* NO OTHER THREADS ARE RUNNING */
}
/*
* cleaning up, called from _fini()
*/
void
{
/* NO OTHER THREADS ARE RUNNING */
/*
* hope and pray that the thread has exited
*/
}
/*
* allocate an swr (scsi watch request structure) and initialize pkts
*/
struct scsi_device *devp,
int interval,
int sense_length,
int (*callback)(), /* callback function */
{
}
struct scsi_device *devp,
int interval,
int sense_length,
int (*callback)(), /* callback function */
{
}
static opaque_t
struct scsi_device *devp,
int interval,
int sense_length,
int (*callback)(), /* callback function */
{
"scsi_watch_request_submit: Entering ...\n");
register kthread_t *t;
}
if ((p->swr_callback_arg == cb_arg) &&
(p->swr_callback == callback))
break;
}
/* update time interval for an existing request */
if (p) {
p->swr_timeout = p->swr_interval
= drv_usectohz(interval);
p->swr_ref++;
return ((opaque_t)p);
}
}
/*
* allocate space for scsi_watch_request
*/
/*
* allocate request sense bp and pkt and make cmd
* we shouldn't really need it if ARQ is enabled but it is useful
* if the ARQ failed.
*/
SCMD_REQUEST_SENSE, 0, SENSE_LENGTH, 0);
/*
* Create TUR pkt or GET STATUS EVENT NOTIFICATION for MMC requests or
* a zero byte WRITE(10) based on the disk-type for reservation state.
* For inq_dtype of SBC (DIRECT, dtype == 0)
* OR for RBC devices (dtype is 0xE) AND for
*/
if (mmc) {
CDB_GROUP1, sizeof (struct scsi_arq_status),
0, 0, SLEEP_FUNC, NULL);
SCMD_GET_EVENT_STATUS_NOTIFICATION, 0, 8, 0);
CDB_GROUP1, sizeof (struct scsi_arq_status),
0, 0, SLEEP_FUNC, NULL);
SCMD_WRITE_G1, 0, 0, 0);
} else {
CDB_GROUP0, sizeof (struct scsi_arq_status),
0, 0, SLEEP_FUNC, NULL);
SCMD_TEST_UNIT_READY, 0, 0, 0);
}
}
/*
* set the allocated resources in swr
*/
/*
* add to the list and wake up the thread
*/
}
/*
* reset all timeouts, so all requests are in sync again
* XXX there is a small window where the watch thread releases
* the mutex so that could upset the resyncing
*/
while (sswr) {
}
}
/*
* called by (eg. pwr management) to resume the scsi_watch_thread
*/
void
{
/*
* Change the state to SW_RUNNING and wake up the scsi_watch_thread
*/
goto exit;
/* search for token */
break;
}
/* if we can't find this value, then we just do nothing */
goto exit;
/* see if all swr's are awake, then start the thread again */
goto exit;
}
exit:
}
/*
* called by clients (eg. pwr management) to suspend the scsi_watch_thread
*/
void
{
goto exit;
/* search for token */
break;
}
/* if we can't find this value, then we just do nothing */
goto exit;
for (;;) {
/*
* XXX: Assumes that this thread can rerun
* till all outstanding cmds are complete
*/
} else {
break;
}
}
/* see if all swr's are suspended, then suspend the thread */
goto exit;
}
exit:
}
/*
* destroy swr, called for watch thread
*/
static void
{
"scsi_watch_request_destroy: Entering ...\n");
return;
/*
* remove swr from linked list and destroy pkts
*/
}
}
}
}
}
}
/*
* scsi_watch_request_terminate()
* called by requestor to terminate any pending watch request.
* if the request is currently "busy", and the caller cannot wait, failure
* is returned. O/w the request is cleaned up immediately.
*/
int
{
(struct scsi_watch_request *)token;
int count = 0;
int free_flag = 0;
/*
* We try to clean up this request if we can. We also inform
* the watch thread that we mucked around the list so it has
* to start reading from head of list again.
*/
"scsi_watch_request_terminate: Entering(0x%p) ...\n",
(void *)swr);
/*
* check if it is still in the list
*/
while (sswr) {
if (flags == SCSI_WATCH_TERMINATE_NOWAIT) {
return (SCSI_WATCH_TERMINATE_FAIL);
}
return (SCSI_WATCH_TERMINATE_SUCCESS);
}
if (SCSI_WATCH_TERMINATE_ALL_WAIT == flags) {
count = 0;
}
free_flag = 1;
goto done;
} else {
if (SCSI_WATCH_TERMINATE_NOWAIT == flags ||
count = 0;
}
if (0 == count) {
free_flag = 1;
}
goto done;
}
}
}
done:
if (!sswr) {
return (SCSI_WATCH_TERMINATE_FAIL);
}
if (1 == free_flag &&
}
return (SCSI_WATCH_TERMINATE_SUCCESS);
}
/*
* The routines scsi_watch_thread & scsi_watch_request_intr are
* on different threads.
* If there is no work to be done by the lower level driver
* then swr->swr_busy will not be set.
* In this case we will call CALLB_CPR_SAFE_BEGIN before
* calling cv_timedwait.
* In the other case where there is work to be done by
* the lower level driver then the flag swr->swr_busy will
* be set.
* We cannot call CALLB_CPR_SAFE_BEGIN at this point the reason
* is the intr thread can interfere with our operations. So
* we do a cv_timedwait here. Now at the completion of the
* lower level driver's work we will call CALLB_CPR_SAFE_BEGIN
* in scsi_watch_request_intr.
* In all the cases we will call CALLB_CPR_SAFE_END only if
* we already called a CALLB_CPR_SAFE_BEGIN and this is flagged
* by sw_cpr_flag.
* Warlock has a problem when we use different locks
* on the same type of structure in different contexts.
* We use callb_cpr_t in both scsi_watch and esp_callback threads.
* we use different mutexe's in different threads. And
* this is not acceptable to warlock. To avoid this
* problem we use the same name for the mutex in
* both scsi_watch & esp_callback. when __lock_lint is not defined
* esp_callback uses the mutex on the stack and in scsi_watch
* a static variable. But when __lock_lint is defined
* we make a mutex which is global in esp_callback and
* a external mutex for scsi_watch.
*/
static int sw_cmd_count = 0;
static int sw_cpr_flag = 0;
#ifndef __lock_lint
#else
#endif
#if !defined(lint)
#endif
/*
* the scsi watch thread:
* it either wakes up if there is work to do or if the cv_timeait
* timed out
* normally, it wakes up every <delay> seconds and checks the list.
* the interval is not very accurate if the cv was signalled but that
* really doesn't matter much
* it is more important that we fire off all TURs simulataneously so
* we don't have to wake up frequently
*/
static void
{
"scsi_watch_thread: Entering ...\n");
#if !defined(lint)
#endif
sw_cpr_flag = 0;
#if !defined(lint)
/*LINTED*/
#endif
/*
* grab the mutex and wait for work
*/
}
/*
* now loop forever for work; if queue is empty exit
*/
for (;;) {
head:
while (swr) {
/*
* If state is not running, wait for scsi_watch_resume
* to signal restart, but before going into cv_wait
* need to let the PM framework know that it is safe
* to stop this thread for CPR
*/
"scsi_watch_thread suspended\n");
if (!sw_cmd_count) {
sw_cpr_flag = 1;
}
/*
* Need to let the PM framework know that it
* is no longer safe to stop the thread for
* CPR.
*/
if (sw_cpr_flag == 1) {
sw_cpr_flag = 0;
}
sizeof (struct scsi_watch_request));
goto head;
} else {
}
}
if (next_delay == 0) {
} else {
}
"scsi_watch_thread: "
"swr(0x%p),what=%x,timeout=%lx,"
"interval=%lx,delay=%lx\n",
case SWR_SUSPENDED:
case SWR_SUSPEND_REQUESTED:
/* if we are suspended, don't do anything */
break;
case SWR_STOP:
}
break;
default:
/*
* submit the cmd and let the completion
* function handle the result
* release the mutex (good practice)
* this should be safe even if the list
* is changing
*/
sw_cmd_count++;
"scsi_watch_thread: "
"Starting TUR\n");
TRAN_ACCEPT) {
/*
* try again later
*/
"scsi_watch_thread: "
"Transport Failed\n");
sw_cmd_count--;
}
}
break;
}
goto head;
}
}
/*
* delay using cv_timedwait; we return when
* signalled or timed out
*/
if (next_delay <= 0) {
next_delay = onesec;
}
} else {
}
if (!sw_cmd_count) {
sw_cpr_flag = 1;
}
/*
* if we return from cv_timedwait because we were
* signalled, the delay is not accurate but that doesn't
* really matter
*/
if (sw_cpr_flag == 1) {
sw_cpr_flag = 0;
}
next_delay = 0;
/*
* is there still work to do?
*/
break;
}
}
/*
* no more work to do, reset sw_thread and exit
*/
#ifndef __lock_lint
#endif
"scsi_watch_thread: Exiting ...\n");
}
/*
* callback completion function for scsi watch pkt
*/
static void
{
int amt = 0;
"scsi_watch_intr: Entering ...\n");
/*
* first check if it is the TUR or RQS pkt
*/
/*
* submit the request sense pkt
*/
"scsi_watch_intr: "
"Submitting a Request Sense "
"Packet\n");
TRAN_ACCEPT) {
/*
* just give up and try again later
*/
"scsi_watch_intr: "
"Request Sense "
"Transport Failed\n");
goto done;
}
/*
* wait for rqsense to complete
*/
return;
/*
* check the autorequest sense data
*/
"scsi_watch_intr: "
"Auto Request Sense, amt=%x\n", amt);
}
}
/*
* check the request sense data
*/
rqsensep = (struct scsi_extended_sense *)
"scsi_watch_intr: "
"Request Sense Completed, amt=%x\n", amt);
} else {
/*
* should not reach here!!!
*/
"scsi_watch_intr: Bad Packet(0x%p)", (void *)pkt);
}
if (rqsensep) {
/*
* check rqsense status and data
*/
/*
* try again later
*/
"scsi_watch_intr: "
"Auto Request Sense Failed - "
"Busy or Check Condition\n");
goto done;
}
"scsi_watch_intr: "
"es_key=%x, adq=%x, amt=%x\n",
}
/*
* callback to target driver to do the real work
*/
}
}
done:
sw_cmd_count --;
if (!sw_cmd_count) {
sw_cpr_flag = 1;
}
}
/*
* scsi_watch_get_ref_count
* called by clients to query the reference count for a given token.
* return the number of reference count or 0 if the given token is
* not found.
*/
int
{
(struct scsi_watch_request *)token;
int rval = 0;
"scsi_watch_get_ref_count: Entering(0x%p) ...\n",
(void *)swr);
while (sswr) {
return (rval);
}
}
return (rval);
}