初始化
初始化每CPU softnet_data变量sd。其中需要关注的是struct napi_struct结构体变量backlog。backlog为所有非napi接口统一处理。sd->backlog.poll挂上process_backlog,处理驱动递交过来的数据包。
接收流程
step 1:驱动行为
对于非napi驱动接口,调用netif_rx,该函数将sd->backlog添加到softnet_data.poll_list链表,将skb添加到sd->input_pkt_queue队列。然后调度net_rx_action工作
队列。
对于napi驱动接口,将驱动自己维护的struct napi_struct结构体变量添加到softnet_data.poll_list链表,然后调度net_rx_action工作队列。
step 2: net_rx_action
首先遍历sd->poll_list链表。
找到链表中的每个napi_struct节点n。
执行n->poll函数处理驱动递交过来的数据包。对于非napi接口,n->poll即为process_backlog。
在规定的处理周期内,如果处理完成,将n从sd->poll_list中出队;如果处理未完成,则根据策略将其出队或将其重新入队到sd->poll_list队尾。等待下次执行。
如果处理次数超过buget或处理时间超时,重新设置NET_RX_SOFTIRQ,继续下一次调度。
step 3:poll(以process_backlog为例)
将input_pkt_queue队列中所有的节点挪到process_queue中。
遍历process_queue链表,将skb逐一出队。这里设置了一个quota参数,控制处理次数。当处理次数达到quota时,即使process_queue还有包,也立刻退出。
step 4:__netif_receive_skb
接收到的skb有三个去向:
1、送往Layer3
不同类型的数据包送往上层协议注册的回调函数。
这儿又有三条路径:
1.1 接收应用程序
1.2 转发
1.3 丢弃
2、送往bridge
3、丢弃
这里有两个重要的全局变量ptype_all和ptype_base:前者是接收所有包的packet_type类型的双链表,后者是接收上层协议注册的packet_type类型数组(如ip_packet_type)。
每个packet_type中都有一个函数指针.func,此函数指针即为处理包的回调函数(如ip层为ip_rcv)。
发送流程
| sock_write | -> | inet_write->inet_send | -> | udp_write -> udp_sendto -> udp_send | -> | ip_queue_xmit | -> | dev_queue_xmit | -> | ndo_start_xmit |
sock.c af_inet.c udp.c ip.c dev.c mydriver.c