devres简介
在驱动中经常要在初始化函数或probe函数中对设备分配一些资源,比如:irq、regulator、gpio等。在驱动进行初始化的时候如果失败,那么通常会goto到某个地方释放资源。有时候编写驱动时会忘记释放资源,Linux为了解决这个问题而引入了devres机制。devres 是一种设备资源管理机制(device resource management), 类似于一种垃圾收集处理器。而资源的处理时机是在驱动的 install / remove 时候。这样我们在为设备分配相关资源之后, 就不必要关心如何释放它们了。
设备资源
Linux中设备资源包含:
Power
Clock
Memory
GPIO
IRQ
DMA
虚拟地址空间
API函数
Clock
//drivers/clk/clk-devres.c devm_clk_get() devm_clk_put() devm_clk_hw_register() devm_of_clk_add_hw_provider()
DMA
//drivers/base/dma-mapping.c dmaenginem_async_device_register() dmam_alloc_coherent() dmam_alloc_attrs() dmam_declare_coherent_memory() dmam_free_coherent() dmam_pool_create() dmam_pool_destroy()
GPIO
//drivers/gpio/gpiolib-devres.c devm_gpiod_get() devm_gpiod_get_index() devm_gpiod_get_index_optional() devm_gpiod_get_optional() devm_gpiod_put() devm_gpiochip_add_data() devm_gpiochip_remove() devm_gpio_request() devm_gpio_request_one() devm_gpio_free()
IIO
//drivers/iio/industrialio-core.c devm_iio_device_alloc() devm_iio_device_free() devm_iio_device_register() devm_iio_device_unregister() devm_iio_kfifo_allocate() devm_iio_kfifo_free() devm_iio_triggered_buffer_setup() devm_iio_triggered_buffer_cleanup() devm_iio_trigger_alloc() devm_iio_trigger_free() devm_iio_trigger_register() devm_iio_trigger_unregister() devm_iio_channel_get() devm_iio_channel_release() devm_iio_channel_get_all() devm_iio_channel_release_all()
Input
//drivers/input/input.c devm_input_allocate_device()
IO region
//kernel/resource.c devm_release_mem_region() devm_release_region() devm_release_resource() devm_request_mem_region() devm_request_region() devm_request_resource()
IOMAP
//lib/devres.c devm_ioport_map() devm_ioport_unmap() devm_ioremap() devm_ioremap_nocache() devm_ioremap_wc() devm_ioremap_resource() devm_iounmap() pcim_iomap() pcim_iomap_regions() pcim_iomap_table() pcim_iounmap()
IRQ
//kernel/irq/devres.c devm_free_irq() devm_request_any_context_irq() devm_request_irq() devm_request_threaded_irq() devm_irq_alloc_descs() devm_irq_alloc_desc() devm_irq_alloc_desc_at() devm_irq_alloc_desc_from() devm_irq_alloc_descs_from() devm_irq_alloc_generic_chip() devm_irq_setup_generic_chip() devm_irq_sim_init()
Memory
//drivers/base/devres.c devm_free_pages() devm_get_free_pages() devm_kasprintf() devm_kcalloc() devm_kfree() devm_kmalloc() devm_kmalloc_array() devm_kmemdup() devm_kstrdup() devm_kvasprintf() devm_kzalloc()
PCI
//drivers/pci/probe.c devm_pci_alloc_host_bridge() devm_pci_remap_cfgspace() devm_pci_remap_cfg_resource() pcim_enable_device() pcim_pin_device()
Pinctrl
//drivres/pinctrl/core.c devm_pinctrl_get() devm_pinctrl_put() devm_pinctrl_register() devm_pinctrl_unregister()
Regulator
//drivers/regulator/core.c devm_regulator_bulk_get() devm_regulator_get() devm_regulator_put() devm_regulator_register()
驱动对比
非devres驱动:
static int __init soc_camera_probe(struct platform_device *pdev) { ... res = platform_get_resource(pdev, IORESOURCE_MEM, 0); irq = platform_get_irq(pdev, 0); if (!res || (int)irq <= 0) { err = -ENODEV; goto exit; } clk = clk_get(&pdev->dev, "csi_clk"); if (IS_ERR(clk)) { err = PTR_ERR(clk); goto exit; } pcdev = kzalloc(sizeof(*pcdev), GFP_KERNEL); if (!pcdev) { dev_err(&pdev->dev, "Could not allocate pcdev "); err = -ENOMEM; goto exit_put_clk; } ... /* * Request the regions. */ if (!request_mem_region(res->start, resource_size(res), DRIVER_NAME)) { err = -EBUSY; goto exit_kfree; } base = ioremap(res->start, resource_size(res)); if (!base) { err = -ENOMEM; goto exit_release; } ... /* request dma */ pcdev->dma_chan = imx_dma_request_by_prio(DRIVER_NAME, DMA_PRIO_HIGH); if (pcdev->dma_chan < 0) { dev_err(&pdev->dev, "Can't request DMA for MX1 CSI "); err = -EBUSY; goto exit_iounmap; } ... /* request irq */ err = claim_fiq(&fh); if (err) { dev_err(&pdev->dev, "Camera interrupt register failed "); goto exit_free_dma; } ... err = soc_camera_host_register(&pcdev->soc_host); if (err) goto exit_free_irq; dev_info(&pdev->dev, "MX1 Camera driver loaded "); return 0; exit_free_irq: disable_fiq(irq); mxc_set_irq_fiq(irq, 0); release_fiq(&fh); exit_free_dma: imx_dma_free(pcdev->dma_chan); exit_iounmap: iounmap(base); exit_release: release_mem_region(res->start, resource_size(res)); exit_kfree: kfree(pcdev); exit_put_clk: clk_put(clk); exit: return err; }
devres驱动:
static int __init soc_camera_probe(struct platform_device *pdev) { ... res = platform_get_resource(pdev, IORESOURCE_MEM, 0); irq = platform_get_irq(pdev, 0); if (!res || (int)irq <= 0) { return -ENODEV; } clk = devm_clk_get(&pdev->dev, "csi_clk"); if (IS_ERR(clk)) { return PTR_ERR(clk); } pcdev = devm_kzalloc(&pdev->dev, sizeof(*pcdev), GFP_KERNEL); if (!pcdev) { dev_err(&pdev->dev, "Could not allocate pcdev "); return -ENOMEM; } ... /* * Request the regions. */ if (!devm_request_mem_region(&pdev->dev, res->start, resource_size(res), DRIVER_NAME)) { return -EBUSY; } base = devm_ioremap(&pdev->dev, res->start, resource_size(res)); if (!base) { return -ENOMEM; } ... /* request dma */ pcdev->dma_chan = imx_dma_request_by_prio(DRIVER_NAME, DMA_PRIO_HIGH); if (pcdev->dma_chan < 0) { dev_err(&pdev->dev, "Can't request DMA for MX1 CSI "); return -EBUSY; } ... /* request irq */ err = claim_fiq(&fh); if (err) { dev_err(&pdev->dev, "Camera interrupt register failed "); return err; } ... err = soc_camera_host_register(&pcdev->soc_host); if (err) return err; dev_info(&pdev->dev, "MX1 Camera driver loaded "); return 0; }
通过上面的比对大概能知道这些函数的差别了。
总结
目前除了一些旧代码之外,大部分驱动都使用devres相关的接口。也推荐大家在代码中更多的使用相关接口,这样代码会更简洁,不容易出现内存泄露。
全部0条评论
快来发表一下你的评论吧 !