Alexander Bluhm
2016-01-29 14:22:51 UTC
Hi,
I have seen some use after free panics when unplugging USB umass
sticks that were still in use. In sdopen() the scsi link pointer
is taken from the scsi disk struct. While the scsi disk memory is
refcounted by autoconf, the scsi link may be detached and freed
after every sleep.
The solution is to check wether the disk is dying after every sleep.
The SDF_DYING flag is set before the scsi bus and scsi disk get
detached, so without this flag the link must be valid.
If a function gets a scsi transfer, the link and disk are always
valid. scsi_detach_lun() removes and waits for the scsi transfers
as the first step. To make the code consistent I use the variable
name sc_link for links from the sc. There for every access after
sleep the disk is checked. The link from a transfer is called link
and can be used without check.
Althogh it adds a lot of code, I think it has to be done to remove
the races.
ok?
bluhm
Index: scsi/sd.c
===================================================================
RCS file: /data/mirror/openbsd/cvs/src/sys/scsi/sd.c,v
retrieving revision 1.261
diff -u -p -r1.261 sd.c
--- scsi/sd.c 7 Jun 2015 19:13:27 -0000 1.261
+++ scsi/sd.c 28 Jan 2016 22:54:41 -0000
@@ -199,7 +199,7 @@ sdattach(struct device *parent, struct d
&sc->sc_xsh);
/* Spin up non-UMASS devices ready or not. */
- if ((sc->sc_link->flags & SDEV_UMASS) == 0)
+ if ((sc_link->flags & SDEV_UMASS) == 0)
scsi_start(sc_link, SSS_START, sd_autoconf);
/*
@@ -341,12 +341,12 @@ sdopen(dev_t dev, int flag, int fmt, str
sc = sdlookup(unit);
if (sc == NULL)
return (ENXIO);
- sc_link = sc->sc_link;
-
if (sc->flags & SDF_DYING) {
device_unref(&sc->sc_dev);
return (ENXIO);
}
+ sc_link = sc->sc_link;
+
if (ISSET(flag, FWRITE) && ISSET(sc_link->flags, SDEV_READONLY)) {
device_unref(&sc->sc_dev);
return (EACCES);
@@ -366,6 +366,8 @@ sdopen(dev_t dev, int flag, int fmt, str
* If any partition is open, but the disk has been invalidated,
* disallow further opens of non-raw partition.
*/
+ if (sc->flags & SDF_DYING)
+ goto die;
if ((sc_link->flags & SDEV_MEDIA_LOADED) == 0) {
if (rawopen)
goto out;
@@ -374,7 +376,9 @@ sdopen(dev_t dev, int flag, int fmt, str
}
} else {
/* Spin up non-UMASS devices ready or not. */
- if ((sc->sc_link->flags & SDEV_UMASS) == 0)
+ if (sc->flags & SDF_DYING)
+ goto die;
+ if ((sc_link->flags & SDEV_UMASS) == 0)
scsi_start(sc_link, SSS_START, (rawopen ? SCSI_SILENT :
0) | SCSI_IGNORE_ILLEGAL_REQUEST |
SCSI_IGNORE_MEDIA_CHANGE);
@@ -385,6 +389,8 @@ sdopen(dev_t dev, int flag, int fmt, str
* device returns "Initialization command required." and causes
* a loop of scsi_start() calls.
*/
+ if (sc->flags & SDF_DYING)
+ goto die;
sc_link->flags |= SDEV_OPEN;
/*
@@ -399,10 +405,11 @@ sdopen(dev_t dev, int flag, int fmt, str
}
/* Check that it is still responding and ok. */
+ if (sc->flags & SDF_DYING)
+ goto die;
error = scsi_test_unit_ready(sc_link,
TEST_READY_RETRIES, SCSI_SILENT |
SCSI_IGNORE_ILLEGAL_REQUEST | SCSI_IGNORE_MEDIA_CHANGE);
-
if (error) {
if (rawopen) {
error = 0;
@@ -412,9 +419,13 @@ sdopen(dev_t dev, int flag, int fmt, str
}
/* Load the physical device parameters. */
+ if (sc->flags & SDF_DYING)
+ goto die;
sc_link->flags |= SDEV_MEDIA_LOADED;
if (sd_get_parms(sc, &sc->params, (rawopen ? SCSI_SILENT : 0))
== SDGP_RESULT_OFFLINE) {
+ if (sc->flags & SDF_DYING)
+ goto die;
sc_link->flags &= ~SDEV_MEDIA_LOADED;
error = ENXIO;
goto bad;
@@ -429,25 +440,34 @@ sdopen(dev_t dev, int flag, int fmt, str
SC_DEBUG(sc_link, SDEV_DB3, ("Disklabel loaded\n"));
}
-out:
+ out:
if ((error = disk_openpart(&sc->sc_dk, part, fmt, 1)) != 0)
goto bad;
SC_DEBUG(sc_link, SDEV_DB3, ("open complete\n"));
/* It's OK to fall through because dk_openmask is now non-zero. */
-bad:
+ bad:
if (sc->sc_dk.dk_openmask == 0) {
- if ((sc->sc_link->flags & SDEV_REMOVABLE) != 0)
+ if (sc->flags & SDF_DYING)
+ goto die;
+ if ((sc_link->flags & SDEV_REMOVABLE) != 0)
scsi_prevent(sc_link, PR_ALLOW, SCSI_SILENT |
SCSI_IGNORE_ILLEGAL_REQUEST |
SCSI_IGNORE_MEDIA_CHANGE);
+ if (sc->flags & SDF_DYING)
+ goto die;
sc_link->flags &= ~(SDEV_OPEN | SDEV_MEDIA_LOADED);
}
disk_unlock(&sc->sc_dk);
device_unref(&sc->sc_dev);
return (error);
+
+ die:
+ disk_unlock(&sc->sc_dk);
+ device_unref(&sc->sc_dev);
+ return (ENXIO);
}
/*
@@ -457,6 +477,7 @@ bad:
int
sdclose(dev_t dev, int flag, int fmt, struct proc *p)
{
+ struct scsi_link *sc_link;
struct sd_softc *sc;
int part = DISKPART(dev);
@@ -467,6 +488,7 @@ sdclose(dev_t dev, int flag, int fmt, st
device_unref(&sc->sc_dev);
return (ENXIO);
}
+ sc_link = sc->sc_link;
disk_lock_nointr(&sc->sc_dk);
@@ -476,15 +498,21 @@ sdclose(dev_t dev, int flag, int fmt, st
if ((sc->flags & SDF_DIRTY) != 0)
sd_flush(sc, 0);
- if ((sc->sc_link->flags & SDEV_REMOVABLE) != 0)
- scsi_prevent(sc->sc_link, PR_ALLOW,
+ if (sc->flags & SDF_DYING)
+ goto die;
+ if ((sc_link->flags & SDEV_REMOVABLE) != 0)
+ scsi_prevent(sc_link, PR_ALLOW,
SCSI_IGNORE_ILLEGAL_REQUEST |
SCSI_IGNORE_NOT_READY | SCSI_SILENT);
- sc->sc_link->flags &= ~(SDEV_OPEN | SDEV_MEDIA_LOADED);
+ if (sc->flags & SDF_DYING)
+ goto die;
+ sc_link->flags &= ~(SDEV_OPEN | SDEV_MEDIA_LOADED);
- if (sc->sc_link->flags & SDEV_EJECTING) {
- scsi_start(sc->sc_link, SSS_STOP|SSS_LOEJ, 0);
- sc->sc_link->flags &= ~SDEV_EJECTING;
+ if (sc_link->flags & SDEV_EJECTING) {
+ scsi_start(sc_link, SSS_STOP|SSS_LOEJ, 0);
+ if (sc->flags & SDF_DYING)
+ goto die;
+ sc_link->flags &= ~SDEV_EJECTING;
}
timeout_del(&sc->sc_timeout);
@@ -494,6 +522,11 @@ sdclose(dev_t dev, int flag, int fmt, st
disk_unlock(&sc->sc_dk);
device_unref(&sc->sc_dev);
return 0;
+
+ die:
+ disk_unlock(&sc->sc_dk);
+ device_unref(&sc->sc_dev);
+ return (ENXIO);
}
/*
@@ -504,6 +537,7 @@ sdclose(dev_t dev, int flag, int fmt, st
void
sdstrategy(struct buf *bp)
{
+ struct scsi_link *sc_link;
struct sd_softc *sc;
int s;
@@ -516,14 +550,15 @@ sdstrategy(struct buf *bp)
bp->b_error = ENXIO;
goto bad;
}
+ sc_link = sc->sc_link;
- SC_DEBUG(sc->sc_link, SDEV_DB2, ("sdstrategy: %ld bytes @ blk %lld\n",
+ SC_DEBUG(sc_link, SDEV_DB2, ("sdstrategy: %ld bytes @ blk %lld\n",
bp->b_bcount, (long long)bp->b_blkno));
/*
* If the device has been made invalid, error out
*/
- if ((sc->sc_link->flags & SDEV_MEDIA_LOADED) == 0) {
- if (sc->sc_link->flags & SDEV_OPEN)
+ if ((sc_link->flags & SDEV_MEDIA_LOADED) == 0) {
+ if (sc_link->flags & SDEV_OPEN)
bp->b_error = EIO;
else
bp->b_error = ENODEV;
@@ -770,12 +805,18 @@ retry:
void
sdminphys(struct buf *bp)
{
+ struct scsi_link *sc_link;
struct sd_softc *sc;
long max;
sc = sdlookup(DISKUNIT(bp->b_dev));
if (sc == NULL)
return; /* XXX - right way to fail this? */
+ if (sc->flags & SDF_DYING) {
+ device_unref(&sc->sc_dev);
+ return;
+ }
+ sc_link = sc->sc_link;
/*
* If the device is ancient, we want to make sure that
@@ -795,7 +836,7 @@ sdminphys(struct buf *bp)
bp->b_bcount = max;
}
- (*sc->sc_link->adapter->scsi_minphys)(bp, sc->sc_link);
+ (*sc_link->adapter->scsi_minphys)(bp, sc_link);
device_unref(&sc->sc_dev);
}
@@ -819,6 +860,7 @@ sdwrite(dev_t dev, struct uio *uio, int
int
sdioctl(dev_t dev, u_long cmd, caddr_t addr, int flag, struct proc *p)
{
+ struct scsi_link *sc_link;
struct sd_softc *sc;
struct disklabel *lp;
int error = 0;
@@ -831,13 +873,14 @@ sdioctl(dev_t dev, u_long cmd, caddr_t a
device_unref(&sc->sc_dev);
return (ENXIO);
}
+ sc_link = sc->sc_link;
- SC_DEBUG(sc->sc_link, SDEV_DB2, ("sdioctl 0x%lx\n", cmd));
+ SC_DEBUG(sc_link, SDEV_DB2, ("sdioctl 0x%lx\n", cmd));
/*
* If the device is not valid.. abandon ship
*/
- if ((sc->sc_link->flags & SDEV_MEDIA_LOADED) == 0) {
+ if ((sc_link->flags & SDEV_MEDIA_LOADED) == 0) {
switch (cmd) {
case DIOCLOCK:
case DIOCEJECT:
@@ -848,7 +891,7 @@ sdioctl(dev_t dev, u_long cmd, caddr_t a
break;
/* FALLTHROUGH */
default:
- if ((sc->sc_link->flags & SDEV_OPEN) == 0) {
+ if ((sc_link->flags & SDEV_OPEN) == 0) {
error = ENODEV;
goto exit;
} else {
@@ -902,7 +945,7 @@ sdioctl(dev_t dev, u_long cmd, caddr_t a
goto exit;
case DIOCLOCK:
- error = scsi_prevent(sc->sc_link,
+ error = scsi_prevent(sc_link,
(*(int *)addr) ? PR_PREVENT : PR_ALLOW, 0);
goto exit;
@@ -913,15 +956,15 @@ sdioctl(dev_t dev, u_long cmd, caddr_t a
}
/* FALLTHROUGH */
case DIOCEJECT:
- if ((sc->sc_link->flags & SDEV_REMOVABLE) == 0) {
+ if ((sc_link->flags & SDEV_REMOVABLE) == 0) {
error = ENOTTY;
goto exit;
}
- sc->sc_link->flags |= SDEV_EJECTING;
+ sc_link->flags |= SDEV_EJECTING;
goto exit;
case DIOCINQ:
- error = scsi_do_ioctl(sc->sc_link, cmd, addr, flag);
+ error = scsi_do_ioctl(sc_link, cmd, addr, flag);
if (error == ENOTTY)
error = sd_ioctl_inquiry(sc,
(struct dk_inquiry *)addr);
@@ -942,7 +985,7 @@ sdioctl(dev_t dev, u_long cmd, caddr_t a
error = ENOTTY;
goto exit;
}
- error = scsi_do_ioctl(sc->sc_link, cmd, addr, flag);
+ error = scsi_do_ioctl(sc_link, cmd, addr, flag);
}
exit:
@@ -953,21 +996,27 @@ sdioctl(dev_t dev, u_long cmd, caddr_t a
int
sd_ioctl_inquiry(struct sd_softc *sc, struct dk_inquiry *di)
{
+ struct scsi_link *sc_link;
struct scsi_vpd_serial *vpd;
vpd = dma_alloc(sizeof(*vpd), PR_WAITOK | PR_ZERO);
+ if (sc->flags & SDF_DYING) {
+ dma_free(vpd, sizeof(*vpd));
+ return (ENXIO);
+ }
+ sc_link = sc->sc_link;
+
bzero(di, sizeof(struct dk_inquiry));
- scsi_strvis(di->vendor, sc->sc_link->inqdata.vendor,
- sizeof(sc->sc_link->inqdata.vendor));
- scsi_strvis(di->product, sc->sc_link->inqdata.product,
- sizeof(sc->sc_link->inqdata.product));
- scsi_strvis(di->revision, sc->sc_link->inqdata.revision,
- sizeof(sc->sc_link->inqdata.revision));
+ scsi_strvis(di->vendor, sc_link->inqdata.vendor,
+ sizeof(sc_link->inqdata.vendor));
+ scsi_strvis(di->product, sc_link->inqdata.product,
+ sizeof(sc_link->inqdata.product));
+ scsi_strvis(di->revision, sc_link->inqdata.revision,
+ sizeof(sc_link->inqdata.revision));
/* the serial vpd page is optional */
- if (scsi_inquire_vpd(sc->sc_link, vpd, sizeof(*vpd),
- SI_PG_SERIAL, 0) == 0)
+ if (scsi_inquire_vpd(sc_link, vpd, sizeof(*vpd), SI_PG_SERIAL, 0) == 0)
scsi_strvis(di->serial, vpd->serial, sizeof(vpd->serial));
else
strlcpy(di->serial, "(unknown)", sizeof(vpd->serial));
@@ -979,17 +1028,22 @@ sd_ioctl_inquiry(struct sd_softc *sc, st
int
sd_ioctl_cache(struct sd_softc *sc, long cmd, struct dk_cache *dkc)
{
+ struct scsi_link *sc_link;
union scsi_mode_sense_buf *buf;
struct page_caching_mode *mode = NULL;
u_int wrcache, rdcache;
int big;
int rv;
- if (ISSET(sc->sc_link->flags, SDEV_UMASS))
+ if (sc->flags & SDF_DYING)
+ return (ENXIO);
+ sc_link = sc->sc_link;
+
+ if (ISSET(sc_link->flags, SDEV_UMASS))
return (EOPNOTSUPP);
/* see if the adapter has special handling */
- rv = scsi_do_ioctl(sc->sc_link, cmd, (caddr_t)dkc, 0);
+ rv = scsi_do_ioctl(sc_link, cmd, (caddr_t)dkc, 0);
if (rv != ENOTTY)
return (rv);
@@ -997,7 +1051,9 @@ sd_ioctl_cache(struct sd_softc *sc, long
if (buf == NULL)
return (ENOMEM);
- rv = scsi_do_mode_sense(sc->sc_link, PAGE_CACHING_MODE,
+ if (sc->flags & SDF_DYING)
+ goto die;
+ rv = scsi_do_mode_sense(sc_link, PAGE_CACHING_MODE,
buf, (void **)&mode, NULL, NULL, NULL,
sizeof(*mode) - 4, scsi_autoconf | SCSI_SILENT, &big);
if (rv != 0)
@@ -1031,19 +1087,25 @@ sd_ioctl_cache(struct sd_softc *sc, long
else
SET(mode->flags, PG_CACHE_FL_RCD);
+ if (sc->flags & SDF_DYING)
+ goto die;
if (big) {
- rv = scsi_mode_select_big(sc->sc_link, SMS_PF,
+ rv = scsi_mode_select_big(sc_link, SMS_PF,
&buf->hdr_big, scsi_autoconf | SCSI_SILENT, 20000);
} else {
- rv = scsi_mode_select(sc->sc_link, SMS_PF,
+ rv = scsi_mode_select(sc_link, SMS_PF,
&buf->hdr, scsi_autoconf | SCSI_SILENT, 20000);
}
break;
}
-done:
+ done:
dma_free(buf, sizeof(*buf));
return (rv);
+
+ die:
+ dma_free(buf, sizeof(*buf));
+ return (ENXIO);
}
/*
@@ -1053,10 +1115,15 @@ int
sdgetdisklabel(dev_t dev, struct sd_softc *sc, struct disklabel *lp,
int spoofonly)
{
+ struct scsi_link *sc_link;
size_t len;
char packname[sizeof(lp->d_packname) + 1];
char product[17], vendor[9];
+ if (sc->flags & SDF_DYING)
+ return (ENXIO);
+ sc_link = sc->sc_link;
+
bzero(lp, sizeof(struct disklabel));
lp->d_secsize = sc->params.secsize;
@@ -1070,7 +1137,7 @@ sdgetdisklabel(dev_t dev, struct sd_soft
}
lp->d_type = DTYPE_SCSI;
- if ((sc->sc_link->inqdata.device & SID_TYPE) == T_OPTICAL)
+ if ((sc_link->inqdata.device & SID_TYPE) == T_OPTICAL)
strncpy(lp->d_typename, "SCSI optical",
sizeof(lp->d_typename));
else
@@ -1082,8 +1149,8 @@ sdgetdisklabel(dev_t dev, struct sd_soft
* then leave out '<vendor> ' and use only as much of '<product>' as
* does fit.
*/
- viscpy(vendor, sc->sc_link->inqdata.vendor, 8);
- viscpy(product, sc->sc_link->inqdata.product, 16);
+ viscpy(vendor, sc_link->inqdata.vendor, 8);
+ viscpy(product, sc_link->inqdata.product, 16);
len = snprintf(packname, sizeof(packname), "%s %s", vendor, product);
if (len > sizeof(lp->d_packname)) {
strlcpy(packname, product, sizeof(packname));
@@ -1123,8 +1190,7 @@ int
sd_interpret_sense(struct scsi_xfer *xs)
{
struct scsi_sense_data *sense = &xs->sense;
- struct scsi_link *sc_link = xs->sc_link;
- struct sd_softc *sc = sc_link->device_softc;
+ struct scsi_link *link = xs->sc_link;
u_int8_t serr = sense->error_code & SSD_ERRCODE;
int retval;
@@ -1132,7 +1198,7 @@ sd_interpret_sense(struct scsi_xfer *xs)
* Let the generic code handle everything except a few categories of
* LUN not ready errors on open devices.
*/
- if (((sc_link->flags & SDEV_OPEN) == 0) ||
+ if (((link->flags & SDEV_OPEN) == 0) ||
(serr != SSD_ERRCODE_CURRENT && serr != SSD_ERRCODE_DEFERRED) ||
((sense->flags & SSD_KEY) != SKEY_NOT_READY) ||
(sense->extra_len < 6))
@@ -1143,13 +1209,13 @@ sd_interpret_sense(struct scsi_xfer *xs)
switch (ASC_ASCQ(sense)) {
case SENSE_NOT_READY_BECOMING_READY:
- SC_DEBUG(sc_link, SDEV_DB1, ("becoming ready.\n"));
+ SC_DEBUG(link, SDEV_DB1, ("becoming ready.\n"));
retval = scsi_delay(xs, 5);
break;
case SENSE_NOT_READY_INIT_REQUIRED:
- SC_DEBUG(sc_link, SDEV_DB1, ("spinning up\n"));
- retval = scsi_start(sc->sc_link, SSS_START,
+ SC_DEBUG(link, SDEV_DB1, ("spinning up\n"));
+ retval = scsi_start(link, SSS_START,
SCSI_IGNORE_ILLEGAL_REQUEST | SCSI_NOSLEEP);
if (retval == 0)
retval = ERESTART;
@@ -1157,7 +1223,7 @@ sd_interpret_sense(struct scsi_xfer *xs)
/* Can't issue the command. Fall back on a delay. */
retval = scsi_delay(xs, 5);
else
- SC_DEBUG(sc_link, SDEV_DB1, ("spin up failed (%#x)\n",
+ SC_DEBUG(link, SDEV_DB1, ("spin up failed (%#x)\n",
retval));
break;
@@ -1180,32 +1246,34 @@ sdsize(dev_t dev)
sc = sdlookup(DISKUNIT(dev));
if (sc == NULL)
return -1;
- if (sc->flags & SDF_DYING) {
- size = -1;
- goto exit;
- }
+ if (sc->flags & SDF_DYING)
+ goto die;
part = DISKPART(dev);
omask = sc->sc_dk.dk_openmask & (1 << part);
- if (omask == 0 && sdopen(dev, 0, S_IFBLK, NULL) != 0) {
- size = -1;
- goto exit;
- }
+ if (omask == 0 && sdopen(dev, 0, S_IFBLK, NULL) != 0)
+ goto die;
lp = sc->sc_dk.dk_label;
+ if (sc->flags & SDF_DYING)
+ goto die;
if ((sc->sc_link->flags & SDEV_MEDIA_LOADED) == 0)
size = -1;
else if (lp->d_partitions[part].p_fstype != FS_SWAP)
size = -1;
else
size = DL_SECTOBLK(lp, DL_GETPSIZE(&lp->d_partitions[part]));
+
if (omask == 0 && sdclose(dev, 0, S_IFBLK, NULL) != 0)
- size = -1;
+ goto die;
- exit:
device_unref(&sc->sc_dev);
return size;
+
+ die:
+ device_unref(&sc->sc_dev);
+ return -1;
}
/* #define SD_DUMP_NOT_TRUSTED if you just want to watch */
@@ -1350,6 +1418,8 @@ sd_read_cap_10(struct sd_softc *sc, int
if (rdcap == NULL)
return (ENOMEM);
+ if (sc->flags & SDF_DYING)
+ goto die;
xs = scsi_xs_get(sc->sc_link, flags | SCSI_DATA_IN | SCSI_SILENT);
if (xs == NULL)
goto done;
@@ -1375,6 +1445,10 @@ sd_read_cap_10(struct sd_softc *sc, int
done:
dma_free(rdcap, sizeof(*rdcap));
return (rv);
+
+ die:
+ dma_free(rdcap, sizeof(*rdcap));
+ return (ENXIO);
}
int
@@ -1392,6 +1466,8 @@ sd_read_cap_16(struct sd_softc *sc, int
if (rdcap == NULL)
return (ENOMEM);
+ if (sc->flags & SDF_DYING)
+ goto die;
xs = scsi_xs_get(sc->sc_link, flags | SCSI_DATA_IN | SCSI_SILENT);
if (xs == NULL)
goto done;
@@ -1427,6 +1503,10 @@ sd_read_cap_16(struct sd_softc *sc, int
done:
dma_free(rdcap, sizeof(*rdcap));
return (rv);
+
+ die:
+ dma_free(rdcap, sizeof(*rdcap));
+ return (ENXIO);
}
int
@@ -1434,6 +1514,8 @@ sd_size(struct sd_softc *sc, int flags)
{
int rv;
+ if (sc->flags & SDF_DYING)
+ return (ENXIO);
if (SCSISPC(sc->sc_link->inqdata.version) >= 3) {
rv = sd_read_cap_16(sc, flags);
if (rv != 0)
@@ -1461,6 +1543,8 @@ sd_thin_pages(struct sd_softc *sc, int f
if (pg == NULL)
return (ENOMEM);
+ if (sc->flags & SDF_DYING)
+ goto die;
rv = scsi_inquire_vpd(sc->sc_link, pg, sizeof(*pg),
SI_PG_SUPPORTED, flags);
if (rv != 0)
@@ -1474,6 +1558,8 @@ sd_thin_pages(struct sd_softc *sc, int f
if (pg == NULL)
return (ENOMEM);
+ if (sc->flags & SDF_DYING)
+ goto die;
rv = scsi_inquire_vpd(sc->sc_link, pg, sizeof(*pg) + len,
SI_PG_SUPPORTED, flags);
if (rv != 0)
@@ -1500,6 +1586,10 @@ sd_thin_pages(struct sd_softc *sc, int f
done:
dma_free(pg, sizeof(*pg) + len);
return (rv);
+
+ die:
+ dma_free(pg, sizeof(*pg) + len);
+ return (ENXIO);
}
int
@@ -1513,6 +1603,8 @@ sd_vpd_block_limits(struct sd_softc *sc,
if (pg == NULL)
return (ENOMEM);
+ if (sc->flags & SDF_DYING)
+ goto die;
rv = scsi_inquire_vpd(sc->sc_link, pg, sizeof(*pg),
SI_PG_DISK_LIMITS, flags);
if (rv != 0)
@@ -1527,6 +1619,10 @@ sd_vpd_block_limits(struct sd_softc *sc,
done:
dma_free(pg, sizeof(*pg));
return (rv);
+
+ die:
+ dma_free(pg, sizeof(*pg));
+ return (ENXIO);
}
int
@@ -1540,6 +1636,8 @@ sd_vpd_thin(struct sd_softc *sc, int fla
if (pg == NULL)
return (ENOMEM);
+ if (sc->flags & SDF_DYING)
+ goto die;
rv = scsi_inquire_vpd(sc->sc_link, pg, sizeof(*pg),
SI_PG_DISK_THIN, flags);
if (rv != 0)
@@ -1558,6 +1656,10 @@ sd_vpd_thin(struct sd_softc *sc, int fla
done:
dma_free(pg, sizeof(*pg));
return (rv);
+
+ die:
+ dma_free(pg, sizeof(*pg));
+ return (ENXIO);
}
int
@@ -1589,6 +1691,7 @@ sd_thin_params(struct sd_softc *sc, int
int
sd_get_parms(struct sd_softc *sc, struct disk_parms *dp, int flags)
{
+ struct scsi_link *sc_link;
union scsi_mode_sense_buf *buf = NULL;
struct page_rigid_geometry *rigid = NULL;
struct page_flex_geometry *flex = NULL;
@@ -1608,20 +1711,26 @@ sd_get_parms(struct sd_softc *sc, struct
buf = dma_alloc(sizeof(*buf), PR_NOWAIT);
if (buf == NULL)
goto validate;
+
+ if (sc->flags & SDF_DYING)
+ goto die;
+ sc_link = sc->sc_link;
/*
* Ask for page 0 (vendor specific) mode sense data to find
* READONLY info. The only thing USB devices will ask for.
*/
- err = scsi_do_mode_sense(sc->sc_link, 0, buf, (void **)&page0,
+ err = scsi_do_mode_sense(sc_link, 0, buf, (void **)&page0,
NULL, NULL, NULL, 1, flags | SCSI_SILENT, &big);
+ if (sc->flags & SDF_DYING)
+ goto die;
if (err == 0) {
if (big && buf->hdr_big.dev_spec & SMH_DSP_WRITE_PROT)
- SET(sc->sc_link->flags, SDEV_READONLY);
+ SET(sc_link->flags, SDEV_READONLY);
else if (!big && buf->hdr.dev_spec & SMH_DSP_WRITE_PROT)
- SET(sc->sc_link->flags, SDEV_READONLY);
+ SET(sc_link->flags, SDEV_READONLY);
else
- CLR(sc->sc_link->flags, SDEV_READONLY);
+ CLR(sc_link->flags, SDEV_READONLY);
}
/*
@@ -1629,17 +1738,17 @@ sd_get_parms(struct sd_softc *sc, struct
* don't have a meaningful geometry anyway, so just fake it if
* scsi_size() worked.
*/
- if ((sc->sc_link->flags & SDEV_UMASS) && (dp->disksize > 0))
+ if ((sc_link->flags & SDEV_UMASS) && (dp->disksize > 0))
goto validate;
- switch (sc->sc_link->inqdata.device & SID_TYPE) {
+ switch (sc_link->inqdata.device & SID_TYPE) {
case T_OPTICAL:
/* No more information needed or available. */
break;
case T_RDIRECT:
/* T_RDIRECT supports only PAGE_REDUCED_GEOMETRY (6). */
- err = scsi_do_mode_sense(sc->sc_link, PAGE_REDUCED_GEOMETRY,
+ err = scsi_do_mode_sense(sc_link, PAGE_REDUCED_GEOMETRY,
buf, (void **)&reduced, NULL, NULL, &secsize,
sizeof(*reduced), flags | SCSI_SILENT, NULL);
if (!err && reduced &&
@@ -1659,9 +1768,9 @@ sd_get_parms(struct sd_softc *sc, struct
* so accept the page. The extra bytes will be zero and RPM will
* end up with the default value of 3600.
*/
- if (((sc->sc_link->flags & SDEV_ATAPI) == 0) ||
- ((sc->sc_link->flags & SDEV_REMOVABLE) == 0))
- err = scsi_do_mode_sense(sc->sc_link,
+ if (((sc_link->flags & SDEV_ATAPI) == 0) ||
+ ((sc_link->flags & SDEV_REMOVABLE) == 0))
+ err = scsi_do_mode_sense(sc_link,
PAGE_RIGID_GEOMETRY, buf, (void **)&rigid, NULL,
NULL, &secsize, sizeof(*rigid) - 4,
flags | SCSI_SILENT, NULL);
@@ -1671,7 +1780,9 @@ sd_get_parms(struct sd_softc *sc, struct
if (heads * cyls > 0)
sectors = dp->disksize / (heads * cyls);
} else {
- err = scsi_do_mode_sense(sc->sc_link,
+ if (sc->flags & SDF_DYING)
+ goto die;
+ err = scsi_do_mode_sense(sc_link,
PAGE_FLEX_GEOMETRY, buf, (void **)&flex, NULL, NULL,
&secsize, sizeof(*flex) - 4,
flags | SCSI_SILENT, NULL);
@@ -1689,7 +1800,7 @@ sd_get_parms(struct sd_softc *sc, struct
break;
}
-validate:
+ validate:
if (buf)
dma_free(buf, sizeof(*buf));
@@ -1746,16 +1857,24 @@ validate:
}
return (SDGP_RESULT_OK);
+
+ die:
+ dma_free(buf, sizeof(*buf));
+ return (SDGP_RESULT_OFFLINE);
}
void
sd_flush(struct sd_softc *sc, int flags)
{
- struct scsi_link *link = sc->sc_link;
+ struct scsi_link *sc_link;
struct scsi_xfer *xs;
struct scsi_synchronize_cache *cmd;
- if (link->quirks & SDEV_NOSYNCCACHE)
+ if (sc->flags & SDF_DYING)
+ return;
+ sc_link = sc->sc_link;
+
+ if (sc_link->quirks & SDEV_NOSYNCCACHE)
return;
/*
@@ -1764,9 +1883,9 @@ sd_flush(struct sd_softc *sc, int flags)
* that the command is not supported by the device.
*/
- xs = scsi_xs_get(link, flags);
+ xs = scsi_xs_get(sc_link, flags);
if (xs == NULL) {
- SC_DEBUG(link, SDEV_DB1, ("cache sync failed to get xs\n"));
+ SC_DEBUG(sc_link, SDEV_DB1, ("cache sync failed to get xs\n"));
return;
}
@@ -1780,7 +1899,7 @@ sd_flush(struct sd_softc *sc, int flags)
if (scsi_xs_sync(xs) == 0)
sc->flags &= ~SDF_DIRTY;
else
- SC_DEBUG(link, SDEV_DB1, ("cache sync failed\n"));
+ SC_DEBUG(sc_link, SDEV_DB1, ("cache sync failed\n"));
scsi_xs_put(xs);
}
I have seen some use after free panics when unplugging USB umass
sticks that were still in use. In sdopen() the scsi link pointer
is taken from the scsi disk struct. While the scsi disk memory is
refcounted by autoconf, the scsi link may be detached and freed
after every sleep.
The solution is to check wether the disk is dying after every sleep.
The SDF_DYING flag is set before the scsi bus and scsi disk get
detached, so without this flag the link must be valid.
If a function gets a scsi transfer, the link and disk are always
valid. scsi_detach_lun() removes and waits for the scsi transfers
as the first step. To make the code consistent I use the variable
name sc_link for links from the sc. There for every access after
sleep the disk is checked. The link from a transfer is called link
and can be used without check.
Althogh it adds a lot of code, I think it has to be done to remove
the races.
ok?
bluhm
Index: scsi/sd.c
===================================================================
RCS file: /data/mirror/openbsd/cvs/src/sys/scsi/sd.c,v
retrieving revision 1.261
diff -u -p -r1.261 sd.c
--- scsi/sd.c 7 Jun 2015 19:13:27 -0000 1.261
+++ scsi/sd.c 28 Jan 2016 22:54:41 -0000
@@ -199,7 +199,7 @@ sdattach(struct device *parent, struct d
&sc->sc_xsh);
/* Spin up non-UMASS devices ready or not. */
- if ((sc->sc_link->flags & SDEV_UMASS) == 0)
+ if ((sc_link->flags & SDEV_UMASS) == 0)
scsi_start(sc_link, SSS_START, sd_autoconf);
/*
@@ -341,12 +341,12 @@ sdopen(dev_t dev, int flag, int fmt, str
sc = sdlookup(unit);
if (sc == NULL)
return (ENXIO);
- sc_link = sc->sc_link;
-
if (sc->flags & SDF_DYING) {
device_unref(&sc->sc_dev);
return (ENXIO);
}
+ sc_link = sc->sc_link;
+
if (ISSET(flag, FWRITE) && ISSET(sc_link->flags, SDEV_READONLY)) {
device_unref(&sc->sc_dev);
return (EACCES);
@@ -366,6 +366,8 @@ sdopen(dev_t dev, int flag, int fmt, str
* If any partition is open, but the disk has been invalidated,
* disallow further opens of non-raw partition.
*/
+ if (sc->flags & SDF_DYING)
+ goto die;
if ((sc_link->flags & SDEV_MEDIA_LOADED) == 0) {
if (rawopen)
goto out;
@@ -374,7 +376,9 @@ sdopen(dev_t dev, int flag, int fmt, str
}
} else {
/* Spin up non-UMASS devices ready or not. */
- if ((sc->sc_link->flags & SDEV_UMASS) == 0)
+ if (sc->flags & SDF_DYING)
+ goto die;
+ if ((sc_link->flags & SDEV_UMASS) == 0)
scsi_start(sc_link, SSS_START, (rawopen ? SCSI_SILENT :
0) | SCSI_IGNORE_ILLEGAL_REQUEST |
SCSI_IGNORE_MEDIA_CHANGE);
@@ -385,6 +389,8 @@ sdopen(dev_t dev, int flag, int fmt, str
* device returns "Initialization command required." and causes
* a loop of scsi_start() calls.
*/
+ if (sc->flags & SDF_DYING)
+ goto die;
sc_link->flags |= SDEV_OPEN;
/*
@@ -399,10 +405,11 @@ sdopen(dev_t dev, int flag, int fmt, str
}
/* Check that it is still responding and ok. */
+ if (sc->flags & SDF_DYING)
+ goto die;
error = scsi_test_unit_ready(sc_link,
TEST_READY_RETRIES, SCSI_SILENT |
SCSI_IGNORE_ILLEGAL_REQUEST | SCSI_IGNORE_MEDIA_CHANGE);
-
if (error) {
if (rawopen) {
error = 0;
@@ -412,9 +419,13 @@ sdopen(dev_t dev, int flag, int fmt, str
}
/* Load the physical device parameters. */
+ if (sc->flags & SDF_DYING)
+ goto die;
sc_link->flags |= SDEV_MEDIA_LOADED;
if (sd_get_parms(sc, &sc->params, (rawopen ? SCSI_SILENT : 0))
== SDGP_RESULT_OFFLINE) {
+ if (sc->flags & SDF_DYING)
+ goto die;
sc_link->flags &= ~SDEV_MEDIA_LOADED;
error = ENXIO;
goto bad;
@@ -429,25 +440,34 @@ sdopen(dev_t dev, int flag, int fmt, str
SC_DEBUG(sc_link, SDEV_DB3, ("Disklabel loaded\n"));
}
-out:
+ out:
if ((error = disk_openpart(&sc->sc_dk, part, fmt, 1)) != 0)
goto bad;
SC_DEBUG(sc_link, SDEV_DB3, ("open complete\n"));
/* It's OK to fall through because dk_openmask is now non-zero. */
-bad:
+ bad:
if (sc->sc_dk.dk_openmask == 0) {
- if ((sc->sc_link->flags & SDEV_REMOVABLE) != 0)
+ if (sc->flags & SDF_DYING)
+ goto die;
+ if ((sc_link->flags & SDEV_REMOVABLE) != 0)
scsi_prevent(sc_link, PR_ALLOW, SCSI_SILENT |
SCSI_IGNORE_ILLEGAL_REQUEST |
SCSI_IGNORE_MEDIA_CHANGE);
+ if (sc->flags & SDF_DYING)
+ goto die;
sc_link->flags &= ~(SDEV_OPEN | SDEV_MEDIA_LOADED);
}
disk_unlock(&sc->sc_dk);
device_unref(&sc->sc_dev);
return (error);
+
+ die:
+ disk_unlock(&sc->sc_dk);
+ device_unref(&sc->sc_dev);
+ return (ENXIO);
}
/*
@@ -457,6 +477,7 @@ bad:
int
sdclose(dev_t dev, int flag, int fmt, struct proc *p)
{
+ struct scsi_link *sc_link;
struct sd_softc *sc;
int part = DISKPART(dev);
@@ -467,6 +488,7 @@ sdclose(dev_t dev, int flag, int fmt, st
device_unref(&sc->sc_dev);
return (ENXIO);
}
+ sc_link = sc->sc_link;
disk_lock_nointr(&sc->sc_dk);
@@ -476,15 +498,21 @@ sdclose(dev_t dev, int flag, int fmt, st
if ((sc->flags & SDF_DIRTY) != 0)
sd_flush(sc, 0);
- if ((sc->sc_link->flags & SDEV_REMOVABLE) != 0)
- scsi_prevent(sc->sc_link, PR_ALLOW,
+ if (sc->flags & SDF_DYING)
+ goto die;
+ if ((sc_link->flags & SDEV_REMOVABLE) != 0)
+ scsi_prevent(sc_link, PR_ALLOW,
SCSI_IGNORE_ILLEGAL_REQUEST |
SCSI_IGNORE_NOT_READY | SCSI_SILENT);
- sc->sc_link->flags &= ~(SDEV_OPEN | SDEV_MEDIA_LOADED);
+ if (sc->flags & SDF_DYING)
+ goto die;
+ sc_link->flags &= ~(SDEV_OPEN | SDEV_MEDIA_LOADED);
- if (sc->sc_link->flags & SDEV_EJECTING) {
- scsi_start(sc->sc_link, SSS_STOP|SSS_LOEJ, 0);
- sc->sc_link->flags &= ~SDEV_EJECTING;
+ if (sc_link->flags & SDEV_EJECTING) {
+ scsi_start(sc_link, SSS_STOP|SSS_LOEJ, 0);
+ if (sc->flags & SDF_DYING)
+ goto die;
+ sc_link->flags &= ~SDEV_EJECTING;
}
timeout_del(&sc->sc_timeout);
@@ -494,6 +522,11 @@ sdclose(dev_t dev, int flag, int fmt, st
disk_unlock(&sc->sc_dk);
device_unref(&sc->sc_dev);
return 0;
+
+ die:
+ disk_unlock(&sc->sc_dk);
+ device_unref(&sc->sc_dev);
+ return (ENXIO);
}
/*
@@ -504,6 +537,7 @@ sdclose(dev_t dev, int flag, int fmt, st
void
sdstrategy(struct buf *bp)
{
+ struct scsi_link *sc_link;
struct sd_softc *sc;
int s;
@@ -516,14 +550,15 @@ sdstrategy(struct buf *bp)
bp->b_error = ENXIO;
goto bad;
}
+ sc_link = sc->sc_link;
- SC_DEBUG(sc->sc_link, SDEV_DB2, ("sdstrategy: %ld bytes @ blk %lld\n",
+ SC_DEBUG(sc_link, SDEV_DB2, ("sdstrategy: %ld bytes @ blk %lld\n",
bp->b_bcount, (long long)bp->b_blkno));
/*
* If the device has been made invalid, error out
*/
- if ((sc->sc_link->flags & SDEV_MEDIA_LOADED) == 0) {
- if (sc->sc_link->flags & SDEV_OPEN)
+ if ((sc_link->flags & SDEV_MEDIA_LOADED) == 0) {
+ if (sc_link->flags & SDEV_OPEN)
bp->b_error = EIO;
else
bp->b_error = ENODEV;
@@ -770,12 +805,18 @@ retry:
void
sdminphys(struct buf *bp)
{
+ struct scsi_link *sc_link;
struct sd_softc *sc;
long max;
sc = sdlookup(DISKUNIT(bp->b_dev));
if (sc == NULL)
return; /* XXX - right way to fail this? */
+ if (sc->flags & SDF_DYING) {
+ device_unref(&sc->sc_dev);
+ return;
+ }
+ sc_link = sc->sc_link;
/*
* If the device is ancient, we want to make sure that
@@ -795,7 +836,7 @@ sdminphys(struct buf *bp)
bp->b_bcount = max;
}
- (*sc->sc_link->adapter->scsi_minphys)(bp, sc->sc_link);
+ (*sc_link->adapter->scsi_minphys)(bp, sc_link);
device_unref(&sc->sc_dev);
}
@@ -819,6 +860,7 @@ sdwrite(dev_t dev, struct uio *uio, int
int
sdioctl(dev_t dev, u_long cmd, caddr_t addr, int flag, struct proc *p)
{
+ struct scsi_link *sc_link;
struct sd_softc *sc;
struct disklabel *lp;
int error = 0;
@@ -831,13 +873,14 @@ sdioctl(dev_t dev, u_long cmd, caddr_t a
device_unref(&sc->sc_dev);
return (ENXIO);
}
+ sc_link = sc->sc_link;
- SC_DEBUG(sc->sc_link, SDEV_DB2, ("sdioctl 0x%lx\n", cmd));
+ SC_DEBUG(sc_link, SDEV_DB2, ("sdioctl 0x%lx\n", cmd));
/*
* If the device is not valid.. abandon ship
*/
- if ((sc->sc_link->flags & SDEV_MEDIA_LOADED) == 0) {
+ if ((sc_link->flags & SDEV_MEDIA_LOADED) == 0) {
switch (cmd) {
case DIOCLOCK:
case DIOCEJECT:
@@ -848,7 +891,7 @@ sdioctl(dev_t dev, u_long cmd, caddr_t a
break;
/* FALLTHROUGH */
default:
- if ((sc->sc_link->flags & SDEV_OPEN) == 0) {
+ if ((sc_link->flags & SDEV_OPEN) == 0) {
error = ENODEV;
goto exit;
} else {
@@ -902,7 +945,7 @@ sdioctl(dev_t dev, u_long cmd, caddr_t a
goto exit;
case DIOCLOCK:
- error = scsi_prevent(sc->sc_link,
+ error = scsi_prevent(sc_link,
(*(int *)addr) ? PR_PREVENT : PR_ALLOW, 0);
goto exit;
@@ -913,15 +956,15 @@ sdioctl(dev_t dev, u_long cmd, caddr_t a
}
/* FALLTHROUGH */
case DIOCEJECT:
- if ((sc->sc_link->flags & SDEV_REMOVABLE) == 0) {
+ if ((sc_link->flags & SDEV_REMOVABLE) == 0) {
error = ENOTTY;
goto exit;
}
- sc->sc_link->flags |= SDEV_EJECTING;
+ sc_link->flags |= SDEV_EJECTING;
goto exit;
case DIOCINQ:
- error = scsi_do_ioctl(sc->sc_link, cmd, addr, flag);
+ error = scsi_do_ioctl(sc_link, cmd, addr, flag);
if (error == ENOTTY)
error = sd_ioctl_inquiry(sc,
(struct dk_inquiry *)addr);
@@ -942,7 +985,7 @@ sdioctl(dev_t dev, u_long cmd, caddr_t a
error = ENOTTY;
goto exit;
}
- error = scsi_do_ioctl(sc->sc_link, cmd, addr, flag);
+ error = scsi_do_ioctl(sc_link, cmd, addr, flag);
}
exit:
@@ -953,21 +996,27 @@ sdioctl(dev_t dev, u_long cmd, caddr_t a
int
sd_ioctl_inquiry(struct sd_softc *sc, struct dk_inquiry *di)
{
+ struct scsi_link *sc_link;
struct scsi_vpd_serial *vpd;
vpd = dma_alloc(sizeof(*vpd), PR_WAITOK | PR_ZERO);
+ if (sc->flags & SDF_DYING) {
+ dma_free(vpd, sizeof(*vpd));
+ return (ENXIO);
+ }
+ sc_link = sc->sc_link;
+
bzero(di, sizeof(struct dk_inquiry));
- scsi_strvis(di->vendor, sc->sc_link->inqdata.vendor,
- sizeof(sc->sc_link->inqdata.vendor));
- scsi_strvis(di->product, sc->sc_link->inqdata.product,
- sizeof(sc->sc_link->inqdata.product));
- scsi_strvis(di->revision, sc->sc_link->inqdata.revision,
- sizeof(sc->sc_link->inqdata.revision));
+ scsi_strvis(di->vendor, sc_link->inqdata.vendor,
+ sizeof(sc_link->inqdata.vendor));
+ scsi_strvis(di->product, sc_link->inqdata.product,
+ sizeof(sc_link->inqdata.product));
+ scsi_strvis(di->revision, sc_link->inqdata.revision,
+ sizeof(sc_link->inqdata.revision));
/* the serial vpd page is optional */
- if (scsi_inquire_vpd(sc->sc_link, vpd, sizeof(*vpd),
- SI_PG_SERIAL, 0) == 0)
+ if (scsi_inquire_vpd(sc_link, vpd, sizeof(*vpd), SI_PG_SERIAL, 0) == 0)
scsi_strvis(di->serial, vpd->serial, sizeof(vpd->serial));
else
strlcpy(di->serial, "(unknown)", sizeof(vpd->serial));
@@ -979,17 +1028,22 @@ sd_ioctl_inquiry(struct sd_softc *sc, st
int
sd_ioctl_cache(struct sd_softc *sc, long cmd, struct dk_cache *dkc)
{
+ struct scsi_link *sc_link;
union scsi_mode_sense_buf *buf;
struct page_caching_mode *mode = NULL;
u_int wrcache, rdcache;
int big;
int rv;
- if (ISSET(sc->sc_link->flags, SDEV_UMASS))
+ if (sc->flags & SDF_DYING)
+ return (ENXIO);
+ sc_link = sc->sc_link;
+
+ if (ISSET(sc_link->flags, SDEV_UMASS))
return (EOPNOTSUPP);
/* see if the adapter has special handling */
- rv = scsi_do_ioctl(sc->sc_link, cmd, (caddr_t)dkc, 0);
+ rv = scsi_do_ioctl(sc_link, cmd, (caddr_t)dkc, 0);
if (rv != ENOTTY)
return (rv);
@@ -997,7 +1051,9 @@ sd_ioctl_cache(struct sd_softc *sc, long
if (buf == NULL)
return (ENOMEM);
- rv = scsi_do_mode_sense(sc->sc_link, PAGE_CACHING_MODE,
+ if (sc->flags & SDF_DYING)
+ goto die;
+ rv = scsi_do_mode_sense(sc_link, PAGE_CACHING_MODE,
buf, (void **)&mode, NULL, NULL, NULL,
sizeof(*mode) - 4, scsi_autoconf | SCSI_SILENT, &big);
if (rv != 0)
@@ -1031,19 +1087,25 @@ sd_ioctl_cache(struct sd_softc *sc, long
else
SET(mode->flags, PG_CACHE_FL_RCD);
+ if (sc->flags & SDF_DYING)
+ goto die;
if (big) {
- rv = scsi_mode_select_big(sc->sc_link, SMS_PF,
+ rv = scsi_mode_select_big(sc_link, SMS_PF,
&buf->hdr_big, scsi_autoconf | SCSI_SILENT, 20000);
} else {
- rv = scsi_mode_select(sc->sc_link, SMS_PF,
+ rv = scsi_mode_select(sc_link, SMS_PF,
&buf->hdr, scsi_autoconf | SCSI_SILENT, 20000);
}
break;
}
-done:
+ done:
dma_free(buf, sizeof(*buf));
return (rv);
+
+ die:
+ dma_free(buf, sizeof(*buf));
+ return (ENXIO);
}
/*
@@ -1053,10 +1115,15 @@ int
sdgetdisklabel(dev_t dev, struct sd_softc *sc, struct disklabel *lp,
int spoofonly)
{
+ struct scsi_link *sc_link;
size_t len;
char packname[sizeof(lp->d_packname) + 1];
char product[17], vendor[9];
+ if (sc->flags & SDF_DYING)
+ return (ENXIO);
+ sc_link = sc->sc_link;
+
bzero(lp, sizeof(struct disklabel));
lp->d_secsize = sc->params.secsize;
@@ -1070,7 +1137,7 @@ sdgetdisklabel(dev_t dev, struct sd_soft
}
lp->d_type = DTYPE_SCSI;
- if ((sc->sc_link->inqdata.device & SID_TYPE) == T_OPTICAL)
+ if ((sc_link->inqdata.device & SID_TYPE) == T_OPTICAL)
strncpy(lp->d_typename, "SCSI optical",
sizeof(lp->d_typename));
else
@@ -1082,8 +1149,8 @@ sdgetdisklabel(dev_t dev, struct sd_soft
* then leave out '<vendor> ' and use only as much of '<product>' as
* does fit.
*/
- viscpy(vendor, sc->sc_link->inqdata.vendor, 8);
- viscpy(product, sc->sc_link->inqdata.product, 16);
+ viscpy(vendor, sc_link->inqdata.vendor, 8);
+ viscpy(product, sc_link->inqdata.product, 16);
len = snprintf(packname, sizeof(packname), "%s %s", vendor, product);
if (len > sizeof(lp->d_packname)) {
strlcpy(packname, product, sizeof(packname));
@@ -1123,8 +1190,7 @@ int
sd_interpret_sense(struct scsi_xfer *xs)
{
struct scsi_sense_data *sense = &xs->sense;
- struct scsi_link *sc_link = xs->sc_link;
- struct sd_softc *sc = sc_link->device_softc;
+ struct scsi_link *link = xs->sc_link;
u_int8_t serr = sense->error_code & SSD_ERRCODE;
int retval;
@@ -1132,7 +1198,7 @@ sd_interpret_sense(struct scsi_xfer *xs)
* Let the generic code handle everything except a few categories of
* LUN not ready errors on open devices.
*/
- if (((sc_link->flags & SDEV_OPEN) == 0) ||
+ if (((link->flags & SDEV_OPEN) == 0) ||
(serr != SSD_ERRCODE_CURRENT && serr != SSD_ERRCODE_DEFERRED) ||
((sense->flags & SSD_KEY) != SKEY_NOT_READY) ||
(sense->extra_len < 6))
@@ -1143,13 +1209,13 @@ sd_interpret_sense(struct scsi_xfer *xs)
switch (ASC_ASCQ(sense)) {
case SENSE_NOT_READY_BECOMING_READY:
- SC_DEBUG(sc_link, SDEV_DB1, ("becoming ready.\n"));
+ SC_DEBUG(link, SDEV_DB1, ("becoming ready.\n"));
retval = scsi_delay(xs, 5);
break;
case SENSE_NOT_READY_INIT_REQUIRED:
- SC_DEBUG(sc_link, SDEV_DB1, ("spinning up\n"));
- retval = scsi_start(sc->sc_link, SSS_START,
+ SC_DEBUG(link, SDEV_DB1, ("spinning up\n"));
+ retval = scsi_start(link, SSS_START,
SCSI_IGNORE_ILLEGAL_REQUEST | SCSI_NOSLEEP);
if (retval == 0)
retval = ERESTART;
@@ -1157,7 +1223,7 @@ sd_interpret_sense(struct scsi_xfer *xs)
/* Can't issue the command. Fall back on a delay. */
retval = scsi_delay(xs, 5);
else
- SC_DEBUG(sc_link, SDEV_DB1, ("spin up failed (%#x)\n",
+ SC_DEBUG(link, SDEV_DB1, ("spin up failed (%#x)\n",
retval));
break;
@@ -1180,32 +1246,34 @@ sdsize(dev_t dev)
sc = sdlookup(DISKUNIT(dev));
if (sc == NULL)
return -1;
- if (sc->flags & SDF_DYING) {
- size = -1;
- goto exit;
- }
+ if (sc->flags & SDF_DYING)
+ goto die;
part = DISKPART(dev);
omask = sc->sc_dk.dk_openmask & (1 << part);
- if (omask == 0 && sdopen(dev, 0, S_IFBLK, NULL) != 0) {
- size = -1;
- goto exit;
- }
+ if (omask == 0 && sdopen(dev, 0, S_IFBLK, NULL) != 0)
+ goto die;
lp = sc->sc_dk.dk_label;
+ if (sc->flags & SDF_DYING)
+ goto die;
if ((sc->sc_link->flags & SDEV_MEDIA_LOADED) == 0)
size = -1;
else if (lp->d_partitions[part].p_fstype != FS_SWAP)
size = -1;
else
size = DL_SECTOBLK(lp, DL_GETPSIZE(&lp->d_partitions[part]));
+
if (omask == 0 && sdclose(dev, 0, S_IFBLK, NULL) != 0)
- size = -1;
+ goto die;
- exit:
device_unref(&sc->sc_dev);
return size;
+
+ die:
+ device_unref(&sc->sc_dev);
+ return -1;
}
/* #define SD_DUMP_NOT_TRUSTED if you just want to watch */
@@ -1350,6 +1418,8 @@ sd_read_cap_10(struct sd_softc *sc, int
if (rdcap == NULL)
return (ENOMEM);
+ if (sc->flags & SDF_DYING)
+ goto die;
xs = scsi_xs_get(sc->sc_link, flags | SCSI_DATA_IN | SCSI_SILENT);
if (xs == NULL)
goto done;
@@ -1375,6 +1445,10 @@ sd_read_cap_10(struct sd_softc *sc, int
done:
dma_free(rdcap, sizeof(*rdcap));
return (rv);
+
+ die:
+ dma_free(rdcap, sizeof(*rdcap));
+ return (ENXIO);
}
int
@@ -1392,6 +1466,8 @@ sd_read_cap_16(struct sd_softc *sc, int
if (rdcap == NULL)
return (ENOMEM);
+ if (sc->flags & SDF_DYING)
+ goto die;
xs = scsi_xs_get(sc->sc_link, flags | SCSI_DATA_IN | SCSI_SILENT);
if (xs == NULL)
goto done;
@@ -1427,6 +1503,10 @@ sd_read_cap_16(struct sd_softc *sc, int
done:
dma_free(rdcap, sizeof(*rdcap));
return (rv);
+
+ die:
+ dma_free(rdcap, sizeof(*rdcap));
+ return (ENXIO);
}
int
@@ -1434,6 +1514,8 @@ sd_size(struct sd_softc *sc, int flags)
{
int rv;
+ if (sc->flags & SDF_DYING)
+ return (ENXIO);
if (SCSISPC(sc->sc_link->inqdata.version) >= 3) {
rv = sd_read_cap_16(sc, flags);
if (rv != 0)
@@ -1461,6 +1543,8 @@ sd_thin_pages(struct sd_softc *sc, int f
if (pg == NULL)
return (ENOMEM);
+ if (sc->flags & SDF_DYING)
+ goto die;
rv = scsi_inquire_vpd(sc->sc_link, pg, sizeof(*pg),
SI_PG_SUPPORTED, flags);
if (rv != 0)
@@ -1474,6 +1558,8 @@ sd_thin_pages(struct sd_softc *sc, int f
if (pg == NULL)
return (ENOMEM);
+ if (sc->flags & SDF_DYING)
+ goto die;
rv = scsi_inquire_vpd(sc->sc_link, pg, sizeof(*pg) + len,
SI_PG_SUPPORTED, flags);
if (rv != 0)
@@ -1500,6 +1586,10 @@ sd_thin_pages(struct sd_softc *sc, int f
done:
dma_free(pg, sizeof(*pg) + len);
return (rv);
+
+ die:
+ dma_free(pg, sizeof(*pg) + len);
+ return (ENXIO);
}
int
@@ -1513,6 +1603,8 @@ sd_vpd_block_limits(struct sd_softc *sc,
if (pg == NULL)
return (ENOMEM);
+ if (sc->flags & SDF_DYING)
+ goto die;
rv = scsi_inquire_vpd(sc->sc_link, pg, sizeof(*pg),
SI_PG_DISK_LIMITS, flags);
if (rv != 0)
@@ -1527,6 +1619,10 @@ sd_vpd_block_limits(struct sd_softc *sc,
done:
dma_free(pg, sizeof(*pg));
return (rv);
+
+ die:
+ dma_free(pg, sizeof(*pg));
+ return (ENXIO);
}
int
@@ -1540,6 +1636,8 @@ sd_vpd_thin(struct sd_softc *sc, int fla
if (pg == NULL)
return (ENOMEM);
+ if (sc->flags & SDF_DYING)
+ goto die;
rv = scsi_inquire_vpd(sc->sc_link, pg, sizeof(*pg),
SI_PG_DISK_THIN, flags);
if (rv != 0)
@@ -1558,6 +1656,10 @@ sd_vpd_thin(struct sd_softc *sc, int fla
done:
dma_free(pg, sizeof(*pg));
return (rv);
+
+ die:
+ dma_free(pg, sizeof(*pg));
+ return (ENXIO);
}
int
@@ -1589,6 +1691,7 @@ sd_thin_params(struct sd_softc *sc, int
int
sd_get_parms(struct sd_softc *sc, struct disk_parms *dp, int flags)
{
+ struct scsi_link *sc_link;
union scsi_mode_sense_buf *buf = NULL;
struct page_rigid_geometry *rigid = NULL;
struct page_flex_geometry *flex = NULL;
@@ -1608,20 +1711,26 @@ sd_get_parms(struct sd_softc *sc, struct
buf = dma_alloc(sizeof(*buf), PR_NOWAIT);
if (buf == NULL)
goto validate;
+
+ if (sc->flags & SDF_DYING)
+ goto die;
+ sc_link = sc->sc_link;
/*
* Ask for page 0 (vendor specific) mode sense data to find
* READONLY info. The only thing USB devices will ask for.
*/
- err = scsi_do_mode_sense(sc->sc_link, 0, buf, (void **)&page0,
+ err = scsi_do_mode_sense(sc_link, 0, buf, (void **)&page0,
NULL, NULL, NULL, 1, flags | SCSI_SILENT, &big);
+ if (sc->flags & SDF_DYING)
+ goto die;
if (err == 0) {
if (big && buf->hdr_big.dev_spec & SMH_DSP_WRITE_PROT)
- SET(sc->sc_link->flags, SDEV_READONLY);
+ SET(sc_link->flags, SDEV_READONLY);
else if (!big && buf->hdr.dev_spec & SMH_DSP_WRITE_PROT)
- SET(sc->sc_link->flags, SDEV_READONLY);
+ SET(sc_link->flags, SDEV_READONLY);
else
- CLR(sc->sc_link->flags, SDEV_READONLY);
+ CLR(sc_link->flags, SDEV_READONLY);
}
/*
@@ -1629,17 +1738,17 @@ sd_get_parms(struct sd_softc *sc, struct
* don't have a meaningful geometry anyway, so just fake it if
* scsi_size() worked.
*/
- if ((sc->sc_link->flags & SDEV_UMASS) && (dp->disksize > 0))
+ if ((sc_link->flags & SDEV_UMASS) && (dp->disksize > 0))
goto validate;
- switch (sc->sc_link->inqdata.device & SID_TYPE) {
+ switch (sc_link->inqdata.device & SID_TYPE) {
case T_OPTICAL:
/* No more information needed or available. */
break;
case T_RDIRECT:
/* T_RDIRECT supports only PAGE_REDUCED_GEOMETRY (6). */
- err = scsi_do_mode_sense(sc->sc_link, PAGE_REDUCED_GEOMETRY,
+ err = scsi_do_mode_sense(sc_link, PAGE_REDUCED_GEOMETRY,
buf, (void **)&reduced, NULL, NULL, &secsize,
sizeof(*reduced), flags | SCSI_SILENT, NULL);
if (!err && reduced &&
@@ -1659,9 +1768,9 @@ sd_get_parms(struct sd_softc *sc, struct
* so accept the page. The extra bytes will be zero and RPM will
* end up with the default value of 3600.
*/
- if (((sc->sc_link->flags & SDEV_ATAPI) == 0) ||
- ((sc->sc_link->flags & SDEV_REMOVABLE) == 0))
- err = scsi_do_mode_sense(sc->sc_link,
+ if (((sc_link->flags & SDEV_ATAPI) == 0) ||
+ ((sc_link->flags & SDEV_REMOVABLE) == 0))
+ err = scsi_do_mode_sense(sc_link,
PAGE_RIGID_GEOMETRY, buf, (void **)&rigid, NULL,
NULL, &secsize, sizeof(*rigid) - 4,
flags | SCSI_SILENT, NULL);
@@ -1671,7 +1780,9 @@ sd_get_parms(struct sd_softc *sc, struct
if (heads * cyls > 0)
sectors = dp->disksize / (heads * cyls);
} else {
- err = scsi_do_mode_sense(sc->sc_link,
+ if (sc->flags & SDF_DYING)
+ goto die;
+ err = scsi_do_mode_sense(sc_link,
PAGE_FLEX_GEOMETRY, buf, (void **)&flex, NULL, NULL,
&secsize, sizeof(*flex) - 4,
flags | SCSI_SILENT, NULL);
@@ -1689,7 +1800,7 @@ sd_get_parms(struct sd_softc *sc, struct
break;
}
-validate:
+ validate:
if (buf)
dma_free(buf, sizeof(*buf));
@@ -1746,16 +1857,24 @@ validate:
}
return (SDGP_RESULT_OK);
+
+ die:
+ dma_free(buf, sizeof(*buf));
+ return (SDGP_RESULT_OFFLINE);
}
void
sd_flush(struct sd_softc *sc, int flags)
{
- struct scsi_link *link = sc->sc_link;
+ struct scsi_link *sc_link;
struct scsi_xfer *xs;
struct scsi_synchronize_cache *cmd;
- if (link->quirks & SDEV_NOSYNCCACHE)
+ if (sc->flags & SDF_DYING)
+ return;
+ sc_link = sc->sc_link;
+
+ if (sc_link->quirks & SDEV_NOSYNCCACHE)
return;
/*
@@ -1764,9 +1883,9 @@ sd_flush(struct sd_softc *sc, int flags)
* that the command is not supported by the device.
*/
- xs = scsi_xs_get(link, flags);
+ xs = scsi_xs_get(sc_link, flags);
if (xs == NULL) {
- SC_DEBUG(link, SDEV_DB1, ("cache sync failed to get xs\n"));
+ SC_DEBUG(sc_link, SDEV_DB1, ("cache sync failed to get xs\n"));
return;
}
@@ -1780,7 +1899,7 @@ sd_flush(struct sd_softc *sc, int flags)
if (scsi_xs_sync(xs) == 0)
sc->flags &= ~SDF_DIRTY;
else
- SC_DEBUG(link, SDEV_DB1, ("cache sync failed\n"));
+ SC_DEBUG(sc_link, SDEV_DB1, ("cache sync failed\n"));
scsi_xs_put(xs);
}