runtime-rs: Add CcwSubChannel to provide devno for CCW devices

To explicitly specify a device number on the QEMU command line
for the following devices using the CCW transport on s390x:

- SerialDevice
- BlockDevice
- VhostUserDevice
- SCSIController
- VSOCKDevice

this commit introduces a new structure CcwSubChannel and implements
the following methods:

- add_device()
- remove_device()
- address_format_ccw()
- set_addr()

You can see the detailed explanation for each method in the comment.

This resolves the 1st part of #10573.

Signed-off-by: Hyounggyu Choi <Hyounggyu.Choi@ibm.com>
This commit is contained in:
Hyounggyu Choi
2024-11-21 13:03:51 +01:00
parent 322073bea1
commit 30a64092a7

View File

@@ -369,6 +369,88 @@ impl ToQemuParams for Cpu {
}
}
/// Error type for CCW Subchannel operations
#[derive(Debug)]
enum CcwError {
DeviceAlreadyExists(String), // Error when trying to add an existing device
#[allow(dead_code)]
DeviceNotFound(String), // Error when trying to remove a nonexistent device
}
/// Represents a CCW subchannel for managing devices
#[derive(Debug)]
struct CcwSubChannel {
devices: HashMap<String, u32>, // Maps device IDs to slot indices
addr: u32, // Subchannel address
next_slot: u32, // Next available slot index
}
impl CcwSubChannel {
fn new() -> Self {
Self {
devices: HashMap::new(),
addr: 0,
next_slot: 0,
}
}
/// Adds a device to the subchannel.
///
/// # Arguments
/// - `dev_id`: device ID to add
///
/// # Returns
/// - `Result<u32, CcwError>`: slot index of the added device
/// or an error if the device already exists
fn add_device(&mut self, dev_id: &str) -> Result<u32, CcwError> {
if self.devices.contains_key(dev_id) {
Err(CcwError::DeviceAlreadyExists(dev_id.to_owned()))
} else {
let slot = self.next_slot;
self.devices.insert(dev_id.to_owned(), slot);
self.next_slot += 1;
Ok(slot)
}
}
/// Removes a device from the subchannel by its ID.
///
/// # Arguments
/// - `dev_id`: device ID to remove
///
/// # Returns
/// - `Result<(), CcwError>`: Ok(()) if the device was removed
/// or an error if the device was not found
#[allow(dead_code)]
fn remove_device(&mut self, dev_id: &str) -> Result<(), CcwError> {
if self.devices.remove(dev_id).is_some() {
Ok(())
} else {
Err(CcwError::DeviceNotFound(dev_id.to_owned()))
}
}
/// Formats the CCW address for a given slot
///
/// # Arguments
/// - `slot`: slot index
///
/// # Returns
/// - `String`: formatted CCW address (e.g. `fe.0.0000`)
fn address_format_ccw(&self, slot: u32) -> String {
format!("fe.{:x}.{:04x}", self.addr, slot)
}
/// Sets the address of the subchannel.
/// # Arguments
/// - `addr`: subchannel address to set
#[allow(dead_code)]
fn set_addr(&mut self, addr: u32) -> &mut Self {
self.addr = addr;
self
}
}
#[derive(Debug)]
struct Machine {
r#type: String,
@@ -1622,10 +1704,15 @@ pub struct QemuCmdLine<'a> {
knobs: Knobs,
devices: Vec<Box<dyn ToQemuParams>>,
ccw_subchannel: Option<CcwSubChannel>,
}
impl<'a> QemuCmdLine<'a> {
pub fn new(id: &str, config: &'a HypervisorConfig) -> Result<QemuCmdLine<'a>> {
let ccw_subchannel = match bus_type(config) {
VirtioBusType::Ccw => Some(CcwSubChannel::new()),
_ => None,
};
let mut qemu_cmd_line = QemuCmdLine {
id: id.to_string(),
config,
@@ -1637,6 +1724,7 @@ impl<'a> QemuCmdLine<'a> {
qmp_socket: QmpSocket::new(MonitorProtocol::Qmp)?,
knobs: Knobs::new(config),
devices: Vec::new(),
ccw_subchannel,
};
if config.device_info.enable_iommu {