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