summaryrefslogtreecommitdiff
path: root/sys/scsi/st.c
diff options
context:
space:
mode:
authorKenneth R Westerback <krw@cvs.openbsd.org>2008-05-12 22:14:47 +0000
committerKenneth R Westerback <krw@cvs.openbsd.org>2008-05-12 22:14:47 +0000
commit2ef1d339c24719cc656bc46412149c9ed99b9648 (patch)
treee7fc2dc3ee3d32887a308c8a596ee29e69dd2b89 /sys/scsi/st.c
parent21d9b3d5ea7249898f9f313c5edecb19961b8000 (diff)
Fix device reference counting. Now that we try to support detachable
tape drives it is nice not to crash if one is detached. Basically use a consistant mechanism modelled on sd to lookup devices and do the device reference increments and decrements. Problem reported (PR#5811) and fix tested by Jozef Hatala. Still some corner cases Jozef is looking for but we'll fix those as discovered.
Diffstat (limited to 'sys/scsi/st.c')
-rw-r--r--sys/scsi/st.c155
1 files changed, 102 insertions, 53 deletions
diff --git a/sys/scsi/st.c b/sys/scsi/st.c
index 24832f045a8..aa13a5b16d8 100644
--- a/sys/scsi/st.c
+++ b/sys/scsi/st.c
@@ -1,4 +1,4 @@
-/* $OpenBSD: st.c,v 1.81 2008/05/09 00:25:41 krw Exp $ */
+/* $OpenBSD: st.c,v 1.82 2008/05/12 22:14:46 krw Exp $ */
/* $NetBSD: st.c,v 1.71 1997/02/21 23:03:49 thorpej Exp $ */
/*
@@ -287,6 +287,8 @@ struct scsi_device st_switch = {
ST_FIXEDBLOCKS | ST_READONLY | ST_FM_WRITTEN | \
ST_2FM_AT_EOD | ST_PER_ACTION)
+#define stlookup(unit) (struct st_softc *)device_lookup(&st_cd, (unit))
+
const struct scsi_inquiry_pattern st_patterns[] = {
{T_SEQUENTIAL, T_REMOV,
"", "", ""},
@@ -457,30 +459,27 @@ stopen(dev_t dev, int flags, int fmt, struct proc *p)
{
struct scsi_link *sc_link;
struct st_softc *st;
- int error = 0, unit;
+ int error = 0;
- unit = STUNIT(dev);
- if (unit >= st_cd.cd_ndevs)
- return (ENXIO);
- st = st_cd.cd_devs[unit];
- if (!st)
- return (ENXIO);
- if ((st->flags & ST_DYING)) {
- device_unref(&st->sc_dev);
+ st = stlookup(STUNIT(dev));
+ if (st == NULL)
return (ENXIO);
+ if (st->flags & ST_DYING) {
+ error = ENXIO;
+ goto done;
}
-
sc_link = st->sc_link;
SC_DEBUG(sc_link, SDEV_DB1, ("open: dev=0x%x (unit %d (of %d))\n", dev,
- unit, st_cd.cd_ndevs));
+ STUNIT(dev), st_cd.cd_ndevs));
/*
* Tape is an exclusive media. Only one open at a time.
*/
if (sc_link->flags & SDEV_OPEN) {
SC_DEBUG(sc_link, SDEV_DB4, ("already open\n"));
- return (EBUSY);
+ error = EBUSY;
+ goto done;
}
/* Use st_interpret_sense() now. */
@@ -502,14 +501,14 @@ stopen(dev_t dev, int flags, int fmt, struct proc *p)
if (error) {
sc_link->flags &= ~SDEV_OPEN;
- return (error);
+ goto done;
}
if ((st->flags & ST_MOUNTED) == 0) {
error = st_mount_tape(dev, flags);
if (error) {
sc_link->flags &= ~SDEV_OPEN;
- return (error);
+ goto done;
}
}
@@ -521,8 +520,10 @@ stopen(dev_t dev, int flags, int fmt, struct proc *p)
if ((flags & O_ACCMODE) == FWRITE)
st->flags |= ST_WRITTEN;
+done:
SC_DEBUG(sc_link, SDEV_DB2, ("open complete\n"));
- return (0);
+ device_unref(&st->sc_dev);
+ return (error);
}
/*
@@ -532,15 +533,24 @@ stopen(dev_t dev, int flags, int fmt, struct proc *p)
int
stclose(dev_t dev, int flags, int mode, struct proc *p)
{
- struct st_softc *st = st_cd.cd_devs[STUNIT(dev)];
+ struct scsi_link *sc_link;
+ struct st_softc *st;
+ int error;
- SC_DEBUG(st->sc_link, SDEV_DB1, ("closing\n"));
- if (st->flags & ST_DYING) {
- device_unref(&st->sc_dev);
+ st = stlookup(STUNIT(dev));
+ if (st == NULL)
return (ENXIO);
+ if (st->flags & ST_DYING) {
+ error = ENXIO;
+ goto done;
}
+ sc_link = st->sc_link;
+
+ SC_DEBUG(sc_link, SDEV_DB1, ("closing\n"));
+
if ((st->flags & (ST_WRITTEN | ST_FM_WRITTEN)) == ST_WRITTEN)
st_write_filemarks(st, 1, 0);
+
switch (STMODE(dev)) {
case 0: /* normal */
st_unmount(st, NOEJECT, DOREWIND);
@@ -550,17 +560,19 @@ stclose(dev_t dev, int flags, int mode, struct proc *p)
break;
case 1: /* no rewind */
/* leave mounted unless media seems to have been removed */
- if (!(st->sc_link->flags & SDEV_MEDIA_LOADED))
+ if (!(sc_link->flags & SDEV_MEDIA_LOADED))
st_unmount(st, NOEJECT, NOREWIND);
break;
case 2: /* rewind, eject */
st_unmount(st, EJECT, DOREWIND);
break;
}
- st->sc_link->flags &= ~SDEV_OPEN;
+ sc_link->flags &= ~SDEV_OPEN;
timeout_del(&st->sc_timeout);
- return 0;
+done:
+ device_unref(&st->sc_dev);
+ return (error);
}
/*
@@ -571,28 +583,32 @@ stclose(dev_t dev, int flags, int mode, struct proc *p)
int
st_mount_tape(dev_t dev, int flags)
{
- int unit;
- u_int mode;
struct st_softc *st;
struct scsi_link *sc_link;
int error = 0;
- unit = STUNIT(dev);
- mode = STMODE(dev);
- st = st_cd.cd_devs[unit];
+ st = stlookup(STUNIT(dev));
+ if (st == NULL)
+ return (ENXIO);
+ if (st->flags & ST_DYING) {
+ error = ENXIO;
+ goto done;
+ }
sc_link = st->sc_link;
+ SC_DEBUG(sc_link, SDEV_DB1, ("mounting\n"));
+
if (st->flags & ST_MOUNTED)
- return 0;
+ goto done;
- SC_DEBUG(sc_link, SDEV_DB1, ("mounting\n"));
st->quirks = st->drive_quirks | st->modes.quirks;
/*
* If the media is new, then make sure we give it a chance to
* to do a 'load' instruction. (We assume it is new.)
*/
if ((error = st_load(st, LD_LOAD, 0)) != 0)
- return error;
+ goto done;
+
/*
* Throw another dummy instruction to catch
* 'Unit attention' errors. Some drives appear to give
@@ -614,9 +630,8 @@ st_mount_tape(dev_t dev, int flags)
* loads: blkmin, blkmax
*/
if (!(sc_link->flags & SDEV_ATAPI) &&
- (error = st_read_block_limits(st, 0)) != 0) {
- return error;
- }
+ (error = st_read_block_limits(st, 0)) != 0)
+ goto done;
/*
* Load the media dependent parameters
@@ -625,7 +640,7 @@ st_mount_tape(dev_t dev, int flags)
* If not you may need the "quirk" above.
*/
if ((error = st_mode_sense(st, 0)) != 0)
- return error;
+ goto done;
/*
* If we have gained a permanent density from somewhere,
@@ -648,18 +663,20 @@ st_mount_tape(dev_t dev, int flags)
st->flags |= ST_FIXEDBLOCKS;
} else {
if ((error = st_decide_mode(st, FALSE)) != 0)
- return error;
+ goto done;
}
if ((error = st_mode_select(st, 0)) != 0) {
printf("%s: cannot set selected mode\n", st->sc_dev.dv_xname);
- return error;
+ goto done;
}
scsi_prevent(sc_link, PR_PREVENT,
SCSI_IGNORE_ILLEGAL_REQUEST | SCSI_IGNORE_NOT_READY);
st->flags |= ST_MOUNTED;
sc_link->flags |= SDEV_MEDIA_LOADED; /* move earlier? */
- return 0;
+done:
+ device_unref(&st->sc_dev);
+ return (error);
}
/*
@@ -812,11 +829,21 @@ done:
void
ststrategy(struct buf *bp)
{
- struct st_softc *st = st_cd.cd_devs[STUNIT(bp->b_dev)];
+ struct scsi_link *sc_link;
+ struct st_softc *st;
struct buf *dp;
- int s;
+ int error, s;
+
+ st = stlookup(STUNIT(bp->b_dev));
+ if (st == NULL)
+ return;
+ if (st->flags & ST_DYING) {
+ error = ENXIO;
+ goto done;
+ }
+ sc_link = st->sc_link;
- SC_DEBUG(st->sc_link, SDEV_DB2, ("ststrategy: %ld bytes @ blk %d\n",
+ SC_DEBUG(sc_link, SDEV_DB2, ("ststrategy: %ld bytes @ blk %d\n",
bp->b_bcount, bp->b_blkno));
if (st->flags & ST_DYING) {
@@ -870,10 +897,12 @@ ststrategy(struct buf *bp)
ststart(st);
splx(s);
+ device_unref(&st->sc_dev);
return;
bad:
bp->b_flags |= B_ERROR;
done:
+ device_unref(&st->sc_dev);
/*
* Correctly set the buf to indicate a completed xfer
*/
@@ -1063,7 +1092,16 @@ strestart(void *v)
int
stread(dev_t dev, struct uio *uio, int iomode)
{
- struct st_softc *st = st_cd.cd_devs[STUNIT(dev)];
+ struct st_softc *st;
+
+ st = stlookup(STUNIT(dev));
+ if (st == NULL)
+ return (ENXIO);
+
+ if (st->flags & ST_DYING) {
+ device_unref(&st->sc_dev);
+ return (ENXIO);
+ }
return (physio(ststrategy, NULL, dev, B_READ,
st->sc_link->adapter->scsi_minphys, uio));
@@ -1072,7 +1110,16 @@ stread(dev_t dev, struct uio *uio, int iomode)
int
stwrite(dev_t dev, struct uio *uio, int iomode)
{
- struct st_softc *st = st_cd.cd_devs[STUNIT(dev)];
+ struct st_softc *st;
+
+ st = stlookup(STUNIT(dev));
+ if (st == NULL)
+ return (ENXIO);
+
+ if (st->flags & ST_DYING) {
+ device_unref(&st->sc_dev);
+ return (ENXIO);
+ }
return (physio(ststrategy, NULL, dev, B_WRITE,
st->sc_link->adapter->scsi_minphys, uio));
@@ -1086,9 +1133,8 @@ int
stioctl(dev_t dev, u_long cmd, caddr_t arg, int flag, struct proc *p)
{
int error = 0;
- int unit;
int nmarks;
- int flags;
+ int flags = 0;
struct st_softc *st;
int hold_blksize;
u_int8_t hold_density;
@@ -1098,13 +1144,13 @@ stioctl(dev_t dev, u_long cmd, caddr_t arg, int flag, struct proc *p)
/*
* Find the device that the user is talking about
*/
- flags = 0; /* give error messages, act on errors etc. */
- unit = STUNIT(dev);
- st = st_cd.cd_devs[unit];
+ st = stlookup(STUNIT(dev));
+ if (st == NULL)
+ return (ENXIO);
if (st->flags & ST_DYING) {
- device_unref(&st->sc_dev);
- return (ENXIO);
+ error = ENXIO;
+ goto done;
}
hold_blksize = st->blksize;
@@ -1251,7 +1297,8 @@ stioctl(dev_t dev, u_long cmd, caddr_t arg, int flag, struct proc *p)
error = scsi_do_ioctl(st->sc_link, dev, cmd, arg, flag, p);
break;
}
- return error;
+ goto done;
+
try_new_value:
/*
* Check that the mode being asked for is aggreeable to the
@@ -1265,7 +1312,7 @@ try_new_value:
st->flags |= ST_FIXEDBLOCKS;
else
st->flags &= ~ST_FIXEDBLOCKS;
- return error;
+ goto done;
}
/*
* As the drive liked it, if we are setting a new default,
@@ -1282,7 +1329,9 @@ try_new_value:
break;
}
- return 0;
+done:
+ device_unref(&st->sc_dev);
+ return (error);
}
/*