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 + -
显示快捷键?