]> git.neil.brown.name Git - history.git/commitdiff
[PATCH] scsi disk (sd) driver 2.5.12
authorDouglas Gilbert <dougg@torque.net>
Wed, 1 May 2002 03:07:03 +0000 (20:07 -0700)
committerJames Bottomley <jejb@mulgrave.(none)>
Wed, 1 May 2002 03:07:03 +0000 (20:07 -0700)
This patch has the last bit of Justin Gibb's patch
described in:
http://marc.theaimsgroup.com/?l=linux-scsi&m=101200279101550&w=2
[this bit for the sd driver]

There is also a major code cleanup of the sd driver with
documentation headers added and a few obvious bugs fixed
described in:
http://marc.theaimsgroup.com/?l=linux-scsi&m=101798201714399&w=2
I did this cleanup.

Justin's patch has been in Dave's tree for several months while
my code cleanup patch has been there since 2.5.9-dj1 .

drivers/scsi/sd.c

index f181515cb037502456ee7605526bcd62cd201a83..4402a929557a9afd5f47237f75dd609ac6695c69 100644 (file)
@@ -5,29 +5,28 @@
  *      Linux scsi disk driver
  *              Initial versions: Drew Eckhardt
  *              Subsequent revisions: Eric Youngdale
+ *     Modification history:
+ *       - Drew Eckhardt <drew@colorado.edu> original
+ *       - Eric Youngdale <eric@andante.org> add scatter-gather, multiple 
+ *         outstanding request, and other enhancements.
+ *         Support loadable low-level scsi drivers.
+ *       - Jirka Hanika <geo@ff.cuni.cz> support more scsi disks using 
+ *         eight major numbers.
+ *       - Richard Gooch <rgooch@atnf.csiro.au> support devfs.
+ *      - Torben Mathiasen <tmm@image.dk> Resource allocation fixes in 
+ *        sd_init and cleanups.
+ *      - Alex Davis <letmein@erols.com> Fix problem where partition info
+ *        not being read in sd_open. Fix problem where removable media 
+ *        could be ejected after sd_open.
+ *      - Douglas Gilbert <dgilbert@interlog.com> cleanup for lk 2.5 series
  *
- *      <drew@colorado.edu>
- *
- *       Modified by Eric Youngdale ericy@andante.org to
- *       add scatter-gather, multiple outstanding request, and other
- *       enhancements.
- *
- *       Modified by Eric Youngdale eric@andante.org to support loadable
- *       low-level scsi drivers.
- *
- *       Modified by Jirka Hanika geo@ff.cuni.cz to support more
- *       scsi disks using eight major numbers.
- *
- *       Modified by Richard Gooch rgooch@atnf.csiro.au to support devfs.
- *     
- *      Modified by Torben Mathiasen tmm@image.dk
- *       Resource allocation fixes in sd_init and cleanups.
- *     
- *      Modified by Alex Davis <letmein@erols.com>
- *       Fix problem where partition info not being read in sd_open.
- *     
- *      Modified by Alex Davis <letmein@erols.com>
- *       Fix problem where removable media could be ejected after sd_open.
+ *     Logging policy (needs CONFIG_SCSI_LOGGING defined):
+ *      - setting up transfer: SCSI_LOG_HLQUEUE levels 1 and 2
+ *      - end of transfer (bh + scsi_lib): SCSI_LOG_HLCOMPLETE level 1
+ *      - entering sd_ioctl: SCSI_LOG_IOCTL level 1
+ *      - entering other commands: SCSI_LOG_HLQUEUE level 3
+ *     Note: when the logging level is set by the user, it must be greater
+ *     than the level indicated above to trigger output.       
  */
 
 #include <linux/config.h>
@@ -50,6 +49,7 @@
 #include <asm/io.h>
 
 #define MAJOR_NR SCSI_DISK0_MAJOR
+#define LOCAL_END_REQUEST
 #include <linux/blk.h>
 #include <linux/blkpg.h>
 #include "scsi.h"
@@ -61,6 +61,8 @@
 
 #include <linux/genhd.h>
 
+static char sd_version_str[] = "Version: 2.0.3 (20020417)";
+
 #define SD_MAJOR(i) (!(i) ? SCSI_DISK0_MAJOR : SCSI_DISK1_MAJOR-1+(i))
 
 #define SCSI_DISKS_PER_MAJOR   16
 #define SD_TIMEOUT (30 * HZ)
 #define SD_MOD_TIMEOUT (75 * HZ)
 
+#define SD_DSK_ARR_LUMP 6 /* amount to over allocate sd_dsk_arr by */
+
+
 struct hd_struct *sd;
 
-static Scsi_Disk *rscsi_disks;
+static Scsi_Disk ** sd_dsk_arr;
+static rwlock_t sd_dsk_arr_lock = RW_LOCK_UNLOCKED;
+
 static int *sd_sizes;
 static int *sd_blocksizes;
 static int *sd_max_sectors;
@@ -89,7 +96,7 @@ static int *sd_max_sectors;
 static int check_scsidisk_media_change(kdev_t);
 static int fop_revalidate_scsidisk(kdev_t);
 
-static int sd_init_onedisk(int);
+static int sd_init_onedisk(Scsi_Disk * sdkp, int dsk_nr);
 
 static int sd_init(void);
 static void sd_finish(void);
@@ -118,36 +125,81 @@ static struct Scsi_Device_Template sd_template = {
        init_command:sd_init_command,
 };
 
-static void rw_intr(Scsi_Cmnd * SCpnt);
+static void sd_rw_intr(Scsi_Cmnd * SCpnt);
+
+static Scsi_Disk * sd_get_sdisk(int index);
+
 
 #if defined(CONFIG_PPC)
-/*
- * Moved from arch/ppc/pmac_setup.c.  This is where it really belongs.
- */
+/**
+ *     sd_find_target - find kdev_t of first scsi disk that matches
+ *     given host and scsi_id. 
+ *     @host: Scsi_Host object pointer that owns scsi device of interest
+ *     @scsi_id: scsi (target) id number of device of interest
+ *
+ *     Returns kdev_t of first scsi device that matches arguments or
+ *     NODEV of no match.
+ *
+ *     Notes: Looks like a hack, should scan for <host,channel,id,lin>
+ *     tuple.
+ *     [Architectural dependency: ppc only.] Moved here from 
+ *     arch/ppc/pmac_setup.c.
+ **/
 kdev_t __init
-sd_find_target(void *host, int tgt)
+sd_find_target(void *hp, int scsi_id)
 {
-    Scsi_Disk *dp;
-    int i;
-    for (dp = rscsi_disks, i = 0; i < sd_template.dev_max; ++i, ++dp)
-        if (dp->device != NULL && dp->device->host == host
-            && dp->device->id == tgt)
-            return MKDEV_SD(i);
-    return NODEV;
+       Scsi_Disk *sdkp;
+       Scsi_Device *sdp;
+       Scsi_Host *shp = hp;
+       int dsk_nr;
+       unsigned long iflags;
+
+       SCSI_LOG_HLQUEUE(3, printk("sd_find_target: host_nr=%d, "
+                           "scsi_id=%d\n", shp->host_no, scsi_id));
+       read_lock_irqsave(&sd_dsk_arr_lock, iflags);
+       for (dsk_nr = 0; dsk_nr < sd_template.dev_max; ++dsk_nr) {
+               if (NULL == (sdkp = sd_dsk_arr[dsk_nr]))
+                       continue;
+               sdp = sdkp->device;
+               if (sdp && (sdp->host == shp) && (sdp->id == scsi_id)) {
+                       read_unlock_irqrestore(&sd_dsk_arr_lock, iflags);
+                       return MKDEV_SD(k);
+               }
+       }
+       read_unlock_irqrestore(&sd_dsk_arr_lock, iflags);
+       return NODEV;
 }
 #endif
 
-static int sd_ioctl(struct inode * inode, struct file * file, unsigned int cmd, unsigned long arg)
+/**
+ *     sd_ioctl - process an ioctl
+ *     @inode: only i_rdev member may be used
+ *     @filp: only f_mode and f_flags may be used
+ *     @cmd: ioctl command number
+ *     @arg: this is third argument given to ioctl(2) system call.
+ *     Often contains a pointer.
+ *
+ *     Returns 0 if successful (some ioctls return postive numbers on
+ *     success as well). Returns a negated errno value in case of error.
+ *
+ *     Note: most ioctls are forward onto the block subsystem or further
+ *     down in the scsi subsytem.
+ **/
+static int sd_ioctl(struct inode * inode, struct file * filp, 
+                   unsigned int cmd, unsigned long arg)
 {
        kdev_t dev = inode->i_rdev;
+        Scsi_Disk * sdkp;
        struct Scsi_Host * host;
-       Scsi_Device * SDev;
+       Scsi_Device * sdp;
        int diskinfo[4];
+       int dsk_nr = DEVICE_NR(dev);
     
-       SDev = rscsi_disks[DEVICE_NR(dev)].device;
-       if (!SDev)
+       SCSI_LOG_IOCTL(1, printk("sd_ioctl: dsk_nr=%d, cmd=0x%x\n",
+                      dsk_nr, cmd));
+       sdkp = sd_get_sdisk(dsk_nr);
+       if ((NULL == sdkp) || (NULL == (sdp = sdkp->device)))
                return -ENODEV;
-
        /*
         * If we are in the middle of error recovery, don't let anyone
         * else try and use this device.  Also, if error recovery fails, it
@@ -155,10 +207,8 @@ static int sd_ioctl(struct inode * inode, struct file * file, unsigned int cmd,
         * access to the device is prohibited.
         */
 
-       if( !scsi_block_when_processing_errors(SDev) )
-       {
+       if( !scsi_block_when_processing_errors(sdp) )
                return -ENODEV;
-       }
 
        switch (cmd) 
        {
@@ -168,58 +218,59 @@ static int sd_ioctl(struct inode * inode, struct file * file, unsigned int cmd,
                        if(!loc)
                                return -EINVAL;
 
-                       host = rscsi_disks[DEVICE_NR(dev)].device->host;
+                       host = sdp->host;
        
                        /* default to most commonly used values */
        
                        diskinfo[0] = 0x40;
                        diskinfo[1] = 0x20;
-                       diskinfo[2] = rscsi_disks[DEVICE_NR(dev)].capacity >> 11;
+                       diskinfo[2] = sdkp->capacity >> 11;
        
-                       /* override with calculated, extended default, or driver values */
+                       /* override with calculated, extended default, 
+                          or driver values */
        
                        if(host->hostt->bios_param != NULL)
-                               host->hostt->bios_param(&rscsi_disks[DEVICE_NR(dev)],
-                                           dev,
-                                           &diskinfo[0]);
-                       else scsicam_bios_param(&rscsi_disks[DEVICE_NR(dev)],
-                                       dev, &diskinfo[0]);
+                               host->hostt->bios_param(sdkp, dev, 
+                                                       &diskinfo[0]);
+                       else
+                               scsicam_bios_param(sdkp, dev, &diskinfo[0]);
                        if (put_user(diskinfo[0], &loc->heads) ||
                                put_user(diskinfo[1], &loc->sectors) ||
                                put_user(diskinfo[2], &loc->cylinders) ||
-                               put_user((unsigned) get_start_sect(inode->i_rdev),
+                               put_user((unsigned) 
+                                            get_start_sect(inode->i_rdev),
                                         (unsigned long *) &loc->start))
                                return -EFAULT;
                        return 0;
                }
                case HDIO_GETGEO_BIG:
                {
-                       struct hd_big_geometry *loc = (struct hd_big_geometry *) arg;
+                       struct hd_big_geometry *loc = 
+                                       (struct hd_big_geometry *) arg;
 
                        if(!loc)
                                return -EINVAL;
-
-                       host = rscsi_disks[DEVICE_NR(dev)].device->host;
+                       host = sdp->host;
 
                        /* default to most commonly used values */
-
                        diskinfo[0] = 0x40;
                        diskinfo[1] = 0x20;
-                       diskinfo[2] = rscsi_disks[DEVICE_NR(dev)].capacity >> 11;
+                       diskinfo[2] = sdkp->capacity >> 11;
 
-                       /* override with calculated, extended default, or driver values */
-
-                       if(host->hostt->bios_param != NULL)
-                               host->hostt->bios_param(&rscsi_disks[DEVICE_NR(dev)],
-                                           dev,
-                                           &diskinfo[0]);
-                       else scsicam_bios_param(&rscsi_disks[DEVICE_NR(dev)],
-                                       dev, &diskinfo[0]);
+                       /* override with calculated, extended default, 
+                          or driver values */
+                       if(host->hostt->bios_param != NULL) 
+                               host->hostt->bios_param(sdkp, dev,
+                                                       &diskinfo[0]);
+                       else
+                               scsicam_bios_param(sdkp, dev, &diskinfo[0]);
 
                        if (put_user(diskinfo[0], &loc->heads) ||
                                put_user(diskinfo[1], &loc->sectors) ||
-                               put_user(diskinfo[2], (unsigned int *) &loc->cylinders) ||
-                               put_user((unsigned)get_start_sect(inode->i_rdev),
+                               put_user(diskinfo[2], 
+                                        (unsigned int *) &loc->cylinders) ||
+                               put_user((unsigned)
+                                          get_start_sect(inode->i_rdev),
                                         (unsigned long *)&loc->start))
                                return -EFAULT;
                        return 0;
@@ -243,15 +294,14 @@ static int sd_ioctl(struct inode * inode, struct file * file, unsigned int cmd,
                        return revalidate_scsidisk(dev, 1);
 
                default:
-                       return scsi_ioctl(rscsi_disks[DEVICE_NR(dev)].device,
-                                         cmd, (void *) arg);
+                       return scsi_ioctl(sdp, cmd, (void *) arg);
        }
 }
 
-static void sd_devname(unsigned int disknum, char *buffer)
+static void sd_dskname(unsigned int dsk_nr, char *buffer)
 {
-       if (disknum < 26)
-               sprintf(buffer, "sd%c", 'a' + disknum);
+       if (dsk_nr < 26)
+               sprintf(buffer, "sd%c", 'a' + dsk_nr);
        else {
                unsigned int min1;
                unsigned int min2;
@@ -259,67 +309,88 @@ static void sd_devname(unsigned int disknum, char *buffer)
                 * For larger numbers of disks, we need to go to a new
                 * naming scheme.
                 */
-               min1 = disknum / 26;
-               min2 = disknum % 26;
+               min1 = dsk_nr / 26;
+               min2 = dsk_nr % 26;
                sprintf(buffer, "sd%c%c", 'a' + min1 - 1, 'a' + min2);
        }
 }
 
+/**
+ *     sd_find_queue - yields queue associated with device
+ *     @dev: kernel device descriptor (kdev_t)
+ *
+ *     Returns NULL if no match, otherwise returns pointer to associated
+ *     request queue.
+ *
+ *     Note: this function is invoked (often) from the block subsystem
+ *     and should not wait on anything (sd_get_sdisk() does have a read
+ *     spinlock).
+ **/
 static request_queue_t *sd_find_queue(kdev_t dev)
 {
-       Scsi_Disk *dpnt;
-       int target;
-       target = DEVICE_NR(dev);
+       Scsi_Disk *sdkp;
+       int dsk_nr = DEVICE_NR(dev);
 
-       dpnt = &rscsi_disks[target];
-       if (!dpnt)
+       sdkp = sd_get_sdisk(dsk_nr);
+       if (sdkp && sdkp->device)
+               return &sdkp->device->request_queue;
+       else
                return NULL;    /* No such device */
-       return &dpnt->device->request_queue;
 }
 
+/**
+ *     sd_init_command - build a scsi (read or write) command from
+ *     information in the request structure.
+ *     @SCpnt: pointer to mid-level's per scsi command structure that
+ *     contains request and into which the scsi command is written
+ *
+ *     Returns 1 if successful and 0 if error (or cannot be done now).
+ **/
 static int sd_init_command(Scsi_Cmnd * SCpnt)
 {
-       int dev, devm, block, this_count;
-       Scsi_Disk *dpnt;
+       int dsk_nr, part_nr, block, this_count;
+       Scsi_Device *sdp;
 #if CONFIG_SCSI_LOGGING
        char nbuff[6];
 #endif
-
        /*
         * don't support specials for nwo
         */
        if (!(SCpnt->request.flags & REQ_CMD))
                return 0;
 
-       devm = SD_PARTITION(SCpnt->request.rq_dev);
-       dev = DEVICE_NR(SCpnt->request.rq_dev);
+       part_nr = SD_PARTITION(SCpnt->request.rq_dev);
+       dsk_nr = DEVICE_NR(SCpnt->request.rq_dev);
 
        block = SCpnt->request.sector;
        this_count = SCpnt->request_bufflen >> 9;
 
-       SCSI_LOG_HLQUEUE(1, printk("Doing sd request, dev = %d, block = %d\n", devm, block));
-
-       dpnt = &rscsi_disks[dev];
-       if (devm >= (sd_template.dev_max << 4) || (devm & 0xf) ||
-           !dpnt ||
-           !dpnt->device->online ||
-           block + SCpnt->request.nr_sectors > sd[devm].nr_sects) {
-               SCSI_LOG_HLQUEUE(2, printk("Finishing %ld sectors\n", SCpnt->request.nr_sectors));
+       SCSI_LOG_HLQUEUE(1, printk("sd_command_init: dsk_nr=%d, block=%d, "
+                           "count=%d\n", dsk_nr, block, this_count));
+
+       sdp = SCpnt->device;
+       /* >>>>> the "(part_nr & 0xf)" excludes 15th partition, why?? */
+       /* >>>>> this change is not in the lk 2.5 series */
+       if (part_nr >= (sd_template.dev_max << 4) || (part_nr & 0xf) ||
+           !sdp || !sdp->online ||
+           block + SCpnt->request.nr_sectors > sd[part_nr].nr_sects) {
+               SCSI_LOG_HLQUEUE(2, printk("Finishing %ld sectors\n", 
+                                SCpnt->request.nr_sectors));
                SCSI_LOG_HLQUEUE(2, printk("Retry with 0x%p\n", SCpnt));
                return 0;
        }
 
-       if (dpnt->device->changed) {
+       if (sdp->changed) {
                /*
-                * quietly refuse to do anything to a changed disc until the changed
-                * bit has been reset
+                * quietly refuse to do anything to a changed disc until 
+                * the changed bit has been reset
                 */
                /* printk("SCSI disk has been changed. Prohibiting further I/O.\n"); */
                return 0;
        }
-       SCSI_LOG_HLQUEUE(2, sd_devname(devm, nbuff));
-       SCSI_LOG_HLQUEUE(2, printk("%s : real dev = /dev/%d, block = %d\n",
-                                  nbuff, dev, block));
+       SCSI_LOG_HLQUEUE(2, sd_dskname(dsk_nr, nbuff));
+       SCSI_LOG_HLQUEUE(2, printk("%s : [part_nr=%d], block=%d\n",
+                                  nbuff, part_nr, block));
 
        /*
         * If we have a 1K hardware sectorsize, prevent access to single
@@ -332,27 +403,27 @@ static int sd_init_command(Scsi_Cmnd * SCpnt)
         * and not force the scsi disk driver to use bounce buffers
         * for this.
         */
-       if (dpnt->device->sector_size == 1024) {
+       if (sdp->sector_size == 1024) {
                if ((block & 1) || (SCpnt->request.nr_sectors & 1)) {
-                       printk("sd.c:Bad block number requested");
+                       printk(KERN_ERR "sd: Bad block number requested");
                        return 0;
                } else {
                        block = block >> 1;
                        this_count = this_count >> 1;
                }
        }
-       if (dpnt->device->sector_size == 2048) {
+       if (sdp->sector_size == 2048) {
                if ((block & 3) || (SCpnt->request.nr_sectors & 3)) {
-                       printk("sd.c:Bad block number requested");
+                       printk(KERN_ERR "sd: Bad block number requested");
                        return 0;
                } else {
                        block = block >> 2;
                        this_count = this_count >> 2;
                }
        }
-       if (dpnt->device->sector_size == 4096) {
+       if (sdp->sector_size == 4096) {
                if ((block & 7) || (SCpnt->request.nr_sectors & 7)) {
-                       printk("sd.c:Bad block number requested");
+                       printk(KERN_ERR "sd: Bad block number requested");
                        return 0;
                } else {
                        block = block >> 3;
@@ -360,7 +431,7 @@ static int sd_init_command(Scsi_Cmnd * SCpnt)
                }
        }
        if (rq_data_dir(&SCpnt->request) == WRITE) {
-               if (!dpnt->device->writeable) {
+               if (!sdp->writeable) {
                        return 0;
                }
                SCpnt->cmnd[0] = WRITE_6;
@@ -368,13 +439,16 @@ static int sd_init_command(Scsi_Cmnd * SCpnt)
        } else if (rq_data_dir(&SCpnt->request) == READ) {
                SCpnt->cmnd[0] = READ_6;
                SCpnt->sc_data_direction = SCSI_DATA_READ;
-       } else
-               panic("Unknown sd command %lx\n", SCpnt->request.flags);
+       } else {
+               printk(KERN_ERR "sd: Unknown command %lx\n", 
+                      SCpnt->request.flags);
+/* overkill    panic("Unknown sd command %lx\n", SCpnt->request.flags); */
+               return 0;
+       }
 
-       SCSI_LOG_HLQUEUE(2, printk("%s : %s %d/%ld 512 byte blocks.\n",
-                                  nbuff,
-                  (rq_data_dir(&SCpnt->request) == WRITE) ? "writing" : "reading",
-                                this_count, SCpnt->request.nr_sectors));
+       SCSI_LOG_HLQUEUE(2, printk("%s : %s %d/%ld 512 byte blocks.\n", 
+               nbuff, (rq_data_dir(&SCpnt->request) == WRITE) ? 
+               "writing" : "reading", this_count, SCpnt->request.nr_sectors));
 
        SCpnt->cmnd[1] = (SCpnt->device->scsi_level <= SCSI_2) ?
                         ((SCpnt->lun << 5) & 0xe0) : 0;
@@ -407,7 +481,7 @@ static int sd_init_command(Scsi_Cmnd * SCpnt)
         * host adapter, it's safe to assume that we can at least transfer
         * this many bytes between each connect / disconnect.
         */
-       SCpnt->transfersize = dpnt->device->sector_size;
+       SCpnt->transfersize = sdp->sector_size;
        SCpnt->underflow = this_count << 9;
 
        SCpnt->allowed = MAX_RETRIES;
@@ -418,7 +492,7 @@ static int sd_init_command(Scsi_Cmnd * SCpnt)
         * This is the completion routine we use.  This is matched in terms
         * of capability to this function.
         */
-       SCpnt->done = rw_intr;
+       SCpnt->done = sd_rw_intr;
 
        /*
         * This indicates that the command is ready from our end to be
@@ -427,31 +501,46 @@ static int sd_init_command(Scsi_Cmnd * SCpnt)
        return 1;
 }
 
+/**
+ *     sd_open - open a scsi disk device
+ *     @inode: only i_rdev member may be used
+ *     @filp: only f_mode and f_flags may be used
+ *
+ *     Returns 0 if successful. Returns a negated errno value in case 
+ *     of error.
+ *
+ *     Note: This can be called from a user context (e.g. fsck(1) )
+ *     or from within the kernel (e.g. as a result of a mount(1) ).
+ *     In the latter case @inode and @filp carry an abridged amount
+ *     of information as noted above.
+ **/
 static int sd_open(struct inode *inode, struct file *filp)
 {
-       int target, retval = -ENXIO;
-       Scsi_Device * SDev;
-       target = DEVICE_NR(inode->i_rdev);
+       int retval = -ENXIO;
+       Scsi_Device * sdp;
+       Scsi_Disk * sdkp;
+       int dsk_nr = DEVICE_NR(inode->i_rdev);
 
-       SCSI_LOG_HLQUEUE(1, printk("target=%d, max=%d\n", target, sd_template.dev_max));
+       SCSI_LOG_HLQUEUE(3, printk("sd_open: dsk_nr=%d, part_nr=%d\n", 
+                           dsk_nr, SD_PARTITION(inode->i_rdev)));
 
-       if (target >= sd_template.dev_max || !rscsi_disks[target].device)
+       sdkp = sd_get_sdisk(dsk_nr);
+       if ((NULL == sdkp) || (NULL == (sdp = sdkp->device)))
                return -ENXIO;  /* No such device */
 
        /*
         * If the device is in error recovery, wait until it is done.
         * If the device is offline, then disallow any access to it.
         */
-       if (!scsi_block_when_processing_errors(rscsi_disks[target].device)) {
+       if (!scsi_block_when_processing_errors(sdp))
                return -ENXIO;
-       }
        /*
-        * Make sure that only one process can do a check_change_disk at one time.
-        * This is also used to lock out further access when the partition table
-        * is being re-read.
+        * Make sure that only one process can do a check_change_disk at 
+        * one time.  This is also used to lock out further access when 
+        * the partition table is being re-read.
         */
 
-       while (rscsi_disks[target].device->busy) {
+       while (sdp->busy) {
                barrier();
                cpu_relax();
        }
@@ -459,22 +548,21 @@ static int sd_open(struct inode *inode, struct file *filp)
         * The following code can sleep.
         * Module unloading must be prevented
         */
-       SDev = rscsi_disks[target].device;
-       if (SDev->host->hostt->module)
-               __MOD_INC_USE_COUNT(SDev->host->hostt->module);
+       if (sdp->host->hostt->module)
+               __MOD_INC_USE_COUNT(sdp->host->hostt->module);
        if (sd_template.module)
                __MOD_INC_USE_COUNT(sd_template.module);
-       SDev->access_count++;
+       sdp->access_count++;
 
-       if (rscsi_disks[target].device->removable) {
-               SDev->allow_revalidate = 1;
+       if (sdp->removable) {
+               sdp->allow_revalidate = 1;
                check_disk_change(inode->i_rdev);
-               SDev->allow_revalidate = 0;
+               sdp->allow_revalidate = 0;
 
                /*
                 * If the drive is empty, just let the open fail.
                 */
-               if ((!rscsi_disks[target].ready) && !(filp->f_flags & O_NDELAY)) {
+               if ((!sdkp->ready) && !(filp->f_flags & O_NDELAY)) {
                        retval = -ENOMEDIUM;
                        goto error_out;
                }
@@ -484,7 +572,7 @@ static int sd_open(struct inode *inode, struct file *filp)
                 * 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)) {
+               if ((sdkp->write_prot) && (filp->f_mode & FMODE_WRITE)) {
                        retval = -EROFS;
                        goto error_out;
                }
@@ -495,7 +583,7 @@ static int sd_open(struct inode *inode, struct file *filp)
         * and don't pretend that
         * the open actually succeeded.
         */
-       if (!SDev->online) {
+       if (!sdp->online) {
                goto error_out;
        }
        /*
@@ -506,42 +594,57 @@ static int sd_open(struct inode *inode, struct file *filp)
                goto error_out;
        }
 
-       if (SDev->removable)
-               if (SDev->access_count==1)
-                       if (scsi_block_when_processing_errors(SDev))
-                               scsi_ioctl(SDev, SCSI_IOCTL_DOORLOCK, NULL);
+       if (sdp->removable)
+               if (sdp->access_count==1)
+                       if (scsi_block_when_processing_errors(sdp))
+                               scsi_ioctl(sdp, SCSI_IOCTL_DOORLOCK, NULL);
 
-       
        return 0;
 
 error_out:
-       SDev->access_count--;
-       if (SDev->host->hostt->module)
-               __MOD_DEC_USE_COUNT(SDev->host->hostt->module);
+       sdp->access_count--;
+       if (sdp->host->hostt->module)
+               __MOD_DEC_USE_COUNT(sdp->host->hostt->module);
        if (sd_template.module)
                __MOD_DEC_USE_COUNT(sd_template.module);
        return retval;  
 }
 
-static int sd_release(struct inode *inode, struct file *file)
+/**
+ *     sd_release - invoked when the (last) close(2) is called on this
+ *     scsi disk.
+ *     @inode: only i_rdev member may be used
+ *     @filp: only f_mode and f_flags may be used
+ *
+ *     Returns 0. 
+ *
+ *     Note: may block (uninterruptible) if error recovery is underway
+ *     on this disk.
+ **/
+static int sd_release(struct inode *inode, struct file *filp)
 {
-       int target;
-       Scsi_Device * SDev;
+       Scsi_Disk * sdkp;
+       int dsk_nr = DEVICE_NR(inode->i_rdev);
+       Scsi_Device * sdp;
 
-       target = DEVICE_NR(inode->i_rdev);
-       SDev = rscsi_disks[target].device;
-       if (!SDev)
-               return -ENODEV;
+       SCSI_LOG_HLQUEUE(3, printk("sd_release: dsk_nr=%d, part_nr=%d\n", 
+                           dsk_nr, SD_PARTITION(inode->i_rdev)));
+       sdkp = sd_get_sdisk(dsk_nr);
+       if ((NULL == sdkp) || (NULL == (sdp = sdkp->device)))
+               return -ENODEV; /* open uses ENXIO ?? */
+
+       /* ... and what if there are packets in flight and this close()
+        * is followed by a "rmmod sd_mod" */
 
-       SDev->access_count--;
+       sdp->access_count--;
 
-       if (SDev->removable) {
-               if (!SDev->access_count)
-                       if (scsi_block_when_processing_errors(SDev))
-                               scsi_ioctl(SDev, SCSI_IOCTL_DOORUNLOCK, NULL);
+       if (sdp->removable) {
+               if (!sdp->access_count)
+                       if (scsi_block_when_processing_errors(sdp))
+                               scsi_ioctl(sdp, SCSI_IOCTL_DOORUNLOCK, NULL);
        }
-       if (SDev->host->hostt->module)
-               __MOD_DEC_USE_COUNT(SDev->host->hostt->module);
+       if (sdp->host->hostt->module)
+               __MOD_DEC_USE_COUNT(sdp->host->hostt->module);
        if (sd_template.module)
                __MOD_DEC_USE_COUNT(sd_template.module);
        return 0;
@@ -559,7 +662,7 @@ static struct block_device_operations sd_fops =
 
 /*
  *    If we need more than one SCSI disk major (i.e. more than
- *      16 SCSI disks), we'll have to kmalloc() more gendisks later.
+ *      16 SCSI disks), we'll have to vmalloc() more gendisks later.
  */
 
 static struct gendisk sd_gendisk =
@@ -574,30 +677,34 @@ static struct gendisk *sd_gendisks = &sd_gendisk;
 
 #define SD_GENDISK(i)    sd_gendisks[(i) / SCSI_DISKS_PER_MAJOR]
 
-/*
- * rw_intr is the interrupt routine for the device driver.
- * It will be notified on the end of a SCSI read / write, and
- * will take one of several actions based on success or failure.
- */
-
-static void rw_intr(Scsi_Cmnd * SCpnt)
+/**
+ *     sd_rw_intr - bottom half handler: called when the lower level
+ *     driver has completed (successfully or otherwise) a scsi command.
+ *     @SCpnt: mid-level's per command structure.
+ *
+ *     Note: potentially run from within an ISR. Must not block.
+ **/
+static void sd_rw_intr(Scsi_Cmnd * SCpnt)
 {
        int result = SCpnt->result;
-#if CONFIG_SCSI_LOGGING
-       char nbuff[6];
-#endif
        int this_count = SCpnt->bufflen >> 9;
        int good_sectors = (result == 0 ? this_count : 0);
        int block_sectors = 1;
+       long error_sector;
+#if CONFIG_SCSI_LOGGING
+       char nbuff[6];
 
-       SCSI_LOG_HLCOMPLETE(1, sd_devname(DEVICE_NR(SCpnt->request.rq_dev), nbuff));
-
-       SCSI_LOG_HLCOMPLETE(1, printk("%s : rw_intr(%d, %x [%x %x])\n", nbuff,
-                                     SCpnt->host->host_no,
-                                     result,
-                                     SCpnt->sense_buffer[0],
-                                     SCpnt->sense_buffer[2]));
-
+       SCSI_LOG_HLCOMPLETE(1, sd_dskname(DEVICE_NR(SCpnt->request.rq_dev), 
+                           nbuff));
+       SCSI_LOG_HLCOMPLETE(1, printk("sd_rw_intr: %s: res=0x%x\n", 
+                                     nbuff, result));
+       if (0 != result) {
+               SCSI_LOG_HLCOMPLETE(1, printk("sd_rw_intr: sb[0,2,asc,ascq]"
+                               "=%x,%x,%x,%x\n", SCpnt->sense_buffer[0],
+                       SCpnt->sense_buffer[2], SCpnt->sense_buffer[12],
+                       SCpnt->sense_buffer[13]));
+       }
+#endif
        /*
           Handle MEDIUM ERRORs that indicate partial success.  Since this is a
           relatively rare error condition, no care is taken to avoid
@@ -605,10 +712,11 @@ static void rw_intr(Scsi_Cmnd * SCpnt)
         */
 
        /* An error occurred */
-       if (driver_byte(result) != 0) {
-               /* Sense data is valid */
-               if (SCpnt->sense_buffer[0] == 0xF0 && SCpnt->sense_buffer[2] == MEDIUM_ERROR) {
-                       long error_sector = (SCpnt->sense_buffer[3] << 24) |
+       if (driver_byte(result) != 0 &&         /* An error occured */
+           SCpnt->sense_buffer[0] != 0xF0) {   /* Sense data is valid */
+               switch (SCpnt->sense_buffer[2]) {
+               case MEDIUM_ERROR:
+                       error_sector = (SCpnt->sense_buffer[3] << 24) |
                        (SCpnt->sense_buffer[4] << 16) |
                        (SCpnt->sense_buffer[5] << 8) |
                        SCpnt->sense_buffer[6];
@@ -641,13 +749,30 @@ static void rw_intr(Scsi_Cmnd * SCpnt)
                        good_sectors = error_sector - SCpnt->request.sector;
                        if (good_sectors < 0 || good_sectors >= this_count)
                                good_sectors = 0;
-               }
-               if (SCpnt->sense_buffer[2] == ILLEGAL_REQUEST) {
+                       break;
+
+               case RECOVERED_ERROR:
+                       /*
+                        * An error occured, but it recovered.  Inform the
+                        * user, but make sure that it's not treated as a
+                        * hard error.
+                        */
+                       print_sense("sd", SCpnt);
+                       result = 0;
+                       SCpnt->sense_buffer[0] = 0x0;
+                       good_sectors = this_count;
+                       break;
+
+               case ILLEGAL_REQUEST:
                        if (SCpnt->device->ten == 1) {
                                if (SCpnt->cmnd[0] == READ_10 ||
                                    SCpnt->cmnd[0] == WRITE_10)
                                        SCpnt->device->ten = 0;
                        }
+                       break;
+
+               default:
+                       break;
                }
        }
        /*
@@ -657,28 +782,33 @@ static void rw_intr(Scsi_Cmnd * SCpnt)
         */
        scsi_io_completion(SCpnt, good_sectors, block_sectors);
 }
-/*
- * requeue_sd_request() is the request handler function for the sd driver.
- * Its function in life is to take block device requests, and translate
- * them to SCSI commands.
- */
-
 
+/**
+ *     check_scsidisk_media_change - self descriptive
+ *     @full_dev: kernel device descriptor (kdev_t)
+ *
+ *     Returns 0 if not applicable or no change; 1 if change
+ *
+ *     Note: this function is invoked from the block subsystem.
+ **/
 static int check_scsidisk_media_change(kdev_t full_dev)
 {
        int retval;
-       int target;
-       int flag = 0;
-       Scsi_Device * SDev;
-
-       target = DEVICE_NR(full_dev);
-       SDev = rscsi_disks[target].device;
-
-       if (target >= sd_template.dev_max || !SDev) {
-               printk("SCSI disk request error: invalid device.\n");
+       int flag = 0;   /* <<<< what is this for?? */
+       Scsi_Disk * sdkp;
+       Scsi_Device * sdp;
+       int dsk_nr = DEVICE_NR(full_dev);
+
+       sdkp = sd_get_sdisk(dsk_nr);
+
+       SCSI_LOG_HLQUEUE(3, printk("check_scsidisk_media_change: "
+                           "dsk_nr=%d\n", dsk_nr));
+       if ((NULL == sdkp) || (NULL == (sdp = sdkp->device))) {
+               printk(KERN_ERR "check_scsidisk_media_change: dsk_nr=%d, "
+                      "invalid device\n", dsk_nr);
                return 0;
        }
-       if (!SDev->removable)
+       if (!sdp->removable)
                return 0;
 
        /*
@@ -687,9 +817,9 @@ static int check_scsidisk_media_change(kdev_t full_dev)
         * can deal with it then.  It is only because of unrecoverable errors
         * that we would ever take a device offline in the first place.
         */
-       if (SDev->online == FALSE) {
-               rscsi_disks[target].ready = 0;
-               SDev->changed = 1;
+       if (sdp->online == FALSE) {
+               sdkp->ready = 0;
+               sdp->changed = 1;
                return 1;       /* This will force a flush, if called from
                                 * check_disk_change */
        }
@@ -701,8 +831,8 @@ static int check_scsidisk_media_change(kdev_t full_dev)
         * as this will spin up the drive.
         */
        retval = -ENODEV;
-       if (scsi_block_when_processing_errors(SDev))
-               retval = scsi_ioctl(SDev, SCSI_IOCTL_START_UNIT, NULL);
+       if (scsi_block_when_processing_errors(sdp))
+               retval = scsi_ioctl(sdp, SCSI_IOCTL_START_UNIT, NULL);
 
        if (retval) {           /* Unable to test, unit probably not ready.
                                 * This usually means there is no disc in the
@@ -710,8 +840,8 @@ static int check_scsidisk_media_change(kdev_t full_dev)
                                 * it out later once the drive is available
                                 * again.  */
 
-               rscsi_disks[target].ready = 0;
-               SDev->changed = 1;
+               sdkp->ready = 0;
+               sdp->changed = 1;
                return 1;       /* This will force a flush, if called from
                                 * check_disk_change */
        }
@@ -721,15 +851,26 @@ static int check_scsidisk_media_change(kdev_t full_dev)
         * struct and tested at open !  Daniel Roche ( dan@lectra.fr )
         */
 
-       rscsi_disks[target].ready = 1;  /* FLOPTICAL */
+       sdkp->ready = 1;        /* FLOPTICAL */
 
-       retval = SDev->changed;
+       retval = sdp->changed;
        if (!flag)
-               SDev->changed = 0;
+               sdp->changed = 0;
        return retval;
 }
 
-static int sd_init_onedisk(int i)
+/**
+ *     sd_init_onedisk - called the first time a new disk is seen,
+ *     performs read_capacity, disk spin up (as required), etc.
+ *     @sdkp: pointer to associated Scsi_Disk object
+ *     @dsk_nr: disk number within this driver (e.g. 0->/dev/sda,
+ *     1->/dev/sdb, etc)
+ *
+ *     Returns dsk_nr (pointless)
+ *
+ *     Note: this function is local to this driver.
+ **/
+static int sd_init_onedisk(Scsi_Disk * sdkp, int dsk_nr)
 {
        unsigned char cmd[10];
        char nbuff[6];
@@ -737,19 +878,23 @@ static int sd_init_onedisk(int i)
        unsigned long spintime_value = 0;
        int the_result, retries, spintime;
        int sector_size;
+       Scsi_Device *sdp;
        Scsi_Request *SRpnt;
 
+       SCSI_LOG_HLQUEUE(3, printk("sd_init_onedisk: dsk_nr=%d\n", 
+                           dsk_nr));
        /*
         * Get the name of the disk, in case we need to log it somewhere.
         */
-       sd_devname(i, nbuff);
+       sd_dskname(dsk_nr, nbuff);
 
        /*
         * If the device is offline, don't try and read capacity or any
         * of the other niceties.
         */
-       if (rscsi_disks[i].device->online == FALSE)
-               return i;
+       sdp = sdkp->device;
+       if (sdp->online == FALSE)
+               return dsk_nr;
 
        /*
         * We need to retry the READ_CAPACITY because a UNIT_ATTENTION is
@@ -757,17 +902,19 @@ static int sd_init_onedisk(int i)
         * just after a scsi bus reset.
         */
 
-       SRpnt = scsi_allocate_request(rscsi_disks[i].device);
+       SRpnt = scsi_allocate_request(sdp);
        if (!SRpnt) {
-               printk(KERN_WARNING "(sd_init_onedisk:) Request allocation failure.\n");
-               return i;
+               printk(KERN_WARNING "(sd_init_onedisk:) Request allocation "
+                      "failure.\n");
+               return dsk_nr;
        }
 
        buffer = kmalloc(512, GFP_DMA);
        if (!buffer) {
-               printk(KERN_WARNING "(sd_init_onedisk:) Memory allocation failure.\n");
+               printk(KERN_WARNING "(sd_init_onedisk:) Memory allocation "
+                      "failure.\n");
                scsi_release_request(SRpnt);
-               return i;
+               return dsk_nr;
        }
 
        spintime = 0;
@@ -779,8 +926,8 @@ static int sd_init_onedisk(int i)
 
                while (retries < 3) {
                        cmd[0] = TEST_UNIT_READY;
-                       cmd[1] = (rscsi_disks[i].device->scsi_level <= SCSI_2) ?
-                                ((rscsi_disks[i].device->lun << 5) & 0xe0) : 0;
+                       cmd[1] = (sdp->scsi_level <= SCSI_2) ?
+                                ((sdp->lun << 5) & 0xe0) : 0;
                        memset((void *) &cmd[2], 0, 8);
                        SRpnt->sr_cmd_len = 0;
                        SRpnt->sr_sense_buffer[0] = 0;
@@ -806,23 +953,24 @@ static int sd_init_onedisk(int i)
                    && ((driver_byte(the_result) & DRIVER_SENSE) != 0)
                    && SRpnt->sr_sense_buffer[2] == UNIT_ATTENTION
                    && SRpnt->sr_sense_buffer[12] == 0x3A ) {
-                       rscsi_disks[i].capacity = 0x1fffff;
+                       sdkp->capacity = 0x1fffff;
                        sector_size = 512;
-                       rscsi_disks[i].device->changed = 1;
-                       rscsi_disks[i].ready = 0;
+                       sdp->changed = 1;
+                       sdkp->ready = 0;
                        break;
                }
 
                /* Look for non-removable devices that return NOT_READY.
                 * Issue command to spin up drive for these cases. */
-               if (the_result && !rscsi_disks[i].device->removable &&
+               if (the_result && !sdp->removable &&
                    SRpnt->sr_sense_buffer[2] == NOT_READY) {
                        unsigned long time1;
                        if (!spintime) {
-                               printk("%s: Spinning up disk...", nbuff);
+                               printk(KERN_NOTICE "%s: Spinning up disk...",
+                                      nbuff);
                                cmd[0] = START_STOP;
-                               cmd[1] = (rscsi_disks[i].device->scsi_level <= SCSI_2) ?
-                                        ((rscsi_disks[i].device->lun << 5) & 0xe0) : 0;
+                               cmd[1] = (sdp->scsi_level <= SCSI_2) ?
+                                        ((sdp->lun << 5) & 0xe0) : 0;
                                cmd[1] |= 1;    /* Return immediately */
                                memset((void *) &cmd[2], 0, 8);
                                cmd[4] = 1;     /* Start spin cycle */
@@ -831,8 +979,9 @@ static int sd_init_onedisk(int i)
                                SRpnt->sr_sense_buffer[2] = 0;
 
                                SRpnt->sr_data_direction = SCSI_DATA_READ;
-                               scsi_wait_req(SRpnt, (void *) cmd, (void *) buffer,
-                                           0/*512*/, SD_TIMEOUT, MAX_RETRIES);
+                               scsi_wait_req(SRpnt, (void *)cmd, 
+                                             (void *) buffer, 0/*512*/, 
+                                             SD_TIMEOUT, MAX_RETRIES);
                                spintime_value = jiffies;
                        }
                        spintime = 1;
@@ -855,8 +1004,8 @@ static int sd_init_onedisk(int i)
        retries = 3;
        do {
                cmd[0] = READ_CAPACITY;
-               cmd[1] = (rscsi_disks[i].device->scsi_level <= SCSI_2) ?
-                        ((rscsi_disks[i].device->lun << 5) & 0xe0) : 0;
+               cmd[1] = (sdp->scsi_level <= SCSI_2) ?
+                        ((sdp->lun << 5) & 0xe0) : 0;
                memset((void *) &cmd[2], 0, 8);
                memset((void *) buffer, 0, 8);
                SRpnt->sr_cmd_len = 0;
@@ -888,8 +1037,8 @@ static int sd_init_onedisk(int i)
         */
 
        if (the_result) {
-               printk("%s : READ CAPACITY failed.\n"
-                      "%s : status = %x, message = %02x, host = %d, driver = %02x \n",
+               printk(KERN_NOTICE "%s : READ CAPACITY failed.\n"
+                      "%s : status=%x, message=%02x, host=%d, driver=%02x \n",
                       nbuff, nbuff,
                       status_byte(the_result),
                       msg_byte(the_result),
@@ -901,50 +1050,50 @@ static int sd_init_onedisk(int i)
                else
                        printk("%s : sense not available. \n", nbuff);
 
-               printk("%s : block size assumed to be 512 bytes, disk size 1GB.  \n",
-                      nbuff);
-               rscsi_disks[i].capacity = 0x1fffff;
+               printk(KERN_NOTICE "%s : block size assumed to be 512 "
+                      "bytes, disk size 1GB.  \n", nbuff);
+               sdkp->capacity = 0x1fffff;
                sector_size = 512;
 
                /* Set dirty bit for removable devices if not ready -
                 * sometimes drives will not report this properly. */
-               if (rscsi_disks[i].device->removable &&
+               if (sdp->removable &&
                    SRpnt->sr_sense_buffer[2] == NOT_READY)
-                       rscsi_disks[i].device->changed = 1;
+                       sdp->changed = 1;
 
        } else {
                /*
                 * FLOPTICAL, if read_capa is ok, drive is assumed to be ready
                 */
-               rscsi_disks[i].ready = 1;
+               sdkp->ready = 1;
 
-               rscsi_disks[i].capacity = 1 + ((buffer[0] << 24) |
-                                              (buffer[1] << 16) |
-                                              (buffer[2] << 8) |
-                                              buffer[3]);
+               sdkp->capacity = 1 + ((buffer[0] << 24) |
+                                     (buffer[1] << 16) |
+                                     (buffer[2] << 8) |
+                                     buffer[3]);
 
                sector_size = (buffer[4] << 24) |
                    (buffer[5] << 16) | (buffer[6] << 8) | buffer[7];
 
                if (sector_size == 0) {
                        sector_size = 512;
-                       printk("%s : sector size 0 reported, assuming 512.\n",
-                              nbuff);
+                       printk(KERN_NOTICE "%s : sector size 0 reported, "
+                              "assuming 512.\n", nbuff);
                }
                if (sector_size != 512 &&
                    sector_size != 1024 &&
                    sector_size != 2048 &&
                    sector_size != 4096 &&
                    sector_size != 256) {
-                       printk("%s : unsupported sector size %d.\n",
-                              nbuff, sector_size);
+                       printk(KERN_NOTICE "%s : unsupported sector size "
+                              "%d.\n", nbuff, sector_size);
                        /*
                         * The user might want to re-format the drive with
                         * a supported sectorsize.  Once this happens, it
                         * would be relatively trivial to set the thing up.
                         * For this reason, we leave the thing in the table.
                         */
-                       rscsi_disks[i].capacity = 0;
+                       sdkp->capacity = 0;
                }
                if (sector_size > 1024) {
                        int m;
@@ -955,7 +1104,7 @@ static int sd_init_onedisk(int i)
                         * The disk reading code does not allow for reading
                         * of partial sectors.
                         */
-                       for (m = i << 4; m < ((i + 1) << 4); m++) {
+                       for (m = dsk_nr << 4; m < ((dsk_nr + 1) << 4); m++) {
                                sd_blocksizes[m] = sector_size;
                        }
                } {
@@ -965,33 +1114,33 @@ static int sd_init_onedisk(int i)
                         * Jacques Gelinas (Jacques@solucorp.qc.ca)
                         */
                        int hard_sector = sector_size;
-                       int sz = rscsi_disks[i].capacity * (hard_sector/256);
-                       request_queue_t *queue = &rscsi_disks[i].device->request_queue;
+                       int sz = sdkp->capacity * (hard_sector/256);
+                       request_queue_t *queue = &sdp->request_queue;
 
                        blk_queue_hardsect_size(queue, hard_sector);
-                       printk("SCSI device %s: "
+                       printk(KERN_NOTICE "SCSI device %s: "
                               "%d %d-byte hdwr sectors (%d MB)\n",
-                              nbuff, rscsi_disks[i].capacity,
+                              nbuff, sdkp->capacity,
                               hard_sector, (sz/2 - sz/1250 + 974)/1950);
                }
 
                /* Rescale capacity to 512-byte units */
                if (sector_size == 4096)
-                       rscsi_disks[i].capacity <<= 3;
+                       sdkp->capacity <<= 3;
                if (sector_size == 2048)
-                       rscsi_disks[i].capacity <<= 2;
+                       sdkp->capacity <<= 2;
                if (sector_size == 1024)
-                       rscsi_disks[i].capacity <<= 1;
+                       sdkp->capacity <<= 1;
                if (sector_size == 256)
-                       rscsi_disks[i].capacity >>= 1;
+                       sdkp->capacity >>= 1;
        }
 
 
        /*
         * Unless otherwise specified, this is not write protected.
         */
-       rscsi_disks[i].write_prot = 0;
-       if (rscsi_disks[i].device->removable && rscsi_disks[i].ready) {
+       sdkp->write_prot = 0;
+       if (sdp->removable && sdkp->ready) {
                /* FLOPTICAL */
 
                /*
@@ -1009,8 +1158,8 @@ static int sd_init_onedisk(int i)
 
                memset((void *) &cmd[0], 0, 8);
                cmd[0] = MODE_SENSE;
-               cmd[1] = (rscsi_disks[i].device->scsi_level <= SCSI_2) ?
-                        ((rscsi_disks[i].device->lun << 5) & 0xe0) : 0;
+               cmd[1] = (sdp->scsi_level <= SCSI_2) ?
+                        ((sdp->lun << 5) & 0xe0) : 0;
                cmd[2] = 0x3f;  /* Get all pages */
                cmd[4] = 255;   /* Ask for 255 bytes, even tho we want just the first 8 */
                SRpnt->sr_cmd_len = 0;
@@ -1025,11 +1174,12 @@ static int sd_init_onedisk(int i)
                the_result = SRpnt->sr_result;
 
                if (the_result) {
-                       printk("%s: test WP failed, assume Write Enabled\n", nbuff);
+                       printk(KERN_NOTICE "%s: test WP failed, assume "
+                              "Write Enabled\n", nbuff);
                } else {
-                       rscsi_disks[i].write_prot = ((buffer[2] & 0x80) != 0);
-                       printk("%s: Write Protect is %s\n", nbuff,
-                              rscsi_disks[i].write_prot ? "on" : "off");
+                       sdkp->write_prot = ((buffer[2] & 0x80) != 0);
+                       printk(KERN_NOTICE "%s: Write Protect is %s\n", nbuff,
+                              sdkp->write_prot ? "on" : "off");
                }
 
        }                       /* check for write protect */
@@ -1041,7 +1191,7 @@ static int sd_init_onedisk(int i)
        SRpnt = NULL;
 
        kfree(buffer);
-       return i;
+       return dsk_nr;
 }
 
 /*
@@ -1051,14 +1201,25 @@ static int sd_init_onedisk(int i)
 
 static int sd_registered;
 
+/**
+ *     sd_init- called during driver initialization (after
+ *     sd_detect() is called for each scsi device present).
+ *
+ *     Returns 0 is successful (or already called); 1 if error
+ *
+ *     Note: this function is invoked from the scsi mid-level.
+ **/
 static int sd_init()
 {
-       int i, maxparts;
+       int k, maxparts;
+       Scsi_Disk * sdkp;
 
+       SCSI_LOG_HLQUEUE(3, printk("sd_init: dev_noticed=%d\n",
+                           sd_template.dev_noticed));
        if (sd_template.dev_noticed == 0)
                return 0;
 
-       if (!rscsi_disks)
+       if (NULL == sd_dsk_arr)
                sd_template.dev_max = sd_template.dev_noticed + SD_EXTRA_DEVS;
 
        if (sd_template.dev_max > N_SD_MAJORS * SCSI_DISKS_PER_MAJOR)
@@ -1070,77 +1231,86 @@ static int sd_init()
                return 0;
 
        if (!sd_registered) {
-               for (i = 0; i < N_USED_SD_MAJORS; i++) {
-                       if (devfs_register_blkdev(SD_MAJOR(i), "sd",
+               for (k = 0; k < N_USED_SD_MAJORS; k++) {
+                       if (devfs_register_blkdev(SD_MAJOR(k), "sd",
                                                  &sd_fops)) {
-                               printk("Unable to get major %d for SCSI disk\n",
-                                      SD_MAJOR(i));
+                               printk(KERN_NOTICE "Unable to get major %d "
+                                      "for SCSI disk\n", SD_MAJOR(k));
                                return 1;
                        }
                }
                sd_registered++;
        }
        /* We do not support attaching loadable devices yet. */
-       if (rscsi_disks)
+       if (sd_dsk_arr)
                return 0;
 
        /* allocate memory */
-#define init_mem_lth(x,n)      x = kmalloc((n) * sizeof(*x), GFP_ATOMIC)
+#define init_mem_lth(x,n)      x = vmalloc((n) * sizeof(*x))
 #define zero_mem_lth(x,n)      memset(x, 0, (n) * sizeof(*x))
 
-       init_mem_lth(rscsi_disks, sd_template.dev_max);
+       init_mem_lth(sd_dsk_arr, sd_template.dev_max);
+       if (sd_dsk_arr) {
+               zero_mem_lth(sd_dsk_arr, sd_template.dev_max);
+               for (k = 0; k < sd_template.dev_max; ++k) {
+                       sdkp = vmalloc(sizeof(Scsi_Disk));
+                       if (NULL == sdkp)
+                               goto cleanup_mem;
+                       memset(sdkp, 0, sizeof(Scsi_Disk));
+                       sd_dsk_arr[k] = sdkp;
+               }
+       }
        init_mem_lth(sd_sizes, maxparts);
        init_mem_lth(sd_blocksizes, maxparts);
        init_mem_lth(sd, maxparts);
        init_mem_lth(sd_gendisks, N_USED_SD_MAJORS);
        init_mem_lth(sd_max_sectors, sd_template.dev_max << 4);
 
-       if (!rscsi_disks || !sd_sizes || !sd_blocksizes || !sd || !sd_gendisks)
+       if (!sd_dsk_arr || !sd_sizes || !sd_blocksizes || !sd || !sd_gendisks)
                goto cleanup_mem;
 
-       zero_mem_lth(rscsi_disks, sd_template.dev_max);
        zero_mem_lth(sd_sizes, maxparts);
        zero_mem_lth(sd, maxparts);
 
-       for (i = 0; i < maxparts; i++) {
-               sd_blocksizes[i] = 1024;
+       for (k = 0; k < maxparts; k++) {
+               sd_blocksizes[k] = 1024;
                /*
                 * Allow lowlevel device drivers to generate 512k large scsi
                 * commands if they know what they're doing and they ask for it
                 * explicitly via the SHpnt->max_sectors API.
                 */
-               sd_max_sectors[i] = MAX_PHYS_SEGMENTS*8;
+               sd_max_sectors[k] = MAX_PHYS_SEGMENTS*8;
        }
 
-       for (i = 0; i < N_USED_SD_MAJORS; i++) {
-               request_queue_t *q = blk_get_queue(mk_kdev(SD_MAJOR(i), 0));
+       for (k = 0; k < N_USED_SD_MAJORS; k++) {
+               request_queue_t *q = blk_get_queue(mk_kdev(SD_MAJOR(k), 0));
                int parts_per_major = (SCSI_DISKS_PER_MAJOR << 4);
 
-               blksize_size[SD_MAJOR(i)] =
-                       sd_blocksizes + i * parts_per_major;
+               blksize_size[SD_MAJOR(k)] =
+                       sd_blocksizes + k * parts_per_major;
                blk_queue_hardsect_size(q, 512);
        }
 
-       for (i = 0; i < N_USED_SD_MAJORS; i++) {
+       for (k = 0; k < N_USED_SD_MAJORS; k++) {
                int N = SCSI_DISKS_PER_MAJOR;
 
-               sd_gendisks[i] = sd_gendisk;
+               sd_gendisks[k] = sd_gendisk;
 
-               init_mem_lth(sd_gendisks[i].de_arr, N);
-               init_mem_lth(sd_gendisks[i].flags, N);
+               init_mem_lth(sd_gendisks[k].de_arr, N);
+               init_mem_lth(sd_gendisks[k].flags, N);
 
-               if (!sd_gendisks[i].de_arr || !sd_gendisks[i].flags)
+               if (!sd_gendisks[k].de_arr || !sd_gendisks[k].flags)
                        goto cleanup_gendisks;
 
-               zero_mem_lth(sd_gendisks[i].de_arr, N);
-               zero_mem_lth(sd_gendisks[i].flags, N);
+               zero_mem_lth(sd_gendisks[k].de_arr, N);
+               zero_mem_lth(sd_gendisks[k].flags, N);
 
-               sd_gendisks[i].major = SD_MAJOR(i);
-               sd_gendisks[i].major_name = "sd";
-               sd_gendisks[i].minor_shift = 4;
-               sd_gendisks[i].part = sd + i * (N << 4);
-               sd_gendisks[i].sizes = sd_sizes + i * (N << 4);
-               sd_gendisks[i].nr_real = 0;
+               sd_gendisks[k].major = SD_MAJOR(k);
+               sd_gendisks[k].major_name = "sd";
+               sd_gendisks[k].minor_shift = 4;
+               sd_gendisks[k].part = sd + k * (N << 4);
+               sd_gendisks[k].sizes = sd_sizes + k * (N << 4);
+               sd_gendisks[k].nr_real = 0;
        }
 
        return 0;
@@ -1149,130 +1319,198 @@ static int sd_init()
 #undef zero_mem_lth
 
 cleanup_gendisks:
-       /* kfree can handle NULL, so no test is required here */
-       for (i = 0; i < N_USED_SD_MAJORS; i++) {
-               kfree(sd_gendisks[i].de_arr);
-               kfree(sd_gendisks[i].flags);
+       /* vfree can handle NULL, so no test is required here */
+       for (k = 0; k < N_USED_SD_MAJORS; k++) {
+               vfree(sd_gendisks[k].de_arr);
+               vfree(sd_gendisks[k].flags);
        }
 cleanup_mem:
-       kfree(sd_gendisks);
-       kfree(sd);
-       kfree(sd_blocksizes);
-       kfree(sd_sizes);
-       kfree(rscsi_disks);
-       for (i = 0; i < N_USED_SD_MAJORS; i++) {
-               devfs_unregister_blkdev(SD_MAJOR(i), "sd");
+       if (sd_gendisks) vfree(sd_gendisks);
+       sd_gendisks = NULL;
+       if (sd) vfree(sd);
+       sd = NULL;
+       if (sd_blocksizes) vfree(sd_blocksizes);
+       sd_blocksizes = NULL;
+       if (sd_sizes) vfree(sd_sizes);
+       sd_sizes = NULL;
+       if (sd_dsk_arr) {
+                for (k = 0; k < sd_template.dev_max; ++k) {
+                       sdkp = sd_dsk_arr[k];
+                       if (sdkp)
+                               vfree(sdkp);
+               }
+               vfree(sd_dsk_arr);
+               sd_dsk_arr = NULL;
+       }
+       for (k = 0; k < N_USED_SD_MAJORS; k++) {
+               devfs_unregister_blkdev(SD_MAJOR(k), "sd");
        }
        sd_registered--;
        return 1;
 }
 
-
+/**
+ *     sd_finish- called during driver initialization, after all
+ *     the sd_attach() calls are finished.
+ *
+ *     Note: this function is invoked from the scsi mid-level.
+ *     This function is not called after driver initialization has completed.
+ *     Specifically later device attachments invoke sd_attach() but not
+ *     this function.
+ **/
 static void sd_finish()
 {
-       int i;
+       int k;
+       Scsi_Disk * sdkp;
 
-       for (i = 0; i < N_USED_SD_MAJORS; i++) {
-               blk_dev[SD_MAJOR(i)].queue = sd_find_queue;
-               add_gendisk(&(sd_gendisks[i]));
+       SCSI_LOG_HLQUEUE(3, printk("sd_finish: \n"));
+       for (k = 0; k < N_USED_SD_MAJORS; k++) {
+               blk_dev[SD_MAJOR(k)].queue = sd_find_queue;
+               add_gendisk(&(sd_gendisks[k]));
        }
 
-       for (i = 0; i < sd_template.dev_max; ++i)
-               if (!rscsi_disks[i].capacity && rscsi_disks[i].device) {
-                       sd_init_onedisk(i);
-                       if (!rscsi_disks[i].has_part_table) {
-                               sd_sizes[i << 4] = rscsi_disks[i].capacity;
-                               register_disk(&SD_GENDISK(i), MKDEV_SD(i),
+       for (k = 0; k < sd_template.dev_max; ++k) {
+               sdkp = sd_get_sdisk(k);
+               if (sdkp && (0 == sdkp->capacity) && sdkp->device) {
+                       sd_init_onedisk(sdkp, k);
+                       if (!sdkp->has_part_table) {
+                               sd_sizes[k << 4] = sdkp->capacity;
+                               register_disk(&SD_GENDISK(k), MKDEV_SD(k),
                                                1<<4, &sd_fops,
-                                               rscsi_disks[i].capacity);
-                               rscsi_disks[i].has_part_table = 1;
+                                               sdkp->capacity);
+                               sdkp->has_part_table = 1;
                        }
                }
-
+       }
        return;
 }
 
-static int sd_detect(Scsi_Device * SDp)
+/**
+ *     sd_detect- called at the start of driver initialization, once 
+ *     for each scsi device (not just disks) present.
+ *
+ *     Returns 0 if not interested in this scsi device (e.g. scanner);
+ *     1 if this device is of interest (e.g. a disk).
+ *
+ *     Note: this function is invoked from the scsi mid-level.
+ *     This function is called before sd_init() so very little is available.
+ **/
+static int sd_detect(Scsi_Device * sdp)
 {
-       if (SDp->type != TYPE_DISK && SDp->type != TYPE_MOD)
+       SCSI_LOG_HLQUEUE(3, printk("sd_detect: type=%d\n", sdp->type));
+       if (sdp->type != TYPE_DISK && sdp->type != TYPE_MOD)
                return 0;
        sd_template.dev_noticed++;
        return 1;
 }
 
-static int sd_attach(Scsi_Device * SDp)
+/**
+ *     sd_attach- called during driver initialization and whenever a
+ *     new scsi device is attached to the system. It is called once
+ *     for each scsi device (not just disks) present.
+ *     @sdp: pointer to mid level scsi device object
+ *
+ *     Returns 0 if successful (or not interested in this scsi device 
+ *     (e.g. scanner)); 1 when there is an error.
+ *
+ *     Note: this function is invoked from the scsi mid-level.
+ *     This function sets up the mapping between a given 
+ *     <host,channel,id,lun> (found in sdp) and new device name 
+ *     (e.g. /dev/sda). More precisely it is the block device major 
+ *     and minor number that is chosen here.
+ **/
+static int sd_attach(Scsi_Device * sdp)
 {
         unsigned int devnum;
-       Scsi_Disk *dpnt;
-       int i;
+       Scsi_Disk *sdkp;
+       int dsk_nr;
        char nbuff[6];
+       unsigned long iflags;
 
-       if (SDp->type != TYPE_DISK && SDp->type != TYPE_MOD)
+       if ((NULL == sdp) ||
+           ((sdp->type != TYPE_DISK) && (sdp->type != TYPE_MOD)))
                return 0;
 
+       SCSI_LOG_HLQUEUE(3, printk("sd_attach: scsi device: <%d,%d,%d,%d>\n", 
+                        sdp->host->host_no, sdp->channel, sdp->id, sdp->lun));
        if (sd_template.nr_dev >= sd_template.dev_max) {
-               SDp->attached--;
+               sdp->attached--;
+               printk(KERN_ERR "sd_init: no more room for device\n");
                return 1;
        }
-       for (dpnt = rscsi_disks, i = 0; i < sd_template.dev_max; i++, dpnt++)
-               if (!dpnt->device)
+
+/* Assume sd_attach is not re-entrant (for time being) */
+/* Also think about sd_attach() and sd_detach() running coincidentally. */
+       write_lock_irqsave(&sd_dsk_arr_lock, iflags);
+       for (dsk_nr = 0; dsk_nr < sd_template.dev_max; dsk_nr++) {
+               sdkp = sd_dsk_arr[dsk_nr];
+               if (!sdkp->device) {
+                       sdkp->device = sdp;
+                       sdkp->has_part_table = 0;
                        break;
+               }
+       }
+       write_unlock_irqrestore(&sd_dsk_arr_lock, iflags);
 
-       if (i >= sd_template.dev_max)
-               panic("scsi_devices corrupt (sd)");
+       if (dsk_nr >= sd_template.dev_max) {
+               /* panic("scsi_devices corrupt (sd)");  overkill */
+               printk(KERN_ERR "sd_init: sd_dsk_arr corrupted\n");
+               return 1;
+       }
 
-       rscsi_disks[i].device = SDp;
-       rscsi_disks[i].has_part_table = 0;
        sd_template.nr_dev++;
-       SD_GENDISK(i).nr_real++;
-        devnum = i % SCSI_DISKS_PER_MAJOR;
-        SD_GENDISK(i).de_arr[devnum] = SDp->de;
-        if (SDp->removable)
-               SD_GENDISK(i).flags[devnum] |= GENHD_FL_REMOVABLE;
-       sd_devname(i, nbuff);
-       printk("Attached scsi %sdisk %s at scsi%d, channel %d, id %d, lun %d\n",
-              SDp->removable ? "removable " : "",
-              nbuff, SDp->host->host_no, SDp->channel, SDp->id, SDp->lun);
+       SD_GENDISK(dsk_nr).nr_real++;
+        devnum = dsk_nr % SCSI_DISKS_PER_MAJOR;
+        SD_GENDISK(dsk_nr).de_arr[devnum] = sdp->de;
+        if (sdp->removable)
+               SD_GENDISK(dsk_nr).flags[devnum] |= GENHD_FL_REMOVABLE;
+       sd_dskname(dsk_nr, nbuff);
+       printk(KERN_NOTICE "Attached scsi %sdisk %s at scsi%d, channel %d, "
+              "id %d, lun %d\n", sdp->removable ? "removable " : "",
+              nbuff, sdp->host->host_no, sdp->channel, sdp->id, sdp->lun);
        return 0;
 }
 
-#define DEVICE_BUSY rscsi_disks[target].device->busy
-#define ALLOW_REVALIDATE rscsi_disks[target].device->allow_revalidate
-#define USAGE rscsi_disks[target].device->access_count
-#define CAPACITY rscsi_disks[target].capacity
-#define MAYBE_REINIT  sd_init_onedisk(target)
-
-/* This routine is called to flush all partitions and partition tables
- * for a changed scsi disk, and then re-read the new partition table.
- * If we are revalidating a disk because of a media change, then we
- * enter with usage == 0.  If we are using an ioctl, we automatically have
- * usage == 1 (we need an open channel to use an ioctl :-), so this
- * is our limit.
+/**
+ *     revalidate_scsidisk- called to flush all partitions and partition 
+ *     tables for a changed scsi disk. sd_init_onedisk() is then called
+ *     followed by re-reading the new partition table.
+ *      @dev: kernel device descriptor (kdev_t)
+ *      @maxusage: 0 when called from block level, 1 when called from
+ *      sd_ioctl().
+ *
+ *     Returns 0 if successful; negated errno value otherwise.
  */
 int revalidate_scsidisk(kdev_t dev, int maxusage)
 {
-       int target;
+       int dsk_nr = DEVICE_NR(dev);
        int res;
+       Scsi_Disk * sdkp;
+       Scsi_Device * sdp;
 
-       target = DEVICE_NR(dev);
+        SCSI_LOG_HLQUEUE(3, printk("revalidate_scsidisk: dsk_nr=%d\n", 
+                            DEVICE_NR(dev)));
+       sdkp = sd_get_sdisk(dsk_nr);
+       if ((NULL == sdkp) || (NULL == (sdp = sdkp->device)))
+               return -ENODEV;
 
-       if (DEVICE_BUSY || (ALLOW_REVALIDATE == 0 && USAGE > maxusage)) {
-               printk("Device busy for revalidation (usage=%d)\n", USAGE);
+       if (sdp->busy || ((sdp->allow_revalidate == 0) && 
+                         (sdp->access_count > maxusage))) {
+               printk(KERN_WARNING "Device busy for revalidation "
+                      "(access_count=%d)\n", sdp->access_count);
                return -EBUSY;
        }
-       DEVICE_BUSY = 1;
+       sdp->busy = 1;
 
        res = wipe_partitions(dev);
        if (res)
                goto leave;
 
-#ifdef MAYBE_REINIT
-       MAYBE_REINIT;
-#endif
+       sd_init_onedisk(sdkp, dsk_nr);
 
-       grok_partitions(dev, CAPACITY);
+       grok_partitions(dev, sdkp->capacity);
 leave:
-       DEVICE_BUSY = 0;
+       sdp->busy = 0;
        return res;
 }
 
@@ -1280,69 +1518,126 @@ static int fop_revalidate_scsidisk(kdev_t dev)
 {
        return revalidate_scsidisk(dev, 0);
 }
-static void sd_detach(Scsi_Device * SDp)
+
+/**
+ *     sd_detach- called whenever a scsi disk (previously recognized by
+ *     sd_attach) is detached from the system. It is called (potentially
+ *     multiple times) during sd module unload.
+ *     @sdp: pointer to mid level scsi device object
+ *
+ *     Note: this function is invoked from the scsi mid-level.
+ *     This function potentially frees up a device name (e.g. /dev/sdc)
+ *     that could be re-used by a subsequent sd_attach().
+ *     This function is not called when the built-in sd driver is "exit-ed".
+ **/
+static void sd_detach(Scsi_Device * sdp)
 {
-       Scsi_Disk *dpnt;
+       Scsi_Disk *sdkp = NULL;
        kdev_t dev;
-       int i, j;
+       int dsk_nr, j;
        int max_p;
        int start;
-
-       for (dpnt = rscsi_disks, i = 0; i < sd_template.dev_max; i++, dpnt++)
-               if (dpnt->device == SDp) {
-
-                       max_p = 1 << sd_gendisk.minor_shift;
-                       start = i << sd_gendisk.minor_shift;
-                       dev = MKDEV_SD_PARTITION(start);
-                       wipe_partitions(dev);
-                       for (j = max_p - 1; j >= 0; j--)
-                               sd_sizes[start + j] = 0;
-
-                        devfs_register_partitions (&SD_GENDISK (i),
-                                                   SD_MINOR_NUMBER (start), 1);
-                       /* unregister_disk() */
-                       dpnt->has_part_table = 0;
-                       dpnt->device = NULL;
-                       dpnt->capacity = 0;
-                       SDp->attached--;
-                       sd_template.dev_noticed--;
-                       sd_template.nr_dev--;
-                       SD_GENDISK(i).nr_real--;
-                       return;
+       unsigned long iflags;
+
+       SCSI_LOG_HLQUEUE(3, printk("sd_detach: <%d,%d,%d,%d>\n", 
+                           sdp->host->host_no, sdp->channel, sdp->id, 
+                           sdp->lun));
+       write_lock_irqsave(&sd_dsk_arr_lock, iflags);
+       for (dsk_nr = 0; dsk_nr < sd_template.dev_max; dsk_nr++) {
+               sdkp = sd_dsk_arr[dsk_nr];
+               if (sdkp->device == sdp) {
+                       sdkp->has_part_table = 0;
+                       sdkp->device = NULL;
+                       sdkp->capacity = 0;
+                       /* sdkp->detaching = 1; */
+                       break;
                }
+       }
+       write_unlock_irqrestore(&sd_dsk_arr_lock, iflags);
+       if (dsk_nr >= sd_template.dev_max)
+               return;
+
+       max_p = 1 << sd_gendisk.minor_shift;
+       start = dsk_nr << sd_gendisk.minor_shift;
+       dev = MKDEV_SD_PARTITION(start);
+       wipe_partitions(dev);
+       for (j = max_p - 1; j >= 0; j--)
+               sd_sizes[start + j] = 0;
+
+       devfs_register_partitions (&SD_GENDISK (dsk_nr),
+                                  SD_MINOR_NUMBER (start), 1);
+       /* unregister_disk() */
+       sdp->attached--;
+       sd_template.dev_noticed--;
+       sd_template.nr_dev--;
+       SD_GENDISK(dsk_nr).nr_real--;
 }
 
+/**
+ *     init_sd- entry point for this driver (both when built in or when
+ *     a module).
+ *
+ *     Note: this function registers this driver with the scsi mid-level.
+ **/
 static int __init init_sd(void)
 {
+       SCSI_LOG_HLQUEUE(3, printk("init_sd: sd driver entry point\n"));
        sd_template.module = THIS_MODULE;
        return scsi_register_device(&sd_template);
 }
 
+/**
+ *     exit_sd- exit point for this driver (when it is a module).
+ *
+ *     Note: this function unregisters this driver from the scsi mid-level.
+ **/
 static void __exit exit_sd(void)
 {
-       int i;
+       int k;
+       Scsi_Disk * sdkp;
 
+       SCSI_LOG_HLQUEUE(3, printk("exit_sd: exiting sd driver\n"));
        scsi_unregister_device(&sd_template);
-
-       for (i = 0; i < N_USED_SD_MAJORS; i++)
-               devfs_unregister_blkdev(SD_MAJOR(i), "sd");
+       for (k = 0; k < N_USED_SD_MAJORS; k++)
+               devfs_unregister_blkdev(SD_MAJOR(k), "sd");
 
        sd_registered--;
-       if (rscsi_disks != NULL) {
-               kfree(rscsi_disks);
-               kfree(sd_sizes);
-               kfree(sd_blocksizes);
-               kfree((char *) sd);
+       if (sd_dsk_arr != NULL) {
+               for (k = 0; k < sd_template.dev_max; ++k) {
+                       sdkp = sd_dsk_arr[k];
+                       if (sdkp)
+                               vfree(sdkp);
+               }
+               vfree(sd_dsk_arr);
        }
-       for (i = 0; i < N_USED_SD_MAJORS; i++) {
-               del_gendisk(&(sd_gendisks[i]));
-               blk_clear(SD_MAJOR(i));
+       if (sd_sizes) vfree(sd_sizes);
+       if (sd_blocksizes) vfree(sd_blocksizes);
+       if (sd) vfree((char *) sd);
+       for (k = 0; k < N_USED_SD_MAJORS; k++) {
+               blk_dev[SD_MAJOR(k)].queue = NULL;
+               del_gendisk(&(sd_gendisks[k]));
+               blk_clear(SD_MAJOR(k));
        }
        sd_template.dev_max = 0;
        if (sd_gendisks != &sd_gendisk)
-               kfree(sd_gendisks);
+               vfree(sd_gendisks);
 }
 
+static Scsi_Disk * sd_get_sdisk(int index)
+{
+       Scsi_Disk * sdkp = NULL;
+       unsigned long iflags;
+
+       read_lock_irqsave(&sd_dsk_arr_lock, iflags);
+       if (sd_dsk_arr && (index >= 0) && (index < sd_template.dev_max))
+               sdkp = sd_dsk_arr[index];
+       read_unlock_irqrestore(&sd_dsk_arr_lock, iflags);
+       return sdkp;
+}
+
+MODULE_LICENSE("GPL");
+MODULE_AUTHOR("Eric Youngdale");
+MODULE_DESCRIPTION("SCSI disk (sd) driver");
+
 module_init(init_sd);
 module_exit(exit_sd);
-MODULE_LICENSE("GPL");