N2.4 Softirq processing NET_RX
Softirq processing was scheduled by the bottom half, and NET_RX begins. All of NET_RX processing is done in net_rx_action(). The logic is to process packets until one of the following events occurs:
- The packet queue is empty. In this case NET_RX softirq stops.
- NET_RX has been running for longer than
netdev_budget_usecs, whose default value is 2 milliseconds. - NET_RX has processed more than
netdev_budget(fixed value of 300) packets. (We will revist this constraint while looking at NAPI)
In cases 2 and 3, there still might be packets to process, in which case NET_RX will schedule itself before exiting, (i.e. set the NET_RX_SOFTIRQ bit before exiting), so it can process some more packets in another session. In cases 2 and 3 NET_RX processing is almost at it's limits. To indicate this sd->time_squeeze is incremented, so that a few parameters can be tuned. We will revisit this while discussing NAPI.
Softirq processing is done with elevated privilages, which can easily cause it to high-jack the complete CPU. The above constraints are to make sure that softirq processing allows the applications run. If the softirq were to high-jack the CPU, the user application would never run, and the end user would see applications not responding.
The actual function that dequeues packets from input_pkt_queue and begins processing them is process_backlog(). After dequeueing the packet it calls __netif_receive_skb() which pushes the packet up into the protocol stacks.
For now ignore the napi part of net_rx_action(), it calls napi_poll() which will call the registered poll function n->poll(). The poll function is set to process_backlog. For now believe me even if it does not make much sense. It will make sense one we look at the NAPI framework.
net_rx_action()
{
unsigned long time_limit = jiffies +
usecs_to_jiffies(netdev_budget_usecs);
int budget = netdev_budget;
budget -= napi_poll(n, &repoll)
{
work = n->poll(n, weight) // same as process_backlog
process_backlog(n, weight)
{
skb_queue_splice_tail_init(&sd->input_pkt_queue,
&sd->process_queue);
while ((skb = __skb_dequeue(&sd->process_queue)))
__netif_receive_skb(skb);
}
}
// time and budget constraints
if (unlikely(budget <= 0 ||
time_after_eq(jiffies, time_limit))) {
sd->time_squeeze++;
break;
}
}