scsi.c

来自「Linux Kernel 2.6.9 for OMAP1710」· C语言 代码 · 共 1,249 行 · 第 1/3 页

C
1,249
字号
		cmd->result |= (DRIVER_SENSE << 24);	SCSI_LOG_MLCOMPLETE(4, printk("Notifying upper driver of completion "				"for device %d %x\n", sdev->id, cmd->result));	cmd->owner = SCSI_OWNER_HIGHLEVEL;	cmd->state = SCSI_STATE_FINISHED;	/*	 * We can get here with use_sg=0, causing a panic in the upper level	 */	cmd->use_sg = cmd->old_use_sg;	/*	 * If there is an associated request structure, copy the data over	 * before we call the completion function.	 */	sreq = cmd->sc_request;	if (sreq) {	       sreq->sr_result = sreq->sr_command->result;	       if (sreq->sr_result) {		       memcpy(sreq->sr_sense_buffer,			      sreq->sr_command->sense_buffer,			      sizeof(sreq->sr_sense_buffer));	       }	}	cmd->done(cmd);}EXPORT_SYMBOL(scsi_finish_command);/* * Function:	scsi_adjust_queue_depth() * * Purpose:	Allow low level drivers to tell us to change the queue depth * 		on a specific SCSI device * * Arguments:	sdev	- SCSI Device in question * 		tagged	- Do we use tagged queueing (non-0) or do we treat * 			  this device as an untagged device (0) * 		tags	- Number of tags allowed if tagged queueing enabled, * 			  or number of commands the low level driver can * 			  queue up in non-tagged mode (as per cmd_per_lun). * * Returns:	Nothing * * Lock Status:	None held on entry * * Notes:	Low level drivers may call this at any time and we will do * 		the right thing depending on whether or not the device is * 		currently active and whether or not it even has the * 		command blocks built yet. * * XXX(hch):	What exactly is device_request_lock trying to protect? */void scsi_adjust_queue_depth(struct scsi_device *sdev, int tagged, int tags){	static spinlock_t device_request_lock = SPIN_LOCK_UNLOCKED;	unsigned long flags;	/*	 * refuse to set tagged depth to an unworkable size	 */	if (tags <= 0)		return;	spin_lock_irqsave(&device_request_lock, flags);	spin_lock(sdev->request_queue->queue_lock);	/* Check to see if the queue is managed by the block layer	 * if it is, and we fail to adjust the depth, exit */	if (blk_queue_tagged(sdev->request_queue) &&	    blk_queue_resize_tags(sdev->request_queue, tags) != 0)		goto out;	sdev->queue_depth = tags;	switch (tagged) {		case MSG_ORDERED_TAG:			sdev->ordered_tags = 1;			sdev->simple_tags = 1;			break;		case MSG_SIMPLE_TAG:			sdev->ordered_tags = 0;			sdev->simple_tags = 1;			break;		default:			printk(KERN_WARNING "(scsi%d:%d:%d:%d) "				"scsi_adjust_queue_depth, bad queue type, "				"disabled\n", sdev->host->host_no,				sdev->channel, sdev->id, sdev->lun); 		case 0:			sdev->ordered_tags = sdev->simple_tags = 0;			sdev->queue_depth = tags;			break;	} out:	spin_unlock(sdev->request_queue->queue_lock);	spin_unlock_irqrestore(&device_request_lock, flags);}/* * Function:	scsi_track_queue_full() * * Purpose:	This function will track successive QUEUE_FULL events on a * 		specific SCSI device to determine if and when there is a * 		need to adjust the queue depth on the device. * * Arguments:	sdev	- SCSI Device in question * 		depth	- Current number of outstanding SCSI commands on * 			  this device, not counting the one returned as * 			  QUEUE_FULL. * * Returns:	0 - No change needed * 		>0 - Adjust queue depth to this new depth * 		-1 - Drop back to untagged operation using host->cmd_per_lun * 			as the untagged command depth * * Lock Status:	None held on entry * * Notes:	Low level drivers may call this at any time and we will do * 		"The Right Thing."  We are interrupt context safe. */int scsi_track_queue_full(struct scsi_device *sdev, int depth){	if ((jiffies >> 4) == sdev->last_queue_full_time)		return 0;	sdev->last_queue_full_time = (jiffies >> 4);	if (sdev->last_queue_full_depth != depth) {		sdev->last_queue_full_count = 1;		sdev->last_queue_full_depth = depth;	} else {		sdev->last_queue_full_count++;	}	if (sdev->last_queue_full_count <= 10)		return 0;	if (sdev->last_queue_full_depth < 8) {		/* Drop back to untagged */		scsi_adjust_queue_depth(sdev, 0, sdev->host->cmd_per_lun);		return -1;	}		if (sdev->ordered_tags)		scsi_adjust_queue_depth(sdev, MSG_ORDERED_TAG, depth);	else		scsi_adjust_queue_depth(sdev, MSG_SIMPLE_TAG, depth);	return depth;}/** * scsi_device_get  -  get an addition reference to a scsi_device * @sdev:	device to get a reference to * * Gets a reference to the scsi_device and increments the use count * of the underlying LLDD module.  You must hold host_lock of the * parent Scsi_Host or already have a reference when calling this. */int scsi_device_get(struct scsi_device *sdev){	if (sdev->sdev_state == SDEV_DEL || sdev->sdev_state == SDEV_CANCEL)		return -ENXIO;	if (!get_device(&sdev->sdev_gendev))		return -ENXIO;	if (!try_module_get(sdev->host->hostt->module)) {		put_device(&sdev->sdev_gendev);		return -ENXIO;	}	return 0;}EXPORT_SYMBOL(scsi_device_get);/** * scsi_device_put  -  release a reference to a scsi_device * @sdev:	device to release a reference on. * * Release a reference to the scsi_device and decrements the use count * of the underlying LLDD module.  The device is freed once the last * user vanishes. */void scsi_device_put(struct scsi_device *sdev){	module_put(sdev->host->hostt->module);	put_device(&sdev->sdev_gendev);}EXPORT_SYMBOL(scsi_device_put);/* helper for shost_for_each_device, thus not documented */struct scsi_device *__scsi_iterate_devices(struct Scsi_Host *shost,					   struct scsi_device *prev){	struct list_head *list = (prev ? &prev->siblings : &shost->__devices);	struct scsi_device *next = NULL;	unsigned long flags;	spin_lock_irqsave(shost->host_lock, flags);	while (list->next != &shost->__devices) {		next = list_entry(list->next, struct scsi_device, siblings);		/* skip devices that we can't get a reference to */		if (!scsi_device_get(next))			break;		list = list->next;	}	spin_unlock_irqrestore(shost->host_lock, flags);	if (prev)		scsi_device_put(prev);	return next;}EXPORT_SYMBOL(__scsi_iterate_devices);/** * scsi_device_lookup - find a device given the host (UNLOCKED) * @shost:	SCSI host pointer * @channel:	SCSI channel (zero if only one channel) * @pun:	SCSI target number (physical unit number) * @lun:	SCSI Logical Unit Number * * Looks up the scsi_device with the specified @channel, @id, @lun for a * give host. The returned scsi_device does not have an additional reference. * You must hold the host's host_lock over this call and any access to the * returned scsi_device. * * Note:  The only reason why drivers would want to use this is because * they're need to access the device list in irq context.  Otherwise you * really want to use scsi_device_lookup instead. **/struct scsi_device *__scsi_device_lookup(struct Scsi_Host *shost,		uint channel, uint id, uint lun){	struct scsi_device *sdev;	list_for_each_entry(sdev, &shost->__devices, siblings) {		if (sdev->channel == channel && sdev->id == id &&				sdev->lun ==lun)			return sdev;	}	return NULL;}EXPORT_SYMBOL(__scsi_device_lookup);/** * scsi_device_lookup - find a device given the host * @shost:	SCSI host pointer * @channel:	SCSI channel (zero if only one channel) * @id:		SCSI target number (physical unit number) * @lun:	SCSI Logical Unit Number * * Looks up the scsi_device with the specified @channel, @id, @lun for a * give host.  The returned scsi_device has an additional reference that * needs to be release with scsi_host_put once you're done with it. **/struct scsi_device *scsi_device_lookup(struct Scsi_Host *shost,		uint channel, uint id, uint lun){	struct scsi_device *sdev;	unsigned long flags;	spin_lock_irqsave(shost->host_lock, flags);	sdev = __scsi_device_lookup(shost, channel, id, lun);	if (sdev && scsi_device_get(sdev))		sdev = NULL;	spin_unlock_irqrestore(shost->host_lock, flags);	return sdev;}EXPORT_SYMBOL(scsi_device_lookup);/** * scsi_device_cancel - cancel outstanding IO to this device * @sdev:	pointer to struct scsi_device * @data:	pointer to cancel value. * **/int scsi_device_cancel(struct scsi_device *sdev, int recovery){	struct scsi_cmnd *scmd;	LIST_HEAD(active_list);	struct list_head *lh, *lh_sf;	unsigned long flags;	scsi_device_set_state(sdev, SDEV_CANCEL);	spin_lock_irqsave(&sdev->list_lock, flags);	list_for_each_entry(scmd, &sdev->cmd_list, list) {		if (scmd->request && scmd->request->rq_status != RQ_INACTIVE) {			/*			 * If we are unable to remove the timer, it means			 * that the command has already timed out or			 * finished.			 */			if (!scsi_delete_timer(scmd))				continue;			list_add_tail(&scmd->eh_entry, &active_list);		}	}	spin_unlock_irqrestore(&sdev->list_lock, flags);	if (!list_empty(&active_list)) {		list_for_each_safe(lh, lh_sf, &active_list) {			scmd = list_entry(lh, struct scsi_cmnd, eh_entry);			list_del_init(lh);			if (recovery) {				scsi_eh_scmd_add(scmd, SCSI_EH_CANCEL_CMD);			} else {				scmd->result = (DID_ABORT << 16);				scsi_finish_command(scmd);			}		}	}	return 0;}#ifdef CONFIG_HOTPLUG_CPUstatic int scsi_cpu_notify(struct notifier_block *self,			   unsigned long action, void *hcpu){	int cpu = (unsigned long)hcpu;	switch(action) {	case CPU_DEAD:		/* Drain scsi_done_q. */		local_irq_disable();		list_splice_init(&per_cpu(scsi_done_q, cpu),				 &__get_cpu_var(scsi_done_q));		raise_softirq_irqoff(SCSI_SOFTIRQ);		local_irq_enable();		break;	default:		break;	}	return NOTIFY_OK;}static struct notifier_block __devinitdata scsi_cpu_nb = {	.notifier_call	= scsi_cpu_notify,};#define register_scsi_cpu() register_cpu_notifier(&scsi_cpu_nb)#define unregister_scsi_cpu() unregister_cpu_notifier(&scsi_cpu_nb)#else#define register_scsi_cpu()#define unregister_scsi_cpu()#endif /* CONFIG_HOTPLUG_CPU */MODULE_DESCRIPTION("SCSI core");MODULE_LICENSE("GPL");module_param(scsi_logging_level, int, S_IRUGO|S_IWUSR);MODULE_PARM_DESC(scsi_logging_level, "a bit mask of logging levels");static int __init init_scsi(void){	int error, i;	error = scsi_init_queue();	if (error)		return error;	error = scsi_init_procfs();	if (error)		goto cleanup_queue;	error = scsi_init_devinfo();	if (error)		goto cleanup_procfs;	error = scsi_init_hosts();	if (error)		goto cleanup_devlist;	error = scsi_init_sysctl();	if (error)		goto cleanup_hosts;	error = scsi_sysfs_register();	if (error)		goto cleanup_sysctl;	for (i = 0; i < NR_CPUS; i++)		INIT_LIST_HEAD(&per_cpu(scsi_done_q, i));	devfs_mk_dir("scsi");	open_softirq(SCSI_SOFTIRQ, scsi_softirq, NULL);	register_scsi_cpu();	printk(KERN_NOTICE "SCSI subsystem initialized\n");	return 0;cleanup_sysctl:	scsi_exit_sysctl();cleanup_hosts:	scsi_exit_hosts();cleanup_devlist:	scsi_exit_devinfo();cleanup_procfs:	scsi_exit_procfs();cleanup_queue:	scsi_exit_queue();	printk(KERN_ERR "SCSI subsystem failed to initialize, error = %d\n",	       -error);	return error;}static void __exit exit_scsi(void){	scsi_sysfs_unregister();	scsi_exit_sysctl();	scsi_exit_hosts();	scsi_exit_devinfo();	devfs_remove("scsi");	scsi_exit_procfs();	scsi_exit_queue();	unregister_scsi_cpu();}subsys_initcall(init_scsi);module_exit(exit_scsi);

⌨️ 快捷键说明

复制代码Ctrl + C
搜索代码Ctrl + F
全屏模式F11
增大字号Ctrl + =
减小字号Ctrl + -
显示快捷键?