devres是内核为驱动开发者提供的用来申请资源的机制,函数名都是以devm开头。总的来说,在大多数情况下,调用这类接口不需要关心资源的释放问题。 在驱动进行probe时,可以调用这类接口,若probe失败,申请的资源也会释放。 以devm_request_threaded_irq接口为例,简单了解下这个机制。
函数开始调用devres_alloc将devm_irq_release保存在dr->node.release节点下,然后返回dr->data,再然后通过返回的节点,把调用relase时需要传递的参数irq及dev_id保存起来。然后调用devres_add把该数据结构添加到dev->devres_head链表,供probe_failed,__device_release_driver等等时调用。
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; } void * __devres_alloc_node(dr_release_t release, size_t size, gfp_t gfp, int nid, const char *name) { struct devres *dr; dr = alloc_dr(release, size, gfp | __GFP_ZERO, nid); if (unlikely(!dr)) return NULL; set_node_dbginfo(&dr->node, name, size); return dr->data; } static __always_inline struct devres * alloc_dr(dr_release_t release, size_t size, gfp_t gfp, int nid) { size_t tot_size; struct devres *dr; /* We must catch any near-SIZE_MAX cases that could overflow. */ if (unlikely(check_add_overflow(sizeof(struct devres), size, &tot_size))) return NULL; 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; }驱动probe_failed时,调用devres_release_all,其实就是调用之前的回调函数dr->node.release将probe时申请的资源释放。
if (dev->bus->probe) { ret = dev->bus->probe(dev); if (ret) goto probe_failed; } else if (drv->probe) { ret = drv->probe(dev); if (ret) goto probe_failed; } probe_failed: devres_release_all(dev); 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); }