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.cdevm_clk_get()devm_clk_put()devm_clk_hw_register()devm_of_clk_add_hw_provider()

  • DMA

//drivers/base/dma-mapping.cdmaenginem_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.cdevm_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.cdevm_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.cdevm_input_allocate_device()

  • IO region

//kernel/resource.cdevm_release_mem_region()devm_release_region()devm_release_resource()devm_request_mem_region()devm_request_region()devm_request_resource()

  • IOMAP

//lib/devres.cdevm_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.cdevm_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.cdevm_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.cdevm_pci_alloc_host_bridge()devm_pci_remap_cfgspace()devm_pci_remap_cfg_resource()pcim_enable_device()pcim_pin_device()

  • Pinctrl

//drivres/pinctrl/core.cdevm_pinctrl_get()devm_pinctrl_put()devm_pinctrl_register()devm_pinctrl_unregister()

  • Regulator

//drivers/regulator/core.cdevm_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\n");       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\n");       err = -EBUSY;       goto exit_iounmap;   }   ...
   /* request irq */   err = claim_fiq(&fh);   if (err) {       dev_err(&pdev->dev, "Camera interrupt register failed\n");       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\n");
   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\n");       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\n");       return -EBUSY;   }   ...
   /* request irq */   err = claim_fiq(&fh);   if (err) {       dev_err(&pdev->dev, "Camera interrupt register failed\n");       return err;   }
   ...   err = soc_camera_host_register(&pcdev->soc_host);   if (err)       return err;
   dev_info(&pdev->dev, "MX1 Camera driver loaded\n");
   return 0;}

通过上面的比对大概能知道这些函数的差别了。

总结

目前除了一些旧代码之外,大部分驱动都使用devres相关的接口。也推荐大家在代码中更多的使用相关接口,这样代码会更简洁,不容易出现内存泄露。

参考文档

Documentation/driver-model/devres.txt

drivers/base/devres.c

drivers/base/dma-mapping.c

kernel/irq/devres.c

mm/dmapool.c

drivers/gpio/devres.c

drivers/base/regmap/regmap.c

drivers/regulator/core.c

图片

END

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值