krata: begin work on guest message channel

This commit is contained in:
Alex Zenla
2024-03-27 06:28:47 +00:00
parent 66465793cd
commit d4f1ee5521
13 changed files with 340 additions and 58 deletions

View File

@ -17,6 +17,8 @@ serde_json = { workspace = true }
tokio = { workspace = true }
uuid = { workspace = true }
xenclient = { path = "../xen/xenclient" }
xenevtchn = { path = "../xen/xenevtchn" }
xengnt = { path = "../xen/xengnt" }
xenstore = { path = "../xen/xenstore" }
[lib]
@ -28,3 +30,7 @@ env_logger = { workspace = true }
[[example]]
name = "kratart-squashify"
path = "examples/squashify.rs"
[[example]]
name = "kratart-channel"
path = "examples/channel.rs"

View File

@ -0,0 +1,15 @@
use anyhow::Result;
use env_logger::Env;
use kratart::chan::KrataChannelService;
use xenevtchn::EventChannel;
use xenstore::XsdClient;
#[tokio::main]
async fn main() -> Result<()> {
env_logger::Builder::from_env(Env::default().default_filter_or("info")).init();
let mut krata =
KrataChannelService::new(EventChannel::open().await?, XsdClient::open().await?)?;
krata.init().await?;
Ok(())
}

158
crates/kratart/src/chan.rs Normal file
View File

@ -0,0 +1,158 @@
use std::collections::HashMap;
use anyhow::Result;
use xenevtchn::EventChannel;
use xengnt::{sys::GrantRef, GrantTab};
use xenstore::{XsdClient, XsdInterface};
#[repr(C)]
struct XenConsoleInterface {
input: [u8; 1024],
output: [u8; 2048],
in_cons: u32,
in_prod: u32,
out_cons: u32,
out_prod: u32,
}
pub struct KrataChannelService {
backends: HashMap<(u32, u32), KrataChannelBackend>,
evtchn: EventChannel,
store: XsdClient,
}
impl KrataChannelService {
pub fn new(evtchn: EventChannel, store: XsdClient) -> Result<KrataChannelService> {
Ok(KrataChannelService {
backends: HashMap::new(),
evtchn,
store,
})
}
pub async fn init(&mut self) -> Result<()> {
let domains = self.store.list("/local/domain/0/backend/console").await?;
for domid_string in domains {
let domid = domid_string.parse::<u32>()?;
let domid_path = format!("/local/domain/0/backend/console/{}", domid);
for id_string in self.store.list(&domid_path).await? {
let id = id_string.parse::<u32>()?;
let console_path = format!(
"/local/domain/0/backend/console/{}/{}",
domid_string, id_string
);
let Some(frontend_path) = self
.store
.read_string(format!("{}/frontend", console_path))
.await?
else {
continue;
};
let Some(typ) = self
.store
.read_string(format!("{}/type", frontend_path))
.await?
else {
continue;
};
if typ != "krata-channel" {
continue;
}
let Some(ring_ref_string) = self
.store
.read_string(format!("{}/ring-ref", frontend_path))
.await?
else {
continue;
};
let Some(port_string) = self
.store
.read_string(format!("{}/port", frontend_path))
.await?
else {
continue;
};
let ring_ref = ring_ref_string.parse::<u64>()?;
let port = port_string.parse::<u32>()?;
let backend = KrataChannelBackend {
backend: console_path.clone(),
domid,
ring_ref,
port,
store: self.store.clone(),
evtchn: self.evtchn.clone(),
grant: GrantTab::open()?,
};
backend.init().await?;
self.backends.insert((domid, id), backend);
}
}
Ok(())
}
}
#[derive(Clone)]
pub struct KrataChannelBackend {
backend: String,
domid: u32,
ring_ref: u64,
port: u32,
store: XsdClient,
evtchn: EventChannel,
grant: GrantTab,
}
impl KrataChannelBackend {
pub async fn init(&self) -> Result<()> {
self.store.write_string(&self.backend, "4").await?;
Ok(())
}
pub async fn read(&self) -> Result<()> {
let memory = self.grant.map_grant_refs(
vec![GrantRef {
domid: self.domid,
reference: self.ring_ref as u32,
}],
true,
true,
)?;
let interface = memory.ptr() as *mut XenConsoleInterface;
let mut channel = self.evtchn.bind(self.domid, self.port).await?;
unsafe { self.read_buffer(channel.local_port, interface).await? };
loop {
channel.receiver.recv().await?;
unsafe { self.read_buffer(channel.local_port, interface).await? };
channel.unmask_sender.send(channel.local_port).await?;
}
}
async unsafe fn read_buffer(
&self,
local_port: u32,
interface: *mut XenConsoleInterface,
) -> Result<()> {
let mut cons = (*interface).out_cons;
let prod = (*interface).out_prod;
let size = prod - cons;
if size == 0 || size > 2048 {
return Ok(());
}
let mut data: Vec<u8> = Vec::new();
loop {
if cons == prod {
break;
}
data.push((*interface).output[cons as usize]);
cons += 1;
}
(*interface).out_cons = cons;
self.evtchn.notify(local_port).await?;
Ok(())
}
}

View File

@ -9,7 +9,7 @@ use krata::launchcfg::{
LaunchInfo, LaunchNetwork, LaunchNetworkIpv4, LaunchNetworkIpv6, LaunchNetworkResolver,
};
use uuid::Uuid;
use xenclient::{DomainConfig, DomainDisk, DomainNetworkInterface};
use xenclient::{DomainChannel, DomainConfig, DomainDisk, DomainNetworkInterface};
use xenstore::XsdInterface;
use crate::cfgblk::ConfigBlock;
@ -180,7 +180,10 @@ impl GuestLauncher {
writable: false,
},
],
consoles: vec![],
channels: vec![DomainChannel {
typ: "krata-channel".to_string(),
initialized: true,
}],
vifs: vec![DomainNetworkInterface {
mac: &guest_mac_string,
mtu: 1500,
@ -193,10 +196,10 @@ impl GuestLauncher {
extra_rw_paths: vec!["krata/guest".to_string()],
};
match context.xen.create(&config).await {
Ok(domid) => Ok(GuestInfo {
Ok(created) => Ok(GuestInfo {
name: request.name.map(|x| x.to_string()),
uuid,
domid,
domid: created.domid,
image: request.image.to_string(),
loops: vec![],
guest_ipv4: Some(IpNetwork::new(

View File

@ -26,6 +26,7 @@ use krataoci::cache::ImageCache;
pub mod autoloop;
pub mod cfgblk;
pub mod chan;
pub mod console;
pub mod launch;