1 В избранное 0 Ответвления 0

OSCHINA-MIRROR/openeuler-stratovirt

Присоединиться к Gitlife
Откройте для себя и примите участие в публичных проектах с открытым исходным кодом с участием более 10 миллионов разработчиков. Приватные репозитории также полностью бесплатны :)
Присоединиться бесплатно
В этом репозитории не указан файл с открытой лицензией (LICENSE). При использовании обратитесь к конкретному описанию проекта и его зависимостям в коде.
Клонировать/Скачать
chardev.rs 20 КБ
Копировать Редактировать Исходные данные Просмотреть построчно История
liuxiangdong Отправлено 2 лет назад 8e8edd0
123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664
// Copyright (c) 2020 Huawei Technologies Co.,Ltd. All rights reserved.
//
// StratoVirt is licensed under Mulan PSL v2.
// You can use this software according to the terms and conditions of the Mulan
// PSL v2.
// You may obtain a copy of Mulan PSL v2 at:
// http://license.coscl.org.cn/MulanPSL2
// THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY
// KIND, EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO
// NON-INFRINGEMENT, MERCHANTABILITY OR FIT FOR A PARTICULAR PURPOSE.
// See the Mulan PSL v2 for more details.
use anyhow::{anyhow, bail, Context, Result};
use log::error;
use serde::{Deserialize, Serialize};
use super::{error::ConfigError, get_pci_bdf, pci_args_check, PciBdf};
use crate::config::{
check_arg_too_long, CmdParser, ConfigCheck, ExBool, VmConfig, MAX_PATH_LENGTH,
};
use crate::qmp::qmp_schema;
const MAX_GUEST_CID: u64 = 4_294_967_295;
const MIN_GUEST_CID: u64 = 3;
/// Default value of max ports for virtio-serial.
const DEFAULT_SERIAL_PORTS_NUMBER: u32 = 31;
/// Character device options.
#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
pub enum ChardevType {
Stdio,
Pty,
Socket {
path: String,
server: bool,
nowait: bool,
},
File(String),
}
/// Config structure for virtio-serial-port.
#[derive(Debug, Clone)]
pub struct VirtioSerialPort {
pub id: String,
pub chardev: ChardevConfig,
pub nr: u32,
pub is_console: bool,
}
impl ConfigCheck for VirtioSerialPort {
fn check(&self) -> Result<()> {
check_arg_too_long(&self.id, "chardev id")
}
}
/// Config structure for character device.
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct ChardevConfig {
pub id: String,
pub backend: ChardevType,
}
impl ConfigCheck for ChardevConfig {
fn check(&self) -> Result<()> {
check_arg_too_long(&self.id, "chardev id")?;
let len = match &self.backend {
ChardevType::Socket { path, .. } => path.len(),
ChardevType::File(path) => path.len(),
_ => 0,
};
if len > MAX_PATH_LENGTH {
return Err(anyhow!(ConfigError::StringLengthTooLong(
"socket path".to_string(),
MAX_PATH_LENGTH
)));
}
Ok(())
}
}
fn check_chardev_args(cmd_parser: CmdParser) -> Result<()> {
if let Some(chardev_type) = cmd_parser.get_value::<String>("")? {
let chardev_str = chardev_type.as_str();
let server = cmd_parser.get_value::<String>("server")?;
let nowait = cmd_parser.get_value::<String>("nowait")?;
match chardev_str {
"stdio" | "pty" | "file" => {
if server.is_some() {
bail!(
"Chardev of {}-type does not support \'server\' argument",
chardev_str
);
}
if nowait.is_some() {
bail!(
"Chardev of {}-type does not support \'nowait\' argument",
chardev_str
);
}
}
"socket" => {
if let Some(server) = server {
if server.ne("") {
bail!("No parameter needed for server");
}
}
if let Some(nowait) = nowait {
if nowait.ne("") {
bail!("No parameter needed for nowait");
}
}
}
_ => (),
}
}
Ok(())
}
pub fn parse_chardev(cmd_parser: CmdParser) -> Result<ChardevConfig> {
let chardev_id = cmd_parser
.get_value::<String>("id")?
.with_context(|| ConfigError::FieldIsMissing("id".to_string(), "chardev".to_string()))?;
let backend = cmd_parser.get_value::<String>("")?;
let path = cmd_parser.get_value::<String>("path")?;
let server = if let Some(server) = cmd_parser.get_value::<String>("server")? {
if server.ne("") {
bail!("No parameter needed for server");
}
true
} else {
false
};
let nowait = if let Some(nowait) = cmd_parser.get_value::<String>("nowait")? {
if nowait.ne("") {
bail!("No parameter needed for nowait");
}
true
} else {
false
};
check_chardev_args(cmd_parser)?;
let chardev_type = if let Some(backend) = backend {
match backend.as_str() {
"stdio" => ChardevType::Stdio,
"pty" => ChardevType::Pty,
"socket" => {
if let Some(path) = path {
ChardevType::Socket {
path,
server,
nowait,
}
} else {
return Err(anyhow!(ConfigError::FieldIsMissing(
"path".to_string(),
"socket-type chardev".to_string()
)));
}
}
"file" => {
if let Some(path) = path {
ChardevType::File(path)
} else {
return Err(anyhow!(ConfigError::FieldIsMissing(
"path".to_string(),
"file-type chardev".to_string()
)));
}
}
_ => {
return Err(anyhow!(ConfigError::InvalidParam(
backend,
"chardev".to_string()
)))
}
}
} else {
return Err(anyhow!(ConfigError::FieldIsMissing(
"backend".to_string(),
"chardev".to_string()
)));
};
Ok(ChardevConfig {
id: chardev_id,
backend: chardev_type,
})
}
/// Get chardev config from qmp arguments.
///
/// # Arguments
///
/// * `args` - The qmp arguments.
pub fn get_chardev_config(args: qmp_schema::CharDevAddArgument) -> Result<ChardevConfig> {
let backend = args.backend;
if backend.backend_type.as_str() != "socket" {
return Err(anyhow!(ConfigError::InvalidParam(
"backend".to_string(),
backend.backend_type
)));
}
let data = backend.backend_data;
if data.server {
error!("Not support chardev socket as server now.");
return Err(anyhow!(ConfigError::InvalidParam(
"backend".to_string(),
"server".to_string()
)));
}
let addr = data.addr;
if addr.addr_type.as_str() != "unix" {
error!("Just support \"unix\" addr type option now.");
return Err(anyhow!(ConfigError::InvalidParam(
"backend".to_string(),
"addr".to_string()
)));
}
Ok(ChardevConfig {
id: args.id,
backend: ChardevType::Socket {
path: addr.addr_data.path,
server: data.server,
nowait: false,
},
})
}
/// Get chardev socket path from ChardevConfig struct.
///
/// # Arguments
///
/// * `char_dev` - ChardevConfig struct reference.
/// * `vm_config` - mutable VmConfig struct reference.
pub fn get_chardev_socket_path(chardev: &str, vm_config: &mut VmConfig) -> Result<String> {
if let Some(char_dev) = vm_config.chardev.remove(chardev) {
match char_dev.backend.clone() {
ChardevType::Socket {
path,
server,
nowait,
} => {
if server || nowait {
bail!(
"Argument \'server\' or \'nowait\' is not need for chardev \'{}\'",
path
);
}
Ok(path)
}
_ => {
bail!("Chardev {:?} backend should be socket type.", &char_dev.id);
}
}
} else {
bail!("Chardev: {:?} not found for character device", &chardev);
}
}
pub fn parse_virtserialport(
vm_config: &mut VmConfig,
config_args: &str,
is_console: bool,
free_nr: u32,
) -> Result<VirtioSerialPort> {
let mut cmd_parser = CmdParser::new("virtserialport");
cmd_parser.push("").push("id").push("chardev").push("nr");
cmd_parser.parse(config_args)?;
let chardev_name = cmd_parser
.get_value::<String>("chardev")?
.with_context(|| {
ConfigError::FieldIsMissing("chardev".to_string(), "virtserialport".to_string())
})?;
let id = cmd_parser.get_value::<String>("id")?.with_context(|| {
ConfigError::FieldIsMissing("id".to_string(), "virtserialport".to_string())
})?;
let nr = cmd_parser.get_value::<u32>("nr")?.unwrap_or(free_nr);
if nr == 0 && !is_console {
bail!("Port number 0 on virtio-serial devices reserved for virtconsole device.");
}
if let Some(chardev) = vm_config.chardev.remove(&chardev_name) {
let port_cfg = VirtioSerialPort {
id,
chardev,
nr,
is_console,
};
port_cfg.check()?;
return Ok(port_cfg);
}
bail!("Chardev {:?} not found or is in use", &chardev_name);
}
impl VmConfig {
/// Add chardev config to `VmConfig`.
pub fn add_chardev(&mut self, chardev_config: &str) -> Result<()> {
let mut cmd_parser = CmdParser::new("chardev");
cmd_parser
.push("")
.push("id")
.push("path")
.push("server")
.push("nowait");
cmd_parser.parse(chardev_config)?;
let chardev = parse_chardev(cmd_parser)?;
chardev.check()?;
let chardev_id = chardev.id.clone();
if self.chardev.get(&chardev_id).is_none() {
self.chardev.insert(chardev_id, chardev);
} else {
bail!("Chardev {:?} has been added", &chardev_id);
}
Ok(())
}
/// Add chardev config to vm config.
///
/// # Arguments
///
/// * `conf` - The chardev config to be added to the vm.
pub fn add_chardev_with_config(&mut self, conf: ChardevConfig) -> Result<()> {
if let Err(e) = conf.check() {
bail!("Chardev config checking failed, {}", e.to_string());
}
let chardev_id = conf.id.clone();
if self.chardev.get(&chardev_id).is_none() {
self.chardev.insert(chardev_id, conf);
} else {
bail!("Chardev {:?} has been added", chardev_id);
}
Ok(())
}
/// Delete chardev config from vm config.
///
/// # Arguments
///
/// * `id` - The chardev id which is used to delete chardev config.
pub fn del_chardev_by_id(&mut self, id: &str) -> Result<()> {
if self.chardev.get(id).is_some() {
self.chardev.remove(id);
} else {
bail!("Chardev {} not found", id);
}
Ok(())
}
}
/// Config structure for serial.
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct SerialConfig {
pub chardev: ChardevConfig,
}
impl VmConfig {
pub fn add_serial(&mut self, serial_config: &str) -> Result<()> {
let parse_vec: Vec<&str> = serial_config.split(':').collect();
let chardev_id = match parse_vec[0] {
"chardev" => {
if parse_vec.len() == 2 {
parse_vec[1]
} else {
return Err(anyhow!(ConfigError::InvalidParam(
serial_config.to_string(),
"serial".to_string(),
)));
}
}
_ => {
let chardev_config = serial_config.to_string() + ",id=serial_chardev";
self.add_chardev(&chardev_config)
.with_context(|| "Failed to add chardev")?;
"serial_chardev"
}
};
if let Some(char_dev) = self.chardev.remove(chardev_id) {
self.serial = Some(SerialConfig { chardev: char_dev });
return Ok(());
}
bail!("Chardev {:?} not found or is in use", chardev_id);
}
}
/// Config structure for virtio-vsock.
#[derive(Debug, Clone, Default, Serialize, Deserialize)]
pub struct VsockConfig {
pub id: String,
pub guest_cid: u64,
pub vhost_fd: Option<i32>,
}
impl ConfigCheck for VsockConfig {
fn check(&self) -> Result<()> {
check_arg_too_long(&self.id, "vsock id")?;
if self.guest_cid < MIN_GUEST_CID || self.guest_cid >= MAX_GUEST_CID {
return Err(anyhow!(ConfigError::IllegalValue(
"Vsock guest-cid".to_string(),
MIN_GUEST_CID,
true,
MAX_GUEST_CID,
false,
)));
}
Ok(())
}
}
pub fn parse_vsock(vsock_config: &str) -> Result<VsockConfig> {
let mut cmd_parser = CmdParser::new("vhost-vsock");
cmd_parser
.push("")
.push("id")
.push("bus")
.push("addr")
.push("multifunction")
.push("guest-cid")
.push("vhostfd");
cmd_parser.parse(vsock_config)?;
pci_args_check(&cmd_parser)?;
let id = cmd_parser
.get_value::<String>("id")?
.with_context(|| ConfigError::FieldIsMissing("id".to_string(), "vsock".to_string()))?;
let guest_cid = cmd_parser.get_value::<u64>("guest-cid")?.with_context(|| {
ConfigError::FieldIsMissing("guest-cid".to_string(), "vsock".to_string())
})?;
let vhost_fd = cmd_parser.get_value::<i32>("vhostfd")?;
let vsock = VsockConfig {
id,
guest_cid,
vhost_fd,
};
Ok(vsock)
}
#[derive(Clone, Debug, Serialize, Deserialize)]
pub struct VirtioSerialInfo {
pub id: String,
pub pci_bdf: Option<PciBdf>,
pub multifunction: bool,
pub max_ports: u32,
}
impl ConfigCheck for VirtioSerialInfo {
fn check(&self) -> Result<()> {
check_arg_too_long(&self.id, "virtio-serial id")?;
if self.max_ports < 1 || self.max_ports > DEFAULT_SERIAL_PORTS_NUMBER {
return Err(anyhow!(ConfigError::IllegalValue(
"Virtio-serial max_ports".to_string(),
1,
true,
DEFAULT_SERIAL_PORTS_NUMBER as u64,
true
)));
}
Ok(())
}
}
pub fn parse_virtio_serial(
vm_config: &mut VmConfig,
serial_config: &str,
) -> Result<VirtioSerialInfo> {
let mut cmd_parser = CmdParser::new("virtio-serial");
cmd_parser
.push("")
.push("id")
.push("bus")
.push("addr")
.push("multifunction")
.push("max_ports");
cmd_parser.parse(serial_config)?;
pci_args_check(&cmd_parser)?;
if vm_config.virtio_serial.is_some() {
bail!("Only one virtio serial device is supported");
}
let id = cmd_parser.get_value::<String>("id")?.unwrap_or_default();
let multifunction = cmd_parser
.get_value::<ExBool>("multifunction")?
.map_or(false, |switch| switch.into());
let max_ports = cmd_parser
.get_value::<u32>("max_ports")?
.unwrap_or(DEFAULT_SERIAL_PORTS_NUMBER);
let virtio_serial = if serial_config.contains("-pci") {
let pci_bdf = get_pci_bdf(serial_config)?;
VirtioSerialInfo {
id,
pci_bdf: Some(pci_bdf),
multifunction,
max_ports,
}
} else {
VirtioSerialInfo {
id,
pci_bdf: None,
multifunction,
// Micro_vm does not support multi-ports in virtio-serial-device.
max_ports: 1,
}
};
virtio_serial.check()?;
vm_config.virtio_serial = Some(virtio_serial.clone());
Ok(virtio_serial)
}
#[cfg(test)]
mod tests {
use super::*;
use crate::config::parse_virtio_serial;
#[test]
fn test_mmio_console_config_cmdline_parser() {
let mut vm_config = VmConfig::default();
assert!(parse_virtio_serial(&mut vm_config, "virtio-serial-device").is_ok());
assert!(vm_config
.add_chardev("socket,id=test_console,path=/path/to/socket,server,nowait")
.is_ok());
let virt_console = parse_virtserialport(
&mut vm_config,
"virtconsole,chardev=test_console,id=console1,nr=1",
true,
0,
);
assert!(virt_console.is_ok());
let console_cfg = virt_console.unwrap();
assert_eq!(console_cfg.id, "console1");
assert_eq!(
console_cfg.chardev.backend,
ChardevType::Socket {
path: "/path/to/socket".to_string(),
server: true,
nowait: true,
}
);
let mut vm_config = VmConfig::default();
assert!(
parse_virtio_serial(&mut vm_config, "virtio-serial-device,bus=pcie.0,addr=0x1")
.is_err()
);
assert!(vm_config
.add_chardev("sock,id=test_console,path=/path/to/socket")
.is_err());
let mut vm_config = VmConfig::default();
assert!(parse_virtio_serial(&mut vm_config, "virtio-serial-device").is_ok());
assert!(vm_config
.add_chardev("socket,id=test_console,path=/path/to/socket,server,nowait")
.is_ok());
let virt_console = parse_virtserialport(
&mut vm_config,
"virtconsole,chardev=test_console1,id=console1,nr=1",
true,
0,
);
// test_console1 does not exist.
assert!(virt_console.is_err());
}
#[test]
fn test_pci_console_config_cmdline_parser() {
let mut vm_config = VmConfig::default();
assert!(
parse_virtio_serial(&mut vm_config, "virtio-serial-pci,bus=pcie.0,addr=0x1.0x2")
.is_ok()
);
assert!(vm_config
.add_chardev("socket,id=test_console,path=/path/to/socket,server,nowait")
.is_ok());
let virt_console = parse_virtserialport(
&mut vm_config,
"virtconsole,chardev=test_console,id=console1,nr=1",
true,
0,
);
assert!(virt_console.is_ok());
let console_cfg = virt_console.unwrap();
assert_eq!(console_cfg.id, "console1");
let serial_info = vm_config.virtio_serial.clone().unwrap();
assert!(serial_info.pci_bdf.is_some());
let bdf = serial_info.pci_bdf.unwrap();
assert_eq!(bdf.bus, "pcie.0");
assert_eq!(bdf.addr, (1, 2));
assert_eq!(
console_cfg.chardev.backend,
ChardevType::Socket {
path: "/path/to/socket".to_string(),
server: true,
nowait: true,
}
);
let mut vm_config = VmConfig::default();
assert!(parse_virtio_serial(
&mut vm_config,
"virtio-serial-pci,bus=pcie.0,addr=0x1.0x2,multifunction=on"
)
.is_ok());
}
#[test]
fn test_vsock_config_cmdline_parser() {
let vsock_cfg_op = parse_vsock("vhost-vsock-device,id=test_vsock,guest-cid=3");
assert!(vsock_cfg_op.is_ok());
let vsock_config = vsock_cfg_op.unwrap();
assert_eq!(vsock_config.id, "test_vsock");
assert_eq!(vsock_config.guest_cid, 3);
assert_eq!(vsock_config.vhost_fd, None);
assert!(vsock_config.check().is_ok());
let vsock_cfg_op = parse_vsock("vhost-vsock-device,id=test_vsock,guest-cid=3,vhostfd=4");
assert!(vsock_cfg_op.is_ok());
let vsock_config = vsock_cfg_op.unwrap();
assert_eq!(vsock_config.id, "test_vsock");
assert_eq!(vsock_config.guest_cid, 3);
assert_eq!(vsock_config.vhost_fd, Some(4));
assert!(vsock_config.check().is_ok());
}
#[test]
fn test_chardev_config_cmdline_parser() {
let mut vm_config = VmConfig::default();
assert!(vm_config
.add_chardev("socket,id=test_id,path=/path/to/socket")
.is_ok());
assert!(vm_config
.add_chardev("socket,id=test_id,path=/path/to/socket")
.is_err());
if let Some(char_dev) = vm_config.chardev.remove("test_id") {
assert_eq!(
char_dev.backend,
ChardevType::Socket {
path: "/path/to/socket".to_string(),
server: false,
nowait: false,
}
);
} else {
assert!(false);
}
}
}

Опубликовать ( 0 )

Вы можете оставить комментарий после Вход в систему

1
https://gitlife.ru/oschina-mirror/openeuler-stratovirt.git
git@gitlife.ru:oschina-mirror/openeuler-stratovirt.git
oschina-mirror
openeuler-stratovirt
openeuler-stratovirt
v2.3.0