vhost-device-gpu: Add support for specifying GPU path

Rutabaga_gfx, since v0.1.75, supports specifying a GPU path (e.g.
/dev/dri/renderD128) for virglrenderer. This patch introduces a
`--gpu-path` argument to allow users to use any GPU they want.

Signed-off-by: Xuewei Niu <niuxuewei.nxw@antgroup.com>
This commit is contained in:
Xuewei Niu 2025-11-03 17:36:48 +08:00
parent 4bbe030e65
commit a7629e5566
6 changed files with 43 additions and 10 deletions

View File

@ -3,6 +3,8 @@
### Added
- [https://github.com/rust-vmm/vhost-device/pull/900] vhost-device-gpu: Add support for specifying GPU path
### Changed
### Fixed

View File

@ -56,6 +56,9 @@ A virtio-gpu device using the vhost-user protocol.
[default: true]
[possible values: true, false]
-p, --gpu-path <PATH>
GPU path (e.g. /dev/dri/renderD128), only available for virglrenderer backend
-h, --help
Print help (see a summary with '-h')

View File

@ -765,6 +765,7 @@ mod tests {
GpuMode::VirglRenderer,
Some(GpuCapset::VIRGL | GpuCapset::VIRGL2),
GpuFlags::default(),
None,
)
.unwrap();
let backend = VhostUserGpuBackend::new(config).unwrap();
@ -1353,7 +1354,7 @@ mod tests {
rusty_fork_test! {
#[test]
fn test_verify_backend() {
let gpu_config = GpuConfig::new(GpuMode::VirglRenderer, None, GpuFlags::default()).unwrap();
let gpu_config = GpuConfig::new(GpuMode::VirglRenderer, None, GpuFlags::default(), None).unwrap();
let backend = VhostUserGpuBackend::new(gpu_config).unwrap();
assert_eq!(backend.num_queues(), NUM_QUEUES);

View File

@ -127,6 +127,7 @@ pub struct GpuConfig {
gpu_mode: GpuMode,
capset: GpuCapset,
flags: GpuFlags,
gpu_path: Option<String>,
}
#[derive(Debug, Clone, PartialEq, Eq)]
@ -198,6 +199,7 @@ impl GpuConfig {
gpu_mode: GpuMode,
capset: Option<GpuCapset>,
flags: GpuFlags,
gpu_path: Option<String>,
) -> Result<Self, GpuConfigError> {
let capset = capset.unwrap_or_else(|| Self::get_default_capset_for_mode(gpu_mode));
Self::validate_capset(gpu_mode, capset)?;
@ -211,6 +213,7 @@ impl GpuConfig {
gpu_mode,
capset,
flags,
gpu_path,
})
}
@ -263,7 +266,8 @@ mod tests {
#[test]
fn test_gpu_config_create_default_virglrenderer() {
let config = GpuConfig::new(GpuMode::VirglRenderer, None, GpuFlags::new_default()).unwrap();
let config =
GpuConfig::new(GpuMode::VirglRenderer, None, GpuFlags::new_default(), None).unwrap();
assert_eq!(config.gpu_mode(), GpuMode::VirglRenderer);
assert_eq!(config.capsets(), GpuConfig::DEFAULT_VIRGLRENDER_CAPSET_MASK);
}
@ -271,14 +275,14 @@ mod tests {
#[test]
#[cfg(feature = "gfxstream")]
fn test_gpu_config_create_default_gfxstream() {
let config = GpuConfig::new(GpuMode::Gfxstream, None, GpuFlags::default()).unwrap();
let config = GpuConfig::new(GpuMode::Gfxstream, None, GpuFlags::default(), None).unwrap();
assert_eq!(config.gpu_mode(), GpuMode::Gfxstream);
assert_eq!(config.capsets(), GpuConfig::DEFAULT_GFXSTREAM_CAPSET_MASK);
}
#[cfg(feature = "gfxstream")]
fn assert_invalid_gpu_config(mode: GpuMode, capset: GpuCapset, expected_capset: GpuCapset) {
let result = GpuConfig::new(mode, Some(capset), GpuFlags::new_default());
let result = GpuConfig::new(mode, Some(capset), GpuFlags::new_default(), None);
assert_matches!(
result,
Err(GpuConfigError::CapsetUnsupportedByMode(
@ -294,6 +298,7 @@ mod tests {
GpuMode::VirglRenderer,
Some(GpuCapset::VIRGL2),
GpuFlags::default(),
None,
)
.unwrap();
assert_eq!(config.gpu_mode(), GpuMode::VirglRenderer);
@ -323,7 +328,7 @@ mod tests {
use_gles: false,
..GpuFlags::new_default()
};
let result = GpuConfig::new(GpuMode::Gfxstream, Some(capset), flags);
let result = GpuConfig::new(GpuMode::Gfxstream, Some(capset), flags, None);
assert_matches!(result, Err(GpuConfigError::GlesRequiredByGfxstream));
}
@ -355,7 +360,8 @@ mod tests {
fn test_fail_listener() {
// This will fail the listeners and thread will panic.
let socket_name = Path::new("/proc/-1/nonexistent");
let config = GpuConfig::new(GpuMode::VirglRenderer, None, GpuFlags::default()).unwrap();
let config =
GpuConfig::new(GpuMode::VirglRenderer, None, GpuFlags::default(), None).unwrap();
assert_matches!(
start_backend(socket_name, config).unwrap_err(),

View File

@ -63,6 +63,11 @@ pub struct GpuArgs {
#[clap(flatten)]
pub flags: GpuFlagsArgs,
/// GPU path (e.g. /dev/dri/renderD128), only available for
/// virglrenderer backend.
#[clap(short = 'p', long, value_name = "PATH")]
pub gpu_path: Option<String>,
}
#[derive(Parser, Debug)]
@ -115,7 +120,7 @@ impl From<GpuFlagsArgs> for GpuFlags {
pub fn config_from_args(args: GpuArgs) -> Result<(PathBuf, GpuConfig), GpuConfigError> {
let flags = GpuFlags::from(args.flags);
let capset = args.capset.map(capset_names_into_capset);
let config = GpuConfig::new(args.gpu_mode, capset, flags)?;
let config = GpuConfig::new(args.gpu_mode, capset, flags, args.gpu_path)?;
Ok((args.socket_path, config))
}
@ -186,6 +191,7 @@ mod tests {
use_gles: false,
use_surfaceless: false,
},
gpu_path: None,
};
let (socket_path, config) = config_from_args(args).unwrap();

View File

@ -7,7 +7,9 @@ use std::{
collections::BTreeMap,
io::IoSliceMut,
os::fd::{AsFd, FromRawFd, RawFd},
path::PathBuf,
result::Result,
str::FromStr,
sync::{Arc, Mutex},
};
@ -16,7 +18,8 @@ use log::{debug, error, trace, warn};
use rutabaga_gfx::{
Resource3DInfo, ResourceCreate3D, ResourceCreateBlob, Rutabaga, RutabagaBuilder,
RutabagaComponentType, RutabagaFence, RutabagaFenceHandler, RutabagaHandle,
RutabagaIntoRawDescriptor, RutabagaIovec, Transfer3D, RUTABAGA_HANDLE_TYPE_MEM_DMABUF,
RutabagaIntoRawDescriptor, RutabagaIovec, RutabagaPath, Transfer3D,
RUTABAGA_HANDLE_TYPE_MEM_DMABUF, RUTABAGA_PATH_TYPE_GPU,
};
#[cfg(feature = "gfxstream")]
use vhost::vhost_user::gpu_message::VhostUserGpuScanout;
@ -403,7 +406,7 @@ impl RutabagaVirtioGpu {
GpuMode::Gfxstream => RutabagaComponentType::Gfxstream,
};
let builder = RutabagaBuilder::new(gpu_config.capsets().bits(), fence)
let mut builder = RutabagaBuilder::new(gpu_config.capsets().bits(), fence)
.set_use_egl(gpu_config.flags().use_egl)
.set_use_gles(gpu_config.flags().use_gles)
.set_use_surfaceless(gpu_config.flags().use_surfaceless)
@ -411,6 +414,18 @@ impl RutabagaVirtioGpu {
// could work, so this is always enabled
.set_use_external_blob(true);
let mut rutabaga_paths = Vec::new();
if let Some(gpu_path) = gpu_config.gpu_path.as_ref() {
rutabaga_paths.push(RutabagaPath {
// PathBuf::from_str() never fails
path: PathBuf::from_str(gpu_path).unwrap(),
path_type: RUTABAGA_PATH_TYPE_GPU,
});
}
if !rutabaga_paths.is_empty() {
builder = builder.set_rutabaga_paths(Some(rutabaga_paths));
}
(builder, component)
}
@ -1155,7 +1170,7 @@ mod tests {
_ => panic!("Unsupported component type for test"),
};
let config = GpuConfig::new(gpu_mode, capsets, GpuFlags::default()).unwrap();
let config = GpuConfig::new(gpu_mode, capsets, GpuFlags::default(), None).unwrap();
// Mock memory
let mem = GuestMemoryAtomic::new(