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