netvsp/
buffers.rs

1// Copyright (c) Microsoft Corporation.
2// Licensed under the MIT License.
3
4//! Implementation of [`RxBufferAccess`] and friends on top of the receive
5//! buffers.
6
7use crate::rndisprot;
8use guestmem::GuestMemory;
9use guestmem::GuestMemoryError;
10use guestmem::LockedPages;
11use net_backend::BufferAccess;
12use net_backend::L4Protocol;
13use net_backend::RxBufferSegment;
14use net_backend::RxChecksumState;
15use net_backend::RxId;
16use net_backend::RxMetadata;
17use safeatomic::AtomicSliceOps;
18use std::ops::Range;
19use std::sync::Arc;
20use vmbus_channel::gpadl::GpadlView;
21use zerocopy::FromZeros;
22use zerocopy::Immutable;
23use zerocopy::IntoBytes;
24use zerocopy::KnownLayout;
25
26const PAGE_SIZE: usize = 4096;
27const PAGE_SIZE32: u32 = 4096;
28
29/// A type providing access to the netvsp receive buffer.
30pub struct GuestBuffers {
31    mem: GuestMemory,
32    _gpadl: GpadlView,
33    locked_pages: LockedPages,
34    gpns: Vec<u64>,
35    sub_allocation_size: u32,
36    mtu: u32,
37}
38
39/// A per-queue wrapper around guest buffers. The receive buffer is shared
40/// across all queues, but they are statically partitioned into per-queue
41/// suballocations.
42pub struct BufferPool {
43    buffers: Arc<GuestBuffers>,
44}
45
46impl BufferPool {
47    pub fn new(buffers: Arc<GuestBuffers>) -> Self {
48        Self { buffers }
49    }
50
51    fn offset(&self, id: RxId) -> u32 {
52        id.0 * self.buffers.sub_allocation_size
53    }
54}
55
56impl GuestBuffers {
57    pub fn new(
58        mem: GuestMemory,
59        gpadl: GpadlView,
60        sub_allocation_size: u32,
61        mtu: u32,
62    ) -> Result<Self, GuestMemoryError> {
63        assert!(sub_allocation_size >= sub_allocation_size_for_mtu(mtu));
64
65        let gpns = gpadl.first().unwrap().gpns().to_vec();
66        let locked_pages = mem.lock_gpns(false, &gpns)?;
67        Ok(Self {
68            mem,
69            _gpadl: gpadl,
70            gpns,
71            sub_allocation_size,
72            locked_pages,
73            mtu,
74        })
75    }
76
77    fn write_at(&self, offset: u32, mut buf: &[u8]) {
78        let mut offset = offset as usize;
79        while !buf.is_empty() {
80            let len = (PAGE_SIZE - offset % PAGE_SIZE).min(buf.len());
81            let (this, next) = buf.split_at(len);
82            self.locked_pages.pages()[offset / PAGE_SIZE][offset % PAGE_SIZE..][..len]
83                .atomic_write(this);
84            buf = next;
85            offset += len;
86        }
87    }
88}
89
90// Reserve this many bytes for the RNDIS headers.
91const RX_HEADER_LEN: u32 = 256;
92
93// The last 36 bytes of each suballocation cannot be used due to a bug in netvsc
94// in newer versions of Windows.
95const BROKEN_CO_NETVSC_FOOTER_LEN: u32 = 36;
96
97/// Computes the suballocation size needed for the specified MTU.
98pub const fn sub_allocation_size_for_mtu(mtu: u32) -> u32 {
99    RX_HEADER_LEN + mtu + BROKEN_CO_NETVSC_FOOTER_LEN
100}
101
102/// Computes the buffer segments for accessing a range of the receive buffer.
103fn compute_buffer_segments(v: &mut Vec<RxBufferSegment>, gpns: &[u64], mut range: Range<u32>) {
104    while !range.is_empty() {
105        let start_page = range.start / PAGE_SIZE32;
106        let start_offset = range.start % PAGE_SIZE32;
107        let max_page = (range.end - 1) / PAGE_SIZE32 + 1;
108        let mut end_page = start_page + 1;
109        while end_page < max_page && gpns[end_page as usize] == gpns[end_page as usize - 1] + 1 {
110            end_page += 1;
111        }
112
113        let gpa = gpns[start_page as usize] * PAGE_SIZE as u64 + start_offset as u64;
114        let end = (end_page * PAGE_SIZE32).min(range.end);
115
116        v.push(RxBufferSegment {
117            gpa,
118            len: (end - range.start),
119        });
120
121        range.start = end;
122    }
123}
124
125impl BufferAccess for BufferPool {
126    fn guest_memory(&self) -> &GuestMemory {
127        &self.buffers.mem
128    }
129
130    fn push_guest_addresses(&self, id: RxId, buf: &mut Vec<RxBufferSegment>) {
131        let offset = self.offset(id);
132        compute_buffer_segments(
133            buf,
134            &self.buffers.gpns,
135            offset + RX_HEADER_LEN..offset + RX_HEADER_LEN + self.buffers.mtu,
136        );
137    }
138
139    fn capacity(&self, _id: RxId) -> u32 {
140        self.buffers.mtu
141    }
142
143    fn write_data(&mut self, id: RxId, data: &[u8]) {
144        self.buffers.write_at(self.offset(id) + RX_HEADER_LEN, data);
145    }
146
147    fn write_header(&mut self, id: RxId, metadata: &RxMetadata) {
148        #[repr(C)]
149        #[derive(zerocopy::IntoBytes, Immutable, KnownLayout, Debug)]
150        struct Header {
151            header: rndisprot::MessageHeader,
152            packet: rndisprot::Packet,
153            per_packet_info: PerPacketInfo,
154        }
155
156        #[repr(C)]
157        #[derive(zerocopy::IntoBytes, Immutable, KnownLayout, Debug)]
158        struct PerPacketInfo {
159            header: rndisprot::PerPacketInfo,
160            checksum: rndisprot::RxTcpIpChecksumInfo,
161        }
162
163        let checksum = rndisprot::RxTcpIpChecksumInfo::new_zeroed()
164            .set_ip_checksum_failed(metadata.ip_checksum == RxChecksumState::Bad)
165            .set_ip_checksum_succeeded(metadata.ip_checksum.is_valid())
166            .set_ip_checksum_value_invalid(
167                metadata.ip_checksum == RxChecksumState::ValidatedButWrong,
168            )
169            .set_tcp_checksum_failed(
170                metadata.l4_protocol == L4Protocol::Tcp
171                    && metadata.l4_checksum == RxChecksumState::Bad,
172            )
173            .set_tcp_checksum_succeeded(
174                metadata.l4_protocol == L4Protocol::Tcp && metadata.l4_checksum.is_valid(),
175            )
176            .set_tcp_checksum_value_invalid(
177                metadata.l4_protocol == L4Protocol::Tcp
178                    && metadata.l4_checksum == RxChecksumState::ValidatedButWrong,
179            )
180            .set_udp_checksum_failed(
181                metadata.l4_protocol == L4Protocol::Udp
182                    && metadata.l4_checksum == RxChecksumState::Bad,
183            )
184            .set_udp_checksum_succeeded(
185                metadata.l4_protocol == L4Protocol::Udp && metadata.l4_checksum.is_valid(),
186            );
187
188        let header = Header {
189            header: rndisprot::MessageHeader {
190                message_type: rndisprot::MESSAGE_TYPE_PACKET_MSG,
191                // Always claim the full suballocation length to avoid needing
192                // to track this more accurately. This needs to match the
193                // transfer page length but is not otherwise constrained for
194                // packet messages.
195                message_length: self.buffers.sub_allocation_size,
196            },
197            packet: rndisprot::Packet {
198                data_offset: RX_HEADER_LEN - size_of::<rndisprot::MessageHeader>() as u32
199                    + metadata.offset as u32,
200                data_length: metadata.len as u32,
201                oob_data_offset: 0,
202                oob_data_length: 0,
203                num_oob_data_elements: 0,
204                per_packet_info_offset: size_of::<rndisprot::Packet>() as u32,
205                per_packet_info_length: size_of::<PerPacketInfo>() as u32,
206                vc_handle: 0,
207                reserved: 0,
208            },
209            per_packet_info: PerPacketInfo {
210                header: rndisprot::PerPacketInfo {
211                    size: size_of::<PerPacketInfo>() as u32,
212                    typ: rndisprot::PPI_TCP_IP_CHECKSUM,
213                    per_packet_information_offset: size_of::<rndisprot::PerPacketInfo>() as u32,
214                },
215                checksum,
216            },
217        };
218
219        self.buffers.write_at(self.offset(id), header.as_bytes());
220    }
221}
222
223#[cfg(test)]
224mod tests {
225    use crate::buffers::compute_buffer_segments;
226    use net_backend::RxBufferSegment;
227
228    #[test]
229    fn test_buffer_segments() {
230        fn check(addrs: &[RxBufferSegment], check: &[(u64, u32)]) {
231            assert_eq!(addrs.len(), check.len());
232            let v: Vec<_> = addrs.iter().map(|range| (range.gpa, range.len)).collect();
233            assert_eq!(v.as_slice(), check);
234        }
235
236        let gpns = [1, 3, 4, 5, 8];
237        let cases = [
238            (0x1..0x5, &[(0x1001, 4)][..]),
239            (0x1..0x1005, &[(0x1001, 0xfff), (0x3000, 5)]),
240            (0x1001..0x2005, &[(0x3001, 0x1004)]),
241            (0x1001..0x5000, &[(0x3001, 0x2fff), (0x8000, 0x1000)]),
242        ];
243        for (range, data) in cases {
244            let mut v = Vec::new();
245            compute_buffer_segments(&mut v, &gpns, range);
246            check(&v, data);
247        }
248    }
249}