Linux Kernel devm_* API源码分析
本文通过对devm_kzalloc函数的实现进行解析,帮助理解devm系列api的用法。
devm系列api的主要特别之处:
- 使用devm系列申请到的资源可以由系统自动释放,解放双手
devm_kzalloc代码:
void * devm_kzalloc(struct device *dev, size_t size, gfp_t gfp)
{
struct devres *dr;
/* use raw alloc_dr for kmalloc caller tracing */
dr = alloc_dr(devm_kzalloc_release, size, gfp);
if (unlikely(!dr))
return NULL;
set_node_dbginfo(&dr->node, "devm_kzalloc_release", size);
devres_add(dev, dr->data);
return dr->data;
}
static __always_inline struct devres * alloc_dr(dr_release_t release,
size_t size, gfp_t gfp)
{
size_t tot_size = sizeof(struct devres) + size;
struct devres *dr;
dr = kmalloc_track_caller(tot_size, gfp);
if (unlikely(!dr))
return NULL;
memset(dr, 0, tot_size);
INIT_LIST_HEAD(&dr->node.entry);
dr->node.release = release;
return dr;
}
struct devres {
struct devres_node node;
/* -- 3 pointers */
unsigned long long data[]; /* guarantee ull alignment */
};
struct devres_node {
struct list_head entry;
dr_release_t release;
#ifdef CONFIG_DEBUG_DEVRES
const char *name;
size_t size;
#endif
};
alloc_dr函数负责分配内存,返回的devres就包含分配到的内存,通过devres.data来访问即可.
devres_add(dev, dr->data);函数将数据与device进行绑定,实际调用add_dr,在这里将资源的节点头添加到device的devres_head链表中
static void add_dr(struct device *dev, struct devres_node *node)
{
devres_log(dev, node, "ADD");
BUG_ON(!list_empty(&node->entry));
list_add_tail(&node->entry, &dev->devres_head);
}
现在资源与dev的绑定已经完成,下面就找一找是如何释放资源的.
device_release_driver -> devres_release_all->release_nodes
static int release_nodes(struct device *dev, struct list_head *first,
struct list_head *end, unsigned long flags)
__releases(&dev->devres_lock)
{
LIST_HEAD(todo);
int cnt;
struct devres *dr, *tmp;
cnt = remove_nodes(dev, first, end, &todo);
spin_unlock_irqrestore(&dev->devres_lock, flags);
/* Release. Note that both devres and devres_group are
* handled as devres in the following loop. This is safe.
*/
list_for_each_entry_safe_reverse(dr, tmp, &todo, node.entry) {
devres_log(dev, &dr->node, "REL");
dr->node.release(dev, dr->data);
kfree(dr);
}
return cnt;
}
在这里首先执行node的release函数,这个函数跟据资源的不同而有不同的实现,接着吧dr释放掉.由此,生成过程以及释放过程已经全部完成.
release函数在这里指定:
dr = alloc_dr(devm_kzalloc_release, size, gfp);