【XV6】 networking

代码:https://github.com/JasenChao/xv6-labs.git

E1000网络设备驱动

题目已经在kernel/e1000.c中给出了E1000的初始化函数和发送接收函数,要求完善发送和接收的功能。

其他相关的代码,上层的网络协议在kernel/net.ckernel/net.h中。

PCI总线上搜索网卡的代码在kernel/pci.c中:

// tell the e1000 to reveal its registers at
// physical address 0x40000000.
base[4+0] = e1000_regs;

e1000_init((uint32*)e1000_regs);

e1000_init()函数配置了E1000为DMA模式,预备了多个缓冲区组成了两个环,用来存储需要发送和接收的消息,相当于一个队列,两个环配置为大小为 RX_RING_SIZETX_RING_SIZE

发送函数

net.c 中的网络堆栈需要发送数据包时,e1000_transmit函数使用要发送的数据包mbuf。发送完成的数据包需要确保mbuf最终被释放,同时在传输数据包完成后,mbufs的status标志位会变为E1000_TXD_STAT_DD。

int
e1000_transmit(struct mbuf *m)
{
  //
  // Your code here.
  //
  // the mbuf contains an ethernet frame; program it into
  // the TX descriptor ring so that the e1000 sends it. Stash
  // a pointer so that it can be freed after sending.
  //
  acquire(&e1000_lock);
  int idx = regs[E1000_TDT];                              // 取E1000_TDT寄存器的值作为队列尾部
  if ((tx_ring[idx].status & E1000_TXD_STAT_DD) == 0) {   // 如果发送已完成,则退出
    release(&e1000_lock);
    return -1;
  }

  if (tx_mbufs[idx]) mbuffree(tx_mbufs[idx]);             // 如果mbuf非空,则释放掉

  tx_mbufs[idx] = m;                                      // mbuf指向需要发送的m
  tx_ring[idx].length = m->len;
  tx_ring[idx].addr = (uint64)m->head;
  tx_ring[idx].css = tx_ring[idx].cso = tx_ring[idx].special = 0;
  tx_ring[idx].cmd = E1000_TXD_CMD_RS | E1000_TXD_CMD_EOP;
  regs[E1000_TDT] = (idx + 1) % TX_RING_SIZE;             // 更新寄存器

  release(&e1000_lock);
  return 0;
}

接收函数

接收函数应该扫描队列,将新收到的mbuf传送到上层的网络协议中,也就是net_rx(),然后用空的mbuf代替。

static void
e1000_recv(void)
{
  //
  // Your code here.
  //
  // Check for packets that have arrived from the e1000
  // Create and deliver an mbuf for each packet (using net_rx()).
  //
  int idx = (regs[E1000_RDT] + 1) % RX_RING_SIZE;
  while(rx_ring[idx].status & E1000_RXD_STAT_DD) {
    rx_mbufs[idx]->len = rx_ring[idx].length;
    net_rx(rx_mbufs[idx]);                            // mbuf送到上层协议

    // 清空mbuf
    rx_mbufs[idx] = mbufalloc(0);
    rx_ring[idx].status = 0;
    rx_ring[idx].addr = (uint64)rx_mbufs[idx]->head;
    regs[E1000_RDT] = idx;
    idx = (idx + 1) % RX_RING_SIZE;
  }
}

测试结果

直观的测试可以用两个xv6目录的终端,分别运行make servermake qemu,在make qemu的xv6中运行nettests,然后可以在make server的终端里看到测试结果。

也可以使用make grade测试,结果如下:

== Test running nettests == 
$ make qemu-gdb
(3.7s) 
== Test   nettest: ping == 
  nettest: ping: OK 
== Test   nettest: single process == 
  nettest: single process: OK 
== Test   nettest: multi-process == 
  nettest: multi-process: OK 
== Test   nettest: DNS == 
  nettest: DNS: OK 
posted on 2024-02-14 19:05  未连接到互联网  阅读(64)  评论(0编辑  收藏  举报