分类: LINUX
2008-10-16 23:07:51
SCSI UL和LLD的关系是driver和device的关系。内核中定义了device_driver和device结构,分别来抽象设备驱动和设备。这两个结构相当于所有设备驱动和设备的超类。UL代表的scsi_driver和LLD所代表的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;
……
};
由这两个结构可以看出,二者的关系是device:device_driver=n:1。UL和LLD间的联系,实际上是二者的联系,而二者的联系是在初始化时确定。UL初始化时准备device_driver结构,其中包括probe方法。而LLD(准确地说,应该是middle layer)准备device结构。二者都把其bus初始化为scsi_bus_type,并挂入该总线。之后,如果是UL初始化,则调用自身的probe方法,去探测bus上的所有device,从而把device_driver绑定倒device。而如果是LLD初始化,则调用bus上所有的device_driver的probe方法来探测自身的device。因此,无论是UL先初始化还是LLD先初始化,二者都能取得联系。
下面看一下sd(UL)和scsi host driver(LLD)如何绑定的。
首先,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首先把driver的bus初始化为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方法。在该方法中会调用driver的probe方法(这里即是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,target和lun。因此,它分别调用scsi_scan_channel,__scsi_scan_target,scsi_probe_and_add_lun来每个target及其lun。其中,在scsi_probe_and_add_lun中会分配代表每个lun即scsi设备的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命令,探测制定的lun。scsi设备返回的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,调用这些driver的probe方法来探测自身的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_driver和device联系在了一起。总之,对于每个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=
#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=
#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
#13 0xc019eb50 in scsi_scan_host (shost=0xc715e800) at drivers/scsi/scsi_scan.c:1813