host stack: update stale copyright
[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   if (clib_mem_vm_ext_map (&mapa))
94     {
95       clib_unix_warning ("mmap");
96       close (ssvm_fd);
97       return SSVM_API_ERROR_MMAP;
98     }
99   close (ssvm_fd);
100
101   sh = mapa.addr;
102   sh->master_pid = ssvm->my_pid;
103   sh->ssvm_size = ssvm->ssvm_size;
104   sh->ssvm_va = pointer_to_uword (sh);
105   sh->type = SSVM_SEGMENT_SHM;
106 #if USE_DLMALLOC == 0
107   sh->heap = mheap_alloc_with_flags (((u8 *) sh) + page_size,
108                                      ssvm->ssvm_size - page_size, mh_flags);
109 #else
110   sh->heap = create_mspace_with_base (((u8 *) sh) + page_size,
111                                       ssvm->ssvm_size - page_size,
112                                       1 /* locked */ );
113   mspace_disable_expand (sh->heap);
114 #endif
115
116   oldheap = ssvm_push_heap (sh);
117   sh->name = format (0, "%s", ssvm->name, 0);
118   ssvm_pop_heap (oldheap);
119
120   ssvm->sh = sh;
121   ssvm->my_pid = getpid ();
122   ssvm->i_am_master = 1;
123
124   /* The application has to set set sh->ready... */
125   return 0;
126 }
127
128 int
129 ssvm_slave_init_shm (ssvm_private_t * ssvm)
130 {
131   struct stat stat;
132   int ssvm_fd = -1;
133   ssvm_shared_header_t *sh;
134
135   ASSERT (vec_c_string_is_terminated (ssvm->name));
136   ssvm->i_am_master = 0;
137
138   while (ssvm->attach_timeout-- > 0)
139     {
140       if (ssvm_fd < 0)
141         ssvm_fd = shm_open ((char *) ssvm->name, O_RDWR, 0777);
142       if (ssvm_fd < 0)
143         {
144           sleep (1);
145           continue;
146         }
147       if (fstat (ssvm_fd, &stat) < 0)
148         {
149           sleep (1);
150           continue;
151         }
152
153       if (stat.st_size > 0)
154         goto map_it;
155     }
156   clib_warning ("slave timeout");
157   return SSVM_API_ERROR_SLAVE_TIMEOUT;
158
159 map_it:
160   sh = (void *) mmap (0, MMAP_PAGESIZE, PROT_READ | PROT_WRITE, MAP_SHARED,
161                       ssvm_fd, 0);
162   if (sh == MAP_FAILED)
163     {
164       clib_unix_warning ("slave research mmap");
165       close (ssvm_fd);
166       return SSVM_API_ERROR_MMAP;
167     }
168
169   while (ssvm->attach_timeout-- > 0)
170     {
171       if (sh->ready)
172         goto re_map_it;
173     }
174   close (ssvm_fd);
175   munmap (sh, MMAP_PAGESIZE);
176   clib_warning ("slave timeout 2");
177   return SSVM_API_ERROR_SLAVE_TIMEOUT;
178
179 re_map_it:
180   ssvm->requested_va = sh->ssvm_va;
181   ssvm->ssvm_size = sh->ssvm_size;
182   munmap (sh, MMAP_PAGESIZE);
183
184   sh = ssvm->sh = (void *) mmap ((void *) ssvm->requested_va, ssvm->ssvm_size,
185                                  PROT_READ | PROT_WRITE,
186                                  MAP_SHARED | MAP_FIXED, ssvm_fd, 0);
187
188   if (sh == MAP_FAILED)
189     {
190       clib_unix_warning ("slave final mmap");
191       close (ssvm_fd);
192       return SSVM_API_ERROR_MMAP;
193     }
194   sh->slave_pid = getpid ();
195   return 0;
196 }
197
198 void
199 ssvm_delete_shm (ssvm_private_t * ssvm)
200 {
201   u8 *fn;
202
203   fn = format (0, "/dev/shm/%s%c", ssvm->name, 0);
204
205   if (CLIB_DEBUG > 1)
206     clib_warning ("[%d] unlinking ssvm (%s) backing file '%s'", getpid (),
207                   ssvm->name, fn);
208
209   /* Throw away the backing file */
210   if (unlink ((char *) fn) < 0)
211     clib_unix_warning ("unlink segment '%s'", ssvm->name);
212
213   vec_free (fn);
214   vec_free (ssvm->name);
215
216   munmap ((void *) ssvm->requested_va, ssvm->ssvm_size);
217 }
218
219 /**
220  * Initialize memfd segment master
221  */
222 int
223 ssvm_master_init_memfd (ssvm_private_t * memfd)
224 {
225   uword page_size;
226   ssvm_shared_header_t *sh;
227   void *oldheap;
228   clib_mem_vm_alloc_t alloc = { 0 };
229   clib_error_t *err;
230
231   if (memfd->ssvm_size == 0)
232     return SSVM_API_ERROR_NO_SIZE;
233
234   ASSERT (vec_c_string_is_terminated (memfd->name));
235
236   alloc.name = (char *) memfd->name;
237   alloc.size = memfd->ssvm_size;
238   alloc.flags = CLIB_MEM_VM_F_SHARED;
239   alloc.requested_va = memfd->requested_va;
240   if ((err = clib_mem_vm_ext_alloc (&alloc)))
241     {
242       clib_error_report (err);
243       return SSVM_API_ERROR_CREATE_FAILURE;
244     }
245
246   memfd->fd = alloc.fd;
247   memfd->sh = (ssvm_shared_header_t *) alloc.addr;
248   memfd->my_pid = getpid ();
249   memfd->i_am_master = 1;
250
251   page_size = 1ull << alloc.log2_page_size;
252   sh = memfd->sh;
253   sh->master_pid = memfd->my_pid;
254   sh->ssvm_size = memfd->ssvm_size;
255   sh->ssvm_va = pointer_to_uword (sh);
256   sh->type = SSVM_SEGMENT_MEMFD;
257
258 #if USE_DLMALLOC == 0
259   uword flags = MHEAP_FLAG_DISABLE_VM | MHEAP_FLAG_THREAD_SAFE;
260
261   sh->heap = mheap_alloc_with_flags (((u8 *) sh) + page_size,
262                                      memfd->ssvm_size - page_size, flags);
263 #else
264   sh->heap = create_mspace_with_base (((u8 *) sh) + page_size,
265                                       memfd->ssvm_size - page_size,
266                                       1 /* locked */ );
267   mspace_disable_expand (sh->heap);
268 #endif
269   oldheap = ssvm_push_heap (sh);
270   sh->name = format (0, "%s", memfd->name, 0);
271   ssvm_pop_heap (oldheap);
272
273   /* The application has to set set sh->ready... */
274   return 0;
275 }
276
277 /**
278  * Initialize memfd segment slave
279  *
280  * Subtly different than svm_slave_init. The caller needs to acquire
281  * a usable file descriptor for the memfd segment e.g. via
282  * vppinfra/socket.c:default_socket_recvmsg
283  */
284 int
285 ssvm_slave_init_memfd (ssvm_private_t * memfd)
286 {
287   clib_mem_vm_map_t mapa = { 0 };
288   ssvm_shared_header_t *sh;
289   uword page_size;
290
291   memfd->i_am_master = 0;
292
293   page_size = clib_mem_get_fd_page_size (memfd->fd);
294   if (!page_size)
295     {
296       clib_unix_warning ("page size unknown");
297       return SSVM_API_ERROR_MMAP;
298     }
299
300   /*
301    * Map the segment once, to look at the shared header
302    */
303   mapa.fd = memfd->fd;
304   mapa.size = page_size;
305
306   if (clib_mem_vm_ext_map (&mapa))
307     {
308       clib_unix_warning ("slave research mmap (fd %d)", mapa.fd);
309       close (memfd->fd);
310       return SSVM_API_ERROR_MMAP;
311     }
312
313   sh = mapa.addr;
314   memfd->requested_va = sh->ssvm_va;
315   memfd->ssvm_size = sh->ssvm_size;
316   clib_mem_vm_free (sh, page_size);
317
318   /*
319    * Remap the segment at the 'right' address
320    */
321   mapa.requested_va = memfd->requested_va;
322   mapa.size = memfd->ssvm_size;
323   if (clib_mem_vm_ext_map (&mapa))
324     {
325       clib_unix_warning ("slave final mmap");
326       close (memfd->fd);
327       return SSVM_API_ERROR_MMAP;
328     }
329
330   sh = mapa.addr;
331   sh->slave_pid = getpid ();
332   memfd->sh = sh;
333   return 0;
334 }
335
336 void
337 ssvm_delete_memfd (ssvm_private_t * memfd)
338 {
339   vec_free (memfd->name);
340   clib_mem_vm_free (memfd->sh, memfd->ssvm_size);
341   close (memfd->fd);
342 }
343
344 /**
345  * Initialize segment in a private heap
346  */
347 int
348 ssvm_master_init_private (ssvm_private_t * ssvm)
349 {
350   ssvm_shared_header_t *sh;
351   u32 pagesize = clib_mem_get_page_size ();
352   u32 rnd_size = 0;
353   u8 *heap;
354
355   rnd_size = (ssvm->ssvm_size + (pagesize - 1)) & ~(pagesize - 1);
356   rnd_size = clib_min (rnd_size, ((u64) 1 << 32) - pagesize);
357
358 #if USE_DLMALLOC == 0
359   {
360     mheap_t *heap_header;
361
362     heap = mheap_alloc (0, rnd_size);
363     if (heap == 0)
364       {
365         clib_unix_warning ("mheap alloc");
366         return -1;
367       }
368     heap_header = mheap_header (heap);
369     heap_header->flags |= MHEAP_FLAG_THREAD_SAFE;
370   }
371 #else
372   heap = create_mspace (rnd_size, 1 /* locked */ );
373 #endif
374
375   ssvm->ssvm_size = rnd_size;
376   ssvm->i_am_master = 1;
377   ssvm->my_pid = getpid ();
378   ssvm->requested_va = ~0;
379
380   /* Allocate a [sic] shared memory header, in process memory... */
381   sh = clib_mem_alloc_aligned (sizeof (*sh), CLIB_CACHE_LINE_BYTES);
382   ssvm->sh = sh;
383
384   clib_memset (sh, 0, sizeof (*sh));
385   sh->heap = heap;
386   sh->type = SSVM_SEGMENT_PRIVATE;
387
388   return 0;
389 }
390
391 int
392 ssvm_slave_init_private (ssvm_private_t * ssvm)
393 {
394   clib_warning ("BUG: this should not be called!");
395   return -1;
396 }
397
398 void
399 ssvm_delete_private (ssvm_private_t * ssvm)
400 {
401   vec_free (ssvm->name);
402 #if USE_DLMALLOC == 0
403   mheap_free (ssvm->sh->heap);
404 #else
405   destroy_mspace (ssvm->sh->heap);
406 #endif
407   clib_mem_free (ssvm->sh);
408 }
409
410 int
411 ssvm_master_init (ssvm_private_t * ssvm, ssvm_segment_type_t type)
412 {
413   return (master_init_fns[type]) (ssvm);
414 }
415
416 int
417 ssvm_slave_init (ssvm_private_t * ssvm, ssvm_segment_type_t type)
418 {
419   return (slave_init_fns[type]) (ssvm);
420 }
421
422 void
423 ssvm_delete (ssvm_private_t * ssvm)
424 {
425   delete_fns[ssvm->sh->type] (ssvm);
426 }
427
428 ssvm_segment_type_t
429 ssvm_type (const ssvm_private_t * ssvm)
430 {
431   return ssvm->sh->type;
432 }
433
434 u8 *
435 ssvm_name (const ssvm_private_t * ssvm)
436 {
437   return ssvm->sh->name;
438 }
439
440 /*
441  * fd.io coding-style-patch-verification: ON
442  *
443  * Local Variables:
444  * eval: (c-set-style "gnu")
445  * End:
446  */