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