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