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