scsi_scan.c
来自「linux 内核源代码」· C语言 代码 · 共 1,906 行 · 第 1/4 页
C
1,906 行
/* * scsi_scan.c * * Copyright (C) 2000 Eric Youngdale, * Copyright (C) 2002 Patrick Mansfield * * The general scanning/probing algorithm is as follows, exceptions are * made to it depending on device specific flags, compilation options, and * global variable (boot or module load time) settings. * * A specific LUN is scanned via an INQUIRY command; if the LUN has a * device attached, a scsi_device is allocated and setup for it. * * For every id of every channel on the given host: * * Scan LUN 0; if the target responds to LUN 0 (even if there is no * device or storage attached to LUN 0): * * If LUN 0 has a device attached, allocate and setup a * scsi_device for it. * * If target is SCSI-3 or up, issue a REPORT LUN, and scan * all of the LUNs returned by the REPORT LUN; else, * sequentially scan LUNs up until some maximum is reached, * or a LUN is seen that cannot have a device attached to it. */#include <linux/module.h>#include <linux/moduleparam.h>#include <linux/init.h>#include <linux/blkdev.h>#include <linux/delay.h>#include <linux/kthread.h>#include <linux/spinlock.h>#include <scsi/scsi.h>#include <scsi/scsi_cmnd.h>#include <scsi/scsi_device.h>#include <scsi/scsi_driver.h>#include <scsi/scsi_devinfo.h>#include <scsi/scsi_host.h>#include <scsi/scsi_transport.h>#include <scsi/scsi_eh.h>#include "scsi_priv.h"#include "scsi_logging.h"#define ALLOC_FAILURE_MSG KERN_ERR "%s: Allocation failure during" \ " SCSI scanning, some SCSI devices might not be configured\n"/* * Default timeout */#define SCSI_TIMEOUT (2*HZ)/* * Prefix values for the SCSI id's (stored in sysfs name field) */#define SCSI_UID_SER_NUM 'S'#define SCSI_UID_UNKNOWN 'Z'/* * Return values of some of the scanning functions. * * SCSI_SCAN_NO_RESPONSE: no valid response received from the target, this * includes allocation or general failures preventing IO from being sent. * * SCSI_SCAN_TARGET_PRESENT: target responded, but no device is available * on the given LUN. * * SCSI_SCAN_LUN_PRESENT: target responded, and a device is available on a * given LUN. */#define SCSI_SCAN_NO_RESPONSE 0#define SCSI_SCAN_TARGET_PRESENT 1#define SCSI_SCAN_LUN_PRESENT 2static const char *scsi_null_device_strs = "nullnullnullnull";#define MAX_SCSI_LUNS 512#ifdef CONFIG_SCSI_MULTI_LUNstatic unsigned int max_scsi_luns = MAX_SCSI_LUNS;#elsestatic unsigned int max_scsi_luns = 1;#endifmodule_param_named(max_luns, max_scsi_luns, uint, S_IRUGO|S_IWUSR);MODULE_PARM_DESC(max_luns, "last scsi LUN (should be between 1 and 2^32-1)");#ifdef CONFIG_SCSI_SCAN_ASYNC#define SCSI_SCAN_TYPE_DEFAULT "async"#else#define SCSI_SCAN_TYPE_DEFAULT "sync"#endifstatic char scsi_scan_type[6] = SCSI_SCAN_TYPE_DEFAULT;module_param_string(scan, scsi_scan_type, sizeof(scsi_scan_type), S_IRUGO);MODULE_PARM_DESC(scan, "sync, async or none");/* * max_scsi_report_luns: the maximum number of LUNS that will be * returned from the REPORT LUNS command. 8 times this value must * be allocated. In theory this could be up to an 8 byte value, but * in practice, the maximum number of LUNs suppored by any device * is about 16k. */static unsigned int max_scsi_report_luns = 511;module_param_named(max_report_luns, max_scsi_report_luns, uint, S_IRUGO|S_IWUSR);MODULE_PARM_DESC(max_report_luns, "REPORT LUNS maximum number of LUNS received (should be" " between 1 and 16384)");static unsigned int scsi_inq_timeout = SCSI_TIMEOUT/HZ+3;module_param_named(inq_timeout, scsi_inq_timeout, uint, S_IRUGO|S_IWUSR);MODULE_PARM_DESC(inq_timeout, "Timeout (in seconds) waiting for devices to answer INQUIRY." " Default is 5. Some non-compliant devices need more.");/* This lock protects only this list */static DEFINE_SPINLOCK(async_scan_lock);static LIST_HEAD(scanning_hosts);struct async_scan_data { struct list_head list; struct Scsi_Host *shost; struct completion prev_finished;};/** * scsi_complete_async_scans - Wait for asynchronous scans to complete * * When this function returns, any host which started scanning before * this function was called will have finished its scan. Hosts which * started scanning after this function was called may or may not have * finished. */int scsi_complete_async_scans(void){ struct async_scan_data *data; do { if (list_empty(&scanning_hosts)) return 0; /* If we can't get memory immediately, that's OK. Just * sleep a little. Even if we never get memory, the async * scans will finish eventually. */ data = kmalloc(sizeof(*data), GFP_KERNEL); if (!data) msleep(1); } while (!data); data->shost = NULL; init_completion(&data->prev_finished); spin_lock(&async_scan_lock); /* Check that there's still somebody else on the list */ if (list_empty(&scanning_hosts)) goto done; list_add_tail(&data->list, &scanning_hosts); spin_unlock(&async_scan_lock); printk(KERN_INFO "scsi: waiting for bus probes to complete ...\n"); wait_for_completion(&data->prev_finished); spin_lock(&async_scan_lock); list_del(&data->list); if (!list_empty(&scanning_hosts)) { struct async_scan_data *next = list_entry(scanning_hosts.next, struct async_scan_data, list); complete(&next->prev_finished); } done: spin_unlock(&async_scan_lock); kfree(data); return 0;}/* Only exported for the benefit of scsi_wait_scan */EXPORT_SYMBOL_GPL(scsi_complete_async_scans);#ifndef MODULE/* * For async scanning we need to wait for all the scans to complete before * trying to mount the root fs. Otherwise non-modular drivers may not be ready * yet. */late_initcall(scsi_complete_async_scans);#endif/** * scsi_unlock_floptical - unlock device via a special MODE SENSE command * @sdev: scsi device to send command to * @result: area to store the result of the MODE SENSE * * Description: * Send a vendor specific MODE SENSE (not a MODE SELECT) command. * Called for BLIST_KEY devices. **/static void scsi_unlock_floptical(struct scsi_device *sdev, unsigned char *result){ unsigned char scsi_cmd[MAX_COMMAND_SIZE]; printk(KERN_NOTICE "scsi: unlocking floptical drive\n"); scsi_cmd[0] = MODE_SENSE; scsi_cmd[1] = 0; scsi_cmd[2] = 0x2e; scsi_cmd[3] = 0; scsi_cmd[4] = 0x2a; /* size */ scsi_cmd[5] = 0; scsi_execute_req(sdev, scsi_cmd, DMA_FROM_DEVICE, result, 0x2a, NULL, SCSI_TIMEOUT, 3);}/** * scsi_alloc_sdev - allocate and setup a scsi_Device * * Description: * Allocate, initialize for io, and return a pointer to a scsi_Device. * Stores the @shost, @channel, @id, and @lun in the scsi_Device, and * adds scsi_Device to the appropriate list. * * Return value: * scsi_Device pointer, or NULL on failure. **/static struct scsi_device *scsi_alloc_sdev(struct scsi_target *starget, unsigned int lun, void *hostdata){ struct scsi_device *sdev; int display_failure_msg = 1, ret; struct Scsi_Host *shost = dev_to_shost(starget->dev.parent); extern void scsi_evt_thread(struct work_struct *work); sdev = kzalloc(sizeof(*sdev) + shost->transportt->device_size, GFP_ATOMIC); if (!sdev) goto out; sdev->vendor = scsi_null_device_strs; sdev->model = scsi_null_device_strs; sdev->rev = scsi_null_device_strs; sdev->host = shost; sdev->id = starget->id; sdev->lun = lun; sdev->channel = starget->channel; sdev->sdev_state = SDEV_CREATED; INIT_LIST_HEAD(&sdev->siblings); INIT_LIST_HEAD(&sdev->same_target_siblings); INIT_LIST_HEAD(&sdev->cmd_list); INIT_LIST_HEAD(&sdev->starved_entry); INIT_LIST_HEAD(&sdev->event_list); spin_lock_init(&sdev->list_lock); INIT_WORK(&sdev->event_work, scsi_evt_thread); sdev->sdev_gendev.parent = get_device(&starget->dev); sdev->sdev_target = starget; /* usually NULL and set by ->slave_alloc instead */ sdev->hostdata = hostdata; /* if the device needs this changing, it may do so in the * slave_configure function */ sdev->max_device_blocked = SCSI_DEFAULT_DEVICE_BLOCKED; /* * Some low level driver could use device->type */ sdev->type = -1; /* * Assume that the device will have handshaking problems, * and then fix this field later if it turns out it * doesn't */ sdev->borken = 1; sdev->request_queue = scsi_alloc_queue(sdev); if (!sdev->request_queue) { /* release fn is set up in scsi_sysfs_device_initialise, so * have to free and put manually here */ put_device(&starget->dev); kfree(sdev); goto out; } sdev->request_queue->queuedata = sdev; scsi_adjust_queue_depth(sdev, 0, sdev->host->cmd_per_lun); scsi_sysfs_device_initialize(sdev); if (shost->hostt->slave_alloc) { ret = shost->hostt->slave_alloc(sdev); if (ret) { /* * if LLDD reports slave not present, don't clutter * console with alloc failure messages */ if (ret == -ENXIO) display_failure_msg = 0; goto out_device_destroy; } } return sdev;out_device_destroy: transport_destroy_device(&sdev->sdev_gendev); put_device(&sdev->sdev_gendev);out: if (display_failure_msg) printk(ALLOC_FAILURE_MSG, __FUNCTION__); return NULL;}static void scsi_target_dev_release(struct device *dev){ struct device *parent = dev->parent; struct scsi_target *starget = to_scsi_target(dev); kfree(starget); put_device(parent);}int scsi_is_target_device(const struct device *dev){ return dev->release == scsi_target_dev_release;}EXPORT_SYMBOL(scsi_is_target_device);static struct scsi_target *__scsi_find_target(struct device *parent, int channel, uint id){ struct scsi_target *starget, *found_starget = NULL; struct Scsi_Host *shost = dev_to_shost(parent); /* * Search for an existing target for this sdev. */ list_for_each_entry(starget, &shost->__targets, siblings) { if (starget->id == id && starget->channel == channel) { found_starget = starget; break; } } if (found_starget) get_device(&found_starget->dev); return found_starget;}/** * scsi_alloc_target - allocate a new or find an existing target * @parent: parent of the target (need not be a scsi host) * @channel: target channel number (zero if no channels) * @id: target id number * * Return an existing target if one exists, provided it hasn't already * gone into STARGET_DEL state, otherwise allocate a new target. * * The target is returned with an incremented reference, so the caller * is responsible for both reaping and doing a last put */static struct scsi_target *scsi_alloc_target(struct device *parent, int channel, uint id){ struct Scsi_Host *shost = dev_to_shost(parent); struct device *dev = NULL; unsigned long flags; const int size = sizeof(struct scsi_target) + shost->transportt->target_size; struct scsi_target *starget; struct scsi_target *found_target; int error; starget = kzalloc(size, GFP_KERNEL); if (!starget) { printk(KERN_ERR "%s: allocation failure\n", __FUNCTION__); return NULL; } dev = &starget->dev; device_initialize(dev); starget->reap_ref = 1; dev->parent = get_device(parent); dev->release = scsi_target_dev_release; sprintf(dev->bus_id, "target%d:%d:%d", shost->host_no, channel, id); starget->id = id; starget->channel = channel; INIT_LIST_HEAD(&starget->siblings); INIT_LIST_HEAD(&starget->devices); starget->state = STARGET_RUNNING; starget->scsi_level = SCSI_2; retry: spin_lock_irqsave(shost->host_lock, flags); found_target = __scsi_find_target(parent, channel, id); if (found_target) goto found; list_add_tail(&starget->siblings, &shost->__targets); spin_unlock_irqrestore(shost->host_lock, flags); /* allocate and add */ transport_setup_device(dev); error = device_add(dev); if (error) { dev_err(dev, "target device_add failed, error %d\n", error); spin_lock_irqsave(shost->host_lock, flags); list_del_init(&starget->siblings); spin_unlock_irqrestore(shost->host_lock, flags); transport_destroy_device(dev); put_device(parent); kfree(starget); return NULL; } transport_add_device(dev); if (shost->hostt->target_alloc) { error = shost->hostt->target_alloc(starget); if(error) { dev_printk(KERN_ERR, dev, "target allocation failed, error %d\n", error); /* don't want scsi_target_reap to do the final * put because it will be under the host lock */ get_device(dev); scsi_target_reap(starget); put_device(dev); return NULL; } } get_device(dev); return starget; found: found_target->reap_ref++; spin_unlock_irqrestore(shost->host_lock, flags); if (found_target->state != STARGET_DEL) { put_device(parent); kfree(starget); return found_target; } /* Unfortunately, we found a dying target; need to * wait until it's dead before we can get a new one */ put_device(&found_target->dev); flush_scheduled_work(); goto retry;}static void scsi_target_reap_usercontext(struct work_struct *work){ struct scsi_target *starget = container_of(work, struct scsi_target, ew.work); struct Scsi_Host *shost = dev_to_shost(starget->dev.parent); unsigned long flags; transport_remove_device(&starget->dev); device_del(&starget->dev); transport_destroy_device(&starget->dev); spin_lock_irqsave(shost->host_lock, flags); if (shost->hostt->target_destroy) shost->hostt->target_destroy(starget); list_del_init(&starget->siblings); spin_unlock_irqrestore(shost->host_lock, flags); put_device(&starget->dev);}/** * scsi_target_reap - check to see if target is in use and destroy if not * * @starget: target to be checked *
⌨️ 快捷键说明
复制代码Ctrl + C
搜索代码Ctrl + F
全屏模式F11
增大字号Ctrl + =
减小字号Ctrl + -
显示快捷键?