xref: /linux/tools/testing/selftests/drivers/net/xdp.py (revision 5feaa7a07b85ebbef418ba4b80e4e0d23dc379f5)
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