devres簡介
在驅動中經(jīng)常要在初始化函數(shù)或probe函數(shù)中對設備分配一些資源,比如:irq、regulator、gpio等。在驅動進行初始化的時候如果失敗,那么通常會goto到某個地方釋放資源。有時候編寫驅動時會忘記釋放資源,Linux為了解決這個問題而引入了devres機制。devres 是一種設備資源管理機制(device resource management), 類似于一種垃圾收集處理器。而資源的處理時機是在驅動的 install / remove 時候。這樣我們在為設備分配相關資源之后, 就不必要關心如何釋放它們了。
設備資源
Linux中設備資源包含:
Power
Clock
Memory
GPIO
IRQ
虛擬地址空間
API函數(shù)
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;
}
通過上面的比對大概能知道這些函數(shù)的差別了。
總結
目前除了一些舊代碼之外,大部分驅動都使用devres相關的接口。也推薦大家在代碼中更多的使用相關接口,這樣代碼會更簡潔,不容易出現(xiàn)內存泄露。
-
處理器
+關注
關注
68文章
20142瀏覽量
246639 -
Linux
+關注
關注
88文章
11622瀏覽量
217824 -
函數(shù)
+關注
關注
3文章
4405瀏覽量
66792
原文標題:Linux devres機制
文章出處:【微信號:嵌入式軟件開發(fā)交流,微信公眾號:嵌入式軟件開發(fā)交流】歡迎添加關注!文章轉載請注明出處。
發(fā)布評論請先 登錄
SPM引入的顆粒FSI無法去除,不知為何?
SPM引入的顆粒,為何FSI也無法去除?
Linux的platform機制開發(fā)驅動流程是怎么樣的?
linux內存管理機制淺析
linux內核機制有哪些
Linux系統(tǒng)的fork運行機制分析
Linux的notifier機制在TP中的應用
Linux Device Tree的基本概念
Linux內核驅動的platform機制是怎樣的
Linux內核文件Cache機制

Linux為何引入devres機制
評論