源码地址:linux-4.19.90driversnetethernetrealtekr8169.c
源码阅读环境:Windows 搭建 opengrok|极客教程
阅读网卡驱动源码第一步,简要看一下发送描述符,接收描述符,发送缓存区,接收缓冲区定义与初始化
RTL8169数据手册
第九节介绍发送描述符与接收描述符
发送描述符
接收描述符
// 发送描述符
596 struct TxDesc {
597 __le32 opts1;
598 __le32 opts2;
599 __le64 addr; // 小端地址
600 };
// 接收描述符
602 struct RxDesc {
603 __le32 opts1;
604 __le32 opts2;
605 __le64 addr; // 小端地址
606 };
// 缓冲区相关定义
650 struct rtl8169_private {
657 u32 cur_rx;
658 u32 cur_tx;
659 u32 dirty_tx;
660 struct rtl8169_stats rx_stats;
661 struct rtl8169_stats tx_stats;
662 struct TxDesc *TxDescArray;
663 struct RxDesc *RxDescArray;
664 dma_addr_t TxPhyAddr;
665 dma_addr_t RxPhyAddr;
666 void *Rx_databuff[NUM_RX_DESC];
667 struct ring_info tx_skb[NUM_TX_DESC];
715 };
// 缓冲区初始化
6860
6864 tp->TxDescArray = dma_alloc_coherent(&pdev->dev, R8169_TX_RING_BYTES,
6865 &tp->TxPhyAddr, GFP_KERNEL);
6869 tp->RxDescArray = dma_alloc_coherent(&pdev->dev, R8169_RX_RING_BYTES,
6870 &tp->RxPhyAddr, GFP_KERNEL);
手册中写明了描述符的大小与字节对齐要求
Each descriptor consists of 4 consecutive double words. The start address of each descriptor group should be 256-byte alignment.
然后看网络设备的操作函数,简要分析发包过程
7193 static const struct net_device_ops rtl_netdev_ops = {
7194 .ndo_open = rtl_open,
7195 .ndo_stop = rtl8169_close,
7197 .ndo_start_xmit = rtl8169_start_xmit,
7198 .ndo_tx_timeout = rtl8169_tx_timeout,
7210 };
1、网卡驱动创建tx descriptor ring(一致性DMA内存),将tx descriptor ring的总线地址写入网卡寄存器TDBA
2、协议栈通过dev_queue_xmit()将sk_buff下送网卡驱动
3、网卡驱动将sk_buff放入tx descriptor ring,更新TDT
4、DMA感知到TDT的改变后,找到tx descriptor ring中下一个将要使用的descriptor
5、DMA通过PCI总线将descriptor的数据缓存区复制到Tx FIFO
6、复制完后,通过MAC芯片将数据包发送出去
7、发送完后,网卡更新TDH,启动硬中断通知CPU释放数据缓存区中的数据包
6279 static netdev_tx_t rtl8169_start_xmit(struct sk_buff *skb,
6280 struct net_device *dev)
6281 {
6282 struct rtl8169_private *tp = netdev_priv(dev);
6283 unsigned int entry = tp->cur_tx % NUM_TX_DESC;
6284 struct TxDesc *txd = tp->TxDescArray + entry;
6285 struct device *d = tp_to_dev(tp);
6286 dma_addr_t mapping;
6287 u32 status, len;
6288 u32 opts[2];
6289 int frags;
6290
6291 if (unlikely(!TX_FRAGS_READY_FOR(tp, skb_shinfo(skb)->nr_frags))) {
6292 netif_err(tp, drv, dev, "BUG! Tx Ring full when queue awake!n");
6293 goto err_stop_0;
6294 }
6295
6296 if (unlikely(le32_to_cpu(txd->opts1) & DescOwn))
6297 goto err_stop_0;
6298
6299 opts[1] = cpu_to_le32(rtl8169_tx_vlan_tag(skb));
6300 opts[0] = DescOwn;
6301
6302 if (!tp->tso_csum(tp, skb, opts)) {
6303 r8169_csum_workaround(tp, skb);
6304 return NETDEV_TX_OK;
6305 }
6306
6307 len = skb_headlen(skb);
6308 mapping = dma_map_single(d, skb->data, len, DMA_TO_DEVICE);
6309 if (unlikely(dma_mapping_error(d, mapping))) {
6310 if (net_ratelimit())
6311 netif_err(tp, drv, dev, "Failed to map TX DMA!n");
6312 goto err_dma_0;
6313 }
6314
6315 tp->tx_skb[entry].len = len;
6316 txd->addr = cpu_to_le64(mapping);
6317
6318 frags = rtl8169_xmit_frags(tp, skb, opts);
6319 if (frags < 0)
6320 goto err_dma_1;
6321 else if (frags)
6322 opts[0] |= FirstFrag;
6323 else {
6324 opts[0] |= FirstFrag | LastFrag;
6325 tp->tx_skb[entry].skb = skb;
6326 }
6327
6328 txd->opts2 = cpu_to_le32(opts[1]);
6329
6330 skb_tx_timestamp(skb);
6331
6332
6333 dma_wmb();
6334
6335
6336 status = opts[0] | len | (RingEnd * !((entry + 1) % NUM_TX_DESC));
6337 txd->opts1 = cpu_to_le32(status);
6338
6339
6340 wmb();
6341
6342 tp->cur_tx += frags + 1;
6343
6344 RTL_W8(tp, TxPoll, NPQ);
6345
6346 mmiowb();
6347
6348 if (!TX_FRAGS_READY_FOR(tp, MAX_SKB_FRAGS)) {
6349
6352 smp_wmb();
6353 netif_stop_queue(dev);
6354
6361 smp_mb();
6362 if (TX_FRAGS_READY_FOR(tp, MAX_SKB_FRAGS))
6363 netif_wake_queue(dev);
6364 }
6365
6366 return NETDEV_TX_OK;
6367
6368 err_dma_1:
6369 rtl8169_unmap_tx_skb(d, tp->tx_skb + entry, txd);
6370 err_dma_0:
6371 dev_kfree_skb_any(skb);
6372 dev->stats.tx_dropped++;
6373 return NETDEV_TX_OK;
6374
6375 err_stop_0:
6376 netif_stop_queue(dev);
6377 dev->stats.tx_dropped++;
6378 return NETDEV_TX_BUSY;
6379 }
TxDesc结构体即为发送描述符,其定义如下:
596 struct TxDesc {
597 __le32 opts1;
598 __le32 opts2;
599 __le64 addr;
600 };
tp->TxDescArray为发送缓冲区
参考博客:
Linux 网络子系统
网卡驱动的收发包流程



