ide-scsi.c
来自「Linux Kernel 2.6.9 for OMAP1710」· C语言 代码 · 共 1,160 行 · 第 1/3 页
C
1,160 行
}static inline struct bio *idescsi_kmalloc_bio (int count){ struct bio *bh, *bhp, *first_bh; if ((first_bh = bhp = bh = bio_alloc(GFP_ATOMIC, 1)) == NULL) goto abort; bio_init(bh); bh->bi_vcnt = 1; while (--count) { if ((bh = bio_alloc(GFP_ATOMIC, 1)) == NULL) goto abort; bio_init(bh); bh->bi_vcnt = 1; bhp->bi_next = bh; bhp = bh; bh->bi_next = NULL; } return first_bh;abort: idescsi_free_bio (first_bh); return NULL;}static inline int idescsi_set_direction (idescsi_pc_t *pc){ switch (pc->c[0]) { case READ_6: case READ_10: case READ_12: clear_bit (PC_WRITING, &pc->flags); return 0; case WRITE_6: case WRITE_10: case WRITE_12: set_bit (PC_WRITING, &pc->flags); return 0; default: return 1; }}static inline struct bio *idescsi_dma_bio(ide_drive_t *drive, idescsi_pc_t *pc){ struct bio *bh = NULL, *first_bh = NULL; int segments = pc->scsi_cmd->use_sg; struct scatterlist *sg = pc->scsi_cmd->request_buffer; if (!drive->using_dma || !pc->request_transfer || pc->request_transfer % 1024) return NULL; if (idescsi_set_direction(pc)) return NULL; if (segments) { if ((first_bh = bh = idescsi_kmalloc_bio (segments)) == NULL) return NULL;#if IDESCSI_DEBUG_LOG printk ("ide-scsi: %s: building DMA table, %d segments, %dkB total\n", drive->name, segments, pc->request_transfer >> 10);#endif /* IDESCSI_DEBUG_LOG */ while (segments--) { bh->bi_io_vec[0].bv_page = sg->page; bh->bi_io_vec[0].bv_len = sg->length; bh->bi_io_vec[0].bv_offset = sg->offset; bh->bi_size = sg->length; bh = bh->bi_next; sg++; } } else { if ((first_bh = bh = idescsi_kmalloc_bio (1)) == NULL) return NULL;#if IDESCSI_DEBUG_LOG printk ("ide-scsi: %s: building DMA table for a single buffer (%dkB)\n", drive->name, pc->request_transfer >> 10);#endif /* IDESCSI_DEBUG_LOG */ bh->bi_io_vec[0].bv_page = virt_to_page(pc->scsi_cmd->request_buffer); bh->bi_io_vec[0].bv_offset = offset_in_page(pc->scsi_cmd->request_buffer); bh->bi_io_vec[0].bv_len = pc->request_transfer; bh->bi_size = pc->request_transfer; } return first_bh;}static inline int should_transform(ide_drive_t *drive, Scsi_Cmnd *cmd){ idescsi_scsi_t *scsi = drive_to_idescsi(drive); /* this was a layering violation and we can't support it anymore, sorry. */#if 0 struct gendisk *disk = cmd->request->rq_disk; if (disk) { 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); }#endif return test_bit(IDESCSI_TRANSFORM, &scsi->transform);}static int idescsi_queue (Scsi_Cmnd *cmd, void (*done)(Scsi_Cmnd *)){ struct Scsi_Host *host = cmd->device->host; idescsi_scsi_t *scsi = scsihost_to_idescsi(host); ide_drive_t *drive = scsi->drive; struct request *rq = NULL; idescsi_pc_t *pc = NULL; if (!drive) { printk (KERN_ERR "ide-scsi: drive id %d not present\n", cmd->device->id); goto abort; } scsi = drive_to_idescsi(drive); pc = kmalloc (sizeof (idescsi_pc_t), GFP_ATOMIC); rq = kmalloc (sizeof (struct request), GFP_ATOMIC); if (rq == NULL || pc == NULL) { printk (KERN_ERR "ide-scsi: %s: out of memory\n", drive->name); goto abort; } memset (pc->c, 0, 12); pc->flags = 0; pc->rq = rq; memcpy (pc->c, cmd->cmnd, cmd->cmd_len); if (cmd->use_sg) { pc->buffer = NULL; pc->sg = cmd->request_buffer; } else { pc->buffer = cmd->request_buffer; pc->sg = NULL; } pc->b_count = 0; pc->request_transfer = pc->buffer_size = cmd->request_bufflen; pc->scsi_cmd = cmd; pc->done = done; pc->timeout = jiffies + cmd->timeout_per_command; if (should_transform(drive, cmd)) set_bit(PC_TRANSFORM, &pc->flags); idescsi_transform_pc1 (drive, pc); if (test_bit(IDESCSI_LOG_CMD, &scsi->log)) { printk ("ide-scsi: %s: que %lu, cmd = ", drive->name, cmd->serial_number); hexdump(cmd->cmnd, cmd->cmd_len); if (memcmp(pc->c, cmd->cmnd, cmd->cmd_len)) { printk ("ide-scsi: %s: que %lu, tsl = ", drive->name, cmd->serial_number); hexdump(pc->c, 12); } } ide_init_drive_cmd (rq); rq->special = (char *) pc; rq->bio = idescsi_dma_bio (drive, pc); rq->flags = REQ_SPECIAL; spin_unlock_irq(host->host_lock); (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); cmd->result = DID_ERROR << 16; done(cmd); return 1;}static int idescsi_eh_abort (Scsi_Cmnd *cmd){ idescsi_scsi_t *scsi = scsihost_to_idescsi(cmd->device->host); ide_drive_t *drive = scsi->drive; int busy; int ret = FAILED; /* In idescsi_eh_abort we try to gently pry our command from the ide subsystem */ if (test_bit(IDESCSI_LOG_CMD, &scsi->log)) printk (KERN_WARNING "ide-scsi: abort called for %lu\n", cmd->serial_number); if (!drive) { printk (KERN_WARNING "ide-scsi: Drive not set in idescsi_eh_abort\n"); WARN_ON(1); goto no_drive; } /* First give it some more time, how much is "right" is hard to say :-( */ busy = ide_wait_not_busy(HWIF(drive), 100); /* FIXME - uses mdelay which causes latency? */ if (test_bit(IDESCSI_LOG_CMD, &scsi->log)) printk (KERN_WARNING "ide-scsi: drive did%s become ready\n", busy?" not":""); spin_lock_irq(&ide_lock); /* If there is no pc running we're done (our interrupt took care of it) */ if (!scsi->pc) { ret = SUCCESS; goto ide_unlock; } /* It's somewhere in flight. Does ide subsystem agree? */ if (scsi->pc->scsi_cmd->serial_number == cmd->serial_number && !busy && elv_queue_empty(drive->queue) && HWGROUP(drive)->rq != scsi->pc->rq) { /* * FIXME - not sure this condition can ever occur */ printk (KERN_ERR "ide-scsi: cmd aborted!\n"); idescsi_free_bio(scsi->pc->rq->bio); if (scsi->pc->rq->flags & REQ_SENSE) kfree(scsi->pc->buffer); kfree(scsi->pc->rq); kfree(scsi->pc); scsi->pc = NULL; ret = SUCCESS; }ide_unlock: spin_unlock_irq(&ide_lock);no_drive: if (test_bit(IDESCSI_LOG_CMD, &scsi->log)) printk (KERN_WARNING "ide-scsi: abort returns %s\n", ret == SUCCESS?"success":"failed"); return ret;}static int idescsi_eh_reset (Scsi_Cmnd *cmd){ struct request *req; idescsi_scsi_t *scsi = scsihost_to_idescsi(cmd->device->host); ide_drive_t *drive = scsi->drive; int ready = 0; int ret = SUCCESS; /* In idescsi_eh_reset we forcefully remove the command from the ide subsystem and reset the device. */ if (test_bit(IDESCSI_LOG_CMD, &scsi->log)) printk (KERN_WARNING "ide-scsi: reset called for %lu\n", cmd->serial_number); if (!drive) { printk (KERN_WARNING "ide-scsi: Drive not set in idescsi_eh_reset\n"); WARN_ON(1); return FAILED; } spin_lock_irq(&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); return FAILED; } /* kill current request */ blkdev_dequeue_request(req); end_that_request_last(req); idescsi_free_bio(req->bio); if (req->flags & REQ_SENSE) kfree(scsi->pc->buffer); kfree(scsi->pc); scsi->pc = NULL; kfree(req); /* now nuke the drive queue */ while ((req = elv_next_request(drive->queue))) { blkdev_dequeue_request(req); end_that_request_last(req); } 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); 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); spin_lock_irq(cmd->device->host->host_lock); } while ( HWGROUP(drive)->handler ); ready = drive_is_ready(drive); HWGROUP(drive)->busy--; if (!ready) { printk (KERN_ERR "ide-scsi: reset failed!\n"); ret = FAILED; } return ret;}static int idescsi_bios(struct scsi_device *sdev, struct block_device *bdev, sector_t capacity, int *parm){ idescsi_scsi_t *idescsi = scsihost_to_idescsi(sdev->host); ide_drive_t *drive = idescsi->drive; if (drive->bios_cyl && drive->bios_head && drive->bios_sect) { parm[0] = drive->bios_head; parm[1] = drive->bios_sect; parm[2] = drive->bios_cyl; } return 0;}static Scsi_Host_Template idescsi_template = { .module = THIS_MODULE, .name = "idescsi", .info = idescsi_info, .slave_configure = idescsi_slave_configure, .ioctl = idescsi_ioctl, .queuecommand = idescsi_queue, .eh_abort_handler = idescsi_eh_abort, .eh_host_reset_handler = idescsi_eh_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, .proc_name = "ide-scsi",};static int idescsi_attach(ide_drive_t *drive){ idescsi_scsi_t *idescsi; struct Scsi_Host *host; static int warned; int err; 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"); warned = 1; } if (!strstr("ide-scsi", drive->driver_req) || !drive->present || drive->media == ide_disk || !(host = scsi_host_alloc(&idescsi_template,sizeof(idescsi_scsi_t)))) return 1; host->max_id = 1;#if IDESCSI_DEBUG_LOG if (drive->id->last_lun) printk(KERN_NOTICE "%s: id->last_lun=%u\n", drive->name, drive->id->last_lun);#endif if ((drive->id->last_lun & 0x7) != 7) host->max_lun = (drive->id->last_lun & 0x7) + 1; else host->max_lun = 1; drive->driver_data = host; idescsi = scsihost_to_idescsi(host); idescsi->drive = drive; err = ide_register_subdriver(drive, &idescsi_driver); 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_host_put(host); return err;}static int __init init_idescsi_module(void){ return ide_register_driver(&idescsi_driver);}static void __exit exit_idescsi_module(void){ ide_unregister_driver(&idescsi_driver);}module_init(init_idescsi_module);module_exit(exit_idescsi_module);MODULE_LICENSE("GPL");
⌨️ 快捷键说明
复制代码Ctrl + C
搜索代码Ctrl + F
全屏模式F11
增大字号Ctrl + =
减小字号Ctrl + -
显示快捷键?