1 /*
2 * lib/cache_mngt.c Cache Management
3 *
4 * This library is free software; you can redistribute it and/or
5 * modify it under the terms of the GNU Lesser General Public
6 * License as published by the Free Software Foundation version 2.1
7 * of the License.
8 *
9 * Copyright (c) 2003-2012 Thomas Graf <tgraf@suug.ch>
10 */
11
12 /**
13 * @ingroup core
14 * @defgroup cache_mngt Caching System
15 *
16 * Related sections in the development guide:
17 * - @core_doc{core_cache, Caching System}
18 *
19 * @{
20 *
21 * Header
22 * ------
23 * ~~~~{.c}
24 * #include <netlink/cache.h>
25 * ~~~~
26 */
27
28 #include <netlink-private/netlink.h>
29 #include <netlink/netlink.h>
30 #include <netlink/cache.h>
31 #include <netlink/utils.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 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 nl_write_lock(&cache_ops_lock);
258 if (__nl_cache_ops_lookup(ops->co_name)) {
259 nl_write_unlock(&cache_ops_lock);
260 return -NLE_EXIST;
261 }
262
263 ops->co_refcnt = 0;
264 ops->co_next = cache_ops;
265 cache_ops = ops;
266 nl_write_unlock(&cache_ops_lock);
267
268 NL_DBG(1, "Registered cache operations %s\n", ops->co_name);
269
270 return 0;
271 }
272
273 /**
274 * Unregister a set of cache operations
275 * @arg ops cache operations
276 *
277 * Called by users of caches to announce a set of
278 * cache operations is no longer available. The
279 * specified cache operations must have been registered
280 * previously using nl_cache_mngt_register()
281 *
282 * @return 0 on success or a negative error code
283 */
nl_cache_mngt_unregister(struct nl_cache_ops * ops)284 int nl_cache_mngt_unregister(struct nl_cache_ops *ops)
285 {
286 struct nl_cache_ops *t, **tp;
287 int err = 0;
288
289 nl_write_lock(&cache_ops_lock);
290
291 if (ops->co_refcnt > 0) {
292 err = -NLE_BUSY;
293 goto errout;
294 }
295
296 for (tp = &cache_ops; (t=*tp) != NULL; tp = &t->co_next)
297 if (t == ops)
298 break;
299
300 if (!t) {
301 err = -NLE_NOCACHE;
302 goto errout;
303 }
304
305 NL_DBG(1, "Unregistered cache operations %s\n", ops->co_name);
306
307 *tp = t->co_next;
308 errout:
309 nl_write_unlock(&cache_ops_lock);
310
311 return err;
312 }
313
314 /** @} */
315
316 /**
317 * @name Global Cache Provisioning/Requiring
318 * @{
319 */
320
321 /**
322 * Provide a cache for global use
323 * @arg cache cache to provide
324 *
325 * Offers the specified cache to be used by other modules.
326 * Only one cache per type may be shared at a time,
327 * a previsouly provided caches will be overwritten.
328 */
nl_cache_mngt_provide(struct nl_cache * cache)329 void nl_cache_mngt_provide(struct nl_cache *cache)
330 {
331 struct nl_cache_ops *ops;
332
333 nl_write_lock(&cache_ops_lock);
334
335 ops = cache_ops_lookup_for_obj(cache->c_ops->co_obj_ops);
336 if (!ops)
337 BUG();
338 else {
339 nl_cache_get(cache);
340
341 /*
342 * Hold a reference to the cache operations to ensure the
343 * ops don't go away while we use it to store the cache pointer.
344 */
345 if (!ops->co_major_cache)
346 nl_cache_ops_get(ops);
347
348 ops->co_major_cache = cache;
349 }
350
351 nl_write_unlock(&cache_ops_lock);
352 }
353
354 /**
355 * Unprovide a cache for global use
356 * @arg cache cache to unprovide
357 *
358 * Cancels the offer to use a cache globally. The
359 * cache will no longer be returned via lookups but
360 * may still be in use.
361 */
nl_cache_mngt_unprovide(struct nl_cache * cache)362 void nl_cache_mngt_unprovide(struct nl_cache *cache)
363 {
364 struct nl_cache_ops *ops;
365
366 nl_write_lock(&cache_ops_lock);
367
368 ops = cache_ops_lookup_for_obj(cache->c_ops->co_obj_ops);
369 if (!ops)
370 BUG();
371 else if (ops->co_major_cache == cache) {
372 nl_cache_free(ops->co_major_cache);
373 nl_cache_ops_put(ops);
374 ops->co_major_cache = NULL;
375 }
376
377 nl_write_unlock(&cache_ops_lock);
378 }
379
__nl_cache_mngt_require(const char * name)380 struct nl_cache *__nl_cache_mngt_require(const char *name)
381 {
382 struct nl_cache_ops *ops;
383 struct nl_cache *cache = NULL;
384
385 ops = nl_cache_ops_lookup_safe(name);
386 if (ops) {
387 cache = ops->co_major_cache;
388 nl_cache_ops_put(ops);
389 }
390
391 return cache;
392 }
393
394 /**
395 * Return cache previously provided via nl_cache_mngt_provide()
396 * @arg name Name of cache to lookup
397 *
398 * @attention This function is not safe, it does not increment the reference
399 * counter. Please use nl_cache_mngt_require_safe().
400 *
401 * @see nl_cache_mngt_require_safe()
402 *
403 * @return Pointer to cache or NULL if none registered
404 */
nl_cache_mngt_require(const char * name)405 struct nl_cache *nl_cache_mngt_require(const char *name)
406 {
407 struct nl_cache *cache;
408
409 if (!(cache = __nl_cache_mngt_require(name)))
410 NL_DBG(1, "Application BUG: Your application must "
411 "call nl_cache_mngt_provide() and\nprovide a valid "
412 "%s cache to be used for internal lookups.\nSee the "
413 " API documentation for more details.\n", name);
414
415 return cache;
416 }
417
418 /**
419 * Return cache previously provided via nl_cache_mngt_provide()
420 * @arg name Name of cache to lookup
421 *
422 * @note The reference counter of the returned cache is incremented
423 * and must be decremented after use with nl_cache_put().
424 *
425 * @return Pointer to cache or NULL if none registered
426 */
nl_cache_mngt_require_safe(const char * name)427 struct nl_cache *nl_cache_mngt_require_safe(const char *name)
428 {
429 struct nl_cache *cache;
430
431 if ((cache = nl_cache_mngt_require(name)))
432 nl_cache_get(cache);
433
434 return cache;
435 }
436
437 /** @} */
438
439 /** @} */
440