因为数据包的爆发可能比驱动程序处理它们的速度更快,所以 e1000_init() 为 E1000 提供了多个缓冲区,E1000 可以将数据包写入其中。 E1000 要求这些缓冲区由 RAM 中的“描述符”数组描述;每个描述符都包含 RAM 中的一个地址,E1000 可以在其中写入接收到的数据包。 struct rx_desc 描述描述符格式。 e1000_init() 使用 mbufalloc() 将 E1000 的 mbuf 数据包缓冲区分配给 DMA 。还有一个传输环,驱动程序将它希望 E1000 发送的数据包放入其中。 您需要确保每个 mbuf 最终都被释放,但只有在 E1000 完成数据包传输之后(E1000 设置描述符中的 E1000_TXD_STAT_DD 位来指示这一点)。 您将需要锁来应对 xv6 可能从多个进程使用 E1000 的可能性,或者当中断到达时可能在内核线程中使用 E1000。
的笔记记录,会包含一部分代码注释和要求的翻译记录,以及踩过的坑/个人的解决方案 目录: MIT 6.828 操作系统工程 Lab6: 网络驱动程序 练习 1.time_tick 练习 2.浏览英特尔的E1000 实现一个附加函数来初始化 E1000 练习 4. mmio_map_regio 练习 5.执行第 14.5 节(但不是其小节)中描述的初始化步骤。 练习 6. 练习 7. 练习 8. 实现net/output.c 练习 10.按照 14.4 节的过程设置接收队列并配置 E1000 练习 11. 练习 12. 实现net/input.c。 练习 13.send_data。 return time_msec(); } 练习 2.浏览英特尔的E1000软件开发人员手册 现在不要担心细节;只需感受一下文档的结构,您就可以稍后查找内容。 练习 3. 编写一个函数来接收来自 E1000 的数据包,并通过添加系统调用将其暴露给用户空间。确保您处理接收队列为空。
一、背景本实验开发一个网卡驱动程序,其相关代码分布如下:kernel/e1000.c包含E1000的初始化代码以及你需要实现的transmitting与receiving数据包函数;kernel/e1000 _dev包含寄存器定义和E1000开发者文档定义的flag bits;kernel/net.c和kernel/net.h包含简单的IP、UDP、ARP网络协议栈。 这些文件中使用mbuf存放数据包;最后,kernel/pci.c实现了xv6启动时在PCI总线搜索E1000网卡。 考虑到突发流量,E1000在e1000_init中初始化了多个buffer用来写packets,采用tx_desc、tx_desc来描述。 当E1000接收到packet时,首先将packet写入到RX ring中,然后生成一个中断。
如 e1000 0000:02:01.0: PCI INT A -> GSI 19 (level, low) -> IRQ 19 e1000 0000:02:01.0: setting latency timer to 64 e1000 0000:02:01.0: eth0: (PCI:66MHz:32-bit) 00:0c:29:62:d0:69 e1000 0000:02:01.0: eth0:
74ca79ca393c" DEVICE="eth0" ONBOOT="yes" 查看网卡及驱动信息 # dmesg | grep -i eth0 或 # ethtool -i eth0 driver: e1000 supports-eeprom-access: yes supports-register-dump: yes supports-priv-flags: no 卸载网卡驱动 # modprobe -r e1000 或 # rmmod e1000 重新安装网卡驱动 # modprobe e1000 重启网络 systemctl restart network 此时查看网卡信息,ens33已经变为eht0 # ifconfig
就有了e1000/rtl8139为代表的物理网卡软件模拟实现;为了加速Guest和Host之间的数据交换速度,就有了virtio网卡;再virtio的基础上,为了减少qemu进程和host os之间的数据拷贝 2,e1000/rtl8139的实现 Guest在遍历PCI设备的时候,扫描到
的时候,读对应的PCI config,就会发现一个e1000网卡(以e1000为例)。 3,virtio-net e1000/rtl8139类型的网卡,CPU会把很多时间花费在写register,读register上。 后记: 虚拟网卡的能力,从e1000到virtio,再到vhost-net ,越来越强大。当然要比SRIOVS的能力弱一点。 但是虚拟网卡的好处在于和硬件无关,一台普通的x86服务器都可以。numactl-devel pciutils net-tools gcc # 查看网卡信息 [root@backendcloud-centos7 ~]# dmesg |grep -i eth [ 1.231028] e1000 0000:02:01.0 eth0: (PCI:66MHz:32-bit) 00:0c:29:db:15:9e [ 1.231030] e1000 0000:02:01.0 eth0: Intel (R) PRO/1000 Network Connection [ 1.646402] e1000 0000:02:02.0 eth1: (PCI:66MHz:32-bit) 00:0c:29:db :15:a8 [ 1.646406] e1000 0000:02:02.0 eth1: Intel(R) PRO/1000 Network Connection [root@backendcloud-centos7 ===================== 0000:02:01.0 '82545EM Gigabit Ethernet Controller (Copper) 100f' if=ens33 drv=e1000
以Qemu使用TUN/TAP,虚拟内网卡E1000为例介绍。 2. 一句话总结 物理网卡收到发往虚拟机的数据包后,将其转发到对应的TAP设备。 E1000 Qemu中设备模拟有一套框架QOM,在具体介绍E1000之前简要概述下。 tap_receivetap_send-->qemu_send_packet_async-->qemu_send_packet_async_with_flags这里获取到对端网口的队列,如前文所述就是E1000 receive_iove1000_receive_iov通过receive_filter过滤数据包,然后根据数据包的不同进行不同处理然后不停的循环,把数据包拷贝至虚拟机所对应的内存中去最后通过set_ics注入中断给虚拟机 E1000
我本人对网卡驱动兴趣不大,但skb path又无法脱离网卡驱动,这里选择了Intel的e1000网卡驱动:原因有二,一是Intel网卡驱动是内核里面写得最清楚,可读性最高的;二是e1000是Intel里面比较简单的驱动
---- 代码解析 网络子系统初始化 pci_init函数源码如下: void pci_init() { // we'll place the e1000 registers at this address . // vm.c maps this range. // e1000寄存器的地址 uint64 e1000_regs = 0x40000000L; // qemu -machine to reveal its registers at // physical address 0x40000000. // 将e1000寄存器的物理地址设置给e1000网卡的BAR ,使其将寄存器映射到该地址 base[4+0] = e1000_regs; // 对e1000网卡进行初始化,传递e1000寄存器的地址作为参数 e1000_init 我们已经处理了该中断; // 如果不执行此操作,e1000将不会触发任何进一步的中断。
需要我们修改 vm01.xml 配置文件网卡段,添加如下红色标记行,改 为 e1000,千兆以太网卡: <interface type='bridge'> <mac address='52:54:00: c2:84:c0'/> <source bridge='br0'/> <model type='<em>e1000</em>'/> <address type='pci' domain='0x0000' bus='0x00
# PCI device 0x8086:0x100e (e1000) #SUBSYSTEM=="net", ACTION=="add", DRIVERS=="? address}=="08:00:27:1c:68:04", ATTR{type}=="1", KERNEL=="eth*", NAME="eth0" # PCI device 0x8086:0x100e (e1000 address}=="08:00:27:ee:4b:73", ATTR{type}=="1", KERNEL=="eth*", NAME="eth1" # PCI device 0x8086:0x100e (e1000
例如,在我的当前目录下有一个符号连接e1000,现在我想查找文件名中最后一个字母是数字的源文件,那么 $ find -H . ./2234.c 像上面这样写只能查找出当前目录下符合要求的文件,却找不出e1000下的文件。因此可以这么写: $ find -H e1000 .
5E:BF" 3、检查/etc/udev/rules.d/70-persistent-net.rules文件,会发现里面会有两行这样的配置项: # PCI device 0x8086:0x100e (e1000 =="08:00:27:23:86:95", ATTR{type}=="1", KERNEL=="eth*", NAME="eth0" # PCI device 0x8086:0x100e (e1000 其中上面的一项是之前的网卡的mac地址,也就是被克隆的网卡的地址,我将它删除掉,下面的一项是我们新添加的网卡的地址,但是NAME是eth1,这里我们需要改成eth0: # PCI device 0x8086:0x100e (e1000
安装 安装虚拟机设置: CPU 内存大小(MB) 硬盘方式、大小(G) 网卡方式 Windows 2003 系列 2个 512 IDE、5G e1000 Windows 2008 系列 2个 1024 IDE、20G e1000 安装方法: 用操作系统iso镜像安装,完成后安装最新的补丁和安全策略程序。 1) 关机 2) 添加一块virtio磁盘 3) 添加一块virtio网卡 4) 挂载最新的virtio驱动iso镜像 5) 开机,会提示发现新硬件,再光驱上查找安装相应的驱动 6) 升级intel e1000 网卡驱动到最新版本,驱动可以再intel官网上下载 注意事项:因为目前windows机器基本硬件是硬盘使用virtio,网卡使用e1000,一切完成之后,尽量保障模版的硬盘采用virtio方式,并配置2 块e1000的网卡。
support" config NETDEVICES bool "Network device support" depends on NET # 依赖NET被选中 config E1000 # 选择为模块 # 对应的Makefile逻辑 obj-$(CONFIG_NET) += net/ # 展开为: obj-y += net/ obj-$(CONFIG_E1000) += e1000 / # 展开为: obj-m += e1000/ 3. # 驱动的编译规则 │ ├── net/ # 网络驱动 │ │ ├── Kconfig │ │ ├── Makefile │ │ └── e1000 / │ │ ├── Kconfig # e1000网卡配置 │ │ ├── Makefile # e1000编译规则 │ │ └──
创建虚拟机:网络设置 这里选择桥接模式,桥接的接口是之前配置NAT时设置的接口,模型可以选Intel E1000或者VirtIO (半虚拟化)。 根据官方文档,Intel E1000是模拟英特尔千兆网卡(客户机应该不用额外的驱动);VirtIO (半虚拟化)能提供更高的性能,但是需要客户机安装相应驱动。 如果虚拟机要安装windows系统, 尽量选Intel E1000;linux系列的系统可以选VirtIO (半虚拟化)。另外2个模型无需考虑。
您将使用名为E1000的网络设备来处理网络通信。对于xv6(以及您编写的驱动程序),E1000看起来像是连接到真正以太网局域网(LAN)的真正硬件。 kernel/e1000_dev.h包含E1000定义的寄存器和标志位的定义,并在《英特尔E1000软件开发人员手册》中进行了描述。 由于数据包突发到达的速度可能快于驱动程序处理数据包的速度,因此e1000_init()为E1000提供了多个缓冲区,E1000可以将数据包写入其中。 您需要确保每个mbuf最终被释放,但只能在E1000完成数据包传输之后(E1000在描述符中设置E1000_TXD_STAT_DD位以指示此情况)。 您将需要锁来应对xv6可能从多个进程使用E1000,或者在中断到达时在内核线程中使用E1000的可能性。
因为数据包的爆发可能比驱动程序处理它们的速度更快,所以 e1000_init() 为 E1000 提供了多个缓冲区,E1000 可以将数据包写入其中。 E1000 要求这些缓冲区由 RAM 中的“描述符”数组描述;每个描述符都包含 RAM 中的一个地址,E1000 可以在其中写入接收到的数据包。 struct rx_desc 描述描述符格式。 e1000_init() 使用 mbufalloc() 将 E1000 的 mbuf 数据包缓冲区分配给 DMA 。还有一个传输环,驱动程序将它希望 E1000 发送的数据包放入其中。 您需要确保每个 mbuf 最终都被释放,但只有在 E1000 完成数据包传输之后(E1000 设置描述符中的 E1000_TXD_STAT_DD 位来指示这一点)。 您将需要锁来应对 xv6 可能从多个进程使用 E1000 的可能性,或者当中断到达时可能在内核线程中使用 E1000。
single 5 # line, and change only the value of the NAME= key. 6 7 # PCI device 0x8086:0x100f (e1000 :0c:29:6e:2b:3c", ATTR{type}=="1", KERNEL=="eth*", NAME="eth0" 9 10 # PCI device 0x8086:0x100f (e1000 :0C:29:29:85:84", ATTR{type}=="1", KERNEL=="eth*", NAME="eth0" 12 13 # PCI device 0x8086:0x100f (e1000