svm: harmonize ssvm names
[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   return 0;
187 }
188
189 void
190 ssvm_delete_shm (ssvm_private_t * ssvm)
191 {
192   u8 *fn;
193
194   fn = format (0, "/dev/shm/%s%c", ssvm->name, 0);
195
196   if (CLIB_DEBUG > 1)
197     clib_warning ("[%d] unlinking ssvm (%s) backing file '%s'", getpid (),
198                   ssvm->name, fn);
199
200   /* Throw away the backing file */
201   if (unlink ((char *) fn) < 0)
202     clib_unix_warning ("unlink segment '%s'", ssvm->name);
203
204   vec_free (fn);
205   vec_free (ssvm->name);
206
207   if (ssvm->is_server)
208     clib_mem_vm_unmap (ssvm->sh);
209   else
210     munmap ((void *) ssvm->sh, ssvm->ssvm_size);
211 }
212
213 /**
214  * Initialize memfd segment server
215  */
216 int
217 ssvm_server_init_memfd (ssvm_private_t * memfd)
218 {
219   int log2_page_size, n_pages;
220   uword page_size;
221   ssvm_shared_header_t *sh;
222   void *oldheap;
223
224   if (memfd->ssvm_size == 0)
225     return SSVM_API_ERROR_NO_SIZE;
226
227   ASSERT (vec_c_string_is_terminated (memfd->name));
228
229   memfd->fd = clib_mem_vm_create_fd (CLIB_MEM_PAGE_SZ_DEFAULT,
230                                      (char *) memfd->name);
231
232   if (memfd->fd == CLIB_MEM_ERROR)
233     {
234       clib_unix_warning (0, "failed to create memfd");
235       return SSVM_API_ERROR_CREATE_FAILURE;
236     }
237
238   log2_page_size = clib_mem_get_fd_log2_page_size (memfd->fd);
239   if (log2_page_size == 0)
240     {
241       clib_unix_warning (0, "cannot determine page size");
242       return SSVM_API_ERROR_CREATE_FAILURE;
243     }
244
245   n_pages = ((memfd->ssvm_size - 1) >> log2_page_size) + 1;
246
247   if ((ftruncate (memfd->fd, n_pages << log2_page_size)) == -1)
248     {
249       clib_unix_warning (0, "memfd ftruncate failure");
250       return SSVM_API_ERROR_CREATE_FAILURE;
251     }
252
253   sh = clib_mem_vm_map_shared (uword_to_pointer (memfd->requested_va, void *),
254                                memfd->ssvm_size, memfd->fd, 0,
255                                (char *) memfd->name);
256   if (sh == CLIB_MEM_VM_MAP_FAILED)
257     {
258       clib_unix_warning ("memfd map (fd %d)", memfd->fd);
259       close (memfd->fd);
260       return SSVM_API_ERROR_CREATE_FAILURE;
261     }
262
263   memfd->sh = sh;
264   memfd->my_pid = getpid ();
265   memfd->is_server = 1;
266
267   sh->server_pid = memfd->my_pid;
268   sh->ssvm_size = memfd->ssvm_size;
269   sh->ssvm_va = pointer_to_uword (sh);
270   sh->type = SSVM_SEGMENT_MEMFD;
271
272   page_size = 1 << log2_page_size;
273   sh->heap = clib_mem_create_heap (((u8 *) sh) + page_size,
274                                    memfd->ssvm_size - page_size,
275                                    1 /* locked */ , "ssvm server memfd");
276   oldheap = ssvm_push_heap (sh);
277   sh->name = format (0, "%s", memfd->name, 0);
278   ssvm_pop_heap (oldheap);
279
280   /* The application has to set set sh->ready... */
281   return 0;
282 }
283
284 /**
285  * Initialize memfd segment client
286  *
287  * Subtly different than svm_client_init. The caller needs to acquire
288  * a usable file descriptor for the memfd segment e.g. via
289  * vppinfra/socket.c:default_socket_recvmsg
290  */
291 int
292 ssvm_client_init_memfd (ssvm_private_t * memfd)
293 {
294   clib_mem_vm_map_t mapa = { 0 };
295   ssvm_shared_header_t *sh;
296   uword page_size;
297
298   memfd->is_server = 0;
299
300   page_size = clib_mem_get_fd_page_size (memfd->fd);
301   if (!page_size)
302     {
303       clib_unix_warning ("page size unknown");
304       return SSVM_API_ERROR_MMAP;
305     }
306
307   /*
308    * Map the segment once, to look at the shared header
309    */
310   mapa.fd = memfd->fd;
311   mapa.size = page_size;
312
313   if (clib_mem_vm_ext_map (&mapa))
314     {
315       clib_unix_warning ("client research mmap (fd %d)", mapa.fd);
316       close (memfd->fd);
317       return SSVM_API_ERROR_MMAP;
318     }
319
320   sh = mapa.addr;
321   memfd->requested_va = sh->ssvm_va;
322   memfd->ssvm_size = sh->ssvm_size;
323   clib_mem_vm_free (sh, page_size);
324
325   /*
326    * Remap the segment at the 'right' address
327    */
328   mapa.requested_va = memfd->requested_va;
329   mapa.size = memfd->ssvm_size;
330   if (clib_mem_vm_ext_map (&mapa))
331     {
332       clib_unix_warning ("client final mmap");
333       close (memfd->fd);
334       return SSVM_API_ERROR_MMAP;
335     }
336
337   sh = mapa.addr;
338   sh->client_pid = getpid ();
339   memfd->sh = sh;
340   return 0;
341 }
342
343 void
344 ssvm_delete_memfd (ssvm_private_t * memfd)
345 {
346   vec_free (memfd->name);
347   if (memfd->is_server)
348     clib_mem_vm_unmap (memfd->sh);
349   else
350     clib_mem_vm_free (memfd->sh, memfd->ssvm_size);
351   close (memfd->fd);
352 }
353
354 /**
355  * Initialize segment in a private heap
356  */
357 int
358 ssvm_server_init_private (ssvm_private_t * ssvm)
359 {
360   uword page_size, log2_page_size, rnd_size = 0;
361   ssvm_shared_header_t *sh;
362   void *oldheap;
363   u8 *heap;
364
365   log2_page_size = clib_mem_get_log2_page_size ();
366   if (log2_page_size == 0)
367     {
368       clib_unix_warning (0, "cannot determine page size");
369       return SSVM_API_ERROR_CREATE_FAILURE;
370     }
371
372   page_size = 1 << log2_page_size;
373   rnd_size = clib_max (ssvm->ssvm_size + (page_size - 1), ssvm->ssvm_size);
374   rnd_size &= ~(page_size - 1);
375
376   sh = clib_mem_vm_map (0, rnd_size + page_size, log2_page_size,
377                         (char *) ssvm->name);
378   if (sh == CLIB_MEM_VM_MAP_FAILED)
379     {
380       clib_unix_warning ("private map failed");
381       return SSVM_API_ERROR_CREATE_FAILURE;
382     }
383
384   heap = clib_mem_create_heap ((u8 *) sh + page_size, rnd_size,
385                                1 /* locked */ , "ssvm server private");
386   if (heap == 0)
387     {
388       clib_unix_warning ("heap alloc");
389       return -1;
390     }
391
392   rnd_size = clib_mem_get_heap_free_space (heap);
393
394   ssvm->ssvm_size = rnd_size;
395   ssvm->is_server = 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   ssvm->sh = sh;
401
402   clib_memset (sh, 0, sizeof (*sh));
403   sh->heap = heap;
404   sh->ssvm_size = rnd_size;
405   sh->ssvm_va = pointer_to_uword (heap);
406   sh->type = SSVM_SEGMENT_PRIVATE;
407
408   oldheap = ssvm_push_heap (sh);
409   sh->name = format (0, "%s", ssvm->name, 0);
410   ssvm_pop_heap (oldheap);
411
412   return 0;
413 }
414
415 int
416 ssvm_client_init_private (ssvm_private_t * ssvm)
417 {
418   clib_warning ("BUG: this should not be called!");
419   return -1;
420 }
421
422 void
423 ssvm_delete_private (ssvm_private_t * ssvm)
424 {
425   vec_free (ssvm->name);
426   clib_mem_destroy_heap (ssvm->sh->heap);
427   clib_mem_vm_unmap (ssvm->sh);
428 }
429
430 int
431 ssvm_server_init (ssvm_private_t * ssvm, ssvm_segment_type_t type)
432 {
433   return (server_init_fns[type]) (ssvm);
434 }
435
436 int
437 ssvm_client_init (ssvm_private_t * ssvm, ssvm_segment_type_t type)
438 {
439   return (client_init_fns[type]) (ssvm);
440 }
441
442 void
443 ssvm_delete (ssvm_private_t * ssvm)
444 {
445   delete_fns[ssvm->sh->type] (ssvm);
446 }
447
448 ssvm_segment_type_t
449 ssvm_type (const ssvm_private_t * ssvm)
450 {
451   return ssvm->sh->type;
452 }
453
454 u8 *
455 ssvm_name (const ssvm_private_t * ssvm)
456 {
457   return ssvm->sh->name;
458 }
459
460 /*
461  * fd.io coding-style-patch-verification: ON
462  *
463  * Local Variables:
464  * eval: (c-set-style "gnu")
465  * End:
466  */