summaryrefslogtreecommitdiff
path: root/sys
diff options
context:
space:
mode:
Diffstat (limited to 'sys')
-rw-r--r--sys/dev/ic/mpi.c4
-rw-r--r--sys/scsi/mpath.c97
-rw-r--r--sys/scsi/scsi_base.c461
-rw-r--r--sys/scsi/scsi_ioctl.c301
-rw-r--r--sys/scsi/scsiconf.c229
-rw-r--r--sys/scsi/scsiconf.h50
-rw-r--r--sys/scsi/sd.c459
-rw-r--r--sys/scsi/sdvar.h15
8 files changed, 721 insertions, 895 deletions
diff --git a/sys/dev/ic/mpi.c b/sys/dev/ic/mpi.c
index 0137a8dee4b..a08fddef6cd 100644
--- a/sys/dev/ic/mpi.c
+++ b/sys/dev/ic/mpi.c
@@ -1,4 +1,4 @@
-/* $OpenBSD: mpi.c,v 1.121 2009/11/12 06:20:27 dlg Exp $ */
+/* $OpenBSD: mpi.c,v 1.122 2009/12/01 01:40:02 dlg Exp $ */
/*
* Copyright (c) 2005, 2006, 2009 David Gwynne <dlg@openbsd.org>
@@ -2176,7 +2176,6 @@ mpi_eventnotify_done(struct mpi_ccb *ccb)
void
mpi_evt_sas(struct mpi_softc *sc, struct mpi_rcb *rcb)
{
-#if 0
struct mpi_evt_sas_change *ch;
u_int8_t *data;
@@ -2214,7 +2213,6 @@ mpi_evt_sas(struct mpi_softc *sc, struct mpi_rcb *rcb)
"0x%02x\n", DEVNAME(sc), ch->reason);
break;
}
-#endif
}
void
diff --git a/sys/scsi/mpath.c b/sys/scsi/mpath.c
index 4bccf1c9d43..342763ed552 100644
--- a/sys/scsi/mpath.c
+++ b/sys/scsi/mpath.c
@@ -1,4 +1,4 @@
-/* $OpenBSD: mpath.c,v 1.10 2009/11/12 06:20:27 dlg Exp $ */
+/* $OpenBSD: mpath.c,v 1.11 2009/12/01 01:40:02 dlg Exp $ */
/*
* Copyright (c) 2009 David Gwynne <dlg@openbsd.org>
@@ -47,7 +47,7 @@ struct mpath_path {
TAILQ_HEAD(mpath_paths, mpath_path);
struct mpath_node {
- struct devid node_id;
+ struct devid *node_id;
struct mpath_paths node_paths;
};
@@ -78,6 +78,8 @@ int mpath_cmd(struct scsi_xfer *);
void mpath_minphys(struct buf *, struct scsi_link *);
int mpath_probe(struct scsi_link *);
+void mpath_done(struct scsi_xfer *);
+
struct scsi_adapter mpath_switch = {
mpath_cmd,
scsi_minphys,
@@ -136,9 +138,13 @@ mpath_xs_stuffup(struct scsi_xfer *xs)
int
mpath_probe(struct scsi_link *link)
{
- if (link->lun != 0 || mpath_nodes[link->target] == NULL)
+ struct mpath_node *n = mpath_nodes[link->target];
+
+ if (link->lun != 0 || n == NULL)
return (ENXIO);
+ link->id = devid_copy(n->node_id);
+
return (0);
}
@@ -148,36 +154,53 @@ mpath_cmd(struct scsi_xfer *xs)
struct scsi_link *link = xs->sc_link;
struct mpath_node *n = mpath_nodes[link->target];
struct mpath_path *p = TAILQ_FIRST(&n->node_paths);
- int rv;
- int s;
+ struct scsi_xfer *mxs;
if (n == NULL || p == NULL) {
mpath_xs_stuffup(xs);
return (COMPLETE);
}
- rv = scsi_scsi_cmd(p->path_link, xs->cmd, xs->cmdlen,
- xs->data, xs->datalen,
- 2, xs->timeout, NULL, SCSI_POLL |
- (xs->flags & (SCSI_DATA_IN|SCSI_DATA_OUT)));
+ mxs = scsi_xs_get(p->path_link, xs->flags);
+ if (mxs == NULL) {
+ mpath_xs_stuffup(xs);
+ return (COMPLETE);
+ }
+ memcpy(mxs->cmd, xs->cmd, xs->cmdlen);
+ mxs->cmdlen = xs->cmdlen;
+ mxs->data = xs->data;
+ mxs->datalen = xs->datalen;
+ mxs->retries = xs->retries;
+ mxs->timeout = xs->timeout;
+ mxs->req_sense_length = xs->req_sense_length;
- xs->flags |= ITSDONE;
- if (rv == 0) {
- xs->error = XS_NOERROR;
- xs->status = SCSI_OK;
- xs->resid = 0;
- } else {
- printf("%s: t%dl%d rv %d cmd %x\n", DEVNAME(mpath),
- link->target, link->lun, rv, xs->cmd->opcode);
- xs->error = XS_DRIVER_STUFFUP;
- }
+ mxs->cookie = xs;
+ mxs->done = mpath_done;
+
+ scsi_xs_exec(mxs);
+
+ return (COMPLETE); /* doesnt matter anymore */
+}
+
+void
+mpath_done(struct scsi_xfer *mxs)
+{
+ struct scsi_xfer *xs = mxs->cookie;
+ int s;
+
+ xs->error = mxs->error;
+ xs->status = mxs->status;
+ xs->flags = mxs->flags;
+ xs->resid = mxs->resid;
+
+ memcpy(&xs->sense, &mxs->sense, sizeof(xs->sense));
+
+ scsi_xs_put(mxs);
s = splbio();
scsi_done(xs);
splx(s);
-
- return (COMPLETE);
}
void
@@ -205,14 +228,14 @@ mpath_path_attach(struct scsi_link *link)
return (ENODEV);
/* XXX this is dumb. should check inq shizz */
- if (link->id.d_type == DEVID_NONE)
+ if (ISSET(link->flags, SDEV_VIRTUAL) || link->id == NULL)
return (ENXIO);
for (target = 0; target < MPATH_BUSWIDTH; target++) {
if ((n = mpath_nodes[target]) == NULL)
continue;
- if (DEVID_CMP(&n->node_id, &link->id))
+ if (DEVID_CMP(n->node_id, link->id))
break;
n = NULL;
@@ -229,13 +252,18 @@ mpath_path_attach(struct scsi_link *link)
n = malloc(sizeof(*n), M_DEVBUF, M_WAITOK | M_ZERO);
TAILQ_INIT(&n->node_paths);
- n->node_id.d_type = link->id.d_type;
- n->node_id.d_len = link->id.d_len;
- n->node_id.d_id = malloc(n->node_id.d_len, M_DEVBUF, M_DEVBUF);
- memcpy(n->node_id.d_id, link->id.d_id, n->node_id.d_len);
+ n->node_id = devid_copy(link->id);
mpath_nodes[target] = n;
probe = 1;
+ } else {
+ /*
+ * instead of carrying identical values in different devid
+ * instances, delete the new one and reference the old one in
+ * the new scsi_link.
+ */
+ devid_free(link->id);
+ link->id = devid_copy(n->node_id);
}
p = malloc(sizeof(*p), M_DEVBUF, M_WAITOK);
@@ -260,7 +288,7 @@ mpath_path_detach(struct scsi_link *link, int flags)
if ((n = mpath_nodes[target]) == NULL)
continue;
- if (DEVID_CMP(&n->node_id, &link->id))
+ if (DEVID_CMP(n->node_id, link->id))
break;
n = NULL;
@@ -279,3 +307,16 @@ mpath_path_detach(struct scsi_link *link, int flags)
panic("mpath: unable to locate path for detach");
}
+
+void
+mpath_path_activate(struct scsi_link *link)
+{
+
+}
+
+void
+mpath_path_deactivate(struct scsi_link *link)
+{
+
+}
+
diff --git a/sys/scsi/scsi_base.c b/sys/scsi/scsi_base.c
index 55d695f6c4e..f844d955881 100644
--- a/sys/scsi/scsi_base.c
+++ b/sys/scsi/scsi_base.c
@@ -1,4 +1,4 @@
-/* $OpenBSD: scsi_base.c,v 1.143 2009/11/22 20:09:53 krw Exp $ */
+/* $OpenBSD: scsi_base.c,v 1.144 2009/12/01 01:40:02 dlg Exp $ */
/* $NetBSD: scsi_base.c,v 1.43 1997/04/02 02:29:36 mycroft Exp $ */
/*
@@ -50,15 +50,14 @@
#include <scsi/scsi_disk.h>
#include <scsi/scsiconf.h>
-static __inline struct scsi_xfer *scsi_make_xs(struct scsi_link *,
- struct scsi_generic *, int cmdlen, u_char *data_addr,
- int datalen, int retries, int timeout, struct buf *, int flags);
static __inline void asc2ascii(u_int8_t, u_int8_t ascq, char *result,
size_t len);
int sc_err1(struct scsi_xfer *);
int scsi_interpret_sense(struct scsi_xfer *);
char *scsi_decode_sense(struct scsi_sense_data *, int);
+void scsi_xs_done(struct scsi_xfer *);
+
/* Values for flag parameter to scsi_decode_sense. */
#define DECODE_SENSE_KEY 1
#define DECODE_ASC_ASCQ 2
@@ -95,6 +94,7 @@ scsi_init()
/* Initialize the scsi_xfer pool. */
pool_init(&scsi_xfer_pool, sizeof(struct scsi_xfer), 0,
0, 0, "scxspl", NULL);
+ pool_setipl(&scsi_xfer_pool, IPL_BIO);
/* Initialize the scsi_plug pool */
pool_init(&scsi_plug_pool, sizeof(struct scsi_plug), 0,
0, 0, "scsiplug", NULL);
@@ -183,42 +183,43 @@ scsi_deinit()
*/
struct scsi_xfer *
-scsi_get_xs(struct scsi_link *sc_link, int flags)
+scsi_xs_get(struct scsi_link *link, int flags)
{
- struct scsi_xfer *xs;
- int s;
-
- SC_DEBUG(sc_link, SDEV_DB3, ("scsi_get_xs\n"));
+ struct scsi_xfer *xs;
- s = splbio();
- while (sc_link->openings == 0) {
- SC_DEBUG(sc_link, SDEV_DB3, ("sleeping\n"));
- if ((flags & SCSI_NOSLEEP) != 0) {
- splx(s);
- return (NULL);
- }
- sc_link->flags |= SDEV_WAITING;
- if (tsleep(sc_link, PRIBIO|PCATCH, "getxs", 0)) {
- /* Bail out on getting a signal. */
- sc_link->flags &= ~SDEV_WAITING;
- splx(s);
+ mtx_enter(&link->mtx);
+ while (link->openings == 0) {
+ if (ISSET(flags, SCSI_NOSLEEP)) {
+ mtx_leave(&link->mtx);
return (NULL);
}
+
+ SET(link->flags, SDEV_WAITING);
+ msleep(link, &link->mtx, PRIBIO, "getxs", 0);
}
- SC_DEBUG(sc_link, SDEV_DB3, ("calling pool_get\n"));
+ link->openings--;
+ mtx_leave(&link->mtx);
+
+ /* pool is shared, link mtx is not */
xs = pool_get(&scsi_xfer_pool,
- ((flags & SCSI_NOSLEEP) != 0 ? PR_NOWAIT : PR_WAITOK));
- if (xs != NULL) {
- bzero(xs, sizeof(*xs));
- sc_link->openings--;
- xs->flags = flags;
+ ISSET(flags, SCSI_NOSLEEP) ? PR_NOWAIT : PR_WAITOK);
+ if (xs == NULL) {
+ mtx_enter(&link->mtx);
+ link->openings++;
+ mtx_leave(&link->mtx);
} else {
- sc_print_addr(sc_link);
- printf("cannot allocate scsi xs\n");
+ xs->flags = flags;
+ xs->sc_link = link;
+ xs->retries = SCSI_RETRIES;
+ xs->timeout = 0;
+ bzero(&xs->cmdstore, sizeof(xs->cmdstore));
+ xs->cmd = &xs->cmdstore;
+ xs->cmdlen = 0;
+ xs->data = NULL;
+ xs->datalen = 0;
+ xs->resid = 0;
+ xs->bp = NULL;
}
- splx(s);
-
- SC_DEBUG(sc_link, SDEV_DB3, ("returning\n"));
return (xs);
}
@@ -229,75 +230,21 @@ scsi_get_xs(struct scsi_link *sc_link, int flags)
* If another process is waiting for an xs, do a wakeup, let it proceed
*/
void
-scsi_free_xs(struct scsi_xfer *xs, int start)
+scsi_xs_put(struct scsi_xfer *xs)
{
- struct scsi_link *sc_link = xs->sc_link;
-
- splassert(IPL_BIO);
-
- SC_DEBUG(sc_link, SDEV_DB3, ("scsi_free_xs\n"));
+ struct scsi_link *link = xs->sc_link;
pool_put(&scsi_xfer_pool, xs);
- sc_link->openings++;
+
+ mtx_enter(&link->mtx);
+ link->openings++;
/* If someone is waiting for scsi_xfer, wake them up. */
- if ((sc_link->flags & SDEV_WAITING) != 0) {
- sc_link->flags &= ~SDEV_WAITING;
- wakeup(sc_link);
- } else if (start && sc_link->device->start) {
- SC_DEBUG(sc_link, SDEV_DB2,
- ("calling private start()\n"));
- (*(sc_link->device->start)) (sc_link->device_softc);
+ if (ISSET(link->flags, SDEV_WAITING)) {
+ CLR(link->flags, SDEV_WAITING);
+ wakeup(link);
}
-}
-
-/*
- * Make a scsi_xfer, and return a pointer to it.
- */
-static __inline struct scsi_xfer *
-scsi_make_xs(struct scsi_link *sc_link, struct scsi_generic *scsi_cmd,
- int cmdlen, u_char *data_addr, int datalen, int retries, int timeout,
- struct buf *bp, int flags)
-{
- struct scsi_xfer *xs;
-
- if ((xs = scsi_get_xs(sc_link, flags)) == NULL)
- return (NULL);
-
- /*
- * Fill out the scsi_xfer structure. We don't know whose context
- * the cmd is in, so copy it.
- */
- xs->sc_link = sc_link;
- bcopy(scsi_cmd, &xs->cmdstore, cmdlen);
- xs->cmd = &xs->cmdstore;
- xs->cmdlen = cmdlen;
- xs->data = data_addr;
- xs->datalen = datalen;
- xs->retries = retries;
- xs->timeout = timeout;
- xs->bp = bp;
-
- /*
- * Set the LUN in the CDB if it fits in the three bits available. This
- * may only be needed if we have an older device. However, we also set
- * it for more modern SCSI devices "just in case". The old code
- * assumed everything newer than SCSI-2 would not need it, but why risk
- * it? This was the old conditional:
- *
- * if ((SCSISPC(sc_link->inqdata.version) <= 2))
- */
- xs->cmd->bytes[0] &= ~SCSI_CMD_LUN_MASK;
- if (sc_link->lun < 8)
- xs->cmd->bytes[0] |= ((sc_link->lun << SCSI_CMD_LUN_SHIFT) &
- SCSI_CMD_LUN_MASK);
-
-#ifdef SCSIDEBUG
- if ((sc_link->flags & SDEV_DB1) != 0)
- show_scsi_xs(xs);
-#endif /* SCSIDEBUG */
-
- return (xs);
+ mtx_leave(&link->mtx);
}
/*
@@ -759,171 +706,52 @@ scsi_report_luns(struct scsi_link *sc_link, int selectreport,
return (error);
}
-/*
- * This routine is called by the scsi interrupt when the transfer is complete.
- */
void
-scsi_done(struct scsi_xfer *xs)
+scsi_xs_exec(struct scsi_xfer *xs)
{
- struct scsi_link *sc_link = xs->sc_link;
- struct buf *bp;
- int error;
-
- SC_DEBUG(sc_link, SDEV_DB2, ("scsi_done\n"));
+ int rv;
+ int s;
- splassert(IPL_BIO);
-
- xs->flags |= ITSDONE;
+ xs->flags &= ~ITSDONE;
+ xs->error = XS_NOERROR;
+ xs->resid = xs->datalen;
+ xs->status = 0;
/*
- * 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).
- */
- if ((xs->flags & SCSI_USER) != 0) {
- SC_DEBUG(sc_link, SDEV_DB3, ("calling user done()\n"));
- scsi_user_done(xs); /* to take a copy of the sense etc. */
- SC_DEBUG(sc_link, SDEV_DB3, ("returned from user done()\n"));
-
- scsi_free_xs(xs, 1); /* restarts queue too */
- SC_DEBUG(sc_link, SDEV_DB3, ("returning to adapter\n"));
- return;
- }
+ * scsi_xs_exec() guarantees that scsi_done() will be called on the xs
+ * it was given. The adapter is responsible for calling scsi_done()
+ * except if its scsi_cmd() routine returns NO_CCB or TRY_AGAIN_LATER.
+ * In those cases we must call scsi_done() for it.
+ */
- if (!((xs->flags & (SCSI_NOSLEEP | SCSI_POLL)) == SCSI_NOSLEEP)) {
+ rv = xs->sc_link->adapter->scsi_cmd(xs);
+ if (rv == NO_CCB) {
/*
- * 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
+ * Give the xs back to the device driver to retry on its own.
*/
- wakeup(xs);
- return;
+
+ xs->error = XS_NO_CCB;
+ s = splbio();
+ scsi_done(xs);
+ splx(s);
}
/*
- * Go and handle errors now.
- * If it returns ERESTART then we should RETRY
+ * The adapter has called or will call scsi_done().
*/
-retry:
- error = sc_err1(xs);
- if (error == ERESTART) {
- switch ((*(sc_link->adapter->scsi_cmd)) (xs)) {
- case SUCCESSFULLY_QUEUED:
- return;
-
- case COMPLETE:
- goto retry;
- }
- }
-
- bp = xs->bp;
- if (bp != NULL) {
- 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);
- }
- scsi_free_xs(xs, 1);
- if (bp != NULL)
- biodone(bp);
}
-int
-scsi_execute_xs(struct scsi_xfer *xs)
+/*
+ * This routine is called by the adapter when its xs handling is done.
+ */
+void
+scsi_done(struct scsi_xfer *xs)
{
- int error, flags, rslt, s;
-
- xs->flags &= ~ITSDONE;
- xs->error = XS_NOERROR;
- xs->resid = xs->datalen;
- xs->status = 0;
-
- /*
- * Do the transfer. If we are polling we will return:
- * COMPLETE.
- *
- * if under full steam (interrupts) it will return:
- * SUCCESSFULLY_QUEUED, will do a wakeup when complete.
- * After the wakeup, we must still check if it succeeded
- *
- * If we have a SCSI_NOSLEEP (typically because we have a buf)
- * we just return. All the error processing and the buffer
- * code both expect us to return straight to them, so as soon
- * as the command is queued, return.
- */
-
- /*
- * We save the flags here because the xs structure may already
- * be freed by scsi_done by the time adapter->scsi_cmd returns.
- *
- * scsi_done is responsible for freeing the xs if either
- * (flags & (SCSI_NOSLEEP | SCSI_POLL)) == SCSI_NOSLEEP
- * -or-
- * (flags & SCSI_USER) != 0
- *
- * Note: SCSI_USER must always be called with SCSI_NOSLEEP
- * and never with SCSI_POLL, so the second expression should be
- * is equivalent to the first.
- */
-
- flags = xs->flags;
-#ifdef DIAGNOSTIC
- if ((flags & (SCSI_USER | SCSI_NOSLEEP)) == SCSI_USER)
- panic("scsi_execute_xs: USER without NOSLEEP");
- if ((flags & (SCSI_USER | SCSI_POLL)) == (SCSI_USER | SCSI_POLL))
- panic("scsi_execute_xs: USER with POLL");
-#endif
-retry:
- rslt = (*(xs->sc_link->adapter->scsi_cmd))(xs);
- switch (rslt) {
- case SUCCESSFULLY_QUEUED:
- if ((flags & (SCSI_NOSLEEP | SCSI_POLL)) == SCSI_NOSLEEP)
- return (EJUSTRETURN);
-#ifdef DIAGNOSTIC
- if (flags & SCSI_NOSLEEP)
- panic("scsi_execute_xs: NOSLEEP and POLL");
-#endif
- s = splbio();
- /* Since the xs is active we can't bail out on a signal. */
- while ((xs->flags & ITSDONE) == 0)
- tsleep(xs, PRIBIO + 1, "scsicmd", 0);
- splx(s);
- /* FALLTHROUGH */
- case COMPLETE: /* Polling command completed ok */
- if ((flags & (SCSI_NOSLEEP | SCSI_POLL)) == SCSI_NOSLEEP)
- return (EJUSTRETURN);
- if (xs->bp)
- return (EJUSTRETURN);
- SC_DEBUG(xs->sc_link, SDEV_DB3, ("back in cmd()\n"));
- if ((error = sc_err1(xs)) != ERESTART)
- return (error);
- goto retry;
-
- case NO_CCB:
- return (EAGAIN);
+ splassert(IPL_BIO);
- default:
- panic("scsi_execute_xs: invalid return code (%#x)", rslt);
- }
+ xs->flags |= ITSDONE;
-#ifdef DIAGNOSTIC
- panic("scsi_execute_xs: impossible");
-#endif
- return (EINVAL);
+ xs->done(xs);
}
/*
@@ -933,54 +761,83 @@ retry:
* to associate with the transfer, we need that too.
*/
int
-scsi_scsi_cmd(struct scsi_link *sc_link, struct scsi_generic *scsi_cmd,
+scsi_scsi_cmd(struct scsi_link *link, struct scsi_generic *scsi_cmd,
int cmdlen, u_char *data_addr, int datalen, int retries, int timeout,
struct buf *bp, int flags)
{
- struct scsi_xfer *xs;
- int error;
- int s;
-
- SC_DEBUG(sc_link, SDEV_DB2, ("scsi_cmd\n"));
+ struct scsi_xfer *xs;
+ int error;
+ int s;
#ifdef DIAGNOSTIC
if (bp != NULL && (flags & SCSI_NOSLEEP) == 0)
panic("scsi_scsi_cmd: buffer without nosleep");
#endif
- if ((xs = scsi_make_xs(sc_link, scsi_cmd, cmdlen, data_addr, datalen,
- retries, timeout, bp, flags)) == NULL)
+ xs = scsi_xs_get(link, flags);
+ if (xs == NULL)
return (ENOMEM);
-#ifdef SCSIDEBUG
- if ((sc_link->flags & SDEV_DB1) != 0)
- if (xs->datalen && (xs->flags & SCSI_DATA_OUT))
- show_mem(xs->data, min(64, xs->datalen));
-#endif /* SCSIDEBUG */
-
- error = scsi_execute_xs(xs);
+ memcpy(xs->cmd, scsi_cmd, cmdlen);
+ xs->cmdlen = cmdlen;
+ xs->data = data_addr;
+ xs->datalen = datalen;
+ xs->retries = retries;
+ xs->timeout = timeout;
-#ifdef SCSIDEBUG
- if ((sc_link->flags & SDEV_DB1) != 0)
- if (xs->datalen && (xs->flags & SCSI_DATA_IN))
- show_mem(xs->data, min(64, xs->datalen));
-#endif /* SCSIDEBUG */
+ xs->done = scsi_xs_done;
- if (error == EJUSTRETURN)
- return (0);
+ do {
+ scsi_xs_exec(xs);
+ if (!ISSET(xs->flags, SCSI_POLL)) {
+ s = splbio();
+ while (!ISSET(xs->flags, ITSDONE))
+ tsleep(xs, PRIBIO, "scsicmd", 0);
+ splx(s);
+ }
- s = splbio();
+ error = sc_err1(xs);
+ } while (error == ERESTART);
+
+ if (error != EAGAIN) {
+ if (bp != NULL) {
+ 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;
+ }
+
+ s = splbio();
+ biodone(bp);
+ splx(s);
+ }
- if (error == EAGAIN)
- scsi_free_xs(xs, 0); /* Don't restart queue. */
- else
- scsi_free_xs(xs, 1);
+ if (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.
+ */
+ link->device->done(xs);
+ }
+ }
- splx(s);
+ scsi_xs_put(xs);
return (error);
}
+void
+scsi_xs_done(struct scsi_xfer *xs)
+{
+ if (!ISSET(xs->flags, SCSI_POLL))
+ wakeup_one(xs);
+}
+
int
sc_err1(struct scsi_xfer *xs)
{
@@ -1041,6 +898,10 @@ sc_err1(struct scsi_xfer *xs)
error = EIO;
break;
+ case XS_NO_CCB:
+ error = EAGAIN;
+ break;
+
default:
sc_print_addr(xs->sc_link);
printf("unknown error category (0x%x) from scsi driver\n",
@@ -1100,10 +961,6 @@ scsi_interpret_sense(struct scsi_xfer *xs)
sense->flags & SSD_EOM ? 1 : 0,
sense->flags & SSD_FILEMARK ? 1 : 0,
sense->extra_len));
-#ifdef SCSIDEBUG
- if ((sc_link->flags & SDEV_DB1) != 0)
- show_mem((u_char *)&xs->sense, sizeof xs->sense);
-#endif /* SCSIDEBUG */
/*
* If the device has its own error handler, call it first.
@@ -1996,57 +1853,3 @@ scsi_decode_sense(struct scsi_sense_data *sense, int flag)
return (rqsbuf);
}
-
-#ifdef SCSIDEBUG
-/*
- * Given a scsi_xfer, dump the request, in all its glory
- */
-void
-show_scsi_xs(struct scsi_xfer *xs)
-{
- u_char *b = (u_char *) xs->cmd;
- int i = 0;
-
- sc_print_addr(xs->sc_link);
-
- printf("xs(%p): ", xs);
-
- printf("flg(0x%x)", xs->flags);
- 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)\n", xs->bp);
-
- printf("command: ");
-
- if ((xs->flags & SCSI_RESET) == 0) {
- while (i < xs->cmdlen) {
- if (i)
- printf(",");
- printf("%x", b[i++]);
- }
- printf("-[%d bytes]\n", xs->datalen);
- } else
- printf("-RESET-\n");
-}
-
-void
-show_mem(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 */
diff --git a/sys/scsi/scsi_ioctl.c b/sys/scsi/scsi_ioctl.c
index 2b4d79d2532..bda32e3516b 100644
--- a/sys/scsi/scsi_ioctl.c
+++ b/sys/scsi/scsi_ioctl.c
@@ -1,4 +1,4 @@
-/* $OpenBSD: scsi_ioctl.c,v 1.37 2009/11/12 06:20:27 dlg Exp $ */
+/* $OpenBSD: scsi_ioctl.c,v 1.38 2009/12/01 01:40:02 dlg Exp $ */
/* $NetBSD: scsi_ioctl.c,v 1.23 1996/10/12 23:23:17 christos Exp $ */
/*
@@ -52,22 +52,7 @@
#include <scsi/scsiconf.h>
#include <sys/scsiio.h>
-struct scsi_ioctl {
- LIST_ENTRY(scsi_ioctl) si_list;
- struct buf si_bp;
- struct uio si_uio;
- struct iovec si_iov;
- scsireq_t si_screq;
- struct scsi_link *si_sc_link;
-};
-
-LIST_HEAD(, scsi_ioctl) si_head;
-
-struct scsi_ioctl *si_get(void);
-void si_free(struct scsi_ioctl *);
-struct scsi_ioctl *si_find(struct buf *);
-void scsistrategy(struct buf *);
-void scsiminphys(struct buf *);
+int scsi_ioc_cmd(struct scsi_link *, scsireq_t *);
const unsigned char scsi_readsafe_cmd[256] = {
[0x00] = 1, /* TEST UNIT READY */
@@ -110,237 +95,98 @@ const unsigned char scsi_readsafe_cmd[256] = {
[0xbe] = 1 /* READ CD */
};
-struct scsi_ioctl *
-si_get(void)
+int
+scsi_ioc_cmd(struct scsi_link *link, scsireq_t *screq)
{
- struct scsi_ioctl *si;
- int s;
+ struct scsi_xfer *xs;
+ int err = 0;
+ int s;
- si = malloc(sizeof(*si), M_TEMP, M_WAITOK | M_ZERO);
- s = splbio();
- LIST_INSERT_HEAD(&si_head, si, si_list);
- splx(s);
- return (si);
-}
+ if (screq->cmdlen > sizeof(struct scsi_generic))
+ return (EFAULT);
-void
-si_free(struct scsi_ioctl *si)
-{
- int s;
+ xs = scsi_xs_get(link, 0);
+ if (xs == NULL)
+ return (ENOMEM);
- s = splbio();
- LIST_REMOVE(si, si_list);
- splx(s);
- free(si, M_TEMP);
-}
-
-struct scsi_ioctl *
-si_find(struct buf *bp)
-{
- struct scsi_ioctl *si;
- int s;
+ memcpy(xs->cmd, screq->cmd, screq->cmdlen);
+ xs->cmdlen = screq->cmdlen;
- s = splbio();
- LIST_FOREACH(si, &si_head, si_list) {
- if (bp == &si->si_bp)
- break;
+ if (screq->datalen > 0) {
+ xs->data = malloc(screq->datalen, M_TEMP, M_WAITOK);
+ xs->datalen = screq->datalen;
}
- splx(s);
- return (si);
-}
-
-/*
- * We let the user interpret his own sense in the generic scsi world.
- * This routine is called at interrupt time if the SCSI_USER bit was set
- * in the flags passed to scsi_scsi_cmd(). No other completion processing
- * takes place, even if we are running over another device driver.
- * The lower level routines that call us here, will free the xs and restart
- * the device's queue if such exists.
- */
-void
-scsi_user_done(struct scsi_xfer *xs)
-{
- struct buf *bp;
- struct scsi_ioctl *si;
- scsireq_t *screq;
- struct scsi_link *sc_link;
-
- splassert(IPL_BIO);
+ if (screq->flags & SCCMD_READ)
+ xs->flags |= SCSI_DATA_IN;
+ if (screq->flags & SCCMD_WRITE) {
+ if (screq->datalen > 0) {
+ err = copyin(screq->databuf, xs->data, screq->datalen);
+ if (err != 0)
+ goto err;
+ }
- bp = xs->bp;
- if (bp == NULL) { /* ALL user requests must have a buf */
- sc_print_addr(xs->sc_link);
- printf("User command with no buf\n");
- return;
+ xs->flags |= SCSI_DATA_OUT;
}
- si = si_find(bp);
- if (si == NULL) {
- sc_print_addr(xs->sc_link);
- printf("User command with no ioctl\n");
- return;
- }
+ xs->timeout = screq->timeout;
+ xs->retries = 0; /* user must do the retries *//* ignored */
- screq = &si->si_screq;
- sc_link = si->si_sc_link;
- SC_DEBUG(xs->sc_link, SDEV_DB2, ("user-done\n"));
+ xs->done = (void (*)(struct scsi_xfer *))wakeup;
+
+ scsi_xs_exec(xs);
+ s = splbio();
+ while (!ISSET(xs->flags, ITSDONE))
+ tsleep(xs, PRIBIO, "scsiioc", 0);
+ splx(s);
screq->retsts = 0;
screq->status = xs->status;
switch (xs->error) {
case XS_NOERROR:
- SC_DEBUG(sc_link, SDEV_DB3, ("no error\n"));
/* probably rubbish */
screq->datalen_used = xs->datalen - xs->resid;
screq->retsts = SCCMD_OK;
break;
case XS_SENSE:
- SC_DEBUG(sc_link, SDEV_DB3, ("have sense\n"));
- screq->senselen_used = min(sizeof(xs->sense), SENSEBUFLEN);
- bcopy(&xs->sense, screq->sense, screq->senselen);
+ screq->senselen_used = min(sizeof(xs->sense),
+ sizeof(screq->sense));
+ bcopy(&xs->sense, screq->sense, screq->senselen_used);
screq->retsts = SCCMD_SENSE;
break;
case XS_SHORTSENSE:
- SC_DEBUG(sc_link, SDEV_DB3, ("have short sense\n"));
- screq->senselen_used = min(sizeof(xs->sense), SENSEBUFLEN);
- bcopy(&xs->sense, screq->sense, screq->senselen);
+ printf("XS_SHORTSENSE\n");
+ screq->senselen_used = min(sizeof(xs->sense),
+ sizeof(screq->sense));
+ bcopy(&xs->sense, screq->sense, screq->senselen_used);
screq->retsts = SCCMD_UNKNOWN;
break;
case XS_DRIVER_STUFFUP:
- sc_print_addr(sc_link);
- printf("host adapter code inconsistency\n");
screq->retsts = SCCMD_UNKNOWN;
break;
case XS_TIMEOUT:
- SC_DEBUG(sc_link, SDEV_DB3, ("timeout\n"));
screq->retsts = SCCMD_TIMEOUT;
break;
case XS_BUSY:
- SC_DEBUG(sc_link, SDEV_DB3, ("busy\n"));
screq->retsts = SCCMD_BUSY;
break;
default:
- sc_print_addr(sc_link);
- printf("unknown error category (0x%x) from host adapter code\n",
- xs->error);
screq->retsts = SCCMD_UNKNOWN;
break;
}
- biodone(bp); /* we're waiting on it in scsi_strategy() */
-}
-
-
-/* Pseudo strategy function
- * Called by scsi_do_ioctl() via physio/physstrat if there is to
- * be data transferred, and directly if there is no data transfer.
- *
- * Should I reorganize this so it returns to physio instead
- * of sleeping in scsiio_scsi_cmd? Is there any advantage, other
- * than avoiding the probable duplicate wakeup in iodone? [PD]
- *
- * No, seems ok to me... [JRE]
- * (I don't see any duplicate wakeups)
- *
- * Can't be used with block devices or raw_read/raw_write directly
- * from the cdevsw/bdevsw tables because they couldn't have added
- * the screq structure. [JRE]
- */
-void
-scsistrategy(struct buf *bp)
-{
- struct scsi_ioctl *si;
- scsireq_t *screq;
- struct scsi_link *sc_link;
- int error;
- int flags = 0;
- int s;
-
- si = si_find(bp);
- if (si == NULL) {
- printf("user_strat: No ioctl\n");
- error = EINVAL;
- goto bad;
- }
-
- screq = &si->si_screq;
- sc_link = si->si_sc_link;
- SC_DEBUG(sc_link, SDEV_DB2, ("user_strategy\n"));
-
- /*
- * We're in trouble if physio tried to break up the transfer.
- */
- if (bp->b_bcount != screq->datalen) {
- sc_print_addr(sc_link);
- printf("physio split the request.. cannot proceed\n");
- error = EIO;
- goto bad;
- }
-
- if (screq->timeout == 0) {
- error = EINVAL;
- goto bad;
+ if (screq->datalen > 0 && screq->flags & SCCMD_READ) {
+ err = copyout(xs->data, screq->databuf, screq->datalen);
+ if (err != 0)
+ goto err;
}
- if (screq->cmdlen > sizeof(struct scsi_generic)) {
- sc_print_addr(sc_link);
- printf("cmdlen too big\n");
- error = EFAULT;
- goto bad;
- }
-
- if (screq->flags & SCCMD_READ)
- flags |= SCSI_DATA_IN;
- if (screq->flags & SCCMD_WRITE)
- flags |= SCSI_DATA_OUT;
- if (screq->flags & SCCMD_TARGET)
- flags |= SCSI_TARGET;
- if (screq->flags & SCCMD_ESCAPE)
- flags |= SCSI_ESCAPE;
-
- error = scsi_scsi_cmd(sc_link, (struct scsi_generic *)screq->cmd,
- screq->cmdlen, (u_char *)bp->b_data, screq->datalen,
- 0, /* user must do the retries *//* ignored */
- screq->timeout, bp, flags | SCSI_USER | SCSI_NOSLEEP);
-
- /* because there is a bp, scsi_scsi_cmd will return immediately */
- if (error)
- goto bad;
-
- SC_DEBUG(sc_link, SDEV_DB3, ("about to sleep\n"));
- s = splbio();
- while ((bp->b_flags & B_DONE) == 0)
- tsleep(bp, PRIBIO, "scistr", 0);
- splx(s);
- SC_DEBUG(sc_link, SDEV_DB3, ("back from sleep\n"));
-
- return;
-
-bad:
- bp->b_flags |= B_ERROR;
- bp->b_error = error;
- s = splbio();
- biodone(bp);
- splx(s);
-}
-
-void
-scsiminphys(struct buf *bp)
-{
- struct scsi_ioctl *si;
- struct scsi_link *sc_link;
-
- si = si_find(bp);
- if (si == NULL) {
- /* should not happen */
- bp->b_flags |= B_ERROR;
- bp->b_error = EINVAL;
- return;
- }
+err:
+ if (screq->datalen > 0)
+ free(xs->data, M_TEMP);
+ scsi_xs_put(xs);
- sc_link = si->si_sc_link;
- (*sc_link->adapter->scsi_minphys)(bp, sc_link);
+ return (err);
}
/*
@@ -354,8 +200,6 @@ int
scsi_do_ioctl(struct scsi_link *sc_link, dev_t dev, u_long cmd, caddr_t addr,
int flag, struct proc *p)
{
- int error;
-
SC_DEBUG(sc_link, SDEV_DB2, ("scsi_do_ioctl(0x%lx)\n", cmd));
switch(cmd) {
@@ -378,7 +222,6 @@ scsi_do_ioctl(struct scsi_link *sc_link, dev_t dev, u_long cmd, caddr_t addr,
break;
/* FALLTHROUGH */
case SCIOCDEBUG:
- case SCIOCRESET:
if ((flag & FWRITE) == 0)
return (EPERM);
break;
@@ -391,41 +234,8 @@ scsi_do_ioctl(struct scsi_link *sc_link, dev_t dev, u_long cmd, caddr_t addr,
}
switch(cmd) {
- case SCIOCCOMMAND: {
- scsireq_t *screq = (scsireq_t *)addr;
- struct scsi_ioctl *si;
-
- si = si_get();
- si->si_screq = *screq;
- si->si_sc_link = sc_link;
- if (screq->datalen) {
- si->si_iov.iov_base = screq->databuf;
- si->si_iov.iov_len = screq->datalen;
- si->si_uio.uio_iov = &si->si_iov;
- si->si_uio.uio_iovcnt = 1;
- si->si_uio.uio_resid = screq->datalen;
- si->si_uio.uio_offset = 0;
- si->si_uio.uio_segflg = UIO_USERSPACE;
- si->si_uio.uio_rw =
- (screq->flags & SCCMD_READ) ? UIO_READ : UIO_WRITE;
- si->si_uio.uio_procp = p;
- error = physio(scsistrategy, &si->si_bp, dev,
- (screq->flags & SCCMD_READ) ? B_READ : B_WRITE,
- scsiminphys, &si->si_uio);
- } else {
- /* if no data, no need to translate it.. */
- si->si_bp.b_flags = 0;
- si->si_bp.b_data = 0;
- si->si_bp.b_bcount = 0;
- si->si_bp.b_dev = dev;
- si->si_bp.b_proc = p;
- scsistrategy(&si->si_bp);
- error = si->si_bp.b_error;
- }
- *screq = si->si_screq;
- si_free(si);
- return (error);
- }
+ case SCIOCCOMMAND:
+ return (scsi_ioc_cmd(sc_link, (scsireq_t *)addr));
case SCIOCDEBUG: {
int level = *((int *)addr);
@@ -441,11 +251,6 @@ scsi_do_ioctl(struct scsi_link *sc_link, dev_t dev, u_long cmd, caddr_t addr,
sc_link->flags |= SDEV_DB4;
return (0);
}
- case SCIOCRESET: {
- scsi_scsi_cmd(sc_link, 0, 0, 0, 0, GENRETRY, 2000, NULL,
- SCSI_RESET);
- return (0);
- }
default:
#ifdef DIAGNOSTIC
panic("scsi_do_ioctl: impossible cmd (%#lx)", cmd);
diff --git a/sys/scsi/scsiconf.c b/sys/scsi/scsiconf.c
index 58d0f609d22..c2dbd8c4cbd 100644
--- a/sys/scsi/scsiconf.c
+++ b/sys/scsi/scsiconf.c
@@ -1,4 +1,4 @@
-/* $OpenBSD: scsiconf.c,v 1.151 2009/11/12 06:20:27 dlg Exp $ */
+/* $OpenBSD: scsiconf.c,v 1.152 2009/12/01 01:40:02 dlg Exp $ */
/* $NetBSD: scsiconf.c,v 1.57 1996/05/02 01:09:01 neil Exp $ */
/*
@@ -113,6 +113,10 @@ int scsi_autoconf = SCSI_AUTOCONF;
int scsibusprint(void *, const char *);
void scsibus_printlink(struct scsi_link *);
+void scsi_activate_bus(struct scsibus_softc *, int);
+void scsi_activate_target(struct scsibus_softc *, int, int);
+void scsi_activate_lun(struct scsibus_softc *, int, int, int);
+
const u_int8_t version_to_spc [] = {
0, /* 0x00: The device does not claim conformance to any standard. */
1, /* 0x01: (Obsolete) SCSI-1 in olden times. */
@@ -199,30 +203,99 @@ scsibusattach(struct device *parent, struct device *self, void *aux)
int
scsibusactivate(struct device *dev, int act)
{
- return (config_activate_children(dev, act));
+ struct scsibus_softc *sc = (struct scsibus_softc *)dev;
+
+ scsi_activate(sc, -1, -1, act);
+
+ return (0);
+}
+
+void
+scsi_activate(struct scsibus_softc *sc, int target, int lun, int act)
+{
+ if (target == -1 && lun == -1)
+ scsi_activate_bus(sc, act);
+
+ if (target == -1)
+ return;
+
+ if (lun == -1)
+ scsi_activate_target(sc, target, act);
+
+ scsi_activate_lun(sc, target, lun, act);
+}
+
+void
+scsi_activate_bus(struct scsibus_softc *sc, int act)
+{
+ int target;
+
+ for (target = 0; target < sc->sc_buswidth; target++)
+ scsi_activate_target(sc, target, act);
+}
+
+void
+scsi_activate_target(struct scsibus_softc *sc, int target, int act)
+{
+ int lun;
+
+ for (lun = 0; lun < sc->adapter_link->luns; lun++)
+ scsi_activate_lun(sc, target, lun, act);
+}
+
+void
+scsi_activate_lun(struct scsibus_softc *sc, int target, int lun, int act)
+{
+ struct scsi_link *link = sc->sc_link[target][lun];
+ struct device *dev;
+
+ if (link == NULL)
+ return;
+
+ dev = link->device_softc;
+ switch (act) {
+ case DVACT_ACTIVATE:
+#if NMPATH > 0
+ if (dev == NULL)
+ mpath_path_activate(link);
+ else
+#endif /* NMPATH */
+ config_activate(dev);
+ break;
+
+ case DVACT_DEACTIVATE:
+#if NMPATH > 0
+ if (dev == NULL)
+ mpath_path_deactivate(link);
+ else
+#endif /* NMPATH */
+ config_deactivate(dev);
+ break;
+ default:
+#ifdef DIAGNOSTIC
+ printf("%s: unsupported act %d\n", sc->sc_dev.dv_xname, act);
+#endif
+ break;
+ }
}
int
scsibusdetach(struct device *dev, int type)
{
struct scsibus_softc *sb = (struct scsibus_softc *)dev;
- int i, j, error;
+ int i, error;
#if NBIO > 0
bio_unregister(&sb->sc_dev);
#endif
- if ((error = config_detach_children(dev, type)) != 0)
+ error = scsi_detach_bus(sb, type);
+ if (error != 0)
return (error);
for (i = 0; i < sb->sc_buswidth; i++) {
- if (sb->sc_link[i] != NULL) {
- for (j = 0; j < sb->adapter_link->luns; j++) {
- if (sb->sc_link[i][j] != NULL)
- free(sb->sc_link[i][j], M_DEVBUF);
- }
+ if (sb->sc_link[i] != NULL)
free(sb->sc_link[i], M_DEVBUF);
- }
}
free(sb->sc_link, M_DEVBUF);
@@ -274,6 +347,9 @@ scsibus_bioctl(struct device *dev, u_long cmd, caddr_t addr)
case SBIOCDETACH:
sdev = (struct sbioc_device *)addr;
+ if (sdev->sd_target == -1 && sdev->sd_lun == -1)
+ return (scsi_detach_bus(sc, 0));
+
if (sdev->sd_target == -1)
return (EINVAL);
@@ -379,19 +455,22 @@ int
scsi_detach_bus(struct scsibus_softc *sc, int flags)
{
struct scsi_link *alink = sc->adapter_link;
- int i;
+ int i, err, rv = 0;
- for (i = 0; i < alink->adapter_buswidth; i++)
- scsi_detach_target(sc, i, flags);
+ for (i = 0; i < alink->adapter_buswidth; i++) {
+ err = scsi_detach_target(sc, i, flags);
+ if (err != 0 && err != ENXIO)
+ rv = err;
+ }
- return (0);
+ return (rv);
}
int
scsi_detach_target(struct scsibus_softc *sc, int target, int flags)
{
struct scsi_link *alink = sc->adapter_link;
- int i, err, rv = 0, detached = 0;
+ int i, err, rv = 0;
if (target < 0 || target >= alink->adapter_buswidth ||
target == alink->adapter_target)
@@ -405,12 +484,11 @@ scsi_detach_target(struct scsibus_softc *sc, int target, int flags)
continue;
err = scsi_detach_lun(sc, target, i, flags);
- if (err != 0)
+ if (err != 0 && err != ENXIO)
rv = err;
- detached = 1;
}
- return (detached ? rv : ENXIO);
+ return (rv);
}
int
@@ -453,6 +531,8 @@ scsi_detach_lun(struct scsibus_softc *sc, int target, int lun, int flags)
alink->adapter->dev_free(link);
/* 3. free up its state in the midlayer */
+ if (link->id != NULL)
+ devid_free(link->id);
free(link, M_DEVBUF);
sc->sc_link[target][lun] = NULL;
@@ -685,6 +765,42 @@ scsibus_printlink(struct scsi_link *link)
printf("SCSI%d", SCSISPC(inqbuf->version));
printf(" %d/%s %s%s", type, dtype, removable ? "removable" : "fixed",
qtype);
+
+#if NMPATH > 0
+ if (link->id != NULL && link->id->d_type != DEVID_NONE) {
+ u_int8_t *id = (u_int8_t *)(link->id + 1);
+ int i;
+
+ switch (link->id->d_type) {
+ case DEVID_NAA:
+ printf(" naa.");
+ break;
+ case DEVID_EUI:
+ printf(" eui.");
+ break;
+ case DEVID_T10:
+ printf(" t10.");
+ break;
+ }
+
+ if (ISSET(link->id->d_flags, DEVID_F_PRINT)) {
+ for (i = 0; i < link->id->d_len; i++) {
+ if (id[i] == '\0' || id[i] == ' ')
+ printf("_");
+ else if (id[i] < 0x20 || id[i] >= 0x80) {
+ /* non-printable characters */
+ printf("~");
+ } else {
+ /* normal characters */
+ printf("%c", id[i]);
+ }
+ }
+ } else {
+ for (i = 0; i < link->id->d_len; i++)
+ printf("%02x", id[i]);
+ }
+ }
+#endif /* NMPATH > 0 */
}
/*
@@ -738,6 +854,7 @@ scsi_probedev(struct scsibus_softc *scsi, int target, int lun)
sc_link->target = target;
sc_link->lun = lun;
sc_link->device = &probe_switch;
+ mtx_init(&sc_link->mtx, IPL_BIO);
inqbuf = &sc_link->inqdata;
SC_DEBUG(sc_link, SDEV_DB2, ("scsi_link created.\n"));
@@ -810,15 +927,15 @@ scsi_probedev(struct scsibus_softc *scsi, int target, int lun)
;
else if (sc_link->flags & SDEV_UMASS)
;
- else if (sc_link->id.d_type != DEVID_NONE &&
- !DEVID_CMP(&scsi->sc_link[target][0]->id, &sc_link->id))
+ else if (sc_link->id != NULL &&
+ !DEVID_CMP(scsi->sc_link[target][0]->id, sc_link->id))
;
else if (memcmp(inqbuf, &scsi->sc_link[target][0]->inqdata,
sizeof(*inqbuf)) == 0) {
/* The device doesn't distinguish between LUNs. */
SC_DEBUG(sc_link, SDEV_DB1, ("IDENTIFY not supported.\n"));
rslt = EINVAL;
- goto bad;
+ goto free_devid;
}
#if NMPATH > 0
@@ -872,7 +989,7 @@ scsi_probedev(struct scsibus_softc *scsi, int target, int lun)
&sa)) == 0) {
scsibusprint(&sa, scsi->sc_dev.dv_xname);
printf(" not configured\n");
- goto bad;
+ goto free_devid;
}
/*
@@ -909,6 +1026,9 @@ scsi_probedev(struct scsibus_softc *scsi, int target, int lun)
return (0);
+free_devid:
+ if (sc_link->id)
+ devid_free(sc_link->id);
bad:
if (scsi->adapter_link->adapter->dev_free != NULL)
scsi->adapter_link->adapter->dev_free(sc_link);
@@ -979,6 +1099,9 @@ scsi_devid(struct scsi_link *link)
} __packed pg;
int pg80 = 0, pg83 = 0, i;
+ if (link->id != NULL)
+ return;
+
if (SCSISPC(link->inqdata.version) >= 2) {
if (scsi_inquire_vpd(link, &pg, sizeof(pg), SI_PG_SUPPORTED,
scsi_autoconf) != 0)
@@ -1008,9 +1131,10 @@ int
scsi_devid_pg83(struct scsi_link *link)
{
struct scsi_vpd_hdr hdr;
- struct scsi_vpd_devid_hdr dhdr;
+ struct scsi_vpd_devid_hdr dhdr, chdr;
u_int8_t *pg, *id;
- int type, idtype = 0, idlen;
+ int type, idtype = 0;
+ u_char idflags;
int len, pos;
int rv;
@@ -1048,7 +1172,8 @@ scsi_devid_pg83(struct scsi_link *link)
case VPD_DEVID_TYPE_T10:
if (type >= idtype) {
idtype = type;
- idlen = dhdr.len;
+
+ chdr = dhdr;
id = &pg[pos];
}
break;
@@ -1063,21 +1188,27 @@ scsi_devid_pg83(struct scsi_link *link)
} while (idtype != VPD_DEVID_TYPE_NAA && len != pos);
if (idtype > 0) {
- link->id.d_id = malloc(idlen, M_DEVBUF, M_WAITOK);
-
- switch (idtype) {
+ switch (VPD_DEVID_TYPE(chdr.flags)) {
case VPD_DEVID_TYPE_NAA:
- link->id.d_type = DEVID_NAA;
+ idtype = DEVID_NAA;
break;
case VPD_DEVID_TYPE_EUI64:
- link->id.d_type = DEVID_EUI;
+ idtype = DEVID_EUI;
break;
case VPD_DEVID_TYPE_T10:
- link->id.d_type = DEVID_T10;
+ idtype = DEVID_T10;
break;
}
- link->id.d_len = idlen;
- memcpy(link->id.d_id, id, idlen);
+ switch (VPD_DEVID_CODE(chdr.pi_code)) {
+ case VPD_DEVID_CODE_ASCII:
+ case VPD_DEVID_CODE_UTF8:
+ idflags = DEVID_F_PRINT;
+ break;
+ default:
+ idflags = 0;
+ break;
+ }
+ link->id = devid_alloc(idtype, idflags, chdr.len, id);
} else
rv = ENODEV;
@@ -1095,3 +1226,35 @@ scsi_minphys(struct buf *bp, struct scsi_link *sl)
{
minphys(bp);
}
+
+struct devid *
+devid_alloc(u_int8_t type, u_int8_t flags, u_int8_t len, u_int8_t *id)
+{
+ struct devid *d;
+
+ d = malloc(sizeof(*d) + len, M_DEVBUF, M_WAITOK|M_CANFAIL);
+ if (d == NULL)
+ return (NULL);
+
+ d->d_type = type;
+ d->d_flags = flags;
+ d->d_len = len;
+ d->d_refcount = 1;
+ memcpy(d + 1, id, len);
+
+ return (d);
+}
+
+struct devid *
+devid_copy(struct devid *d)
+{
+ d->d_refcount++;
+ return (d);
+}
+
+void
+devid_free(struct devid *d)
+{
+ if (--d->d_refcount == 0)
+ free(d, M_DEVBUF);
+}
diff --git a/sys/scsi/scsiconf.h b/sys/scsi/scsiconf.h
index 1d896e3e4ad..8f9619d8fb9 100644
--- a/sys/scsi/scsiconf.h
+++ b/sys/scsi/scsiconf.h
@@ -1,4 +1,4 @@
-/* $OpenBSD: scsiconf.h,v 1.111 2009/11/22 20:16:43 krw Exp $ */
+/* $OpenBSD: scsiconf.h,v 1.112 2009/12/01 01:40:02 dlg Exp $ */
/* $NetBSD: scsiconf.h,v 1.35 1997/04/02 02:29:38 mycroft Exp $ */
/*
@@ -53,6 +53,7 @@
#include <sys/queue.h>
#include <sys/timeout.h>
#include <sys/workq.h>
+#include <sys/mutex.h>
#include <machine/cpu.h>
#include <scsi/scsi_debug.h>
@@ -234,20 +235,31 @@ _4ltol(u_int8_t *bytes)
#define DEVID_T10 3
struct devid {
- int d_type;
- u_int d_len;
- u_int8_t *d_id;
+ u_int8_t d_type;
+ u_int8_t d_flags;
+#define DEVID_F_PRINT (1<<0)
+ u_int8_t d_refcount;
+ u_int8_t d_len;
+
+ /*
+ * the devid struct is basically a header, the actual id is allocated
+ * immediately after it.
+ */
};
-#define DEVID_CMP(_a, _b) ( \
- (_a) != NULL && \
- (_b) != NULL && \
- (_a)->d_type != DEVID_NONE && \
- (_a)->d_type == (_b)->d_type && \
- (_a)->d_len == (_b)->d_len && \
- bcmp((_a)->d_id, (_b)->d_id, (_a)->d_len) == 0 \
+#define DEVID_CMP(_a, _b) ( \
+ (_a) != NULL && (_b) != NULL && \
+ ((_a) == (_b) || \
+ ((_a)->d_type != DEVID_NONE && \
+ (_a)->d_type == (_b)->d_type && \
+ (_a)->d_len == (_b)->d_len && \
+ bcmp((_a) + 1, (_b) + 1, (_a)->d_len) == 0)) \
)
+struct devid * devid_alloc(u_int8_t, u_int8_t, u_int8_t, u_int8_t *);
+struct devid * devid_copy(struct devid *);
+void devid_free(struct devid *);
+
/*
* The following documentation tries to describe the relationship between the
* various structures defined in this file:
@@ -337,7 +349,6 @@ struct scsi_link {
u_int64_t node_wwn; /* world wide name of node */
u_int16_t adapter_target; /* what are we on the scsi bus */
u_int16_t adapter_buswidth; /* 8 (regular) or 16 (wide). (0 becomes 8) */
- u_int16_t active; /* operations in progress */
u_int16_t flags; /* flags that all devices have */
#define SDEV_REMOVABLE 0x0001 /* media is removable */
#define SDEV_MEDIA_LOADED 0x0002 /* device figures are still valid */
@@ -366,7 +377,8 @@ struct scsi_link {
void *adapter_softc; /* needed for call to foo_scsi_cmd */
struct scsibus_softc *bus; /* link to the scsibus we're on */
struct scsi_inquiry_data inqdata; /* copy of INQUIRY data from probe */
- struct devid id;
+ struct devid *id;
+ struct mutex mtx;
};
int scsiprint(void *, const char *);
@@ -442,6 +454,8 @@ struct scsi_xfer {
* timeout structure for hba's to use for a command
*/
struct timeout stimeout;
+ void *cookie;
+ void (*done)(struct scsi_xfer *);
};
/*
@@ -484,6 +498,7 @@ struct scsi_xfer {
#define XS_BUSY 5 /* The device busy, try again later? */
#define XS_SHORTSENSE 6 /* Check the ATAPI sense for the error */
#define XS_RESET 8 /* bus was reset; possible retry command */
+#define XS_NO_CCB 9 /* device should requeue io and retry */
/*
* Possible retries for scsi_test_unit_ready()
@@ -555,14 +570,23 @@ int scsi_detach_lun(struct scsibus_softc *, int, int, int);
int scsi_req_probe(struct scsibus_softc *, int, int);
int scsi_req_detach(struct scsibus_softc *, int, int, int);
+void scsi_activate(struct scsibus_softc *, int, int, int);
+
extern const u_int8_t version_to_spc[];
#define SCSISPC(x)(version_to_spc[(x) & SID_ANSII])
+struct scsi_xfer * scsi_xs_get(struct scsi_link *, int);
+void scsi_xs_exec(struct scsi_xfer *);
+void scsi_xs_put(struct scsi_xfer *);
+
/*
* Entrypoints for multipathing
*/
int mpath_path_attach(struct scsi_link *);
int mpath_path_detach(struct scsi_link *, int);
+void mpath_path_activate(struct scsi_link *);
+void mpath_path_deactivate(struct scsi_link *);
+
#endif /* _KERNEL */
#endif /* SCSI_SCSICONF_H */
diff --git a/sys/scsi/sd.c b/sys/scsi/sd.c
index baf354d80b0..081733da095 100644
--- a/sys/scsi/sd.c
+++ b/sys/scsi/sd.c
@@ -1,4 +1,4 @@
-/* $OpenBSD: sd.c,v 1.164 2009/11/12 06:20:27 dlg Exp $ */
+/* $OpenBSD: sd.c,v 1.165 2009/12/01 01:40:02 dlg Exp $ */
/* $NetBSD: sd.c,v 1.111 1997/04/02 02:29:41 mycroft Exp $ */
/*-
@@ -83,10 +83,7 @@ int sddetach(struct device *, int);
void sdminphys(struct buf *);
int sdgetdisklabel(dev_t, struct sd_softc *, struct disklabel *, int);
void sdstart(void *);
-void sdrestart(void *);
-void sddone(struct scsi_xfer *);
void sd_shutdown(void *);
-int sd_reassign_blocks(struct sd_softc *, u_long);
int sd_interpret_sense(struct scsi_xfer *);
int sd_get_parms(struct sd_softc *, struct disk_parms *, int);
void sd_flush(struct sd_softc *, int);
@@ -96,6 +93,16 @@ void viscpy(u_char *, u_char *, int);
int sd_ioctl_inquiry(struct sd_softc *, struct dk_inquiry *);
+struct buf *sd_buf_dequeue(struct sd_softc *);
+void sd_buf_requeue(struct sd_softc *, struct buf *);
+
+void sd_cmd_rw6(struct scsi_xfer *, int, daddr64_t, u_int);
+void sd_cmd_rw10(struct scsi_xfer *, int, daddr64_t, u_int);
+void sd_cmd_rw12(struct scsi_xfer *, int, daddr64_t, u_int);
+void sd_cmd_rw16(struct scsi_xfer *, int, daddr64_t, u_int);
+
+void sd_buf_done(struct scsi_xfer *);
+
struct cfattach sd_ca = {
sizeof(struct sd_softc), sdmatch, sdattach,
sddetach, sdactivate
@@ -111,7 +118,7 @@ struct scsi_device sd_switch = {
sd_interpret_sense, /* check out error handler first */
sdstart, /* have a queue, served by this */
NULL, /* have no async handler */
- sddone, /* deal with stats at interrupt time */
+ NULL, /* have no done handler */
};
const struct scsi_inquiry_pattern sd_patterns[] = {
@@ -163,6 +170,8 @@ sdattach(struct device *parent, struct device *self, void *aux)
SC_DEBUG(sc_link, SDEV_DB2, ("sdattach:\n"));
+ mtx_init(&sd->sc_buf_mtx, IPL_BIO);
+
/*
* Store information needed to contact our base driver
*/
@@ -197,7 +206,7 @@ sdattach(struct device *parent, struct device *self, void *aux)
*/
printf("\n");
- timeout_set(&sd->sc_timeout, sdrestart, sd);
+ timeout_set(&sd->sc_timeout, sdstart, sd);
/* Spin up non-UMASS devices ready or not. */
if ((sd->sc_link->flags & SDEV_UMASS) == 0)
@@ -551,12 +560,12 @@ sdstrategy(struct buf *bp)
(sd->flags & (SDF_WLABEL|SDF_LABELLING)) != 0) <= 0)
goto done;
- s = splbio();
-
/*
* Place it in the queue of disk activities for this disk
*/
- disksort(&sd->buf_queue, bp);
+ mtx_enter(&sd->sc_buf_mtx);
+ disksort(&sd->sc_buf_queue, bp);
+ mtx_leave(&sd->sc_buf_mtx);
/*
* Tell the device to get going on the transfer if it's
@@ -564,8 +573,6 @@ sdstrategy(struct buf *bp)
*/
sdstart(sd);
- splx(s);
-
device_unref(&sd->sc_dev);
return;
@@ -583,6 +590,77 @@ done:
device_unref(&sd->sc_dev);
}
+struct buf *
+sd_buf_dequeue(struct sd_softc *sc)
+{
+ struct buf *bp;
+
+ mtx_enter(&sc->sc_buf_mtx);
+ bp = sc->sc_buf_queue.b_actf;
+ if (bp != NULL)
+ sc->sc_buf_queue.b_actf = bp->b_actf;
+ mtx_leave(&sc->sc_buf_mtx);
+
+ return (bp);
+}
+
+void
+sd_buf_requeue(struct sd_softc *sc, struct buf *bp)
+{
+ mtx_enter(&sc->sc_buf_mtx);
+ bp->b_actf = sc->sc_buf_queue.b_actf;
+ sc->sc_buf_queue.b_actf = bp;
+ mtx_leave(&sc->sc_buf_mtx);
+}
+
+void
+sd_cmd_rw6(struct scsi_xfer *xs, int read, daddr64_t blkno, u_int nblks)
+{
+ struct scsi_rw *cmd = (struct scsi_rw *)xs->cmd;
+
+ cmd->opcode = read ? READ_COMMAND : WRITE_COMMAND;
+ _lto3b(blkno, cmd->addr);
+ cmd->length = nblks;
+
+ xs->cmdlen = sizeof(*cmd);
+}
+
+void
+sd_cmd_rw10(struct scsi_xfer *xs, int read, daddr64_t blkno, u_int nblks)
+{
+ struct scsi_rw_big *cmd = (struct scsi_rw_big *)xs->cmd;
+
+ cmd->opcode = read ? READ_BIG : WRITE_BIG;
+ _lto4b(blkno, cmd->addr);
+ _lto2b(nblks, cmd->length);
+
+ xs->cmdlen = sizeof(*cmd);
+}
+
+void
+sd_cmd_rw12(struct scsi_xfer *xs, int read, daddr64_t blkno, u_int nblks)
+{
+ struct scsi_rw_12 *cmd = (struct scsi_rw_12 *)xs->cmd;
+
+ cmd->opcode = read ? READ_12 : WRITE_12;
+ _lto4b(blkno, cmd->addr);
+ _lto4b(nblks, cmd->length);
+
+ xs->cmdlen = sizeof(*cmd);
+}
+
+void
+sd_cmd_rw16(struct scsi_xfer *xs, int read, daddr64_t blkno, u_int nblks)
+{
+ struct scsi_rw_16 *cmd = (struct scsi_rw_16 *)xs->cmd;
+
+ cmd->opcode = read ? READ_16 : WRITE_16;
+ _lto8b(blkno, cmd->addr);
+ _lto4b(nblks, cmd->length);
+
+ xs->cmdlen = sizeof(*cmd);
+}
+
/*
* sdstart looks to see if there is a buf waiting for the device
* and that the device is not already busy. If both are true,
@@ -595,62 +673,33 @@ done:
* This routine is also called after other non-queued requests
* have been made of the scsi driver, to ensure that the queue
* continues to be drained.
- *
- * must be called at the correct (highish) spl level
- * sdstart() is called at splbio from sdstrategy, sdrestart and scsi_done
*/
void
sdstart(void *v)
{
- struct sd_softc *sd = (struct sd_softc *)v;
- struct scsi_link *sc_link = sd->sc_link;
- struct buf *bp = 0;
- struct buf *dp;
- struct scsi_rw_big cmd_big;
- struct scsi_rw_12 cmd_12;
- struct scsi_rw_16 cmd_16;
- struct scsi_rw cmd_small;
- struct scsi_generic *cmdp;
+ struct sd_softc *sc = (struct sd_softc *)v;
+ struct scsi_link *link = sc->sc_link;
+ struct scsi_xfer *xs;
+ struct buf *bp;
daddr64_t blkno;
- int nblks, cmdlen, error;
+ int nblks;
+ int read;
struct partition *p;
- if (sd->flags & SDF_DYING)
+ if (sc->flags & SDF_DYING)
return;
SC_DEBUG(sc_link, SDEV_DB2, ("sdstart\n"));
- splassert(IPL_BIO);
-
- /*
- * Check if the device has room for another command
- */
- while (sc_link->openings > 0) {
- /*
- * there is excess capacity, but a special waits
- * It'll need the adapter as soon as we clear out of the
- * way and let it run (user level wait).
- */
- if (sc_link->flags & SDEV_WAITING) {
- sc_link->flags &= ~SDEV_WAITING;
- wakeup((caddr_t)sc_link);
- return;
- }
-
- /*
- * See if there is a buf with work for us to do..
- */
- dp = &sd->buf_queue;
- if ((bp = dp->b_actf) == NULL) /* yes, an assign */
- return;
- dp->b_actf = bp->b_actf;
-
+ CLR(sc->flags, SDF_WAITING);
+ while (!ISSET(sc->flags, SDF_WAITING) &&
+ (bp = sd_buf_dequeue(sc)) != NULL) {
/*
* If the device has become invalid, abort all the
* reads and writes until all files have been closed and
* re-opened
*/
- if ((sc_link->flags & SDEV_MEDIA_LOADED) == 0) {
+ if ((link->flags & SDEV_MEDIA_LOADED) == 0) {
bp->b_error = EIO;
bp->b_flags |= B_ERROR;
bp->b_resid = bp->b_bcount;
@@ -658,129 +707,86 @@ sdstart(void *v)
continue;
}
- /*
- * We have a buf, now we should make a command
- *
- * First, translate the block to absolute and put it in terms
- * of the logical blocksize of the device.
- */
+ xs = scsi_xs_get(link, SCSI_NOSLEEP);
+ if (xs == NULL) {
+ sd_buf_requeue(sc, bp);
+ return;
+ }
+
blkno =
- bp->b_blkno / (sd->sc_dk.dk_label->d_secsize / DEV_BSIZE);
- p = &sd->sc_dk.dk_label->d_partitions[DISKPART(bp->b_dev)];
+ bp->b_blkno / (sc->sc_dk.dk_label->d_secsize / DEV_BSIZE);
+ p = &sc->sc_dk.dk_label->d_partitions[DISKPART(bp->b_dev)];
blkno += DL_GETPOFFSET(p);
- nblks = howmany(bp->b_bcount, sd->sc_dk.dk_label->d_secsize);
+ nblks = howmany(bp->b_bcount, sc->sc_dk.dk_label->d_secsize);
+ read = bp->b_flags & B_READ;
/*
* Fill out the scsi command. If the transfer will
* fit in a "small" cdb, use it.
*/
- if (!(sc_link->flags & SDEV_ATAPI) &&
- !(sc_link->quirks & SDEV_ONLYBIG) &&
+ if (!(link->flags & SDEV_ATAPI) &&
+ !(link->quirks & SDEV_ONLYBIG) &&
((blkno & 0x1fffff) == blkno) &&
- ((nblks & 0xff) == nblks)) {
- /*
- * We can fit in a 6 byte cdb.
- */
- bzero(&cmd_small, sizeof(cmd_small));
- cmd_small.opcode = (bp->b_flags & B_READ) ?
- READ_COMMAND : WRITE_COMMAND;
- _lto3b(blkno, cmd_small.addr);
- cmd_small.length = nblks;
- cmdlen = sizeof(cmd_small);
- cmdp = (struct scsi_generic *)&cmd_small;
- } else if (((blkno & 0xffffffff) == blkno) &&
- ((nblks & 0xffff) == nblks)) {
- /*
- * We can fit in a 10 byte cdb.
- */
- bzero(&cmd_big, sizeof(cmd_big));
- cmd_big.opcode = (bp->b_flags & B_READ) ?
- READ_BIG : WRITE_BIG;
- _lto4b(blkno, cmd_big.addr);
- _lto2b(nblks, cmd_big.length);
- cmdlen = sizeof(cmd_big);
- cmdp = (struct scsi_generic *)&cmd_big;
- } else if (((blkno & 0xffffffff) == blkno) &&
- ((nblks & 0xffffffff) == nblks)) {
- /*
- * We can fit in a 12 byte cdb.
- */
- bzero(&cmd_12, sizeof(cmd_12));
- cmd_12.opcode = (bp->b_flags & B_READ) ?
- READ_12 : WRITE_12;
- _lto4b(blkno, cmd_12.addr);
- _lto4b(nblks, cmd_12.length);
- cmdlen = sizeof(cmd_12);
- cmdp = (struct scsi_generic *)&cmd_12;
- } else {
- /*
- * Need a 16 byte cdb. There's nothing bigger.
- */
- bzero(&cmd_16, sizeof(cmd_16));
- cmd_16.opcode = (bp->b_flags & B_READ) ?
- READ_16 : WRITE_16;
- _lto8b(blkno, cmd_16.addr);
- _lto4b(nblks, cmd_16.length);
- cmdlen = sizeof(cmd_16);
- cmdp = (struct scsi_generic *)&cmd_16;
- }
+ ((nblks & 0xff) == nblks))
+ sd_cmd_rw6(xs, read, blkno, nblks);
+ else if (((blkno & 0xffffffff) == blkno) &&
+ ((nblks & 0xffff) == nblks))
+ sd_cmd_rw10(xs, read, blkno, nblks);
+ else if (((blkno & 0xffffffff) == blkno) &&
+ ((nblks & 0xffffffff) == nblks))
+ sd_cmd_rw12(xs, read, blkno, nblks);
+ else
+ sd_cmd_rw16(xs, read, blkno, nblks);
- /* Instrumentation. */
- disk_busy(&sd->sc_dk);
+ xs->flags |= (read ? SCSI_DATA_IN : SCSI_DATA_OUT);
+ xs->timeout = 60000;
+ xs->data = bp->b_data;
+ xs->datalen = bp->b_bcount;
- /*
- * Call the routine that chats with the adapter.
- * Note: we cannot sleep as we may be an interrupt
- */
- error = scsi_scsi_cmd(sc_link, cmdp, cmdlen,
- (u_char *)bp->b_data, bp->b_bcount,
- SCSI_RETRIES, 60000, bp, SCSI_NOSLEEP |
- ((bp->b_flags & B_READ) ? SCSI_DATA_IN : SCSI_DATA_OUT));
- switch (error) {
- case 0:
- /*
- * Mark the disk dirty so that the cache will be
- * flushed on close.
- */
- if ((bp->b_flags & B_READ) == 0)
- sd->flags |= SDF_DIRTY;
- timeout_del(&sd->sc_timeout);
- break;
- case EAGAIN:
- /*
- * The device can't start another i/o. Try again later.
- */
- dp->b_actf = bp;
- disk_unbusy(&sd->sc_dk, 0, 0);
- timeout_add(&sd->sc_timeout, 1);
- return;
- default:
- disk_unbusy(&sd->sc_dk, 0, 0);
- printf("%s: not queued, error %d\n",
- sd->sc_dev.dv_xname, error);
- break;
- }
+ xs->done = sd_buf_done;
+ xs->cookie = bp;
+
+ /* Instrumentation. */
+ disk_busy(&sc->sc_dk);
+ scsi_xs_exec(xs);
}
}
void
-sdrestart(void *v)
+sd_buf_done(struct scsi_xfer *xs)
{
- int s;
+ struct sd_softc *sc = xs->sc_link->device_softc;
+ struct buf *bp = xs->cookie;
- s = splbio();
- sdstart(v);
- splx(s);
-}
+ splassert(IPL_BIO);
-void
-sddone(struct scsi_xfer *xs)
-{
- struct sd_softc *sd = xs->sc_link->device_softc;
+ disk_unbusy(&sc->sc_dk, bp->b_bcount - xs->resid,
+ bp->b_flags & B_READ);
+
+ switch (xs->error) {
+ case XS_NOERROR:
+ bp->b_error = 0;
+ bp->b_resid = xs->resid;
+ break;
- if (xs->bp != NULL)
- disk_unbusy(&sd->sc_dk, (xs->bp->b_bcount - xs->bp->b_resid),
- (xs->bp->b_flags & B_READ));
+ case XS_NO_CCB:
+ /* The hardware is busy, requeue the buf and try it later. */
+ sd_buf_requeue(sc, bp);
+ scsi_xs_put(xs);
+ SET(sc->flags, SDF_WAITING); /* break out of sdstart loop */
+ timeout_add(&sc->sc_timeout, 1);
+ return;
+
+ default:
+ bp->b_error = EIO;
+ bp->b_flags |= B_ERROR;
+ bp->b_resid = bp->b_bcount;
+ break;
+ }
+
+ biodone(bp);
+ scsi_xs_put(xs);
+ sdstart(sc); /* restart io */
}
void
@@ -1081,27 +1087,6 @@ sd_shutdown(void *arg)
}
/*
- * Tell the device to map out a defective block
- */
-int
-sd_reassign_blocks(struct sd_softc *sd, u_long blkno)
-{
- struct scsi_reassign_blocks scsi_cmd;
- struct scsi_reassign_blocks_data rbdata;
-
- bzero(&scsi_cmd, sizeof(scsi_cmd));
- bzero(&rbdata, sizeof(rbdata));
- scsi_cmd.opcode = REASSIGN_BLOCKS;
-
- _lto2b(sizeof(rbdata.defect_descriptor[0]), rbdata.length);
- _lto4b(blkno, rbdata.defect_descriptor[0].dlbaddr);
-
- return scsi_scsi_cmd(sd->sc_link, (struct scsi_generic *)&scsi_cmd,
- sizeof(scsi_cmd), (u_char *)&rbdata, sizeof(rbdata), SCSI_RETRIES,
- 5000, NULL, SCSI_DATA_OUT);
-}
-
-/*
* Check Errors
*/
int
@@ -1186,7 +1171,6 @@ sdsize(dev_t dev)
}
/* #define SD_DUMP_NOT_TRUSTED if you just want to watch */
-static struct scsi_xfer sx;
static int sddoingadump;
/*
@@ -1196,7 +1180,7 @@ static int sddoingadump;
int
sddump(dev_t dev, daddr64_t blkno, caddr_t va, size_t size)
{
- struct sd_softc *sd; /* disk unit to do the I/O */
+ struct sd_softc *sc; /* disk unit to do the I/O */
struct disklabel *lp; /* disk's disklabel */
int unit, part;
int sectorsize; /* size of a disk sector */
@@ -1204,9 +1188,7 @@ sddump(dev_t dev, daddr64_t blkno, caddr_t va, size_t size)
daddr64_t sectoff; /* sector offset of partition */
int totwrt; /* total number of sectors left to write */
int nwrt; /* current number of sectors to write */
- struct scsi_rw_big cmd; /* write command */
struct scsi_xfer *xs; /* ... convenience */
- int retval;
/* Check if recursive dump; if so, punt. */
if (sddoingadump)
@@ -1219,7 +1201,7 @@ sddump(dev_t dev, daddr64_t blkno, caddr_t va, size_t size)
part = DISKPART(dev);
/* Check for acceptable drive number. */
- if (unit >= sd_cd.cd_ndevs || (sd = sd_cd.cd_devs[unit]) == NULL)
+ if (unit >= sd_cd.cd_ndevs || (sc = sd_cd.cd_devs[unit]) == NULL)
return ENXIO;
/*
@@ -1229,12 +1211,12 @@ sddump(dev_t dev, daddr64_t blkno, caddr_t va, size_t size)
*/
#if 0
/* Make sure it was initialized. */
- if ((sd->sc_link->flags & SDEV_MEDIA_LOADED) != SDEV_MEDIA_LOADED)
+ if ((sc->sc_link->flags & SDEV_MEDIA_LOADED) != SDEV_MEDIA_LOADED)
return ENXIO;
#endif
/* Convert to disk sectors. Request must be a multiple of size. */
- lp = sd->sc_dk.dk_label;
+ lp = sc->sc_dk.dk_label;
sectorsize = lp->d_secsize;
if ((size % sectorsize) != 0)
return EFAULT;
@@ -1251,43 +1233,25 @@ sddump(dev_t dev, daddr64_t blkno, caddr_t va, size_t size)
/* Offset block number to start of partition. */
blkno += sectoff;
- xs = &sx;
-
while (totwrt > 0) {
nwrt = totwrt; /* XXX */
+
#ifndef SD_DUMP_NOT_TRUSTED
- /*
- * Fill out the scsi command
- */
- bzero(&cmd, sizeof(cmd));
- cmd.opcode = WRITE_BIG;
- _lto4b(blkno, cmd.addr);
- _lto2b(nwrt, cmd.length);
- /*
- * Fill out the scsi_xfer structure
- * Note: we cannot sleep as we may be an interrupt
- * don't use scsi_scsi_cmd() as it may want
- * to wait for an xs.
- */
- bzero(xs, sizeof(sx));
- xs->flags |= SCSI_AUTOCONF | SCSI_DATA_OUT;
- xs->sc_link = sd->sc_link;
- xs->retries = SCSI_RETRIES;
- xs->timeout = 10000; /* 10000 millisecs for a disk ! */
- xs->cmd = (struct scsi_generic *)&cmd;
- xs->cmdlen = sizeof(cmd);
- xs->resid = nwrt * sectorsize;
- xs->error = XS_NOERROR;
- xs->bp = NULL;
+ xs = scsi_xs_get(sc->sc_link, SCSI_NOSLEEP);
+ if (xs == NULL)
+ return (ENOMEM);
+
+ xs->timeout = 10000;
+ xs->flags = SCSI_POLL | SCSI_NOSLEEP | SCSI_DATA_OUT;
xs->data = va;
xs->datalen = nwrt * sectorsize;
- /*
- * Pass all this info to the scsi driver.
- */
- retval = (*(sd->sc_link->adapter->scsi_cmd)) (xs);
- if (retval != COMPLETE)
- return ENXIO;
+ sd_cmd_rw10(xs, 0, blkno, nwrt); /* XXX */
+
+ scsi_xs_exec(xs);
+ if (xs->error != XS_NOERROR)
+ return (ENXIO);
+ scsi_xs_put(xs);
#else /* SD_DUMP_NOT_TRUSTED */
/* Let's just talk about this first... */
printf("sd%d: dump addr 0x%x, blk %d\n", unit, va, blkno);
@@ -1299,8 +1263,10 @@ sddump(dev_t dev, daddr64_t blkno, caddr_t va, size_t size)
blkno += nwrt;
va += sectorsize * nwrt;
}
+
sddoingadump = 0;
- return 0;
+
+ return (0);
}
/*
@@ -1469,14 +1435,17 @@ validate:
return (SDGP_RESULT_OK);
}
+void
+sd_flush_done(struct scsi_xfer *xs);
void
-sd_flush(struct sd_softc *sd, int flags)
+sd_flush(struct sd_softc *sc, int flags)
{
- struct scsi_link *sc_link = sd->sc_link;
- struct scsi_synchronize_cache cmd;
+ struct scsi_link *link = sc->sc_link;
+ struct scsi_xfer *xs;
+ struct scsi_synchronize_cache *cmd;
- if (sc_link->quirks & SDEV_NOSYNCCACHE)
+ if (link->quirks & SDEV_NOSYNCCACHE)
return;
/*
@@ -1485,15 +1454,40 @@ sd_flush(struct sd_softc *sd, int flags)
* that the command is not supported by the device.
*/
- bzero(&cmd, sizeof(cmd));
- cmd.opcode = SYNCHRONIZE_CACHE;
-
- if (scsi_scsi_cmd(sc_link, (struct scsi_generic *)&cmd, sizeof(cmd),
- NULL, 0, SCSI_RETRIES, 100000, NULL,
- flags | SCSI_IGNORE_ILLEGAL_REQUEST)) {
+ xs = scsi_xs_get(link, flags);
+ if (xs == NULL) {
+ SC_DEBUG(sc_link, SDEV_DB1, ("cache sync failed to get xs\n"));
+ return;
+ }
+
+ cmd = (struct scsi_synchronize_cache *)xs->cmd;
+ cmd->opcode = SYNCHRONIZE_CACHE;
+
+ xs->timeout = 100000;
+
+ xs->done = sd_flush_done;
+
+ do {
+ scsi_xs_exec(xs);
+ if (!ISSET(xs->flags, SCSI_POLL)) {
+ while (!ISSET(xs->flags, ITSDONE))
+ tsleep(xs, PRIBIO, "sdflush", 0);
+ }
+ } while (xs->status == XS_NO_CCB);
+
+ if (xs->error != XS_NOERROR)
SC_DEBUG(sc_link, SDEV_DB1, ("cache sync failed\n"));
- } else
- sd->flags &= ~SDF_DIRTY;
+ else
+ sc->flags &= ~SDF_DIRTY;
+
+ scsi_xs_put(xs);
+}
+
+void
+sd_flush_done(struct scsi_xfer *xs)
+{
+ if (!ISSET(xs->flags, SCSI_POLL))
+ wakeup_one(xs);
}
/*
@@ -1502,16 +1496,11 @@ sd_flush(struct sd_softc *sd, int flags)
void
sd_kill_buffers(struct sd_softc *sd)
{
- struct buf *dp, *bp;
- int s;
-
- s = splbio();
- for (dp = &sd->buf_queue; (bp = dp->b_actf) != NULL; ) {
- dp->b_actf = bp->b_actf;
+ struct buf *bp;
+ while ((bp = sd_buf_dequeue(sd)) != NULL) {
bp->b_error = ENXIO;
bp->b_flags |= B_ERROR;
biodone(bp);
}
- splx(s);
}
diff --git a/sys/scsi/sdvar.h b/sys/scsi/sdvar.h
index 7147b6f6f0e..d9b8d2837a6 100644
--- a/sys/scsi/sdvar.h
+++ b/sys/scsi/sdvar.h
@@ -1,4 +1,4 @@
-/* $OpenBSD: sdvar.h,v 1.23 2009/11/12 06:20:27 dlg Exp $ */
+/* $OpenBSD: sdvar.h,v 1.24 2009/12/01 01:40:02 dlg Exp $ */
/* $NetBSD: sdvar.h,v 1.7 1998/08/17 00:49:03 mycroft Exp $ */
/*-
@@ -49,10 +49,10 @@
#ifdef _KERNEL
struct sd_softc {
- struct device sc_dev;
- struct disk sc_dk;
+ struct device sc_dev;
+ struct disk sc_dk;
- int flags;
+ int flags;
#define SDF_LOCKED 0x01
#define SDF_WANTED 0x02
#define SDF_WLABEL 0x04 /* label is writable */
@@ -60,7 +60,8 @@ struct sd_softc {
#define SDF_ANCIENT 0x10 /* disk is ancient; for minphys */
#define SDF_DIRTY 0x20 /* disk is dirty; needs cache flush */
#define SDF_DYING 0x40 /* dying, when deactivated */
- struct scsi_link *sc_link; /* contains our targ, lun, etc. */
+#define SDF_WAITING 0x80 /* bus is busy, try again later */
+ struct scsi_link *sc_link; /* contains our targ, lun, etc. */
struct disk_parms {
u_long heads; /* number of heads */
u_long cyls; /* number of cylinders */
@@ -69,9 +70,11 @@ struct sd_softc {
u_long rot_rate; /* rotational rate, in RPM */
daddr64_t disksize; /* total number sectors */
} params;
- struct buf buf_queue;
+ struct mutex sc_buf_mtx;
+ struct buf sc_buf_queue;
void *sc_sdhook; /* our shutdown hook */
struct timeout sc_timeout;
+
};
#define SDGP_RESULT_OK 0 /* parameters obtained */