The core of napi is that when a network packet arrives in a busy network, it does not need to be interrupted, because a high frequency of interruption may affect the overall efficiency of the system, network Card interruption may occur several thousand times per second. If each interruption needs to be handled by the system, it is a great pressure, while napi uses polling to disable Nic reception interruption, which reduces the pressure on the system to handle the interruption. napi is a technology used on Linux to improve network processing efficiency, the core concept of poll is to read data without interruption. Instead, it first uses the service program to interrupt and wake up data receiving, and then poll
(Similar to the half-bottom-half processing mode );
However, napi has some serious defects: for upper-layer applications, the system cannot process each data packet as soon as it receives it, and as the transmission speed increases, the accumulated data packets will consume a lot of memory. Experiments show that the problem on the Linux platform is more serious than on FreeBSD; another problem caused by napi is that it is difficult to process large data packets, the reason is that it takes much longer to send large data packets to the network layer than short data packets (even using DMA), so as mentioned above, napi technology is suitable for processing high-speed Short-length data packets,
Core API:
1. _ netif_rx_schedule (Dev)
This function is called by the interrupt service program and the device's poll method is added to the poll processing queue at the network level,
Queuing and preparing to receive data packets, you need to call netif_rx_reschedule_prep before use, and the number of returned data packets is 1,
A net_rx_softirq Soft Interrupt notification is triggered to send packets to the network layer.
2. netif_rx_schedule_prep (Dev)
Make sure the device is running and the device is not added to the poll queue at the network layer,
This function is called before netif_rx_schedule is called.
3. netif_rx_complete (Dev)
Clears the specified device from the poll queue, which is usually called by the poll method of the device,
Note that the specified device cannot be cleared when the poll queue is in the working status; otherwise, an error will occur.
Static inline void _ netif_rx_schedule (struct net_device * Dev)
{
Unsigned long flags;
Local_irq_save (flags );
Dev_hold (Dev );
List_add_tail (& Dev-> poll_list, & __ get_cpu_var (softnet_data). poll_list );
If (Dev-> quota <0)
Dev-> quota + = Dev-> weight;
Else
Dev-> quota = Dev-> weight;
_ Raise_softirq_irqoff (net_rx_softirq );
Local_irq_restore (flags );
}
Static inline void netif_rx_schedule (struct net_device * Dev)
{
If (netif_rx_schedule_prep (Dev ))
_ Netif_rx_schedule (Dev );
}
# Define athr_mac_rx_sched_prep (M, d) napi_schedule_prep (& M-> napi)
# DEFINE _ athr_mac_rx_sched (M, d) _ napi_schedule (& M-> napi)
# Define athr_mac_rx_sched (m) napi_schedule (& M-> napi)
Ii. Registration of net_rx_softirq soft terminal:
The default soft terminal vector defined by OS is as follows:
Enum
{
Hi_softirq = 0,
Timer_softirq,
Net_tx_softirq,
Net_rx_softirq,
Block_softirq,
Tasklet_softirq,
Sched_softirq,
Hrtimer_softirq,
Rcu_softirq,/* preferable RCU shoshould always be the last softirq */
Nr_softirqs
};
The following code is available in the Linux \ kernels \ mips-linux-2.6.15 \ net \ core \ Dev. c file of the kernel:
Void open_softirq (int nr, void (* Action) (struct softirq_action *))
{
Softirq_vec [Nr]. Action = action;
}
Open_softirq (net_rx_softirq, net_rx_action, null); // register net_rx_softirq. Kernel Soft Interrupt function net_rx_action ();
3. net_rx_softirq Soft Interrupt settings:
We cannot directly call the soft terminal processing function. We must set the interrupt vector bit before calling it.
/**
* _ Napi_schedule-schedule for receive
* @ N: entry to schedule
*
* The entry's receive function will be scheduled to run
*/
Void _ napi_schedule (struct napi_struct * n)
{
Unsigned long flags;
Local_irq_save (flags );
List_add_tail (& N-> poll_list, & __ get_cpu_var (softnet_data). poll_list );
_ Raise_softirq_irqoff (net_rx_softirq );
Local_irq_restore (flags );
}
4. Execution of net_rx_softirq software interruption
I have seen the following functions before:
Do_irq (ath_cpu_irq_ge0); its definition is as follows:
Unsigned int do_irq (int irq, struct uml_pt_regs * regs)
{
Struct pt_regs * old_regs = set_irq_regs (struct pt_regs *) regs );
Irq_enter ();
_ Do_irq (IRQ );
Irq_exit ();
Set_irq_regs (old_regs );
Return 1;
}
It can be seen from the do_irq () function that when hard interrupt _ do_irq (IRQ) is executed, the irq_exit () function is executed,
* Exit an interrupt context. Process softirqs if needed and possible:
*/
Void irq_exit (void)
{
Account_system_vtime (current );
Trace_hardirq_exit ();
Sub_preempt_count (irq_exit_offset );
If (! In_interrupt () & local_softirq_pending () // you can determine whether a Soft Interrupt is registered when the nested hardware interrupt occurs.
{
Invoke_softirq (); // This function is a macro defined as follows:
}
# Ifdef config_no_hz
/* Make sure that timer wheel updates are propagated */
Rcu_irq_exit ();
If (idle_cpu (smp_processor_id ())&&! In_interrupt ()&&! Need_resched ())
Tick_nohz_stop_sched_tick (0 );
# Endif
Preempt_enable_no_resched ();
}
# Ifdef _ arch_irq_exit_irqs_disabled
# Define invoke_softirq () _ do_softirq ()
# Else
# Define invoke_softirq () do_softirq ()
# Endif
Do_softirq () is defined as follows:
Asmlinkage void do_softirq (void)
{
_ U32 pending;
Unsigned long flags;
If (in_interrupt () // If a hardware terminal exists, return.
Return;
Local_irq_save (flags );
Pending = local_softirq_pending (); // call a Soft Interrupt registration.
If (pending)
_ Do_softirq ();
Local_irq_restore (flags );
}
The _ do_softirq () function is defined as follows:
# Define max_softirq_restart 10
Asmlinkage void _ do_softirq (void)
{
Struct softirq_action * h;
_ U32 pending;
Int max_restart = max_softirq_restart;
Int CPU;
Pending = local_softirq_pending ();
Account_system_vtime (current );
_ Local_bh_disable (unsigned long) _ builtin_return_address (0 ));
Lockdep_softirq_enter ();
CPU = smp_processor_id ();
Restart:
/* Reset the pending bitmask before enabling irqs */
Set_softirq_pending (0 );
Local_irq_enable ();
H = softirq_vec;
Do {
If (pending & 1 ){
Int prev_count = preempt_count ();
Kstat_incr_softirqs_this_cpu (H-softirq_vec );
Trace_softirq_entry (H, softirq_vec );
/*
// Call the functions of the registered soft terminal. We will call open_softirq (net_rx_softirq, net_rx_action, null );
Registered function static void net_rx_action (struct softirq_action * H ),
In the net_rx_action () function, you can call our network-driven poll () function.
Allows you to query and accept data.
*/
H-> action (h );
Trace_softirq_exit (H, softirq_vec );
If (unlikely (prev_count! = Preempt_count ())){
Printk (kern_err "huh, entered softirq % TD % S % P"
"With preempt_count % 08x ,"
"Exited with % 08x? \ N ", H-softirq_vec,
Softirq_to_name [H-softirq_vec],
H-> action, prev_count, preempt_count ());
Preempt_count () = prev_count;
}
Rcu_bh_qsctr_inc (CPU );
}
H ++;
Pending> = 1;
} While (pending );
Local_irq_disable ();
Pending = local_softirq_pending ();
If (pending & -- max_restart)
Goto restart;
If (pending)
Wakeup_softirqd ();
Lockdep_softirq_exit ();
Account_system_vtime (current );
_ Local_bh_enable ();
}