-
Notifications
You must be signed in to change notification settings - Fork 95
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
vmm: support VFIO device #99
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -16,15 +16,14 @@ limitations under the License. | |
|
||
use std::{ | ||
os::unix::net::UnixStream, | ||
path::Path, | ||
thread::sleep, | ||
time::{Duration, SystemTime}, | ||
}; | ||
|
||
use anyhow::anyhow; | ||
use api_client::{simple_api_command, simple_api_full_command_with_fds_and_response}; | ||
use containerd_sandbox::error::Result; | ||
use log::{error, trace}; | ||
use log::error; | ||
|
||
use crate::{ | ||
cloud_hypervisor::devices::{block::DiskConfig, AddDeviceResponse, RemoveDeviceRequest}, | ||
|
@@ -38,25 +37,26 @@ pub struct ChClient { | |
} | ||
|
||
impl ChClient { | ||
pub fn new<P: AsRef<Path>>(socket_path: P) -> Result<Self> { | ||
pub async fn new(socket_path: String) -> Result<Self> { | ||
let start_time = SystemTime::now(); | ||
loop { | ||
tokio::task::spawn_blocking(move || loop { | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Why do we have to change it to async? There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. We have called sync function |
||
match UnixStream::connect(&socket_path) { | ||
Ok(socket) => { | ||
return Ok(Self { socket }); | ||
} | ||
Err(e) => { | ||
trace!("failed to create client: {:?}", e); | ||
if start_time.elapsed().unwrap().as_secs() | ||
> CLOUD_HYPERVISOR_START_TIMEOUT_IN_SEC | ||
{ | ||
error!("failed to create client: {:?}", e); | ||
error!("failed to connect api server: {:?}", e); | ||
return Err(anyhow!("timeout connect client, {}", e).into()); | ||
} | ||
sleep(Duration::from_millis(10)); | ||
} | ||
} | ||
} | ||
}) | ||
.await | ||
.map_err(|e| anyhow!("failed to spawn a task {}", e))? | ||
} | ||
|
||
pub fn hot_attach(&mut self, device_info: DeviceInfo) -> Result<String> { | ||
|
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,54 @@ | ||
/* | ||
Copyright 2022 The Kuasar Authors. | ||
|
||
Licensed under the Apache License, Version 2.0 (the "License"); | ||
you may not use this file except in compliance with the License. | ||
You may obtain a copy of the License at | ||
|
||
http://www.apache.org/licenses/LICENSE-2.0 | ||
|
||
Unless required by applicable law or agreed to in writing, software | ||
distributed under the License is distributed on an "AS IS" BASIS, | ||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | ||
See the License for the specific language governing permissions and | ||
limitations under the License. | ||
*/ | ||
|
||
use sandbox_derive::CmdLineParams; | ||
|
||
const VFIO_DEVICE_SYSFS_PATH: &str = "/sys/bus/pci/devices"; | ||
|
||
#[derive(CmdLineParams, Debug, Clone)] | ||
#[params("device")] | ||
pub struct VfioDevice { | ||
#[property(ignore)] | ||
pub id: String, | ||
pub(crate) path: String, | ||
} | ||
|
||
impl_device_no_bus!(VfioDevice); | ||
|
||
impl VfioDevice { | ||
pub fn new(id: &str, bdf: &str) -> Self { | ||
Self { | ||
id: id.to_string(), | ||
path: format!("{}/{}", VFIO_DEVICE_SYSFS_PATH, bdf), | ||
} | ||
} | ||
} | ||
|
||
#[cfg(test)] | ||
mod tests { | ||
use crate::{cloud_hypervisor::devices::vfio::VfioDevice, param::ToParams}; | ||
|
||
#[test] | ||
fn test_attr() { | ||
let device = VfioDevice::new("", "0000:b4:05.1"); | ||
let params = device.to_params(); | ||
let property = params.get(0).unwrap(); | ||
assert_eq!( | ||
property.get("path").unwrap(), | ||
"/sys/bus/pci/devices/0000:b4:05.1" | ||
); | ||
} | ||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -215,7 +215,7 @@ pub struct NetworkInterface { | |
impl NetworkInterface { | ||
pub async fn parse_from_message( | ||
msg: LinkMessage, | ||
_netns: &str, | ||
netns: &str, | ||
queue: u32, | ||
handle: &Handle, | ||
) -> Result<Self> { | ||
|
@@ -266,6 +266,20 @@ impl NetworkInterface { | |
} | ||
} | ||
} | ||
// find the pci device for unknown type interface, maybe it is a physical interface. | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Is it certain that all physical interface with unknown type? Or is there any possibility that any other inferface with unknown type, will it cause any problem? There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
We checked the netlink.LinkDeserialize() golang code, every type does have a correspond struct, but some of the structs do not be used, so we centralize them by Unknown type. |
||
if let LinkType::Unkonwn = intf.r#type { | ||
// only search those with ip addresses | ||
if !intf.ip_addresses.is_empty() { | ||
let if_name = intf.name.to_string(); | ||
let bdf = if !netns.is_empty() { | ||
run_in_new_netns(netns, move || get_bdf_for_eth(&if_name)).await?? | ||
abel-von marked this conversation as resolved.
Show resolved
Hide resolved
|
||
} else { | ||
get_bdf_for_eth(&if_name)? | ||
}; | ||
let driver = get_pci_driver(&bdf).await?; | ||
intf.r#type = LinkType::Physical(bdf, driver); | ||
} | ||
} | ||
Ok(intf) | ||
} | ||
|
||
|
@@ -411,7 +425,6 @@ impl NetworkInterface { | |
} | ||
} | ||
|
||
#[allow(dead_code)] | ||
fn get_bdf_for_eth(if_name: &str) -> Result<String> { | ||
if if_name.len() > 16 { | ||
return Err(anyhow!("the interface name length is larger than 16").into()); | ||
|
@@ -544,10 +557,11 @@ fn create_tap_device(tap_name: &str, mut queue: u32) -> Result<Vec<OwnedFd>> { | |
Ok(fds) | ||
} | ||
|
||
#[allow(dead_code)] | ||
async fn get_pci_driver(bdf: &str) -> Result<String> { | ||
let driver_path = format!("/sys/bus/pci/devices/{}/driver", bdf); | ||
let driver_dest = tokio::fs::read_link(driver_path).await?; | ||
let driver_dest = tokio::fs::read_link(&driver_path) | ||
.await | ||
.map_err(|e| anyhow!("fail to readlink of {} : {}", driver_path, e))?; | ||
let file_name = driver_dest.file_name().ok_or(anyhow!( | ||
"failed to get file name from driver path {:?}", | ||
driver_dest | ||
|
@@ -560,14 +574,21 @@ async fn get_pci_driver(bdf: &str) -> Result<String> { | |
} | ||
|
||
async fn bind_device_to_driver(driver: &str, bdf: &str) -> Result<()> { | ||
// 1. Switch the device driver | ||
let driver_override_path = format!("/sys/bus/pci/devices/{}/driver_override", bdf); | ||
write_file_async(&driver_override_path, driver).await?; | ||
|
||
// 2. Unbind the device from its native driver | ||
let unbind_path = format!("/sys/bus/pci/devices/{}/driver/unbind", bdf); | ||
if Path::new(&*unbind_path).exists() { | ||
write_file_async(&unbind_path, bdf).await?; | ||
} | ||
|
||
// 3. Probe driver for device | ||
let probe_path = "/sys/bus/pci/drivers_probe"; | ||
write_file_async(probe_path, bdf).await?; | ||
|
||
// 4. Check the result | ||
let driver_link = format!("/sys/bus/pci/devices/{}/driver", bdf); | ||
let driver_path = tokio::fs::read_link(&*driver_link).await?; | ||
|
||
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Why is the timeout removed when waiting?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
We have three kinds of "connectiing" method for unix socket, vsock and hvsock. All the methonds only handle the connecting, leaving the timeout be handled in the upper caller, e.g.
new_ttrpc_client
.