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
架构添加多个串口设备并不会出现启动失败的问题
对比x86
和aarch64
下qemu
支持的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代码分析
PVE
的github
官方只读镜像项目路径: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";
}
}
PVE
在aarch64
的架构情况下,第一个串口设备会进行特殊处理,添加的实际是一个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
是另一套提供管理虚拟化平台的工具包,可以参考一下libvirt
在aarch64
架构下是如何创建串口设备的
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中串口设备类型介绍
一般虚拟机要添加串口设备,和chardev
、serial
、device
3个配置项相关
下面列举几个典型的例子:
-
虚拟机中的串口设备重定向到物理机的
/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 to0.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, andport
cannot be bound, QEMU will attempt to bind to subsequent ports up to and includingto
until it succeeds.to
must be specified as a port number.ipv4=on|off
andipv6=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
,系统需要加载相关驱动(usbserial
、ftdi_sio
)
支持的最大数量取决于USB
控制器类型(PVE
中MAX_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 UART
的pci
包装,详见: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-agent
、spice
中都有成熟的应用
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
架构中默认创建的类型,设备模型为pl011
(x86
架构中无此类型)
在虚拟机中一般默认为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 monitor
对USB
设备进行热拔插
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) | usbserial1 | usbserial1 | 通过 | 通过 |
Kylin-V10-Desktop(2207) | / | usbserial4 | 通过 | 通过 |
Kylin-V10-Server(2204) | usbserial1 | usbserial1 | 通过 | 通过 |
Kylin-V10-Server(2204) | / | usbserial4 | 通过 | 通过 |
Kylin-V4-Desktop(sp4-20200728) | usbserial1 | usbserial1 | 通过 | 通过 |
Kylin-V4-Desktop(sp4-20200728) | / | usbserial4 | 通过 | 通过 |
Kylin-V4-Server(sp4-20070420) | usbserial1 | usbserial1 | 通过 | 通过 |
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 monitor
对PCI
设备进行热拔插
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
系统支持,有pciehp
、acpiphp
、pci_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.0
、pxb-pcie
一般情况
aarch64
架构下是挂载到pcie-root-port
下由于
pcie-root-port
本身不支持热拔插,所以需要准备多个pcie-root-port
才能支持多设备热拔插详情参考这份文档:github.com/qemu/qemu/b…
-
虚拟机类型需要为
virt
(aarch64
)或者q35
(x86
)
由于银河麒麟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 monitor
对virtserialport
设备进行热拔插
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.1 | virtserialport.1 | 通过 | 通过 |
Kylin-V10-Desktop(2207) | / | virtserialport.4 | 通过 | 通过 |
Kylin-V10-Server(2204) | virtserialport.1 | virtserialport.1 | 通过 | 通过 |
Kylin-V10-Server(2204) | / | virtserialport.4 | 通过 | 通过 |
Kylin-V4-Desktop(sp4-20200728) | virtserialport.1 | virtserialport.1 | 失败(拔出后再次插入失败) | 失败(拔出后再次插入失败) |
Kylin-V4-Desktop(sp4-20200728) | / | virtserialport.4 | 通过 | 通过 |
Kylin-V4-Server(sp4-20070420) | virtserialport.1 | virtserialport.1 | 失败(拔出后再次插入失败) | 失败(拔出后再次插入失败) |
Kylin-V4-Server(sp4-20070420) | / | virtserialport.4 | 通过 | 通过 |
4.4.4 最终选型(usb-serial)
测试结果汇总:
串口类型 | 支持最大数量 | 热拔插 |
---|---|---|
usb-serial | 14(PVE默认写死最大值为14,但最大值取决于USB控制器类型,某些USB控制器支持数量可能少于14) | 支持 |
pci-serial | /(取决于pcie的架构设计及控制器类型,pcie.0默认支持32路pcie设备) | 有限支持(需要guest支持、pcie控制器支持) |
virtio-serial | 单个设备最大31个端口,需求更多时可以创建多个设备 | 有限支持(需要guest的对应驱动支持) |
个人主观的选择是usb-serial
,仅代表个人观点
先说说为什么不选择其他的设备类型
virtio-serial
从设备角度来说已经脱离了串口设备这样的概念,实际是只有一个Communication controller
的pci
设备,串口只是该设备下的端口
从驱动角度来说,virtio
驱动是在内核中的,在测试中暴露出一些老旧的系统中出现问题
pci-serial
PVE
写死了pcie
的设备map
,增加pci-serial
需要削减其他设备的数量
另外测试发现pci-serial
挂载在pcie-root-port
下时,需要从0
地址挂载才能生效,其余地址均初始化失败(其他设备也有该问题)
最后选择了usb-serial
,虽然PVE
写死了最大USB
设备数量,串口会抢占一定的数量,总体来说还算能接受