qemu - 新聞資訊 - 雲南小程序開發|雲南軟件開發|雲南網站建設-昆明融晨信息技術有限公司

159-8711-8523

雲南網建設/小程序開發/軟件開發

知識

不(bù)管是(shì)網站,軟件還是(shì)小程序,都要(yào / yāo)直接或間接能爲(wéi / wèi)您産生價值,我們在(zài)追求其視覺表現的(de)同時(shí),更側重于(yú)功能的(de)便捷,營銷的(de)便利,運營的(de)高效,讓網站成爲(wéi / wèi)營銷工具,讓軟件能切實提升企業内部管理水平和(hé / huò)效率。優秀的(de)程序爲(wéi / wèi)後期升級提供便捷的(de)支持!

您當前位置>首頁 » 新聞資訊 » 技術分享 >

qemu

發表時(shí)間:2020-11-13

發布人(rén):融晨科技

浏覽次數:64

Linux客戶機 virtio設備初始化
virtio設備物理上(shàng)連接在(zài)pci物理總線上(shàng),邏輯上(shàng)連接在(zài)virtio虛拟總線。做爲(wéi / wèi)pci設備便于(yú)資源分配與配置,邏輯設備模型中,便于(yú)管理與組織。
1.qemu-kvm提供的(de)virtio pci設備
virtio-blk(硬盤),virtio-net(網絡),virtio-balloon(氣球)等pci設備,這(zhè)些設備連接在(zài)pci總線上(shàng)。代碼位于(yú)qemu: hw/virtio-pci.c
static PCIDeviceInfo virtio_info[] = {
    {
        .qdev.name = "virtio-blk-pci",
    },{
        .qdev.name  = "virtio-net-pci",
    },{
        .qdev.name = "virtio-serial-pci",
    },{
        .qdev.name = "virtio-balloon-pci",
       },
}
static void virtio_pci_register_devices(void)
{
    pci_qdev_register_many(virtio_info);

2.客戶機PCI設備進行枚舉和(hé / huò)資源分配
當Linux客戶機系統啓動時(shí),對PCI設備進行枚舉和(hé / huò)資源分配(配置PCI的(de)配置空間),通常由BIOS完成。不(bù)過對Linux系統提供方式,一種由BIOS實現,另一種自己實現枚舉和(hé / huò)資源分配功能。代碼位于(yú)kernel:arch/x86/pci/init.c
static __init int pci_arch_init(void)
{
#ifdef CONFIG_PCI_DIRECT
        int type = 0;
        type = pci_direct_probe();
#endif
#endif
#ifdef CONFIG_PCI_BIOS
        pci_pcbios_init();
#endif
}
真正設備枚舉和(hé / huò)資源分配由這(zhè)裏開始
static int __init pci_legacy_init(void)
{
        printk("PCI: Probing PCI hardware\n");
        pci_root_bus = pcibios_scan_root(0);
        if (pci_root_bus)
                pci_bus_add_devices(pci_root_bus);
        return 0;
}
pcibios_scan_root()---->pci_scan_bus_parented()---->pci_scan_child_bus()--->pci_scan_slot()--->pci_scan_single_device()----->pci_device_add()
将PCI總線上(shàng)的(de)設備添加到(dào)鏈表
void pci_device_add(struct pci_dev *dev, struct pci_bus *bus)
{
        /*
         * Add the device to our list of discovered devices
         * and the bus list for fixup functions, etc.
         */
        down_write(&pci_bus_sem);
        list_add_tail(&dev->bus_list, &bus->devices);
        up_write(&pci_bus_sem);
}
上(shàng)述過程執行完成,在(zài)/sys/devices/pci0000:00目錄下,創建virtio pci設備。并且在(zài)/sys/bus/pci/devices/目錄下,創建相應對于(yú)pci設備的(de)符号連接,同時(shí)在(zài)/sys/bus/pci/drivers/目錄下,創建virtio-pci目錄,目錄下存在(zài)支持設備符号連接文件。
3.virtio總線定義與注冊,virtio總線爲(wéi / wèi)虛拟的(de)總線,目的(de)爲(wéi / wèi)了(le/liǎo)設備管理與組織需要(yào / yāo)。代碼位于(yú):
static struct bus_type virtio_bus = {
        .name  = "virtio",
        .match = virtio_dev_match,
        .dev_attrs = virtio_dev_attrs,
        .uevent = virtio_uevent,
        .probe = virtio_dev_probe,
        .remove = virtio_dev_remove,
};
static int virtio_init(void)
{
        if (bus_register(&virtio_bus) != 0)
                panic("virtio bus registration failed");
        return 0;
}   
上(shàng)述注冊函數調用執行完成,在(zài)/sys/bus/目錄下,創建了(le/liǎo)一個(gè)新的(de)目錄virtio,在(zài)該目錄下同時(shí)創建了(le/liǎo)兩個(gè)文件夾爲(wéi / wèi)devices和(hé / huò)drivers。表示創建virtio總線,總線支持設備與驅動devices和(hé / huò)drivers目錄下。  
4. virtio-pci設備驅動加載
static struct pci_driver virtio_pci_driver = {
        .name           = "virtio-pci",
        .id_table       = virtio_pci_id_table,
        .probe          = virtio_pci_probe,
        .remove         = virtio_pci_remove,
#ifdef CONFIG_PM
        .suspend        = virtio_pci_suspend,
        .resume         = virtio_pci_resume,
#endif
};
static int __init virtio_pci_init(void)
{
        
        virtio_pci_root = root_device_register("virtio-pci");
        err = pci_register_driver(&virtio_pci_driver);
        return err;
}
上(shàng)述注冊函數調用執行完成,在(zài)/sys/bus/pci/drivers和(hé / huò)/sys/devices目錄下創建了(le/liǎo)virtio-pci文件夾
5,virtio總線子(zǐ)設備注冊
上(shàng)面步驟2,對PCI設備進行枚舉和(hé / huò)資源分配中介紹了(le/liǎo),枚舉的(de)設備,已經關聯到(dào)總線鏈表中。對函數調用pci_register_driver(&virtio_pci_driver)就(jiù)是(shì)對鏈表的(de)每一個(gè)pci設備進行探測,該驅動是(shì)否支持該設備,如果支持進,調用驅動probe函數,完成啓用該pci設備,同時(shí)在(zài)virtio總線進行注冊設備。
        bus_for_each_dev(drv->bus, NULL, drv, __driver_attach);
        if (drv->probe) {
                ret = drv->probe(dev);
        }
static int __devinit virtio_pci_probe(struct pci_dev *pci_dev,
                                      const struct pci_device_id *id)
{
        struct virtio_pci_device *vp_dev;
        int err;
        
        /* We only own devices >= 0x1000 and <= 0x103f: leave the rest. */
        if (pci_dev->device < 0x1000 || pci_dev->device > 0x103f)
                return -ENODEV;
        
        /* allocate our structure and fill it out */
        vp_dev = kzalloc(sizeof(struct virtio_pci_device), GFP_KERNEL);
        if (vp_dev == NULL)
                return -ENOMEM;
        vp_dev->vdev.dev.parent = virtio_pci_root;
        vp_dev->vdev.dev.release = virtio_pci_release_dev;
        vp_dev->vdev.config = &virtio_pci_config_ops;
        vp_dev->pci_dev = pci_dev;
        INIT_LIST_HEAD(&vp_dev->virtqueues);
        spin_lock_init(&vp_dev->lock);
        /* Disable MSI/MSIX to bring device to a known good state. */
        pci_msi_off(pci_dev);
        /* enable the device */
        err = pci_enable_device(pci_dev);
        if (err)
                goto out;
        err = pci_request_regions(pci_dev, "virtio-pci");
        if (err)
                goto out_enable_device;
        vp_dev->ioaddr = pci_iomap(pci_dev, 0, 0);
        if (vp_dev->ioaddr == NULL)
                goto out_req_regions;
        pci_set_drvdata(pci_dev, vp_dev);
        /* we use the subsystem vendor/device id as the virtio vendor/device
         * id.  this allows us to use the same PCI vendor/device id for all
         * virtio devices and to identify the particular virtio driver by
         * the subsytem ids */
        vp_dev->vdev.id.vendor = pci_dev->subsystem_vendor;
        vp_dev->vdev.id.device = pci_dev->subsystem_device;
        /* finally register the virtio device */
        err = register_virtio_device(&vp_dev->vdev);
        if (err)
                goto out_set_drvdata;
        return 0;
}
上(shàng)述注冊函數調用執行完成,/sys/devices/virtio-pci/創建相應子(zǐ)設備{virtio1,virtio2,virtio3},同時(shí)在(zài)/sys/bus/virtio/devices下面創建三個(gè)符号連接文件{virtio1,virtio2,virtio3}
6. virtio總線子(zǐ)設備驅動注冊。
當virtio總線進行注冊設備register_virtio_device,将調用virtio總線的(de)probe函數:virtio_dev_probe()。該函數遍曆驅動,找到(dào)支持驅動關聯到(dào)該設備。
   register_virtio_device()--->bus_probe_device()---->device_attach();
   bus_for_each_drv(dev->bus, NULL, dev, __device_attach);
      if (dev->bus->probe) {
                ret = dev->bus->probe(dev);
        } 
static int virtio_dev_probe(struct device *_d)
{       
        int err, i;
        struct virtio_device *dev = container_of(_d,struct virtio_device,dev);
        struct virtio_driver *drv = container_of(dev->dev.driver,
                                                 struct virtio_driver, driver);
        u32 device_features;
        /* We have a driver! */
        add_status(dev, VIRTIO_CONFIG_S_DRIVER);
        /* Figure out what features the device supports. */
        device_features = dev->config->get_features(dev);
        
        /* Features supported by both device and driver into dev->features. */
        memset(dev->features, 0, sizeof(dev->features));
        for (i = 0; i < drv->feature_table_size; i++) {
                unsigned int f = drv->feature_table[i];
                BUG_ON(f >= 32);
                if (device_features & (1 << f))
                        set_bit(f, dev->features);
        }
        /* Transport features always preserved to pass to finalize_features. */
        for (i = VIRTIO_TRANSPORT_F_START; i < VIRTIO_TRANSPORT_F_END; i++)
                if (device_features & (1 << i))
                        set_bit(i, dev->features);
        dev->config->finalize_features(dev);
       err = drv->probe(dev);
        if (err)
                add_status(dev, VIRTIO_CONFIG_S_FAILED);
        else
                add_status(dev, VIRTIO_CONFIG_S_DRIVER_OK);
        return err;
}
//virtio_balloon設備驅動實例
static struct virtio_driver virtio_balloon_driver = {
        .feature_table = features,
        .feature_table_size = ARRAY_SIZE(features),
        .driver.name =  KBUILD_MODNAME,
        .driver.owner = THIS_MODULE,
        .id_table =     id_table,
        .probe =        virtballoon_probe,
        .remove =       __devexit_p(virtballoon_remove),
        .config_changed = virtballoon_changed,
};
static int __init init(void)
{
        return register_virtio_driver(&virtio_balloon_driver);
}      
同時(shí)在(zài)/sys/bus/virtio/drivers下面創建三個(gè)文件{virtio_balloon,virtio_blk,virtio_console},并且與設備發生關聯 
//////////////////////////////
熱插拔事件的(de)産生往往是(shì)由總線驅動級的(de)邏輯處理,所以(yǐ)總線一般提供事件發送函數。例如virtio總線事件函數virtio_uevent。
static int virtio_uevent(struct device *_dv, struct kobj_uevent_env *env)
{
        struct virtio_device *dev = container_of(_dv,struct virtio_device,dev);
        return add_uevent_var(env, "MODALIAS=virtio:d%08Xv%08X",
                              dev->id.device, dev->id.vendor);
}
下面函數工作流程如下:
1.由設備對象往上(shàng)查找,直到(dào)找到(dào)包含kset的(de)kobject(總線包含着kset)
2.判斷kobject對象是(shì)否提供filter,name,uevent函數,如果提供,調用它。
3.分配一個(gè)kobj_uevent_env,并開始填充env環境變量:ACTION,DEVPATH,SUBSYSTEM,SEQNUM,MODALIAS
4.通過netlink發送到(dào)用戶空間
register_virtio_device()---->device_register()---->device_add()---->kobject_uevent()---->kobject_uevent_env()
int kobject_uevent_env(struct kobject *kobj, enum kobject_action action,
                       char *envp_ext[])
{
      /* search the kset we belong to */
        top_kobj = kobj;
        while (!top_kobj->kset && top_kobj->parent)
                top_kobj = top_kobj->parent;
        kset = top_kobj->kset;
        uevent_ops = kset->uevent_ops;
        /* skip the event, if the filter returns zero. */
        if (uevent_ops && uevent_ops->filter)
                if (!uevent_ops->filter(kset, kobj)) {
                        pr_debug("kobject: '%s' (%p): %s: filter function "
                                 "caused the event to drop!\n",
                                 kobject_name(kobj), kobj, __func__);
                        return 0;
                }
        /* originating subsystem */
        if (uevent_ops && uevent_ops->name)
                subsystem = uevent_ops->name(kset, kobj);
        else
                subsystem = kobject_name(&kset->kobj);
       /* environment buffer */
        env = kzalloc(sizeof(struct kobj_uevent_env), GFP_KERNEL);
        if (!env)
                return -ENOMEM;
        /* complete object path */
        devpath = kobject_get_path(kobj, GFP_KERNEL);
        if (!devpath) {
                retval = -ENOENT;
                goto exit;
        }
        /* default keys */
        retval = add_uevent_var(env, "ACTION=%s", action_string);
        if (retval)
                goto exit;
        retval = add_uevent_var(env, "DEVPATH=%s", devpath);
        if (retval)
                goto exit;
        retval = add_uevent_var(env, "SUBSYSTEM=%s", subsystem);
        if (retval)
                goto exit;
        /* keys passed in from the caller */
        if (envp_ext) {
                for (i = 0; envp_ext[i]; i++) {
                        retval = add_uevent_var(env, "%s", envp_ext[i]);
                        if (retval)
                                goto exit;
                }
        }
         /* let the kset specific function add its stuff */
        if (uevent_ops && uevent_ops->uevent) {
                retval = uevent_ops->uevent(kset, kobj, env);
                if (retval) {
                        pr_debug("kobject: '%s' (%p): %s: uevent() returned "
                                 "%d\n", kobject_name(kobj), kobj,
                                 __func__, retval);
                        goto exit;
                }
        }
        /*
         * Mark "add" and "remove" events in the object to ensure proper
         * events to userspace during automatic cleanup. If the object did
         * send an "add" event, "remove" will automatically generated by
         * the core, if not already done by the caller.
         */
        if (action == KOBJ_ADD)
                kobj->state_add_uevent_sent = 1;
        else if (action == KOBJ_REMOVE)
                kobj->state_remove_uevent_sent = 1;
    /* we will send an event, so request a new sequence number */
        spin_lock(&sequence_lock);
        seq = ++uevent_seqnum;
        spin_unlock(&sequence_lock);
        retval = add_uevent_var(env, "SEQNUM=%llu", (unsigned long long)seq);
        if (retval)
                goto exit;
          /* send netlink message */
        if (uevent_sock) {
                struct sk_buff *skb;
                size_t len;
                /* allocate message with the maximum possible size */
                len = strlen(action_string) + strlen(devpath) + 2;
                skb = alloc_skb(len + env->buflen, GFP_KERNEL);
                if (skb) {
                        char *scratch;
                        /* add header */
                        scratch = skb_put(skb, len);
                        sprintf(scratch, "%s@%s", action_string, devpath);
                        /* copy keys to our continuous event payload buffer */
                        for (i = 0; i < env->envp_idx; i++) {
                                len = strlen(env->envp[i]) + 1;
                                scratch = skb_put(skb, len);
                                strcpy(scratch, env->envp[i]);
                        }
                        NETLINK_CB(skb).dst_group = 1;
                        retval = netlink_broadcast(uevent_sock, skb, 0, 1,
                                                   GFP_KERNEL);
                        /* ENOBUFS should be handled in userspace */
                        if (retval == -ENOBUFS)
                                retval = 0;
                } else
                        retval = -ENOMEM;
        }
用戶空間
    當發送信息達到(dào)了(le/liǎo)用戶空間,用戶空間的(de)udevd守護進程,接受到(dào)此信息。在(zài)udev規則文件裏匹配,相應的(de)規則。

相關案例查看更多