本次 lab 要给 xv6 结束网卡(Qemu 模拟 E1000 网卡)驱动。任务阐明书里给了一大段阐明以及 E1000 的操作手册。

结束原理

xv6 发送和接纳网络包的流程

发送数据包:

  1. 用户程序调用 connect 系统调用创立 socket 并获取 socket 的文件描绘符;
  2. 调用 write 系统调用往 socket 的文件描绘符中写入数据;
  3. 进入内核,调用 filewrite 函数,因为要写入的文件类型是 FD_SOCK,所以调用 sockwrite 函数,创立一个 mbuf 并将数据从用户空间拷贝过来;
  4. 调用 net_tx_udp 函数进行 udp 头部封装;
  5. 进一步调用 net_tx_ip 函数进行 ip 头部封装;
  6. 调用 net_tx_eth 函数,并将 m 经过e1000_transmit 函数传递给网卡驱动;
  7. 由 e1000_transmit 函数将 mbuf 放到发送行列的尾部,等候网卡设备发送。

承受数据包:

  1. 网卡接纳到新的数据包,产生中止,内核调用中止处理函数 e1000_intr;
  2. 调用 e1000_recv 函数,开端读取缓冲行列中的音讯;
  3. 读到一条音讯后,经过net_rx 函数向上层传递;
  4. 依据音讯的类型,判别是调用 net_rx_ip 函数仍是 net_rx_arp 函数;
  5. 假如是 ip 音讯,还需求进一步调用net_rx_udp 函数进行拆解;
  6. 调用 sockrecvudp 函数,在其中找到对应的 socket,再经过 mbufq_pushtail 函数将音讯放到行列中,待 sockread 函数读取;

以上便是 xv6 收发网络包的大体流程,具体能够自己阅读源码

描绘符和缓冲行列

驱动中有两种缓冲行列,发送和接纳的行列,分别有两种描绘符对应这两种行列。描绘中中记录了对应缓存的存储地址、数据长度,还有一些标志位来让网卡和网卡驱动进行一些判别:

  • E1000_TXD_STAT_DD 标志位便是让网卡驱动在发送数据时判别当时拿到的描绘符对应的缓存是否现已发送了;
  • E1000_RXD_STAT_DD 标志位便是在承受数据的时分判别是否是没有接纳过的数据;
  • E1000_TXD_CMD_RS 表明 Report Status,当这个字段被设置时,表明在数据包发送结束后,e1000 网卡会自动填充传输描绘符中的报告状况区域,能够用来检查数据包是否发送成功。
  • E1000_TXD_CMD_EOP 表明 End Of Packet,当这个字段被设置时,表明数据包现已到达了传输描绘符中的缓冲区的结尾。当该字段被设置时,意味着这是一个完整的数据包,能够开端传输了。

上面的这几个 status 或 cmd 位是咱们需求用到的。

环形缓冲行列的头尾

regs 数组中存储着 e1000 的寄存器的值,结束 lab 来说,需求使用到 regs[E1000_TDT],即下一个需求传输的环形缓冲行列的索引,还有 regs[E1000_RDT],即当时现已读到而且读过的环形缓冲行列索引。依据 Hints 来增加索引即可。

代码结束

首先结束 e1000_transmit。依据刚刚的调用流程剖析,可能会有多个进程一起调用该函数,所以为了避免产生竞态,需求对函数上锁。接下来的步骤便是按照 Hints 来就行:

  1. 上锁;
  2. 获取 regs[E1000_TDT] 方位的描绘符;
  3. 判别描绘符 status 的 E1000_TXD_STAT_DD 是否被设置,没被设置阐明之前数据还没发送,这个描绘符对应的方位不能放入一个新的数据,回来 -1;
  4. 不然假如这个描绘符对应的方位有数据则开释(调用 mbuffree);
  5. 从头设置描绘符的 addr、length、cmd;
  6. 更新 regs[E1000_TDT] 为 (regs[E1000_TDT]+1)% TX_RING_SIZE;
  7. 将参数 m 放入缓冲区中;
  8. 开释锁,回来 0;
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);
  uint32 txrid = regs[E1000_TDT];
  struct tx_desc *txdesc = &tx_ring[txrid];
  if((txdesc->status & E1000_TXD_STAT_DD) == 0) {
    release(&e1000_lock);
    return -1;
  }
  if(txdesc->addr != 0) {
    mbuffree((struct mbuf *) tx_mbufs[txrid]);
  }
  txdesc->addr = (uint64) m->head;
  txdesc->length = m->len;
  txdesc->cmd |= E1000_TXD_CMD_RS;
  txdesc->cmd |= E1000_TXD_CMD_EOP;
  regs[E1000_TDT] = (txrid + 1) % TX_RING_SIZE;
  tx_mbufs[txrid] = m;
  release(&e1000_lock);
  return 0;
}

接着是 e1000_recv,留意它是不能够上锁的。第一是只需在处理中止的时分会调用该函数,不会产生竞态,第二是假如接纳到的数据包是 ARP 数据包,那么在解包的时分就会调用 net_tx_arp 函数回复自己的 mac,会调用 e1000_transmit 再次获取锁,产生 panic。

依据 Hints:

  1. 获取下一个要读的描绘符;
  2. 判别描绘符的 E1000_RXD_STAT_DD 位是否被设置,没被设置就回来。留意这儿要用一个循环来读取,直到不满足条件:

Your e1000_recv() code must scan the RX ring and deliver each new packet’s mbuf to the network stack (in net.c) by calling net_rx().

  1. 将描绘符指向的数据经过 net_rx 传递给上层;
  2. 创立一个新的 mbuf 放入该方位中;
  3. 更新 regs[E1000_RDT] 为当时方位;
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()).
  //
  uint32 rxrid = (regs[E1000_RDT] + 1) % RX_RING_SIZE;
  struct rx_desc *rxdesc = &rx_ring[rxrid];
  while (rxdesc->status & E1000_RXD_STAT_DD) {
    struct mbuf *m = rx_mbufs[rxrid];
    m->len = rxdesc->length;
    net_rx(m);
    struct mbuf *nm = mbufalloc(0);
    rxdesc->addr = (uint64) nm->head;
    rxdesc->status = 0;
    rx_mbufs[rxrid] = nm;
    regs[E1000_RDT] = rxrid;
    rxrid = (regs[E1000_RDT] + 1) % RX_RING_SIZE;
    rxdesc = &rx_ring[rxrid];
  }
}

运转结果

MIT 6.s081 Lab11: networking

这次不必跑 usertests,很快就跑完了。

总结

这次试验给的材料太多了,还挺难看完的。主张选着看就行了,因为我感觉实际上只看 Hints 也能做个八九不离十。这次 lab 相对简略,有很多能够做的工作都在 Optional Challenges 中,感觉有时间能够做一做。

结束撒花

MIT 6.S081 Fall 2020 的 lab 算是全部做完了,如释重负,不过 Optional Challenges 是一个没做。后续计划再整体好好过一遍代码整理对应的知识。其实我感觉收益仍是蛮大的,只需着手了,即使结束的版本是功能不高而且简略的,对相应知识的了解也能愈加深刻,更甭说后续再做 Optional Challenges。

而且我感觉这门课的 lab 比起 6.824 来说愈加与课程内容强相关,上课没懂的地方看看代码或许就懂了,上手也会愈加简略。

还剩了几堂 Lecture 没看完,期望五月份之前能搞定吧。