MIT 6.S081 | 0x07 Networking
首先想解~~jiao~~释~~bian~~一下,为什么拖了这么久:文档太多,没看进去 😂,拖着拖着就彻底忘了。我经过深刻反思,认为这是一个很不好的习惯,所以我又来了,抓紧更新,争取把这个系列收尾。
Your job is to complete e1000_transmit()
and e1000_recv()
, both in kernel/e1000.c
, so that the driver can transmit and receive packets. You are done when make grade says your solution passes all the tests.
这个部分让我们实现网络设备 E1000 的发送和接收功能,主要就是完成 e1000_transmit()
和 e1000_recv()
这两个函数。初看的时候没什么头绪,参考的那个文件长的不得了,磨了几天就忘掉了。现在看到实验的 Hints 部分原来把收发过程写的很清楚,我就照着这个提示写了,主要需要理解的就是这个环形缓冲区的使用。
提示
首先,在e1000_transmit()
和e1000_recv()
中添加打印语句,并运行make server
以及在xv6中执行nettests
。你应该能从打印语句中看到nettests
触发了对e1000_transmit
的调用。
实现e1000_transmit()
的一些提示:
- 首先,通过读取
E1000_TDT
控制寄存器,询问E1000下一个数据包期望的发送环索引。 - 然后检查环是否溢出。如果
E1000_TXD_STAT_DD
位在由E1000_TDT
索引的描述符中未设置,表示E1000尚未完成相应的前一个传输请求,此时应返回错误。 - 否则,使用
mbuffree()
释放该描述符上一次发送的mbuf(如果有的话)。 - 接着填充描述符。
m->head
指向内存中数据包的内容,m->len
是数据包长度。设置必要的cmd标志(参考E1000手册的3.3节),并保留一个指向mbuf的指针以便稍后释放。 - 最后,通过对
E1000_TDT
加一并取模TX_RING_SIZE
来更新环的位置。 - 如果
e1000_transmit()
成功地将mbuf添加到环中,则返回0。如果失败(例如,没有可用的描述符来传输mbuf),返回-1,让调用者知道需要释放mbuf。
实现e1000_recv()
的一些提示:
- 首先,通过获取
E1000_RDT
控制寄存器并对其加一取模RX_RING_SIZE
,询问E1000下一个等待接收的数据包(如果有)所在环索引。 - 然后,通过检查描述符状态部分中的
E1000_RXD_STAT_DD
位来判断是否有新数据包可用。如果没有,停止处理。 - 否则,根据描述符中的长度报告更新mbuf的
m->len
。使用net_rx()
将mbuf传递给网络栈。 - 然后,使用
mbufalloc()
分配一个新的mbuf以替换刚刚给net_rx()
的那个。将其数据指针(m->head
)编程到描述符中,并清零描述符的状态位。 - 最后,更新
E1000_RDT
寄存器为最后处理的环描述符的索引。
e1000_init()
使用mbufs初始化RX环,你可能需要查看它是如何做到的,并可能借鉴其中的代码。
总有一天到达的数据包总数将超过环的大小(16个);确保你的代码能够处理这种情况。
你将需要锁机制来应对xv6可能从多个进程使用E1000,或在中断到达时正在内核线程中使用E1000的可能性。
代码
diff --git a/kernel/e1000.c b/kernel/e1000.c
index 70a2adf..0414749 100644
--- a/kernel/e1000.c
+++ b/kernel/e1000.c
@@ -102,7 +102,30 @@ e1000_transmit(struct mbuf *m)
// 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 idx = regs[E1000_TDT];
+
+ if (!(tx_ring[idx].status & E1000_TXD_STAT_DD)) {
+ release(&e1000_lock);
+ return -1;
+ }
+
+ if (tx_mbufs[idx]) {
+ mbuffree(tx_mbufs[idx]);
+ tx_mbufs[idx] = 0;
+ }
+
+ tx_ring[idx].addr = (uint64)m->head;
+ tx_ring[idx].length = m->len;
+
+ tx_ring[idx].cmd = E1000_TXD_CMD_EOP | E1000_TXD_CMD_RS;
+
+ tx_mbufs[idx] = m;
+
+ regs[E1000_TDT] = (regs[E1000_TDT] + 1) % TX_RING_SIZE;
+
+ release(&e1000_lock);
return 0;
}
@@ -115,6 +138,23 @@ e1000_recv(void)
// Check for packets that have arrived from the e1000
// Create and deliver an mbuf for each packet (using net_rx()).
//
+ while (1) {
+ uint32 tail = (regs[E1000_RDT] + 1) % RX_RING_SIZE;
+
+ if (!(rx_ring[tail].status & E1000_TXD_STAT_DD)) {
+ break;
+ }
+
+ rx_mbufs[tail]->len = rx_ring[tail].length;
+ net_rx(rx_mbufs[tail]);
+
+ rx_mbufs[tail] = mbufalloc(0);
+
+ rx_ring[tail].addr = (uint64)rx_mbufs[tail]->head;
+ rx_ring[tail].status = 0;
+
+ regs[E1000_RDT] = tail;
+ }
}
评测
在进行 make grade
的时候有个测试 nettest: DNS
没有通过。看了一下测试代码,它要求的 IP 是 128.52.129.126,但我实际拿到的 IP 是 198.18.0.51,看了一下这两个地址其实是一个网站。这么来看,我的代码应该是没问题的,只是因为测试代码没有更新 IP 地址吧。