Linux内核里的ixgb驱动代码走读

代码结构介绍

Linux内核里的ixgb目录为linux/drivers/net/ethernet/intel/ixgb/

目录下内容有

# ls
ixgb_ee.c  ixgb_ee.h  ixgb_ethtool.c  ixgb.h  ixgb_hw.c  ixgb_hw.h  ixgb_ids.h  ixgb_main.c  ixgb_osdep.h  ixgb_param.c  Makefile

ixgb_main.c为ixgb驱动主要实现;

ixgb_hw.c实现了直接操作硬件的接口,主要是对硬件寄存器操作的一层封装,比如phy的读写,led的控制等;

ixgb_ethtool.c主要实现了netdev->ethtool_ops = &ixgb_ethtool_ops;即,网卡的ethtool系列操作;

ixgb_ee.c主要实现了针对网卡设备上的eeprom操作。

具体函数介绍

ixgb_init_module

ixgb驱动的入口函数为ixgb_init_module,当驱动被加载是调用

static int __init
ixgb_init_module(void)
{
        pr_info("%s\n", ixgb_driver_string);
        pr_info("%s\n", ixgb_copyright);

        return pci_register_driver(&ixgb_driver);
}

module_init(ixgb_init_module);
/* ixgb_main.c */

pci_register_driver(&ixgb_driver); 将ixgb_driver注册上去,ixgb_driver的实例为

static struct pci_driver ixgb_driver = {
        .name     = ixgb_driver_name,
        .id_table = ixgb_pci_tbl,
        .probe    = ixgb_probe,
        .remove   = ixgb_remove,
        .err_handler = &ixgb_err_handler
};
/* ixgb_main.c */

ixgb_probe

当pci bus上匹配到device和driver时,会调用probe函数,probe函数的具体实现如下:

static int ixgb_probe(struct pci_dev *pdev, const struct pci_device_id *ent)
{
        /* PCI设备的初始化 */
        err = pci_enable_device(pdev);
        err = dma_set_mask_and_coherent(&pdev->dev, DMA_BIT_MASK(64));
        err = pci_request_regions(pdev, ixgb_driver_name);
        pci_set_master(pdev);
        netdev = alloc_etherdev(sizeof(struct ixgb_adapter));
        SET_NETDEV_DEV(netdev, &pdev->dev);
        pci_set_drvdata(pdev, netdev);

        /* adapter结构体的初始化 */
        adapter = netdev_priv(netdev);
        adapter->netdev = netdev;
        adapter->pdev = pdev;
        adapter->hw.back = adapter;
        adapter->msg_enable = netif_msg_init(debug, DEFAULT_MSG_ENABLE);
        adapter->hw.hw_addr = pci_ioremap_bar(pdev, BAR_0);
        adapter->hw.io_base = pci_resource_start(pdev, i);
        adapter->bd_number = cards_found;
        adapter->link_speed = 0;
        adapter->link_duplex = 0;
  
        /* ixgb_netdev_ops是网卡设备操作的重点结构体 */
        netdev->netdev_ops = &ixgb_netdev_ops;
        ixgb_set_ethtool_ops(netdev);
        netdev->watchdog_timeo = 5 * HZ;

        /* NAPI的poll函数为ixgb_clean,将其挂上结构体 */
        netif_napi_add(netdev, &adapter->napi, ixgb_clean);

        /* 设置网卡设备的四有结构体 */

        /* ixgb_sw_init函数里主要将PCI设备信息和网路设置如MTU信息初始化到adapter->hw */
        err = ixgb_sw_init(adapter);

        netdev->hw_features = NETIF_F_SG |
                           NETIF_F_TSO |
                           NETIF_F_HW_CSUM |
                           NETIF_F_HW_VLAN_CTAG_TX |
                           NETIF_F_HW_VLAN_CTAG_RX;
        netdev->features = netdev->hw_features |
                           NETIF_F_HW_VLAN_CTAG_FILTER;
        netdev->hw_features |= NETIF_F_RXCSUM;

        netdev->features |= NETIF_F_HIGHDMA;
        netdev->vlan_features |= NETIF_F_HIGHDMA;

        /* MTU range: 68 - 16114 */
        netdev->min_mtu = ETH_MIN_MTU;
        netdev->max_mtu = IXGB_MAX_JUMBO_FRAME_SIZE - ETH_HLEN;

        /* 初始化定时器 */
        timer_setup(&adapter->watchdog_timer, ixgb_watchdog, 0);

        INIT_WORK(&adapter->tx_timeout_task, ixgb_tx_timeout_task);

        /* 注册设备 */
        err = register_netdev(netdev);

        /* carrier off reporting is important to ethtool even BEFORE open */
        netif_carrier_off(netdev);

        ixgb_check_options(adapter);

        /* 用新的配置重新硬件, ixgb_reset内的重点函数是ixgb_init_hw */
        ixgb_reset(adapter);

        cards_found++;
        return 0;
}
/* ixgb_main.c */

ixgb_probe函数里涉及到的初始化结构体ixgb_netdev_ops实现如下:

static const struct net_device_ops ixgb_netdev_ops = {
        .ndo_open               = ixgb_open,
        .ndo_stop               = ixgb_close,
        .ndo_start_xmit         = ixgb_xmit_frame,
        .ndo_set_rx_mode        = ixgb_set_multi,
        .ndo_validate_addr      = eth_validate_addr,
        .ndo_set_mac_address    = ixgb_set_mac,
        .ndo_change_mtu         = ixgb_change_mtu,
        .ndo_tx_timeout         = ixgb_tx_timeout,
        .ndo_vlan_rx_add_vid    = ixgb_vlan_rx_add_vid,
        .ndo_vlan_rx_kill_vid   = ixgb_vlan_rx_kill_vid,
        .ndo_fix_features       = ixgb_fix_features,
        .ndo_set_features       = ixgb_set_features,
};
/* ixgb_main.c */

ixgb_open

每当网络接口被UP激活的时候,ixgb_open函数都会被调用,ixgb_open的具体实现如下:

static int ixgb_open(struct net_device *netdev)
{
        struct ixgb_adapter *adapter = netdev_priv(netdev);

        /* 分配发送数据包的描述体资源,使用dma_alloc_coherent初始化TX Descriptor Ring */
        err = ixgb_setup_tx_resources(adapter);

        netif_carrier_off(netdev);

        /* 分配接收数据包的描述体资源,使用dma_alloc_coherent初始化RX Descriptor Ring */
        err = ixgb_setup_rx_resources(adapter);

        /* 将网卡接口UP起来的一系列操作 */
        err = ixgb_up(adapter);

        /* 允许上层协议栈调用网卡设备的hard_start_xmit功能 */
        netif_start_queue(netdev);

        return 0;

err_up:
        ixgb_free_rx_resources(adapter);
err_setup_rx:
        ixgb_free_tx_resources(adapter);
err_setup_tx:
        ixgb_reset(adapter);

        return err;
}
/* ixgb_main.c */

ixgb_close

当网络接口被link down的时候,ixgb_close函数被调用,ixgb_close的具体实现如下:

static int
ixgb_close(struct net_device *netdev)
{
        struct ixgb_adapter *adapter = netdev_priv(netdev);

        ixgb_down(adapter, true);

        ixgb_free_tx_resources(adapter);
        ixgb_free_rx_resources(adapter);

        return 0;
}
/* ixgb_main.c */

ixgb_xmit_frame

当协议栈要发送数据包时,会调用ixgb_xmit_frame,ixgb_xmit_frame的具体实现如下:

static netdev_tx_t ixgb_xmit_frame(struct sk_buff *skb, struct net_device *netdev)
{
        struct ixgb_adapter *adapter = netdev_priv(netdev);
        unsigned int first;
        unsigned int tx_flags = 0;
        int vlan_id = 0;
        int count = 0;
        int tso;

        first = adapter->tx_ring.next_to_use;

        /* 是否做TCP Segment Offload,即TCP字段硬件卸载 */
        tso = ixgb_tso(adapter, skb);

        if (likely(tso))
                tx_flags |= IXGB_TX_FLAGS_TSO;
        else if (ixgb_tx_csum(adapter, skb)) /* 是否做校验和的硬件卸载 */
                tx_flags |= IXGB_TX_FLAGS_CSUM;

        /* ixgb_tx_map为网卡具体发送数据包的函数
         * 实际操作是使用buffer_info->dma = dma_map_single(&pdev->dev, skb->data + offset,size, DMA_TO_DEVICE);
         * 将发送数据包skb->data映射到host与NIC交互的TX Ring的DMA空间上。
         */
        count = ixgb_tx_map(adapter, skb, first); 

        if (count) {
                /* 根据count,遍历填充tx_desc,把数据包交给网卡设备发出去
                 * tx_desc->buff_addr = cpu_to_le64(buffer_info->dma);
                 */
                ixgb_tx_queue(adapter, count, vlan_id, tx_flags);
                /* Make sure there is space in the ring for the next send. */
                ixgb_maybe_stop_tx(netdev, &adapter->tx_ring, DESC_NEEDED);

        } else {
                dev_kfree_skb_any(skb);
                adapter->tx_ring.buffer_info[first].time_stamp = 0;
                adapter->tx_ring.next_to_use = first;
        }

        return NETDEV_TX_OK;
}
/* ixgb_main.c */

ixgb_clean

网卡的收数据包部分,采用NAPI机制,即在ixgb_probe函数里有

netif_napi_add(netdev, &adapter->napi, ixgb_clean);执行语句,其中ixgb_clean为NAPI Polling函数,让我们来看看ixgb_clean的具体实现:

static int ixgb_clean(struct napi_struct *napi, int budget)
{
        struct ixgb_adapter *adapter = container_of(napi, struct ixgb_adapter, napi);
        int work_done = 0;

        ixgb_clean_tx_irq(adapter);
        ixgb_clean_rx_irq(adapter, &work_done, budget);

        /* If budget not fully consumed, exit the polling mode */
        if (work_done < budget) {
                napi_complete_done(napi, work_done);
                if (!test_bit(__IXGB_DOWN, &adapter->flags))
                        ixgb_irq_enable(adapter);
        }

        return work_done;
}

设置网卡的features,网络协议栈会查询这些feature了解网卡的能力做对数据包做不同的处理,比如网卡是否支持tso的硬件卸载,是否支持checksum的硬件卸载都在features里有所体现

static int ixgb_set_features(struct net_device *netdev, netdev_features_t features)
{
        struct ixgb_adapter *adapter = netdev_priv(netdev);
        netdev_features_t changed = features ^ netdev->features;

        if (!(changed & (NETIF_F_RXCSUM|NETIF_F_HW_VLAN_CTAG_RX)))
                return 0;

        adapter->rx_csum = !!(features & NETIF_F_RXCSUM);

        if (netif_running(netdev)) {
                ixgb_down(adapter, true);
                ixgb_up(adapter);
                ixgb_set_speed_duplex(netdev);
        } else
                ixgb_reset(adapter);

        return 0;
}
/* ixgb_main.c */
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值