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