ixgb网卡是intel公司推出的一款网卡芯片,很强大,具体一些属性可以百度。linux下此网卡芯片的驱动,在数据包接收过程中采用了non_napi 和napi两种方式,要采用napi方式要把宏CONFIG_IXGB_NAPI打开。另外在构建接收和发送数据缓冲区也和8139网卡芯片有不同之处。下面具体分析一下
I)初始化
因为ixgb网卡也是PCI设备,所以按套路来一路到ixgb_probe函数
-
if((err = pci_enable_device(pdev)))
-
return err;
-
-
if(!(err = pci_set_dma_mask(pdev, DMA_64BIT_MASK)) &&
-
!(err = pci_set_consistent_dma_mask(pdev, DMA_64BIT_MASK))) {
-
pci_using_dac = 1;
-
} else {
-
if((err = pci_set_dma_mask(pdev, DMA_32BIT_MASK)) ||
-
(err = pci_set_consistent_dma_mask(pdev, DMA_32BIT_MASK))) {
-
printk(KERN_ERR
-
"ixgb: No usable DMA configuration, aborting\n");
-
goto err_dma_mask;
-
}
-
pci_using_dac = 0;
-
}
-
-
if((err = pci_request_regions(pdev, ixgb_driver_name)))
-
goto err_request_regions;
-
-
-
mmio_start = pci_resource_start(pdev, BAR_0);
-
mmio_len = pci_resource_len(pdev, BAR_0);
-
-
adapter->hw.hw_addr = ioremap(mmio_start, mmio_len);
-
if(!adapter->hw.hw_addr) {
-
err = -EIO;
-
goto err_ioremap;
-
}
这段代码进行了网卡芯片板载内存映射,得到adapter
->hw
.hw_addr虚拟地址,以后在配置网卡芯片寄存器时就用这个基地址+偏移量的方式来直接配置。
接下来就对接收和发送缓冲区进行初始化
-
int
-
ixgb_setup_tx_resources(struct ixgb_adapter *adapter)
-
{
-
struct ixgb_desc_ring *txdr = &adapter->tx_ring;
-
struct pci_dev *pdev = adapter->pdev;
-
int size;
-
-
size = sizeof(struct ixgb_buffer) * txdr->count;
-
txdr->buffer_info = vmalloc(size); //分配内存时可以用不连续的内存块
-
if(!txdr->buffer_info) {
-
DPRINTK(PROBE, ERR,
-
"Unable to allocate transmit descriptor ring memory\n");
-
return -ENOMEM;
-
}
-
memset(txdr->buffer_info, 0, size);
-
-
/* round up to nearest 4K */
-
-
txdr->size = txdr->count * sizeof(struct ixgb_tx_desc);
-
IXGB_ROUNDUP(txdr->size, 4096);
-
-
txdr->desc = pci_alloc_consistent(pdev, txdr->size, &txdr->dma); //对发送描述符进行了一致性内存映射
-
if(!txdr->desc) {
-
vfree(txdr->buffer_info);
-
DPRINTK(PROBE, ERR,
-
"Unable to allocate transmit descriptor memory\n");
-
return -ENOMEM;
-
}
-
memset(txdr->desc, 0, txdr->size);
-
-
txdr->next_to_use = 0;
-
txdr->next_to_clean = 0;
-
-
return 0;
-
}
-
int
-
ixgb_setup_rx_resources(struct ixgb_adapter *adapter)
-
{
-
struct ixgb_desc_ring *rxdr = &adapter->rx_ring;
-
struct pci_dev *pdev = adapter->pdev;
-
int size;
-
-
size = sizeof(struct ixgb_buffer) * rxdr->count;
-
rxdr->buffer_info = vmalloc(size);
-
if(!rxdr->buffer_info) {
-
DPRINTK(PROBE, ERR,
-
"Unable to allocate receive descriptor ring\n");
-
return -ENOMEM;
-
}
-
memset(rxdr->buffer_info, 0, size);
-
-
/* Round up to nearest 4K */
-
-
rxdr->size = rxdr->count * sizeof(struct ixgb_rx_desc);
-
IXGB_ROUNDUP(rxdr->size, 4096);
-
-
rxdr->desc = pci_alloc_consistent(pdev, rxdr->size, &rxdr->dma);
-
-
if(!rxdr->desc) {
-
vfree(rxdr->buffer_info);
-
DPRINTK(PROBE, ERR,
-
"Unable to allocate receive descriptors\n");
-
return -ENOMEM;
-
}
-
memset(rxdr->desc, 0, rxdr->size);
-
-
rxdr->next_to_clean = 0;
-
rxdr->next_to_use = 0;
-
-
return 0;
-
}
初始化后,数据结构如下(以接收环形缓冲区为例):
从图中可以看到对描述符进行了一致性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完成了此项工作,代码如下:
-
static void
-
ixgb_alloc_rx_buffers(struct ixgb_adapter *adapter)
-
{
-
struct ixgb_desc_ring *rx_ring = &adapter->rx_ring;
-
struct net_device *netdev = adapter->netdev;
-
struct pci_dev *pdev = adapter->pdev;
-
struct ixgb_rx_desc *rx_desc;
-
struct ixgb_buffer *buffer_info;
-
struct sk_buff *skb;
-
unsigned int i;
-
int num_group_tail_writes;
-
long cleancount;
-
-
i = rx_ring->next_to_use;
-
buffer_info = &rx_ring->buffer_info[i];
-
cleancount = IXGB_DESC_UNUSED(rx_ring);
-
-
num_group_tail_writes = IXGB_RX_BUFFER_WRITE;
-
-
/* leave three descriptors unused */
-
while(--cleancount > 2) {
-
/* its good for you */
-
skb = buffer_info->skb;
-
if (skb) {
-
skb_trim(skb, 0);
-
goto map_skb;
-
}
-
-
skb = netdev_alloc_skb(netdev, adapter->rx_buffer_len
-
+ NET_IP_ALIGN);
-
if (unlikely(!skb)) {
-
/* Better luck next round */
-
adapter->alloc_rx_buff_failed++;
-
break;
-
}
-
-
/* Make buffer alignment 2 beyond a 16 byte boundary
-
* this will result in a 16 byte aligned IP header after
-
* the 14 byte MAC header is removed
-
*/
-
skb_reserve(skb, NET_IP_ALIGN);
-
-
buffer_info->skb = skb;
-
buffer_info->length = adapter->rx_buffer_len;
-
map_skb:
-
buffer_info->dma = pci_map_single(pdev,
-
skb->data,
-
adapter->rx_buffer_len,
-
PCI_DMA_FROMDEVICE); //在这里进行了流式DMA映射,这时这块内存属于网卡,CPU不能使用。在中断函数中要调用配对函数让这块内存属于CPU,完成数据包的接收
-
-
rx_desc = IXGB_RX_DESC(*rx_ring, i);
-
rx_desc->buff_addr = cpu_to_le64(buffer_info->dma);
-
/* guarantee DD bit not set now before h/w gets descriptor
-
* this is the rest of the workaround for h/w double
-
* writeback. */
-
rx_desc->status = 0;
-
-
-
if(++i == rx_ring->count) i = 0;
-
buffer_info = &rx_ring->buffer_info[i];
-
}
-
-
if (likely(rx_ring->next_to_use != i)) {
-
rx_ring->next_to_use = i;
-
if (unlikely(i-- == 0))
-
i = (rx_ring->count - 1);
-
-
/* Force memory writes to complete before letting h/w
-
* know there are new descriptors to fetch. (Only
-
* applicable for weak-ordered memory model archs, such
-
* as IA-64). */
-
wmb();
-
IXGB_WRITE_REG(&adapter->hw, RDT, i);
-
}
-
}
阅读(4941) | 评论(1) | 转发(0) |