天天看點

virtio net前端

######版本為linux4.13.2#########

virtio_init

    bus_register(&virtio_bus)

static struct bus_type virtio_bus = {

    .name  = "virtio",

    .match = virtio_dev_match,

    .dev_groups = virtio_dev_groups,

    .uevent = virtio_uevent,

    .probe = virtio_dev_probe,

    .remove = virtio_dev_remove,

};

virtio_dev_probe //virtio bus的probe

    virtio_add_status(dev, VIRTIO_CONFIG_S_DRIVER) //發現virtio驅動

        dev->config->set_status(dev, dev->config->get_status(dev) | status);

    virtio_finalize_features

        virtio_add_status(dev, VIRTIO_CONFIG_S_FEATURES_OK) //features ok

    drv->probe(dev) //調用驅動的probe,比如virtio net的 virtnet_probe

    virtio_device_ready //設定VIRTIO_CONFIG_S_DRIVER_OK

    virtio_config_enable //配置使能

-----------

//virtio net前端驅動入口

virtio_net_driver_init

    register_virtio_driver(&virtio_net_driver)

        driver_register

            bus_add_driver

                driver_attach //bus_for_each_dev(drv->bus, NULL, drv, __driver_attach)

                    __driver_attach    

                        driver_probe_device

                            really_probe

                                dev->bus->probe(dev) 或 drv->probe //一般bus的probe裡會調用drv的probe

省略掉device的probe,這裡驅動在注冊時也會調用probe(這裡最好把device的probe分析下,不然始終沒連接配接起來!)

static struct virtio_driver virtio_net_driver = {

    .feature_table = features,

    .feature_table_size = ARRAY_SIZE(features),

    .feature_table_legacy = features_legacy,

    .feature_table_size_legacy = ARRAY_SIZE(features_legacy),

    .driver.name =    KBUILD_MODNAME,

    .driver.owner =    THIS_MODULE,

    .id_table =    id_table,

    .validate =    virtnet_validate,

    .probe =    virtnet_probe,

    .remove =    virtnet_remove,

    .config_changed = virtnet_config_changed,

#ifdef CONFIG_PM_SLEEP

    .freeze =    virtnet_freeze,

    .restore =    virtnet_restore,

#endif

};

virtnet_probe

    dev->netdev_ops = &virtnet_netdev; //此ops回調結構體包含了open、stop、start_xmit、set_mac_address、poll_controller等

    dev->ethtool_ops = &virtnet_ethtool_ops; //ethtool的ops

    INIT_WORK(&vi->config_work, virtnet_config_changed_work); //配置改變工作隊列

    init_vqs

        virtnet_alloc_queues

            INIT_DELAYED_WORK(&vi->refill, refill_work); //後面會用到,用于延時填充avail ring

            netif_napi_add //添加virtnet_poll到rq[i].napi

            netif_tx_napi_add //添加virtnet_poll_tx到sq[i].napi

            sg_init_table 

        virtnet_find_vqs

            callbacks[rxq2vq(i)] = skb_recv_done; //接收回調

            callbacks[txq2vq(i)] = skb_xmit_done; //發送回調

            vi->vdev->config->find_vqs(XXX,callbacks,XXX) //回調指派,中斷中會調用此回調

//填充avail ring

refill_work

//接收回調

skb_recv_done

    virtqueue_napi_schedule

        virtnet_poll

//接收處理napi

virtnet_poll

    virtnet_poll_cleantx

    virtnet_receive

        {virtqueue_get_buf

            virtqueue_get_buf_ctx //從vring used中取出id、len;從desc_state中取出data?(在virtqueue_add 的時候會進行指派)

                detach_buf //釋放相關記憶體及指針,這裡會有num_free++操作(空閑buffer數,已經處理後釋放的buffer數)

        receive_buf

            receive_small[receive_big、receive_mergeable]

                build_skb

            skb_vnet_hdr

            virtio_net_hdr_to_skb

            napi_gro_receive //送給上層處理

        } //循環處理完所有used ring包

        try_fill_recv //條件是:空閑vring數量大于一半;如果失敗會啟動delaywork

try_fill_recv 添加buffer。主要在receiving路徑被調用;也可以在receiving之前的ndo_open中;另一個場景是refill_work(利用napi_disable來關閉receiving)

    {add_recvbuf_mergeable

    add_recvbuf_big

    add_recvbuf_small

        virtqueue_add_inbuf

            virtqueue_add //添加一個buffer到avail ring

    }//循環,每次添加一個,知道把空閑的buffer添加滿!

    virtqueue_kick //notify

        virtqueue_kick_prepare

            vring_need_event

                return (__u16)(new_idx - event_idx - 1) < (__u16)(new_idx - old);//簡化後就是event_idx+1>old;而event_idx是記錄後端處理位置;意思就是後端處理位置超過(加1後超過,其實是等于)了上次添加buffer的位置,就傳回真,需要新添加buffer

            //沒用event的場景看vring.used->flags判斷是否notify

        virtqueue_notify

調用其的函數有:

refill_work //用work隊列延時填

virtnet_receive《- virtnet_poll《-virtnet_alloc_queues(netif_napi_add)《- virtnet_netpoll(napi_schedule)《-virtnet_netdev.ndo_poll_controller《-netpoll_poll_dev(ops->ndo_poll_controller)//《-netpoll_send_skb_on_dev《-netpoll_send_skb《-netpoll_send_udp(貌似跟丢了,反正是在napi中調用)

調用try_fill_recv條件:if (rq->vq->num_free > virtqueue_get_vring_size(rq->vq) / 2);vq->num_free為空閑的vring數量(vring_num-avail_num);即空閑vring數量大于一半時就觸發添加。

virtnet_open

virtnet_restore_up

總結:guest中virtio網卡驅動在napi中調用接收函數中判斷是否應該添加buffer到vring中。當空閑vring數量大于一半時,會調用try_fill_recv來試圖添加buffer,因為還得滿足vring_need_event條件;如果try_fill_recv失敗會利用schedule_delayed_work來延時再調用。pci中斷處理程式中會調用,virtio net注冊的回調,進而調用napi來處理接收。

------------------------

//發送處理napi

virtnet_poll_tx

//發送回調

skb_xmit_done

=========================

module_pci_driver(virtio_pci_driver)

    module_driver(__pci_driver, pci_register_driver, pci_unregister_driver) //驅動注冊宏

        pci_register_driver

            __pci_register_driver

                drv->driver.bus = &pci_bus_type;

                driver_register

struct bus_type pci_bus_type = {

    .name        = "pci",

    .match        = pci_bus_match,

    .uevent        = pci_uevent,

    .probe        = pci_device_probe,

    .remove        = pci_device_remove,

    .shutdown    = pci_device_shutdown,

    .dev_groups    = pci_dev_groups,

    .bus_groups    = pci_bus_groups,

    .drv_groups    = pci_drv_groups,

    .pm        = PCI_PM_OPS_PTR,

    .num_vf        = pci_bus_num_vf,

};

pci_device_probe

    __pci_device_probe

        pci_call_probe

            local_pci_probe

                pci_drv->probe //這裡會調用virtio_pci_driver的probe(virtio_pci_probe)

//virtio pci驅動入口

virtio_pci_probe

    virtio_pci_legacy_probe

        vp_dev->ioaddr = pci_iomap(pci_dev, 0, 0) //通過device映射io位址

        vp_dev->isr = vp_dev->ioaddr + VIRTIO_PCI_ISR //擷取isr中斷寄存器位址

        vp_dev->vdev.config = &virtio_pci_config_ops //包含vp的set、get、vp_find_vqs等

        vp_dev->config_vector = vp_config_vector

        vp_dev->setup_vq = setup_vq

    [virtio_pci_modern_probe]

    register_virtio_device

        device_register

vp_find_vqs //config->find_vqs

    vp_find_vqs_msix //軟中斷?

        vp_request_msix_vectors

            request_irq(XXX,vp_config_changed,XXX) //pci配置改變中斷

            request_irq(XXX,vp_vring_interrupt,XXX) //pci硬體中斷?

        vp_setup_vq

            vp_dev->setup_vq

        request_irq(XXX,vring_interrupt,XXX) //pci硬體中斷?

    [vp_find_vqs_intx] //正常中斷,就一個

        request_irq(XXX,vp_interrupt,XXX)

        vp_setup_vq

            vp_dev->setup_vq

vp_config_vector //設定VIRTIO_MSI_CONFIG_VECTOR寄存器,用來做msi軟中斷?

setup_vq

    vring_create_virtqueue

        vring_init

        __vring_new_virtqueue

vring_interrupt

    more_used //

    vq->vq.callback //調用virtqueue的接受或發送回調

vp_config_changed

    virtio_config_changed

        __virtio_config_changed

            drv->config_changed

vp_vring_interrupt

    vring_interrupt

vp_interrupt

    ioread8(vp_dev->isr) //讀清isr

    vp_config_changed

    vp_vring_interrupt