|
| 1 | +use std::io; |
| 2 | + |
1 | 3 | use crate::dhcpv6::*; |
2 | 4 | use futures::stream::TryStreamExt; |
3 | 5 | use log::{info, warn}; |
4 | 6 | use rtnetlink::new_connection; |
5 | | -use tokio::fs::OpenOptions; |
6 | | -use tokio::io::AsyncWriteExt; |
7 | | -use tokio::time::{self, Duration}; |
| 7 | +use tokio::fs::{read_link, OpenOptions}; |
| 8 | +use tokio::io::{AsyncReadExt, AsyncWriteExt}; |
| 9 | +use tokio::time::{self, sleep, Duration}; |
8 | 10 |
|
9 | 11 | use pnet::datalink::{self, Channel::Ethernet, Config}; |
10 | 12 | use pnet::packet::ethernet::EthernetPacket; |
@@ -140,24 +142,126 @@ fn format_mac(bytes: Vec<u8>) -> String { |
140 | 142 | } |
141 | 143 |
|
142 | 144 | pub async fn configure_sriov(num_vfs: u32) -> Result<(), String> { |
143 | | - let file_path = format!("/sys/class/net/{}/device/sriov_numvfs", INTERFACE_NAME); |
144 | | - |
145 | | - let result = OpenOptions::new().write(true).open(&file_path).await; |
| 145 | + let base_path = format!("/sys/class/net/{}/device", INTERFACE_NAME); |
146 | 146 |
|
147 | | - let mut file = match result { |
148 | | - Ok(file) => file, |
149 | | - Err(e) => return Err(format!("Failed to open the file: {}", e)), |
150 | | - }; |
| 147 | + let file_path = format!("{}/sriov_numvfs", base_path); |
| 148 | + let mut file = OpenOptions::new() |
| 149 | + .write(true) |
| 150 | + .open(&file_path) |
| 151 | + .await |
| 152 | + .map_err(|e| e.to_string())?; |
151 | 153 |
|
152 | 154 | let value = format!("{}\n", num_vfs); |
153 | 155 | if let Err(e) = file.write_all(value.as_bytes()).await { |
154 | 156 | return Err(format!("Failed to write to the file: {}", e)); |
155 | 157 | } |
| 158 | + info!("Created {} sriov virtual functions", num_vfs); |
| 159 | + |
| 160 | + let device_path = read_link(base_path).await.map_err(|e| e.to_string())?; |
| 161 | + let pci_address = device_path |
| 162 | + .file_name() |
| 163 | + .ok_or("No PCI address found".to_string())?; |
| 164 | + let pci_address = pci_address.to_str().ok_or("No PCI address found")?; |
| 165 | + |
| 166 | + info!("Found PCI address of {}: {}", INTERFACE_NAME, pci_address); |
| 167 | + |
| 168 | + let sriov_offset = get_device_information(pci_address, "sriov_offset") |
| 169 | + .await |
| 170 | + .map_err(|e| e.to_string())?; |
| 171 | + |
| 172 | + let sriov_offset = match sriov_offset.parse::<u32>() { |
| 173 | + Ok(n) => n, |
| 174 | + Err(e) => return Err(e.to_string()), |
| 175 | + }; |
| 176 | + |
| 177 | + let pci_address_parts: Vec<&str> = pci_address.split(&[':', '.'][..]).collect(); |
| 178 | + if pci_address_parts.len() != 4 { |
| 179 | + return Err(format!("invalid pci address format: {}", pci_address)); |
| 180 | + } |
| 181 | + |
| 182 | + let virtual_funcs: Vec<String> = (0..num_vfs) |
| 183 | + .map(|x| x + sriov_offset) |
| 184 | + .map(|x| format!("{}.{}", pci_address_parts[0..3].join(":"), x)) |
| 185 | + .collect(); |
| 186 | + |
| 187 | + const RETRIES: i32 = 3; |
| 188 | + for (index, vf) in virtual_funcs.iter().enumerate() { |
| 189 | + for i in 1..RETRIES { |
| 190 | + info!("try to unbind device {}: {:?}/{}", vf, i, RETRIES); |
| 191 | + if let Err(e) = unbind_device(vf).await { |
| 192 | + warn!("failed to unbind device {}: {}", vf, e.to_string()); |
| 193 | + sleep(Duration::from_secs(2)).await; |
| 194 | + } else { |
| 195 | + info!("successfull unbound device {}", vf); |
| 196 | + |
| 197 | + if let Err(e) = bind_device(index, vf).await { |
| 198 | + warn!("failed to bind devices: {}", e.to_string()) |
| 199 | + } |
| 200 | + break; |
| 201 | + } |
| 202 | + } |
| 203 | + } |
156 | 204 |
|
157 | | - info!("Successfully wrote to the file."); |
158 | 205 | Ok(()) |
159 | 206 | } |
160 | 207 |
|
| 208 | +async fn unbind_device(pci: &str) -> Result<(), io::Error> { |
| 209 | + let unbind_path = format!("/sys/bus/pci/devices/{}/driver/unbind", pci); |
| 210 | + let mut file = OpenOptions::new().write(true).open(&unbind_path).await?; |
| 211 | + |
| 212 | + file.write_all(pci.as_bytes()).await?; |
| 213 | + info!("unbound device: {}", pci); |
| 214 | + Ok(()) |
| 215 | +} |
| 216 | + |
| 217 | +async fn bind_device(index: usize, pci_address: &str) -> Result<(), io::Error> { |
| 218 | + info!("try to bind device to vfio: {}", pci_address); |
| 219 | + if index == 0 { |
| 220 | + vfio_new_id(pci_address).await |
| 221 | + } else { |
| 222 | + vfio_bind(pci_address).await |
| 223 | + } |
| 224 | +} |
| 225 | + |
| 226 | +async fn vfio_new_id(pci_address: &str) -> Result<(), io::Error> { |
| 227 | + let vendor = get_device_information(pci_address, "vendor").await?; |
| 228 | + let vendor = vendor[2..].to_string(); |
| 229 | + |
| 230 | + let device = get_device_information(pci_address, "device").await?; |
| 231 | + let device = device[2..].to_string(); |
| 232 | + |
| 233 | + let mut file = OpenOptions::new() |
| 234 | + .write(true) |
| 235 | + .open("/sys/bus/pci/drivers/vfio-pci/new_id") |
| 236 | + .await?; |
| 237 | + |
| 238 | + let content = format!("{} {}", vendor, device); |
| 239 | + file.write_all(content.as_bytes()).await?; |
| 240 | + info!("bound devices ({}) to vfio-pci", pci_address); |
| 241 | + Ok(()) |
| 242 | +} |
| 243 | + |
| 244 | +async fn vfio_bind(pci_address: &str) -> Result<(), io::Error> { |
| 245 | + let mut file = OpenOptions::new() |
| 246 | + .write(true) |
| 247 | + .open("/sys/bus/pci/drivers/vfio-pci/bind") |
| 248 | + .await?; |
| 249 | + |
| 250 | + file.write_all(pci_address.as_bytes()).await?; |
| 251 | + info!("bound devices ({}) to vfio-pci", pci_address); |
| 252 | + Ok(()) |
| 253 | +} |
| 254 | + |
| 255 | +async fn get_device_information(pci: &str, field: &str) -> Result<String, io::Error> { |
| 256 | + let path = format!("/sys/bus/pci/devices/{}/{}", pci, field); |
| 257 | + let mut file = OpenOptions::new().read(true).open(&path).await?; |
| 258 | + |
| 259 | + let mut dst = String::new(); |
| 260 | + file.read_to_string(&mut dst).await?; |
| 261 | + |
| 262 | + Ok(dst.trim().to_string()) |
| 263 | +} |
| 264 | + |
161 | 265 | // Print all packets to the console for debugging purposes |
162 | 266 | async fn _capture_packets(interface_name: String) { |
163 | 267 | let interfaces = datalink::interfaces(); |
|
0 commit comments