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