Linux网络接受
本文基于2.6.15的内核版本来简单学习一下有关Linux内核如何进行网络数据的处理流程。
网卡驱动注册
以ixgb驱动为例,在网卡注册的时候会进入如下流程。
static struct pci_driver ixgb_driver = {
.name = ixgb_driver_name,
.id_table = ixgb_pci_tbl,
.probe = ixgb_probe,
.remove = __devexit_p(ixgb_remove),
};
...
/**
* ixgb_init_module - Driver Registration Routine
*
* ixgb_init_module is the first routine called when the driver is
* loaded. All it does is register with the PCI subsystem.
**/
static int __init
ixgb_init_module(void)
{
printk(KERN_INFO "%s - version %s\n",
ixgb_driver_string, ixgb_driver_version);
printk(KERN_INFO "%s\n", ixgb_copyright);
return pci_module_init(&ixgb_driver);
}
module_init(ixgb_init_module);
PCI注册的流程会将调用probe方法,即ixgb_probe。
/**
* ixgb_probe - Device Initialization Routine
* @pdev: PCI device information struct
* @ent: entry in ixgb_pci_tbl
*
* Returns 0 on success, negative on failure
*
* ixgb_probe initializes an adapter identified by a pci_dev structure.
* The OS initialization, configuring of the adapter private structure,
* and a hardware reset occur.
**/
static int __devinit
ixgb_probe(struct pci_dev *pdev,
const struct pci_device_id *ent)
{
struct net_device *netdev = NULL;
struct ixgb_adapter *adapter;
static int cards_found = 0;
unsigned long mmio_start;
int mmio_len;
int pci_using_dac;
int i;
int err;
if((err = pci_enable_device(pdev))) // 是否可以注册该设备
return err;
if(!(err = pci_set_dma_mask(pdev, DMA_64BIT_MASK))) {
//设置DMA的掩码
pci_using_dac = 1;
} else {
if((err = pci_set_dma_mask(pdev, DMA_32BIT_MASK))) {
IXGB_ERR("No usable DMA configuration, aborting\n");
return err;
}
pci_using_dac = 0;
}
if((err = pci_request_regions(pdev, ixgb_driver_name)))
return err;
pci_set_master(pdev);
netdev = alloc_etherdev(sizeof(struct ixgb_adapter)); // 申请设备结构体
if(!netdev) {
err = -ENOMEM;
goto err_alloc_etherdev;
}
SET_MODULE_OWNER(netdev);
SET_NETDEV_DEV(netdev, &pdev->dev);
pci_set_drvdata(pdev, netdev); // 设置DMA对应的管理的内存空间
adapter = netdev_priv(netdev);
adapter->netdev = netdev;
adapter->pdev = pdev;
adapter->hw.back = adapter;
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;
}
for(i = BAR_1; i <= BAR_5; i++) {
if(pci_resource_len(pdev, i) == 0)
continue;
if(pci_resource_flags(pdev, i) & IORESOURCE_IO) {
adapter->hw.io_base = pci_resource_start(pdev, i);
break;
}
}
netdev->open = &ixgb_open; // 设置ethtool对应的操作的方法,即设备的打开的方法
netdev->stop = &ixgb_close; // 设备的关闭的方法
netdev->hard_start_xmit = &ixgb_xmit_frame;
netdev->get_stats = &ixgb_get_stats;
netdev->set_multicast_list = &ixgb_set_multi;
netdev->set_mac_address = &ixgb_set_mac;
netdev->change_mtu = &ixgb_change_mtu; // 设置mtu长度
ixgb_set_ethtool_ops(netdev);
netdev->tx_timeout = &ixgb_tx_timeout; // 设置超时时间
netdev->watchdog_timeo = HZ;
#ifdef CONFIG_IXGB_NAPI
netdev->poll = &ixgb_clean; // 设置设备的poll方法 一般用在NAPI模式中
netdev->weight = 64;
#endif
netdev->vlan_rx_register = ixgb_vlan_rx_register; // 设置vlan的方法
netdev->vlan_rx_add_vid = ixgb_vlan_rx_add_vid;
netdev->vlan_rx_kill_vid = ixgb_vlan_rx_kill_vid;
#ifdef CONFIG_NET_POLL_CONTROLLER
netdev->poll_controller = ixgb_netpoll