VirtioNet独立进程分析

VirtioNet
======在vmm中启动和初始化
vmm/main.cc中main函数启动网络设备时,会优先判断legacy_net是否使能,在legacy_net不使能的情况下会进入VirtioNet初始化分支,下面具体分析。
1.在virtio设备初始化之前先把pci总线初始化起来(virtio设备都挂载pci总线上):
1.1PciBus构造函数:
PciBus bus(&guest, &interrupt_controller)
    对guest_和interrupt_controller_等成员对象进行赋值初始化
1.2PciBus::Init
    将root_complex_的bar0的size设置为16,trap_type设置为MMIO_SYNC
    PciBus::Connect //设置root_complex_设备
        以PAGE_SIZE为对齐单位为每个bar分配mmio空间
        设置command寄存器为io和mem enable,并为设备分配中断号
        SetupBarTraps
            guest->CreateMapping //将bar的地址空间加入到guest的IoMappingList中,并设置访问trap为ZX_GUEST_TRAP_MEM(陷出).同时设置IoHandler为PciBar(实现Read和Write函数)
    guest_->CreateMapping // Setup ECAM trap for a single bus
        TODO:补充PciEcam知识!

2.VirtioNet初始化
2.1VirtioNet构造函数
VirtioNet()
    VirtioComponentDevice //调用父类构造,传入ConfigureQueue和Ready作为virtio后端的hook函数
        VirtioDevice //父类构造
        zx::event::create(0, &event_) //创建event对象
        设置wait_等待对象为event_,并设置triger事件为ZX_USER_SIGNAL_ALL(所有用户信号)
2.2初始化VirtioNet的PCI设备
bus.Connect(net.pci_device(),device_loop.dispatcher(), true) //设置virtionet的PciDevice参数,内部实现同1.2节
2.3启动VirtioNet设备
net.Start
    launcher->CreateComponent //创建virtio_net.cmx进程
    services.ConnectToService(net_.NewRequest()) //连接virtio_net.cmx进程服务端,net_中存储客户端句柄
    PrepStart
        wait_.Begin //启动事件(event_)等待
        this->pci_.bar(kVirtioPciNotifyBar) //设置virtio的pcibar为bar1(notify)--TODO:补充virtio pci bar相关知识
        start_info->trap = {……} //设置notify bar的trap区域
        guest.duplicate //复制guest写和传输权限
        event().duplicate //复制event传输和信号权限
        this->phys_mem_.vmo().duplicate //物理内存的vmo复制
    net_->Start //调用服务端Start函数(见后面分析)
    设置virtio的config_寄存器mac地址为固定的kGuestMacAddress(02:1A:11:00:01:00),将virtionet的link状态设置为UP。

VirtioComponentDevice::OnInterrupt //事件(event_)等待回调函数
    event_.signal(signal->trigger, 0) //清除触发事件的信号
    VirtioDevice::Interrupt //调用父类中断处理函数
        根据actions类型(SET_QUEUE、SET_CONFIG、TRY_INTERRUPT),分别调用VirtioPci设备的add_isr_flags或Interrupt方法
    wait->Begin //重新启动事件等待
    
    
    
======================================================================virtio_net.cmx进程
vmm/device/virtio_net.cc主函数中实例化了VirtioNetImpl类,构造函数如下:
VirtioNetImpl
    DeviceBase //父类构造
    netstack_ = context_.ConnectToEnvironmentService<fuchsia::netstack::Netstack>() //连接协议栈,获取客户端句柄

Start函数:初始化VirtioNet设备的event及trap信息、调用协议栈接口添加虚拟网卡设备,并对rx_stream_和tx_stream_进行初始化。
    PrepStart //利用start_info中保存的信息恢复相关配置,是2.3中的PrepStart的反过程。
    构造config(利用"/dev/class/ethernet/virtio"作为interface,"ethv0"为interface名,设置ip地址为10.0.0.1/24) //TODO:interface是否存在
    AddEthernetDevice
        netstack_->AddEthernetDevice
    SetInterfaceAddress
        netstack_->SetInterfaceAddress
    netstack_->SetInterfaceStatus
    rx_stream_.Init、tx_stream_.Init
        初始化guest_ethernet_和phys_mem_成员
        StreamBase::Init
            queue_.set_phys_mem
            queue_.set_interrupt

NotifyQueue函数:
根据入参queue值,分别调用rx_stream_或tx_stream_的Notify函数
    RxStream::Notify(遍历packet_queue_)
        循环条件:packet_queue_不为空且接收queue的avail ring还有可用desc
            packet_queue_.front() //取出第一个包
            chain_.NextDescriptor //获取chain的下一个desc
            header = static_cast<virtio_net_hdr_t*>(desc_.addr)
            设置header,不用VIRTIO_NET_F_MRG_RXBUF、VIRTIO_NET_F_GUEST_CSUM、VIRTIO_NET_F_GUEST_TSO4, TSO6 and UFO
            memcpy //拷贝pkt到desc指向的buffer
            *chain_.Used() = pkt.length + sizeof(*header) //更新chain的used为desc_.len
            guest_ethernet_->Complete //写tx_fifo_,告诉协议栈包已经发送到guest
          chain_.Return //更新used ring(用avail的idx填充used<同avail指向同一个desc>,且idx+1[,注中断])
    TxStream::Notify
        循环条件:遍历avail ring中所有可用desc
            chain_.NextDescriptor //取出desc
            如果desc的has_next有值,则不符合规范(一次只能发一个包?),continue
            guest_ethernet_->Send
          chain_.Return

ConfigureQueue函数:
根据入参queue值,分别调用rx_stream_或tx_stream_的Configure函数(都调用基类StreamBase的Configure函数)

Ready函数:
配置negotiated_features_,并调用传入的cb。

Receive函数:当协议栈要发送一个包给guest时,GuestEthernet调用其来通知设备。
    rx_stream_.Receive
        packet_queue_.push //包入队列
        RxStream::Notify
================================================涉及的其他类
TODO:async::GuestBellTrapMethod;分析guest notify过程!

class GuestEthernet
继承自fuchsia::hardware::ethernet::Device类,虚拟出网卡的大部分操作,如下:
Send 
    rx_fifo_.read //读rxfifo获取rx entry数
    memcpy(reinterpret_cast<void*>(io_addr_ + entry.offset), data, length) //entry指向最后一个rx entry
    rx_fifo_.write //将entry写入rxfifo,协议栈就可以读取了
OnTxFifoReadable
    tx_fifo_.read //获取txfifo数
    [tx_fifo_wait_.Begin //发送等待?]
    receiver_->Receive //调用VirtioNetImpl::Receive
Complete //写txfifo,完成发送
GetInfo //将固定mac地址02:1A:11:00:01:00返回
GetFifos //系统调用创建rx和tx fifo(收发都是256个),并将fifo的另一端传入调用者的cb
SetIOBuffer //todo,协议栈谁来调用这个接口
    zx::vmar::root_self()->map //映射vmo到本地io_addr_(模拟io基址?)
Start
    tx_fifo_.signal(0, ZX_USER_SIGNAL_0) //发信号给协议栈,bring the link up
    tx_fifo_wait_.xxx //设置ZX_SOCKET_READABLE等待
Stop
ListenStart
ListenStop
SetClientName
GetStatus //返回DEVICE_STATUS_ONLINE
SetPromiscuousMode
ConfigMulticastAddMac
ConfigMulticastDeleteMac
ConfigMulticastSetPromiscuousMode
ConfigMulticastTestFilter
DumpRegisters

class StreamBase
Init
    queue_.set_phys_mem
    queue_.set_interrupt //这里设置guest中断注入hook,在VirtioQueue::Return会调用
Configure
    queue_.Configure
Used
    chain_.Used

class DeviceBase
OnQueueNotify //将设备trap转化为queue notify
    static_cast<T*>(this)->NotifyQueue //调用VirtioNetImpl::NotifyQueue方法
PrepStart //设置event_、phys_mem_、trap_
    trap_.SetTrap
        async_set_guest_bell_trap
            set_guest_bell_trap
Interrupt
    event_.signal //设备中断通过发信号?谁来捕获?!
说明:由于PrepStart中将pcibar的地址设置为了belltrap,当guest访问virtiopci相关寄存器时就会陷出,并会调用DeviceBase::OnQueueNotify方法处理。就像其函数注释的一样,将一个设备trap转化为queue notifications。

struct GuestEthernetReceiver //Interface for GuestEthernet to send a packet to the guest.
struct GuestEthernetReceiver {
  virtual void Receive(uintptr_t addr, size_t length, const eth_fifo_entry_t& entry) = 0;
};

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值