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