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