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

# 配置和管理内存

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

有关 Linux 内核内存管理的更多信息，请参阅 [Memory management](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 allocator）用于可移动（movable）分配。

要更改默认 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` 节点下定义，并带有表示 CMA 区域的 `shared-dma-pool` compatible 标记：

```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 内存管理伙伴分配器的 system 堆。 |
| Reserved | /dev/dma\_heap/reserved | 系统中创建的默认 CMA 类型堆，使用默认的 *reserved* CMA 区域。 |           如果因某些约束需要连续内存，请使用 CMA 堆。          |

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

Qualcomm Linux 发行版支持 DMA-BUF 堆，用于指定自定义 CMA 堆。使用 DMA-BUF 堆时，`/dev/dma_heap` 文件系统中的每个堆都对应一个设备文件。除了 system 堆和通用的 CMA reserved 堆之外，您还可以创建自己的 CMA 类型 DMA-BUF 堆。

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

```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 中作为模块启用。

ZRAM 在 Yocto 构建中的 `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       |    这是一个可选属性，表示用于内存分配的固定区域；如果没有该属性，内存将在随机地址动态分配。   |
