首先,让我们先进入ata_host_register函数,看如何一步一步的去向上层注册的。
intata_host_register(struct ata_host *host, struct scsi_host_template *sht)
{
int i, rc;
host->n_tags =clamp(sht->can_queue, 1, ATA_MAX_QUEUE - 1);// can_queue =1 MAX_QUEUE = 32 by wyf
/* host must have been started */
if (!(host->flags &ATA_HOST_STARTED)) { //ata_host_startset ATA_HOST_STARTED by wyf
dev_err(host->dev,”BUG: trying to register unstarted host\n”);
WARN_ON(1);
return -EINVAL;
}
/* Blow away unused ports. This happens when LLD can’t
* determine the exact number of ports toallocate at
* allocation time.
*/
for (i = host->n_ports;host->ports[i]; i++)
kfree(host->ports[i]);
/* give ports names and add SCSI hosts*/
for (i = 0; i < host->n_ports; i++){
host->ports[i]->print_id= atomic_inc_return(&ata_print_id);
host->ports[i]->local_port_no= i + 1;
}
/* Create associated sysfs transportobjects */
for (i = 0; i < host->n_ports;i++) {
rc =ata_tport_add(host->dev,host->ports[i]);
if (rc) {
goto err_tadd;
}
}
rc = ata_scsi_add_hosts(host, sht);
if (rc)
goto err_tadd;
/* set cable, sata_spd_limit and report*/
for (i = 0; i < host->n_ports;i++) {
struct ata_port *ap =host->ports[i];
unsigned long xfer_mask;
/* set SATA cable type ifstill unset */
if (ap->cbl ==ATA_CBL_NONE && (ap->flags & ATA_FLAG_SATA))
ap->cbl =ATA_CBL_SATA;
/* init sata_spd_limit to thecurrent value */
sata_link_init_spd(&ap->link);
if (ap->slave_link)
sata_link_init_spd(ap->slave_link);
/* print per-port info todmesg */
xfer_mask =ata_pack_xfermask(ap->pio_mask, ap->mwdma_mask,
ap->udma_mask);
if (!ata_port_is_dummy(ap)) {
ata_port_info(ap,”%cATA max %s %s\n”,
(ap->flags & ATA_FLAG_SATA) ? ‘S’: ‘P’,
ata_mode_string(xfer_mask),
ap->link.eh_info.desc);
ata_ehi_clear_desc(&ap->link.eh_info);
} else
ata_port_info(ap,”DUMMY\n”);
}
/* perform each probe asynchronously */
for (i = 0; i < host->n_ports;i++) {
struct ata_port *ap =host->ports[i];
async_schedule(async_port_probe,ap);
}
return 0;
err_tadd:
while (–i >= 0) {
ata_tport_delete(host->ports[i]);
}
return rc;
}
在ata_host_register中调用了ata_scsi_add_hosts和async_port_probe两个重要的函数,其中ata_scsi_add_hosts主要是分配一个scsi_host结构,并向上层注册一个scsi_host;
在async_port_probe 中主要调用ata_scsi_scan_host进行扫描scsi_devcie,并向上层注册scsi_device;
下面让我们一起走进scsi_device如何被注册的把!
voidata_scsi_scan_host(struct ata_port *ap, int sync)
{
int tries = 5;
struct ata_device *last_failed_dev =NULL;
struct ata_link *link;
struct ata_device *dev;
repeat:
ata_for_each_link(link, ap, EDGE) {
ata_for_each_dev(dev, link,ENABLED) {
struct scsi_device *sdev;
int channel = 0, id= 0;
if (dev->sdev)
continue;
if(ata_is_host_link(link))
id =dev->devno;
else
channel =link->pmp;
sdev =__scsi_add_device(ap->scsi_host, channel, id, 0,
NULL);
if (!IS_ERR(sdev)) {
dev->sdev= sdev;
scsi_device_put(sdev);
} else {
dev->sdev= NULL;
}
}
}
/* If we scanned while EH was inprogress or allocation
* failure occurred, scan would have failedsilently. Check
* whether all devices are attached.
*/
ata_for_each_link(link, ap, EDGE) {
ata_for_each_dev(dev, link,ENABLED) {
if (!dev->sdev)
gotoexit_loop;
}
}
exit_loop:
if (!link)
return;
/* we’re missing some SCSI devices */
if (sync) {
/* If caller requestedsynchrnous scan && we’ve made
* any progress, sleep briefly and repeat.
*/
if (dev != last_failed_dev) {
msleep(100);
last_failed_dev =dev;
goto repeat;
}
/* We might be failing todetect boot device, give it
* a few more chances.
*/
if (–tries) {
msleep(100);
goto repeat;
}
ata_port_err(ap,
“WARNING: synchronous SCSI scanfailed without making any progress, switching to async\n”);
}
queue_delayed_work(system_long_wq,&ap->hotplug_task,
round_jiffies_relative(HZ));
}
通过__add_scsi_device添加并注册一个scsi_device;
structscsi_device *__scsi_add_device(struct Scsi_Host *shost, uint channel,
uint id, u64 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);//*sdev = alloced scsi_device by wyf
scsi_autopm_put_host(shost);
}
mutex_unlock(&shost->scan_mutex);
scsi_autopm_put_target(starget);
/*
* paired with scsi_alloc_target(). Target will be destroyed unless
* scsi_probe_and_add_lun made an underlyingdevice visible
*/
scsi_target_reap(starget);
put_device(&starget->dev);
return sdev;
}
在上面函数中调用了scsi_probe_and_add_lun,该函数主要实现轮询是否有scsi_device 并添加一个Lun 设备。
staticint scsi_probe_and_add_lun(struct scsi_target *starget,
u64 lun, int *bflagsp,
struct scsi_device **sdevp, int rescan,
void *hostdata)//starget, 0, NULL, &sdev,1, hostdata by wyf
{
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,sdev_printk(KERN_INFO, sdev,
“scsiscan: 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);
returnSCSI_SCAN_LUN_PRESENT;
}
scsi_device_put(sdev);
} else
sdev = scsi_alloc_sdev(starget, lun, hostdata);
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))
goto out_free_result;
if (bflagsp)
*bflagsp = bflags;
/*
* result contains valid SCSI INQUIRY data.
*/
if (((result[0] >> 5) == 3)&& !(bflags & BLIST_ATTACH_PQ3)) {
/*
* For a Peripheral qualifier 3 (011b), theSCSI
* spec says: The device server is not capableof
* supporting a physical device on this logical
* unit.
*
* For disks, this implies that there is no
* logical disk configured at sdev->lun, butthere
* is a target id responding.
*/
SCSI_LOG_SCAN_BUS(2,sdev_printk(KERN_INFO, sdev, “scsi scan:”
” peripheral qualifier of 3, devicenot”
” added\n”))
if (lun == 0) {
SCSI_LOG_SCAN_BUS(1,{
unsignedchar vend[9];
unsigned char mod[17];
sdev_printk(KERN_INFO,sdev,
“scsiscan: consider passing scsi_mod.”
“dev_flags=%s:%s:0x240or 0x1000240\n”,
scsi_inq_str(vend,result, 8, 16),
scsi_inq_str(mod,result, 16, 32));
});
}
res =SCSI_SCAN_TARGET_PRESENT;
goto out_free_result;
}
/*
* Some targets may set slight variations of PQand PDT to signal
* that no LUN is present, so don’t add sdev inthese cases.
* Two specific examples are:
* 1) NetApp targets: return PQ=1, PDT=0x1f
* 2) USB UFI: returns PDT=0x1f, with the PQbits being “reserved”
* inthe UFI 1.0 spec (we cannot rely on reserved bits).
*
* References:
* 1) SCSI SPC-3, pp. 145-146
* PQ=1: “A peripheral device having thespecified peripheral
* device type is not connected to this logicalunit. However, the
* device server is capable of supporting thespecified peripheral
* device type on this logical unit.”
* PDT=0x1f: “Unknown or no devicetype”
* 2) USB UFI 1.0, p. 20
* PDT=00h Direct-access device (floppy)
* PDT=1Fh none (no FDD connected to therequested logical unit)
*/
if (((result[0] >> 5) == 1 ||starget->pdt_1f_for_no_lun) &&
(result[0] & 0x1f) == 0x1f &&
!scsi_is_wlun(lun)) {
SCSI_LOG_SCAN_BUS(3,sdev_printk(KERN_INFO, sdev,
“scsiscan: peripheral device type”
“of 31, no device added\n”));
res =SCSI_SCAN_TARGET_PRESENT;
goto out_free_result;
}
res = scsi_add_lun(sdev, result, &bflags,shost->async_scan);
if (res == SCSI_SCAN_LUN_PRESENT) {
if (bflags & BLIST_KEY) {
sdev->lockable =0;
scsi_unlock_floptical(sdev,result);
}
}
out_free_result:
kfree(result);
out_free_sdev:
if (res == SCSI_SCAN_LUN_PRESENT) {
if (sdevp) {
if (scsi_device_get(sdev)== 0) {
*sdevp =sdev;
} else {
__scsi_remove_device(sdev);
res =SCSI_SCAN_NO_RESPONSE;
}
}
} else
__scsi_remove_device(sdev);
out:
return res;
}
可以在上面函数中看到,如果找到一个sdev,就返回SCSI_SCAN_LUN_PRESENT;如果sdev为kong,则scsi_alloc_sdev分配一个sdev,然后scsi_probe_lun利用SCSI INQUIRY命令探测一个lun,最后通过scsi_add_lun注册一个lun设备。
重点看scsi层如何向上层提交一个scsi_device的。
staticint scsi_add_lun(struct scsi_device *sdev, unsigned char *inq_result,
int *bflags, int async)
{
int ret;
/*
* XXX do not save the inquiry, since it canchange underneath us,
* save just vendor/model/rev.
*
* Rather than save it and have an ioctl thatretrieves the saved
* value, have an ioctl that executes the sameINQUIRY code used
* in scsi_probe_lun, let user level programsdoing INQUIRY
* scanning run at their own risk, or supply auser level program
* that can correctly scan.
*/
/*
* Copy at least 36 bytes of INQUIRY data, sothat we don’t
* dereference unallocated memory whenaccessing the Vendor,
* Product, and Revision strings. Badly behaved devices may set
* the INQUIRY Additional Length byte to asmall value, indicating
* these strings are invalid, but often theycontain plausible data
* nonetheless. It doesn’t matter if the device sent < 36 bytes
* total, since scsi_probe_lun() initializesinq_result with 0s.
*/
sdev->inquiry = kmemdup(inq_result,
max_t(size_t,sdev->inquiry_len, 36),
GFP_ATOMIC);
if (sdev->inquiry == NULL)
return SCSI_SCAN_NO_RESPONSE;
sdev->vendor = (char *)(sdev->inquiry + 8);
sdev->model = (char *)(sdev->inquiry + 16);
sdev->rev = (char *)(sdev->inquiry + 32);
if (strncmp(sdev->vendor,”ATA “, 8) == 0) {
/*
* sata emulation layer device. This is a hack to work around
* the SATL power management specificationswhich state that
* when the SATL detects the device has goneinto standby
* mode, it shall respond with NOT READY.
*/
sdev->allow_restart = 1;
}
if (*bflags & BLIST_ISROM) {
sdev->type = TYPE_ROM;
sdev->removable = 1;
} else {
sdev->type =(inq_result[0] & 0x1f);
sdev->removable =(inq_result[1] & 0x80) >> 7;
/*
* some devices may respond with wrong type for
* well-known logical units. Force well-knowntype
* to enumerate them correctly.
*/
if(scsi_is_wlun(sdev->lun) && sdev->type != TYPE_WLUN) {
sdev_printk(KERN_WARNING,sdev,
“%s:correcting incorrect peripheral device type 0x%x for W-LUN 0x%16xhN\n”,
__func__,sdev->type, (unsigned int)sdev->lun);
sdev->type =TYPE_WLUN;
}
}
if (sdev->type == TYPE_RBC ||sdev->type == TYPE_ROM) {
/* RBC and MMC devices can returnSCSI-3 compliance and yet
* still not support REPORT LUNS, so make themact as
* BLIST_NOREPORTLUN unless BLIST_REPORTLUN2 is
* specifically set */
if ((*bflags &BLIST_REPORTLUN2) == 0)
*bflags |=BLIST_NOREPORTLUN;
}
/*
* For a peripheral qualifier (PQ) value of 1(001b), the SCSI
* spec says: The device server is capable ofsupporting the
* specified peripheral device type on thislogical unit. However,
* the physical device is not currentlyconnected to this logical
* unit.
*
* The above is vague, as it implies that wecould treat 001 and
* 011 the same. Stay compatible with previouscode, and create a
* scsi_device for a PQ of 1
*
* Don’t set the device offline here; ratherlet the upper
* level drivers eval the PQ to decide whetherthey should
* attach. So remove ((inq_result[0] >>5) & 7) == 1 check.
*/
sdev->inq_periph_qual =(inq_result[0] >> 5) & 7;
sdev->lockable = sdev->removable;
sdev->soft_reset = (inq_result[7]& 1) && ((inq_result[3] & 7) == 2);
if (sdev->scsi_level >= SCSI_3 ||
(sdev->inquiry_len> 56 && inq_result[56] & 0x04))
sdev->ppr = 1;
if (inq_result[7] & 0x60)
sdev->wdtr = 1;
if (inq_result[7] & 0x10)
sdev->sdtr = 1;
sdev_printk(KERN_NOTICE, sdev, “%s%.8s %.16s %.4s PQ: %d “
“ANSI:%d%s\n”, scsi_device_type(sdev->type),
sdev->vendor,sdev->model, sdev->rev,
sdev->inq_periph_qual,inq_result[2] & 0x07,
(inq_result[3] &0x0f) == 1 ? ” CCS” : “”);
if ((sdev->scsi_level >= SCSI_2)&& (inq_result[7] & 2) &&
!(*bflags & BLIST_NOTQ)) {
sdev->tagged_supported =1;
sdev->simple_tags = 1;
}
/*
* Some devices (Texel CD ROM drives) havehandshaking problems
* when used with the Seagate controllers.borken is initialized
* to 1, and then set it to 0 here.
*/
if ((*bflags & BLIST_BORKEN) == 0)
sdev->borken = 0;
if (*bflags & BLIST_NO_ULD_ATTACH)
sdev->no_uld_attach = 1;
/*
* Apparently some really broken devices(contrary to the SCSI
* standards) need to be selected withoutasserting ATN
*/
if (*bflags & BLIST_SELECT_NO_ATN)
sdev->select_no_atn = 1;
/*
* Maximum 512 sector transfer length
* broken RA4x00 Compaq Disk Array
*/
if (*bflags & BLIST_MAX_512)
blk_queue_max_hw_sectors(sdev->request_queue,512);
/*
* Some devices may not want to have a startcommand automatically
* issued when a device is added.
*/
if (*bflags & BLIST_NOSTARTONADD)
sdev->no_start_on_add = 1;
if (*bflags & BLIST_SINGLELUN)
scsi_target(sdev)->single_lun= 1;
sdev->use_10_for_rw = 1;
if (*bflags &BLIST_MS_SKIP_PAGE_08)
sdev->skip_ms_page_8 = 1;
if (*bflags &BLIST_MS_SKIP_PAGE_3F)
sdev->skip_ms_page_3f = 1;
if (*bflags & BLIST_USE_10_BYTE_MS)
sdev->use_10_for_ms = 1;
/* some devices don’t like REPORTSUPPORTED OPERATION CODES
* and will simply timeout causing sd_mod initto take a very
* very long time */
if (*bflags & BLIST_NO_RSOC)
sdev->no_report_opcodes =1;
/* set the device running here so thatslave configure
* may do I/O */
ret = scsi_device_set_state(sdev,SDEV_RUNNING);
if (ret) {
ret =scsi_device_set_state(sdev, SDEV_BLOCK);
if (ret) {
sdev_printk(KERN_ERR,sdev,
“in wrong state %s to completescan\n”,
scsi_device_state_name(sdev->sdev_state));
returnSCSI_SCAN_NO_RESPONSE;
}
}
if (*bflags &BLIST_MS_192_BYTES_FOR_3F)
sdev->use_192_bytes_for_3f= 1;
if (*bflags & BLIST_NOT_LOCKABLE)
sdev->lockable = 0;
if (*bflags & BLIST_RETRY_HWERROR)
sdev->retry_hwerror = 1;
if (*bflags & BLIST_NO_DIF)
sdev->no_dif = 1;
sdev->eh_timeout =SCSI_DEFAULT_EH_TIMEOUT;
if (*bflags & BLIST_TRY_VPD_PAGES)
sdev->try_vpd_pages = 1;
else if (*bflags &BLIST_SKIP_VPD_PAGES)
sdev->skip_vpd_pages = 1;
transport_configure_device(&sdev->sdev_gendev);
if(sdev->host->hostt->slave_configure) {
ret =sdev->host->hostt->slave_configure(sdev);
if (ret) {
/*
* if LLDD reports slave not present, don’tclutter
* console with alloc failure messages
*/
if (ret != -ENXIO) {
sdev_printk(KERN_ERR,sdev,
“failedto configure device\n”);
}
returnSCSI_SCAN_NO_RESPONSE;
}
}
if (sdev->scsi_level >= SCSI_3)
scsi_attach_vpd(sdev);
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)
return SCSI_SCAN_NO_RESPONSE;
return SCSI_SCAN_LUN_PRESENT;
}
在scsi_add_lun中主要做的工作是通过SCSI INQUIRY 返回的参数初始化sdev的一些成员,并通过if (!async && scsi_sysfs_add_sdev(sdev) != 0)中的scsi_sysfs_add_sdev向上层注册sdev。
/**
* scsi_sysfs_add_sdev - add scsi device tosysfs
* @sdev: scsi_deviceto add
*
* Return value:
* 0 onSuccess / non-zero on Failure
**/
intscsi_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);
scsi_autopm_get_device(sdev);
error = device_add(&sdev->sdev_gendev);
if (error) {
sdev_printk(KERN_INFO, sdev,
“failedto 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,
“failedto add class device: %d\n”, error);
device_del(&sdev->sdev_gendev);
return error;
}
transport_add_device(&sdev->sdev_gendev);
sdev->is_visible = 1;
error = bsg_register_queue(rq,&sdev->sdev_gendev, NULL, NULL);
if (error)
/* we’re treating error onbsg 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 specificattributes */
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)
returnerror;
}
}
scsi_autopm_put_device(sdev);
return error;
}
通过device_add(sdev-> sdev_gendev)注册scsi middle layer。
int device_add(struct device*dev)
{
struct device *parent = NULL;
struct kobject *kobj;
struct class_interface *class_intf;
int error = -EINVAL;
dev = get_device(dev);
if (!dev)
goto done;
if (!dev->p) {
error =device_private_init(dev);
if (error)
goto done;
}
/*
* for statically allocated devices, whichshould all be converted
* some day, we need to initialize the name. Weprevent reading back
* the name, and force the use of dev_name()
*/
if (dev->init_name) {
dev_set_name(dev,”%s”, dev->init_name);
dev->init_name = NULL;
}
/* subsystems can specify simple deviceenumeration */
if (!dev_name(dev) &&dev->bus && dev->bus->dev_name)
dev_set_name(dev,”%s%u”, dev->bus->dev_name, dev->id);
if (!dev_name(dev)) {
error = -EINVAL;
goto name_error;
}
pr_debug(“device: ‘%s’:%s\n”, dev_name(dev), __func__);
parent = get_device(dev->parent);
kobj = get_device_parent(dev, parent);
if (kobj)
dev->kobj.parent = kobj;
/* use parent numa_node */
if (parent)
set_dev_node(dev,dev_to_node(parent));
/* first, register with generic layer.*/
/* we require the name to be setbefore, and pass NULL */
error = kobject_add(&dev->kobj,dev->kobj.parent, NULL);
if (error)
goto Error;
/* notify platform of device entry */
if (platform_notify)
platform_notify(dev);
error = device_create_file(dev,&dev_attr_uevent);
if (error)
goto attrError;
error = device_add_class_symlinks(dev);
if (error)
goto SymlinkError;
error = device_add_attrs(dev);
if (error)
goto AttrsError;
error = bus_add_device(dev);
if (error)
goto BusError;
error = dpm_sysfs_add(dev);
if (error)
goto DPMError;
device_pm_add(dev);
if (MAJOR(dev->devt)) {
error =device_create_file(dev, &dev_attr_dev);
if (error)
goto DevAttrError;
error =device_create_sys_dev_entry(dev);
if (error)
goto SysEntryError;
devtmpfs_create_node(dev);
}
/* Notify clients of deviceaddition. This call must come
* after dpm_sysfs_add() and beforekobject_uevent().
*/
if (dev->bus)
blocking_notifier_call_chain(&dev->bus->p->bus_notifier,
BUS_NOTIFY_ADD_DEVICE, dev);
kobject_uevent(&dev->kobj,KOBJ_ADD);
bus_probe_device(dev);
if (parent)
klist_add_tail(&dev->p->knode_parent,
&parent->p->klist_children);
if (dev->class) {
mutex_lock(&dev->class->p->mutex);
/* tie the class to thedevice */
klist_add_tail(&dev->knode_class,
&dev->class->p->klist_devices);
/* notify any interfaces thatthe device is here */
list_for_each_entry(class_intf,
&dev->class->p->interfaces,node)
if (class_intf->add_dev)
class_intf->add_dev(dev,class_intf);
mutex_unlock(&dev->class->p->mutex);
}
done:
put_device(dev);
return error;
SysEntryError:
if (MAJOR(dev->devt))
device_remove_file(dev,&dev_attr_dev);
DevAttrError:
device_pm_remove(dev);
dpm_sysfs_remove(dev);
DPMError:
bus_remove_device(dev);
BusError:
device_remove_attrs(dev);
AttrsError:
device_remove_class_symlinks(dev);
SymlinkError:
device_remove_file(dev,&dev_attr_uevent);
attrError:
kobject_uevent(&dev->kobj,KOBJ_REMOVE);
kobject_del(&dev->kobj);
Error:
cleanup_device_parent(dev);
if (parent)
put_device(parent);
name_error:
kfree(dev->p);
dev->p = NULL;
goto done;
}
在device_add中调用了bus_probe_device,为新找到的设备匹配一个driver。
voidbus_probe_device(struct device *dev)
{
struct bus_type *bus = dev->bus;
struct subsys_interface *sif;
int ret;
if (!bus)
return;
if (bus->p->drivers_autoprobe) {
ret = device_attach(dev);
WARN_ON(ret < 0);
}
mutex_lock(&bus->p->mutex);
list_for_each_entry(sif,&bus->p->interfaces, node)
if (sif->add_dev)
sif->add_dev(dev,sif);
mutex_unlock(&bus->p->mutex);
}
匹配过程通过device_attach来实现。
intdevice_attach(struct device *dev)
{
int ret = 0;
device_lock(dev);
if (dev->driver) {
if(klist_node_attached(&dev->p->knode_driver)) {
ret = 1;
goto out_unlock;
}
ret =device_bind_driver(dev);
if (ret == 0)
ret = 1;
else {
dev->driver =NULL;
ret = 0;
}
} else {
ret = bus_for_each_drv(dev->bus, NULL, dev,__device_attach);
pm_request_idle(dev);
}
out_unlock:
device_unlock(dev);
return ret;
}
在match过程中会执行ret = bus_for_each_drv(dev->bus, NULL, dev, __device_attach);,用bus上的每个drv来匹配dev。具体的匹配函数由__device_attach来实现。
staticint __device_attach(struct device_driver *drv, void *data)
{
struct device *dev = data;
if (!driver_match_device(drv, dev))
return 0;
return driver_probe_device(drv, dev);
}
如果匹配成功,则执行driver_probe_device函数。具体匹配过程自己看driver_match_device.
intdriver_probe_device(struct device_driver *drv, struct device *dev)
{
int ret = 0;
if (!device_is_registered(dev))
return -ENODEV;
pr_debug(“bus: ‘%s’: %s: matcheddevice %s with driver %s\n”,
drv->bus->name, __func__, dev_name(dev),drv->name);
pm_runtime_barrier(dev);
ret = really_probe(dev, drv);
pm_request_idle(dev);
return ret;
}
在driver_probe_device中调用了really_probe;
staticint really_probe(struct device *dev, struct device_driver *drv)
{
int ret = 0;
int local_trigger_count =atomic_read(&deferred_trigger_count);
atomic_inc(&probe_count);
pr_debug(“bus: ‘%s’: %s: probingdriver %s with device %s\n”,
drv->bus->name, __func__, drv->name,dev_name(dev));
WARN_ON(!list_empty(&dev->devres_head));
dev->driver = drv;
/* If using pinctrl, bind pins nowbefore probing */
ret = pinctrl_bind_pins(dev);
if (ret)
goto probe_failed;
if (driver_sysfs_add(dev)) {
printk(KERN_ERR “%s:driver_sysfs_add(%s) failed\n”,
__func__,dev_name(dev));
goto probe_failed;
}
if (dev->bus->probe) {
ret = dev->bus->probe(dev);
if (ret)
goto probe_failed;
} else if (drv->probe) {
ret = drv->probe(dev);
if (ret)
goto probe_failed;
}
driver_bound(dev);
ret = 1;
pr_debug(“bus: ‘%s’: %s: bounddevice %s to driver %s\n”,
drv->bus->name, __func__, dev_name(dev),drv->name);
goto done;
probe_failed:
devres_release_all(dev);
driver_sysfs_remove(dev);
dev->driver = NULL;
dev_set_drvdata(dev, NULL);
if (ret == -EPROBE_DEFER) {
/* Driver requested deferredprobing */
dev_info(dev, “Driver %srequests probe deferral\n”, drv->name);
driver_deferred_probe_add(dev);
/* Did a trigger occur whileprobing? Need to re-trigger if yes */
if (local_trigger_count !=atomic_read(&deferred_trigger_count))
driver_deferred_probe_trigger();
} else if (ret != -ENODEV &&ret != -ENXIO) {
/* driver matched but theprobe failed */
printk(KERN_WARNING
“%s: probe of %s failed with error%d\n”,
drv->name, dev_name(dev), ret);
} else {
pr_debug(“%s: probe of%s rejects match %d\n”,
drv->name, dev_name(dev), ret);
}
/*
* Ignore errors returned by ->probe so thatthe next driver can try
* its luck.
*/
ret = 0;
done:
atomic_dec(&probe_count);
wake_up(&probe_waitqueue);
return ret;
}
在really_probe中会根据bus->probe是否为真来判断是否调用drv->probe;无论调用哪个probe,都会执行到sd_probe函数。
staticstruct scsi_driver sd_template = {
.gendrv = {
.name = “sd”,
.owner = THIS_MODULE,
.probe = sd_probe,
.remove = sd_remove,
.shutdown = sd_shutdown,
.pm = &sd_pm_ops,
},
.rescan =sd_rescan,
.init_command = sd_init_command,
.uninit_command = sd_uninit_command,
.done =sd_done,
.eh_action = sd_eh_action,
};
这是由于在sd.c中注册scsi mid layer 的driver。
module_init(init_sd);
staticint __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)
continue;
majors++;
blk_register_region(sd_major(i),SD_MINORS, NULL,
sd_default_probe, NULL, NULL);
}
if (!majors)
return -ENODEV;
err =class_register(&sd_disk_class);
if (err)
goto err_out;
sd_cdb_cache =kmem_cache_create(“sd_ext_cdb”, SD_EXT_CDB_SIZE,
0, 0, NULL);
if (!sd_cdb_cache) {
printk(KERN_ERR “sd:can’t init extended cdb cache\n”);
err = -ENOMEM;
goto err_out_class;
}
sd_cdb_pool =mempool_create_slab_pool(SD_MEMPOOL_SIZE, sd_cdb_cache);
if (!sd_cdb_pool) {
printk(KERN_ERR “sd:can’t init extended cdb pool\n”);
err = -ENOMEM;
goto err_out_cache;
}
err = scsi_register_driver(&sd_template.gendrv);
if (err)
goto err_out_driver;
return 0;
err_out_driver:
mempool_destroy(sd_cdb_pool);
err_out_cache:
kmem_cache_destroy(sd_cdb_cache);
err_out_class:
class_unregister(&sd_disk_class);
err_out:
for (i = 0; i < SD_MAJORS; i++)
unregister_blkdev(sd_major(i),”sd”);
return err;
}
在sd_init中注册了scsi_reigster_driver(&sd_template_gendrv);这样gendrv就加入到bus上了,当在上面添加设备时,轮询drv时,就找到gendrv与scsi_device匹配,从而调用到sd_probe.
staticint sd_probe(struct device *dev)
{
struct scsi_device *sdp =to_scsi_device(dev);
struct scsi_disk *sdkp;
struct gendisk *gd;
int index;
int error;
scsi_autopm_get_device(sdp);
error = -ENODEV;
if (sdp->type != TYPE_DISK&& sdp->type != TYPE_MOD && sdp->type != TYPE_RBC)
goto out;
SCSI_LOG_HLQUEUE(3,sdev_printk(KERN_INFO, sdp,
“sd_probe\n”));
error = -ENOMEM;
sdkp = kzalloc(sizeof(*sdkp),GFP_KERNEL);
if (!sdkp)
goto out;
gd = alloc_disk(SD_MINORS);
if (!gd)
goto out_free;
do {
if(!ida_pre_get(&sd_index_ida, GFP_KERNEL))
goto out_put;
spin_lock(&sd_index_lock);
error =ida_get_new(&sd_index_ida, &index);
spin_unlock(&sd_index_lock);
} while (error == -EAGAIN);
if (error) {
sdev_printk(KERN_WARNING,sdp, “sd_probe: memory exhausted.\n”);
goto out_put;
}
error =sd_format_disk_name(“sd”, index, gd->disk_name, DISK_NAME_LEN);//得到gd->disk_name by wyf
if (error) {
sdev_printk(KERN_WARNING,sdp, “SCSI disk (sd) name length exceeded.\n”);
goto out_free_index;
}
sdkp->device = sdp;
sdkp->driver = &sd_template;
sdkp->disk = gd;
sdkp->index = index;
atomic_set(&sdkp->openers, 0);
atomic_set(&sdkp->device->ioerr_cnt,0);
if(!sdp->request_queue->rq_timeout) {
if (sdp->type != TYPE_MOD)
blk_queue_rq_timeout(sdp->request_queue,SD_TIMEOUT);
else
blk_queue_rq_timeout(sdp->request_queue,
SD_MOD_TIMEOUT);
}
device_initialize(&sdkp->dev);
sdkp->dev.parent = dev;
sdkp->dev.class =&sd_disk_class;
dev_set_name(&sdkp->dev,”%s”, dev_name(dev));
if (device_add(&sdkp->dev))
goto out_free_index;
get_device(dev);
dev_set_drvdata(dev, sdkp);
get_device(&sdkp->dev); /* prevent release beforeasync_schedule */
async_schedule_domain(sd_probe_async, sdkp,&scsi_sd_probe_domain);
return 0;
out_free_index:
spin_lock(&sd_index_lock);
ida_remove(&sd_index_ida, index);
spin_unlock(&sd_index_lock);
out_put:
put_disk(gd);
out_free:
kfree(sdkp);
out:
scsi_autopm_put_device(sdp);
return error;
}
在sd_probe中先alloc_disk,然后通过sd_probe_async调用add_disk来注册一个scsi-device。
voidadd_disk(struct gendisk *disk)
{
struct backing_dev_info *bdi;
dev_t devt;
int retval;
/* minors == 0 indicates to use extdevt from part0 and should
* be accompanied with EXT_DEVT flag. Make sure all
* parameters make sense.
*/
WARN_ON(disk->minors &&!(disk->major || disk->first_minor));
WARN_ON(!disk->minors &&!(disk->flags & GENHD_FL_EXT_DEVT));
disk->flags |= GENHD_FL_UP;
retval =blk_alloc_devt(&disk->part0, &devt);
if (retval) {
WARN_ON(1);
return;
}
disk_to_dev(disk)->devt = devt;
/* ->major and ->first_minoraren’t supposed to be
* dereferenced from here on, but set them justin case.
*/
disk->major = MAJOR(devt);
disk->first_minor = MINOR(devt);
disk_alloc_events(disk);
/* Register BDI before referencing itfrom bdev */
bdi =&disk->queue->backing_dev_info;
bdi_register_dev(bdi, disk_devt(disk));
blk_register_region(disk_devt(disk),disk->minors, NULL,
exact_match, exact_lock, disk);
register_disk(disk);
blk_register_queue(disk);
/*
* Take an extra ref on queue which will be puton disk_release()
* so that it sticks around as long as @disk isthere.
*/
WARN_ON_ONCE(!blk_get_queue(disk->queue));
retval =sysfs_create_link(&disk_to_dev(disk)->kobj, &bdi->dev->kobj,
“bdi”);
WARN_ON(retval);
disk_add_events(disk);
}
这里面重要的函数有这几个,首先分配一个diskevent(通过disk_alloc_event),注册一个disk(register_disk(disk)),注册一个queue(blk_reigster_queue(disk))、添加一个事件(disk_add_events(disk))。其中事件机制是linux通知上层的一种机制,这里不详细阐述。下面来看看如何注册一个disk的。
staticvoid register_disk(struct gendisk *disk)
{
struct device *ddev = disk_to_dev(disk);
struct block_device *bdev;
struct disk_part_iter piter;
struct hd_struct *part;
int err;
ddev->parent =disk->driverfs_dev;
dev_set_name(ddev, “%s”,disk->disk_name);
/* delay uevents, until we scannedpartition table */
dev_set_uevent_suppress(ddev, 1);
if (device_add(ddev))
return;
if (!sysfs_deprecated) {
err =sysfs_create_link(block_depr, &ddev->kobj,
kobject_name(&ddev->kobj));
if (err) {
device_del(ddev);
return;
}
}
/*
* avoid probable deadlock caused by allocatingmemory with
* GFP_KERNEL in runtime_resume callback of itsall ancestor
* devices
*/
pm_runtime_set_memalloc_noio(ddev,true);
disk->part0.holder_dir =kobject_create_and_add(“holders”, &ddev->kobj);
disk->slave_dir =kobject_create_and_add(“slaves”, &ddev->kobj);
/* No minors to use for partitions */
if (!disk_part_scan_enabled(disk))
goto exit;
/* No such device (e.g., media werejust removed) */
if (!get_capacity(disk))
goto exit;
bdev = bdget_disk(disk, 0);
if (!bdev)
goto exit;
bdev->bd_invalidated = 1;
err = blkdev_get(bdev, FMODE_READ,NULL);
if (err < 0)
goto exit;
blkdev_put(bdev, FMODE_READ);
exit:
/* announce disk after possiblepartitions are created */
dev_set_uevent_suppress(ddev, 0);
kobject_uevent(&ddev->kobj,KOBJ_ADD);
/* announce possible partitions */
disk_part_iter_init(&piter, disk,0);
while ((part =disk_part_iter_next(&piter)))
kobject_uevent(&part_to_dev(part)->kobj,KOBJ_ADD);
disk_part_iter_exit(&piter);
}
我们看到在register_disk中得到一个block_device块设备。这样scsi层和block_dev层就这样联系在一起了。看看如何得到struct block_device *bdev的。
structblock_device *bdget_disk(struct gendisk *disk, int partno)
{
struct hd_struct *part;
struct block_device *bdev = NULL;
part = disk_get_part(disk, partno);
if (part)
bdev = bdget(part_devt(part));
disk_put_part(part);
return bdev;
}
调用了bdget(part_devt(part);
structblock_device *bdget(dev_t dev)
{
struct block_device *bdev;
struct inode *inode;
inode = iget5_locked(blockdev_superblock, hash(dev),
bdev_test,bdev_set, &dev);
if (!inode)
return NULL;
bdev = &BDEV_I(inode)->bdev;
if (inode->i_state & I_NEW) {
bdev->bd_contains = NULL;
bdev->bd_super = NULL;
bdev->bd_inode = inode;
bdev->bd_block_size = (1<< inode->i_blkbits);
bdev->bd_part_count = 0;
bdev->bd_invalidated = 0;
inode->i_mode = S_IFBLK;
inode->i_rdev = dev;
inode->i_bdev = bdev;
inode->i_data.a_ops =&def_blk_aops;
mapping_set_gfp_mask(&inode->i_data,GFP_USER);
inode->i_data.backing_dev_info= &default_backing_dev_info;
spin_lock(&bdev_lock);
list_add(&bdev->bd_list,&all_bdevs);
spin_unlock(&bdev_lock);
unlock_new_inode(inode);
}
return bdev;
}
这里就调用了上层的东东了,居然分配了一个inode,然后通过这个inode得到bdev啦。Tips:inode中有一个block_device指针,同样也存在char_device指针,和网络设备指针。具体参看inode结构体定义。
看看这个inode是怎么创建的把。
structinode *iget5_locked(struct super_block *sb, unsigned long hashval,
int (*test)(struct inode *,void *),
int (*set)(struct inode *,void *), void *data)
{
struct hlist_head *head =inode_hashtable + hash(sb, hashval);
struct inode *inode;
spin_lock(&inode_hash_lock);
inode = find_inode(sb, head, test,data);
spin_unlock(&inode_hash_lock);
if (inode) {
wait_on_inode(inode);
return inode;
}
inode = alloc_inode(sb);
if (inode) {
struct inode *old;
spin_lock(&inode_hash_lock);
/* We released the lock, so..*/
old = find_inode(sb, head,test, data);
if (!old) {
if (set(inode,data))
gotoset_failed;
spin_lock(&inode->i_lock);
inode->i_state =I_NEW;
hlist_add_head(&inode->i_hash,head);
spin_unlock(&inode->i_lock);
inode_sb_list_add(inode);
spin_unlock(&inode_hash_lock);
/* Return the lockedinode with I_NEW set, the
* caller is responsible for filling in thecontents
*/
return inode;
}
/*
* Uhhuh, somebody else created the same inodeunder
* us. Use the old inode instead of the one wejust
* allocated.
*/
spin_unlock(&inode_hash_lock);
destroy_inode(inode);
inode = old;
wait_on_inode(inode);
}
return inode;
set_failed:
spin_unlock(&inode_hash_lock);
destroy_inode(inode);
return NULL;
}
通过调用alloc_inode分配了一个inode。
staticstruct inode *alloc_inode(struct super_block *sb)
{
struct inode *inode;
if (sb->s_op->alloc_inode)
inode = sb->s_op->alloc_inode(sb);
else
inode = kmem_cache_alloc(inode_cachep,GFP_KERNEL);
if (!inode)
return NULL;
if (unlikely(inode_init_always(sb,inode))) {
if(inode->i_sb->s_op->destroy_inode)
inode->i_sb->s_op->destroy_inode(inode);
else
kmem_cache_free(inode_cachep,inode);
return NULL;
}
return inode;
}
这里分配有两个路径,到底走哪个呢?当然走上面的inode啦,如果走下面的inode,通过在inode_cachep中获取一个inode,那么在struct block_device *bdget(dev_t dev)函数中通过bdev =&BDEV_I(inode)->bdev;就得不到bdev啦。那么这个sb->s_op->alloc_inode(sb)哪里来的呢? 不要把它当作傻逼了呀,这个就是我们的超级块super block。
一步步返回我们看看sb是从哪里传进来的。
这个sb居然是blockdev_superblock这个东东。
好,既然知道了blockdev_superblock,那么看看在哪里创建的把。
搜索了以下,他就是在bdev_cache_init中创建的。
void__init bdev_cache_init(void)
{
int err;
static struct vfsmount *bd_mnt;
bdev_cachep =kmem_cache_create(“bdev_cache”, sizeof(struct bdev_inode),
0,(SLAB_HWCACHE_ALIGN|SLAB_RECLAIM_ACCOUNT|
SLAB_MEM_SPREAD|SLAB_PANIC),
init_once);
err =register_filesystem(&bd_type);
if (err)
panic(“Cannot registerbdev pseudo-fs”);
bd_mnt = kern_mount(&bd_type);
if (IS_ERR(bd_mnt))
panic(“Cannot createbdev pseudo-fs”);
blockdev_superblock = bd_mnt->mnt_sb; /* For writeback */
}
这里有个bdev_cachep东东,这是内核向开辟了一块缓存,然后我们用到的时候,直接从这里面拿,速度较快。看看究竟是什么东东在这里面取呢?
staticstruct inode *bdev_alloc_inode(struct super_block *sb)
{
struct bdev_inode *ei = kmem_cache_alloc(bdev_cachep,GFP_KERNEL);
if (!ei)
return NULL;
return &ei->vfs_inode;
}
发现我们要的bdev_inode从这里面取到的。还记得我们上面讲到的得到block_device的地方吗?有个bdev =&BDEV_I(inode)->bdev;BDEV_I(inode)先返回的是bdev_inode指针。这个bdev_inode就是在这里分配了。
再往上追,发现bdev_alloc_inode赋给了它。
staticconst struct super_operations bdev_sops = {
.statfs = simple_statfs,
.alloc_inode = bdev_alloc_inode,
.destroy_inode = bdev_destroy_inode,
.drop_inode = generic_delete_inode,
.evict_inode = bdev_evict_inode,
};
好吧,看看那bdev_sops是怎么用的呢?
结果发现,在这里被调用。
回顾__initbdev_cache_init函数,里面先注册了bd_type,register_filesystem(&bd_type),然后调用了kern_mount(&bd_type);
#definekern_mount(type) kern_mount_data(type,NULL)
structvfsmount *kern_mount_data(structfile_system_type *type, void *data)
{
struct vfsmount *mnt;
mnt = vfs_kern_mount(type, MS_KERNMOUNT, type->name, data);
if (!IS_ERR(mnt)) {
/*
* it is a longterm mount, don’t release mntuntil
* we unmount before file sys is unregistered
*/
real_mount(mnt)->mnt_ns =MNT_NS_INTERNAL;
}
return mnt;
}
structvfsmount *
vfs_kern_mount(struct file_system_type *type, int flags, const char *name, void*data)
{
struct mount *mnt;
struct dentry *root;
if (!type)
return ERR_PTR(-ENODEV);
mnt = alloc_vfsmnt(name);
if (!mnt)
return ERR_PTR(-ENOMEM);
if (flags & MS_KERNMOUNT)
mnt->mnt.mnt_flags =MNT_INTERNAL;
root = mount_fs(type, flags, name, data);
if (IS_ERR(root)) {
mnt_free_id(mnt);
free_vfsmnt(mnt);
return ERR_CAST(root);
}
mnt->mnt.mnt_root = root;
mnt->mnt.mnt_sb = root->d_sb;
mnt->mnt_mountpoint =mnt->mnt.mnt_root;
mnt->mnt_parent = mnt;
lock_mount_hash();
list_add_tail(&mnt->mnt_instance,&root->d_sb->s_mounts);
unlock_mount_hash();
return &mnt->mnt;
}
structdentry *
mount_fs(struct file_system_type *type, int flags, const char *name, void*data)
{
struct dentry *root;
struct super_block *sb;
char *secdata = NULL;
int error = -ENOMEM;
if (data && !(type->fs_flags& FS_BINARY_MOUNTDATA)) {
secdata = alloc_secdata();
if (!secdata)
goto out;
error = security_sb_copy_data(data,secdata);
if (error)
gotoout_free_secdata;
}
root = type->mount(type, flags, name, data);
if (IS_ERR(root)) {
error = PTR_ERR(root);
goto out_free_secdata;
}
sb = root->d_sb;
BUG_ON(!sb);
WARN_ON(!sb->s_bdi);
WARN_ON(sb->s_bdi ==&default_backing_dev_info);
sb->s_flags |= MS_BORN;
error = security_sb_kern_mount(sb,flags, secdata);
if (error)
goto out_sb;
/*
* filesystems should never set s_maxbyteslarger than MAX_LFS_FILESIZE
* but s_maxbytes was an unsigned long long formany releases. Throw
* this warning for a little while to try andcatch filesystems that
* violate this rule.
*/
WARN((sb->s_maxbytes < 0),”%s set sb->s_maxbytes to “
“negative value(%lld)\n”, type->name, sb->s_maxbytes);
up_write(&sb->s_umount);
free_secdata(secdata);
return root;
out_sb:
dput(root);
deactivate_locked_super(sb);
out_free_secdata:
free_secdata(secdata);
out:
return ERR_PTR(error);
}
这里的type->mount(type, flags, name, data);中的mount就是bd_mount,传进来的bd_type中赋值:
staticstruct file_system_type bd_type = {
.name =”bdev”,
.mount =bd_mount,
.kill_sb = kill_anon_super,
};
staticstruct dentry *bd_mount(struct file_system_type *fs_type,
int flags, const char *dev_name, void*data)
{
return mount_pseudo(fs_type, “bdev:”, &bdev_sops, NULL,BDEVFS_MAGIC);
}
structdentry *mount_pseudo(structfile_system_type *fs_type, char *name,
const struct super_operations *ops,
const struct dentry_operations *dops,unsigned long magic)
{
struct super_block *s;
struct dentry *dentry;
struct inode *root;
struct qstr d_name = QSTR_INIT(name,strlen(name));
s = sget(fs_type, NULL, set_anon_super,MS_NOUSER, NULL);
if (IS_ERR(s))
return ERR_CAST(s);
s->s_maxbytes = MAX_LFS_FILESIZE;
s->s_blocksize = PAGE_SIZE;
s->s_blocksize_bits = PAGE_SHIFT;
s->s_magic = magic;
s->s_op = ops ? ops : &simple_super_operations;
s->s_time_gran = 1;
root = new_inode(s);
if (!root)
goto Enomem;
/*
* since this is the first inode, make itnumber 1. New inodes created
* after this must take care not to collidewith it (by passing
* max_reserved of 1 to iunique).
*/
root->i_ino = 1;
root->i_mode = S_IFDIR | S_IRUSR |S_IWUSR;
root->i_atime = root->i_mtime =root->i_ctime = CURRENT_TIME;
dentry = __d_alloc(s, &d_name);
if (!dentry) {
iput(root);
goto Enomem;
}
d_instantiate(dentry, root);
s->s_root = dentry;
s->s_d_op = dops;
s->s_flags |= MS_ACTIVE;
return dget(s->s_root);
Enomem:
deactivate_locked_super(s);
return ERR_PTR(-ENOMEM);
}
在这里把super_operation bdev_sops的操作赋给了super_block成员的s_op。
到这里我们super_block的super operations有了。
下面来看看bdev_cache_init在哪里调用?
void__init vfs_caches_init(unsigned long mempages)
{
unsigned long reserve;
/* Base hash sizes on available memory,with a reserve equal to
150% of current kernel size */
reserve = min((mempages -nr_free_pages()) * 3/2, mempages - 1);
mempages -= reserve;
names_cachep =kmem_cache_create(“names_cache”, PATH_MAX, 0,
SLAB_HWCACHE_ALIGN|SLAB_PANIC,NULL);
dcache_init();
inode_init();
files_init(mempages);
mnt_init();
bdev_cache_init();
chrdev_init();
}
可以发现在vfs初始化时,调用的bdev_cache_init(),把相关的block_device需要的信息初始化。