本文记录了 Lab: networking 的实验过程

Lab: networking

查看相关代码

  • kernel/net.hkernel/net.c
  • kernel/e1000_dev.hkernel/e1000.c

e1000_transmit

  • 按照实验指导页面的 Hints,已经很详细了,跟着一步步做
  • 记得获取/释放锁

e1000_recv

Q1:一开始用 e1000_recv 函数只读取一个 mbuf,然后测试的时候过不了

A1:我们需要在一次中断时,就把所有到达的数据都处理掉。

Q2:在测试多线程那个函数时一直有问题,重复获取锁了

A1:在最后一节课中,Frans 教授给出了两种解决办法

  • e1000_recv 可以不加锁,处理完一个recv 前,下一个recv 会被阻塞住。
  • e1000_recv 不可以和 e1000_transmit 用同一把锁,如果 recv 接收到一个 ARP 包,会在recv 内部发送 ARP 响应包,从而调用 e1000_transmit,两者用同一把锁就死锁了,因此需要两把锁。

其他的跟着 Hints 做就行了

测试

1
2
make qemu
nettests
1
make grade

Diff

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
diff --git a/kernel/e1000.c b/kernel/e1000.c
index 70a2adf..88e021d 100644
--- a/kernel/e1000.c
+++ b/kernel/e1000.c
@@ -103,6 +103,33 @@ e1000_transmit(struct mbuf *m)
// a pointer so that it can be freed after sending.
//

+ acquire(&e1000_lock);
+
+ // First ask the E1000 for the TX ring index at which it's expecting the next packet
+ int tail = regs[E1000_TDT];
+
+ // Then whether the E1000 have finished the corresponding previous
+ // transmission request, if no, then return an error
+ if((tx_ring[tail].status & E1000_TXD_STAT_DD) == 0){
+ release(&e1000_lock);
+ return -1;
+ }
+
+ // Otherwish free the last mbuf if there was one
+ if(tx_mbufs[tail]){
+ mbuffree(tx_mbufs[tail]);
+ }
+
+ // Then fill in the descriptor
+ tx_ring[tail].addr = (uint64) m->head;
+ tx_ring[tail].length = m->len;
+ tx_ring[tail].cmd = E1000_TXD_CMD_RS | E1000_TXD_CMD_EOP;
+
+ tx_mbufs[tail] = m;
+
+ __sync_synchronize();
+
+ // Finally, update the ring position
+ regs[E1000_TDT] = (tail + 1) % TX_RING_SIZE;
+ release(&e1000_lock);
return 0;
}

@@ -115,6 +142,30 @@ 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){
+ // First fetching the E1000_RDT control register
+ // and adding one modulo RX_RING_SIZE
+ int tail = (regs[E1000_RDT] + 1) % RX_RING_SIZE;
+
+ // Then check if a new packet is available
+ if((rx_ring[tail].status & E1000_RXD_STAT_DD) == 0){
+ return;
+ }
+ __sync_synchronize();
+ // Otherwise, update the mbuf's m->len to the length reported in the descriptor
+ // Deliver the mbuf to the network stack using net_rx()
+ rx_mbufs[tail]->len = rx_ring[tail].length;
+ net_rx(rx_mbufs[tail]);
+
+ // 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[tail] = mbufalloc(0);
+ if(!rx_mbufs[tail])
+ panic("e1000_recv");
+ rx_ring[tail].addr = (uint64) rx_mbufs[tail]->head;
+ rx_ring[tail].status = 0;
+ __sync_synchronize();
+
+ regs[E1000_RDT] = tail % RX_RING_SIZE;
+ }
}

void
diff --git a/packets.pcap b/packets.pcap
index 82d353e..58bcc0f 100644
Binary files a/packets.pcap and b/packets.pcap differ