Chinaunix首页 | 论坛 | 博客
  • 博客访问: 380805
  • 博文数量: 62
  • 博客积分: 5015
  • 博客等级: 大校
  • 技术积分: 915
  • 用 户 组: 普通用户
  • 注册时间: 2006-03-08 02:00
文章分类

全部博文(62)

文章存档

2009年(45)

2008年(17)

我的朋友

分类: LINUX

2008-10-16 23:07:51

SCSI ULLLD的关系是driverdevice的关系。内核中定义了device_driverdevice结构,分别来抽象设备驱动和设备。这两个结构相当于所有设备驱动和设备的超类。UL代表的scsi_driverLLD所代表的scsi_device分别是它们子类。 struct device_driver {

const char * name;

struct bus_type * bus;

……

int (*probe) (struct device * dev);

int (*remove) (struct device * dev);

void (*shutdown) (struct device * dev);

int (*suspend) (struct device * dev, pm_message_t state);

int (*resume) (struct device * dev);

};

struct device {

 ……

 struct device *parent;

 ……

device_type *type;

struct bus_type * bus; /* type of bus device is on */

struct device_driver *driver; /* which driver has allocated this device */

void *driver_data;

……

};

由这两个结构可以看出,二者的关系是devicedevice_driver=n:1ULLLD间的联系,实际上是二者的联系,而二者的联系是在初始化时确定。UL初始化时准备device_driver结构,其中包括probe方法。而LLD(准确地说,应该是middle layer)准备device结构。二者都把其bus初始化为scsi_bus_type,并挂入该总线。之后,如果是UL初始化,则调用自身的probe方法,去探测bus上的所有device,从而把device_driver绑定倒device。而如果是LLD初始化,则调用bus上所有的device_driverprobe方法来探测自身的device。因此,无论是UL先初始化还是LLD先初始化,二者都能取得联系。

下面看一下sdUL)和scsi host driverLLD)如何绑定的。

首先,sd中定义了一个scsi_driver结构,即device_driver的派生类:

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,

};

初始化时,调用scsi_register_driver注册该scsi_driver. scsi_register_driver:

drv->bus = &scsi_bus_type;

scsi_register_driver首先把driverbus初始化为scsi_bus_type。接着调用driver_register来注册driver

scsi_register_driver->driver_register->bus_add_driver->driver_attach:

return bus_for_each_dev(drv->bus, NULL, drv, __driver_attach);

driver_attach中,对bus上的每个device调用__driver_attach方法。在该方法中会调用driverprobe方法(这里即是sd_probe)去探测每个device(此时,如果scsi host driver初始化,则bus上有相应的device,则sd_probe则为其生成相应gend)。

 __driver_attach->driver_probe_device->really_probe:

ret = drv->probe(dev);

 

另一方面,scsi host driver在初始化时,会调用scsi_scan_host来扫描host。扫描整个host以为着扫描host所对应的channel,targetlun。因此,它分别调用scsi_scan_channel__scsi_scan_targetscsi_probe_and_add_lun来每个target及其lun。其中,在scsi_probe_and_add_lun中会分配代表每个lunscsi设备的scsi_device结构:

 scsi_scan_host->do_scsi_scan_host->scsi_scan_host_selected->scsi_scan_channel->__scsi_scan_target->scsi_probe_and_add_lun:

sdev = scsi_alloc_sdev(starget, lun, hostdata);

其中,在scsi_device结构中,已经包含了超类device结构:

struct scsi_device {

struct Scsi_Host *host;

struct request_queue *request_queue;

     ……

int timeout;

struct device sdev_gendev;

struct class_device sdev_classdev;

……

};

scsi_scan_host->do_scsi_scan_host->scsi_scan_host_selected->scsi_scan_channel->__scsi_scan_target->scsi_probe_and_add_lun->scsi_probe_lun

分配好scsi_device结构以后,调用scsi_probe_lun来发送INQUIRY命令,探测制定的lunscsi设备返回的inquiry data将保存在result参数中,以备scsi_add_lun使用。其中包括了设备的信息,包括设备的种类type等。

scsi_scan_host->do_scsi_scan_host->scsi_scan_host_selected->scsi_scan_channel->__scsi_scan_target->scsi_probe_and_add_lun->scsi_add_lun

如果scsi_probe_lun成功,则调用scsi_add_lun来添加lun。在scsi_add_lun中,先根据inquiry data来初始化scsi_device中的一些属性,包括其type属性(在这儿为TYPE_DISK)。

scsi_scan_host->do_scsi_scan_host->scsi_scan_host_selected->scsi_scan_channel->__scsi_scan_target->scsi_probe_and_add_lun->scsi_add_lun->scsi_sysfs_add_sdev

之后调用scsi_sysfs_add_sdev来添加scsi_device。这儿与device_driver的注册类似,调用device_attach来对扫描bus上所有的driver,调用这些driverprobe方法来探测自身的device。如果此时,sd没有初始话,即bus上没有相应的驱动,则不会调用probe方法。即不会生成lun对应的gend。并且,sd_probe中,会检测scsi_device的具体类型,只有自己支持的才回去探测。我想这个应该是各种scsi UL driver必须检测的:

if (sdp->type != TYPE_DISK && sdp->type != TYPE_MOD && sdp->type != TYPE_RBC) goto out;

通过上述两个过程,device_driverdevice联系在了一起。总之,对于每个lun的加入,sd_probe都会执行一次。只不过sd_probe的触发,要么是通过sd驱动scsi_register_driver,要么是通过LLD scsi_scan_host

 

sd_probe调用栈(由scsi_scan_host触发):

#0 sd_probe (dev=0xc714a4b0) at drivers/scsi/sd.c:1597

#1 0xc018df10 in driver_probe_device (drv=0xc0335df8, dev=0xc714a4b0) at drivers/base/dd.c:121

#2 0xc018dfd8 in __device_attach (drv=0xc714a4b0, data=0x0) at drivers/base/dd.c:207

#3 0xc018d110 in bus_for_each_drv (bus=, start=, data=0x0, fn=0xc018dfc8 <__device_attach>) at drivers/base/bus.c:349

#4 0xc018e078 in device_attach (dev=0x1) at drivers/base/dd.c:238

#5 0xc018d070 in bus_attach_device (dev=0xc714a4b0) at drivers/base/bus.c:492

#6 0xc018be64 in device_add (dev=0xc714a4b0) at drivers/base/core.c:781

#7 0xc019fc84 in scsi_sysfs_add_sdev (sdev=0xc714a400) at drivers/scsi/scsi_sysfs.c:783

#8 0xc019d9b8 in scsi_probe_and_add_lun (starget=0xc76d9000, lun=, bflagsp=, sdevp=0x0, rescan=0, hostdata=0x0) at drivers/scsi/scsi_scan.c:914

#9 0xc019e088 in __scsi_scan_target (parent=0xc715e8d8, channel=0, id=0, lun=4294967295, rescan=0) at drivers/scsi/scsi_scan.c:1550

#10 0xc019e538 in scsi_scan_channel (shost=0xc715e800, channel=0, id=0, lun=4294967295, rescan=0) at drivers/scsi/scsi_scan.c:1626

#11 0xc019e608 in scsi_scan_host_selected (shost=0xc714a4b0, channel=1, id=1, lun=3222995532, rescan=0) at drivers/scsi/scsi_scan.c:1654

#12 0xc019e6e8 in do_scsi_scan_host (shost=0xc715e800) ---Type to continue, or q to quit--- at drivers/scsi/scsi_scan.c:1786

#13 0xc019eb50 in scsi_scan_host (shost=0xc715e800) at drivers/scsi/scsi_scan.c:1813

 

阅读(2176) | 评论(0) | 转发(0) |
给主人留下些什么吧!~~