Add sw_interface_clear_stats API call
[vpp.git] / svm / svmdb.c
1 /* 
2  *------------------------------------------------------------------
3  * svmdb.c -- simple shared memory database
4  *
5  * Copyright (c) 2009 Cisco and/or its affiliates.
6  * Licensed under the Apache License, Version 2.0 (the "License");
7  * you may not use this file except in compliance with the License.
8  * You may obtain a copy of the License at:
9  *
10  *     http://www.apache.org/licenses/LICENSE-2.0
11  *
12  * Unless required by applicable law or agreed to in writing, software
13  * distributed under the License is distributed on an "AS IS" BASIS,
14  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
15  * See the License for the specific language governing permissions and
16  * limitations under the License.
17  *------------------------------------------------------------------
18  */
19
20 #include <stdio.h>
21 #include <stdlib.h>
22 #include <sys/types.h>
23 #include <sys/mman.h>
24 #include <sys/stat.h>
25 #include <netinet/in.h>
26 #include <signal.h>
27 #include <pthread.h>
28 #include <unistd.h>
29 #include <time.h>
30 #include <fcntl.h>
31 #include <string.h>
32 #include <vppinfra/clib.h>
33 #include <vppinfra/vec.h>
34 #include <vppinfra/hash.h>
35 #include <vppinfra/bitmap.h>
36 #include <vppinfra/fifo.h>
37 #include <vppinfra/time.h>
38 #include <vppinfra/mheap.h>
39 #include <vppinfra/heap.h>
40 #include <vppinfra/pool.h>
41 #include <vppinfra/format.h>
42
43 #include "svmdb.h"
44
45 static void local_set_variable_nolock (svmdb_client_t *client,
46                                        svmdb_namespace_t namespace,
47                                        u8 * var, u8 * val, u32 elsize);
48
49 always_inline void region_lock(svm_region_t *rp, int tag)
50 {
51     pthread_mutex_lock(&rp->mutex);
52 #ifdef MUTEX_DEBUG
53     rp->mutex_owner_pid = getpid();
54     rp->mutex_owner_tag = tag;
55 #endif    
56 }
57
58 always_inline void region_unlock(svm_region_t *rp)
59 {
60 #ifdef MUTEX_DEBUG
61     rp->mutex_owner_pid = 0;
62     rp->mutex_owner_tag = 0;
63 #endif    
64     pthread_mutex_unlock(&rp->mutex);
65 }
66
67 static svmdb_client_t *svmdb_map_internal (char *root_path, uword size)
68 {
69     svmdb_client_t *client = 0;
70     svm_map_region_args_t *a = 0;
71     svm_region_t *db_rp;
72     void *oldheap;
73     svmdb_shm_hdr_t *hp = 0;
74
75     vec_validate (client, 0);
76     vec_validate (a, 0);
77
78     svm_region_init_chroot(root_path);
79
80     a->root_path = root_path;
81     a->name = "/db";
82     a->size = size ? size : SVMDB_DEFAULT_SIZE;
83     a->flags = SVM_FLAGS_MHEAP;
84
85     db_rp = client->db_rp = svm_region_find_or_create (a);
86
87     ASSERT(db_rp);
88     
89     vec_free (a);
90
91     region_lock (client->db_rp, 10);
92     /* Has someone else set up the shared-memory variable table? */
93     if (db_rp->user_ctx) {
94         client->shm = (void *) db_rp->user_ctx;
95         client->pid = getpid();
96         region_unlock (client->db_rp);
97         ASSERT (client->shm->version == SVMDB_SHM_VERSION);
98         return (client);
99     }
100     /* Nope, it's our problem... */
101
102     /* Add a bogus client (pid=0) so the svm won't be deallocated */
103     oldheap = svm_push_pvt_heap (db_rp);
104     vec_add1(client->db_rp->client_pids, 0);
105     svm_pop_heap (oldheap);
106
107     oldheap = svm_push_data_heap (db_rp);
108
109     vec_validate(hp, 0);
110     hp->version = SVMDB_SHM_VERSION;
111     hp->namespaces[SVMDB_NAMESPACE_STRING] 
112         = hash_create_string(0, sizeof(uword));
113     hp->namespaces[SVMDB_NAMESPACE_VEC] 
114         = hash_create_string(0, sizeof(uword));
115
116     db_rp->user_ctx = hp;
117     client->shm = hp;
118
119     svm_pop_heap (oldheap);
120     region_unlock (client->db_rp);
121     client->pid = getpid();
122     
123     return (client);
124 }
125 svmdb_client_t *svmdb_map (void)
126 {
127     return svmdb_map_internal (0, 0);
128 }
129
130 svmdb_client_t *svmdb_map_size (uword size)
131 {
132     return svmdb_map_internal (0, size);
133 }
134
135 svmdb_client_t *svmdb_map_chroot (char *root_path)
136 {
137     return svmdb_map_internal (root_path, 0);
138 }
139
140 svmdb_client_t *svmdb_map_chroot_size (char *root_path, uword size)
141 {
142     return svmdb_map_internal (root_path, size);
143 }
144
145 void svmdb_unmap (svmdb_client_t *client)
146 {
147     ASSERT(client);
148
149     if (! svm_get_root_rp())
150         return;
151
152     svm_region_unmap ((void *) client->db_rp);
153     svm_region_exit ();
154     vec_free(client);
155 }
156
157 static void notify_value (svmdb_value_t * v, svmdb_action_t a)
158 {
159     int i;
160     int rv;
161     union sigval sv;
162     u32 value;
163     u32 *dead_registrations = 0;
164
165     svmdb_notify_t *np;
166
167     for (i = 0; i < vec_len (v->notifications); i++) {
168         np = vec_elt_at_index (v->notifications, i);
169         if (np->action == a) {
170             value = (np->action<<28) | (np->opaque);
171             sv.sival_ptr = (void *)(uword)value;
172             do {
173                 rv = 0;
174                 if (sigqueue (np->pid, np->signum, sv) == 0)
175                     break;
176                 rv = errno;
177             } while (rv == EAGAIN);
178             if (rv == 0)
179                 continue;
180             vec_add1 (dead_registrations, i);
181         }
182     }
183
184     for (i = 0; i < vec_len (dead_registrations); i++) {
185         np = vec_elt_at_index (v->notifications, dead_registrations[i]);
186         clib_warning ("dead reg pid %d sig %d action %d opaque %x",
187                       np->pid, np->signum, np->action, np->opaque);
188         vec_delete (v->notifications, 1, dead_registrations[i]);
189     }
190     vec_free (dead_registrations);
191 }
192
193 int svmdb_local_add_del_notification (svmdb_client_t *client, 
194                                       svmdb_notification_args_t *a)
195 {
196     uword *h;
197     void *oldheap;
198     hash_pair_t *hp;
199     svmdb_shm_hdr_t * shm;
200     u8 *dummy_value = 0;
201     svmdb_value_t *value;
202     svmdb_notify_t *np;
203     int i;
204     int rv = 0;
205
206     ASSERT (a->elsize);
207
208     region_lock (client->db_rp, 18);
209     shm = client->shm;
210     oldheap = svm_push_data_heap (client->db_rp);
211
212     h = shm->namespaces[a->nspace];
213
214     hp = hash_get_pair_mem (h, a->var);
215     if (hp == 0) {
216         local_set_variable_nolock (client, a->nspace, (u8 *)a->var,
217                                    dummy_value, a->elsize);
218         /* might have moved */
219         h = shm->namespaces[a->nspace];
220         hp = hash_get_pair_mem (h, a->var);
221         ASSERT(hp);
222     }
223     
224     value = pool_elt_at_index (shm->values, hp->value[0]);
225     
226     for (i = 0; i < vec_len (value->notifications); i++) {
227         np = vec_elt_at_index (value->notifications, i);
228         if ((np->pid == client->pid)
229             && (np->signum == a->signum)
230             && (np->action == a->action)
231             && (np->opaque == a->opaque)) {
232             if (a->add_del == 0 /* delete */) {
233                 vec_delete (value->notifications, 1, i);
234                 goto out;
235             } else { /* add */
236                 clib_warning (
237                     "%s: ignore dup reg pid %d signum %d action %d opaque %x",
238                     a->var, client->pid, a->signum, a->action, a->opaque);
239                 rv = -2;
240                 goto out;
241             }
242         }
243     }
244     if (a->add_del == 0) {
245         rv = -3;
246         goto out;
247     }
248     
249     vec_add2 (value->notifications, np, 1);
250     np->pid = client->pid;
251     np->signum = a->signum;
252     np->action = a->action;
253     np->opaque = a->opaque;
254
255 out:
256     svm_pop_heap(oldheap);
257     region_unlock (client->db_rp);    
258     return rv;
259 }
260
261
262 static void local_unset_variable_nolock (svmdb_client_t *client, 
263                                          svmdb_namespace_t namespace,
264                                          char * var)
265 {
266     uword *h;
267     svmdb_value_t *oldvalue;
268     hash_pair_t *hp;
269
270     h = client->shm->namespaces[namespace];
271     hp = hash_get_pair_mem (h, var);
272     if (hp) {
273         oldvalue = pool_elt_at_index (client->shm->values, hp->value[0]);
274         if (vec_len (oldvalue->notifications)) 
275             notify_value (oldvalue, SVMDB_ACTION_UNSET);
276         /* zero length value means unset */
277         _vec_len (oldvalue->value) = 0;
278     }
279     client->shm->namespaces[namespace] = h;
280 }
281
282 void svmdb_local_unset_string_variable (svmdb_client_t *client, char *var)
283 {
284     void *oldheap;
285
286     region_lock (client->db_rp, 11);
287     oldheap = svm_push_data_heap (client->db_rp);
288     local_unset_variable_nolock (client, SVMDB_NAMESPACE_STRING, var);
289     svm_pop_heap(oldheap);
290     region_unlock (client->db_rp);
291 }
292
293 static void local_set_variable_nolock (svmdb_client_t *client,
294                                        svmdb_namespace_t namespace,
295                                        u8 * var, u8 * val, u32 elsize)
296 {
297     uword *h;
298     hash_pair_t *hp;
299     u8 *name;
300     svmdb_shm_hdr_t * shm;
301
302     shm = client->shm;
303     h = shm->namespaces[namespace];
304     hp = hash_get_pair_mem (h, var);
305     if (hp) {
306         svmdb_value_t * oldvalue;
307         oldvalue = pool_elt_at_index (client->shm->values, hp->value[0]);
308         vec_alloc (oldvalue->value, vec_len(val)*elsize);
309         memcpy (oldvalue->value, val, vec_len(val)*elsize);
310         _vec_len (oldvalue->value) = vec_len(val);
311         notify_value (oldvalue, SVMDB_ACTION_SET);
312     } else {
313         svmdb_value_t * newvalue;
314         pool_get (shm->values, newvalue);
315         memset (newvalue, 0, sizeof (*newvalue));
316         newvalue->elsize = elsize;
317         vec_alloc (newvalue->value, vec_len(val)*elsize);
318         memcpy (newvalue->value, val, vec_len(val)*elsize);
319         _vec_len (newvalue->value) = vec_len(val);
320         name = format (0, "%s%c", var, 0);
321         hash_set_mem (h, name, newvalue - shm->values);
322     }
323     shm->namespaces[namespace] = h;
324 }
325
326 void svmdb_local_set_string_variable (svmdb_client_t *client, 
327                                       char *var, char *val)
328 {
329     void *oldheap;
330
331     region_lock (client->db_rp, 12);
332     oldheap = svm_push_data_heap (client->db_rp);
333
334     local_unset_variable_nolock (client, SVMDB_NAMESPACE_STRING, var);
335
336     local_set_variable_nolock (client, SVMDB_NAMESPACE_STRING, 
337                                (u8 *) var, (u8 *) val, 1 /* elsize */);
338     svm_pop_heap(oldheap);
339     region_unlock (client->db_rp);
340 }
341
342 static u8 * local_get_variable_nolock (svmdb_client_t *client,
343                                        svmdb_namespace_t namespace,
344                                        u8 * var)
345 {
346     uword *h;
347     uword *p;
348     svmdb_shm_hdr_t * shm;
349     svmdb_value_t *oldvalue;
350     
351     shm = client->shm;
352     h = shm->namespaces[namespace];
353     p = hash_get_mem (h, var);
354     if (p) {
355         oldvalue = pool_elt_at_index (shm->values, p[0]);
356         notify_value (oldvalue, SVMDB_ACTION_GET);
357         return (oldvalue->value);
358     }
359     return 0;
360 }
361
362 void *svmdb_local_get_variable_reference (svmdb_client_t *client,
363                                           svmdb_namespace_t namespace,
364                                           char *var)
365 {
366     u8 *rv;
367
368     region_lock (client->db_rp, 19);
369     rv = local_get_variable_nolock (client, namespace, (u8 *)var);
370     region_unlock (client->db_rp);
371     return (void *)rv;
372 }
373
374 char *svmdb_local_get_string_variable (svmdb_client_t *client, char *var)
375 {
376     u8 *rv = 0;
377
378     region_lock (client->db_rp, 13);
379     rv = local_get_variable_nolock (client, SVMDB_NAMESPACE_STRING, (u8 *) var);
380
381     if (rv && vec_len (rv)) {
382         rv = format (0, "%s", rv);
383         vec_add1(rv, 0);
384     }
385     region_unlock (client->db_rp);
386     return ((char *) rv);
387 }
388
389 void svmdb_local_dump_strings (svmdb_client_t *client)
390 {
391     uword *h;
392     u8 *key;
393     u32 value;
394     svmdb_shm_hdr_t *shm = client->shm;
395
396     region_lock (client->db_rp, 14);
397
398     h = client->shm->namespaces [SVMDB_NAMESPACE_STRING];
399     
400     hash_foreach_mem(key, value, h,
401     ({
402         svmdb_value_t *v = pool_elt_at_index (shm->values, value);
403         
404         fformat(stdout, "%s: %s\n", key, 
405                 vec_len(v->value) ? v->value : (u8 *)"(nil)");
406     }));
407     region_unlock (client->db_rp);
408 }
409
410 void svmdb_local_unset_vec_variable (svmdb_client_t *client, char *var)
411 {
412     void *oldheap;
413
414     region_lock (client->db_rp, 15);
415     oldheap = svm_push_data_heap (client->db_rp);
416     local_unset_variable_nolock (client, SVMDB_NAMESPACE_VEC, var);
417     svm_pop_heap(oldheap);
418     region_unlock (client->db_rp);
419 }
420
421 void svmdb_local_set_vec_variable (svmdb_client_t *client, 
422                                    char *var, void *val_arg, u32 elsize)
423 {
424     u8 *val = (u8 *)val_arg;
425     void *oldheap;
426
427     region_lock (client->db_rp, 16);
428     oldheap = svm_push_data_heap (client->db_rp);
429
430     local_unset_variable_nolock (client, SVMDB_NAMESPACE_VEC, var);
431     local_set_variable_nolock (client, SVMDB_NAMESPACE_VEC, (u8 *) var, 
432                                val, elsize);
433
434     svm_pop_heap(oldheap);
435     region_unlock (client->db_rp);
436 }
437
438 void *svmdb_local_get_vec_variable (svmdb_client_t *client, char *var, 
439                                     u32 elsize)
440 {
441     u8 *rv = 0;
442     u8 *copy = 0;
443
444     region_lock (client->db_rp, 17);
445
446     rv = local_get_variable_nolock (client, SVMDB_NAMESPACE_VEC, (u8 *) var);
447
448     if (rv && vec_len(rv)) {
449         /* Make a copy in process-local memory */
450         vec_alloc (copy, vec_len(rv)*elsize);
451         memcpy (copy, rv, vec_len(rv)*elsize);
452         _vec_len(copy) = vec_len(rv);
453         region_unlock (client->db_rp);
454         return (copy);
455     }
456     region_unlock (client->db_rp);
457     return (0);
458 }
459
460 void svmdb_local_dump_vecs (svmdb_client_t *client)
461 {
462     uword *h;
463     u8 *key;
464     u32 value;
465     svmdb_shm_hdr_t *shm;
466
467     region_lock (client->db_rp, 17);
468     shm = client->shm;
469
470     h = client->shm->namespaces [SVMDB_NAMESPACE_VEC];
471
472     hash_foreach_mem(key, value, h, 
473     ({
474         svmdb_value_t *v = pool_elt_at_index (shm->values, value);
475         (void) fformat(stdout, "%s:\n %U\n", key, 
476                        format_hex_bytes, v->value, 
477                        vec_len(v->value)*v->elsize);
478     }));
479
480     region_unlock (client->db_rp);
481 }
482
483 void *svmdb_local_find_or_add_vec_variable (svmdb_client_t *client, 
484                                             char *var, u32 nbytes)
485 {
486     void *oldheap;
487     u8 *rv = 0;
488
489     region_lock (client->db_rp, 18);
490     oldheap = svm_push_data_heap (client->db_rp);
491
492     rv = local_get_variable_nolock (client, SVMDB_NAMESPACE_VEC, (u8 *)var);
493
494     if (rv)  {
495         goto out;
496     } else  {
497         uword *h;
498         u8 *name;
499         svmdb_shm_hdr_t * shm;
500         svmdb_value_t * newvalue;
501         
502         shm = client->shm;
503         h = shm->namespaces[SVMDB_NAMESPACE_VEC];
504
505         pool_get (shm->values, newvalue);
506         memset (newvalue, 0, sizeof (*newvalue));
507         newvalue->elsize = 1;
508         vec_alloc (newvalue->value, nbytes);
509         _vec_len (newvalue->value) = nbytes;
510         name = format (0, "%s%c", var, 0);
511         hash_set_mem (h, name, newvalue - shm->values);
512         shm->namespaces[SVMDB_NAMESPACE_VEC] = h;
513         rv = newvalue->value;
514     }
515
516 out:
517     svm_pop_heap(oldheap);
518     region_unlock (client->db_rp);
519     return (rv);
520 }