xref: /aosp_15_r20/external/libnl/lib/cache_mngt.c (revision 4dc78e53d49367fa8e61b07018507c90983a077d)
1 /* SPDX-License-Identifier: LGPL-2.1-only */
2 /*
3  * Copyright (c) 2003-2012 Thomas Graf <[email protected]>
4  */
5 
6 /**
7  * @ingroup core
8  * @defgroup cache_mngt Caching System
9  *
10  * Related sections in the development guide:
11  * - @core_doc{core_cache, Caching System}
12  *
13  * @{
14  *
15  * Header
16  * ------
17  * ~~~~{.c}
18  * #include <netlink/cache.h>
19  * ~~~~
20  */
21 
22 #include "nl-default.h"
23 
24 #include <netlink/netlink.h>
25 #include <netlink/cache.h>
26 #include <netlink/utils.h>
27 
28 #include "nl-priv-dynamic-core/nl-core.h"
29 #include "nl-priv-dynamic-core/object-api.h"
30 #include "nl-priv-dynamic-core/cache-api.h"
31 #include "nl-aux-core/nl-core.h"
32 
33 static struct nl_cache_ops *cache_ops;
34 static NL_RW_LOCK(cache_ops_lock);
35 
36 /**
37  * @name Cache Operations Sets
38  * @{
39  */
40 
__nl_cache_ops_lookup(const char * name)41 static struct nl_cache_ops *__nl_cache_ops_lookup(const char *name)
42 {
43 	struct nl_cache_ops *ops;
44 
45 	for (ops = cache_ops; ops; ops = ops->co_next)
46 		if (!strcmp(ops->co_name, name))
47 			return ops;
48 
49 	return NULL;
50 }
51 
52 /**
53  * Increment reference counter
54  * @arg ops		Cache operations
55  */
nl_cache_ops_get(struct nl_cache_ops * ops)56 void nl_cache_ops_get(struct nl_cache_ops *ops)
57 {
58 	ops->co_refcnt++;
59 }
60 
61 /**
62  * Decrement reference counter
63  * @arg ops		Cache operations
64  */
nl_cache_ops_put(struct nl_cache_ops * ops)65 void nl_cache_ops_put(struct nl_cache_ops *ops)
66 {
67 	ops->co_refcnt--;
68 }
69 
70 /**
71  * Lookup cache operations by name
72  * @arg name		name of the cache type
73  *
74  * @attention This function is not safe, it does not increment the reference
75  *            counter. Please use nl_cache_ops_lookup_safe().
76  *
77  * @return The cache operations or NULL if not found.
78  */
nl_cache_ops_lookup(const char * name)79 struct nl_cache_ops *nl_cache_ops_lookup(const char *name)
80 {
81 	struct nl_cache_ops *ops;
82 
83 	nl_read_lock(&cache_ops_lock);
84 	ops = __nl_cache_ops_lookup(name);
85 	nl_read_unlock(&cache_ops_lock);
86 
87 	return ops;
88 }
89 
90 /**
91  * Lookup cache operations by name
92  * @arg name		name of the cache type
93  *
94  * @note The reference counter of the returned cache operation is incremented
95  *       and must be decremented after use with nl_cache_ops_put().
96  *
97  * @return The cache operations or NULL if not found.
98  */
nl_cache_ops_lookup_safe(const char * name)99 struct nl_cache_ops *nl_cache_ops_lookup_safe(const char *name)
100 {
101 	struct nl_cache_ops *ops;
102 
103 	nl_write_lock(&cache_ops_lock);
104 	if ((ops = __nl_cache_ops_lookup(name)))
105 		nl_cache_ops_get(ops);
106 	nl_write_unlock(&cache_ops_lock);
107 
108 	return ops;
109 }
110 
__cache_ops_associate(int protocol,int msgtype)111 static struct nl_cache_ops *__cache_ops_associate(int protocol, int msgtype)
112 {
113 	int i;
114 	struct nl_cache_ops *ops;
115 
116 	for (ops = cache_ops; ops; ops = ops->co_next) {
117 		if (ops->co_protocol != protocol)
118 			continue;
119 
120 		for (i = 0; ops->co_msgtypes[i].mt_id >= 0; i++)
121 			if (ops->co_msgtypes[i].mt_id == msgtype)
122 				return ops;
123 	}
124 
125 	return NULL;
126 }
127 
128 /**
129  * Associate protocol and message type to cache operations
130  * @arg protocol		netlink protocol
131  * @arg msgtype			netlink message type
132  *
133  * @attention This function is not safe, it does not increment the reference
134  *            counter. Please use nl_cache_ops_associate_safe().
135  *
136  * @see nl_cache_ops_associate_safe()
137  *
138  * @return The cache operations or NULL if no match found.
139  */
nl_cache_ops_associate(int protocol,int msgtype)140 struct nl_cache_ops *nl_cache_ops_associate(int protocol, int msgtype)
141 {
142 	struct nl_cache_ops *ops;
143 
144 	nl_read_lock(&cache_ops_lock);
145 	ops = __cache_ops_associate(protocol, msgtype);
146 	nl_read_unlock(&cache_ops_lock);
147 
148 	return ops;
149 }
150 
151 /**
152  * Associate protocol and message type to cache operations
153  * @arg protocol		netlink protocol
154  * @arg msgtype			netlink message type
155  *
156  * Searches the registered cache operations for a matching protocol
157  * and message type.
158  *
159  * @note The reference counter of the returned cache operation is incremented
160  *       and must be decremented after use with nl_cache_ops_put().
161  *
162  * @return The cache operations or NULL if no no match was found.
163  */
nl_cache_ops_associate_safe(int protocol,int msgtype)164 struct nl_cache_ops *nl_cache_ops_associate_safe(int protocol, int msgtype)
165 {
166 	struct nl_cache_ops *ops;
167 
168 	nl_write_lock(&cache_ops_lock);
169 	if ((ops = __cache_ops_associate(protocol, msgtype)))
170 		nl_cache_ops_get(ops);
171 	nl_write_unlock(&cache_ops_lock);
172 
173 	return ops;
174 }
175 
176 /**
177  * Lookup message type cache association
178  * @arg ops			cache operations
179  * @arg msgtype			netlink message type
180  *
181  * Searches for a matching message type association ing the specified
182  * cache operations.
183  *
184  * @attention The guranteed lifetime of the returned message type is bound
185  *            to the lifetime of the underlying cache operations.
186  *
187  * @return A message type association or NULL.
188  */
nl_msgtype_lookup(struct nl_cache_ops * ops,int msgtype)189 struct nl_msgtype *nl_msgtype_lookup(struct nl_cache_ops *ops, int msgtype)
190 {
191 	int i;
192 
193 	for (i = 0; ops->co_msgtypes[i].mt_id >= 0; i++)
194 		if (ops->co_msgtypes[i].mt_id == msgtype)
195 			return &ops->co_msgtypes[i];
196 
197 	return NULL;
198 }
199 
200 /* Must hold cache_ops_lock */
cache_ops_lookup_for_obj(struct nl_object_ops * obj_ops)201 static struct nl_cache_ops *cache_ops_lookup_for_obj(struct nl_object_ops *obj_ops)
202 {
203 	struct nl_cache_ops *ops;
204 
205 	for (ops = cache_ops; ops; ops = ops->co_next)
206 		if (ops->co_obj_ops == obj_ops)
207 			return ops;
208 
209 	return NULL;
210 
211 }
212 
213 /**
214  * Call a function for each registered cache operation
215  * @arg cb		Callback function to be called
216  * @arg arg		User specific argument.
217  */
nl_cache_ops_foreach(void (* cb)(struct nl_cache_ops *,void *),void * arg)218 void nl_cache_ops_foreach(void (*cb)(struct nl_cache_ops *, void *), void *arg)
219 {
220 	struct nl_cache_ops *ops;
221 
222 	nl_read_lock(&cache_ops_lock);
223 	for (ops = cache_ops; ops; ops = ops->co_next)
224 		cb(ops, arg);
225 	nl_read_unlock(&cache_ops_lock);
226 }
227 
228 /**
229  * Set default flags for caches of this type
230  * @arg ops		Cache ops
231  * @arg flags		Flags to set
232  *
233  * The cache operation flags will be derived to all caches allocates
234  * based on this set of cache operations.
235  */
nl_cache_ops_set_flags(struct nl_cache_ops * ops,unsigned int flags)236 void nl_cache_ops_set_flags(struct nl_cache_ops *ops, unsigned int flags)
237 {
238 	nl_write_lock(&cache_ops_lock);
239 	ops->co_flags |= flags;
240 	nl_write_unlock(&cache_ops_lock);
241 }
242 
243 /**
244  * Register a set of cache operations
245  * @arg ops		cache operations
246  *
247  * Called by users of caches to announce the avaibility of
248  * a certain cache type.
249  *
250  * @return 0 on success or a negative error code.
251  */
nl_cache_mngt_register(struct nl_cache_ops * ops)252 int nl_cache_mngt_register(struct nl_cache_ops *ops)
253 {
254 	if (!ops->co_name || !ops->co_obj_ops)
255 		return -NLE_INVAL;
256 
257 	/* oo_keygen() also needs oo_compare() */
258 	BUG_ON (ops->co_obj_ops->oo_keygen && !ops->co_obj_ops->oo_compare);
259 
260 	nl_write_lock(&cache_ops_lock);
261 	if (__nl_cache_ops_lookup(ops->co_name)) {
262 		nl_write_unlock(&cache_ops_lock);
263 		return -NLE_EXIST;
264 	}
265 
266 	ops->co_refcnt = 0;
267 	ops->co_next = cache_ops;
268 	cache_ops = ops;
269 	nl_write_unlock(&cache_ops_lock);
270 
271 	NL_DBG(1, "Registered cache operations %s\n", ops->co_name);
272 
273 	return 0;
274 }
275 
276 /**
277  * Unregister a set of cache operations
278  * @arg ops		cache operations
279  *
280  * Called by users of caches to announce a set of
281  * cache operations is no longer available. The
282  * specified cache operations must have been registered
283  * previously using nl_cache_mngt_register()
284  *
285  * @return 0 on success or a negative error code
286  */
nl_cache_mngt_unregister(struct nl_cache_ops * ops)287 int nl_cache_mngt_unregister(struct nl_cache_ops *ops)
288 {
289 	struct nl_cache_ops *t, **tp;
290 	int err = 0;
291 
292 	nl_write_lock(&cache_ops_lock);
293 
294 	if (ops->co_refcnt > 0) {
295 		err = -NLE_BUSY;
296 		goto errout;
297 	}
298 
299 	for (tp = &cache_ops; (t=*tp) != NULL; tp = &t->co_next)
300 		if (t == ops)
301 			break;
302 
303 	if (!t) {
304 		err = -NLE_NOCACHE;
305 		goto errout;
306 	}
307 
308 	NL_DBG(1, "Unregistered cache operations %s\n", ops->co_name);
309 
310 	*tp = t->co_next;
311 errout:
312 	nl_write_unlock(&cache_ops_lock);
313 
314 	return err;
315 }
316 
317 /** @} */
318 
319 /**
320  * @name Global Cache Provisioning/Requiring
321  * @{
322  */
323 
324 /**
325  * Provide a cache for global use
326  * @arg cache		cache to provide
327  *
328  * Offers the specified cache to be used by other modules.
329  * Only one cache per type may be shared at a time,
330  * a previsouly provided caches will be overwritten.
331  */
nl_cache_mngt_provide(struct nl_cache * cache)332 void nl_cache_mngt_provide(struct nl_cache *cache)
333 {
334 	struct nl_cache_ops *ops;
335 
336 	nl_write_lock(&cache_ops_lock);
337 
338 	ops = cache_ops_lookup_for_obj(cache->c_ops->co_obj_ops);
339 	if (!ops)
340 		BUG();
341 	else {
342 		nl_cache_get(cache);
343 
344 		/*
345 		 * Hold a reference to the cache operations to ensure the
346 		 * ops don't go away while we use it to store the cache pointer.
347 		 */
348 		if (!ops->co_major_cache)
349 			nl_cache_ops_get(ops);
350 
351 		ops->co_major_cache = cache;
352 	}
353 
354 	nl_write_unlock(&cache_ops_lock);
355 }
356 
357 /**
358  * Unprovide a cache for global use
359  * @arg cache		cache to unprovide
360  *
361  * Cancels the offer to use a cache globally. The
362  * cache will no longer be returned via lookups but
363  * may still be in use.
364  */
nl_cache_mngt_unprovide(struct nl_cache * cache)365 void nl_cache_mngt_unprovide(struct nl_cache *cache)
366 {
367 	struct nl_cache_ops *ops;
368 
369 	nl_write_lock(&cache_ops_lock);
370 
371 	ops = cache_ops_lookup_for_obj(cache->c_ops->co_obj_ops);
372 	if (!ops)
373 		BUG();
374 	else if (ops->co_major_cache == cache) {
375 		nl_cache_free(ops->co_major_cache);
376 		nl_cache_ops_put(ops);
377 		ops->co_major_cache = NULL;
378 	}
379 
380 	nl_write_unlock(&cache_ops_lock);
381 }
382 
__nl_cache_mngt_require(const char * name)383 struct nl_cache *__nl_cache_mngt_require(const char *name)
384 {
385 	struct nl_cache_ops *ops;
386 	struct nl_cache *cache = NULL;
387 
388 	ops = nl_cache_ops_lookup_safe(name);
389 	if (ops) {
390 		cache = ops->co_major_cache;
391 		nl_cache_ops_put(ops);
392 	}
393 
394 	return cache;
395 }
396 
397 /**
398  * Return cache previously provided via nl_cache_mngt_provide()
399  * @arg name		Name of cache to lookup
400  *
401  * @attention This function is not safe, it does not increment the reference
402  *            counter. Please use nl_cache_mngt_require_safe().
403  *
404  * @see nl_cache_mngt_require_safe()
405  *
406  * @return Pointer to cache or NULL if none registered
407  */
nl_cache_mngt_require(const char * name)408 struct nl_cache *nl_cache_mngt_require(const char *name)
409 {
410 	struct nl_cache *cache;
411 
412 	if (!(cache = __nl_cache_mngt_require(name)))
413 		NL_DBG(1, "Application BUG: Your application must "
414 		       "call nl_cache_mngt_provide() and\nprovide a valid "
415 		       "%s cache to be used for internal lookups.\nSee the "
416 		       " API documentation for more details.\n", name);
417 
418 	return cache;
419 }
420 
421 /**
422  * Return cache previously provided via nl_cache_mngt_provide()
423  * @arg name		Name of cache to lookup
424  *
425  * @note The reference counter of the returned cache is incremented
426  *       and must be decremented after use with nl_cache_put().
427  *
428  * @return Pointer to cache or NULL if none registered
429  */
nl_cache_mngt_require_safe(const char * name)430 struct nl_cache *nl_cache_mngt_require_safe(const char *name)
431 {
432 	struct nl_cache *cache;
433 
434 	if ((cache = nl_cache_mngt_require(name)))
435 		nl_cache_get(cache);
436 
437 	return cache;
438 }
439 
440 /** @} */
441 
442 /** @} */
443