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