nmstate/query_apply/
sriov.rs

1// SPDX-License-Identifier: Apache-2.0
2
3use std::collections::hash_map::Entry;
4
5use crate::{
6    BaseInterface, ErrorKind, EthernetConfig, EthernetInterface, Interface,
7    InterfaceType, Interfaces, NmstateError, SrIovConfig, SrIovVfConfig,
8};
9
10impl SrIovConfig {
11    // * Set 'vfs: []' to None which is just reverting all VF config to default.
12    // * Set `vf.iface_name` empty string,
13    pub(crate) fn sanitize_desired_for_verify(&mut self) {
14        if let Some(vfs) = self.vfs.as_mut() {
15            for vf in vfs.iter_mut() {
16                vf.iface_name = String::new();
17            }
18            if vfs.is_empty() {
19                self.vfs = None;
20            }
21        }
22    }
23
24    pub(crate) fn update(&mut self, other: Option<&SrIovConfig>) {
25        if let Some(other) = other {
26            if let Some(autoprobe) = other.drivers_autoprobe {
27                self.drivers_autoprobe = Some(autoprobe);
28            }
29            if let Some(total_vfs) = other.total_vfs {
30                self.total_vfs = Some(total_vfs);
31            }
32            if let Some(vfs) = other.vfs.as_ref() {
33                self.vfs = Some(vfs.clone());
34            }
35        }
36    }
37
38    // Many SRIOV card require extra time for kernel and udev to setup the
39    // VF interface. This function will wait VF interface been found in
40    // cur_ifaces.
41    // This function does not handle the decrease of SRIOV count(interface been
42    // removed from kernel) as our test showed kernel does not require extra
43    // time on deleting interface.
44    pub(crate) fn verify_sriov(
45        &self,
46        pf_name: &str,
47        cur_ifaces: &Interfaces,
48    ) -> Result<(), NmstateError> {
49        let cur_pf_iface =
50            match cur_ifaces.get_iface(pf_name, InterfaceType::Ethernet) {
51                Some(Interface::Ethernet(i)) => i,
52                _ => {
53                    return Err(NmstateError::new(
54                        ErrorKind::SrIovVfNotFound,
55                        format!("Failed to find PF interface {pf_name}"),
56                    ));
57                }
58            };
59
60        if let Some(desired_autoprobe) = self.drivers_autoprobe {
61            if !desired_autoprobe {
62                return Ok(());
63            }
64        }
65
66        if let Some(cur_autoprobe) = cur_pf_iface
67            .ethernet
68            .as_ref()
69            .and_then(|eth_conf| eth_conf.sr_iov.as_ref())
70            .and_then(|sriov_conf| sriov_conf.drivers_autoprobe.as_ref())
71        {
72            if !cur_autoprobe {
73                return Ok(());
74            }
75        }
76
77        let vfs = if let Some(vfs) = cur_pf_iface
78            .ethernet
79            .as_ref()
80            .and_then(|eth_conf| eth_conf.sr_iov.as_ref())
81            .and_then(|sriov_conf| sriov_conf.vfs.as_ref())
82        {
83            vfs
84        } else {
85            return Ok(());
86        };
87        for vf in vfs {
88            if vf.iface_name.is_empty() {
89                return Err(NmstateError::new(
90                    ErrorKind::SrIovVfNotFound,
91                    format!(
92                        "Failed to find VF {} interface name of PF {pf_name}",
93                        vf.id
94                    ),
95                ));
96            } else if cur_ifaces
97                .get_iface(vf.iface_name.as_str(), InterfaceType::Ethernet)
98                .is_none()
99            {
100                return Err(NmstateError::new(
101                    ErrorKind::SrIovVfNotFound,
102                    format!(
103                        "Find VF {} interface name {} of PF {pf_name} is not \
104                         exist yet",
105                        vf.id, &vf.iface_name
106                    ),
107                ));
108            }
109        }
110        Ok(())
111    }
112}
113
114impl Interfaces {
115    pub(crate) fn has_sriov_naming(&self) -> bool {
116        self.kernel_ifaces
117            .values()
118            .any(|i| i.name().starts_with(SrIovConfig::VF_NAMING_PREFIX))
119    }
120
121    pub(crate) fn use_pseudo_sriov_vf_name(&self, current: &mut Self) {
122        let mut new_vf_names: Vec<String> = Vec::new();
123
124        for (des_iface, des_sriov_count) in
125            self.kernel_ifaces.values().filter_map(|i| {
126                if let Interface::Ethernet(eth_iface) = i {
127                    let sriov_count = eth_iface
128                        .ethernet
129                        .as_ref()
130                        .and_then(|e| e.sr_iov.as_ref())
131                        .and_then(|s| s.total_vfs)
132                        .unwrap_or_default();
133                    if sriov_count > 0 {
134                        Some((eth_iface, sriov_count))
135                    } else {
136                        None
137                    }
138                } else {
139                    None
140                }
141            })
142        {
143            let cur_iface: &mut Interface = match current
144                .kernel_ifaces
145                .entry(des_iface.base.name.clone())
146            {
147                Entry::Occupied(o) => o.into_mut(),
148                Entry::Vacant(v) => {
149                    v.insert(Interface::Ethernet(des_iface.clone()))
150                }
151            };
152
153            let des_sriov_conf = if let Some(c) =
154                des_iface.ethernet.as_ref().and_then(|e| e.sr_iov.as_ref())
155            {
156                c
157            } else {
158                continue;
159            };
160
161            let cur_iface = if let Interface::Ethernet(i) = cur_iface {
162                i
163            } else {
164                continue;
165            };
166
167            // Only add psudo VF if current SRIOV setting differs
168            let cur_sriov_count = cur_iface
169                .ethernet
170                .as_ref()
171                .and_then(|e| e.sr_iov.as_ref())
172                .and_then(|s| s.total_vfs)
173                .unwrap_or_default();
174            if cur_sriov_count < des_sriov_count {
175                let cur_vfs = cur_iface
176                    .ethernet
177                    .get_or_insert(EthernetConfig {
178                        sr_iov: Some(des_sriov_conf.clone()),
179                        ..Default::default()
180                    })
181                    .sr_iov
182                    .get_or_insert(SrIovConfig::default())
183                    .vfs
184                    .get_or_insert(Vec::new());
185                for vfid in cur_sriov_count..des_sriov_count {
186                    let psudo_vf_name =
187                        format!("{}v{vfid}", des_iface.base.name);
188                    new_vf_names.push(psudo_vf_name.clone());
189                    match cur_vfs.get_mut(vfid as usize) {
190                        Some(vf) => {
191                            vf.id = vfid;
192                            if vf.iface_name.is_empty() {
193                                vf.iface_name = psudo_vf_name;
194                            }
195                        }
196                        None => {
197                            cur_vfs.push(SrIovVfConfig {
198                                id: vfid,
199                                iface_name: psudo_vf_name,
200                                ..Default::default()
201                            });
202                        }
203                    }
204                }
205            }
206        }
207        for psudo_vf_name in new_vf_names {
208            current.push(Interface::Ethernet(Box::new(EthernetInterface {
209                base: BaseInterface {
210                    name: psudo_vf_name,
211                    iface_type: InterfaceType::Ethernet,
212                    ..Default::default()
213                },
214                ..Default::default()
215            })));
216        }
217    }
218}