scsi底层设备注册——如何一步步注册到block层

首先,让我们先进入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需要的信息初始化。

时间: 2024-08-23 05:50:31

scsi底层设备注册——如何一步步注册到block层的相关文章

注册送白菜网站-注册送白菜-php教程

注册送白菜网站-注册送白菜-php教程注册送白菜网站-注册送白菜-php教程注册送白菜网站-注册送白菜-php教程 注册送白菜网站-注册送白菜-php教程注册送白菜网站-注册送白菜-php教程注册送白菜网站-注册送白菜-php教程 注册送白菜网站-注册送白菜-php教程注册送白菜网站-注册送白菜-php教程注册送白菜网站-注册送白菜-php教程 注册送白菜网站-注册送白菜-php教程注册送白菜网站-注册送白菜-php教程注册送白菜网站-注册送白菜-php教程 注册送白菜网站-注册送白菜-php教

Facebook注册机_Facebook账号注册_Csharp代码示例_.Net代码_VS2013

[Facebook注册机_Facebook账号注册_Csharp代码示例_.Net代码] 朋友叫了很久帮忙写,无奈今天花时间研究下.... 下面只是一个简单示例, 里面我写有一些类,对于写注册机之类的,或许有帮助哟!! ====================================== 注册是成功了,可登录发现跳出手机验证.... 部分代码: using System; using System.Collections.Generic; using System.ComponentMo

类型:.net;问题:iis注册;结果:.net4.0注册到IIS ,重新注册IIS ,iis注册

.net4.0注册到IIS ,重新注册IIS ,iis注册 IIS和.netfw4.0安装顺序是从前到后,如果不小心颠倒了,无所谓. 打开程序-运行-cmd:输入一下命令重新注册IIS C:\WINDOWS\Microsoft.NET\Framework\v4.0.30319\aspnet_regiis.exe -i 一.找到C:\WINNT\MICROSOFT.NET\FRAMEWORK\V版本号\ASPNET_REGIIS.EXE 直接双击运行 即可,好像通过DOS命令不行. 二.开始- 程

EJR平台技术解析 EJR众创平台注册 EJR互助社区注册

EJR众创注册,EJR互助注册,EJR众创平台咨询微信:duoshouvip EJR平台技术解析EJR众创平台信守承诺,绝不辜负会员对平台的信任与支持,透明清晰的模式,为会员提供最优秀的平台.EJR众创平台是一个人人都能参与,人人都有回报的平台.平台坚守“诚信.互助.友善.共富”共建我们自己的体系.高防服务器平台采用多台高防服务器,若攻击致使其中一台服务器无法正常访问,将在短时间内自动启用第二台服务器.顶尖CDN节点 CDN节点分布在全球以及各省份,分散攻击压力,提升会员访问速度.数据库实时备份

注册即送白菜-注册即送白菜-注册即送白菜

注册即送白菜 注册即送白菜 注册即送白菜 注册即送白菜 注册即送白菜 注册即送白菜 注册即送白菜 注册即送白菜 注册即送白菜 注册即送白菜 注册即送白菜 注册即送白菜 注册即送白菜 注册即送白菜 注册即送白菜 注册即送白菜 注册即送白菜 注册即送白菜 注册即送白菜 注册即送白菜 注册即送白菜 注册即送白菜 注册即送白菜 注册即送白菜 但是看见管理机构离开世界各地司法局注册即送白菜死了kg就死饿就够了看电视解放路开始大概几点上课国发几个is刻录机施工进度森林砍伐kg看电视上课了的感觉打算离开家思考

RedGate .NET Reflector注册问题(反注册)

Reflector分为桌面版和VS集成版本,当我们使用注册机注册的时候如果注册了Standvard版本,那么我们的VS就不能集成查看,也不能Debug,那么这 显然不是我们想要的,我们会选择重新注册,但是就遇到无法再次注册,即使卸载也无法注册: 打开这个地址 C:\Users\XXX\AppData\Local\Red Gate\Licenses在这个目录下面有我们注册的文件,删除即可

Atitit.操作注册表 树形数据库 注册表的历史 java版本类库总结

Atitit.操作注册表 树形数据库 注册表的历史 java版本类库总结 1. 注册表是树形数据库 1 2. 注册表的由来 1 3. Java  操作注册表 2 3.1. 使用Preferences API (限定访问路径了) 2 3.2. 使用JNI 3 3.3. Jregistrykey 推荐 4 3.4. Jregistry 4 4. org.openqa.selenium.os.WindowsUtils 4 4.1. 以及进程管理 4 1. 注册表是树形数据库 树形数据库,但不提供类似S

ATL使用.rgs注册脚本文件操作注册表注册Com组件

1.      ATL注册组件 1.1     创建注册脚本 注册脚本通过操作 系统注册表完成Com服务的注册,通过数字形式而非代码API的形式完成,这种形式显得更加简单有效,因为它只需要几行数字代码就可以将一个Key添加到注册表中. 使用ATL向导时,会自动的生成一个后缀为.rgs的注册脚本文件,ATL在服务安装时,会自动的调用脚本文件,实现对注册表的修改,完成Com服务的注册. 1.1.1       基本术语 符号释义 符号 解释 ::= 相等 | 或 X+ 一个或多个Xs [X] X是可

Linux btrfsfilesystem之管理底层设备

文件系统的底层设备肯定是需要增加和删除的. 增加磁盘/分区 现在的状态 现在来添加设备 这个时候需要对文件系统进行重新数据分布. 默认是对所有数据进行重新分布 这里做的仅仅是对数据的重新分布 之前的可用空间是3G,现在的可用空间时1.5G 现在我们来删除底层硬盘/分区 将数据以raid0的方式重新分布 可用空间由1变2. 原文地址:http://blog.51cto.com/yueyue207/2082744