vserver 1.9.3
[linux-2.6.git] / drivers / scsi / sd.c
index 8fd4590..f44e96d 100644 (file)
@@ -42,6 +42,7 @@
 #include <linux/genhd.h>
 #include <linux/hdreg.h>
 #include <linux/errno.h>
+#include <linux/idr.h>
 #include <linux/interrupt.h>
 #include <linux/init.h>
 #include <linux/blkdev.h>
 
 #include "scsi_logging.h"
 
-
 /*
- * Remaining dev_t-handling stuff
+ * More than enough for everybody ;)  The huge number of majors
+ * is a leftover from 16bit dev_t days, we don't really need that
+ * much numberspace.
  */
 #define SD_MAJORS      16
-#define SD_DISKS       32768   /* anything between 256 and 262144 */
+
+/*
+ * This is limited by the naming scheme enforced in sd_probe,
+ * add another character to it if you really need more disks.
+ */
+#define SD_MAX_DISKS   (((26 * 26) + 26 + 1) * 26)
 
 /*
  * Time out in seconds for disks and Magneto-opticals (which are slower).
@@ -96,8 +103,7 @@ struct scsi_disk {
        unsigned        RCD : 1;        /* state of disk RCD bit, unused */
 };
 
-
-static unsigned long sd_index_bits[SD_DISKS / BITS_PER_LONG];
+static DEFINE_IDR(sd_index_idr);
 static spinlock_t sd_index_lock = SPIN_LOCK_UNLOCKED;
 
 /* This semaphore is used to mediate the 0->1 reference get in the
@@ -113,6 +119,7 @@ static int sd_remove(struct device *);
 static void sd_shutdown(struct device *dev);
 static void sd_rescan(struct device *);
 static int sd_init_command(struct scsi_cmnd *);
+static int sd_issue_flush(struct device *, sector_t *);
 static void sd_read_capacity(struct scsi_disk *sdkp, char *diskname,
                 struct scsi_request *SRpnt, unsigned char *buffer);
 
@@ -126,9 +133,11 @@ static struct scsi_driver sd_template = {
        },
        .rescan                 = sd_rescan,
        .init_command           = sd_init_command,
+       .issue_flush            = sd_issue_flush,
 };
 
-/* Device no to disk mapping:
+/*
+ * Device no to disk mapping:
  * 
  *       major         disc2     disc  p1
  *   |............|.............|....|....| <- dev_t
@@ -141,7 +150,6 @@ static struct scsi_driver sd_template = {
  * As we stay compatible with our numbering scheme, we can reuse 
  * the well-know SCSI majors 8, 65--71, 136--143.
  */
-
 static int sd_major(int major_idx)
 {
        switch (major_idx) {
@@ -157,14 +165,6 @@ static int sd_major(int major_idx)
        }
 }
 
-static unsigned int make_sd_dev(unsigned int sd_nr, unsigned int part)
-{
-       return  (part & 0xf) | ((sd_nr & 0xf) << 4) |
-               (sd_major((sd_nr & 0xf0) >> 4) << 20) | (sd_nr & 0xfff00);
-}
-
-/* reverse mapping dev -> (sd_nr, part) not currently needed */
-
 #define to_scsi_disk(obj) container_of(obj,struct scsi_disk,kref)
 
 static inline struct scsi_disk *scsi_disk(struct gendisk *disk)
@@ -180,16 +180,14 @@ static struct scsi_disk *scsi_disk_get(struct gendisk *disk)
        if (disk->private_data == NULL)
                goto out;
        sdkp = scsi_disk(disk);
-       if (!kref_get(&sdkp->kref))
-               goto out_sdkp;
+       kref_get(&sdkp->kref);
        if (scsi_device_get(sdkp->device))
                goto out_put;
        up(&sd_ref_sem);
        return sdkp;
 
  out_put:
-       kref_put(&sdkp->kref);
- out_sdkp:
+       kref_put(&sdkp->kref, scsi_disk_release);
        sdkp = NULL;
  out:
        up(&sd_ref_sem);
@@ -200,7 +198,7 @@ static void scsi_disk_put(struct scsi_disk *sdkp)
 {
        down(&sd_ref_sem);
        scsi_device_put(sdkp->device);
-       kref_put(&sdkp->kref);
+       kref_put(&sdkp->kref, scsi_disk_release);
        up(&sd_ref_sem);
 }
 
@@ -648,7 +646,7 @@ static int sd_media_changed(struct gendisk *disk)
         */
        retval = -ENODEV;
        if (scsi_block_when_processing_errors(sdp))
-               retval = scsi_ioctl(sdp, SCSI_IOCTL_TEST_UNIT_READY, NULL);
+               retval = scsi_test_unit_ready(sdp, SD_TIMEOUT, SD_MAX_RETRIES);
 
        /*
         * Unable to test, unit probably not ready.   This usually
@@ -676,6 +674,62 @@ not_present:
        return 1;
 }
 
+static int sd_sync_cache(struct scsi_device *sdp)
+{
+       struct scsi_request *sreq;
+       int retries, res;
+
+       if (!scsi_device_online(sdp))
+               return -ENODEV;
+
+       sreq = scsi_allocate_request(sdp, GFP_KERNEL);
+       if (!sreq) {
+               printk("FAILED\n  No memory for request\n");
+               return -ENOMEM;
+       }
+
+       sreq->sr_data_direction = DMA_NONE;
+       for (retries = 3; retries > 0; --retries) {
+               unsigned char cmd[10] = { 0 };
+
+               cmd[0] = SYNCHRONIZE_CACHE;
+               /*
+                * Leave the rest of the command zero to indicate
+                * flush everything.
+                */
+               scsi_wait_req(sreq, cmd, NULL, 0, SD_TIMEOUT, SD_MAX_RETRIES);
+               if (sreq->sr_result == 0)
+                       break;
+       }
+
+       res = sreq->sr_result;
+       if (res) {
+               printk(KERN_WARNING "FAILED\n  status = %x, message = %02x, "
+                                   "host = %d, driver = %02x\n  ",
+                                   status_byte(res), msg_byte(res),
+                                   host_byte(res), driver_byte(res));
+                       if (driver_byte(res) & DRIVER_SENSE)
+                               scsi_print_req_sense("sd", sreq);
+       }
+
+       scsi_release_request(sreq);
+       return res;
+}
+
+static int sd_issue_flush(struct device *dev, sector_t *error_sector)
+{
+       struct scsi_device *sdp = to_scsi_device(dev);
+       struct scsi_disk *sdkp = dev_get_drvdata(dev);
+
+       if (!sdkp)
+               return -ENODEV;
+
+       if (!sdkp->WCE)
+               return 0;
+
+       return sd_sync_cache(sdp);
+}
+
 static void sd_rescan(struct device *dev)
 {
        struct scsi_disk *sdkp = dev_get_drvdata(dev);
@@ -1347,7 +1401,7 @@ static int sd_probe(struct device *dev)
        struct scsi_disk *sdkp;
        struct gendisk *gd;
        u32 index;
-       int error, devno;
+       int error;
 
        error = -ENODEV;
        if ((sdp->type != TYPE_DISK) && (sdp->type != TYPE_MOD))
@@ -1362,27 +1416,23 @@ static int sd_probe(struct device *dev)
                goto out;
 
        memset (sdkp, 0, sizeof(*sdkp));
-       kref_init(&sdkp->kref, scsi_disk_release);
-
-       /* Note: We can accomodate 64 partitions, but the genhd code
-        * assumes partitions allocate consecutive minors, which they don't.
-        * So for now stay with max 16 partitions and leave two spare bits. 
-        * Later, we may change the genhd code and the alloc_disk() call
-        * and the ->minors assignment here.    KG, 2004-02-10
-        */ 
+       kref_init(&sdkp->kref);
+
        gd = alloc_disk(16);
        if (!gd)
                goto out_free;
 
+       if (!idr_pre_get(&sd_index_idr, GFP_KERNEL))
+               goto out_put;
+
        spin_lock(&sd_index_lock);
-       index = find_first_zero_bit(sd_index_bits, SD_DISKS);
-       if (index == SD_DISKS) {
-               spin_unlock(&sd_index_lock);
+       error = idr_get_new(&sd_index_idr, NULL, &index);
+       spin_unlock(&sd_index_lock);
+
+       if (index >= SD_MAX_DISKS)
                error = -EBUSY;
+       if (error)
                goto out_put;
-       }
-       __set_bit(index, sd_index_bits);
-       spin_unlock(&sd_index_lock);
 
        sdkp->device = sdp;
        sdkp->driver = &sd_template;
@@ -1397,15 +1447,14 @@ static int sd_probe(struct device *dev)
                        sdp->timeout = SD_MOD_TIMEOUT;
        }
 
-       devno = make_sd_dev(index, 0);
-       gd->major = MAJOR(devno);
-       gd->first_minor = MINOR(devno);
+       gd->major = sd_major((index & 0xf0) >> 4);
+       gd->first_minor = ((index & 0xf) << 4) | (index & 0xfff00);
        gd->minors = 16;
        gd->fops = &sd_fops;
 
        if (index < 26) {
                sprintf(gd->disk_name, "sd%c", 'a' + index % 26);
-       } else if (index < (26*27)) {
+       } else if (index < (26 + 1) * 26) {
                sprintf(gd->disk_name, "sd%c%c",
                        'a' + index / 26 - 1,'a' + index % 26);
        } else {
@@ -1464,7 +1513,7 @@ static int sd_remove(struct device *dev)
        del_gendisk(sdkp->disk);
        sd_shutdown(dev);
        down(&sd_ref_sem);
-       kref_put(&sdkp->kref);
+       kref_put(&sdkp->kref, scsi_disk_release);
        up(&sd_ref_sem);
 
        return 0;
@@ -1485,7 +1534,7 @@ static void scsi_disk_release(struct kref *kref)
        struct gendisk *disk = sdkp->disk;
        
        spin_lock(&sd_index_lock);
-       clear_bit(sdkp->index, sd_index_bits);
+       idr_remove(&sd_index_idr, sdkp->index);
        spin_unlock(&sd_index_lock);
 
        disk->private_data = NULL;
@@ -1503,52 +1552,17 @@ static void scsi_disk_release(struct kref *kref)
 static void sd_shutdown(struct device *dev)
 {
        struct scsi_device *sdp = to_scsi_device(dev);
-       struct scsi_disk *sdkp;
-       struct scsi_request *sreq;
-       int retries, res;
+       struct scsi_disk *sdkp = dev_get_drvdata(dev);
 
-       sdkp = dev_get_drvdata(dev);
        if (!sdkp)
-               return;         /* this can happen */
+               return;         /* this can happen */
 
-       if (!scsi_device_online(sdp) || !sdkp->WCE)
+       if (!sdkp->WCE)
                return;
 
-       printk(KERN_NOTICE "Synchronizing SCSI cache for disk %s: ",
+       printk(KERN_NOTICE "Synchronizing SCSI cache for disk %s: \n",
                        sdkp->disk->disk_name);
-
-       sreq = scsi_allocate_request(sdp, GFP_KERNEL);
-       if (!sreq) {
-               printk("FAILED\n  No memory for request\n");
-               return;
-       }
-
-       sreq->sr_data_direction = DMA_NONE;
-       for (retries = 3; retries > 0; --retries) {
-               unsigned char cmd[10] = { 0 };
-
-               cmd[0] = SYNCHRONIZE_CACHE;
-               /*
-                * Leave the rest of the command zero to indicate
-                * flush everything.
-                */
-               scsi_wait_req(sreq, cmd, NULL, 0, SD_TIMEOUT, SD_MAX_RETRIES);
-               if (sreq->sr_result == 0)
-                       break;
-       }
-
-       res = sreq->sr_result;
-       if (res) {
-               printk(KERN_WARNING "FAILED\n  status = %x, message = %02x, "
-                                   "host = %d, driver = %02x\n  ",
-                                   status_byte(res), msg_byte(res),
-                                   host_byte(res), driver_byte(res));
-                       if (driver_byte(res) & DRIVER_SENSE)
-                               scsi_print_req_sense("sd", sreq);
-       }
-       
-       scsi_release_request(sreq);
-       printk("\n");
+       sd_sync_cache(sdp);
 }      
 
 /**