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需要的信息初始化。



你可能感兴趣的:(设备驱动)