歡迎來到Linux教程網
Linux教程網
Linux教程網
Linux教程網
Linux教程網 >> Linux綜合 >> Linux內核 >> Linux內核最新的連續內存分配器(CMA)——避免預留大塊內存

Linux內核最新的連續內存分配器(CMA)——避免預留大塊內存

日期:2017/2/28 15:55:26   编辑:Linux內核

在我們使用ARM等嵌入式Linux系統的時候,一個頭疼的問題是GPU,Camera,HDMI等都需要預留大量連續內存,這部分內存平時不用,但是一般的做法又必須先預留著。目前,Marek Szyprowski和Michal Nazarewicz實現了一套全新的Contiguous Memory Allocator。通過這套機制,我們可以做到不預留內存,這些內存平時是可用的,只有當需要的時候才被分配給Camera,HDMI等設備。下面分析它的基本代碼流程。

聲明連續內存

內核啟動過程中arch/arm/mm/init.c中的arm_memblock_init()會調用dma_contiguous_reserve(min(arm_dma_limit, arm_lowmem_limit));

該函數位於:drivers/base/dma-contiguous.c

  1. /**
  2. * dma_contiguous_reserve() - reserve area for contiguous memory handling
  3. * @limit: End address of the reserved memory (optional, 0 for any).
  4. *
  5. * This function reserves memory from early allocator. It should be
  6. * called by arch specific code once the early allocator (memblock or bootmem)
  7. * has been activated and all other subsystems have already allocated/reserved
  8. * memory.
  9. */
  10. void __init dma_contiguous_reserve(phys_addr_t limit)
  11. {
  12. unsigned long selected_size = 0;
  13. pr_debug("%s(limit %08lx)\n", __func__, (unsigned long)limit);
  14. if (size_cmdline != -1) {
  15. selected_size = size_cmdline;
  16. } else {
  17. #ifdef CONFIG_CMA_SIZE_SEL_MBYTES
  18. selected_size = size_bytes;
  19. #elif defined(CONFIG_CMA_SIZE_SEL_PERCENTAGE)
  20. selected_size = cma_early_percent_memory();
  21. #elif defined(CONFIG_CMA_SIZE_SEL_MIN)
  22. selected_size = min(size_bytes, cma_early_percent_memory());
  23. #elif defined(CONFIG_CMA_SIZE_SEL_MAX)
  24. selected_size = max(size_bytes, cma_early_percent_memory());
  25. #endif
  26. }
  27. if (selected_size) {
  28. pr_debug("%s: reserving %ld MiB for global area\n", __func__,
  29. selected_size / SZ_1M);
  30. dma_declare_contiguous(NULL, selected_size, 0, limit);
  31. }
  32. };

其中的size_bytes定義為:

static const unsigned long size_bytes = CMA_SIZE_MBYTES * SZ_1M; 默認情況下,CMA_SIZE_MBYTES會被定義為16MB,來源於CONFIG_CMA_SIZE_MBYTES=16

->
  1. int __init dma_declare_contiguous(struct device *dev, unsigned long size,
  2. phys_addr_t base, phys_addr_t limit)
  3. {
  4. ...
  5. /* Reserve memory */
  6. if (base) {
  7. if (memblock_is_region_reserved(base, size) ||
  8. memblock_reserve(base, size) < 0) {
  9. base = -EBUSY;
  10. goto err;
  11. }
  12. } else {
  13. /*
  14. * Use __memblock_alloc_base() since
  15. * memblock_alloc_base() panic()s.
  16. */
  17. phys_addr_t addr = __memblock_alloc_base(size, alignment, limit);
  18. if (!addr) {
  19. base = -ENOMEM;
  20. goto err;
  21. } else if (addr + size > ~(unsigned long)0) {
  22. memblock_free(addr, size);
  23. base = -EINVAL;
  24. base = -EINVAL;
  25. goto err;
  26. } else {
  27. base = addr;
  28. }
  29. }
  30. /*
  31. * Each reserved area must be initialised later, when more kernel
  32. * subsystems (like slab allocator) are available.
  33. */
  34. r->start = base;
  35. r->size = size;
  36. r->dev = dev;
  37. cma_reserved_count++;
  38. pr_info("CMA: reserved %ld MiB at %08lx\n", size / SZ_1M,
  39. (unsigned long)base);
  40. /* Architecture specific contiguous memory fixup. */
  41. dma_contiguous_early_fixup(base, size);
  42. return 0;
  43. err:
  44. pr_err("CMA: failed to reserve %ld MiB\n", size / SZ_1M);
  45. return base;
  46. }

由此可見,連續內存區域也是在內核啟動的早期,通過__memblock_alloc_base()拿到的。

另外:

drivers/base/dma-contiguous.c裡面的core_initcall()會導致cma_init_reserved_areas()被調用:

  1. static int __init cma_init_reserved_areas(void)
  2. {
  3. struct cma_reserved *r = cma_reserved;
  4. unsigned i = cma_reserved_count;
  5. pr_debug("%s()\n", __func__);
  6. for (; i; --i, ++r) {
  7. struct cma *cma;
  8. cma = cma_create_area(PFN_DOWN(r->start),
  9. r->size >> PAGE_SHIFT);
  10. if (!IS_ERR(cma))
  11. dev_set_cma_area(r->dev, cma);
  12. }
  13. return 0;
  14. }
  15. core_initcall(cma_init_reserved_areas);
Copyright © Linux教程網 All Rights Reserved