vmbus_channel/
lib.rs

1// Copyright (c) Microsoft Corporation.
2// Licensed under the MIT License.
3
4//! Vmbus channel offer support and related functionality.
5
6#![forbid(unsafe_code)]
7
8pub mod bus;
9pub mod channel;
10pub mod gpadl;
11pub mod gpadl_ring;
12pub mod offer;
13pub mod resources;
14pub mod simple;
15
16use std::sync::Arc;
17use std::sync::atomic::AtomicBool;
18use std::sync::atomic::Ordering;
19use std::task::Context;
20use std::task::Poll;
21use vmbus_ring::FlatRingMem;
22use vmbus_ring::IncomingRing;
23use vmbus_ring::OutgoingRing;
24use vmbus_ring::RingMem;
25use vmcore::slim_event::SlimEvent;
26
27/// An object to use to communicate notifications with the remote endpoint of a
28/// vmbus channel.
29///
30/// When dropped, this may (or may not) signal to the remote endpoint that the
31/// channel is being closed.
32pub trait SignalVmbusChannel: Send + Sync {
33    /// Signals the remote endpoint that there is work to do: either the
34    /// outgoing ring buffer has transitioned from empty to non-empty, or the
35    /// incoming ring buffer now has enough space for the remote endpoint's
36    /// pending send.
37    fn signal_remote(&self);
38
39    /// Poll the channel for a ring buffer signal or for being closed.
40    ///
41    /// If the remote endpoint has closed the channel, then this can return
42    /// [`ChannelClosed`].
43    fn poll_for_signal(&self, cx: &mut Context<'_>) -> Poll<Result<(), ChannelClosed>>;
44}
45
46/// Error returned from [`SignalVmbusChannel::poll_for_signal`] indicating the channel
47/// has been closed by the other endpoint.
48pub struct ChannelClosed;
49
50/// Creates a pair of connected channels. Useful for testing.
51pub fn connected_async_channels(
52    ring_size: usize,
53) -> (RawAsyncChannel<FlatRingMem>, RawAsyncChannel<FlatRingMem>) {
54    #[derive(Default)]
55    struct EventWithDoneInner {
56        event: SlimEvent,
57        done: AtomicBool,
58    }
59
60    struct SignalInProc {
61        local: Arc<EventWithDoneInner>,
62        remote: Arc<EventWithDoneInner>,
63        close_on_drop: bool,
64    }
65
66    impl SignalVmbusChannel for SignalInProc {
67        fn signal_remote(&self) {
68            self.remote.event.signal();
69        }
70
71        fn poll_for_signal(&self, cx: &mut Context<'_>) -> Poll<Result<(), ChannelClosed>> {
72            if self.local.done.load(Ordering::Relaxed) {
73                return Err(ChannelClosed).into();
74            }
75            self.local.event.poll_wait(cx).map(Ok)
76        }
77    }
78
79    impl Drop for SignalInProc {
80        fn drop(&mut self) {
81            if self.close_on_drop {
82                self.remote.done.store(true, Ordering::Relaxed);
83                self.remote.event.signal();
84            }
85        }
86    }
87
88    let host_notify = SignalInProc {
89        local: Default::default(),
90        remote: Default::default(),
91        close_on_drop: false,
92    };
93    let guest_notify = SignalInProc {
94        local: host_notify.remote.clone(),
95        remote: host_notify.local.clone(),
96        close_on_drop: true,
97    };
98    let (host_in_ring, guest_out_ring) = make_ring_pair(ring_size);
99    let (guest_in_ring, host_out_ring) = make_ring_pair(ring_size);
100
101    let host = RawAsyncChannel {
102        in_ring: host_in_ring,
103        out_ring: host_out_ring,
104        signal: Box::new(host_notify),
105    };
106    let guest = RawAsyncChannel {
107        in_ring: guest_in_ring,
108        out_ring: guest_out_ring,
109        signal: Box::new(guest_notify),
110    };
111    (host, guest)
112}
113
114fn make_ring_pair(size: usize) -> (IncomingRing<FlatRingMem>, OutgoingRing<FlatRingMem>) {
115    let flat_mem = FlatRingMem::new(size);
116    let ring1 = IncomingRing::new(flat_mem.clone()).unwrap();
117    let ring2 = OutgoingRing::new(flat_mem).unwrap();
118    (ring1, ring2)
119}
120
121/// The resources for a bidirectional vmbus channel.
122pub struct RawAsyncChannel<M: RingMem> {
123    /// The incoming ring buffer.
124    pub in_ring: IncomingRing<M>,
125    /// The outgoing ring buffer.
126    pub out_ring: OutgoingRing<M>,
127    /// The object to use to communicate notifications with the remote endpoint.
128    pub signal: Box<dyn SignalVmbusChannel>,
129}