X-Git-Url: http://git.onelab.eu/?a=blobdiff_plain;f=drivers%2Fscsi%2Fide-scsi.c;h=94d1de55607f2dc00a05605259b0d8e864937062;hb=16c70f8c1b54b61c3b951b6fb220df250fe09b32;hp=599e7137c4100ab80a9489ca8d60a91cb54d2027;hpb=6a77f38946aaee1cd85eeec6cf4229b204c15071;p=linux-2.6.git diff --git a/drivers/scsi/ide-scsi.c b/drivers/scsi/ide-scsi.c index 599e7137c..94d1de556 100644 --- a/drivers/scsi/ide-scsi.c +++ b/drivers/scsi/ide-scsi.c @@ -34,7 +34,6 @@ #define IDESCSI_VERSION "0.92" #include -#include #include #include #include @@ -46,6 +45,8 @@ #include #include #include +#include +#include #include #include @@ -96,14 +97,42 @@ typedef struct idescsi_pc_s { */ #define IDESCSI_LOG_CMD 0 /* Log SCSI commands */ -typedef struct { - ide_drive_t *drive; +typedef struct ide_scsi_obj { + ide_drive_t *drive; + ide_driver_t *driver; + struct gendisk *disk; + struct Scsi_Host *host; + idescsi_pc_t *pc; /* Current packet command */ unsigned long flags; /* Status/Action flags */ unsigned long transform; /* SCSI cmd translation layer */ unsigned long log; /* log flags */ } idescsi_scsi_t; +static DEFINE_MUTEX(idescsi_ref_mutex); + +#define ide_scsi_g(disk) \ + container_of((disk)->private_data, struct ide_scsi_obj, driver) + +static struct ide_scsi_obj *ide_scsi_get(struct gendisk *disk) +{ + struct ide_scsi_obj *scsi = NULL; + + mutex_lock(&idescsi_ref_mutex); + scsi = ide_scsi_g(disk); + if (scsi) + scsi_host_get(scsi->host); + mutex_unlock(&idescsi_ref_mutex); + return scsi; +} + +static void ide_scsi_put(struct ide_scsi_obj *scsi) +{ + mutex_lock(&idescsi_ref_mutex); + scsi_host_put(scsi->host); + mutex_unlock(&idescsi_ref_mutex); +} + static inline idescsi_scsi_t *scsihost_to_idescsi(struct Scsi_Host *host) { return (idescsi_scsi_t*) (&host[1]); @@ -151,8 +180,21 @@ static void idescsi_input_buffers (ide_drive_t *drive, idescsi_pc_t *pc, unsigne return; } count = min(pc->sg->length - pc->b_count, bcount); - buf = page_address(pc->sg->page) + pc->sg->offset; - drive->hwif->atapi_input_bytes(drive, buf + pc->b_count, count); + if (PageHighMem(pc->sg->page)) { + unsigned long flags; + + local_irq_save(flags); + buf = kmap_atomic(pc->sg->page, KM_IRQ0) + + pc->sg->offset; + drive->hwif->atapi_input_bytes(drive, + buf + pc->b_count, count); + kunmap_atomic(buf - pc->sg->offset, KM_IRQ0); + local_irq_restore(flags); + } else { + buf = page_address(pc->sg->page) + pc->sg->offset; + drive->hwif->atapi_input_bytes(drive, + buf + pc->b_count, count); + } bcount -= count; pc->b_count += count; if (pc->b_count == pc->sg->length) { pc->sg++; @@ -173,8 +215,21 @@ static void idescsi_output_buffers (ide_drive_t *drive, idescsi_pc_t *pc, unsign return; } count = min(pc->sg->length - pc->b_count, bcount); - buf = page_address(pc->sg->page) + pc->sg->offset; - drive->hwif->atapi_output_bytes(drive, buf + pc->b_count, count); + if (PageHighMem(pc->sg->page)) { + unsigned long flags; + + local_irq_save(flags); + buf = kmap_atomic(pc->sg->page, KM_IRQ0) + + pc->sg->offset; + drive->hwif->atapi_output_bytes(drive, + buf + pc->b_count, count); + kunmap_atomic(buf - pc->sg->offset, KM_IRQ0); + local_irq_restore(flags); + } else { + buf = page_address(pc->sg->page) + pc->sg->offset; + drive->hwif->atapi_output_bytes(drive, + buf + pc->b_count, count); + } bcount -= count; pc->b_count += count; if (pc->b_count == pc->sg->length) { pc->sg++; @@ -276,9 +331,9 @@ static int idescsi_check_condition(ide_drive_t *drive, struct request *failed_co 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); + kfree(buf); + kfree(rq); + kfree(pc); return -ENOMEM; } memset (pc, 0, sizeof (idescsi_pc_t)); @@ -298,9 +353,12 @@ static int idescsi_check_condition(ide_drive_t *drive, struct request *failed_co printk ("ide-scsi: %s: queue cmd = ", drive->name); hexdump(pc->c, 6); } + rq->rq_disk = scsi->disk; return ide_do_drive_cmd(drive, rq, ide_preempt); } +static int idescsi_end_request(ide_drive_t *, int, int); + static ide_startstop_t idescsi_atapi_error(ide_drive_t *drive, struct request *rq, u8 stat, u8 err) { @@ -309,7 +367,9 @@ idescsi_atapi_error(ide_drive_t *drive, struct request *rq, u8 stat, u8 err) HWIF(drive)->OUTB(WIN_IDLEIMMEDIATE,IDE_COMMAND_REG); rq->errors++; - DRIVER(drive)->end_request(drive, 0, 0); + + idescsi_end_request(drive, 0, 0); + return ide_stopped; } @@ -321,7 +381,9 @@ idescsi_atapi_abort(ide_drive_t *drive, struct request *rq) ((idescsi_pc_t *) rq->special)->scsi_cmd->serial_number); #endif rq->errors |= ERROR_MAX; - DRIVER(drive)->end_request(drive, 0, 0); + + idescsi_end_request(drive, 0, 0); + return ide_stopped; } @@ -333,6 +395,7 @@ static int idescsi_end_request (ide_drive_t *drive, int uptodate, int nrsecs) int log = test_bit(IDESCSI_LOG_CMD, &scsi->log); struct Scsi_Host *host; u8 *scsi_buf; + int errors = rq->errors; unsigned long flags; if (!(rq->flags & (REQ_SPECIAL|REQ_SENSE))) { @@ -359,11 +422,11 @@ static int idescsi_end_request (ide_drive_t *drive, int uptodate, int nrsecs) printk (KERN_WARNING "ide-scsi: %s: timed out for %lu\n", drive->name, pc->scsi_cmd->serial_number); pc->scsi_cmd->result = DID_TIME_OUT << 16; - } else if (rq->errors >= ERROR_MAX) { + } else if (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) { + } else if (errors) { if (log) printk ("ide-scsi: %s: check condition for %lu\n", drive->name, pc->scsi_cmd->serial_number); if (!idescsi_check_condition(drive, rq)) @@ -454,7 +517,7 @@ static ide_startstop_t idescsi_pc_intr (ide_drive_t *drive) /* No more interrupts */ if (test_bit(IDESCSI_LOG_CMD, &scsi->log)) printk (KERN_INFO "Packet command completed, %d bytes transferred\n", pc->actually_transferred); - local_irq_enable(); + local_irq_enable_in_hardirq(); if (status.b.check) rq->errors++; idescsi_end_request (drive, 1, 0); @@ -536,8 +599,7 @@ static ide_startstop_t idescsi_transfer_pc(ide_drive_t *drive) "issuing a packet command\n"); return ide_do_reset (drive); } - if (HWGROUP(drive)->handler != NULL) - BUG(); + BUG_ON(HWGROUP(drive)->handler != NULL); /* Set the interrupt routine */ ide_set_handler(drive, &idescsi_pc_intr, get_timeout(pc), idescsi_expiry); /* Send the actual packet */ @@ -627,8 +689,7 @@ static ide_startstop_t idescsi_issue_pc (ide_drive_t *drive, idescsi_pc_t *pc) set_bit(PC_DMA_OK, &pc->flags); if (test_bit(IDESCSI_DRQ_INTERRUPT, &scsi->flags)) { - if (HWGROUP(drive)->handler != NULL) - BUG(); + BUG_ON(HWGROUP(drive)->handler != NULL); ide_set_handler(drive, &idescsi_transfer_pc, get_timeout(pc), idescsi_expiry); /* Issue the packet command */ @@ -678,8 +739,6 @@ static void idescsi_add_settings(ide_drive_t *drive) */ static void idescsi_setup (ide_drive_t *drive, idescsi_scsi_t *scsi) { - DRIVER(drive)->busy++; - drive->ready_stat = 0; if (drive->id && (drive->id->config & 0x0060) == 0x20) set_bit (IDESCSI_DRQ_INTERRUPT, &scsi->flags); set_bit(IDESCSI_TRANSFORM, &scsi->transform); @@ -688,57 +747,81 @@ static void idescsi_setup (ide_drive_t *drive, idescsi_scsi_t *scsi) set_bit(IDESCSI_LOG_CMD, &scsi->log); #endif /* IDESCSI_DEBUG_LOG */ idescsi_add_settings(drive); - DRIVER(drive)->busy--; } -static int idescsi_cleanup (ide_drive_t *drive) +static void ide_scsi_remove(ide_drive_t *drive) { struct Scsi_Host *scsihost = drive->driver_data; + struct ide_scsi_obj *scsi = scsihost_to_idescsi(scsihost); + struct gendisk *g = scsi->disk; + + ide_unregister_subdriver(drive, scsi->driver); + + ide_unregister_region(g); - if (ide_unregister_subdriver(drive)) - return 1; - - /* FIXME?: Are these two statements necessary? */ drive->driver_data = NULL; - drive->disk->fops = ide_fops; + g->private_data = NULL; + put_disk(g); scsi_remove_host(scsihost); - scsi_host_put(scsihost); - return 0; + ide_scsi_put(scsi); } -static int idescsi_attach(ide_drive_t *drive); +static int ide_scsi_probe(ide_drive_t *); + +#ifdef CONFIG_PROC_FS +static ide_proc_entry_t idescsi_proc[] = { + { "capacity", S_IFREG|S_IRUGO, proc_ide_read_capacity, NULL }, + { NULL, 0, NULL, NULL } +}; +#else +# define idescsi_proc NULL +#endif -/* - * IDE subdriver functions, registered with ide.c - */ static ide_driver_t idescsi_driver = { - .owner = THIS_MODULE, - .name = "ide-scsi", + .gen_driver = { + .owner = THIS_MODULE, + .name = "ide-scsi", + .bus = &ide_bus_type, + }, + .probe = ide_scsi_probe, + .remove = ide_scsi_remove, .version = IDESCSI_VERSION, .media = ide_scsi, - .busy = 0, .supports_dsc_overlap = 0, - .attach = idescsi_attach, - .cleanup = idescsi_cleanup, + .proc = idescsi_proc, .do_request = idescsi_do_request, .end_request = idescsi_end_request, .error = idescsi_atapi_error, .abort = idescsi_atapi_abort, - .drives = LIST_HEAD_INIT(idescsi_driver.drives), }; static int idescsi_ide_open(struct inode *inode, struct file *filp) { - ide_drive_t *drive = inode->i_bdev->bd_disk->private_data; + struct gendisk *disk = inode->i_bdev->bd_disk; + struct ide_scsi_obj *scsi; + ide_drive_t *drive; + + if (!(scsi = ide_scsi_get(disk))) + return -ENXIO; + + drive = scsi->drive; + drive->usage++; + return 0; } static int idescsi_ide_release(struct inode *inode, struct file *filp) { - ide_drive_t *drive = inode->i_bdev->bd_disk->private_data; + struct gendisk *disk = inode->i_bdev->bd_disk; + struct ide_scsi_obj *scsi = ide_scsi_g(disk); + ide_drive_t *drive = scsi->drive; + drive->usage--; + + ide_scsi_put(scsi); + return 0; } @@ -746,7 +829,8 @@ static int idescsi_ide_ioctl(struct inode *inode, struct file *file, unsigned int cmd, unsigned long arg) { struct block_device *bdev = inode->i_bdev; - return generic_ide_ioctl(file, bdev, cmd, arg); + struct ide_scsi_obj *scsi = ide_scsi_g(bdev->bd_disk); + return generic_ide_ioctl(scsi->drive, file, bdev, cmd, arg); } static struct block_device_operations idescsi_ops = { @@ -756,8 +840,6 @@ static struct block_device_operations idescsi_ops = { .ioctl = idescsi_ide_ioctl, }; -static int idescsi_attach(ide_drive_t *drive); - static int idescsi_slave_configure(struct scsi_device * sdp) { /* Configure detected device */ @@ -795,7 +877,7 @@ static inline int should_transform(ide_drive_t *drive, struct scsi_cmnd *cmd) struct gendisk *disk = cmd->request->rq_disk; if (disk) { - struct Scsi_Device_Template **p = disk->private_data; + struct struct scsi_device_Template **p = disk->private_data; if (strcmp((*p)->scsi_driverfs_driver.name, "sg") == 0) return test_bit(IDESCSI_SG_TRANSFORM, &scsi->transform); } @@ -813,7 +895,7 @@ static int idescsi_queue (struct scsi_cmnd *cmd, idescsi_pc_t *pc = NULL; if (!drive) { - printk (KERN_ERR "ide-scsi: drive id %d not present\n", cmd->device->id); + scmd_printk (KERN_ERR, cmd, "drive not present\n"); goto abort; } scsi = drive_to_idescsi(drive); @@ -858,12 +940,13 @@ static int idescsi_queue (struct scsi_cmnd *cmd, rq->special = (char *) pc; rq->flags = REQ_SPECIAL; spin_unlock_irq(host->host_lock); + rq->rq_disk = scsi->disk; (void) ide_do_drive_cmd (drive, rq, ide_end); spin_lock_irq(host->host_lock); return 0; abort: - if (pc) kfree (pc); - if (rq) kfree (rq); + kfree (pc); + kfree (rq); cmd->result = DID_ERROR << 16; done(cmd); return 0; @@ -946,17 +1029,19 @@ static int idescsi_eh_reset (struct scsi_cmnd *cmd) return FAILED; } - spin_lock_irq(&ide_lock); + spin_lock_irq(cmd->device->host->host_lock); + spin_lock(&ide_lock); if (!scsi->pc || (req = scsi->pc->rq) != HWGROUP(drive)->rq || !HWGROUP(drive)->handler) { printk (KERN_WARNING "ide-scsi: No active request in idescsi_eh_reset\n"); spin_unlock(&ide_lock); + spin_unlock_irq(cmd->device->host->host_lock); return FAILED; } /* kill current request */ blkdev_dequeue_request(req); - end_that_request_last(req); + end_that_request_last(req, 0); if (req->flags & REQ_SENSE) kfree(scsi->pc->buffer); kfree(scsi->pc); @@ -966,22 +1051,21 @@ static int idescsi_eh_reset (struct scsi_cmnd *cmd) /* now nuke the drive queue */ while ((req = elv_next_request(drive->queue))) { blkdev_dequeue_request(req); - end_that_request_last(req); + end_that_request_last(req, 0); } HWGROUP(drive)->rq = NULL; HWGROUP(drive)->handler = NULL; HWGROUP(drive)->busy = 1; /* will set this to zero when ide reset finished */ - spin_unlock_irq(&ide_lock); + spin_unlock(&ide_lock); ide_do_reset(drive); /* ide_do_reset starts a polling handler which restarts itself every 50ms until the reset finishes */ do { - set_current_state(TASK_UNINTERRUPTIBLE); spin_unlock_irq(cmd->device->host->host_lock); - schedule_timeout(HZ/20); + msleep(50); spin_lock_irq(cmd->device->host->host_lock); } while ( HWGROUP(drive)->handler ); @@ -992,6 +1076,7 @@ static int idescsi_eh_reset (struct scsi_cmnd *cmd) ret = FAILED; } + spin_unlock_irq(cmd->device->host->host_lock); return ret; } @@ -1029,12 +1114,13 @@ static struct scsi_host_template idescsi_template = { .proc_name = "ide-scsi", }; -static int idescsi_attach(ide_drive_t *drive) +static int ide_scsi_probe(ide_drive_t *drive) { idescsi_scsi_t *idescsi; struct Scsi_Host *host; + struct gendisk *g; static int warned; - int err; + int err = -ENOMEM; if (!warned && drive->media == ide_cdrom) { printk(KERN_WARNING "ide-scsi is deprecated for cd burning! Use ide-cd and give dev=/dev/hdX as device\n"); @@ -1045,7 +1131,13 @@ static int idescsi_attach(ide_drive_t *drive) !drive->present || drive->media == ide_disk || !(host = scsi_host_alloc(&idescsi_template,sizeof(idescsi_scsi_t)))) - return 1; + return -ENODEV; + + g = alloc_disk(1 << PARTN_BITS); + if (!g) + goto out_host_put; + + ide_init_disk(g, drive); host->max_id = 1; @@ -1061,31 +1153,38 @@ static int idescsi_attach(ide_drive_t *drive) drive->driver_data = host; idescsi = scsihost_to_idescsi(host); idescsi->drive = drive; - err = ide_register_subdriver(drive, &idescsi_driver); + idescsi->driver = &idescsi_driver; + idescsi->host = host; + idescsi->disk = g; + g->private_data = &idescsi->driver; + ide_register_subdriver(drive, &idescsi_driver); + err = 0; + idescsi_setup(drive, idescsi); + g->fops = &idescsi_ops; + ide_register_region(g); + err = scsi_add_host(host, &drive->gendev); if (!err) { - idescsi_setup (drive, idescsi); - drive->disk->fops = &idescsi_ops; - err = scsi_add_host(host, &drive->gendev); - if (!err) { - scsi_scan_host(host); - return 0; - } - /* fall through on error */ - ide_unregister_subdriver(drive); + scsi_scan_host(host); + return 0; } + /* fall through on error */ + ide_unregister_region(g); + ide_unregister_subdriver(drive, &idescsi_driver); + put_disk(g); +out_host_put: scsi_host_put(host); return err; } static int __init init_idescsi_module(void) { - return ide_register_driver(&idescsi_driver); + return driver_register(&idescsi_driver.gen_driver); } static void __exit exit_idescsi_module(void) { - ide_unregister_driver(&idescsi_driver); + driver_unregister(&idescsi_driver.gen_driver); } module_init(init_idescsi_module);