1use serde::{Deserialize, Serialize};
4
5use crate::{
6 ErrorKind, Interface, InterfaceType, Interfaces, MergedInterface,
7 NmstateError, VlanProtocol,
8};
9
10#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize, Default)]
11#[serde(rename_all = "kebab-case", deny_unknown_fields)]
12#[non_exhaustive]
13pub struct SrIovConfig {
47 #[serde(
48 skip_serializing_if = "Option::is_none",
49 default,
50 deserialize_with = "crate::deserializer::option_bool_or_string"
51 )]
52 pub drivers_autoprobe: Option<bool>,
57 #[serde(
58 skip_serializing_if = "Option::is_none",
59 default,
60 deserialize_with = "crate::deserializer::option_u32_or_string"
61 )]
62 pub total_vfs: Option<u32>,
65 #[serde(skip_serializing_if = "Option::is_none")]
66 pub vfs: Option<Vec<SrIovVfConfig>>,
71}
72
73impl SrIovConfig {
74 pub(crate) const VF_NAMING_PREFIX: &'static str = "sriov:";
75 pub(crate) const VF_NAMING_SEPERATOR: char = ':';
76
77 pub fn new() -> Self {
78 Self::default()
79 }
80
81 pub(crate) fn sanitize(&mut self) -> Result<(), NmstateError> {
84 if let Some(vfs) = self.vfs.as_mut() {
85 for vf in vfs.iter_mut() {
86 if let Some(address) = vf.mac_address.as_mut() {
87 address.make_ascii_uppercase()
88 }
89
90 if let Some(VlanProtocol::Ieee8021Ad) = vf.vlan_proto {
91 if vf.vlan_id.unwrap_or_default() == 0
92 && vf.qos.unwrap_or_default() == 0
93 {
94 let e = NmstateError::new(
95 ErrorKind::InvalidArgument,
96 "VLAN protocol 802.1ad is not allowed when both \
97 VLAN ID and VLAN QoS are zero or unset"
98 .to_string(),
99 );
100 log::error!("VF ID {}: {}", vf.id, e);
101 return Err(e);
102 }
103 }
104 }
105 vfs.sort_unstable_by(|a, b| a.id.cmp(&b.id));
106 }
107
108 Ok(())
109 }
110
111 pub(crate) fn auto_fill_unmentioned_vf_id(
113 &mut self,
114 current: Option<&Self>,
115 ) {
116 if let Some(vfs) = self.vfs.as_mut() {
117 for vf in vfs.iter_mut() {
118 if let Some(address) = vf.mac_address.as_mut() {
119 address.make_ascii_uppercase()
120 }
121 }
122 vfs.sort_unstable_by(|a, b| a.id.cmp(&b.id));
123
124 if !vfs.is_empty() {
125 let total_vfs = self.total_vfs.unwrap_or_else(|| {
126 current.and_then(|c| c.total_vfs).unwrap_or(
127 vfs.iter().map(|v| v.id).max().unwrap_or_default() + 1,
128 )
129 });
130 self.total_vfs = Some(total_vfs);
131 if total_vfs as usize != vfs.len() {
133 let mut new_vf_confs: Vec<SrIovVfConfig> = (0..total_vfs)
134 .map(|i| {
135 let mut vf_conf = SrIovVfConfig::new();
136 vf_conf.id = i;
137 vf_conf
138 })
139 .collect();
140 for vf in vfs {
141 if new_vf_confs.len() > vf.id as usize {
142 new_vf_confs[vf.id as usize] = vf.clone();
143 }
144 }
145 self.vfs = Some(new_vf_confs);
146 }
147 }
148 }
149 }
150}
151
152#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize, Default)]
153#[serde(rename_all = "kebab-case", deny_unknown_fields)]
154#[non_exhaustive]
155pub struct SrIovVfConfig {
156 #[serde(deserialize_with = "crate::deserializer::u32_or_string")]
157 pub id: u32,
158 #[serde(default, skip_serializing_if = "String::is_empty")]
161 pub iface_name: String,
162 #[serde(skip_serializing_if = "Option::is_none")]
163 pub mac_address: Option<String>,
165 #[serde(
166 skip_serializing_if = "Option::is_none",
167 default,
168 deserialize_with = "crate::deserializer::option_bool_or_string"
169 )]
170 pub spoof_check: Option<bool>,
172 #[serde(
173 skip_serializing_if = "Option::is_none",
174 default,
175 deserialize_with = "crate::deserializer::option_bool_or_string"
176 )]
177 pub trust: Option<bool>,
178 #[serde(
179 skip_serializing_if = "Option::is_none",
180 default,
181 deserialize_with = "crate::deserializer::option_u32_or_string"
182 )]
183 pub min_tx_rate: Option<u32>,
185 #[serde(
186 skip_serializing_if = "Option::is_none",
187 default,
188 deserialize_with = "crate::deserializer::option_u32_or_string"
189 )]
190 pub max_tx_rate: Option<u32>,
192 #[serde(
193 skip_serializing_if = "Option::is_none",
194 default,
195 deserialize_with = "crate::deserializer::option_u32_or_string"
196 )]
197 pub vlan_id: Option<u32>,
199 #[serde(
200 skip_serializing_if = "Option::is_none",
201 default,
202 deserialize_with = "crate::deserializer::option_u32_or_string"
203 )]
204 pub qos: Option<u32>,
205
206 #[serde(skip_serializing_if = "Option::is_none")]
207 pub vlan_proto: Option<VlanProtocol>,
208}
209
210impl SrIovVfConfig {
211 pub fn new() -> Self {
212 Self::default()
213 }
214}
215
216impl Interfaces {
217 pub(crate) fn resolve_sriov_reference(
218 &mut self,
219 current: &Self,
220 ) -> Result<(), NmstateError> {
221 self.resolve_sriov_reference_iface_name(current)?;
222 self.resolve_sriov_reference_port_name(current)?;
223 Ok(())
224 }
225
226 fn resolve_sriov_reference_iface_name(
227 &mut self,
228 current: &Self,
229 ) -> Result<(), NmstateError> {
230 let mut changed_iface_names: Vec<String> = Vec::new();
231 for iface in self.kernel_ifaces.values_mut() {
232 if let Some((pf_name, vf_id)) = parse_sriov_vf_naming(iface.name())?
233 {
234 if let Some(vf_iface_name) =
235 get_sriov_vf_iface_name(current, pf_name, vf_id)
236 {
237 changed_iface_names.push(iface.name().to_string());
238 log::info!(
239 "SR-IOV VF {} resolved to interface name {}",
240 iface.name(),
241 vf_iface_name
242 );
243 iface.base_iface_mut().name = vf_iface_name;
244 } else {
245 let e = NmstateError::new(
246 ErrorKind::InvalidArgument,
247 format!(
248 "Failed to find SR-IOV VF interface name for {}",
249 iface.name()
250 ),
251 );
252 log::error!("{e}");
253 return Err(e);
254 }
255 }
256 }
257 for changed_iface_name in changed_iface_names {
258 if let Some(iface) = self.kernel_ifaces.remove(&changed_iface_name)
259 {
260 if self.kernel_ifaces.contains_key(iface.name()) {
261 let e = NmstateError::new(
262 ErrorKind::InvalidArgument,
263 format!(
264 "SR-IOV VF name {} has been resolved as interface \
265 {}, but it is already defined in desire state",
266 changed_iface_name,
267 iface.name()
268 ),
269 );
270 log::error!("{e}");
271 return Err(e);
272 }
273 self.kernel_ifaces.insert(iface.name().to_string(), iface);
274 }
275 }
276 Ok(())
277 }
278
279 fn resolve_sriov_reference_port_name(
280 &mut self,
281 current: &Self,
282 ) -> Result<(), NmstateError> {
283 let mut pending_changes = Vec::new();
286 for iface in self
287 .kernel_ifaces
288 .values()
289 .chain(self.user_ifaces.values())
290 .filter(|i| i.is_controller())
291 {
292 let ports = match iface.ports() {
293 Some(p) => p,
294 None => continue,
295 };
296 for port in ports {
297 if let Some((pf_name, vf_id)) = parse_sriov_vf_naming(port)? {
298 if let Some(vf_iface_name) =
299 get_sriov_vf_iface_name(current, pf_name, vf_id)
300 {
301 log::info!(
302 "SR-IOV VF {port} resolved to interface name \
303 {vf_iface_name}"
304 );
305 pending_changes.push((
306 iface.name().to_string(),
307 iface.iface_type(),
308 port.to_string(),
309 vf_iface_name.to_string(),
310 ));
311 } else {
312 return Err(NmstateError::new(
313 ErrorKind::InvalidArgument,
314 format!(
315 "Failed to find SR-IOV VF interface name for \
316 {}",
317 iface.name()
318 ),
319 ));
320 }
321 }
322 }
323 }
324 for (ctrl, ctrl_iface_type, origin_name, new_name) in pending_changes {
325 if let Some(iface) = self.get_iface_mut(&ctrl, ctrl_iface_type) {
326 iface.change_port_name(origin_name.as_str(), new_name);
327 }
328 }
329 Ok(())
330 }
331}
332
333fn parse_sriov_vf_naming(
334 iface_name: &str,
335) -> Result<Option<(&str, u32)>, NmstateError> {
336 if iface_name.starts_with(SrIovConfig::VF_NAMING_PREFIX) {
337 let names: Vec<&str> =
338 iface_name.split(SrIovConfig::VF_NAMING_SEPERATOR).collect();
339 if names.len() == 3 {
340 match names[2].parse::<u32>() {
341 Ok(vf_id) => Ok(Some((names[1], vf_id))),
342 Err(e) => {
343 let e = NmstateError::new(
344 ErrorKind::InvalidArgument,
345 format!(
346 "Invalid SR-IOV VF ID in {iface_name}, correct \
347 format is 'sriov:<pf_name>:<vf_id>', error: {e}"
348 ),
349 );
350 log::error!("{e}");
351 Err(e)
352 }
353 }
354 } else {
355 let e = NmstateError::new(
356 ErrorKind::InvalidArgument,
357 format!(
358 "Invalid SR-IOV VF name {iface_name}, correct format is \
359 'sriov:<pf_name>:<vf_id>'",
360 ),
361 );
362 log::error!("{e}");
363 Err(e)
364 }
365 } else {
366 Ok(None)
367 }
368}
369
370fn get_sriov_vf_iface_name(
371 current: &Interfaces,
372 pf_name: &str,
373 vf_id: u32,
374) -> Option<String> {
375 if let Some(Interface::Ethernet(pf_iface)) =
376 current.get_iface(pf_name, InterfaceType::Ethernet)
377 {
378 if let Some(vfs) = pf_iface
379 .ethernet
380 .as_ref()
381 .and_then(|e| e.sr_iov.as_ref())
382 .and_then(|s| s.vfs.as_ref())
383 {
384 for vf in vfs {
385 if vf.id == vf_id {
386 if !vf.iface_name.is_empty() {
387 return Some(vf.iface_name.clone());
388 }
389 break;
390 }
391 }
392 }
393 }
394 None
395}
396
397impl MergedInterface {
398 pub(crate) fn post_inter_ifaces_process_sriov(
399 &mut self,
400 ) -> Result<(), NmstateError> {
401 if let (
402 Some(Interface::Ethernet(apply_iface)),
403 Some(Interface::Ethernet(verify_iface)),
404 Some(Interface::Ethernet(cur_iface)),
405 ) = (
406 self.for_apply.as_mut(),
407 self.for_verify.as_mut(),
408 self.current.as_ref(),
409 ) {
410 let cur_conf =
411 cur_iface.ethernet.as_ref().and_then(|e| e.sr_iov.as_ref());
412 if let (Some(apply_conf), Some(verify_conf)) = (
413 apply_iface
414 .ethernet
415 .as_mut()
416 .and_then(|e| e.sr_iov.as_mut()),
417 verify_iface
418 .ethernet
419 .as_mut()
420 .and_then(|e| e.sr_iov.as_mut()),
421 ) {
422 apply_conf.auto_fill_unmentioned_vf_id(cur_conf);
423 verify_conf.auto_fill_unmentioned_vf_id(cur_conf);
424 }
425 }
426 Ok(())
427 }
428}