Subject: CHanges to sd and cd drivers
To: None <tech-kern@netbsd.org>
From: Manuel Bouyer <bouyer@antioche.lip6.fr>
List: tech-kern
Date: 01/20/1999 16:30:49
--PEIAKu/WMn1b1Hv9
Content-Type: text/plain; charset=us-ascii

Hi,
I'd like to make a few changes to the sd and cd drivers
(patches included below):
- don't reset the SDEV_MEDIA_LOADED flag when the last partition of a sd device
  is closed, but only when we believe the media has changed (cd already behaves
  this way). The reset of SDEV_MEDIA_LOADED is done in the generic error
  handler. This change allows to load a in-memory disklabel, and then
  mount a partition. For now one would have to use a kludge to keep a parition
  open while changing the disklabel, otherwise it would get lost when
  'disklabel' closes the raw device.
- allows the raw partition of a sd or cd device to be opened even if
  scsi_start() returns an error. The main goal here is to allow IOCTLs even
  when there's no media in a removable device (for example, the CDIOCCLOSE
  ioctl is totally useless for now, this is also needed for changers that don't
  have a separate LUN from the drive, like ATAPI changers),
  but one could want to send some commands to a disk that doesn't spin up to
  help diagnose or solve the problem ... 
  read/write are allowed only if SDEV_MEDIA_LOADED is set, which doesn't
  happen if scsipi_start() failed ...
  For now only an explicit list of IOCTL are allowed when SDEV_MEDIA_LOADED
  is not set. Maybe all ioctls should be allowed, letting the device return
  an error if appropriate.

Another change that could be made: add SCSI_IGNORE_NOT_READY when
unlocking the device (for CDIOCALLOW, and DIOCLOCK when unlocking), to that
a CD tray could be opened by eject(1) even when no disk is loaded
(this would also require to change eject to use the raw part instead of
'a'; it could also be changed to issue CDIOCCLOSE ioctls as well).

These changes have been tested with a SCSI magneto-opetical drive and an
ATAPI CDROM, all seems to behave as expected so far. If no one complains,
I'll commit these changes next week.

--
Manuel Bouyer, LIP6, Universite Paris VI.           Manuel.Bouyer@lip6.fr
--

--PEIAKu/WMn1b1Hv9
Content-Type: text/plain; charset=us-ascii
Content-Disposition: attachment; filename="scsi.diff"

Index: cd.c
===================================================================
RCS file: /cvsroot/src/sys/dev/scsipi/cd.c,v
retrieving revision 1.118
diff -u -r1.118 cd.c
--- cd.c	1999/01/04 15:32:08	1.118
+++ cd.c	1999/01/20 14:59:38
@@ -70,6 +70,7 @@
 #include <sys/disklabel.h>
 #include <sys/disk.h>
 #include <sys/cdio.h>
+#include <sys/scsiio.h>
 #include <sys/proc.h>
 #include <sys/conf.h>
 #if NRND > 0
@@ -234,6 +235,7 @@
 		return (ENXIO);
 
 	sc_link = cd->sc_link;
+	part = CDPART(dev);
 
 	SC_DEBUG(sc_link, SDEV_DB1,
 	    ("cdopen: dev=0x%x (unit %d (of %d), partition %d)\n", dev, unit,
@@ -250,7 +252,7 @@
 	if ((error = cdlock(cd)) != 0)
 		goto bad4;
 
-	if (cd->sc_dk.dk_openmask != 0) {
+	if ((sc_link->flags & SDEV_OPEN) != 0) {
 		/*
 		 * If any partition is open, but the disk has been invalidated,
 		 * disallow further opens.
@@ -269,14 +271,22 @@
 		if (error)
 			goto bad3;
 
-		/* Start the pack spinning if necessary. */
+		/*
+		 * Start the pack spinning if necessary. Always allow the
+		 * raw parition to be opened, for raw IOCTLs. Data transfers
+		 * will check for SDEV_MEDIA_LOADED.
+		 */
 		error = scsipi_start(sc_link, SSS_START,
 		    SCSI_IGNORE_ILLEGAL_REQUEST | SCSI_IGNORE_MEDIA_CHANGE |
 		    SCSI_SILENT);
 		SC_DEBUG(sc_link, SDEV_DB1,
 		    ("cdopen: scsipi_start, error=%d\n", error));
-		if (error)
-			goto bad3;
+		if (error) {
+			if (part != RAW_PART) 
+				goto bad3;
+			else
+				goto out;
+		}
 
 		sc_link->flags |= SDEV_OPEN;
 
@@ -304,8 +314,6 @@
 		}
 	}
 
-	part = CDPART(dev);
-
 	/* Check that the partition exists. */
 	if (part != RAW_PART &&
 	    (part >= cd->sc_dk.dk_label->d_npartitions ||
@@ -314,7 +322,7 @@
 		goto bad;
 	}
 
-	/* Insure only one open at a time. */
+out:	/* Insure only one open at a time. */
 	switch (fmt) {
 	case S_IFCHR:
 		cd->sc_dk.dk_copenmask |= (1 << part);
@@ -408,13 +416,6 @@
 	SC_DEBUG(cd->sc_link, SDEV_DB1,
 	    ("%ld bytes @ blk %d\n", bp->b_bcount, bp->b_blkno));
 	/*
-	 * The transfer must be a whole number of blocks.
-	 */
-	if ((bp->b_bcount % cd->sc_dk.dk_label->d_secsize) != 0) {
-		bp->b_error = EINVAL;
-		goto bad;
-	}
-	/*
 	 * If the device has been made invalid, error out
 	 * maybe the media changed
 	 */
@@ -423,6 +424,13 @@
 		goto bad;
 	}
 	/*
+	 * The transfer must be a whole number of blocks.
+	 */
+	if ((bp->b_bcount % cd->sc_dk.dk_label->d_secsize) != 0) {
+		bp->b_error = EINVAL;
+		goto bad;
+	}
+	/*
 	 * If it's a null transfer, return immediately
 	 */
 	if (bp->b_bcount == 0)
@@ -702,10 +710,40 @@
 	SC_DEBUG(cd->sc_link, SDEV_DB2, ("cdioctl 0x%lx ", cmd));
 
 	/*
-	 * If the device is not valid.. abandon ship
+	 * If the device is not valid, some IOCTLs can still be
+	 * handled on the raw partition. Check this here.
 	 */
-	if ((cd->sc_link->flags & SDEV_MEDIA_LOADED) == 0)
-		return (EIO);
+	if ((cd->sc_link->flags & SDEV_MEDIA_LOADED) == 0) {
+		switch (cmd) {
+		case DIOCWLABEL:
+		case DIOCLOCK:
+		case DIOCEJECT:
+		case SCIOCIDENTIFY:
+		case OSCIOCIDENTIFY:
+		case SCIOCCOMMAND:
+		case SCIOCDEBUG:
+		case CDIOCGETVOL:
+		case CDIOCSETVOL:
+		case CDIOCSETMONO:
+		case CDIOCSETSTEREO:
+		case CDIOCSETMUTE:
+		case CDIOCSETLEFT:
+		case CDIOCSETRIGHT:
+		case CDIOCCLOSE:
+		case CDIOCALLOW:
+		case CDIOCPREVENT:
+		case CDIOCSETDEBUG:
+		case CDIOCCLRDEBUG:
+		case CDIOCRESET:
+		case SCIOCRESET:
+		case CDIOCLOADUNLOAD:
+			if (CDPART(dev) == RAW_PART)
+				break;
+		/* FALLTHROUGH */
+		default:
+			return (EIO);
+		}
+	}
 
 	switch (cmd) {
 	case DIOCGDINFO:
@@ -938,6 +976,7 @@
 		cd->sc_link->flags &= ~(SDEV_DB1 | SDEV_DB2);
 		return (0);
 	case CDIOCRESET:
+	case SCIOCRESET:
 		return (cd_reset(cd));
 	case CDIOCLOADUNLOAD: {
 		struct ioc_load_unload *args = (struct ioc_load_unload *)addr;
Index: sd.c
===================================================================
RCS file: /cvsroot/src/sys/dev/scsipi/sd.c,v
retrieving revision 1.139
diff -u -r1.139 sd.c
--- sd.c	1999/01/19 10:57:11	1.139
+++ sd.c	1999/01/20 14:59:39
@@ -63,6 +63,7 @@
 #include <sys/file.h>
 #include <sys/stat.h>
 #include <sys/ioctl.h>
+#include <sys/scsiio.h>
 #include <sys/buf.h>
 #include <sys/uio.h>
 #include <sys/malloc.h>
@@ -273,10 +274,11 @@
 		return (ENXIO);
 
 	sc_link = sd->sc_link;
+	part = SDPART(dev);
 
 	SC_DEBUG(sc_link, SDEV_DB1,
 	    ("sdopen: dev=0x%x (unit %d (of %d), partition %d)\n", dev, unit,
-	    sd_cd.cd_ndevs, SDPART(dev)));
+	    sd_cd.cd_ndevs, part));
 
 	/*
 	 * If this is the first open of this device, add a reference
@@ -289,12 +291,13 @@
 	if ((error = sdlock(sd)) != 0)
 		goto bad4;
 
-	if (sd->sc_dk.dk_openmask != 0) {
+	if ((sc_link->flags & SDEV_OPEN) != 0) {
 		/*
 		 * If any partition is open, but the disk has been invalidated,
-		 * disallow further opens.
+		 * disallow further opens of non-raw partition
 		 */
-		if ((sc_link->flags & SDEV_MEDIA_LOADED) == 0) {
+		if ((sc_link->flags & SDEV_MEDIA_LOADED) == 0 &&
+		    part != RAW_PART) {
 			error = EIO;
 			goto bad3;
 		}
@@ -306,12 +309,20 @@
 		if (error)
 			goto bad3;
 
-		/* Start the pack spinning if necessary. */
+		/*
+		 * Start the pack spinning if necessary. Always allow the
+		 * raw parition to be opened, for raw IOCTLs. Data transfers
+		 * will check for SDEV_MEDIA_LOADED.
+		 */
 		error = scsipi_start(sc_link, SSS_START,
 		    SCSI_IGNORE_ILLEGAL_REQUEST |
 		    SCSI_IGNORE_MEDIA_CHANGE | SCSI_SILENT);
-		if (error)
-			goto bad3;
+		if (error) {
+			if (part != RAW_PART)
+				goto bad3;
+			else
+				goto out;
+		}
 
 		sc_link->flags |= SDEV_OPEN;
 
@@ -345,8 +356,6 @@
 		}
 	}
 
-	part = SDPART(dev);
-
 	/* Check that the partition exists. */
 	if (part != RAW_PART &&
 	    (part >= sd->sc_dk.dk_label->d_npartitions ||
@@ -355,7 +364,7 @@
 		goto bad;
 	}
 
-	/* Insure only one open at a time. */
+out:	/* Insure only one open at a time. */
 	switch (fmt) {
 	case S_IFCHR:
 		sd->sc_dk.dk_copenmask |= (1 << part);
@@ -430,7 +439,7 @@
 
 		scsipi_prevent(sd->sc_link, PR_ALLOW,
 		    SCSI_IGNORE_ILLEGAL_REQUEST | SCSI_IGNORE_NOT_READY);
-		sd->sc_link->flags &= ~(SDEV_OPEN|SDEV_MEDIA_LOADED);
+		sd->sc_link->flags &= ~SDEV_OPEN;
 
 		scsipi_wait_drain(sd->sc_link);
 
@@ -457,17 +466,17 @@
 	SC_DEBUG(sd->sc_link, SDEV_DB1,
 	    ("%ld bytes @ blk %d\n", bp->b_bcount, bp->b_blkno));
 	/*
-	 * The transfer must be a whole number of blocks.
+	 * If the device has been made invalid, error out
 	 */
-	if ((bp->b_bcount % sd->sc_dk.dk_label->d_secsize) != 0) {
-		bp->b_error = EINVAL;
+	if ((sd->sc_link->flags & SDEV_MEDIA_LOADED) == 0) {
+		bp->b_error = EIO;
 		goto bad;
 	}
 	/*
-	 * If the device has been made invalid, error out
+	 * The transfer must be a whole number of blocks.
 	 */
-	if ((sd->sc_link->flags & SDEV_MEDIA_LOADED) == 0) {
-		bp->b_error = EIO;
+	if ((bp->b_bcount % sd->sc_dk.dk_label->d_secsize) != 0) {
+		bp->b_error = EINVAL;
 		goto bad;
 	}
 	/*
@@ -737,10 +746,25 @@
 	SC_DEBUG(sd->sc_link, SDEV_DB2, ("sdioctl 0x%lx ", cmd));
 
 	/*
-	 * If the device is not valid.. abandon ship
+	 * If the device is not valid, some IOCTLs can still be
+	 * handled on the raw partition. Check this here.
 	 */
-	if ((sd->sc_link->flags & SDEV_MEDIA_LOADED) == 0)
-		return (EIO);
+	if ((sd->sc_link->flags & SDEV_MEDIA_LOADED) == 0) {
+		switch (cmd) {
+		case DIOCWLABEL:
+		case DIOCLOCK:
+		case DIOCEJECT:
+		case SCIOCIDENTIFY:
+		case OSCIOCIDENTIFY:
+		case SCIOCCOMMAND:
+		case SCIOCDEBUG:
+			if (SDPART(dev) == RAW_PART)
+				break;
+		/* FALLTHROUGH */
+		default:
+			return (EIO);
+		}
+	}
 
 	switch (cmd) {
 	case DIOCGDINFO:
@@ -937,7 +961,7 @@
 	/*
 	 * If the device is not open yet, let the generic code handle it.
 	 */
-	if ((sc_link->flags & SDEV_OPEN) == 0) {
+	if ((sc_link->flags & SDEV_MEDIA_LOADED) == 0) {
 		return (retval);
 	}
 
@@ -1005,7 +1029,9 @@
 
 	if (omask == 0 && sdopen(dev, 0, S_IFBLK, NULL) != 0)
 		return (-1);
-	if (sd->sc_dk.dk_label->d_partitions[part].p_fstype != FS_SWAP)
+	if ((sd->sc_link->flags & SDEV_MEDIA_LOADED) == 0)
+		size = -1;
+	else if (sd->sc_dk.dk_label->d_partitions[part].p_fstype != FS_SWAP)
 		size = -1;
 	else
 		size = sd->sc_dk.dk_label->d_partitions[part].p_size *
@@ -1057,16 +1083,9 @@
 	if (unit >= sd_cd.cd_ndevs || (sd = sd_cd.cd_devs[unit]) == NULL)
 		return (ENXIO);
 
-	/*
-	 * XXX Can't do this check, since the media might have been
-	 * XXX marked `invalid' by successful unmounting of all
-	 * XXX filesystems.
-	 */
-#if 0
 	/* Make sure it was initialized. */
 	if ((sd->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;

--PEIAKu/WMn1b1Hv9--