xref: /openbmc/linux/include/kunit/resource.h (revision 61695f8c5d5190db11aece403304f06d22c90597)
1*61695f8cSDaniel Latypov /* SPDX-License-Identifier: GPL-2.0 */
2*61695f8cSDaniel Latypov /*
3*61695f8cSDaniel Latypov  * KUnit resource API for test managed resources (allocations, etc.).
4*61695f8cSDaniel Latypov  *
5*61695f8cSDaniel Latypov  * Copyright (C) 2022, Google LLC.
6*61695f8cSDaniel Latypov  * Author: Daniel Latypov <dlatypov@google.com>
7*61695f8cSDaniel Latypov  */
8*61695f8cSDaniel Latypov 
9*61695f8cSDaniel Latypov #ifndef _KUNIT_RESOURCE_H
10*61695f8cSDaniel Latypov #define _KUNIT_RESOURCE_H
11*61695f8cSDaniel Latypov 
12*61695f8cSDaniel Latypov #include <kunit/test.h>
13*61695f8cSDaniel Latypov 
14*61695f8cSDaniel Latypov #include <linux/kref.h>
15*61695f8cSDaniel Latypov #include <linux/list.h>
16*61695f8cSDaniel Latypov #include <linux/slab.h>
17*61695f8cSDaniel Latypov #include <linux/spinlock.h>
18*61695f8cSDaniel Latypov 
19*61695f8cSDaniel Latypov struct kunit_resource;
20*61695f8cSDaniel Latypov 
21*61695f8cSDaniel Latypov typedef int (*kunit_resource_init_t)(struct kunit_resource *, void *);
22*61695f8cSDaniel Latypov typedef void (*kunit_resource_free_t)(struct kunit_resource *);
23*61695f8cSDaniel Latypov 
24*61695f8cSDaniel Latypov /**
25*61695f8cSDaniel Latypov  * struct kunit_resource - represents a *test managed resource*
26*61695f8cSDaniel Latypov  * @data: for the user to store arbitrary data.
27*61695f8cSDaniel Latypov  * @name: optional name
28*61695f8cSDaniel Latypov  * @free: a user supplied function to free the resource. Populated by
29*61695f8cSDaniel Latypov  * kunit_resource_alloc().
30*61695f8cSDaniel Latypov  *
31*61695f8cSDaniel Latypov  * Represents a *test managed resource*, a resource which will automatically be
32*61695f8cSDaniel Latypov  * cleaned up at the end of a test case.
33*61695f8cSDaniel Latypov  *
34*61695f8cSDaniel Latypov  * Resources are reference counted so if a resource is retrieved via
35*61695f8cSDaniel Latypov  * kunit_alloc_and_get_resource() or kunit_find_resource(), we need
36*61695f8cSDaniel Latypov  * to call kunit_put_resource() to reduce the resource reference count
37*61695f8cSDaniel Latypov  * when finished with it.  Note that kunit_alloc_resource() does not require a
38*61695f8cSDaniel Latypov  * kunit_resource_put() because it does not retrieve the resource itself.
39*61695f8cSDaniel Latypov  *
40*61695f8cSDaniel Latypov  * Example:
41*61695f8cSDaniel Latypov  *
42*61695f8cSDaniel Latypov  * .. code-block:: c
43*61695f8cSDaniel Latypov  *
44*61695f8cSDaniel Latypov  *	struct kunit_kmalloc_params {
45*61695f8cSDaniel Latypov  *		size_t size;
46*61695f8cSDaniel Latypov  *		gfp_t gfp;
47*61695f8cSDaniel Latypov  *	};
48*61695f8cSDaniel Latypov  *
49*61695f8cSDaniel Latypov  *	static int kunit_kmalloc_init(struct kunit_resource *res, void *context)
50*61695f8cSDaniel Latypov  *	{
51*61695f8cSDaniel Latypov  *		struct kunit_kmalloc_params *params = context;
52*61695f8cSDaniel Latypov  *		res->data = kmalloc(params->size, params->gfp);
53*61695f8cSDaniel Latypov  *
54*61695f8cSDaniel Latypov  *		if (!res->data)
55*61695f8cSDaniel Latypov  *			return -ENOMEM;
56*61695f8cSDaniel Latypov  *
57*61695f8cSDaniel Latypov  *		return 0;
58*61695f8cSDaniel Latypov  *	}
59*61695f8cSDaniel Latypov  *
60*61695f8cSDaniel Latypov  *	static void kunit_kmalloc_free(struct kunit_resource *res)
61*61695f8cSDaniel Latypov  *	{
62*61695f8cSDaniel Latypov  *		kfree(res->data);
63*61695f8cSDaniel Latypov  *	}
64*61695f8cSDaniel Latypov  *
65*61695f8cSDaniel Latypov  *	void *kunit_kmalloc(struct kunit *test, size_t size, gfp_t gfp)
66*61695f8cSDaniel Latypov  *	{
67*61695f8cSDaniel Latypov  *		struct kunit_kmalloc_params params;
68*61695f8cSDaniel Latypov  *
69*61695f8cSDaniel Latypov  *		params.size = size;
70*61695f8cSDaniel Latypov  *		params.gfp = gfp;
71*61695f8cSDaniel Latypov  *
72*61695f8cSDaniel Latypov  *		return kunit_alloc_resource(test, kunit_kmalloc_init,
73*61695f8cSDaniel Latypov  *			kunit_kmalloc_free, &params);
74*61695f8cSDaniel Latypov  *	}
75*61695f8cSDaniel Latypov  *
76*61695f8cSDaniel Latypov  * Resources can also be named, with lookup/removal done on a name
77*61695f8cSDaniel Latypov  * basis also.  kunit_add_named_resource(), kunit_find_named_resource()
78*61695f8cSDaniel Latypov  * and kunit_destroy_named_resource().  Resource names must be
79*61695f8cSDaniel Latypov  * unique within the test instance.
80*61695f8cSDaniel Latypov  */
81*61695f8cSDaniel Latypov struct kunit_resource {
82*61695f8cSDaniel Latypov 	void *data;
83*61695f8cSDaniel Latypov 	const char *name;
84*61695f8cSDaniel Latypov 	kunit_resource_free_t free;
85*61695f8cSDaniel Latypov 
86*61695f8cSDaniel Latypov 	/* private: internal use only. */
87*61695f8cSDaniel Latypov 	struct kref refcount;
88*61695f8cSDaniel Latypov 	struct list_head node;
89*61695f8cSDaniel Latypov };
90*61695f8cSDaniel Latypov 
91*61695f8cSDaniel Latypov /*
92*61695f8cSDaniel Latypov  * Like kunit_alloc_resource() below, but returns the struct kunit_resource
93*61695f8cSDaniel Latypov  * object that contains the allocation. This is mostly for testing purposes.
94*61695f8cSDaniel Latypov  */
95*61695f8cSDaniel Latypov struct kunit_resource *kunit_alloc_and_get_resource(struct kunit *test,
96*61695f8cSDaniel Latypov 						    kunit_resource_init_t init,
97*61695f8cSDaniel Latypov 						    kunit_resource_free_t free,
98*61695f8cSDaniel Latypov 						    gfp_t internal_gfp,
99*61695f8cSDaniel Latypov 						    void *context);
100*61695f8cSDaniel Latypov 
101*61695f8cSDaniel Latypov /**
102*61695f8cSDaniel Latypov  * kunit_get_resource() - Hold resource for use.  Should not need to be used
103*61695f8cSDaniel Latypov  *			  by most users as we automatically get resources
104*61695f8cSDaniel Latypov  *			  retrieved by kunit_find_resource*().
105*61695f8cSDaniel Latypov  * @res: resource
106*61695f8cSDaniel Latypov  */
107*61695f8cSDaniel Latypov static inline void kunit_get_resource(struct kunit_resource *res)
108*61695f8cSDaniel Latypov {
109*61695f8cSDaniel Latypov 	kref_get(&res->refcount);
110*61695f8cSDaniel Latypov }
111*61695f8cSDaniel Latypov 
112*61695f8cSDaniel Latypov /*
113*61695f8cSDaniel Latypov  * Called when refcount reaches zero via kunit_put_resource();
114*61695f8cSDaniel Latypov  * should not be called directly.
115*61695f8cSDaniel Latypov  */
116*61695f8cSDaniel Latypov static inline void kunit_release_resource(struct kref *kref)
117*61695f8cSDaniel Latypov {
118*61695f8cSDaniel Latypov 	struct kunit_resource *res = container_of(kref, struct kunit_resource,
119*61695f8cSDaniel Latypov 						  refcount);
120*61695f8cSDaniel Latypov 
121*61695f8cSDaniel Latypov 	/* If free function is defined, resource was dynamically allocated. */
122*61695f8cSDaniel Latypov 	if (res->free) {
123*61695f8cSDaniel Latypov 		res->free(res);
124*61695f8cSDaniel Latypov 		kfree(res);
125*61695f8cSDaniel Latypov 	}
126*61695f8cSDaniel Latypov }
127*61695f8cSDaniel Latypov 
128*61695f8cSDaniel Latypov /**
129*61695f8cSDaniel Latypov  * kunit_put_resource() - When caller is done with retrieved resource,
130*61695f8cSDaniel Latypov  *			  kunit_put_resource() should be called to drop
131*61695f8cSDaniel Latypov  *			  reference count.  The resource list maintains
132*61695f8cSDaniel Latypov  *			  a reference count on resources, so if no users
133*61695f8cSDaniel Latypov  *			  are utilizing a resource and it is removed from
134*61695f8cSDaniel Latypov  *			  the resource list, it will be freed via the
135*61695f8cSDaniel Latypov  *			  associated free function (if any).  Only
136*61695f8cSDaniel Latypov  *			  needs to be used if we alloc_and_get() or
137*61695f8cSDaniel Latypov  *			  find() resource.
138*61695f8cSDaniel Latypov  * @res: resource
139*61695f8cSDaniel Latypov  */
140*61695f8cSDaniel Latypov static inline void kunit_put_resource(struct kunit_resource *res)
141*61695f8cSDaniel Latypov {
142*61695f8cSDaniel Latypov 	kref_put(&res->refcount, kunit_release_resource);
143*61695f8cSDaniel Latypov }
144*61695f8cSDaniel Latypov 
145*61695f8cSDaniel Latypov /**
146*61695f8cSDaniel Latypov  * kunit_add_resource() - Add a *test managed resource*.
147*61695f8cSDaniel Latypov  * @test: The test context object.
148*61695f8cSDaniel Latypov  * @init: a user-supplied function to initialize the result (if needed).  If
149*61695f8cSDaniel Latypov  *        none is supplied, the resource data value is simply set to @data.
150*61695f8cSDaniel Latypov  *	  If an init function is supplied, @data is passed to it instead.
151*61695f8cSDaniel Latypov  * @free: a user-supplied function to free the resource (if needed).
152*61695f8cSDaniel Latypov  * @res: The resource.
153*61695f8cSDaniel Latypov  * @data: value to pass to init function or set in resource data field.
154*61695f8cSDaniel Latypov  */
155*61695f8cSDaniel Latypov int kunit_add_resource(struct kunit *test,
156*61695f8cSDaniel Latypov 		       kunit_resource_init_t init,
157*61695f8cSDaniel Latypov 		       kunit_resource_free_t free,
158*61695f8cSDaniel Latypov 		       struct kunit_resource *res,
159*61695f8cSDaniel Latypov 		       void *data);
160*61695f8cSDaniel Latypov 
161*61695f8cSDaniel Latypov /**
162*61695f8cSDaniel Latypov  * kunit_add_named_resource() - Add a named *test managed resource*.
163*61695f8cSDaniel Latypov  * @test: The test context object.
164*61695f8cSDaniel Latypov  * @init: a user-supplied function to initialize the resource data, if needed.
165*61695f8cSDaniel Latypov  * @free: a user-supplied function to free the resource data, if needed.
166*61695f8cSDaniel Latypov  * @res: The resource.
167*61695f8cSDaniel Latypov  * @name: name to be set for resource.
168*61695f8cSDaniel Latypov  * @data: value to pass to init function or set in resource data field.
169*61695f8cSDaniel Latypov  */
170*61695f8cSDaniel Latypov int kunit_add_named_resource(struct kunit *test,
171*61695f8cSDaniel Latypov 			     kunit_resource_init_t init,
172*61695f8cSDaniel Latypov 			     kunit_resource_free_t free,
173*61695f8cSDaniel Latypov 			     struct kunit_resource *res,
174*61695f8cSDaniel Latypov 			     const char *name,
175*61695f8cSDaniel Latypov 			     void *data);
176*61695f8cSDaniel Latypov 
177*61695f8cSDaniel Latypov /**
178*61695f8cSDaniel Latypov  * kunit_alloc_resource() - Allocates a *test managed resource*.
179*61695f8cSDaniel Latypov  * @test: The test context object.
180*61695f8cSDaniel Latypov  * @init: a user supplied function to initialize the resource.
181*61695f8cSDaniel Latypov  * @free: a user supplied function to free the resource.
182*61695f8cSDaniel Latypov  * @internal_gfp: gfp to use for internal allocations, if unsure, use GFP_KERNEL
183*61695f8cSDaniel Latypov  * @context: for the user to pass in arbitrary data to the init function.
184*61695f8cSDaniel Latypov  *
185*61695f8cSDaniel Latypov  * Allocates a *test managed resource*, a resource which will automatically be
186*61695f8cSDaniel Latypov  * cleaned up at the end of a test case. See &struct kunit_resource for an
187*61695f8cSDaniel Latypov  * example.
188*61695f8cSDaniel Latypov  *
189*61695f8cSDaniel Latypov  * Note: KUnit needs to allocate memory for a kunit_resource object. You must
190*61695f8cSDaniel Latypov  * specify an @internal_gfp that is compatible with the use context of your
191*61695f8cSDaniel Latypov  * resource.
192*61695f8cSDaniel Latypov  */
193*61695f8cSDaniel Latypov static inline void *kunit_alloc_resource(struct kunit *test,
194*61695f8cSDaniel Latypov 					 kunit_resource_init_t init,
195*61695f8cSDaniel Latypov 					 kunit_resource_free_t free,
196*61695f8cSDaniel Latypov 					 gfp_t internal_gfp,
197*61695f8cSDaniel Latypov 					 void *context)
198*61695f8cSDaniel Latypov {
199*61695f8cSDaniel Latypov 	struct kunit_resource *res;
200*61695f8cSDaniel Latypov 
201*61695f8cSDaniel Latypov 	res = kzalloc(sizeof(*res), internal_gfp);
202*61695f8cSDaniel Latypov 	if (!res)
203*61695f8cSDaniel Latypov 		return NULL;
204*61695f8cSDaniel Latypov 
205*61695f8cSDaniel Latypov 	if (!kunit_add_resource(test, init, free, res, context))
206*61695f8cSDaniel Latypov 		return res->data;
207*61695f8cSDaniel Latypov 
208*61695f8cSDaniel Latypov 	return NULL;
209*61695f8cSDaniel Latypov }
210*61695f8cSDaniel Latypov 
211*61695f8cSDaniel Latypov typedef bool (*kunit_resource_match_t)(struct kunit *test,
212*61695f8cSDaniel Latypov 				       struct kunit_resource *res,
213*61695f8cSDaniel Latypov 				       void *match_data);
214*61695f8cSDaniel Latypov 
215*61695f8cSDaniel Latypov /**
216*61695f8cSDaniel Latypov  * kunit_resource_instance_match() - Match a resource with the same instance.
217*61695f8cSDaniel Latypov  * @test: Test case to which the resource belongs.
218*61695f8cSDaniel Latypov  * @res: The resource.
219*61695f8cSDaniel Latypov  * @match_data: The resource pointer to match against.
220*61695f8cSDaniel Latypov  *
221*61695f8cSDaniel Latypov  * An instance of kunit_resource_match_t that matches a resource whose
222*61695f8cSDaniel Latypov  * allocation matches @match_data.
223*61695f8cSDaniel Latypov  */
224*61695f8cSDaniel Latypov static inline bool kunit_resource_instance_match(struct kunit *test,
225*61695f8cSDaniel Latypov 						 struct kunit_resource *res,
226*61695f8cSDaniel Latypov 						 void *match_data)
227*61695f8cSDaniel Latypov {
228*61695f8cSDaniel Latypov 	return res->data == match_data;
229*61695f8cSDaniel Latypov }
230*61695f8cSDaniel Latypov 
231*61695f8cSDaniel Latypov /**
232*61695f8cSDaniel Latypov  * kunit_resource_name_match() - Match a resource with the same name.
233*61695f8cSDaniel Latypov  * @test: Test case to which the resource belongs.
234*61695f8cSDaniel Latypov  * @res: The resource.
235*61695f8cSDaniel Latypov  * @match_name: The name to match against.
236*61695f8cSDaniel Latypov  */
237*61695f8cSDaniel Latypov static inline bool kunit_resource_name_match(struct kunit *test,
238*61695f8cSDaniel Latypov 					     struct kunit_resource *res,
239*61695f8cSDaniel Latypov 					     void *match_name)
240*61695f8cSDaniel Latypov {
241*61695f8cSDaniel Latypov 	return res->name && strcmp(res->name, match_name) == 0;
242*61695f8cSDaniel Latypov }
243*61695f8cSDaniel Latypov 
244*61695f8cSDaniel Latypov /**
245*61695f8cSDaniel Latypov  * kunit_find_resource() - Find a resource using match function/data.
246*61695f8cSDaniel Latypov  * @test: Test case to which the resource belongs.
247*61695f8cSDaniel Latypov  * @match: match function to be applied to resources/match data.
248*61695f8cSDaniel Latypov  * @match_data: data to be used in matching.
249*61695f8cSDaniel Latypov  */
250*61695f8cSDaniel Latypov static inline struct kunit_resource *
251*61695f8cSDaniel Latypov kunit_find_resource(struct kunit *test,
252*61695f8cSDaniel Latypov 		    kunit_resource_match_t match,
253*61695f8cSDaniel Latypov 		    void *match_data)
254*61695f8cSDaniel Latypov {
255*61695f8cSDaniel Latypov 	struct kunit_resource *res, *found = NULL;
256*61695f8cSDaniel Latypov 	unsigned long flags;
257*61695f8cSDaniel Latypov 
258*61695f8cSDaniel Latypov 	spin_lock_irqsave(&test->lock, flags);
259*61695f8cSDaniel Latypov 
260*61695f8cSDaniel Latypov 	list_for_each_entry_reverse(res, &test->resources, node) {
261*61695f8cSDaniel Latypov 		if (match(test, res, (void *)match_data)) {
262*61695f8cSDaniel Latypov 			found = res;
263*61695f8cSDaniel Latypov 			kunit_get_resource(found);
264*61695f8cSDaniel Latypov 			break;
265*61695f8cSDaniel Latypov 		}
266*61695f8cSDaniel Latypov 	}
267*61695f8cSDaniel Latypov 
268*61695f8cSDaniel Latypov 	spin_unlock_irqrestore(&test->lock, flags);
269*61695f8cSDaniel Latypov 
270*61695f8cSDaniel Latypov 	return found;
271*61695f8cSDaniel Latypov }
272*61695f8cSDaniel Latypov 
273*61695f8cSDaniel Latypov /**
274*61695f8cSDaniel Latypov  * kunit_find_named_resource() - Find a resource using match name.
275*61695f8cSDaniel Latypov  * @test: Test case to which the resource belongs.
276*61695f8cSDaniel Latypov  * @name: match name.
277*61695f8cSDaniel Latypov  */
278*61695f8cSDaniel Latypov static inline struct kunit_resource *
279*61695f8cSDaniel Latypov kunit_find_named_resource(struct kunit *test,
280*61695f8cSDaniel Latypov 			  const char *name)
281*61695f8cSDaniel Latypov {
282*61695f8cSDaniel Latypov 	return kunit_find_resource(test, kunit_resource_name_match,
283*61695f8cSDaniel Latypov 				   (void *)name);
284*61695f8cSDaniel Latypov }
285*61695f8cSDaniel Latypov 
286*61695f8cSDaniel Latypov /**
287*61695f8cSDaniel Latypov  * kunit_destroy_resource() - Find a kunit_resource and destroy it.
288*61695f8cSDaniel Latypov  * @test: Test case to which the resource belongs.
289*61695f8cSDaniel Latypov  * @match: Match function. Returns whether a given resource matches @match_data.
290*61695f8cSDaniel Latypov  * @match_data: Data passed into @match.
291*61695f8cSDaniel Latypov  *
292*61695f8cSDaniel Latypov  * RETURNS:
293*61695f8cSDaniel Latypov  * 0 if kunit_resource is found and freed, -ENOENT if not found.
294*61695f8cSDaniel Latypov  */
295*61695f8cSDaniel Latypov int kunit_destroy_resource(struct kunit *test,
296*61695f8cSDaniel Latypov 			   kunit_resource_match_t match,
297*61695f8cSDaniel Latypov 			   void *match_data);
298*61695f8cSDaniel Latypov 
299*61695f8cSDaniel Latypov static inline int kunit_destroy_named_resource(struct kunit *test,
300*61695f8cSDaniel Latypov 					       const char *name)
301*61695f8cSDaniel Latypov {
302*61695f8cSDaniel Latypov 	return kunit_destroy_resource(test, kunit_resource_name_match,
303*61695f8cSDaniel Latypov 				      (void *)name);
304*61695f8cSDaniel Latypov }
305*61695f8cSDaniel Latypov 
306*61695f8cSDaniel Latypov /**
307*61695f8cSDaniel Latypov  * kunit_remove_resource() - remove resource from resource list associated with
308*61695f8cSDaniel Latypov  *			     test.
309*61695f8cSDaniel Latypov  * @test: The test context object.
310*61695f8cSDaniel Latypov  * @res: The resource to be removed.
311*61695f8cSDaniel Latypov  *
312*61695f8cSDaniel Latypov  * Note that the resource will not be immediately freed since it is likely
313*61695f8cSDaniel Latypov  * the caller has a reference to it via alloc_and_get() or find();
314*61695f8cSDaniel Latypov  * in this case a final call to kunit_put_resource() is required.
315*61695f8cSDaniel Latypov  */
316*61695f8cSDaniel Latypov void kunit_remove_resource(struct kunit *test, struct kunit_resource *res);
317*61695f8cSDaniel Latypov 
318*61695f8cSDaniel Latypov #endif /* _KUNIT_RESOURCE_H */
319