2000-01-17 20:59:48 +03:00
|
|
|
/* $NetBSD: scsipi_base.c,v 1.29 2000/01/17 17:59:48 bouyer Exp $ */
|
1997-08-27 15:22:52 +04:00
|
|
|
|
1998-08-15 14:10:47 +04:00
|
|
|
/*-
|
|
|
|
* Copyright (c) 1998 The NetBSD Foundation, Inc.
|
|
|
|
* All rights reserved.
|
|
|
|
*
|
|
|
|
* This code is derived from software contributed to The NetBSD Foundation
|
|
|
|
* by Charles M. Hannum.
|
1997-08-27 15:22:52 +04:00
|
|
|
*
|
|
|
|
* Redistribution and use in source and binary forms, with or without
|
|
|
|
* modification, are permitted provided that the following conditions
|
|
|
|
* are met:
|
|
|
|
* 1. Redistributions of source code must retain the above copyright
|
|
|
|
* notice, this list of conditions and the following disclaimer.
|
|
|
|
* 2. Redistributions in binary form must reproduce the above copyright
|
|
|
|
* notice, this list of conditions and the following disclaimer in the
|
|
|
|
* documentation and/or other materials provided with the distribution.
|
|
|
|
* 3. All advertising materials mentioning features or use of this software
|
|
|
|
* must display the following acknowledgement:
|
1998-08-15 14:10:47 +04:00
|
|
|
* This product includes software developed by the NetBSD
|
|
|
|
* Foundation, Inc. and its contributors.
|
|
|
|
* 4. Neither the name of The NetBSD Foundation nor the names of its
|
|
|
|
* contributors may be used to endorse or promote products derived
|
|
|
|
* from this software without specific prior written permission.
|
1997-08-27 15:22:52 +04:00
|
|
|
*
|
1998-08-15 14:10:47 +04:00
|
|
|
* THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS
|
|
|
|
* ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
|
|
|
|
* TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
|
|
|
|
* PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS
|
|
|
|
* BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
|
|
|
|
* CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
|
|
|
|
* SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
|
|
|
|
* INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
|
|
|
|
* CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
|
|
|
|
* ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
|
|
|
|
* POSSIBILITY OF SUCH DAMAGE.
|
1997-08-27 15:22:52 +04:00
|
|
|
*/
|
|
|
|
|
1998-11-17 17:38:42 +03:00
|
|
|
#include "opt_scsi.h"
|
|
|
|
|
1997-08-27 15:22:52 +04:00
|
|
|
#include <sys/types.h>
|
|
|
|
#include <sys/param.h>
|
|
|
|
#include <sys/systm.h>
|
|
|
|
#include <sys/kernel.h>
|
|
|
|
#include <sys/buf.h>
|
|
|
|
#include <sys/uio.h>
|
|
|
|
#include <sys/malloc.h>
|
1998-07-31 07:00:51 +04:00
|
|
|
#include <sys/pool.h>
|
1997-08-27 15:22:52 +04:00
|
|
|
#include <sys/errno.h>
|
|
|
|
#include <sys/device.h>
|
|
|
|
#include <sys/proc.h>
|
|
|
|
|
|
|
|
#include <dev/scsipi/scsipi_all.h>
|
|
|
|
#include <dev/scsipi/scsipi_disk.h>
|
|
|
|
#include <dev/scsipi/scsipiconf.h>
|
|
|
|
#include <dev/scsipi/scsipi_base.h>
|
|
|
|
|
1998-07-31 07:00:51 +04:00
|
|
|
struct pool scsipi_xfer_pool;
|
|
|
|
|
1997-10-01 05:18:38 +04:00
|
|
|
int sc_err1 __P((struct scsipi_xfer *, int));
|
1997-08-27 15:22:52 +04:00
|
|
|
|
1998-07-31 07:00:51 +04:00
|
|
|
/*
|
|
|
|
* Called when a scsibus is attached to initialize global data.
|
|
|
|
*/
|
|
|
|
void
|
|
|
|
scsipi_init()
|
|
|
|
{
|
|
|
|
static int scsipi_init_done;
|
|
|
|
|
|
|
|
if (scsipi_init_done)
|
|
|
|
return;
|
|
|
|
scsipi_init_done = 1;
|
|
|
|
|
|
|
|
/* Initialize the scsipi_xfer pool. */
|
|
|
|
pool_init(&scsipi_xfer_pool, sizeof(struct scsipi_xfer), 0,
|
|
|
|
0, 0, "scxspl", 0, NULL, NULL, M_DEVBUF);
|
|
|
|
}
|
|
|
|
|
1997-08-27 15:22:52 +04:00
|
|
|
/*
|
|
|
|
* Get a scsipi transfer structure for the caller. Charge the structure
|
1997-10-01 05:18:38 +04:00
|
|
|
* to the device that is referenced by the sc_link structure. If the
|
1997-08-27 15:22:52 +04:00
|
|
|
* sc_link structure has no 'credits' then the device already has the
|
|
|
|
* maximum number or outstanding operations under way. In this stage,
|
|
|
|
* wait on the structure so that when one is freed, we are awoken again
|
1999-10-01 02:57:52 +04:00
|
|
|
* If the XS_CTL_NOSLEEP flag is set, then do not wait, but rather, return
|
1997-08-27 15:22:52 +04:00
|
|
|
* a NULL pointer, signifying that no slots were available
|
|
|
|
* Note in the link structure, that we are waiting on it.
|
|
|
|
*/
|
|
|
|
|
|
|
|
struct scsipi_xfer *
|
|
|
|
scsipi_get_xs(sc_link, flags)
|
|
|
|
struct scsipi_link *sc_link; /* who to charge the xs to */
|
|
|
|
int flags; /* if this call can sleep */
|
|
|
|
{
|
|
|
|
struct scsipi_xfer *xs;
|
|
|
|
int s;
|
|
|
|
|
|
|
|
SC_DEBUG(sc_link, SDEV_DB3, ("scsipi_get_xs\n"));
|
1998-07-31 07:00:51 +04:00
|
|
|
|
1999-10-01 02:57:52 +04:00
|
|
|
/*
|
|
|
|
* If we're cold, make sure we poll.
|
|
|
|
*/
|
|
|
|
if (cold)
|
|
|
|
flags |= XS_CTL_NOSLEEP | XS_CTL_POLL;
|
|
|
|
|
1997-08-27 15:22:52 +04:00
|
|
|
s = splbio();
|
1999-10-01 02:57:52 +04:00
|
|
|
while (sc_link->active >= sc_link->openings) {
|
1997-08-27 15:22:52 +04:00
|
|
|
SC_DEBUG(sc_link, SDEV_DB3, ("sleeping\n"));
|
1999-10-01 02:57:52 +04:00
|
|
|
if ((flags & XS_CTL_NOSLEEP) != 0) {
|
1997-08-27 15:22:52 +04:00
|
|
|
splx(s);
|
1997-10-01 05:18:38 +04:00
|
|
|
return (0);
|
1997-08-27 15:22:52 +04:00
|
|
|
}
|
|
|
|
sc_link->flags |= SDEV_WAITING;
|
1997-10-01 05:18:38 +04:00
|
|
|
(void)tsleep(sc_link, PRIBIO, "getxs", 0);
|
1997-08-27 15:22:52 +04:00
|
|
|
}
|
1998-07-31 07:00:51 +04:00
|
|
|
SC_DEBUG(sc_link, SDEV_DB3, ("calling pool_get\n"));
|
|
|
|
xs = pool_get(&scsipi_xfer_pool,
|
1999-10-01 02:57:52 +04:00
|
|
|
((flags & XS_CTL_NOSLEEP) != 0 ? PR_NOWAIT : PR_WAITOK));
|
1998-07-31 07:00:51 +04:00
|
|
|
if (xs != NULL)
|
1999-10-01 02:57:52 +04:00
|
|
|
sc_link->active++;
|
1998-07-31 07:00:51 +04:00
|
|
|
else {
|
|
|
|
(*sc_link->sc_print_addr)(sc_link);
|
|
|
|
printf("cannot allocate scsipi xs\n");
|
1997-08-27 15:22:52 +04:00
|
|
|
}
|
1998-07-31 07:00:51 +04:00
|
|
|
splx(s);
|
1997-08-27 15:22:52 +04:00
|
|
|
|
|
|
|
SC_DEBUG(sc_link, SDEV_DB3, ("returning\n"));
|
1998-07-31 07:00:51 +04:00
|
|
|
|
1997-10-01 05:18:38 +04:00
|
|
|
/*
|
1998-08-11 09:47:43 +04:00
|
|
|
* zeroes out the command, as ATAPI may use longer commands
|
1997-10-01 05:18:38 +04:00
|
|
|
* than SCSI
|
|
|
|
*/
|
1998-08-11 09:47:43 +04:00
|
|
|
if (xs != NULL) {
|
1999-10-01 02:57:52 +04:00
|
|
|
xs->xs_control = flags;
|
1998-12-08 03:17:21 +03:00
|
|
|
TAILQ_INSERT_TAIL(&sc_link->pending_xfers, xs, device_q);
|
1998-08-11 09:47:43 +04:00
|
|
|
bzero(&xs->cmdstore, sizeof(xs->cmdstore));
|
|
|
|
}
|
1997-10-01 05:18:38 +04:00
|
|
|
return (xs);
|
1997-08-27 15:22:52 +04:00
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Given a scsipi_xfer struct, and a device (referenced through sc_link)
|
|
|
|
* return the struct to the free pool and credit the device with it
|
|
|
|
* If another process is waiting for an xs, do a wakeup, let it proceed
|
1998-07-31 07:00:51 +04:00
|
|
|
*
|
|
|
|
* MUST BE CALLED AT splbio()!!
|
1997-08-27 15:22:52 +04:00
|
|
|
*/
|
1997-10-01 05:18:38 +04:00
|
|
|
void
|
1997-08-27 15:22:52 +04:00
|
|
|
scsipi_free_xs(xs, flags)
|
|
|
|
struct scsipi_xfer *xs;
|
|
|
|
int flags;
|
|
|
|
{
|
|
|
|
struct scsipi_link *sc_link = xs->sc_link;
|
|
|
|
|
1998-12-08 03:17:21 +03:00
|
|
|
TAILQ_REMOVE(&sc_link->pending_xfers, xs, device_q);
|
|
|
|
if (TAILQ_FIRST(&sc_link->pending_xfers) == NULL &&
|
1998-12-08 03:26:22 +03:00
|
|
|
(sc_link->flags & SDEV_WAITDRAIN) != 0) {
|
|
|
|
sc_link->flags &= ~SDEV_WAITDRAIN;
|
1998-12-08 03:17:21 +03:00
|
|
|
wakeup(&sc_link->pending_xfers);
|
1998-12-08 03:26:22 +03:00
|
|
|
}
|
1998-07-31 07:00:51 +04:00
|
|
|
pool_put(&scsipi_xfer_pool, xs);
|
1997-08-27 15:22:52 +04:00
|
|
|
|
|
|
|
SC_DEBUG(sc_link, SDEV_DB3, ("scsipi_free_xs\n"));
|
|
|
|
/* if was 0 and someone waits, wake them up */
|
1999-10-01 02:57:52 +04:00
|
|
|
sc_link->active--;
|
1997-08-27 15:22:52 +04:00
|
|
|
if ((sc_link->flags & SDEV_WAITING) != 0) {
|
|
|
|
sc_link->flags &= ~SDEV_WAITING;
|
|
|
|
wakeup(sc_link);
|
|
|
|
} else {
|
|
|
|
if (sc_link->device->start) {
|
1997-10-01 05:18:38 +04:00
|
|
|
SC_DEBUG(sc_link, SDEV_DB2,
|
|
|
|
("calling private start()\n"));
|
|
|
|
(*(sc_link->device->start))(sc_link->device_softc);
|
1997-08-27 15:22:52 +04:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
1998-12-08 03:17:21 +03:00
|
|
|
/*
|
|
|
|
* Wait for a scsipi_link's pending xfers to drain.
|
|
|
|
*/
|
|
|
|
void
|
|
|
|
scsipi_wait_drain(sc_link)
|
|
|
|
struct scsipi_link *sc_link;
|
|
|
|
{
|
|
|
|
int s;
|
|
|
|
|
|
|
|
s = splbio();
|
1998-12-08 03:26:22 +03:00
|
|
|
while (TAILQ_FIRST(&sc_link->pending_xfers) != NULL) {
|
1998-12-08 03:17:21 +03:00
|
|
|
sc_link->flags |= SDEV_WAITDRAIN;
|
|
|
|
(void) tsleep(&sc_link->pending_xfers, PRIBIO, "sxdrn", 0);
|
|
|
|
}
|
|
|
|
splx(s);
|
|
|
|
}
|
|
|
|
|
1999-09-12 01:39:53 +04:00
|
|
|
/*
|
|
|
|
* Kill off all pending xfers for a scsipi_link.
|
|
|
|
*
|
|
|
|
* Must be called at splbio().
|
|
|
|
*/
|
|
|
|
void
|
|
|
|
scsipi_kill_pending(sc_link)
|
|
|
|
struct scsipi_link *sc_link;
|
|
|
|
{
|
|
|
|
|
1999-10-20 19:22:24 +04:00
|
|
|
(*sc_link->scsipi_kill_pending)(sc_link);
|
|
|
|
#ifdef DIAGNOSTIC
|
|
|
|
if (TAILQ_FIRST(&sc_link->pending_xfers) != NULL)
|
|
|
|
panic("scsipi_kill_pending");
|
|
|
|
#endif
|
1999-09-12 01:39:53 +04:00
|
|
|
}
|
|
|
|
|
1998-11-17 17:38:42 +03:00
|
|
|
/*
|
|
|
|
* Look at the returned sense and act on the error, determining
|
|
|
|
* the unix error number to pass back. (0 = report no error)
|
|
|
|
*
|
|
|
|
* THIS IS THE DEFAULT ERROR HANDLER FOR SCSI DEVICES
|
|
|
|
*/
|
|
|
|
int
|
|
|
|
scsipi_interpret_sense(xs)
|
|
|
|
struct scsipi_xfer *xs;
|
|
|
|
{
|
|
|
|
struct scsipi_sense_data *sense;
|
|
|
|
struct scsipi_link *sc_link = xs->sc_link;
|
|
|
|
u_int8_t key;
|
|
|
|
u_int32_t info;
|
|
|
|
int error;
|
|
|
|
#ifndef SCSIVERBOSE
|
|
|
|
static char *error_mes[] = {
|
|
|
|
"soft error (corrected)",
|
|
|
|
"not ready", "medium error",
|
|
|
|
"non-media hardware failure", "illegal request",
|
|
|
|
"unit attention", "readonly device",
|
|
|
|
"no data found", "vendor unique",
|
|
|
|
"copy aborted", "command aborted",
|
|
|
|
"search returned equal", "volume overflow",
|
|
|
|
"verify miscompare", "unknown error key"
|
|
|
|
};
|
|
|
|
#endif
|
|
|
|
|
|
|
|
sense = &xs->sense.scsi_sense;
|
|
|
|
#ifdef SCSIDEBUG
|
|
|
|
if ((sc_link->flags & SDEV_DB1) != 0) {
|
|
|
|
int count;
|
|
|
|
printf("code 0x%x valid 0x%x ",
|
|
|
|
sense->error_code & SSD_ERRCODE,
|
|
|
|
sense->error_code & SSD_ERRCODE_VALID ? 1 : 0);
|
|
|
|
printf("seg 0x%x key 0x%x ili 0x%x eom 0x%x fmark 0x%x\n",
|
|
|
|
sense->segment,
|
|
|
|
sense->flags & SSD_KEY,
|
|
|
|
sense->flags & SSD_ILI ? 1 : 0,
|
|
|
|
sense->flags & SSD_EOM ? 1 : 0,
|
|
|
|
sense->flags & SSD_FILEMARK ? 1 : 0);
|
|
|
|
printf("info: 0x%x 0x%x 0x%x 0x%x followed by %d extra bytes\n",
|
|
|
|
sense->info[0],
|
|
|
|
sense->info[1],
|
|
|
|
sense->info[2],
|
|
|
|
sense->info[3],
|
|
|
|
sense->extra_len);
|
|
|
|
printf("extra: ");
|
|
|
|
for (count = 0; count < ADD_BYTES_LIM(sense); count++)
|
|
|
|
printf("0x%x ", sense->cmd_spec_info[count]);
|
|
|
|
printf("\n");
|
|
|
|
}
|
|
|
|
#endif /* SCSIDEBUG */
|
|
|
|
/*
|
|
|
|
* If the device has it's own error handler, call it first.
|
|
|
|
* If it returns a legit error value, return that, otherwise
|
|
|
|
* it wants us to continue with normal error processing.
|
|
|
|
*/
|
|
|
|
if (sc_link->device->err_handler) {
|
|
|
|
SC_DEBUG(sc_link, SDEV_DB2,
|
|
|
|
("calling private err_handler()\n"));
|
|
|
|
error = (*sc_link->device->err_handler)(xs);
|
|
|
|
if (error != SCSIRET_CONTINUE)
|
|
|
|
return (error); /* error >= 0 better ? */
|
|
|
|
}
|
|
|
|
/* otherwise use the default */
|
|
|
|
switch (sense->error_code & SSD_ERRCODE) {
|
|
|
|
/*
|
|
|
|
* If it's code 70, use the extended stuff and
|
|
|
|
* interpret the key
|
|
|
|
*/
|
|
|
|
case 0x71: /* delayed error */
|
|
|
|
sc_link->sc_print_addr(sc_link);
|
|
|
|
key = sense->flags & SSD_KEY;
|
|
|
|
printf(" DEFERRED ERROR, key = 0x%x\n", key);
|
|
|
|
/* FALLTHROUGH */
|
|
|
|
case 0x70:
|
|
|
|
if ((sense->error_code & SSD_ERRCODE_VALID) != 0)
|
|
|
|
info = _4btol(sense->info);
|
|
|
|
else
|
|
|
|
info = 0;
|
|
|
|
key = sense->flags & SSD_KEY;
|
|
|
|
|
|
|
|
switch (key) {
|
|
|
|
case SKEY_NO_SENSE:
|
|
|
|
case SKEY_RECOVERED_ERROR:
|
|
|
|
if (xs->resid == xs->datalen && xs->datalen) {
|
|
|
|
/*
|
|
|
|
* Why is this here?
|
|
|
|
*/
|
|
|
|
xs->resid = 0; /* not short read */
|
|
|
|
}
|
|
|
|
case SKEY_EQUAL:
|
|
|
|
error = 0;
|
|
|
|
break;
|
|
|
|
case SKEY_NOT_READY:
|
|
|
|
if ((sc_link->flags & SDEV_REMOVABLE) != 0)
|
|
|
|
sc_link->flags &= ~SDEV_MEDIA_LOADED;
|
1999-10-01 02:57:52 +04:00
|
|
|
if ((xs->xs_control & XS_CTL_IGNORE_NOT_READY) != 0)
|
1998-11-17 17:38:42 +03:00
|
|
|
return (0);
|
1999-01-29 14:17:58 +03:00
|
|
|
if (sense->add_sense_code == 0x3A &&
|
|
|
|
sense->add_sense_code_qual == 0x00)
|
|
|
|
error = ENODEV; /* Medium not present */
|
|
|
|
else
|
|
|
|
error = EIO;
|
1999-10-01 02:57:52 +04:00
|
|
|
if ((xs->xs_control & XS_CTL_SILENT) != 0)
|
1999-01-29 14:17:58 +03:00
|
|
|
return (error);
|
1998-11-17 17:38:42 +03:00
|
|
|
break;
|
|
|
|
case SKEY_ILLEGAL_REQUEST:
|
1999-10-01 02:57:52 +04:00
|
|
|
if ((xs->xs_control &
|
|
|
|
XS_CTL_IGNORE_ILLEGAL_REQUEST) != 0)
|
1998-11-17 17:38:42 +03:00
|
|
|
return (0);
|
1999-10-01 02:57:52 +04:00
|
|
|
/*
|
|
|
|
* Handle the case where a device reports
|
|
|
|
* Logical Unit Not Supported during discovery.
|
|
|
|
*/
|
|
|
|
if ((xs->xs_control & XS_CTL_DISCOVERY) != 0 &&
|
|
|
|
sense->add_sense_code == 0x25 &&
|
|
|
|
sense->add_sense_code_qual == 0x00)
|
|
|
|
return (EINVAL);
|
|
|
|
if ((xs->xs_control & XS_CTL_SILENT) != 0)
|
1998-11-17 17:38:42 +03:00
|
|
|
return (EIO);
|
|
|
|
error = EINVAL;
|
|
|
|
break;
|
|
|
|
case SKEY_UNIT_ATTENTION:
|
1999-02-02 16:01:36 +03:00
|
|
|
if (sense->add_sense_code == 0x29 &&
|
|
|
|
sense->add_sense_code_qual == 0x00)
|
|
|
|
return (ERESTART); /* device or bus reset */
|
1998-11-17 17:38:42 +03:00
|
|
|
if ((sc_link->flags & SDEV_REMOVABLE) != 0)
|
|
|
|
sc_link->flags &= ~SDEV_MEDIA_LOADED;
|
1999-10-01 02:57:52 +04:00
|
|
|
if ((xs->xs_control &
|
|
|
|
XS_CTL_IGNORE_MEDIA_CHANGE) != 0 ||
|
1998-11-17 17:38:42 +03:00
|
|
|
/* XXX Should reupload any transient state. */
|
|
|
|
(sc_link->flags & SDEV_REMOVABLE) == 0)
|
|
|
|
return (ERESTART);
|
1999-10-01 02:57:52 +04:00
|
|
|
if ((xs->xs_control & XS_CTL_SILENT) != 0)
|
1998-11-17 17:38:42 +03:00
|
|
|
return (EIO);
|
|
|
|
error = EIO;
|
|
|
|
break;
|
|
|
|
case SKEY_WRITE_PROTECT:
|
|
|
|
error = EROFS;
|
|
|
|
break;
|
|
|
|
case SKEY_BLANK_CHECK:
|
|
|
|
error = 0;
|
|
|
|
break;
|
|
|
|
case SKEY_ABORTED_COMMAND:
|
|
|
|
error = ERESTART;
|
|
|
|
break;
|
|
|
|
case SKEY_VOLUME_OVERFLOW:
|
|
|
|
error = ENOSPC;
|
|
|
|
break;
|
|
|
|
default:
|
|
|
|
error = EIO;
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
|
|
|
|
#ifdef SCSIVERBOSE
|
1999-10-01 02:57:52 +04:00
|
|
|
if ((xs->xs_control & XS_CTL_SILENT) == 0)
|
1998-11-17 17:38:42 +03:00
|
|
|
scsipi_print_sense(xs, 0);
|
|
|
|
#else
|
|
|
|
if (key) {
|
|
|
|
sc_link->sc_print_addr(sc_link);
|
|
|
|
printf("%s", error_mes[key - 1]);
|
|
|
|
if ((sense->error_code & SSD_ERRCODE_VALID) != 0) {
|
|
|
|
switch (key) {
|
|
|
|
case SKEY_NOT_READY:
|
|
|
|
case SKEY_ILLEGAL_REQUEST:
|
|
|
|
case SKEY_UNIT_ATTENTION:
|
|
|
|
case SKEY_WRITE_PROTECT:
|
|
|
|
break;
|
|
|
|
case SKEY_BLANK_CHECK:
|
|
|
|
printf(", requested size: %d (decimal)",
|
|
|
|
info);
|
|
|
|
break;
|
|
|
|
case SKEY_ABORTED_COMMAND:
|
|
|
|
if (xs->retries)
|
|
|
|
printf(", retrying");
|
|
|
|
printf(", cmd 0x%x, info 0x%x",
|
|
|
|
xs->cmd->opcode, info);
|
|
|
|
break;
|
|
|
|
default:
|
|
|
|
printf(", info = %d (decimal)", info);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
if (sense->extra_len != 0) {
|
|
|
|
int n;
|
|
|
|
printf(", data =");
|
|
|
|
for (n = 0; n < sense->extra_len; n++)
|
|
|
|
printf(" %02x",
|
|
|
|
sense->cmd_spec_info[n]);
|
|
|
|
}
|
|
|
|
printf("\n");
|
|
|
|
}
|
|
|
|
#endif
|
|
|
|
return (error);
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Not code 70, just report it
|
|
|
|
*/
|
|
|
|
default:
|
2000-01-14 05:40:45 +03:00
|
|
|
#if defined(SCSIDEBUG) || defined(DEBUG)
|
|
|
|
{
|
|
|
|
static char *uc = "undecodable sense error";
|
|
|
|
int i;
|
|
|
|
u_int8_t *cptr = (u_int8_t *) sense;
|
|
|
|
sc_link->sc_print_addr(sc_link);
|
|
|
|
if (xs->cmd == &xs->cmdstore) {
|
|
|
|
printf("%s for opcode 0x%x, data=",
|
|
|
|
uc, xs->cmdstore.opcode);
|
|
|
|
} else {
|
|
|
|
printf("%s, data=", uc);
|
|
|
|
}
|
|
|
|
for (i = 0; i < sizeof (sense); i++)
|
|
|
|
printf(" 0x%02x", *(cptr++) & 0xff);
|
|
|
|
printf("\n");
|
|
|
|
}
|
|
|
|
#else
|
1998-11-17 17:38:42 +03:00
|
|
|
sc_link->sc_print_addr(sc_link);
|
1999-01-10 09:26:23 +03:00
|
|
|
printf("Sense Error Code 0x%x",
|
|
|
|
sense->error_code & SSD_ERRCODE);
|
1998-11-17 17:38:42 +03:00
|
|
|
if ((sense->error_code & SSD_ERRCODE_VALID) != 0) {
|
|
|
|
struct scsipi_sense_data_unextended *usense =
|
|
|
|
(struct scsipi_sense_data_unextended *)sense;
|
|
|
|
printf(" at block no. %d (decimal)",
|
|
|
|
_3btol(usense->block));
|
|
|
|
}
|
|
|
|
printf("\n");
|
2000-01-14 05:40:45 +03:00
|
|
|
#endif
|
1998-11-17 17:38:42 +03:00
|
|
|
return (EIO);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
1997-08-27 15:22:52 +04:00
|
|
|
/*
|
|
|
|
* Find out from the device what its capacity is.
|
|
|
|
*/
|
|
|
|
u_long
|
|
|
|
scsipi_size(sc_link, flags)
|
|
|
|
struct scsipi_link *sc_link;
|
|
|
|
int flags;
|
|
|
|
{
|
|
|
|
struct scsipi_read_cap_data rdcap;
|
|
|
|
struct scsipi_read_capacity scsipi_cmd;
|
|
|
|
|
|
|
|
/*
|
|
|
|
* make up a scsipi command and ask the scsipi driver to do
|
|
|
|
* it for you.
|
|
|
|
*/
|
|
|
|
bzero(&scsipi_cmd, sizeof(scsipi_cmd));
|
|
|
|
scsipi_cmd.opcode = READ_CAPACITY;
|
|
|
|
|
|
|
|
/*
|
|
|
|
* If the command works, interpret the result as a 4 byte
|
|
|
|
* number of blocks
|
|
|
|
*/
|
1997-10-18 23:50:51 +04:00
|
|
|
if (scsipi_command(sc_link, (struct scsipi_generic *)&scsipi_cmd,
|
1997-10-01 05:18:38 +04:00
|
|
|
sizeof(scsipi_cmd), (u_char *)&rdcap, sizeof(rdcap),
|
2000-01-17 20:59:48 +03:00
|
|
|
SCSIPIRETRIES, 20000, NULL, flags | XS_CTL_DATA_IN) != 0) {
|
1997-08-27 15:22:52 +04:00
|
|
|
sc_link->sc_print_addr(sc_link);
|
|
|
|
printf("could not get size\n");
|
1997-10-01 05:18:38 +04:00
|
|
|
return (0);
|
1997-08-27 15:22:52 +04:00
|
|
|
}
|
|
|
|
|
1997-10-01 05:18:38 +04:00
|
|
|
return (_4btol(rdcap.addr) + 1);
|
1997-08-27 15:22:52 +04:00
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Get scsipi driver to send a "are you ready?" command
|
|
|
|
*/
|
1997-10-01 05:18:38 +04:00
|
|
|
int
|
1997-08-27 15:22:52 +04:00
|
|
|
scsipi_test_unit_ready(sc_link, flags)
|
|
|
|
struct scsipi_link *sc_link;
|
|
|
|
int flags;
|
|
|
|
{
|
|
|
|
struct scsipi_test_unit_ready scsipi_cmd;
|
|
|
|
|
|
|
|
/* some ATAPI drives don't support TEST_UNIT_READY. Sigh */
|
|
|
|
if (sc_link->quirks & ADEV_NOTUR)
|
1997-10-01 05:18:38 +04:00
|
|
|
return (0);
|
1997-08-27 15:22:52 +04:00
|
|
|
|
|
|
|
bzero(&scsipi_cmd, sizeof(scsipi_cmd));
|
|
|
|
scsipi_cmd.opcode = TEST_UNIT_READY;
|
|
|
|
|
1997-10-18 23:50:51 +04:00
|
|
|
return (scsipi_command(sc_link,
|
1997-10-01 05:18:38 +04:00
|
|
|
(struct scsipi_generic *)&scsipi_cmd, sizeof(scsipi_cmd),
|
2000-01-17 20:59:48 +03:00
|
|
|
0, 0, SCSIPIRETRIES, 10000, NULL, flags));
|
1997-08-27 15:22:52 +04:00
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Do a scsipi operation asking a device what it is
|
|
|
|
* Use the scsipi_cmd routine in the switch table.
|
|
|
|
* XXX actually this is only used for scsi devices, because I have the feeling
|
|
|
|
* that some atapi CDROM may not implement it, althouh it marked as mandatory
|
|
|
|
* in the atapi specs.
|
|
|
|
*/
|
1997-10-01 05:18:38 +04:00
|
|
|
int
|
1997-08-27 15:22:52 +04:00
|
|
|
scsipi_inquire(sc_link, inqbuf, flags)
|
|
|
|
struct scsipi_link *sc_link;
|
|
|
|
struct scsipi_inquiry_data *inqbuf;
|
|
|
|
int flags;
|
|
|
|
{
|
|
|
|
struct scsipi_inquiry scsipi_cmd;
|
|
|
|
|
|
|
|
bzero(&scsipi_cmd, sizeof(scsipi_cmd));
|
|
|
|
scsipi_cmd.opcode = INQUIRY;
|
|
|
|
scsipi_cmd.length = sizeof(struct scsipi_inquiry_data);
|
|
|
|
|
1997-10-18 23:50:51 +04:00
|
|
|
return (scsipi_command(sc_link,
|
1997-10-01 05:18:38 +04:00
|
|
|
(struct scsipi_generic *) &scsipi_cmd, sizeof(scsipi_cmd),
|
|
|
|
(u_char *) inqbuf, sizeof(struct scsipi_inquiry_data),
|
2000-01-17 20:59:48 +03:00
|
|
|
SCSIPIRETRIES, 10000, NULL, XS_CTL_DATA_IN | flags));
|
1997-08-27 15:22:52 +04:00
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Prevent or allow the user to remove the media
|
|
|
|
*/
|
1997-10-01 05:18:38 +04:00
|
|
|
int
|
1997-08-27 15:22:52 +04:00
|
|
|
scsipi_prevent(sc_link, type, flags)
|
|
|
|
struct scsipi_link *sc_link;
|
|
|
|
int type, flags;
|
|
|
|
{
|
|
|
|
struct scsipi_prevent scsipi_cmd;
|
|
|
|
|
|
|
|
if (sc_link->quirks & ADEV_NODOORLOCK)
|
1997-10-01 05:18:38 +04:00
|
|
|
return (0);
|
1997-08-27 15:22:52 +04:00
|
|
|
|
|
|
|
bzero(&scsipi_cmd, sizeof(scsipi_cmd));
|
|
|
|
scsipi_cmd.opcode = PREVENT_ALLOW;
|
|
|
|
scsipi_cmd.how = type;
|
1997-10-18 23:50:51 +04:00
|
|
|
return (scsipi_command(sc_link,
|
1997-10-01 05:18:38 +04:00
|
|
|
(struct scsipi_generic *) &scsipi_cmd, sizeof(scsipi_cmd),
|
2000-01-17 20:59:48 +03:00
|
|
|
0, 0, SCSIPIRETRIES, 5000, NULL, flags));
|
1997-08-27 15:22:52 +04:00
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Get scsipi driver to send a "start up" command
|
|
|
|
*/
|
1997-10-01 05:18:38 +04:00
|
|
|
int
|
1997-08-27 15:22:52 +04:00
|
|
|
scsipi_start(sc_link, type, flags)
|
|
|
|
struct scsipi_link *sc_link;
|
|
|
|
int type, flags;
|
|
|
|
{
|
|
|
|
struct scsipi_start_stop scsipi_cmd;
|
|
|
|
|
1999-01-19 13:57:11 +03:00
|
|
|
if (sc_link->quirks & SDEV_NOSTARTUNIT)
|
|
|
|
return 0;
|
|
|
|
|
1997-08-27 15:22:52 +04:00
|
|
|
bzero(&scsipi_cmd, sizeof(scsipi_cmd));
|
|
|
|
scsipi_cmd.opcode = START_STOP;
|
|
|
|
scsipi_cmd.byte2 = 0x00;
|
|
|
|
scsipi_cmd.how = type;
|
1997-10-18 23:50:51 +04:00
|
|
|
return (scsipi_command(sc_link,
|
1997-10-01 05:18:38 +04:00
|
|
|
(struct scsipi_generic *) &scsipi_cmd, sizeof(scsipi_cmd),
|
2000-01-17 20:59:48 +03:00
|
|
|
0, 0, SCSIPIRETRIES, (type & SSS_START) ? 60000 : 10000,
|
|
|
|
NULL, flags));
|
1997-08-27 15:22:52 +04:00
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
1997-10-01 05:18:38 +04:00
|
|
|
* This routine is called by the scsipi interrupt when the transfer is
|
|
|
|
* complete.
|
1997-08-27 15:22:52 +04:00
|
|
|
*/
|
1997-10-01 05:18:38 +04:00
|
|
|
void
|
1997-08-27 15:22:52 +04:00
|
|
|
scsipi_done(xs)
|
|
|
|
struct scsipi_xfer *xs;
|
|
|
|
{
|
|
|
|
struct scsipi_link *sc_link = xs->sc_link;
|
|
|
|
struct buf *bp;
|
|
|
|
int error;
|
|
|
|
|
|
|
|
SC_DEBUG(sc_link, SDEV_DB2, ("scsipi_done\n"));
|
|
|
|
#ifdef SCSIDEBUG
|
|
|
|
if ((sc_link->flags & SDEV_DB1) != 0)
|
|
|
|
show_scsipi_cmd(xs);
|
|
|
|
#endif /* SCSIDEBUG */
|
|
|
|
|
|
|
|
/*
|
1997-10-01 05:18:38 +04:00
|
|
|
* If it's a user level request, bypass all usual completion
|
|
|
|
* processing, let the user work it out.. We take
|
|
|
|
* reponsibility for freeing the xs when the user returns.
|
|
|
|
* (and restarting the device's queue).
|
|
|
|
*/
|
1999-10-01 02:57:52 +04:00
|
|
|
if ((xs->xs_control & XS_CTL_USERCMD) != 0) {
|
1997-08-27 15:22:52 +04:00
|
|
|
SC_DEBUG(sc_link, SDEV_DB3, ("calling user done()\n"));
|
|
|
|
scsipi_user_done(xs); /* to take a copy of the sense etc. */
|
|
|
|
SC_DEBUG(sc_link, SDEV_DB3, ("returned from user done()\n "));
|
|
|
|
|
Fix a problem uncovered when we started to use the pool allocator to manage
scsipi_xfer structures.
When scsipi_execute_xs() calls the driver's scsi_cmd function, it assumes
that it can still dereference a pointer to the scsipi_xfer struct. Since
scsipi_done() has already been called, which in turn has called
scsipi_free_xs(), the struct has already been returned to the pool! In
other words, xs->flags has been compromised, but we are still testing it.
These changes resolve the problem by doing the following:
- In scsipi_execute_xs(), if the lower-level driver's scsi_cmd function
returns SUCCESSFULLY_QUEUED and SCSI_NOSLEEP is set in xs->flags, set a
new flag (SCSI_ASYNCREQ). This indicates that scsipi_done() should free
the scsipi_xfer struct.
If the lower-level driver's scsi_cmd function returns SUCCESSFULLY_QUEUED
but SCSI_NOSLEEP is not set, we wait (via tsleep()) for the request to
complete, then fall through to the COMPLETE case.
If the lower-level driver's scsi_cmd function returns COMPLETE, we now
simply return any actual errors, or 0 if none occurred. (Previously,
we may have returned EJUSTRETURN, of which the sole effect was to
avoid freeing the scsipi_xfer struct in our caller. No code seems
to depend on this behavior, however.)
- In scsipi_done(), only free the scsipi_xfer struct for async requests.
The contents of the struct will otherwise remain valid until the
function that initiated the transfer frees it.
With this change, responsibility for freeing the struct now lies in two
places, depending on the type of the request:
- For synchronous requests, the routine calling scsipi_execute_xs()
must clean up.
- For asynchronous requests, scsipi_done() cleans up (as it always has).
[Note: this change also corrects a problem with sddump(): scsipi_done()
was attempting to return a static scsipi_xfer struct to the pool! Since
dumps are performed synchronously, we now handle this correctly.]
This solution was provided by Jason Thorpe, after I got him to look at
some related (but insufficient) attempts of my own.
1998-09-14 09:49:20 +04:00
|
|
|
/*
|
|
|
|
* If this was an asynchronous operation (i.e. adapter
|
|
|
|
* returned SUCCESSFULLY_QUEUED when the command was
|
|
|
|
* submitted), we need to free the scsipi_xfer here.
|
|
|
|
*/
|
1999-10-01 02:57:52 +04:00
|
|
|
if (xs->xs_control & XS_CTL_ASYNC)
|
|
|
|
scsipi_free_xs(xs, XS_CTL_NOSLEEP);
|
1997-08-27 15:22:52 +04:00
|
|
|
SC_DEBUG(sc_link, SDEV_DB3, ("returning to adapter\n"));
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
1999-10-01 02:57:52 +04:00
|
|
|
if ((xs->xs_control & XS_CTL_ASYNC) == 0) {
|
1997-08-27 15:22:52 +04:00
|
|
|
/*
|
|
|
|
* if it's a normal upper level request, then ask
|
|
|
|
* the upper level code to handle error checking
|
|
|
|
* rather than doing it here at interrupt time
|
|
|
|
*/
|
|
|
|
wakeup(xs);
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Go and handle errors now.
|
|
|
|
* If it returns ERESTART then we should RETRY
|
|
|
|
*/
|
|
|
|
retry:
|
|
|
|
error = sc_err1(xs, 1);
|
1999-02-02 16:01:36 +03:00
|
|
|
if (error == ERESTART) {
|
1997-10-18 23:50:51 +04:00
|
|
|
switch (scsipi_command_direct(xs)) {
|
1997-08-27 15:22:52 +04:00
|
|
|
case SUCCESSFULLY_QUEUED:
|
|
|
|
return;
|
|
|
|
|
|
|
|
case TRY_AGAIN_LATER:
|
|
|
|
xs->error = XS_BUSY;
|
|
|
|
case COMPLETE:
|
|
|
|
goto retry;
|
|
|
|
}
|
1999-02-02 16:01:36 +03:00
|
|
|
}
|
1997-08-27 15:22:52 +04:00
|
|
|
|
|
|
|
bp = xs->bp;
|
|
|
|
if (bp) {
|
|
|
|
if (error) {
|
|
|
|
bp->b_error = error;
|
|
|
|
bp->b_flags |= B_ERROR;
|
|
|
|
bp->b_resid = bp->b_bcount;
|
|
|
|
} else {
|
|
|
|
bp->b_error = 0;
|
|
|
|
bp->b_resid = xs->resid;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
if (sc_link->device->done) {
|
|
|
|
/*
|
|
|
|
* Tell the device the operation is actually complete.
|
|
|
|
* No more will happen with this xfer. This for
|
|
|
|
* notification of the upper-level driver only; they
|
|
|
|
* won't be returning any meaningful information to us.
|
|
|
|
*/
|
|
|
|
(*sc_link->device->done)(xs);
|
|
|
|
}
|
Fix a problem uncovered when we started to use the pool allocator to manage
scsipi_xfer structures.
When scsipi_execute_xs() calls the driver's scsi_cmd function, it assumes
that it can still dereference a pointer to the scsipi_xfer struct. Since
scsipi_done() has already been called, which in turn has called
scsipi_free_xs(), the struct has already been returned to the pool! In
other words, xs->flags has been compromised, but we are still testing it.
These changes resolve the problem by doing the following:
- In scsipi_execute_xs(), if the lower-level driver's scsi_cmd function
returns SUCCESSFULLY_QUEUED and SCSI_NOSLEEP is set in xs->flags, set a
new flag (SCSI_ASYNCREQ). This indicates that scsipi_done() should free
the scsipi_xfer struct.
If the lower-level driver's scsi_cmd function returns SUCCESSFULLY_QUEUED
but SCSI_NOSLEEP is not set, we wait (via tsleep()) for the request to
complete, then fall through to the COMPLETE case.
If the lower-level driver's scsi_cmd function returns COMPLETE, we now
simply return any actual errors, or 0 if none occurred. (Previously,
we may have returned EJUSTRETURN, of which the sole effect was to
avoid freeing the scsipi_xfer struct in our caller. No code seems
to depend on this behavior, however.)
- In scsipi_done(), only free the scsipi_xfer struct for async requests.
The contents of the struct will otherwise remain valid until the
function that initiated the transfer frees it.
With this change, responsibility for freeing the struct now lies in two
places, depending on the type of the request:
- For synchronous requests, the routine calling scsipi_execute_xs()
must clean up.
- For asynchronous requests, scsipi_done() cleans up (as it always has).
[Note: this change also corrects a problem with sddump(): scsipi_done()
was attempting to return a static scsipi_xfer struct to the pool! Since
dumps are performed synchronously, we now handle this correctly.]
This solution was provided by Jason Thorpe, after I got him to look at
some related (but insufficient) attempts of my own.
1998-09-14 09:49:20 +04:00
|
|
|
/*
|
|
|
|
* If this was an asynchronous operation (i.e. adapter
|
|
|
|
* returned SUCCESSFULLY_QUEUED when the command was
|
|
|
|
* submitted), we need to free the scsipi_xfer here.
|
|
|
|
*/
|
1999-10-01 02:57:52 +04:00
|
|
|
if (xs->xs_control & XS_CTL_ASYNC) {
|
1999-06-16 11:35:31 +04:00
|
|
|
int s = splbio();
|
1999-10-01 02:57:52 +04:00
|
|
|
scsipi_free_xs(xs, XS_CTL_NOSLEEP);
|
1999-06-16 11:35:31 +04:00
|
|
|
splx(s);
|
|
|
|
}
|
1997-08-27 15:22:52 +04:00
|
|
|
if (bp)
|
|
|
|
biodone(bp);
|
|
|
|
}
|
|
|
|
|
|
|
|
int
|
|
|
|
scsipi_execute_xs(xs)
|
|
|
|
struct scsipi_xfer *xs;
|
|
|
|
{
|
1998-09-18 09:53:07 +04:00
|
|
|
int async;
|
1997-08-27 15:22:52 +04:00
|
|
|
int error;
|
|
|
|
int s;
|
|
|
|
|
1999-10-01 02:57:52 +04:00
|
|
|
xs->xs_status &= ~XS_STS_DONE;
|
1997-08-27 15:22:52 +04:00
|
|
|
xs->error = XS_NOERROR;
|
|
|
|
xs->resid = xs->datalen;
|
1998-02-10 22:48:51 +03:00
|
|
|
xs->status = 0;
|
1997-08-27 15:22:52 +04:00
|
|
|
|
|
|
|
retry:
|
|
|
|
/*
|
|
|
|
* Do the transfer. If we are polling we will return:
|
|
|
|
* COMPLETE, Was poll, and scsipi_done has been called
|
|
|
|
* TRY_AGAIN_LATER, Adapter short resources, try again
|
1997-10-01 05:18:38 +04:00
|
|
|
*
|
1997-08-27 15:22:52 +04:00
|
|
|
* if under full steam (interrupts) it will return:
|
|
|
|
* SUCCESSFULLY_QUEUED, will do a wakeup when complete
|
|
|
|
* TRY_AGAIN_LATER, (as for polling)
|
|
|
|
* After the wakeup, we must still check if it succeeded
|
1997-10-01 05:18:38 +04:00
|
|
|
*
|
1999-10-01 02:57:52 +04:00
|
|
|
* If we have a XS_CTL_ASYNC (typically because we have a buf)
|
1997-08-27 15:22:52 +04:00
|
|
|
* we just return. All the error proccessing and the buffer
|
|
|
|
* code both expect us to return straight to them, so as soon
|
|
|
|
* as the command is queued, return.
|
|
|
|
*/
|
|
|
|
#ifdef SCSIDEBUG
|
|
|
|
if (xs->sc_link->flags & SDEV_DB3) {
|
|
|
|
printf("scsipi_exec_cmd: ");
|
|
|
|
show_scsipi_xs(xs);
|
|
|
|
printf("\n");
|
|
|
|
}
|
|
|
|
#endif
|
1999-10-01 02:57:52 +04:00
|
|
|
async = (xs->xs_control & XS_CTL_ASYNC);
|
1997-10-18 23:50:51 +04:00
|
|
|
switch (scsipi_command_direct(xs)) {
|
1997-08-27 15:22:52 +04:00
|
|
|
case SUCCESSFULLY_QUEUED:
|
1998-09-18 09:53:07 +04:00
|
|
|
if (async) {
|
|
|
|
/* scsipi_done() will free the scsipi_xfer. */
|
1997-10-01 05:18:38 +04:00
|
|
|
return (EJUSTRETURN);
|
Fix a problem uncovered when we started to use the pool allocator to manage
scsipi_xfer structures.
When scsipi_execute_xs() calls the driver's scsi_cmd function, it assumes
that it can still dereference a pointer to the scsipi_xfer struct. Since
scsipi_done() has already been called, which in turn has called
scsipi_free_xs(), the struct has already been returned to the pool! In
other words, xs->flags has been compromised, but we are still testing it.
These changes resolve the problem by doing the following:
- In scsipi_execute_xs(), if the lower-level driver's scsi_cmd function
returns SUCCESSFULLY_QUEUED and SCSI_NOSLEEP is set in xs->flags, set a
new flag (SCSI_ASYNCREQ). This indicates that scsipi_done() should free
the scsipi_xfer struct.
If the lower-level driver's scsi_cmd function returns SUCCESSFULLY_QUEUED
but SCSI_NOSLEEP is not set, we wait (via tsleep()) for the request to
complete, then fall through to the COMPLETE case.
If the lower-level driver's scsi_cmd function returns COMPLETE, we now
simply return any actual errors, or 0 if none occurred. (Previously,
we may have returned EJUSTRETURN, of which the sole effect was to
avoid freeing the scsipi_xfer struct in our caller. No code seems
to depend on this behavior, however.)
- In scsipi_done(), only free the scsipi_xfer struct for async requests.
The contents of the struct will otherwise remain valid until the
function that initiated the transfer frees it.
With this change, responsibility for freeing the struct now lies in two
places, depending on the type of the request:
- For synchronous requests, the routine calling scsipi_execute_xs()
must clean up.
- For asynchronous requests, scsipi_done() cleans up (as it always has).
[Note: this change also corrects a problem with sddump(): scsipi_done()
was attempting to return a static scsipi_xfer struct to the pool! Since
dumps are performed synchronously, we now handle this correctly.]
This solution was provided by Jason Thorpe, after I got him to look at
some related (but insufficient) attempts of my own.
1998-09-14 09:49:20 +04:00
|
|
|
}
|
1997-08-27 15:22:52 +04:00
|
|
|
#ifdef DIAGNOSTIC
|
1999-10-01 02:57:52 +04:00
|
|
|
if (xs->xs_control & XS_CTL_ASYNC)
|
|
|
|
panic("scsipi_execute_xs: ASYNC and POLL");
|
1997-08-27 15:22:52 +04:00
|
|
|
#endif
|
1998-09-18 09:53:07 +04:00
|
|
|
s = splbio();
|
1999-10-01 02:57:52 +04:00
|
|
|
while ((xs->xs_status & XS_STS_DONE) == 0)
|
1997-08-27 15:22:52 +04:00
|
|
|
tsleep(xs, PRIBIO + 1, "scsipi_cmd", 0);
|
|
|
|
splx(s);
|
|
|
|
case COMPLETE: /* Polling command completed ok */
|
|
|
|
if (xs->bp)
|
Fix a problem uncovered when we started to use the pool allocator to manage
scsipi_xfer structures.
When scsipi_execute_xs() calls the driver's scsi_cmd function, it assumes
that it can still dereference a pointer to the scsipi_xfer struct. Since
scsipi_done() has already been called, which in turn has called
scsipi_free_xs(), the struct has already been returned to the pool! In
other words, xs->flags has been compromised, but we are still testing it.
These changes resolve the problem by doing the following:
- In scsipi_execute_xs(), if the lower-level driver's scsi_cmd function
returns SUCCESSFULLY_QUEUED and SCSI_NOSLEEP is set in xs->flags, set a
new flag (SCSI_ASYNCREQ). This indicates that scsipi_done() should free
the scsipi_xfer struct.
If the lower-level driver's scsi_cmd function returns SUCCESSFULLY_QUEUED
but SCSI_NOSLEEP is not set, we wait (via tsleep()) for the request to
complete, then fall through to the COMPLETE case.
If the lower-level driver's scsi_cmd function returns COMPLETE, we now
simply return any actual errors, or 0 if none occurred. (Previously,
we may have returned EJUSTRETURN, of which the sole effect was to
avoid freeing the scsipi_xfer struct in our caller. No code seems
to depend on this behavior, however.)
- In scsipi_done(), only free the scsipi_xfer struct for async requests.
The contents of the struct will otherwise remain valid until the
function that initiated the transfer frees it.
With this change, responsibility for freeing the struct now lies in two
places, depending on the type of the request:
- For synchronous requests, the routine calling scsipi_execute_xs()
must clean up.
- For asynchronous requests, scsipi_done() cleans up (as it always has).
[Note: this change also corrects a problem with sddump(): scsipi_done()
was attempting to return a static scsipi_xfer struct to the pool! Since
dumps are performed synchronously, we now handle this correctly.]
This solution was provided by Jason Thorpe, after I got him to look at
some related (but insufficient) attempts of my own.
1998-09-14 09:49:20 +04:00
|
|
|
return (0);
|
1997-08-27 15:22:52 +04:00
|
|
|
doit:
|
|
|
|
SC_DEBUG(xs->sc_link, SDEV_DB3, ("back in cmd()\n"));
|
|
|
|
if ((error = sc_err1(xs, 0)) != ERESTART)
|
1997-10-01 05:18:38 +04:00
|
|
|
return (error);
|
1997-08-27 15:22:52 +04:00
|
|
|
goto retry;
|
|
|
|
|
|
|
|
case TRY_AGAIN_LATER: /* adapter resource shortage */
|
|
|
|
xs->error = XS_BUSY;
|
|
|
|
goto doit;
|
|
|
|
|
|
|
|
default:
|
|
|
|
panic("scsipi_execute_xs: invalid return code");
|
|
|
|
}
|
|
|
|
|
|
|
|
#ifdef DIAGNOSTIC
|
|
|
|
panic("scsipi_execute_xs: impossible");
|
|
|
|
#endif
|
1997-10-01 05:18:38 +04:00
|
|
|
return (EINVAL);
|
1997-08-27 15:22:52 +04:00
|
|
|
}
|
|
|
|
|
1997-10-01 05:18:38 +04:00
|
|
|
int
|
1997-08-27 15:22:52 +04:00
|
|
|
sc_err1(xs, async)
|
|
|
|
struct scsipi_xfer *xs;
|
|
|
|
int async;
|
|
|
|
{
|
|
|
|
int error;
|
|
|
|
|
|
|
|
SC_DEBUG(xs->sc_link, SDEV_DB3, ("sc_err1,err = 0x%x \n", xs->error));
|
|
|
|
|
|
|
|
/*
|
|
|
|
* If it has a buf, we might be working with
|
|
|
|
* a request from the buffer cache or some other
|
|
|
|
* piece of code that requires us to process
|
|
|
|
* errors at inetrrupt time. We have probably
|
|
|
|
* been called by scsipi_done()
|
|
|
|
*/
|
|
|
|
switch (xs->error) {
|
|
|
|
case XS_NOERROR: /* nearly always hit this one */
|
|
|
|
error = 0;
|
|
|
|
break;
|
|
|
|
|
|
|
|
case XS_SENSE:
|
1998-11-17 17:38:42 +03:00
|
|
|
case XS_SHORTSENSE:
|
1997-10-01 05:18:38 +04:00
|
|
|
if ((error = (*xs->sc_link->scsipi_interpret_sense)(xs)) ==
|
|
|
|
ERESTART)
|
1997-08-27 15:22:52 +04:00
|
|
|
goto retry;
|
|
|
|
SC_DEBUG(xs->sc_link, SDEV_DB3,
|
|
|
|
("scsipi_interpret_sense returned %d\n", error));
|
|
|
|
break;
|
|
|
|
|
|
|
|
case XS_BUSY:
|
|
|
|
if (xs->retries) {
|
1999-10-01 02:57:52 +04:00
|
|
|
if ((xs->xs_control & XS_CTL_POLL) != 0)
|
1997-08-27 15:22:52 +04:00
|
|
|
delay(1000000);
|
1999-10-06 09:01:05 +04:00
|
|
|
else if ((xs->xs_control & (XS_CTL_NOSLEEP|XS_CTL_DISCOVERY)) == 0)
|
1997-08-27 15:22:52 +04:00
|
|
|
tsleep(&lbolt, PRIBIO, "scbusy", 0);
|
|
|
|
else
|
|
|
|
#if 0
|
|
|
|
timeout(scsipi_requeue, xs, hz);
|
|
|
|
#else
|
|
|
|
goto lose;
|
|
|
|
#endif
|
|
|
|
}
|
|
|
|
case XS_TIMEOUT:
|
|
|
|
retry:
|
1998-10-10 07:42:53 +04:00
|
|
|
if (xs->retries) {
|
|
|
|
xs->retries--;
|
1997-08-27 15:22:52 +04:00
|
|
|
xs->error = XS_NOERROR;
|
1999-10-01 02:57:52 +04:00
|
|
|
xs->xs_status &= ~XS_STS_DONE;
|
1997-10-01 05:18:38 +04:00
|
|
|
return (ERESTART);
|
1997-08-27 15:22:52 +04:00
|
|
|
}
|
|
|
|
case XS_DRIVER_STUFFUP:
|
|
|
|
lose:
|
|
|
|
error = EIO;
|
|
|
|
break;
|
|
|
|
|
|
|
|
case XS_SELTIMEOUT:
|
|
|
|
/* XXX Disable device? */
|
|
|
|
error = EIO;
|
|
|
|
break;
|
|
|
|
|
1998-10-10 07:42:53 +04:00
|
|
|
case XS_RESET:
|
|
|
|
if (xs->retries) {
|
|
|
|
SC_DEBUG(xs->sc_link, SDEV_DB3,
|
|
|
|
("restarting command destroyed by reset\n"));
|
|
|
|
goto retry;
|
|
|
|
}
|
|
|
|
error = EIO;
|
|
|
|
break;
|
|
|
|
|
1997-08-27 15:22:52 +04:00
|
|
|
default:
|
1997-10-01 05:18:38 +04:00
|
|
|
(*xs->sc_link->sc_print_addr)(xs->sc_link);
|
1997-08-27 15:22:52 +04:00
|
|
|
printf("unknown error category from scsipi driver\n");
|
|
|
|
error = EIO;
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
|
1997-10-01 05:18:38 +04:00
|
|
|
return (error);
|
1997-08-27 15:22:52 +04:00
|
|
|
}
|
|
|
|
|
1998-11-19 23:08:52 +03:00
|
|
|
/*
|
|
|
|
* Add a reference to the adapter pointed to by the provided
|
|
|
|
* link, enabling the adapter if necessary.
|
|
|
|
*/
|
|
|
|
int
|
|
|
|
scsipi_adapter_addref(link)
|
|
|
|
struct scsipi_link *link;
|
|
|
|
{
|
|
|
|
struct scsipi_adapter *adapter = link->adapter;
|
|
|
|
int s, error = 0;
|
|
|
|
|
|
|
|
s = splbio();
|
|
|
|
if (adapter->scsipi_refcnt++ == 0 &&
|
|
|
|
adapter->scsipi_enable != NULL) {
|
|
|
|
error = (*adapter->scsipi_enable)(link->adapter_softc, 1);
|
|
|
|
if (error)
|
|
|
|
adapter->scsipi_refcnt--;
|
|
|
|
}
|
|
|
|
splx(s);
|
|
|
|
return (error);
|
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Delete a reference to the adapter pointed to by the provided
|
|
|
|
* link, disabling the adapter if possible.
|
|
|
|
*/
|
|
|
|
void
|
|
|
|
scsipi_adapter_delref(link)
|
|
|
|
struct scsipi_link *link;
|
|
|
|
{
|
|
|
|
struct scsipi_adapter *adapter = link->adapter;
|
|
|
|
int s;
|
|
|
|
|
|
|
|
s = splbio();
|
|
|
|
if (adapter->scsipi_refcnt-- == 1 &&
|
|
|
|
adapter->scsipi_enable != NULL)
|
|
|
|
(void) (*adapter->scsipi_enable)(link->adapter_softc, 0);
|
|
|
|
splx(s);
|
|
|
|
}
|
|
|
|
|
1997-08-27 15:22:52 +04:00
|
|
|
#ifdef SCSIDEBUG
|
|
|
|
/*
|
|
|
|
* Given a scsipi_xfer, dump the request, in all it's glory
|
|
|
|
*/
|
|
|
|
void
|
|
|
|
show_scsipi_xs(xs)
|
|
|
|
struct scsipi_xfer *xs;
|
|
|
|
{
|
1997-10-01 05:18:38 +04:00
|
|
|
|
1997-08-27 15:22:52 +04:00
|
|
|
printf("xs(%p): ", xs);
|
1999-10-01 02:57:52 +04:00
|
|
|
printf("xs_control(0x%08x)", xs->xs_control);
|
|
|
|
printf("xs_status(0x%08x)", xs->xs_status);
|
1997-08-27 15:22:52 +04:00
|
|
|
printf("sc_link(%p)", xs->sc_link);
|
|
|
|
printf("retr(0x%x)", xs->retries);
|
|
|
|
printf("timo(0x%x)", xs->timeout);
|
|
|
|
printf("cmd(%p)", xs->cmd);
|
|
|
|
printf("len(0x%x)", xs->cmdlen);
|
|
|
|
printf("data(%p)", xs->data);
|
|
|
|
printf("len(0x%x)", xs->datalen);
|
|
|
|
printf("res(0x%x)", xs->resid);
|
|
|
|
printf("err(0x%x)", xs->error);
|
|
|
|
printf("bp(%p)", xs->bp);
|
|
|
|
show_scsipi_cmd(xs);
|
|
|
|
}
|
|
|
|
|
|
|
|
void
|
|
|
|
show_scsipi_cmd(xs)
|
|
|
|
struct scsipi_xfer *xs;
|
|
|
|
{
|
|
|
|
u_char *b = (u_char *) xs->cmd;
|
1997-10-01 05:18:38 +04:00
|
|
|
int i = 0;
|
1997-08-27 15:22:52 +04:00
|
|
|
|
1997-10-01 05:18:38 +04:00
|
|
|
(*xs->sc_link->sc_print_addr)(xs->sc_link);
|
1997-08-27 15:22:52 +04:00
|
|
|
printf("command: ");
|
|
|
|
|
1999-10-01 02:57:52 +04:00
|
|
|
if ((xs->xs_control & XS_CTL_RESET) == 0) {
|
1997-08-27 15:22:52 +04:00
|
|
|
while (i < xs->cmdlen) {
|
|
|
|
if (i)
|
|
|
|
printf(",");
|
|
|
|
printf("0x%x", b[i++]);
|
|
|
|
}
|
|
|
|
printf("-[%d bytes]\n", xs->datalen);
|
|
|
|
if (xs->datalen)
|
|
|
|
show_mem(xs->data, min(64, xs->datalen));
|
|
|
|
} else
|
|
|
|
printf("-RESET-\n");
|
|
|
|
}
|
|
|
|
|
|
|
|
void
|
|
|
|
show_mem(address, num)
|
|
|
|
u_char *address;
|
|
|
|
int num;
|
|
|
|
{
|
|
|
|
int x;
|
|
|
|
|
|
|
|
printf("------------------------------");
|
|
|
|
for (x = 0; x < num; x++) {
|
|
|
|
if ((x % 16) == 0)
|
|
|
|
printf("\n%03d: ", x);
|
|
|
|
printf("%02x ", *address++);
|
|
|
|
}
|
|
|
|
printf("\n------------------------------\n");
|
|
|
|
}
|
|
|
|
#endif /*SCSIDEBUG */
|