实验的目的是为了让我们实现一个网卡驱动在数据链路层的数据收发函数,该函数的实现需要参考E1000的用户手册来完成。

实现

  • 函数e1000_recv需要注意的地方:

    • 不需要加锁,因为该函数会一次性读取rx_ring上所有的数据,且该函数位于数据链路层,数据链路层还不涉及进程的概念。在读取完数据之后,该函数会调用net_rx函数,将数据送给上层应用。
    • 关于index的理解,在recv函数中,寄存器里保存的index是上次已经被处理过的位置,因此,拿到寄存器里的值后,需要加一移动至下个位置。且本次的数据被送至上层应用之后,会对其mbuf进行清理并申请新的mbuf用于保存下次接收的数据。
  • 函数e1000_transmit需要注意的地方:

    • 该相关寄存器中保存的index值为本次需要处理的数据索引,因此,本次处理完之后,需要对该值加一并重新放入寄存器。
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
77
78
79
80
81
82
83
84
85
86
87
88
89
// kernel/e1000.c
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.
//
// printf("e1000 transmit\n");
acquire(&e1000_lock);
// First ask the E1000 for the TX ring index at which it's expecting
// the next packet, by reading the E1000_TDT control register.
uint32 index = regs[E1000_TDT];
// Then check if the the ring is overflowing. If E1000_TXD_STAT_DD is
// not set in the descriptor indexed by E1000_TDT, the E1000 hasn't
// finished the corresponding previous transmission request, so return an error.
if ((tx_ring[index].status & E1000_TXD_STAT_DD) == 0) {
release(&e1000_lock);
return -1;
}

// Otherwise, use mbuffree() to free the last mbuf that was transmitted
// from that descriptor (if there was one).
if (tx_mbufs[index]) {
mbuffree(tx_mbufs[index]);
}

// m->head points to the packet's content in memory
tx_ring[index].addr = (uint64)(m->head);
// and m->len is the packet length
tx_ring[index].length = m->len;
// Set the necessary cmd flags (look at Section 3.3 in the E1000 manual)
tx_ring[index].cmd = E1000_TXD_CMD_RS | E1000_TXD_CMD_EOP;
// and stash away a pointer to the mbuf for later freeing.
tx_mbufs[index] = m;

// update the ring position by adding one to E1000_TDT modulo TX_RING_SIZE.
regs[E1000_TDT] = (index + 1) % TX_RING_SIZE;
release(&e1000_lock);
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()).
//
// printf("e2000 receive\n");
for (;;) {
// 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.
uint32 index = regs[E1000_RDT];
index = (index + 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[index].status & E1000_RXD_STAT_DD) == 0) {
return;
}

// update the mbuf's m->len to the length reported in the descriptor.
rx_mbufs[index]->len = rx_ring[index].length;
// rx_mbufs[index]->head = (char*)rx_ring[index].addr;

// Deliver the mbuf to the network stack using net_rx()
net_rx(rx_mbufs[index]);
// Then allocate a new mbuf using mbufalloc() to replace the one just
// given to net_rx().
struct mbuf* buf = mbufalloc(0);
rx_mbufs[index] = buf;
// Program its data pointer (m->head) into the descriptor.
rx_ring[index].addr = (uint64)buf->head;
// Clear the descriptor's status bits to zero
rx_ring[index].status = 0;

// Finally, update the E1000_RDT register to be the index of the
// last ring descriptor processed.
regs[E1000_RDT] = index;
}
}

文件nettest.c的dns函数,将8.8.8.8改为114.114.114.114;

1
2
// user/nettest.c
dst = (114 << 24) | (114 << 16) | (114 << 8) | (114 << 0);

make grade

1
2
3
4
5
6
7
8
9
10
11
12
13
14
grand@Lubuntu ~/xv6-labs-2020 (net)> ./grade-lab-net
make: 'kernel/kernel' is up to date.
== Test running nettests == (4.2s)
== 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: OK
Score: 100/100

git 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
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
diff --git a/kernel/e1000.c b/kernel/e1000.c
index 70a2adf..9260f09 100644
--- a/kernel/e1000.c
+++ b/kernel/e1000.c
@@ -91,7 +91,7 @@ e1000_init(uint32 *xregs)
regs[E1000_RADV] = 0; // interrupt after every packet (no timer)
regs[E1000_IMS] = (1 << 7); // RXDW -- Receiver Descriptor Write Back
}
-
+/*
int
e1000_transmit(struct mbuf *m)
{
@@ -116,6 +116,96 @@ e1000_recv(void)
// Create and deliver an mbuf for each packet (using net_rx()).
//
}
+*/
+
+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.
+ //
+ // printf("e1000 transmit\n");
+ acquire(&e1000_lock);
+ // First ask the E1000 for the TX ring index at which it's expecting
+ // the next packet, by reading the E1000_TDT control register.
+ uint32 index = regs[E1000_TDT];
+ // Then check if the the ring is overflowing. If E1000_TXD_STAT_DD is
+ // not set in the descriptor indexed by E1000_TDT, the E1000 hasn't
+ // finished the corresponding previous transmission request, so return an error.
+ if ((tx_ring[index].status & E1000_TXD_STAT_DD) == 0) {
+ release(&e1000_lock);
+ return -1;
+ }
+
+ // Otherwise, use mbuffree() to free the last mbuf that was transmitted
+ // from that descriptor (if there was one).
+ if (tx_mbufs[index]) {
+ mbuffree(tx_mbufs[index]);
+ }
+
+ // m->head points to the packet's content in memory
+ tx_ring[index].addr = (uint64)(m->head);
+ // and m->len is the packet length
+ tx_ring[index].length = m->len;
+ // Set the necessary cmd flags (look at Section 3.3 in the E1000 manual)
+ tx_ring[index].cmd = E1000_TXD_CMD_RS | E1000_TXD_CMD_EOP;
+ // and stash away a pointer to the mbuf for later freeing.
+ tx_mbufs[index] = m;
+
+ // update the ring position by adding one to E1000_TDT modulo TX_RING_SIZE.
+ regs[E1000_TDT] = (index + 1) % TX_RING_SIZE;
+ release(&e1000_lock);
+ 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()).
+ //
+ // printf("e2000 receive\n");
+ for (;;) {
+ // 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.
+ uint32 index = regs[E1000_RDT];
+ index = (index + 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[index].status & E1000_RXD_STAT_DD) == 0) {
+ return;
+ }
+
+ // update the mbuf's m->len to the length reported in the descriptor.
+ rx_mbufs[index]->len = rx_ring[index].length;
+ // rx_mbufs[index]->head = (char*)rx_ring[index].addr;
+
+ // Deliver the mbuf to the network stack using net_rx()
+ net_rx(rx_mbufs[index]);
+ // Then allocate a new mbuf using mbufalloc() to replace the one just
+ // given to net_rx().
+ struct mbuf* buf = mbufalloc(0);
+ rx_mbufs[index] = buf;
+ // Program its data pointer (m->head) into the descriptor.
+ rx_ring[index].addr = (uint64)buf->head;
+ // Clear the descriptor's status bits to zero
+ rx_ring[index].status = 0;
+
+ // Finally, update the E1000_RDT register to be the index of the
+ // last ring descriptor processed.
+ regs[E1000_RDT] = index;
+ }
+}

void
e1000_intr(void)
diff --git a/packets.pcap b/packets.pcap
index 82d353e..505fd0d 100644
Binary files a/packets.pcap and b/packets.pcap differ
diff --git a/time.txt b/time.txt
new file mode 100644
index 0000000..f599e28
--- /dev/null
+++ b/time.txt
@@ -0,0 +1 @@
+10
diff --git a/user/nettests.c b/user/nettests.c
index edf46ef..6f297b3 100644
--- a/user/nettests.c
+++ b/user/nettests.c
@@ -198,7 +198,9 @@ dns()
memset(ibuf, 0, N);

// 8.8.8.8: google's name server
- dst = (8 << 24) | (8 << 16) | (8 << 8) | (8 << 0);
+ // dst = (8 << 24) | (8 << 16) | (8 << 8) | (8 << 0);
+ // 114.114.114.114: 电信联通移动全国通用DNS
+ dst = (114 << 24) | (114 << 16) | (114 << 8) | (114 << 0);

if((fd = connect(dst, 10000, 53)) < 0){
fprintf(2, "ping: connect() failed\n");