From: Jakub Kicinski <kuba@kernel.org>
To: davem@davemloft.net
Cc: netdev@vger.kernel.org, edumazet@google.com, pabeni@redhat.com,
przemyslaw.kitszel@intel.com, petrm@nvidia.com,
willemdebruijn.kernel@gmail.com, Jakub Kicinski <kuba@kernel.org>
Subject: [PATCH net-next v2 2/3] selftests: drv-net: add ability to schedule cleanup with defer()
Date: Thu, 27 Jun 2024 11:55:01 -0700 [thread overview]
Message-ID: <20240627185502.3069139-3-kuba@kernel.org> (raw)
In-Reply-To: <20240627185502.3069139-1-kuba@kernel.org>
This implements what I was describing in [1]. When writing a test
author can schedule cleanup / undo actions right after the creation
completes, eg:
cmd("touch /tmp/file")
defer(cmd, "rm /tmp/file")
defer() takes the function name as first argument, and the rest are
arguments for that function. defer()red functions are called in
inverse order after test exits. It's also possible to capture them
and execute earlier (in which case they get automatically de-queued).
undo = defer(cmd, "rm /tmp/file")
# ... some unsafe code ...
undo.exec()
As a nice safety all exceptions from defer()ed calls are captured,
printed, and ignored (they do make the test fail, however).
This addresses the common problem of exceptions in cleanup paths
often being unhandled, leading to potential leaks.
There is a global action queue, flushed by ksft_run(). We could support
function level defers too, I guess, but there's no immediate need..
Link: https://lore.kernel.org/all/877cedb2ki.fsf@nvidia.com/ # [1]
Signed-off-by: Jakub Kicinski <kuba@kernel.org>
---
v2:
- split refactor to previous patch
- user bare except instead of except Exception
- rename _exec() -> exec_only() and use in flush
- reorder queue removal vs calling callback
- add print to indicate ID of failed callback
- remove the state flags
---
tools/testing/selftests/net/lib/py/ksft.py | 21 +++++++++++++
tools/testing/selftests/net/lib/py/utils.py | 34 +++++++++++++++++++++
2 files changed, 55 insertions(+)
diff --git a/tools/testing/selftests/net/lib/py/ksft.py b/tools/testing/selftests/net/lib/py/ksft.py
index 789433262dc7..3aaa2748a58e 100644
--- a/tools/testing/selftests/net/lib/py/ksft.py
+++ b/tools/testing/selftests/net/lib/py/ksft.py
@@ -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 @@ KSFT_RESULT_ALL = True
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 []
@@ -148,6 +167,8 @@ KSFT_RESULT_ALL = True
KSFT_RESULT = False
cnt_key = 'fail'
+ ksft_flush_defer()
+
if not cnt_key:
cnt_key = 'pass' if KSFT_RESULT else 'fail'
diff --git a/tools/testing/selftests/net/lib/py/utils.py b/tools/testing/selftests/net/lib/py/utils.py
index 405aa510aaf2..72590c3f90f1 100644
--- a/tools/testing/selftests/net/lib/py/utils.py
+++ b/tools/testing/selftests/net/lib/py/utils.py
@@ -66,6 +66,40 @@ import time
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:
--
2.45.2
next prev parent reply other threads:[~2024-06-27 18:55 UTC|newest]
Thread overview: 9+ messages / expand[flat|nested] mbox.gz Atom feed top
2024-06-27 18:54 [PATCH net-next v2 0/3] selftests: drv-net: add ability to schedule cleanup with defer() Jakub Kicinski
2024-06-27 18:55 ` [PATCH net-next v2 1/3] selftests: net: ksft: avoid continue when handling results Jakub Kicinski
2024-06-28 14:30 ` Petr Machata
2024-06-27 18:55 ` Jakub Kicinski [this message]
2024-06-28 12:35 ` [PATCH net-next v2 2/3] selftests: drv-net: add ability to schedule cleanup with defer() Przemek Kitszel
2024-06-28 14:31 ` Petr Machata
2024-06-27 18:55 ` [PATCH net-next v2 3/3] selftests: drv-net: rss_ctx: convert to defer() Jakub Kicinski
2024-06-28 14:31 ` Petr Machata
2024-06-29 1:50 ` [PATCH net-next v2 0/3] selftests: drv-net: add ability to schedule cleanup with defer() patchwork-bot+netdevbpf
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=20240627185502.3069139-3-kuba@kernel.org \
--to=kuba@kernel.org \
--cc=davem@davemloft.net \
--cc=edumazet@google.com \
--cc=netdev@vger.kernel.org \
--cc=pabeni@redhat.com \
--cc=petrm@nvidia.com \
--cc=przemyslaw.kitszel@intel.com \
--cc=willemdebruijn.kernel@gmail.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;
as well as URLs for NNTP newsgroup(s).