scsi_scan.c

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

C
1,352
字号
/* * 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/config.h>#include <linux/module.h>#include <linux/moduleparam.h>#include <linux/init.h>#include <linux/blkdev.h>#include <asm/semaphore.h>#include <scsi/scsi.h>#include <scsi/scsi_device.h>#include <scsi/scsi_driver.h>#include <scsi/scsi_devinfo.h>#include <scsi/scsi_host.h>#include <scsi/scsi_request.h>#include <scsi/scsi_transport.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 driverfs 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 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, int, S_IRUGO|S_IWUSR);MODULE_PARM_DESC(max_luns,		 "last scsi LUN (should be between 1 and 2^32-1)");/* * 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, int, 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, int, 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.");/** * scsi_unlock_floptical - unlock device via a special MODE SENSE command * @sreq:	used to send the command * @result:	area to store the result of the MODE SENSE * * Description: *     Send a vendor specific MODE SENSE (not a MODE SELECT) command using *     @sreq to unlock a device, storing the (unused) results into result. *     Called for BLIST_KEY devices. **/static void scsi_unlock_floptical(struct scsi_request *sreq,				  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;	sreq->sr_cmd_len = 0;	sreq->sr_data_direction = DMA_FROM_DEVICE;	scsi_wait_req(sreq, scsi_cmd, result, 0x2a /* size */, SCSI_TIMEOUT, 3);}/** * print_inquiry - printk the inquiry information * @inq_result:	printk this SCSI INQUIRY * * Description: *     printk the vendor, model, and other information found in the *     INQUIRY data in @inq_result. * * Notes: *     Remove this, and replace with a hotplug event that logs any *     relevant information. **/static void print_inquiry(unsigned char *inq_result){	int i;	printk(KERN_NOTICE "  Vendor: ");	for (i = 8; i < 16; i++)		if (inq_result[i] >= 0x20 && i < inq_result[4] + 5)			printk("%c", inq_result[i]);		else			printk(" ");	printk("  Model: ");	for (i = 16; i < 32; i++)		if (inq_result[i] >= 0x20 && i < inq_result[4] + 5)			printk("%c", inq_result[i]);		else			printk(" ");	printk("  Rev: ");	for (i = 32; i < 36; i++)		if (inq_result[i] >= 0x20 && i < inq_result[4] + 5)			printk("%c", inq_result[i]);		else			printk(" ");	printk("\n");	i = inq_result[0] & 0x1f;	printk(KERN_NOTICE "  Type:   %s ",	       i <	       MAX_SCSI_DEVICE_CODE ? scsi_device_types[i] :	       "Unknown          ");	printk("                 ANSI SCSI revision: %02x",	       inq_result[2] & 0x07);	if ((inq_result[2] & 0x07) == 1 && (inq_result[3] & 0x0f) == 1)		printk(" CCS\n");	else		printk("\n");}/** * 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_Host *shost,	       	uint channel, uint id, uint lun, void *hostdata){	struct scsi_device *sdev, *device;	unsigned long flags;	sdev = kmalloc(sizeof(*sdev) + shost->transportt->size, GFP_ATOMIC);	if (!sdev)		goto out;	memset(sdev, 0, sizeof(*sdev));	sdev->vendor = scsi_null_device_strs;	sdev->model = scsi_null_device_strs;	sdev->rev = scsi_null_device_strs;	sdev->host = shost;	sdev->id = id;	sdev->lun = lun;	sdev->channel = 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);	spin_lock_init(&sdev->list_lock);	/* 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;	spin_lock_init(&sdev->sdev_lock);	sdev->request_queue = scsi_alloc_queue(sdev);	if (!sdev->request_queue)		goto out_free_dev;	sdev->request_queue->queuedata = sdev;	scsi_adjust_queue_depth(sdev, 0, sdev->host->cmd_per_lun);	if (shost->hostt->slave_alloc) {		if (shost->hostt->slave_alloc(sdev))			goto out_free_queue;	}	if (shost->transportt->setup) {		if (shost->transportt->setup(sdev))			goto out_cleanup_slave;	}	if (get_device(&sdev->host->shost_gendev)) {		device_initialize(&sdev->sdev_gendev);		sdev->sdev_gendev.parent = &sdev->host->shost_gendev;		sdev->sdev_gendev.bus = &scsi_bus_type;		sdev->sdev_gendev.release = scsi_device_dev_release;		sprintf(sdev->sdev_gendev.bus_id,"%d:%d:%d:%d",			sdev->host->host_no, sdev->channel, sdev->id,			sdev->lun);		class_device_initialize(&sdev->sdev_classdev);		sdev->sdev_classdev.dev = &sdev->sdev_gendev;		sdev->sdev_classdev.class = &sdev_class;		snprintf(sdev->sdev_classdev.class_id, BUS_ID_SIZE,			 "%d:%d:%d:%d", sdev->host->host_no,			 sdev->channel, sdev->id, sdev->lun);		class_device_initialize(&sdev->transport_classdev);		sdev->transport_classdev.dev = &sdev->sdev_gendev;		sdev->transport_classdev.class = sdev->host->transportt->class;		snprintf(sdev->transport_classdev.class_id, BUS_ID_SIZE,			 "%d:%d:%d:%d", sdev->host->host_no,			 sdev->channel, sdev->id, sdev->lun);	} else		goto out_cleanup_transport;	/*	 * If there are any same target siblings, add this to the	 * sibling list	 */	spin_lock_irqsave(shost->host_lock, flags);	list_for_each_entry(device, &shost->__devices, siblings) {		if (device->id == sdev->id &&		    device->channel == sdev->channel) {			list_add_tail(&sdev->same_target_siblings,				      &device->same_target_siblings);			sdev->scsi_level = device->scsi_level;			break;		}	}	/*	 * If there wasn't another lun already configured at this	 * target, then default this device to SCSI_2 until we	 * know better	 */	if (!sdev->scsi_level)		sdev->scsi_level = SCSI_2;	list_add_tail(&sdev->siblings, &shost->__devices);	spin_unlock_irqrestore(shost->host_lock, flags);	return sdev;out_cleanup_transport:	if (shost->transportt->cleanup)		shost->transportt->cleanup(sdev);out_cleanup_slave:	if (shost->hostt->slave_destroy)		shost->hostt->slave_destroy(sdev);out_free_queue:	scsi_free_queue(sdev->request_queue);out_free_dev:	kfree(sdev);out:	printk(ALLOC_FAILURE_MSG, __FUNCTION__);	return NULL;}/** * scsi_probe_lun - probe a single LUN using a SCSI INQUIRY * @sreq:	used to send the INQUIRY * @inq_result:	area to store the INQUIRY result * @bflags:	store any bflags found here * * Description: *     Probe the lun associated with @sreq using a standard SCSI INQUIRY; * *     If the INQUIRY is successful, sreq->sr_result is zero and: the *     INQUIRY data is in @inq_result; the scsi_level and INQUIRY length *     are copied to the Scsi_Device at @sreq->sr_device (sdev); *     any flags value is stored in *@bflags. **/static void scsi_probe_lun(struct scsi_request *sreq, char *inq_result,			   int *bflags){	struct scsi_device *sdev = sreq->sr_device;	/* a bit ugly */	unsigned char scsi_cmd[MAX_COMMAND_SIZE];	int possible_inq_resp_len;	int count = 0;	*bflags = 0; repeat_inquiry:	SCSI_LOG_SCAN_BUS(3, printk(KERN_INFO "scsi scan: INQUIRY to host %d"			" channel %d id %d lun %d\n", sdev->host->host_no,			sdev->channel, sdev->id, sdev->lun));	memset(scsi_cmd, 0, 6);	scsi_cmd[0] = INQUIRY;	scsi_cmd[4] = 36;	/* issue conservative alloc_length */	sreq->sr_cmd_len = 0;	sreq->sr_data_direction = DMA_FROM_DEVICE;	memset(inq_result, 0, 36);	scsi_wait_req(sreq, (void *) scsi_cmd, (void *) inq_result, 36,		      HZ/2 + HZ*scsi_inq_timeout, 3);	SCSI_LOG_SCAN_BUS(3, printk(KERN_INFO "scsi scan: 1st INQUIRY %s with"			" code 0x%x\n", sreq->sr_result ?			"failed" : "successful", sreq->sr_result));	++count;	if (sreq->sr_result) {		if ((driver_byte(sreq->sr_result) & DRIVER_SENSE) != 0 &&		    (sreq->sr_sense_buffer[2] & 0xf) == UNIT_ATTENTION &&		    (sreq->sr_sense_buffer[12] == 0x28 ||		     sreq->sr_sense_buffer[12] == 0x29) &&		    sreq->sr_sense_buffer[13] == 0) {			/* not-ready to ready transition or power-on - good */			/* dpg: bogus? INQUIRY never returns UNIT_ATTENTION */			/* Supposedly, but many buggy devices do so anyway */			if (count < 3)				goto repeat_inquiry;		}		/*		 * assume no peripheral if any other sort of error		 */		return;	}	/*	 * Get any flags for this device.	 *	 * XXX add a bflags to Scsi_Device, and replace the corresponding	 * bit fields in Scsi_Device, so bflags need not be passed as an	 * argument.	 */	*bflags |= scsi_get_device_flags(sdev, &inq_result[8], &inq_result[16]);	possible_inq_resp_len = (unsigned char) inq_result[4] + 5;	if (BLIST_INQUIRY_36 & *bflags)		possible_inq_resp_len = 36;	else if (BLIST_INQUIRY_58 & *bflags)		possible_inq_resp_len = 58;	else if (possible_inq_resp_len > 255)		possible_inq_resp_len = 36;	/* sanity */	if (possible_inq_resp_len > 36) {	/* do additional INQUIRY */		memset(scsi_cmd, 0, 6);		scsi_cmd[0] = INQUIRY;		scsi_cmd[4] = (unsigned char) possible_inq_resp_len;		sreq->sr_cmd_len = 0;		sreq->sr_data_direction = DMA_FROM_DEVICE;		/*		 * re-zero inq_result just to be safe.		 */		memset(inq_result, 0, possible_inq_resp_len);		scsi_wait_req(sreq, (void *) scsi_cmd,			      (void *) inq_result,			      possible_inq_resp_len, (1+scsi_inq_timeout)*(HZ/2), 3);		SCSI_LOG_SCAN_BUS(3, printk(KERN_INFO "scsi scan: 2nd INQUIRY"				" %s with code 0x%x\n", sreq->sr_result ?				"failed" : "successful", sreq->sr_result));		if (sreq->sr_result) {			/* if the longer inquiry has failed, flag the device			 * as only accepting 36 byte inquiries and retry the			 * 36 byte inquiry */			printk(KERN_INFO "scsi scan: %d byte inquiry failed"			       " with code %d.  Consider BLIST_INQUIRY_36 for"			       " this device\n", possible_inq_resp_len,			       sreq->sr_result);			*bflags = BLIST_INQUIRY_36;			goto repeat_inquiry;		}		/*		 * The INQUIRY can change, this means the length can change.		 */		possible_inq_resp_len = (unsigned char) inq_result[4] + 5;		if (BLIST_INQUIRY_58 & *bflags)			possible_inq_resp_len = 58;		else if (possible_inq_resp_len > 255)			possible_inq_resp_len = 36;	/* sanity */	}	sdev->inquiry_len = possible_inq_resp_len;	/*	 * XXX Abort if the response length is less than 36? If less than

⌨️ 快捷键说明

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