lkml.org 
[lkml]   [2023]   [Apr]   [5]   [last100]   RSS Feed
Views: [wrap][no wrap]   [headers]  [forward] 
 
Messages in this thread
    /
    From
    Date
    SubjectRe: [RFC PATCH v2 1/3] kunit: Add kunit_add_action() to defer a call until test exit
    On Wed, 5 Apr 2023 at 01:55, Benjamin Berg <benjamin@sipsolutions.net> wrote:
    >
    > 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's not strictly-speaking guaranteed. (Indeed, we have some, albeit
    contrived, counterexamples in the test.)

    The theoretical use-case here is if the kunit context pointer is
    passed to another thread or workqueue or something.

    There aren't any such users, yet (apart from, possibly,
    kunit_kmalloc_array()), though. So we could use GFP_KERNEL by default
    for now, and add a variant if such a use-case appears.

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

    I'm tempted to just have both kunit_add_action() and
    kunit_add_action_or_reset(), just to keep things matching the devm_
    API to minimise any confusion.

    And if we're not too worried about proliferating variants of these
    (and, personally, I quite like them), we could have a
    kunit_add_action_or_asserrt() version as well.

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

    We definitely already have some functions (e.g.
    __kunit_activate_static_stub()) which just assert on failure. In
    general, we've avoided doing so where we think there might be a good
    reason to handle failures separately (or it makes the API diverge a
    lot from a function we're imitating), but I'm open to using them more.
    Specialised handling of allocation failures in a test is likely to be
    rare enough that making those who need it write their own helpers
    wouldn't be a disaster. Or we could have an _or_assert() variant.

    In any case, I think your example pretty comfortably speaks for itself.

    Cheers,
    -- David
    [unhandled content-type:application/pkcs7-signature]
    \
     
     \ /
      Last update: 2023-04-05 10:10    [W:4.852 / U:0.128 seconds]
    ©2003-2020 Jasper Spaans|hosted at Digital Ocean and TransIP|Read the blog|Advertise on this site