总览
这次实验的主要要点是理解抽象
mbuf 是对 OS 内部网络数据的抽象,而 {tx, rx}_desc
是对网卡网络数据的抽象。网卡作为 file,有 descriptor 也是抽象。总之能理解抽象这次 lab 就完成大半了
还有一点可以研究的是 DMA,中文文档似乎没有细提,这是第一次接触 DMA,没想到就是 memory mapped?可能还要对 memory mapped 有一定了解就好做。
还补充一点,需要一些黑盒思想,要相信环形缓冲区中的数据会被网卡等自动发送。
实现
总的来说是跟着 hint 和 init 做
在 transmit 部份,主要知道有什么可以操作 (tx_mbufs
and tx_ring
) ,知道传入一个 mbuf 要从 ring 传出就好了,另外要知道 regs 怎么对网卡操作
在 recv 部份,第一次看官方 hint 的时候没想到一次 recv 可能接收多个包,所以就没有放在循环里面错了,后来看了中文文档才想到这一点,放在 while 里面遍历所有内容 其余的也是跟着 hint 做
实现不难,主要是理解
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.
//
int idx = regs[E1000_TDT];
struct tx_desc desc = tx_ring[idx];
if (desc.status != E1000_TXD_STAT_DD) {
return -1;
}
// use mbuffree() to free the last mbuf that was transmitted from that descriptor (if there was one).
if (tx_mbufs[idx]) {
mbuffree(tx_mbufs[idx]);
}
// Then fill in the descriptor. m->head points to the packet's content in memory, and m->len is the packet length. Set the necessary cmd flags (look at Section 3.3 in the E1000 manual) and stash away a pointer to the mbuf for later freeing.
tx_ring[idx].addr = (uint64) m->head;
tx_ring[idx].length = m->len;
tx_ring[idx].cmd |= E1000_TXD_CMD_RS | E1000_TXD_CMD_EOP;
tx_mbufs[idx] = m;
// Finally, update the ring position by adding one to E1000_TDT modulo TX_RING_SIZE.
regs[E1000_TDT] = (regs[E1000_TDT] + 1) % TX_RING_SIZE;
// If e1000_transmit() added the mbuf successfully to the ring, return 0. On failure (e.g., there is no descriptor available to transmit the mbuf), return -1 so that the caller knows to free the mbuf.
return 0;
}
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()).
//
// First ask the E1000 for the ring index at which the next waiting received packet (if any) is located, by fetching the E1000_RDT control register and adding one modulo RX_RING_SIZE.
int idx = (regs[E1000_RDT] + 1) % RX_RING_SIZE;
// Then check if a new packet is available by checking for the E1000_RXD_STAT_DD bit in the status portion of the descriptor. If not, stop.
if (!(rx_ring[idx].status & E1000_RXD_STAT_DD)) {
return;
}
// Otherwise, update the mbuf's m->len to the length reported in the descriptor. Deliver the mbuf to the network stack using net_rx().
while (rx_ring[idx].status & E1000_RXD_STAT_DD) {
struct mbuf *m = rx_mbufs[idx];
m->len = rx_ring[idx].length;
net_rx(m);
// Then allocate a new mbuf using mbufalloc() to replace the one just given to net_rx(). Program its data pointer (m->head) into the descriptor. Clear the descriptor's status bits to zero.
rx_mbufs[idx] = mbufalloc(0);
if (!rx_mbufs[idx]) {
panic("e1000");
}
rx_ring[idx].addr = (uint64) rx_mbufs[idx]->head;
rx_ring[idx].status = 0;
// Finally, update the E1000_RDT register to be the index of the last ring descriptor processed.
regs[E1000_RDT] = idx;
idx = (regs[E1000_RDT] + 1) % RX_RING_SIZE;
}
}
misc
tcpdump -XXnr packets.pcap
-XX
: When parsing and printing, in addition to printing the headers of each packet, print the data of each packet, including its link level header, in hex and ASCII.-n
: Don’t convert addresses (i.e., host addresses, port numbers, etc.) to names.-r
: Read packets from file (which was created with the -w option or by other tools that write pcap or pcapng files).
测试
$ nettests
nettests running on port 25099
testing ping: OK
testing single-process pings: OK
testing multi-process pings: OK
testing DNS
DNS arecord for pdos.csail.mit.edu. is 128.52.129.126
DNS OK
all tests passed.
# make grade
== Test running nettests ==
$ make qemu-gdb
(7.6s)
== 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
== Test time ==
time: FAIL
Cannot read time.txt
Score: 99/100
make: *** [Makefile:336: grade] Error 1
总结
不得不说 MIT 的课程以及作业设计真的很不错,目标明确,网上资源多,加上自动 grading,体验非常好。 单就这个 lab 来说,主要是理解这种抽象,实现本身并不难。 这种环形 buufer 还有 knuth 提出的环形寄存器,用于 PL,以后有机会去学习一下 好像总体来说,这五个 lab 都不算非常困难?最困难的是读 xv6 book,那个读起来好无聊。