天天看點

[記憶體管理] Linux Reserved Memory 預留記憶體

目錄

前言

預留記憶體給裝置驅動

通過DMA API預留記憶體

給CMA預留記憶體

原文位址:

Linux Reserved Memory

前言

基于Xilinx Zynq SoC / MPSoC的系統的常見要求之一是為特殊用途預留記憶體。預留的記憶體區域需要從linux核心的使用區域中分離出來,僅給特定的驅動程式使用。

reserved-memory 架構包含了預留記憶體的功能。預留記憶體的功能又與核心中的 DMA-API 和 CMA 架構密切相關。

本文旨在展示和解釋一些可用的用例,并且已經使用Petalinux建構工具進行了測試。由于本文中的修改僅涉及DTS檔案定制和裝置驅動程式中配置設定記憶體位置的改動,還是可以将其導出到Yocto或OSL工作流程中的。

預留記憶體給裝置驅動

為了從系統位址空間預留記憶體,裝置樹須配置預留記憶體的節點。每個節點定義一個特定的記憶體空間,并且可以根據核心文檔中關于可用于預留記憶體節點的說明配置不同的參數。然後就可以通過memory-region參數将預留的記憶體空間配置設定給特定的裝置驅動程式使用。

用于64位Cortex-A53 MPSoC的system-top.dts檔案中的裝置樹節點:

reserved-memory {
      #address-cells = <2>;
      #size-cells = <2>;
      ranges;
 
      reserved: [email protected] {
         no-map;
         reg = <0x0 0x70000000 0x0 0x10000000>;
      };
   };
 
   [email protected] {
      compatible = "xlnx,reserved-memory";
      memory-region = <&reserved>;
   };
           

或32位Cortex-A9 Zynq上,最新的基于 Yocto的Petalinux 的自定義的類似的裝置樹節點:

/include/ "system-conf.dtsi"
/ {
   reserved-memory {
      #address-cells = <1>;
      #size-cells = <1>;
      ranges;
  
      reserved: [email protected] {
         no-map;
         reg = <0x38000000 0x08000000>;
      };
   };
  
   [email protected] {
      compatible = "xlnx,reserved-memory";
      memory-region = <&reserved>;
   };
};
           

在裝置驅動程式中,可以通過解析裝置樹節點來處理記憶體區域的屬性,并且一旦知道了實體位址和大小,就可以使用 memremap / ioremap 調用來映射記憶體區域。

下面的代碼使用了預留的記憶體配置設定:

/* Get reserved memory region from Device-tree */
np = of_parse_phandle(dev->of_node, "memory-region", 0);
if (!np) {
  dev_err(dev, "No %s specified\n", "memory-region");
  goto error1;
}
  
rc = of_address_to_resource(np, 0, &r);
if (rc) {
  dev_err(dev, "No memory address assigned to the region\n");
  goto error1;
}
  
lp->paddr = r.start;
lp->vaddr = memremap(r.start, resource_size(&r), MEMREMAP_WB);
dev_info(dev, "Allocated reserved memory, vaddr: 0x%0llX, paddr: 0x%0llX\n", (u64)lp->vaddr, lp->paddr);
           

由于保留的記憶體區域已被核心排除,并标記為no-map,是以 iomem 資訊(/ proc / iomem)顯示系統RAM小于主機闆中的記憶體量。

[email protected]_aarch64:~# cat /proc/iomem
00000000-6fffffff : System RAM
  00080000-00b37fff : Kernel code
  011c9000-012b8fff : Kernel data
           

加載裝置後,可以确認記憶體配置設定:

[  126.191774] reserved-memory [email protected]: Device Tree Probing
[  126.198595] reserved-memory [email protected]: Allocated reserved memory, vaddr: 0xFFFFFF8020000000, paddr: 0x70000000
           

通過DMA API預留記憶體

有的時候裝置驅動程式需要采用DMA的方式使用預留的記憶體,對于這種場景,可以将 dts 中的節點屬性設定為 shared-dma-pool,進而生成為特定裝置驅動程式預留的 DMA 記憶體池。

reserved-memory {
      #address-cells = <2>;
      #size-cells = <2>;
      ranges;
 
      reserved: [email protected] {
         compatible = "shared-dma-pool";
         no-map;
         reg = <0x0 0x70000000 0x0 0x10000000>;
      };
   };
 
   [email protected] {
      compatible = "xlnx,reserved-memory";
      memory-region = <&reserved>;
   };
           

這樣,裝置驅動程式僅需要以正常方式使用 DMA API,但無需使用預設的 CMA 記憶體池,它将使用該特定裝置的預留記憶體區域。(獨占這一塊記憶體,仍然使用DMA的接口)

/* Initialize reserved memory resources */
  rc = of_reserved_mem_device_init(dev);
  if(rc) {
    dev_err(dev, "Could not get reserved memory\n");
    goto error1;
  }
  
  /* Allocate memory */
  dma_set_coherent_mask(dev, 0xFFFFFFFF);
  lp->vaddr = dma_alloc_coherent(dev, ALLOC_SIZE, &lp->paddr, GFP_KERNEL);
  dev_info(dev, "Allocated coherent memory, vaddr: 0x%0llX, paddr: 0x%0llX\n", (u64)lp->vaddr, lp->paddr);
           

核心啟動的log:

[    0.000000] Reserved memory: created DMA memory pool at 0x0000000070000000, size 256 MiB
[    0.000000] Reserved memory: initialized node [email protected], compatible id shared-dma-pool
[    0.000000] cma: Reserved 128 MiB at 0x0000000068000000
           

驅動加載的log:

[email protected]_aarch64:~# insmod /lib/modules/4.6.0-xilinx/extra/reserved-memory.ko
[   80.745166] reserved-memory [email protected]: Device Tree Probing
[   80.750183] reserved-memory [email protected]: assigned reserved memory node [email protected]
[   81.220878] reserved-memory [email protected]: Allocated coherent memory, vaddr: 0xFFFFFF8020000000, paddr: 0x70000000
 
           

給CMA預留記憶體

有時,不需要将預留的記憶體配置設定給特定的裝置驅動程式,而隻打算給 CMA 記憶體池配置設定一塊固定的記憶體區域。 對于這種場景,可以使用一個額外的屬性來指向核心,以将預留的記憶體區域用作預設的 CMA 記憶體池。

屬性:

reusable;

linux,cma-default

reserved-memory {
      #address-cells = <2>;
      #size-cells = <2>;
      ranges;
 
      reserved: [email protected] {
         compatible = "shared-dma-pool";
         reusable;
         reg = <0x0 0x70000000 0x0 0x10000000>;
         linux,cma-default;
      };
   };

           

核心啟動關于CMA配置設定的log:

[    0.000000] Reserved memory: created CMA memory pool at 0x0000000070000000, size 256 MiB
[    0.000000] Reserved memory: initialized node [email protected], compatible id shared-dma-pool
           

繼續閱讀