virtio-devices: net: Add support for VIRTIO_NET_F_CTRL_GUEST_OFFLOADS

This allows the guest to reprogram the offload settings and mitigates
issues where the Linux kernel tries to reprogram the queues even when
the feature is not advertised.

Fixes: #2528

Signed-off-by: Rob Bradford <robert.bradford@intel.com>
This commit is contained in:
Rob Bradford 2021-04-28 15:35:50 +01:00 committed by Sebastien Boeuf
parent 9ef1a68539
commit 51a93bc635
4 changed files with 47 additions and 9 deletions

View File

@ -315,6 +315,7 @@ impl Net {
rate_limiter_config: Option<RateLimiterConfig>,
) -> Result<Self> {
let mut avail_features = 1 << VIRTIO_NET_F_CSUM
| 1 << VIRTIO_NET_F_CTRL_GUEST_OFFLOADS
| 1 << VIRTIO_NET_F_GUEST_CSUM
| 1 << VIRTIO_NET_F_GUEST_ECN
| 1 << VIRTIO_NET_F_GUEST_TSO4
@ -511,7 +512,7 @@ impl VirtioDevice for Net {
mem: mem.clone(),
kill_evt,
pause_evt,
ctrl_q: NetCtrl::new(cvq_queue, cvq_queue_evt),
ctrl_q: NetCtrl::new(cvq_queue, cvq_queue_evt, Some(self.taps.clone())),
};
let paused = self.common.paused.clone();

View File

@ -4,6 +4,7 @@
use super::{EpollHelper, EpollHelperError, EpollHelperHandler, Queue, EPOLL_HELPER_EVENT_LAST};
use net_util::MacAddr;
use net_util::Tap;
use std::os::raw::c_uint;
use std::os::unix::io::AsRawFd;
use std::sync::atomic::AtomicBool;
@ -48,6 +49,7 @@ pub enum Error {
pub struct NetCtrl {
pub queue_evt: EventFd,
pub queue: Queue,
pub taps: Option<Vec<Tap>>,
}
#[repr(C, packed)]
@ -60,8 +62,12 @@ pub struct ControlHeader {
unsafe impl ByteValued for ControlHeader {}
impl NetCtrl {
pub fn new(queue: Queue, queue_evt: EventFd) -> Self {
NetCtrl { queue_evt, queue }
pub fn new(queue: Queue, queue_evt: EventFd, taps: Option<Vec<Tap>>) -> Self {
NetCtrl {
queue_evt,
queue,
taps,
}
}
pub fn process_cvq(&mut self, mem: &GuestMemoryMmap) -> Result<()> {
@ -96,6 +102,29 @@ impl NetCtrl {
true
}
}
VIRTIO_NET_CTRL_GUEST_OFFLOADS => {
let features = mem
.read_obj::<u64>(data_desc.addr)
.map_err(Error::GuestMemory)?;
if u32::from(ctrl_hdr.cmd) != VIRTIO_NET_CTRL_GUEST_OFFLOADS_SET {
warn!("Unsupported command: {}", ctrl_hdr.cmd);
false
} else {
let mut ok = true;
if let Some(ref mut taps) = &mut self.taps {
for tap in taps.iter_mut() {
info!("Reprogramming tap offload with features: {}", features);
tap.set_offload(virtio_features_to_tap_offload(features))
.map_err(|e| {
error!("Error programming tap offload: {:?}", e);
ok = false
})
.ok();
}
}
ok
}
}
_ => {
warn!("Unsupported command {:?}", ctrl_hdr);
false

View File

@ -59,6 +59,9 @@ const FIONBIO: u64 = 0x5421;
const VFIO_IOMMU_MAP_DMA: u64 = 0x3b71;
const VFIO_IOMMU_UNMAP_DMA: u64 = 0x3b72;
// See include/uapi/linux/if_tun.h in the kernel code.
const TUNSETOFFLOAD: u64 = 0x4004_54d0;
fn create_virtio_iommu_ioctl_seccomp_rule() -> Vec<SeccompRule> {
or![
and![Cond::new(1, ArgLen::DWORD, Eq, VFIO_IOMMU_MAP_DMA).unwrap()],
@ -234,8 +237,12 @@ fn virtio_net_thread_rules() -> Vec<SyscallRuleSet> {
]
}
fn virtio_net_ctl_thread_rules() -> Vec<SyscallRuleSet> {
vec![
fn create_virtio_net_ctl_ioctl_seccomp_rule() -> Result<Vec<SeccompRule>, Error> {
Ok(or![and![Cond::new(1, ArgLen::DWORD, Eq, TUNSETOFFLOAD)?],])
}
fn virtio_net_ctl_thread_rules() -> Result<Vec<SyscallRuleSet>, Error> {
Ok(vec![
allow_syscall(libc::SYS_brk),
allow_syscall(libc::SYS_close),
allow_syscall(libc::SYS_dup),
@ -246,13 +253,14 @@ fn virtio_net_ctl_thread_rules() -> Vec<SyscallRuleSet> {
allow_syscall(libc::SYS_epoll_wait),
allow_syscall(libc::SYS_exit),
allow_syscall(libc::SYS_futex),
allow_syscall_if(libc::SYS_ioctl, create_virtio_net_ctl_ioctl_seccomp_rule()?),
allow_syscall(libc::SYS_madvise),
allow_syscall(libc::SYS_munmap),
allow_syscall(libc::SYS_read),
allow_syscall(libc::SYS_rt_sigprocmask),
allow_syscall(libc::SYS_sigaltstack),
allow_syscall(libc::SYS_write),
]
])
}
fn virtio_pmem_thread_rules() -> Vec<SyscallRuleSet> {
@ -451,7 +459,7 @@ fn get_seccomp_filter_trap(thread_type: Thread) -> Result<SeccompFilter, Error>
Thread::VirtioIommu => virtio_iommu_thread_rules(),
Thread::VirtioMem => virtio_mem_thread_rules(),
Thread::VirtioNet => virtio_net_thread_rules(),
Thread::VirtioNetCtl => virtio_net_ctl_thread_rules(),
Thread::VirtioNetCtl => virtio_net_ctl_thread_rules()?,
Thread::VirtioPmem => virtio_pmem_thread_rules(),
Thread::VirtioRng => virtio_rng_thread_rules(),
Thread::VirtioVhostBlk => virtio_vhost_blk_thread_rules(),
@ -473,7 +481,7 @@ fn get_seccomp_filter_log(thread_type: Thread) -> Result<SeccompFilter, Error> {
Thread::VirtioIommu => virtio_iommu_thread_rules(),
Thread::VirtioMem => virtio_mem_thread_rules(),
Thread::VirtioNet => virtio_net_thread_rules(),
Thread::VirtioNetCtl => virtio_net_ctl_thread_rules(),
Thread::VirtioNetCtl => virtio_net_ctl_thread_rules()?,
Thread::VirtioPmem => virtio_pmem_thread_rules(),
Thread::VirtioRng => virtio_rng_thread_rules(),
Thread::VirtioVhostBlk => virtio_vhost_blk_thread_rules(),

View File

@ -247,7 +247,7 @@ impl VirtioDevice for Net {
mem: mem.clone(),
kill_evt,
pause_evt,
ctrl_q: NetCtrl::new(cvq_queue, cvq_queue_evt),
ctrl_q: NetCtrl::new(cvq_queue, cvq_queue_evt, None),
};
let paused = self.common.paused.clone();