|
2 | 2 |
|
3 | 3 | ## Design |
4 | 4 |
|
5 | | -This crate provides convenient abstractions for implementing `vhost-user` device server backends: |
6 | | - |
7 | | -- A vhost-user backend trait (`VhostUserBackend`) |
8 | | -- A public API for the backend to interact with (`VhostUserDaemon`) |
9 | | -- An structure including virtio queue related elements (`Vring`) |
10 | | -- A worker for receiving queue events and forwarding them to the backend. |
| 5 | +The `vhost-user-backend` crate provides a framework to implement `vhost-user` backend services, |
| 6 | +which includes following external public APIs: |
| 7 | +- A daemon control object (`VhostUserDaemon`) to start and stop the service daemon. |
| 8 | +- A vhost-user backend trait (`VhostUserBackendMut`) to handle vhost-user control messages and virtio |
| 9 | + messages. |
| 10 | +- A vring access trait (`VringT`) to access virtio queues, and three implementations of the trait: |
| 11 | + `VringState`, `VringMutex` and `VringRwLock`. |
11 | 12 |
|
12 | 13 | ## Usage |
| 14 | +The `vhost-user-backend` crate provides a framework to implement vhost-user backend services. The main interface provided by `vhost-user-backend` library is the `struct VhostUserDaemon`: |
| 15 | +```rust |
| 16 | +pub struct VhostUserDaemon<S, V, B = ()> |
| 17 | +where |
| 18 | + S: VhostUserBackend<V, B>, |
| 19 | + V: VringT<GM<B>> + Clone + Send + Sync + 'static, |
| 20 | + B: Bitmap + 'static, |
| 21 | +{ |
| 22 | + pub fn new(name: String, backend: S, atomic_mem: GuestMemoryAtomic<GuestMemoryMmap<B>>) -> Result<Self>; |
| 23 | + pub fn start(&mut self, listener: Listener) -> Result<()>; |
| 24 | + pub fn wait(&mut self) -> Result<()>; |
| 25 | + pub fn get_epoll_handlers(&self) -> Vec<Arc<VringEpollHandler<S, V, B>>>; |
| 26 | +} |
| 27 | +``` |
| 28 | + |
| 29 | +### Create a `VhostUserDaemon` Instance |
| 30 | +The `VhostUserDaemon::new()` creates an instance of `VhostUserDaemon` object. The client needs to |
| 31 | +pass in an `VhostUserBackend` object, which will be used to configure the `VhostUserDaemon` |
| 32 | +instance, handle control messages from the vhost-user master and handle virtio requests from |
| 33 | +virtio queues. A group of working threads will be created to handle virtio requests from configured |
| 34 | +virtio queues. |
| 35 | + |
| 36 | +### Start the `VhostUserDaemon` |
| 37 | +The `VhostUserDaemon::start()` method waits for an incoming connection from the vhost-user masters |
| 38 | +on the `listener`. Once a connection is ready, a main thread will be created to handle vhost-user |
| 39 | +messages from the vhost-user master. |
| 40 | + |
| 41 | +### Stop the `VhostUserDaemon` |
| 42 | +The `VhostUserDaemon::stop()` method waits for the main thread to exit. An exit event must be sent |
| 43 | +to the main thread by writing to the `exit_event` EventFd before waiting for it to exit. |
| 44 | + |
| 45 | +### Threading Model |
| 46 | +The main thread and virtio queue working threads will concurrently access the underlying virtio |
| 47 | +queues, so all virtio queue in multi-threading model. But the main thread only accesses virtio |
| 48 | +queues for configuration, so client could adopt locking policies to optimize for the virtio queue |
| 49 | +working threads. |
| 50 | + |
| 51 | +## Example |
| 52 | +Example code to handle virtio messages from a virtio queue: |
| 53 | +```rust |
| 54 | +impl VhostUserBackendMut for VhostUserService { |
| 55 | + fn process_queue(&mut self, vring: &VringMutex) -> Result<bool> { |
| 56 | + let mut used_any = false; |
| 57 | + let mem = match &self.mem { |
| 58 | + Some(m) => m.memory(), |
| 59 | + None => return Err(Error::NoMemoryConfigured), |
| 60 | + }; |
| 61 | + |
| 62 | + let mut vring_state = vring.get_mut(); |
| 63 | + |
| 64 | + while let Some(avail_desc) = vring_state |
| 65 | + .get_queue_mut() |
| 66 | + .iter() |
| 67 | + .map_err(|_| Error::IterateQueue)? |
| 68 | + .next() |
| 69 | + { |
| 70 | + // Process the request... |
| 71 | + |
| 72 | + if self.event_idx { |
| 73 | + if vring_state.add_used(head_index, 0).is_err() { |
| 74 | + warn!("Couldn't return used descriptors to the ring"); |
| 75 | + } |
| 76 | + |
| 77 | + match vring_state.needs_notification() { |
| 78 | + Err(_) => { |
| 79 | + warn!("Couldn't check if queue needs to be notified"); |
| 80 | + vring_state.signal_used_queue().unwrap(); |
| 81 | + } |
| 82 | + Ok(needs_notification) => { |
| 83 | + if needs_notification { |
| 84 | + vring_state.signal_used_queue().unwrap(); |
| 85 | + } |
| 86 | + } |
| 87 | + } |
| 88 | + } else { |
| 89 | + if vring_state.add_used(head_index, 0).is_err() { |
| 90 | + warn!("Couldn't return used descriptors to the ring"); |
| 91 | + } |
| 92 | + vring_state.signal_used_queue().unwrap(); |
| 93 | + } |
| 94 | + } |
| 95 | + |
| 96 | + Ok(used_any) |
| 97 | + } |
| 98 | +} |
| 99 | +``` |
| 100 | + |
| 101 | +## License |
| 102 | + |
| 103 | +This project is licensed under |
13 | 104 |
|
14 | | -Users of this create are expected to implement the `VhostUserBackend` trait and to initialize the execution context by instantiating `VhostUserDaemon` and calling to its `start` method. |
| 105 | +- [Apache License](http://www.apache.org/licenses/LICENSE-2.0), Version 2.0 |
0 commit comments