Hi, On Tue, 2023-04-04 at 15:32 +0200, Maxime Ripard wrote: > [SNIP] > > +/** > > + * kunit_add_action() - Defer an 'action' (function call) until the test ends. > > + * @test: Test case to associate the action with. > > + * @func: The function to run on test exit > > + * @ctx: Data passed into @func > > + * @internal_gfp: gfp to use for internal allocations, if unsure, use GFP_KERNEL > > + * > > + * Defer the execution of a function until the test exits, either normally or > > + * due to a failure. @ctx is passed as additional context. All functions > > + * registered with kunit_add_action() will execute in the opposite order to that > > + * they were registered in. > > + * > > + * This is useful for cleaning up allocated memory and resources. > > + * > > + * Returns: > > + * An opaque "cancellation token", or NULL on error. Pass this token to > > + * kunit_remove_action_token() in order to cancel the deferred execution of > > + * func(). > > + */ > > +struct kunit_action_ctx *kunit_add_action(struct kunit *test, kunit_defer_function_t func, > > + void *ctx, gfp_t internal_gfp); > > Do we expect any other context than GFP_KERNEL? > > If so, then maybe we can have kunit_add_action() assume GFP_KERNEL and > add a variant for the odd case where we would actually need a different > GFP flag. Does anything other than GFP_KERNEL make sense? I would assume these functions should only ever be called from a kunit context, i.e. the passed test is guaranteed to be identical to the value returned by kunit_get_current_test(). That said, I am happy with merging this in this form. I feel the right thing here is a patch (with corresponding spatch) that changes all of the related APIs to remove the gfp argument. > > +/** > > + * kunit_remove_action_token() - Cancel a deferred action. > > + * @test: Test case the action is associated with. > > + * @cancel_token: The cancellation token returned by kunit_add_action() > > + * > > + * Prevent an action deferred using kunit_add_action() from executing when the > > + * test ends. > > + * > > + * You can also use the (test, function, context) triplet to remove an action > > + * with kunit_remove_action(). > > + */ > > +void kunit_remove_action_token(struct kunit *test, struct kunit_action_ctx *cancel_token); > > It's not clear to me why we still need the token. If > kunit_remove_action() works fine, why would we need to store the token? > > Can't we just make kunit_add_action() return an int to indicate whether > it failed or not, and that's it? > > > [SNIP] > > One thing worth pointing is that if kunit_add_action() fails, the > cleanup function passed as an argument won't run. > > So, if the kzalloc call ever fails, patch 2 will leak its res->data() > resource for example. > > devm (and drmm) handles this using a variant called > devm_add_action_or_reset, we should either provide the same variant or > just go for that behavior by default. Both version of the function would need a return value. An alternative might be to make assertions part of the API. But as with dropping the gfp argument, that seems like a more intrusive change that needs to happen independently. Anyway, I am fine with action_or_reset as the default and possibly the only behaviour. I expect that every API user will want an assertion that checks for failure here anyway. Benjamin If kunit_* functions can assert in error conditions, then the example void test_func(struct kunit *test) { char u8 *buf = kunit_kzalloc(test, 1024, GFP_KERNEL); struct sk_buff *skb_a; struct sk_buff *skb_b; /* Further variables */ KUNIT_ASSERT_NOT_NULL(test, buf); skb_a = skb_alloc(1024, GFP_KERNEL); KUNIT_ASSERT_NOT_NULL(test, skb_a); if (kunit_add_cleanup(test, (kunit_defer_function_t) kfree_skb, skb_a)) KUNIT_ASSERT_FAILURE("Failed to add cleanup"); /* Or, maybe: */ skb_b = skb_alloc(1024, GFP_KERNEL); KUNIT_ASSERT_NOT_NULL(test, skb_b); KUNIT_ASSERT_EQ(test, 0, kunit_add_cleanup(test, (kunit_defer_function_t) kfree_skb, skb_b)); /* run code that may assert */ } could be shortened to (with a trivial kunit_skb_alloc helper) void test_func(struct kunit *test) { char u8 *buf = kunit_kzalloc(test, 1024, GFP_KERNEL); struct sk_buff *skb_a = kunit_skb_alloc(1024, GFP_KERNEL); struct sk_buff *skb_b = kunit_skb_alloc(1024, GFP_KERNEL); /* Further variables */ /* run code that may assert */ } I should just post a patch for the existing API and see what people say then ...