1. 程式人生 > 其它 >7. Lab: networking

7. Lab: networking

https://pdos.csail.mit.edu/6.S081/2021/labs/net.html

1. 要求

lab 要求簡單來說就是實現網絡卡驅動的 transmitrecv 功能。其實只要跟著 lab 的 hints 做就可以了,難度較低。

2. 實現

首先是 transmit 功能,這裡比較麻煩的是確定 tx_desc.cmd 的值,查閱下文件即可。文件中標註:

VLE, IFCS, and IC are qualified by EOP. That is, hardware interprets these bits ONLY when
EOP is set.
Hardware only sets the DD bit for descriptors with RS set.

因此設定 DD bit 和 EOP bit 即可。其實標頭檔案中關於 cmd 也只定義了這 2 個 bit。

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 tail = regs[E1000_TDT];
  struct tx_desc* txdesc = &tx_ring[tail];
  if ((txdesc->status & E1000_TXD_STAT_DD) == 0){
    release(&e1000_lock);
    return -1;    // the E1000 hasn't finished the corresponding previous transmission request, so return an error.
  }
  
  if (tx_mbufs[tail])
    mbuffree(tx_mbufs[tail]);

  tx_mbufs[tail] = m;
  txdesc->addr = (uint64)m->head;
  txdesc->length = m->len;
  // VLE, IFCS, and IC are qualified by EOP. That is, hardware interprets these bits ONLY when
  // EOP is set.
  // Hardware only sets the DD bit for descriptors with RS set.
  txdesc->cmd = (E1000_TXD_CMD_EOP | E1000_TXD_CMD_RS);
  regs[E1000_TDT] = (tail + 1) % TX_RING_SIZE;
  release(&e1000_lock);
  return 0;
}

接著實現 recv 功能,這裡需要注意的是,recv 在一次中斷可能收到多個 packet。因此我們需要從 tail 處開始,依次遍歷,檢查 DD bit 是否為 1,為 1 表示該描述符可以進行 net_rx 操作。
其次需要注意要在 net_rx 前釋放鎖,否則會引起 panic。

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 tail = (regs[E1000_RDT] + 1) % RX_RING_SIZE;
  struct rx_desc* rxdesc = &rx_ring[tail];

  while(rxdesc->status & E1000_RXD_STAT_DD){
    acquire(&e1000_lock);

    struct mbuf* buf = rx_mbufs[tail];
    mbufput(buf, rxdesc->length);

    struct mbuf* m = mbufalloc(0);
    rxdesc->addr = (uint64)m->head; 
    rxdesc->status = 0;
    rx_mbufs[tail] = m;
    regs[E1000_RDT] = tail;
    release(&e1000_lock);

    net_rx(buf);
    tail = (tail + 1) % RX_RING_SIZE;
    rxdesc = &rx_ring[tail];
  }
}

3. 小結

該 lab 實現的思路都在 hint 中,關於 recv 和 transmit 的 ring 陣列,其結構圖大致如下: