svm: asan: fix asan support
[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       CLIB_MEM_UNPOISON (rp->data_base, map_size);
333       rp->backing_file = (char *) format (0, "%s\0", a->backing_file);
334       rp->flags |= SVM_FLAGS_FILE;
335     }
336
337   if (a->flags & SVM_FLAGS_MHEAP)
338     {
339       rp->data_heap = create_mspace_with_base (rp->data_base,
340                                                map_size, 1 /* locked */ );
341       mspace_disable_expand (rp->data_heap);
342
343       rp->flags |= SVM_FLAGS_MHEAP;
344     }
345   return 0;
346 }
347
348 static int
349 svm_data_region_map (svm_map_region_args_t * a, svm_region_t * rp)
350 {
351   int fd;
352   u8 junk = 0;
353   uword map_size;
354   struct stat statb;
355
356   map_size = rp->virtual_size -
357     (MMAP_PAGESIZE
358      + (a->pvt_heap_size ? a->pvt_heap_size : SVM_PVT_MHEAP_SIZE));
359
360   if (a->flags & SVM_FLAGS_FILE)
361     {
362
363       fd = open (a->backing_file, O_RDWR, 0777);
364
365       if (fd < 0)
366         {
367           clib_unix_warning ("open");
368           return -1;
369         }
370
371       if (fstat (fd, &statb) < 0)
372         {
373           clib_unix_warning ("fstat");
374           close (fd);
375           return -2;
376         }
377
378       if (statb.st_mode & S_IFREG)
379         {
380           if (statb.st_size == 0)
381             {
382               if (lseek (fd, map_size, SEEK_SET) == (off_t) - 1)
383                 {
384                   clib_unix_warning ("seek region size");
385                   close (fd);
386                   return -3;
387                 }
388               if (write (fd, &junk, 1) != 1)
389                 {
390                   clib_unix_warning ("set region size");
391                   close (fd);
392                   return -3;
393                 }
394             }
395           else
396             {
397               map_size = rnd_pagesize (statb.st_size);
398             }
399         }
400       else
401         {
402           map_size = a->backing_mmap_size;
403         }
404
405       ASSERT (map_size <= rp->virtual_size
406               - (MMAP_PAGESIZE
407                  +
408                  (a->pvt_heap_size ? a->pvt_heap_size : SVM_PVT_MHEAP_SIZE)));
409
410       if (mmap (rp->data_base, map_size, PROT_READ | PROT_WRITE,
411                 MAP_SHARED | MAP_FIXED, fd, 0) == MAP_FAILED)
412         {
413           clib_unix_warning ("mmap");
414           close (fd);
415           return -3;
416         }
417       close (fd);
418       CLIB_MEM_UNPOISON (rp->data_base, map_size);
419     }
420   return 0;
421 }
422
423 u8 *
424 shm_name_from_svm_map_region_args (svm_map_region_args_t * a)
425 {
426   u8 *shm_name;
427   int root_path_offset = 0;
428   int name_offset = 0;
429
430   if (a->root_path)
431     {
432       /* Tolerate present or absent slashes */
433       if (a->root_path[0] == '/')
434         root_path_offset++;
435
436       if (a->name[0] == '/')
437         name_offset = 1;
438
439       shm_name = format (0, "/%s-%s%c", &a->root_path[root_path_offset],
440                          &a->name[name_offset], 0);
441     }
442   else
443     shm_name = format (0, "%s%c", a->name, 0);
444   return (shm_name);
445 }
446
447 void
448 svm_region_init_mapped_region (svm_map_region_args_t * a, svm_region_t * rp)
449 {
450   pthread_mutexattr_t attr;
451   pthread_condattr_t cattr;
452   int nbits, words, bit;
453   int overhead_space;
454   void *oldheap;
455   uword data_base;
456   ASSERT (rp);
457   int rv;
458
459   clib_memset (rp, 0, sizeof (*rp));
460
461   if (pthread_mutexattr_init (&attr))
462     clib_unix_warning ("mutexattr_init");
463
464   if (pthread_mutexattr_setpshared (&attr, PTHREAD_PROCESS_SHARED))
465     clib_unix_warning ("mutexattr_setpshared");
466
467   if (pthread_mutex_init (&rp->mutex, &attr))
468     clib_unix_warning ("mutex_init");
469
470   if (pthread_mutexattr_destroy (&attr))
471     clib_unix_warning ("mutexattr_destroy");
472
473   if (pthread_condattr_init (&cattr))
474     clib_unix_warning ("condattr_init");
475
476   if (pthread_condattr_setpshared (&cattr, PTHREAD_PROCESS_SHARED))
477     clib_unix_warning ("condattr_setpshared");
478
479   if (pthread_cond_init (&rp->condvar, &cattr))
480     clib_unix_warning ("cond_init");
481
482   if (pthread_condattr_destroy (&cattr))
483     clib_unix_warning ("condattr_destroy");
484
485   region_lock (rp, 1);
486
487   rp->virtual_base = a->baseva;
488   rp->virtual_size = a->size;
489
490   rp->region_heap = create_mspace_with_base
491     (uword_to_pointer (a->baseva + MMAP_PAGESIZE, void *),
492      (a->pvt_heap_size !=
493       0) ? a->pvt_heap_size : SVM_PVT_MHEAP_SIZE, 1 /* locked */ );
494
495   mspace_disable_expand (rp->region_heap);
496
497   oldheap = svm_push_pvt_heap (rp);
498
499   rp->region_name = (char *) format (0, "%s%c", a->name, 0);
500   vec_add1 (rp->client_pids, getpid ());
501
502   nbits = rp->virtual_size / MMAP_PAGESIZE;
503
504   ASSERT (nbits > 0);
505   rp->bitmap_size = nbits;
506   words = (nbits + BITS (uword) - 1) / BITS (uword);
507   vec_validate (rp->bitmap, words - 1);
508
509   overhead_space = MMAP_PAGESIZE /* header */  +
510     ((a->pvt_heap_size != 0) ? a->pvt_heap_size : SVM_PVT_MHEAP_SIZE);
511
512   bit = 0;
513   data_base = (uword) rp->virtual_base;
514
515   if (a->flags & SVM_FLAGS_NODATA)
516     rp->flags |= SVM_FLAGS_NEED_DATA_INIT;
517
518   do
519     {
520       clib_bitmap_set_no_check (rp->bitmap, bit, 1);
521       bit++;
522       overhead_space -= MMAP_PAGESIZE;
523       data_base += MMAP_PAGESIZE;
524     }
525   while (overhead_space > 0);
526
527   rp->data_base = (void *) data_base;
528
529   /*
530    * Note: although the POSIX spec guarantees that only one
531    * process enters this block, we have to play games
532    * to hold off clients until e.g. the mutex is ready
533    */
534   rp->version = SVM_VERSION;
535
536   /* setup the data portion of the region */
537
538   rv = svm_data_region_create (a, rp);
539   if (rv)
540     {
541       clib_warning ("data_region_create: %d", rv);
542     }
543
544   region_unlock (rp);
545
546   svm_pop_heap (oldheap);
547 }
548
549 /*
550  * svm_map_region
551  */
552 void *
553 svm_map_region (svm_map_region_args_t * a)
554 {
555   int svm_fd;
556   svm_region_t *rp;
557   int deadman = 0;
558   u8 junk = 0;
559   void *oldheap;
560   int rv;
561   int pid_holding_region_lock;
562   u8 *shm_name;
563   int dead_region_recovery = 0;
564   int time_left;
565   struct stat stat;
566   struct timespec ts, tsrem;
567
568   ASSERT ((a->size & ~(MMAP_PAGESIZE - 1)) == a->size);
569   ASSERT (a->name);
570
571   shm_name = shm_name_from_svm_map_region_args (a);
572
573   if (CLIB_DEBUG > 1)
574     clib_warning ("[%d] map region %s: shm_open (%s)",
575                   getpid (), a->name, shm_name);
576
577   svm_fd = shm_open ((char *) shm_name, O_RDWR | O_CREAT | O_EXCL, 0777);
578
579   if (svm_fd >= 0)
580     {
581       if (fchmod (svm_fd, S_IRUSR | S_IWUSR | S_IRGRP | S_IWGRP) < 0)
582         clib_unix_warning ("segment chmod");
583       /* This turns out to fail harmlessly if the client starts first */
584       if (fchown (svm_fd, a->uid, a->gid) < 0)
585         clib_unix_warning ("segment chown [ok if client starts first]");
586
587       vec_free (shm_name);
588
589       if (lseek (svm_fd, a->size, SEEK_SET) == (off_t) - 1)
590         {
591           clib_warning ("seek region size");
592           close (svm_fd);
593           return (0);
594         }
595       if (write (svm_fd, &junk, 1) != 1)
596         {
597           clib_warning ("set region size");
598           close (svm_fd);
599           return (0);
600         }
601
602       rp = mmap (uword_to_pointer (a->baseva, void *), a->size,
603                  PROT_READ | PROT_WRITE, MAP_SHARED | MAP_FIXED, svm_fd, 0);
604
605       if (rp == (svm_region_t *) MAP_FAILED)
606         {
607           clib_unix_warning ("mmap create");
608           close (svm_fd);
609           return (0);
610         }
611       close (svm_fd);
612       CLIB_MEM_UNPOISON (rp, a->size);
613
614       svm_region_init_mapped_region (a, rp);
615
616       return ((void *) rp);
617     }
618   else
619     {
620       svm_fd = shm_open ((char *) shm_name, O_RDWR, 0777);
621
622       vec_free (shm_name);
623
624       if (svm_fd < 0)
625         {
626           perror ("svm_region_map(mmap open)");
627           return (0);
628         }
629
630       /* Reset ownership in case the client started first */
631       if (fchown (svm_fd, a->uid, a->gid) < 0)
632         clib_unix_warning ("segment chown [ok if client starts first]");
633
634       time_left = 20;
635       while (1)
636         {
637           if (0 != fstat (svm_fd, &stat))
638             {
639               clib_warning ("fstat failed: %d", errno);
640               close (svm_fd);
641               return (0);
642             }
643           if (stat.st_size > 0)
644             {
645               break;
646             }
647           if (0 == time_left)
648             {
649               clib_warning ("waiting for resize of shm file timed out");
650               close (svm_fd);
651               return (0);
652             }
653           ts.tv_sec = 0;
654           ts.tv_nsec = 100000000;
655           while (nanosleep (&ts, &tsrem) < 0)
656             ts = tsrem;
657           time_left--;
658         }
659
660       rp = mmap (0, MMAP_PAGESIZE,
661                  PROT_READ | PROT_WRITE, MAP_SHARED, svm_fd, 0);
662
663       if (rp == (svm_region_t *) MAP_FAILED)
664         {
665           close (svm_fd);
666           clib_warning ("mmap");
667           return (0);
668         }
669
670       CLIB_MEM_UNPOISON (rp, MMAP_PAGESIZE);
671
672       /*
673        * We lost the footrace to create this region; make sure
674        * the winner has crossed the finish line.
675        */
676       while (rp->version == 0 && deadman++ < 5)
677         {
678           sleep (1);
679         }
680
681       /*
682        * <bleep>-ed?
683        */
684       if (rp->version == 0)
685         {
686           clib_warning ("rp->version %d not %d", rp->version, SVM_VERSION);
687           close (svm_fd);
688           munmap (rp, a->size);
689           return (0);
690         }
691       /* Remap now that the region has been placed */
692       a->baseva = rp->virtual_base;
693       a->size = rp->virtual_size;
694       munmap (rp, MMAP_PAGESIZE);
695
696       rp = (void *) mmap (uword_to_pointer (a->baseva, void *), a->size,
697                           PROT_READ | PROT_WRITE,
698                           MAP_SHARED | MAP_FIXED, svm_fd, 0);
699       if ((uword) rp == (uword) MAP_FAILED)
700         {
701           clib_unix_warning ("mmap");
702           close (svm_fd);
703           return (0);
704         }
705
706       close (svm_fd);
707
708       CLIB_MEM_UNPOISON (rp, a->size);
709
710       if ((uword) rp != rp->virtual_base)
711         {
712           clib_warning ("mmap botch");
713         }
714
715       /*
716        * Try to fix the region mutex if it is held by
717        * a dead process
718        */
719       pid_holding_region_lock = rp->mutex_owner_pid;
720       if (pid_holding_region_lock && kill (pid_holding_region_lock, 0) < 0)
721         {
722           clib_warning
723             ("region %s mutex held by dead pid %d, tag %d, force unlock",
724              rp->region_name, pid_holding_region_lock, rp->mutex_owner_tag);
725           /* owner pid is nonexistent */
726           rp->mutex.__data.__owner = 0;
727           rp->mutex.__data.__lock = 0;
728           dead_region_recovery = 1;
729         }
730
731       if (dead_region_recovery)
732         clib_warning ("recovery: attempt to re-lock region");
733
734       region_lock (rp, 2);
735       oldheap = svm_push_pvt_heap (rp);
736       vec_add1 (rp->client_pids, getpid ());
737
738       if (dead_region_recovery)
739         clib_warning ("recovery: attempt svm_data_region_map");
740
741       rv = svm_data_region_map (a, rp);
742       if (rv)
743         {
744           clib_warning ("data_region_map: %d", rv);
745         }
746
747       if (dead_region_recovery)
748         clib_warning ("unlock and continue");
749
750       region_unlock (rp);
751
752       svm_pop_heap (oldheap);
753
754       return ((void *) rp);
755
756     }
757   return 0;                     /* NOTREACHED *///NOSONAR
758 }
759
760 static void
761 svm_mutex_cleanup (void)
762 {
763   int i;
764   for (i = 0; i < nheld; i++)
765     {
766       pthread_mutex_unlock (mutexes_held[i]);   //NOSONAR
767     }
768 }
769
770 static int
771 svm_region_init_internal (svm_map_region_args_t * a)
772 {
773   svm_region_t *rp;
774   u64 ticks = clib_cpu_time_now ();
775   uword randomize_baseva;
776
777   /* guard against klutz calls */
778   if (root_rp)
779     return -1;
780
781   root_rp_refcount++;
782
783   atexit (svm_mutex_cleanup);
784
785   /* Randomize the shared-VM base at init time */
786   if (MMAP_PAGESIZE <= (4 << 10))
787     randomize_baseva = (ticks & 15) * MMAP_PAGESIZE;
788   else
789     randomize_baseva = (ticks & 3) * MMAP_PAGESIZE;
790
791   a->baseva += randomize_baseva;
792
793   rp = svm_map_region (a);
794   if (!rp)
795     return -1;
796
797   region_lock (rp, 3);
798
799   /* Set up the main region data structures */
800   if (rp->flags & SVM_FLAGS_NEED_DATA_INIT)
801     {
802       svm_main_region_t *mp = 0;
803       void *oldheap;
804
805       rp->flags &= ~(SVM_FLAGS_NEED_DATA_INIT);
806
807       oldheap = svm_push_pvt_heap (rp);
808       vec_validate (mp, 0);
809       mp->name_hash = hash_create_string (0, sizeof (uword));
810       mp->root_path = a->root_path ? format (0, "%s%c", a->root_path, 0) : 0;
811       mp->uid = a->uid;
812       mp->gid = a->gid;
813       rp->data_base = mp;
814       svm_pop_heap (oldheap);
815     }
816   region_unlock (rp);
817   root_rp = rp;
818
819   return 0;
820 }
821
822 void
823 svm_region_init (void)
824 {
825   svm_map_region_args_t _a, *a = &_a;
826
827   clib_memset (a, 0, sizeof (*a));
828   a->root_path = 0;
829   a->name = SVM_GLOBAL_REGION_NAME;
830   a->baseva = svm_get_global_region_base_va ();
831   a->size = SVM_GLOBAL_REGION_SIZE;
832   a->flags = SVM_FLAGS_NODATA;
833   a->uid = 0;
834   a->gid = 0;
835
836   svm_region_init_internal (a);
837 }
838
839 int
840 svm_region_init_chroot (const char *root_path)
841 {
842   svm_map_region_args_t _a, *a = &_a;
843
844   clib_memset (a, 0, sizeof (*a));
845   a->root_path = root_path;
846   a->name = SVM_GLOBAL_REGION_NAME;
847   a->baseva = svm_get_global_region_base_va ();
848   a->size = SVM_GLOBAL_REGION_SIZE;
849   a->flags = SVM_FLAGS_NODATA;
850   a->uid = 0;
851   a->gid = 0;
852
853   return svm_region_init_internal (a);
854 }
855
856 void
857 svm_region_init_chroot_uid_gid (const char *root_path, int uid, int gid)
858 {
859   svm_map_region_args_t _a, *a = &_a;
860
861   clib_memset (a, 0, sizeof (*a));
862   a->root_path = root_path;
863   a->name = SVM_GLOBAL_REGION_NAME;
864   a->baseva = svm_get_global_region_base_va ();
865   a->size = SVM_GLOBAL_REGION_SIZE;
866   a->flags = SVM_FLAGS_NODATA;
867   a->uid = uid;
868   a->gid = gid;
869
870   svm_region_init_internal (a);
871 }
872
873 void
874 svm_region_init_args (svm_map_region_args_t * a)
875 {
876   svm_region_init_internal (a);
877 }
878
879 void *
880 svm_region_find_or_create (svm_map_region_args_t * a)
881 {
882   svm_main_region_t *mp;
883   svm_region_t *rp;
884   uword need_nbits;
885   int index, i;
886   void *oldheap;
887   uword *p;
888   u8 *name;
889   svm_subregion_t *subp;
890
891   ASSERT (root_rp);
892
893   a->size += MMAP_PAGESIZE +
894     ((a->pvt_heap_size != 0) ? a->pvt_heap_size : SVM_PVT_MHEAP_SIZE);
895   a->size = rnd_pagesize (a->size);
896
897   region_lock (root_rp, 4);
898   oldheap = svm_push_pvt_heap (root_rp);
899   mp = root_rp->data_base;
900
901   ASSERT (mp);
902
903   /* Map the named region from the correct chroot environment */
904   if (a->root_path == NULL)
905     a->root_path = (char *) mp->root_path;
906
907   /*
908    * See if this region is already known. If it is, we're
909    * almost done...
910    */
911   p = hash_get_mem (mp->name_hash, a->name);
912
913   if (p)
914     {
915       rp = svm_map_region (a);
916       region_unlock (root_rp);
917       svm_pop_heap (oldheap);
918       return rp;
919     }
920
921   /* Create the region. */
922   ASSERT ((a->size & ~(MMAP_PAGESIZE - 1)) == a->size);
923
924   need_nbits = a->size / MMAP_PAGESIZE;
925
926   index = 1;                    /* $$$ fixme, figure out how many bit to really skip */
927
928   /*
929    * Scan the virtual space allocation bitmap, looking for a large
930    * enough chunk
931    */
932   do
933     {
934       if (clib_bitmap_get_no_check (root_rp->bitmap, index) == 0)
935         {
936           for (i = 0; i < (need_nbits - 1); i++)
937             {
938               if (clib_bitmap_get_no_check (root_rp->bitmap, index + i) == 1)
939                 {
940                   index = index + i;
941                   goto next;
942                 }
943             }
944           break;
945         }
946       index++;
947     next:;
948     }
949   while (index < root_rp->bitmap_size);
950
951   /* Completely out of VM? */
952   if (index >= root_rp->bitmap_size)
953     {
954       clib_warning ("region %s: not enough VM to allocate 0x%llx (%lld)",
955                     root_rp->region_name, a->size, a->size);
956       svm_pop_heap (oldheap);
957       region_unlock (root_rp);
958       return 0;
959     }
960
961   /*
962    * Mark virtual space allocated
963    */
964 #if CLIB_DEBUG > 1
965   clib_warning ("set %d bits at index %d", need_nbits, index);
966 #endif
967
968   for (i = 0; i < need_nbits; i++)
969     {
970       clib_bitmap_set_no_check (root_rp->bitmap, index + i, 1);
971     }
972
973   /* Place this region where it goes... */
974   a->baseva = root_rp->virtual_base + index * MMAP_PAGESIZE;
975
976   rp = svm_map_region (a);
977
978   pool_get (mp->subregions, subp);
979   name = format (0, "%s%c", a->name, 0);
980   subp->subregion_name = name;
981
982   hash_set_mem (mp->name_hash, name, subp - mp->subregions);
983
984   svm_pop_heap (oldheap);
985
986   region_unlock (root_rp);
987
988   return (rp);
989 }
990
991 void
992 svm_region_unlink (svm_region_t * rp)
993 {
994   svm_map_region_args_t _a, *a = &_a;
995   svm_main_region_t *mp;
996   u8 *shm_name;
997
998   ASSERT (root_rp);
999   ASSERT (rp);
1000   ASSERT (vec_c_string_is_terminated (rp->region_name));
1001
1002   mp = root_rp->data_base;
1003   ASSERT (mp);
1004
1005   a->root_path = (char *) mp->root_path;
1006   a->name = rp->region_name;
1007   shm_name = shm_name_from_svm_map_region_args (a);
1008   if (CLIB_DEBUG > 1)
1009     clib_warning ("[%d] shm_unlink (%s)", getpid (), shm_name);
1010   shm_unlink ((const char *) shm_name);
1011   vec_free (shm_name);
1012 }
1013
1014 /*
1015  * svm_region_unmap
1016  *
1017  * Let go of the indicated region. If the calling process
1018  * is the last customer, throw it away completely.
1019  * The root region mutex guarantees atomicity with respect to
1020  * a new region client showing up at the wrong moment.
1021  */
1022 void
1023 svm_region_unmap_internal (void *rp_arg, u8 is_client)
1024 {
1025   int i, mypid = getpid ();
1026   int nclients_left;
1027   void *oldheap;
1028   uword virtual_base, virtual_size;
1029   svm_region_t *rp = rp_arg;
1030   char *name;
1031
1032   /*
1033    * If we take a signal while holding one or more shared-memory
1034    * mutexes, we may end up back here from an otherwise
1035    * benign exit handler. Bail out to avoid a recursive
1036    * mutex screw-up.
1037    */
1038   if (nheld)
1039     return;
1040
1041   ASSERT (rp);
1042   ASSERT (root_rp);
1043
1044   if (CLIB_DEBUG > 1)
1045     clib_warning ("[%d] unmap region %s", getpid (), rp->region_name);
1046
1047   region_lock (root_rp, 5);
1048   region_lock (rp, 6);
1049
1050   oldheap = svm_push_pvt_heap (rp);     /* nb vec_delete() in the loop */
1051
1052   /* Remove the caller from the list of mappers */
1053   CLIB_MEM_UNPOISON (rp->client_pids, vec_bytes (rp->client_pids));
1054   for (i = 0; i < vec_len (rp->client_pids); i++)
1055     {
1056       if (rp->client_pids[i] == mypid)
1057         {
1058           vec_delete (rp->client_pids, 1, i);
1059           goto found;
1060         }
1061     }
1062   clib_warning ("pid %d AWOL", mypid);
1063
1064 found:
1065
1066   svm_pop_heap (oldheap);
1067
1068   nclients_left = vec_len (rp->client_pids);
1069   virtual_base = rp->virtual_base;
1070   virtual_size = rp->virtual_size;
1071
1072   if (nclients_left == 0)
1073     {
1074       int index, nbits, i;
1075       svm_main_region_t *mp;
1076       uword *p;
1077       svm_subregion_t *subp;
1078
1079       /* Kill the region, last guy on his way out */
1080
1081       oldheap = svm_push_pvt_heap (root_rp);
1082       name = vec_dup (rp->region_name);
1083
1084       virtual_base = rp->virtual_base;
1085       virtual_size = rp->virtual_size;
1086
1087       /* Figure out which bits to clear in the root region bitmap */
1088       index = (virtual_base - root_rp->virtual_base) / MMAP_PAGESIZE;
1089
1090       nbits = (virtual_size + MMAP_PAGESIZE - 1) / MMAP_PAGESIZE;
1091
1092 #if CLIB_DEBUG > 1
1093       clib_warning ("clear %d bits at index %d", nbits, index);
1094 #endif
1095       /* Give back the allocated VM */
1096       for (i = 0; i < nbits; i++)
1097         {
1098           clib_bitmap_set_no_check (root_rp->bitmap, index + i, 0);
1099         }
1100
1101       mp = root_rp->data_base;
1102
1103       p = hash_get_mem (mp->name_hash, name);
1104
1105       /* Better never happen ... */
1106       if (p == NULL)
1107         {
1108           region_unlock (rp);
1109           region_unlock (root_rp);
1110           svm_pop_heap (oldheap);
1111           clib_warning ("Region name '%s' not found?", name);
1112           return;
1113         }
1114
1115       /* Remove from the root region subregion pool */
1116       subp = mp->subregions + p[0];
1117       pool_put (mp->subregions, subp);
1118
1119       hash_unset_mem (mp->name_hash, name);
1120
1121       vec_free (name);
1122
1123       region_unlock (rp);
1124
1125       /* If a client asks for the cleanup, don't unlink the backing
1126        * file since we can't tell if it has been recreated. */
1127       if (!is_client)
1128         svm_region_unlink (rp);
1129
1130       munmap ((void *) virtual_base, virtual_size);
1131       region_unlock (root_rp);
1132       svm_pop_heap (oldheap);
1133       return;
1134     }
1135
1136   region_unlock (rp);
1137   region_unlock (root_rp);
1138
1139   munmap ((void *) virtual_base, virtual_size);
1140 }
1141
1142 void
1143 svm_region_unmap (void *rp_arg)
1144 {
1145   svm_region_unmap_internal (rp_arg, 0 /* is_client */ );
1146 }
1147
1148 void
1149 svm_region_unmap_client (void *rp_arg)
1150 {
1151   svm_region_unmap_internal (rp_arg, 1 /* is_client */ );
1152 }
1153
1154 /*
1155  * svm_region_exit
1156  */
1157 static void
1158 svm_region_exit_internal (u8 is_client)
1159 {
1160   void *oldheap;
1161   int i, mypid = getpid ();
1162   uword virtual_base, virtual_size;
1163
1164   /* It felt so nice we did it twice... */
1165   if (root_rp == 0)
1166     return;
1167
1168   if (--root_rp_refcount > 0)
1169     return;
1170
1171   /*
1172    * If we take a signal while holding one or more shared-memory
1173    * mutexes, we may end up back here from an otherwise
1174    * benign exit handler. Bail out to avoid a recursive
1175    * mutex screw-up.
1176    */
1177   if (nheld)
1178     return;
1179
1180   region_lock (root_rp, 7);
1181   oldheap = svm_push_pvt_heap (root_rp);
1182
1183   virtual_base = root_rp->virtual_base;
1184   virtual_size = root_rp->virtual_size;
1185
1186   CLIB_MEM_UNPOISON (root_rp->client_pids, vec_bytes (root_rp->client_pids));
1187   for (i = 0; i < vec_len (root_rp->client_pids); i++)
1188     {
1189       if (root_rp->client_pids[i] == mypid)
1190         {
1191           vec_delete (root_rp->client_pids, 1, i);
1192           goto found;
1193         }
1194     }
1195   clib_warning ("pid %d AWOL", mypid);
1196
1197 found:
1198
1199   if (!is_client && vec_len (root_rp->client_pids) == 0)
1200     svm_region_unlink (root_rp);
1201
1202   region_unlock (root_rp);
1203   svm_pop_heap (oldheap);
1204
1205   root_rp = 0;
1206   munmap ((void *) virtual_base, virtual_size);
1207 }
1208
1209 void
1210 svm_region_exit (void)
1211 {
1212   svm_region_exit_internal (0 /* is_client */ );
1213 }
1214
1215 void
1216 svm_region_exit_client (void)
1217 {
1218   svm_region_exit_internal (1 /* is_client */ );
1219 }
1220
1221 void
1222 svm_client_scan_this_region_nolock (svm_region_t * rp)
1223 {
1224   int j;
1225   int mypid = getpid ();
1226   void *oldheap;
1227
1228   for (j = 0; j < vec_len (rp->client_pids); j++)
1229     {
1230       if (mypid == rp->client_pids[j])
1231         continue;
1232       if (rp->client_pids[j] && (kill (rp->client_pids[j], 0) < 0))
1233         {
1234           clib_warning ("%s: cleanup ghost pid %d",
1235                         rp->region_name, rp->client_pids[j]);
1236           /* nb: client vec in rp->region_heap */
1237           oldheap = svm_push_pvt_heap (rp);
1238           vec_delete (rp->client_pids, 1, j);
1239           j--;
1240           svm_pop_heap (oldheap);
1241         }
1242     }
1243 }
1244
1245
1246 /*
1247  * Scan svm regions for dead clients
1248  */
1249 void
1250 svm_client_scan (const char *root_path)
1251 {
1252   int i, j;
1253   svm_main_region_t *mp;
1254   svm_map_region_args_t *a = 0;
1255   svm_region_t *root_rp;
1256   svm_region_t *rp;
1257   svm_subregion_t *subp;
1258   u8 *name = 0;
1259   u8 **svm_names = 0;
1260   void *oldheap;
1261   int mypid = getpid ();
1262
1263   vec_validate (a, 0);
1264
1265   svm_region_init_chroot (root_path);
1266
1267   root_rp = svm_get_root_rp ();
1268
1269   pthread_mutex_lock (&root_rp->mutex);
1270
1271   mp = root_rp->data_base;
1272
1273   for (j = 0; j < vec_len (root_rp->client_pids); j++)
1274     {
1275       if (mypid == root_rp->client_pids[j])
1276         continue;
1277       if (root_rp->client_pids[j] && (kill (root_rp->client_pids[j], 0) < 0))
1278         {
1279           clib_warning ("%s: cleanup ghost pid %d",
1280                         root_rp->region_name, root_rp->client_pids[j]);
1281           /* nb: client vec in root_rp->region_heap */
1282           oldheap = svm_push_pvt_heap (root_rp);
1283           vec_delete (root_rp->client_pids, 1, j);
1284           j--;
1285           svm_pop_heap (oldheap);
1286         }
1287     }
1288
1289   /*
1290    * Snapshoot names, can't hold root rp mutex across
1291    * find_or_create.
1292    */
1293   /* *INDENT-OFF* */
1294   pool_foreach (subp, mp->subregions, ({
1295         name = vec_dup (subp->subregion_name);
1296         vec_add1(svm_names, name);
1297       }));
1298   /* *INDENT-ON* */
1299
1300   pthread_mutex_unlock (&root_rp->mutex);
1301
1302   for (i = 0; i < vec_len (svm_names); i++)
1303     {
1304       vec_validate (a, 0);
1305       a->root_path = root_path;
1306       a->name = (char *) svm_names[i];
1307       rp = svm_region_find_or_create (a);
1308       if (rp)
1309         {
1310           pthread_mutex_lock (&rp->mutex);
1311
1312           svm_client_scan_this_region_nolock (rp);
1313
1314           pthread_mutex_unlock (&rp->mutex);
1315           svm_region_unmap (rp);
1316           vec_free (svm_names[i]);
1317         }
1318       vec_free (a);
1319     }
1320   vec_free (svm_names);
1321
1322   svm_region_exit ();
1323
1324   vec_free (a);
1325 }
1326
1327 /*
1328  * fd.io coding-style-patch-verification: ON
1329  *
1330  * Local Variables:
1331  * eval: (c-set-style "gnu")
1332  * End:
1333  */