• Home
  • Features
  • Pricing
  • Docs
  • Announcements
  • Sign In

google / alioth / 17114607742

21 Aug 2025 01:31AM UTC coverage: 10.411%. Remained the same
17114607742

Pull #273

github

web-flow
Merge 897d3fdbf into 7925c9625
Pull Request #273: feat: virtio packed queue

30 of 57 new or added lines in 9 files covered. (52.63%)

50 existing lines in 4 files now uncovered.

714 of 6858 relevant lines covered (10.41%)

16.1 hits per line

Source File
Press 'n' to go to next uncovered line, 'b' for previous

0.0
/alioth/src/virtio/dev/fs/fs.rs
1
// Copyright 2025 Google LLC
2
//
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 at
6
//
7
//     https://www.apache.org/licenses/LICENSE-2.0
8
//
9
// Unless required by applicable law or agreed to in writing, software
10
// 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 and
13
// limitations under the License.
14

15
pub mod shared_dir;
16
#[cfg(target_os = "linux")]
17
pub mod vu;
18

19
use std::fmt::Debug;
20
use std::fs::File;
21
use std::io::{self, IoSlice, IoSliceMut, Read};
22
use std::os::fd::AsRawFd;
23
use std::sync::Arc;
24
use std::sync::mpsc::Receiver;
25
use std::thread::JoinHandle;
26

27
use bitflags::bitflags;
28
use mio::Registry;
29
use mio::event::Event;
30
use zerocopy::{FromBytes, Immutable, IntoBytes, KnownLayout};
31

32
use crate::fuse::bindings::{FuseInHeader, FuseOpcode, FuseOutHeader, FuseSetupmappingFlag};
33
use crate::fuse::{self, DaxRegion, Fuse};
34
use crate::hv::IoeventFd;
35
use crate::mem::mapped::{ArcMemPages, RamBus};
36
use crate::mem::{MemRegion, MemRegionType};
37
#[cfg(target_os = "linux")]
38
use crate::virtio::dev::fs::vu::VuDaxRegion;
39
use crate::virtio::dev::{Result, Virtio, WakeEvent};
40
use crate::virtio::queue::{Descriptor, Queue, VirtQueue};
41
#[cfg(target_os = "linux")]
42
use crate::virtio::vu::conn::VuChannel;
43
use crate::virtio::worker::Waker;
44
use crate::virtio::worker::mio::{ActiveMio, Mio, VirtioMio};
45
use crate::virtio::{DeviceId, FEATURE_BUILT_IN, IrqSender};
46
use crate::{ffi, impl_mmio_for_zerocopy};
47

48
impl DaxRegion for ArcMemPages {
49
    fn map(
×
50
        &self,
51
        m_offset: u64,
52
        fd: &File,
53
        f_offset: u64,
54
        len: u64,
55
        flag: FuseSetupmappingFlag,
56
    ) -> fuse::Result<()> {
57
        let fd = fd.as_raw_fd();
×
58

59
        let map_addr = self.addr() + m_offset as usize;
×
60

61
        let mut prot = 0;
×
62
        if flag.contains(FuseSetupmappingFlag::READ) {
×
63
            prot |= libc::PROT_READ;
×
64
        };
65
        if flag.contains(FuseSetupmappingFlag::WRITE) {
×
66
            prot |= libc::PROT_WRITE;
×
67
        }
68

69
        ffi!(
×
70
            unsafe {
71
                libc::mmap(
×
72
                    map_addr as _,
73
                    len as usize,
74
                    prot,
×
75
                    libc::MAP_SHARED | libc::MAP_FIXED,
76
                    fd,
77
                    f_offset as _,
78
                )
79
            },
80
            libc::MAP_FAILED
81
        )?;
82

83
        Ok(())
×
84
    }
85

86
    fn unmap(&self, m_offset: u64, len: u64) -> fuse::Result<()> {
×
87
        let map_addr = self.addr() + m_offset as usize;
×
88
        let flags = libc::MAP_ANONYMOUS | libc::MAP_PRIVATE | libc::MAP_FIXED;
×
89
        ffi!(
×
90
            unsafe { libc::mmap(map_addr as _, len as _, libc::PROT_NONE, flags, -1, 0) },
×
91
            libc::MAP_FAILED
92
        )?;
93

94
        Ok(())
×
95
    }
96
}
97

98
#[repr(C, align(4))]
99
#[derive(Debug, FromBytes, Immutable, IntoBytes)]
100
pub struct FsConfig {
101
    pub tag: [u8; 36],
102
    pub num_request_queues: u32,
103
    pub notify_buf_size: u32,
104
}
105

106
impl_mmio_for_zerocopy!(FsConfig);
107

108
bitflags! {
109
    #[derive(Default, Debug, Clone, Copy, PartialEq, Eq, Hash)]
110
    pub struct FsFeature: u128 {
111
        const NOTIFICATION = 1 << 0;
112
    }
113
}
114

115
#[derive(Debug)]
116
pub struct Fs<F> {
117
    name: Arc<str>,
118
    config: Arc<FsConfig>,
119
    fuse: F,
120
    feature: FsFeature,
121
    driver_feature: FsFeature,
122
    dax_region: Option<ArcMemPages>,
123
}
124

125
impl<F> Fs<F>
126
where
127
    F: Fuse,
128
{
129
    pub fn new(
×
130
        name: impl Into<Arc<str>>,
131
        mut fuse: F,
132
        config: FsConfig,
133
        dax_window: usize,
134
    ) -> Result<Self> {
135
        let mut feature = FsFeature::empty();
×
136
        if config.notify_buf_size > 0 {
×
137
            feature |= FsFeature::NOTIFICATION;
×
138
        }
139
        let mut dax_region = None;
×
140
        if dax_window > 0 {
×
141
            let prot = Some(libc::PROT_NONE);
×
142
            let region = ArcMemPages::from_anonymous(dax_window, prot, None)?;
×
143
            fuse.set_dax_region(Box::new(region.clone()));
×
144
            dax_region = Some(region);
×
145
        };
146
        Ok(Fs {
×
147
            name: name.into(),
×
148
            config: Arc::new(config),
×
149
            fuse,
×
150
            feature,
×
151
            driver_feature: FsFeature::empty(),
×
152
            dax_region,
×
153
        })
154
    }
155

156
    fn handle_msg(
×
157
        &mut self,
158
        hdr: &FuseInHeader,
159
        in_: &[IoSlice],
160
        out: &mut [IoSliceMut],
161
    ) -> fuse::Result<usize> {
162
        let name = &*self.name;
×
163
        let opcode = hdr.opcode;
×
164

165
        fn parse_in<'a, T>(bufs: &'a [IoSlice<'a>]) -> fuse::Result<(&'a T, &'a [u8])>
×
166
        where
167
            T: FromBytes + KnownLayout + Immutable,
168
        {
169
            let [buf] = bufs else {
×
170
                return Err(io::Error::from_raw_os_error(libc::EINVAL))?;
×
171
            };
172
            match T::ref_from_prefix(buf) {
×
173
                Ok((r, buf)) => Ok((r, buf)),
×
174
                Err(_) => Err(io::Error::from_raw_os_error(libc::EINVAL))?,
×
175
            }
176
        }
177

178
        fn parse_in_iov<'a, T>(bufs: &'a [IoSlice<'a>]) -> fuse::Result<(&'a T, &'a [IoSlice<'a>])>
×
179
        where
180
            T: FromBytes + KnownLayout + Immutable,
181
        {
182
            let [h, bufs @ ..] = bufs else {
×
183
                return Err(io::Error::from_raw_os_error(libc::EINVAL))?;
×
184
            };
185
            match T::ref_from_bytes(h) {
×
186
                Ok(r) => Ok((r, bufs)),
×
187
                Err(_) => Err(io::Error::from_raw_os_error(libc::EINVAL))?,
×
188
            }
189
        }
190

191
        macro_rules! opcode_branch {
×
192
            ($func:ident, &[u8],_) => {{
×
193
                let [in_] = in_ else {
×
194
                    return Err(io::Error::from_raw_os_error(libc::EINVAL))?;
×
195
                };
196
                let ret = self.fuse.$func(hdr, in_)?;
×
197
                let size = ret.as_bytes().read_vectored(out)?;
×
198
                let in_s = String::from_utf8_lossy(in_);
×
199
                log::trace!("{name}: {opcode:?}\n{in_s:?}\n{ret:x?}");
×
200
                Ok(size)
×
201
            }};
202
            ($func:ident, &[u8], &mut[u8]) => {{
×
203
                let ([in_], [out]) = (in_, out) else {
×
204
                    return Err(io::Error::from_raw_os_error(libc::EINVAL))?;
×
205
                };
206
                let size = self.fuse.$func(hdr, in_, out)?;
×
207
                let in_s = String::from_utf8_lossy(in_);
×
208
                log::trace!("{name}: {opcode:?}\n{in_s:?}\nsize = {size:?}",);
×
209
                Ok(size)
×
210
            }};
211
            ($func:ident, &_, &mut[u8]) => {{
×
212
                let [out] = out else {
×
213
                    return Err(io::Error::from_raw_os_error(libc::EINVAL))?;
×
214
                };
215
                let (in_, _) = parse_in(in_)?;
×
216
                let size = self.fuse.$func(hdr, in_, out)?;
×
217
                log::trace!("{name}: {opcode:?}\n{in_:x?}\nsize = {size}");
×
218
                Ok(size)
×
219
            }};
220
            ($func:ident, &_, &mut[IoSliceMut]) => {{
×
221
                let (in_, _) = parse_in(in_)?;
×
222
                let size = self.fuse.$func(hdr, in_, out)?;
×
223
                log::trace!("{name}: {opcode:?}\n{in_:x?}\nsize = {size}");
×
224
                Ok(size)
×
225
            }};
226
            ($func:ident, &_,_) => {{
×
227
                let (in_, _) = parse_in(in_)?;
×
228
                let ret = self.fuse.$func(hdr, in_)?;
×
229
                let size = ret.as_bytes().read_vectored(out)?;
×
230
                log::trace!("{name}: {opcode:?}\n{in_:x?}\n{ret:x?}");
×
231
                Ok(size)
×
232
            }};
233
            ($func:ident, &_, &[u8],_) => {{
×
234
                let (in_, buf) = parse_in(in_)?;
×
235
                let ret = self.fuse.$func(hdr, in_, buf)?;
×
236
                let size = ret.as_bytes().read_vectored(out)?;
×
237
                log::trace!("{name}: {opcode:?}\n{in_:x?}\n{ret:x?}");
×
238
                Ok(size)
×
239
            }};
240
            ($func:ident, &_, &[IoSlice],_) => {{
×
241
                let (in_, bufs) = parse_in_iov(in_)?;
×
242
                let ret = self.fuse.$func(hdr, in_, bufs)?;
×
243
                let size = ret.as_bytes().read_vectored(out)?;
×
244
                log::trace!("{name}: {opcode:?}\n{in_:x?}\n{ret:x?}");
×
245
                Ok(size)
×
246
            }};
247
        }
248
        match opcode {
×
249
            FuseOpcode::INIT => opcode_branch!(init, &_, _),
×
250
            FuseOpcode::GETATTR => opcode_branch!(get_attr, &_, _),
×
251
            FuseOpcode::OPEN => opcode_branch!(open, &_, _),
×
252
            FuseOpcode::OPENDIR => opcode_branch!(open_dir, &_, _),
×
253
            FuseOpcode::READDIR => opcode_branch!(read_dir, &_, &mut [u8]),
×
254
            FuseOpcode::RELEASEDIR => opcode_branch!(release_dir, &_, _),
×
255
            FuseOpcode::LOOKUP => opcode_branch!(lookup, &[u8], _),
×
256
            FuseOpcode::FORGET => opcode_branch!(forget, &_, _),
×
257
            FuseOpcode::POLL => opcode_branch!(poll, &_, _),
×
258
            FuseOpcode::READ => opcode_branch!(read, &_, &mut [IoSliceMut]),
×
259
            FuseOpcode::FLUSH => opcode_branch!(flush, &_, _),
×
260
            FuseOpcode::RELEASE => opcode_branch!(release, &_, _),
×
261
            FuseOpcode::SYNCFS => opcode_branch!(syncfs, &_, _),
×
262
            FuseOpcode::IOCTL => opcode_branch!(ioctl, &_, _),
×
263
            FuseOpcode::GETXATTR => opcode_branch!(get_xattr, &[u8], &mut [u8]),
×
264
            FuseOpcode::SETXATTR => opcode_branch!(set_xattr, &[u8], _),
×
265
            FuseOpcode::CREATE => opcode_branch!(create, &_, &[u8], _),
×
266
            FuseOpcode::UNLINK => opcode_branch!(unlink, &[u8], _),
×
267
            FuseOpcode::RMDIR => opcode_branch!(rmdir, &[u8], _),
×
268
            FuseOpcode::RENAME => opcode_branch!(rename, &_, &[u8], _),
×
269
            FuseOpcode::WRITE => opcode_branch!(write, &_, &[IoSlice], _),
×
270
            FuseOpcode::RENAME2 => opcode_branch!(rename2, &_, &[u8], _),
×
271
            FuseOpcode::SETUPMAPPING => opcode_branch!(setup_mapping, &_, _),
×
272
            FuseOpcode::REMOVEMAPPING => opcode_branch!(remove_mapping, &[u8], _),
×
273
            _ => Err(io::Error::from_raw_os_error(libc::ENOSYS))?,
×
274
        }
275
    }
276

NEW
277
    fn handle_desc(&mut self, desc: &mut Descriptor, _registry: &Registry) -> Result<usize> {
×
278
        let name = &*self.name;
×
279

280
        let (hdr_out, out) = match &mut desc.writable[..] {
×
281
            [] => (None, &mut [] as &mut _),
×
282
            [hdr, out @ ..] => {
×
283
                let Ok(hdr) = FuseOutHeader::mut_from_bytes(hdr) else {
×
284
                    log::error!("{name}: cannot parse FuseOutHeader");
×
285
                    return Ok(0);
×
286
                };
287
                (Some(hdr), out)
×
288
            }
289
        };
290

291
        let Some((hdr_in, mut in_)) = desc.readable.split_first() else {
×
292
            log::error!("{name}: cannot find opcode");
×
293
            return Ok(0);
×
294
        };
295

296
        let Ok((hdr_in, tail)) = FuseInHeader::ref_from_prefix(hdr_in) else {
×
297
            log::error!("{name}: cannot parse FuseInHeader");
×
298
            return Ok(0);
×
299
        };
300
        let opcode = hdr_in.opcode;
×
301

302
        let tails = [IoSlice::new(tail)];
×
303
        if !tail.is_empty() {
×
304
            if !in_.is_empty() {
×
305
                let len = tail.len();
×
306
                log::error!("{name}: {opcode:?}: cannot handle {len} bytes after header");
×
307
                return Ok(0);
×
308
            }
309
            in_ = &tails;
×
310
        }
311

312
        log::trace!("{name}: {opcode:?}, nodeid = {:#x}", hdr_in.nodeid);
×
313

314
        let ret = self.handle_msg(hdr_in, in_, out);
×
315
        if let Err(e) = &ret {
×
316
            log::error!("{}: {opcode:?}: {e:?}", self.name);
×
317
        };
318

319
        let Some(hdr_out) = hdr_out else {
×
320
            return Ok(0);
×
321
        };
322
        hdr_out.unique = hdr_in.unique;
×
323
        match ret {
×
324
            Ok(size) => {
×
325
                hdr_out.error = 0;
×
326
                hdr_out.len = (size + size_of_val(hdr_out)) as u32;
×
327
            }
328
            Err(e) => {
×
329
                hdr_out.error = -e.error_code();
×
330
                hdr_out.len = size_of_val(hdr_out) as u32;
×
331
            }
332
        }
333
        Ok(hdr_out.len as usize)
×
334
    }
335
}
336

337
impl<F> VirtioMio for Fs<F>
338
where
339
    F: Fuse + Debug + Send + Sync + 'static,
340
{
341
    fn activate<'a, 'm, Q, S, E>(
×
342
        &mut self,
343
        feature: u128,
344
        _active_mio: &mut ActiveMio<'a, 'm, Q, S, E>,
345
    ) -> Result<()>
346
    where
347
        Q: VirtQueue<'m>,
348
        S: IrqSender,
349
        E: IoeventFd,
350
    {
351
        self.driver_feature = FsFeature::from_bits_retain(feature);
×
352
        Ok(())
×
353
    }
354

355
    fn handle_event<'a, 'm, Q, S, E>(
356
        &mut self,
357
        _event: &Event,
358
        _active_mio: &mut ActiveMio<'a, 'm, Q, S, E>,
359
    ) -> Result<()>
360
    where
361
        Q: VirtQueue<'m>,
362
        S: IrqSender,
363
        E: IoeventFd,
364
    {
365
        unreachable!()
366
    }
367

368
    fn handle_queue<'a, 'm, Q, S, E>(
×
369
        &mut self,
370
        index: u16,
371
        active_mio: &mut ActiveMio<'a, 'm, Q, S, E>,
372
    ) -> Result<()>
373
    where
374
        Q: VirtQueue<'m>,
375
        S: IrqSender,
376
        E: IoeventFd,
377
    {
378
        let Some(Some(queue)) = active_mio.queues.get_mut(index as usize) else {
×
379
            log::error!("{}: invalid queue index {index}", self.name);
×
380
            return Ok(());
×
381
        };
382
        if self.feature.contains(FsFeature::NOTIFICATION) && index == 1 {
×
383
            todo!("handle notification queue");
384
        }
385
        let irq_sender = active_mio.irq_sender;
×
386
        let registry = active_mio.poll.registry();
×
NEW
387
        queue.handle_desc(index, irq_sender, |desc| {
×
NEW
388
            let len = self.handle_desc(desc, registry)?;
×
UNCOV
389
            Ok(Some(len))
×
390
        })
391
    }
392

393
    fn reset(&mut self, _registry: &Registry) {}
×
394
}
395

396
impl<F> Virtio for Fs<F>
397
where
398
    F: Fuse + Debug + Send + Sync + 'static,
399
{
400
    type Config = FsConfig;
401
    type Feature = FsFeature;
402

403
    fn id(&self) -> DeviceId {
×
404
        DeviceId::FileSystem
×
405
    }
406

407
    fn name(&self) -> &str {
×
408
        &self.name
×
409
    }
410

411
    fn feature(&self) -> u128 {
×
412
        self.feature.bits() | FEATURE_BUILT_IN
×
413
    }
414

415
    fn num_queues(&self) -> u16 {
×
416
        let mut count = 1; // high priority queue
×
417
        if self.feature.contains(FsFeature::NOTIFICATION) {
×
418
            count += 1;
×
419
        }
420
        count + self.config.num_request_queues as u16 * 2
×
421
    }
422

423
    fn config(&self) -> Arc<FsConfig> {
×
424
        self.config.clone()
×
425
    }
426

427
    fn spawn_worker<S, E>(
×
428
        self,
429
        event_rx: Receiver<WakeEvent<S, E>>,
430
        memory: Arc<RamBus>,
431
        queue_regs: Arc<[Queue]>,
432
    ) -> Result<(JoinHandle<()>, Arc<Waker>)>
433
    where
434
        S: IrqSender,
435
        E: IoeventFd,
436
    {
437
        Mio::spawn_worker(self, event_rx, memory, queue_regs)
×
438
    }
439

440
    fn shared_mem_regions(&self) -> Option<Arc<MemRegion>> {
×
441
        let dax_region = self.dax_region.as_ref()?;
×
442
        Some(Arc::new(MemRegion::with_dev_mem(
×
443
            dax_region.clone(),
×
444
            MemRegionType::Hidden,
×
445
        )))
446
    }
447

448
    #[cfg(target_os = "linux")]
449
    fn set_vu_channel(&mut self, channel: Arc<VuChannel>) {
×
450
        let vu_dax_region = VuDaxRegion { channel };
×
451
        self.fuse.set_dax_region(Box::new(vu_dax_region));
×
452
    }
453
}
STATUS · Troubleshooting · Open an Issue · Sales · Support · CAREERS · ENTERPRISE · START FREE · SCHEDULE DEMO
ANNOUNCEMENTS · TWITTER · TOS & SLA · Supported CI Services · What's a CI service? · Automated Testing

© 2025 Coveralls, Inc