1. Linux网络数据的接收始于中断,本文用wifi驱动ath10k进行分析,中断处理函数为
ath10k_pci_interrupt_handler,那么中断处理函数是如何与网卡关联?要了解这个
问题就得了解pcie接口网卡的注册流程:
->ath10k_pci_init drivers/net/wireless/ath/ath10k/pci.c
->ath10k_pci_probe
->ath10k_pci_request_irq(ar);
->ath10k_pci_request_irq_msi(ar);
->request_irq(ar_pci->pdev->irq, ath10k_pci_interrupt_handler, ...);
至此我们将中断处理函数ath10k_pci_interrupt_handler和网卡的device结构体关联起来了。
2. 当接收数据唤醒网络中断时,中断处理函数ath10k_pci_interrupt_handler做了些什么事情?
1. 唤醒pcie设备,ath10k_pci_force_wake(ar);
2. 关pcie中断,ath10k_pci_irq_msi_fw_mask(ar),关中断的作用是为了使用NAPI polling
数据。为什么使用NAPI呢?在NAPI之前网络数据的接收模式有两种, polling方式和中
断方式, 如果只使用中断模式的话,在数据量比较大的情况下, CPU不停的进出中断,
基本上无法响应其他进程了。这种情况下使用polling模式比较好,但是polling模式不
适合数据量低的情况。所以NAPI应运而生,在高数据率的情况下使用polling模式,低数
据量的时候使用中断模式。
3. 触发软中断用来接收数据, napi_schedule(&ar->napi);
->__napi_schedule() net/core/dev.c
->__raise_softirq_irqoff(NET_RX_SOFTIRQ); kernel/softirq.c
->or_softirq_pending(1UL << nr);
3. 软中断的action的触发过程? 我们知道在2.3中设置软中断,那么软中断是如何触发的。
通过对GIC framework的分析,我们知道任何硬件中断都会触发软中断。分析过程如下:
gic_handle_irq为gic的isr, 任何一个硬件中断都会调用gic_handle_irq:
->gic_handle_irq drivers/irqchip/irq-gic.c
->handle_domain_irq
->irq_enter:arch/arm/kernel/smp.c
->irq_exit():kernel/softirq.c
->invoke_softirq();
->__do_softirq include/linux/interrupt.h
->while ((softirq_bit = ffs(pending))) h->action(h);
->net_rx_action
4. 为什么会调用的net_rx_action()?而不是其他的软中断处理函数?这个就得从network
framework分析了,网络驱动在net/core/dev.c指定了net的rx和tx对应的action。
net_dev_init() net/core/dev.c
->open_softirq(NET_TX_SOFTIRQ, net_tx_action);
->open_softirq(NET_RX_SOFTIRQ, net_rx_action);
->softirq_vec[nr].action = action; softirq_vec[nr].action = action;
而在上文中,我们知道__napi_schedule()会设置软件中断的flag,
or_softirq_pending(1UL << nr),arm在响应gic_handle_irq()时发现net_rx_action()
对应的flag置位,因此会触发net_rx_action()
5. net_rx_action()是怎么实现接受网络数据的? 具体的code在net/core/dev.c中:
1. 每个CPU都有一个softnet_data, softnet_data有结构体成员poll_list,
____napi_schedule的时候将napi->poll_list,挂在sd->poll_list上, code为
list_add_tail(&napi->poll_list, &sd->poll_list);
2. 遍历sd->poll_list, sd->poll_list的一个节点代表一个网卡设备,每遍历一个节点,
取出节点的内容赋值给napi_struct结构体, 并调用对应的poll函数
for (;;)
{
struct napi_struct *n;
if (list_empty(&list))
{
if (!sd_has_rps_ipi_waiting(sd) && list_empty(&repoll))
return;
break;
}
ath10k_pci_interrupt_handler,那么中断处理函数是如何与网卡关联?要了解这个
问题就得了解pcie接口网卡的注册流程:
->ath10k_pci_init drivers/net/wireless/ath/ath10k/pci.c
->ath10k_pci_probe
->ath10k_pci_request_irq(ar);
->ath10k_pci_request_irq_msi(ar);
->request_irq(ar_pci->pdev->irq, ath10k_pci_interrupt_handler, ...);
至此我们将中断处理函数ath10k_pci_interrupt_handler和网卡的device结构体关联起来了。
2. 当接收数据唤醒网络中断时,中断处理函数ath10k_pci_interrupt_handler做了些什么事情?
1. 唤醒pcie设备,ath10k_pci_force_wake(ar);
2. 关pcie中断,ath10k_pci_irq_msi_fw_mask(ar),关中断的作用是为了使用NAPI polling
数据。为什么使用NAPI呢?在NAPI之前网络数据的接收模式有两种, polling方式和中
断方式, 如果只使用中断模式的话,在数据量比较大的情况下, CPU不停的进出中断,
基本上无法响应其他进程了。这种情况下使用polling模式比较好,但是polling模式不
适合数据量低的情况。所以NAPI应运而生,在高数据率的情况下使用polling模式,低数
据量的时候使用中断模式。
3. 触发软中断用来接收数据, napi_schedule(&ar->napi);
->__napi_schedule() net/core/dev.c
->__raise_softirq_irqoff(NET_RX_SOFTIRQ); kernel/softirq.c
->or_softirq_pending(1UL << nr);
3. 软中断的action的触发过程? 我们知道在2.3中设置软中断,那么软中断是如何触发的。
通过对GIC framework的分析,我们知道任何硬件中断都会触发软中断。分析过程如下:
gic_handle_irq为gic的isr, 任何一个硬件中断都会调用gic_handle_irq:
->gic_handle_irq drivers/irqchip/irq-gic.c
->handle_domain_irq
->irq_enter:arch/arm/kernel/smp.c
->irq_exit():kernel/softirq.c
->invoke_softirq();
->__do_softirq include/linux/interrupt.h
->while ((softirq_bit = ffs(pending))) h->action(h);
->net_rx_action
4. 为什么会调用的net_rx_action()?而不是其他的软中断处理函数?这个就得从network
framework分析了,网络驱动在net/core/dev.c指定了net的rx和tx对应的action。
net_dev_init() net/core/dev.c
->open_softirq(NET_TX_SOFTIRQ, net_tx_action);
->open_softirq(NET_RX_SOFTIRQ, net_rx_action);
->softirq_vec[nr].action = action; softirq_vec[nr].action = action;
而在上文中,我们知道__napi_schedule()会设置软件中断的flag,
or_softirq_pending(1UL << nr),arm在响应gic_handle_irq()时发现net_rx_action()
对应的flag置位,因此会触发net_rx_action()
5. net_rx_action()是怎么实现接受网络数据的? 具体的code在net/core/dev.c中:
1. 每个CPU都有一个softnet_data, softnet_data有结构体成员poll_list,
____napi_schedule的时候将napi->poll_list,挂在sd->poll_list上, code为
list_add_tail(&napi->poll_list, &sd->poll_list);
2. 遍历sd->poll_list, sd->poll_list的一个节点代表一个网卡设备,每遍历一个节点,
取出节点的内容赋值给napi_struct结构体, 并调用对应的poll函数
for (;;)
{
struct napi_struct *n;
if (list_empty(&list))
{
if (!sd_has_rps_ipi_waiting(sd) && list_empty(&repoll))
return;
break;
}