# Lab8 networking ip of xv6: 10.0.2.15 ip of host: 10.0.2.2 這個 lab 要我們完成 `e1000_transmit()` 和 `e1000_recv()` ### trace code 檔案介紹: * `kernel/e1000.c`: init, transmiting, receving * `kernel/e1000_dev.h`: registers and flag by E1000 manual * `kernel/net.c`, `kernel/net.h`: net stack(IP, UDP, ARP) * `mbuf`: flexible data structure to hold packets * `kernel/pci.c`: searches for and E1000 card when boot 需要特別注意的 function: `write()`, `connect()`, `read()`, 尤其是要特別注意他們是如何使用到 `kernel/e1000.c` 的 * `main()` * `ping()` * `connect()` * `k/sysfile.c: sys_connect()` * `k/sysnet.c: sockalloc()` * `write()` * `k/sysfile.c: sys_write()` * `k/file.c: filewrite()` * `k/sysnet.c: sockwrite()` * `k/net.c: net_tx_udp()` * `k/net.c: net_tx_ip()` * `k/net.c: net_tx_eth()` * `k/e1000.c: e1000_transmit()` * `read()` * `dns()` * `k/net.c: net_tx_arp()` * `k/net.c: net_tx_eth()` * `k/e1000.c: e1000_transmit()` 當我們針對 `regs` 做動作時,實際上就是在對 e1000 做操作 ``` #define TX_RING_SIZE 16 static struct tx_desc tx_ring[TX_RING_SIZE] __attribute__((aligned(16))); static struct mbuf *tx_mbufs[TX_RING_SIZE]; #define RX_RING_SIZE 16 static struct rx_desc rx_ring[RX_RING_SIZE] __attribute__((aligned(16))); static struct mbuf *rx_mbufs[RX_RING_SIZE]; ``` #### Manual #### Manual 中對於 receive 的描述,以及如何用 C 語言說出來 在 `e1000_dev.h` 中,它根據了 E1000 3.3.3 定義出這些東西 ```C /* Receive Descriptor bit definitions [E1000 3.2.3.1] */ #define E1000_RXD_STAT_DD 0x01 /* Descriptor Done */ #define E1000_RXD_STAT_EOP 0x02 /* End of Packet */ // [E1000 3.2.3] struct rx_desc { uint64 addr; /* Address of the descriptor's data buffer */ uint16 length; /* Length of data DMAed into data buffer */ uint16 csum; /* Packet checksum */ uint8 status; /* Descriptor status */ uint8 errors; /* Descriptor Errors */ uint16 special; }; ``` 根據 3.2.3 and 3.2.3.1 * `addr`: 資料的 address,應該是指到 ring * `length`: 資料的長度 * `status`: status 中的 DD (Descriptor Done) 需要特別注意 (3.2.3.1) * ring (`rx_ring`) 的 item 也就是一個又一個的 `rx_desc` (?) ##### receive queue ![Fiqure 3-2]() * tail pointer is incremented by swoftware. * head pointer is incremented by hardware. * if `head` == `tail` then empty * if queue is full then hw stop receive * software 可以藉由 descriptor 來決定這個他是不是 valid (不必用一般的 I/O read) * 只要 `status` != 0, 這就可以 read 了 #### Manual 中對於 transmit 的描述,以及如何用 C 語言說出來 在 `e1000_dev.h` 中,它根據了 E1000 3.3.3 定義出這些東西 ```C /* Transmit Descriptor command definitions [E1000 3.3.3.1] */ #define E1000_TXD_CMD_EOP 0x01 /* End of Packet */ #define E1000_TXD_CMD_RS 0x08 /* Report Status */ /* Transmit Descriptor status definitions [E1000 3.3.3.2] */ #define E1000_TXD_STAT_DD 0x00000001 /* Descriptor Done */ // [E1000 3.3.3] struct tx_desc { uint64 addr; uint16 length; uint8 cso; uint8 cmd; uint8 status; uint8 css; uint16 special; }; ``` 根據 3.3.3 * `addr`: 資料的 address, 應該是指到 ring * `length`: 資料的長度 * `cmd`: 一些設定 * `status`: status 中的 DD (Descriptor Done) 需要特別注意 * ring (`tx_ring`) 的 item 也就是一個又一個的 `tx_desc`(?) ##### transmit queue ![Fiqure 3-4]() * 新的 descriptor 加到 tail, 並且 tail + 1 #### mbuf ```C struct mbuf { struct mbuf *next; // the next mbuf in the chain char *head; // the current start position of the buffer unsigned int len; // the length of the buffer char buf[MBUF_SIZE]; // the backing store }; ``` * mbuf 是一個 linked list * `head`: circular queue 的 head * `len`: length of the buffer 例如 transmit 時,我們就需要把這個 `mbuf` 寫到 `regs` 中 ## reference * https://pdos.csail.mit.edu/6.S081/2022/labs/net.html * https://www.youtube.com/watch?v=Fcjychg4Tvk * [Xv6 Lab8 Networking 实验记录](https://ttzytt.com/2022/08/xv6_lab8_record/) * https://github.com/ttzytt/xv6-riscv/blob/net/kernel/e1000.c * `devintr()` * `e1000_intr()` * `e1000_recv()` ### `read()` * `nettests.c: pipe()` * `connect()` * `kernel/sysfile.c: sys_connect()` * `kernel/sysnet.c: sockalloc()` * (*f)->type = FD_SOCK; * `read()` * `kernel/sysfile.c: sys_read()` * `kernel/file.c: fileread()` * `kernel/sysnet.c: sockread()` * `kernel/net.c: mbufq_pophead()` ```C struct mbuf * mbufq_pophead(struct mbufq *q) { struct mbuf *head = q->head; if (!head) return 0; q->head = head->next; return head; } ``` 這個在做的也就只是把 `head` 給 pop 掉而已 下一個問題就是: 這個 `struct mbufq q` 是哪裡來的 * 其實想要寫出這個 lab 了話,並不一定要知道這個問題的答案, * 想要寫出這個 lab 重點在於:(read) > interrupt 時,會呼叫到 `e1000_recv()`, 而它就負責把收到的資料放到 `rx_mbufs` > 等到 `ping()` 呼叫到 `read()` 時,它應該會自己去 `rx_mbufs` 拿資料 ## questions * `e1000_recv(void)` * `net_rx()` 是做什麼的 * `desc->status = 0` 的原因為何 * `mbufalloc()` 如何運作 ## write 接收 `struct mbuf *m` 把東西放到 `tx_ring` and `tx_mbuf` 裡 ## write questions * 為什麼要放到`tx_ring` and `tx_mbuf` 裡,應該放個一個就好(?) * `mbuffree()` 是作什麼的 * `desc->cmd = E1000_TXD_CMD_RS | E1000_TXD_CMD_EOP` 的來源為何