vmm: config: Fix generic vhost-user parsing

The generic vhost-user device took its virtio device type on the
command line via the `virtio_id` parameter, but the same value is
called `device_type` in the API and the resulting config struct. This
irregularity was due to churn during the review process, `device_type`
was the intended name.

Accept `device_type` on the command line and keep `virtio_id` as a
deprecated alias that logs a warning. The alias will then be removed in
a later release.

Fixes: #8545

Assisted-by: Claude:Opus-4.8
Signed-off-by: Rob Bradford <rbradford@meta.com>
This commit is contained in:
Rob Bradford
2026-07-08 12:18:58 -07:00
parent 30c0fdaff1
commit 818fc07266
4 changed files with 32 additions and 14 deletions

View File

@@ -957,7 +957,7 @@ pub(crate) fn _test_virtio_fs(
"socket={},id=myfs0,{}{}", "socket={},id=myfs0,{}{}",
virtiofsd_socket_path, virtiofsd_socket_path,
if use_generic_vhost_user { if use_generic_vhost_user {
"queue_sizes=[1024,1024],virtio_id=26" "queue_sizes=[1024,1024],device_type=26"
} else { } else {
"tag=myfs,num_queues=1,queue_size=1024" "tag=myfs,num_queues=1,queue_size=1024"
}, },
@@ -1090,7 +1090,7 @@ pub(crate) fn _test_virtio_fs(
"id=myfs0,socket={},{}{}", "id=myfs0,socket={},{}{}",
virtiofsd_socket_path, virtiofsd_socket_path,
if use_generic_vhost_user { if use_generic_vhost_user {
"queue_sizes=[1024,1024],virtio_id=26" "queue_sizes=[1024,1024],device_type=26"
} else { } else {
"tag=myfs,num_queues=1,queue_size=1024" "tag=myfs,num_queues=1,queue_size=1024"
}, },

View File

@@ -43,14 +43,14 @@ similar to this:
--disk path=your-linux-image.iso \ --disk path=your-linux-image.iso \
--kernel vmlinux \ --kernel vmlinux \
--cmdline "console=hvc0 root=/dev/vda1 rw" \ --cmdline "console=hvc0 root=/dev/vda1 rw" \
--generic-vhost-user "socket=\"${path_to_virtiofsd_socket//\"/\"\"}\",virtio_id=26,queue_sizes=[512,512]" \ --generic-vhost-user "socket=\"${path_to_virtiofsd_socket//\"/\"\"}\",device_type=26,queue_sizes=[512,512]" \
"${other_cloud_hypervisor_options[@]}" "${other_cloud_hypervisor_options[@]}"
``` ```
26 is the ID for a virtio-fs device. The IDs for other devices are defined 26 is the ID for a virtio-fs device. The IDs for other devices are defined
by the VIRTIO specification. The odd-looking variable expansion escapes by the VIRTIO specification. The odd-looking variable expansion escapes
any double quotes in the socket path. It is also possible to provide any double quotes in the socket path. It is also possible to provide
the name that is defined by the virtio specification, so `virtio_id=fs` the name that is defined by the virtio specification, so `device_type=fs`
will also work. will also work.
Inside the guest, you can mount the virtio-fs device with Inside the guest, you can mount the virtio-fs device with

View File

@@ -53,14 +53,14 @@ pub enum Error {
/// Filesystem socket is missing /// Filesystem socket is missing
#[error("Error parsing --fs: socket missing")] #[error("Error parsing --fs: socket missing")]
ParseFsSockMissing, ParseFsSockMissing,
/// Generic vhost-user virtio ID is invalid /// Generic vhost-user device type is invalid
#[error( #[error(
"Error parsing --generic-vhost-user: virtio ID {0:?} invalid (leading zeros or unknown string)" "Error parsing --generic-vhost-user: device_type {0:?} invalid (leading zeros or unknown string)"
)] )]
ParseGenericVhostUserVirtioIdInvalid(String), ParseGenericVhostUserVirtioIdInvalid(String),
/// Generic vhost-user virtio ID is unsupported /// Generic vhost-user device type is unsupported
#[error( #[error(
"Error parsing --generic-vhost-user: device with virtio ID {0:?} cannot be implemented via vhost-user" "Error parsing --generic-vhost-user: device with device_type {0:?} cannot be implemented via vhost-user"
)] )]
ParseGenericVhostUserVirtioIdUnsupported(String), ParseGenericVhostUserVirtioIdUnsupported(String),
/// Generic vhost-user socket is missing /// Generic vhost-user socket is missing
@@ -69,8 +69,8 @@ pub enum Error {
/// Generic vhost-user number of queues is missing /// Generic vhost-user number of queues is missing
#[error("Error parsing --generic-vhost-user: number of queues missing")] #[error("Error parsing --generic-vhost-user: number of queues missing")]
ParseGenericVhostUserNumResponseQueuesMissing, ParseGenericVhostUserNumResponseQueuesMissing,
/// Generic vhost-user virtio ID is missing /// Generic vhost-user device type is missing
#[error("Error parsing --generic-vhost-user: virtio ID missing")] #[error("Error parsing --generic-vhost-user: device_type missing")]
ParseGenericVhostUserVirtioIdMissing, ParseGenericVhostUserVirtioIdMissing,
/// Generic vhost-user available features is missing /// Generic vhost-user available features is missing
#[error("Error parsing --generic-vhost-user: available features missing")] #[error("Error parsing --generic-vhost-user: available features missing")]
@@ -1998,7 +1998,7 @@ impl BalloonConfig {
impl GenericVhostUserConfig { impl GenericVhostUserConfig {
pub const SYNTAX: &'static str = "generic vhost-user parameters \ pub const SYNTAX: &'static str = "generic vhost-user parameters \
\"virtio_id=<ID number for virtio device type (FS, block, net, etc) or symbolic name>,\ \"device_type=<ID number for virtio device type (FS, block, net, etc) or symbolic name>,\
socket=<socket_path>,\ socket=<socket_path>,\
queue_sizes=<list of queue sizes>,\ queue_sizes=<list of queue sizes>,\
id=<device_id>,pci_segment=<segment_id>,pci_device_id=<pci_slot>\""; id=<device_id>,pci_segment=<segment_id>,pci_device_id=<pci_slot>\"";
@@ -2006,6 +2006,8 @@ impl GenericVhostUserConfig {
pub fn parse(vhost_user: &str) -> Result<Self> { pub fn parse(vhost_user: &str) -> Result<Self> {
let mut parser = OptionParser::new(); let mut parser = OptionParser::new();
parser parser
.add("device_type")
// TODO: Remove 'virtio_id' as a deprecated alias for 'device_type'
.add("virtio_id") .add("virtio_id")
.add("queue_sizes") .add("queue_sizes")
.add("socket") .add("socket")
@@ -2022,9 +2024,16 @@ impl GenericVhostUserConfig {
.convert::<IntegerList<u16>>("queue_sizes") .convert::<IntegerList<u16>>("queue_sizes")
.map_err(Error::ParseGenericVhostUser)? .map_err(Error::ParseGenericVhostUser)?
.ok_or(Error::ParseGenericVhostUserQueueSizeMissing)?; .ok_or(Error::ParseGenericVhostUserQueueSizeMissing)?;
let device_type_str = parser let legacy_virtio_id = parser
.convert::<String>("virtio_id") .convert::<String>("virtio_id")
.map_err(Error::ParseGenericVhostUser)?;
if legacy_virtio_id.is_some() {
warn!("'virtio_id' in --generic-vhost-user is deprecated; use 'device_type'.");
}
let device_type_str = parser
.convert::<String>("device_type")
.map_err(Error::ParseGenericVhostUser)? .map_err(Error::ParseGenericVhostUser)?
.or(legacy_virtio_id)
.ok_or(Error::ParseGenericVhostUserVirtioIdMissing)?; .ok_or(Error::ParseGenericVhostUserVirtioIdMissing)?;
let device_type = match device_type_str.as_bytes() { let device_type = match device_type_str.as_bytes() {
b"net" => VIRTIO_ID_NET, b"net" => VIRTIO_ID_NET,
@@ -4583,7 +4592,7 @@ mod unit_tests {
assert!(!socket.contains(",[]\n\r\0\"")); assert!(!socket.contains(",[]\n\r\0\""));
assert!(!id.contains(",[]\n\r\0\"")); assert!(!id.contains(",[]\n\r\0\""));
let config = GenericVhostUserConfig::parse(&format!( let config = GenericVhostUserConfig::parse(&format!(
"virtio_id={virtio_id},socket=\"{socket}\",\ "device_type={virtio_id},socket=\"{socket}\",\
id=\"{id}\",pci_segment={pci_segment},queue_sizes={queue_sizes}" id=\"{id}\",pci_segment={pci_segment},queue_sizes={queue_sizes}"
)); ));
if pci_segment <= u16::MAX.into() if pci_segment <= u16::MAX.into()
@@ -4658,6 +4667,15 @@ id=\"{id}\",pci_segment={pci_segment},queue_sizes={queue_sizes}"
10, 10,
&IntegerList(vec![20u64]), &IntegerList(vec![20u64]),
); );
// The deprecated 'virtio_id' key is an alias for 'device_type' and must
// parse to an identical configuration.
assert_eq!(
GenericVhostUserConfig::parse("virtio_id=26,socket=/tmp/sock,queue_sizes=[1024]")
.unwrap(),
GenericVhostUserConfig::parse("device_type=26,socket=/tmp/sock,queue_sizes=[1024]")
.unwrap(),
);
Ok(()) Ok(())
} }

View File

@@ -3590,7 +3590,7 @@ mod unit_tests {
fn test_vmm_vm_cold_add_generic_vhost_user() { fn test_vmm_vm_cold_add_generic_vhost_user() {
let mut vmm = create_dummy_vmm(); let mut vmm = create_dummy_vmm();
let generic_vhost_user_config = let generic_vhost_user_config =
GenericVhostUserConfig::parse("virtio_id=26,socket=/tmp/sock,queue_sizes=[1024]") GenericVhostUserConfig::parse("device_type=26,socket=/tmp/sock,queue_sizes=[1024]")
.unwrap(); .unwrap();
assert!(matches!( assert!(matches!(