> ## Documentation Index
> Fetch the complete documentation index at: https://dragonwingdocs-staging.qualcomm.com/llms.txt
> Use this file to discover all available pages before exploring further.

# 配置和管理内存

Qualcomm<sup>®</sup> Linux 内核基线支持所有内存管理特性和分配器。以下信息概述了如何自定义内存映射并执行堆管理。

有关 Linux 内核内存管理的更多信息，请参阅[内存管理](https://docs.kernel.org/core-api/index.html#memory-management)。

**内存映射**

内存映射描述了在内核启动期间为各子系统（如调制解调器、相机、aDSP 和 cDSP）保留的区域。

内存映射会为 DTSI 中划出（carved out）的区域设置 `no-map`，使内核无法访问这些区域。

可配置的划出区域在 `arch/arm64/boot/dts/qcom/kodiak.dtsi` 文件的 `reserved-memory` 节点下定义。

```text theme={null}
reserved-memory {
               cdsp_secure_heap_mem: cdsp-secure-heap@81800000 {
                        reg = <0x0 0x81800000 0x0 0x1e00000>;
                        no-map;
               };

               camera_mem: camera@84300000 {
                        reg = <0x0 0x84300000 0x0 0x500000>;
                        no-map;
               };

               wpss_mem: wpss@0x84800000 {
                        reg = <0x0 0x84800000 0x0 0x1900000>;
                        no-map;
               };

               adsp_mem: adsp@86100000 {
                        reg = <0x0 0x86100000 0x0 0x2800000>;
                        no-map;
               };
};
```

<Note>
  对于 Qualcomm SoC，请查看 SoC 专属的 Qualcomm DTSI 文件以获取此信息。
</Note>

以下早期启动日志显示了为不同子系统创建划出区域的过程：

```text theme={null}
[    0.000000] OF: reserved mem: 0x0000000081800000..0x00000000835fffff (30720 KiB) nomap non-reusable cdsp-secure-heap@81800000
[    0.000000] OF: reserved mem: 0x0000000084300000..0x00000000847fffff (5120 KiB) nomap non-reusable camera@84300000
[    0.000000] OF: reserved mem: 0x0000000084800000..0x00000000860fffff (25600 KiB) nomap non-reusable wpss@0x84800000
[    0.000000] OF: reserved mem: 0x0000000086100000..0x00000000888fffff (40960 KiB) nomap non-reusable adsp@86100000
```

## 连续内存分配器

Qualcomm Linux 发行版支持使用 CMA 来分配大块物理连续内存。CMA 在启动时保留一大块物理连续内存区域，并为 CMA 分配提供物理连续的内存。在不使用时，CMA 内存可供内核伙伴（buddy）分配器用于可移动分配。

要更改默认 CMA 区域的大小，请在内核命令行参数中使用 `cma=size_in_MB`。有关如何使用该内核参数的更多信息，请参阅以下示例：

```text theme={null}
cma=nn[MG]@[start[MG][-end[MG]]]
                        [KNL,CMA]
                        Sets the size of kernel global memory area for
                        contiguous memory allocations and optionally the
                        placement constraint by the physical address range of
                        memory allocations. A value of 0 disables CMA
                        altogether. For more information, see
                        kernel/dma/contiguous.c
```

自定义 CMA 区域在 `reserved-memory` 节点下定义，并带有 `shared-dma-pool` compatible 标记来指示 CMA 区域：

```text theme={null}
adsp_heap_mem: adsp-heap {
                        compatible = "shared-dma-pool";
                        alloc-ranges = <0x0 0x00000000 0x0 0xffffffff>;
                        reusable;
                        alignment = <0x0 0x400000>;
                        size = <0x0 0xc00000>;
               };
```

以下是启动时保留 aDSP CMA 内存区域的示例日志：

```text theme={null}
[    0.000000] OF: reserved mem: initialized node adsp-heap, compatible id shared-dma-pool
[    0.000000] OF: reserved mem: 0x00000000ff000000..0x00000000ffbfffff (12288 KiB) map reusable adsp-heap
```

### **支持的堆**

下表列出了 Qualcomm Linux 发行版默认支持的堆：

**表：默认支持的堆**

|  **堆名称** |         **设备节点**        |                   **描述**                  |                   **用途**                  |
| :------: | :---------------------: | :---------------------------------------: | :---------------------------------------: |
|  System  |  /dev/dma\_heap/system  |             内核创建的默认 DMA-BUF 堆。            | 所有通用使用场景都应使用系统堆，其底层使用通用的 Linux 内存管理伙伴分配器。 |
| Reserved | /dev/dma\_heap/reserved | 系统中创建的默认 CMA 类型堆，使用默认的 *reserved* CMA 区域。 |         如果由于某些约束需要连续内存，请使用 CMA 堆。         |

### **使用 DMA-BUF 堆**

Qualcomm Linux 发行版支持通过 DMA-BUF 堆来分配自定义 CMA 堆。使用 DMA-BUF 堆时，`/dev/dma_heap` 文件系统中会为每个堆提供一个设备文件。除系统堆和通用的 CMA reserved 堆外，您还可以创建自己的 CMA 类型 DMA-BUF 堆。

以下示例程序演示了如何使用 Qualcomm Linux 内核在 `/dev/dma_heap/system` 中创建的 DMA-BUF 系统堆：

```text theme={null}
include <stdio.h>
include <stdlib.h>
include <fcntl.h>
include <errno.h>
include <unistd.h>
include <sys/ioctl.h>
include <linux/dma-buf.h>
#include <linux/dma-heap.h>

define DMA_HEAP_NAME "system"
define SZ_4 0x00000004  // to allocate a 4K buffer

int main()
{
int fd, dma_buf_fd;

struct dma_heap_allocation_data dma_alloc_data = {
   .len = SZ_4,
   .fd_flags = O_RDWR | O_CLOEXEC,
};

struct dma_buf_sync sync_start = {
   .flags = DMA_BUF_SYNC_START,
};
struct dma_buf_sync sync_end = {
   .flags = DMA_BUF_SYNC_END,
};

fd = open("/dev/dma_heap/system", O_RDWR);
if (fd < 0) {
   perror("open");
   return errno;
}

dma_buf_fd = ioctl(fd, DMA_HEAP_IOCTL_ALLOC, &dma_alloc_data);
if (dma_buf_fd < 0) {
   perror("ioctl");
   return errno;
}
printf("Allocated DMA buffer with fd %d\n", dma_buf_fd);

if (ioctl(dma_buf_fd, DMA_BUF_IOCTL_SYNC, &sync_start)) {
   perror("ioctl DMA_BUF_IOCTL_SYNC start");
   return errno;
}

//        Do something with the buffer here

if (ioctl(dma_buf_fd, DMA_BUF_IOCTL_SYNC, &sync_end))
{
   perror("ioctl DMA_BUF_IOCTL_SYNC end");
   return errno;
}

if (close(dma_buf_fd)) {
   perror("close");
   return errno;
}

if (close(fd)) {
   perror("close");
   return errno;
}

return 0;
}
```

### **将 ZRAM 配置为交换设备**

ZRAM 是一种压缩交换机制，它在 RAM 中创建一个虚拟块设备。ZRAM 在内核 defconfig 中作为模块启用。

在 Yocto 构建中，ZRAM 在 `recipes-extended/zram/zram/zram-swap-init-update` 文件中配置。

要启用或配置 ZRAM，请执行以下操作：

```text theme={null}
# check if zram module is loaded
  lsmod | grep zram

# else load it
  modprobe zram

# Configure /dev/zram0 size according to your RAM size
  echo 128M > /sys/block/zram0/disksize

# activate swap
  mkswap /dev/zram0
  swapon /dev/zram0
```

要将 ZRAM 配置为交换设备，请参阅 [zram: Compressed RAM-based block devices](https://www.kernel.org/doc/html/v6.18/admin-guide/blockdev/zram.html)。

### **扩展内存映射**

要扩展内存映射，请在 DTSI 文件中调整内存区域的地址和大小。

使用以下信息来扩展划出区域：

使用以下语法，在 `arch/arm64/boot/dts/qcom/<SoC>-<board>-<variant>.dts` 文件的 **reserved-memory** 节点中添加划出区域：

```text theme={null}
my_carveout_mem: my_carveout_mem@address {
      reg= <0x0 0xbase_address 0x0 0xsize>;
      no-map;
}
```

例如：

```text theme={null}
my_carveout_mem: my_carveout_mem@d0800000 {
      reg= <0x0 0xd0800000 0x0 0x100000>;
      no-map;
}
```

**表：划出区域的语法**

|            **变量**           |                  **描述**                  |
| :-------------------------: | :--------------------------------------: |
| my\_carveout\_mem\@d0800000 |    表示设备节点的名称。按照约定，必须将内存区域的基地址附加到名称后面。    |
|      my\_carveout\_mem      | 表示分配给此节点的标签，可用于设备树中其他节点通过 phandle 引用此节点。 |
|             reg             |     表示该属性，它是一个 64 位值，定义了内存区域的基地址和大小。     |
|            no-map           |        表示此区域被划出，内核应从其可寻址范围中移除该映射。        |

<Note>
  * 任何区域都不应重叠。如果必须增大某个区域的大小，请移动所有后续区域，并在设备树中进行配置以避免重叠。
  * 内核要求为内核使用而定义的所有内存区域边界都是 1 MB。
  * 现有划出区域受可信固件保护，内核无法访问。减小其大小或删除它们可能导致外部中止（external abort），从而导致内核崩溃。
</Note>

**添加 CMA 区域**

要在 `arch/arm64/boot/dts/qcom/<SoC>-<board>-<variant>.dts` 文件的 **reserved-memory** 节点下添加 CMA 区域，请使用以下语法：

```text theme={null}
my_cma_mem: my_cma {
   compatible = "shared-dma-pool";
   alloc-ranges = <0x0 0x00000000 0x0 0xffffffff>;
   reusable;
   alignment = <0x0 0x400000>;
   size = <0x0 0x1400000>;
};
```

**表：添加 CMA 区域的语法**

|      **参数**     |                       **描述**                      |
| :-------------: | :-----------------------------------------------: |
|   my\_cma\_mem  | CMA 节点的标签，可作为 phandle 访问。标签 `my_cma` 是 CMA 区域的名称。 |
| shared-dma-pool |                  表示此区域是一个 CMA 区域。                 |
|   alloc-ranges  |   指示该区域是否应位于某个内存限制之内，因为某些设备无法访问超出 32 位地址限制的内存区域。  |
|     reusable    |               表示当该区域空闲时，内核可以使用其中的内存。              |
|    alignment    |                   表示该区域的任何对齐要求。                   |
|       size      |                      表示区域的大小。                     |
|       reg       |     这是一个可选属性，指示用于内存分配的固定区域；若不指定，则在随机地址动态分配内存。     |
