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