devm_xxx申请资源释放资源的流程

 

devm_xxx的函数是在申请资源,释放资源的一种补充,对于devm_xxx打头的申请资源的函数,在设备detach的时候会自动释放申请的资源。本文来看看devm_xxxx机制的实现

首先在设备device结构体中,有一个devres_head链表,维护该设备所绑定的资源,在设备detach的时候,遍历这个链表,一个一个进行资源释放。

struct device {
	...
	spinlock_t		devres_lock;
	struct list_head	devres_head;
	struct klist_node	knode_class;
	...

};

资源的数据结构位于drivers/base/devres.c当中,如下

struct devres {
	struct devres_node		node;
	/* -- 3 pointers */
	unsigned long long		data[];	/* guarantee ull alignment */
};

第二个参数是零长度数据,由于devres是供所有资源使用的,而每个资源使用的长度不是完全一致的,所以需要有一个不定长的数组,零长度数组的知识可参考https://blog.csdn.net/u012142460/article/details/100080849

第一个参数是devres的node节点,详细如下,一个entry,以及一个释放函数

struct devres_node {
	struct list_head		entry;
	dr_release_t			release;
#ifdef CONFIG_DEBUG_DEVRES
	const char			*name;
	size_t				size;
#endif
};

相比于普通情况下的资源申请和释放,dev_xxx函数中会使用到devres_alloc/devres_free、devres_add/devres_remove来实现这个机制,下面以申请irq为例来进行说明,首先每种资源都需要有一个资源结构体,中断的资源就是irq和dev_id

struct irq_devres {
	unsigned int irq;
	void *dev_id;
};
int devm_request_threaded_irq(struct device *dev, unsigned int irq,
			      irq_handler_t handler, irq_handler_t thread_fn,
			      unsigned long irqflags, const char *devname,
			      void *dev_id)
{
	struct irq_devres *dr;
	int rc;

	dr = devres_alloc(devm_irq_release, sizeof(struct irq_devres),
			  GFP_KERNEL);
	if (!dr)
		return -ENOMEM;

	if (!devname)
		devname = dev_name(dev);

	rc = request_threaded_irq(irq, handler, thread_fn, irqflags, devname,
				  dev_id);
	if (rc) {
		devres_free(dr);
		return rc;
	}

	dr->irq = irq;
	dr->dev_id = dev_id;
	devres_add(dev, dr);

	return 0;
}

在上面的函数中,

1、devres_alloc分配该资源结构体空间

2、申请相关资源

3、假如该设备的资源链表中

上面三部就是一个整体的流程,然后我们再把第一步和第三步在深入看一下

static __always_inline struct devres * alloc_dr(dr_release_t release,
						size_t size, gfp_t gfp, int nid)
{
	size_t tot_size = sizeof(struct devres) + size;
	struct devres *dr;

	dr = kmalloc_node_track_caller(tot_size, gfp, nid);
	if (unlikely(!dr))
		return NULL;

	memset(dr, 0, offsetof(struct devres, data));

	INIT_LIST_HEAD(&dr->node.entry);
	dr->node.release = release;
	return dr;
}
void * devres_alloc_node(dr_release_t release, size_t size, gfp_t gfp, int nid)
{
	struct devres *dr;

	dr = alloc_dr(release, size, gfp | __GFP_ZERO, nid);
	if (unlikely(!dr))
		return NULL;
	return dr->data;
}
static inline void *devres_alloc(dr_release_t release, size_t size, gfp_t gfp)
{
	return devres_alloc_node(release, size, gfp, NUMA_NO_NODE);
}

alloc_dr就是在申请devres结构体,根据传入的size大小来申请不同的大小,并将release释放函数赋值。

完成dr申请之后,将申请的dr放入到该设备结构体device的资源链表中,也就是第三步

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);
}

void devres_add(struct device *dev, void *res)
{
	struct devres *dr = container_of(res, struct devres, data);
	unsigned long flags;

	spin_lock_irqsave(&dev->devres_lock, flags);
	add_dr(dev, &dr->node);
	spin_unlock_irqrestore(&dev->devres_lock, flags);
}

而对于释放的过程,有两类,一种是probe失败,一种是device remove的时候,最终都会调用到devres_release_all

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;
}

int devres_release_all(struct device *dev)
{
	unsigned long flags;

	/* Looks like an uninitialized device structure */
	if (WARN_ON(dev->devres_head.next == NULL))
		return -ENODEV;
	spin_lock_irqsave(&dev->devres_lock, flags);
	return release_nodes(dev, dev->devres_head.next, &dev->devres_head,
			     flags);
}

从上面的代码来看,在释放的时候,遍历dev中的资源链表,然后一个一个注册的release函数,将相应的资源释放掉

  • 2
    点赞
  • 4
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值