Initial commit of vpp code.
[vpp.git] / svm / svmtool.c
1 /* 
2  *------------------------------------------------------------------
3  * svmtool.c 
4  *
5  * Copyright (c) 2009 Cisco and/or its affiliates.
6  * Licensed under the Apache License, Version 2.0 (the "License");
7  * you may not use this file except in compliance with the License.
8  * You may obtain a copy of the License at:
9  *
10  *     http://www.apache.org/licenses/LICENSE-2.0
11  *
12  * Unless required by applicable law or agreed to in writing, software
13  * distributed under the License is distributed on an "AS IS" BASIS,
14  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
15  * See the License for the specific language governing permissions and
16  * limitations under the License.
17  *------------------------------------------------------------------
18  */
19
20 #include <stdio.h>
21 #include <stdlib.h>
22 #include <sys/types.h>
23 #include <sys/mman.h>
24 #include <sys/stat.h>
25 #include <netinet/in.h>
26 #include <signal.h>
27 #include <pthread.h>
28 #include <unistd.h>
29 #include <time.h>
30 #include <fcntl.h>
31 #include <string.h>
32 #include <vppinfra/clib.h>
33 #include <vppinfra/vec.h>
34 #include <vppinfra/hash.h>
35 #include <vppinfra/bitmap.h>
36 #include <vppinfra/fifo.h>
37 #include <vppinfra/time.h>
38 #include <vppinfra/mheap.h>
39 #include <vppinfra/heap.h>
40 #include <vppinfra/pool.h>
41 #include <vppinfra/format.h>
42
43 #include "svm.h"
44
45
46
47 /*
48  * format_all_svm_regions
49  * Maps / unmaps regions. Do NOT call from client code!
50  */
51 u8 *format_all_svm_regions (u8 *s, va_list * args)
52 {
53     int verbose = va_arg (*args, int);
54     svm_region_t *root_rp = svm_get_root_rp();
55     svm_main_region_t *mp;
56     svm_subregion_t *subp;
57     svm_region_t *rp;
58     svm_map_region_args_t *a = 0;
59     u8 ** svm_names=0;
60     u8 *name=0;
61     int i;
62
63     ASSERT(root_rp);
64
65     pthread_mutex_lock (&root_rp->mutex);
66
67     s = format (s, "%U", format_svm_region, root_rp, verbose);
68
69     mp = root_rp->data_base;
70
71     /* 
72      * Snapshoot names, can't hold root rp mutex across
73      * find_or_create.
74      */
75     pool_foreach (subp, mp->subregions, ({
76           name = vec_dup (subp->subregion_name);
77           vec_add1(svm_names, name);
78         }));
79
80     pthread_mutex_unlock (&root_rp->mutex);
81
82     for (i = 0; i < vec_len(svm_names); i++) {
83           vec_validate(a, 0);
84           a->name = (char *) svm_names[i];
85           rp = svm_region_find_or_create (a);
86           if (rp) {
87             pthread_mutex_lock (&rp->mutex);
88             s = format (s, "%U", format_svm_region, rp, verbose);
89             pthread_mutex_unlock (&rp->mutex);
90             svm_region_unmap (rp);
91             vec_free(svm_names[i]);
92           }
93           vec_free (a);
94     }
95     vec_free(svm_names);
96     return (s);
97 }
98
99 void show (char *chroot_path, int verbose)
100 {
101     svm_map_region_args_t *a = 0;
102
103     vec_validate (a, 0);
104
105     svm_region_init_chroot(chroot_path);
106
107     fformat(stdout, "My pid is %d\n", getpid());
108
109     fformat(stdout, "%U", format_all_svm_regions, verbose);
110
111     svm_region_exit ();
112
113     vec_free (a);
114 }
115
116
117 static void *svm_map_region_nolock (svm_map_region_args_t *a)
118 {
119     int svm_fd;
120     svm_region_t *rp;
121     int deadman=0;
122     u8 *shm_name;
123
124     ASSERT((a->size & ~(MMAP_PAGESIZE-1)) == a->size);
125
126     shm_name = shm_name_from_svm_map_region_args (a);
127
128     svm_fd = shm_open((char *)shm_name, O_RDWR, 0777);
129
130     if (svm_fd < 0) {
131         perror("svm_region_map(mmap open)");
132         return (0);
133     }
134     vec_free (shm_name);
135
136     rp = mmap(0, MMAP_PAGESIZE, 
137               PROT_READ | PROT_WRITE, MAP_SHARED, svm_fd, 0);
138     
139     if (rp == (svm_region_t *) MAP_FAILED) {
140         close(svm_fd);
141         clib_warning("mmap");
142         return (0);
143     }
144     /*
145      * We lost the footrace to create this region; make sure
146      * the winner has crossed the finish line.
147      */
148     while (rp->version == 0 && deadman++ < 5) {
149         sleep(1);
150     }
151
152     /*
153      * <bleep>-ed? 
154      */
155     if (rp->version == 0) {
156         clib_warning("rp->version %d not %d", rp->version,
157                      SVM_VERSION);
158         return (0);
159     } 
160     /* Remap now that the region has been placed */
161     a->baseva = rp->virtual_base;
162     a->size = rp->virtual_size;
163     munmap(rp, MMAP_PAGESIZE);
164     
165     rp = (void *) mmap ((void *)a->baseva, a->size, 
166                         PROT_READ | PROT_WRITE, 
167                         MAP_SHARED | MAP_FIXED, svm_fd, 0);
168     if ((uword)rp == (uword)MAP_FAILED) {
169         clib_unix_warning ("mmap");
170         return (0);
171     }
172     
173     if ((uword) rp != rp->virtual_base) {
174         clib_warning("mmap botch");
175     }
176     
177     if (pthread_mutex_trylock(&rp->mutex)) {
178         clib_warning ("rp->mutex LOCKED by pid %d, tag %d, cleared...",
179                       rp->mutex_owner_pid, rp->mutex_owner_tag);
180         memset(&rp->mutex, 0, sizeof (rp->mutex));
181         
182     } else {
183         clib_warning ("mutex OK...\n");
184         pthread_mutex_unlock(&rp->mutex);
185     }
186
187     return ((void *) rp);
188 }
189
190 /*
191  * rnd_pagesize
192  * Round to a pagesize multiple, presumably 4k works
193  */
194 static unsigned int rnd_pagesize(unsigned int size)
195 {
196     unsigned int rv;
197
198     rv = (size + (MMAP_PAGESIZE-1)) & ~(MMAP_PAGESIZE-1);
199     return(rv);
200 }
201
202 #define MUTEX_DEBUG
203
204 always_inline void region_lock(svm_region_t *rp, int tag)
205 {
206     pthread_mutex_lock(&rp->mutex);
207 #ifdef MUTEX_DEBUG
208     rp->mutex_owner_pid = getpid();
209     rp->mutex_owner_tag = tag;
210 #endif    
211 }
212
213 always_inline void region_unlock(svm_region_t *rp)
214 {
215 #ifdef MUTEX_DEBUG
216     rp->mutex_owner_pid = 0;
217     rp->mutex_owner_tag = 0;
218 #endif    
219     pthread_mutex_unlock(&rp->mutex);
220 }
221
222
223 static void *svm_existing_region_map_nolock (void *root_arg, 
224                                              svm_map_region_args_t *a)
225 {
226     svm_region_t *root_rp = root_arg;
227     svm_main_region_t *mp;
228     svm_region_t *rp;
229     void *oldheap;
230     uword *p;
231     
232     a->size +=  MMAP_PAGESIZE + SVM_PVT_MHEAP_SIZE;
233     a->size = rnd_pagesize(a->size);
234
235     region_lock (root_rp, 4);
236     oldheap = svm_push_pvt_heap(root_rp);
237     mp = root_rp->data_base;
238     
239     ASSERT(mp);
240
241     p = hash_get_mem (mp->name_hash, a->name);
242
243     if (p) {
244         rp = svm_map_region_nolock (a);
245         region_unlock(root_rp);
246         svm_pop_heap (oldheap);
247         return rp;
248     }
249     return 0;
250
251 }
252
253 static void trace (char *chroot_path, char *name, int enable_disable)
254 {
255     svm_map_region_args_t *a = 0;
256     svm_region_t *db_rp;
257     void *oldheap;
258
259     vec_validate (a, 0);
260
261     svm_region_init_chroot(chroot_path);
262
263     a->name = name;
264     a->size = 1<<20;
265     a->flags = SVM_FLAGS_MHEAP;
266
267     db_rp = svm_region_find_or_create (a);
268     
269     ASSERT(db_rp);
270     
271     region_lock (db_rp, 20);
272     
273     oldheap = svm_push_data_heap (db_rp);
274
275     mheap_trace (db_rp->data_heap, enable_disable);
276     
277     svm_pop_heap (oldheap);
278     region_unlock (db_rp);
279
280     svm_region_unmap ((void *)db_rp);
281     svm_region_exit ();
282     vec_free (a);
283 }
284
285
286
287 static void subregion_repair(char *chroot_path)
288 {
289     int i;
290     svm_main_region_t *mp;
291     svm_map_region_args_t a;
292     svm_region_t *root_rp;
293     svm_region_t *rp;
294     svm_subregion_t *subp;
295     u8 *name=0;
296     u8 ** svm_names=0;
297
298     svm_region_init_chroot(chroot_path);
299     root_rp = svm_get_root_rp();
300     
301     pthread_mutex_lock (&root_rp->mutex);
302     
303     mp = root_rp->data_base;
304     
305     /* 
306      * Snapshoot names, can't hold root rp mutex across
307      * find_or_create.
308      */
309     pool_foreach (subp, mp->subregions, ({
310                 name = vec_dup (subp->subregion_name);
311                 vec_add1(svm_names, name);
312             }));
313     
314     pthread_mutex_unlock (&root_rp->mutex);
315     
316     for (i = 0; i < vec_len(svm_names); i++) {
317         memset (&a, 0, sizeof (a));
318         a.root_path = chroot_path;
319         a.name = (char *) svm_names[i];
320         fformat(stdout, "Checking %s region...\n",
321                 a.name);
322         rp = svm_existing_region_map_nolock (root_rp, &a);
323         if (rp) {
324             svm_region_unmap (rp);
325             vec_free(svm_names[i]);
326         }
327     }
328     vec_free(svm_names);
329 }
330
331 void repair (char *chroot_path, int crash_root_region)
332 {
333     svm_region_t *root_rp = 0;
334     svm_map_region_args_t *a = 0;
335     void *svm_map_region (svm_map_region_args_t *a);
336     int svm_fd;
337     u8 *shm_name;
338
339     fformat(stdout, "our pid: %d\n", getpid());
340
341     vec_validate (a, 0);
342
343     a->root_path = chroot_path;
344     a->name = SVM_GLOBAL_REGION_NAME;
345     a->baseva = SVM_GLOBAL_REGION_BASEVA;
346     a->size = SVM_GLOBAL_REGION_SIZE;
347     a->flags = SVM_FLAGS_NODATA;
348
349     shm_name = shm_name_from_svm_map_region_args (a);
350
351     svm_fd = shm_open ((char *)shm_name, O_RDWR, 0777);
352
353     if (svm_fd < 0) {
354         perror("svm_region_map(mmap open)");
355         goto out;
356     }
357
358     vec_free(shm_name);
359
360     root_rp = mmap(0, MMAP_PAGESIZE, 
361               PROT_READ | PROT_WRITE, MAP_SHARED, svm_fd, 0);
362     
363     if (root_rp == (svm_region_t *) MAP_FAILED) {
364         close(svm_fd);
365         clib_warning("mmap");
366         goto out;
367     }
368     
369     /* Remap now that the region has been placed */
370     clib_warning ("remap to 0x%x", root_rp->virtual_base);
371
372     a->baseva = root_rp->virtual_base;
373     a->size = root_rp->virtual_size;
374     munmap(root_rp, MMAP_PAGESIZE);
375     
376     root_rp = (void *) mmap ((void *)a->baseva, a->size, 
377                         PROT_READ | PROT_WRITE, 
378                         MAP_SHARED | MAP_FIXED, svm_fd, 0);
379     if ((uword)root_rp == (uword)MAP_FAILED) {
380         clib_unix_warning ("mmap");
381         goto out;
382     }
383     
384     close(svm_fd);
385
386     if ((uword) root_rp != root_rp->virtual_base) {
387         clib_warning("mmap botch");
388         goto out;
389     }
390     
391     if (pthread_mutex_trylock(&root_rp->mutex)) {
392         clib_warning ("root_rp->mutex LOCKED by pid %d, tag %d, cleared...",
393                       root_rp->mutex_owner_pid, root_rp->mutex_owner_tag);
394         memset(&root_rp->mutex, 0, sizeof (root_rp->mutex));
395         goto out;
396     } else {
397         clib_warning ("root_rp->mutex OK...\n");
398         pthread_mutex_unlock(&root_rp->mutex);
399     }
400     
401  out:
402     vec_free (a);
403     /*
404      * Now that the root region is known to be OK, 
405      * fix broken subregions
406      */
407     subregion_repair(chroot_path);
408
409     if (crash_root_region) {
410         clib_warning ("Leaving root region locked on purpose...");
411         pthread_mutex_lock(&root_rp->mutex);
412         root_rp->mutex_owner_pid = getpid();
413         root_rp->mutex_owner_tag = 99;
414     }
415     svm_region_exit ();
416 }
417
418 int main (int argc, char **argv)
419 {
420     unformat_input_t input;
421     int parsed =0;
422     char *name;
423     char *chroot_path = 0;
424     u8 *chroot_u8;
425
426     unformat_init_command_line (&input, argv);
427
428     while (unformat_check_input (&input) != UNFORMAT_END_OF_INPUT) {
429         if (unformat(&input, "show-verbose")) {
430             show (chroot_path, 1);
431             parsed++;
432         } else if (unformat (&input, "show")) {
433             show (chroot_path, 0);
434             parsed++;
435         } else if (unformat (&input, "client-scan")) {
436             svm_client_scan(chroot_path);
437             parsed++;
438         } else if (unformat (&input, "repair")) {
439             repair(chroot_path, 0 /* fix it */);
440             parsed++;
441         } else if (unformat (&input, "crash")) {
442             repair (chroot_path, 1 /* crash it */);
443             parsed++;
444         } else if (unformat (&input, "trace-on %s", &name)) {
445             trace (chroot_path, name, 1);
446             parsed++;
447         } else if (unformat (&input, "trace-off %s", &name)) {
448             trace (chroot_path, name, 0);
449             parsed++;
450         } else if (unformat (&input, "chroot %s", &chroot_u8)) {
451             chroot_path = (char *) chroot_u8;
452         } else {
453             break;
454         }
455     }
456
457     unformat_free (&input);
458
459     if (!parsed) {
460         fformat(stdout, "%s: show | show-verbose | client-scan | trace-on <region-name>\n", argv[0]);
461         fformat(stdout, "      trace-off <region-name>\n");
462     }
463     exit (0);
464 }