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