FD.io VPP  v21.01.1
Vector Packet Processing
ssvm.c
Go to the documentation of this file.
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 
27 
28 int
30 {
31  int ssvm_fd;
32  u8 junk = 0, *ssvm_filename;
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 
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? */
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
121 {
122  struct stat stat;
123  int ssvm_fd = -1;
125 
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
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
218 {
219  uword page_size, n_pages;
221  int log2_page_size;
222  void *oldheap;
223 
224  if (memfd->ssvm_size == 0)
225  return SSVM_API_ERROR_NO_SIZE;
226 
228 
230  (char *) memfd->name);
231 
232  if (memfd->fd == CLIB_MEM_ERROR)
233  {
234  clib_unix_warning ("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 ("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 ("memfd ftruncate failure");
250  return SSVM_API_ERROR_CREATE_FAILURE;
251  }
252 
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 = 1ULL << 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
293 {
294  int mmap_flags = MAP_SHARED;
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  if (memfd->requested_va)
308  mmap_flags |= MAP_FIXED;
309 
310  /*
311  * Map the segment once, to look at the shared header
312  */
313  sh = (void *) mmap (0, page_size, PROT_READ | PROT_WRITE, MAP_SHARED,
314  memfd->fd, 0);
315 
316  if (sh == MAP_FAILED)
317  {
318  clib_unix_warning ("client research mmap (fd %d)", memfd->fd);
319  close (memfd->fd);
320  return SSVM_API_ERROR_MMAP;
321  }
322 
323  memfd->requested_va = sh->ssvm_va;
324  memfd->ssvm_size = sh->ssvm_size;
325  munmap (sh, page_size);
326 
327  /*
328  * Remap the segment at the 'right' address
329  */
330  sh = (void *) mmap (uword_to_pointer (memfd->requested_va, void *),
331  memfd->ssvm_size,
332  PROT_READ | PROT_WRITE, mmap_flags, memfd->fd, 0);
333 
334  if (sh == MAP_FAILED)
335  {
336  clib_unix_warning ("client final mmap");
337  close (memfd->fd);
338  return SSVM_API_ERROR_MMAP;
339  }
340 
341  sh->client_pid = getpid ();
342  memfd->sh = sh;
343  return 0;
344 }
345 
346 void
348 {
349  vec_free (memfd->name);
350  if (memfd->is_server)
351  clib_mem_vm_unmap (memfd->sh);
352  else
353  munmap (memfd->sh, memfd->ssvm_size);
354  close (memfd->fd);
355 }
356 
357 /**
358  * Initialize segment in a private heap
359  */
360 int
362 {
363  uword page_size, log2_page_size, rnd_size = 0;
365  clib_mem_heap_t *heap, *oldheap;
366 
367  log2_page_size = clib_mem_get_log2_page_size ();
368  if (log2_page_size == 0)
369  {
370  clib_unix_warning ("cannot determine page size");
371  return SSVM_API_ERROR_CREATE_FAILURE;
372  }
373 
374  page_size = 1ULL << log2_page_size;
375  rnd_size = clib_max (ssvm->ssvm_size + (page_size - 1), ssvm->ssvm_size);
376  rnd_size &= ~(page_size - 1);
377 
378  sh = clib_mem_vm_map (0, rnd_size + page_size, log2_page_size,
379  (char *) ssvm->name);
380  if (sh == CLIB_MEM_VM_MAP_FAILED)
381  {
382  clib_unix_warning ("private map failed");
383  return SSVM_API_ERROR_CREATE_FAILURE;
384  }
385 
386  heap = clib_mem_create_heap ((u8 *) sh + page_size, rnd_size,
387  1 /* locked */ , "ssvm server private");
388  if (heap == 0)
389  {
390  clib_unix_warning ("heap alloc");
391  return -1;
392  }
393 
394  rnd_size = clib_mem_get_heap_free_space (heap);
395 
396  ssvm->ssvm_size = rnd_size;
397  ssvm->is_server = 1;
398  ssvm->my_pid = getpid ();
399  ssvm->requested_va = ~0;
400 
401  /* First page in allocated memory is set aside for the shared header */
402  ssvm->sh = sh;
403 
404  clib_memset (sh, 0, sizeof (*sh));
405  sh->heap = heap;
406  sh->ssvm_size = rnd_size;
407  sh->ssvm_va = pointer_to_uword (sh);
409 
410  oldheap = ssvm_push_heap (sh);
411  sh->name = format (0, "%s", ssvm->name, 0);
412  ssvm_pop_heap (oldheap);
413 
414  return 0;
415 }
416 
417 int
419 {
420  clib_warning ("BUG: this should not be called!");
421  return -1;
422 }
423 
424 void
426 {
427  vec_free (ssvm->name);
428  clib_mem_destroy_heap (ssvm->sh->heap);
429  clib_mem_vm_unmap (ssvm->sh);
430 }
431 
432 int
434 {
435  return (server_init_fns[type]) (ssvm);
436 }
437 
438 int
440 {
441  return (client_init_fns[type]) (ssvm);
442 }
443 
444 void
446 {
447  delete_fns[ssvm->sh->type] (ssvm);
448 }
449 
451 ssvm_type (const ssvm_private_t * ssvm)
452 {
453  return ssvm->sh->type;
454 }
455 
456 u8 *
457 ssvm_name (const ssvm_private_t * ssvm)
458 {
459  return ssvm->sh->name;
460 }
461 
462 /*
463  * fd.io coding-style-patch-verification: ON
464  *
465  * Local Variables:
466  * eval: (c-set-style "gnu")
467  * End:
468  */
__clib_export int clib_mem_vm_create_fd(clib_mem_page_sz_t log2_page_size, char *fmt,...)
Definition: mem.c:264
#define CLIB_MEM_UNPOISON(a, s)
Definition: sanitizer.h:47
#define CLIB_MEM_VM_MAP_FAILED
Definition: mem.h:54
svm_region_t * svm_get_root_rp(void)
Definition: svm.c:53
uword requested_va
Definition: ssvm.h:85
#define vec_c_string_is_terminated(V)
Test whether a vector is a NULL terminated c-string.
Definition: vec.h:1082
__clib_export int clib_mem_vm_unmap(void *base)
Definition: mem.c:511
__clib_export void clib_mem_vm_randomize_va(uword *requested_va, clib_mem_page_sz_t log2_page_size)
Definition: mem.c:197
uword ssvm_size
Definition: ssvm.h:84
int ssvm_client_init_memfd(ssvm_private_t *memfd)
Initialize memfd segment client.
Definition: ssvm.c:292
clib_mem_heap_t * clib_mem_create_heap(void *base, uword size, int is_locked, char *fmt,...)
Definition: mem_dlmalloc.c:533
volatile u32 ready
Definition: ssvm.h:76
__clib_export void * clib_mem_vm_map_shared(void *base, uword size, int fd, uword offset, char *fmt,...)
Definition: mem.c:59
clib_memset(h->entries, 0, sizeof(h->entries[0]) *entries)
ssvm_segment_type_t type
Definition: ssvm.h:78
void(* delete_fn)(ssvm_private_t *)
Definition: ssvm.c:19
ssvm_shared_header_t * sh
Definition: ssvm.h:83
unsigned char u8
Definition: types.h:56
static uword min_log2(uword x)
Definition: clib.h:162
void ssvm_delete(ssvm_private_t *ssvm)
Definition: ssvm.c:445
__clib_export void * clib_mem_vm_map(void *base, uword size, clib_mem_page_sz_t log2_page_sz, char *fmt,...)
Definition: mem.c:25
u8 * ssvm_name(const ssvm_private_t *ssvm)
Definition: ssvm.c:457
int ssvm_server_init(ssvm_private_t *ssvm, ssvm_segment_type_t type)
Definition: ssvm.c:433
enum ssvm_segment_type_ ssvm_segment_type_t
static init_fn client_init_fns[SSVM_N_SEGMENT_TYPES]
Definition: ssvm.c:23
int(* init_fn)(ssvm_private_t *)
Definition: ssvm.c:18
description fragment has unexpected format
Definition: map.api:433
static void * ssvm_push_heap(ssvm_shared_header_t *sh)
Definition: ssvm.h:143
int attach_timeout
shm segments attach timeout (sec)
Definition: ssvm.h:94
static void ssvm_pop_heap(void *oldheap)
Definition: ssvm.h:151
vl_api_fib_path_type_t type
Definition: fib_types.api:123
void * data_base
Definition: svm_common.h:45
void clib_mem_destroy_heap(clib_mem_heap_t *heap)
Definition: mem_dlmalloc.c:563
int ssvm_server_init_memfd(ssvm_private_t *memfd)
Initialize memfd segment server.
Definition: ssvm.c:217
int ssvm_client_init_private(ssvm_private_t *ssvm)
Definition: ssvm.c:418
uword clib_mem_get_heap_free_space(clib_mem_heap_t *heap)
Definition: mem_dlmalloc.c:576
#define vec_free(V)
Free vector&#39;s memory (no header).
Definition: vec.h:380
#define clib_warning(format, args...)
Definition: error.h:59
static_always_inline clib_mem_page_sz_t clib_mem_get_log2_page_size(void)
Definition: mem.h:462
int ssvm_server_init_private(ssvm_private_t *ssvm)
Initialize segment in a private heap.
Definition: ssvm.c:361
u32 my_pid
Definition: ssvm.h:86
int is_server
Definition: ssvm.h:89
int fd
memfd segments
Definition: ssvm.h:93
#define CLIB_MEM_ERROR
Definition: mem.h:55
#define uword_to_pointer(u, type)
Definition: types.h:136
#define ASSERT(truth)
int ssvm_client_init(ssvm_private_t *ssvm, ssvm_segment_type_t type)
Definition: ssvm.c:439
__clib_export clib_mem_page_sz_t clib_mem_get_fd_log2_page_size(int fd)
Definition: mem.c:190
static uword pointer_to_uword(const void *p)
Definition: types.h:131
#define clib_max(x, y)
Definition: clib.h:321
u8 * name
Definition: ssvm.h:87
#define MMAP_PAGESIZE
Definition: ssvm.h:42
__clib_export u64 clib_mem_get_fd_page_size(int fd)
Definition: mem.c:181
static init_fn server_init_fns[SSVM_N_SEGMENT_TYPES]
Definition: ssvm.c:21
uword ssvm_size
Definition: ssvm.h:69
u64 uword
Definition: types.h:112
static delete_fn delete_fns[SSVM_N_SEGMENT_TYPES]
Definition: ssvm.c:25
#define clib_unix_warning(format, args...)
Definition: error.h:68
void ssvm_delete_memfd(ssvm_private_t *memfd)
Definition: ssvm.c:347
int ssvm_client_init_shm(ssvm_private_t *ssvm)
Definition: ssvm.c:120
void ssvm_delete_private(ssvm_private_t *ssvm)
Definition: ssvm.c:425
void ssvm_delete_shm(ssvm_private_t *ssvm)
Definition: ssvm.c:190
Private segments.
Definition: ssvm.h:52
int ssvm_server_init_shm(ssvm_private_t *ssvm)
Definition: ssvm.c:29
ssvm_segment_type_t ssvm_type(const ssvm_private_t *ssvm)
Definition: ssvm.c:451