linux dev alloc name,深入理解Linux网络技术内幕-设备注册和初始化(二)
NIC注冊和注銷的通用架構
Linux系統中NIC網絡設備驅動程序利用網絡代碼進行注冊和注銷有其通用的架構,這里以PCI Ethernet NIC為例,其他設備類型只是所以函數名稱和調用方式不同,主要依據于設備總線提供的接口。
其中(a)為設備注冊的大致流程圖,而(b)為設備注銷的流程圖。
在PCI Ethernet NIC設備驅動程序的探測函數(熱插拔設備)或模塊初始化函數中,首先要為設備分配一個net_device數據結構,并對其中的成員進行必要的初始化,對其中與設備類型密切相關的特殊成員利用驅動程序自己實現的setup函數進行初始化;Ethernet NIC設備驅動程序還需要調用netdev_boot_setup_check檢查是否在系統啟動參數中對網絡設備進行了設置;然后調用register_netdev完成設備的注冊。
在分配net_device數據結構時,驅動程序一般不直接調用alloc_netdev函數,而是調用為其類型封裝后的函數,如Ethernet NIC設備直接調用alloc_etherdev函數,使用更加方便簡單。
而Ethernet NIC設備的注銷則是相反的過程,首先調用unregister_netdev在系統中注銷設備,然后將分配的net_device數據結構釋放。
在釋放net_device數據結構時,設備也可能不直接調用free_netdev函數中,而是調用net_device數據結構中的成員函數:
/* Called from unregister, can be used to call free_netdev */
void (*destructor)(struct net_device *dev);
虛擬設備驅動程序一般采用這種方式,實現自己的destructor函數來釋放net_device數據結構。
網絡設備注冊過程
網絡設備在系統中注冊后,內核在處理數據包時才能調用設備接口實現的處理函數。網絡設備的注冊是通過register_netdev函數完成的:
/**
*??? register_netdev??? - register a network device
*??? @dev: device to register
*
*??? Take a completed network device structure and add it to the kernel
*??? interfaces. A %NETDEV_REGISTER message is sent to the netdev notifier
*??? chain. 0 is returned on success. A negative errno code is returned
*??? on a failure to set up the device, or if the name is a duplicate.
*
*??? This is a wrapper around register_netdevice that takes the rtnl semaphore
*??? and expands the device name if you passed a format string to
*??? alloc_netdev.
*/
int register_netdev(struct net_device *dev)
{
int err;
rtnl_lock();
/*
* If the name is a format string the caller wants us to do a
* name allocation.
*/
if (strchr(dev->name, '%')) {
err = dev_alloc_name(dev, dev->name);
if (err < 0)
goto out;
}
err = register_netdevice(dev);
out:
rtnl_unlock();
return err;
}
EXPORT_SYMBOL(register_netdev);
其中rtnl_lock是內核保護運行時的net_device數據結構的互斥手段,一般在修改net_device中flag字段,表示有事件發生需要改變設備的狀態;或者用戶通過ifconfig、route等命令修改接口的配置時,通過ioctl和netlink接口告訴內核操作設備的net_device結構,都需要調用這個鎖來進行互斥。
dev_alloc_name(dev, dev->name)函數會在系統中找到這種類型的網絡設備中第一個沒有使用的序列號來替換設備名稱中的%d,生成如eth2的設備名稱。
int dev_alloc_name(struct net_device *dev, const char *name)
{
char buf[IFNAMSIZ];
struct net *net;
int ret;
BUG_ON(!dev_net(dev));
net = dev_net(dev);
ret = __dev_alloc_name(net, name, buf);
if (ret >= 0)
strlcpy(dev->name, buf, IFNAMSIZ); //將返回的設備名稱復制到net_device的name字段
return ret;
}
static int __dev_alloc_name(struct net *net, const char *name, char *buf)
{
int i = 0;
const char *p;
const int max_netdevices = 8*PAGE_SIZE;
unsigned long *inuse;
struct net_device *d;
/*檢查設備名稱中是否有%d,或其他不合法字符*/
p = strnchr(name, IFNAMSIZ-1, '%');
if (p) {
if (p[1] != 'd' || strchr(p + 2, '%'))
return -EINVAL;
/*分配一個物理頁面作為位圖,來對系統中該類型設備已用序列號進行標記*/
inuse = (unsigned long *) get_zeroed_page(GFP_ATOMIC);
if (!inuse)
return -ENOMEM;
/*變量網絡命名空間中的所有設備,即net_device結構*/
for_each_netdev(net, d) {
if (!sscanf(d->name, name, &i)) //獲取同類型網絡設備的其序列號,這里極為巧妙
continue;
if (i < 0 || i >= max_netdevices) //判斷序列號的范圍
continue;
snprintf(buf, IFNAMSIZ, name, i);
if (!strncmp(buf, d->name, IFNAMSIZ)) /*驗證解析的序列號是否正確*/
set_bit(i, inuse); //在位圖中將該位標記
}
i = find_first_zero_bit(inuse, max_netdevices); //找到第一個為0的序列號
free_page((unsigned long) inuse);
}
if (buf != name)
snprintf(buf, IFNAMSIZ, name, i); //根據找到的序列號,輸出完整的設備名
if (!__dev_get_by_name(net, buf)) //在name_list鏈表中查找是否有同名的設備
return i;
/* It is possible to run out of possible slots
* when the name is long and there isn't enough space left
* for the digits, or if all bits are used.
*/
return -ENFILE;
}
在這里就為設備完成了完整設備名的組合,內核在這里位圖的使用非常巧妙,以后可以在處理位圖時,可以直接使用內核實現的set_bit和find_first_zero_bit、clear_bit等函數。
register_netdevice才是網絡設備注冊的最重要步驟:
int register_netdevice(struct net_device *dev)
{
int ret;
struct net *net = dev_net(dev);? //設備的網絡空間
BUG_ON(dev_boot_phase);
ASSERT_RTNL();
might_sleep();
/* When net_device's are persistent, this will be fatal. */
BUG_ON(dev->reg_state != NETREG_UNINITIALIZED); //alloc_netdev時不需要設置這個成員,因為其為0
BUG_ON(!net);
/*初始化net_device中的一些成員鎖*/
spin_lock_init(&dev->addr_list_lock);
netdev_set_addr_lockdep_class(dev);
dev->iflink = -1;
/* Init, if this function is available */
if (dev->netdev_ops->ndo_init) { //調用設備驅動程序操作中實現的初始化函數
ret = dev->netdev_ops->ndo_init(dev);
if (ret) {
if (ret > 0)
ret = -EIO;
goto out;
}
}
ret = dev_get_valid_name(dev, dev->name, 0); //檢查設備名稱的有效性
if (ret)
goto err_uninit;
dev->ifindex = dev_new_index(net); //為設備分配一個唯一的索引號
if (dev->iflink == -1)
dev->iflink = dev->ifindex;
/* Transfer changeable features to wanted_features and enable
* software offloads (GSO and GRO).
*/
/*設置設備的一些特性*/
dev->hw_features |= NETIF_F_SOFT_FEATURES;
dev->features |= NETIF_F_SOFT_FEATURES;
dev->wanted_features = dev->features & dev->hw_features;
/* Enable GRO and NETIF_F_HIGHDMA for vlans by default,
* vlan_dev_init() will do the dev->features check, so these features
* are enabled only if supported by underlying device.
*/
dev->vlan_features |= (NETIF_F_GRO | NETIF_F_HIGHDMA);
ret = call_netdevice_notifiers(NETDEV_POST_INIT, dev); //調用通知鏈,發出事件通知
ret = notifier_to_errno(ret);
if (ret)
goto err_uninit;
ret = netdev_register_kobject(dev); //設備注冊的核心函數,主要是調用device_add函數,將設備添加到內核的設備管理器中
if (ret)
goto err_uninit;
dev->reg_state = NETREG_REGISTERED;? //設置net_device的狀態
netdev_update_features(dev);
/*
*??? Default initial state at registry is that the
*??? device is present.
*/
set_bit(__LINK_STATE_PRESENT, &dev->state);
dev_init_scheduler(dev); //在這里會設置設備的看門狗定時器
dev_hold(dev); //增加設備的引用計數
list_netdevice(dev);? //將設備加入系統的indexlist、namelist和devlist中
/* Notify protocols, that a new device appeared. */
ret = call_netdevice_notifiers(NETDEV_REGISTER, dev); //通過通知鏈發出設備注冊通知
ret = notifier_to_errno(ret);
if (ret) {
rollback_registered(dev);
dev->reg_state = NETREG_UNREGISTERED;
}
/*
*??? Prevent userspace races by waiting until the network
*??? device is fully setup before sending notifications.
*/
if (!dev->rtnl_link_ops ||
dev->rtnl_link_state == RTNL_LINK_INITIALIZED)
rtmsg_ifinfo(RTM_NEWLINK, dev, ~0U);
out:
return ret;
err_uninit:
if (dev->netdev_ops->ndo_uninit)
dev->netdev_ops->ndo_uninit(dev);
goto out;
}
EXPORT_SYMBOL(register_netdevice);
由上可知注冊的主要過程是netdev_register_kobject函數中的device_add過程,和list_netdevice(dev)將設備加入到系統的幾個hash鏈表中,便于系統處理數據包時查找對應的設備。
總結
以上是生活随笔為你收集整理的linux dev alloc name,深入理解Linux网络技术内幕-设备注册和初始化(二)的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: hyper-v虚拟服务器内存满了,在Hy
- 下一篇: cp命令显示进度条_干货|| Linux