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