* [RFC net-next 0/2] selftests: drv-net: add ability to schedule cleanup with defer() @ 2024-06-26 1:36 Jakub Kicinski 2024-06-26 1:36 ` [RFC net-next 1/2] " Jakub Kicinski 2024-06-26 1:36 ` [RFC net-next 2/2] selftests: drv-net: rss_ctx: convert to defer() Jakub Kicinski 0 siblings, 2 replies; 14+ messages in thread From: Jakub Kicinski @ 2024-06-26 1:36 UTC (permalink / raw) To: davem Cc: netdev, edumazet, pabeni, willemdebruijn.kernel, przemyslaw.kitszel, leitao, petrm, Jakub Kicinski This is on top of the rss_ctx patches. I don't want to delay those from getting merged, and this may be a longer discussion... Jakub Kicinski (2): selftests: drv-net: add ability to schedule cleanup with defer() selftests: drv-net: rss_ctx: convert to defer() .../selftests/drivers/net/hw/rss_ctx.py | 225 ++++++++---------- tools/testing/selftests/net/lib/py/ksft.py | 49 ++-- tools/testing/selftests/net/lib/py/utils.py | 41 ++++ 3 files changed, 173 insertions(+), 142 deletions(-) -- 2.45.2 ^ permalink raw reply [flat|nested] 14+ messages in thread
* [RFC net-next 1/2] selftests: drv-net: add ability to schedule cleanup with defer() 2024-06-26 1:36 [RFC net-next 0/2] selftests: drv-net: add ability to schedule cleanup with defer() Jakub Kicinski @ 2024-06-26 1:36 ` Jakub Kicinski 2024-06-26 7:43 ` Przemek Kitszel 2024-06-26 10:18 ` Petr Machata 2024-06-26 1:36 ` [RFC net-next 2/2] selftests: drv-net: rss_ctx: convert to defer() Jakub Kicinski 1 sibling, 2 replies; 14+ messages in thread From: Jakub Kicinski @ 2024-06-26 1:36 UTC (permalink / raw) To: davem Cc: netdev, edumazet, pabeni, willemdebruijn.kernel, przemyslaw.kitszel, leitao, petrm, Jakub Kicinski 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> --- tools/testing/selftests/net/lib/py/ksft.py | 49 +++++++++++++++------ tools/testing/selftests/net/lib/py/utils.py | 41 +++++++++++++++++ 2 files changed, 76 insertions(+), 14 deletions(-) diff --git a/tools/testing/selftests/net/lib/py/ksft.py b/tools/testing/selftests/net/lib/py/ksft.py index 45ffe277d94a..4a72b9cbb27d 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 + + while global_defer_queue: + entry = global_defer_queue[-1] + try: + entry.exec() + except Exception: + if global_defer_queue and global_defer_queue[-1] == entry: + global_defer_queue.pop() + + ksft_pr("Exception while handling defer / cleanup!") + 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 @@ KSFT_RESULT_ALL = True 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' - ktap_result(KSFT_RESULT, cnt, case) - if KSFT_RESULT: - totals['pass'] += 1 - else: - totals['fail'] += 1 + ksft_flush_defer() + + if not cnt_key: + cnt_key = 'pass' if KSFT_RESULT else 'fail' + + 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" diff --git a/tools/testing/selftests/net/lib/py/utils.py b/tools/testing/selftests/net/lib/py/utils.py index 405aa510aaf2..1ef6ebaa369e 100644 --- a/tools/testing/selftests/net/lib/py/utils.py +++ b/tools/testing/selftests/net/lib/py/utils.py @@ -66,6 +66,47 @@ 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 global_defer_queue is None: + raise Exception("defer environment has not been set up") + + 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.queued = True + self.executed = False + + 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(self): + self.func(*self.args, **self.kwargs) + + def cancel(self): + self._queue.remove(self) + self.queued = False + + def exec(self): + self._exec() + self.cancel() + self.executed = True + + def tool(name, args, json=None, ns=None, host=None): cmd_str = name + ' ' if json: -- 2.45.2 ^ permalink raw reply related [flat|nested] 14+ messages in thread
* Re: [RFC net-next 1/2] selftests: drv-net: add ability to schedule cleanup with defer() 2024-06-26 1:36 ` [RFC net-next 1/2] " Jakub Kicinski @ 2024-06-26 7:43 ` Przemek Kitszel 2024-06-26 9:19 ` Petr Machata 2024-06-26 16:49 ` Jakub Kicinski 2024-06-26 10:18 ` Petr Machata 1 sibling, 2 replies; 14+ messages in thread From: Przemek Kitszel @ 2024-06-26 7:43 UTC (permalink / raw) To: Jakub Kicinski Cc: netdev, edumazet, pabeni, willemdebruijn.kernel, leitao, petrm, davem On 6/26/24 03:36, Jakub Kicinski wrote: > 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. I like the defer name more than undo, and that makes immediate connection to golang's defer. For reference golang's defer is called at function exit, the choice was made that way (instead C++'s destructor at the end of the scope) to make it more performant. It's a language construct, so could not be easily translated into python. I see why you expect discussion to be potentially long here ;), I just showed it to my python speaking friends and all the alternatives came up: context manager, yield, pytest "built in" fixtures and so on. I like your approach more through, it is much nicer at actual-test code side. > It's also possible to capture them > and execute earlier (in which case they get automatically de-queued). not sure if this is good, either test developer should be able to easily guard call to defer, or could call it with a lambda capture so actual execution will be no-op-ish. OTOH, why not, both .exec() and .cancel() are nice to use so, I'm fine both ways > > 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. Nice! Please only make it so that cleanup-failure does not overwrite happy-test-path-failure (IOW "ret = ret ? ret : cleanup_ret") > > 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.. That would be a must have for general solution, would it require some boilerplate code at function level? > > Link: https://lore.kernel.org/all/877cedb2ki.fsf@nvidia.com/ # [1] > Signed-off-by: Jakub Kicinski <kuba@kernel.org> > --- > tools/testing/selftests/net/lib/py/ksft.py | 49 +++++++++++++++------ > tools/testing/selftests/net/lib/py/utils.py | 41 +++++++++++++++++ > 2 files changed, 76 insertions(+), 14 deletions(-) > > diff --git a/tools/testing/selftests/net/lib/py/ksft.py b/tools/testing/selftests/net/lib/py/ksft.py > index 45ffe277d94a..4a72b9cbb27d 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 > + > + while global_defer_queue: > + entry = global_defer_queue[-1] > + try: > + entry.exec() > + except Exception: > + if global_defer_queue and global_defer_queue[-1] == entry: > + global_defer_queue.pop() > + > + ksft_pr("Exception while handling defer / cleanup!") please print current queue size, if only for convenience of test developer to be able tell if they are moving forward in fix-rerun-observe cycle > + tb = traceback.format_exc() > + for line in tb.strip().split('\n'): > + ksft_pr("Defer Exception|", line) > + KSFT_RESULT = False I have no idea if this could be other error than just False, if so, don't overwrite [...] > > +global_defer_queue = [] > + > + > +class defer: > + def __init__(self, func, *args, **kwargs): > + global global_defer_queue > + if global_defer_queue is None: > + raise Exception("defer environment has not been set up") > + > + 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.queued = True > + self.executed = False > + > + 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() why do you need __enter__ and __exit__ if this is not a context manager / to-be-used-via-with? > + > + def _exec(self): > + self.func(*self.args, **self.kwargs) > + > + def cancel(self): > + self._queue.remove(self) > + self.queued = False > + > + def exec(self): > + self._exec() > + self.cancel() > + self.executed = True > + > + > def tool(name, args, json=None, ns=None, host=None): > cmd_str = name + ' ' > if json: ^ permalink raw reply [flat|nested] 14+ messages in thread
* Re: [RFC net-next 1/2] selftests: drv-net: add ability to schedule cleanup with defer() 2024-06-26 7:43 ` Przemek Kitszel @ 2024-06-26 9:19 ` Petr Machata 2024-06-26 9:38 ` Przemek Kitszel 2024-06-26 16:44 ` Jakub Kicinski 2024-06-26 16:49 ` Jakub Kicinski 1 sibling, 2 replies; 14+ messages in thread From: Petr Machata @ 2024-06-26 9:19 UTC (permalink / raw) To: Przemek Kitszel Cc: Jakub Kicinski, netdev, edumazet, pabeni, willemdebruijn.kernel, leitao, petrm, davem Przemek Kitszel <przemyslaw.kitszel@intel.com> writes: > On 6/26/24 03:36, Jakub Kicinski wrote: > >> 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.. > > That would be a must have for general solution, would it require some > boilerplate code at function level? Presumably you'd need a per-function defer pool. Which would be naturally modeled as a context manager, but I promised myself I'd shut up about that. >> + def __enter__(self): >> + return self >> + >> + def __exit__(self, ex_type, ex_value, ex_tb): >> + return self.exec() > > why do you need __enter__ and __exit__ if this is not a context > manager / to-be-used-via-with? But you could use it as a context manager. with defer(blah blah): do stuff # blah blah runs here IMHO makes sense. ^ permalink raw reply [flat|nested] 14+ messages in thread
* Re: [RFC net-next 1/2] selftests: drv-net: add ability to schedule cleanup with defer() 2024-06-26 9:19 ` Petr Machata @ 2024-06-26 9:38 ` Przemek Kitszel 2024-06-26 16:44 ` Jakub Kicinski 1 sibling, 0 replies; 14+ messages in thread From: Przemek Kitszel @ 2024-06-26 9:38 UTC (permalink / raw) To: Petr Machata Cc: Jakub Kicinski, netdev, edumazet, pabeni, willemdebruijn.kernel, leitao, davem On 6/26/24 11:19, Petr Machata wrote: > > Przemek Kitszel <przemyslaw.kitszel@intel.com> writes: > >> On 6/26/24 03:36, Jakub Kicinski wrote: >> >>> 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.. >> >> That would be a must have for general solution, would it require some >> boilerplate code at function level? > > Presumably you'd need a per-function defer pool. > > Which would be naturally modeled as a context manager, but I promised > myself I'd shut up about that. > >>> + def __enter__(self): >>> + return self >>> + >>> + def __exit__(self, ex_type, ex_value, ex_tb): >>> + return self.exec() >> >> why do you need __enter__ and __exit__ if this is not a context >> manager / to-be-used-via-with? > > But you could use it as a context manager. > > with defer(blah blah): > do stuff > # blah blah runs here > > IMHO makes sense. oh, nice! agree! Then this little part is "the general solution", with the rest (global queue) added as a sugar layer for kselftests, to only avoid inflating indentation levels, great ^ permalink raw reply [flat|nested] 14+ messages in thread
* Re: [RFC net-next 1/2] selftests: drv-net: add ability to schedule cleanup with defer() 2024-06-26 9:19 ` Petr Machata 2024-06-26 9:38 ` Przemek Kitszel @ 2024-06-26 16:44 ` Jakub Kicinski 1 sibling, 0 replies; 14+ messages in thread From: Jakub Kicinski @ 2024-06-26 16:44 UTC (permalink / raw) To: Petr Machata Cc: Przemek Kitszel, netdev, edumazet, pabeni, willemdebruijn.kernel, leitao, davem On Wed, 26 Jun 2024 11:19:08 +0200 Petr Machata wrote: > >> 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.. > > > > That would be a must have for general solution, would it require some > > boilerplate code at function level? > > Presumably you'd need a per-function defer pool. > > Which would be naturally modeled as a context manager, but I promised > myself I'd shut up about that. No preference on the internal mechanism :) The part I was unsure about was whether in this case test writer will want to mix the contexts: fq = FuncDeferQueue() fq.defer(...) # this one is called when function exits defer(...) # this one is global or not: defer_func_context() # any defer after this is assumed to be func-level defer(...) so probably best to wait until we have some real examples. ^ permalink raw reply [flat|nested] 14+ messages in thread
* Re: [RFC net-next 1/2] selftests: drv-net: add ability to schedule cleanup with defer() 2024-06-26 7:43 ` Przemek Kitszel 2024-06-26 9:19 ` Petr Machata @ 2024-06-26 16:49 ` Jakub Kicinski 2024-06-27 8:40 ` Przemek Kitszel 1 sibling, 1 reply; 14+ messages in thread From: Jakub Kicinski @ 2024-06-26 16:49 UTC (permalink / raw) To: Przemek Kitszel Cc: netdev, edumazet, pabeni, willemdebruijn.kernel, leitao, petrm, davem On Wed, 26 Jun 2024 09:43:54 +0200 Przemek Kitszel wrote: > > 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. > > Nice! Please only make it so that cleanup-failure does not overwrite > happy-test-path-failure (IOW "ret = ret ? ret : cleanup_ret") That should be what we end up doing. The ret is a boolean (pass / fail) so we have: pass &= cleanup_pass effectively. > > + ksft_pr("Exception while handling defer / cleanup!") > > please print current queue size, if only for convenience of test > developer to be able tell if they are moving forward in > fix-rerun-observe cycle Hm... not a bad point, defer() cycles are possible. But then again, we don't guard against infinite loops in tests either, and kselftest runner (the general one, outside our Python) has a timeout, so it will kill the script. > > + tb = traceback.format_exc() > > + for line in tb.strip().split('\n'): > > + ksft_pr("Defer Exception|", line) > > + KSFT_RESULT = False > > I have no idea if this could be other error than just False, if so, > don't overwrite Yup, just True / False. The other types (skip, xfail) are a pass (True) plus a comment, per KTAP spec. ^ permalink raw reply [flat|nested] 14+ messages in thread
* Re: [RFC net-next 1/2] selftests: drv-net: add ability to schedule cleanup with defer() 2024-06-26 16:49 ` Jakub Kicinski @ 2024-06-27 8:40 ` Przemek Kitszel 2024-06-27 15:35 ` Jakub Kicinski 0 siblings, 1 reply; 14+ messages in thread From: Przemek Kitszel @ 2024-06-27 8:40 UTC (permalink / raw) To: Jakub Kicinski Cc: netdev, edumazet, pabeni, willemdebruijn.kernel, leitao, petrm, davem On 6/26/24 18:49, Jakub Kicinski wrote: > On Wed, 26 Jun 2024 09:43:54 +0200 Przemek Kitszel wrote: >>> 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. >> >> Nice! Please only make it so that cleanup-failure does not overwrite >> happy-test-path-failure (IOW "ret = ret ? ret : cleanup_ret") > > That should be what we end up doing. The ret is a boolean (pass / fail) > so we have: > > pass &= cleanup_pass > > effectively. > >>> + ksft_pr("Exception while handling defer / cleanup!") >> >> please print current queue size, if only for convenience of test >> developer to be able tell if they are moving forward in >> fix-rerun-observe cycle > > Hm... not a bad point, defer() cycles are possible. > But then again, we don't guard against infinite loops > in tests either, and kselftest runner (the general one, > outside our Python) has a timeout, so it will kill the script. I mean the flow: $EDITOR mytest.py ./mytest.py # output: Exception while handling defer / cleanup (at 4 out of 13 cleanups) then repeat with the hope that fix to cleanup procedure will move us forward, say: $EDITOR mytest.py; ./mytest.py #output: ... (at 7 of 13 cleanups) just name of failed cleanup method is not enough as those could be added via loop > >>> + tb = traceback.format_exc() >>> + for line in tb.strip().split('\n'): >>> + ksft_pr("Defer Exception|", line) >>> + KSFT_RESULT = False >> >> I have no idea if this could be other error than just False, if so, >> don't overwrite > > Yup, just True / False. The other types (skip, xfail) are a pass > (True) plus a comment, per KTAP spec. > Thanks! ^ permalink raw reply [flat|nested] 14+ messages in thread
* Re: [RFC net-next 1/2] selftests: drv-net: add ability to schedule cleanup with defer() 2024-06-27 8:40 ` Przemek Kitszel @ 2024-06-27 15:35 ` Jakub Kicinski 0 siblings, 0 replies; 14+ messages in thread From: Jakub Kicinski @ 2024-06-27 15:35 UTC (permalink / raw) To: Przemek Kitszel Cc: netdev, edumazet, pabeni, willemdebruijn.kernel, leitao, petrm, davem On Thu, 27 Jun 2024 10:40:31 +0200 Przemek Kitszel wrote: > > Hm... not a bad point, defer() cycles are possible. > > But then again, we don't guard against infinite loops > > in tests either, and kselftest runner (the general one, > > outside our Python) has a timeout, so it will kill the script. > > I mean the flow: > $EDITOR mytest.py > ./mytest.py > # output: Exception while handling defer / cleanup (at 4 out of 13 cleanups) > > then repeat with the hope that fix to cleanup procedure will move us > forward, say: > $EDITOR mytest.py; ./mytest.py > #output: ... (at 7 of 13 cleanups) > > just name of failed cleanup method is not enough as those could be > added via loop Oh, yes, nice one! ^ permalink raw reply [flat|nested] 14+ messages in thread
* Re: [RFC net-next 1/2] selftests: drv-net: add ability to schedule cleanup with defer() 2024-06-26 1:36 ` [RFC net-next 1/2] " Jakub Kicinski 2024-06-26 7:43 ` Przemek Kitszel @ 2024-06-26 10:18 ` Petr Machata 2024-06-26 16:09 ` Jakub Kicinski 1 sibling, 1 reply; 14+ messages in thread From: Petr Machata @ 2024-06-26 10:18 UTC (permalink / raw) To: Jakub Kicinski Cc: davem, netdev, edumazet, pabeni, willemdebruijn.kernel, przemyslaw.kitszel, leitao, petrm Jakub Kicinski <kuba@kernel.org> writes: > 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> > --- > tools/testing/selftests/net/lib/py/ksft.py | 49 +++++++++++++++------ > tools/testing/selftests/net/lib/py/utils.py | 41 +++++++++++++++++ > 2 files changed, 76 insertions(+), 14 deletions(-) > > diff --git a/tools/testing/selftests/net/lib/py/ksft.py b/tools/testing/selftests/net/lib/py/ksft.py > index 45ffe277d94a..4a72b9cbb27d 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 > + > + while global_defer_queue: > + entry = global_defer_queue[-1] > + try: > + entry.exec() I wonder if you added _exec() to invoke it here. Because then you could just do entry = global_defer_queue.pop() and entry._exec(), and in the except branch you would just have the test-related business, without the queue management. > + except Exception: I think this should be either an unqualified except: or except BaseException:. > + if global_defer_queue and global_defer_queue[-1] == entry: > + global_defer_queue.pop() > + > + ksft_pr("Exception while handling defer / cleanup!") Hmm, I was thinking about adding defer.__str__ and using it here to give more clue as to where it went wrong, but the traceback is IMHO plenty good enough. > + 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 @@ KSFT_RESULT_ALL = True > 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' > > - ktap_result(KSFT_RESULT, cnt, case) > - if KSFT_RESULT: > - totals['pass'] += 1 > - else: > - totals['fail'] += 1 > + ksft_flush_defer() > + > + if not cnt_key: > + cnt_key = 'pass' if KSFT_RESULT else 'fail' > + > + 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" Majority of this hunk is just preparatory and should be in a patch of its own. Then in this patch it should just introduce the flush. > diff --git a/tools/testing/selftests/net/lib/py/utils.py b/tools/testing/selftests/net/lib/py/utils.py > index 405aa510aaf2..1ef6ebaa369e 100644 > --- a/tools/testing/selftests/net/lib/py/utils.py > +++ b/tools/testing/selftests/net/lib/py/utils.py > @@ -66,6 +66,47 @@ 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 global_defer_queue is None: > + raise Exception("defer environment has not been set up") > + > + 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.queued = True > + self.executed = False > + > + 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(self): > + self.func(*self.args, **self.kwargs) > + > + def cancel(self): This shouldn't dequeue if not self.queued. > + self._queue.remove(self) > + self.queued = False > + > + def exec(self): This shouldn't exec if self.executed. But I actually wonder if we need two flags at all. Whether the defer entry is resolved through exec(), cancel() or __exit__(), it's "done". It could be left in the queue, in which case the "done" flag is going to disable future exec requests. Or it can just be dropped from the queue when done, in which case we don't even need the "done" flag as such. > + self._exec() > + self.cancel() > + self.executed = True > + > + > def tool(name, args, json=None, ns=None, host=None): > cmd_str = name + ' ' > if json: ^ permalink raw reply [flat|nested] 14+ messages in thread
* Re: [RFC net-next 1/2] selftests: drv-net: add ability to schedule cleanup with defer() 2024-06-26 10:18 ` Petr Machata @ 2024-06-26 16:09 ` Jakub Kicinski 2024-06-27 7:37 ` Petr Machata 0 siblings, 1 reply; 14+ messages in thread From: Jakub Kicinski @ 2024-06-26 16:09 UTC (permalink / raw) To: Petr Machata Cc: davem, netdev, edumazet, pabeni, willemdebruijn.kernel, przemyslaw.kitszel, leitao On Wed, 26 Jun 2024 12:18:58 +0200 Petr Machata wrote: > Jakub Kicinski <kuba@kernel.org> writes: > > +def ksft_flush_defer(): > > + global KSFT_RESULT > > + > > + while global_defer_queue: > > + entry = global_defer_queue[-1] > > + try: > > + entry.exec() > > I wonder if you added _exec() to invoke it here. Because then you could > just do entry = global_defer_queue.pop() and entry._exec(), and in the > except branch you would just have the test-related business, without the > queue management. Initially I had both _exec, and _dequeue as separate helpers, but then _dequeue was identical to cancel, so I removed that one, but _exec stayed. As you point out _exec() would do nicely during "flush".. but linter was angry at me for calling private functions. I couldn't quickly think of a clean scheme of naming things. Or rather, I should say, I like that the only non-private functions in class defer right now are test-author-facing. At some point I considered renaming _exec() to __call__() or run() but I was worried people will incorrectly call it, instead of calling exec(). So I decided to stick to a bit of awkward handling in the internals for the benefit of more obvious test-facing API. But no strong preference, LMK if calling _exec() here is fine or I should rename it.. > > + except Exception: > > I think this should be either an unqualified except: or except > BaseException:. SG > > print( > > f"# Totals: pass:{totals['pass']} fail:{totals['fail']} xfail:{totals['xfail']} xpass:0 skip:{totals['skip']} error:0" > > Majority of this hunk is just preparatory and should be in a patch of > its own. Then in this patch it should just introduce the flush. True, will split. > > + def cancel(self): > > This shouldn't dequeue if not self.queued. I was wondering if we're better off throwing the exception from remove() or silently ignoring (what is probably an error in the test code). I went with the former intentionally, but happy to change. > > + self._queue.remove(self) > > + self.queued = False > > + > > + def exec(self): > > This shouldn't exec if self.executed. > > But I actually wonder if we need two flags at all. Whether the defer > entry is resolved through exec(), cancel() or __exit__(), it's "done". > It could be left in the queue, in which case the "done" flag is going to > disable future exec requests. Or it can just be dropped from the queue > when done, in which case we don't even need the "done" flag as such. If you recall there's a rss_ctx test case which removes contexts out of order. The flags are basically for that test. We run the .exec() to remove a context, and then we can check if thing.queued: .. code for context that's alive .. else: .. code for dead context .. ^ permalink raw reply [flat|nested] 14+ messages in thread
* Re: [RFC net-next 1/2] selftests: drv-net: add ability to schedule cleanup with defer() 2024-06-26 16:09 ` Jakub Kicinski @ 2024-06-27 7:37 ` Petr Machata 2024-06-27 15:41 ` Jakub Kicinski 0 siblings, 1 reply; 14+ messages in thread From: Petr Machata @ 2024-06-27 7:37 UTC (permalink / raw) To: Jakub Kicinski Cc: Petr Machata, davem, netdev, edumazet, pabeni, willemdebruijn.kernel, przemyslaw.kitszel, leitao Jakub Kicinski <kuba@kernel.org> writes: > On Wed, 26 Jun 2024 12:18:58 +0200 Petr Machata wrote: >> Jakub Kicinski <kuba@kernel.org> writes: >> > +def ksft_flush_defer(): >> > + global KSFT_RESULT >> > + >> > + while global_defer_queue: >> > + entry = global_defer_queue[-1] >> > + try: >> > + entry.exec() >> >> I wonder if you added _exec() to invoke it here. Because then you could >> just do entry = global_defer_queue.pop() and entry._exec(), and in the >> except branch you would just have the test-related business, without the >> queue management. > > Initially I had both _exec, and _dequeue as separate helpers, but then > _dequeue was identical to cancel, so I removed that one, but _exec > stayed. > > As you point out _exec() would do nicely during "flush".. but linter was > angry at me for calling private functions. I couldn't quickly think of > a clean scheme of naming things. Or rather, I should say, I like that > the only non-private functions in class defer right now are > test-author-facing. At some point I considered renaming _exec() to > __call__() or run() but I was worried people will incorrectly > call it, instead of calling exec(). > > So I decided to stick to a bit of awkward handling in the internals for > the benefit of more obvious test-facing API. But no strong preference, > LMK if calling _exec() here is fine or I should rename it.. Maybe call it something like exec_only()? There's a list that you just need to go through, it looks a shame not to just .pop() everything out one by one and instead have this management business in the error path. >> > + except Exception: >> >> I think this should be either an unqualified except: or except >> BaseException:. > > SG > > >> > print( >> > f"# Totals: pass:{totals['pass']} fail:{totals['fail']} xfail:{totals['xfail']} xpass:0 skip:{totals['skip']} error:0" >> >> Majority of this hunk is just preparatory and should be in a patch of >> its own. Then in this patch it should just introduce the flush. > > True, will split. > >> > + def cancel(self): >> >> This shouldn't dequeue if not self.queued. > > I was wondering if we're better off throwing the exception from > remove() or silently ignoring (what is probably an error in the > test code). I went with the former intentionally, but happy to > change. Hmm, right, it would throw. Therefore second exec() would as well. Good. But that means that exec() should first cancel, then exec, otherwise second exec invocation would actually exec the cleanup a second time before bailing out. > >> > + self._queue.remove(self) >> > + self.queued = False >> > + >> > + def exec(self): >> >> This shouldn't exec if self.executed. >> >> But I actually wonder if we need two flags at all. Whether the defer >> entry is resolved through exec(), cancel() or __exit__(), it's "done". >> It could be left in the queue, in which case the "done" flag is going to >> disable future exec requests. Or it can just be dropped from the queue >> when done, in which case we don't even need the "done" flag as such. > > If you recall there's a rss_ctx test case which removes contexts out of > order. The flags are basically for that test. We run the .exec() to > remove a context, and then we can check > > if thing.queued: > .. code for context that's alive .. > else: > .. code for dead context .. That test already has its own flags to track which was removed, can't it use those? My preference is always to keep an API as minimal as possible and the flags, if any, would ideally be private. I don't think defer objects should keep track of whether the user has already invoked them or not, that's their user's business to know. ^ permalink raw reply [flat|nested] 14+ messages in thread
* Re: [RFC net-next 1/2] selftests: drv-net: add ability to schedule cleanup with defer() 2024-06-27 7:37 ` Petr Machata @ 2024-06-27 15:41 ` Jakub Kicinski 0 siblings, 0 replies; 14+ messages in thread From: Jakub Kicinski @ 2024-06-27 15:41 UTC (permalink / raw) To: Petr Machata Cc: davem, netdev, edumazet, pabeni, willemdebruijn.kernel, przemyslaw.kitszel, leitao On Thu, 27 Jun 2024 09:37:50 +0200 Petr Machata wrote: > > I was wondering if we're better off throwing the exception from > > remove() or silently ignoring (what is probably an error in the > > test code). I went with the former intentionally, but happy to > > change. > > Hmm, right, it would throw. Therefore second exec() would as well. Good. > But that means that exec() should first cancel, then exec, otherwise > second exec invocation would actually exec the cleanup a second time > before bailing out. Good point, that sounds safer. > >> This shouldn't exec if self.executed. > >> > >> But I actually wonder if we need two flags at all. Whether the defer > >> entry is resolved through exec(), cancel() or __exit__(), it's "done". > >> It could be left in the queue, in which case the "done" flag is going to > >> disable future exec requests. Or it can just be dropped from the queue > >> when done, in which case we don't even need the "done" flag as such. > > > > If you recall there's a rss_ctx test case which removes contexts out of > > order. The flags are basically for that test. We run the .exec() to > > remove a context, and then we can check > > > > if thing.queued: > > .. code for context that's alive .. > > else: > > .. code for dead context .. > > That test already has its own flags to track which was removed, can't it > use those? My preference is always to keep an API as minimal as possible > and the flags, if any, would ideally be private. I don't think defer > objects should keep track of whether the user has already invoked them > or not, that's their user's business to know. Ack, will delete it then. ^ permalink raw reply [flat|nested] 14+ messages in thread
* [RFC net-next 2/2] selftests: drv-net: rss_ctx: convert to defer() 2024-06-26 1:36 [RFC net-next 0/2] selftests: drv-net: add ability to schedule cleanup with defer() Jakub Kicinski 2024-06-26 1:36 ` [RFC net-next 1/2] " Jakub Kicinski @ 2024-06-26 1:36 ` Jakub Kicinski 1 sibling, 0 replies; 14+ messages in thread From: Jakub Kicinski @ 2024-06-26 1:36 UTC (permalink / raw) To: davem Cc: netdev, edumazet, pabeni, willemdebruijn.kernel, przemyslaw.kitszel, leitao, petrm, Jakub Kicinski Use just added defer(). Signed-off-by: Jakub Kicinski <kuba@kernel.org> --- .../selftests/drivers/net/hw/rss_ctx.py | 225 ++++++++---------- 1 file changed, 97 insertions(+), 128 deletions(-) diff --git a/tools/testing/selftests/drivers/net/hw/rss_ctx.py b/tools/testing/selftests/drivers/net/hw/rss_ctx.py index 68c7d40214eb..6f55a3b49698 100755 --- a/tools/testing/selftests/drivers/net/hw/rss_ctx.py +++ b/tools/testing/selftests/drivers/net/hw/rss_ctx.py @@ -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,64 +127,56 @@ from lib.py import ethtool, ip, GenerateTraffic, CmdExitFailure # 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") - for i in range(ctx_cnt): - want_cfg = f"start {2 + i * 2} equal 2" - create_cfg = want_cfg if create_with_cfg else "" + # Use queues 0 and 1 for normal traffic + ethtool(f"-X {cfg.ifname} equal 2") + defer(ethtool, f"-X {cfg.ifname} default") - try: - ctx_id.append(ethtool_create(cfg, "-X", f"context new {create_cfg}")) - except CmdExitFailure: - # try to carry on and skip at the end - if i == 0: - raise - ksft_pr(f"Failed to create context {i + 1}, trying to test what we got") - ctx_cnt = i - break + for i in range(ctx_cnt): + want_cfg = f"start {2 + i * 2} equal 2" + create_cfg = want_cfg if create_with_cfg else "" - if not create_with_cfg: - ethtool(f"-X {cfg.ifname} context {ctx_id[i]} {want_cfg}") + try: + 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: + raise + ksft_pr(f"Failed to create context {i + 1}, trying to test what we got") + ctx_cnt = i + break - # Sanity check the context we just created - data = get_rss(cfg, ctx_id[i]) - 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)) + if not create_with_cfg: + ethtool(f"-X {cfg.ifname} context {ctx_id} {want_cfg}") - 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)) + # Sanity check the context we just created + 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)) - for i in range(ctx_cnt): - cnts = _get_rx_cnts(cfg) - GenerateTraffic(cfg, port=ports[i]).wait_pkts_and_stop(20000) - cnts = _get_rx_cnts(cfg, prev=cnts) + ports.append(rand_port()) + 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}") - 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}") + for i in range(ctx_cnt): + cnts = _get_rx_cnts(cfg) + GenerateTraffic(cfg, port=ports[i]).wait_pkts_and_stop(20000) + cnts = _get_rx_cnts(cfg, prev=cnts) + + 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)) if requested_ctx_cnt != ctx_cnt: raise KsftSkipEx(f"Tested only {ctx_cnt} contexts, wanted {requested_ctx_cnt}") @@ -216,24 +208,21 @@ from lib.py import ethtool, ip, GenerateTraffic, CmdExitFailure # 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] = None - ethtool(f"-X {cfg.ifname} context {ctx_id[idx]} delete") - ctx_id[idx] = None + ntuple[idx].exec() + ctx[idx].exec() def check_traffic(): for i in range(ctx_cnt): @@ -241,7 +230,7 @@ from lib.py import ethtool, ip, GenerateTraffic, CmdExitFailure 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].queued: 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,41 +238,32 @@ from lib.py import ethtool, ip, GenerateTraffic, CmdExitFailure 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") + # 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")) + for i in range(ctx_cnt): + 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)) + ports.append(rand_port()) + 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() + check_traffic() - # Remove middle context - remove_ctx(ctx_cnt // 2) - check_traffic() + # Remove middle context + remove_ctx(ctx_cnt // 2) + check_traffic() - # Remove first context - remove_ctx(0) - check_traffic() + # Remove first context + remove_ctx(0) + check_traffic() - # Remove last context - 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}") + # Remove last context + remove_ctx(-1) + check_traffic() if requested_ctx_cnt != ctx_cnt: raise KsftSkipEx(f"Tested only {ctx_cnt} contexts, wanted {requested_ctx_cnt}") @@ -298,69 +278,58 @@ from lib.py import ethtool, ip, GenerateTraffic, CmdExitFailure 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") + 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) + port = rand_port() + if other_ctx: + flow = f"flow-type tcp{cfg.addr_ipver} dst-port {port} context {other_ctx}" + 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) - GenerateTraffic(cfg, port=port).wait_pkts_and_stop(20000) - cnts = _get_rx_cnts(cfg, prev=cnts) + # Test the main context + cnts = _get_rx_cnts(cfg) + GenerateTraffic(cfg, port=port).wait_pkts_and_stop(20000) + cnts = _get_rx_cnts(cfg, prev=cnts) - ksft_ge(sum(cnts[ :4]), 20000, "traffic on main context: " + str(cnts)) - ksft_ge(sum(cnts[ :2]), 7000, "traffic on main context (1/2): " + str(cnts)) - ksft_ge(sum(cnts[2:4]), 7000, "traffic on main context (2/2): " + str(cnts)) - if other_ctx == 0: - ksft_eq(sum(cnts[4: ]), 0, "traffic on other queues: " + str(cnts)) + ksft_ge(sum(cnts[ :4]), 20000, "traffic on main context: " + str(cnts)) + ksft_ge(sum(cnts[ :2]), 7000, "traffic on main context (1/2): " + str(cnts)) + ksft_ge(sum(cnts[2:4]), 7000, "traffic on main context (2/2): " + str(cnts)) + if other_ctx == 0: + ksft_eq(sum(cnts[4: ]), 0, "traffic on other queues: " + str(cnts)) - # 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 - flow = f"flow-type tcp{cfg.addr_ipver} dst-port {port} context {ctx_id}" - ntuple = ethtool_create(cfg, "-N", flow) + # Now create a rule for context 1 and make sure traffic goes to a subset + if other_ctx: + ntuple.exec() + flow = f"flow-type tcp{cfg.addr_ipver} dst-port {port} context {ctx_id}" + 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) - cnts = _get_rx_cnts(cfg, prev=cnts) + cnts = _get_rx_cnts(cfg) + GenerateTraffic(cfg, port=port).wait_pkts_and_stop(20000) + cnts = _get_rx_cnts(cfg, prev=cnts) - ksft_lt(sum(cnts[ :2]), 7000, "traffic on main context: " + str(cnts)) - 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}") + ksft_lt(sum(cnts[ :2]), 7000, "traffic on main context: " + str(cnts)) + 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)) def test_rss_context_overlap2(cfg): -- 2.45.2 ^ permalink raw reply related [flat|nested] 14+ messages in thread
end of thread, other threads:[~2024-06-27 15:41 UTC | newest] Thread overview: 14+ messages (download: mbox.gz follow: Atom feed -- links below jump to the message on this page -- 2024-06-26 1:36 [RFC net-next 0/2] selftests: drv-net: add ability to schedule cleanup with defer() Jakub Kicinski 2024-06-26 1:36 ` [RFC net-next 1/2] " Jakub Kicinski 2024-06-26 7:43 ` Przemek Kitszel 2024-06-26 9:19 ` Petr Machata 2024-06-26 9:38 ` Przemek Kitszel 2024-06-26 16:44 ` Jakub Kicinski 2024-06-26 16:49 ` Jakub Kicinski 2024-06-27 8:40 ` Przemek Kitszel 2024-06-27 15:35 ` Jakub Kicinski 2024-06-26 10:18 ` Petr Machata 2024-06-26 16:09 ` Jakub Kicinski 2024-06-27 7:37 ` Petr Machata 2024-06-27 15:41 ` Jakub Kicinski 2024-06-26 1:36 ` [RFC net-next 2/2] selftests: drv-net: rss_ctx: convert to defer() Jakub Kicinski
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).