1 В избранное 0 Ответвления 0

OSCHINA-MIRROR/openeuler-stratovirt

Присоединиться к Gitlife
Откройте для себя и примите участие в публичных проектах с открытым исходным кодом с участием более 10 миллионов разработчиков. Приватные репозитории также полностью бесплатны :)
Присоединиться бесплатно
Это зеркальный репозиторий, синхронизируется ежедневно с исходного репозитория.
В этом репозитории не указан файл с открытой лицензией (LICENSE). При использовании обратитесь к конкретному описанию проекта и его зависимостям в коде.
Клонировать/Скачать
net.rs 38 КБ
Копировать Редактировать Исходные данные Просмотреть построчно История
Xinle.Guo Отправлено 3 лет назад 385e1a0
1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192939495969798991001011021031041051061071081091101111121131141151161171181191201211221231241251261271281291301311321331341351361371381391401411421431441451461471481491501511521531541551561571581591601611621631641651661671681691701711721731741751761771781791801811821831841851861871881891901911921931941951961971981992002012022032042052062072082092102112122132142152162172182192202212222232242252262272282292302312322332342352362372382392402412422432442452462472482492502512522532542552562572582592602612622632642652662672682692702712722732742752762772782792802812822832842852862872882892902912922932942952962972982993003013023033043053063073083093103113123133143153163173183193203213223233243253263273283293303313323333343353363373383393403413423433443453463473483493503513523533543553563573583593603613623633643653663673683693703713723733743753763773783793803813823833843853863873883893903913923933943953963973983994004014024034044054064074084094104114124134144154164174184194204214224234244254264274284294304314324334344354364374384394404414424434444454464474484494504514524534544554564574584594604614624634644654664674684694704714724734744754764774784794804814824834844854864874884894904914924934944954964974984995005015025035045055065075085095105115125135145155165175185195205215225235245255265275285295305315325335345355365375385395405415425435445455465475485495505515525535545555565575585595605615625635645655665675685695705715725735745755765775785795805815825835845855865875885895905915925935945955965975985996006016026036046056066076086096106116126136146156166176186196206216226236246256266276286296306316326336346356366376386396406416426436446456466476486496506516526536546556566576586596606616626636646656666676686696706716726736746756766776786796806816826836846856866876886896906916926936946956966976986997007017027037047057067077087097107117127137147157167177187197207217227237247257267277287297307317327337347357367377387397407417427437447457467477487497507517527537547557567577587597607617627637647657667677687697707717727737747757767777787797807817827837847857867877887897907917927937947957967977987998008018028038048058068078088098108118128138148158168178188198208218228238248258268278288298308318328338348358368378388398408418428438448458468478488498508518528538548558568578588598608618628638648658668678688698708718728738748758768778788798808818828838848858868878888898908918928938948958968978988999009019029039049059069079089099109119129139149159169179189199209219229239249259269279289299309319329339349359369379389399409419429439449459469479489499509519529539549559569579589599609619629639649659669679689699709719729739749759769779789799809819829839849859869879889899909919929939949959969979989991000100110021003100410051006100710081009101010111012101310141015101610171018101910201021102210231024102510261027102810291030103110321033103410351036103710381039104010411042104310441045104610471048104910501051105210531054105510561057105810591060106110621063106410651066106710681069107010711072107310741075107610771078107910801081108210831084108510861087108810891090109110921093109410951096109710981099110011011102110311041105110611071108110911101111111211131114111511161117111811191120112111221123112411251126112711281129113011311132113311341135113611371138
// Copyright (c) 2020 Huawei Technologies Co.,Ltd. All rights reserved.
//
// StratoVirt is licensed under Mulan PSL v2.
// You can use this software according to the terms and conditions of the Mulan
// PSL v2.
// You may obtain a copy of Mulan PSL v2 at:
// http://license.coscl.org.cn/MulanPSL2
// THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY
// KIND, EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO
// NON-INFRINGEMENT, MERCHANTABILITY OR FIT FOR A PARTICULAR PURPOSE.
// See the Mulan PSL v2 for more details.
use std::io::Write;
use std::os::unix::io::{AsRawFd, RawFd};
use std::path::Path;
use std::sync::mpsc::{channel, Receiver, Sender};
use std::sync::{Arc, Mutex};
use std::{cmp, fs, mem};
use address_space::AddressSpace;
use error_chain::bail;
use log::{error, warn};
use machine_manager::{
config::{ConfigCheck, NetworkInterfaceConfig},
event_loop::EventLoop,
};
use migration::{DeviceStateDesc, FieldDesc, MigrationHook, MigrationManager, StateTransfer};
use migration_derive::{ByteCode, Desc};
use util::byte_code::ByteCode;
use util::loop_context::{
read_fd, EventNotifier, EventNotifierHelper, NotifierCallback, NotifierOperation,
};
use util::num_ops::{read_u32, write_u32};
use util::tap::{Tap, IFF_MULTI_QUEUE, TUN_F_VIRTIO};
use vmm_sys_util::{epoll::EventSet, eventfd::EventFd};
use super::errors::{ErrorKind, Result, ResultExt};
use super::{
Queue, VirtioDevice, VirtioInterrupt, VirtioInterruptType, VirtioNetHdr,
VIRTIO_F_RING_EVENT_IDX, VIRTIO_F_VERSION_1, VIRTIO_NET_CTRL_MQ,
VIRTIO_NET_CTRL_MQ_VQ_PAIRS_MAX, VIRTIO_NET_CTRL_MQ_VQ_PAIRS_MIN,
VIRTIO_NET_CTRL_MQ_VQ_PAIRS_SET, VIRTIO_NET_F_CSUM, VIRTIO_NET_F_CTRL_VQ,
VIRTIO_NET_F_GUEST_CSUM, VIRTIO_NET_F_GUEST_TSO4, VIRTIO_NET_F_GUEST_UFO,
VIRTIO_NET_F_HOST_TSO4, VIRTIO_NET_F_HOST_UFO, VIRTIO_NET_F_MAC, VIRTIO_NET_F_MQ,
VIRTIO_NET_OK, VIRTIO_TYPE_NET,
};
/// Number of virtqueues.
const QUEUE_NUM_NET: usize = 2;
/// Size of each virtqueue.
const QUEUE_SIZE_NET: u16 = 256;
type SenderConfig = Option<Tap>;
/// Configuration of virtio-net devices.
#[repr(C, packed)]
#[derive(Copy, Clone, Debug, Default)]
pub struct VirtioNetConfig {
/// Mac Address.
pub mac: [u8; 6],
/// Device status.
pub status: u16,
/// Maximum number of each of transmit and receive queues.
pub max_virtqueue_pairs: u16,
/// Maximum Transmission Unit.
pub mtu: u16,
/// Speed, in units of 1Mb.
pub speed: u32,
/// 0x00 - half duplex
/// 0x01 - full duplex
pub duplex: u8,
}
impl ByteCode for VirtioNetConfig {}
/// The control queue is used to verify the multi queue feature.
pub struct CtrlVirtio {
queue: Arc<Mutex<Queue>>,
queue_evt: EventFd,
}
impl CtrlVirtio {
pub fn new(queue: Arc<Mutex<Queue>>, queue_evt: EventFd) -> Self {
Self { queue, queue_evt }
}
}
/// Handle the frontend and the backend control channel virtio queue events and data.
pub struct NetCtrlHandler {
/// The control virtio queue.
pub ctrl: CtrlVirtio,
/// Memory space.
pub mem_space: Arc<AddressSpace>,
/// The interrupt call back function.
pub interrupt_cb: Arc<VirtioInterrupt>,
/// Bit mask of features negotiated by the backend and the frontend.
pub driver_features: u64,
/// Deactivate event to delete net control handler.
pub deactivate_evt: EventFd,
}
#[repr(C, packed)]
#[derive(Copy, Clone, Debug, Default)]
struct CrtlHdr {
class: u8,
cmd: u8,
}
impl ByteCode for CrtlHdr {}
impl NetCtrlHandler {
fn handle_ctrl(&mut self) -> Result<()> {
let elem = self
.ctrl
.queue
.lock()
.unwrap()
.vring
.pop_avail(&self.mem_space, self.driver_features)
.chain_err(|| "Failed to pop avail ring for net control queue")?;
let mut used_len = 0;
if let Some(ctrl_desc) = elem.out_iovec.get(0) {
used_len += ctrl_desc.len;
let ctrl_hdr = self
.mem_space
.read_object::<CrtlHdr>(ctrl_desc.addr)
.chain_err(|| "Failed to get control queue descriptor")?;
match ctrl_hdr.class as u16 {
VIRTIO_NET_CTRL_MQ => {
if ctrl_hdr.cmd as u16 != VIRTIO_NET_CTRL_MQ_VQ_PAIRS_SET {
bail!(
"Control queue header command can't match {}",
VIRTIO_NET_CTRL_MQ_VQ_PAIRS_SET
);
}
if let Some(mq_desc) = elem.out_iovec.get(0) {
used_len += mq_desc.len;
let queue_pairs = self
.mem_space
.read_object::<u16>(mq_desc.addr)
.chain_err(|| "Failed to read multi queue descriptor")?;
if !(VIRTIO_NET_CTRL_MQ_VQ_PAIRS_MIN..=VIRTIO_NET_CTRL_MQ_VQ_PAIRS_MAX)
.contains(&queue_pairs)
{
bail!("Invalid queue pairs {}", queue_pairs);
}
}
}
_ => {
bail!(
"Control queue header class can't match {}",
VIRTIO_NET_CTRL_MQ
);
}
}
}
if let Some(status) = elem.in_iovec.get(0) {
used_len += status.len;
let data = VIRTIO_NET_OK;
self.mem_space.write_object::<u8>(&data, status.addr)?;
}
self.ctrl
.queue
.lock()
.unwrap()
.vring
.add_used(&self.mem_space, elem.index, used_len)
.chain_err(|| format!("Failed to add used ring {}", elem.index))?;
(self.interrupt_cb)(
&VirtioInterruptType::Vring,
Some(&self.ctrl.queue.lock().unwrap()),
)
.chain_err(|| ErrorKind::InterruptTrigger("ctrl", VirtioInterruptType::Vring))?;
Ok(())
}
fn deactivate_evt_handler(&mut self) -> Vec<EventNotifier> {
let notifiers = vec![
EventNotifier::new(
NotifierOperation::Delete,
self.ctrl.queue_evt.as_raw_fd(),
None,
EventSet::IN,
Vec::new(),
),
EventNotifier::new(
NotifierOperation::Delete,
self.deactivate_evt.as_raw_fd(),
None,
EventSet::IN,
Vec::new(),
),
];
notifiers
}
}
impl EventNotifierHelper for NetCtrlHandler {
fn internal_notifiers(net_io: Arc<Mutex<Self>>) -> Vec<EventNotifier> {
let locked_net_io = net_io.lock().unwrap();
let cloned_net_io = net_io.clone();
let handler: Box<NotifierCallback> = Box::new(move |_, fd: RawFd| {
read_fd(fd);
cloned_net_io
.lock()
.unwrap()
.handle_ctrl()
.unwrap_or_else(|e| error!("Failed to handle ctrl queue, error is {}.", e));
None
});
let mut notifiers = Vec::new();
let ctrl_fd = locked_net_io.ctrl.queue_evt.as_raw_fd();
notifiers.push(build_event_notifier(
ctrl_fd,
Some(handler),
NotifierOperation::AddShared,
EventSet::IN,
));
// Register event notifier for deactivate_evt.
let cloned_net_io = net_io.clone();
let handler: Box<NotifierCallback> = Box::new(move |_, fd: RawFd| {
read_fd(fd);
Some(cloned_net_io.lock().unwrap().deactivate_evt_handler())
});
notifiers.push(build_event_notifier(
locked_net_io.deactivate_evt.as_raw_fd(),
Some(handler),
NotifierOperation::AddShared,
EventSet::IN,
));
notifiers
}
}
struct TxVirtio {
queue: Arc<Mutex<Queue>>,
queue_evt: EventFd,
}
impl TxVirtio {
fn new(queue: Arc<Mutex<Queue>>, queue_evt: EventFd) -> Self {
TxVirtio { queue, queue_evt }
}
}
struct RxVirtio {
queue_full: bool,
need_irqs: bool,
queue: Arc<Mutex<Queue>>,
queue_evt: EventFd,
}
impl RxVirtio {
fn new(queue: Arc<Mutex<Queue>>, queue_evt: EventFd) -> Self {
RxVirtio {
queue_full: false,
need_irqs: false,
queue,
queue_evt,
}
}
}
struct NetIoHandler {
rx: RxVirtio,
tx: TxVirtio,
tap: Option<Tap>,
tap_fd: RawFd,
mem_space: Arc<AddressSpace>,
interrupt_cb: Arc<VirtioInterrupt>,
driver_features: u64,
receiver: Receiver<SenderConfig>,
update_evt: EventFd,
deactivate_evt: EventFd,
is_listening: bool,
}
impl NetIoHandler {
fn handle_rx(&mut self) -> Result<()> {
let mut queue = self.rx.queue.lock().unwrap();
while let Some(tap) = self.tap.as_mut() {
if queue.vring.avail_ring_len(&self.mem_space)? == 0 {
self.rx.queue_full = true;
break;
}
let elem = queue
.vring
.pop_avail(&self.mem_space, self.driver_features)
.chain_err(|| "Failed to pop avail ring for net rx")?;
let mut iovecs = Vec::new();
for elem_iov in elem.in_iovec.iter() {
let host_addr = queue
.vring
.get_host_address_from_cache(elem_iov.addr, &self.mem_space);
if host_addr != 0 {
let iovec = libc::iovec {
iov_base: host_addr as *mut libc::c_void,
iov_len: elem_iov.len as libc::size_t,
};
iovecs.push(iovec);
} else {
error!("Failed to get host address for {}", elem_iov.addr.0);
}
}
let write_count = unsafe {
libc::readv(
tap.as_raw_fd() as libc::c_int,
iovecs.as_ptr() as *const libc::iovec,
iovecs.len() as libc::c_int,
)
};
if write_count < 0 {
let e = std::io::Error::last_os_error();
queue.vring.push_back();
if e.kind() == std::io::ErrorKind::WouldBlock {
break;
}
// If the backend tap device is removed, readv returns less than 0.
// At this time, the content in the tap needs to be cleaned up.
// Here, read is called to process, otherwise handle_rx may be triggered all the time.
let mut buf = [0; 1024];
match tap.read(&mut buf) {
Ok(cnt) => error!("Failed to call readv but tap read is ok: cnt {}", cnt),
Err(e) => {
// When the backend tap device is abnormally removed, read return EBADFD.
error!("Failed to read tap: {}", e);
}
}
bail!("Failed to call readv for net handle_rx: {}", e);
}
queue
.vring
.add_used(&self.mem_space, elem.index, write_count as u32)
.chain_err(|| {
format!(
"Failed to add used ring for net rx, index: {}, len: {}",
elem.index, write_count
)
})?;
self.rx.need_irqs = true;
}
if self.rx.need_irqs {
self.rx.need_irqs = false;
(self.interrupt_cb)(&VirtioInterruptType::Vring, Some(&queue))
.chain_err(|| ErrorKind::InterruptTrigger("net", VirtioInterruptType::Vring))?;
}
Ok(())
}
fn handle_tx(&mut self) -> Result<()> {
let mut queue = self.tx.queue.lock().unwrap();
let mut need_irq = false;
while let Ok(elem) = queue.vring.pop_avail(&self.mem_space, self.driver_features) {
let mut iovecs = Vec::new();
for elem_iov in elem.out_iovec.iter() {
let host_addr = queue
.vring
.get_host_address_from_cache(elem_iov.addr, &self.mem_space);
if host_addr != 0 {
let iovec = libc::iovec {
iov_base: host_addr as *mut libc::c_void,
iov_len: elem_iov.len as libc::size_t,
};
iovecs.push(iovec);
} else {
error!("Failed to get host address for {}", elem_iov.addr.0);
}
}
let mut read_len = 0;
if let Some(tap) = self.tap.as_mut() {
if !iovecs.is_empty() {
read_len = unsafe {
libc::writev(
tap.as_raw_fd() as libc::c_int,
iovecs.as_ptr() as *const libc::iovec,
iovecs.len() as libc::c_int,
)
};
}
};
if read_len < 0 {
let e = std::io::Error::last_os_error();
bail!("Failed to call writev for net handle_tx: {}", e);
}
queue
.vring
.add_used(&self.mem_space, elem.index, 0)
.chain_err(|| format!("Net tx: Failed to add used ring {}", elem.index))?;
need_irq = true;
}
if need_irq {
(self.interrupt_cb)(&VirtioInterruptType::Vring, Some(&queue))
.chain_err(|| ErrorKind::InterruptTrigger("net", VirtioInterruptType::Vring))?;
}
Ok(())
}
fn update_evt_handler(net_io: &Arc<Mutex<Self>>) -> Vec<EventNotifier> {
let mut locked_net_io = net_io.lock().unwrap();
locked_net_io.tap = match locked_net_io.receiver.recv() {
Ok(tap) => tap,
Err(e) => {
error!("Failed to receive the tap {}", e);
None
}
};
let old_tap_fd = locked_net_io.tap_fd;
locked_net_io.tap_fd = -1;
if let Some(tap) = locked_net_io.tap.as_ref() {
locked_net_io.tap_fd = tap.as_raw_fd();
}
let mut notifiers = vec![
build_event_notifier(
locked_net_io.update_evt.as_raw_fd(),
None,
NotifierOperation::Delete,
EventSet::IN,
),
build_event_notifier(
locked_net_io.rx.queue_evt.as_raw_fd(),
None,
NotifierOperation::Delete,
EventSet::IN,
),
build_event_notifier(
locked_net_io.tx.queue_evt.as_raw_fd(),
None,
NotifierOperation::Delete,
EventSet::IN,
),
];
if old_tap_fd != -1 {
notifiers.push(build_event_notifier(
old_tap_fd,
None,
NotifierOperation::Delete,
EventSet::IN,
));
}
drop(locked_net_io);
notifiers.append(&mut EventNotifierHelper::internal_notifiers(net_io.clone()));
notifiers
}
fn deactivate_evt_handler(&mut self) -> Vec<EventNotifier> {
let mut notifiers = vec![
EventNotifier::new(
NotifierOperation::Delete,
self.update_evt.as_raw_fd(),
None,
EventSet::IN,
Vec::new(),
),
EventNotifier::new(
NotifierOperation::Delete,
self.deactivate_evt.as_raw_fd(),
None,
EventSet::IN,
Vec::new(),
),
EventNotifier::new(
NotifierOperation::Delete,
self.rx.queue_evt.as_raw_fd(),
None,
EventSet::IN,
Vec::new(),
),
EventNotifier::new(
NotifierOperation::Delete,
self.tx.queue_evt.as_raw_fd(),
None,
EventSet::IN,
Vec::new(),
),
];
if self.tap_fd != -1 {
notifiers.push(EventNotifier::new(
NotifierOperation::Delete,
self.tap_fd,
None,
EventSet::IN,
Vec::new(),
));
self.tap_fd = -1;
}
notifiers
}
}
fn build_event_notifier(
fd: RawFd,
handler: Option<Box<NotifierCallback>>,
op: NotifierOperation,
event: EventSet,
) -> EventNotifier {
let mut handlers = Vec::new();
if let Some(h) = handler {
handlers.push(Arc::new(Mutex::new(h)));
}
EventNotifier::new(op, fd, None, event, handlers)
}
impl EventNotifierHelper for NetIoHandler {
fn internal_notifiers(net_io: Arc<Mutex<Self>>) -> Vec<EventNotifier> {
// Register event notifier for update_evt.
let locked_net_io = net_io.lock().unwrap();
let cloned_net_io = net_io.clone();
let handler: Box<NotifierCallback> = Box::new(move |_, fd: RawFd| {
read_fd(fd);
Some(NetIoHandler::update_evt_handler(&cloned_net_io))
});
let mut notifiers = vec![build_event_notifier(
locked_net_io.update_evt.as_raw_fd(),
Some(handler),
NotifierOperation::AddShared,
EventSet::IN,
)];
// Register event notifier for deactivate_evt.
let cloned_net_io = net_io.clone();
let handler: Box<NotifierCallback> = Box::new(move |_, fd: RawFd| {
read_fd(fd);
Some(cloned_net_io.lock().unwrap().deactivate_evt_handler())
});
notifiers.push(build_event_notifier(
locked_net_io.deactivate_evt.as_raw_fd(),
Some(handler),
NotifierOperation::AddShared,
EventSet::IN,
));
// Register event notifier for rx.
let cloned_net_io = net_io.clone();
let handler: Box<NotifierCallback> = Box::new(move |_, fd: RawFd| {
let mut locked_net_io = cloned_net_io.lock().unwrap();
read_fd(fd);
if let Some(tap) = locked_net_io.tap.as_ref() {
if !locked_net_io.is_listening {
let notifier = vec![EventNotifier::new(
NotifierOperation::Resume,
tap.as_raw_fd(),
None,
EventSet::IN,
Vec::new(),
)];
locked_net_io.is_listening = true;
return Some(notifier);
}
}
None
});
let rx_fd = locked_net_io.rx.queue_evt.as_raw_fd();
notifiers.push(build_event_notifier(
rx_fd,
Some(handler),
NotifierOperation::AddShared,
EventSet::IN,
));
// Register event notifier for tx.
let cloned_net_io = net_io.clone();
let handler: Box<NotifierCallback> = Box::new(move |_, fd: RawFd| {
read_fd(fd);
if let Err(ref e) = cloned_net_io.lock().unwrap().handle_tx() {
error!(
"Failed to handle tx(tx event) for net, {}",
error_chain::ChainedError::display_chain(e)
);
}
None
});
let tx_fd = locked_net_io.tx.queue_evt.as_raw_fd();
notifiers.push(build_event_notifier(
tx_fd,
Some(handler),
NotifierOperation::AddShared,
EventSet::IN,
));
// Register event notifier for tap.
let cloned_net_io = net_io.clone();
if let Some(tap) = locked_net_io.tap.as_ref() {
let handler: Box<NotifierCallback> = Box::new(move |_, _| {
let mut locked_net_io = cloned_net_io.lock().unwrap();
if let Err(ref e) = locked_net_io.handle_rx() {
error!(
"Failed to handle rx(tap event), {}",
error_chain::ChainedError::display_chain(e)
);
}
if let Some(tap) = locked_net_io.tap.as_ref() {
if locked_net_io.rx.queue_full {
let notifier = vec![EventNotifier::new(
NotifierOperation::Park,
tap.as_raw_fd(),
None,
EventSet::IN,
Vec::new(),
)];
locked_net_io.is_listening = false;
locked_net_io.rx.queue_full = false;
return Some(notifier);
}
}
None
});
let tap_fd = tap.as_raw_fd();
notifiers.push(build_event_notifier(
tap_fd,
Some(handler),
NotifierOperation::AddShared,
EventSet::IN | EventSet::EDGE_TRIGGERED,
));
}
notifiers
}
}
/// Status of net device.
#[repr(C)]
#[derive(Copy, Clone, Desc, ByteCode)]
#[desc_version(compat_version = "0.1.0")]
pub struct VirtioNetState {
/// Bit mask of features supported by the backend.
device_features: u64,
/// Bit mask of features negotiated by the backend and the frontend.
driver_features: u64,
/// Virtio net configurations.
config_space: VirtioNetConfig,
}
/// Network device structure.
pub struct Net {
/// Configuration of the network device.
net_cfg: NetworkInterfaceConfig,
/// Tap device opened.
taps: Option<Vec<Tap>>,
/// The status of net device.
state: VirtioNetState,
/// The send half of Rust's channel to send tap information.
senders: Option<Vec<Sender<SenderConfig>>>,
/// Eventfd for config space update.
update_evt: EventFd,
/// Eventfd for device deactivate.
deactivate_evt: EventFd,
}
impl Default for Net {
fn default() -> Self {
Self {
net_cfg: Default::default(),
taps: None,
state: VirtioNetState::default(),
senders: None,
update_evt: EventFd::new(libc::EFD_NONBLOCK).unwrap(),
deactivate_evt: EventFd::new(libc::EFD_NONBLOCK).unwrap(),
}
}
}
impl Net {
pub fn new(net_cfg: NetworkInterfaceConfig) -> Self {
Self {
net_cfg,
taps: None,
state: VirtioNetState::default(),
senders: None,
update_evt: EventFd::new(libc::EFD_NONBLOCK).unwrap(),
deactivate_evt: EventFd::new(libc::EFD_NONBLOCK).unwrap(),
}
}
}
/// Set Mac address configured into the virtio configuration, and return features mask with
/// VIRTIO_NET_F_MAC set.
///
/// # Arguments
///
/// * `device_config` - Virtio net configurations.
/// * `mac` - Mac address configured by user.
pub fn build_device_config_space(device_config: &mut VirtioNetConfig, mac: &str) -> u64 {
let mut config_features = 0_u64;
let mut bytes = [0_u8; 6];
for (i, s) in mac.split(':').collect::<Vec<&str>>().iter().enumerate() {
bytes[i] = if let Ok(v) = u8::from_str_radix(s, 16) {
v
} else {
return config_features;
};
}
device_config.mac.copy_from_slice(&bytes);
config_features |= 1 << VIRTIO_NET_F_MAC;
config_features
}
/// Check that tap flag supports multi queue feature.
///
/// # Arguments
///
/// * `dev_name` - The name of tap device on host.
/// * `queue_pairs` - The number of virtio queue pairs.
pub fn check_mq(dev_name: &str, queue_pair: u16) -> Result<()> {
let path = format!("/sys/class/net/{}/tun_flags", dev_name);
let tap_path = Path::new(&path);
if !tap_path.exists() {
bail!("Tap path doesn't exist");
}
let is_mq = queue_pair > 1;
let ifr_flag =
fs::read_to_string(tap_path).chain_err(|| "Failed to read content from tun_flags file")?;
let flags = u16::from_str_radix(ifr_flag.trim().trim_start_matches("0x"), 16)
.chain_err(|| "Failed to parse tap ifr flag")?;
if (flags & IFF_MULTI_QUEUE != 0) && !is_mq {
bail!(format!(
"Tap device supports mq, but command set queue pairs {}.",
queue_pair
));
} else if (flags & IFF_MULTI_QUEUE == 0) && is_mq {
bail!(format!(
"Tap device doesn't support mq, but command set queue pairs {}.",
queue_pair
));
}
Ok(())
}
/// Open tap device if no fd provided, configure and return it.
///
/// # Arguments
///
/// * `net_fd` - Fd of tap device opened.
/// * `host_dev_name` - Path of tap device on host.
/// * `queue_pairs` - The number of virtio queue pairs.
pub fn create_tap(
net_fds: Option<&Vec<i32>>,
host_dev_name: Option<&str>,
queue_pairs: u16,
) -> Result<Option<Vec<Tap>>> {
if net_fds.is_none() && host_dev_name.is_none() {
return Ok(None);
}
if net_fds.is_some() && host_dev_name.is_some() {
error!("Create tap: fd and file_path exist meanwhile (use fd by default)");
}
let mut taps = Vec::with_capacity(queue_pairs as usize);
for index in 0..queue_pairs {
let tap = if let Some(fds) = net_fds {
let fd = fds
.get(index as usize)
.chain_err(|| format!("Failed to get fd from index {}", index))?;
Tap::new(None, Some(*fd), queue_pairs)
.chain_err(|| format!("Failed to create tap, index is {}", index))?
} else {
// `unwrap()` won't fail because the arguments have been checked
let dev_name = host_dev_name.unwrap();
check_mq(dev_name, queue_pairs)?;
Tap::new(Some(dev_name), None, queue_pairs).chain_err(|| {
format!(
"Failed to create tap with name {}, index is {}",
dev_name, index
)
})?
};
tap.set_offload(TUN_F_VIRTIO)
.chain_err(|| "Failed to set tap offload")?;
let vnet_hdr_size = mem::size_of::<VirtioNetHdr>() as u32;
tap.set_hdr_size(vnet_hdr_size)
.chain_err(|| "Failed to set tap hdr size")?;
taps.push(tap);
}
Ok(Some(taps))
}
impl VirtioDevice for Net {
/// Realize virtio network device.
fn realize(&mut self) -> Result<()> {
// if iothread not found, return err
if self.net_cfg.iothread.is_some()
&& EventLoop::get_ctx(self.net_cfg.iothread.as_ref()).is_none()
{
bail!(
"IOThread {:?} of Net is not configured in params.",
self.net_cfg.iothread,
);
}
self.state.device_features = 1 << VIRTIO_F_VERSION_1
| 1 << VIRTIO_NET_F_CSUM
| 1 << VIRTIO_NET_F_GUEST_CSUM
| 1 << VIRTIO_NET_F_GUEST_TSO4
| 1 << VIRTIO_NET_F_GUEST_UFO
| 1 << VIRTIO_NET_F_HOST_TSO4
| 1 << VIRTIO_NET_F_HOST_UFO
| 1 << VIRTIO_F_RING_EVENT_IDX;
let queue_pairs = self.net_cfg.queues / 2;
if self.net_cfg.mq
&& queue_pairs >= VIRTIO_NET_CTRL_MQ_VQ_PAIRS_MIN
&& queue_pairs <= VIRTIO_NET_CTRL_MQ_VQ_PAIRS_MAX
{
self.state.device_features |= 1 << VIRTIO_NET_F_MQ;
self.state.device_features |= 1 << VIRTIO_NET_F_CTRL_VQ;
self.state.config_space.max_virtqueue_pairs = queue_pairs;
}
if !self.net_cfg.host_dev_name.is_empty() {
self.taps = None;
self.taps = create_tap(None, Some(&self.net_cfg.host_dev_name), queue_pairs)
.chain_err(|| "Failed to open tap with file path")?;
} else if let Some(fds) = self.net_cfg.tap_fds.as_mut() {
let mut created_fds = 0;
if let Some(taps) = &self.taps {
for (index, tap) in taps.iter().enumerate() {
if fds.get(index).map_or(-1, |fd| *fd as RawFd) == tap.as_raw_fd() {
created_fds += 1;
}
}
}
if created_fds != fds.len() {
self.taps =
create_tap(Some(fds), None, queue_pairs).chain_err(|| "Failed to open tap")?;
}
} else {
self.taps = None;
}
if let Some(mac) = &self.net_cfg.mac {
self.state.device_features |=
build_device_config_space(&mut self.state.config_space, mac);
}
Ok(())
}
fn unrealize(&mut self) -> Result<()> {
MigrationManager::unregister_device_instance(
VirtioNetState::descriptor(),
&self.net_cfg.id,
);
Ok(())
}
/// Get the virtio device type, refer to Virtio Spec.
fn device_type(&self) -> u32 {
VIRTIO_TYPE_NET
}
/// Get the count of virtio device queues.
fn queue_num(&self) -> usize {
if self.net_cfg.mq {
(self.net_cfg.queues + 1) as usize
} else {
QUEUE_NUM_NET
}
}
/// Get the queue size of virtio device.
fn queue_size(&self) -> u16 {
QUEUE_SIZE_NET
}
/// Get device features from host.
fn get_device_features(&self, features_select: u32) -> u32 {
read_u32(self.state.device_features, features_select)
}
/// Set driver features by guest.
fn set_driver_features(&mut self, page: u32, value: u32) {
let mut v = write_u32(value, page);
let unrequested_features = v & !self.state.device_features;
if unrequested_features != 0 {
warn!("Received acknowledge request with unknown feature: {:x}", v);
v &= !unrequested_features;
}
self.state.driver_features |= v;
}
/// Read data of config from guest.
fn read_config(&self, offset: u64, mut data: &mut [u8]) -> Result<()> {
let config_slice = self.state.config_space.as_bytes();
let config_len = config_slice.len() as u64;
if offset >= config_len {
return Err(ErrorKind::DevConfigOverflow(offset, config_len).into());
}
if let Some(end) = offset.checked_add(data.len() as u64) {
data.write_all(&config_slice[offset as usize..cmp::min(end, config_len) as usize])?;
}
Ok(())
}
/// Write data to config from guest.
fn write_config(&mut self, offset: u64, data: &[u8]) -> Result<()> {
let data_len = data.len();
let config_slice = self.state.config_space.as_mut_bytes();
let config_len = config_slice.len();
if offset as usize + data_len > config_len {
return Err(ErrorKind::DevConfigOverflow(offset, config_len as u64).into());
}
config_slice[(offset as usize)..(offset as usize + data_len)].copy_from_slice(data);
Ok(())
}
/// Activate the virtio device, this function is called by vcpu thread when frontend
/// virtio driver is ready and write `DRIVER_OK` to backend.
fn activate(
&mut self,
mem_space: Arc<AddressSpace>,
interrupt_cb: Arc<VirtioInterrupt>,
queues: &[Arc<Mutex<Queue>>],
mut queue_evts: Vec<EventFd>,
) -> Result<()> {
let queue_num = queues.len();
if (self.state.driver_features & 1 << VIRTIO_NET_F_CTRL_VQ != 0) && (queue_num % 2 != 0) {
let ctrl_queue = queues[queue_num - 1].clone();
let ctrl_queue_evt = queue_evts.remove(queue_num - 1);
let ctrl_handler = NetCtrlHandler {
ctrl: CtrlVirtio::new(ctrl_queue, ctrl_queue_evt),
mem_space: mem_space.clone(),
interrupt_cb: interrupt_cb.clone(),
driver_features: self.state.driver_features,
deactivate_evt: self.deactivate_evt.try_clone().unwrap(),
};
EventLoop::update_event(
EventNotifierHelper::internal_notifiers(Arc::new(Mutex::new(ctrl_handler))),
self.net_cfg.iothread.as_ref(),
)?;
}
let mut senders = Vec::new();
let queue_pairs = queue_num / 2;
for index in 0..queue_pairs {
let rx_queue = queues[index * 2].clone();
let rx_queue_evt = queue_evts.remove(0);
let tx_queue = queues[index * 2 + 1].clone();
let tx_queue_evt = queue_evts.remove(0);
let (sender, receiver) = channel();
senders.push(sender);
let mut handler = NetIoHandler {
rx: RxVirtio::new(rx_queue, rx_queue_evt),
tx: TxVirtio::new(tx_queue, tx_queue_evt),
tap: self.taps.as_ref().map(|t| t[index].clone()),
tap_fd: -1,
mem_space: mem_space.clone(),
interrupt_cb: interrupt_cb.clone(),
driver_features: self.state.driver_features,
receiver,
update_evt: self.update_evt.try_clone().unwrap(),
deactivate_evt: self.deactivate_evt.try_clone().unwrap(),
is_listening: true,
};
if let Some(tap) = &handler.tap {
handler.tap_fd = tap.as_raw_fd();
}
EventLoop::update_event(
EventNotifierHelper::internal_notifiers(Arc::new(Mutex::new(handler))),
self.net_cfg.iothread.as_ref(),
)?;
}
self.senders = Some(senders);
Ok(())
}
fn update_config(&mut self, dev_config: Option<Arc<dyn ConfigCheck>>) -> Result<()> {
if let Some(conf) = dev_config {
self.net_cfg = conf
.as_any()
.downcast_ref::<NetworkInterfaceConfig>()
.unwrap()
.clone();
} else {
self.net_cfg = Default::default();
}
self.realize()?;
if let Some(senders) = &self.senders {
for (index, sender) in senders.iter().enumerate() {
match self.taps.take() {
Some(taps) => {
let tap = taps
.get(index)
.cloned()
.chain_err(|| format!("Failed to get index {} tap", index))?;
sender
.send(Some(tap))
.chain_err(|| ErrorKind::ChannelSend("tap fd".to_string()))?;
}
None => sender
.send(None)
.chain_err(|| "Failed to send status of None to channel".to_string())?,
}
}
self.update_evt
.write(1)
.chain_err(|| ErrorKind::EventFdWrite)?;
}
Ok(())
}
fn deactivate(&mut self) -> Result<()> {
self.deactivate_evt
.write(1)
.chain_err(|| ErrorKind::EventFdWrite)
}
}
// Send and Sync is not auto-implemented for `Sender` type.
// Implementing them is safe because `Sender` field of Net won't change in migration
// workflow.
unsafe impl Sync for Net {}
impl StateTransfer for Net {
fn get_state_vec(&self) -> migration::errors::Result<Vec<u8>> {
Ok(self.state.as_bytes().to_vec())
}
fn set_state_mut(&mut self, state: &[u8]) -> migration::errors::Result<()> {
self.state = *VirtioNetState::from_bytes(state)
.ok_or(migration::errors::ErrorKind::FromBytesError("NET"))?;
Ok(())
}
fn get_device_alias(&self) -> u64 {
if let Some(alias) = MigrationManager::get_desc_alias(&VirtioNetState::descriptor().name) {
alias
} else {
!0
}
}
}
impl MigrationHook for Net {}
#[cfg(test)]
mod tests {
pub use super::super::*;
pub use super::*;
#[test]
fn test_net_init() {
// test net new method
let mut net = Net::default();
assert_eq!(net.state.device_features, 0);
assert_eq!(net.state.driver_features, 0);
assert_eq!(net.taps.is_none(), true);
assert_eq!(net.senders.is_none(), true);
assert_eq!(net.net_cfg.mac.is_none(), true);
assert_eq!(net.net_cfg.tap_fds.is_none(), true);
assert_eq!(net.net_cfg.vhost_type.is_none(), true);
assert_eq!(net.net_cfg.vhost_fds.is_none(), true);
// test net realize method
net.realize().unwrap();
assert_eq!(net.device_type(), 1);
assert_eq!(net.queue_num(), 2);
assert_eq!(net.queue_size(), 256);
// test read_config and write_config method
let write_data: Vec<u8> = vec![7; 4];
let mut random_data: Vec<u8> = vec![0; 4];
let mut origin_data: Vec<u8> = vec![0; 4];
net.read_config(0x00, &mut origin_data).unwrap();
net.write_config(0x00, &write_data).unwrap();
net.read_config(0x00, &mut random_data).unwrap();
assert_eq!(random_data, write_data);
net.write_config(0x00, &origin_data).unwrap();
// test boundary condition of offset and data parameters
let device_config = net.state.config_space.as_bytes();
let len = device_config.len() as u64;
let mut data: Vec<u8> = vec![0; 10];
let offset: u64 = len + 1;
assert_eq!(net.read_config(offset, &mut data).is_ok(), false);
let offset: u64 = len;
assert_eq!(net.read_config(offset, &mut data).is_ok(), false);
let offset: u64 = 0;
assert_eq!(net.read_config(offset, &mut data).is_ok(), true);
let offset: u64 = len;
let mut data: Vec<u8> = vec![0; 1];
assert_eq!(net.write_config(offset, &mut data).is_ok(), false);
let offset: u64 = len - 1;
let mut data: Vec<u8> = vec![0; 1];
assert_eq!(net.write_config(offset, &mut data).is_ok(), true);
let offset: u64 = 0;
let mut data: Vec<u8> = vec![0; len as usize];
assert_eq!(net.write_config(offset, &mut data).is_ok(), true);
}
}

Комментарий ( 0 )

Вы можете оставить комментарий после Вход в систему

1
https://gitlife.ru/oschina-mirror/openeuler-stratovirt.git
git@gitlife.ru:oschina-mirror/openeuler-stratovirt.git
oschina-mirror
openeuler-stratovirt
openeuler-stratovirt
v2.2.0-rc2