@@ -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,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