linux dev alloc name,深入理解Linux网络技术内幕-设备注册和初始化(二)
NIC注冊和注銷的通用架構(gòu)
Linux系統(tǒng)中NIC網(wǎng)絡(luò)設(shè)備驅(qū)動程序利用網(wǎng)絡(luò)代碼進行注冊和注銷有其通用的架構(gòu),這里以PCI Ethernet NIC為例,其他設(shè)備類型只是所以函數(shù)名稱和調(diào)用方式不同,主要依據(jù)于設(shè)備總線提供的接口。
其中(a)為設(shè)備注冊的大致流程圖,而(b)為設(shè)備注銷的流程圖。
在PCI Ethernet NIC設(shè)備驅(qū)動程序的探測函數(shù)(熱插拔設(shè)備)或模塊初始化函數(shù)中,首先要為設(shè)備分配一個net_device數(shù)據(jù)結(jié)構(gòu),并對其中的成員進行必要的初始化,對其中與設(shè)備類型密切相關(guān)的特殊成員利用驅(qū)動程序自己實現(xiàn)的setup函數(shù)進行初始化;Ethernet NIC設(shè)備驅(qū)動程序還需要調(diào)用netdev_boot_setup_check檢查是否在系統(tǒng)啟動參數(shù)中對網(wǎng)絡(luò)設(shè)備進行了設(shè)置;然后調(diào)用register_netdev完成設(shè)備的注冊。
在分配net_device數(shù)據(jù)結(jié)構(gòu)時,驅(qū)動程序一般不直接調(diào)用alloc_netdev函數(shù),而是調(diào)用為其類型封裝后的函數(shù),如Ethernet NIC設(shè)備直接調(diào)用alloc_etherdev函數(shù),使用更加方便簡單。
而Ethernet NIC設(shè)備的注銷則是相反的過程,首先調(diào)用unregister_netdev在系統(tǒng)中注銷設(shè)備,然后將分配的net_device數(shù)據(jù)結(jié)構(gòu)釋放。
在釋放net_device數(shù)據(jù)結(jié)構(gòu)時,設(shè)備也可能不直接調(diào)用free_netdev函數(shù)中,而是調(diào)用net_device數(shù)據(jù)結(jié)構(gòu)中的成員函數(shù):
/* Called from unregister, can be used to call free_netdev */
void (*destructor)(struct net_device *dev);
虛擬設(shè)備驅(qū)動程序一般采用這種方式,實現(xiàn)自己的destructor函數(shù)來釋放net_device數(shù)據(jù)結(jié)構(gòu)。
網(wǎng)絡(luò)設(shè)備注冊過程
網(wǎng)絡(luò)設(shè)備在系統(tǒng)中注冊后,內(nèi)核在處理數(shù)據(jù)包時才能調(diào)用設(shè)備接口實現(xiàn)的處理函數(shù)。網(wǎng)絡(luò)設(shè)備的注冊是通過register_netdev函數(shù)完成的:
/**
*??? 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是內(nèi)核保護運行時的net_device數(shù)據(jù)結(jié)構(gòu)的互斥手段,一般在修改net_device中flag字段,表示有事件發(fā)生需要改變設(shè)備的狀態(tài);或者用戶通過ifconfig、route等命令修改接口的配置時,通過ioctl和netlink接口告訴內(nèi)核操作設(shè)備的net_device結(jié)構(gòu),都需要調(diào)用這個鎖來進行互斥。
dev_alloc_name(dev, dev->name)函數(shù)會在系統(tǒng)中找到這種類型的網(wǎng)絡(luò)設(shè)備中第一個沒有使用的序列號來替換設(shè)備名稱中的%d,生成如eth2的設(shè)備名稱。
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); //將返回的設(shè)備名稱復(fù)制到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;
/*檢查設(shè)備名稱中是否有%d,或其他不合法字符*/
p = strnchr(name, IFNAMSIZ-1, '%');
if (p) {
if (p[1] != 'd' || strchr(p + 2, '%'))
return -EINVAL;
/*分配一個物理頁面作為位圖,來對系統(tǒng)中該類型設(shè)備已用序列號進行標(biāo)記*/
inuse = (unsigned long *) get_zeroed_page(GFP_ATOMIC);
if (!inuse)
return -ENOMEM;
/*變量網(wǎng)絡(luò)命名空間中的所有設(shè)備,即net_device結(jié)構(gòu)*/
for_each_netdev(net, d) {
if (!sscanf(d->name, name, &i)) //獲取同類型網(wǎng)絡(luò)設(shè)備的其序列號,這里極為巧妙
continue;
if (i < 0 || i >= max_netdevices) //判斷序列號的范圍
continue;
snprintf(buf, IFNAMSIZ, name, i);
if (!strncmp(buf, d->name, IFNAMSIZ)) /*驗證解析的序列號是否正確*/
set_bit(i, inuse); //在位圖中將該位標(biāo)記
}
i = find_first_zero_bit(inuse, max_netdevices); //找到第一個為0的序列號
free_page((unsigned long) inuse);
}
if (buf != name)
snprintf(buf, IFNAMSIZ, name, i); //根據(jù)找到的序列號,輸出完整的設(shè)備名
if (!__dev_get_by_name(net, buf)) //在name_list鏈表中查找是否有同名的設(shè)備
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;
}
在這里就為設(shè)備完成了完整設(shè)備名的組合,內(nèi)核在這里位圖的使用非常巧妙,以后可以在處理位圖時,可以直接使用內(nèi)核實現(xiàn)的set_bit和find_first_zero_bit、clear_bit等函數(shù)。
register_netdevice才是網(wǎng)絡(luò)設(shè)備注冊的最重要步驟:
int register_netdevice(struct net_device *dev)
{
int ret;
struct net *net = dev_net(dev);? //設(shè)備的網(wǎng)絡(luò)空間
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時不需要設(shè)置這個成員,因為其為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) { //調(diào)用設(shè)備驅(qū)動程序操作中實現(xiàn)的初始化函數(shù)
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); //檢查設(shè)備名稱的有效性
if (ret)
goto err_uninit;
dev->ifindex = dev_new_index(net); //為設(shè)備分配一個唯一的索引號
if (dev->iflink == -1)
dev->iflink = dev->ifindex;
/* Transfer changeable features to wanted_features and enable
* software offloads (GSO and GRO).
*/
/*設(shè)置設(shè)備的一些特性*/
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); //調(diào)用通知鏈,發(fā)出事件通知
ret = notifier_to_errno(ret);
if (ret)
goto err_uninit;
ret = netdev_register_kobject(dev); //設(shè)備注冊的核心函數(shù),主要是調(diào)用device_add函數(shù),將設(shè)備添加到內(nèi)核的設(shè)備管理器中
if (ret)
goto err_uninit;
dev->reg_state = NETREG_REGISTERED;? //設(shè)置net_device的狀態(tài)
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); //在這里會設(shè)置設(shè)備的看門狗定時器
dev_hold(dev); //增加設(shè)備的引用計數(shù)
list_netdevice(dev);? //將設(shè)備加入系統(tǒng)的indexlist、namelist和devlist中
/* Notify protocols, that a new device appeared. */
ret = call_netdevice_notifiers(NETDEV_REGISTER, dev); //通過通知鏈發(fā)出設(shè)備注冊通知
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函數(shù)中的device_add過程,和list_netdevice(dev)將設(shè)備加入到系統(tǒng)的幾個hash鏈表中,便于系統(tǒng)處理數(shù)據(jù)包時查找對應(yīng)的設(shè)備。
總結(jié)
以上是生活随笔為你收集整理的linux dev alloc name,深入理解Linux网络技术内幕-设备注册和初始化(二)的全部內(nèi)容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: hyper-v虚拟服务器内存满了,在Hy
- 下一篇: java反序列化异常接不到_由Java对