网卡驱动数据包接收流程——基于RTL8139网卡

 1 中断函数

static irqreturn_t rtl8139_interrupt (int irq, void *dev_instance)

所有有网卡产生的中断都会引起该中断函数的调用,这是在

static int rtl8139_open (struct net_device *dev)
{
 struct rtl8139_private *tp = netdev_priv(dev);
 int retval;
 void __iomem *ioaddr = tp->mmio_addr;
        /* 注册中断处理函数 ,中断号为共享 */
 retval = request_irq (dev->irq, rtl8139_interrupt, IRQF_SHARED, dev->name, dev);

完成的。

中断函数处理的事件可以大致分为几类:

A 数据包到达产生的中断(RxAckBits = RxFIFOOver | RxOverflow | RxOK);

B 异常事件,通常都是出错的情况(RxAckBits = RxFIFOOver | RxOverflow | RxOK)

C发送完成事件(TxOK | TxErr)

下面我们看看具体的代码

static irqreturn_t rtl8139_interrupt (int irq, void *dev_instance)
{
        /* 参数dev_instance是在上面注册中断处理函数的时候传入的 */
 struct net_device *dev = (struct net_device *) dev_instance;
        /* tp 为网卡驱动自定义的驱动特有的数据,和dev一起分配的 */
 struct rtl8139_private *tp = netdev_priv(dev);
 void __iomem *ioaddr = tp->mmio_addr;
 u16 status, ackstat;
 int link_changed = 0; /* avoid bogus "uninit" warning */
 int handled = 0;
        /* 对驱动数据加锁*/
 spin_lock (&tp->lock);
        /*读中断状态寄存器,获取中断状态*/
 status = RTL_R16 (IntrStatus);

 /* 这时由共享此中断号的其它设备产生的中断 */
 if (unlikely((status & rtl8139_intr_mask) == 0))
  goto out;

 handled = 1;

 /* 硬件错误 */
 if (unlikely(status == 0xFFFF))
  goto out;

 /* 设备已关闭*/
 if (unlikely(!netif_running(dev))) {
                /* 屏蔽所有中断*/
  RTL_W16 (IntrMask, 0);
  goto out;
 }

 /* Acknowledge all of the current interrupt sources ASAP, but
    an first get an additional status bit from CSCR. */
 if (unlikely(status & RxUnderrun))
  link_changed = RTL_R16 (CSCR) & CSCR_LinkChangeBit;

 ackstat = status & ~(RxAckBits | TxErr);
 if (ackstat)
  RTL_W16 (IntrStatus, ackstat);

下一步处理数据包到达事件

/* Receive packets are processed by poll routine.
    If not running start it now. */
 if (status & RxAckBits){
  if (netif_rx_schedule_prep(dev, &tp->napi)) {
   RTL_W16_F (IntrMask, rtl8139_norx_intr_mask);
   __netif_rx_schedule(dev, &tp->napi);
  }
 }

先强调下8139网卡驱动的数据接收方式采用的是Linux内核的NAPI新机制。

NAPI 是 Linux 上采用的一种提高网络处理效率的技术,它的核心概念就是不采用中断的方式读取数据,而代之以首先采用中断唤醒数据接收的服务程序,然后 POLL 的方法来轮询数据。随着网络的接收速度的增加,NIC 触发的中断能做到不断减少。

使用 NAPI 先决条件:
驱动可以继续使用老的 2.4 内核的网络驱动程序接口,NAPI 的加入并不会导致向前兼容性的丧失,但是 NAPI 的使用至少要得到下面的保证:
1.         要使用 DMA 的环形输入队列(也就是 ring_dma,这个在 2.4 驱动中关于 Ethernet 的部分有详细的介绍),或者是有足够的内存空间缓存驱动获得的包。
2.         在发送/接收数据包产生中断的时候有能力关断 NIC 中断的事件处理,并且在关断 NIC 以后,并不影响数据包接收到网络设备的环形缓冲区(以下简称 rx-ring)处理队列中。 (这就是锁NAPI机制并不是所有的网卡都能支持的,只有在关闭中断的情况下,任然能通过DMA接收数据包的网卡才可以)
NAPI 对数据包到达的事件的处理采用轮询方法,在数据包达到的时候,NAPI 就会强制执行dev->poll 方法。而和不像以前的驱动那样为了减少包到达时间的处理延迟,通常采用中断的方法来进行。

--------------------------------------------------------------------------------

 如果定义了采用NAPI模式接收数据包,则进入这个调用点。首先调用netif_rx_schedule_prep(dev),确定设备处于运行,而且设备还没有被添加到网络层的 POLL 处理队列中,在调用 netif_rx_schedule之前会调用这个函数。接下来调用__netif_rx_schedule(dev),将设备的 POLL 方法添加到网络层次的 POLL 处理队列中去,排队并且准备接收数据包,在使用之前需要调用 netif_rx_reschedule_prep,并且返回的数为 1,并且触发一个 NET_RX_SOFTIRQ 的软中断通知网络层接收数据包。处理完成。

下面介绍一下__netif_rx_schedule(netdev)函数的作用:

static inline void __netif_rx_schedule(struct net_device *dev)
{
       unsigned long flags;
       /*    获取当前CPU。   */
int cpu = smp_processor_id();
 
       local_irq_save(flags);
       dev_hold(dev);
       /*将当前设备加入CPU相关全局队列softnet_data的轮询设备列表中,不过值得注意的是,这个列表中的设备不一定都执行轮询接收数据包,这里的poll_list只是表示当前设备需要接收数据,具体采用中断还是轮询的方式,取决于设备提供的poll方法。*/
       list_add_tail(&dev->poll_list, &softnet_data[cpu].poll_list);
      
       /*
       调用函数产生网络接收软中断。也就是系统将运行net_rx_action()处理网络数据。
       */
       __cpu_raise_softirq(cpu, NET_RX_SOFTIRQ);
       local_irq_restore(flags);
}
 

接下来是多出错事件以及数据包发送完成事件的处理

/* Check uncommon events with one test. */
 if (unlikely(status & (PCIErr | PCSTimeout | RxUnderrun | RxErr)))
  rtl8139_weird_interrupt (dev, tp, ioaddr,
      status, link_changed);

 if (status & (TxOK | TxErr)) {
  rtl8139_tx_interrupt (dev, tp, ioaddr);
  if (status & TxErr)
   RTL_W16 (IntrStatus, TxErr);
 }

 

出错事件的处理较为简单,只是对一些出错计数的处理。下面我们看看发送完成事件的处理

 

2 发送完成事件处理

static void rtl8139_tx_interrupt (struct net_device *dev,
      struct rtl8139_private *tp,
      void __iomem *ioaddr)
{
 unsigned long dirty_tx, tx_left;

 assert (dev != NULL);
 assert (ioaddr != NULL);
        /*dirty_tx是最近发送数据包时,没有经中断处理的最早数据包所对应的

  • 0
    点赞
  • 5
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值