b22d2fab1bb8d888f19ea9a3870450c4dbce9b63
[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
50 region_lock (svm_region_t * rp, int tag)
51 {
52   pthread_mutex_lock (&rp->mutex);
53 #ifdef MUTEX_DEBUG
54   rp->mutex_owner_pid = getpid ();
55   rp->mutex_owner_tag = tag;
56 #endif
57 }
58
59 always_inline void
60 region_unlock (svm_region_t * rp)
61 {
62 #ifdef MUTEX_DEBUG
63   rp->mutex_owner_pid = 0;
64   rp->mutex_owner_tag = 0;
65 #endif
66   pthread_mutex_unlock (&rp->mutex);
67 }
68
69 static svmdb_client_t *
70 svmdb_map_internal (char *root_path, uword size)
71 {
72   svmdb_client_t *client = 0;
73   svm_map_region_args_t *a = 0;
74   svm_region_t *db_rp;
75   void *oldheap;
76   svmdb_shm_hdr_t *hp = 0;
77
78   vec_validate (client, 0);
79   vec_validate (a, 0);
80
81   svm_region_init_chroot (root_path);
82
83   a->root_path = root_path;
84   a->name = "/db";
85   a->size = size ? size : SVMDB_DEFAULT_SIZE;
86   a->flags = SVM_FLAGS_MHEAP;
87
88   db_rp = client->db_rp = svm_region_find_or_create (a);
89
90   ASSERT (db_rp);
91
92   vec_free (a);
93
94   region_lock (client->db_rp, 10);
95   /* Has someone else set up the shared-memory variable table? */
96   if (db_rp->user_ctx)
97     {
98       client->shm = (void *) db_rp->user_ctx;
99       client->pid = getpid ();
100       region_unlock (client->db_rp);
101       ASSERT (client->shm->version == SVMDB_SHM_VERSION);
102       return (client);
103     }
104   /* Nope, it's our problem... */
105
106   /* Add a bogus client (pid=0) so the svm won't be deallocated */
107   oldheap = svm_push_pvt_heap (db_rp);
108   vec_add1 (client->db_rp->client_pids, 0);
109   svm_pop_heap (oldheap);
110
111   oldheap = svm_push_data_heap (db_rp);
112
113   vec_validate (hp, 0);
114   hp->version = SVMDB_SHM_VERSION;
115   hp->namespaces[SVMDB_NAMESPACE_STRING]
116     = hash_create_string (0, sizeof (uword));
117   hp->namespaces[SVMDB_NAMESPACE_VEC]
118     = hash_create_string (0, sizeof (uword));
119
120   db_rp->user_ctx = hp;
121   client->shm = hp;
122
123   svm_pop_heap (oldheap);
124   region_unlock (client->db_rp);
125   client->pid = getpid ();
126
127   return (client);
128 }
129
130 svmdb_client_t *
131 svmdb_map (void)
132 {
133   return svmdb_map_internal (0, 0);
134 }
135
136 svmdb_client_t *
137 svmdb_map_size (uword size)
138 {
139   return svmdb_map_internal (0, size);
140 }
141
142 svmdb_client_t *
143 svmdb_map_chroot (char *root_path)
144 {
145   return svmdb_map_internal (root_path, 0);
146 }
147
148 svmdb_client_t *
149 svmdb_map_chroot_size (char *root_path, uword size)
150 {
151   return svmdb_map_internal (root_path, size);
152 }
153
154 void
155 svmdb_unmap (svmdb_client_t * client)
156 {
157   ASSERT (client);
158
159   if (!svm_get_root_rp ())
160     return;
161
162   svm_region_unmap ((void *) client->db_rp);
163   svm_region_exit ();
164   vec_free (client);
165 }
166
167 static void
168 notify_value (svmdb_value_t * v, svmdb_action_t a)
169 {
170   int i;
171   int rv;
172   union sigval sv;
173   u32 value;
174   u32 *dead_registrations = 0;
175
176   svmdb_notify_t *np;
177
178   for (i = 0; i < vec_len (v->notifications); i++)
179     {
180       np = vec_elt_at_index (v->notifications, i);
181       if (np->action == a)
182         {
183           value = (np->action << 28) | (np->opaque);
184           sv.sival_ptr = (void *) (uword) value;
185           do
186             {
187               rv = 0;
188               if (sigqueue (np->pid, np->signum, sv) == 0)
189                 break;
190               rv = errno;
191             }
192           while (rv == EAGAIN);
193           if (rv == 0)
194             continue;
195           vec_add1 (dead_registrations, i);
196         }
197     }
198
199   for (i = 0; i < vec_len (dead_registrations); i++)
200     {
201       np = vec_elt_at_index (v->notifications, dead_registrations[i]);
202       clib_warning ("dead reg pid %d sig %d action %d opaque %x",
203                     np->pid, np->signum, np->action, np->opaque);
204       vec_delete (v->notifications, 1, dead_registrations[i]);
205     }
206   vec_free (dead_registrations);
207 }
208
209 int
210 svmdb_local_add_del_notification (svmdb_client_t * client,
211                                   svmdb_notification_args_t * a)
212 {
213   uword *h;
214   void *oldheap;
215   hash_pair_t *hp;
216   svmdb_shm_hdr_t *shm;
217   u8 *dummy_value = 0;
218   svmdb_value_t *value;
219   svmdb_notify_t *np;
220   int i;
221   int rv = 0;
222
223   ASSERT (a->elsize);
224
225   region_lock (client->db_rp, 18);
226   shm = client->shm;
227   oldheap = svm_push_data_heap (client->db_rp);
228
229   h = shm->namespaces[a->nspace];
230
231   hp = hash_get_pair_mem (h, a->var);
232   if (hp == 0)
233     {
234       local_set_variable_nolock (client, a->nspace, (u8 *) a->var,
235                                  dummy_value, a->elsize);
236       /* might have moved */
237       h = shm->namespaces[a->nspace];
238       hp = hash_get_pair_mem (h, a->var);
239       ASSERT (hp);
240     }
241
242   value = pool_elt_at_index (shm->values, hp->value[0]);
243
244   for (i = 0; i < vec_len (value->notifications); i++)
245     {
246       np = vec_elt_at_index (value->notifications, i);
247       if ((np->pid == client->pid)
248           && (np->signum == a->signum)
249           && (np->action == a->action) && (np->opaque == a->opaque))
250         {
251           if (a->add_del == 0 /* delete */ )
252             {
253               vec_delete (value->notifications, 1, i);
254               goto out;
255             }
256           else
257             {                   /* add */
258               clib_warning
259                 ("%s: ignore dup reg pid %d signum %d action %d opaque %x",
260                  a->var, client->pid, a->signum, a->action, a->opaque);
261               rv = -2;
262               goto out;
263             }
264         }
265     }
266   if (a->add_del == 0)
267     {
268       rv = -3;
269       goto out;
270     }
271
272   vec_add2 (value->notifications, np, 1);
273   np->pid = client->pid;
274   np->signum = a->signum;
275   np->action = a->action;
276   np->opaque = a->opaque;
277
278 out:
279   svm_pop_heap (oldheap);
280   region_unlock (client->db_rp);
281   return rv;
282 }
283
284
285 static void
286 local_unset_variable_nolock (svmdb_client_t * client,
287                              svmdb_namespace_t namespace, char *var)
288 {
289   uword *h;
290   svmdb_value_t *oldvalue;
291   hash_pair_t *hp;
292
293   h = client->shm->namespaces[namespace];
294   hp = hash_get_pair_mem (h, var);
295   if (hp)
296     {
297       oldvalue = pool_elt_at_index (client->shm->values, hp->value[0]);
298       if (vec_len (oldvalue->notifications))
299         notify_value (oldvalue, SVMDB_ACTION_UNSET);
300       /* zero length value means unset */
301       _vec_len (oldvalue->value) = 0;
302     }
303   client->shm->namespaces[namespace] = h;
304 }
305
306 void
307 svmdb_local_unset_string_variable (svmdb_client_t * client, char *var)
308 {
309   void *oldheap;
310
311   region_lock (client->db_rp, 11);
312   oldheap = svm_push_data_heap (client->db_rp);
313   local_unset_variable_nolock (client, SVMDB_NAMESPACE_STRING, var);
314   svm_pop_heap (oldheap);
315   region_unlock (client->db_rp);
316 }
317
318 static void
319 local_set_variable_nolock (svmdb_client_t * client,
320                            svmdb_namespace_t namespace,
321                            u8 * var, u8 * val, u32 elsize)
322 {
323   uword *h;
324   hash_pair_t *hp;
325   u8 *name;
326   svmdb_shm_hdr_t *shm;
327
328   shm = client->shm;
329   h = shm->namespaces[namespace];
330   hp = hash_get_pair_mem (h, var);
331   if (hp)
332     {
333       svmdb_value_t *oldvalue;
334       oldvalue = pool_elt_at_index (client->shm->values, hp->value[0]);
335       vec_alloc (oldvalue->value, vec_len (val) * elsize);
336       clib_memcpy (oldvalue->value, val, vec_len (val) * elsize);
337       _vec_len (oldvalue->value) = vec_len (val);
338       notify_value (oldvalue, SVMDB_ACTION_SET);
339     }
340   else
341     {
342       svmdb_value_t *newvalue;
343       pool_get (shm->values, newvalue);
344       memset (newvalue, 0, sizeof (*newvalue));
345       newvalue->elsize = elsize;
346       vec_alloc (newvalue->value, vec_len (val) * elsize);
347       clib_memcpy (newvalue->value, val, vec_len (val) * elsize);
348       _vec_len (newvalue->value) = vec_len (val);
349       name = format (0, "%s%c", var, 0);
350       hash_set_mem (h, name, newvalue - shm->values);
351     }
352   shm->namespaces[namespace] = h;
353 }
354
355 void
356 svmdb_local_set_string_variable (svmdb_client_t * client,
357                                  char *var, char *val)
358 {
359   void *oldheap;
360
361   region_lock (client->db_rp, 12);
362   oldheap = svm_push_data_heap (client->db_rp);
363
364   local_unset_variable_nolock (client, SVMDB_NAMESPACE_STRING, var);
365
366   local_set_variable_nolock (client, SVMDB_NAMESPACE_STRING,
367                              (u8 *) var, (u8 *) val, 1 /* elsize */ );
368   svm_pop_heap (oldheap);
369   region_unlock (client->db_rp);
370 }
371
372 static u8 *
373 local_get_variable_nolock (svmdb_client_t * client,
374                            svmdb_namespace_t namespace, u8 * var)
375 {
376   uword *h;
377   uword *p;
378   svmdb_shm_hdr_t *shm;
379   svmdb_value_t *oldvalue;
380
381   shm = client->shm;
382   h = shm->namespaces[namespace];
383   p = hash_get_mem (h, var);
384   if (p)
385     {
386       oldvalue = pool_elt_at_index (shm->values, p[0]);
387       notify_value (oldvalue, SVMDB_ACTION_GET);
388       return (oldvalue->value);
389     }
390   return 0;
391 }
392
393 void *
394 svmdb_local_get_variable_reference (svmdb_client_t * client,
395                                     svmdb_namespace_t namespace, char *var)
396 {
397   u8 *rv;
398
399   region_lock (client->db_rp, 19);
400   rv = local_get_variable_nolock (client, namespace, (u8 *) var);
401   region_unlock (client->db_rp);
402   return (void *) rv;
403 }
404
405 char *
406 svmdb_local_get_string_variable (svmdb_client_t * client, char *var)
407 {
408   u8 *rv = 0;
409
410   region_lock (client->db_rp, 13);
411   rv = local_get_variable_nolock (client, SVMDB_NAMESPACE_STRING, (u8 *) var);
412
413   if (rv && vec_len (rv))
414     {
415       rv = format (0, "%s", rv);
416       vec_add1 (rv, 0);
417     }
418   region_unlock (client->db_rp);
419   return ((char *) rv);
420 }
421
422 void
423 svmdb_local_dump_strings (svmdb_client_t * client)
424 {
425   uword *h;
426   u8 *key;
427   u32 value;
428   svmdb_shm_hdr_t *shm = client->shm;
429
430   region_lock (client->db_rp, 14);
431
432   h = client->shm->namespaces[SVMDB_NAMESPACE_STRING];
433
434   /* *INDENT-OFF* */
435   hash_foreach_mem(key, value, h,
436   ({
437     svmdb_value_t *v = pool_elt_at_index (shm->values, value);
438
439     fformat(stdout, "%s: %s\n", key,
440             vec_len(v->value) ? v->value : (u8 *)"(nil)");
441   }));
442   /* *INDENT-ON* */
443   region_unlock (client->db_rp);
444 }
445
446 void
447 svmdb_local_unset_vec_variable (svmdb_client_t * client, char *var)
448 {
449   void *oldheap;
450
451   region_lock (client->db_rp, 15);
452   oldheap = svm_push_data_heap (client->db_rp);
453   local_unset_variable_nolock (client, SVMDB_NAMESPACE_VEC, var);
454   svm_pop_heap (oldheap);
455   region_unlock (client->db_rp);
456 }
457
458 void
459 svmdb_local_set_vec_variable (svmdb_client_t * client,
460                               char *var, void *val_arg, u32 elsize)
461 {
462   u8 *val = (u8 *) val_arg;
463   void *oldheap;
464
465   region_lock (client->db_rp, 16);
466   oldheap = svm_push_data_heap (client->db_rp);
467
468   local_unset_variable_nolock (client, SVMDB_NAMESPACE_VEC, var);
469   local_set_variable_nolock (client, SVMDB_NAMESPACE_VEC, (u8 *) var,
470                              val, elsize);
471
472   svm_pop_heap (oldheap);
473   region_unlock (client->db_rp);
474 }
475
476 void *
477 svmdb_local_get_vec_variable (svmdb_client_t * client, char *var, u32 elsize)
478 {
479   u8 *rv = 0;
480   u8 *copy = 0;
481
482   region_lock (client->db_rp, 17);
483
484   rv = local_get_variable_nolock (client, SVMDB_NAMESPACE_VEC, (u8 *) var);
485
486   if (rv && vec_len (rv))
487     {
488       /* Make a copy in process-local memory */
489       vec_alloc (copy, vec_len (rv) * elsize);
490       clib_memcpy (copy, rv, vec_len (rv) * elsize);
491       _vec_len (copy) = vec_len (rv);
492       region_unlock (client->db_rp);
493       return (copy);
494     }
495   region_unlock (client->db_rp);
496   return (0);
497 }
498
499 void
500 svmdb_local_dump_vecs (svmdb_client_t * client)
501 {
502   uword *h;
503   u8 *key;
504   u32 value;
505   svmdb_shm_hdr_t *shm;
506
507   region_lock (client->db_rp, 17);
508   shm = client->shm;
509
510   h = client->shm->namespaces[SVMDB_NAMESPACE_VEC];
511
512   /* *INDENT-OFF* */
513   hash_foreach_mem(key, value, h,
514   ({
515     svmdb_value_t *v = pool_elt_at_index (shm->values, value);
516     (void) fformat(stdout, "%s:\n %U (%.2f)\n", key,
517                    format_hex_bytes, v->value,
518                    vec_len(v->value)*v->elsize, ((f64 *)(v->value))[0]);
519   }));
520   /* *INDENT-ON* */
521
522   region_unlock (client->db_rp);
523 }
524
525 void *
526 svmdb_local_find_or_add_vec_variable (svmdb_client_t * client,
527                                       char *var, u32 nbytes)
528 {
529   void *oldheap;
530   u8 *rv = 0;
531
532   region_lock (client->db_rp, 18);
533   oldheap = svm_push_data_heap (client->db_rp);
534
535   rv = local_get_variable_nolock (client, SVMDB_NAMESPACE_VEC, (u8 *) var);
536
537   if (rv)
538     {
539       goto out;
540     }
541   else
542     {
543       uword *h;
544       u8 *name;
545       svmdb_shm_hdr_t *shm;
546       svmdb_value_t *newvalue;
547
548       shm = client->shm;
549       h = shm->namespaces[SVMDB_NAMESPACE_VEC];
550
551       pool_get (shm->values, newvalue);
552       memset (newvalue, 0, sizeof (*newvalue));
553       newvalue->elsize = 1;
554       vec_alloc (newvalue->value, nbytes);
555       _vec_len (newvalue->value) = nbytes;
556       name = format (0, "%s%c", var, 0);
557       hash_set_mem (h, name, newvalue - shm->values);
558       shm->namespaces[SVMDB_NAMESPACE_VEC] = h;
559       rv = newvalue->value;
560     }
561
562 out:
563   svm_pop_heap (oldheap);
564   region_unlock (client->db_rp);
565   return (rv);
566 }
567
568 /*
569  * fd.io coding-style-patch-verification: ON
570  *
571  * Local Variables:
572  * eval: (c-set-style "gnu")
573  * End:
574  */