Skip to content

Commit 21c220a

Browse files
authored
feat(virtio) Implement CTRL_RX support for virtio-net (#669)
1 parent 8250baf commit 21c220a

1 file changed

Lines changed: 324 additions & 2 deletions

File tree

awkernel_drivers/src/pcie/virtio/virtio_net.rs

Lines changed: 324 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -47,6 +47,7 @@ const VIRTIO_NET_F_MAC: u64 = 1 << 5;
4747
const VIRTIO_NET_F_MRG_RXBUF: u64 = 1 << 15;
4848
const VIRTIO_NET_F_STATUS: u64 = 1 << 16;
4949
const VIRTIO_NET_F_CTRL_VQ: u64 = 1 << 17;
50+
const VIRTIO_NET_F_CTRL_RX: u64 = 1 << 18;
5051
const VIRTIO_NET_F_SPEED_DUPLEX: u64 = 1 << 63;
5152

5253
// Reserved Feature Bits
@@ -601,6 +602,54 @@ const _VIRTIO_NET_HDR_GSO_UDP: u8 = 3;
601602
const _VIRTIO_NET_HDR_GSO_TCPV6: u8 = 4;
602603
const _VIRTIO_NET_HDR_GSO_ECN: u8 = 0x80;
603604

605+
const VIRTIO_NET_CTRL_RX_CLASS: u8 = 0;
606+
const VIRTIO_NET_CTRL_MAC: u8 = 1;
607+
608+
const VIRTIO_NET_CTRL_RX_PROMISC: u8 = 0;
609+
const VIRTIO_NET_CTRL_RX_ALLMULTI: u8 = 1;
610+
const VIRTIO_NET_CTRL_MAC_TABLE_SET: u8 = 0;
611+
612+
const VIRTIO_NET_CTRL_OK: u8 = 0;
613+
const VIRTIO_NET_CTRL_STAT_PENDING: u8 = 0xff;
614+
const VIRTIO_NET_CTRL_WAIT_SPINS: usize = 100_000;
615+
const VIRTIO_NET_CTRL_MAC_UC_ENTRIES: usize = 1;
616+
const VIRTIO_NET_CTRL_MAC_MC_ENTRIES: usize = 64;
617+
618+
#[repr(C)]
619+
#[derive(Default, Copy, Clone)]
620+
struct VirtioNetCtrlCmd {
621+
class: u8,
622+
cmd: u8,
623+
}
624+
625+
#[repr(C)]
626+
#[derive(Default, Copy, Clone)]
627+
struct VirtioNetCtrlStatus {
628+
ack: u8,
629+
}
630+
631+
#[repr(C)]
632+
#[derive(Default, Copy, Clone)]
633+
struct VirtioNetCtrlRx {
634+
onoff: u8,
635+
}
636+
637+
#[repr(C)]
638+
#[derive(Copy, Clone)]
639+
struct VirtioNetCtrlMacTable<const N: usize> {
640+
nentries: u32,
641+
macs: [[u8; 6]; N],
642+
}
643+
644+
impl<const N: usize> Default for VirtioNetCtrlMacTable<N> {
645+
fn default() -> Self {
646+
Self {
647+
nentries: 0,
648+
macs: [[0; 6]; N],
649+
}
650+
}
651+
}
652+
604653
pub fn match_device(vendor: u16, id: u16) -> bool {
605654
vendor == pcie_id::VIRTIO_VENDOR_ID && id == VIRTIO_NET_ID
606655
}
@@ -646,6 +695,12 @@ struct VirtioNetInner {
646695
flags: NetFlags,
647696
capabilities: NetCapabilities,
648697
multicast_addrs: MulticastAddrs,
698+
ctrl_cmd: Option<DMAPool<VirtioNetCtrlCmd>>,
699+
ctrl_status: Option<DMAPool<VirtioNetCtrlStatus>>,
700+
ctrl_rx: Option<DMAPool<VirtioNetCtrlRx>>,
701+
ctrl_mac_uc: Option<DMAPool<VirtioNetCtrlMacTable<VIRTIO_NET_CTRL_MAC_UC_ENTRIES>>>,
702+
ctrl_mac_mc: Option<DMAPool<VirtioNetCtrlMacTable<VIRTIO_NET_CTRL_MAC_MC_ENTRIES>>>,
703+
ctrl_mac_table_supported: bool,
649704
virtqueues: Vec<Queue>,
650705
ctrl_vq: Option<Mutex<Virtq>>,
651706
irq_to_type: BTreeMap<u16, IRQType>,
@@ -668,6 +723,12 @@ impl VirtioNetInner {
668723
multicast_addrs: MulticastAddrs::new(),
669724
virtqueues: Vec::new(),
670725
ctrl_vq: None,
726+
ctrl_cmd: None,
727+
ctrl_status: None,
728+
ctrl_rx: None,
729+
ctrl_mac_uc: None,
730+
ctrl_mac_mc: None,
731+
ctrl_mac_table_supported: true,
671732
irq_to_type: BTreeMap::new(),
672733
pcie_int: PCIeInt::None,
673734
}
@@ -718,6 +779,7 @@ impl VirtioNetInner {
718779
self.driver_features |= VIRTIO_NET_F_MRG_RXBUF;
719780
self.driver_features |= VIRTIO_NET_F_STATUS;
720781
self.driver_features |= VIRTIO_NET_F_CTRL_VQ;
782+
self.driver_features |= VIRTIO_NET_F_CTRL_RX;
721783
self.driver_features |= VIRTIO_NET_F_SPEED_DUPLEX;
722784

723785
self.virtio_pci_negotiate_features()?;
@@ -758,6 +820,33 @@ impl VirtioNetInner {
758820
vq.virtio_start_vq_intr();
759821

760822
self.ctrl_vq = Some(Mutex::new(vq));
823+
824+
let mut ctrl_cmd: DMAPool<VirtioNetCtrlCmd> =
825+
DMAPool::new(0, 1).ok_or(VirtioDriverErr::DMAPool)?;
826+
*ctrl_cmd.as_mut() = VirtioNetCtrlCmd::default();
827+
self.ctrl_cmd = Some(ctrl_cmd);
828+
829+
let mut ctrl_status: DMAPool<VirtioNetCtrlStatus> =
830+
DMAPool::new(0, 1).ok_or(VirtioDriverErr::DMAPool)?;
831+
*ctrl_status.as_mut() = VirtioNetCtrlStatus {
832+
ack: VIRTIO_NET_CTRL_STAT_PENDING,
833+
};
834+
self.ctrl_status = Some(ctrl_status);
835+
836+
let mut ctrl_rx: DMAPool<VirtioNetCtrlRx> =
837+
DMAPool::new(0, 1).ok_or(VirtioDriverErr::DMAPool)?;
838+
*ctrl_rx.as_mut() = VirtioNetCtrlRx::default();
839+
self.ctrl_rx = Some(ctrl_rx);
840+
841+
let mut ctrl_mac_uc: DMAPool<VirtioNetCtrlMacTable<VIRTIO_NET_CTRL_MAC_UC_ENTRIES>> =
842+
DMAPool::new(0, 1).ok_or(VirtioDriverErr::DMAPool)?;
843+
*ctrl_mac_uc.as_mut() = VirtioNetCtrlMacTable::default();
844+
self.ctrl_mac_uc = Some(ctrl_mac_uc);
845+
846+
let mut ctrl_mac_mc: DMAPool<VirtioNetCtrlMacTable<VIRTIO_NET_CTRL_MAC_MC_ENTRIES>> =
847+
DMAPool::new(0, 1).ok_or(VirtioDriverErr::DMAPool)?;
848+
*ctrl_mac_mc.as_mut() = VirtioNetCtrlMacTable::default();
849+
self.ctrl_mac_mc = Some(ctrl_mac_mc);
761850
}
762851

763852
{
@@ -871,6 +960,7 @@ impl VirtioNetInner {
871960
}
872961

873962
fn virtio_pci_kick(&mut self, idx: u16) -> Result<(), VirtioDriverErr> {
963+
self.common_cfg.virtio_set_queue_select(idx)?;
874964
let queue_notify_off = self.common_cfg.virtio_get_queue_notify_off()? as usize;
875965
let notify_off_multiplier = self.notify_off_multiplier as usize;
876966
let offset = queue_notify_off * notify_off_multiplier;
@@ -927,6 +1017,15 @@ impl VirtioNetInner {
9271017
self.virtio_pci_set_msix_queue_vector(idx, vector)?;
9281018
}
9291019

1020+
if let Some(ctrl_vq) = &self.ctrl_vq {
1021+
let (idx, vector) = {
1022+
let mut node = MCSNode::new();
1023+
let ctrl_vq = ctrl_vq.lock(&mut node);
1024+
(ctrl_vq.vq_index, ctrl_vq.vq_intr_vec)
1025+
};
1026+
self.virtio_pci_set_msix_queue_vector(idx, vector)?;
1027+
}
1028+
9301029
self.virtio_pci_set_msix_config_vector(0)
9311030
}
9321031

@@ -1120,9 +1219,232 @@ impl VirtioNetInner {
11201219
Ok(())
11211220
}
11221221

1222+
fn vio_ctrl_finish(&mut self) {
1223+
if let Some(ctrl_vq) = &self.ctrl_vq {
1224+
let mut node = MCSNode::new();
1225+
let mut ctrl_vq = ctrl_vq.lock(&mut node);
1226+
while let Some((slot, _)) = ctrl_vq.virtio_dequeue() {
1227+
ctrl_vq.virtio_dequeue_commit(slot);
1228+
}
1229+
}
1230+
}
1231+
1232+
fn vio_ctrl_submit(&mut self, vq_index: u16) -> Result<(), VirtioDriverErr> {
1233+
self.virtio_pci_kick(vq_index)?;
1234+
1235+
for _ in 0..VIRTIO_NET_CTRL_WAIT_SPINS {
1236+
self.vio_ctrl_finish();
1237+
1238+
if let Some(status) = &self.ctrl_status {
1239+
if status.as_ref().ack != VIRTIO_NET_CTRL_STAT_PENDING {
1240+
if status.as_ref().ack == VIRTIO_NET_CTRL_OK {
1241+
return Ok(());
1242+
}
1243+
return Err(VirtioDriverErr::InitFailure);
1244+
}
1245+
} else {
1246+
return Err(VirtioDriverErr::InitFailure);
1247+
}
1248+
1249+
core::hint::spin_loop();
1250+
}
1251+
1252+
Err(VirtioDriverErr::InitFailure)
1253+
}
1254+
1255+
fn vio_ctrl_start(
1256+
&mut self,
1257+
class: u8,
1258+
cmd: u8,
1259+
nsegs: usize,
1260+
) -> Result<(usize, u16), VirtioDriverErr> {
1261+
let cmd_phy = {
1262+
let ctrl_cmd = self.ctrl_cmd.as_mut().ok_or(VirtioDriverErr::InitFailure)?;
1263+
ctrl_cmd.as_mut().class = class;
1264+
ctrl_cmd.as_mut().cmd = cmd;
1265+
ctrl_cmd.get_phy_addr().as_usize()
1266+
};
1267+
1268+
if let Some(ctrl_status) = self.ctrl_status.as_mut() {
1269+
ctrl_status.as_mut().ack = VIRTIO_NET_CTRL_STAT_PENDING;
1270+
} else {
1271+
return Err(VirtioDriverErr::InitFailure);
1272+
}
1273+
1274+
let ctrl_vq = self.ctrl_vq.as_ref().ok_or(VirtioDriverErr::InitFailure)?;
1275+
let mut node = MCSNode::new();
1276+
let mut ctrl_vq = ctrl_vq.lock(&mut node);
1277+
let slot = ctrl_vq
1278+
.virtio_enqueue_prep()
1279+
.ok_or(VirtioDriverErr::NoSlot)?;
1280+
ctrl_vq.virtio_enqueue_reserve(slot, nsegs)?;
1281+
ctrl_vq.virtio_enqueue(
1282+
slot,
1283+
cmd_phy,
1284+
core::mem::size_of::<VirtioNetCtrlCmd>(),
1285+
true,
1286+
);
1287+
1288+
Ok((slot, ctrl_vq.vq_index))
1289+
}
1290+
1291+
fn vio_ctrl_rx(&mut self, cmd: u8, onoff: bool) -> Result<(), VirtioDriverErr> {
1292+
let (rx_phy, status_phy) = {
1293+
let ctrl_rx = self.ctrl_rx.as_mut().ok_or(VirtioDriverErr::InitFailure)?;
1294+
ctrl_rx.as_mut().onoff = if onoff { 1 } else { 0 };
1295+
let rx_phy = ctrl_rx.get_phy_addr().as_usize();
1296+
1297+
let ctrl_status = self
1298+
.ctrl_status
1299+
.as_mut()
1300+
.ok_or(VirtioDriverErr::InitFailure)?;
1301+
ctrl_status.as_mut().ack = VIRTIO_NET_CTRL_STAT_PENDING;
1302+
let status_phy = ctrl_status.get_phy_addr().as_usize();
1303+
1304+
(rx_phy, status_phy)
1305+
};
1306+
1307+
let (slot, vq_index) = self.vio_ctrl_start(VIRTIO_NET_CTRL_RX_CLASS, cmd, 3)?;
1308+
1309+
{
1310+
let ctrl_vq = self.ctrl_vq.as_ref().ok_or(VirtioDriverErr::InitFailure)?;
1311+
let mut node = MCSNode::new();
1312+
let mut ctrl_vq = ctrl_vq.lock(&mut node);
1313+
1314+
ctrl_vq.virtio_enqueue(slot, rx_phy, core::mem::size_of::<VirtioNetCtrlRx>(), true);
1315+
ctrl_vq.virtio_enqueue(
1316+
slot,
1317+
status_phy,
1318+
core::mem::size_of::<VirtioNetCtrlStatus>(),
1319+
false,
1320+
);
1321+
ctrl_vq.virtio_enqueue_commit(slot);
1322+
ctrl_vq.publish_avail_idx();
1323+
membar_sync();
1324+
}
1325+
1326+
self.vio_ctrl_submit(vq_index)
1327+
}
1328+
1329+
fn vio_set_rx_filter(&mut self, multicast_list: &[[u8; 6]]) -> Result<(), VirtioDriverErr> {
1330+
if !self.ctrl_mac_table_supported {
1331+
return Err(VirtioDriverErr::InitFailure);
1332+
}
1333+
1334+
let len_uc = core::mem::size_of::<u32>() + 6;
1335+
let len_mc = core::mem::size_of::<u32>() + (multicast_list.len() * 6);
1336+
1337+
let (uc_phy, mc_phy, status_phy) = {
1338+
let ctrl_status = self
1339+
.ctrl_status
1340+
.as_mut()
1341+
.ok_or(VirtioDriverErr::InitFailure)?;
1342+
ctrl_status.as_mut().ack = VIRTIO_NET_CTRL_STAT_PENDING;
1343+
let status_phy = ctrl_status.get_phy_addr().as_usize();
1344+
1345+
let ctrl_mac_uc = self
1346+
.ctrl_mac_uc
1347+
.as_mut()
1348+
.ok_or(VirtioDriverErr::InitFailure)?;
1349+
ctrl_mac_uc.as_mut().nentries = 1;
1350+
ctrl_mac_uc.as_mut().macs[0] = self.mac_addr;
1351+
let uc_phy = ctrl_mac_uc.get_phy_addr().as_usize();
1352+
1353+
let ctrl_mac_mc = self
1354+
.ctrl_mac_mc
1355+
.as_mut()
1356+
.ok_or(VirtioDriverErr::InitFailure)?;
1357+
ctrl_mac_mc.as_mut().nentries = multicast_list.len() as u32;
1358+
for (i, addr) in multicast_list.iter().enumerate() {
1359+
ctrl_mac_mc.as_mut().macs[i] = *addr;
1360+
}
1361+
for i in multicast_list.len()..VIRTIO_NET_CTRL_MAC_MC_ENTRIES {
1362+
ctrl_mac_mc.as_mut().macs[i] = [0; 6];
1363+
}
1364+
let mc_phy = ctrl_mac_mc.get_phy_addr().as_usize();
1365+
1366+
(uc_phy, mc_phy, status_phy)
1367+
};
1368+
1369+
let (slot, vq_index) =
1370+
self.vio_ctrl_start(VIRTIO_NET_CTRL_MAC, VIRTIO_NET_CTRL_MAC_TABLE_SET, 4)?;
1371+
1372+
{
1373+
let ctrl_vq = self.ctrl_vq.as_ref().ok_or(VirtioDriverErr::InitFailure)?;
1374+
let mut node = MCSNode::new();
1375+
let mut ctrl_vq = ctrl_vq.lock(&mut node);
1376+
1377+
ctrl_vq.virtio_enqueue(slot, uc_phy, len_uc, true);
1378+
ctrl_vq.virtio_enqueue(slot, mc_phy, len_mc, true);
1379+
ctrl_vq.virtio_enqueue(
1380+
slot,
1381+
status_phy,
1382+
core::mem::size_of::<VirtioNetCtrlStatus>(),
1383+
false,
1384+
);
1385+
ctrl_vq.virtio_enqueue_commit(slot);
1386+
ctrl_vq.publish_avail_idx();
1387+
membar_sync();
1388+
}
1389+
1390+
let ret = self.vio_ctrl_submit(vq_index);
1391+
if ret.is_err() {
1392+
self.ctrl_mac_table_supported = false;
1393+
log::info!("virtio-net: disable MAC_TABLE_SET after failure");
1394+
}
1395+
ret
1396+
}
1397+
11231398
fn vio_iff(&mut self) {
1124-
self.flags.insert(NetFlags::MULTICAST);
1125-
self.flags.insert(NetFlags::PROMISC);
1399+
self.flags.remove(NetFlags::ALLMULTI);
1400+
1401+
if !self.virtio_has_feature(VIRTIO_NET_F_CTRL_RX)
1402+
|| self.ctrl_vq.is_none()
1403+
|| self.ctrl_cmd.is_none()
1404+
|| self.ctrl_status.is_none()
1405+
|| self.ctrl_rx.is_none()
1406+
|| self.ctrl_mac_uc.is_none()
1407+
|| self.ctrl_mac_mc.is_none()
1408+
{
1409+
self.flags.insert(NetFlags::ALLMULTI);
1410+
self.flags.insert(NetFlags::PROMISC);
1411+
return;
1412+
}
1413+
1414+
let mut promisc = self.flags.contains(NetFlags::PROMISC);
1415+
let mut allmulti = false;
1416+
1417+
let multicast_list: Vec<[u8; 6]> = self.multicast_addrs.iter().copied().collect();
1418+
if !self.ctrl_mac_table_supported {
1419+
allmulti = true;
1420+
promisc = true;
1421+
self.flags.insert(NetFlags::ALLMULTI);
1422+
self.flags.insert(NetFlags::PROMISC);
1423+
} else if promisc || multicast_list.len() >= VIRTIO_NET_CTRL_MAC_MC_ENTRIES {
1424+
self.flags.insert(NetFlags::ALLMULTI);
1425+
if !promisc {
1426+
allmulti = true;
1427+
}
1428+
} else if self.vio_set_rx_filter(&multicast_list).is_err() {
1429+
allmulti = true;
1430+
self.flags.insert(NetFlags::ALLMULTI);
1431+
}
1432+
1433+
if self
1434+
.vio_ctrl_rx(VIRTIO_NET_CTRL_RX_ALLMULTI, allmulti)
1435+
.is_err()
1436+
{
1437+
promisc = true;
1438+
self.flags.insert(NetFlags::ALLMULTI);
1439+
self.flags.insert(NetFlags::PROMISC);
1440+
}
1441+
1442+
if self
1443+
.vio_ctrl_rx(VIRTIO_NET_CTRL_RX_PROMISC, promisc)
1444+
.is_err()
1445+
{
1446+
self.flags.insert(NetFlags::PROMISC);
1447+
}
11261448
}
11271449

11281450
fn vio_init(&mut self) -> Result<(), VirtioDriverErr> {

0 commit comments

Comments
 (0)