解决qemu虚拟机中内存偏小的问题

2023-10-28

问题描述:
最近测试部报了一个问题,云平台中设置大于4GB的内存并设置numa,启动linux2.6.32内核的客户机,之后操作系统中查看实际内存是1.9G,比设置内存小了大概2.1GB。

使用版本信息如下:
QEMU version: 3.0.0
guest os kernel version: 2.6.32
host kernel version: 4.9.0

问题排查如下:
1)在系统中排查问题
通过如下命令查看60系统下内存槽硬件信息,说明内存卡硬件识别正常:

$ dmidecode -t memory

在这里插入图片描述
在这里插入图片描述
通过$ free -m查看,系统识别出来却有问题:
在这里插入图片描述
通过测试如下命令启动QEMU虚拟机必然复现该问题:

$ qemu-system-x86_64 -enable-kvm -name guest=vm1,debug-threads=on -machine pc-i440fx-2.6,accel=kvm,usb=off,dump-guest-core=off -cpu Westmere -m size=4194304k,slots=16,maxmem=16777216k -realtime mlock=off -smp 1,sockets=1,cores=1,threads=1 -numa node,nodeid=0,cpus=0 -uuid fd3535db-2558-43e9-b067-314f48211343 -no-user-config -rtc base=localtime -no-shutdown -boot menu=on,strict=on -device piix3-usb-uhci,id=usb,bus=pci.0,addr=0x1.0x2 -drive file=/opt/issue32/generic.qcow2,format=raw,if=none,id=drive-ide0-0-0 -device ide-hd,bus=ide.0,unit=0,drive=drive-ide0-0-0,id=ide0-0-0,bootindex=1 -spice port=5900,addr=0.0.0.0,disable-ticketing,seamless-migration=on -k en-us -device cirrus-vga,id=video0,bus=pci.0,addr=0x4 -device virtio-balloon-pci,id=balloon0,bus=pci.0,addr=0x3 -msg timestamp=on

(这里的slots为16,所以先抛下一个问题:“为什么实测slots为3的时候不会出现2.1G的内存占用?”)
2)接下来在2.6.32内核源码中排查该问题:
通过debug发现是bootmem释放未占用的内存到buddy内存系统的时候少了2.1G,接下来继续研究bootmem的内存分配。

setup_node_bootmem->init_bootmem_node->free_boot_mem_with_active_regions->early_res_to_bootmem(这里干的好事)

bootmem是系统初始化期间的临时内存分配系统,通过位图来标识内存占用,bootmem自身的初始化很简单,首先将位图所有位置1,表示所有页已保留,之后通过free_boot_mem_with_active_regions将可用页帧置0,而free_boot_mem_with_active_regions中会调用early_res_to_bootmem将early_res分配器占用的内存页继承到bootmem中来,early_res是在bootmem之前更早的内存分配器,排查发现是early_res分配器使用过程中占用了2.1G的内存页,而bootmem继承了它导致未释放至buddy子系统。
因此继续向前排查,在这里发现了问题:

start_kernel->setup_arch->initmem_init->acpi_scan_nodes->compute_hash_shift->allocate_cachealigned_memnodemap->reserve_early

CONFIG_ACPI_NUMA确定此选项后内核会编译acpi_numa_init函数,获取numa支持,从硬件系统的acpi表中得到物理硬件的nodes信息,ACPI是在系统启动阶段由BIOS/UEFI收集各方面信息并创建的。而2.1G的内存分配正是在allocate_cachealigned_memnodemap中通过reserve_early完成的。
allocate_cachealigned_memnodemap分配的大小计算如下:

static int __init allocate_cachealigned_memnodemap(void)
{
	。。。
	nodemap_size = roundup(sizeof(s16) * memnodemapsize, L1_CACHE_BYTES);
	。。。
	reserve_early(nodemap_addr, nodemap_addr + nodemap_size, "MEMNODEMAP");
	。。。
}

memnodemapsize定义如下:

#define memnodemapsize memnode.mapsize

而memnode.mapsize的计算和设置是在compute_hash_shift->extract_lsb_from_nodes中完成的:

/*
 * The LSB of all start and end addresses in the node map is the value of the
 * maximum possible shift.
 */
static int __init extract_lsb_from_nodes(const struct bootnode *nodes,
					 int numnodes)
{
	int i, nodes_used = 0;
	unsigned long start, end;
	unsigned long bitfield = 0, memtop = 0;

	for (i = 0; i < numnodes; i++) {
		start = nodes[i].start;
		end = nodes[i].end;
		if (start >= end)
			continue;
		bitfield |= start;
		nodes_used++;
		if (end > memtop)
			memtop = end;
	}
	if (nodes_used <= 1)
		i = 63;
	else
		i = find_first_bit(&bitfield, sizeof(unsigned long)*8);
	memnodemapsize = (memtop >> i)+1;
	return i;
}

extract_lsb_from_nodes中将所有内存节点的start值进行了一个位或。之前埋下过一个问题也可以解释了“为什么slots为3的时候不会出现2.1G的内存reserve?”其实答案在于最后memnodemapsize = (memtop >> i)+1中,将unsigned long转换成了unsigned int,而i=0,直接导致了高32位的精度丢失,而在slot=3的时候恰好1全在高32位上面,导致mapsize=1,因此没有内存占用。所以这里又可以抛出一个问题了 ;)
(为什么内存区域会随着slots个数而变化?)

node区域范围有如下四个:
a) 0-655,360
b)1,048,576-3,221,225,472
c)4,200,000,000-5,300,000,000
d)35,433,480,192-35,433,480,191
最后一个区域范围恰好是hotplug的内存区域范围,很明显这里的hotplug区域范围有问题,怎么可能只有1。由于start只比end小1,也就直接导致了根据start位与计算的memnodemapsize值偏大,并且还出现了精度丢失!

好了,现在知道问题的直接导致是热插拔内存区设置的有问题,但是为什么会有问题? hotplug内存区参数是怎么读取的,之后想起开机启动的时候好像也报了和hotplug区相关的问题:
在这里插入图片描述
系统启动过程报hotplug区的过小,而hotplug参数也是来自于SRAT表的解析,SRAT(静态资源亲和性表)是ACPI规范的一部分,SRAT表解析流程如下:

start_kernel->setup_arch()->acpi_numa_init()->acpi_table_parse_srat()->acpi_table_parse_entries()
->acpi_parse_memory_affinity()->acpi_numa_memory_affinity_init()->update_nodes_add()

update_nodes_add函数中:

    if ((signed long)(end - start) < NODE_MIN_SIZE) {
        printk(KERN_ERR "SRAT: Hotplug area too small\n");
        return;
    }

这里的SRAT表是前期BIOS存放在内存中的,其根本来源还是qemu。

3)因此就开始了在qemu源码中排查问题的旅途
qemu中建立SRAT表的流程:

main->qemu_run_machine_init_done_notifiers->notifier_list_notify->pc_machine_done->acpi_setup->acpi_build->build_srat

build_srat是构建SRAT表的函数,其中有:

    if (hotplugabble_address_space_size) {
        build_srat_hotpluggable_memory(table_data, machine->device_memory->base,
                                       hotplugabble_address_space_size,
                                       pcms->numa_nodes - 1);
    }

这里的hotplug memory的基址是machine->device_memory->base=5,368,709,120,这个数值从前面的区域范围表中看起来是合理的,为什么这个base addr进入虚拟机后就变成
35,433,480,191了?

    ram_addr_t hotplugabble_address_space_size =
            object_property_get_int(OBJECT(pcms), PC_MACHINE_DEVMEM_REGION_SIZE,
                                		   NULL);

往前看,前面获取的hotplugaable_address_space_size=30064771072恰好是(35,433,480,192 - 5,368,709,120),也就是说end地址传入虚拟机是正确的。

最终在如下代码段找到了问题所在,在info为null是,build_srat_memory设置的start=end-1,size=1,和虚拟机中读取到的完全相符,看来是这里导致的该bug。

static void build_srat_hotpluggable_memory(GArray *table_data, uint64_t base,
                                           uint64_t len, int default_node)
{
    MemoryDeviceInfoList *info_list = qmp_memory_device_list();
    MemoryDeviceInfoList *info;
。。。
    for (cur = base, info = info_list;
         cur < end;
         cur += size, info = info->next) {
        numamem = acpi_data_push(table_data, sizeof *numamem);

        if (!info) {
            build_srat_memory(numamem, end - 1, 1, default_node,
                              MEM_AFFINITY_HOTPLUGGABLE | MEM_AFFINITY_ENABLED);
            break;
        }
。。。

通过参考最新版的的代码以及老版本(2.x)的代码稍作修改即可。

PS:这里其实也可以通过设置device模型为pc-dimm,让info不为null,比如如下:

-numa node -object memory-backend-ram,policy=default,size=4G,id=mem-mem1 -device pc-dimm,node=0,id=dimm-mem1,memdev=mem-mem1

#)
这里最后再解释下为什么内存区域会随着slots个数而变化:
hotplugin内存区域初始化流程如下:

main->machine_run_board_init->pc_init_v3_0->pc_init1->pc_memory_init

如下可知device_mem_size = max_size - ram_size + 1G * slots

device_mem_size = machine->maxram_size - machine->ram_size
        if (pcmc->enforce_aligned_dimm) {
            /* size device region assuming 1G page max alignment per slot */
            device_mem_size += (1 * GiB) * machine->ram_slots;
        }

device_mem_size = 12G(16G-4G) + 1G * 16(slots) = 28G(30064771072)。这里的1G * slots是考虑到大页对齐的问题,支持单页最大1G。所以hotplug区域也会随着slots而变化。

本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

解决qemu虚拟机中内存偏小的问题 的相关文章

  • 是否可以使用gdb和qemu同时调试linux用户空间程序和内核空间?

    到目前为止 使用 gdb qemu 我可以单步执行 Linux 内核源代码 是否可以同时调试用户空间程序 例如 将程序从用户空间单步执行到内核空间 这样我就可以通过发出以下命令来观察 qemu 监视器上寄存器的变化info register
  • 如何在 QEMU 用户模式下 GDB 单步调试动态链接的可执行文件?

    例如 对于 ARM 如果我静态编译 一切正常 sudo apt get install gdb multiarch gcc arm linux gnueabihf qemu user printf include
  • 在linux下构建edk2

    我开始用 edk2 编写一个小而简单的应用程序 因此 要编写一个简单的 edk2 UEFI 应用程序 我是这样开始的 git克隆https github com tianocore edk2 git edksetup sh BaseTool
  • 阿里云服务器有WordPress还可以再安装宝塔面板吗?

    不推荐 宝塔面板要求必须是纯净的操作系统环境安装宝塔 否则可能会有问题的 所以最好是先安装宝塔面板 再去安装wordpress 原文地址 阿里云服务器 WordPress 还可以再安装宝塔面板吗 轻量云Cloud WordPress一款广泛
  • Gem5 中与 ARM 裸机的 UART 通信

    我目前正在使用 Gem5 我必须通过 UART 从我的主机访问 ARMv8 裸机选项 所以我尝试了很多方法 但我还没有准备好 您能否让我知道 如何在裸机类型编程中将主机的串行端口映射到 ARMv8 的串行端口 任何帮助 将不胜感激 工作设置
  • 干货下载丨不分业态、不关注核心需求,怎么做得好项目管理?!

    项目管理 装备制造业的破局利刃 对于装备制造行业而言 每一笔订单都是 非标定制 小批量制造 这种特性决定了其行业企业普遍存在 新品开发周期长 生产效率低 质量不稳定 交货期不稳定 成本预算难控制 非标品报价慢 等问题 如何提升企业的管理水平
  • AWS解决方案架构师学习与备考

    系列文章目录 送书第一期 用户画像 平台构建与业务实践 送书活动之抽奖工具的打造 获取博客评论用户抽取幸运中奖者 送书第二期 Spring Cloud Alibaba核心技术与实战案例 送书第三期 深入浅出Java虚拟机 送书第四期 AI时
  • QEMU、无可启动设备、Linux 的 Windows 子系统

    我正在学习如何构建基本的操作系统内核https intermezzos github io https intermezzos github io 我已经创建了我的 iso文件 我现在正在运行qemu system x86 64 cdrom
  • 如何使用 QEMU 模拟混合平台?

    背景 有很多关于使用 QEMU 模拟特定架构系统 平台 的文档 例如 x86 ARM 或 RISCV 系统 第一步是配置 QEMU target list 例如 configure target list riscv32 softmmu h
  • 如何使用 QEMU 模拟 vmx 功能?

    我读自here https www kernel org doc Documentation virtual kvm nested vmx txt必须通过向命令提供 vmx 选项来显式启用 QEMU 上的 vmx 功能支持 但问题是它似乎不
  • 在 SR-IOV 虚拟功能 (VF) NIC 之间转发数据包

    我有一个支持 Intel SR IOV 的 Intel 82599ES 10G NIC 我已成功创建了 8 个虚拟功能 VF 并将其分配给 2 个 qemu kvm VM 每个 VM 2 个 VF 两台虚拟机都使用分配的 VF 运行 DPD
  • 阿里云服务器地域怎么选择?哪个地域好?

    阿里云服务器地域和可用区怎么选择 地域是指云服务器所在物理数据中心的位置 地域选择就近选择 访客距离地域所在城市越近网络延迟越低 速度就越快 可用区是指同一个地域下 网络和电力相互独立的区域 可用区之间可以做到故障隔离 将应用部署到不同可用
  • 阿里云服务器地域怎么选择?哪个地域好?

    阿里云服务器地域和可用区怎么选择 地域是指云服务器所在物理数据中心的位置 地域选择就近选择 访客距离地域所在城市越近网络延迟越低 速度就越快 可用区是指同一个地域下 网络和电力相互独立的区域 可用区之间可以做到故障隔离 将应用部署到不同可用
  • QEMU和KVM在虚拟机I/O中扮演什么角色?

    我发现 QEMU 和 KVM 之间的界限非常模糊 我发现有人说虚拟机是qemu进程 而另一些人说是kvm进程 究竟是什么 而QEMU和KVM在虚拟机I O中扮演什么角色呢 比如一个vm做PIO MMIO的时候 是qemu还是kvm会把它困住
  • 如何在进程结束后自动关闭`qemu`的执行?

    我想要的是qemu打开并显示输出后的窗口运行后自动关闭pintOS 就像我运行命令时一样pintos run alarm multiple in tcshshell qemu 显示过程开始 然后一些alarm notifications然后
  • 从 C、GCC(裸机)调用 ARM 汇编

    我正在尝试使用 GCC 在 ARM 中进行一些裸机编程并在 QEMU 上进行测试 每当我从 C 调用 ARM 标签时 我的程序就会挂起 我有一个简单的代码示例 显示了问题https gist github com 1654392 https
  • 读取时不返回写入 I2C 的缓冲区

    我试图在写入后从 I2C 总线上的内存位置读取一个值 当我在终端中运行它时 我得到奇怪的输出 这是我的程序 include
  • Android 模拟器在启动时永远挂起

    模拟器已启动 但未启动进一步的 android 闪烁文本 开始于 Android Sdk emulator emulator Nexus 5X API 25 verbose 日志中仅重复记录 emulator MemoryReport Ep
  • qemu kvm:如何获取性能监控中断?

    我在操作系统内核中编写了一些函数 以便在指令计数器溢出时发出性能监控中断 PMI 它在我的机器 Intel core i5 上运行良好 但是当我使用 qemu 在 qemu 上运行它时 qemu system x86 64 enable k
  • qemu 访客自动化

    我找不到任何文档说明存在可用于在 qemu guest 内部实现自动化操作的 API 例如 我想从主机启动来宾计算机内部的进程 Libvirt 似乎不包含此类功能 注意 无需使用任何虚拟化 API 即可实现自动化 从我的博客文章 http

随机推荐

  • Flutter实时动态UI刷新、数据交互

    setState 简介 setState 函数的作用是标记 StatefulWidget 中的 State 发生变化 需要重新构建 UI 即让Flutter架构自动实时刷新UI 当 StatefulWidget 的 State 发生变化时
  • 乔戈里推荐的新版Java学习路线,开源!

    Java 学习路线一条龙版 by 程序员鱼皮 所以我又抽空做了新版的 Java 学习路线一条龙 补充了很多内容 比如面试题 常用 Java 类库 常用软件等 让整个路线 字数翻倍 同时区分了各知识点的学习必要性 使得无论是急着找工作还是想花
  • vue3中setup语法糖下父子组件之间如何传递数据

    vue3中setup语法糖下父子组件之间如何传递数据 先弄明白什么是父子组件 父传子 子传父 组件间通信都有哪些方式 父子组件通信和兄弟组件通信的区别 先弄明白什么是父子组件 父子组件 分为父组件和子组件 Vue3中 父组件指的是包含一个或
  • 前端实现动态导航栏样式

  • 如何在电脑中安装虚拟机?

    第一步 先下载vmware软件 新手小白第一次下载软件会特别麻烦 自己也有可能在官网找不到相对应的软件 比如现在刚接触的我 内心无数的懵逼烦躁 还有很多很多的负面情绪 也懒得一个一个去摸索 下面是压缩包以及后续所需的激活码 加我百度网盘 别
  • 51单片机定时器/计数器T0

    选择方式0 方式1 方式2时 T0 T1的工作情况相同 选择方式3时 T0 T1的工作情况不同 方式0 13位定时器 计数器 TH0的高8位 TL0的低5位 方式1 16位定时器 计数器 TH0的高8位 TL0的低8位 方式2 自动重装的8
  • git出现error: invalid object for ‘xxxxx‘

    该问题说明git本地仓库 git objects里丢失了部分文件 执行git hash object w xxxxx 即可修复 xxxxx是invalid object for后面的文件路径
  • 向上管理(中高层核心能力的表现)

    向上管理 即在工作中为了让公司或上级以及自己取得更好的结果而下意识地配合上级一起工作的过程 在职业生涯中 向上管理其实也是工作能力的一部分 一项重要的职业技能 管理者不仅要做好向下管理 他们还要学会向上管理 1 向下管理 主要涉及的是管理者
  • 基于51单片机霍尔传感器测速(仿真+源程序)

    资料编号 196 下面是该资料仿真演示视频 196 基于51单片机霍尔传感器测速 仿真 源程序 全套资料 功能简介 51单片机计数测速转速测量 在仿真中等价于测量外部脉冲频率 如果修改输入脉冲的频率 在数码管上可实时显示当前频率 功能 霍尔
  • MTCNN人脸及特征点检测---代码应用详解(基于ncnn架构)

    本博记录为卤煮理解 如有疏漏 请指正 转载请注明出处 卤煮 非文艺小燕儿 本博地址 MTCNN人脸及特征点检测 代码应用详解 本文主要讲述当你拿到MTCNN的caffemodel后 如何使用它对一张图里的人脸进行检测和特征点标定 相当于一个
  • Qt信号和槽机制

    Qt信号和槽机制 什么是信号和槽 当某个事件发生 就执行一个操作 发生的事情就是信号 执行的操作就是槽 函数 给二者加上主体 信号发出者发出信号 信号接收者执行操作 将二者联系起来 松耦合 connect 函数 connect sender
  • 网络安全必备1000道面试题集锦(附答案)

    前言 以下为网络安全各个方向涉及的面试题 星数越多代表问题出现的几率越大 祝各位都能找到满意的工作 注 本套面试题 已整理成pdf文档 但内容还在持续更新中 因为无论如何都不可能覆盖所有的面试问题 更多的还是希望由点达面 查漏补缺 一 渗透
  • Error: @vitejs/plugin-vue requires vue (>=3.2.13) or @vue/compiler-sfc to be present in the dependen

    1 没有下载安装axios运行依赖 2 或者缺少这个库没有安装 npm i vue compiler sfc 3 node版本冲不冲突
  • Android Button、TextView等控件使用Toolbar中默认的返回图标

    Button backButton findViewById R id back button backButton setBackgroundResource R drawable abc ic ab back material R dr
  • 网站框架识别方法

    cms一般有dedecms 织梦 dzcms phpweb phpwind phpcms ecshop dvbbs siteweaver aspcms 帝国 zblog wordpress等 一般cms都有特定的文件 只需要识别特定的文件便
  • 运行jetty-maven-plugin时,出现错误

    ERROR Failed to execute goal org eclipse jetty jetty maven plugin 9 4 0 v20161208 run default cli on project kind perm w
  • 【漏洞复现】CVE-2023-22809 sudo提权漏洞

    一 前言 漏洞简介 Sudo中的sudoedit对处理用户提供的环境变量 如SUDO EDITOR VISUAL和EDITOR 中传递的额外参数存在缺陷 当用户指定的编辑器包含绕过sudoers策略的 参数时 拥有sudoedit访问权限的
  • 「iOS」swift 和 objectivec 获得对象的 class 或者 Type 的方法

    一 oc 中使用 oc 中非常简单 一行搞定 NSString str1 test str1 class 这里的 str1 class 就是获取对象 class 的方法 二 swift 中使用 时间紧 任务重 上代码 var str Str
  • Windows 找不到文件 ‘chrome‘。请确认文件名是否正确后,再试一次。

    Windows 找不到文件 chrome 请确认文件名是否正确后 再试一次 错误 当不运行 IDEA 项目 通过快捷键进入浏览器时 可能会出现以下错误 原因 未设置 chrome 路径 导致找不到路径报错 解决办法 1 在桌面上找到 chr
  • 解决qemu虚拟机中内存偏小的问题

    问题描述 最近测试部报了一个问题 云平台中设置大于4GB的内存并设置numa 启动linux2 6 32内核的客户机 之后操作系统中查看实际内存是1 9G 比设置内存小了大概2 1GB 使用版本信息如下 QEMU version 3 0 0