代码结构介绍
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 */