svm: asan: fix asan support
[vpp.git] / src / svm / ssvm.c
1 /*
2  * Copyright (c) 2015-2019 Cisco and/or its affiliates.
3  * Licensed under the Apache License, Version 2.0 (the "License");
4  * you may not use this file except in compliance with the License.
5  * You may obtain a copy of the License at:
6  *
7  *     http://www.apache.org/licenses/LICENSE-2.0
8  *
9  * Unless required by applicable law or agreed to in writing, software
10  * distributed under the License is distributed on an "AS IS" BASIS,
11  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12  * See the License for the specific language governing permissions and
13  * limitations under the License.
14  */
15 #include <svm/ssvm.h>
16 #include <svm/svm_common.h>
17
18 typedef int (*init_fn) (ssvm_private_t *);
19 typedef void (*delete_fn) (ssvm_private_t *);
20
21 static init_fn master_init_fns[SSVM_N_SEGMENT_TYPES] =
22   { ssvm_master_init_shm, ssvm_master_init_memfd, ssvm_master_init_private };
23 static init_fn slave_init_fns[SSVM_N_SEGMENT_TYPES] =
24   { ssvm_slave_init_shm, ssvm_slave_init_memfd, ssvm_slave_init_private };
25 static delete_fn delete_fns[SSVM_N_SEGMENT_TYPES] =
26   { ssvm_delete_shm, ssvm_delete_memfd, ssvm_delete_private };
27
28 int
29 ssvm_master_init_shm (ssvm_private_t * ssvm)
30 {
31   int ssvm_fd;
32   clib_mem_vm_map_t mapa = { 0 };
33   u8 junk = 0, *ssvm_filename;
34   ssvm_shared_header_t *sh;
35   uword page_size, requested_va = 0;
36   void *oldheap;
37
38   if (ssvm->ssvm_size == 0)
39     return SSVM_API_ERROR_NO_SIZE;
40
41   if (CLIB_DEBUG > 1)
42     clib_warning ("[%d] creating segment '%s'", getpid (), ssvm->name);
43
44   ASSERT (vec_c_string_is_terminated (ssvm->name));
45   ssvm_filename = format (0, "/dev/shm/%s%c", ssvm->name, 0);
46   unlink ((char *) ssvm_filename);
47   vec_free (ssvm_filename);
48
49   ssvm_fd = shm_open ((char *) ssvm->name, O_RDWR | O_CREAT | O_EXCL, 0777);
50   if (ssvm_fd < 0)
51     {
52       clib_unix_warning ("create segment '%s'", ssvm->name);
53       return SSVM_API_ERROR_CREATE_FAILURE;
54     }
55
56   if (fchmod (ssvm_fd, S_IRUSR | S_IWUSR | S_IRGRP | S_IWGRP) < 0)
57     clib_unix_warning ("ssvm segment chmod");
58   if (svm_get_root_rp ())
59     {
60       /* TODO: is this really needed? */
61       svm_main_region_t *smr = svm_get_root_rp ()->data_base;
62       if (fchown (ssvm_fd, smr->uid, smr->gid) < 0)
63         clib_unix_warning ("ssvm segment chown");
64     }
65
66   if (lseek (ssvm_fd, ssvm->ssvm_size, SEEK_SET) < 0)
67     {
68       clib_unix_warning ("lseek");
69       close (ssvm_fd);
70       return SSVM_API_ERROR_SET_SIZE;
71     }
72
73   if (write (ssvm_fd, &junk, 1) != 1)
74     {
75       clib_unix_warning ("set ssvm size");
76       close (ssvm_fd);
77       return SSVM_API_ERROR_SET_SIZE;
78     }
79
80   page_size = clib_mem_get_fd_page_size (ssvm_fd);
81   if (ssvm->requested_va)
82     {
83       requested_va = ssvm->requested_va;
84       clib_mem_vm_randomize_va (&requested_va, min_log2 (page_size));
85     }
86
87   mapa.requested_va = requested_va;
88   mapa.size = ssvm->ssvm_size;
89   mapa.fd = ssvm_fd;
90   mapa.numa_node = ssvm->numa;
91   if (clib_mem_vm_ext_map (&mapa))
92     {
93       clib_unix_warning ("mmap");
94       close (ssvm_fd);
95       return SSVM_API_ERROR_MMAP;
96     }
97   close (ssvm_fd);
98
99   sh = mapa.addr;
100   CLIB_MEM_UNPOISON (sh, sizeof (*sh));
101   sh->master_pid = ssvm->my_pid;
102   sh->ssvm_size = ssvm->ssvm_size;
103   sh->ssvm_va = pointer_to_uword (sh);
104   sh->type = SSVM_SEGMENT_SHM;
105   sh->heap = create_mspace_with_base (((u8 *) sh) + page_size,
106                                       ssvm->ssvm_size - page_size,
107                                       1 /* locked */ );
108   mspace_disable_expand (sh->heap);
109
110   oldheap = ssvm_push_heap (sh);
111   sh->name = format (0, "%s", ssvm->name, 0);
112   ssvm_pop_heap (oldheap);
113
114   ssvm->sh = sh;
115   ssvm->my_pid = getpid ();
116   ssvm->i_am_master = 1;
117
118   /* The application has to set set sh->ready... */
119   return 0;
120 }
121
122 int
123 ssvm_slave_init_shm (ssvm_private_t * ssvm)
124 {
125   struct stat stat;
126   int ssvm_fd = -1;
127   ssvm_shared_header_t *sh;
128
129   ASSERT (vec_c_string_is_terminated (ssvm->name));
130   ssvm->i_am_master = 0;
131
132   while (ssvm->attach_timeout-- > 0)
133     {
134       if (ssvm_fd < 0)
135         ssvm_fd = shm_open ((char *) ssvm->name, O_RDWR, 0777);
136       if (ssvm_fd < 0)
137         {
138           sleep (1);
139           continue;
140         }
141       if (fstat (ssvm_fd, &stat) < 0)
142         {
143           sleep (1);
144           continue;
145         }
146
147       if (stat.st_size > 0)
148         goto map_it;
149     }
150   clib_warning ("slave timeout");
151   return SSVM_API_ERROR_SLAVE_TIMEOUT;
152
153 map_it:
154   sh = (void *) mmap (0, MMAP_PAGESIZE, PROT_READ | PROT_WRITE, MAP_SHARED,
155                       ssvm_fd, 0);
156   if (sh == MAP_FAILED)
157     {
158       clib_unix_warning ("slave research mmap");
159       close (ssvm_fd);
160       return SSVM_API_ERROR_MMAP;
161     }
162
163   while (ssvm->attach_timeout-- > 0)
164     {
165       if (sh->ready)
166         goto re_map_it;
167     }
168   close (ssvm_fd);
169   munmap (sh, MMAP_PAGESIZE);
170   clib_warning ("slave timeout 2");
171   return SSVM_API_ERROR_SLAVE_TIMEOUT;
172
173 re_map_it:
174   ssvm->requested_va = sh->ssvm_va;
175   ssvm->ssvm_size = sh->ssvm_size;
176   munmap (sh, MMAP_PAGESIZE);
177
178   sh = ssvm->sh = (void *) mmap ((void *) ssvm->requested_va, ssvm->ssvm_size,
179                                  PROT_READ | PROT_WRITE,
180                                  MAP_SHARED | MAP_FIXED, ssvm_fd, 0);
181
182   if (sh == MAP_FAILED)
183     {
184       clib_unix_warning ("slave final mmap");
185       close (ssvm_fd);
186       return SSVM_API_ERROR_MMAP;
187     }
188   sh->slave_pid = getpid ();
189   return 0;
190 }
191
192 void
193 ssvm_delete_shm (ssvm_private_t * ssvm)
194 {
195   u8 *fn;
196
197   fn = format (0, "/dev/shm/%s%c", ssvm->name, 0);
198
199   if (CLIB_DEBUG > 1)
200     clib_warning ("[%d] unlinking ssvm (%s) backing file '%s'", getpid (),
201                   ssvm->name, fn);
202
203   /* Throw away the backing file */
204   if (unlink ((char *) fn) < 0)
205     clib_unix_warning ("unlink segment '%s'", ssvm->name);
206
207   vec_free (fn);
208   vec_free (ssvm->name);
209
210   munmap ((void *) ssvm->sh, ssvm->ssvm_size);
211 }
212
213 /**
214  * Initialize memfd segment master
215  */
216 int
217 ssvm_master_init_memfd (ssvm_private_t * memfd)
218 {
219   uword page_size;
220   ssvm_shared_header_t *sh;
221   void *oldheap;
222   clib_mem_vm_alloc_t alloc = { 0 };
223   clib_error_t *err;
224
225   if (memfd->ssvm_size == 0)
226     return SSVM_API_ERROR_NO_SIZE;
227
228   ASSERT (vec_c_string_is_terminated (memfd->name));
229
230   alloc.name = (char *) memfd->name;
231   alloc.size = memfd->ssvm_size;
232   alloc.flags = CLIB_MEM_VM_F_SHARED;
233   alloc.requested_va = memfd->requested_va;
234   if (memfd->numa)
235     {
236       alloc.numa_node = memfd->numa;
237       alloc.flags |= CLIB_MEM_VM_F_NUMA_PREFER;
238     }
239   if ((err = clib_mem_vm_ext_alloc (&alloc)))
240     {
241       clib_error_report (err);
242       return SSVM_API_ERROR_CREATE_FAILURE;
243     }
244
245   memfd->fd = alloc.fd;
246   memfd->sh = (ssvm_shared_header_t *) alloc.addr;
247   memfd->my_pid = getpid ();
248   memfd->i_am_master = 1;
249
250   page_size = 1ull << alloc.log2_page_size;
251   sh = memfd->sh;
252   sh->master_pid = memfd->my_pid;
253   sh->ssvm_size = memfd->ssvm_size;
254   sh->ssvm_va = pointer_to_uword (sh);
255   sh->type = SSVM_SEGMENT_MEMFD;
256
257   sh->heap = create_mspace_with_base (((u8 *) sh) + page_size,
258                                       memfd->ssvm_size - page_size,
259                                       1 /* locked */ );
260   mspace_disable_expand (sh->heap);
261   oldheap = ssvm_push_heap (sh);
262   sh->name = format (0, "%s", memfd->name, 0);
263   ssvm_pop_heap (oldheap);
264
265   /* The application has to set set sh->ready... */
266   return 0;
267 }
268
269 /**
270  * Initialize memfd segment slave
271  *
272  * Subtly different than svm_slave_init. The caller needs to acquire
273  * a usable file descriptor for the memfd segment e.g. via
274  * vppinfra/socket.c:default_socket_recvmsg
275  */
276 int
277 ssvm_slave_init_memfd (ssvm_private_t * memfd)
278 {
279   clib_mem_vm_map_t mapa = { 0 };
280   ssvm_shared_header_t *sh;
281   uword page_size;
282
283   memfd->i_am_master = 0;
284
285   page_size = clib_mem_get_fd_page_size (memfd->fd);
286   if (!page_size)
287     {
288       clib_unix_warning ("page size unknown");
289       return SSVM_API_ERROR_MMAP;
290     }
291
292   /*
293    * Map the segment once, to look at the shared header
294    */
295   mapa.fd = memfd->fd;
296   mapa.size = page_size;
297
298   if (clib_mem_vm_ext_map (&mapa))
299     {
300       clib_unix_warning ("slave research mmap (fd %d)", mapa.fd);
301       close (memfd->fd);
302       return SSVM_API_ERROR_MMAP;
303     }
304
305   sh = mapa.addr;
306   memfd->requested_va = sh->ssvm_va;
307   memfd->ssvm_size = sh->ssvm_size;
308   clib_mem_vm_free (sh, page_size);
309
310   /*
311    * Remap the segment at the 'right' address
312    */
313   mapa.requested_va = memfd->requested_va;
314   mapa.size = memfd->ssvm_size;
315   if (clib_mem_vm_ext_map (&mapa))
316     {
317       clib_unix_warning ("slave final mmap");
318       close (memfd->fd);
319       return SSVM_API_ERROR_MMAP;
320     }
321
322   sh = mapa.addr;
323   sh->slave_pid = getpid ();
324   memfd->sh = sh;
325   return 0;
326 }
327
328 void
329 ssvm_delete_memfd (ssvm_private_t * memfd)
330 {
331   vec_free (memfd->name);
332   clib_mem_vm_free (memfd->sh, memfd->ssvm_size);
333   close (memfd->fd);
334 }
335
336 /**
337  * Initialize segment in a private heap
338  */
339 int
340 ssvm_master_init_private (ssvm_private_t * ssvm)
341 {
342   uword pagesize = clib_mem_get_page_size (), rnd_size = 0;
343   clib_mem_vm_alloc_t alloc = { 0 };
344   struct dlmallinfo dlminfo;
345   ssvm_shared_header_t *sh;
346   clib_error_t *err;
347   u8 *heap;
348
349   rnd_size = clib_max (ssvm->ssvm_size + (pagesize - 1), ssvm->ssvm_size);
350   rnd_size &= ~(pagesize - 1);
351
352   alloc.name = (char *) ssvm->name;
353   alloc.size = rnd_size + pagesize;
354   if (ssvm->numa)
355     {
356       alloc.numa_node = ssvm->numa;
357       alloc.flags |= CLIB_MEM_VM_F_NUMA_PREFER;
358     }
359
360   if ((err = clib_mem_vm_ext_alloc (&alloc)))
361     {
362       clib_error_report (err);
363       return SSVM_API_ERROR_CREATE_FAILURE;
364     }
365
366   heap = create_mspace_with_base ((u8 *) alloc.addr + pagesize, rnd_size,
367                                   1 /* locked */ );
368   if (heap == 0)
369     {
370       clib_unix_warning ("mheap alloc");
371       return -1;
372     }
373
374   mspace_disable_expand (heap);
375
376   /* Find actual size because mspace size is rounded up by dlmalloc */
377   dlminfo = mspace_mallinfo (heap);
378   rnd_size = dlminfo.fordblks;
379
380   ssvm->ssvm_size = rnd_size;
381   ssvm->i_am_master = 1;
382   ssvm->my_pid = getpid ();
383   ssvm->requested_va = ~0;
384
385   /* First page in allocated memory is set aside for the shared header */
386   sh = alloc.addr;
387   ssvm->sh = sh;
388
389   clib_memset (sh, 0, sizeof (*sh));
390   sh->heap = heap;
391   sh->ssvm_size = rnd_size;
392   sh->ssvm_va = pointer_to_uword (heap);
393   sh->type = SSVM_SEGMENT_PRIVATE;
394   sh->name = ssvm->name;
395
396   return 0;
397 }
398
399 int
400 ssvm_slave_init_private (ssvm_private_t * ssvm)
401 {
402   clib_warning ("BUG: this should not be called!");
403   return -1;
404 }
405
406 void
407 ssvm_delete_private (ssvm_private_t * ssvm)
408 {
409   vec_free (ssvm->name);
410   destroy_mspace (ssvm->sh->heap);
411   clib_mem_vm_free (ssvm->sh, ssvm->ssvm_size + clib_mem_get_page_size ());
412 }
413
414 int
415 ssvm_master_init (ssvm_private_t * ssvm, ssvm_segment_type_t type)
416 {
417   return (master_init_fns[type]) (ssvm);
418 }
419
420 int
421 ssvm_slave_init (ssvm_private_t * ssvm, ssvm_segment_type_t type)
422 {
423   return (slave_init_fns[type]) (ssvm);
424 }
425
426 void
427 ssvm_delete (ssvm_private_t * ssvm)
428 {
429   delete_fns[ssvm->sh->type] (ssvm);
430 }
431
432 ssvm_segment_type_t
433 ssvm_type (const ssvm_private_t * ssvm)
434 {
435   return ssvm->sh->type;
436 }
437
438 u8 *
439 ssvm_name (const ssvm_private_t * ssvm)
440 {
441   return ssvm->sh->name;
442 }
443
444 /*
445  * fd.io coding-style-patch-verification: ON
446  *
447  * Local Variables:
448  * eval: (c-set-style "gnu")
449  * End:
450  */