1111from dataclasses import dataclass
1212from enum import Enum
1313
14- from lib .py import ksft_run , ksft_exit , ksft_eq , ksft_ne , ksft_pr
15- from lib .py import KsftFailEx , NetDrvEpEnv , EthtoolFamily , NlError
14+ from lib .py import ksft_run , ksft_exit , ksft_eq , ksft_ge , ksft_ne , ksft_pr
15+ from lib .py import KsftFailEx , NetDrvEpEnv
16+ from lib .py import EthtoolFamily , NetdevFamily , NlError
1617from lib .py import bkg , cmd , rand_port , wait_port_listen
1718from lib .py import ip , bpftool , defer
1819
@@ -671,6 +672,88 @@ def test_xdp_native_adjst_head_shrnk_data(cfg):
671672 _validate_res (res , offset_lst , pkt_sz_lst )
672673
673674
675+ def _test_xdp_native_ifc_stats (cfg , act ):
676+ cfg .require_cmd ("socat" )
677+
678+ bpf_info = BPFProgInfo ("xdp_prog" , "xdp_native.bpf.o" , "xdp" , 1500 )
679+ prog_info = _load_xdp_prog (cfg , bpf_info )
680+ port = rand_port ()
681+
682+ _set_xdp_map ("map_xdp_setup" , TestConfig .MODE .value , act .value )
683+ _set_xdp_map ("map_xdp_setup" , TestConfig .PORT .value , port )
684+
685+ # Discard the input, but we need a listener to avoid ICMP errors
686+ rx_udp = f"socat -{ cfg .addr_ipver } -T 2 -u UDP-RECV:{ port } ,reuseport " + \
687+ "/dev/null"
688+ # Listener runs on "remote" in case of XDP_TX
689+ rx_host = cfg .remote if act == XDPAction .TX else None
690+ # We want to spew 2000 packets quickly, bash seems to do a good enough job
691+ tx_udp = f"exec 5<>/dev/udp/{ cfg .addr } /{ port } ; " \
692+ "for i in `seq 2000`; do echo a >&5; done; exec 5>&-"
693+
694+ cfg .wait_hw_stats_settle ()
695+ # Qstats have more clearly defined semantics than rtnetlink.
696+ # XDP is the "first layer of the stack" so XDP packets should be counted
697+ # as received and sent as if the decision was made in the routing layer.
698+ before = cfg .netnl .qstats_get ({"ifindex" : cfg .ifindex }, dump = True )[0 ]
699+
700+ with bkg (rx_udp , host = rx_host , exit_wait = True ):
701+ wait_port_listen (port , proto = "udp" , host = rx_host )
702+ cmd (tx_udp , host = cfg .remote , shell = True )
703+
704+ cfg .wait_hw_stats_settle ()
705+ after = cfg .netnl .qstats_get ({"ifindex" : cfg .ifindex }, dump = True )[0 ]
706+
707+ ksft_ge (after ['rx-packets' ] - before ['rx-packets' ], 2000 )
708+ if act == XDPAction .TX :
709+ ksft_ge (after ['tx-packets' ] - before ['tx-packets' ], 2000 )
710+
711+ expected_pkts = 2000
712+ stats = _get_stats (prog_info ["maps" ]["map_xdp_stats" ])
713+ ksft_eq (stats [XDPStats .RX .value ], expected_pkts , "XDP RX stats mismatch" )
714+ if act == XDPAction .TX :
715+ ksft_eq (stats [XDPStats .TX .value ], expected_pkts , "XDP TX stats mismatch" )
716+
717+ # Flip the ring count back and forth to make sure the stats from XDP rings
718+ # don't get lost.
719+ chans = cfg .ethnl .channels_get ({'header' : {'dev-index' : cfg .ifindex }})
720+ if chans .get ('combined-count' , 0 ) > 1 :
721+ cfg .ethnl .channels_set ({'header' : {'dev-index' : cfg .ifindex },
722+ 'combined-count' : 1 })
723+ cfg .ethnl .channels_set ({'header' : {'dev-index' : cfg .ifindex },
724+ 'combined-count' : chans ['combined-count' ]})
725+ before = after
726+ after = cfg .netnl .qstats_get ({"ifindex" : cfg .ifindex }, dump = True )[0 ]
727+
728+ ksft_ge (after ['rx-packets' ], before ['rx-packets' ])
729+ if act == XDPAction .TX :
730+ ksft_ge (after ['tx-packets' ], before ['tx-packets' ])
731+
732+
733+ def test_xdp_native_qstats_pass (cfg ):
734+ """
735+ Send 2000 messages, expect XDP_PASS, make sure the packets were counted
736+ to interface level qstats (Rx).
737+ """
738+ _test_xdp_native_ifc_stats (cfg , XDPAction .PASS )
739+
740+
741+ def test_xdp_native_qstats_drop (cfg ):
742+ """
743+ Send 2000 messages, expect XDP_DROP, make sure the packets were counted
744+ to interface level qstats (Rx).
745+ """
746+ _test_xdp_native_ifc_stats (cfg , XDPAction .DROP )
747+
748+
749+ def test_xdp_native_qstats_tx (cfg ):
750+ """
751+ Send 2000 messages, expect XDP_TX, make sure the packets were counted
752+ to interface level qstats (Rx and Tx)
753+ """
754+ _test_xdp_native_ifc_stats (cfg , XDPAction .TX )
755+
756+
674757def main ():
675758 """
676759 Main function to execute the XDP tests.
@@ -682,6 +765,7 @@ def main():
682765 """
683766 with NetDrvEpEnv (__file__ ) as cfg :
684767 cfg .ethnl = EthtoolFamily ()
768+ cfg .netnl = NetdevFamily ()
685769 ksft_run (
686770 [
687771 test_xdp_native_pass_sb ,
@@ -694,6 +778,9 @@ def main():
694778 test_xdp_native_adjst_tail_shrnk_data ,
695779 test_xdp_native_adjst_head_grow_data ,
696780 test_xdp_native_adjst_head_shrnk_data ,
781+ test_xdp_native_qstats_pass ,
782+ test_xdp_native_qstats_drop ,
783+ test_xdp_native_qstats_tx ,
697784 ],
698785 args = (cfg ,))
699786 ksft_exit ()
0 commit comments