scsi_scan.c

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

C
1,352
字号
static int scsilun_to_int(struct scsi_lun *scsilun){	int i;	unsigned int lun;	lun = 0;	for (i = 0; i < sizeof(lun); i += 2)		lun = lun | (((scsilun->scsi_lun[i] << 8) |			      scsilun->scsi_lun[i + 1]) << (i * 8));	return lun;}/** * scsi_report_lun_scan - Scan using SCSI REPORT LUN results * @sdevscan:	scan the host, channel, and id of this Scsi_Device * * Description: *     If @sdevscan is for a SCSI-3 or up device, send a REPORT LUN *     command, and scan the resulting list of LUNs by calling *     scsi_probe_and_add_lun. * *     Modifies sdevscan->lun. * * Return: *     0: scan completed (or no memory, so further scanning is futile) *     1: no report lun scan, or not configured **/static int scsi_report_lun_scan(struct scsi_device *sdev, int bflags,				int rescan){	char devname[64];	unsigned char scsi_cmd[MAX_COMMAND_SIZE];	unsigned int length;	unsigned int lun;	unsigned int num_luns;	unsigned int retries;	struct scsi_lun *lunp, *lun_data;	struct scsi_request *sreq;	u8 *data;	/*	 * Only support SCSI-3 and up devices if BLIST_NOREPORTLUN is not set.	 * Also allow SCSI-2 if BLIST_REPORTLUN2 is set and host adapter does	 * support more than 8 LUNs.	 */	if ((bflags & BLIST_NOREPORTLUN) || 	     sdev->scsi_level < SCSI_2 ||	    (sdev->scsi_level < SCSI_3 && 	     (!(bflags & BLIST_REPORTLUN2) || sdev->host->max_lun <= 8)) )		return 1;	if (bflags & BLIST_NOLUN)		return 0;	sreq = scsi_allocate_request(sdev, GFP_ATOMIC);	if (!sreq)		goto out;	sprintf(devname, "host %d channel %d id %d",		sdev->host->host_no, sdev->channel, sdev->id);	/*	 * Allocate enough to hold the header (the same size as one scsi_lun)	 * plus the max number of luns we are requesting.	 *	 * Reallocating and trying again (with the exact amount we need)	 * would be nice, but then we need to somehow limit the size	 * allocated based on the available memory and the limits of	 * kmalloc - we don't want a kmalloc() failure of a huge value to	 * prevent us from finding any LUNs on this target.	 */	length = (max_scsi_report_luns + 1) * sizeof(struct scsi_lun);	lun_data = kmalloc(length, GFP_ATOMIC |			   (sdev->host->unchecked_isa_dma ? __GFP_DMA : 0));	if (!lun_data)		goto out_release_request;	scsi_cmd[0] = REPORT_LUNS;	/*	 * bytes 1 - 5: reserved, set to zero.	 */	memset(&scsi_cmd[1], 0, 5);	/*	 * bytes 6 - 9: length of the command.	 */	scsi_cmd[6] = (unsigned char) (length >> 24) & 0xff;	scsi_cmd[7] = (unsigned char) (length >> 16) & 0xff;	scsi_cmd[8] = (unsigned char) (length >> 8) & 0xff;	scsi_cmd[9] = (unsigned char) length & 0xff;	scsi_cmd[10] = 0;	/* reserved */	scsi_cmd[11] = 0;	/* control */	sreq->sr_cmd_len = 0;	sreq->sr_data_direction = DMA_FROM_DEVICE;	/*	 * We can get a UNIT ATTENTION, for example a power on/reset, so	 * retry a few times (like sd.c does for TEST UNIT READY).	 * Experience shows some combinations of adapter/devices get at	 * least two power on/resets.	 *	 * Illegal requests (for devices that do not support REPORT LUNS)	 * should come through as a check condition, and will not generate	 * a retry.	 */	for (retries = 0; retries < 3; retries++) {		SCSI_LOG_SCAN_BUS(3, printk (KERN_INFO "scsi scan: Sending"				" REPORT LUNS to %s (try %d)\n", devname,				retries));		scsi_wait_req(sreq, scsi_cmd, lun_data, length,				SCSI_TIMEOUT + 4*HZ, 3);		SCSI_LOG_SCAN_BUS(3, printk (KERN_INFO "scsi scan: REPORT LUNS"				" %s (try %d) result 0x%x\n", sreq->sr_result				?  "failed" : "successful", retries,				sreq->sr_result));		if (sreq->sr_result == 0 ||		    sreq->sr_sense_buffer[2] != UNIT_ATTENTION)			break;	}	if (sreq->sr_result) {		/*		 * The device probably does not support a REPORT LUN command		 */		kfree(lun_data);		scsi_release_request(sreq);		return 1;	}	scsi_release_request(sreq);	/*	 * Get the length from the first four bytes of lun_data.	 */	data = (u8 *) lun_data->scsi_lun;	length = ((data[0] << 24) | (data[1] << 16) |		  (data[2] << 8) | (data[3] << 0));	num_luns = (length / sizeof(struct scsi_lun));	if (num_luns > max_scsi_report_luns) {		printk(KERN_WARNING "scsi: On %s only %d (max_scsi_report_luns)"		       " of %d luns reported, try increasing"		       " max_scsi_report_luns.\n", devname,		       max_scsi_report_luns, num_luns);		num_luns = max_scsi_report_luns;	}	SCSI_LOG_SCAN_BUS(3, printk (KERN_INFO "scsi scan: REPORT LUN scan of"			" host %d channel %d id %d\n", sdev->host->host_no,			sdev->channel, sdev->id));	/*	 * Scan the luns in lun_data. The entry at offset 0 is really	 * the header, so start at 1 and go up to and including num_luns.	 */	for (lunp = &lun_data[1]; lunp <= &lun_data[num_luns]; lunp++) {		lun = scsilun_to_int(lunp);		/*		 * Check if the unused part of lunp is non-zero, and so		 * does not fit in lun.		 */		if (memcmp(&lunp->scsi_lun[sizeof(lun)], "\0\0\0\0", 4)) {			int i;			/*			 * Output an error displaying the LUN in byte order,			 * this differs from what linux would print for the			 * integer LUN value.			 */			printk(KERN_WARNING "scsi: %s lun 0x", devname);			data = (char *)lunp->scsi_lun;			for (i = 0; i < sizeof(struct scsi_lun); i++)				printk("%02x", data[i]);			printk(" has a LUN larger than currently supported.\n");		} else if (lun == 0) {			/*			 * LUN 0 has already been scanned.			 */		} else if (lun > sdev->host->max_lun) {			printk(KERN_WARNING "scsi: %s lun%d has a LUN larger"			       " than allowed by the host adapter\n",			       devname, lun);		} else {			int res;			res = scsi_probe_and_add_lun(sdev->host, sdev->channel,				sdev->id, lun, NULL, NULL, rescan, NULL);			if (res == SCSI_SCAN_NO_RESPONSE) {				/*				 * Got some results, but now none, abort.				 */				printk(KERN_ERR "scsi: Unexpected response"				       " from %s lun %d while scanning, scan"				       " aborted\n", devname, lun);				break;			}		}	}	kfree(lun_data);	return 0; out_release_request:	scsi_release_request(sreq); out:	/*	 * We are out of memory, don't try scanning any further.	 */	printk(ALLOC_FAILURE_MSG, __FUNCTION__);	return 0;}struct scsi_device *__scsi_add_device(struct Scsi_Host *shost, uint channel,		uint id, uint lun, void *hostdata){	struct scsi_device *sdev;	int res;	down(&shost->scan_mutex);	res = scsi_probe_and_add_lun(shost, channel, id, lun, NULL,				     &sdev, 1, hostdata);	if (res != SCSI_SCAN_LUN_PRESENT)		sdev = ERR_PTR(-ENODEV);	up(&shost->scan_mutex);	return sdev;}void scsi_rescan_device(struct device *dev){	struct scsi_driver *drv;		if (!dev->driver)		return;	drv = to_scsi_driver(dev->driver);	if (try_module_get(drv->owner)) {		if (drv->rescan)			drv->rescan(dev);		module_put(drv->owner);	}}/** * scsi_scan_target - scan a target id, possibly including all LUNs on the *     target. * @sdevsca:	Scsi_Device handle for scanning * @shost:	host to scan * @channel:	channel to scan * @id:		target id to scan * * Description: *     Scan the target id on @shost, @channel, and @id. Scan at least LUN *     0, and possibly all LUNs on the target id. * *     Use the pre-allocated @sdevscan as a handle for the scanning. This *     function sets sdevscan->host, sdevscan->id and sdevscan->lun; the *     scanning functions modify sdevscan->lun. * *     First try a REPORT LUN scan, if that does not scan the target, do a *     sequential scan of LUNs on the target id. **/static void scsi_scan_target(struct Scsi_Host *shost, unsigned int channel,			     unsigned int id, unsigned int lun, int rescan){	int bflags = 0;	int res;	struct scsi_device *sdev;	if (shost->this_id == id)		/*		 * Don't scan the host adapter		 */		return;	if (lun != SCAN_WILD_CARD) {		/*		 * Scan for a specific host/chan/id/lun.		 */		scsi_probe_and_add_lun(shost, channel, id, lun, NULL, NULL,				       rescan, NULL);		return;	}	/*	 * Scan LUN 0, if there is some response, scan further. Ideally, we	 * would not configure LUN 0 until all LUNs are scanned.	 */	res = scsi_probe_and_add_lun(shost, channel, id, 0, &bflags, &sdev,				     rescan, NULL);	if (res == SCSI_SCAN_LUN_PRESENT) {		if (scsi_report_lun_scan(sdev, bflags, rescan) != 0)			/*			 * The REPORT LUN did not scan the target,			 * do a sequential scan.			 */			scsi_sequential_lun_scan(shost, channel, id, bflags,				       	res, sdev->scsi_level, rescan);	} else if (res == SCSI_SCAN_TARGET_PRESENT) {		/*		 * There's a target here, but lun 0 is offline so we		 * can't use the report_lun scan.  Fall back to a		 * sequential lun scan with a bflags of SPARSELUN and		 * a default scsi level of SCSI_2		 */		scsi_sequential_lun_scan(shost, channel, id, BLIST_SPARSELUN,				SCSI_SCAN_TARGET_PRESENT, SCSI_2, rescan);	}}static void scsi_scan_channel(struct Scsi_Host *shost, unsigned int channel,			      unsigned int id, unsigned int lun, int rescan){	uint order_id;	if (id == SCAN_WILD_CARD)		for (id = 0; id < shost->max_id; ++id) {			/*			 * XXX adapter drivers when possible (FCP, iSCSI)			 * could modify max_id to match the current max,			 * not the absolute max.			 *			 * XXX add a shost id iterator, so for example,			 * the FC ID can be the same as a target id			 * without a huge overhead of sparse id's.			 */			if (shost->reverse_ordering)				/*				 * Scan from high to low id.				 */				order_id = shost->max_id - id - 1;			else				order_id = id;			scsi_scan_target(shost, channel, order_id, lun, rescan);		}	else		scsi_scan_target(shost, channel, id, lun, rescan);}int scsi_scan_host_selected(struct Scsi_Host *shost, unsigned int channel,			    unsigned int id, unsigned int lun, int rescan){	SCSI_LOG_SCAN_BUS(3, printk (KERN_INFO "%s: <%u:%u:%u:%u>\n",		__FUNCTION__, shost->host_no, channel, id, lun));	if (((channel != SCAN_WILD_CARD) && (channel > shost->max_channel)) ||	    ((id != SCAN_WILD_CARD) && (id > shost->max_id)) ||	    ((lun != SCAN_WILD_CARD) && (lun > shost->max_lun)))		return -EINVAL;	down(&shost->scan_mutex);	if (channel == SCAN_WILD_CARD) 		for (channel = 0; channel <= shost->max_channel; channel++)			scsi_scan_channel(shost, channel, id, lun, rescan);	else		scsi_scan_channel(shost, channel, id, lun, rescan);	up(&shost->scan_mutex);	return 0;}/** * scsi_scan_host - scan the given adapter * @shost:	adapter to scan **/void scsi_scan_host(struct Scsi_Host *shost){	scsi_scan_host_selected(shost, SCAN_WILD_CARD, SCAN_WILD_CARD,				SCAN_WILD_CARD, 0);}void scsi_forget_host(struct Scsi_Host *shost){	struct scsi_device *sdev, *tmp;	unsigned long flags;	/*	 * Ok, this look a bit strange.  We always look for the first device	 * on the list as scsi_remove_device removes them from it - thus we	 * also have to release the lock.	 * We don't need to get another reference to the device before	 * releasing the lock as we already own the reference from	 * scsi_register_device that's release in scsi_remove_device.  And	 * after that we don't look at sdev anymore.	 */	spin_lock_irqsave(shost->host_lock, flags);	list_for_each_entry_safe(sdev, tmp, &shost->__devices, siblings) {		spin_unlock_irqrestore(shost->host_lock, flags);		scsi_remove_device(sdev);		spin_lock_irqsave(shost->host_lock, flags);	}	spin_unlock_irqrestore(shost->host_lock, flags);}/* * Function:    scsi_get_host_dev() * * Purpose:     Create a Scsi_Device that points to the host adapter itself. * * Arguments:   SHpnt   - Host that needs a Scsi_Device * * Lock status: None assumed. * * Returns:     The Scsi_Device or NULL * * Notes: *	Attach a single Scsi_Device to the Scsi_Host - this should *	be made to look like a "pseudo-device" that points to the *	HA itself. * *	Note - this device is not accessible from any high-level *	drivers (including generics), which is probably not *	optimal.  We can add hooks later to attach  */struct scsi_device *scsi_get_host_dev(struct Scsi_Host *shost){	struct scsi_device *sdev;	sdev = scsi_alloc_sdev(shost, 0, shost->this_id, 0, NULL);	if (sdev) {		sdev->borken = 0;	}	return sdev;}/* * Function:    scsi_free_host_dev() * * Purpose:     Free a scsi_device that points to the host adapter itself. * * Arguments:   SHpnt   - Host that needs a Scsi_Device * * Lock status: None assumed. * * Returns:     Nothing * * Notes: */void scsi_free_host_dev(struct scsi_device *sdev){	BUG_ON(sdev->id != sdev->host->this_id);	if (sdev->host->hostt->slave_destroy)		sdev->host->hostt->slave_destroy(sdev);	if (sdev->host->transportt->cleanup)		sdev->host->transportt->cleanup(sdev);	put_device(&sdev->sdev_gendev);}

⌨️ 快捷键说明

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