backend.rs0.00%
1
// Copyright 2025 Google LLC2
//3
// Licensed under the Apache License, Version 2.0 (the "License");4
// you may not use this file except in compliance with the License.5
// You may obtain a copy of the License at6
//7
// https://www.apache.org/licenses/LICENSE-2.08
//9
// Unless required by applicable law or agreed to in writing, software10
// distributed under the License is distributed on an "AS IS" BASIS,11
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.12
// See the License for the specific language governing permissions and13
// limitations under the License.14
15
use std::cmp::min;16
use std::fs::File;17
use std::io::{ErrorKind, Write};18
use std::iter::zip;19
use std::os::fd::{AsFd, AsRawFd, BorrowedFd, OwnedFd};20
use std::os::unix::net::UnixStream;21
use std::sync::Arc;22
use std::sync::atomic::Ordering;23
24
use alioth_macros::trace_error;25
use snafu::Snafu;26
use zerocopy::IntoBytes;27
28
use crate::errors::DebugTrace;29
use crate::hv::IoeventFd;30
use crate::mem::mapped::{ArcMemPages, RamBus};31
use crate::virtio::dev::{StartParam, VirtioDevice, WakeEvent};32
use crate::virtio::vu::Error as VuError;33
use crate::virtio::vu::bindings::{34
MAX_CONFIG_SIZE, MemoryRegion, MemorySingleRegion, Message, VirtqAddr, VirtqState, VuFeature,35
VuFrontMsg,36
};37
use crate::virtio::vu::conn::{VuChannel, VuSession};38
use crate::virtio::{self, DevStatus, IrqSender, VirtioFeature};39
40
#[trace_error]41
#[derive(Snafu, DebugTrace)]42
#[snafu(module, context(suffix(false)))]43
pub enum Error {44
#[snafu(display("Error from OS"), context(false))]45
System { error: std::io::Error },46
#[snafu(display("Failed to access guest memory"), context(false))]47
Memory { source: Box<crate::mem::Error> },48
#[snafu(display("vhost-user protocol error"), context(false))]49
Vu {50
source: Box<crate::virtio::vu::Error>,51
},52
#[snafu(display("failed to parse the payload of {req:?}"))]53
Parse { req: VuFrontMsg },54
#[snafu(display("frontend requested invalid queue index: {index}"))]55
InvalidQueue { index: u16 },56
#[snafu(display("{req:?} did not contain an FD"))]57
MissingFd { req: VuFrontMsg },58
#[snafu(display("frontend did not set size for queue {index}"))]59
MissingSize { index: u16 },60
#[snafu(display("frontend did not set addresses for queue {index}"))]61
MissingAddr { index: u16 },62
#[snafu(display("frontend did not set ioeventfd for queue {index}"))]63
MissingIoeventfd { index: u16 },64
#[snafu(display("cannot convert frontend HVA {hva:#x} to GPA"))]65
Convert { hva: u64 },66
#[snafu(display("invalid message {req:?} with payload size {size}"))]67
InvalidMsg { req: VuFrontMsg, size: u32 },68
#[snafu(display("Cannot change memory layout at runtime"))]69
ChangeMemoryLayout,70
#[snafu(display("Failed to send backend request channel to device"))]71
SendChannel,72
}73
74
type Result<T, E = Error> = std::result::Result<T, E>;75
76
#[derive(Debug)]77
pub struct VuIrqSender {78
queues: Box<[Option<File>]>,79
}80
81
impl VuIrqSender {82
fn signal_irqfd(&self, mut fd: &File) {83
if let Err(e) = fd.write(1u64.as_bytes()) {84
log::error!("failed to signal irqfd: {e:?}");85
}86
}87
}88
89
impl IrqSender for VuIrqSender {90
fn config_irq(&self) {91
// TODO: investigate VHOST_USER_BACKEND_CONFIG_CHANGE_MSG92
log::error!("config irqfd is not available");93
}94
95
fn queue_irq(&self, idx: u16) {96
let Some(queue) = self.queues.get(idx as usize) else {97
log::error!("invalid queue index: {idx}");98
return;99
};100
let Some(fd) = queue.as_ref() else {101
log::error!("queue-{idx} irqfd is not available");102
return;103
};104
self.signal_irqfd(fd);105
}106
107
fn config_irqfd<F, T>(&self, _: F) -> virtio::Result<T>108
where109
F: FnOnce(BorrowedFd) -> virtio::Result<T>,110
{111
unreachable!()112
}113
114
fn queue_irqfd<F, T>(&self, _: u16, _: F) -> virtio::Result<T>115
where116
F: FnOnce(BorrowedFd) -> virtio::Result<T>,117
{118
unreachable!()119
}120
}121
122
#[derive(Debug)]123
pub struct VuEventfd {124
fd: File,125
}126
127
impl AsFd for VuEventfd {128
fn as_fd(&self) -> BorrowedFd<'_> {129
self.fd.as_fd()130
}131
}132
133
impl IoeventFd for VuEventfd {}134
135
#[derive(Debug, Default)]136
struct VuQueueInit {137
enable: bool,138
size: Option<u16>,139
addr: Option<VirtqAddr>,140
ioeventfd: Option<File>,141
irqfd: Option<File>,142
errfd: Option<File>,143
}144
145
#[derive(Debug)]146
struct VuInit {147
drv_feat: u64,148
queues: Box<[VuQueueInit]>,149
regions: Vec<MemoryRegion>,150
}151
152
pub struct VuBackend {153
session: VuSession,154
channel: Option<Arc<VuChannel>>,155
status: DevStatus,156
memory: Arc<RamBus>,157
dev: VirtioDevice<VuIrqSender, VuEventfd>,158
init: VuInit,159
}160
161
impl VuBackend {162
pub fn new(163
conn: UnixStream,164
dev: VirtioDevice<VuIrqSender, VuEventfd>,165
memory: Arc<RamBus>,166
) -> Result<Self> {167
conn.set_nonblocking(false)?;168
let queue_num = dev.queue_regs.len();169
Ok(VuBackend {170
session: VuSession { conn },171
channel: None,172
dev,173
memory,174
status: DevStatus::empty(),175
init: VuInit {176
drv_feat: 0,177
queues: (0..queue_num).map(|_| VuQueueInit::default()).collect(),178
regions: vec![],179
},180
})181
}182
183
pub fn name(&self) -> &str {184
self.dev.name.as_ref()185
}186
187
fn wake_up_dev(&self, event: WakeEvent<VuIrqSender, VuEventfd>) {188
let is_start = matches!(event, WakeEvent::Start { .. });189
if let Err(e) = self.dev.event_tx.send(event) {190
log::error!("{}: failed to send event: {e}", self.dev.name);191
return;192
}193
if is_start {194
return;195
}196
if let Err(e) = self.dev.notifier.notify() {197
log::error!("{}: failed to wake up device: {e}", self.dev.name);198
}199
}200
201
fn convert_frontend_hva(&self, hva: u64) -> Result<u64> {202
for r in &self.init.regions {203
if hva >= r.hva && hva < r.hva + r.size {204
return Ok(r.gpa + (hva - r.hva));205
}206
}207
error::Convert { hva }.fail()208
}209
210
fn parse_init(&mut self) -> Result<StartParam<VuIrqSender, VuEventfd>> {211
for (index, (param, queue)) in zip(&self.init.queues, &*self.dev.queue_regs).enumerate() {212
let index = index as u16;213
queue.enabled.store(param.enable, Ordering::Release);214
if !param.enable {215
continue;216
}217
218
let Some(size) = param.size else {219
return error::MissingSize { index }.fail();220
};221
queue.size.store(size, Ordering::Release);222
223
let Some(addr) = ¶m.addr else {224
return error::MissingAddr { index }.fail();225
};226
227
let desc_gpa = self.convert_frontend_hva(addr.desc_hva)?;228
queue.desc.store(desc_gpa, Ordering::Release);229
230
let dev_gpa = self.convert_frontend_hva(addr.used_hva)?;231
queue.device.store(dev_gpa, Ordering::Release);232
233
let drv_gpa = self.convert_frontend_hva(addr.avail_hva)?;234
queue.driver.store(drv_gpa, Ordering::Release);235
}236
237
let queues = &mut self.init.queues;238
239
let queue_irqfds = queues.iter_mut().map(|q| q.irqfd.take()).collect();240
let irq_sender = VuIrqSender {241
queues: queue_irqfds,242
};243
244
let mut ioeventfds = vec![];245
for (index, q) in queues.iter_mut().enumerate() {246
match q.ioeventfd.take() {247
Some(fd) => ioeventfds.push(VuEventfd { fd }),248
None => {249
let index = index as u16;250
return error::MissingIoeventfd { index }.fail();251
}252
}253
}254
255
Ok(StartParam {256
feature: self.init.drv_feat as u128,257
irq_sender: Arc::new(irq_sender),258
ioeventfds: Some(ioeventfds.into()),259
})260
}261
262
fn handle_msg(&mut self, msg: &mut Message, fds: &mut [Option<OwnedFd>; 8]) -> Result<()> {263
let name = &*self.dev.name;264
let (req, size) = (VuFrontMsg::from(msg.request), msg.size);265
266
match (req, size) {267
(VuFrontMsg::GET_PROTOCOL_FEATURES, 0) => {268
let feature = VuFeature::MQ269
| VuFeature::REPLY_ACK270
| VuFeature::CONFIGURE_MEM_SLOTS271
| VuFeature::BACKEND_REQ272
| VuFeature::BACKEND_SEND_FD273
| VuFeature::CONFIG274
| VuFeature::STATUS;275
self.session.reply(req, &feature.bits(), &[])?;276
msg.flag.set_need_reply(false);277
log::debug!("{name}: get protocol feature: {feature:x?}");278
}279
(VuFrontMsg::SET_PROTOCOL_FEATURES, 8) => {280
let feature: u64 = self.session.recv_payload()?;281
let feature = VuFeature::from_bits_retain(feature);282
log::debug!("{name}: set protocol feature: {feature:x?}");283
}284
(VuFrontMsg::GET_FEATURES, 0) => {285
let feature = self.dev.device_feature | VirtioFeature::VHOST_PROTOCOL.bits();286
self.session.reply(req, &(feature as u64), &[])?;287
msg.flag.set_need_reply(false);288
log::debug!("{name}: get device feature: {feature:#x}");289
}290
(VuFrontMsg::SET_FEATURES, 8) => {291
self.init.drv_feat = self.session.recv_payload()?;292
log::debug!("{name}: set driver feature: {:#x}", self.init.drv_feat);293
}294
(VuFrontMsg::SET_OWNER, 0) => {295
log::trace!("{name}: set owner");296
}297
(VuFrontMsg::GET_QUEUE_NUM, 0) => {298
let count = self.init.queues.len() as u64;299
self.session.reply(req, &count, &[])?;300
log::debug!("{name}: get queue number: {count}");301
msg.flag.set_need_reply(false);302
}303
(VuFrontMsg::SET_BACKEND_REQ_FD, 0) => {304
let Some(fd) = fds[0].take() else {305
return error::MissingFd { req }.fail()?;306
};307
log::trace!("{name}: set backend request fd: {}", fd.as_raw_fd());308
let channel = Arc::new(VuChannel {309
conn: UnixStream::from(fd),310
});311
let r = self.dev.event_tx.send(WakeEvent::VuChannel {312
channel: channel.clone(),313
});314
if r.is_err() {315
return error::SendChannel.fail();316
}317
self.channel = Some(channel);318
}319
(VuFrontMsg::SET_VIRTQ_ERR, 8) => {320
let index = self.session.recv_payload::<u64>()? as u16;321
let Some(fd) = fds[0].take() else {322
return error::MissingFd { req: msg.request }.fail();323
};324
let Some(q) = self.init.queues.get_mut(index as usize) else {325
return error::InvalidQueue { index }.fail();326
};327
log::debug!("{name}: queue-{index}: set error fd: {}", fd.as_raw_fd());328
q.errfd = Some(File::from(fd));329
}330
(VuFrontMsg::SET_VIRTQ_CALL, 8) => {331
let index = self.session.recv_payload::<u64>()? as u16;332
let Some(fd) = fds[0].take() else {333
return error::MissingFd { req: msg.request }.fail();334
};335
let Some(q) = self.init.queues.get_mut(index as usize) else {336
return error::InvalidQueue { index }.fail();337
};338
log::debug!("{name}: queue-{index}: set call fd: {}", fd.as_raw_fd());339
q.irqfd = Some(File::from(fd));340
}341
(VuFrontMsg::SET_VIRTQ_KICK, 8) => {342
let index = self.session.recv_payload::<u64>()? as u16;343
let Some(fd) = fds[0].take() else {344
return error::MissingFd { req: msg.request }.fail();345
};346
let Some(q) = self.init.queues.get_mut(index as usize) else {347
return error::InvalidQueue { index }.fail();348
};349
log::debug!("{name}: queue-{index}: set kick fd: {}", fd.as_raw_fd());350
q.ioeventfd = Some(File::from(fd));351
}352
(VuFrontMsg::SET_VIRTQ_NUM, 8) => {353
let virtq_num: VirtqState = self.session.recv_payload()?;354
let (index, size) = (virtq_num.index as u16, virtq_num.val as u16);355
let Some(q) = self.init.queues.get_mut(index as usize) else {356
return error::InvalidQueue { index }.fail();357
};358
q.size = Some(size);359
log::debug!("{name}: queue-{index}: set size: {size}");360
}361
(VuFrontMsg::SET_VIRTQ_BASE, 8) => {362
let virtq_base: VirtqState = self.session.recv_payload()?;363
let (index, base) = (virtq_base.index as u16, virtq_base.val);364
let Some(_q) = self.init.queues.get_mut(index as usize) else {365
return error::InvalidQueue { index }.fail();366
};367
log::warn!("{name}: queue-{index}: set base: {base}");368
}369
(VuFrontMsg::GET_VIRTQ_BASE, 8) => {370
let mut virtq_base: VirtqState = self.session.recv_payload()?;371
let (index, base) = (virtq_base.index as u16, virtq_base.val);372
let Some(_q) = self.init.queues.get_mut(index as usize) else {373
return error::InvalidQueue { index }.fail();374
};375
virtq_base.val = 0;376
self.session.reply(req, &virtq_base, &[])?;377
msg.flag.set_need_reply(false);378
log::warn!("{name}: queue-{index}: get base: {base}");379
}380
(VuFrontMsg::SET_VIRTQ_ADDR, 40) => {381
let virtq_addr: VirtqAddr = self.session.recv_payload()?;382
let index = virtq_addr.index as u16;383
let Some(q) = self.init.queues.get_mut(index as usize) else {384
return error::InvalidQueue { index }.fail();385
};386
log::debug!("{name}: queue-{index}: set addr: {virtq_addr:x?}");387
q.addr = Some(virtq_addr);388
}389
(VuFrontMsg::SET_VIRTQ_ENABLE, 8) => {390
let virtq_num: VirtqState = self.session.recv_payload()?;391
let (index, enabled) = (virtq_num.index as u16, virtq_num.val != 0);392
let Some(q) = self.init.queues.get_mut(index as usize) else {393
return error::InvalidQueue { index }.fail();394
};395
q.enable = enabled;396
log::debug!("{name}: queue-{index}: set enabled: {enabled}");397
}398
(VuFrontMsg::GET_MAX_MEM_SLOTS, 0) => {399
self.session.reply(req, &128u64, &[])?;400
msg.flag.set_need_reply(false);401
log::debug!("{name}: get max mem slots: 128");402
}403
(VuFrontMsg::ADD_MEM_REG, 40) => {404
let single: MemorySingleRegion = self.session.recv_payload()?;405
let Some(fd) = fds[0].take() else {406
return error::MissingFd { req: msg.request }.fail();407
};408
let region = &single.region;409
if self.status.contains(DevStatus::DRIVER_OK) {410
return error::ChangeMemoryLayout.fail();411
}412
log::debug!("{name}: add mem: {region:x?}, fd: {}", fd.as_raw_fd());413
let user_mem = ArcMemPages::from_file(414
File::from(fd),415
region.mmap_offset as i64,416
region.size as usize,417
libc::PROT_READ | libc::PROT_WRITE,418
)?;419
self.memory.add(region.gpa, user_mem)?;420
self.init.regions.push(single.region);421
}422
(VuFrontMsg::REM_MEM_REG, 40) => {423
let single: MemorySingleRegion = self.session.recv_payload()?;424
let region = &single.region;425
if self.status.contains(DevStatus::DRIVER_OK) {426
return error::ChangeMemoryLayout.fail();427
}428
for (index, r) in self.init.regions.iter().enumerate() {429
if r.gpa == region.gpa && r.hva == region.hva && r.size == region.size {430
log::info!("{name}: remove mem: {r:x?}");431
self.init.regions.remove(index);432
let _ = self.memory.remove(region.gpa);433
break;434
}435
}436
}437
(VuFrontMsg::GET_STATUS, 0) => {438
let status = self.status.bits() as u64;439
self.session.reply(req, &status, &[])?;440
msg.flag.set_need_reply(false);441
log::debug!("{name}: get status: {status:x?}");442
}443
(VuFrontMsg::SET_STATUS, 8) => {444
let status: u64 = self.session.recv_payload()?;445
let new = DevStatus::from_bits_retain(status as u8);446
let old = self.status;447
self.status = new;448
log::debug!("{name}: set status: {old:x?} -> {new:x?}");449
if (old ^ new).contains(DevStatus::DRIVER_OK) {450
let event = if new.contains(DevStatus::DRIVER_OK) {451
let param = self.parse_init()?;452
WakeEvent::Start { param }453
} else {454
WakeEvent::Reset455
};456
self.wake_up_dev(event);457
}458
}459
(VuFrontMsg::GET_CONFIG, 12..) => {460
let mut region = [0u8; MAX_CONFIG_SIZE];461
let dev_config = self.session.recv_config(&mut region)?;462
let mut done = 0;463
while let Some(n) = (dev_config.size as usize - done).checked_ilog2() {464
let size = min(1 << n, 8) as u8;465
let offset = dev_config.offset as u64 + done as u64;466
let v = self.dev.device_config.read(offset, size)?;467
region[done..(done + size as usize)]468
.copy_from_slice(&v.as_bytes()[..size as usize]);469
done += size as usize;470
}471
self.session.reply_config(&dev_config, ®ion[..done])?;472
log::debug!("{name}: get config: {dev_config:?}");473
msg.flag.set_need_reply(false);474
}475
(VuFrontMsg::SET_CONFIG, 12..) => {476
let mut region = [0u8; MAX_CONFIG_SIZE];477
let dev_config = self.session.recv_config(&mut region)?;478
let mut done = 0;479
while let Some(n) = (dev_config.size as usize - done).checked_ilog2() {480
let size = min(1 << n, 8) as u8;481
let mut v = 0;482
v.as_mut_bytes()[..size as usize]483
.copy_from_slice(®ion[done..(done + size as usize)]);484
let offset = dev_config.offset as u64 + done as u64;485
self.dev.device_config.write(offset, size, v)?;486
done += size as usize;487
}488
log::debug!("{name}: set config: {dev_config:?}");489
}490
_ => return error::InvalidMsg { req, size }.fail(),491
}492
Ok(())493
}494
495
pub fn run(&mut self) -> Result<()> {496
let mut fds = [const { None }; 8];497
loop {498
let msg = self.session.recv_msg(&mut fds);499
match msg {500
Ok(mut msg) => {501
let ret = self.handle_msg(&mut msg, &mut fds);502
if let Err(e) = &ret {503
let name = &*self.dev.name;504
log::error!("{name}: cannot handle message {:#x}: {e:?}", msg.request);505
}506
let req = VuFrontMsg::from(msg.request);507
if msg.flag.need_reply() {508
let code = if ret.is_ok() { 0 } else { u64::MAX };509
self.session.reply(req, &code, &[])?;510
}511
}512
Err(VuError::System { error, .. })513
if error.kind() == ErrorKind::ConnectionAborted =>514
{515
break;516
}517
Err(e) => return Err(e)?,518
}519
}520
Ok(())521
}522
}523