【20240912】Proxmox VE aarch64架构下虚拟机多串口启动失败问题

257 阅读29分钟

0 前言

转载请附上原文出处链接

稀土掘金本文链接:juejin.cn/spost/74181…

CSDN本文链接:blog.csdn.net/qq_43252610…

1 问题概述

1.1 基础环境

【系统】Kylin-Desktop-V10-GFB-Release-2207-Build11-ARM64.iso(5.4.18-65-generic)

【CPU】vCPU

【显卡】virtio-gpu

1.2 问题现象

虚拟机添加多个串口设备后,启动失败

报错信息

kvm: -device isa-serial,chardev=serial1: 'isa-serial' is not a valid device model name
TASK ERROR: start failed: QEMU exited with code 1

2 问题定位

从报错来看,是isa-serial这种类型不支持

2.1 X86架构对比验证

PVE也有x86架构的版本,x86架构添加多个串口设备并不会出现启动失败的问题

对比x86aarch64qemu支持的device

x86:

/usr/bin/qemu-system-x86_64 -device help | grep serial

name "isa-serial", bus ISA
name "pci-serial", bus PCI
name "pci-serial-2x", bus PCI
name "pci-serial-4x", bus PCI
name "usb-serial", bus usb-bus
name "virtconsole", bus virtio-serial-bus
name "virtio-serial-device", bus virtio-bus
name "virtio-serial-pci", bus PCI, alias "virtio-serial"
name "virtio-serial-pci-non-transitional", bus PCI
name "virtio-serial-pci-transitional", bus PCI
name "virtserialport", bus virtio-serial-bus

aarch64:

/usr/bin/qemu-system-aarch64 -device help | grep serial

name "pci-serial", bus PCI
name "pci-serial-2x", bus PCI
name "pci-serial-4x", bus PCI
name "usb-serial", bus usb-bus
name "virtconsole", bus virtio-serial-bus
name "virtio-serial-device", bus virtio-bus
name "virtio-serial-pci", bus PCI, alias "virtio-serial"
name "virtio-serial-pci-non-transitional", bus PCI
name "virtio-serial-pci-transitional", bus PCI
name "virtserialport", bus virtio-serial-bus

可以看出aarch64架构中确实不包含isa-serial设备

2.2 kvm启动参数分析

那么aarch64架构下,第一个串口设备是如何正常工作的呢?

对比下虚拟机的PVE配置文件的串口部分(去掉了无关信息)

x86:

cat /etc/pve/qemu-server/103.conf

serial0: socket
serial1: socket

aarch64:

cat /etc/pve/qemu-server/142.conf

serial0: socket
serial1: socket

从配置文件来看,串口配置信息是完全一致的

PVE底层是使用kvm启动虚拟机,对比看一下虚拟机在kvm的启动参数的串口部分(去掉了无关信息)

PVE自身提供对应的接口查看

x86:

qm showcmd 103 --pretty

-chardev 'socket,id=serial0,path=/var/run/qemu-server/103.serial0,server=on,wait=off' \
-device 'isa-serial,chardev=serial0' \
-chardev 'socket,id=serial1,path=/var/run/qemu-server/103.serial1,server=on,wait=off' \
-device 'isa-serial,chardev=serial1' \

aarch64:

qm showcmd 142 --pretty

-chardev 'socket,id=serial0,path=/var/run/qemu-server/142.serial0,server=on,wait=off' \
-serial chardev:serial0 \
-chardev 'socket,id=serial1,path=/var/run/qemu-server/142.serial1,server=on,wait=off' \
-device 'isa-serial,chardev=serial1' \

x86架构下第一个串口设备配置的是-device 'isa-serial,chardev=serial0'aarch64架构下第一个串口设备配置的却是-serial chardev:serial0

这也是为什么aarch64架构下第一个串口是能正常启动的,因为第一个串口没有使用isa-serial设备

PVE的虚拟机配置文件到kvm启动参数之间的转换,是由PVE实现并控制的,同样的虚拟机配置文件出现不同架构下的不同表现,需要分析PVE的相关代码

2.3 PVE代码分析

PVEgithub官方只读镜像项目路径:github.com/proxmox

PVE的虚拟机配置文件到kvm启动参数之间的转换代码位于qemu-server的组件中,具体实现的代码文件如下:github.com/proxmox/qem…

# serial devices
for (my $i = 0; $i < $MAX_SERIAL_PORTS; $i++)  {
    my $path = $conf->{"serial$i"} or next;
    if ($path eq 'socket') {
        my $socket = "/var/run/qemu-server/${vmid}.serial$i";
        push @$devices, '-chardev', "socket,id=serial$i,path=$socket,server=on,wait=off";
        # On aarch64, serial0 is the UART device. QEMU only allows
        # connecting UART devices via the '-serial' command line, as
        # the device has a fixed slot on the hardware...
        if ($arch eq 'aarch64' && $i == 0) {
        push @$devices, '-serial', "chardev:serial$i";
        } else {
        push @$devices, '-device', "isa-serial,chardev=serial$i";
        }
    } else {
        die "no such serial device\n" if ! -c $path;
        push @$devices, '-chardev', "serial,id=serial$i,path=$path";
        push @$devices, '-device', "isa-serial,chardev=serial$i";
    }
}

PVEaarch64的架构情况下,第一个串口设备会进行特殊处理,添加的实际是一个UART设备

2.3.1 PVE对于最大串口数量的限制

在这里稍微多说一点,MAX_SERIAL_PORTS的值等于4

因为isa-serial支持的最大数量是4,如果虚拟机启动时超过4个isa-serial时,qemu会报错

kvm: -device isa-serial,chardev=serial4: Max. supported number of ISA serial ports is 4.

然而使用libvirt管理并创建的虚拟机并不会有最大4个串口数量的限制,这是因为两者对于串口设备的创建方式及设备选型的不同

后续章节会简单解析libvirt在串口这一块的处理

2.4 定位结论

如上述分析,虚拟机多串口启动失败的原因为:aarch64架构下kvm不支持isa-serial的设备类型

3 机理分析

3.1 ISA总线介绍

以下内容引用维基百科:zh.wikipedia.org/wiki/工业标准结构

工业标准结构Industry Standard Architecture,简称ISA)是IBM PC兼容机上的一种总线。

ISA在1981年诞生,并作为IBM PC的8位系统,1983年,ISA被升级作为XT总线体系。后来16位的ISA总线在1984年发布。由于ISA设计出来的目的是为了连接扩展卡和主板,因此ISA的协议同样允许总线控制,尽管只有前16MB的存储器可以直接访问。

PCI是第一个在物理展上集成了ISA、MCA、EISA优点的扩展接口,并且它的出现直接地挤压了ISA在主板上的地位。起初,主板上依然是ISA占主流地位,但已经出现了PCI槽了。到了20世纪90年代中叶,两种插槽已经在主板上平分秋色了,而ISA插槽很快就在消费PC市场上成为了少数派。微软的PC 97规范更劝说ISA插槽应该完全被除去,尽管当时的系统架构依然需要ISA存在于一些内部发育不良的管线去操作软驱、串口、等等。ISA接口在随后的几年里依然存在,甚至看见AGP接口的诞生,之后遗留在主板上的ISA接口也退出历史了。

虽然物理设备上ISA已经退出历史了,但是在PVE中,串口仍然默认使用的是ISA总线的串口设备

从上述介绍来看,aarch64架构不支持ISA总线也是完全正常的,毕竟ISA总线已经是历史遗留的产物了

qemu中也能看出aarch64架构完全不支持ISA总线

x86:

/usr/bin/qemu-system-x86_64 -device help | grep isa

name "isa-fdc", bus ISA, desc "virtual floppy controller"
name "isa-ide", bus ISA
name "ne2k_isa", bus ISA
name "isa-parallel", bus ISA
name "isa-serial", bus ISA
name "isa-cirrus-vga", bus ISA
name "isa-vga", bus ISA
name "isa-applesmc", bus ISA
name "isa-debug-exit", bus ISA
name "isa-debugcon", bus ISA
name "isa-ipmi-bt", bus ISA
name "isa-ipmi-kcs", bus ISA

aarch64:

/usr/bin/qemu-system-aarch64 -device help | grep isa


4 解决措施

根据问题定位和机理分析章节,解决该问题需要更换aarch64架构下默认创建的串口设备类型

4.1 libvirt中的串口设备默认类型

libvirt是另一套提供管理虚拟化平台的工具包,可以参考一下libvirtaarch64架构下是如何创建串口设备的

libvirt是使用xml格式管理虚拟机的配置文件,和PVE类似,虚拟机配置文件到kvm启动参数的转换逻辑是由libvirt处理的

virt-manager提供基于libvirt图形化界面管理虚拟机,看一下aarch64架构下默认添加的串口类型

重点关注写入的xml文件内容

<serial type="pty">
  <target type="system-serial" port="2">
    <model name="pl011"/>
  </target>
</serial>

以下内容引用libvirt的官方文档:libvirt.org/formatdomai…

The target element can have an optional port attribute, which specifies the port number (starting from 0), and an optional type attribute: valid values are, since 1.0.2, isa-serial (usable with x86 guests), usb-serial (usable whenever USB support is available) and pci-serial (usable whenever PCI support is available); since 3.10.0, spapr-vio-serial (usable with ppc64/pseries guests), system-serial (usable with aarch64/virt and, since 4.7.0, riscv/virt guests), sclp-serial (usable with s390 and s390x guests) are available as well and since 8.1.0 isa-debug (usable with x86 guests).

Since 3.10.0, the target element can have an optional model subelement; valid values for its name attribute are: isa-serial (usable with the isa-serial target type); usb-serial (usable with the usb-serial target type); pci-serial (usable with the pci-serial target type); spapr-vty (usable with the spapr-vio-serial target type); pl011 and, since 4.7.0, 16550a (usable with the system-serial target type); sclpconsole and sclplmconsole (usable with the sclp-serial target type). Since: 8.1.0, isa-debugcon (usable with the isa-debug target type); provides a virtual console for receiving debug messages from the firmware on x86 platforms. Providing a target model is usually unnecessary: libvirt will automatically pick one that's suitable for the chosen target type, and overriding that value is generally not recommended.

If any of the attributes is not specified by the user, libvirt will choose a value suitable for most users.

Most target types support configuring the guest-visible device address as documented in the Device Addresses section; more specifically, acceptable address types are isa (for isa-serial), usb (for usb-serial), pci (for pci-serial) and spapr-vio (for spapr-vio-serial). The system-serial and sclp-serial target types don't support specifying an address.

For the relationship between serial ports and consoles, refer to the Relationship between serial ports and consoles section.

  • serial

    有两种类型

    pty以伪终端(Pseudo Terminal)形式创建,可以用source标签指定物理机中的pts终端设备,例如<source path='/dev/pts/3'/>

    file以文件形式创建,可以用source标签指定物理机中的文件路径,例如<source path='/tmp/file' append='on'>

  • target:类型很多,可以参考引用内容,其中aarch64架构下默认类型为sysem-serial

  • model:类型很多,可以参考引用内容,其中aarch64架构下默认类型为pl011

最终虚拟机的kvm启动参数,可以在虚拟机启动后用ps -aux | grep kvm命令查看

-chardev pty,id=charserial0
-serial chardev:charserial0
-chardev pty,id=charserial1
-serial chardev:charserial1
-chardev pty,id=charserial2
-serial chardev:charserial2

对比PVE平台的虚拟机的kvm启动参数,libvirt其实是创建UART类型的串口设备(chardev类型libvirt使用的是pty的形式,PVE使用的是socket的形式)

注意:libvirt此时添加的多个串口设备,实际只有一个设备生效,后续章节会进行详细说明

4.2 qemu monitor

这里简单提一下qemu monitor几个调试命令,后续确认设备类型和设备状态都会用到

开启qemu monitor需要在虚拟机的kvm启动参数中添加-monitor stdio,且确保没有-daemonize参数

开启后在终端通过命令启动虚拟机,会自动跳转到qemu monitor界面

4.2.1 info pci

显示虚拟机的pci设备,例如

info pci

Bus  0, device  30, function 0:
    PCI bridge: PCI device 1b36:000c
      IRQ 255, pin A
      BUS 0.
      secondary bus 1.
      subordinate bus 1.
      IO range [0x1000, 0x1fff]
      memory range [0x10000000, 0x101fffff]
      prefetchable memory range [0x8000000000, 0x80001fffff]
      BAR0: 32 bit memory at 0x10444000 [0x10444fff].
      id "pcie.1"

4.2.2 info qtree

显示虚拟机的所有设备,例如

info qtree

dev: kvm-arm-gicv3, id ""
    gpio-out "sysbus-irq" 32
    gpio-in "" 512
    num-cpu = 8 (0x8)
    num-irq = 288 (0x120)
    revision = 3 (0x3)
    has-lpi = false
    has-security-extensions = false
    force-8-bit-prio = false
    len-redist-region-count = 1 (0x1)
    mmio 0000000008000000/0000000000010000
    mmio 00000000080a0000/0000000000100000

4.2.3 info usb

显示虚拟机的usb设备,例如

info usb

  Device 0.1, Port 1, Speed 480 Mb/s, Product QEMU USB Tablet, ID: tablet
  Device 0.2, Port 2, Speed 480 Mb/s, Product QEMU USB Keyboard, ID: keyboard

4.3 qemu中串口设备类型介绍

一般虚拟机要添加串口设备,和chardevserialdevice3个配置项相关

下面列举几个典型的例子:

  • 虚拟机中的串口设备重定向到物理机的/dev/ttyS1设备

    -serial /dev/ttyS1
    
  • 虚拟机中的串口设备重定向到物理机的伪终端(qemu自动创建新的伪终端)

    -chardev pty,id=charserial0
    -serial chardev:charserial0
    
  • 虚拟机中的串口设备(类型为usb-serial)重定向到物理机的伪终端(qemu自动创建新的伪终端)

    -chardev pty,id=charserial0
    -device usb-serial,chardev=charserial0,always-plugged=true
    

4.3.1 chardev

字符设备完整的详细参数可参照官方文档:www.qemu.org/docs/master…

参数格式:-chardev backend,id=id[,mux=on|off][,options]

可用的backend类型:

Backend is one of: null, socket, udp, msmouse, vc, ringbuf, file, pipe, console, serial, pty, stdio, braille, parallel, spicevmc, spiceport. The specific backend will determine the applicable options.

这里挑几个常用的,贴一下详细的参数说明

  • socket

    PVE默认创建的就是该类型(xterm.js的串口控制台就是使用该socket实现)

    -chardev 'socket,id=serial0,path=/var/run/qemu-server/103.serial0,server=on,wait=off'
    
    -chardev socket,id=id[,TCP options or unix options][,server=on|off][,wait=on|off][,telnet=on|off][,websocket=on|off][,reconnect=seconds][,tls-creds=id][,tls-authz=id]
    

    Create a two-way stream socket, which can be either a TCP or a unix socket. A unix socket will be created if path is specified. Behaviour is undefined if TCP options are specified for a unix socket.

    server=on|off specifies that the socket shall be a listening socket.

    wait=on|off specifies that QEMU should not block waiting for a client to connect to a listening socket.

    telnet=on|off specifies that traffic on the socket should interpret telnet escape sequences.

    websocket=on|off specifies that the socket uses WebSocket protocol for communication.

    reconnect sets the timeout for reconnecting on non-server sockets when the remote end goes away. qemu will delay this many seconds and then attempt to reconnect. Zero disables reconnecting, and is the default.

    tls-creds requests enablement of the TLS protocol for encryption, and specifies the id of the TLS credentials to use for the handshake. The credentials must be previously created with the -object tls-creds argument.

    tls-auth provides the ID of the QAuthZ authorization object against which the client’s x509 distinguished name will be validated. This object is only resolved at time of use, so can be deleted and recreated on the fly while the chardev server is active. If missing, it will default to denying access.

    TCP and unix socket options are given below:

    • TCP options: port=port[,host=host][,to=to][,ipv4=on|off][,ipv6=on|off][,nodelay=on|off]

      host for a listening socket specifies the local address to be bound. For a connecting socket species the remote host to connect to. host is optional for listening sockets. If not specified it defaults to 0.0.0.0.port for a listening socket specifies the local port to be bound. For a connecting socket specifies the port on the remote host to connect to. port can be given as either a port number or a service name. port is required.to is only relevant to listening sockets. If it is specified, and port cannot be bound, QEMU will attempt to bind to subsequent ports up to and including to until it succeeds. to must be specified as a port number.ipv4=on|off and ipv6=on|off specify that either IPv4 or IPv6 must be used. If neither is specified the socket may use either protocol.nodelay=on|off disables the Nagle algorithm.

    • unix options: path=path[,abstract=on|off][,tight=on|off]

      path specifies the local path of the unix socket. path is required. abstract=on|off specifies the use of the abstract socket namespace, rather than the filesystem. Optional, defaults to false. tight=on|off sets the socket length of abstract sockets to their minimum, rather than the full sun_path length. Optional, defaults to true.

  • serial

    将字符设备映射到物理机串口设备中

    -chardev serial,id=serial0,path=/dev/ttyS0
    
    -chardev serial,id=id,path=path
    

    Send traffic from the guest to a serial device on the host.

    On Unix hosts serial will actually accept any tty device, not only serial lines.

    path specifies the name of the serial device to open.

  • pty

    将字符设备映射到物理机的伪终端(qemu自动创建新的伪终端)

    -chardev pty,id=serial0
    
    -chardev pty,id=id
    

    Create a new pseudo-terminal on the host and connect to it. pty does not take any options.

    pty is not available on Windows hosts.

4.3.2 serial

serial是一种简写的方式,后端实现其实也是创建了chardev设备的

参数格式:-serial dev

这里挑几个常用的,贴一下详细的参数说明

  • pty

    将串口映射到物理机的伪终端(qemu自动创建新的伪终端)

    -serial pty
    

    [Linux only] Pseudo TTY (a new PTY is automatically allocated)

    等同于

    -chardev pty,id=serial0
    -serial chardev:serial0
    
  • chardev:id

    使用-chardev默认的设备创建串口(存在不同架构下的差异表现)

    aarch64:默认设备模型为pl011

    x86:默认设备类型为isa-serial,设备模型为16550A

    Use a named character device defined with the -chardev option.

  • /dev/XXX

    将字符设备映射到物理机串口设备中

    -serial /dev/ttyS0
    

    [Linux only] Use host tty, e.g. /dev/ttyS0. The host serial port parameters are set according to the emulated ones.

    等同于

    -chardev serial,id=serial0,path=/dev/ttyS0
    -serial chardev:serial0
    

4.3.3 device

4.3.3.1 isa-serial

x86架构可用,需要有isa总线的支持,设备模型为16550A

x86架构中默认创建的串口类型

在虚拟机中一般默认为ttyS*设备

dmesg | grep tty

[    0.052049] printk: console [tty0] enabled
[    0.395284] 00:03: ttyS3 at I/O 0x2e8 (irq = 3, base_baud = 115200) is a 16550A
[    0.418666] 00:04: ttyS2 at I/O 0x3e8 (irq = 4, base_baud = 115200) is a 16550A
[    0.441999] 00:05: ttyS1 at I/O 0x2f8 (irq = 3, base_baud = 115200) is a 16550A
[    0.465141] 00:06: ttyS0 at I/O 0x3f8 (irq = 4, base_baud = 115200) is a 16550A

创建示例:

-chardev pty,id=charserial0
-device isa-serial,id=isaserial0,chardev=charserial0

注意:单个虚拟机最大支持的isa-serial数量为4

qemu monitor中用info qtree确认设备树中最多只有4个isa-serial设备

dev: isa-serial, id ""
index = 3 (0x3)
iobase = 744 (0x2e8)
irq = 3 (0x3)
dev: isa-serial, id ""
index = 2 (0x2)
iobase = 1000 (0x3e8)
irq = 4 (0x4)
dev: isa-serial, id ""
index = 1 (0x1)
iobase = 760 (0x2f8)
irq = 3 (0x3)
dev: isa-serial, id ""
index = 0 (0x0)
iobase = 1016 (0x3f8)
irq = 4 (0x4)
4.3.3.2 usb-serial

设备模型为FTDI FT232BM,系统需要加载相关驱动(usbserialftdi_sio

支持的最大数量取决于USB控制器类型(PVEMAX_USB_DEVICES写死为14)

在虚拟机中一般默认为ttyUSB*设备

dmesg | grep tty

[    0.123973] ARMH0011:00: ttyAMA0 at MMIO 0x9000000 (irq = 4, base_baud = 0) is a SBSA
[    0.258444] console [ttyAMA0] enabled
[    3.552636] usb 1-4: FTDI USB Serial Device converter now attached to ttyUSB0
[    3.555623] usb 1-5: FTDI USB Serial Device converter now attached to ttyUSB1
[    3.558408] usb 1-6: FTDI USB Serial Device converter now attached to ttyUSB2
[    3.561142] usb 1-7: FTDI USB Serial Device converter now attached to ttyUSB3
lsusb | grep Serial

Bus 001 Device 009: ID 0403:6001 Future Technology Devices International, Ltd FT232 Serial (UART) IC
Bus 001 Device 008: ID 0403:6001 Future Technology Devices International, Ltd FT232 Serial (UART) IC
Bus 001 Device 007: ID 0403:6001 Future Technology Devices International, Ltd FT232 Serial (UART) IC
Bus 001 Device 006: ID 0403:6001 Future Technology Devices International, Ltd FT232 Serial (UART) IC
Bus 001 Device 005: ID 0403:6001 Future Technology Devices International, Ltd FT232 Serial (UART) IC
Bus 001 Device 004: ID 0403:6001 Future Technology Devices International, Ltd FT232 Serial (UART) IC

实际测试需要添加always-plugged=true才能正常识别

always-plugged参数的提交:lists.nongnu.org/archive/htm…

简单来说就是某些系统拔插USB设备后设备ID会变化,需要将变化通知到系统。这个参数则保证任何情况下始终保证USB的连接状态,不受后端chardev的影响

Some operating systems will generate a new device ID when a USB device is 
unplugged
and then replugged into the USB. If this is done whilst switching between 
multiple
applications over a virtual serial port, the change of device ID requires going
back into the OS/application to locate the new device accordingly.

Add a new always-plugged property that if specified will ensure that the device
always remains attached to the USB regardless of the state of the backend
chardev.

创建示例:

USB控制器使用qemu-xhci时,可不指定bus

-chardev pty,id=charserial0
-device usb-serial,id=usbserial0,chardev=charserial0,always-plugged=true,bus=xhci.0
4.3.3.3 pci-serial

设备模型为对16550 UARTpci包装,详见:www.qemu.org/docs/master…

在虚拟机中一般默认为ttyS*设备

dmesg | grep tty

[    5.440488] device: 'ttyS0': device_unregister
[    5.440622] PM: Removing info for No Bus:ttyS0
[    5.467484] 0000:00:02.0: ttyS0 at I/O 0x3120 (irq = 39, base_baud = 115200) is a 16550A
[    5.467576] device: 'ttyS0': device_add
[    5.467625] PM: Adding info for No Bus:ttyS0
[    5.478735] device: 'ttyS1': device_unregister
[    5.478856] PM: Removing info for No Bus:ttyS1
[    5.504207] 0000:00:03.0: ttyS1 at I/O 0x3128 (irq = 40, base_baud = 115200) is a 16550A
[    5.504304] device: 'ttyS1': device_add
[    5.504356] PM: Adding info for No Bus:ttyS1
[    5.506137] device: 'ttyS2': device_unregister
[    5.506233] PM: Removing info for No Bus:ttyS2
[    5.532378] 0000:00:04.0: ttyS2 at I/O 0x3130 (irq = 37, base_baud = 115200) is a 16550A
[    5.532486] device: 'ttyS2': device_add
[    5.532517] PM: Adding info for No Bus:ttyS2
lspci | grep Serial

00:02.0 Serial controller: Red Hat, Inc. QEMU PCI 16550A Adapter (rev 01)
00:03.0 Serial controller: Red Hat, Inc. QEMU PCI 16550A Adapter (rev 01)
00:04.0 Serial controller: Red Hat, Inc. QEMU PCI 16550A Adapter (rev 01)

创建示例:

-chardev pty,id=charserial0
-device pci-serial,id=pciserial0,chardev=charserial0,bus=pcie.0
4.3.3.4 virtio-serial

virtio-serial不同于其他的serial设备,基于virtio设备框架,除了支持标准的串行端口操作,还可以用于传输命令、日志、文件等,在qemu-guest-agentspice中都有成熟的应用

virtio-serial原生支持多端口(单个设备最大支持31个端口),支持端口热拔插

在虚拟机中一般默认为vport*设备(/dev/virtio-ports/{portname}

dmesg | grep vport

[    0.903643] device: 'vport0p1': device_add
[    0.903666] PM: Adding info for No Bus:vport0p1
[    0.903851] device: 'vport0p2': device_add
[    0.903889] PM: Adding info for No Bus:vport0p2
[    0.904031] device: 'vport0p3': device_add
[    0.904045] PM: Adding info for No Bus:vport0p3

创建示例:

-device virtio-serial,id=virtioserial,bus=pcie.0,addr=0x2
-chardev pty,id=charserial0
-device virtserialport,chardev=charserial0,name=virtserialport.0,bus=virtioserial.0
4.3.3.5 UART(aarch64)

aarch64架构中默认创建的类型,设备模型为pl011x86架构中无此类型)

在虚拟机中一般默认为ttyAMA*设备

dmesg | grep tty

[    0.135970] ARMH0011:00: ttyAMA0 at MMIO 0x9000000 (irq = 4, base_baud = 0) is a SBSA
[    0.272514] console [ttyAMA0] enabled
[  175.413222] audit: type=1006 audit(1726283302.633:306): pid=3980 uid=0 old-auid=4294967295 auid=0 tty=(none) old-ses=4294967295 ses=3 res=1

创建示例:

-chardev pty,id=charserial0
-serial chardev:charserial0

注意:aarch64架构中单个虚拟机最大支持的数量为1

qemu monitor中用info qtree确认设备树中最多只有1个pl011设备

dev: pl011, id ""
gpio-out "sysbus-irq" 6
clock-in "clk" freq_hz=0 Hz
chardev = "charserial0"
migrate-clk = true
mmio ffffffffffffffff/0000000000001000

4.4 aarch64架构下串口设备选型及验证

4.4.1 usb-serial

4.4.1.1 修改方案

暂不处理$MAX_SERIAL_PORTS的最大4个串口限制

# serial devices
for (my $i = 0; $i < $MAX_SERIAL_PORTS; $i++)  {
    my $path = $conf->{"serial$i"} or next;
    if ($path eq 'socket') {
        my $socket = "/var/run/qemu-server/${vmid}.serial$i";
        push @$devices, '-chardev', "socket,id=serial$i,path=$socket,server=on,wait=off";
	    # On aarch64, serial0 is the UART device. QEMU only allows
	    # connecting UART devices via the '-serial' command line, as
	    # the device has a fixed slot on the hardware...
	    if ($arch eq 'aarch64') {
	        if ($i == 0) {
	            push @$devices, '-serial', "chardev:serial$i";
	        } else {
	            push @$devices, '-device', "usb-serial,id=usbserial$i,chardev=serial$i,always-plugged=true";
	        }
	    } else {
	        push @$devices, '-device', "isa-serial,chardev=serial$i";
	    }
	} else {
	    die "no such serial device\n" if ! -c $path;
	    push @$devices, '-chardev', "serial,id=serial$i,path=$path";
	    push @$devices, '-device', "isa-serial,chardev=serial$i";
	}
}
4.4.1.2 测试验证

为了方便测试,验证时chardev使用pty

4.4.1.2.1 PVE默认最大数量测试(1个UART+3个usb-serial

测试参数:

-chardev socket,id=serial0,path=/var/run/qemu-server/${vmid}.serial0,server=on,wait=off
-serial chardev:serial0
-chardev pty,id=serial1
-device usb-serial,id=usbserial1,chardev=serial1,always-plugged=true
-chardev pty,id=serial2
-device usb-serial,id=usbserial2,chardev=serial2,always-plugged=true
-chardev pty,id=serial3
-device usb-serial,id=usbserial3,chardev=serial3,always-plugged=true

测试方法:

虚拟机串口发送测试(minicom):

虚拟机内串口设备发送字符,物理机对应pty端口可以接收到字符

虚拟机串口接收测试(minicom):

物理机对应pty端口发送字符,虚拟机内串口设备可以接收到字符

虚拟机发送/查看字符命令:

# 打开字符设备
minicom -D /dev/ttyUSB0

物理机发送/查看字符命令:

# 打开字符设备
minicom -D /dev/pts/1

期望结果:

虚拟机串口设备能正常接收或发送字符

测试结果:

系统串口id虚拟机串口发送测试虚拟机串口接收测试
Kylin-V10-Desktop(2207)usbserial1通过通过
Kylin-V10-Desktop(2207)usbserial2通过通过
Kylin-V10-Desktop(2207)usbserial3通过通过
Kylin-V10-Server(2204)usbserial1通过通过
Kylin-V10-Server(2204)usbserial2通过通过
Kylin-V10-Server(2204)usbserial3通过通过
Kylin-V4-Desktop(sp4-20200728)usbserial1通过通过
Kylin-V4-Desktop(sp4-20200728)usbserial2通过通过
Kylin-V4-Desktop(sp4-20200728)usbserial3通过通过
Kylin-V4-Server(sp4-20070420)usbserial1通过通过
Kylin-V4-Server(sp4-20070420)usbserial2通过通过
Kylin-V4-Server(sp4-20070420)usbserial3通过通过
4.4.1.2.2 数量测试(1个UART+9个usb-serial

使用命令行参数启动虚拟机,手动修改命令行参数以跳出PVE的最大限制

测试参数:

-chardev socket,id=serial0,path=/var/run/qemu-server/${vmid}.serial0,server=on,wait=off
-serial chardev:serial0
-chardev pty,id=serial1
-device usb-serial,id=usbserial1,chardev=serial1,always-plugged=true
-chardev pty,id=serial2
-device usb-serial,id=usbserial2,chardev=serial2,always-plugged=true
-chardev pty,id=serial3
-device usb-serial,id=usbserial3,chardev=serial3,always-plugged=true
-chardev pty,id=serial4
-device usb-serial,id=usbserial4,chardev=serial4,always-plugged=true
-chardev pty,id=serial5
-device usb-serial,id=usbserial5,chardev=serial5,always-plugged=true
-chardev pty,id=serial6
-device usb-serial,id=usbserial6,chardev=serial6,always-plugged=true
-chardev pty,id=serial7
-device usb-serial,id=usbserial7,chardev=serial7,always-plugged=true
-chardev pty,id=serial8
-device usb-serial,id=usbserial8,chardev=serial8,always-plugged=true
-chardev pty,id=serial9
-device usb-serial,id=usbserial9,chardev=serial9,always-plugged=true

测试方法:

虚拟机串口发送测试(minicom):

虚拟机内串口设备发送字符,物理机对应pty端口可以接收到字符

虚拟机串口接收测试(minicom):

物理机对应pty端口发送字符,虚拟机内串口设备可以接收到字符

虚拟机发送/查看字符命令:

# 打开字符设备
minicom -D /dev/ttyUSB0

物理机发送/查看字符命令:

# 打开字符设备
minicom -D /dev/pts/1

期望结果:

虚拟机串口设备能正常接收或发送字符

测试结果:

系统串口id虚拟机串口发送测试虚拟机串口接收测试
Kylin-V10-Desktop(2207)usbserial1通过通过
Kylin-V10-Desktop(2207)usbserial2通过通过
Kylin-V10-Desktop(2207)usbserial3通过通过
Kylin-V10-Desktop(2207)usbserial4通过通过
Kylin-V10-Desktop(2207)usbserial5通过通过
Kylin-V10-Desktop(2207)usbserial6通过通过
Kylin-V10-Desktop(2207)usbserial7通过通过
Kylin-V10-Desktop(2207)usbserial8通过通过
Kylin-V10-Desktop(2207)usbserial9通过通过
Kylin-V10-Server(2204)usbserial1通过通过
Kylin-V10-Server(2204)usbserial2通过通过
Kylin-V10-Server(2204)usbserial3通过通过
Kylin-V10-Server(2204)usbserial4通过通过
Kylin-V10-Server(2204)usbserial5通过通过
Kylin-V10-Server(2204)usbserial6通过通过
Kylin-V10-Server(2204)usbserial7通过通过
Kylin-V10-Server(2204)usbserial8通过通过
Kylin-V10-Server(2204)usbserial9通过通过
Kylin-V4-Desktop(sp4-20200728)usbserial1通过通过
Kylin-V4-Desktop(sp4-20200728)usbserial2通过通过
Kylin-V4-Desktop(sp4-20200728)usbserial3通过通过
Kylin-V4-Desktop(sp4-20200728)usbserial4通过通过
Kylin-V4-Desktop(sp4-20200728)usbserial5通过通过
Kylin-V4-Desktop(sp4-20200728)usbserial6通过通过
Kylin-V4-Desktop(sp4-20200728)usbserial7通过通过
Kylin-V4-Desktop(sp4-20200728)usbserial8通过通过
Kylin-V4-Desktop(sp4-20200728)usbserial9通过通过
Kylin-V4-Server(sp4-20070420)usbserial1通过通过
Kylin-V4-Server(sp4-20070420)usbserial2通过通过
Kylin-V4-Server(sp4-20070420)usbserial3通过通过
Kylin-V4-Server(sp4-20070420)usbserial4通过通过
Kylin-V4-Server(sp4-20070420)usbserial5通过通过
Kylin-V4-Server(sp4-20070420)usbserial6通过通过
Kylin-V4-Server(sp4-20070420)usbserial7通过通过
Kylin-V4-Server(sp4-20070420)usbserial8通过通过
Kylin-V4-Server(sp4-20070420)usbserial9通过通过
4.4.1.2.3 热拔插测试

需要使用qemu monitorUSB设备进行热拔插

chardev类型无法进行热拔插,仅能对usb-serial热拔插,故如果需要实现串口灵活热拔插,需要提前添加足额的chardev

测试参数:

-monitor stdio
-chardev socket,id=serial0,path=/var/run/qemu-server/${vmid}.serial0,server=on,wait=off
-serial chardev:serial0
-chardev pty,id=serial1
-device usb-serial,id=usbserial1,chardev=serial1,always-plugged=true
-chardev pty,id=serial2
-device usb-serial,id=usbserial2,chardev=serial2,always-plugged=true
-chardev pty,id=serial3
-device usb-serial,id=usbserial3,chardev=serial3,always-plugged=true
-chardev pty,id=serial4

测试方法:

热拔插USB设备(qemu monitor):

# 拔出设备
device_del usbserial1

# 插入设备(id1)
device_add usb-serial,id=usbserial1,chardev=serial1,always-plugged=true
# 插入设备(id4)
device_add usb-serial,id=usbserial4,chardev=serial4,always-plugged=true

虚拟机串口发送测试(minicom):

虚拟机内串口设备发送字符,物理机对应pty端口可以接收到字符

虚拟机串口接收测试(minicom):

物理机对应pty端口发送字符,虚拟机内串口设备可以接收到字符

虚拟机发送/查看字符命令:

# 打开字符设备
minicom -D /dev/ttyUSB0

物理机发送/查看字符命令:

# 打开字符设备
minicom -D /dev/pts/1

期望结果:

热拔插USB设备后,虚拟机串口设备能正常接收或发送字符

测试结果:

系统拔出串口id插入串口id虚拟机串口发送测试虚拟机串口接收测试
Kylin-V10-Desktop(2207)usbserial1usbserial1通过通过
Kylin-V10-Desktop(2207)/usbserial4通过通过
Kylin-V10-Server(2204)usbserial1usbserial1通过通过
Kylin-V10-Server(2204)/usbserial4通过通过
Kylin-V4-Desktop(sp4-20200728)usbserial1usbserial1通过通过
Kylin-V4-Desktop(sp4-20200728)/usbserial4通过通过
Kylin-V4-Server(sp4-20070420)usbserial1usbserial1通过通过
Kylin-V4-Server(sp4-20070420)/usbserial4通过通过

4.4.2 pci-serial

4.4.2.1 修改方案

暂不处理$MAX_SERIAL_PORTS的最大4个串口限制

# serial devices
for (my $i = 0; $i < $MAX_SERIAL_PORTS; $i++)  {
    my $path = $conf->{"serial$i"} or next;
    if ($path eq 'socket') {
        my $socket = "/var/run/qemu-server/${vmid}.serial$i";
        push @$devices, '-chardev', "socket,id=serial$i,path=$socket,server=on,wait=off";
	    # On aarch64, serial0 is the UART device. QEMU only allows
	    # connecting UART devices via the '-serial' command line, as
	    # the device has a fixed slot on the hardware...
	    if ($arch eq 'aarch64') {
	        if ($i == 0) {
	            push @$devices, '-serial', "chardev:serial$i";
	        } else {
	            push @$devices, '-device', "pci-serial,id=pciserial$i,chardev=serial$i";
	        }
	    } else {
	        push @$devices, '-device', "isa-serial,chardev=serial$i";
	    }
	} else {
	    die "no such serial device\n" if ! -c $path;
	    push @$devices, '-chardev', "serial,id=serial$i,path=$path";
	    push @$devices, '-device', "isa-serial,chardev=serial$i";
	}
}
4.4.2.2 测试验证

为了方便测试,验证时chardev使用pty

4.4.2.2.1 PVE默认最大数量测试(1个UART+3个pci-serial

测试参数:

-chardev socket,id=serial0,path=/var/run/qemu-server/${vmid}.serial0,server=on,wait=off
-serial chardev:serial0
-chardev pty,id=serial1
-device pci-serial,id=pciserial1,chardev=serial1,bus=pcie.0,addr=0x2
-chardev pty,id=serial2
-device pci-serial,id=pciserial2,chardev=serial2,bus=pcie.0,addr=0x3
-chardev pty,id=serial3
-device pci-serial,id=pciserial3,chardev=serial3,bus=pcie.0,addr=0x4

测试方法:

虚拟机串口发送测试(minicom):

虚拟机内串口设备发送字符,物理机对应pty端口可以接收到字符

虚拟机串口接收测试(minicom):

物理机对应pty端口发送字符,虚拟机内串口设备可以接收到字符

虚拟机发送/查看字符命令:

# 打开字符设备
minicom -D /dev/ttyS0

物理机发送/查看字符命令:

# 打开字符设备
minicom -D /dev/pts/1

期望结果:

虚拟机串口设备能正常接收或发送字符

测试结果:

系统串口id虚拟机串口发送测试虚拟机串口接收测试
Kylin-V10-Desktop(2207)pciserial1通过通过
Kylin-V10-Desktop(2207)pciserial2通过通过
Kylin-V10-Desktop(2207)pciserial3通过通过
Kylin-V10-Server(2204)pciserial1通过通过
Kylin-V10-Server(2204)pciserial2通过通过
Kylin-V10-Server(2204)pciserial3通过通过
Kylin-V4-Desktop(sp4-20200728)pciserial1通过通过
Kylin-V4-Desktop(sp4-20200728)pciserial2通过通过
Kylin-V4-Desktop(sp4-20200728)pciserial3通过通过
Kylin-V4-Server(sp4-20070420)pciserial1通过通过
Kylin-V4-Server(sp4-20070420)pciserial2通过通过
Kylin-V4-Server(sp4-20070420)pciserial3通过通过
4.4.2.2.2 数量测试(1个UART+9个pci-serial

使用命令行参数启动虚拟机,手动修改命令行参数以跳出PVE的最大限制

测试参数:

-chardev socket,id=serial0,path=/var/run/qemu-server/${vmid}.serial0,server=on,wait=off
-serial chardev:serial0
-chardev pty,id=serial1
-device pci-serial,id=pciserial1,chardev=serial1,bus=pcie.0,addr=0x2
-chardev pty,id=serial2
-device pci-serial,id=pciserial2,chardev=serial2,bus=pcie.0,addr=0x3
-chardev pty,id=serial3
-device pci-serial,id=pciserial3,chardev=serial3,bus=pcie.0,addr=0x4
-chardev pty,id=serial4
-device pci-serial,id=pciserial4,chardev=serial4,bus=pcie.0,addr=0x5
-chardev pty,id=serial5
-device pci-serial,id=pciserial5,chardev=serial5,bus=pcie.0,addr=0x6
-chardev pty,id=serial6
-device pci-serial,id=pciserial6,chardev=serial6,bus=pcie.0,addr=0x7
-chardev pty,id=serial7
-device pci-serial,id=pciserial7,chardev=serial7,bus=pcie.0,addr=0x8
-chardev pty,id=serial8
-device pci-serial,id=pciserial8,chardev=serial8,bus=pcie.0,addr=0x9
-chardev pty,id=serial9
-device pci-serial,id=pciserial9,chardev=serial9,bus=pcie.0,addr=0x10

测试方法:

虚拟机串口发送测试(minicom):

虚拟机内串口设备发送字符,物理机对应pty端口可以接收到字符

虚拟机串口接收测试(minicom):

物理机对应pty端口发送字符,虚拟机内串口设备可以接收到字符

虚拟机发送/查看字符命令:

# 打开字符设备
minicom -D /dev/ttyS0

物理机发送/查看字符命令:

# 打开字符设备
minicom -D /dev/pts/1

期望结果:

虚拟机串口设备能正常接收或发送字符

测试结果:

系统串口id虚拟机串口发送测试虚拟机串口接收测试
Kylin-V10-Desktop(2207)pciserial1通过通过
Kylin-V10-Desktop(2207)pciserial2通过通过
Kylin-V10-Desktop(2207)pciserial3通过通过
Kylin-V10-Desktop(2207)pciserial4通过通过
Kylin-V10-Desktop(2207)pciserial5通过通过
Kylin-V10-Desktop(2207)pciserial6通过通过
Kylin-V10-Desktop(2207)pciserial7通过通过
Kylin-V10-Desktop(2207)pciserial8通过通过
Kylin-V10-Desktop(2207)pciserial9通过通过
Kylin-V10-Server(2204)pciserial1通过通过
Kylin-V10-Server(2204)pciserial2通过通过
Kylin-V10-Server(2204)pciserial3通过通过
Kylin-V10-Server(2204)pciserial4通过通过
Kylin-V10-Server(2204)pciserial5通过通过
Kylin-V10-Server(2204)pciserial6通过通过
Kylin-V10-Server(2204)pciserial7通过通过
Kylin-V10-Server(2204)pciserial8通过通过
Kylin-V10-Server(2204)pciserial9通过通过
Kylin-V4-Desktop(sp4-20200728)pciserial1通过通过
Kylin-V4-Desktop(sp4-20200728)pciserial2通过通过
Kylin-V4-Desktop(sp4-20200728)pciserial3通过通过
Kylin-V4-Desktop(sp4-20200728)pciserial4通过通过
Kylin-V4-Desktop(sp4-20200728)pciserial5通过通过
Kylin-V4-Desktop(sp4-20200728)pciserial6通过通过
Kylin-V4-Desktop(sp4-20200728)pciserial7通过通过
Kylin-V4-Desktop(sp4-20200728)pciserial8通过通过
Kylin-V4-Desktop(sp4-20200728)pciserial9通过通过
Kylin-V4-Server(sp4-20070420)pciserial1通过通过
Kylin-V4-Server(sp4-20070420)pciserial2通过通过
Kylin-V4-Server(sp4-20070420)pciserial3通过通过
Kylin-V4-Server(sp4-20070420)pciserial4通过通过
Kylin-V4-Server(sp4-20070420)pciserial5通过通过
Kylin-V4-Server(sp4-20070420)pciserial6通过通过
Kylin-V4-Server(sp4-20070420)pciserial7通过通过
Kylin-V4-Server(sp4-20070420)pciserial8通过通过
Kylin-V4-Server(sp4-20070420)pciserial9通过通过
4.4.2.2.3 热拔插测试

需要使用qemu monitorPCI设备进行热拔插

chardev类型无法进行热拔插,仅能对pci-serial热拔插,故如果需要实现串口灵活热拔插,需要提前添加足额的chardev

PCI设备热拔插需要具备以下条件:

  • guest系统支持,打开内核参数CONFIG_HOTPLUG_PCI_PCIE

    zcat /proc/config.gz | grep HOTPLUG
    
    CONFIG_HOTPLUG_PCI_PCIE=y
    CONFIG_HOTPLUG_PCI=y
    CONFIG_HOTPLUG_PCI_ACPI=y
    CONFIG_HOTPLUG_PCI_ACPI_IBM=m
    CONFIG_HOTPLUG_PCI_CPCI=y
    CONFIG_HOTPLUG_PCI_SHPC=y
    
    # 或者在/boot/config-{uname -r}
    cat /boot/config-5.15.0-122-generic | grep HOTPLUG
    
    CONFIG_HOTPLUG_PCI_PCIE=y
    CONFIG_HOTPLUG_PCI=y
    CONFIG_HOTPLUG_PCI_ACPI=y
    CONFIG_HOTPLUG_PCI_ACPI_IBM=m
    CONFIG_HOTPLUG_PCI_CPCI=y
    CONFIG_HOTPLUG_PCI_CPCI_ZT5550=m
    CONFIG_HOTPLUG_PCI_CPCI_GENERIC=m
    CONFIG_HOTPLUG_PCI_SHPC=y
    
  • guest系统支持,有pciehpacpiphppci_hotplug驱动并加载

    lsmod | grep pciehp
    lsmod | grep acpiphp
    lsmod | grep pci_hotplug
    
    # 有些系统这些模块编译到内核里面了,modinfo才能看到
    modinfo pciehp
    name:           pciehp
    filename:       (builtin)
    parm:           pciehp_poll_mode:Using polling mechanism for hot-plug events or not (bool)
    parm:           pciehp_poll_time:Polling mechanism frequency, in seconds (int)
    
    modinfo acpiphp
    name:           acpiphp
    filename:       (builtin)
    license:        GPL
    file:           drivers/pci/hotplug/acpiphp
    description:    ACPI Hot Plug PCI Controller Driver
    author:         Greg Kroah-Hartman <gregkh@us.ibm.com>, Takayoshi Kochi <t-kochi@bq.jp.nec.com>, Matthew Wilcox <willy@infradead.org>
    parm:           disable:disable acpiphp driver (bool)
    
    modinfo pci_hotplug
    name:           pci_hotplug
    filename:       (builtin)
    parm:           debug_acpi:Debugging mode for ACPI enabled or not (bool)
    parm:           debug:Debugging mode enabled or not (bool)
    
  • PCI设备不能挂载在部分总线设备如:Root Bus:pcie.0pxb-pcie

    一般情况aarch64架构下是挂载到pcie-root-port

    由于pcie-root-port本身不支持热拔插,所以需要准备多个pcie-root-port才能支持多设备热拔插

    详情参考这份文档:github.com/qemu/qemu/b…

  • 虚拟机类型需要为virtaarch64)或者q35x86

由于银河麒麟aarch64架构的系统并未支持相关驱动模块,PCIE热拔插功能不支持

4.4.3 virtio-serial

4.4.3.1 修改方案

暂不处理$MAX_SERIAL_PORTS的最大4个串口限制

# serial devices
for (my $i = 0; $i < $MAX_SERIAL_PORTS; $i++)  {
    my $path = $conf->{"serial$i"} or next;
    if ($path eq 'socket') {
        my $socket = "/var/run/qemu-server/${vmid}.serial$i";
        push @$devices, '-chardev', "socket,id=serial$i,path=$socket,server=on,wait=off";
	    # On aarch64, serial0 is the UART device. QEMU only allows
	    # connecting UART devices via the '-serial' command line, as
	    # the device has a fixed slot on the hardware...
	    if ($arch eq 'aarch64') {
	        if ($i == 0) {
	            push @$devices, '-serial', "chardev:serial$i";
	            push @$devices, '-device', "virtio-serial,id=virtioserial";
	        } else {
	            push @$devices, '-device', "virtserialport,bus=virtioserial.0,chardev=serial$i,id=virtserialport.$i,name=virtserialport.$i";
	        }
	    } else {
	        push @$devices, '-device', "isa-serial,chardev=serial$i";
	    }
	} else {
	    die "no such serial device\n" if ! -c $path;
	    push @$devices, '-chardev', "serial,id=serial$i,path=$path";
	    push @$devices, '-device', "isa-serial,chardev=serial$i";
	}
}
4.4.3.2 测试验证

为了方便测试,验证时chardev使用pty

4.4.3.2.1 PVE默认最大数量测试(1个UART+3个virtserialport

测试参数:

-chardev socket,id=serial0,path=/var/run/qemu-server/${vmid}.serial0,server=on,wait=off
-serial chardev:serial0
-device virtio-serial,id=virtioserial,bus=pcie.0,addr=0x2
-chardev pty,id=serial1
-device virtserialport,bus=virtioserial.0,chardev=serial1,id=virtserialport.1,name=virtserialport.1
-chardev pty,id=serial2
-device virtserialport,bus=virtioserial.0,chardev=serial2,id=virtserialport.2,name=virtserialport.2
-chardev pty,id=serial3
-device virtserialport,bus=virtioserial.0,chardev=serial3,id=virtserialport.3,name=virtserialport.3

测试方法:

虚拟机串口发送测试(minicom):

虚拟机内串口设备发送字符,物理机对应pty端口可以接收到字符

虚拟机串口接收测试(minicom):

物理机对应pty端口发送字符,虚拟机内串口设备可以接收到字符

虚拟机发送/查看字符命令:

# 打开字符设备
minicom -D /dev/vport0p10

物理机发送/查看字符命令:

# 打开字符设备
minicom -D /dev/pts/1

期望结果:

虚拟机串口设备能正常接收或发送字符

测试结果:

系统串口id虚拟机串口发送测试虚拟机串口接收测试
Kylin-V10-Desktop(2207)virtserialport.1通过通过
Kylin-V10-Desktop(2207)virtserialport.2通过通过
Kylin-V10-Desktop(2207)virtserialport.3通过通过
Kylin-V10-Server(2204)virtserialport.1通过通过
Kylin-V10-Server(2204)virtserialport.2通过通过
Kylin-V10-Server(2204)virtserialport.3通过通过
Kylin-V4-Desktop(sp4-20200728)virtserialport.1通过通过
Kylin-V4-Desktop(sp4-20200728)virtserialport.2通过通过
Kylin-V4-Desktop(sp4-20200728)virtserialport.3通过通过
Kylin-V4-Server(sp4-20070420)virtserialport.1通过通过
Kylin-V4-Server(sp4-20070420)virtserialport.2通过通过
Kylin-V4-Server(sp4-20070420)virtserialport.3通过通过
4.4.3.2.2 数量测试(1个UART+9个virtserialport

使用命令行参数启动虚拟机,手动修改命令行参数以跳出PVE的最大限制

测试参数:

-chardev socket,id=serial0,path=/var/run/qemu-server/${vmid}.serial0,server=on,wait=off
-serial chardev:serial0
-device virtio-serial,id=virtioserial,bus=pcie.0,addr=0x2
-chardev pty,id=serial1
-device virtserialport,bus=virtioserial.0,chardev=serial1,id=virtserialport.1,name=virtserialport.1
-chardev pty,id=serial2
-device virtserialport,bus=virtioserial.0,chardev=serial2,id=virtserialport.2,name=virtserialport.2
-chardev pty,id=serial3
-device virtserialport,bus=virtioserial.0,chardev=serial3,id=virtserialport.3,name=virtserialport.3
-chardev pty,id=serial4
-device virtserialport,bus=virtioserial.0,chardev=serial4,id=virtserialport.4,name=virtserialport.4
-chardev pty,id=serial5
-device virtserialport,bus=virtioserial.0,chardev=serial5,id=virtserialport.5,name=virtserialport.5
-chardev pty,id=serial6
-device virtserialport,bus=virtioserial.0,chardev=serial6,id=virtserialport.6,name=virtserialport.6
-chardev pty,id=serial7
-device virtserialport,bus=virtioserial.0,chardev=serial7,id=virtserialport.7,name=virtserialport.7
-chardev pty,id=serial8
-device virtserialport,bus=virtioserial.0,chardev=serial8,id=virtserialport.8,name=virtserialport.8
-chardev pty,id=serial9
-device virtserialport,bus=virtioserial.0,chardev=serial9,id=virtserialport.9,name=virtserialport.9

测试方法:

虚拟机串口发送测试(minicom):

虚拟机内串口设备发送字符,物理机对应pty端口可以接收到字符

虚拟机串口接收测试(minicom):

物理机对应pty端口发送字符,虚拟机内串口设备可以接收到字符

虚拟机发送/查看字符命令:

# 打开字符设备
minicom -D /dev/vport0p10

物理机发送/查看字符命令:

# 打开字符设备
minicom -D /dev/pts/1

期望结果:

虚拟机串口设备能正常接收或发送字符

测试结果:

系统串口id虚拟机串口发送测试虚拟机串口接收测试
Kylin-V10-Desktop(2207)virtserialport.1通过通过
Kylin-V10-Desktop(2207)virtserialport.2通过通过
Kylin-V10-Desktop(2207)virtserialport.3通过通过
Kylin-V10-Desktop(2207)virtserialport.4通过通过
Kylin-V10-Desktop(2207)virtserialport.5通过通过
Kylin-V10-Desktop(2207)virtserialport.6通过通过
Kylin-V10-Desktop(2207)virtserialport.7通过通过
Kylin-V10-Desktop(2207)virtserialport.8通过通过
Kylin-V10-Desktop(2207)virtserialport.9通过通过
Kylin-V10-Server(2204)virtserialport.1通过通过
Kylin-V10-Server(2204)virtserialport.2通过通过
Kylin-V10-Server(2204)virtserialport.3通过通过
Kylin-V10-Server(2204)virtserialport.4通过通过
Kylin-V10-Server(2204)virtserialport.5通过通过
Kylin-V10-Server(2204)virtserialport.6通过通过
Kylin-V10-Server(2204)virtserialport.7通过通过
Kylin-V10-Server(2204)virtserialport.8通过通过
Kylin-V10-Server(2204)virtserialport.9通过通过
Kylin-V4-Desktop(sp4-20200728)virtserialport.1通过通过
Kylin-V4-Desktop(sp4-20200728)virtserialport.2通过通过
Kylin-V4-Desktop(sp4-20200728)virtserialport.3通过通过
Kylin-V4-Desktop(sp4-20200728)virtserialport.4通过通过
Kylin-V4-Desktop(sp4-20200728)virtserialport.5通过通过
Kylin-V4-Desktop(sp4-20200728)virtserialport.6通过通过
Kylin-V4-Desktop(sp4-20200728)virtserialport.7通过通过
Kylin-V4-Desktop(sp4-20200728)virtserialport.8通过通过
Kylin-V4-Desktop(sp4-20200728)virtserialport.9通过通过
Kylin-V4-Server(sp4-20070420)virtserialport.1通过通过
Kylin-V4-Server(sp4-20070420)virtserialport.2通过通过
Kylin-V4-Server(sp4-20070420)virtserialport.3通过通过
Kylin-V4-Server(sp4-20070420)virtserialport.4通过通过
Kylin-V4-Server(sp4-20070420)virtserialport.5通过通过
Kylin-V4-Server(sp4-20070420)virtserialport.6通过通过
Kylin-V4-Server(sp4-20070420)virtserialport.7通过通过
Kylin-V4-Server(sp4-20070420)virtserialport.8通过通过
Kylin-V4-Server(sp4-20070420)virtserialport.9通过通过

遗留问题:

虚拟机启动时报错:kvm: virtio-serial-bus: Guest failure in adding port 8 for device virtioserial.0

virtserialport超过7就会报错,但是串口设备能正常创建,不影响使用

4.4.3.2.3 热拔插测试

需要使用qemu monitorvirtserialport设备进行热拔插

chardev类型无法进行热拔插,仅能对virtserialport热拔插,故如果需要实现串口灵活热拔插,需要提前添加足额的chardev

测试参数:

-monitor stdio
-chardev socket,id=serial0,path=/var/run/qemu-server/${vmid}.serial0,server=on,wait=off
-serial chardev:serial0
-device virtio-serial,id=virtioserial,bus=pcie.0,addr=0x2
-chardev pty,id=serial1
-device virtserialport,bus=virtioserial.0,chardev=serial1,id=virtserialport.1,name=virtserialport.1
-chardev pty,id=serial2
-device virtserialport,bus=virtioserial.0,chardev=serial2,id=virtserialport.2,name=virtserialport.2
-chardev pty,id=serial3
-device virtserialport,bus=virtioserial.0,chardev=serial3,id=virtserialport.3,name=virtserialport.3
-chardev pty,id=serial4

测试方法:

热拔插USB设备(qemu monitor):

# 拔出设备
device_del virtserialport.1

# 插入设备(id1)
device_add virtserialport,bus=virtioserial.0,chardev=serial1,id=virtserialport.1,name=virtserialport.1
# 插入设备(id4)
device_add virtserialport,bus=virtioserial.0,chardev=serial4,id=virtserialport.4,name=virtserialport.4

虚拟机串口发送测试(minicom):

虚拟机内串口设备发送字符,物理机对应pty端口可以接收到字符

虚拟机串口接收测试(minicom):

物理机对应pty端口发送字符,虚拟机内串口设备可以接收到字符

虚拟机发送/查看字符命令:

# 打开字符设备
minicom -D /dev/vport0p10

物理机发送/查看字符命令:

# 打开字符设备
minicom -D /dev/pts/1

期望结果:

热拔插USB设备后,虚拟机串口设备能正常接收或发送字符

测试结果:

系统拔出串口id插入串口id虚拟机串口发送测试虚拟机串口接收测试
Kylin-V10-Desktop(2207)virtserialport.1virtserialport.1通过通过
Kylin-V10-Desktop(2207)/virtserialport.4通过通过
Kylin-V10-Server(2204)virtserialport.1virtserialport.1通过通过
Kylin-V10-Server(2204)/virtserialport.4通过通过
Kylin-V4-Desktop(sp4-20200728)virtserialport.1virtserialport.1失败(拔出后再次插入失败)失败(拔出后再次插入失败)
Kylin-V4-Desktop(sp4-20200728)/virtserialport.4通过通过
Kylin-V4-Server(sp4-20070420)virtserialport.1virtserialport.1失败(拔出后再次插入失败)失败(拔出后再次插入失败)
Kylin-V4-Server(sp4-20070420)/virtserialport.4通过通过

4.4.4 最终选型(usb-serial)

测试结果汇总:

串口类型支持最大数量热拔插
usb-serial14(PVE默认写死最大值为14,但最大值取决于USB控制器类型,某些USB控制器支持数量可能少于14)支持
pci-serial/(取决于pcie的架构设计及控制器类型,pcie.0默认支持32路pcie设备)有限支持(需要guest支持、pcie控制器支持)
virtio-serial单个设备最大31个端口,需求更多时可以创建多个设备有限支持(需要guest的对应驱动支持)

个人主观的选择是usb-serial,仅代表个人观点

先说说为什么不选择其他的设备类型

virtio-serial

从设备角度来说已经脱离了串口设备这样的概念,实际是只有一个Communication controllerpci设备,串口只是该设备下的端口

从驱动角度来说,virtio驱动是在内核中的,在测试中暴露出一些老旧的系统中出现问题

pci-serial

PVE写死了pcie的设备map,增加pci-serial需要削减其他设备的数量

另外测试发现pci-serial挂载在pcie-root-port下时,需要从0地址挂载才能生效,其余地址均初始化失败(其他设备也有该问题)

最后选择了usb-serial,虽然PVE写死了最大USB设备数量,串口会抢占一定的数量,总体来说还算能接受