当前位置:   article > 正文

linux硬件中断处理流程3----NAPI_napi_schedule_prep函数详解

napi_schedule_prep函数详解

1 NAPI简介

现在的的网卡为了提升性能,大部分已经已经使用NAPI的方式接受数据帧。linxu内核使用了struct napi_struct来管理NAPI设备的新特性和操作。系统受到数据包后,支持NAPI模式的网络设备会将网络设备的struct napi_struct数据结构的实例放到CPU的struct softnet_data数据结构的poll_list中。当网络子系统接受到软中断NET_RX_SOFTIRQ被指定时,会遍历CPU的Poll_list链表依次执行NAPI设备的poll方法,poll方法一次会从硬件缓冲区读取多个数据帧。所以struct napi_struct数据结构的设计应该考虑以下因素:

  • 在SMP系统中,设备的poll函数当前在那个CPU上执行时由网络设备的struct  napi_struct数据结构放在那个CPU的poll_list链表决定的。
  • 虽然NAPI设备一次可以读入多个数据帧,当时每个CPU输入队列的长度是有限的。这也决定poll函数一次杜甫数据帧的数量也要做限制。
  • NAPI的poll函数时在网络子系统的接收软件中断中执行的,由于软件中断不能执行太长的时间,一面占用CPU资源太久,影响其他任务的处理,这也决定了我们需要限制每个设备poll执行时间。
  • 当前以NAPI方式接收网络设备的是那个网络设备。
  • 当前NAPI设备的状态:poll函数是正在等待被调用执行,还是已执行完成等。

2 struct napi_struct

  1. struct napi_struct {
  2. struct list_head poll_list;
  3. unsigned long state;
  4. int weight;
  5. unsigned int gro_count;
  6. int (*poll)(struct napi_struct *, int);
  7. #ifdef CONFIG_NETPOLL
  8. spinlock_t poll_lock;
  9. int poll_owner;
  10. #endif
  11. struct net_device *dev;
  12. struct sk_buff *gro_list;
  13. struct sk_buff *skb;
  14. struct list_head dev_list;
  15. struct hlist_node napi_hash_node;
  16. unsigned int napi_id;
  17. };

struct napi_struct数据结构中各数据域的含义如下:

  • poll_list:将网络设备的struct napi_struct数据结构实例链接到CPU的poll_list列表中的数据域。poll_list列表中的网络设备都是已经产生中断的网络设备,他们通知CPU收到了网络数据帧。而他们的poll函数等待在接收软件中断被调用执行,来读入网络数据帧。
  • state:poll_list列表中的NAPI网络设备,只由state变量中的NAPI_STATE_SCHED位管理;NAPI_STATE_SCHED标志置位的网络设,它的napi_struct数据结构实例就会加入到CPU的poll_list链表中,NAPI_STATE_SCHED为清除的设备就会从poll_list列表移除。
  • weight:定义了每个设备的poll函数在软中断执行期间poll函数可以从网络设备缓冲区读入的最大数据帧数,以太网默认是64,我们可以通过/proc/sys/core/dev_weitht文件读取或者修改这个值。
  • poll:网络设备的poll函数的指针
  • dev_list:内核的全局网络设备列表,该列表管理系统中所有网络设备的struct net_device数据结构实例。
  • *dev:内核的全局网络设备列表。该列表管理系统中所有网络设备的struct net_device数据结构实例。
  • *gro_list:数据帧被分割成片段后,所有分片数据Socket Buffer的列表。

3 NAPI硬件中断处理

NAPI的硬件中断主要工作就是将网络设备的napi实例添加到CPU的poll_list链表中等待调度,所以他的处理是要分成两部分的。我们以e1000网卡为实例,e1000的硬件中断注册调用e1000_request_irq完成,其中的硬件中断处理函数就是e1000_intr。

  1. static int e1000_request_irq(struct e1000_adapter *adapter)
  2. {
  3. struct net_device *netdev = adapter->netdev;
  4. irq_handler_t handler = e1000_intr; //硬件中断段处理函数
  5. int irq_flags = IRQF_SHARED;
  6. int err;
  7. err = request_irq(adapter->pdev->irq, handler, irq_flags, netdev->name,
  8. netdev);
  9. if (err) {
  10. e_err(probe, "Unable to allocate interrupt Error: %d\n", err);
  11. }
  12. return err;
  13. }

硬件中断处理函数e1000_intr首先调用napi_schedule_prep判断数据域state的NAPI_STATE_SCHED是否设置, 如果已经设置说明设备已经在poll_list列表中。如果没有设置就__napi_schedule调用用__napi_schedule继续处理,并设置state的NAPI_STATE_SCHED位。

  1. static irqreturn_t e1000_intr(int irq, void *data)
  2. {
  3. struct net_device *netdev = data;
  4. struct e1000_adapter *adapter = netdev_priv(netdev);
  5. struct e1000_hw *hw = &adapter->hw;
  6. u32 icr = er32(ICR);
  7. if (unlikely((!icr)))
  8. return IRQ_NONE; /* Not our interrupt */
  9. /* we might have caused the interrupt, but the above
  10. * read cleared it, and just in case the driver is
  11. * down there is nothing to do so return handled
  12. */
  13. if (unlikely(test_bit(__E1000_DOWN, &adapter->flags))) //判断网卡是否down
  14. return IRQ_HANDLED;
  15. if (unlikely(icr & (E1000_ICR_RXSEQ | E1000_ICR_LSC))) {
  16. hw->get_link_status = 1;
  17. /* guard against interrupt when we're going down */
  18. if (!test_bit(__E1000_DOWN, &adapter->flags))
  19. schedule_delayed_work(&adapter->watchdog_task, 1);
  20. }
  21. /* disable interrupts, without the synchronize_irq bit */
  22. ew32(IMC, ~0);
  23. E1000_WRITE_FLUSH();
  24. if (likely(napi_schedule_prep(&adapter->napi))) { //判断数据域state的NAPI_STATE_SCHED是否设置,
  25. adapter->total_tx_bytes = 0; //如果已经设置说明设备已经在poll_list列表中
  26. adapter->total_tx_packets = 0;
  27. adapter->total_rx_bytes = 0;
  28. adapter->total_rx_packets = 0;
  29. __napi_schedule(&adapter->napi);//将设备的napi_struct添加到CPU的poll_list链表,并标记软中断NET_RX_SOFTIRQ
  30. } else {
  31. /* this really should not happen! if it does it is basically a
  32. * bug, but not a hard error, so enable ints and continue
  33. */
  34. if (!test_bit(__E1000_DOWN, &adapter->flags))
  35. e1000_irq_enable(adapter);
  36. }
  37. return IRQ_HANDLED;
  38. }

__napi_schedule说先是关闭并保存中断,然后调用____napi_schedule继续处理,处理完后恢复中断。

  1. void __napi_schedule(struct napi_struct *n)
  2. {
  3. unsigned long flags;
  4. local_irq_save(flags); //关闭并保存中断
  5. ____napi_schedule(this_cpu_ptr(&softnet_data), n);
  6. local_irq_restore(flags); //恢复中断
  7. }

____napi_schedule主要做两件事:

  • 将网络设备的napi_struct添加到CPU的poll_list队列中
  • 标记接收数据软中断NET_RX_SOFTIRQ
  1. static inline void ____napi_schedule(struct softnet_data *sd,
  2. struct napi_struct *napi)
  3. {
  4. list_add_tail(&napi->poll_list, &sd->poll_list); //将软中断处理函数添加到CPU的poll_list
  5. __raise_softirq_irqoff(NET_RX_SOFTIRQ); //标记软件中断
  6. }

至此网络设备的硬件中断已经处理完成,接下来由软件中断中调用napi的poll函数从设备缓冲区拷贝数据帧到内核空间。

声明:本文内容由网友自发贡献,不代表【wpsshop博客】立场,版权归原作者所有,本站不承担相应法律责任。如您发现有侵权的内容,请联系我们。转载请注明出处:https://www.wpsshop.cn/w/Monodyee/article/detail/252919
推荐阅读
相关标签
  

闽ICP备14008679号