Linux为何引入devres机制

描述

              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相关的接口。也推荐大家在代码中更多的使用相关接口,这样代码会更简洁,不容易出现内存泄露。

  
      审核编辑:彭静
打开APP阅读更多精彩内容
声明:本文内容及配图由入驻作者撰写或者入驻合作网站授权转载。文章观点仅代表作者本人,不代表电子发烧友网立场。文章及其配图仅供工程师学习之用,如有内容侵权或者其他违规问题,请联系本站处理。 举报投诉

全部0条评论

快来发表一下你的评论吧 !

×
20
完善资料,
赚取积分