注册 登录  
 加关注
   显示下一条  |  关闭
温馨提示!由于新浪微博认证机制调整,您的新浪微博帐号绑定已过期,请重新绑定!立即重新绑定新浪微博》  |  关闭

gmd20的个人空间

// 编程和生活

 
 
 

日志

 
 

Linux 设备驱动模型和SCSI磁盘的设备的发现和移除(第二部分 SCSI磁盘的设备的发现和移除)  

2011-05-24 18:51:23|  分类: linux相关 |  标签: |举报 |字号 订阅

  下载LOFTER 我的照片书  |

====================
看看scsi的新设备发现
====================
   

http://lxr.linux.no/#linux+v2.6.39/drivers/scsi/scsi_scan.c




int scsi_add_device(struct Scsi_Host *host, uint channel,
                    uint target, uint lun)
{
        struct scsi_device *sdev =
                __scsi_add_device(host, channel, target, lun, NULL);  >>>>>>>>>>>>>>>>>>
        if (IS_ERR(sdev))
                return PTR_ERR(sdev);

        scsi_device_put(sdev);
        return 0;
}



struct scsi_device *__scsi_add_device(struct Scsi_Host *shost, uint channel,
                                      uint id, uint lun, void *hostdata)
{
        struct scsi_device *sdev = ERR_PTR(-ENODEV); ///////////////////////
        struct device *parent = &shost->shost_gendev;
        struct scsi_target *starget;

        if (strncmp(scsi_scan_type, "none", 4) == 0)
                return ERR_PTR(-ENODEV);

        starget = scsi_alloc_target(parent, channel, id);
        if (!starget)
                return ERR_PTR(-ENOMEM);
        scsi_autopm_get_target(starget);

        mutex_lock(&shost->scan_mutex);
        if (!shost->async_scan)
                scsi_complete_async_scans();

        if (scsi_host_scan_allowed(shost) && scsi_autopm_get_host(shost) == 0) {
                scsi_probe_and_add_lun(starget, lun, NULL, &sdev, 1, hostdata);  >>>>>>>>>>>>>>>>>>>>>>
                scsi_autopm_put_host(shost);
        }
        mutex_unlock(&shost->scan_mutex);
        scsi_autopm_put_target(starget);
        scsi_target_reap(starget);
        put_device(&starget->dev);

        return sdev;
}
EXPORT_SYMBOL(__scsi_add_device);







/**
 * scsi_probe_and_add_lun - probe a LUN, if a LUN is found add it
 * @starget:    pointer to target device structure
 * @lun:        LUN of target device
 * @bflagsp:    store bflags here if not NULL
 * @sdevp:      probe the LUN corresponding to this scsi_device
 * @rescan:     if nonzero skip some code only needed on first scan
 * @hostdata:   passed to scsi_alloc_sdev()
 *
 * Description:
 *     Call scsi_probe_lun, if a LUN with an attached device is found,
 *     allocate and set it up by calling scsi_add_lun.
 *
 * Return:
 *     SCSI_SCAN_NO_RESPONSE: could not allocate or setup a scsi_device
 *     SCSI_SCAN_TARGET_PRESENT: target responded, but no device is

 *         attached at the LUN
 *     SCSI_SCAN_LUN_PRESENT: a new scsi_device was allocated and initialized
 **/
static int scsi_probe_and_add_lun(struct scsi_target *starget,
                                  uint lun, int *bflagsp,
                                  struct scsi_device **sdevp, int rescan,
                                  void *hostdata)
{
        struct scsi_device *sdev;
        unsigned char *result;
        int bflags, res = SCSI_SCAN_NO_RESPONSE, result_len = 256;
        struct Scsi_Host *shost = dev_to_shost(starget->dev.parent);

        /*
         * The rescan flag is used as an optimization, the first scan of a
         * host adapter calls into here with rescan == 0.
         */
        sdev = scsi_device_lookup_by_target(starget, lun);  
        if (sdev) {
                if (rescan || !scsi_device_created(sdev)) {
                        SCSI_LOG_SCAN_BUS(3, printk(KERN_INFO
                                "scsi scan: device exists on %s\n",
                                dev_name(&sdev->sdev_gendev)));
                        if (sdevp)
                                *sdevp = sdev;
                        else
                                scsi_device_put(sdev);

                        if (bflagsp)
                                *bflagsp = scsi_get_device_flags(sdev,
                                                                 sdev->vendor,
                                                                 sdev->model);
                        return SCSI_SCAN_LUN_PRESENT;
                }
                scsi_device_put(sdev);
        } else
                sdev = scsi_alloc_sdev(starget, lun, hostdata);  ////////////新建一个scsi_device
        if (!sdev)
                goto out;

        result = kmalloc(result_len, GFP_ATOMIC |
                        ((shost->unchecked_isa_dma) ? __GFP_DMA : 0));
        if (!result)
                goto out_free_sdev;

        if (scsi_probe_lun(sdev, result, result_len, &bflags))  ////往设备上发送inquery命令来检测指定的device是不是可以正常工作的
                goto out_free_result;

。。。。。。。。。。。。。。。。。。。。。。。。。。。。。

        res = scsi_add_lun(sdev, result, &bflags, shost->async_scan);  ///////如果前面检查过这个设备确实可以使用的话,就调用这个完整的初始化scsi-device设备

。。。。。。。。。。。。。。
}









/**
 * scsi_alloc_sdev - allocate and setup a scsi_Device
 * @starget: which target to allocate a &scsi_device for
 * @lun: which lun
 * @hostdata: usually NULL and set by ->slave_alloc instead
 *
 * 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);
        extern void scsi_requeue_run_queue(struct work_struct *work);

        sdev = kzalloc(sizeof(*sdev) + shost->transportt->device_size,
                       GFP_ATOMIC);   /////////////////////新分配一个内存空间来放这个scsi_device结构,注意一般不通的bus实现都是在device结构的外面又加上自己的信息,就形成了现在这个scsi_device结构了
        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->queue_ramp_up_period = SCSI_DEFAULT_RAMP_UP_PERIOD;
.......................

         * 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);     ///////////sysyfs文件的初始化。///////////

        if (shost->hostt->slave_alloc) {
                ret = shost->hostt->slave_alloc(sdev);    /////////////调用scsi底层驱动的slave_alloc回调函数,让底层驱动可以scan在下一步作scan检测磁盘状态前可以做些额外初始化,如果底层驱动需要的话

.............
}



void scsi_sysfs_device_initialize(struct scsi_device *sdev)
{
        unsigned long flags;
        struct Scsi_Host *shost = sdev->host;
        struct scsi_target  *starget = sdev->sdev_target;

        device_initialize(&sdev->sdev_gendev);
        sdev->sdev_gendev.bus = &scsi_bus_type;        /////创建的时候就初始化 device结构的bus类型了,
        sdev->sdev_gendev.type = &scsi_dev_type;
        dev_set_name(&sdev->sdev_gendev, "%d:%d:%d:%d",
                     sdev->host->host_no, sdev->channel, sdev->id, sdev->lun);

        device_initialize(&sdev->sdev_dev);
        sdev->sdev_dev.parent = get_device(&sdev->sdev_gendev);
        sdev->sdev_dev.class = &sdev_class;
        dev_set_name(&sdev->sdev_dev, "%d:%d:%d:%d",
                     sdev->host->host_no, sdev->channel, sdev->id, sdev->lun);
        sdev->scsi_level = starget->scsi_level;
        transport_setup_device(&sdev->sdev_gendev);
        spin_lock_irqsave(shost->host_lock, flags);
        list_add_tail(&sdev->same_target_siblings, &starget->devices);
        list_add_tail(&sdev->siblings, &shost->__devices);
        spin_unlock_irqrestore(shost->host_lock, flags);
}









/**
 * scsi_probe_lun - probe a single LUN using a SCSI INQUIRY
 * @sdev:       scsi_device to probe
 * @inq_result: area to store the INQUIRY result
 * @result_len: len of inq_result
 * @bflags:     store any bflags found here
 *
 * Description:
 *     Probe the lun associated with @req using a standard SCSI INQUIRY;
 *
 *     If the INQUIRY is successful, zero is returned and the
 *     INQUIRY data is in @inq_result; the scsi_level and INQUIRY length
 *     are copied to the scsi_device any flags value is stored in *@bflags.
 **/
static int scsi_probe_lun(struct scsi_device *sdev, unsigned char *inq_result,
                          int result_len, int *bflags)
{
        unsigned char scsi_cmd[MAX_COMMAND_SIZE];
        int first_inquiry_len, try_inquiry_len, next_inquiry_len;
        int response_len = 0;
        int pass, count, result;
        struct scsi_sense_hdr sshdr;

        *bflags = 0;

        /* Perform up to 3 passes.  The first pass uses a conservative
         * transfer length of 36 unless sdev->inquiry_len specifies a
         * different value. */
        first_inquiry_len = sdev->inquiry_len ? sdev->inquiry_len : 36;
        try_inquiry_len = first_inquiry_len;
        pass = 1;

 next_pass:
        SCSI_LOG_SCAN_BUS(3, sdev_printk(KERN_INFO, sdev,
                                "scsi scan: INQUIRY pass %d length %d\n",
                                pass, try_inquiry_len));

        /* Each pass gets up to three chances to ignore Unit Attention */
        for (count = 0; count < 3; ++count) {           /////////尝试3次 SCSI INQUIRY 命令
                int resid;

                memset(scsi_cmd, 0, 6);
                scsi_cmd[0] = INQUIRY;
                scsi_cmd[4] = (unsigned char) try_inquiry_len;

                memset(inq_result, 0, try_inquiry_len);

                result = scsi_execute_req(sdev,  scsi_cmd, DMA_FROM_DEVICE,
                                          inq_result, try_inquiry_len, &sshdr,
                                          HZ / 2 + HZ * scsi_inq_timeout, 3,
                                          &resid);  ////把探测指令插到scsi的队列里面去,就会调用scsi底层驱动queuecommand回调来真正的执行命令了。

                SCSI_LOG_SCAN_BUS(3, printk(KERN_INFO "scsi scan: INQUIRY %s "
                                "with code 0x%x\n",
                                result ? "failed" : "successful", result));

                if (result) {
...............
 
}







/**
 * scsi_add_lun - allocate and fully initialze a scsi_device
 * @sdev:       holds information to be stored in the new scsi_device
 * @inq_result: holds the result of a previous INQUIRY to the LUN
 * @bflags:     black/white list flag
 * @async:      1 if this device is being scanned asynchronously
 *
 * Description:
 *     Initialize the scsi_device @sdev.  Optionally set fields based
 *     on values in *@bflags.
 *
 * Return:
 *     SCSI_SCAN_NO_RESPONSE: could not allocate or setup a scsi_device
 *     SCSI_SCAN_LUN_PRESENT: a new scsi_device was allocated and initialized
 **/
static int scsi_add_lun(struct scsi_device *sdev, unsigned char *inq_result,
                int *bflags, int async)
{
        int ret;

 
        /* set the device running here so that slave configure
         * may do I/O */
        ret = scsi_device_set_state(sdev, SDEV_RUNNING);  //////////改变设备状态

。。。。。。。。。。。。
        if (sdev->host->hostt->slave_configure) {
                ret = sdev->host->hostt->slave_configure(sdev);
                if (ret) {
                        /*
                         * if LLDD reports slave not present, don't clutter
                         * console with alloc failure messages
                         */
                        if (ret != -ENXIO) {
                                sdev_printk(KERN_ERR, sdev,
                                        "failed to configure device\n");
                        }
                        return SCSI_SCAN_NO_RESPONSE;
                }
        }

        sdev->max_queue_depth = sdev->queue_depth;

        /*
         * Ok, the device is now all set up, we can
         * register it and tell the rest of the kernel
         * about it.
         */
        if (!async && scsi_sysfs_add_sdev(sdev) != 0)       ////////////////////调用这个函数把先发现的scsi设备注册到系统的驱动模型里面去。这个完成之后scsi设备的sysfs文件,bus和驱动关联都会被配置好了
                return SCSI_SCAN_NO_RESPONSE;

        return SCSI_SCAN_LUN_PRESENT;
}







/**
 * scsi_sysfs_add_sdev - add scsi device to sysfs
 * @sdev:       scsi_device to add
 *
 * Return value:
 *      0 on Success / non-zero on Failure
 **/
int scsi_sysfs_add_sdev(struct scsi_device *sdev)
{
        int error, i;
        struct request_queue *rq = sdev->request_queue;
        struct scsi_target *starget = sdev->sdev_target;

        error = scsi_device_set_state(sdev, SDEV_RUNNING);
        if (error)
                return error;

        error = scsi_target_add(starget);
        if (error)
                return error;

        transport_configure_device(&starget->dev);

        device_enable_async_suspend(&sdev->sdev_gendev);
        scsi_autopm_get_target(starget);
        pm_runtime_set_active(&sdev->sdev_gendev);
        pm_runtime_forbid(&sdev->sdev_gendev);
        pm_runtime_enable(&sdev->sdev_gendev);
        scsi_autopm_put_target(starget);

        /* The following call will keep sdev active indefinitely, until
         * its driver does a corresponding scsi_autopm_pm_device().  Only
         * drivers supporting autosuspend will do this.
         */
        scsi_autopm_get_device(sdev);

        error = device_add(&sdev->sdev_gendev); /////////往系统中注册设备,这个进去就会把设备和驱动还有相关的总线关联起来了。
        if (error) {
                sdev_printk(KERN_INFO, sdev,
                                "failed to add device: %d\n", error);
                return error;
        }
        device_enable_async_suspend(&sdev->sdev_dev);
        error = device_add(&sdev->sdev_dev);   
        if (error) {
                sdev_printk(KERN_INFO, sdev,
                                "failed to add class device: %d\n", error);
                device_del(&sdev->sdev_gendev);
                return error;
        }
        transport_add_device(&sdev->sdev_gendev);
        sdev->is_visible = 1;

        /* create queue files, which may be writable, depending on the host */
        if (sdev->host->hostt->change_queue_depth) {
                error = device_create_file(&sdev->sdev_gendev,
                                           &sdev_attr_queue_depth_rw);
                error = device_create_file(&sdev->sdev_gendev,
                                           &sdev_attr_queue_ramp_up_period);
        }
        else
                error = device_create_file(&sdev->sdev_gendev, &dev_attr_queue_depth); ///////自己添加sysfs自定义文件
        if (error)
                return error;

        if (sdev->host->hostt->change_queue_type)
                error = device_create_file(&sdev->sdev_gendev, &sdev_attr_queue_type_rw);
        else
                error = device_create_file(&sdev->sdev_gendev, &dev_attr_queue_type);
        if (error)
                return error;

        error = bsg_register_queue(rq, &sdev->sdev_gendev, NULL, NULL);

        if (error)
                /* we're treating error on bsg register as non-fatal,
                 * so pretend nothing went wrong */
                sdev_printk(KERN_INFO, sdev,
                            "Failed to register bsg queue, errno=%d\n", error);

        /* add additional host specific attributes */
        if (sdev->host->hostt->sdev_attrs) {
                for (i = 0; sdev->host->hostt->sdev_attrs[i]; i++) {
                        error = device_create_file(&sdev->sdev_gendev,
                                        sdev->host->hostt->sdev_attrs[i]);
                        if (error)
                                return error;
                }
        }

        return error;
}






struct bus_type scsi_bus_type = {
        .name           = "scsi",
        .match          = scsi_bus_match,    >>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>
        .uevent         = scsi_bus_uevent,
#ifdef CONFIG_PM
        .pm             = &scsi_bus_pm_ops,
#endif
};
EXPORT_SYMBOL_GPL(scsi_bus_type);

int scsi_sysfs_register(void)
{
        int error;

        error = bus_register(&scsi_bus_type);
        if (!error) {
                error = class_register(&sdev_class);
                if (error)
                        bus_unregister(&scsi_bus_type);
        }

        return error;
}







 根据scsi磁盘设备驱动的注册,发现新设备之后将调到 sd驱动sd_probe 函数去






static struct scsi_driver sd_template = {
        .owner                  = THIS_MODULE,
        .gendrv = {
                .name           = "sd",
                .probe          = sd_probe,  >>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>
                .remove         = sd_remove,
                .suspend        = sd_suspend,
                .resume         = sd_resume,
                .shutdown       = sd_shutdown,
        },
        .rescan                 = sd_rescan,
        .done                   = sd_done,
};




/**
 *      init_sd - entry point for this driver (both when built in or when
 *      a module).
 *
 *      Note: this function registers this driver with the scsi mid-level.
 **/
static int __init init_sd(void)
{
        int majors = 0, i, err;

        SCSI_LOG_HLQUEUE(3, printk("init_sd: sd driver entry point\n"));

        for (i = 0; i < SD_MAJORS; i++)
                if (register_blkdev(sd_major(i), "sd") == 0)
                        majors++;

        if (!majors)
                return -ENODEV;

        err = class_register(&sd_disk_class);
        if (err)
                goto err_out;

        err = scsi_register_driver(&sd_template.gendrv);
        if (err)



://lxr.linux.no/#linux+v2.6.39/drivers/scsi/scsi_sysfs.c#L1009

int scsi_register_driver(struct device_driver *drv)
{
        drv->bus = &scsi_bus_type;

        return driver_register(drv);
}




static int sd_probe(struct device *dev)


            sdkp->driver = &sd_template;
            sdkp->disk = gd;




   

   
   
新设备的发现:
echo "- - -" >  /sys/class/scsi_host/host1/scan (横杆表示所有的0~255的所有设备)到 sys 文件系统的host文件夹的scan文件里面去(看一看scsi_scan_host_selected 在scsi_sysfs.c文件里实现),这个就会触发调用scsi_scan_host_selected函数,然后就会触发底层驱动扫描指定的设备有没有是不是可以工作, 然后添加设备到系统。

scsi驱动应该在适当的时候,自己调用scsi_remove_device 来把设备的引用计数减少,以把设备从系统中删除。 比如在驱动卸载的时候,就会把他自己所有的设备从系统里面移除。
   
   
内核文档
http://lxr.linux.no/#linux+v2.6.39/Documentation/scsi/scsi_mid_low_api.txt
的“Hotplug initialization model‘ 也有个流程介绍,可以看一下那里。

参考了内核文档:
 http://lxr.linux.no/#linux+v2.6.39/Documentation/driver-model/
  评论这张
 
阅读(1299)| 评论(0)
推荐 转载

历史上的今天

评论

<#--最新日志,群博日志--> <#--推荐日志--> <#--引用记录--> <#--博主推荐--> <#--随机阅读--> <#--首页推荐--> <#--历史上的今天--> <#--被推荐日志--> <#--上一篇,下一篇--> <#-- 热度 --> <#-- 网易新闻广告 --> <#--右边模块结构--> <#--评论模块结构--> <#--引用模块结构--> <#--博主发起的投票-->
 
 
 
 
 
 
 
 
 
 
 
 
 
 

页脚

网易公司版权所有 ©1997-2017