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