misc: fix static analysis warnings
[vpp.git] / src / svm / svm.c
1 /*
2  *------------------------------------------------------------------
3  * svm.c - shared VM allocation, mmap(...MAP_FIXED...)
4  * library
5  *
6  * Copyright (c) 2009 Cisco and/or its affiliates.
7  * Licensed under the Apache License, Version 2.0 (the "License");
8  * you may not use this file except in compliance with the License.
9  * You may obtain a copy of the License at:
10  *
11  *     http://www.apache.org/licenses/LICENSE-2.0
12  *
13  * Unless required by applicable law or agreed to in writing, software
14  * distributed under the License is distributed on an "AS IS" BASIS,
15  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
16  * See the License for the specific language governing permissions and
17  * limitations under the License.
18  *------------------------------------------------------------------
19  */
20
21 #include <stdio.h>
22 #include <stdlib.h>
23 #include <sys/types.h>
24 #include <sys/mman.h>
25 #include <sys/stat.h>
26 #include <netinet/in.h>
27 #include <signal.h>
28 #include <pthread.h>
29 #include <unistd.h>
30 #include <time.h>
31 #include <fcntl.h>
32 #include <string.h>
33 #include <vppinfra/clib.h>
34 #include <vppinfra/vec.h>
35 #include <vppinfra/hash.h>
36 #include <vppinfra/bitmap.h>
37 #include <vppinfra/fifo.h>
38 #include <vppinfra/time.h>
39 #include <vppinfra/mheap.h>
40 #include <vppinfra/heap.h>
41 #include <vppinfra/pool.h>
42 #include <vppinfra/format.h>
43
44 #include "svm.h"
45
46 static svm_region_t *root_rp;
47 static int root_rp_refcount;
48
49 #define MAXLOCK 2
50 static pthread_mutex_t *mutexes_held[MAXLOCK];
51 static int nheld;
52
53 svm_region_t *
54 svm_get_root_rp (void)
55 {
56   return root_rp;
57 }
58
59 #define MUTEX_DEBUG
60
61 u64
62 svm_get_global_region_base_va ()
63 {
64 #ifdef CLIB_SANITIZE_ADDR
65   return 0x200000000000;
66 #endif
67
68 #if __aarch64__
69   /* On AArch64 VA space can have different size, from 36 to 48 bits.
70      Here we are trying to detect VA bits by parsing /proc/self/maps
71      address ranges */
72   int fd;
73   unformat_input_t input;
74   u64 start, end = 0;
75   u8 bits = 0;
76
77   if ((fd = open ("/proc/self/maps", 0)) < 0)
78     clib_unix_error ("open '/proc/self/maps'");
79
80   unformat_init_clib_file (&input, fd);
81   while (unformat_check_input (&input) != UNFORMAT_END_OF_INPUT)
82     {
83       if (unformat (&input, "%llx-%llx", &start, &end))
84         end--;
85       unformat_skip_line (&input);
86     }
87   unformat_free (&input);
88   close (fd);
89
90   bits = count_leading_zeros (end);
91   bits = 64 - bits;
92   if (bits >= 36 && bits <= 48)
93     return ((1ul << bits) / 4) - (2 * SVM_GLOBAL_REGION_SIZE);
94   else
95     clib_unix_error ("unexpected va bits '%u'", bits);
96 #endif
97
98   /* default value */
99   return 0x130000000ULL;
100 }
101
102 static void
103 region_lock (svm_region_t * rp, int tag)
104 {
105   pthread_mutex_lock (&rp->mutex);
106 #ifdef MUTEX_DEBUG
107   rp->mutex_owner_pid = getpid ();
108   rp->mutex_owner_tag = tag;
109 #endif
110   ASSERT (nheld < MAXLOCK);     //NOSONAR
111   /*
112    * Keep score of held mutexes so we can try to exit
113    * cleanly if the world comes to an end at the worst possible
114    * moment
115    */
116   mutexes_held[nheld++] = &rp->mutex;
117 }
118
119 static void
120 region_unlock (svm_region_t * rp)
121 {
122   int i, j;
123 #ifdef MUTEX_DEBUG
124   rp->mutex_owner_pid = 0;
125   rp->mutex_owner_tag = 0;
126 #endif
127
128   for (i = nheld - 1; i >= 0; i--)
129     {
130       if (mutexes_held[i] == &rp->mutex)
131         {
132           for (j = i; j < MAXLOCK - 1; j++)
133             mutexes_held[j] = mutexes_held[j + 1];
134           nheld--;
135           goto found;
136         }
137     }
138   ASSERT (0);
139
140 found:
141   CLIB_MEMORY_BARRIER ();
142   pthread_mutex_unlock (&rp->mutex);
143 }
144
145
146 static u8 *
147 format_svm_flags (u8 * s, va_list * args)
148 {
149   uword f = va_arg (*args, uword);
150
151   if (f & SVM_FLAGS_MHEAP)
152     s = format (s, "MHEAP ");
153   if (f & SVM_FLAGS_FILE)
154     s = format (s, "FILE ");
155   if (f & SVM_FLAGS_NODATA)
156     s = format (s, "NODATA ");
157   if (f & SVM_FLAGS_NEED_DATA_INIT)
158     s = format (s, "INIT ");
159
160   return (s);
161 }
162
163 static u8 *
164 format_svm_size (u8 * s, va_list * args)
165 {
166   uword size = va_arg (*args, uword);
167
168   if (size >= (1 << 20))
169     {
170       s = format (s, "(%d mb)", size >> 20);
171     }
172   else if (size >= (1 << 10))
173     {
174       s = format (s, "(%d kb)", size >> 10);
175     }
176   else
177     {
178       s = format (s, "(%d bytes)", size);
179     }
180   return (s);
181 }
182
183 u8 *
184 format_svm_region (u8 * s, va_list * args)
185 {
186   svm_region_t *rp = va_arg (*args, svm_region_t *);
187   int verbose = va_arg (*args, int);
188   int i;
189   uword lo, hi;
190
191   s = format (s, "%s: base va 0x%x size 0x%x %U\n",
192               rp->region_name, rp->virtual_base,
193               rp->virtual_size, format_svm_size, rp->virtual_size);
194   s = format (s, "  user_ctx 0x%x, bitmap_size %d\n",
195               rp->user_ctx, rp->bitmap_size);
196
197   if (verbose)
198     {
199       s = format (s, "  flags: 0x%x %U\n", rp->flags,
200                   format_svm_flags, rp->flags);
201       s = format (s,
202                   "  region_heap 0x%x data_base 0x%x data_heap 0x%x\n",
203                   rp->region_heap, rp->data_base, rp->data_heap);
204     }
205
206   s = format (s, "  %d clients, pids: ", vec_len (rp->client_pids));
207
208   for (i = 0; i < vec_len (rp->client_pids); i++)
209     s = format (s, "%d ", rp->client_pids[i]);
210
211   s = format (s, "\n");
212
213   if (verbose)
214     {
215       lo = hi = ~0;
216
217       s = format (s, "  VM in use: ");
218
219       for (i = 0; i < rp->bitmap_size; i++)
220         {
221           if (clib_bitmap_get_no_check (rp->bitmap, i) != 0)
222             {
223               if (lo == ~0)
224                 {
225                   hi = lo = rp->virtual_base + i * MMAP_PAGESIZE;
226                 }
227               else
228                 {
229                   hi = rp->virtual_base + i * MMAP_PAGESIZE;
230                 }
231             }
232           else
233             {
234               if (lo != ~0)
235                 {
236                   hi = rp->virtual_base + i * MMAP_PAGESIZE - 1;
237                   s = format (s, "   0x%x - 0x%x (%dk)\n", lo, hi,
238                               (hi - lo) >> 10);
239                   lo = hi = ~0;
240                 }
241             }
242         }
243     }
244
245   return (s);
246 }
247
248 /*
249  * rnd_pagesize
250  * Round to a pagesize multiple, presumably 4k works
251  */
252 static u64
253 rnd_pagesize (u64 size)
254 {
255   u64 rv;
256
257   rv = (size + (MMAP_PAGESIZE - 1)) & ~(MMAP_PAGESIZE - 1);
258   return (rv);
259 }
260
261 /*
262  * svm_data_region_setup
263  */
264 static int
265 svm_data_region_create (svm_map_region_args_t * a, svm_region_t * rp)
266 {
267   int fd;
268   u8 junk = 0;
269   uword map_size;
270
271   map_size = rp->virtual_size - (MMAP_PAGESIZE +
272                                  (a->pvt_heap_size ? a->pvt_heap_size :
273                                   SVM_PVT_MHEAP_SIZE));
274
275   if (a->flags & SVM_FLAGS_FILE)
276     {
277       struct stat statb;
278
279       fd = open (a->backing_file, O_RDWR | O_CREAT, 0777);
280
281       if (fd < 0)
282         {
283           clib_unix_warning ("open");
284           return -1;
285         }
286
287       if (fstat (fd, &statb) < 0)
288         {
289           clib_unix_warning ("fstat");
290           close (fd);
291           return -2;
292         }
293
294       if (statb.st_mode & S_IFREG)
295         {
296           if (statb.st_size == 0)
297             {
298               if (lseek (fd, map_size, SEEK_SET) == (off_t) - 1)
299                 {
300                   clib_unix_warning ("seek region size");
301                   close (fd);
302                   return -3;
303                 }
304               if (write (fd, &junk, 1) != 1)
305                 {
306                   clib_unix_warning ("set region size");
307                   close (fd);
308                   return -3;
309                 }
310             }
311           else
312             {
313               map_size = rnd_pagesize (statb.st_size);
314             }
315         }
316       else
317         {
318           map_size = a->backing_mmap_size;
319         }
320
321       ASSERT (map_size <= rp->virtual_size -
322               (MMAP_PAGESIZE + SVM_PVT_MHEAP_SIZE));
323
324       if (mmap (rp->data_base, map_size, PROT_READ | PROT_WRITE,
325                 MAP_SHARED | MAP_FIXED, fd, 0) == MAP_FAILED)
326         {
327           clib_unix_warning ("mmap");
328           close (fd);
329           return -3;
330         }
331       close (fd);
332       rp->backing_file = (char *) format (0, "%s\0", a->backing_file);
333       rp->flags |= SVM_FLAGS_FILE;
334     }
335
336   if (a->flags & SVM_FLAGS_MHEAP)
337     {
338       rp->data_heap = create_mspace_with_base (rp->data_base,
339                                                map_size, 1 /* locked */ );
340       mspace_disable_expand (rp->data_heap);
341
342       rp->flags |= SVM_FLAGS_MHEAP;
343     }
344   return 0;
345 }
346
347 static int
348 svm_data_region_map (svm_map_region_args_t * a, svm_region_t * rp)
349 {
350   int fd;
351   u8 junk = 0;
352   uword map_size;
353   struct stat statb;
354
355   map_size = rp->virtual_size -
356     (MMAP_PAGESIZE
357      + (a->pvt_heap_size ? a->pvt_heap_size : SVM_PVT_MHEAP_SIZE));
358
359   if (a->flags & SVM_FLAGS_FILE)
360     {
361
362       fd = open (a->backing_file, O_RDWR, 0777);
363
364       if (fd < 0)
365         {
366           clib_unix_warning ("open");
367           return -1;
368         }
369
370       if (fstat (fd, &statb) < 0)
371         {
372           clib_unix_warning ("fstat");
373           close (fd);
374           return -2;
375         }
376
377       if (statb.st_mode & S_IFREG)
378         {
379           if (statb.st_size == 0)
380             {
381               if (lseek (fd, map_size, SEEK_SET) == (off_t) - 1)
382                 {
383                   clib_unix_warning ("seek region size");
384                   close (fd);
385                   return -3;
386                 }
387               if (write (fd, &junk, 1) != 1)
388                 {
389                   clib_unix_warning ("set region size");
390                   close (fd);
391                   return -3;
392                 }
393             }
394           else
395             {
396               map_size = rnd_pagesize (statb.st_size);
397             }
398         }
399       else
400         {
401           map_size = a->backing_mmap_size;
402         }
403
404       ASSERT (map_size <= rp->virtual_size
405               - (MMAP_PAGESIZE
406                  +
407                  (a->pvt_heap_size ? a->pvt_heap_size : SVM_PVT_MHEAP_SIZE)));
408
409       if (mmap (rp->data_base, map_size, PROT_READ | PROT_WRITE,
410                 MAP_SHARED | MAP_FIXED, fd, 0) == MAP_FAILED)
411         {
412           clib_unix_warning ("mmap");
413           close (fd);
414           return -3;
415         }
416       close (fd);
417     }
418   return 0;
419 }
420
421 u8 *
422 shm_name_from_svm_map_region_args (svm_map_region_args_t * a)
423 {
424   u8 *shm_name;
425   int root_path_offset = 0;
426   int name_offset = 0;
427
428   if (a->root_path)
429     {
430       /* Tolerate present or absent slashes */
431       if (a->root_path[0] == '/')
432         root_path_offset++;
433
434       if (a->name[0] == '/')
435         name_offset = 1;
436
437       shm_name = format (0, "/%s-%s%c", &a->root_path[root_path_offset],
438                          &a->name[name_offset], 0);
439     }
440   else
441     shm_name = format (0, "%s%c", a->name, 0);
442   return (shm_name);
443 }
444
445 void
446 svm_region_init_mapped_region (svm_map_region_args_t * a, svm_region_t * rp)
447 {
448   pthread_mutexattr_t attr;
449   pthread_condattr_t cattr;
450   int nbits, words, bit;
451   int overhead_space;
452   void *oldheap;
453   uword data_base;
454   ASSERT (rp);
455   int rv;
456
457   clib_memset (rp, 0, sizeof (*rp));
458
459   if (pthread_mutexattr_init (&attr))
460     clib_unix_warning ("mutexattr_init");
461
462   if (pthread_mutexattr_setpshared (&attr, PTHREAD_PROCESS_SHARED))
463     clib_unix_warning ("mutexattr_setpshared");
464
465   if (pthread_mutex_init (&rp->mutex, &attr))
466     clib_unix_warning ("mutex_init");
467
468   if (pthread_mutexattr_destroy (&attr))
469     clib_unix_warning ("mutexattr_destroy");
470
471   if (pthread_condattr_init (&cattr))
472     clib_unix_warning ("condattr_init");
473
474   if (pthread_condattr_setpshared (&cattr, PTHREAD_PROCESS_SHARED))
475     clib_unix_warning ("condattr_setpshared");
476
477   if (pthread_cond_init (&rp->condvar, &cattr))
478     clib_unix_warning ("cond_init");
479
480   if (pthread_condattr_destroy (&cattr))
481     clib_unix_warning ("condattr_destroy");
482
483   region_lock (rp, 1);
484
485   rp->virtual_base = a->baseva;
486   rp->virtual_size = a->size;
487
488   rp->region_heap = create_mspace_with_base
489     (uword_to_pointer (a->baseva + MMAP_PAGESIZE, void *),
490      (a->pvt_heap_size !=
491       0) ? a->pvt_heap_size : SVM_PVT_MHEAP_SIZE, 1 /* locked */ );
492
493   mspace_disable_expand (rp->region_heap);
494
495   oldheap = svm_push_pvt_heap (rp);
496
497   rp->region_name = (char *) format (0, "%s%c", a->name, 0);
498   vec_add1 (rp->client_pids, getpid ());
499
500   nbits = rp->virtual_size / MMAP_PAGESIZE;
501
502   ASSERT (nbits > 0);
503   rp->bitmap_size = nbits;
504   words = (nbits + BITS (uword) - 1) / BITS (uword);
505   vec_validate (rp->bitmap, words - 1);
506
507   overhead_space = MMAP_PAGESIZE /* header */  +
508     ((a->pvt_heap_size != 0) ? a->pvt_heap_size : SVM_PVT_MHEAP_SIZE);
509
510   bit = 0;
511   data_base = (uword) rp->virtual_base;
512
513   if (a->flags & SVM_FLAGS_NODATA)
514     rp->flags |= SVM_FLAGS_NEED_DATA_INIT;
515
516   do
517     {
518       clib_bitmap_set_no_check (rp->bitmap, bit, 1);
519       bit++;
520       overhead_space -= MMAP_PAGESIZE;
521       data_base += MMAP_PAGESIZE;
522     }
523   while (overhead_space > 0);
524
525   rp->data_base = (void *) data_base;
526
527   /*
528    * Note: although the POSIX spec guarantees that only one
529    * process enters this block, we have to play games
530    * to hold off clients until e.g. the mutex is ready
531    */
532   rp->version = SVM_VERSION;
533
534   /* setup the data portion of the region */
535
536   rv = svm_data_region_create (a, rp);
537   if (rv)
538     {
539       clib_warning ("data_region_create: %d", rv);
540     }
541
542   region_unlock (rp);
543
544   svm_pop_heap (oldheap);
545 }
546
547 /*
548  * svm_map_region
549  */
550 void *
551 svm_map_region (svm_map_region_args_t * a)
552 {
553   int svm_fd;
554   svm_region_t *rp;
555   int deadman = 0;
556   u8 junk = 0;
557   void *oldheap;
558   int rv;
559   int pid_holding_region_lock;
560   u8 *shm_name;
561   int dead_region_recovery = 0;
562   int time_left;
563   struct stat stat;
564   struct timespec ts, tsrem;
565
566   ASSERT ((a->size & ~(MMAP_PAGESIZE - 1)) == a->size);
567   ASSERT (a->name);
568
569   shm_name = shm_name_from_svm_map_region_args (a);
570
571   if (CLIB_DEBUG > 1)
572     clib_warning ("[%d] map region %s: shm_open (%s)",
573                   getpid (), a->name, shm_name);
574
575   svm_fd = shm_open ((char *) shm_name, O_RDWR | O_CREAT | O_EXCL, 0777);
576
577   if (svm_fd >= 0)
578     {
579       if (fchmod (svm_fd, S_IRUSR | S_IWUSR | S_IRGRP | S_IWGRP) < 0)
580         clib_unix_warning ("segment chmod");
581       /* This turns out to fail harmlessly if the client starts first */
582       if (fchown (svm_fd, a->uid, a->gid) < 0)
583         clib_unix_warning ("segment chown [ok if client starts first]");
584
585       vec_free (shm_name);
586
587       if (lseek (svm_fd, a->size, SEEK_SET) == (off_t) - 1)
588         {
589           clib_warning ("seek region size");
590           close (svm_fd);
591           return (0);
592         }
593       if (write (svm_fd, &junk, 1) != 1)
594         {
595           clib_warning ("set region size");
596           close (svm_fd);
597           return (0);
598         }
599
600       rp = mmap (uword_to_pointer (a->baseva, void *), a->size,
601                  PROT_READ | PROT_WRITE, MAP_SHARED | MAP_FIXED, svm_fd, 0);
602
603       if (rp == (svm_region_t *) MAP_FAILED)
604         {
605           clib_unix_warning ("mmap create");
606           close (svm_fd);
607           return (0);
608         }
609       close (svm_fd);
610
611       svm_region_init_mapped_region (a, rp);
612
613       return ((void *) rp);
614     }
615   else
616     {
617       svm_fd = shm_open ((char *) shm_name, O_RDWR, 0777);
618
619       vec_free (shm_name);
620
621       if (svm_fd < 0)
622         {
623           perror ("svm_region_map(mmap open)");
624           return (0);
625         }
626
627       /* Reset ownership in case the client started first */
628       if (fchown (svm_fd, a->uid, a->gid) < 0)
629         clib_unix_warning ("segment chown [ok if client starts first]");
630
631       time_left = 20;
632       while (1)
633         {
634           if (0 != fstat (svm_fd, &stat))
635             {
636               clib_warning ("fstat failed: %d", errno);
637               close (svm_fd);
638               return (0);
639             }
640           if (stat.st_size > 0)
641             {
642               break;
643             }
644           if (0 == time_left)
645             {
646               clib_warning ("waiting for resize of shm file timed out");
647               close (svm_fd);
648               return (0);
649             }
650           ts.tv_sec = 0;
651           ts.tv_nsec = 100000000;
652           while (nanosleep (&ts, &tsrem) < 0)
653             ts = tsrem;
654           time_left--;
655         }
656
657       rp = mmap (0, MMAP_PAGESIZE,
658                  PROT_READ | PROT_WRITE, MAP_SHARED, svm_fd, 0);
659
660       if (rp == (svm_region_t *) MAP_FAILED)
661         {
662           close (svm_fd);
663           clib_warning ("mmap");
664           return (0);
665         }
666       /*
667        * We lost the footrace to create this region; make sure
668        * the winner has crossed the finish line.
669        */
670       while (rp->version == 0 && deadman++ < 5)
671         {
672           sleep (1);
673         }
674
675       /*
676        * <bleep>-ed?
677        */
678       if (rp->version == 0)
679         {
680           clib_warning ("rp->version %d not %d", rp->version, SVM_VERSION);
681           close (svm_fd);
682           munmap (rp, a->size);
683           return (0);
684         }
685       /* Remap now that the region has been placed */
686       a->baseva = rp->virtual_base;
687       a->size = rp->virtual_size;
688       munmap (rp, MMAP_PAGESIZE);
689
690       rp = (void *) mmap (uword_to_pointer (a->baseva, void *), a->size,
691                           PROT_READ | PROT_WRITE,
692                           MAP_SHARED | MAP_FIXED, svm_fd, 0);
693       if ((uword) rp == (uword) MAP_FAILED)
694         {
695           clib_unix_warning ("mmap");
696           close (svm_fd);
697           return (0);
698         }
699
700       close (svm_fd);
701
702       if ((uword) rp != rp->virtual_base)
703         {
704           clib_warning ("mmap botch");
705         }
706
707       /*
708        * Try to fix the region mutex if it is held by
709        * a dead process
710        */
711       pid_holding_region_lock = rp->mutex_owner_pid;
712       if (pid_holding_region_lock && kill (pid_holding_region_lock, 0) < 0)
713         {
714           clib_warning
715             ("region %s mutex held by dead pid %d, tag %d, force unlock",
716              rp->region_name, pid_holding_region_lock, rp->mutex_owner_tag);
717           /* owner pid is nonexistent */
718           rp->mutex.__data.__owner = 0;
719           rp->mutex.__data.__lock = 0;
720           dead_region_recovery = 1;
721         }
722
723       if (dead_region_recovery)
724         clib_warning ("recovery: attempt to re-lock region");
725
726       region_lock (rp, 2);
727       oldheap = svm_push_pvt_heap (rp);
728       vec_add1 (rp->client_pids, getpid ());
729
730       if (dead_region_recovery)
731         clib_warning ("recovery: attempt svm_data_region_map");
732
733       rv = svm_data_region_map (a, rp);
734       if (rv)
735         {
736           clib_warning ("data_region_map: %d", rv);
737         }
738
739       if (dead_region_recovery)
740         clib_warning ("unlock and continue");
741
742       region_unlock (rp);
743
744       svm_pop_heap (oldheap);
745
746       return ((void *) rp);
747
748     }
749   return 0;                     /* NOTREACHED *///NOSONAR
750 }
751
752 static void
753 svm_mutex_cleanup (void)
754 {
755   int i;
756   for (i = 0; i < nheld; i++)
757     {
758       pthread_mutex_unlock (mutexes_held[i]);   //NOSONAR
759     }
760 }
761
762 static int
763 svm_region_init_internal (svm_map_region_args_t * a)
764 {
765   svm_region_t *rp;
766   u64 ticks = clib_cpu_time_now ();
767   uword randomize_baseva;
768
769   /* guard against klutz calls */
770   if (root_rp)
771     return -1;
772
773   root_rp_refcount++;
774
775   atexit (svm_mutex_cleanup);
776
777   /* Randomize the shared-VM base at init time */
778   if (MMAP_PAGESIZE <= (4 << 10))
779     randomize_baseva = (ticks & 15) * MMAP_PAGESIZE;
780   else
781     randomize_baseva = (ticks & 3) * MMAP_PAGESIZE;
782
783   a->baseva += randomize_baseva;
784
785   rp = svm_map_region (a);
786   if (!rp)
787     return -1;
788
789   region_lock (rp, 3);
790
791   /* Set up the main region data structures */
792   if (rp->flags & SVM_FLAGS_NEED_DATA_INIT)
793     {
794       svm_main_region_t *mp = 0;
795       void *oldheap;
796
797       rp->flags &= ~(SVM_FLAGS_NEED_DATA_INIT);
798
799       oldheap = svm_push_pvt_heap (rp);
800       vec_validate (mp, 0);
801       mp->name_hash = hash_create_string (0, sizeof (uword));
802       mp->root_path = a->root_path ? format (0, "%s%c", a->root_path, 0) : 0;
803       mp->uid = a->uid;
804       mp->gid = a->gid;
805       rp->data_base = mp;
806       svm_pop_heap (oldheap);
807     }
808   region_unlock (rp);
809   root_rp = rp;
810
811   return 0;
812 }
813
814 void
815 svm_region_init (void)
816 {
817   svm_map_region_args_t _a, *a = &_a;
818
819   clib_memset (a, 0, sizeof (*a));
820   a->root_path = 0;
821   a->name = SVM_GLOBAL_REGION_NAME;
822   a->baseva = svm_get_global_region_base_va ();
823   a->size = SVM_GLOBAL_REGION_SIZE;
824   a->flags = SVM_FLAGS_NODATA;
825   a->uid = 0;
826   a->gid = 0;
827
828   svm_region_init_internal (a);
829 }
830
831 int
832 svm_region_init_chroot (const char *root_path)
833 {
834   svm_map_region_args_t _a, *a = &_a;
835
836   clib_memset (a, 0, sizeof (*a));
837   a->root_path = root_path;
838   a->name = SVM_GLOBAL_REGION_NAME;
839   a->baseva = svm_get_global_region_base_va ();
840   a->size = SVM_GLOBAL_REGION_SIZE;
841   a->flags = SVM_FLAGS_NODATA;
842   a->uid = 0;
843   a->gid = 0;
844
845   return svm_region_init_internal (a);
846 }
847
848 void
849 svm_region_init_chroot_uid_gid (const char *root_path, int uid, int gid)
850 {
851   svm_map_region_args_t _a, *a = &_a;
852
853   clib_memset (a, 0, sizeof (*a));
854   a->root_path = root_path;
855   a->name = SVM_GLOBAL_REGION_NAME;
856   a->baseva = svm_get_global_region_base_va ();
857   a->size = SVM_GLOBAL_REGION_SIZE;
858   a->flags = SVM_FLAGS_NODATA;
859   a->uid = uid;
860   a->gid = gid;
861
862   svm_region_init_internal (a);
863 }
864
865 void
866 svm_region_init_args (svm_map_region_args_t * a)
867 {
868   svm_region_init_internal (a);
869 }
870
871 void *
872 svm_region_find_or_create (svm_map_region_args_t * a)
873 {
874   svm_main_region_t *mp;
875   svm_region_t *rp;
876   uword need_nbits;
877   int index, i;
878   void *oldheap;
879   uword *p;
880   u8 *name;
881   svm_subregion_t *subp;
882
883   ASSERT (root_rp);
884
885   a->size += MMAP_PAGESIZE +
886     ((a->pvt_heap_size != 0) ? a->pvt_heap_size : SVM_PVT_MHEAP_SIZE);
887   a->size = rnd_pagesize (a->size);
888
889   region_lock (root_rp, 4);
890   oldheap = svm_push_pvt_heap (root_rp);
891   mp = root_rp->data_base;
892
893   ASSERT (mp);
894
895   /* Map the named region from the correct chroot environment */
896   if (a->root_path == NULL)
897     a->root_path = (char *) mp->root_path;
898
899   /*
900    * See if this region is already known. If it is, we're
901    * almost done...
902    */
903   p = hash_get_mem (mp->name_hash, a->name);
904
905   if (p)
906     {
907       rp = svm_map_region (a);
908       region_unlock (root_rp);
909       svm_pop_heap (oldheap);
910       return rp;
911     }
912
913   /* Create the region. */
914   ASSERT ((a->size & ~(MMAP_PAGESIZE - 1)) == a->size);
915
916   need_nbits = a->size / MMAP_PAGESIZE;
917
918   index = 1;                    /* $$$ fixme, figure out how many bit to really skip */
919
920   /*
921    * Scan the virtual space allocation bitmap, looking for a large
922    * enough chunk
923    */
924   do
925     {
926       if (clib_bitmap_get_no_check (root_rp->bitmap, index) == 0)
927         {
928           for (i = 0; i < (need_nbits - 1); i++)
929             {
930               if (clib_bitmap_get_no_check (root_rp->bitmap, index + i) == 1)
931                 {
932                   index = index + i;
933                   goto next;
934                 }
935             }
936           break;
937         }
938       index++;
939     next:;
940     }
941   while (index < root_rp->bitmap_size);
942
943   /* Completely out of VM? */
944   if (index >= root_rp->bitmap_size)
945     {
946       clib_warning ("region %s: not enough VM to allocate 0x%llx (%lld)",
947                     root_rp->region_name, a->size, a->size);
948       svm_pop_heap (oldheap);
949       region_unlock (root_rp);
950       return 0;
951     }
952
953   /*
954    * Mark virtual space allocated
955    */
956 #if CLIB_DEBUG > 1
957   clib_warning ("set %d bits at index %d", need_nbits, index);
958 #endif
959
960   for (i = 0; i < need_nbits; i++)
961     {
962       clib_bitmap_set_no_check (root_rp->bitmap, index + i, 1);
963     }
964
965   /* Place this region where it goes... */
966   a->baseva = root_rp->virtual_base + index * MMAP_PAGESIZE;
967
968   rp = svm_map_region (a);
969
970   pool_get (mp->subregions, subp);
971   name = format (0, "%s%c", a->name, 0);
972   subp->subregion_name = name;
973
974   hash_set_mem (mp->name_hash, name, subp - mp->subregions);
975
976   svm_pop_heap (oldheap);
977
978   region_unlock (root_rp);
979
980   return (rp);
981 }
982
983 void
984 svm_region_unlink (svm_region_t * rp)
985 {
986   svm_map_region_args_t _a, *a = &_a;
987   svm_main_region_t *mp;
988   u8 *shm_name;
989
990   ASSERT (root_rp);
991   ASSERT (rp);
992   ASSERT (vec_c_string_is_terminated (rp->region_name));
993
994   mp = root_rp->data_base;
995   ASSERT (mp);
996
997   a->root_path = (char *) mp->root_path;
998   a->name = rp->region_name;
999   shm_name = shm_name_from_svm_map_region_args (a);
1000   if (CLIB_DEBUG > 1)
1001     clib_warning ("[%d] shm_unlink (%s)", getpid (), shm_name);
1002   shm_unlink ((const char *) shm_name);
1003   vec_free (shm_name);
1004 }
1005
1006 /*
1007  * svm_region_unmap
1008  *
1009  * Let go of the indicated region. If the calling process
1010  * is the last customer, throw it away completely.
1011  * The root region mutex guarantees atomicity with respect to
1012  * a new region client showing up at the wrong moment.
1013  */
1014 void
1015 svm_region_unmap_internal (void *rp_arg, u8 is_client)
1016 {
1017   int i, mypid = getpid ();
1018   int nclients_left;
1019   void *oldheap;
1020   uword virtual_base, virtual_size;
1021   svm_region_t *rp = rp_arg;
1022   char *name;
1023
1024   /*
1025    * If we take a signal while holding one or more shared-memory
1026    * mutexes, we may end up back here from an otherwise
1027    * benign exit handler. Bail out to avoid a recursive
1028    * mutex screw-up.
1029    */
1030   if (nheld)
1031     return;
1032
1033   ASSERT (rp);
1034   ASSERT (root_rp);
1035
1036   if (CLIB_DEBUG > 1)
1037     clib_warning ("[%d] unmap region %s", getpid (), rp->region_name);
1038
1039   region_lock (root_rp, 5);
1040   region_lock (rp, 6);
1041
1042   oldheap = svm_push_pvt_heap (rp);     /* nb vec_delete() in the loop */
1043
1044   /* Remove the caller from the list of mappers */
1045   for (i = 0; i < vec_len (rp->client_pids); i++)
1046     {
1047       if (rp->client_pids[i] == mypid)
1048         {
1049           vec_delete (rp->client_pids, 1, i);
1050           goto found;
1051         }
1052     }
1053   clib_warning ("pid %d AWOL", mypid);
1054
1055 found:
1056
1057   svm_pop_heap (oldheap);
1058
1059   nclients_left = vec_len (rp->client_pids);
1060   virtual_base = rp->virtual_base;
1061   virtual_size = rp->virtual_size;
1062
1063   if (nclients_left == 0)
1064     {
1065       int index, nbits, i;
1066       svm_main_region_t *mp;
1067       uword *p;
1068       svm_subregion_t *subp;
1069
1070       /* Kill the region, last guy on his way out */
1071
1072       oldheap = svm_push_pvt_heap (root_rp);
1073       name = vec_dup (rp->region_name);
1074
1075       virtual_base = rp->virtual_base;
1076       virtual_size = rp->virtual_size;
1077
1078       /* Figure out which bits to clear in the root region bitmap */
1079       index = (virtual_base - root_rp->virtual_base) / MMAP_PAGESIZE;
1080
1081       nbits = (virtual_size + MMAP_PAGESIZE - 1) / MMAP_PAGESIZE;
1082
1083 #if CLIB_DEBUG > 1
1084       clib_warning ("clear %d bits at index %d", nbits, index);
1085 #endif
1086       /* Give back the allocated VM */
1087       for (i = 0; i < nbits; i++)
1088         {
1089           clib_bitmap_set_no_check (root_rp->bitmap, index + i, 0);
1090         }
1091
1092       mp = root_rp->data_base;
1093
1094       p = hash_get_mem (mp->name_hash, name);
1095
1096       /* Better never happen ... */
1097       if (p == NULL)
1098         {
1099           region_unlock (rp);
1100           region_unlock (root_rp);
1101           svm_pop_heap (oldheap);
1102           clib_warning ("Region name '%s' not found?", name);
1103           return;
1104         }
1105
1106       /* Remove from the root region subregion pool */
1107       subp = mp->subregions + p[0];
1108       pool_put (mp->subregions, subp);
1109
1110       hash_unset_mem (mp->name_hash, name);
1111
1112       vec_free (name);
1113
1114       region_unlock (rp);
1115
1116       /* If a client asks for the cleanup, don't unlink the backing
1117        * file since we can't tell if it has been recreated. */
1118       if (!is_client)
1119         svm_region_unlink (rp);
1120
1121       munmap ((void *) virtual_base, virtual_size);
1122       region_unlock (root_rp);
1123       svm_pop_heap (oldheap);
1124       return;
1125     }
1126
1127   region_unlock (rp);
1128   region_unlock (root_rp);
1129
1130   munmap ((void *) virtual_base, virtual_size);
1131 }
1132
1133 void
1134 svm_region_unmap (void *rp_arg)
1135 {
1136   svm_region_unmap_internal (rp_arg, 0 /* is_client */ );
1137 }
1138
1139 void
1140 svm_region_unmap_client (void *rp_arg)
1141 {
1142   svm_region_unmap_internal (rp_arg, 1 /* is_client */ );
1143 }
1144
1145 /*
1146  * svm_region_exit
1147  */
1148 static void
1149 svm_region_exit_internal (u8 is_client)
1150 {
1151   void *oldheap;
1152   int i, mypid = getpid ();
1153   uword virtual_base, virtual_size;
1154
1155   /* It felt so nice we did it twice... */
1156   if (root_rp == 0)
1157     return;
1158
1159   if (--root_rp_refcount > 0)
1160     return;
1161
1162   /*
1163    * If we take a signal while holding one or more shared-memory
1164    * mutexes, we may end up back here from an otherwise
1165    * benign exit handler. Bail out to avoid a recursive
1166    * mutex screw-up.
1167    */
1168   if (nheld)
1169     return;
1170
1171   region_lock (root_rp, 7);
1172   oldheap = svm_push_pvt_heap (root_rp);
1173
1174   virtual_base = root_rp->virtual_base;
1175   virtual_size = root_rp->virtual_size;
1176
1177   for (i = 0; i < vec_len (root_rp->client_pids); i++)
1178     {
1179       if (root_rp->client_pids[i] == mypid)
1180         {
1181           vec_delete (root_rp->client_pids, 1, i);
1182           goto found;
1183         }
1184     }
1185   clib_warning ("pid %d AWOL", mypid);
1186
1187 found:
1188
1189   if (!is_client && vec_len (root_rp->client_pids) == 0)
1190     svm_region_unlink (root_rp);
1191
1192   region_unlock (root_rp);
1193   svm_pop_heap (oldheap);
1194
1195   root_rp = 0;
1196   munmap ((void *) virtual_base, virtual_size);
1197 }
1198
1199 void
1200 svm_region_exit (void)
1201 {
1202   svm_region_exit_internal (0 /* is_client */ );
1203 }
1204
1205 void
1206 svm_region_exit_client (void)
1207 {
1208   svm_region_exit_internal (1 /* is_client */ );
1209 }
1210
1211 void
1212 svm_client_scan_this_region_nolock (svm_region_t * rp)
1213 {
1214   int j;
1215   int mypid = getpid ();
1216   void *oldheap;
1217
1218   for (j = 0; j < vec_len (rp->client_pids); j++)
1219     {
1220       if (mypid == rp->client_pids[j])
1221         continue;
1222       if (rp->client_pids[j] && (kill (rp->client_pids[j], 0) < 0))
1223         {
1224           clib_warning ("%s: cleanup ghost pid %d",
1225                         rp->region_name, rp->client_pids[j]);
1226           /* nb: client vec in rp->region_heap */
1227           oldheap = svm_push_pvt_heap (rp);
1228           vec_delete (rp->client_pids, 1, j);
1229           j--;
1230           svm_pop_heap (oldheap);
1231         }
1232     }
1233 }
1234
1235
1236 /*
1237  * Scan svm regions for dead clients
1238  */
1239 void
1240 svm_client_scan (const char *root_path)
1241 {
1242   int i, j;
1243   svm_main_region_t *mp;
1244   svm_map_region_args_t *a = 0;
1245   svm_region_t *root_rp;
1246   svm_region_t *rp;
1247   svm_subregion_t *subp;
1248   u8 *name = 0;
1249   u8 **svm_names = 0;
1250   void *oldheap;
1251   int mypid = getpid ();
1252
1253   vec_validate (a, 0);
1254
1255   svm_region_init_chroot (root_path);
1256
1257   root_rp = svm_get_root_rp ();
1258
1259   pthread_mutex_lock (&root_rp->mutex);
1260
1261   mp = root_rp->data_base;
1262
1263   for (j = 0; j < vec_len (root_rp->client_pids); j++)
1264     {
1265       if (mypid == root_rp->client_pids[j])
1266         continue;
1267       if (root_rp->client_pids[j] && (kill (root_rp->client_pids[j], 0) < 0))
1268         {
1269           clib_warning ("%s: cleanup ghost pid %d",
1270                         root_rp->region_name, root_rp->client_pids[j]);
1271           /* nb: client vec in root_rp->region_heap */
1272           oldheap = svm_push_pvt_heap (root_rp);
1273           vec_delete (root_rp->client_pids, 1, j);
1274           j--;
1275           svm_pop_heap (oldheap);
1276         }
1277     }
1278
1279   /*
1280    * Snapshoot names, can't hold root rp mutex across
1281    * find_or_create.
1282    */
1283   /* *INDENT-OFF* */
1284   pool_foreach (subp, mp->subregions, ({
1285         name = vec_dup (subp->subregion_name);
1286         vec_add1(svm_names, name);
1287       }));
1288   /* *INDENT-ON* */
1289
1290   pthread_mutex_unlock (&root_rp->mutex);
1291
1292   for (i = 0; i < vec_len (svm_names); i++)
1293     {
1294       vec_validate (a, 0);
1295       a->root_path = root_path;
1296       a->name = (char *) svm_names[i];
1297       rp = svm_region_find_or_create (a);
1298       if (rp)
1299         {
1300           pthread_mutex_lock (&rp->mutex);
1301
1302           svm_client_scan_this_region_nolock (rp);
1303
1304           pthread_mutex_unlock (&rp->mutex);
1305           svm_region_unmap (rp);
1306           vec_free (svm_names[i]);
1307         }
1308       vec_free (a);
1309     }
1310   vec_free (svm_names);
1311
1312   svm_region_exit ();
1313
1314   vec_free (a);
1315 }
1316
1317 /*
1318  * fd.io coding-style-patch-verification: ON
1319  *
1320  * Local Variables:
1321  * eval: (c-set-style "gnu")
1322  * End:
1323  */