162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0
262306a36Sopenharmony_ci/*
362306a36Sopenharmony_ci * KUnit resource API for test managed resources (allocations, etc.).
462306a36Sopenharmony_ci *
562306a36Sopenharmony_ci * Copyright (C) 2022, Google LLC.
662306a36Sopenharmony_ci * Author: Daniel Latypov <dlatypov@google.com>
762306a36Sopenharmony_ci */
862306a36Sopenharmony_ci
962306a36Sopenharmony_ci#include <kunit/resource.h>
1062306a36Sopenharmony_ci#include <kunit/test.h>
1162306a36Sopenharmony_ci#include <linux/kref.h>
1262306a36Sopenharmony_ci
1362306a36Sopenharmony_ci/*
1462306a36Sopenharmony_ci * Used for static resources and when a kunit_resource * has been created by
1562306a36Sopenharmony_ci * kunit_alloc_resource().  When an init function is supplied, @data is passed
1662306a36Sopenharmony_ci * into the init function; otherwise, we simply set the resource data field to
1762306a36Sopenharmony_ci * the data value passed in. Doesn't initialize res->should_kfree.
1862306a36Sopenharmony_ci */
1962306a36Sopenharmony_ciint __kunit_add_resource(struct kunit *test,
2062306a36Sopenharmony_ci			 kunit_resource_init_t init,
2162306a36Sopenharmony_ci			 kunit_resource_free_t free,
2262306a36Sopenharmony_ci			 struct kunit_resource *res,
2362306a36Sopenharmony_ci			 void *data)
2462306a36Sopenharmony_ci{
2562306a36Sopenharmony_ci	int ret = 0;
2662306a36Sopenharmony_ci	unsigned long flags;
2762306a36Sopenharmony_ci
2862306a36Sopenharmony_ci	res->free = free;
2962306a36Sopenharmony_ci	kref_init(&res->refcount);
3062306a36Sopenharmony_ci
3162306a36Sopenharmony_ci	if (init) {
3262306a36Sopenharmony_ci		ret = init(res, data);
3362306a36Sopenharmony_ci		if (ret)
3462306a36Sopenharmony_ci			return ret;
3562306a36Sopenharmony_ci	} else {
3662306a36Sopenharmony_ci		res->data = data;
3762306a36Sopenharmony_ci	}
3862306a36Sopenharmony_ci
3962306a36Sopenharmony_ci	spin_lock_irqsave(&test->lock, flags);
4062306a36Sopenharmony_ci	list_add_tail(&res->node, &test->resources);
4162306a36Sopenharmony_ci	/* refcount for list is established by kref_init() */
4262306a36Sopenharmony_ci	spin_unlock_irqrestore(&test->lock, flags);
4362306a36Sopenharmony_ci
4462306a36Sopenharmony_ci	return ret;
4562306a36Sopenharmony_ci}
4662306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(__kunit_add_resource);
4762306a36Sopenharmony_ci
4862306a36Sopenharmony_civoid kunit_remove_resource(struct kunit *test, struct kunit_resource *res)
4962306a36Sopenharmony_ci{
5062306a36Sopenharmony_ci	unsigned long flags;
5162306a36Sopenharmony_ci	bool was_linked;
5262306a36Sopenharmony_ci
5362306a36Sopenharmony_ci	spin_lock_irqsave(&test->lock, flags);
5462306a36Sopenharmony_ci	was_linked = !list_empty(&res->node);
5562306a36Sopenharmony_ci	list_del_init(&res->node);
5662306a36Sopenharmony_ci	spin_unlock_irqrestore(&test->lock, flags);
5762306a36Sopenharmony_ci
5862306a36Sopenharmony_ci	if (was_linked)
5962306a36Sopenharmony_ci		kunit_put_resource(res);
6062306a36Sopenharmony_ci}
6162306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(kunit_remove_resource);
6262306a36Sopenharmony_ci
6362306a36Sopenharmony_ciint kunit_destroy_resource(struct kunit *test, kunit_resource_match_t match,
6462306a36Sopenharmony_ci			   void *match_data)
6562306a36Sopenharmony_ci{
6662306a36Sopenharmony_ci	struct kunit_resource *res = kunit_find_resource(test, match,
6762306a36Sopenharmony_ci							 match_data);
6862306a36Sopenharmony_ci
6962306a36Sopenharmony_ci	if (!res)
7062306a36Sopenharmony_ci		return -ENOENT;
7162306a36Sopenharmony_ci
7262306a36Sopenharmony_ci	kunit_remove_resource(test, res);
7362306a36Sopenharmony_ci
7462306a36Sopenharmony_ci	/* We have a reference also via _find(); drop it. */
7562306a36Sopenharmony_ci	kunit_put_resource(res);
7662306a36Sopenharmony_ci
7762306a36Sopenharmony_ci	return 0;
7862306a36Sopenharmony_ci}
7962306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(kunit_destroy_resource);
8062306a36Sopenharmony_ci
8162306a36Sopenharmony_cistruct kunit_action_ctx {
8262306a36Sopenharmony_ci	struct kunit_resource res;
8362306a36Sopenharmony_ci	kunit_action_t *func;
8462306a36Sopenharmony_ci	void *ctx;
8562306a36Sopenharmony_ci};
8662306a36Sopenharmony_ci
8762306a36Sopenharmony_cistatic void __kunit_action_free(struct kunit_resource *res)
8862306a36Sopenharmony_ci{
8962306a36Sopenharmony_ci	struct kunit_action_ctx *action_ctx = container_of(res, struct kunit_action_ctx, res);
9062306a36Sopenharmony_ci
9162306a36Sopenharmony_ci	action_ctx->func(action_ctx->ctx);
9262306a36Sopenharmony_ci}
9362306a36Sopenharmony_ci
9462306a36Sopenharmony_ci
9562306a36Sopenharmony_ciint kunit_add_action(struct kunit *test, void (*action)(void *), void *ctx)
9662306a36Sopenharmony_ci{
9762306a36Sopenharmony_ci	struct kunit_action_ctx *action_ctx;
9862306a36Sopenharmony_ci
9962306a36Sopenharmony_ci	KUNIT_ASSERT_NOT_NULL_MSG(test, action, "Tried to action a NULL function!");
10062306a36Sopenharmony_ci
10162306a36Sopenharmony_ci	action_ctx = kzalloc(sizeof(*action_ctx), GFP_KERNEL);
10262306a36Sopenharmony_ci	if (!action_ctx)
10362306a36Sopenharmony_ci		return -ENOMEM;
10462306a36Sopenharmony_ci
10562306a36Sopenharmony_ci	action_ctx->func = action;
10662306a36Sopenharmony_ci	action_ctx->ctx = ctx;
10762306a36Sopenharmony_ci
10862306a36Sopenharmony_ci	action_ctx->res.should_kfree = true;
10962306a36Sopenharmony_ci	/* As init is NULL, this cannot fail. */
11062306a36Sopenharmony_ci	__kunit_add_resource(test, NULL, __kunit_action_free, &action_ctx->res, action_ctx);
11162306a36Sopenharmony_ci
11262306a36Sopenharmony_ci	return 0;
11362306a36Sopenharmony_ci}
11462306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(kunit_add_action);
11562306a36Sopenharmony_ci
11662306a36Sopenharmony_ciint kunit_add_action_or_reset(struct kunit *test, void (*action)(void *),
11762306a36Sopenharmony_ci			      void *ctx)
11862306a36Sopenharmony_ci{
11962306a36Sopenharmony_ci	int res = kunit_add_action(test, action, ctx);
12062306a36Sopenharmony_ci
12162306a36Sopenharmony_ci	if (res)
12262306a36Sopenharmony_ci		action(ctx);
12362306a36Sopenharmony_ci	return res;
12462306a36Sopenharmony_ci}
12562306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(kunit_add_action_or_reset);
12662306a36Sopenharmony_ci
12762306a36Sopenharmony_cistatic bool __kunit_action_match(struct kunit *test,
12862306a36Sopenharmony_ci				struct kunit_resource *res, void *match_data)
12962306a36Sopenharmony_ci{
13062306a36Sopenharmony_ci	struct kunit_action_ctx *match_ctx = (struct kunit_action_ctx *)match_data;
13162306a36Sopenharmony_ci	struct kunit_action_ctx *res_ctx = container_of(res, struct kunit_action_ctx, res);
13262306a36Sopenharmony_ci
13362306a36Sopenharmony_ci	/* Make sure this is a free function. */
13462306a36Sopenharmony_ci	if (res->free != __kunit_action_free)
13562306a36Sopenharmony_ci		return false;
13662306a36Sopenharmony_ci
13762306a36Sopenharmony_ci	/* Both the function and context data should match. */
13862306a36Sopenharmony_ci	return (match_ctx->func == res_ctx->func) && (match_ctx->ctx == res_ctx->ctx);
13962306a36Sopenharmony_ci}
14062306a36Sopenharmony_ci
14162306a36Sopenharmony_civoid kunit_remove_action(struct kunit *test,
14262306a36Sopenharmony_ci			kunit_action_t *action,
14362306a36Sopenharmony_ci			void *ctx)
14462306a36Sopenharmony_ci{
14562306a36Sopenharmony_ci	struct kunit_action_ctx match_ctx;
14662306a36Sopenharmony_ci	struct kunit_resource *res;
14762306a36Sopenharmony_ci
14862306a36Sopenharmony_ci	match_ctx.func = action;
14962306a36Sopenharmony_ci	match_ctx.ctx = ctx;
15062306a36Sopenharmony_ci
15162306a36Sopenharmony_ci	res = kunit_find_resource(test, __kunit_action_match, &match_ctx);
15262306a36Sopenharmony_ci	if (res) {
15362306a36Sopenharmony_ci		/* Remove the free function so we don't run the action. */
15462306a36Sopenharmony_ci		res->free = NULL;
15562306a36Sopenharmony_ci		kunit_remove_resource(test, res);
15662306a36Sopenharmony_ci		kunit_put_resource(res);
15762306a36Sopenharmony_ci	}
15862306a36Sopenharmony_ci}
15962306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(kunit_remove_action);
16062306a36Sopenharmony_ci
16162306a36Sopenharmony_civoid kunit_release_action(struct kunit *test,
16262306a36Sopenharmony_ci			 kunit_action_t *action,
16362306a36Sopenharmony_ci			 void *ctx)
16462306a36Sopenharmony_ci{
16562306a36Sopenharmony_ci	struct kunit_action_ctx match_ctx;
16662306a36Sopenharmony_ci	struct kunit_resource *res;
16762306a36Sopenharmony_ci
16862306a36Sopenharmony_ci	match_ctx.func = action;
16962306a36Sopenharmony_ci	match_ctx.ctx = ctx;
17062306a36Sopenharmony_ci
17162306a36Sopenharmony_ci	res = kunit_find_resource(test, __kunit_action_match, &match_ctx);
17262306a36Sopenharmony_ci	if (res) {
17362306a36Sopenharmony_ci		kunit_remove_resource(test, res);
17462306a36Sopenharmony_ci		/* We have to put() this here, else free won't be called. */
17562306a36Sopenharmony_ci		kunit_put_resource(res);
17662306a36Sopenharmony_ci	}
17762306a36Sopenharmony_ci}
17862306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(kunit_release_action);
179