session: add support for memfd segments
[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 };
23 static init_fn slave_init_fns[SSVM_N_SEGMENT_TYPES] =
24   { ssvm_slave_init_shm, ssvm_slave_init_memfd };
25 static delete_fn delete_fns[SSVM_N_SEGMENT_TYPES] =
26   { ssvm_delete_shm, ssvm_delete_memfd };
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 int
319 ssvm_master_init (ssvm_private_t * ssvm, ssvm_segment_type_t type)
320 {
321   return (master_init_fns[type]) (ssvm);
322 }
323
324 int
325 ssvm_slave_init (ssvm_private_t * ssvm, ssvm_segment_type_t type)
326 {
327   return (slave_init_fns[type]) (ssvm);
328 }
329
330 void
331 ssvm_delete (ssvm_private_t * ssvm)
332 {
333   delete_fns[ssvm->sh->type] (ssvm);
334 }
335
336 ssvm_segment_type_t
337 ssvm_type (const ssvm_private_t * ssvm)
338 {
339   return ssvm->sh->type;
340 }
341
342 u8 *
343 ssvm_name (const ssvm_private_t * ssvm)
344 {
345   return ssvm->sh->name;
346 }
347
348 /*
349  * fd.io coding-style-patch-verification: ON
350  *
351  * Local Variables:
352  * eval: (c-set-style "gnu")
353  * End:
354  */