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