Netdev List
 help / color / mirror / Atom feed
From: Florian Westphal <fw@strlen.de>
To: <netdev@vger.kernel.org>
Cc: jhs@mojatatu.com, jiri@resnulli.us, victor@mojatatu.com,
	Florian Westphal <fw@strlen.de>
Subject: [PATCH v2 net] selftests/tc-testing: pass mp_pm via initialiser
Date: Thu,  3 Sep 2026 12:36:21 +0200	[thread overview]
Message-ID: <20260903103621.13160-1-fw@strlen.de> (raw)

The script doesn't work for me, "tdc.py -J32" gives:
 -- ns/SubPlugin.__init__
Executing 1205 tests in parallel and 89 in serial
Using 39 batches and 4 workers
multiprocessing.pool.RemoteTraceback:
"""
Traceback (most recent call last):
  File "/usr/lib/python3.14/multiprocessing/pool.py", line 125, in worker
    result = (True, func(*args, **kwds))
                    ~~~~^^^^^^^^^^^^^^^
  File "/usr/lib/python3.14/multiprocessing/pool.py", line 48, in mapstar
    return list(map(*args))
  File "tools/testing/selftests/tc-testing/tdc.py", line 604, in __mp_runner
    (_, tsr) = test_runner(mp_pm, mp_args, tests)
                           ^^^^^
NameError: name 'mp_pm' is not defined

Other problem:

run_one_test() appends random suffix to NAMES[+ (NS, DEV0, ...).
There is a chance that other run_one_test instances pick up the altered
string, not the configured one and append another random suffix.

This causes spurious error when pyroute2 plugin tries to add
a device like "dummy0id1234idabcd".

Keep NAMES[] as-is. Append only to args.NAMES and update
readers to use args.NAMES too.

Assisted-by: ollama:gemma4:26b
Signed-off-by: Florian Westphal <fw@strlen.de>
---
 v2:
 - detach from 'reset conntrack after packet munging' series.
 - make global NAMES readonly, only change local copy.

 .../tc-testing/plugin-lib/scapyPlugin.py      |  2 +-
 tools/testing/selftests/tc-testing/tdc.py     | 64 +++++++++----------
 2 files changed, 32 insertions(+), 34 deletions(-)

diff --git a/tools/testing/selftests/tc-testing/plugin-lib/scapyPlugin.py b/tools/testing/selftests/tc-testing/plugin-lib/scapyPlugin.py
index 254136e3da5a..26998deea0b6 100644
--- a/tools/testing/selftests/tc-testing/plugin-lib/scapyPlugin.py
+++ b/tools/testing/selftests/tc-testing/plugin-lib/scapyPlugin.py
@@ -49,6 +49,6 @@ class SubPlugin(TdcPlugin):
             pkt = eval(scapyinfo['packet'])
             if '$' in scapyinfo['iface']:
                 tpl = Template(scapyinfo['iface'])
-                scapyinfo['iface'] = tpl.safe_substitute(NAMES)
+                scapyinfo['iface'] = tpl.safe_substitute(self.args.NAMES)
             for count in range(scapyinfo['count']):
                 sendp(pkt, iface=scapyinfo['iface'])
diff --git a/tools/testing/selftests/tc-testing/tdc.py b/tools/testing/selftests/tc-testing/tdc.py
index 511d66c36a2a..7c7f96aaef55 100755
--- a/tools/testing/selftests/tc-testing/tdc.py
+++ b/tools/testing/selftests/tc-testing/tdc.py
@@ -188,13 +188,13 @@ class PluginMgr:
         self.argparser = argparse.ArgumentParser(
             description='Linux TC unit tests')
 
-def replace_keywords(cmd):
+def replace_keywords(cmd, names):
     """
     For a given executable command, substitute any known
     variables contained within NAMES with the correct values
     """
     tcmd = Template(cmd)
-    subcmd = tcmd.safe_substitute(NAMES)
+    subcmd = tcmd.safe_substitute(names)
     return subcmd
 
 
@@ -206,7 +206,7 @@ def exec_cmd(caseinfo, args, pm, stage, command):
     if len(command.strip()) == 0:
         return None, None
     if '$' in command:
-        command = replace_keywords(command)
+        command = replace_keywords(command, args.NAMES)
 
     command = pm.call_adjust_command(caseinfo, stage, command)
     if args.verbose > 0:
@@ -374,11 +374,6 @@ def find_in_json_other(res, outputJSONVal, matchJSONVal, matchJSONKey=None):
 
 def run_one_test(pm, args, index, tidx):
     global NAMES
-    ns = NAMES['NS']
-    dev0 = NAMES['DEV0']
-    dev1 = NAMES['DEV1']
-    dummy = NAMES['DUMMY']
-    ifb = NAMES['IFB']
     result = True
     tresult = ""
     tap = ""
@@ -396,6 +391,15 @@ def run_one_test(pm, args, index, tidx):
             pm.call_post_execute(tidx)
             return res
 
+    # populate NAMES with TESTID for this test
+    args.NAMES = NAMES.copy()
+    args.NAMES['TESTID'] = tidx['id']
+    args.NAMES['NS'] = '{}-{}'.format(NAMES['NS'], tidx['random'])
+    args.NAMES['DEV0'] = '{}id{}'.format(NAMES['DEV0'], tidx['id'])
+    args.NAMES['DEV1'] = '{}id{}'.format(NAMES['DEV1'], tidx['id'])
+    args.NAMES['DUMMY'] = '{}id{}'.format(NAMES['DUMMY'], tidx['id'])
+    args.NAMES['IFB'] = '{}id{}'.format(NAMES['IFB'], tidx['id'])
+
     if 'dependsOn' in tidx:
         if (args.verbose > 0):
             print('probe command for test skip')
@@ -409,13 +413,6 @@ def run_one_test(pm, args, index, tidx):
                 pm.call_post_execute(tidx)
                 return res
 
-    # populate NAMES with TESTID for this test
-    NAMES['TESTID'] = tidx['id']
-    NAMES['NS'] = '{}-{}'.format(NAMES['NS'], tidx['random'])
-    NAMES['DEV0'] = '{}id{}'.format(NAMES['DEV0'], tidx['id'])
-    NAMES['DEV1'] = '{}id{}'.format(NAMES['DEV1'], tidx['id'])
-    NAMES['DUMMY'] = '{}id{}'.format(NAMES['DUMMY'], tidx['id'])
-    NAMES['IFB'] = '{}id{}'.format(NAMES['IFB'], tidx['id'])
 
     pm.call_pre_case(tidx)
     prepare_env(tidx, args, pm, 'setup', "-----> prepare stage", tidx["setup"])
@@ -468,16 +465,6 @@ def run_one_test(pm, args, index, tidx):
 
     index += 1
 
-    # remove TESTID from NAMES
-    del(NAMES['TESTID'])
-
-    # Restore names
-    NAMES['NS'] = ns
-    NAMES['DEV0'] = dev0
-    NAMES['DEV1'] = dev1
-    NAMES['DUMMY'] = dummy
-    NAMES['IFB'] = ifb
-
     return res
 
 def prepare_run(pm, args, testlist):
@@ -600,6 +587,18 @@ def mp_bins(alltests):
 
     return (serial, parallel)
 
+mp_pm = None
+mp_args = None
+
+def __mp_init__(pm, args):
+    """
+    This function is called once when each worker process starts.
+    It sets the global variables in the child process's memory space.
+    """
+    global mp_pm, mp_args
+    mp_pm = pm
+    mp_args = args
+
 def __mp_runner(tests):
     (_, tsr) = test_runner(mp_pm, mp_args, tests)
     return tsr._testsuite
@@ -615,14 +614,13 @@ def test_runner_mp(pm, args, alltests):
     print("Executing {} tests in parallel and {} in serial".format(len(parallel), len(serial)))
     print("Using {} batches and {} workers".format(len(batches), args.mp))
 
-    # We can't pickle these objects so workaround them
-    global mp_pm
-    mp_pm = pm
-
-    global mp_args
-    mp_args = args
-
-    with Pool(args.mp) as p:
+    # Use the 'initializer' to pass the unpickleable/shared objects
+    # to each worker process exactly once upon startup.
+    with Pool(
+        processes=args.mp,
+        initializer=__mp_init__,
+        initargs=(pm, args)
+    ) as p:
         pres = p.map(__mp_runner, batches)
 
     tsr = TestSuiteReport()
-- 
2.55.0


             reply	other threads:[~2026-09-03 10:36 UTC|newest]

Thread overview: 2+ messages / expand[flat|nested]  mbox.gz  Atom feed  top
2026-09-03 10:36 Florian Westphal [this message]
2026-09-04 11:01 ` [PATCH v2 net] selftests/tc-testing: pass mp_pm via initialiser netdev-bot+sashiko

Reply instructions:

You may reply publicly to this message via plain-text email
using any one of the following methods:

* Save the following mbox file, import it into your mail client,
  and reply-to-all from there: mbox

  Avoid top-posting and favor interleaved quoting:
  https://en.wikipedia.org/wiki/Posting_style#Interleaved_style

* Reply using the --to, --cc, and --in-reply-to
  switches of git-send-email(1):

  git send-email \
    --in-reply-to=20260903103621.13160-1-fw@strlen.de \
    --to=fw@strlen.de \
    --cc=jhs@mojatatu.com \
    --cc=jiri@resnulli.us \
    --cc=netdev@vger.kernel.org \
    --cc=victor@mojatatu.com \
    /path/to/YOUR_REPLY

  https://kernel.org/pub/software/scm/git/docs/git-send-email.html

* If your mail client supports setting the In-Reply-To header
  via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line before the message body.
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox