1#!/usr/bin/env python3 2# SPDX-License-Identifier: GPL-2.0 3 4""" 5This file contains tests to verify native XDP support in network drivers. 6The tests utilize the BPF program `xdp_native.bpf.o` from the `selftests.net.lib` 7directory, with each test focusing on a specific aspect of XDP functionality. 8""" 9import random 10import string 11from dataclasses import dataclass 12from enum import Enum 13 14from lib.py import ksft_run, ksft_exit, ksft_eq, ksft_ne, ksft_pr 15from lib.py import KsftFailEx, NetDrvEpEnv, EthtoolFamily, NlError 16from lib.py import bkg, cmd, rand_port, wait_port_listen 17from lib.py import ip, bpftool, defer 18 19 20class TestConfig(Enum): 21 """Enum for XDP configuration options.""" 22 MODE = 0 # Configures the BPF program for a specific test 23 PORT = 1 # Port configuration to communicate with the remote host 24 ADJST_OFFSET = 2 # Tail/Head adjustment offset for extension/shrinking 25 ADJST_TAG = 3 # Adjustment tag to annotate the start and end of extension 26 27 28class XDPAction(Enum): 29 """Enum for XDP actions.""" 30 PASS = 0 # Pass the packet up to the stack 31 DROP = 1 # Drop the packet 32 TX = 2 # Route the packet to the remote host 33 TAIL_ADJST = 3 # Adjust the tail of the packet 34 HEAD_ADJST = 4 # Adjust the head of the packet 35 36 37class XDPStats(Enum): 38 """Enum for XDP statistics.""" 39 RX = 0 # Count of valid packets received for testing 40 PASS = 1 # Count of packets passed up to the stack 41 DROP = 2 # Count of packets dropped 42 TX = 3 # Count of incoming packets routed to the remote host 43 ABORT = 4 # Count of packets that were aborted 44 45 46@dataclass 47class BPFProgInfo: 48 """Data class to store information about a BPF program.""" 49 name: str # Name of the BPF program 50 file: str # BPF program object file 51 xdp_sec: str = "xdp" # XDP section name (e.g., "xdp" or "xdp.frags") 52 mtu: int = 1500 # Maximum Transmission Unit, default is 1500 53 54 55def _exchg_udp(cfg, port, test_string): 56 """ 57 Exchanges UDP packets between a local and remote host using the socat tool. 58 59 Args: 60 cfg: Configuration object containing network settings. 61 port: Port number to use for the UDP communication. 62 test_string: String that the remote host will send. 63 64 Returns: 65 The string received by the test host. 66 """ 67 cfg.require_cmd("socat", remote=True) 68 69 rx_udp_cmd = f"socat -{cfg.addr_ipver} -T 2 -u UDP-RECV:{port},reuseport STDOUT" 70 tx_udp_cmd = f"echo -n {test_string} | socat -t 2 -u STDIN UDP:{cfg.baddr}:{port}" 71 72 with bkg(rx_udp_cmd, exit_wait=True) as nc: 73 wait_port_listen(port, proto="udp") 74 cmd(tx_udp_cmd, host=cfg.remote, shell=True) 75 76 return nc.stdout.strip() 77 78 79def _test_udp(cfg, port, size=256): 80 """ 81 Tests UDP packet exchange between a local and remote host. 82 83 Args: 84 cfg: Configuration object containing network settings. 85 port: Port number to use for the UDP communication. 86 size: The length of the test string to be exchanged, default is 256 characters. 87 88 Returns: 89 bool: True if the received string matches the sent string, False otherwise. 90 """ 91 test_str = "".join(random.choice(string.ascii_lowercase) for _ in range(size)) 92 recvd_str = _exchg_udp(cfg, port, test_str) 93 94 return recvd_str == test_str 95 96 97def _load_xdp_prog(cfg, bpf_info): 98 """ 99 Loads an XDP program onto a network interface. 100 101 Args: 102 cfg: Configuration object containing network settings. 103 bpf_info: BPFProgInfo object containing information about the BPF program. 104 105 Returns: 106 dict: A dictionary containing the XDP program ID, name, and associated map IDs. 107 """ 108 abs_path = cfg.net_lib_dir / bpf_info.file 109 prog_info = {} 110 111 cmd(f"ip link set dev {cfg.remote_ifname} mtu {bpf_info.mtu}", shell=True, host=cfg.remote) 112 defer(ip, f"link set dev {cfg.remote_ifname} mtu 1500", host=cfg.remote) 113 114 cmd( 115 f"ip link set dev {cfg.ifname} mtu {bpf_info.mtu} xdp obj {abs_path} sec {bpf_info.xdp_sec}", 116 shell=True 117 ) 118 defer(ip, f"link set dev {cfg.ifname} mtu 1500 xdp off") 119 120 xdp_info = ip(f"-d link show dev {cfg.ifname}", json=True)[0] 121 prog_info["id"] = xdp_info["xdp"]["prog"]["id"] 122 prog_info["name"] = xdp_info["xdp"]["prog"]["name"] 123 prog_id = prog_info["id"] 124 125 map_ids = bpftool(f"prog show id {prog_id}", json=True)["map_ids"] 126 prog_info["maps"] = {} 127 for map_id in map_ids: 128 name = bpftool(f"map show id {map_id}", json=True)["name"] 129 prog_info["maps"][name] = map_id 130 131 return prog_info 132 133 134def format_hex_bytes(value): 135 """ 136 Helper function that converts an integer into a formatted hexadecimal byte string. 137 138 Args: 139 value: An integer representing the number to be converted. 140 141 Returns: 142 A string representing hexadecimal equivalent of value, with bytes separated by spaces. 143 """ 144 hex_str = value.to_bytes(4, byteorder='little', signed=True) 145 return ' '.join(f'{byte:02x}' for byte in hex_str) 146 147 148def _set_xdp_map(map_name, key, value): 149 """ 150 Updates an XDP map with a given key-value pair using bpftool. 151 152 Args: 153 map_name: The name of the XDP map to update. 154 key: The key to update in the map, formatted as a hexadecimal string. 155 value: The value to associate with the key, formatted as a hexadecimal string. 156 """ 157 key_formatted = format_hex_bytes(key) 158 value_formatted = format_hex_bytes(value) 159 bpftool( 160 f"map update name {map_name} key hex {key_formatted} value hex {value_formatted}" 161 ) 162 163 164def _get_stats(xdp_map_id): 165 """ 166 Retrieves and formats statistics from an XDP map. 167 168 Args: 169 xdp_map_id: The ID of the XDP map from which to retrieve statistics. 170 171 Returns: 172 A dictionary containing formatted packet statistics for various XDP actions. 173 The keys are based on the XDPStats Enum values. 174 175 Raises: 176 KsftFailEx: If the stats retrieval fails. 177 """ 178 stats_dump = bpftool(f"map dump id {xdp_map_id}", json=True) 179 if not stats_dump: 180 raise KsftFailEx(f"Failed to get stats for map {xdp_map_id}") 181 182 stats_formatted = {} 183 for key in range(0, 5): 184 val = stats_dump[key]["formatted"]["value"] 185 if stats_dump[key]["formatted"]["key"] == XDPStats.RX.value: 186 stats_formatted[XDPStats.RX.value] = val 187 elif stats_dump[key]["formatted"]["key"] == XDPStats.PASS.value: 188 stats_formatted[XDPStats.PASS.value] = val 189 elif stats_dump[key]["formatted"]["key"] == XDPStats.DROP.value: 190 stats_formatted[XDPStats.DROP.value] = val 191 elif stats_dump[key]["formatted"]["key"] == XDPStats.TX.value: 192 stats_formatted[XDPStats.TX.value] = val 193 elif stats_dump[key]["formatted"]["key"] == XDPStats.ABORT.value: 194 stats_formatted[XDPStats.ABORT.value] = val 195 196 return stats_formatted 197 198 199def _test_pass(cfg, bpf_info, msg_sz): 200 """ 201 Tests the XDP_PASS action by exchanging UDP packets. 202 203 Args: 204 cfg: Configuration object containing network settings. 205 bpf_info: BPFProgInfo object containing information about the BPF program. 206 msg_sz: Size of the test message to send. 207 """ 208 209 prog_info = _load_xdp_prog(cfg, bpf_info) 210 port = rand_port() 211 212 _set_xdp_map("map_xdp_setup", TestConfig.MODE.value, XDPAction.PASS.value) 213 _set_xdp_map("map_xdp_setup", TestConfig.PORT.value, port) 214 215 ksft_eq(_test_udp(cfg, port, msg_sz), True, "UDP packet exchange failed") 216 stats = _get_stats(prog_info["maps"]["map_xdp_stats"]) 217 218 ksft_ne(stats[XDPStats.RX.value], 0, "RX stats should not be zero") 219 ksft_eq(stats[XDPStats.RX.value], stats[XDPStats.PASS.value], "RX and PASS stats mismatch") 220 221 222def test_xdp_native_pass_sb(cfg): 223 """ 224 Tests the XDP_PASS action for single buffer case. 225 226 Args: 227 cfg: Configuration object containing network settings. 228 """ 229 bpf_info = BPFProgInfo("xdp_prog", "xdp_native.bpf.o", "xdp", 1500) 230 231 _test_pass(cfg, bpf_info, 256) 232 233 234def test_xdp_native_pass_mb(cfg): 235 """ 236 Tests the XDP_PASS action for a multi-buff size. 237 238 Args: 239 cfg: Configuration object containing network settings. 240 """ 241 bpf_info = BPFProgInfo("xdp_prog_frags", "xdp_native.bpf.o", "xdp.frags", 9000) 242 243 _test_pass(cfg, bpf_info, 8000) 244 245 246def _test_drop(cfg, bpf_info, msg_sz): 247 """ 248 Tests the XDP_DROP action by exchanging UDP packets. 249 250 Args: 251 cfg: Configuration object containing network settings. 252 bpf_info: BPFProgInfo object containing information about the BPF program. 253 msg_sz: Size of the test message to send. 254 """ 255 256 prog_info = _load_xdp_prog(cfg, bpf_info) 257 port = rand_port() 258 259 _set_xdp_map("map_xdp_setup", TestConfig.MODE.value, XDPAction.DROP.value) 260 _set_xdp_map("map_xdp_setup", TestConfig.PORT.value, port) 261 262 ksft_eq(_test_udp(cfg, port, msg_sz), False, "UDP packet exchange should fail") 263 stats = _get_stats(prog_info["maps"]["map_xdp_stats"]) 264 265 ksft_ne(stats[XDPStats.RX.value], 0, "RX stats should be zero") 266 ksft_eq(stats[XDPStats.RX.value], stats[XDPStats.DROP.value], "RX and DROP stats mismatch") 267 268 269def test_xdp_native_drop_sb(cfg): 270 """ 271 Tests the XDP_DROP action for a signle-buff case. 272 273 Args: 274 cfg: Configuration object containing network settings. 275 """ 276 bpf_info = BPFProgInfo("xdp_prog", "xdp_native.bpf.o", "xdp", 1500) 277 278 _test_drop(cfg, bpf_info, 256) 279 280 281def test_xdp_native_drop_mb(cfg): 282 """ 283 Tests the XDP_DROP action for a multi-buff case. 284 285 Args: 286 cfg: Configuration object containing network settings. 287 """ 288 bpf_info = BPFProgInfo("xdp_prog_frags", "xdp_native.bpf.o", "xdp.frags", 9000) 289 290 _test_drop(cfg, bpf_info, 8000) 291 292 293def test_xdp_native_tx_mb(cfg): 294 """ 295 Tests the XDP_TX action for a multi-buff case. 296 297 Args: 298 cfg: Configuration object containing network settings. 299 """ 300 cfg.require_cmd("socat", remote=True) 301 302 bpf_info = BPFProgInfo("xdp_prog_frags", "xdp_native.bpf.o", "xdp.frags", 9000) 303 prog_info = _load_xdp_prog(cfg, bpf_info) 304 port = rand_port() 305 306 _set_xdp_map("map_xdp_setup", TestConfig.MODE.value, XDPAction.TX.value) 307 _set_xdp_map("map_xdp_setup", TestConfig.PORT.value, port) 308 309 test_string = ''.join(random.choice(string.ascii_lowercase) for _ in range(8000)) 310 rx_udp = f"socat -{cfg.addr_ipver} -T 2 -u UDP-RECV:{port},reuseport STDOUT" 311 tx_udp = f"echo {test_string} | socat -t 2 -u STDIN UDP:{cfg.baddr}:{port}" 312 313 with bkg(rx_udp, host=cfg.remote, exit_wait=True) as rnc: 314 wait_port_listen(port, proto="udp", host=cfg.remote) 315 cmd(tx_udp, host=cfg.remote, shell=True) 316 317 stats = _get_stats(prog_info['maps']['map_xdp_stats']) 318 319 ksft_eq(rnc.stdout.strip(), test_string, "UDP packet exchange failed") 320 ksft_eq(stats[XDPStats.TX.value], 1, "TX stats mismatch") 321 322 323def _validate_res(res, offset_lst, pkt_sz_lst): 324 """ 325 Validates the result of a test. 326 327 Args: 328 res: The result of the test, which should be a dictionary with a "status" key. 329 330 Raises: 331 KsftFailEx: If the test fails to pass any combination of offset and packet size. 332 """ 333 if "status" not in res: 334 raise KsftFailEx("Missing 'status' key in result dictionary") 335 336 # Validate that not a single case was successful 337 if res["status"] == "fail": 338 if res["offset"] == offset_lst[0] and res["pkt_sz"] == pkt_sz_lst[0]: 339 raise KsftFailEx(f"{res['reason']}") 340 341 # Get the previous offset and packet size to report the successful run 342 tmp_idx = offset_lst.index(res["offset"]) 343 prev_offset = offset_lst[tmp_idx - 1] 344 if tmp_idx == 0: 345 tmp_idx = pkt_sz_lst.index(res["pkt_sz"]) 346 prev_pkt_sz = pkt_sz_lst[tmp_idx - 1] 347 else: 348 prev_pkt_sz = res["pkt_sz"] 349 350 # Use these values for error reporting 351 ksft_pr( 352 f"Failed run: pkt_sz {res['pkt_sz']}, offset {res['offset']}. " 353 f"Last successful run: pkt_sz {prev_pkt_sz}, offset {prev_offset}. " 354 f"Reason: {res['reason']}" 355 ) 356 357 358def _check_for_failures(recvd_str, stats): 359 """ 360 Checks for common failures while adjusting headroom or tailroom. 361 362 Args: 363 recvd_str: The string received from the remote host after sending a test string. 364 stats: A dictionary containing formatted packet statistics for various XDP actions. 365 366 Returns: 367 str: A string describing the failure reason if a failure is detected, otherwise None. 368 """ 369 370 # Any adjustment failure result in an abort hence, we track this counter 371 if stats[XDPStats.ABORT.value] != 0: 372 return "Adjustment failed" 373 374 # Since we are using aggregate stats for a single test across all offsets and packet sizes 375 # we can't use RX stats only to track data exchange failure without taking a previous 376 # snapshot. An easier way is to simply check for non-zero length of received string. 377 if len(recvd_str) == 0: 378 return "Data exchange failed" 379 380 # Check for RX and PASS stats mismatch. Ideally, they should be equal for a successful run 381 if stats[XDPStats.RX.value] != stats[XDPStats.PASS.value]: 382 return "RX stats mismatch" 383 384 return None 385 386 387def _test_xdp_native_tail_adjst(cfg, pkt_sz_lst, offset_lst): 388 """ 389 Tests the XDP tail adjustment functionality. 390 391 This function loads the appropriate XDP program based on the provided 392 program name and configures the XDP map for tail adjustment. It then 393 validates the tail adjustment by sending and receiving UDP packets 394 with specified packet sizes and offsets. 395 396 Args: 397 cfg: Configuration object containing network settings. 398 prog: Name of the XDP program to load. 399 pkt_sz_lst: List of packet sizes to test. 400 offset_lst: List of offsets to validate support for tail adjustment. 401 402 Returns: 403 dict: A dictionary with test status and failure details if applicable. 404 """ 405 port = rand_port() 406 bpf_info = BPFProgInfo("xdp_prog_frags", "xdp_native.bpf.o", "xdp.frags", 9000) 407 408 prog_info = _load_xdp_prog(cfg, bpf_info) 409 410 # Configure the XDP map for tail adjustment 411 _set_xdp_map("map_xdp_setup", TestConfig.MODE.value, XDPAction.TAIL_ADJST.value) 412 _set_xdp_map("map_xdp_setup", TestConfig.PORT.value, port) 413 414 for offset in offset_lst: 415 tag = format(random.randint(65, 90), "02x") 416 417 _set_xdp_map("map_xdp_setup", TestConfig.ADJST_OFFSET.value, offset) 418 if offset > 0: 419 _set_xdp_map("map_xdp_setup", TestConfig.ADJST_TAG.value, int(tag, 16)) 420 421 for pkt_sz in pkt_sz_lst: 422 test_str = "".join(random.choice(string.ascii_lowercase) for _ in range(pkt_sz)) 423 recvd_str = _exchg_udp(cfg, port, test_str) 424 stats = _get_stats(prog_info["maps"]["map_xdp_stats"]) 425 426 failure = _check_for_failures(recvd_str, stats) 427 if failure is not None: 428 return { 429 "status": "fail", 430 "reason": failure, 431 "offset": offset, 432 "pkt_sz": pkt_sz, 433 } 434 435 # Validate data content based on offset direction 436 expected_data = None 437 if offset > 0: 438 expected_data = test_str + (offset * chr(int(tag, 16))) 439 else: 440 expected_data = test_str[0:pkt_sz + offset] 441 442 if recvd_str != expected_data: 443 return { 444 "status": "fail", 445 "reason": "Data mismatch", 446 "offset": offset, 447 "pkt_sz": pkt_sz, 448 } 449 450 return {"status": "pass"} 451 452 453def test_xdp_native_adjst_tail_grow_data(cfg): 454 """ 455 Tests the XDP tail adjustment by growing packet data. 456 457 Args: 458 cfg: Configuration object containing network settings. 459 """ 460 pkt_sz_lst = [512, 1024, 2048] 461 offset_lst = [1, 16, 32, 64, 128, 256] 462 res = _test_xdp_native_tail_adjst( 463 cfg, 464 pkt_sz_lst, 465 offset_lst, 466 ) 467 468 _validate_res(res, offset_lst, pkt_sz_lst) 469 470 471def test_xdp_native_adjst_tail_shrnk_data(cfg): 472 """ 473 Tests the XDP tail adjustment by shrinking packet data. 474 475 Args: 476 cfg: Configuration object containing network settings. 477 """ 478 pkt_sz_lst = [512, 1024, 2048] 479 offset_lst = [-16, -32, -64, -128, -256] 480 res = _test_xdp_native_tail_adjst( 481 cfg, 482 pkt_sz_lst, 483 offset_lst, 484 ) 485 486 _validate_res(res, offset_lst, pkt_sz_lst) 487 488 489def get_hds_thresh(cfg): 490 """ 491 Retrieves the header data split (HDS) threshold for a network interface. 492 493 Args: 494 cfg: Configuration object containing network settings. 495 496 Returns: 497 The HDS threshold value. If the threshold is not supported or an error occurs, 498 a default value of 1500 is returned. 499 """ 500 netnl = cfg.netnl 501 hds_thresh = 1500 502 503 try: 504 rings = netnl.rings_get({'header': {'dev-index': cfg.ifindex}}) 505 if 'hds-thresh' not in rings: 506 ksft_pr(f'hds-thresh not supported. Using default: {hds_thresh}') 507 return hds_thresh 508 hds_thresh = rings['hds-thresh'] 509 except NlError as e: 510 ksft_pr(f"Failed to get rings: {e}. Using default: {hds_thresh}") 511 512 return hds_thresh 513 514 515def _test_xdp_native_head_adjst(cfg, prog, pkt_sz_lst, offset_lst): 516 """ 517 Tests the XDP head adjustment action for a multi-buffer case. 518 519 Args: 520 cfg: Configuration object containing network settings. 521 netnl: Network namespace or link object (not used in this function). 522 523 This function sets up the packet size and offset lists, then performs 524 the head adjustment test by sending and receiving UDP packets. 525 """ 526 cfg.require_cmd("socat", remote=True) 527 528 prog_info = _load_xdp_prog(cfg, BPFProgInfo(prog, "xdp_native.bpf.o", "xdp.frags", 9000)) 529 port = rand_port() 530 531 _set_xdp_map("map_xdp_setup", TestConfig.MODE.value, XDPAction.HEAD_ADJST.value) 532 _set_xdp_map("map_xdp_setup", TestConfig.PORT.value, port) 533 534 hds_thresh = get_hds_thresh(cfg) 535 for offset in offset_lst: 536 for pkt_sz in pkt_sz_lst: 537 # The "head" buffer must contain at least the Ethernet header 538 # after we eat into it. We send large-enough packets, but if HDS 539 # is enabled head will only contain headers. Don't try to eat 540 # more than 28 bytes (UDPv4 + eth hdr left: (14 + 20 + 8) - 14) 541 l2_cut_off = 28 if cfg.addr_ipver == 4 else 48 542 if pkt_sz > hds_thresh and offset > l2_cut_off: 543 ksft_pr( 544 f"Failed run: pkt_sz ({pkt_sz}) > HDS threshold ({hds_thresh}) and " 545 f"offset {offset} > {l2_cut_off}" 546 ) 547 return {"status": "pass"} 548 549 test_str = ''.join(random.choice(string.ascii_lowercase) for _ in range(pkt_sz)) 550 tag = format(random.randint(65, 90), '02x') 551 552 _set_xdp_map("map_xdp_setup", 553 TestConfig.ADJST_OFFSET.value, 554 offset) 555 _set_xdp_map("map_xdp_setup", TestConfig.ADJST_TAG.value, int(tag, 16)) 556 _set_xdp_map("map_xdp_setup", TestConfig.ADJST_OFFSET.value, offset) 557 558 recvd_str = _exchg_udp(cfg, port, test_str) 559 560 # Check for failures around adjustment and data exchange 561 failure = _check_for_failures(recvd_str, _get_stats(prog_info['maps']['map_xdp_stats'])) 562 if failure is not None: 563 return { 564 "status": "fail", 565 "reason": failure, 566 "offset": offset, 567 "pkt_sz": pkt_sz 568 } 569 570 # Validate data content based on offset direction 571 expected_data = None 572 if offset < 0: 573 expected_data = chr(int(tag, 16)) * (0 - offset) + test_str 574 else: 575 expected_data = test_str[offset:] 576 577 if recvd_str != expected_data: 578 return { 579 "status": "fail", 580 "reason": "Data mismatch", 581 "offset": offset, 582 "pkt_sz": pkt_sz 583 } 584 585 return {"status": "pass"} 586 587 588def test_xdp_native_adjst_head_grow_data(cfg): 589 """ 590 Tests the XDP headroom growth support. 591 592 Args: 593 cfg: Configuration object containing network settings. 594 595 This function sets up the packet size and offset lists, then calls the 596 _test_xdp_native_head_adjst_mb function to perform the actual test. The 597 test is passed if the headroom is successfully extended for given packet 598 sizes and offsets. 599 """ 600 pkt_sz_lst = [512, 1024, 2048] 601 602 # Negative values result in headroom shrinking, resulting in growing of payload 603 offset_lst = [-16, -32, -64, -128, -256] 604 res = _test_xdp_native_head_adjst(cfg, "xdp_prog_frags", pkt_sz_lst, offset_lst) 605 606 _validate_res(res, offset_lst, pkt_sz_lst) 607 608 609def test_xdp_native_adjst_head_shrnk_data(cfg): 610 """ 611 Tests the XDP headroom shrinking support. 612 613 Args: 614 cfg: Configuration object containing network settings. 615 616 This function sets up the packet size and offset lists, then calls the 617 _test_xdp_native_head_adjst_mb function to perform the actual test. The 618 test is passed if the headroom is successfully shrunk for given packet 619 sizes and offsets. 620 """ 621 pkt_sz_lst = [512, 1024, 2048] 622 623 # Positive values result in headroom growing, resulting in shrinking of payload 624 offset_lst = [16, 32, 64, 128, 256] 625 res = _test_xdp_native_head_adjst(cfg, "xdp_prog_frags", pkt_sz_lst, offset_lst) 626 627 _validate_res(res, offset_lst, pkt_sz_lst) 628 629 630def main(): 631 """ 632 Main function to execute the XDP tests. 633 634 This function runs a series of tests to validate the XDP support for 635 both the single and multi-buffer. It uses the NetDrvEpEnv context 636 manager to manage the network driver environment and the ksft_run 637 function to execute the tests. 638 """ 639 with NetDrvEpEnv(__file__) as cfg: 640 cfg.netnl = EthtoolFamily() 641 ksft_run( 642 [ 643 test_xdp_native_pass_sb, 644 test_xdp_native_pass_mb, 645 test_xdp_native_drop_sb, 646 test_xdp_native_drop_mb, 647 test_xdp_native_tx_mb, 648 test_xdp_native_adjst_tail_grow_data, 649 test_xdp_native_adjst_tail_shrnk_data, 650 test_xdp_native_adjst_head_grow_data, 651 test_xdp_native_adjst_head_shrnk_data, 652 ], 653 args=(cfg,)) 654 ksft_exit() 655 656 657if __name__ == "__main__": 658 main() 659