In 2.6.24.4, all network cards, whether they support napi or not, use the struct napi_struct structure. So let's talk about this structure first.
struct napi_struct{
struct list_head poll_list;
unsigned long state;
int weight;
int (*poll)(struct napi_struct *,int);
}
Corresponding to the network card that supports napi, fill in this structure by yourself; instead of the napi network card, use the softnet_data>backlog of per cpu, and the initialization of this structure is completed in net_dev_init().
Let's talk about the network card of the non-napi mechanism first:
After the network card receives the data packet, DMA goes to the kernel space, and then calls netif_rx() to hook the data packet to softnet_data>input_pkt_queue. If the napi_struct of backlog is not scheduled, napi_schedule(&backlog).napi_schedule() will hang the poll_list of backlog Received on softnet_data->poll_list, and start soft interrupt NET_RX_SOFTIRQ at the same time. NET_RX_SOFTIRQ soft interrupt, call the corresponding function net_rx_action().
The network card corresponding to the napi mechanism:
When the network card is initialized, it will initialize its own data packet receiving queue and put the receiving agent in the poll_list. When a data packet arrives, it will dma the data packet to its own data packet queue. NET_RX_SOFTIRQ soft interrupt, call the corresponding function net_rx_action().
net_rx_action():
First get softnet_data->poll_list, traverse poll_list, get the napi_struct structure corresponding to each poll_list (container_of implementation), and then call the poll function according to the weight of napi_struct, if it is a non-napi network card, the napi_struct here is backlog, so the poll function is process_backlog; If it is a napi network card, it will make its own poll function.
The poll function of the napi network card is to dequeue a skb from its own packet queue, and then call netif_receive_skb().
The non-napi process_backlog will get softnet_data->input_pkt_queue, then dequeue the queue input_pkt_queue, get a skb, and then call netif_receive_skb(skb).
netif_receive_skb():
Do some preparatory work for skb, such as setting mac_len, etc., call deliver_skb() to handle handles for all registered ptype_all types of protocols, then handle bridges and VLANs, and then give the handle of the registered ptype_base of the corresponding protocol. Assuming that the ip protocol is used here, the processing function ip_rcv of the corresponding ip protocol handle will be called.
ip_rcv():
Do some checks on skb, if skb->users!=1, clone a skb, and then transfer to the hook point of NF_IP_PRE_ROUTING of netfilter, and call all hook functions registered at this point. For example, if conntrack is enabled, packet reassembly will be performed here. Then call ip_rcv_finish().
ip_rcv_finish():
First call ip_route_input() to determine the route of the packet, initialize skb->dst, and call dst_input(skb).
dst_input():
In fact, skb->dst->input(skb) is called, and the initialization of the corresponding input is in route.c. If it is a local packet dst->input=ip_local_deliver; if it is a forwarded packet dst->input=ip_forward;
Local process:
ip_local_deliver():
The first is the reassembly of the fragmented data packets, which will be transferred to the hook point of NF_IP_LOCAL_IN of netfilter, and all hook functions registered at this point will be called. After that, ip_local_deliver_finish() will be called, and then it will go to the fourth layer.
Forwarding process:
ip_forward():
After doing some source routing checks, it will transfer to the hook point of NF_IP_FORWARD of netfilter, and call all hook functions registered at this point. Then ip_forward_finish() will be called.
ip_forward_finish():
Call dst_output().
dst_output():
skb->dst->output(skb).一般output=ip_output.
ip_output():
Set the dev of skb as the dev of the package, and set skb->protocol at the same time, it will transfer to the hook point of NF_IP_POST_ROUTING of netfilter, and call all hook functions registered at this point. Then ip_finish_output() will be called.
ip_finish_output():
Check whether the packet needs fragmentation. If fragmentation is required, perform ip_fragement(), and then call ip_finish_output2().
ip_finish_output2():
根据neighbour,调用dst->neighbour->output.
So far, the data packet will be put into the dev's qdisc through dev_queue_xmit. Then there is the flow control out of the queue.