Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
google
GitHub Repository: google/crosvm
Path: blob/main/devices/src/virtio/net/sys/linux.rs
5394 views
1
// Copyright 2022 The ChromiumOS Authors
2
// Use of this source code is governed by a BSD-style license that can be
3
// found in the LICENSE file.
4
5
use std::io;
6
use std::io::Write;
7
use std::mem;
8
use std::result;
9
10
use base::error;
11
use base::warn;
12
use base::EventType;
13
use base::ReadNotifier;
14
use base::WaitContext;
15
use net_util::TapT;
16
use virtio_sys::virtio_net;
17
use virtio_sys::virtio_net::virtio_net_hdr;
18
use virtio_sys::virtio_net::virtio_net_hdr_v1;
19
use zerocopy::IntoBytes;
20
21
use super::super::super::net::NetError;
22
use super::super::super::net::Token;
23
use super::super::super::net::Worker;
24
use super::super::super::Queue;
25
use super::PendingBuffer;
26
27
// Ensure that the tap interface has the correct flags and sets the offload and VNET header size
28
// to the appropriate values.
29
pub fn validate_and_configure_tap<T: TapT>(tap: &T, vq_pairs: u16) -> Result<(), NetError> {
30
let flags = tap.if_flags();
31
let mut required_flags = vec![
32
(net_sys::IFF_TAP, "IFF_TAP"),
33
(net_sys::IFF_NO_PI, "IFF_NO_PI"),
34
(net_sys::IFF_VNET_HDR, "IFF_VNET_HDR"),
35
];
36
if vq_pairs > 1 {
37
required_flags.push((net_sys::IFF_MULTI_QUEUE, "IFF_MULTI_QUEUE"));
38
}
39
let missing_flags = required_flags
40
.iter()
41
.filter_map(
42
|(value, name)| {
43
if value & flags == 0 {
44
Some(name)
45
} else {
46
None
47
}
48
},
49
)
50
.collect::<Vec<_>>();
51
52
if !missing_flags.is_empty() {
53
return Err(NetError::TapValidate(format!(
54
"Missing flags: {missing_flags:?}"
55
)));
56
}
57
58
let vnet_hdr_size = std::mem::size_of::<virtio_net_hdr_v1>();
59
tap.set_vnet_hdr_size(vnet_hdr_size)
60
.map_err(NetError::TapSetVnetHdrSize)?;
61
62
Ok(())
63
}
64
65
/// Converts virtio-net feature bits to tap's offload bits.
66
pub fn virtio_features_to_tap_offload(features: u64) -> u32 {
67
let mut tap_offloads: u32 = 0;
68
if features & (1 << virtio_net::VIRTIO_NET_F_GUEST_CSUM) != 0 {
69
tap_offloads |= net_sys::TUN_F_CSUM;
70
}
71
if features & (1 << virtio_net::VIRTIO_NET_F_GUEST_TSO4) != 0 {
72
tap_offloads |= net_sys::TUN_F_TSO4;
73
}
74
if features & (1 << virtio_net::VIRTIO_NET_F_GUEST_TSO6) != 0 {
75
tap_offloads |= net_sys::TUN_F_TSO6;
76
}
77
if features & (1 << virtio_net::VIRTIO_NET_F_GUEST_ECN) != 0 {
78
tap_offloads |= net_sys::TUN_F_TSO_ECN;
79
}
80
if features & (1 << virtio_net::VIRTIO_NET_F_GUEST_UFO) != 0 {
81
tap_offloads |= net_sys::TUN_F_UFO;
82
}
83
84
tap_offloads
85
}
86
87
/// If avail_feature has mrg_rxbuf, use this function to process rx flow.
88
pub fn process_mrg_rx<T: TapT>(
89
rx_queue: &mut Queue,
90
tap: &mut T,
91
pending: &mut PendingBuffer,
92
) -> result::Result<(), NetError> {
93
let mut needs_interrupt = false;
94
let mut exhausted_queue = false;
95
96
loop {
97
// Refill `pending` if it is empty.
98
if pending.length == 0 {
99
match tap.read(&mut *pending.buffer) {
100
Ok(length) => {
101
pending.length = length as u32;
102
}
103
Err(ref e) if e.kind() == io::ErrorKind::WouldBlock => {
104
// No more to read from the tap.
105
break;
106
}
107
Err(e) => {
108
warn!("net: rx: failed to write slice: {}", e);
109
return Err(NetError::WriteBuffer(e));
110
}
111
}
112
}
113
if pending.length == 0 {
114
break;
115
}
116
let packet_len = pending.length;
117
let Some(mut desc_list) = rx_queue.try_pop_length(packet_len as usize) else {
118
// If vq is exhausted, pending buffer should be used firstly
119
// instead of reading from tap in next loop.
120
exhausted_queue = true;
121
break;
122
};
123
let num_buffers = desc_list.len() as u16;
124
125
// Copy the num_buffers value to specified address
126
let num_buffers_offset = mem::size_of::<virtio_net_hdr>();
127
pending.buffer[num_buffers_offset..num_buffers_offset + 2]
128
.copy_from_slice(num_buffers.as_bytes());
129
let mut offset = 0;
130
let end = packet_len as usize;
131
for desc in desc_list.iter_mut() {
132
let writer = &mut desc.writer;
133
let bytes_written = match writer.write(&pending.buffer[offset..end]) {
134
Ok(n) => n,
135
Err(e) => {
136
warn!(
137
"net: mrg_rx: failed to write slice from pending buffer: {}",
138
e
139
);
140
return Err(NetError::WriteBuffer(e));
141
}
142
};
143
offset += bytes_written;
144
}
145
rx_queue.add_used_batch(desc_list);
146
147
needs_interrupt = true;
148
pending.length = 0;
149
}
150
151
if needs_interrupt {
152
rx_queue.trigger_interrupt();
153
}
154
155
if exhausted_queue {
156
Err(NetError::RxDescriptorsExhausted)
157
} else {
158
Ok(())
159
}
160
}
161
162
pub fn process_rx<T: TapT>(rx_queue: &mut Queue, mut tap: &mut T) -> result::Result<(), NetError> {
163
let mut needs_interrupt = false;
164
let mut exhausted_queue = false;
165
166
// Read as many frames as possible.
167
loop {
168
let mut desc_chain = match rx_queue.peek() {
169
Some(desc) => desc,
170
None => {
171
exhausted_queue = true;
172
break;
173
}
174
};
175
176
let writer = &mut desc_chain.writer;
177
178
match writer.write_from(&mut tap, writer.available_bytes()) {
179
Ok(_) => {}
180
Err(ref e) if e.kind() == io::ErrorKind::WriteZero => {
181
warn!("net: rx: buffer is too small to hold frame");
182
break;
183
}
184
Err(ref e) if e.kind() == io::ErrorKind::WouldBlock => {
185
// No more to read from the tap.
186
break;
187
}
188
Err(e) => {
189
warn!("net: rx: failed to write slice: {}", e);
190
return Err(NetError::WriteBuffer(e));
191
}
192
};
193
194
let bytes_written = writer.bytes_written() as u32;
195
cros_tracing::trace_simple_print!("{bytes_written} bytes read from tap");
196
197
if bytes_written > 0 {
198
let desc_chain = desc_chain.pop();
199
rx_queue.add_used(desc_chain);
200
needs_interrupt = true;
201
}
202
}
203
204
if needs_interrupt {
205
rx_queue.trigger_interrupt();
206
}
207
208
if exhausted_queue {
209
Err(NetError::RxDescriptorsExhausted)
210
} else {
211
Ok(())
212
}
213
}
214
215
pub fn process_tx<T: TapT>(tx_queue: &mut Queue, mut tap: &mut T) {
216
while let Some(mut desc_chain) = tx_queue.pop() {
217
let reader = &mut desc_chain.reader;
218
let expected_count = reader.available_bytes();
219
match reader.read_to(&mut tap, expected_count) {
220
Ok(count) => {
221
// Tap writes must be done in one call. If the entire frame was not
222
// written, it's an error.
223
if count != expected_count {
224
error!(
225
"net: tx: wrote only {} bytes of {} byte frame",
226
count, expected_count
227
);
228
}
229
cros_tracing::trace_simple_print!("{count} bytes write to tap");
230
}
231
Err(e) => error!("net: tx: failed to write frame to tap: {}", e),
232
}
233
234
tx_queue.add_used(desc_chain);
235
}
236
237
tx_queue.trigger_interrupt();
238
}
239
240
impl<T> Worker<T>
241
where
242
T: TapT + ReadNotifier,
243
{
244
pub(in crate::virtio) fn handle_rx_token(
245
&mut self,
246
wait_ctx: &WaitContext<Token>,
247
pending_buffer: &mut PendingBuffer,
248
) -> result::Result<(), NetError> {
249
match self.process_rx(pending_buffer) {
250
Ok(()) => Ok(()),
251
Err(NetError::RxDescriptorsExhausted) => {
252
wait_ctx
253
.modify(&self.tap, EventType::None, Token::RxTap)
254
.map_err(NetError::WaitContextDisableTap)?;
255
Ok(())
256
}
257
Err(e) => Err(e),
258
}
259
}
260
pub(in crate::virtio) fn handle_rx_queue(
261
&mut self,
262
wait_ctx: &WaitContext<Token>,
263
tap_polling_enabled: bool,
264
) -> result::Result<(), NetError> {
265
if !tap_polling_enabled {
266
wait_ctx
267
.modify(&self.tap, EventType::Read, Token::RxTap)
268
.map_err(NetError::WaitContextEnableTap)?;
269
}
270
Ok(())
271
}
272
pub(super) fn process_rx(
273
&mut self,
274
pending_buffer: &mut PendingBuffer,
275
) -> result::Result<(), NetError> {
276
if self.acked_features & 1 << virtio_net::VIRTIO_NET_F_MRG_RXBUF == 0 {
277
process_rx(&mut self.rx_queue, &mut self.tap)
278
} else {
279
process_mrg_rx(&mut self.rx_queue, &mut self.tap, pending_buffer)
280
}
281
}
282
}
283
284