Commit c2dd2139 authored by Jakub Kicinski's avatar Jakub Kicinski
Browse files

Merge branch 'selftests-drv-net-add-ability-to-schedule-cleanup-with-defer'

Jakub Kicinski says:

====================
selftests: drv-net: add ability to schedule cleanup with defer()

Introduce a defer / cleanup mechanism for driver selftests.
More detailed info in the second patch.
====================

Link: https://patch.msgid.link/20240627185502.3069139-1-kuba@kernel.org


Signed-off-by: default avatarJakub Kicinski <kuba@kernel.org>
parents bf7bb7b4 0759356b
Loading
Loading
Loading
Loading
+103 −132
Original line number Diff line number Diff line
@@ -8,7 +8,7 @@ from lib.py import NetDrvEpEnv
from lib.py import NetdevFamily
from lib.py import KsftSkipEx
from lib.py import rand_port
from lib.py import ethtool, ip, GenerateTraffic, CmdExitFailure
from lib.py import ethtool, ip, defer, GenerateTraffic, CmdExitFailure


def _rss_key_str(key):
@@ -127,28 +127,27 @@ def test_rss_context(cfg, ctx_cnt=1, create_with_cfg=None):

    # Try to allocate more queues when necessary
    qcnt = len(_get_rx_cnts(cfg))
    if qcnt >= 2 + 2 * ctx_cnt:
        qcnt = None
    else:
    if qcnt < 2 + 2 * ctx_cnt:
        try:
            ksft_pr(f"Increasing queue count {qcnt} -> {2 + 2 * ctx_cnt}")
            ethtool(f"-L {cfg.ifname} combined {2 + 2 * ctx_cnt}")
            defer(ethtool, f"-L {cfg.ifname} combined {qcnt}")
        except:
            raise KsftSkipEx("Not enough queues for the test")

    ntuple = []
    ctx_id = []
    ports = []
    try:

    # Use queues 0 and 1 for normal traffic
    ethtool(f"-X {cfg.ifname} equal 2")
    defer(ethtool, f"-X {cfg.ifname} default")

    for i in range(ctx_cnt):
        want_cfg = f"start {2 + i * 2} equal 2"
        create_cfg = want_cfg if create_with_cfg else ""

        try:
                ctx_id.append(ethtool_create(cfg, "-X", f"context new {create_cfg}"))
            ctx_id = ethtool_create(cfg, "-X", f"context new {create_cfg}")
            defer(ethtool, f"-X {cfg.ifname} context {ctx_id} delete")
        except CmdExitFailure:
            # try to carry on and skip at the end
            if i == 0:
@@ -158,16 +157,17 @@ def test_rss_context(cfg, ctx_cnt=1, create_with_cfg=None):
            break

        if not create_with_cfg:
                ethtool(f"-X {cfg.ifname} context {ctx_id[i]} {want_cfg}")
            ethtool(f"-X {cfg.ifname} context {ctx_id} {want_cfg}")

        # Sanity check the context we just created
            data = get_rss(cfg, ctx_id[i])
        data = get_rss(cfg, ctx_id)
        ksft_eq(min(data['rss-indirection-table']), 2 + i * 2, "Unexpected context cfg: " + str(data))
        ksft_eq(max(data['rss-indirection-table']), 2 + i * 2 + 1, "Unexpected context cfg: " + str(data))

        ports.append(rand_port())
            flow = f"flow-type tcp{cfg.addr_ipver} dst-port {ports[i]} context {ctx_id[i]}"
            ntuple.append(ethtool_create(cfg, "-N", flow))
        flow = f"flow-type tcp{cfg.addr_ipver} dst-port {ports[i]} context {ctx_id}"
        ntuple = ethtool_create(cfg, "-N", flow)
        defer(ethtool, f"-N {cfg.ifname} delete {ntuple}")

    for i in range(ctx_cnt):
        cnts = _get_rx_cnts(cfg)
@@ -177,14 +177,6 @@ def test_rss_context(cfg, ctx_cnt=1, create_with_cfg=None):
        ksft_lt(sum(cnts[ :2]), 10000, "traffic on main context:" + str(cnts))
        ksft_ge(sum(cnts[2+i*2:4+i*2]), 20000, f"traffic on context {i}: " + str(cnts))
        ksft_eq(sum(cnts[2:2+i*2] + cnts[4+i*2:]), 0, "traffic on other contexts: " + str(cnts))
    finally:
        for nid in ntuple:
            ethtool(f"-N {cfg.ifname} delete {nid}")
        for cid in ctx_id:
            ethtool(f"-X {cfg.ifname} context {cid} delete")
        ethtool(f"-X {cfg.ifname} default")
        if qcnt:
            ethtool(f"-L {cfg.ifname} combined {qcnt}")

    if requested_ctx_cnt != ctx_cnt:
        raise KsftSkipEx(f"Tested only {ctx_cnt} contexts, wanted {requested_ctx_cnt}")
@@ -216,24 +208,23 @@ def test_rss_context_out_of_order(cfg, ctx_cnt=4):

    # Try to allocate more queues when necessary
    qcnt = len(_get_rx_cnts(cfg))
    if qcnt >= 2 + 2 * ctx_cnt:
        qcnt = None
    else:
    if qcnt < 2 + 2 * ctx_cnt:
        try:
            ksft_pr(f"Increasing queue count {qcnt} -> {2 + 2 * ctx_cnt}")
            ethtool(f"-L {cfg.ifname} combined {2 + 2 * ctx_cnt}")
            defer(ethtool, f"-L {cfg.ifname} combined {qcnt}")
        except:
            raise KsftSkipEx("Not enough queues for the test")

    ntuple = []
    ctx_id = []
    ctx = []
    ports = []

    def remove_ctx(idx):
        ethtool(f"-N {cfg.ifname} delete {ntuple[idx]}")
        ntuple[idx].exec()
        ntuple[idx] = None
        ethtool(f"-X {cfg.ifname} context {ctx_id[idx]} delete")
        ctx_id[idx] = None
        ctx[idx].exec()
        ctx[idx] = None

    def check_traffic():
        for i in range(ctx_cnt):
@@ -241,7 +232,7 @@ def test_rss_context_out_of_order(cfg, ctx_cnt=4):
            GenerateTraffic(cfg, port=ports[i]).wait_pkts_and_stop(20000)
            cnts = _get_rx_cnts(cfg, prev=cnts)

            if ctx_id[i] is None:
            if ctx[i]:
                ksft_lt(sum(cnts[ :2]), 10000, "traffic on main context:" + str(cnts))
                ksft_ge(sum(cnts[2+i*2:4+i*2]), 20000, f"traffic on context {i}: " + str(cnts))
                ksft_eq(sum(cnts[2:2+i*2] + cnts[4+i*2:]), 0, "traffic on other contexts: " + str(cnts))
@@ -249,16 +240,18 @@ def test_rss_context_out_of_order(cfg, ctx_cnt=4):
                ksft_ge(sum(cnts[ :2]), 20000, "traffic on main context:" + str(cnts))
                ksft_eq(sum(cnts[2: ]),     0, "traffic on other contexts: " + str(cnts))

    try:
    # Use queues 0 and 1 for normal traffic
    ethtool(f"-X {cfg.ifname} equal 2")
    defer(ethtool, f"-X {cfg.ifname} default")

    for i in range(ctx_cnt):
            ctx_id.append(ethtool_create(cfg, "-X", f"context new start {2 + i * 2} equal 2"))
        ctx_id = ethtool_create(cfg, "-X", f"context new start {2 + i * 2} equal 2")
        ctx.append(defer(ethtool, f"-X {cfg.ifname} context {ctx_id} delete"))

        ports.append(rand_port())
            flow = f"flow-type tcp{cfg.addr_ipver} dst-port {ports[i]} context {ctx_id[i]}"
            ntuple.append(ethtool_create(cfg, "-N", flow))
        flow = f"flow-type tcp{cfg.addr_ipver} dst-port {ports[i]} context {ctx_id}"
        ntuple_id = ethtool_create(cfg, "-N", flow)
        ntuple.append(defer(ethtool, f"-N {cfg.ifname} delete {ntuple_id}"))

    check_traffic()

@@ -274,17 +267,6 @@ def test_rss_context_out_of_order(cfg, ctx_cnt=4):
    remove_ctx(-1)
    check_traffic()

    finally:
        for nid in ntuple:
            if nid is not None:
                ethtool(f"-N {cfg.ifname} delete {nid}")
        for cid in ctx_id:
            if cid is not None:
                ethtool(f"-X {cfg.ifname} context {cid} delete")
        ethtool(f"-X {cfg.ifname} default")
        if qcnt:
            ethtool(f"-L {cfg.ifname} combined {qcnt}")

    if requested_ctx_cnt != ctx_cnt:
        raise KsftSkipEx(f"Tested only {ctx_cnt} contexts, wanted {requested_ctx_cnt}")

@@ -298,31 +280,31 @@ def test_rss_context_overlap(cfg, other_ctx=0):
    require_ntuple(cfg)

    queue_cnt = len(_get_rx_cnts(cfg))
    if queue_cnt >= 4:
        queue_cnt = None
    else:
    if queue_cnt < 4:
        try:
            ksft_pr(f"Increasing queue count {queue_cnt} -> 4")
            ethtool(f"-L {cfg.ifname} combined 4")
            defer(ethtool, f"-L {cfg.ifname} combined {queue_cnt}")
        except:
            raise KsftSkipEx("Not enough queues for the test")

    ctx_id = None
    ntuple = None
    if other_ctx == 0:
        ethtool(f"-X {cfg.ifname} equal 4")
        defer(ethtool, f"-X {cfg.ifname} default")
    else:
        other_ctx = ethtool_create(cfg, "-X", "context new")
        ethtool(f"-X {cfg.ifname} context {other_ctx} equal 4")
        defer(ethtool, f"-X {cfg.ifname} context {other_ctx} delete")

    try:
    ctx_id = ethtool_create(cfg, "-X", "context new")
    ethtool(f"-X {cfg.ifname} context {ctx_id} start 2 equal 2")
    defer(ethtool, f"-X {cfg.ifname} context {ctx_id} delete")

    port = rand_port()
    if other_ctx:
        flow = f"flow-type tcp{cfg.addr_ipver} dst-port {port} context {other_ctx}"
            ntuple = ethtool_create(cfg, "-N", flow)
        ntuple_id = ethtool_create(cfg, "-N", flow)
        ntuple = defer(ethtool, f"-N {cfg.ifname} delete {ntuple_id}")

    # Test the main context
    cnts = _get_rx_cnts(cfg)
@@ -337,10 +319,10 @@ def test_rss_context_overlap(cfg, other_ctx=0):

    # Now create a rule for context 1 and make sure traffic goes to a subset
    if other_ctx:
            ethtool(f"-N {cfg.ifname} delete {ntuple}")
            ntuple = None
        ntuple.exec()
    flow = f"flow-type tcp{cfg.addr_ipver} dst-port {port} context {ctx_id}"
        ntuple = ethtool_create(cfg, "-N", flow)
    ntuple_id = ethtool_create(cfg, "-N", flow)
    defer(ethtool, f"-N {cfg.ifname} delete {ntuple_id}")

    cnts = _get_rx_cnts(cfg)
    GenerateTraffic(cfg, port=port).wait_pkts_and_stop(20000)
@@ -350,17 +332,6 @@ def test_rss_context_overlap(cfg, other_ctx=0):
    ksft_ge(sum(cnts[2:4]), 20000, "traffic on extra context: " + str(cnts))
    if other_ctx == 0:
        ksft_eq(sum(cnts[4: ]),     0, "traffic on other queues: " + str(cnts))
    finally:
        if ntuple is not None:
            ethtool(f"-N {cfg.ifname} delete {ntuple}")
        if ctx_id:
            ethtool(f"-X {cfg.ifname} context {ctx_id} delete")
        if other_ctx == 0:
            ethtool(f"-X {cfg.ifname} default")
        else:
            ethtool(f"-X {cfg.ifname} context {other_ctx} delete")
        if queue_cnt:
            ethtool(f"-L {cfg.ifname} combined {queue_cnt}")


def test_rss_context_overlap2(cfg):
+36 −15
Original line number Diff line number Diff line
@@ -6,6 +6,7 @@ import sys
import time
import traceback
from .consts import KSFT_MAIN_NAME
from .utils import global_defer_queue

KSFT_RESULT = None
KSFT_RESULT_ALL = True
@@ -108,6 +109,24 @@ def ktap_result(ok, cnt=1, case="", comment=""):
    print(res)


def ksft_flush_defer():
    global KSFT_RESULT

    i = 0
    qlen_start = len(global_defer_queue)
    while global_defer_queue:
        i += 1
        entry = global_defer_queue.pop()
        try:
            entry.exec_only()
        except:
            ksft_pr(f"Exception while handling defer / cleanup (callback {i} of {qlen_start})!")
            tb = traceback.format_exc()
            for line in tb.strip().split('\n'):
                ksft_pr("Defer Exception|", line)
            KSFT_RESULT = False


def ksft_run(cases=None, globs=None, case_pfx=None, args=()):
    cases = cases or []

@@ -130,29 +149,31 @@ def ksft_run(cases=None, globs=None, case_pfx=None, args=()):
    for case in cases:
        KSFT_RESULT = True
        cnt += 1
        comment = ""
        cnt_key = ""

        try:
            case(*args)
        except KsftSkipEx as e:
            ktap_result(True, cnt, case, comment="SKIP " + str(e))
            totals['skip'] += 1
            continue
            comment = "SKIP " + str(e)
            cnt_key = 'skip'
        except KsftXfailEx as e:
            ktap_result(True, cnt, case, comment="XFAIL " + str(e))
            totals['xfail'] += 1
            continue
            comment = "XFAIL " + str(e)
            cnt_key = 'xfail'
        except Exception as e:
            tb = traceback.format_exc()
            for line in tb.strip().split('\n'):
                ksft_pr("Exception|", line)
            ktap_result(False, cnt, case)
            totals['fail'] += 1
            continue
            KSFT_RESULT = False
            cnt_key = 'fail'

        ksft_flush_defer()

        if not cnt_key:
            cnt_key = 'pass' if KSFT_RESULT else 'fail'

        ktap_result(KSFT_RESULT, cnt, case)
        if KSFT_RESULT:
            totals['pass'] += 1
        else:
            totals['fail'] += 1
        ktap_result(KSFT_RESULT, cnt, case, comment=comment)
        totals[cnt_key] += 1

    print(
        f"# Totals: pass:{totals['pass']} fail:{totals['fail']} xfail:{totals['xfail']} xpass:0 skip:{totals['skip']} error:0"
+34 −0
Original line number Diff line number Diff line
@@ -66,6 +66,40 @@ class bkg(cmd):
        return self.process(terminate=self.terminate, fail=self.check_fail)


global_defer_queue = []


class defer:
    def __init__(self, func, *args, **kwargs):
        global global_defer_queue

        if not callable(func):
            raise Exception("defer created with un-callable object, did you call the function instead of passing its name?")

        self.func = func
        self.args = args
        self.kwargs = kwargs

        self._queue =  global_defer_queue
        self._queue.append(self)

    def __enter__(self):
        return self

    def __exit__(self, ex_type, ex_value, ex_tb):
        return self.exec()

    def exec_only(self):
        self.func(*self.args, **self.kwargs)

    def cancel(self):
        self._queue.remove(self)

    def exec(self):
        self.cancel()
        self.exec_only()


def tool(name, args, json=None, ns=None, host=None):
    cmd_str = name + ' '
    if json: