Tags: MIT 6.828
实验结束后的全部代码在:https://github.com/monkey2000/xv6-riscv/tree/net/
测试结果:
running nettests:
$ make qemu-gdb
(4.1s)
nettest: one ping: OK
nettest: single process: OK
nettest: multi-process: OK
nettest: DNS: OK
time: OK
Score: 100/100
上来直接:
$ cd xv6-riscv-fall19
$ git checkout net
network 分为几个部分完成:
connect() close() read() write()下面我们分层介绍一些代码细节。
这个实验中仅要求完成 e1000_recv() 和 e1000_transmit() 两个驱动函数。
具体两个函数的功能在实验指导中有详解,此处不赘述。
需要注意的是,在 e1000_recv() 中处理数据包中断时,很可能有多个待处理的包都在 buffer 里。我错误理解为一个 interrupt 对应一个 packet (实际为一对多),导致调了很久。
int
e1000_transmit(struct mbuf *m) {
acquire(&e1000_lock);
uint32 desc_pos = regs[E1000_TDT];
// overflow detection
if((tx_ring[desc_pos].status & E1000_RXD_STAT_DD) == 0) {
release(&e1000_lock);
return -1;
}
if(tx_mbufs[desc_pos] != 0)
mbuffree(tx_mbufs[desc_pos]);
tx_mbufs[desc_pos] = m;
tx_ring[desc_pos].addr = (uint64) m->head;
tx_ring[desc_pos].length = m->len;
tx_ring[desc_pos].cmd = E1000_TXD_CMD_RS | E1000_TXD_CMD_EOP;
regs[E1000_TDT] = (desc_pos + 1) % TX_RING_SIZE;
__sync_synchronize();
release(&e1000_lock);
return 0;
}
static void
e1000_recv(void) {
struct mbuf *buf;
uint32 desc_pos = (regs[E1000_RDT] + 1) % RX_RING_SIZE;
while((rx_ring[desc_pos].status & E1000_RXD_STAT_DD)) {
acquire(&e1000_lock);
buf = rx_mbufs[desc_pos];
mbufput(buf, rx_ring[desc_pos].length);
// refill a new mbuf
rx_mbufs[desc_pos] = mbufalloc(0);
if (!rx_mbufs[desc_pos])
panic("e1000");
rx_ring[desc_pos].addr = (uint64) rx_mbufs[desc_pos]->head;
rx_ring[desc_pos].status = 0;
regs[E1000_RDT] = desc_pos;
__sync_synchronize();
release(&e1000_lock);
net_rx(buf);
desc_pos = (regs[E1000_RDT] + 1) % RX_RING_SIZE;
}
}
这部分无需自己实现,不过 challenge 里对这部分要求很高(如实现一个完整的 arp 协议)。
我个人建议做实验时在这部分添加一些调试用的输出,方便查看(当然也可以用 wireshark 浏览 dump 出来的流量)。
原本程序里的单链表实在难用…
这部分需要实现的包括 sockread() sockwrite() sockclose() 等函数。可以仿照 pipe 的实现编写。
同时还要实现一个 port demuxer sockrecvudp(),根据 (raddr, lport, rport) 找到相应的 socket ,并放入相应的数据包,唤醒在这个 socket 上等待的进程(我认为这个比较重要)
// called by protocol handler layer to deliver UDP packets
void
sockrecvudp(struct mbuf *m, uint32 raddr, uint16 lport, uint16 rport) {
struct sock *so = sockets;
acquire(&lock);
while(so) {
if (so->raddr == raddr &&
so->lport == lport &&
so->rport == rport) {
break;
}
so = so->next;
}
release(&lock);
// if no socket machted
if(so == 0) {
mbuffree(m);
return;
}
acquire(&so->lock);
mbufq_pushtail(&so->rxq, m);
release(&so->lock);
wakeup(&so->rxq);
}