setup_arch:bootmem_init : sparse_init

本文详细介绍了Linux内核的sparsevmemmap内存模型,这是一种用于非连续物理内存的高效管理方式。在sparsevmemmap中,使用虚拟内存映射优化了PFN到structpage的转换,减少了访存次数。文章涵盖了sparse_init函数的执行流程,包括如何初始化mem_section,分配structpage结构体,并创建页表映射到vmemmap。此外,还提及了memblock.memory的初始化以及节点和section之间的关系。

kernel: 5.10
arch: arm64

首先简单说明一下背景知识,启动阶段的内存模型一般有4种:FLATMEM,经典sparse和sparse vmemmap,以及zone devie

  1. FLATMEM
    这种模型适用于具有连续物理内存或大部分连续物理内存的非NUMA系统。 在FLATMEM内存模型中,有一个全局mem_map数组映射整个物理内存。对于即使是空洞的物理内存,也需要分配struct page结构体,只不过不会被初始化,映射到具体的物理内存。因此使用的时候要通过pfn_valid来进行检测,可以看到这样实际是浪费了一些struct page的。
  2. SPARSEMEM
    SPARSEMEM是Linux中最通用的内存模型,也是唯一支持多种高级功能的内存模型。SPARSEMEM模型将物理内存表示为section的集合。一个section用struct mem_section表示,结构体成员section_mem_map指向struct page数组的指针。它还存储了一些有助于section管理的魔数。由体系结构定义的SECTION_SIZE_BITS和MAX_PHYSMEM_BITS来指定section大小和section的最大数量。MAX_PHYSMEM_BITS是体系结构支持的物理地址的实际宽度,而SECTION_SIZE_BITS是任意值,最大数目的section个数为:NR_MEM_SECTIONS = 2 ^ (MAX_PHYSMEM_BITS - SECTION_SIZE_BITS)
    mem_section对象排列在一个名为mem_sections的二维数组中。
    (1) 当CONFIG_SPARSEMEM_EXTREME被禁用时,mem_sections数组是静态定义的,有NR_MEM_SECTIONS行。
    (2) 当CONFIG_SPARSEMEM_EXTREME启用时,mem_sections数组是动态分配的。每行包含相当于PAGE_SIZE大小的mem_section个数
    架构设置代码应该调用sparse_init()来初始化内存sections和内存映射。
    使用SPARSEMEM,有两种可能的方法将PFN转换为相应的结构页——经典sparse和sparse vmemmap,由CONFIG_SPARSEMEM_VMEMMAP的值决定。
    (1) 经典稀疏编码在page->flags中对页面的section编号进行编码,并使用PFN的高位来访问映射该页帧的section。在一个section中,PFN是page数组的索引。 会有多次访存行为,因此出现了稀疏vmemmap
    (2) 稀疏vmemmap使用虚拟内存映射来优化pfn_to_page和page_to_pfn操作。有一个指向全局struct page数组的vmemmap指针。PFN是该数组的索引,且struct page距vmemmap的偏移量是该page的PFN。要使用vmemmap,必须保留一个虚拟地址范围,该范围将映射物理页面,并确保vmemmap指向该范围。架构应该实现vmemmap_populate()方法为struct page分配物理内存并为虚拟内存映射创建页表。如果一个架构对vmemmap映射没有任何特殊要求,它可以使用通用内存管理提供的默认vmemmap_populate_basepages()。
  3. ZONE_DEVICE(TODO)
    虚拟映射存储器映射允许将持久存储器设备的struct page对象存储在这些设备预先分配的存储器中。这个存储用结构vmem_altmap表示,它最终通过一长串函数调用传递给vmemmap_populate()。vmemmap_populate()实现可以使用vmem_altmap以及vmemmap_alloc_block_buf()来在持久性存储器设备上分配存储器映射。

此处我们主要是分析的稀疏vmemmap的内存模型
在这里插入图片描述

sparse_init是在bootmem_init中被调用,它初始化在线的每个mem_section,为这些mem_section创建struct page结构体,并为这些struct page结构体创建页表,映射到vmemmap虚拟映射区

/*
 * Allocate the accumulated non-linear sections, allocate a mem_map
 * for each and record the physical to section mapping.
 */
void __init sparse_init(void)
{
        unsigned long pnum_end, pnum_begin, map_count = 1;
        int nid_begin;

        memblocks_present();

        pnum_begin = first_present_section_nr();
        nid_begin = sparse_early_nid(__nr_to_section(pnum_begin));

        /* Setup pageblock_order for HUGETLB_PAGE_SIZE_VARIABLE */
        set_pageblock_order();

        for_each_present_section_nr(pnum_begin + 1, pnum_end) {
                int nid = sparse_early_nid(__nr_to_section(pnum_end));

                if (nid == nid_begin) {
                        map_count++;
                        continue;
                }
                /* Init node with sections in range [pnum_begin, pnum_end) */
                sparse_init_nid(nid_begin, pnum_begin, pnum_end, map_count);
                nid_begin = nid;
                pnum_begin = pnum_end;
                map_count = 1;
        }
        /* cover the last node */
        sparse_init_nid(nid_begin, pnum_begin, pnum_end, map_count);
        vmemmap_populate_print_last();
}
  1. memblocks_present: memblocks_present实际就是遍历memblock.memory下的所有memblock_region区域,将每个memblock_region区域按section划分,创建对应的mem_section结构体,且通过mem_section的section_mem_map标记为在线状态。关于memblock.memory的初始化可以参考early_init_dt_scan_memorystruct memblock

一般一个node对应一个memblock_region?

  1. first_present_section_nr:一个node由多个root组成,一个root由多个mem_section组成,此处获取首个root的首个mem_section编号
  2. sparse_early_nid(__nr_to_section(pnum_begin)):根据mem_section编号获取早期设置的nid
    在早期阶段mem_section->section_mem_map的布局如下:
    bit0:present, bit1:has memmap; bit2:online; bit3:eary; bit4-:node id
  3. set_pageblock_order:如果没有定义CONFIG_HUGETLB_PAGE_SIZE_VARIABLE则为空
  4. for_each_present_section_nr:
#define for_each_present_section_nr(start, section_nr)          \
        for (section_nr = next_present_section_nr(start-1);     \
             ((section_nr != -1) &&                             \
              (section_nr <= __highest_present_section_nr));    \
             section_nr = next_present_section_nr(section_nr))

从section num为start的section开始,对每个present的section进行遍历

  1. sparse_init_nid
    (1)sparse_early_usemaps_alloc_pgdat_section分配mem_section_usage结构体空间?
    (2)sparse_buffer_init:为一个node在线的所有section分配struct page空间
    (3)for_each_present_section_nr:遍历node下所有在线的mem_section执行__populate_section_memmap和sparse_init_one_section
    (3)__populate_section_memmap:为mem_sectoin对应的struct page本身创建页表
    (4)sparse_init_one_section:初始化mem_section,其中sparse_encode_mem_map返回mem_section的首个page的虚拟地址
static void __meminit sparse_init_one_section(struct mem_section *ms,
                unsigned long pnum, struct page *mem_map,
                struct mem_section_usage *usage, unsigned long flags)
{
        ms->section_mem_map &= ~SECTION_MAP_MASK;
        ms->section_mem_map |= sparse_encode_mem_map(mem_map, pnum)
                | SECTION_HAS_MEM_MAP | flags;
        ms->usage = usage;
}

参考文档

Physical Memory Model
https://lwn.net/Articles/789304/

pub struct VmComponents { #[cfg(all(target_arch = "x86_64", unix))] pub ac_adapter: bool, pub acpi_sdts: Vec<SDT>, pub android_fstab: Option<File>, pub boot_cpu: usize, pub bootorder_fw_cfg_blob: Vec<u8>, #[cfg(target_arch = "x86_64")] pub break_linux_pci_config_io: bool, pub cpu_capacity: BTreeMap<usize, u32>, pub cpu_clusters: Vec<CpuSet>, #[cfg(all( any(target_arch = "arm", target_arch = "aarch64"), any(target_os = "android", target_os = "linux") ))] pub cpu_frequencies: BTreeMap<usize, Vec<u32>>, pub delay_rt: bool, pub dynamic_power_coefficient: BTreeMap<usize, u32>, pub extra_kernel_params: Vec<String>, #[cfg(target_arch = "x86_64")] pub force_s2idle: bool, pub fw_cfg_enable: bool, pub fw_cfg_parameters: Vec<FwCfgParameters>, #[cfg(feature = "gdb")] pub gdb: Option<(u32, Tube)>, // port and control tube. pub host_cpu_topology: bool, pub hugepages: bool, pub hv_cfg: hypervisor::Config, pub initrd_image: Option<File>, pub itmt: bool, pub memory_size: u64, pub no_i8042: bool, pub no_rtc: bool, pub no_smt: bool, #[cfg(target_arch = "x86_64")] pub pci_low_start: Option<u64>, #[cfg(target_arch = "x86_64")] pub pcie_ecam: Option<AddressRange>, pub pflash_block_size: u32, pub pflash_image: Option<File>, pub pstore: Option<Pstore>, /// A file to load as pVM firmware. Must be `Some` iff /// `hv_cfg.protection_type == ProtectionType::UnprotectedWithFirmware`. pub pvm_fw: Option<File>, pub rt_cpus: CpuSet, #[cfg(target_arch = "x86_64")] pub smbios: SmbiosOptions, pub swiotlb: Option<u64>, pub vcpu_affinity: Option<VcpuAffinity>, pub vcpu_count: usize, #[cfg(all( any(target_arch = "arm", target_arch = "aarch64"), any(target_os = "android", target_os = "linux") ))] pub virt_cpufreq_socket: Option<std::os::unix::net::UnixStream>, pub vm_image: VmImage, }
最新发布
07-24
你提供的代码是 Rust 中一个结构体 `VmComponents` 的定义,它似乎用于描述一个虚拟机(VM)的配置组件。这个结构体包含了很多字段,其中一些是条件编译的(通过 `#[cfg(...)]` 属性控制),这表明它可能用于多个平台(如 x86_64、ARM、Android、Linux 等)并根据不同平台启用或禁用某些功能。 --- ### 结构体字段解释 - `ac_adapter`: 表示是否启用交流适配器(仅在 x86_64 + Unix 平台上编译)。 - `acpi_sdts`: 一组 ACPI 表(SDT)。 - `android_fstab`: Android 的 fstab 文件(可选)。 - `boot_cpu`: 启动 CPU 的索引。 - `bootorder_fw_cfg_blob`: 启动顺序的 fw_cfg blob 数据。 - `break_linux_pci_config_io`: 是否破坏 Linux 的 PCI 配置 I/O(仅 x86_64)。 - `cpu_capacity`: 每个 CPU 的容量。 - `cpu_clusters`: CPU 集合的向量。 - `cpu_frequencies`: 每个 CPU 的频率列表(ARM/Aarch64 + Linux/Android)。 - `delay_rt`: 是否延迟实时线程。 - `dynamic_power_coefficient`: 动态功耗系数。 - `extra_kernel_params`: 传递给内核的额外参数。 - `force_s2idle`: 是否强制使用 S2Idle(仅 x86_64)。 - `fw_cfg_enable`: 是否启用 fw_cfg 设备。 - `fw_cfg_parameters`: fw_cfg 参数列表。 - `gdb`: GDB 调试端口和通信管道(仅当启用 `gdb` feature 时)。 - `host_cpu_topology`: 是否使用主机的 CPU 拓扑。 - `hugepages`: 是否使用大页内存。 - `hv_cfg`: 超级管理器(hypervisor)配置。 - `initrd_image`: initrd 镜像文件(可选)。 - `itmt`: 是否启用 ITMT(Intel Thread Director)。 - `memory_size`: VM 的内存大小(以字节为单位)。 - `no_i8042`: 是否禁用 i8042 键盘控制器。 - `no_rtc`: 是否禁用 RTC(实时时钟)。 - `no_smt`: 是否禁用 SMT(同步多线程)。 - `pci_low_start`: PCI 低地址起始位置(x86_64)。 - `pcie_ecam`: PCIe ECAM 地址范围(x86_64)。 - `pflash_block_size`: pflash 块大小。 - `pflash_image`: pflash 镜像文件(可选)。 - `pstore`: pstore 配置(可选)。 - `pvm_fw`: pVM 固件镜像文件(仅当启用特定保护类型时)。 - `rt_cpus`: 实时 CPU 集合。 - `smbios`: SMBIOS 配置选项(x86_64)。 - `swiotlb`: 是否启用 SWIOTLB 以及其大小。 - `vcpu_affinity`: vCPU 亲和性设置(可选)。 - `vcpu_count`: vCPU 数量。 - `virt_cpufreq_socket`: 虚拟 cpufreq socket(ARM/Aarch64 + Linux/Android)。 - `vm_image`: VM 镜像对象。 --- ### 使用示例 以下是一个简单的示例,展示如何构建一个 `VmComponents` 实例(忽略平台相关的字段): ```rust use std::fs::File; use std::path::Path; use std::collections::{BTreeMap, HashSet}; use std::os::unix::net::UnixStream; // 假设这些结构体和类型已经定义 #[derive(Debug)] struct SDT; #[derive(Debug)] struct FwCfgParameters; #[derive(Debug)] struct Pstore; #[derive(Debug)] struct SmbiosOptions; #[derive(Debug)] struct hypervisor { Config: HypervisorConfig, } #[derive(Debug)] struct HypervisorConfig { protection_type: ProtectionType, } #[derive(Debug, PartialEq, Eq)] enum ProtectionType { UnprotectedWithFirmware, Other, } #[derive(Debug)] struct VmImage; #[derive(Debug)] struct VcpuAffinity; #[derive(Debug)] struct CpuSet(HashSet<usize>); impl CpuSet { fn new() -> Self { CpuSet(HashSet::new()) } } fn main() -> std::io::Result<()> { // 示例文件 let initrd_file = File::open("/path/to/initrd")?; let pflash_file = File::open("/path/to/pflash")?; let vm_image = VmImage; // 构建 VmComponents let vm_components = VmComponents { acpi_sdts: vec![SDT], android_fstab: None, boot_cpu: 0, bootorder_fw_cfg_blob: vec![], break_linux_pci_config_io: false, cpu_capacity: BTreeMap::new(), cpu_clusters: vec![CpuSet::new()], cpu_frequencies: None, delay_rt: false, dynamic_power_coefficient: BTreeMap::new(), extra_kernel_params: vec![], force_s2idle: false, fw_cfg_enable: false, fw_cfg_parameters: vec![], gdb: None, host_cpu_topology: false, hugepages: false, hv_cfg: hypervisor { Config: HypervisorConfig { protection_type: ProtectionType::Other, }, }, initrd_image: Some(initrd_file), itmt: false, memory_size: 1024 * 1024 * 1024, // 1GB no_i8042: true, no_rtc: true, no_smt: true, pci_low_start: None, pcie_ecam: None, pflash_block_size: 0x1000, pflash_image: Some(pflash_file), pstore: None, pvm_fw: None, rt_cpus: CpuSet::new(), smbios: SmbiosOptions, swiotlb: None, vcpu_affinity: None, vcpu_count: 4, virt_cpufreq_socket: None, vm_image, }; println!("VM Components initialized successfully."); Ok(()) } ``` --- ### 相关问题
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值