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
next 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