[PATCH] 2.2: O_NONBLOCK for SCSI removable disks and CD-ROMs

From: Chip Salzenberg (chip@valinux.com)
Date: Mon Apr 17 2000 - 15:17:36 EST


O_NONBLOCK (which under Linux is an alias for O_NDELAY) is an open()
flag that is supposed to permit opening a device without waiting for
it to be ready. In the case of drives with removable media,
O_NONBLOCK must permit calling open() on a drive even when it's empty.

I am astounded to find that this basic device feature is missing from
many Linux drivers! The only devices that I am sure support it are
serial ports, floppies, and IDE CD-ROMS. (I haven't checked the
legacy CD-ROM drivers. But CD-ROMs are the easy case anyway -- the
CD-ROM subsystem understands O_NONBLOCK and passes it as a flag to the
CD-dev-specific open func.)

Glaringly missing from this list are SCSI removable disks (e.g. Zip
and Jaz drives) and SCSI CD-ROMs.

The below patch implements support for O_NONBLOCK for SCSI CD-ROMs and
removable SCSI disks. ** Help save the world! ** While this patch
works for me, I haven't enough experience with the SCSI subsystem to
feel confident that I've cought all the issues. I especially invite
Linux SCSI experts to help out here. I'm confident that the idea is
sound, but I may easily have introduced a glitch or three.

STILL TO DO: IDE disks and "floppies". Although the IDE CD-ROM driver
handles O_NONBLOCK, I'm fairly sure that IDE floppy driver doesn't.
And I can't figure out whether IDE disks _need_ to support it ... are
all removables, no matter how large, "floppies" in the IDE world? Hm.

(How in the many worlds of the Terran Empire did Linux go this long
without full support for O_NONBLOCK?! I boggle.)

Here's the patch.

Index: drivers/scsi/sd.c
@@ -151,34 +151,41 @@ static int sd_open(struct inode * inode,
      * is being re-read.
      */
-
     while (rscsi_disks[target].device->busy)
         barrier();
- if(rscsi_disks[target].device->removable) {
+
+ /*
+ * When opening removable disks, we check media...
+ * ... unless media don't matter, due to O_NONBLOCK.
+ */
+ if (rscsi_disks[target].device->removable
+ && !(filp->f_flags & O_NONBLOCK))
+ {
+ /*
+ * Note disk change and/or removal.
+ * Also try to read new partition table (if any).
+ */
         check_disk_change(inode->i_rdev);
 
- /*
- * If the drive is empty, just let the open fail.
- */
- if ( !rscsi_disks[target].ready )
- return -ENXIO;
+ /*
+ * If the drive is empty, let the open fail.
+ */
+ if (rscsi_disks[target].ready)
+ return -ENXIO;
 
- /*
- * Similarly, if the device has the write protect tab set,
- * have the open fail if the user expects to be able to write
- * to the thing.
- */
- if ( (rscsi_disks[target].write_prot) && (filp->f_mode & 2) )
- return -EROFS;
+ /*
+ * If the device has the write protect tab set,
+ * let the open fail if the user expects to be able to write.
+ */
+ if ( (rscsi_disks[target].write_prot) && (filp->f_mode & 2) )
+ return -EROFS;
     }
 
     /*
- * It is possible that the disk changing stuff resulted in the device being taken
- * offline. If this is the case, report this to the user, and don't pretend that
- * the open actually succeeded.
+ * It is possible that the disk changing stuff (or something
+ * else?) resulted in the device being taken offline.
+ * If so, let the open fail.
      */
- if( !rscsi_disks[target].device->online )
- {
+ if (!rscsi_disks[target].device->online)
         return -ENXIO;
- }
 
     /*
@@ -1069,5 +1076,4 @@ static int check_scsidisk_media_change(k
     int target;
     struct inode inode;
- int flag = 0;
 
     target = DEVICE_NR(full_dev);
@@ -1121,9 +1127,9 @@ static int check_scsidisk_media_change(k
      * struct and tested at open ! Daniel Roche ( dan@lectra.fr )
      */
-
     rscsi_disks[target].ready = 1; /* FLOPTICAL */
 
     retval = rscsi_disks[target].device->changed;
- if(!flag) rscsi_disks[target].device->changed = 0;
+ rscsi_disks[target].device->changed = 0;
+
     return retval;
 }

Index: drivers/scsi/sr.h
@@ -29,5 +29,5 @@
         unsigned char sector_bit_size; /* sector size = 2^sector_bit_size */
         unsigned char sector_bit_shift; /* sectors/FS block = 2^sector_bit_shift*/
- unsigned needs_sector_size:1; /* needs to get sector size */
+ unsigned :1; /* (was needs_sector_size) */
         unsigned ten:1; /* support ten byte commands */
         unsigned remap:1; /* support remapping */

Index: drivers/scsi/sr.c
@@ -139,21 +139,17 @@ int sr_media_change(struct cdrom_device_
         retval = scsi_CDs[MINOR(cdi->dev)].device->changed;
         scsi_CDs[MINOR(cdi->dev)].device->changed = 0;
- /* If the disk changed, the capacity will now be different,
- * so we force a re-read of this information */
+
+ /*
+ * If the disk changed, the sector size and capacity may be
+ * different, so we force a re-read of this information.
+ */
         if (retval) {
+ /* discover new sector size */
+ get_sectorsize(MINOR(cdi->dev));
+
                 /* check multisession offset etc */
                 sr_cd_check(cdi);
-
- /*
- * If the disk changed, the capacity will now be different,
- * so we force a re-read of this information
- * Force 2048 for the sector size so that filesystems won't
- * be trying to use something that is too small if the disc
- * has changed.
- */
- scsi_CDs[MINOR(cdi->dev)].needs_sector_size = 1;
-
- scsi_CDs[MINOR(cdi->dev)].sector_size = 2048;
         }
+
         return retval;
 }
@@ -381,6 +377,4 @@ static void rw_intr (Scsi_Cmnd * SCpnt)
 static int sr_open(struct cdrom_device_info *cdi, int purpose)
 {
- check_disk_change(cdi->dev);
-
     if( MINOR(cdi->dev) >= sr_template.dev_max
        || !scsi_CDs[MINOR(cdi->dev)].device)
@@ -404,12 +398,4 @@ static int sr_open(struct cdrom_device_i
         __MOD_INC_USE_COUNT(sr_template.module);
 
- /* If this device did not have media in the drive at boot time, then
- * we would have been unable to get the sector size. Check to see if
- * this is the case, and try again.
- */
-
- if(scsi_CDs[MINOR(cdi->dev)].needs_sector_size)
- get_sectorsize(MINOR(cdi->dev));
-
     return 0;
 }
@@ -920,5 +906,4 @@ void get_sectorsize(int i){
         scsi_CDs[i].capacity = 0x1fffff;
         scsi_CDs[i].sector_size = 2048; /* A guess, just in case */
- scsi_CDs[i].needs_sector_size = 1;
     } else {
         scsi_CDs[i].capacity = 1 + ((buffer[0] << 24) |
@@ -947,14 +932,14 @@ void get_sectorsize(int i){
                                 i, scsi_CDs[i].sector_size);
                         scsi_CDs[i].capacity = 0;
- scsi_CDs[i].needs_sector_size = 1;
+ break;
         }
+ }
+
+ /*
+ * Add this so that we have the ability to correctly gauge
+ * what the device is capable of.
+ */
+ sr_sizes[i] = scsi_CDs[i].capacity >> (BLOCK_SIZE_BITS - 9);
 
- /*
- * Add this so that we have the ability to correctly gauge
- * what the device is capable of.
- */
- scsi_CDs[i].needs_sector_size = 0;
- sr_sizes[i] = scsi_CDs[i].capacity >> (BLOCK_SIZE_BITS - 9);
- };
     scsi_free(buffer, 512);
 }
@@ -1062,5 +1047,4 @@ void sr_finish()
         scsi_CDs[i].capacity = 0x1fffff;
         scsi_CDs[i].sector_size = 2048; /* A guess, just in case */
- scsi_CDs[i].needs_sector_size = 1;
         scsi_CDs[i].device->changed = 1; /* force recheck CD type */
 #if 0

-- 
Chip Salzenberg              - a.k.a. -              <chip@valinux.com>
"I wanted to play hopscotch with the impenetrable mystery of existence,
    but he stepped in a wormhole and had to go in early."  // MST3K

- To unsubscribe from this list: send the line "unsubscribe linux-kernel" in the body of a message to majordomo@vger.rutgers.edu Please read the FAQ at http://www.tux.org/lkml/



This archive was generated by hypermail 2b29 : Sun Apr 23 2000 - 21:00:11 EST