专栏
天翼云开发者社区

虚拟机内存热插拔的演化之路

2023-07-20 09:52:37 261阅读

这篇文章给大家分享的是介绍虚拟机内存热插拔从最初的virtio-balloon到如今virtio-mem的演化之路。

内存热插拔技术演化的时间线:

 

virtio-balloon

DIMM-based memory hot(un)plug

virtio-mem

出现时间

2008年

2015年

2020年

内存热插拔技术的主要区别:

对比项

virtio-balloon

DIMM-based memory hot(un)plug

virtio-mem

虚拟化类型

半虚拟化

全虚拟化

半虚拟化

内存粒度

Page, e.g. 4KB

>=128MB on x86 Linux

4MB on x86-64

硬件架构依赖

No

Yes,依赖ACPI

No

NUMA

 不支持

支持

支持

 

virtio-balloon

Virtio-Balloon可以认为是早期的间接实现内存热插拔的功能,但其实没有热插拔,而是动态增减内存大小,并且依赖于客户机里的virtio_balloon驱动,对客户机来说并没有硬件上的增减。

Virtio-Balloon即内存气球,可以用来动态调整虚拟机内存,在VM和hypervisor之间迁移物理内存。virtio-balloon遵循Virtio半虚拟化规范,包含了虚拟机里virtio-balloon前端驱动和hypervisor模拟的virtio-balloon后端设备。

Virtio-Balloon除了用来动态调整虚拟机内存,还支持监控虚机内存指标的功能。

Virtio-Balloon在节约内存和灵活分配内存方面有明显的优势,其好处有如下三点:

       1、因为能够控制和监控ballooning,所以ballooning能够潜在地节约大量的内存。它不同于内存页共享技术(KSM是内核自发完成的、不可控),VM系统的内存只有在通过命令行调整balloon时才会随之改变,所以能够监控系统内存并验证ballooning引起的变化。

       2、Ballooning对内存的调节很灵活,既可以精细的请求少量内存,又可以粗犷的请求大量的内存。

       3、hypervisor使用ballooning让VM归还部分内存,从而可以缓解其内存压力。而且从气球中回收的内存也不要求一定要被分配给另外某个进程(或另外的VM)。

Virtio-Balloon进行内存复用本身存在一些问题:

       1、Guest对内存变化会进行感知,Balloon特性本身是kernel所具备的,本身是通过修改识别的内存信息来限制Guest中的使用。所以不是很友好。

       2、内存复用需要实时监控,发现客户虚拟机内存使用过多还要及时归还内存,对于系统本身做这个功能有很大的局限性。

       3、Balloon特性的内存复用并不是本质上进行内存的冗余复用,仅仅是借东墙补西墙,当虚拟机都大量使用内存时候,并不能实际突破物理内存上限。

       4、如果有大量内存从VM系统中回收,Ballooning可能会降低VM操作系统运行的性能。一方面,内存的减少,可能会让VM中作为磁盘数据缓存的内存被放到气球中,从而VM中的磁盘I/O访问会增加;另一方面,如果处理机制不够好,也可能让VM中正在运行的进程由于内存不足而执行失败。

       5、内存的动态增加或减少,可能会使内存被过度碎片化,从而降低内存使用时的性能。另外,内存的变化会影响到VM内核对内存使用的优化,比如:内核起初根据目前状态对内存的分配采取了某个策略,而突然由于balloon的效果让可用内存减少了很多,这时起初的内存策略可能就不是太优化的了。

virtio-balloon工作原理

内存气球有两种操作:

       气球收缩:宿主机的内存还给虚拟机,相当于VM内存热缩减

       气球膨胀:虚拟机的内存被拿掉给宿主机,相当于VM内存热添加

气球收缩(balloon deflate)的工作原理:

       1、VM里的virtio-balloon驱动收到hypervisor的请求后,释放掉VM内存气球占用的部分内存。

       2、Guest OS可以使用VM气球释放出来的内存,从而实现VM内存热添加。

气球膨胀(balloon inflate)的工作原理:

       1、Guest virtio-balloon驱动收到hypervisor的请求后,VM内存气球膨胀,气球中的内存就不能被客户机访问,从而实现VM内存缩减。

       2、hypervisor可以重用膨胀的内存(例如:用于其他VM)

virtio-balloon的已知问题

      1、设计上的漏洞:Guest可以重复使用膨胀的内存和伪造balloon stat;Hypervisor不能拒绝任何 膨胀/收缩 的请求。

      2、基于4KB page,hypervisor不能支持大页/不同page size

      3、不支持NUMA

      4、不支持VFIO

 

DIMM-based memory hot(un)plug

基于模拟DIMM的内存热插拔(DIMM-based memory hot(un)plug),是指hypervisor通过hypervisor对DIMM硬件技术进行软件模拟以实现虚拟机内存热插拔。

目前Qemu的内存热插拔通常指的就是基于模拟DIMM的内存热插拔。

基于模拟DIMM的内存热插拔,除了需hypervisor对DIMM硬件进行模拟,还需要依赖Guest系统支持,如Linux 内核仅在的 64 位架构上支持内存热插拔,例如 x86_64、arm64、ppc64、s390x 和 ia64。Linux内存热插拔的粒度取决于架构。例如x86_64 使用 128 MiB,ppc64 使用 16 MiB。

Linux内核不能主动感知到PCI总线上DIMM内存条的插拔,需要有固件先触发一个内存热插拔事件通知Linux内核,Linux内核才能可以开始热插拔内存。在支持 ACPI 的平台,例如 x86_64,可以通过 ACPI固件去支持内存热插拔通知。

Linux编译时配置将 CONFIG_ACPI_HOTPLUG_MEMORY 配置为内核模块,所以我们需要首先加载一个内核模块:acpi_memhotplug.ko。

 ACPI (Advanced Configuration and Power Interface) 是由业界一些软硬件公司共同开发的开放式工业规范。它能使软、硬件、操作系统(OS),主机板和外围设备,依照一定的方式管理用电情况和系统硬件产生的 Hot-Plug 事件,让操作系统从用户的角度上直接支配即插即用设备,不同于以往直接通过基于 BIOS 的方式的管理。

什么是DIMM?

DIMM全称Double-Inline Memory Module,中文名叫双列直插式存储模块,是一种硬件技术,是指奔腾CPU推出后出现的新型内存条,它提供了64位的数据通道。

在80286时代,内存颗粒(Chip)是直接插在主板上的,叫做DIP(Dual In-line Package)。到了80386时代,换成1片焊有内存颗粒的电路板,叫做SIMM(Single-Inline Memory Module)。由阵脚形态变化成电路板带来了很多好处模块化,安装便利等等,由此DIY市场才有可能产生。当时SIMM的位宽是32bit,即一个周期读取4个字节,到了奔腾时,位宽变为64bit,即8个字节,于是SIMM就顺势变为DIMM(Double-Inline Memory Module)。这种形态一直延续至今,也是内存条的基本形态。

现在DIMM分为很多种:

       RDIMM: 全称(Registered DIMM),寄存型模组,主要用在服务器上,为了增加内存的容量和稳定性分有ECC和无ECC两种,但市场上几乎都是ECC的。

       UDIMM:全称(Unbuffered DIMM),无缓冲型模组,这是我们平时所用到的标准台式电脑DIMM,分有ECC和无ECC两种,一般是无ECC的。

       SO-DIMM:全称(Small Outline DIMM),小外型DIMM,笔记本电脑中所使用的DIMM,分ECC和无ECC两种。

       Mini-DIMM:DDR2时代新出现的模组类型,它是Registered DIMM的缩小版本,用于刀片式服务器等对体积要求苛刻的高端领域。

DIMM插槽指用来插DIMM内存条的插槽,主板所支持的内存种类和容量都由内存插槽来决定的,一个插槽只能插一根DIMM条。

DIMM的软件模拟实现

hypervisor会先alloc一块内存,作为memory device的内存,添加到memory slot上,guest里面通过acpi的事件,得知memory device热添加。

如果guest是linux的话,在/sys/devices/system/memory目录下会增加新的memory目录,选择online为0的,修改为1就能让memory上线。例如,执行echo 1 > /sys/devices/system/memory/memory/memory64/online 执行完命令后,通过free -h或者cat /proc/meminfo就可以看到内存增加了。

pc-dimm 设备 (hw/mem/pc-dimm.c) 用来表示一个内存条。内存可以通过创建一个新的 pc-dimm 设备实现热插功能。虽然名称中有 pc 字段,但该设备也可与 ppc、s390 机器配合使用。要注意的是,vm 启动时的初始 RAM 可能无法使用 “pc-dimm” 设备建模,并且无法热拔出。vm RAM 本身并不在 pc-dimm 对象的容器中,也就是说,pc-dimm 对象必须与内存后端对象关联。

DIMM的已知问题

       1、一些架构不支持DIMM,例如:s390x

       2、一些架构不支持memory hotplug通告,例如:ARM64要在guest手动操作

 

virtio-mem

内存气球(virtio-balloon)和基于模拟 DIMM的热插拔(DIMM-based memory hot(un)plug)都可以动态调整虚拟机内存的大小。但是这两种传统方法提供的灵活性有限,与 vNUMA 和快速操作系统重启等重要技术不兼容,或者不适用于托管不受信任的虚拟机。

为了克服这些限制,KVM社区在2018年开始计划引入了 virtio-mem,这是一种基于 VIRTIO 的半虚拟化内存设备,专为云环境中的细粒度、NUMA 感知内存热插拔而设计。

相比基于模拟 DIMM的热插拔, virtio-mem能以更小的粒度为每个 NUMA 节点插拔内存,例如 在x86-64 上支持最小粒度4 MiB。与内存气球相比,virtio-mem 完全支持 NUMA,并通过设计支持快速操作系统重启,同时保证可以可靠地检测到尝试使用比约定更多内存的恶意虚拟机。

virtio-mem的技术特点是细粒度和支持NUMA。

virtio-mem遵循Virtio半虚拟化规范,包含了虚拟机里virtio-mem前端驱动和hypervisor模拟的virtio-mem后端设备。

使用virtio-mem时,hypervisor在启动虚机时就按虚机的最大内存值向物理机申请内存。

virtio-mem的设计目标

virtio-mem的设计结合了virtio-balloon和DIMM-based memory hot(un)plug的优点,避免了已知问题。

  • 适用于所有架构, 提供了一种灵活的、跨架构的内存热插拔解决方案,避免了现有技术、架构和接口造成的许多限制
  • 完全在hypervisor内部管理大小更改
  • 提供一种安全的方式来检测恶意客户端
  • 支持不同page sizes/ 大页
  • 支持NUMA
  • 在x86-64上,目前可以在运行Linux的虚拟机上以4个MiB粒度添加/删除内存,而在不久的将来,目标是支持2个MiB粒度。

virtio-mem的工作原理

virtio-mem devices技术要点:

  • 每个device在VM物理地址空间中管理一个专用的memory region
  • 每个device都可以分配给VM的一个NUMA node
  • 以块为粒度(eg:>= 4 MB on x86-64)

virtio-mem devices的三个主要属性:

  • Size:当前已添加的内存
  • Maximum size: 最多可以添加多少内存
  • Requested size: 请求Guest driver 增/减 内存以达到请求的大小。

virtio-mem目前平台支持

Linux Kernel从v5.8-rc1开始支持virtio-mem

Windows目前还不支持virtio-mem

QEMU 从v5.1.0-rc1开始支持virtio-mem

libvirt 从v7.9.0-rc1开始支持virtio-mem

 

参考资料

virtio-mem主页

https://virtio-mem.gitlab.io/

"virtio-mem: Paravirtualized Memory" talk at KVM Forum 2018

https://events19.linuxfoundation.org/wp-content/uploads/2017/12/virtio-mem-Paravirtualized-Memory-David-Hildenbrand-Red-Hat-1.pdf

"Virtio-(balloon|pmem|mem): Managing Guest Memory" talk at KVM Forum 2020

https://static.sched.com/hosted_files/kvmforum2020/8e/KVM%20Forum%202020%20Virtio-%28balloon%20pmem%20mem%29%20Managing%20Guest%20Memory.pdf

virtio-spec: Add virtio-mem device specification

https://github.com/oasis-tcs/virtio-spec/blob/master/virtio-mem.tex

Qemu memory-hotplug

https://github.com/qemu/qemu/blob/master/docs/memory-hotplug.txt

Linux kernel memory-hotplug

https://www.kernel.org/doc/html/latest/admin-guide/mm/memory-hotplug.html

  • 0
  • 0
  • 0
0 评论
0/1000
评论(0) 发表评论
邓****华

邓****华

3 篇文章 0 粉丝
关注

虚拟机内存热插拔的演化之路

2023-07-20 09:52:37 261阅读

这篇文章给大家分享的是介绍虚拟机内存热插拔从最初的virtio-balloon到如今virtio-mem的演化之路。

内存热插拔技术演化的时间线:

 

virtio-balloon

DIMM-based memory hot(un)plug

virtio-mem

出现时间

2008年

2015年

2020年

内存热插拔技术的主要区别:

对比项

virtio-balloon

DIMM-based memory hot(un)plug

virtio-mem

虚拟化类型

半虚拟化

全虚拟化

半虚拟化

内存粒度

Page, e.g. 4KB

>=128MB on x86 Linux

4MB on x86-64

硬件架构依赖

No

Yes,依赖ACPI

No

NUMA

 不支持

支持

支持

 

virtio-balloon

Virtio-Balloon可以认为是早期的间接实现内存热插拔的功能,但其实没有热插拔,而是动态增减内存大小,并且依赖于客户机里的virtio_balloon驱动,对客户机来说并没有硬件上的增减。

Virtio-Balloon即内存气球,可以用来动态调整虚拟机内存,在VM和hypervisor之间迁移物理内存。virtio-balloon遵循Virtio半虚拟化规范,包含了虚拟机里virtio-balloon前端驱动和hypervisor模拟的virtio-balloon后端设备。

Virtio-Balloon除了用来动态调整虚拟机内存,还支持监控虚机内存指标的功能。

Virtio-Balloon在节约内存和灵活分配内存方面有明显的优势,其好处有如下三点:

       1、因为能够控制和监控ballooning,所以ballooning能够潜在地节约大量的内存。它不同于内存页共享技术(KSM是内核自发完成的、不可控),VM系统的内存只有在通过命令行调整balloon时才会随之改变,所以能够监控系统内存并验证ballooning引起的变化。

       2、Ballooning对内存的调节很灵活,既可以精细的请求少量内存,又可以粗犷的请求大量的内存。

       3、hypervisor使用ballooning让VM归还部分内存,从而可以缓解其内存压力。而且从气球中回收的内存也不要求一定要被分配给另外某个进程(或另外的VM)。

Virtio-Balloon进行内存复用本身存在一些问题:

       1、Guest对内存变化会进行感知,Balloon特性本身是kernel所具备的,本身是通过修改识别的内存信息来限制Guest中的使用。所以不是很友好。

       2、内存复用需要实时监控,发现客户虚拟机内存使用过多还要及时归还内存,对于系统本身做这个功能有很大的局限性。

       3、Balloon特性的内存复用并不是本质上进行内存的冗余复用,仅仅是借东墙补西墙,当虚拟机都大量使用内存时候,并不能实际突破物理内存上限。

       4、如果有大量内存从VM系统中回收,Ballooning可能会降低VM操作系统运行的性能。一方面,内存的减少,可能会让VM中作为磁盘数据缓存的内存被放到气球中,从而VM中的磁盘I/O访问会增加;另一方面,如果处理机制不够好,也可能让VM中正在运行的进程由于内存不足而执行失败。

       5、内存的动态增加或减少,可能会使内存被过度碎片化,从而降低内存使用时的性能。另外,内存的变化会影响到VM内核对内存使用的优化,比如:内核起初根据目前状态对内存的分配采取了某个策略,而突然由于balloon的效果让可用内存减少了很多,这时起初的内存策略可能就不是太优化的了。

virtio-balloon工作原理

内存气球有两种操作:

       气球收缩:宿主机的内存还给虚拟机,相当于VM内存热缩减

       气球膨胀:虚拟机的内存被拿掉给宿主机,相当于VM内存热添加

气球收缩(balloon deflate)的工作原理:

       1、VM里的virtio-balloon驱动收到hypervisor的请求后,释放掉VM内存气球占用的部分内存。

       2、Guest OS可以使用VM气球释放出来的内存,从而实现VM内存热添加。

气球膨胀(balloon inflate)的工作原理:

       1、Guest virtio-balloon驱动收到hypervisor的请求后,VM内存气球膨胀,气球中的内存就不能被客户机访问,从而实现VM内存缩减。

       2、hypervisor可以重用膨胀的内存(例如:用于其他VM)

virtio-balloon的已知问题

      1、设计上的漏洞:Guest可以重复使用膨胀的内存和伪造balloon stat;Hypervisor不能拒绝任何 膨胀/收缩 的请求。

      2、基于4KB page,hypervisor不能支持大页/不同page size

      3、不支持NUMA

      4、不支持VFIO

 

DIMM-based memory hot(un)plug

基于模拟DIMM的内存热插拔(DIMM-based memory hot(un)plug),是指hypervisor通过hypervisor对DIMM硬件技术进行软件模拟以实现虚拟机内存热插拔。

目前Qemu的内存热插拔通常指的就是基于模拟DIMM的内存热插拔。

基于模拟DIMM的内存热插拔,除了需hypervisor对DIMM硬件进行模拟,还需要依赖Guest系统支持,如Linux 内核仅在的 64 位架构上支持内存热插拔,例如 x86_64、arm64、ppc64、s390x 和 ia64。Linux内存热插拔的粒度取决于架构。例如x86_64 使用 128 MiB,ppc64 使用 16 MiB。

Linux内核不能主动感知到PCI总线上DIMM内存条的插拔,需要有固件先触发一个内存热插拔事件通知Linux内核,Linux内核才能可以开始热插拔内存。在支持 ACPI 的平台,例如 x86_64,可以通过 ACPI固件去支持内存热插拔通知。

Linux编译时配置将 CONFIG_ACPI_HOTPLUG_MEMORY 配置为内核模块,所以我们需要首先加载一个内核模块:acpi_memhotplug.ko。

 ACPI (Advanced Configuration and Power Interface) 是由业界一些软硬件公司共同开发的开放式工业规范。它能使软、硬件、操作系统(OS),主机板和外围设备,依照一定的方式管理用电情况和系统硬件产生的 Hot-Plug 事件,让操作系统从用户的角度上直接支配即插即用设备,不同于以往直接通过基于 BIOS 的方式的管理。

什么是DIMM?

DIMM全称Double-Inline Memory Module,中文名叫双列直插式存储模块,是一种硬件技术,是指奔腾CPU推出后出现的新型内存条,它提供了64位的数据通道。

在80286时代,内存颗粒(Chip)是直接插在主板上的,叫做DIP(Dual In-line Package)。到了80386时代,换成1片焊有内存颗粒的电路板,叫做SIMM(Single-Inline Memory Module)。由阵脚形态变化成电路板带来了很多好处模块化,安装便利等等,由此DIY市场才有可能产生。当时SIMM的位宽是32bit,即一个周期读取4个字节,到了奔腾时,位宽变为64bit,即8个字节,于是SIMM就顺势变为DIMM(Double-Inline Memory Module)。这种形态一直延续至今,也是内存条的基本形态。

现在DIMM分为很多种:

       RDIMM: 全称(Registered DIMM),寄存型模组,主要用在服务器上,为了增加内存的容量和稳定性分有ECC和无ECC两种,但市场上几乎都是ECC的。

       UDIMM:全称(Unbuffered DIMM),无缓冲型模组,这是我们平时所用到的标准台式电脑DIMM,分有ECC和无ECC两种,一般是无ECC的。

       SO-DIMM:全称(Small Outline DIMM),小外型DIMM,笔记本电脑中所使用的DIMM,分ECC和无ECC两种。

       Mini-DIMM:DDR2时代新出现的模组类型,它是Registered DIMM的缩小版本,用于刀片式服务器等对体积要求苛刻的高端领域。

DIMM插槽指用来插DIMM内存条的插槽,主板所支持的内存种类和容量都由内存插槽来决定的,一个插槽只能插一根DIMM条。

DIMM的软件模拟实现

hypervisor会先alloc一块内存,作为memory device的内存,添加到memory slot上,guest里面通过acpi的事件,得知memory device热添加。

如果guest是linux的话,在/sys/devices/system/memory目录下会增加新的memory目录,选择online为0的,修改为1就能让memory上线。例如,执行echo 1 > /sys/devices/system/memory/memory/memory64/online 执行完命令后,通过free -h或者cat /proc/meminfo就可以看到内存增加了。

pc-dimm 设备 (hw/mem/pc-dimm.c) 用来表示一个内存条。内存可以通过创建一个新的 pc-dimm 设备实现热插功能。虽然名称中有 pc 字段,但该设备也可与 ppc、s390 机器配合使用。要注意的是,vm 启动时的初始 RAM 可能无法使用 “pc-dimm” 设备建模,并且无法热拔出。vm RAM 本身并不在 pc-dimm 对象的容器中,也就是说,pc-dimm 对象必须与内存后端对象关联。

DIMM的已知问题

       1、一些架构不支持DIMM,例如:s390x

       2、一些架构不支持memory hotplug通告,例如:ARM64要在guest手动操作

 

virtio-mem

内存气球(virtio-balloon)和基于模拟 DIMM的热插拔(DIMM-based memory hot(un)plug)都可以动态调整虚拟机内存的大小。但是这两种传统方法提供的灵活性有限,与 vNUMA 和快速操作系统重启等重要技术不兼容,或者不适用于托管不受信任的虚拟机。

为了克服这些限制,KVM社区在2018年开始计划引入了 virtio-mem,这是一种基于 VIRTIO 的半虚拟化内存设备,专为云环境中的细粒度、NUMA 感知内存热插拔而设计。

相比基于模拟 DIMM的热插拔, virtio-mem能以更小的粒度为每个 NUMA 节点插拔内存,例如 在x86-64 上支持最小粒度4 MiB。与内存气球相比,virtio-mem 完全支持 NUMA,并通过设计支持快速操作系统重启,同时保证可以可靠地检测到尝试使用比约定更多内存的恶意虚拟机。

virtio-mem的技术特点是细粒度和支持NUMA。

virtio-mem遵循Virtio半虚拟化规范,包含了虚拟机里virtio-mem前端驱动和hypervisor模拟的virtio-mem后端设备。

使用virtio-mem时,hypervisor在启动虚机时就按虚机的最大内存值向物理机申请内存。

virtio-mem的设计目标

virtio-mem的设计结合了virtio-balloon和DIMM-based memory hot(un)plug的优点,避免了已知问题。

  • 适用于所有架构, 提供了一种灵活的、跨架构的内存热插拔解决方案,避免了现有技术、架构和接口造成的许多限制
  • 完全在hypervisor内部管理大小更改
  • 提供一种安全的方式来检测恶意客户端
  • 支持不同page sizes/ 大页
  • 支持NUMA
  • 在x86-64上,目前可以在运行Linux的虚拟机上以4个MiB粒度添加/删除内存,而在不久的将来,目标是支持2个MiB粒度。

virtio-mem的工作原理

virtio-mem devices技术要点:

  • 每个device在VM物理地址空间中管理一个专用的memory region
  • 每个device都可以分配给VM的一个NUMA node
  • 以块为粒度(eg:>= 4 MB on x86-64)

virtio-mem devices的三个主要属性:

  • Size:当前已添加的内存
  • Maximum size: 最多可以添加多少内存
  • Requested size: 请求Guest driver 增/减 内存以达到请求的大小。

virtio-mem目前平台支持

Linux Kernel从v5.8-rc1开始支持virtio-mem

Windows目前还不支持virtio-mem

QEMU 从v5.1.0-rc1开始支持virtio-mem

libvirt 从v7.9.0-rc1开始支持virtio-mem

 

参考资料

virtio-mem主页

https://virtio-mem.gitlab.io/

"virtio-mem: Paravirtualized Memory" talk at KVM Forum 2018

https://events19.linuxfoundation.org/wp-content/uploads/2017/12/virtio-mem-Paravirtualized-Memory-David-Hildenbrand-Red-Hat-1.pdf

"Virtio-(balloon|pmem|mem): Managing Guest Memory" talk at KVM Forum 2020

https://static.sched.com/hosted_files/kvmforum2020/8e/KVM%20Forum%202020%20Virtio-%28balloon%20pmem%20mem%29%20Managing%20Guest%20Memory.pdf

virtio-spec: Add virtio-mem device specification

https://github.com/oasis-tcs/virtio-spec/blob/master/virtio-mem.tex

Qemu memory-hotplug

https://github.com/qemu/qemu/blob/master/docs/memory-hotplug.txt

Linux kernel memory-hotplug

https://www.kernel.org/doc/html/latest/admin-guide/mm/memory-hotplug.html

文章来自专栏

虚拟化

3 篇文章 1 订阅
0 评论
0/1000
评论(0) 发表评论
  • 0
    点赞
  • 0
    收藏
  • 0
    评论