]> git.neil.brown.name Git - history.git/commitdiff
ide-scsi changes for new mid level api and error handling in 2.5.52
authorWillem Riede <wrlk@riede.org>
Mon, 23 Dec 2002 07:16:08 +0000 (01:16 -0600)
committerJames Bottomley <jejb@mulgrave.(none)>
Mon, 23 Dec 2002 07:16:08 +0000 (01:16 -0600)
Here is my proposal for bringing ide-scsi in line with the current kernel.

The attached patch does:

1. Change from old style initialization to the new hotplug initialization model.
   It involves declaring both a parent device and bus kernel object, which feels
   a bit awkward, but hey, it works.

2. Make ide-scsi request sense itself when CHECK_CONDITION is asserted. This
   change particularly makes ide-scsi run much smoother on my test box.

3. Implement eh_abort_handler and eh_device_reset_handler. I have no real way
   to thoroughly test these new routines, so for this part particularly I ask
   for peer review.

drivers/scsi/ide-scsi.c

index f800f95ba320db62ad0ae1cc1cd1621d006b582d..358e66e3d9ef5f0fb07a00f7a15b77087ef67775 100644 (file)
  * Ver 0.8   Feb 05 99   Optical media need translation too. Reverse 0.7.
  * Ver 0.9   Jul 04 99   Fix a bug in SG_SET_TRANSFORM.
  * Ver 0.91  Jun 10 02   Fix "off by one" error in transforms
+ * Ver 0.92  Dec 31 02   Implement new SCSI mid level API
  */
 
-#define IDESCSI_VERSION "0.9"
+#define IDESCSI_VERSION "0.92"
 
 #include <linux/module.h>
 #include <linux/config.h>
@@ -259,6 +260,42 @@ static void hexdump(u8 *x, int len)
        printk("]\n");
 }
 
+static int idescsi_check_condition(ide_drive_t *drive, struct request *failed_command)
+{
+       idescsi_scsi_t *scsi = drive->driver_data;
+       idescsi_pc_t   *pc;
+       struct request *rq;
+       u8             *buf;
+
+       /* stuff a sense request in front of our current request */
+       pc = kmalloc (sizeof (idescsi_pc_t), GFP_ATOMIC);
+       rq = kmalloc (sizeof (struct request), GFP_ATOMIC);
+       buf = kmalloc(SCSI_SENSE_BUFFERSIZE, GFP_ATOMIC);
+       if (pc == NULL || rq == NULL || buf == NULL) {
+               if (pc) kfree(pc);
+               if (rq) kfree(rq);
+               if (buf) kfree(buf);
+               return -ENOMEM;
+       }
+       memset (pc, 0, sizeof (idescsi_pc_t));
+       memset (buf, 0, SCSI_SENSE_BUFFERSIZE);
+       ide_init_drive_cmd(rq);
+       rq->special = (char *) pc;
+       pc->rq = rq;
+       pc->buffer = buf;
+       pc->c[0] = REQUEST_SENSE;
+       pc->c[4] = pc->request_transfer = pc->buffer_size = SCSI_SENSE_BUFFERSIZE;
+       rq->flags = REQ_SENSE;
+       pc->timeout = jiffies + WAIT_READY;
+       /* NOTE! Save the failed packet command in "rq->buffer" */
+       rq->buffer = (void *) failed_command->special;
+       if (test_bit(IDESCSI_LOG_CMD, &scsi->log)) {
+               printk ("ide-scsi: %s: queue cmd = ", drive->name);
+               hexdump(pc->c, 6);
+       }
+       return ide_do_drive_cmd(drive, rq, ide_preempt);
+}
+
 static int idescsi_end_request (ide_drive_t *drive, int uptodate, int nrsecs)
 {
        idescsi_scsi_t *scsi = drive->driver_data;
@@ -269,19 +306,35 @@ static int idescsi_end_request (ide_drive_t *drive, int uptodate, int nrsecs)
        u8 *scsi_buf;
        unsigned long flags;
 
-       if (!(rq->flags & REQ_SPECIAL)) {
+       if (!(rq->flags & (REQ_SPECIAL|REQ_SENSE))) {
                ide_end_request(drive, uptodate, nrsecs);
                return 0;
        }
        ide_end_drive_cmd (drive, 0, 0);
-       if (rq->errors >= ERROR_MAX) {
+       if (rq->flags & REQ_SENSE) {
+               idescsi_pc_t *opc = (idescsi_pc_t *) rq->buffer;
+               if (log) {
+                       printk ("ide-scsi: %s: wrap up check %lu, rst = ", drive->name, opc->scsi_cmd->serial_number);
+                       hexdump(pc->buffer,16);
+               }
+               memcpy((void *) opc->scsi_cmd->sense_buffer, pc->buffer, SCSI_SENSE_BUFFERSIZE);
+               kfree(pc->buffer);
+               kfree(pc);
+               kfree(rq);
+               pc = opc;
+               rq = pc->rq;
+               pc->scsi_cmd->result = (CHECK_CONDITION << 1) | (DID_OK << 16);
+       } else if (rq->errors >= ERROR_MAX) {
                pc->scsi_cmd->result = DID_ERROR << 16;
                if (log)
                        printk ("ide-scsi: %s: I/O error for %lu\n", drive->name, pc->scsi_cmd->serial_number);
        } else if (rq->errors) {
-               pc->scsi_cmd->result = (CHECK_CONDITION << 1) | (DID_OK << 16);
                if (log)
                        printk ("ide-scsi: %s: check condition for %lu\n", drive->name, pc->scsi_cmd->serial_number);
+               if (!idescsi_check_condition(drive, rq))
+                       /* we started a request sense, so we'll be back, exit for now */
+                       return 0;
+               pc->scsi_cmd->result = (CHECK_CONDITION << 1) | (DID_OK << 16);
        } else {
                pc->scsi_cmd->result = DID_OK << 16;
                idescsi_transform_pc2 (drive, pc);
@@ -489,11 +542,11 @@ static ide_startstop_t idescsi_issue_pc (ide_drive_t *drive, idescsi_pc_t *pc)
 static ide_startstop_t idescsi_do_request (ide_drive_t *drive, struct request *rq, sector_t block)
 {
 #if IDESCSI_DEBUG_LOG
-       printk (KERN_INFO "rq_status: %d, dev: %s, cmd: %d, errors: %d\n",rq->rq_status, rq->rq_disk->disk_name,rq->cmd,rq->errors);
-       printk (KERN_INFO "sector: %ld, nr_sectors: %ld, current_nr_sectors: %ld\n",rq->sector,rq->nr_sectors,rq->current_nr_sectors);
+       printk (KERN_INFO "rq_status: %d, dev: %s, cmd: %x, errors: %d\n",rq->rq_status, rq->rq_disk->disk_name,rq->cmd[0],rq->errors);
+       printk (KERN_INFO "sector: %ld, nr_sectors: %ld, current_nr_sectors: %d\n",rq->sector,rq->nr_sectors,rq->current_nr_sectors);
 #endif /* IDESCSI_DEBUG_LOG */
 
-       if (rq->flags & REQ_SPECIAL) {
+       if (rq->flags & (REQ_SPECIAL|REQ_SENSE)) {
                return idescsi_issue_pc (drive, (idescsi_pc_t *) rq->special);
        }
        blk_dump_rq_flags(rq, "ide-scsi: unsup command");
@@ -629,35 +682,10 @@ failed:
        return 1;
 }
 
-int idescsi_detect (Scsi_Host_Template *host_template)
+int idescsi_slave_configure(Scsi_Device * sdp)
 {
-       struct Scsi_Host *host;
-       int id;
-       int last_lun = 0;
-
-       host_template->proc_name = "ide-scsi";
-       host = scsi_register(host_template, 0);
-       if(host == NULL)
-               return 0;
-               
-       for (id = 0; id < MAX_HWIFS * MAX_DRIVES && idescsi_drives[id]; id++)
-               last_lun = IDE_MAX(last_lun, idescsi_drives[id]->last_lun);
-       host->max_id = id;
-       host->max_lun = last_lun + 1;
-       host->can_queue = host->cmd_per_lun * id;
-       return 1;
-}
-
-int idescsi_release (struct Scsi_Host *host)
-{
-       ide_drive_t *drive;
-       int id;
-
-       for (id = 0; id < MAX_HWIFS * MAX_DRIVES; id++) {
-               drive = idescsi_drives[id];
-               if (drive)
-                       DRIVER(drive)->busy--;
-       }
+       /* Configure detected device */
+       scsi_adjust_queue_depth(sdp, MSG_SIMPLE_TAG, sdp->host->cmd_per_lun);
        return 0;
 }
 
@@ -832,17 +860,67 @@ abort:
        if (rq) kfree (rq);
        cmd->result = DID_ERROR << 16;
        done(cmd);
-       return 0;
+       return 1;
 }
 
 int idescsi_abort (Scsi_Cmnd *cmd)
 {
-       return SCSI_ABORT_SNOOZE;
+       int countdown = 8;
+       unsigned long flags;
+       ide_drive_t *drive = idescsi_drives[cmd->target];
+       idescsi_scsi_t *scsi;
+
+       printk (KERN_ERR "ide-scsi: abort called for %lu\n", cmd->serial_number);
+       if (drive && (scsi = drive->driver_data))
+               while (countdown--) {
+                       /* is cmd active?
+                        *  need to lock so this stuff doesn't change under us */
+                       spin_lock_irqsave(&ide_lock, flags);
+                       if (scsi->pc && scsi->pc->scsi_cmd->serial_number == cmd->serial_number) {
+                               /* yep - let's give it some more time - 
+                                * we can do that, we're in _our_ error kernel thread */
+                               spin_unlock_irqrestore(&ide_lock, flags);
+                               scsi_sleep(HZ);
+                               continue;
+                       }
+                       /* no, but is it queued in the ide subsystem? */
+                       if (elv_queue_empty(&drive->queue)) {
+                               spin_unlock_irqrestore(&ide_lock, flags);
+                               return SUCCESS;
+                       }
+                       spin_unlock_irqrestore(&ide_lock, flags);
+                       schedule_timeout(HZ/10);
+               }
+       return FAILED;
 }
 
-int idescsi_reset (Scsi_Cmnd *cmd, unsigned int resetflags)
+int idescsi_reset (Scsi_Cmnd *cmd)
 {
-       return SCSI_RESET_SUCCESS;
+       unsigned long flags;
+       struct request *req;
+       ide_drive_t *drive = idescsi_drives[cmd->target];
+
+       printk (KERN_ERR "ide-scsi: reset called for %lu\n", cmd->serial_number);
+       /* first null the handler for the drive and let any process
+        * doing IO (on another CPU) run to (partial) completion
+        * the lock prevents processing new requests */
+       spin_lock_irqsave(&ide_lock, flags);
+       while (HWGROUP(drive)->handler) {
+               HWGROUP(drive)->handler = NULL;
+               schedule_timeout(1);
+       }
+       /* now nuke the drive queue */
+       while ((req = elv_next_request(&drive->queue))) {
+               blkdev_dequeue_request(req);
+               end_that_request_last(req);
+       }
+       /* FIXME - this will probably leak memory */
+       HWGROUP(drive)->rq = NULL;
+       if (drive->driver_data) ((idescsi_scsi_t *)drive->driver_data)->pc = NULL;
+       spin_unlock_irqrestore(&ide_lock, flags);
+       /* finally, reset the drive (and its partner on the bus...) */
+       ide_do_reset (drive);   
+       return SUCCESS;
 }
 
 static int idescsi_bios(struct scsi_device *sdev, struct block_device *bdev,
@@ -859,32 +937,69 @@ static int idescsi_bios(struct scsi_device *sdev, struct block_device *bdev,
 }
 
 static Scsi_Host_Template idescsi_template = {
-       .module         = THIS_MODULE,
-       .name           = "idescsi",
-       .detect         = idescsi_detect,
-       .release        = idescsi_release,
-       .info           = idescsi_info,
-       .ioctl          = idescsi_ioctl,
-       .queuecommand   = idescsi_queue,
-       .bios_param     = idescsi_bios,
-       .can_queue      = 10,
-       .this_id        = -1,
-       .sg_tablesize   = 256,
-       .cmd_per_lun    = 5,
-       .use_clustering = DISABLE_CLUSTERING,
-       .emulated       = 1,
+       .module                 = THIS_MODULE,
+       .name                   = "idescsi",
+       .info                   = idescsi_info,
+       .slave_configure        = idescsi_slave_configure,
+       .ioctl                  = idescsi_ioctl,
+       .queuecommand           = idescsi_queue,
+       .eh_abort_handler       = idescsi_abort,
+       .eh_device_reset_handler = idescsi_reset,
+       .bios_param             = idescsi_bios,
+       .can_queue              = 40,
+       .this_id                = -1,
+       .sg_tablesize           = 256,
+       .cmd_per_lun            = 5,
+       .max_sectors            = 128,
+       .use_clustering         = DISABLE_CLUSTERING,
+       .emulated               = 1,
+};
+
+static struct Scsi_Host *idescsi_host;
+
+static struct device     idescsi_primary = {
+       .name           = "Ide-scsi Parent",
+       .bus_id         = "ide-scsi",
+};
+static struct bus_type   idescsi_emu_bus = {
+       .name           = "ide-scsi",
 };
 
 static int __init init_idescsi_module(void)
 {
+       int id;
+       int last_lun = 0;
+
        ide_register_driver(&idescsi_driver);
-       scsi_register_host(&idescsi_template);
+       device_register(&idescsi_primary);
+       bus_register   (&idescsi_emu_bus);
+       idescsi_template.proc_name = "ide-scsi";
+       idescsi_host = scsi_register(&idescsi_template, 0);
+       if(idescsi_host == NULL)
+               return 1;
+               
+       for (id = 0; id < MAX_HWIFS * MAX_DRIVES && idescsi_drives[id]; id++)
+               last_lun = IDE_MAX(last_lun, idescsi_drives[id]->last_lun);
+       idescsi_host->max_id = id;
+       idescsi_host->max_lun = last_lun + 1;
+       scsi_add_host(idescsi_host, &idescsi_primary);
        return 0;
 }
 
 static void __exit exit_idescsi_module(void)
 {
-       scsi_unregister_host(&idescsi_template);
+       ide_drive_t *drive;
+       int id;
+
+       scsi_remove_host(idescsi_host);
+       for (id = 0; id < MAX_HWIFS * MAX_DRIVES; id++) {
+               drive = idescsi_drives[id];
+               if (drive)
+                       DRIVER(drive)->busy--;
+       }
+       scsi_unregister   (idescsi_host);
+       device_unregister(&idescsi_primary);
+       bus_unregister   (&idescsi_emu_bus);
        ide_unregister_driver(&idescsi_driver);
 }