Commit d55b3076 authored by Willem Riede's avatar Willem Riede Committed by James Bottomley

ide-scsi changes for new mid level api and error handling in 2.5.52

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.
parent 7ee75762
...@@ -28,9 +28,10 @@ ...@@ -28,9 +28,10 @@
* Ver 0.8 Feb 05 99 Optical media need translation too. Reverse 0.7. * 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.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.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/module.h>
#include <linux/config.h> #include <linux/config.h>
...@@ -259,6 +260,42 @@ static void hexdump(u8 *x, int len) ...@@ -259,6 +260,42 @@ static void hexdump(u8 *x, int len)
printk("]\n"); 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) static int idescsi_end_request (ide_drive_t *drive, int uptodate, int nrsecs)
{ {
idescsi_scsi_t *scsi = drive->driver_data; idescsi_scsi_t *scsi = drive->driver_data;
...@@ -269,19 +306,35 @@ static int idescsi_end_request (ide_drive_t *drive, int uptodate, int nrsecs) ...@@ -269,19 +306,35 @@ static int idescsi_end_request (ide_drive_t *drive, int uptodate, int nrsecs)
u8 *scsi_buf; u8 *scsi_buf;
unsigned long flags; unsigned long flags;
if (!(rq->flags & REQ_SPECIAL)) { if (!(rq->flags & (REQ_SPECIAL|REQ_SENSE))) {
ide_end_request(drive, uptodate, nrsecs); ide_end_request(drive, uptodate, nrsecs);
return 0; return 0;
} }
ide_end_drive_cmd (drive, 0, 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; pc->scsi_cmd->result = DID_ERROR << 16;
if (log) if (log)
printk ("ide-scsi: %s: I/O error for %lu\n", drive->name, pc->scsi_cmd->serial_number); printk ("ide-scsi: %s: I/O error for %lu\n", drive->name, pc->scsi_cmd->serial_number);
} else if (rq->errors) { } else if (rq->errors) {
pc->scsi_cmd->result = (CHECK_CONDITION << 1) | (DID_OK << 16);
if (log) if (log)
printk ("ide-scsi: %s: check condition for %lu\n", drive->name, pc->scsi_cmd->serial_number); 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 { } else {
pc->scsi_cmd->result = DID_OK << 16; pc->scsi_cmd->result = DID_OK << 16;
idescsi_transform_pc2 (drive, pc); idescsi_transform_pc2 (drive, pc);
...@@ -489,11 +542,11 @@ static ide_startstop_t idescsi_issue_pc (ide_drive_t *drive, idescsi_pc_t *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) static ide_startstop_t idescsi_do_request (ide_drive_t *drive, struct request *rq, sector_t block)
{ {
#if IDESCSI_DEBUG_LOG #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 "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: %ld\n",rq->sector,rq->nr_sectors,rq->current_nr_sectors); 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 */ #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); return idescsi_issue_pc (drive, (idescsi_pc_t *) rq->special);
} }
blk_dump_rq_flags(rq, "ide-scsi: unsup command"); blk_dump_rq_flags(rq, "ide-scsi: unsup command");
...@@ -629,35 +682,10 @@ static int idescsi_attach(ide_drive_t *drive) ...@@ -629,35 +682,10 @@ static int idescsi_attach(ide_drive_t *drive)
return 1; return 1;
} }
int idescsi_detect (Scsi_Host_Template *host_template) int idescsi_slave_configure(Scsi_Device * sdp)
{ {
struct Scsi_Host *host; /* Configure detected device */
int id; scsi_adjust_queue_depth(sdp, MSG_SIMPLE_TAG, sdp->host->cmd_per_lun);
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--;
}
return 0; return 0;
} }
...@@ -832,17 +860,67 @@ int idescsi_queue (Scsi_Cmnd *cmd, void (*done)(Scsi_Cmnd *)) ...@@ -832,17 +860,67 @@ int idescsi_queue (Scsi_Cmnd *cmd, void (*done)(Scsi_Cmnd *))
if (rq) kfree (rq); if (rq) kfree (rq);
cmd->result = DID_ERROR << 16; cmd->result = DID_ERROR << 16;
done(cmd); done(cmd);
return 0; return 1;
} }
int idescsi_abort (Scsi_Cmnd *cmd) 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, static int idescsi_bios(struct scsi_device *sdev, struct block_device *bdev,
...@@ -861,30 +939,67 @@ static int idescsi_bios(struct scsi_device *sdev, struct block_device *bdev, ...@@ -861,30 +939,67 @@ static int idescsi_bios(struct scsi_device *sdev, struct block_device *bdev,
static Scsi_Host_Template idescsi_template = { static Scsi_Host_Template idescsi_template = {
.module = THIS_MODULE, .module = THIS_MODULE,
.name = "idescsi", .name = "idescsi",
.detect = idescsi_detect,
.release = idescsi_release,
.info = idescsi_info, .info = idescsi_info,
.slave_configure = idescsi_slave_configure,
.ioctl = idescsi_ioctl, .ioctl = idescsi_ioctl,
.queuecommand = idescsi_queue, .queuecommand = idescsi_queue,
.eh_abort_handler = idescsi_abort,
.eh_device_reset_handler = idescsi_reset,
.bios_param = idescsi_bios, .bios_param = idescsi_bios,
.can_queue = 10, .can_queue = 40,
.this_id = -1, .this_id = -1,
.sg_tablesize = 256, .sg_tablesize = 256,
.cmd_per_lun = 5, .cmd_per_lun = 5,
.max_sectors = 128,
.use_clustering = DISABLE_CLUSTERING, .use_clustering = DISABLE_CLUSTERING,
.emulated = 1, .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) static int __init init_idescsi_module(void)
{ {
int id;
int last_lun = 0;
ide_register_driver(&idescsi_driver); 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; return 0;
} }
static void __exit exit_idescsi_module(void) 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); ide_unregister_driver(&idescsi_driver);
} }
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment