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