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