SCSI Upper Layer 与LLD的联系——sd_probe

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=, 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

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值