#if DEBUG || lint
#define SWDEBUG
#endif
#ifdef SWDEBUG
static int swdebug = 0;
#define DEBUGGING ((scsi_options & SCSI_DEBUG_TGT) && sddebug > 1)
#define SW_DEBUG if (swdebug == 1) scsi_log
#define SW_DEBUG2 if (swdebug > 1) scsi_log
#else
#define swdebug (0)
#define DEBUGGING (0)
#define SW_DEBUG if (0) scsi_log
#define SW_DEBUG2 if (0) scsi_log
#endif
#include <sys/note.h>
#include <sys/scsi/scsi.h>
#include <sys/var.h>
#include <sys/proc.h>
#include <sys/thread.h>
#include <sys/callb.h>
#define FILL_SCSI1_LUN(devp, pkt) \
if ((devp->sd_address.a_lun > 0) && \
(devp->sd_inq->inq_ansi == 0x1)) { \
((union scsi_cdb *)(pkt)->pkt_cdbp)->scc_lun = \
devp->sd_address.a_lun; \
}
char *sw_label = "scsi-watch";
static int scsi_watch_io_time = SCSI_WATCH_IO_TIME;
static struct scsi_watch {
kthread_t *sw_thread;
kmutex_t sw_mutex;
kcondvar_t sw_cv;
struct scsi_watch_request *sw_head;
uchar_t sw_state;
uchar_t sw_flags;
struct scsi_watch_request *swr_current;
} sw;
#if !defined(lint)
_NOTE(MUTEX_PROTECTS_DATA(scsi_watch::sw_mutex, scsi_watch))
#endif
#define SW_RUNNING 0
#define SW_SUSPEND_REQUESTED 1
#define SW_SUSPENDED 2
#define SW_START_HEAD 0x1
struct scsi_watch_request {
struct scsi_watch_request *swr_next;
struct scsi_watch_request *swr_prev;
clock_t swr_interval;
clock_t swr_timeout;
uchar_t swr_busy;
uchar_t swr_what;
uchar_t swr_sense_length;
struct scsi_pkt *swr_pkt;
struct scsi_pkt *swr_rqpkt;
struct buf *swr_rqbp;
struct buf *swr_mmcbp;
int (*swr_callback)();
caddr_t swr_callback_arg;
kcondvar_t swr_terminate_cv;
int swr_ref;
uchar_t suspend_destroy;
};
#define SUSPEND_DESTROY 1
#if !defined(lint)
_NOTE(SCHEME_PROTECTS_DATA("unshared data", scsi_watch_request))
#endif
#define SWR_WATCH 0
#define SWR_STOP 1
#define SWR_SUSPEND_REQUESTED 2
#define SWR_SUSPENDED 3
static opaque_t scsi_watch_request_submit_impl(struct scsi_device *devp,
int interval, int sense_length, int (*callback)(), caddr_t cb_arg,
boolean_t mmc);
static void scsi_watch_request_destroy(struct scsi_watch_request *swr);
static void scsi_watch_thread(void);
static void scsi_watch_request_intr(struct scsi_pkt *pkt);
void
scsi_watch_init()
{
mutex_init(&sw.sw_mutex, NULL, MUTEX_DRIVER, NULL);
cv_init(&sw.sw_cv, NULL, CV_DRIVER, NULL);
sw.sw_state = SW_RUNNING;
sw.sw_flags = 0;
sw.swr_current = NULL;
}
void
scsi_watch_fini()
{
ASSERT(sw.sw_thread == 0);
mutex_destroy(&sw.sw_mutex);
cv_destroy(&sw.sw_cv);
}
#define ROUTE &devp->sd_address
opaque_t
scsi_watch_request_submit(
struct scsi_device *devp,
int interval,
int sense_length,
int (*callback)(),
caddr_t cb_arg)
{
return (scsi_watch_request_submit_impl(devp, interval, sense_length,
callback, cb_arg, B_FALSE));
}
opaque_t
scsi_mmc_watch_request_submit(
struct scsi_device *devp,
int interval,
int sense_length,
int (*callback)(),
caddr_t cb_arg)
{
return (scsi_watch_request_submit_impl(devp, interval, sense_length,
callback, cb_arg, B_TRUE));
}
static opaque_t
scsi_watch_request_submit_impl(
struct scsi_device *devp,
int interval,
int sense_length,
int (*callback)(),
caddr_t cb_arg,
boolean_t mmc)
{
register struct scsi_watch_request *swr = NULL;
register struct scsi_watch_request *sswr, *p;
struct buf *bp = NULL;
struct buf *mmcbp = NULL;
struct scsi_pkt *rqpkt = NULL;
struct scsi_pkt *pkt = NULL;
uchar_t dtype;
SW_DEBUG((dev_info_t *)NULL, sw_label, SCSI_DEBUG,
"scsi_watch_request_submit: Entering ...\n");
mutex_enter(&sw.sw_mutex);
if (sw.sw_thread == 0) {
register kthread_t *t;
t = thread_create((caddr_t)NULL, 0, scsi_watch_thread,
NULL, 0, &p0, TS_RUN, v.v_maxsyspri - 2);
sw.sw_thread = t;
}
for (p = sw.sw_head; p != NULL; p = p->swr_next) {
if ((p->swr_callback_arg == cb_arg) &&
(p->swr_callback == callback))
break;
}
if (p) {
if (p->swr_what != SWR_STOP) {
p->swr_timeout = p->swr_interval
= drv_usectohz(interval);
p->swr_what = SWR_WATCH;
p->swr_ref++;
cv_signal(&sw.sw_cv);
mutex_exit(&sw.sw_mutex);
return ((opaque_t)p);
}
}
mutex_exit(&sw.sw_mutex);
swr = kmem_zalloc(sizeof (struct scsi_watch_request), KM_SLEEP);
bp = scsi_alloc_consistent_buf(ROUTE, NULL,
sense_length, B_READ, SLEEP_FUNC, NULL);
rqpkt = scsi_init_pkt(ROUTE, (struct scsi_pkt *)NULL,
bp, CDB_GROUP0, 1, 0, PKT_CONSISTENT, SLEEP_FUNC, NULL);
(void) scsi_setup_cdb((union scsi_cdb *)rqpkt->pkt_cdbp,
SCMD_REQUEST_SENSE, 0, SENSE_LENGTH, 0);
FILL_SCSI1_LUN(devp, rqpkt);
rqpkt->pkt_private = (opaque_t)swr;
rqpkt->pkt_time = scsi_watch_io_time;
rqpkt->pkt_comp = scsi_watch_request_intr;
rqpkt->pkt_flags |= FLAG_HEAD;
dtype = devp->sd_inq->inq_dtype & DTYPE_MASK;
if (mmc) {
mmcbp = scsi_alloc_consistent_buf(ROUTE, NULL,
8, B_READ, SLEEP_FUNC, NULL);
pkt = scsi_init_pkt(ROUTE, (struct scsi_pkt *)NULL, mmcbp,
CDB_GROUP1, sizeof (struct scsi_arq_status),
0, 0, SLEEP_FUNC, NULL);
(void) scsi_setup_cdb((union scsi_cdb *)pkt->pkt_cdbp,
SCMD_GET_EVENT_STATUS_NOTIFICATION, 0, 8, 0);
pkt->pkt_cdbp[1] = 1;
pkt->pkt_cdbp[4] = 1 << SD_GESN_MEDIA_CLASS;
} else if (((dtype == 0) || (dtype == 0xE)) &&
(devp->sd_inq->inq_ansi > 2)) {
pkt = scsi_init_pkt(ROUTE, (struct scsi_pkt *)NULL, NULL,
CDB_GROUP1, sizeof (struct scsi_arq_status),
0, 0, SLEEP_FUNC, NULL);
(void) scsi_setup_cdb((union scsi_cdb *)pkt->pkt_cdbp,
SCMD_WRITE_G1, 0, 0, 0);
} else {
pkt = scsi_init_pkt(ROUTE, (struct scsi_pkt *)NULL, NULL,
CDB_GROUP0, sizeof (struct scsi_arq_status),
0, 0, SLEEP_FUNC, NULL);
(void) scsi_setup_cdb((union scsi_cdb *)pkt->pkt_cdbp,
SCMD_TEST_UNIT_READY, 0, 0, 0);
FILL_SCSI1_LUN(devp, pkt);
}
pkt->pkt_private = (opaque_t)swr;
pkt->pkt_time = scsi_watch_io_time;
pkt->pkt_comp = scsi_watch_request_intr;
if (scsi_ifgetcap(&pkt->pkt_address, "tagged-qing", 1) == 1) {
pkt->pkt_flags |= FLAG_STAG;
}
swr->swr_rqbp = bp;
swr->swr_rqpkt = rqpkt;
swr->swr_mmcbp = mmcbp;
swr->swr_pkt = pkt;
swr->swr_timeout = swr->swr_interval = drv_usectohz(interval);
swr->swr_callback = callback;
swr->swr_callback_arg = cb_arg;
swr->swr_what = SWR_WATCH;
swr->swr_sense_length = (uchar_t)sense_length;
swr->swr_ref = 1;
cv_init(&swr->swr_terminate_cv, NULL, CV_DRIVER, NULL);
mutex_enter(&sw.sw_mutex);
swr->swr_next = sw.sw_head;
swr->swr_prev = NULL;
if (sw.sw_head) {
sw.sw_head->swr_prev = swr;
}
sw.sw_head = swr;
sswr = swr;
while (sswr) {
sswr->swr_timeout = swr->swr_interval;
sswr = sswr->swr_next;
}
cv_signal(&sw.sw_cv);
mutex_exit(&sw.sw_mutex);
return ((opaque_t)swr);
}
void
scsi_watch_resume(opaque_t token)
{
struct scsi_watch_request *swr = (struct scsi_watch_request *)NULL;
SW_DEBUG(0, sw_label, SCSI_DEBUG, "scsi_watch_resume:\n");
mutex_enter(&sw.sw_mutex);
if (!sw.sw_head)
goto exit;
for (swr = sw.sw_head; swr; swr = swr->swr_next) {
if (swr == (struct scsi_watch_request *)token)
break;
}
if (swr == (struct scsi_watch_request *)NULL)
goto exit;
swr->swr_what = SWR_WATCH;
for (swr = sw.sw_head; swr; swr = swr->swr_next) {
if (swr->swr_what != SWR_WATCH)
goto exit;
}
sw.sw_state = SW_RUNNING;
cv_signal(&sw.sw_cv);
exit:
mutex_exit(&sw.sw_mutex);
}
void
scsi_watch_suspend(opaque_t token)
{
struct scsi_watch_request *swr = (struct scsi_watch_request *)NULL;
clock_t halfsec_delay = drv_usectohz(500000);
SW_DEBUG(0, sw_label, SCSI_DEBUG, "scsi_watch_suspend:\n");
mutex_enter(&sw.sw_mutex);
if (!sw.sw_head)
goto exit;
for (swr = sw.sw_head; swr; swr = swr->swr_next) {
if (swr == (struct scsi_watch_request *)token)
break;
}
if (swr == (struct scsi_watch_request *)NULL)
goto exit;
for (;;) {
if (swr->swr_busy) {
swr->swr_what = SWR_SUSPEND_REQUESTED;
(void) cv_reltimedwait(&sw.sw_cv, &sw.sw_mutex,
halfsec_delay, TR_CLOCK_TICK);
} else {
swr->swr_what = SWR_SUSPENDED;
break;
}
}
for (swr = sw.sw_head; swr; swr = swr->swr_next) {
if (swr->swr_what != SWR_SUSPENDED)
goto exit;
}
sw.sw_state = SW_SUSPENDED;
exit:
mutex_exit(&sw.sw_mutex);
}
static void
scsi_watch_request_destroy(struct scsi_watch_request *swr)
{
ASSERT(MUTEX_HELD(&sw.sw_mutex));
ASSERT(swr->swr_busy == 0);
SW_DEBUG((dev_info_t *)NULL, sw_label, SCSI_DEBUG,
"scsi_watch_request_destroy: Entering ...\n");
if (swr->swr_ref != 0)
return;
if (swr->swr_prev) {
swr->swr_prev->swr_next = swr->swr_next;
}
if (swr->swr_next) {
swr->swr_next->swr_prev = swr->swr_prev;
}
if (sw.sw_head == swr) {
sw.sw_head = swr->swr_next;
}
if (sw.swr_current == swr) {
swr->suspend_destroy = SUSPEND_DESTROY;
sw.swr_current = NULL;
}
scsi_destroy_pkt(swr->swr_rqpkt);
scsi_free_consistent_buf(swr->swr_rqbp);
if (swr->swr_mmcbp != NULL) {
scsi_free_consistent_buf(swr->swr_mmcbp);
}
scsi_destroy_pkt(swr->swr_pkt);
cv_signal(&swr->swr_terminate_cv);
}
int
scsi_watch_request_terminate(opaque_t token, int flags)
{
struct scsi_watch_request *swr =
(struct scsi_watch_request *)token;
struct scsi_watch_request *sswr;
int count = 0;
int free_flag = 0;
SW_DEBUG((dev_info_t *)NULL, sw_label, SCSI_DEBUG,
"scsi_watch_request_terminate: Entering(0x%p) ...\n",
(void *)swr);
mutex_enter(&sw.sw_mutex);
sswr = sw.sw_head;
while (sswr) {
if (sswr == swr) {
swr->swr_ref--;
count = swr->swr_ref;
if (swr->swr_busy) {
if (flags == SCSI_WATCH_TERMINATE_NOWAIT) {
mutex_exit(&sw.sw_mutex);
return (SCSI_WATCH_TERMINATE_FAIL);
}
if (count != 0 && flags !=
SCSI_WATCH_TERMINATE_ALL_WAIT) {
mutex_exit(&sw.sw_mutex);
return (SCSI_WATCH_TERMINATE_SUCCESS);
}
if (SCSI_WATCH_TERMINATE_ALL_WAIT == flags) {
swr->swr_ref = 0;
count = 0;
}
swr->swr_what = SWR_STOP;
cv_wait(&swr->swr_terminate_cv, &sw.sw_mutex);
free_flag = 1;
goto done;
} else {
if (SCSI_WATCH_TERMINATE_NOWAIT == flags ||
SCSI_WATCH_TERMINATE_ALL_WAIT == flags) {
swr->swr_ref = 0;
count = 0;
}
scsi_watch_request_destroy(swr);
if (0 == count) {
sw.sw_flags |= SW_START_HEAD;
free_flag = 1;
}
goto done;
}
}
sswr = sswr->swr_next;
}
done:
mutex_exit(&sw.sw_mutex);
if (!sswr) {
return (SCSI_WATCH_TERMINATE_FAIL);
}
if (1 == free_flag &&
sswr->suspend_destroy != SUSPEND_DESTROY) {
cv_destroy(&swr->swr_terminate_cv);
kmem_free((caddr_t)swr, sizeof (struct scsi_watch_request));
}
return (SCSI_WATCH_TERMINATE_SUCCESS);
}
static int sw_cmd_count = 0;
static int sw_cpr_flag = 0;
static callb_cpr_t cpr_info;
#ifndef __lock_lint
static kmutex_t cpr_mutex;
#else
extern kmutex_t cpr_mutex;
#endif
#if !defined(lint)
_NOTE(MUTEX_PROTECTS_DATA(cpr_mutex, cpr_info))
_NOTE(MUTEX_PROTECTS_DATA(cpr_mutex, sw_cmd_count))
#endif
static void
scsi_watch_thread()
{
struct scsi_watch_request *swr, *next;
clock_t last_delay = 0;
clock_t next_delay = 0;
clock_t onesec = drv_usectohz(1000000);
clock_t exit_delay = 60 * onesec;
SW_DEBUG((dev_info_t *)NULL, sw_label, SCSI_DEBUG,
"scsi_watch_thread: Entering ...\n");
#if !defined(lint)
_NOTE(NO_COMPETING_THREADS_NOW);
#endif
mutex_init(&cpr_mutex, NULL, MUTEX_DRIVER, NULL);
CALLB_CPR_INIT(&cpr_info,
&cpr_mutex, callb_generic_cpr, "scsi_watch");
sw_cpr_flag = 0;
#if !defined(lint)
_NOTE(COMPETING_THREADS_NOW);
#endif
mutex_enter(&sw.sw_mutex);
if (sw.sw_head == NULL) {
cv_wait(&sw.sw_cv, &sw.sw_mutex);
}
for (;;) {
head:
swr = sw.sw_head;
while (swr) {
if (sw.sw_state != SW_RUNNING) {
SW_DEBUG(0, sw_label, SCSI_DEBUG,
"scsi_watch_thread suspended\n");
mutex_enter(&cpr_mutex);
if (!sw_cmd_count) {
CALLB_CPR_SAFE_BEGIN(&cpr_info);
sw_cpr_flag = 1;
}
mutex_exit(&cpr_mutex);
sw.swr_current = swr;
cv_wait(&sw.sw_cv, &sw.sw_mutex);
mutex_exit(&sw.sw_mutex);
mutex_enter(&cpr_mutex);
if (sw_cpr_flag == 1) {
CALLB_CPR_SAFE_END(
&cpr_info, &cpr_mutex);
sw_cpr_flag = 0;
}
mutex_exit(&cpr_mutex);
mutex_enter(&sw.sw_mutex);
if (SUSPEND_DESTROY == swr->suspend_destroy) {
cv_destroy(&swr->swr_terminate_cv);
kmem_free((caddr_t)swr,
sizeof (struct scsi_watch_request));
goto head;
} else {
sw.swr_current = NULL;
}
}
if (next_delay == 0) {
next_delay = swr->swr_timeout;
} else {
next_delay = min(swr->swr_timeout, next_delay);
}
swr->swr_timeout -= last_delay;
next = swr->swr_next;
SW_DEBUG((dev_info_t *)NULL, sw_label, SCSI_DEBUG,
"scsi_watch_thread: "
"swr(0x%p),what=%x,timeout=%lx,"
"interval=%lx,delay=%lx\n",
(void *)swr, swr->swr_what, swr->swr_timeout,
swr->swr_interval, last_delay);
switch (swr->swr_what) {
case SWR_SUSPENDED:
case SWR_SUSPEND_REQUESTED:
break;
case SWR_STOP:
if (swr->swr_busy == 0) {
scsi_watch_request_destroy(swr);
}
break;
default:
if (swr->swr_timeout <= 0 && !swr->swr_busy) {
swr->swr_busy = 1;
swr->swr_timeout = swr->swr_interval;
mutex_exit(&sw.sw_mutex);
mutex_enter(&cpr_mutex);
sw_cmd_count++;
mutex_exit(&cpr_mutex);
SW_DEBUG((dev_info_t *)NULL,
sw_label, SCSI_DEBUG,
"scsi_watch_thread: "
"Starting TUR\n");
if (scsi_transport(swr->swr_pkt) !=
TRAN_ACCEPT) {
swr->swr_busy = 0;
SW_DEBUG((dev_info_t *)NULL,
sw_label, SCSI_DEBUG,
"scsi_watch_thread: "
"Transport Failed\n");
mutex_enter(&cpr_mutex);
sw_cmd_count--;
mutex_exit(&cpr_mutex);
}
mutex_enter(&sw.sw_mutex);
}
break;
}
swr = next;
if (sw.sw_flags & SW_START_HEAD) {
sw.sw_flags &= ~SW_START_HEAD;
goto head;
}
}
if (sw.sw_head != NULL) {
if (next_delay <= 0) {
next_delay = onesec;
}
} else {
next_delay = exit_delay;
}
mutex_enter(&cpr_mutex);
if (!sw_cmd_count) {
CALLB_CPR_SAFE_BEGIN(&cpr_info);
sw_cpr_flag = 1;
}
mutex_exit(&cpr_mutex);
(void) cv_reltimedwait(&sw.sw_cv, &sw.sw_mutex, next_delay,
TR_CLOCK_TICK);
mutex_exit(&sw.sw_mutex);
mutex_enter(&cpr_mutex);
if (sw_cpr_flag == 1) {
CALLB_CPR_SAFE_END(&cpr_info, &cpr_mutex);
sw_cpr_flag = 0;
}
mutex_exit(&cpr_mutex);
mutex_enter(&sw.sw_mutex);
last_delay = next_delay;
next_delay = 0;
if (sw.sw_head == NULL) {
break;
}
}
sw.sw_thread = 0;
mutex_exit(&sw.sw_mutex);
#ifndef __lock_lint
mutex_enter(&cpr_mutex);
CALLB_CPR_EXIT(&cpr_info);
#endif
mutex_destroy(&cpr_mutex);
SW_DEBUG((dev_info_t *)NULL, sw_label, SCSI_DEBUG,
"scsi_watch_thread: Exiting ...\n");
}
#define SCBP(pkt) ((struct scsi_status *)(pkt)->pkt_scbp)
#define SCBP_C(pkt) ((*(pkt)->pkt_scbp) & STATUS_MASK)
static void
scsi_watch_request_intr(struct scsi_pkt *pkt)
{
struct scsi_watch_result result;
struct scsi_watch_request *swr =
(struct scsi_watch_request *)pkt->pkt_private;
struct scsi_status *rqstatusp;
struct scsi_extended_sense *rqsensep = NULL;
int amt = 0;
SW_DEBUG((dev_info_t *)NULL, sw_label, SCSI_DEBUG,
"scsi_watch_intr: Entering ...\n");
if (pkt == swr->swr_pkt) {
if (SCBP_C(pkt) != STATUS_GOOD &&
SCBP_C(pkt) != STATUS_RESERVATION_CONFLICT) {
if (SCBP(pkt)->sts_chk &&
((pkt->pkt_state & STATE_ARQ_DONE) == 0)) {
SW_DEBUG((dev_info_t *)NULL,
sw_label, SCSI_DEBUG,
"scsi_watch_intr: "
"Submitting a Request Sense "
"Packet\n");
if (scsi_transport(swr->swr_rqpkt) !=
TRAN_ACCEPT) {
SW_DEBUG((dev_info_t *)NULL,
sw_label, SCSI_DEBUG,
"scsi_watch_intr: "
"Request Sense "
"Transport Failed\n");
goto done;
}
return;
} else if (SCBP(pkt)->sts_chk) {
struct scsi_arq_status *arqstat =
(struct scsi_arq_status *)pkt->pkt_scbp;
rqstatusp = &arqstat->sts_rqpkt_status;
rqsensep = &arqstat->sts_sensedata;
amt = swr->swr_sense_length -
arqstat->sts_rqpkt_resid;
SW_DEBUG((dev_info_t *)NULL,
sw_label, SCSI_DEBUG,
"scsi_watch_intr: "
"Auto Request Sense, amt=%x\n", amt);
}
}
} else if (pkt == swr->swr_rqpkt) {
rqstatusp = (struct scsi_status *)pkt->pkt_scbp;
rqsensep = (struct scsi_extended_sense *)
swr->swr_rqbp->b_un.b_addr;
amt = swr->swr_sense_length - pkt->pkt_resid;
SW_DEBUG((dev_info_t *)NULL, sw_label, SCSI_DEBUG,
"scsi_watch_intr: "
"Request Sense Completed, amt=%x\n", amt);
} else {
scsi_log((dev_info_t *)NULL, sw_label, CE_PANIC,
"scsi_watch_intr: Bad Packet(0x%p)", (void *)pkt);
}
if (rqsensep) {
if (rqstatusp->sts_busy || rqstatusp->sts_chk) {
SW_DEBUG((dev_info_t *)NULL, sw_label, SCSI_DEBUG,
"scsi_watch_intr: "
"Auto Request Sense Failed - "
"Busy or Check Condition\n");
goto done;
}
SW_DEBUG((dev_info_t *)NULL, sw_label, SCSI_DEBUG,
"scsi_watch_intr: "
"es_key=%x, adq=%x, amt=%x\n",
rqsensep->es_key, rqsensep->es_add_code, amt);
}
result.statusp = SCBP(swr->swr_pkt);
result.sensep = rqsensep;
result.actual_sense_length = (uchar_t)amt;
result.pkt = swr->swr_pkt;
if (swr->swr_mmcbp != NULL) {
bcopy(swr->swr_mmcbp->b_un.b_addr, result.mmc_data, 8);
}
if ((*swr->swr_callback)(swr->swr_callback_arg, &result)) {
swr->swr_what = SWR_STOP;
}
done:
swr->swr_busy = 0;
mutex_enter(&cpr_mutex);
sw_cmd_count --;
if (!sw_cmd_count) {
CALLB_CPR_SAFE_BEGIN(&cpr_info);
sw_cpr_flag = 1;
}
mutex_exit(&cpr_mutex);
}
int
scsi_watch_get_ref_count(opaque_t token)
{
struct scsi_watch_request *swr =
(struct scsi_watch_request *)token;
struct scsi_watch_request *sswr;
int rval = 0;
SW_DEBUG((dev_info_t *)NULL, sw_label, SCSI_DEBUG,
"scsi_watch_get_ref_count: Entering(0x%p) ...\n",
(void *)swr);
mutex_enter(&sw.sw_mutex);
sswr = sw.sw_head;
while (sswr) {
if (sswr == swr) {
rval = swr->swr_ref;
mutex_exit(&sw.sw_mutex);
return (rval);
}
sswr = sswr->swr_next;
}
mutex_exit(&sw.sw_mutex);
return (rval);
}