6.S081/6.828: 7 Lab networking

2022-11-26 04:59:29 浏览数 (1)

一、背景

本实验开发一个网卡驱动程序,其相关代码分布如下:

  1. kernel/e1000.c包含E1000的初始化代码以及你需要实现的transmitting与receiving数据包函数;
  2. kernel/e1000_dev包含寄存器定义和E1000开发者文档定义的flag bits;
  3. kernel/net.c和kernel/net.h包含简单的IP、UDP、ARP网络协议栈。这些文件中使用mbuf存放数据包;
  4. 最后,kernel/pci.c实现了xv6启动时在PCI总线搜索E1000网卡。

任务:实现e1000_transmit和e1000_recv,使得网络驱动能够发送和接收数据包。

image.pngimage.png

细节:

  1. e1000_init函数配置了tx_ring、tx_mbufs和rx_ring、rx_mbufs,采用的是DMA技术传输数据。
  2. 考虑到突发流量,E1000在e1000_init中初始化了多个buffer用来写packets,采用tx_desc、tx_desc来描述。
  3. 当协议栈调用e1000_transmit发送packet时,会从一个mbuf中的addr读取packet并发送出去。
  4. 当E1000接收到packet时,首先将packet写入到RX ring中,然后生成一个中断。

实现效果:

  1. 在一个窗口运行make server,然后在另外一个窗口运行make qemu并在xv6中运行nettests。第一个test case将发送UDP packet到宿主机。
  2. 在宿主机发送reply时会发送ARP解析下一站MAC地址,并期望xv6返回一个ARP reply,也就是要先ping 通。

本实验的实现需要了解xv6系统的网络协议及其实现,可以参考:xv6源码分析--networking

二、代码实现

1 发送端

代码语言:c复制
int
e1000_transmit(struct mbuf *m)
{

  acquire(&e1000_lock);
  uint32 bufindex=regs[E1000_TDT];
  struct tx_desc *desc=&tx_ring[bufindex];
  //该packet已经发送完了
  if(!(desc->status&E1000_TXD_STAT_DD)){
    release(&e1000_lock);
    return -1;
  }
  //如果该mbuf还未释放,则释放掉
  if(tx_mbufs[bufindex]){
    mbuffree(tx_mbufs[bufindex]);
    tx_mbufs[bufindex]=0;
  }
  desc->addr=(uint64)m->head;
  desc->length=m->len;
  //该packet是完整的
  desc->cmd=E1000_TXD_CMD_EOP | E1000_TXD_CMD_RS;
  tx_mbufs[bufindex]=m;

  regs[E1000_TDT]=(regs[E1000_TDT] 1)%TX_RING_SIZE;

  release(&e1000_lock);
  
  return 0;
}

2 接收端

代码语言:c复制
static void
e1000_recv(void)
{
  while(1){
    uint32 bufindex=(regs[E1000_RDT] 1)%RX_RING_SIZE;

    struct rx_desc * desc=&rx_ring[bufindex];
    if(!(desc->status & E1000_RXD_STAT_DD))
      return;
    rx_mbufs[bufindex]->len=desc->length;
    //数据由设备写入到内存中,不经过CPU,直接调用协议栈处理即可
    net_rx(rx_mbufs[bufindex]);
  
    rx_mbufs[bufindex]=mbufalloc(0);
    desc->addr=(uint64)rx_mbufs[bufindex]->head;
    desc->status=0;
    regs[E1000_RDT]=bufindex;
  }
}

3 测试结果

测试结果测试结果

0 人点赞