Chinaunix首页 | 论坛 | 博客
  • 博客访问: 258727
  • 博文数量: 52
  • 博客积分: 406
  • 博客等级: 一等列兵
  • 技术积分: 549
  • 用 户 组: 普通用户
  • 注册时间: 2012-04-21 12:34
个人简介

......

文章分类

全部博文(52)

文章存档

2014年(1)

2013年(32)

2012年(19)

我的朋友

分类: LINUX

2013-11-30 11:52:10

2. napi 接收方式

还是得从接收中断处理函数开始,在static irqreturn_t   ixgb_intr(int irq, void *data)函数中:

点击(此处)折叠或打开

  1. #ifdef CONFIG_IXGB_NAPI
  2.     if(netif_rx_schedule_prep(netdev)) {   //判断设备是否启动,以及是否已经处于接收状态

  3.         /* Disable interrupts and register for poll. The flush
  4.          of the posted write is intentionally left out.
  5.         */

  6.         atomic_inc(&adapter->irq_sem);
  7.         IXGB_WRITE_REG(&adapter->hw, IMC, ~0);   //在这里关闭接收中断
  8.         __netif_rx_schedule(netdev);             //在这个函数中,把设备挂到软中断接收调度队列中,并分配在初始化中设置的配额大小dev->quota = dev->weight; 然后
  9.                                                  //触法接收软中断
  10.     }
   现在这个中断处理函数就算处理完了,所做的工作就是对接收硬件中断掩码(即使来了硬件中断也不告知CPU),把设备挂接到接收软中断调度队列中,然后触法软中断,让软中断去处理数据包。接下来看接收
软中断处理函数就行了(还记得上一篇已经提到这个函数),再贴上这个函数吧:

点击(此处)折叠或打开

  1. /*
  2.   *给每个CPU的软中断调度队列分配一个预算,
  3.   * 每个网卡有自己的配额, 一次软中断处理完这个预算,
  4.   *然后再次触法软中断 。。。
  5.   *挂在这个软中断调度队列的所有的网卡以配额为单位去吃这个预算
  6.   *每个网卡以配额为单位吃掉后挂在软中断调度队列的尾部,让给
  7.   *其他网卡去吃预算
  8.   */
  9. static void net_rx_action(struct softirq_action *h)
  10. {
  11.     struct softnet_data *queue = &__get_cpu_var(softnet_data);
  12.     unsigned long start_time = jiffies;
  13.     int budget = netdev_budget; //一次软中断的预算
  14.     void *have;

  15.     local_irq_disable();

  16.     while (!list_empty(&queue->poll_list)) {
  17.         struct net_device *dev;

  18.         if (budget <= 0 || jiffies - start_time > 1) //预算不够了,或者包处理时间超过1s,直接再次触法软中断
  19.             goto softnet_break;

  20.         local_irq_enable();

  21.         dev = list_entry(queue->poll_list.next,
  22.                  struct net_device, poll_list); //遍历链表,找到接收设备
  23.         have = netpoll_poll_lock(dev);

  24.         if (dev->quota <= 0 || dev->poll(dev, &budget)) {
  25.             /*
  26.              *配额用完了或者还有很多数据包要接收
  27.              */
  28.             netpoll_poll_unlock(have);
  29.             local_irq_disable();
  30.             list_move_tail(&dev->poll_list, &queue->poll_list); //把接收链表删除,并再次添加到softnet结构中

  31.             //再次设置配额,然后继续轮询接收剩下的数据包
  32.             if (dev->quota < 0)
  33.                 dev->quota += dev->weight;
  34.             else
  35.                 dev->quota = dev->weight;
  36.         } else {
  37.          //收到的数据包小于配额,则这一次轮询接收就完事了,要打开接收中断,等待数据包的到来
  38.             netpoll_poll_unlock(have);
  39.             dev_put(dev);
  40.             local_irq_disable();
  41.         }
  42.     }
  43. out:
  44. #ifdef CONFIG_NET_DMA
  45.     /*
  46.      * There may not be any more sk_buffs coming right now, so push
  47.      * any pending DMA copies to hardware
  48.      */
  49.     if (net_dma_client) {
  50.         struct dma_chan *chan;
  51.         rcu_read_lock();
  52.         list_for_each_entry_rcu(chan, &net_dma_client->channels, client_node)
  53.             dma_async_memcpy_issue_pending(chan);
  54.         rcu_read_unlock();
  55.     }
  56. #endif
  57.     local_irq_enable();
  58.     return;

  59. softnet_break:
  60.     __get_cpu_var(netdev_rx_stat).time_squeeze++;
  61.     __raise_softirq_irqoff(NET_RX_SOFTIRQ);
  62.     goto out;
  63. }
注意在这里dev是设备自己的,在初始化时进行创建的。在中断函数中我们把它挂在了软中断接收调度队列中。自然而然的,dev->quota ,dev->poll 都是我们在初始化时注册的。下面我们分析初始化时注册的这个接收函数:

点击(此处)折叠或打开

  1. #ifdef CONFIG_IXGB_NAPI
  2.     netdev->poll = &ixgb_clean;  //这就是初始化时我们注册的函数,这个是做驱动必须完成的函数
  3.     netdev->weight = 64;         //这个配额可以根据自己项目的需要进行调节,一般如果流量较大的话,这个值就设置大一点。
  4. #endif



  5. static int
  6. ixgb_clean(struct net_device *netdev, int *budget)
  7. {
  8.     struct ixgb_adapter *adapter = netdev_priv(netdev);
  9.     int work_to_do = min(*budget, netdev->quota);  //取预算和配额 较小的一个
  10.     int tx_cleaned;
  11.     int work_done = 0;

  12.     tx_cleaned = ixgb_clean_tx_irq(adapter);
  13.     ixgb_clean_rx_irq(adapter, &work_done, work_to_do); //在这个函数中进行接收,送到上层协议栈

  14.     *budget -= work_done;    //减少预算
  15.     netdev->quota -= work_done;  //减少配额

  16.     /* if no Tx and not enough Rx work done, exit the polling mode */
  17.     if((!tx_cleaned && (work_done == 0)) || !netif_running(netdev)) {
  18.         netif_rx_complete(netdev);  //没有数据包了,就从软中断接收队列中把设备删除掉,并设置设备的状态,为下一次中断做准备
  19.         ixgb_irq_enable(adapter);   //在这里把硬件接收中断掩码 打开
  20.         return 0;
  21.     }

  22.     return 1;  //还有很多数据包要接收,就等着再次被调度
  23. }

ixgb_clean_rx_irq 这个函数在上一篇non_napi中已经分析过了, 贴过来吧,保持完整性。


点击(此处)折叠或打开

  1. static boolean_t
  2.     #ifdef CONFIG_IXGB_NAPI
  3.     ixgb_clean_rx_irq(struct ixgb_adapter *adapter, int *work_done, int work_to_do)
  4.     #else
  5.     ixgb_clean_rx_irq(struct ixgb_adapter *adapter)
  6.     #endif
  7.     {
  8.         struct ixgb_desc_ring *rx_ring = &adapter->rx_ring;
  9.         struct net_device *netdev = adapter->netdev;
  10.         struct pci_dev *pdev = adapter->pdev;
  11.         struct ixgb_rx_desc *rx_desc, *next_rxd;
  12.         struct ixgb_buffer *buffer_info, *next_buffer, *next2_buffer;
  13.         uint32_t length;
  14.         unsigned int i, j;
  15.         boolean_t cleaned = FALSE;

  16.         i = rx_ring->next_to_clean;
  17.         rx_desc = IXGB_RX_DESC(*rx_ring, i);
  18.         buffer_info = &rx_ring->buffer_info[i];

  19.         while(rx_desc->status & IXGB_RX_DESC_STATUS_DD) { //如前面分析rx_desc是描述符,进行了一致性DMA映射,所以直接读取其值判断这个中断是否是数据包到来产生的中断
  20.             struct sk_buff *skb, *next_skb; //因为对skb进行了DMA映射,一次中断最多可以接收rxd->count个数据包,所以这里对rxd->count个描述符轮询
  21.             u8 status;

  22.     #ifdef CONFIG_IXGB_NAPI
  23.             if(*work_done >= work_to_do)   //超过配额就不接收了,接收这么多,得让给其他网卡来接收了,不能这么自私
  24.                 break;

  25.             (*work_done)++;  //对接收的数据包进行计数
  26.     #endif
  27.             status = rx_desc->status;
  28.             skb = buffer_info->skb;
  29.             buffer_info->skb = NULL;

  30.             prefetch(skb->data);

  31.             if(++i == rx_ring->count) i = 0;
  32.             next_rxd = IXGB_RX_DESC(*rx_ring, i); //得到下一个描述符
  33.             prefetch(next_rxd);

  34.             if((j = i + 1) == rx_ring->count) j = 0;
  35.             next2_buffer = &rx_ring->buffer_info[j]; //预先取得第二个buffer
  36.             prefetch(next2_buffer);

  37.             next_buffer = &rx_ring->buffer_info[i]; //得到下一个buffer
  38.             next_skb = next_buffer->skb;
  39.             prefetch(next_skb);

  40.             cleaned = TRUE;

  41.             pci_unmap_single(pdev,
  42.                      buffer_info->dma,
  43.                      buffer_info->length,
  44.                      PCI_DMA_FROMDEVICE); //把控制权还给CPU

  45.             length = le16_to_cpu(rx_desc->length);

  46.             if(unlikely(!(status & IXGB_RX_DESC_STATUS_EOP))) {

  47.                 /* All receives must fit into a single buffer */

  48.                 IXGB_DBG("Receive packet consumed multiple buffers "
  49.                          "length<%x>\n", length);

  50.                 dev_kfree_skb_irq(skb);
  51.                 goto rxdesc_done;
  52.             }

  53.             if (unlikely(rx_desc->errors
  54.                  & (IXGB_RX_DESC_ERRORS_CE | IXGB_RX_DESC_ERRORS_SE
  55.                     | IXGB_RX_DESC_ERRORS_P |
  56.                     IXGB_RX_DESC_ERRORS_RXE))) {

  57.                 dev_kfree_skb_irq(skb);
  58.                 goto rxdesc_done;
  59.             }

  60.             /* code added for copybreak, this should improve
  61.              * performance for small packets with large amounts
  62.              * of reassembly being done in the stack */
  63.     #define IXGB_CB_LENGTH 256
  64.             if (length < IXGB_CB_LENGTH) {
  65.                 struct sk_buff *new_skb =
  66.                  netdev_alloc_skb(netdev, length + NET_IP_ALIGN);
  67.                 if (new_skb) {
  68.                     skb_reserve(new_skb, NET_IP_ALIGN);
  69.                     memcpy(new_skb->data - NET_IP_ALIGN,
  70.                      skb->data - NET_IP_ALIGN,
  71.                      length + NET_IP_ALIGN);
  72.                     /* save the skb in buffer_info as good */
  73.                     buffer_info->skb = skb;
  74.                     skb = new_skb;
  75.                 }
  76.             }
  77.             /* end copybreak code */

  78.             /* Good Receive */
  79.             skb_put(skb, length);

  80.             /* Receive Checksum Offload */
  81.             ixgb_rx_checksum(adapter, rx_desc, skb);

  82.             skb->protocol = eth_type_trans(skb, netdev);
  83.     #ifdef CONFIG_IXGB_NAPI
  84.             if(adapter->vlgrp && (status & IXGB_RX_DESC_STATUS_VP)) {
  85.                 vlan_hwaccel_receive_skb(skb, adapter->vlgrp,
  86.                     le16_to_cpu(rx_desc->special) &
  87.                         IXGB_RX_DESC_SPECIAL_VLAN_MASK);
  88.             } else {
  89.                 netif_receive_skb(skb);
  90.             }
  91.     #else /* CONFIG_IXGB_NAPI */
  92.             if(adapter->vlgrp && (status & IXGB_RX_DESC_STATUS_VP)) {
  93.                 vlan_hwaccel_rx(skb, adapter->vlgrp,
  94.                     le16_to_cpu(rx_desc->special) &
  95.                         IXGB_RX_DESC_SPECIAL_VLAN_MASK);
  96.             } else {
  97.                 netif_rx(skb); //non_napi 调用的接收函数
  98.             }
  99.     #endif /* CONFIG_IXGB_NAPI */
  100.             netdev->last_rx = jiffies;

  101.     rxdesc_done:
  102.             /* clean up descriptor, might be written over by hw */
  103.             rx_desc->status = 0;

  104.             /* use prefetched values */
  105.             rx_desc = next_rxd; //指向下一个描述符
  106.             buffer_info = next_buffer; //指向下一个buffer
  107.         }

  108.         rx_ring->next_to_clean = i; //移动位置

  109.         ixgb_alloc_rx_buffers(adapter); //再分配前面吃掉的skb,用多少再分配多少

  110.         return cleaned;
  111.     }

over...

其实从上面我们可以看到,网卡驱动,从原理上看还是比较简单的,但如果是一款新的网卡芯片,我们就必须读datasheet,来配置相关寄存器使网卡能正常工作,这个应该比较花时间,剩下的工作可以copy先有的网卡驱动代码,根据需要进行修改就可以了。
阅读(3424) | 评论(0) | 转发(0) |
给主人留下些什么吧!~~