xref: /aosp_15_r20/external/crosvm/devices/src/virtio/net/sys/linux.rs (revision bb4ee6a4ae7042d18b07a98463b9c8b875e44b39)
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::result;
7 
8 use base::error;
9 use base::warn;
10 use base::EventType;
11 use base::ReadNotifier;
12 use base::WaitContext;
13 use net_util::TapT;
14 use virtio_sys::virtio_net;
15 use virtio_sys::virtio_net::virtio_net_hdr_v1;
16 
17 use super::super::super::net::NetError;
18 use super::super::super::net::Token;
19 use super::super::super::net::Worker;
20 use super::super::super::Queue;
21 
22 // Ensure that the tap interface has the correct flags and sets the offload and VNET header size
23 // to the appropriate values.
validate_and_configure_tap<T: TapT>(tap: &T, vq_pairs: u16) -> Result<(), NetError>24 pub fn validate_and_configure_tap<T: TapT>(tap: &T, vq_pairs: u16) -> Result<(), NetError> {
25     let flags = tap.if_flags();
26     let mut required_flags = vec![
27         (net_sys::IFF_TAP, "IFF_TAP"),
28         (net_sys::IFF_NO_PI, "IFF_NO_PI"),
29         (net_sys::IFF_VNET_HDR, "IFF_VNET_HDR"),
30     ];
31     if vq_pairs > 1 {
32         required_flags.push((net_sys::IFF_MULTI_QUEUE, "IFF_MULTI_QUEUE"));
33     }
34     let missing_flags = required_flags
35         .iter()
36         .filter_map(
37             |(value, name)| {
38                 if value & flags == 0 {
39                     Some(name)
40                 } else {
41                     None
42                 }
43             },
44         )
45         .collect::<Vec<_>>();
46 
47     if !missing_flags.is_empty() {
48         return Err(NetError::TapValidate(format!(
49             "Missing flags: {:?}",
50             missing_flags
51         )));
52     }
53 
54     let vnet_hdr_size = std::mem::size_of::<virtio_net_hdr_v1>();
55     tap.set_vnet_hdr_size(vnet_hdr_size)
56         .map_err(NetError::TapSetVnetHdrSize)?;
57 
58     Ok(())
59 }
60 
61 /// Converts virtio-net feature bits to tap's offload bits.
virtio_features_to_tap_offload(features: u64) -> u3262 pub fn virtio_features_to_tap_offload(features: u64) -> u32 {
63     let mut tap_offloads: u32 = 0;
64     if features & (1 << virtio_net::VIRTIO_NET_F_GUEST_CSUM) != 0 {
65         tap_offloads |= net_sys::TUN_F_CSUM;
66     }
67     if features & (1 << virtio_net::VIRTIO_NET_F_GUEST_TSO4) != 0 {
68         tap_offloads |= net_sys::TUN_F_TSO4;
69     }
70     if features & (1 << virtio_net::VIRTIO_NET_F_GUEST_TSO6) != 0 {
71         tap_offloads |= net_sys::TUN_F_TSO6;
72     }
73     if features & (1 << virtio_net::VIRTIO_NET_F_GUEST_ECN) != 0 {
74         tap_offloads |= net_sys::TUN_F_TSO_ECN;
75     }
76     if features & (1 << virtio_net::VIRTIO_NET_F_GUEST_UFO) != 0 {
77         tap_offloads |= net_sys::TUN_F_UFO;
78     }
79 
80     tap_offloads
81 }
82 
process_rx<T: TapT>(rx_queue: &mut Queue, mut tap: &mut T) -> result::Result<(), NetError>83 pub fn process_rx<T: TapT>(rx_queue: &mut Queue, mut tap: &mut T) -> result::Result<(), NetError> {
84     let mut needs_interrupt = false;
85     let mut exhausted_queue = false;
86 
87     // Read as many frames as possible.
88     loop {
89         let mut desc_chain = match rx_queue.peek() {
90             Some(desc) => desc,
91             None => {
92                 exhausted_queue = true;
93                 break;
94             }
95         };
96 
97         let writer = &mut desc_chain.writer;
98 
99         match writer.write_from(&mut tap, writer.available_bytes()) {
100             Ok(_) => {}
101             Err(ref e) if e.kind() == io::ErrorKind::WriteZero => {
102                 warn!("net: rx: buffer is too small to hold frame");
103                 break;
104             }
105             Err(ref e) if e.kind() == io::ErrorKind::WouldBlock => {
106                 // No more to read from the tap.
107                 break;
108             }
109             Err(e) => {
110                 warn!("net: rx: failed to write slice: {}", e);
111                 return Err(NetError::WriteBuffer(e));
112             }
113         };
114 
115         let bytes_written = writer.bytes_written() as u32;
116         cros_tracing::trace_simple_print!("{bytes_written} bytes read from tap");
117 
118         if bytes_written > 0 {
119             let desc_chain = desc_chain.pop();
120             rx_queue.add_used(desc_chain, bytes_written);
121             needs_interrupt = true;
122         }
123     }
124 
125     if needs_interrupt {
126         rx_queue.trigger_interrupt();
127     }
128 
129     if exhausted_queue {
130         Err(NetError::RxDescriptorsExhausted)
131     } else {
132         Ok(())
133     }
134 }
135 
process_tx<T: TapT>(tx_queue: &mut Queue, mut tap: &mut T)136 pub fn process_tx<T: TapT>(tx_queue: &mut Queue, mut tap: &mut T) {
137     while let Some(mut desc_chain) = tx_queue.pop() {
138         let reader = &mut desc_chain.reader;
139         let expected_count = reader.available_bytes();
140         match reader.read_to(&mut tap, expected_count) {
141             Ok(count) => {
142                 // Tap writes must be done in one call. If the entire frame was not
143                 // written, it's an error.
144                 if count != expected_count {
145                     error!(
146                         "net: tx: wrote only {} bytes of {} byte frame",
147                         count, expected_count
148                     );
149                 }
150                 cros_tracing::trace_simple_print!("{count} bytes write to tap");
151             }
152             Err(e) => error!("net: tx: failed to write frame to tap: {}", e),
153         }
154 
155         tx_queue.add_used(desc_chain, 0);
156     }
157 
158     tx_queue.trigger_interrupt();
159 }
160 
161 impl<T> Worker<T>
162 where
163     T: TapT + ReadNotifier,
164 {
handle_rx_token( &mut self, wait_ctx: &WaitContext<Token>, ) -> result::Result<(), NetError>165     pub(in crate::virtio) fn handle_rx_token(
166         &mut self,
167         wait_ctx: &WaitContext<Token>,
168     ) -> result::Result<(), NetError> {
169         match self.process_rx() {
170             Ok(()) => Ok(()),
171             Err(NetError::RxDescriptorsExhausted) => {
172                 wait_ctx
173                     .modify(&self.tap, EventType::None, Token::RxTap)
174                     .map_err(NetError::WaitContextDisableTap)?;
175                 Ok(())
176             }
177             Err(e) => Err(e),
178         }
179     }
handle_rx_queue( &mut self, wait_ctx: &WaitContext<Token>, tap_polling_enabled: bool, ) -> result::Result<(), NetError>180     pub(in crate::virtio) fn handle_rx_queue(
181         &mut self,
182         wait_ctx: &WaitContext<Token>,
183         tap_polling_enabled: bool,
184     ) -> result::Result<(), NetError> {
185         if !tap_polling_enabled {
186             wait_ctx
187                 .modify(&self.tap, EventType::Read, Token::RxTap)
188                 .map_err(NetError::WaitContextEnableTap)?;
189         }
190         Ok(())
191     }
process_rx(&mut self) -> result::Result<(), NetError>192     pub(super) fn process_rx(&mut self) -> result::Result<(), NetError> {
193         process_rx(&mut self.rx_queue, &mut self.tap)
194     }
195 }
196