fuchsia网卡通用接口层及legacy方式网络虚拟化实现

通用接口层实现所在路径 —— zircon\system\dev\ethernet\ethernet\ethernet.cpp

 

通用接口层也是以驱动形式加载,挂接在发布ZX_PROTOCOL_ETHMAC协议的网卡驱动设备下。

通用接口层驱动加载流程:EthDev0::EthBind-->EthDev0::AddDevice

    构造eth::EthDev0对象;调用AddDevice,获取mac、feature等信息,并添加通用网卡驱动层设备。

通用网卡接口层对外以设备节点方式提供ops方法。设备节点在"/dev/class/ethernet/"路径下,完整路径path如"/dev/class/ethernet/000"

client端通过fdio open上面提到设备节点的path,获取通信句柄,通用接口层这边会对应调用EthDev0::DdkOpen函数。

EthDev0::DdkOpen-->EthDev::AddDevice //添加设备实例并返回调用者,后续devfs操作基于此实例,并最终调用DdkMessage函数。

然后利用FIDL接口,通过IPC与驱动所在的devhost通信。devhost通过devfs处理外部IPC请求。devfs处理流程如下:

DevfsConnection::HandleRpc

    devhost_fidl_handler    --devmgr\devhost\rpc-server.cpp

        conn->dev->MessageOp(msg, txn)

            ops->message(zircon\system\ulib\ddktl\include\ddktl\device.h文件InitOp函数message = Message>,而Message函数在下方定义——直接调用DdkMessage函数)

                EthDev::DdkMessage  --zircon\system\dev\ethernet\ethernet\ethernet.cpp

                    fuchsia_hardware_ethernet_Device_dispatch

                        kOps.Start //以start接口举例

                            MsgStartLocked

                                EthDev::StartLocked

                                    判断tx/rx rings是否配置

                                    TransmitThread //启动线程调用TransmitThread函数

                                        transmit_fifo_.read

                                            transmit_fifo_.wait_one

                                        Send

                                            edev0_->TransmitInfoToNetbuf

                                            edev0_->mac_.QueueTx

                                            edev0_->TransmitEcho

                                            PutTransmitInfo

                                            TransmitFifoWrite

                                    edev0_->mac_.Start

                                    edev0_->list_idle_.erase

                                    edev0_->list_active_.push_bac

                                    receive_fifo_.signal_peer

上面举例中,已经说明了start流程。每个client端实例都会新启一个名为"eth-tx-thread"的线程,用于处理发送请求。完整的client端与通用接口层的通信初始化流程主要包含下面几个步骤:

1.clint端open获取通信句柄(前面已经介绍了)

2.fuchsia_hardware_ethernet_DeviceGetInfo,FIDL接口,获取网卡设备基本信息(mac、feature等)

3.fuchsia_hardware_ethernet_DeviceGetFifos,通用网卡接口层收到此请求后,会调用zx_fifo_create分别为rx和tx通道创建数据包描述符fifo,自己保留fifo一端,另一端传回调用者(client)

4.client端通常会根据传回来的fifo深度(rx_depth、tx_depth;描述符个数,也指数据buffer个数),申请一块vmo数据缓冲区,同时调用fuchsia_hardware_ethernet_DeviceSetIOBuffer,将vmo传递给通用网卡接口层。

5.fuchsia_hardware_ethernet_DeviceSetClientName,设置client名字

6.fuchsia_hardware_ethernet_DeviceStart,start流程。

7.在fifo上等待(如VirtioNetLegacy会调用WaitOnFifos),处理收发数据。

 

下面以VirtioNetLegacy实现为例,说明从virtio后端到网卡驱动的整个收发流程。

 

先看一下VirtioNetLegacy构造函数

1.调用VirtioInprocessDevice初始化父类

2.rx_stream_(phys_mem, dispatcher, rx_queue(), rx_trace_flow_id(),&io_buf_),初始化rx stream

3.tx_stream_(phys_mem, dispatcher, tx_queue(), tx_trace_flow_id(),&io_buf_),初始化tx stream

在rx stream和tx stream初始化构造时,会在对应queue上利用VirtioNetLegacy::Stream::OnQueueReady对queue_wait_进行初始化。queue_wait_是VirtioQueueWaiter类型,用于处理virtio queue的通知事件,当对应queue上有virtio消息通知时会调用回调函数,这里就会调用OnQueueReady函数。

 

发送

VirtioNetLegacy::Stream::OnQueueReady

    ReadPacketInfo //根据virtio desc获取包在vmo(virtio前后端共享vmo)中的offset和len

    io_buf_->vmo().write //将virtio前端发来的数据写到vmo(与网卡通用接口层共享的vmo)中,偏移为io_offset

    fifo_entries_[fifo_num_entries_++] = {……} //填充fifo_entries_(数据包描述符)

    WaitOnFifoWritable //等待fifo可写

当与网卡通用接口层的fifo可写时,会调用回调函数OnFifoWritable

VirtioNetLegacy::Stream::OnFifoWritable

    zx_fifo_write //将数据包描述符写入fifo

    WaitOnQueue //继续在queue上等待

数据包描述符写入到了与网卡通用接口层的fifo中,下面是通用接口层的处理流程:

通用接口层的发送线程在fifo上等到数据后流程如下:

TransmitThread

    transmit_fifo_.read

    Send

        GetTransmitInfo //获取发送info

        edev0_->TransmitInfoToNetbuf

        edev0_->mac_.QueueTx

            OPI2MacDevice::EthmacQueueTx //调用驱动发送函数

        [edev0_->TransmitEcho //回环测试场景]

        [ethmac_request_count_ ++] //驱动阻塞场景

        PutTransmitInfo

        TransmitFifoWrite

 

接收

物理网卡驱动收到数据后,会在中断处理中调用接收处理流程如下:

OPI2MacDevice::ProcRxBuffer

    ethmac_client_.Recv //调用网卡通用接口层的Recv函数

        EthDev0::Recv

            edev.RecvLocked //这里会对list_active_的所有client实例调用接收函数

                receive_fifo_.read //从接收fifo中读取entry

                memcpy //选取最后一个entry,将接收到的数据拷贝到其中

                receive_fifo_.write //将有数据的entry重新写入fifo

VirtioNetLegacy端在检测到fifo可读之后会触发OnFifoReadable函数:

VirtioNetLegacy::Stream::OnFifoReadable

    zx_fifo_read //读fifo到entries

    根据前面读取的entries,循环处理;eth_fifo_entry_t.cookie里存放着desc的head

        ReadPacketInfo /根据cookie中virtio desc head获取包应该放在vmo(virtio前后端共享vmo)中的offset和len

        io_buf_->vmo().read //将vmo(与网卡通用接口层共享的vmo)中数据(偏移为io_offset),读到virtio前端vmo中

        io_buf_->Free //释放io buffer,以便后面的传输可以重复使用

        queue_->Return //更新vring(这里会涉及到中断注入,通知guest)

    wait->Begin //重启事件等待

 

链表管理分析

网卡通用接口层对每个试图连接物理网卡的client都建立了一个EthDev实例,并且用两个链表将它们管理了起来。

list_idle_ //空闲client链表

idle链表在如下几种情况下添加成员:

1.client调用open来打开设备节点(会构造EthDev实例,并将此实例加入idle链表)

2.client调用stop来停止网卡设备(会将client对应的EthDev实例,从active链表中删除,并添加都idle链表)

idle链表在如下几种情况下移除成员:

1.通用设备层DdkUnbind时,会调用DestroyAllEthDev将所有active链表中实例转入idle,在将idle表中所有实例移除

2.client调用close关闭设备实例时

3.client调用start,启动设备时(会将client对应的EthDev实例,从idle链表中删除,并添加都active链表)

 

list_active_  //活跃client链表

active链表成员的添加和移除与idle是对立的,这里不赘述。

 

list_active_链表的关联使用

1.多播场景,会扫描所有active实例

2.set status,会对每个active实例调用receive_fifo_.signal_peer发信号

3.收包场景,会将包发送到所有active实例

4.loopback场景,搜索active链表中处于kStateTransmissionListen状态的实例

5.start、stop、destroy和release场景(实例出入链表)

 

list_idle_链表的关联使用:start、stop、open、destroy和release场景(实例出入链表)

 

可以考虑在通用接口层做netswitch功能。这里的list_active_和list_idle_包含了所有试图连接物理网卡的client端实例。

你可能感兴趣的:(fuchsia)