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

......

文章分类

全部博文(52)

文章存档

2014年(1)

2013年(32)

2012年(19)

我的朋友

分类: LINUX

2013-11-25 21:24:55

ixgb网卡是intel公司推出的一款网卡芯片,很强大,具体一些属性可以百度。linux下此网卡芯片的驱动,在数据包接收过程中采用了non_napi 和napi两种方式,要采用napi方式要把宏CONFIG_IXGB_NAPI打开。另外在构建接收和发送数据缓冲区也和8139网卡芯片有不同之处。下面具体分析一下

I)初始化

因为ixgb网卡也是PCI设备,所以按套路来一路到ixgb_probe函数

点击(此处)折叠或打开

  1. if((err = pci_enable_device(pdev)))
  2.         return err;

  3.     if(!(err = pci_set_dma_mask(pdev, DMA_64BIT_MASK)) &&
  4.      !(err = pci_set_consistent_dma_mask(pdev, DMA_64BIT_MASK))) {
  5.         pci_using_dac = 1;
  6.     } else {
  7.         if((err = pci_set_dma_mask(pdev, DMA_32BIT_MASK)) ||
  8.          (err = pci_set_consistent_dma_mask(pdev, DMA_32BIT_MASK))) {
  9.             printk(KERN_ERR
  10.              "ixgb: No usable DMA configuration, aborting\n");
  11.             goto err_dma_mask;
  12.         }
  13.         pci_using_dac = 0;
  14.     }

  15.     if((err = pci_request_regions(pdev, ixgb_driver_name)))
  16.         goto err_request_regions;


  17. mmio_start = pci_resource_start(pdev, BAR_0);
  18.     mmio_len = pci_resource_len(pdev, BAR_0);

  19.     adapter->hw.hw_addr = ioremap(mmio_start, mmio_len);
  20.     if(!adapter->hw.hw_addr) {
  21.         err = -EIO;
  22.         goto err_ioremap;
  23.     }
这段代码进行了网卡芯片板载内存映射,得到adapter->hw.hw_addr虚拟地址,以后在配置网卡芯片寄存器时就用这个基地址+偏移量的方式来直接配置。

接下来就对接收和发送缓冲区进行初始化
 

点击(此处)折叠或打开

  1. int
  2. ixgb_setup_tx_resources(struct ixgb_adapter *adapter)
  3. {
  4.     struct ixgb_desc_ring *txdr = &adapter->tx_ring;
  5.     struct pci_dev *pdev = adapter->pdev;
  6.     int size;

  7.     size = sizeof(struct ixgb_buffer) * txdr->count;
  8.     txdr->buffer_info = vmalloc(size);      //分配内存时可以用不连续的内存块
  9.     if(!txdr->buffer_info) {
  10.         DPRINTK(PROBE, ERR,
  11.          "Unable to allocate transmit descriptor ring memory\n");
  12.         return -ENOMEM;
  13.     }
  14.     memset(txdr->buffer_info, 0, size);

  15.     /* round up to nearest 4K */

  16.     txdr->size = txdr->count * sizeof(struct ixgb_tx_desc);
  17.     IXGB_ROUNDUP(txdr->size, 4096);

  18.     txdr->desc = pci_alloc_consistent(pdev, txdr->size, &txdr->dma);   //对发送描述符进行了一致性内存映射
  19.     if(!txdr->desc) {
  20.         vfree(txdr->buffer_info);
  21.         DPRINTK(PROBE, ERR,
  22.          "Unable to allocate transmit descriptor memory\n");
  23.         return -ENOMEM;
  24.     }
  25.     memset(txdr->desc, 0, txdr->size);

  26.     txdr->next_to_use = 0; 
  27.     txdr->next_to_clean = 0;

  28.     return 0;
  29. }


点击(此处)折叠或打开

  1. int
  2. ixgb_setup_rx_resources(struct ixgb_adapter *adapter)
  3. {
  4.     struct ixgb_desc_ring *rxdr = &adapter->rx_ring;
  5.     struct pci_dev *pdev = adapter->pdev;
  6.     int size;

  7.     size = sizeof(struct ixgb_buffer) * rxdr->count;
  8.     rxdr->buffer_info = vmalloc(size);
  9.     if(!rxdr->buffer_info) {
  10.         DPRINTK(PROBE, ERR,
  11.          "Unable to allocate receive descriptor ring\n");
  12.         return -ENOMEM;
  13.     }
  14.     memset(rxdr->buffer_info, 0, size);

  15.     /* Round up to nearest 4K */

  16.     rxdr->size = rxdr->count * sizeof(struct ixgb_rx_desc);
  17.     IXGB_ROUNDUP(rxdr->size, 4096);

  18.     rxdr->desc = pci_alloc_consistent(pdev, rxdr->size, &rxdr->dma);

  19.     if(!rxdr->desc) {
  20.         vfree(rxdr->buffer_info);
  21.         DPRINTK(PROBE, ERR,
  22.          "Unable to allocate receive descriptors\n");
  23.         return -ENOMEM;
  24.     }
  25.     memset(rxdr->desc, 0, rxdr->size);

  26.     rxdr->next_to_clean = 0;
  27.     rxdr->next_to_use = 0;

  28.     return 0;
  29. }
初始化后,数据结构如下(以接收环形缓冲区为例):


从图中可以看到对描述符进行了一致性dma映射,这样在以后的操作中可以直接读取此描述符地址从而得到ixgb网卡某些状态,另外在以后的接收分析介绍中可以看到对每个接收的skb进行了流式DMA映射。
   代码中对rxdr->next_to_clean = 0;   rxdr->next_to_use = 0; 进行了初始化, 用这两个变量和rxdr->count实现了环形。

接下来要对环形接收缓冲区进行配置, 在ixgb_configure_rx函数中把描述符对应的物理地址写入到网卡寄存器中去(为什么要这么做,得看ixgb网卡芯片datasheet)
uint64_t rdba = adapter->rx_ring.dma;
/* Setup the Base and Length of the Rx Descriptor Ring */
IXGB_WRITE_REG(hw, RDBAL, (rdba & 0x00000000ffffffffULL)); 
 IXGB_WRITE_REG(hw, RDBAH, (rdba >> 32));  
IXGB_WRITE_REG(hw, RDLEN, rdlen);

/* Setup the HW Rx Head and Tail Descriptor Pointers */
    IXGB_WRITE_REG(hw, RDH, 0);
    IXGB_WRITE_REG(hw, RDT, 0); 

在接收数据包之前要先开辟rxdr->count 个 skb内存来接收数据包,并对这些skb进行了流式DMA映射。函数ixgb_alloc_rx_buffers完成了此项工作,代码如下:

点击(此处)折叠或打开

  1. static void
  2. ixgb_alloc_rx_buffers(struct ixgb_adapter *adapter)
  3. {
  4.     struct ixgb_desc_ring *rx_ring = &adapter->rx_ring;
  5.     struct net_device *netdev = adapter->netdev;
  6.     struct pci_dev *pdev = adapter->pdev;
  7.     struct ixgb_rx_desc *rx_desc;
  8.     struct ixgb_buffer *buffer_info;
  9.     struct sk_buff *skb;
  10.     unsigned int i;
  11.     int num_group_tail_writes;
  12.     long cleancount;

  13.     i = rx_ring->next_to_use;
  14.     buffer_info = &rx_ring->buffer_info[i];
  15.     cleancount = IXGB_DESC_UNUSED(rx_ring);

  16.     num_group_tail_writes = IXGB_RX_BUFFER_WRITE;

  17.     /* leave three descriptors unused */
  18.     while(--cleancount > 2) {
  19.         /* its good for you */
  20.         skb = buffer_info->skb;
  21.         if (skb) {
  22.             skb_trim(skb, 0);
  23.             goto map_skb;
  24.         }

  25.         skb = netdev_alloc_skb(netdev, adapter->rx_buffer_len
  26.              + NET_IP_ALIGN);
  27.         if (unlikely(!skb)) {
  28.             /* Better luck next round */
  29.             adapter->alloc_rx_buff_failed++;
  30.             break;
  31.         }

  32.         /* Make buffer alignment 2 beyond a 16 byte boundary
  33.          * this will result in a 16 byte aligned IP header after
  34.          * the 14 byte MAC header is removed
  35.          */
  36.         skb_reserve(skb, NET_IP_ALIGN);

  37.         buffer_info->skb = skb;
  38.         buffer_info->length = adapter->rx_buffer_len;
  39. map_skb:
  40.         buffer_info->dma = pci_map_single(pdev,
  41.          skb->data,
  42.          adapter->rx_buffer_len,
  43.          PCI_DMA_FROMDEVICE);       //在这里进行了流式DMA映射,这时这块内存属于网卡,CPU不能使用。在中断函数中要调用配对函数让这块内存属于CPU,完成数据包的接收

  44.         rx_desc = IXGB_RX_DESC(*rx_ring, i);
  45.         rx_desc->buff_addr = cpu_to_le64(buffer_info->dma);
  46.         /* guarantee DD bit not set now before h/w gets descriptor
  47.          * this is the rest of the workaround for h/w double
  48.          * writeback. */
  49.         rx_desc->status = 0;


  50.         if(++i == rx_ring->count) i = 0;
  51.         buffer_info = &rx_ring->buffer_info[i];
  52.     }

  53.     if (likely(rx_ring->next_to_use != i)) {
  54.         rx_ring->next_to_use = i;
  55.         if (unlikely(i-- == 0))
  56.             i = (rx_ring->count - 1);

  57.         /* Force memory writes to complete before letting h/w
  58.          * know there are new descriptors to fetch. (Only
  59.          * applicable for weak-ordered memory model archs, such
  60.          * as IA-64). */
  61.         wmb();
  62.         IXGB_WRITE_REG(&adapter->hw, RDT, i);
  63.     }
  64. }



 


阅读(4941) | 评论(1) | 转发(0) |
给主人留下些什么吧!~~