Line data Source code
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, ¶ms);
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 280 : 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 271 : static inline void kunit_release_resource(struct kref *kref)
117 : {
118 271 : struct kunit_resource *res = container_of(kref, struct kunit_resource,
119 : refcount);
120 :
121 : /* If free function is defined, resource was dynamically allocated. */
122 271 : if (res->free) {
123 271 : res->free(res);
124 271 : kfree(res);
125 : }
126 271 : }
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 411 : 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 271 : 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 271 : res = kzalloc(sizeof(*res), internal_gfp);
202 271 : if (!res)
203 : return NULL;
204 :
205 271 : if (!kunit_add_resource(test, init, free, res, context))
206 271 : 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 800 : static inline bool kunit_resource_instance_match(struct kunit *test,
225 : struct kunit_resource *res,
226 : void *match_data)
227 : {
228 800 : 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 0 : static inline bool kunit_resource_name_match(struct kunit *test,
238 : struct kunit_resource *res,
239 : void *match_name)
240 : {
241 0 : 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 140 : kunit_find_resource(struct kunit *test,
252 : kunit_resource_match_t match,
253 : void *match_data)
254 : {
255 140 : struct kunit_resource *res, *found = NULL;
256 : unsigned long flags;
257 :
258 140 : spin_lock_irqsave(&test->lock, flags);
259 :
260 800 : list_for_each_entry_reverse(res, &test->resources, node) {
261 800 : if (match(test, res, (void *)match_data)) {
262 140 : found = res;
263 : kunit_get_resource(found);
264 : break;
265 : }
266 : }
267 :
268 280 : spin_unlock_irqrestore(&test->lock, flags);
269 :
270 140 : 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 0 : 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 */
|