1use 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 thiserror::Error;
21use vmbus_channel::gpadl::GpadlView;
22use zerocopy::FromZeros;
23use zerocopy::Immutable;
24use zerocopy::IntoBytes;
25use zerocopy::KnownLayout;
26
27const PAGE_SIZE: usize = 4096;
28const PAGE_SIZE32: u32 = 4096;
29
30#[derive(Debug, Error)]
31pub enum GuestBuffersError {
32 #[error("invalid mtu {mtu}")]
33 InvalidMtu { mtu: u32 },
34 #[error("sub_allocation_size {sub_allocation_size} is too small for mtu {mtu}")]
35 SubAllocationTooSmall { sub_allocation_size: u32, mtu: u32 },
36 #[error("GPADL has no ranges")]
37 EmptyGpadl,
38 #[error("failed to lock guest page numbers")]
39 GpnLock(#[source] GuestMemoryError),
40}
41
42pub struct GuestBuffers {
44 mem: GuestMemory,
45 _gpadl: GpadlView,
46 locked_pages: LockedPages,
47 gpns: Vec<u64>,
48 sub_allocation_size: u32,
49 mtu: u32,
50}
51
52pub struct BufferPool {
56 buffers: Arc<GuestBuffers>,
57 rx_vlan_count: u64,
58}
59
60impl BufferPool {
61 pub fn new(buffers: Arc<GuestBuffers>) -> Self {
62 Self {
63 buffers,
64 rx_vlan_count: 0,
65 }
66 }
67
68 fn offset(&self, id: RxId) -> u32 {
69 id.0 * self.buffers.sub_allocation_size
70 }
71
72 pub fn take_rx_vlan_count(&mut self) -> u64 {
75 std::mem::take(&mut self.rx_vlan_count)
76 }
77}
78
79impl GuestBuffers {
80 pub fn validate_config(
83 gpadl: &GpadlView,
84 sub_allocation_size: u32,
85 mtu: u32,
86 ) -> Result<(), GuestBuffersError> {
87 if gpadl.first().is_none() {
88 return Err(GuestBuffersError::EmptyGpadl);
89 }
90 mtu.checked_add(RX_HEADER_LEN)
91 .and_then(|v| v.checked_add(BROKEN_CO_NETVSC_FOOTER_LEN))
92 .ok_or(GuestBuffersError::InvalidMtu { mtu })?;
93 if sub_allocation_size < sub_allocation_size_for_mtu(mtu) {
94 return Err(GuestBuffersError::SubAllocationTooSmall {
95 sub_allocation_size,
96 mtu,
97 });
98 }
99 Ok(())
100 }
101
102 pub fn new(
103 mem: GuestMemory,
104 gpadl: GpadlView,
105 sub_allocation_size: u32,
106 mtu: u32,
107 ) -> Result<Self, GuestBuffersError> {
108 Self::validate_config(&gpadl, sub_allocation_size, mtu)?;
109
110 let gpns = gpadl.first().unwrap().gpns().to_vec();
111 let locked_pages = mem
112 .lock_gpns(guestmem::AccessType::Write, false, &gpns)
113 .map_err(GuestBuffersError::GpnLock)?;
114 Ok(Self {
115 mem,
116 _gpadl: gpadl,
117 gpns,
118 sub_allocation_size,
119 locked_pages,
120 mtu,
121 })
122 }
123
124 fn write_at(&self, offset: u32, mut buf: &[u8]) {
125 let mut offset = offset as usize;
126 while !buf.is_empty() {
127 let len = (PAGE_SIZE - offset % PAGE_SIZE).min(buf.len());
128 let (this, next) = buf.split_at(len);
129 self.locked_pages.pages()[offset / PAGE_SIZE][offset % PAGE_SIZE..][..len]
130 .atomic_write(this);
131 buf = next;
132 offset += len;
133 }
134 }
135}
136
137const RX_HEADER_LEN: u32 = 256;
139
140const BROKEN_CO_NETVSC_FOOTER_LEN: u32 = 36;
143
144pub const fn sub_allocation_size_for_mtu(mtu: u32) -> u32 {
146 RX_HEADER_LEN + mtu + BROKEN_CO_NETVSC_FOOTER_LEN
147}
148
149fn compute_buffer_segments(v: &mut Vec<RxBufferSegment>, gpns: &[u64], mut range: Range<u32>) {
151 while !range.is_empty() {
152 let start_page = range.start / PAGE_SIZE32;
153 let start_offset = range.start % PAGE_SIZE32;
154 let max_page = (range.end - 1) / PAGE_SIZE32 + 1;
155 let mut end_page = start_page + 1;
156 while end_page < max_page && gpns[end_page as usize] == gpns[end_page as usize - 1] + 1 {
157 end_page += 1;
158 }
159
160 let gpa = gpns[start_page as usize] * PAGE_SIZE as u64 + start_offset as u64;
161 let end = (end_page * PAGE_SIZE32).min(range.end);
162
163 v.push(RxBufferSegment {
164 gpa,
165 len: (end - range.start),
166 });
167
168 range.start = end;
169 }
170}
171
172impl BufferAccess for BufferPool {
173 fn guest_memory(&self) -> &GuestMemory {
174 &self.buffers.mem
175 }
176
177 fn push_guest_addresses(&self, id: RxId, buf: &mut Vec<RxBufferSegment>) {
178 let offset = self.offset(id);
179 compute_buffer_segments(
180 buf,
181 &self.buffers.gpns,
182 offset + RX_HEADER_LEN..offset + RX_HEADER_LEN + self.buffers.mtu,
183 );
184 }
185
186 fn capacity(&self, _id: RxId) -> u32 {
187 self.buffers.mtu
188 }
189
190 fn write_data(&mut self, id: RxId, data: &[u8]) {
191 self.buffers.write_at(self.offset(id) + RX_HEADER_LEN, data);
192 }
193
194 fn write_packet_segments(&mut self, id: RxId, metadata: &RxMetadata, segments: &[&[u8]]) {
195 let mut offset = self.offset(id) + RX_HEADER_LEN;
196 for segment in segments {
197 self.buffers.write_at(offset, segment);
198 offset += segment.len() as u32;
199 }
200 self.write_header(id, metadata);
201 }
202
203 fn write_header(&mut self, id: RxId, metadata: &RxMetadata) {
204 #[repr(C)]
205 #[derive(zerocopy::IntoBytes, Immutable, KnownLayout, Debug)]
206 struct Header {
207 header: rndisprot::MessageHeader,
208 packet: rndisprot::Packet,
209 }
210
211 #[repr(C)]
212 #[derive(zerocopy::IntoBytes, Immutable, KnownLayout, Debug)]
213 struct PerPacketInfo {
214 header: rndisprot::PerPacketInfo,
215 payload: u32,
216 }
217
218 let mut ppi_count = 1;
219 let checksum = rndisprot::RxTcpIpChecksumInfo::new_zeroed()
220 .set_ip_checksum_failed(metadata.ip_checksum == RxChecksumState::Bad)
221 .set_ip_checksum_succeeded(metadata.ip_checksum.is_valid())
222 .set_ip_checksum_value_invalid(
223 metadata.ip_checksum == RxChecksumState::ValidatedButWrong,
224 )
225 .set_tcp_checksum_failed(
226 metadata.l4_protocol == L4Protocol::Tcp
227 && metadata.l4_checksum == RxChecksumState::Bad,
228 )
229 .set_tcp_checksum_succeeded(
230 metadata.l4_protocol == L4Protocol::Tcp && metadata.l4_checksum.is_valid(),
231 )
232 .set_tcp_checksum_value_invalid(
233 metadata.l4_protocol == L4Protocol::Tcp
234 && metadata.l4_checksum == RxChecksumState::ValidatedButWrong,
235 )
236 .set_udp_checksum_failed(
237 metadata.l4_protocol == L4Protocol::Udp
238 && metadata.l4_checksum == RxChecksumState::Bad,
239 )
240 .set_udp_checksum_succeeded(
241 metadata.l4_protocol == L4Protocol::Udp && metadata.l4_checksum.is_valid(),
242 );
243 let checksum_ppi = PerPacketInfo {
244 header: rndisprot::PerPacketInfo {
245 size: size_of::<PerPacketInfo>() as u32,
246 typ: rndisprot::PPI_TCP_IP_CHECKSUM,
247 per_packet_information_offset: size_of::<rndisprot::PerPacketInfo>() as u32,
248 },
249 payload: checksum.0,
250 };
251
252 let vlan = if let Some(vlan_info) = metadata.vlan {
253 self.rx_vlan_count += 1;
254 ppi_count += 1;
255
256 Some(PerPacketInfo {
257 header: rndisprot::PerPacketInfo {
258 size: size_of::<PerPacketInfo>() as u32,
259 typ: rndisprot::PPI_VLAN,
260 per_packet_information_offset: size_of::<rndisprot::PerPacketInfo>() as u32,
261 },
262 payload: Into::<rndisprot::EthVlanInfo>::into(vlan_info).into(),
263 })
264 } else {
265 None
266 };
267
268 let header = Header {
269 header: rndisprot::MessageHeader {
270 message_type: rndisprot::MESSAGE_TYPE_PACKET_MSG,
271 message_length: self.buffers.sub_allocation_size,
276 },
277 packet: rndisprot::Packet {
278 data_offset: RX_HEADER_LEN - size_of::<rndisprot::MessageHeader>() as u32
279 + metadata.offset as u32,
280 data_length: metadata.len as u32,
281 oob_data_offset: 0,
282 oob_data_length: 0,
283 num_oob_data_elements: 0,
284 per_packet_info_offset: size_of::<rndisprot::Packet>() as u32,
285 per_packet_info_length: ppi_count * size_of::<PerPacketInfo>() as u32,
286 vc_handle: 0,
287 reserved: 0,
288 },
289 };
290
291 let mut offset = self.offset(id);
292 self.buffers.write_at(offset, header.as_bytes());
293 offset += size_of::<Header>() as u32;
294 self.buffers.write_at(offset, checksum_ppi.as_bytes());
295 offset += size_of::<PerPacketInfo>() as u32;
296 if let Some(vlan_ppi) = vlan {
297 self.buffers.write_at(offset, vlan_ppi.as_bytes());
298 }
299 static_assertions::const_assert!(
300 (size_of::<Header>() + 2 * size_of::<PerPacketInfo>()) < RX_HEADER_LEN as usize
301 );
302 }
303}
304
305#[cfg(test)]
306mod tests {
307 use crate::buffers::GuestBuffers;
308 use crate::buffers::GuestBuffersError;
309 use crate::buffers::compute_buffer_segments;
310 use crate::buffers::sub_allocation_size_for_mtu;
311 use guestmem::GuestMemory;
312 use net_backend::RxBufferSegment;
313 use vmbus_channel::gpadl::GpadlMap;
314 use vmbus_core::protocol::GpadlId;
315 use vmbus_ring::gparange::GpaRange;
316 use vmbus_ring::gparange::MultiPagedRangeBuf;
317 use zerocopy::IntoBytes;
318
319 #[test]
322 fn sub_allocation_too_small_for_mtu() {
323 let default_mtu = 1514;
324 let max_mtu = 9216;
325 let sub_alloc_for_default = sub_allocation_size_for_mtu(default_mtu);
326
327 assert!(sub_alloc_for_default < sub_allocation_size_for_mtu(max_mtu));
329
330 let num_pages = 16;
332 let hdr = GpaRange {
333 len: (num_pages * 4096) as u32,
334 offset: 0,
335 };
336 let mut buf = vec![u64::from_le_bytes(hdr.as_bytes().try_into().unwrap())];
337 buf.extend((0..num_pages).map(|i| i as u64));
339 let multipaged_ranged_buf = MultiPagedRangeBuf::from_range_buffer(1, buf).unwrap();
340
341 let gpadl_map = GpadlMap::new();
343 let gpadl_id = GpadlId(1);
344 gpadl_map.add(gpadl_id, multipaged_ranged_buf);
345 let gpadl_view = gpadl_map.view().map(gpadl_id).unwrap();
346
347 let mem = GuestMemory::empty();
348 let result = GuestBuffers::new(mem, gpadl_view, sub_alloc_for_default, max_mtu);
349 match result {
350 Err(GuestBuffersError::SubAllocationTooSmall { .. }) => {}
351 Err(e) => panic!("expected SubAllocationTooSmall, got {e}"),
352 Ok(_) => panic!("expected SubAllocationTooSmall, got Ok"),
353 }
354 }
355
356 #[test]
359 fn overflowing_mtu_returns_error() {
360 let num_pages = 16;
361 let hdr = GpaRange {
362 len: (num_pages * 4096) as u32,
363 offset: 0,
364 };
365 let mut buf = vec![u64::from_le_bytes(hdr.as_bytes().try_into().unwrap())];
366 buf.extend((0..num_pages).map(|i| i as u64));
367 let multipaged_ranged_buf = MultiPagedRangeBuf::from_range_buffer(1, buf).unwrap();
368
369 let gpadl_map = GpadlMap::new();
370 let gpadl_id = GpadlId(3);
371 gpadl_map.add(gpadl_id, multipaged_ranged_buf);
372 let gpadl_view = gpadl_map.view().map(gpadl_id).unwrap();
373
374 let result = GuestBuffers::validate_config(&gpadl_view, 1806, u32::MAX);
376 match result {
377 Err(GuestBuffersError::InvalidMtu { .. }) => {}
378 Err(e) => panic!("expected InvalidMtu, got {e}"),
379 Ok(_) => panic!("expected InvalidMtu, got Ok"),
380 }
381 }
382
383 #[test]
386 fn empty_gpadl_returns_error() {
387 let multipaged_ranged_buf = MultiPagedRangeBuf::from_range_buffer(0, vec![]).unwrap();
388
389 let gpadl_map = GpadlMap::new();
390 let gpadl_id = GpadlId(2);
391 gpadl_map.add(gpadl_id, multipaged_ranged_buf);
392 let gpadl_view = gpadl_map.view().map(gpadl_id).unwrap();
393
394 let mem = GuestMemory::empty();
395 let result = GuestBuffers::new(mem, gpadl_view, 1806, 1514);
396 match result {
397 Err(GuestBuffersError::EmptyGpadl) => {}
398 Err(e) => panic!("expected EmptyGpadl, got {e}"),
399 Ok(_) => panic!("expected EmptyGpadl, got Ok"),
400 }
401 }
402
403 #[test]
404 fn test_buffer_segments() {
405 fn check(addrs: &[RxBufferSegment], check: &[(u64, u32)]) {
406 assert_eq!(addrs.len(), check.len());
407 let v: Vec<_> = addrs.iter().map(|range| (range.gpa, range.len)).collect();
408 assert_eq!(v.as_slice(), check);
409 }
410
411 let gpns = [1, 3, 4, 5, 8];
412 let cases = [
413 (0x1..0x5, &[(0x1001, 4)][..]),
414 (0x1..0x1005, &[(0x1001, 0xfff), (0x3000, 5)]),
415 (0x1001..0x2005, &[(0x3001, 0x1004)]),
416 (0x1001..0x5000, &[(0x3001, 0x2fff), (0x8000, 0x1000)]),
417 ];
418 for (range, data) in cases {
419 let mut v = Vec::new();
420 compute_buffer_segments(&mut v, &gpns, range);
421 check(&v, data);
422 }
423 }
424}