FD.io VPP  v21.10.1-2-g0a485f517
Vector Packet Processing
maplog.c
Go to the documentation of this file.
1 /*
2  * Copyright (c) 2017 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 
16 #include <vppinfra/maplog.h>
17 
18 /**
19  * @brief Initialize a maplog object
20  *
21  * Compute record and file size parameters
22  * Create and map two log segments to seed the process
23  *
24  * @param[in/out] a init args structure
25  * @return 0 => success, <0 => failure
26  */
27 __clib_export int
29 {
30  int i, fd, limit;
31  int rv = 0;
32  u8 zero = 0;
33  u32 record_size_in_cache_lines;
34  u64 file_size_in_records;
36  clib_maplog_header_t _h, *h = &_h;
37 
38  ASSERT (a && a->mm);
39  mm = a->mm;
40 
41  /* Already initialized? */
42  if (mm->flags & CLIB_MAPLOG_FLAG_INIT)
43  return (-2);
44 
45  clib_memset (mm, 0, sizeof (*mm));
46 
47  record_size_in_cache_lines =
48  (a->record_size_in_bytes + CLIB_CACHE_LINE_BYTES -
50 
51  file_size_in_records = a->file_size_in_bytes
52  / (record_size_in_cache_lines * CLIB_CACHE_LINE_BYTES);
53 
54  /* Round up file size in records to a power of 2, for speed... */
55  mm->log2_file_size_in_records = max_log2 (file_size_in_records);
56  file_size_in_records = 1ULL << (mm->log2_file_size_in_records);
57  a->file_size_in_bytes = file_size_in_records * record_size_in_cache_lines
59 
60  mm->file_basename = format (0, "%s", a->file_basename);
61  if (vec_len (mm->file_basename) > ARRAY_LEN (h->file_basename))
62  {
63  vec_free (mm->file_basename);
64  return -11;
65  }
66 
67  mm->file_size_in_records = file_size_in_records;
69  mm->record_size_in_cachelines = record_size_in_cache_lines;
70  limit = 2;
71  if (a->maplog_is_circular)
72  {
75  limit = 1;
76  }
77 
78  /*
79  * Map the one and only file for a circular log,
80  * two files for a normal log.
81  */
82  for (i = 0; i < limit; i++)
83  {
84  mm->filenames[i] = format (0, "%v_%d", mm->file_basename,
85  mm->current_file_index++);
86  vec_add1 (mm->filenames[i], 0);
87 
88  fd = open ((char *) mm->filenames[i], O_CREAT | O_RDWR | O_TRUNC, 0600);
89  if (fd < 0)
90  {
91  rv = -3;
92  goto fail;
93  }
94 
95  if (lseek (fd, a->file_size_in_bytes - 1, SEEK_SET) == (off_t) - 1)
96  {
97  rv = -4;
98  goto fail;
99  }
100  if (write (fd, &zero, 1) != 1)
101  {
102  rv = -5;
103  goto fail;
104  }
105 
106  mm->file_baseva[i] = mmap (0, a->file_size_in_bytes,
107  PROT_READ | PROT_WRITE, MAP_SHARED, fd, 0);
108  if (mm->file_baseva[i] == (u8 *) MAP_FAILED)
109  {
110  clib_unix_warning ("mmap");
111  goto fail;
112  }
113  (void) close (fd);
114  }
115 
116  clib_memset (h, 0, sizeof (*h));
117  h->maplog_major_version = MAPLOG_MAJOR_VERSION;
118  h->maplog_minor_version = MAPLOG_MINOR_VERSION;
119  h->maplog_patch_version = MAPLOG_PATCH_VERSION;
120  h->application_id = a->application_id;
121  h->application_major_version = a->application_major_version;
122  h->application_minor_version = a->application_minor_version;
123  h->application_patch_version = a->application_patch_version;
124  h->record_size_in_cachelines = record_size_in_cache_lines;
125  h->cacheline_size = CLIB_CACHE_LINE_BYTES;
126  h->file_size_in_records = file_size_in_records;
127  h->number_of_records = ~0ULL;
128  h->number_of_files = ~0ULL;
129  h->maplog_flag_circular = a->maplog_is_circular;
130  memcpy (h->file_basename, mm->file_basename, vec_len (mm->file_basename));
131 
132  mm->header_filename = format (0, "%v_header", mm->file_basename);
133  vec_add1 (mm->header_filename, 0);
134 
135  fd = open ((char *) mm->header_filename, O_CREAT | O_RDWR | O_TRUNC, 0600);
136  if (fd < 0)
137  {
138  clib_unix_warning ("header create");
139  rv = -6;
140  goto fail;
141  }
142  rv = write (fd, h, sizeof (*h));
143  if (rv != sizeof (*h))
144  {
145  clib_unix_warning ("header write");
146  rv = -7;
147  goto fail;
148  }
149  (void) close (fd);
150  return 0;
151 
152 fail:
153  if (fd >= 0)
154  (void) close (fd);
155 
156  for (i = 0; i < limit; i++)
157  {
158  if (mm->file_baseva[i])
159  (void) munmap ((u8 *) mm->file_baseva[i], a->file_size_in_bytes);
160  if (mm->filenames[i])
161  (void) unlink ((char *) mm->filenames[i]);
162  vec_free (mm->filenames[i]);
163  }
164  if (mm->header_filename)
165  {
166  (void) unlink ((char *) mm->header_filename);
168  }
169  return rv;
170 }
171 
172 /* slow path: unmap a full log segment, and replace it */
173 
174 __clib_export u8 *
175 _clib_maplog_get_entry_slowpath (clib_maplog_main_t *mm, u64 my_record_index)
176 {
177  int fd;
178  u8 *rv;
179  u8 zero = 0;
180  u32 unmap_index = (mm->current_file_index) & 1;
181  u64 file_size_in_bytes = mm->file_size_in_records
183 
184  /* This should never happen */
185  ASSERT ((mm->flags & CLIB_MAPLOG_FLAG_CIRCULAR) == 0);
186 
187  /*
188  * Kill some time by calling format before we make the previous log
189  * segment disappear. Obviously it won't do to call clib_maplog_get_entry(),
190  * wait 100ms, and then fill in the log entry.
191  */
192  vec_reset_length (mm->filenames[unmap_index]);
193  mm->filenames[unmap_index] = format (mm->filenames[unmap_index],
194  "%v_%d", mm->file_basename,
195  mm->current_file_index++);
196 
197  /* Unmap the previous (full) segment */
198  (void) munmap ((u8 *) mm->file_baseva[unmap_index], file_size_in_bytes);
199 
200  /* Create a new segment */
201  fd = open ((char *) mm->filenames[unmap_index],
202  O_CREAT | O_RDWR | O_TRUNC, 0600);
203 
204  /* This is not real error recovery... */
205  if (fd < 0)
206  {
207  clib_unix_warning ("creat");
208  abort ();
209  }
210 
211  if (lseek (fd, file_size_in_bytes - 1, SEEK_SET) == (off_t) - 1)
212  {
213  clib_unix_warning ("lseek");
214  abort ();
215  }
216  if (write (fd, &zero, 1) != 1)
217  {
218  clib_unix_warning ("set-size write");
219  abort ();
220  }
221 
222  mm->file_baseva[unmap_index] =
223  mmap (0, file_size_in_bytes, PROT_READ | PROT_WRITE, MAP_SHARED, fd, 0);
224  if (mm->file_baseva[unmap_index] == (u8 *) MAP_FAILED)
225  {
226  clib_unix_warning ("mmap");
227  abort ();
228  }
229  (void) close (fd);
230 
231  rv = (u8 *)
232  mm->file_baseva[(my_record_index >> mm->log2_file_size_in_records) & 1] +
233  (my_record_index & (mm->file_size_in_records - 1))
235 
236  return rv;
237 }
238 
239 /**
240  * @brief Update a mapped log header file
241  *
242  * Read the log header. Update the number of records, and number of files
243  * @param[in/out] mm mapped log object
244  */
245 void
247 {
248  int fd, rv;
249  clib_maplog_header_t _h, *h = &_h;
250 
251  if (!(mm->flags & CLIB_MAPLOG_FLAG_INIT))
252  return;
253 
254  /* Open the log header */
255  fd = open ((char *) mm->header_filename, O_RDWR, 0600);
256  if (fd < 0)
257  {
258  clib_unix_warning ("reopen maplog header");
259  goto out;
260  }
261 
262  /* Read the log */
263  rv = read (fd, h, sizeof (*h));
264  if (rv != sizeof (*h))
265  {
266  clib_unix_warning ("read maplog header");
267  goto out;
268  }
269  /* Fix the header... */
270  h->number_of_records = mm->next_record_index;
271  h->number_of_files = mm->current_file_index;
272  h->maplog_flag_wrapped = (mm->flags & CLIB_MAPLOG_FLAG_WRAPPED) ? 1 : 0;
273 
274  /* Back to the beginning of the log header... */
275  if (lseek (fd, 0, SEEK_SET) < 0)
276  {
277  clib_unix_warning ("lseek to rewrite header");
278  goto out;
279  }
280  /* Rewrite the log header */
281  rv = write (fd, h, sizeof (*h));
282  if (rv != sizeof (*h))
283  clib_unix_warning ("rewrite header");
284 
285 out:
286  if (fd >= 0)
287  (void) close (fd);
288 }
289 
290 /**
291  * @brief Close a mapped log, and update the log header file
292  *
293  * Unmap the current log segments.
294  * Read the log header. Update the number of records, and number of files
295  *
296  * @param[in/out] mm mapped log object
297  */
298 __clib_export void
300 {
301  int i, limit;
302  u64 file_size_in_bytes;
303 
304  if (!(mm->flags & CLIB_MAPLOG_FLAG_INIT))
305  return;
306 
308 
309  file_size_in_bytes =
312 
313  limit = (mm->flags & CLIB_MAPLOG_FLAG_CIRCULAR) ? 1 : 2;
314 
315  /* unmap current + next segments */
316  for (i = 0; i < limit; i++)
317  {
318  (void) munmap ((u8 *) mm->file_baseva[i], file_size_in_bytes);
319  vec_free (mm->filenames[i]);
320  }
321 
322  vec_free (mm->file_basename);
324  clib_memset (mm, 0, sizeof (*mm));
325 }
326 
327 /**
328  * @brief format a log header
329  *
330  * Usage: s = format (0, "%U", format_maplog_header, headerp, verbose);
331  * @param [in] h clib_maplog_header_t pointer
332  * @param [in] verbose self-explanatory
333  */
334 __clib_export u8 *
335 format_maplog_header (u8 *s, va_list *args)
336 {
337  clib_maplog_header_t *h = va_arg (*args, clib_maplog_header_t *);
338  int verbose = va_arg (*args, int);
339 
340  if (!verbose)
341  goto brief;
342  s = format (s, "basename %s ", h->file_basename);
343  s = format (s, "log ver %d.%d.%d app id %u ver %d.%d.%d %s %s\n",
344  h->maplog_major_version,
345  h->maplog_minor_version,
346  h->maplog_patch_version,
347  h->application_id,
348  h->application_major_version,
349  h->application_minor_version, h->application_patch_version,
350  h->maplog_flag_circular ? "circular" : "linear",
351  h->maplog_flag_wrapped ? "wrapped" : "not wrapped");
352  s = format (s, " records are %d %d-byte cachelines\n",
353  h->record_size_in_cachelines, h->cacheline_size);
354  s = format (s, " files are %lld records long, %lld files\n",
355  h->file_size_in_records, h->number_of_files);
356  s = format (s, " %lld records total\n", h->number_of_records);
357  return s;
358 
359 brief:
360  s = format (s, "%s %lld records %lld files %lld records/file",
361  h->file_basename, h->number_of_records, h->number_of_files,
362  h->file_size_in_records);
363  return s;
364 }
365 
366 /**
367  * @brief Process a complete maplog
368  *
369  * Reads the maplog header. Map and process all log segments in order.
370  * Calls the callback function once per file with a record count.
371  *
372  * Note: if the file header isn't updated by calling
373  * clib_maplog_close(), it will appear to have an infinite
374  * number of records in an infinite number of files.
375  *
376  * So long as the callback function understands that possibility
377  * - by simply ignoring NULL records - the scheme still
378  * works...
379  *
380  * @param [in] file_basename Same basename supplied to clib_maplog_init
381  * @param [in] fp_arg Callback function pointer
382  */
383 __clib_export int
384 clib_maplog_process (char *file_basename, void *fp_arg)
385 {
386  clib_maplog_header_t _h, *h = &_h;
387  int fd, rv = 0;
388  u64 file_index;
389  u64 file_size_in_bytes;
390  u8 *header_filename, *this_filename = 0;
391  u8 *file_baseva;
392  int (*fp) (clib_maplog_header_t *, void *data, u64 count);
393  u64 records_this_file, records_left;
394  ASSERT (fp_arg);
395 
396  fp = fp_arg;
397 
398  header_filename = format (0, "%s_header%c", file_basename, 0);
399 
400  fd = open ((char *) header_filename, O_RDONLY, 0600);
401  if (fd < 0)
402  {
403  clib_unix_warning ("open maplog header");
404  rv = -1;
405  goto out;
406  }
407  rv = read (fd, h, sizeof (*h));
408  if (rv != sizeof (*h))
409  {
410  clib_unix_warning ("read maplog header");
411  rv = -2;
412  goto out;
413  }
414  (void) close (fd);
415  fd = -1;
416 
417  file_size_in_bytes = h->file_size_in_records
418  * h->record_size_in_cachelines * CLIB_CACHE_LINE_BYTES;
419 
420  records_left = h->number_of_records;
421 
422  for (file_index = 0; file_index < h->number_of_files; file_index++)
423  {
424  vec_reset_length (this_filename);
425  this_filename = format (this_filename, "%s_%llu%c", file_basename,
426  file_index, 0);
427  fd = open ((char *) this_filename, O_RDONLY, 0600);
428  if (fd < 0)
429  {
430  rv = -3;
431  goto out;
432  }
433 
434  file_baseva =
435  mmap (0, file_size_in_bytes, PROT_READ, MAP_SHARED, fd, 0);
436  (void) close (fd);
437  fd = -1;
438  if (file_baseva == (u8 *) MAP_FAILED)
439  {
440  clib_unix_warning ("mmap");
441  rv = -4;
442  goto out;
443  }
444 
445  records_this_file = (records_left > h->file_size_in_records) ?
446  h->file_size_in_records : records_left;
447 
448  /*
449  * Normal log, or a circular non-wrapped log, or a circular
450  * wrapped log which happens to be exactly linear
451  */
452  if (h->maplog_flag_circular == 0 || h->maplog_flag_wrapped == 0 ||
453  ((h->number_of_records % h->file_size_in_records) == 0))
454  (*fp) (h, file_baseva, records_this_file);
455  else
456  {
457  /* "Normal" wrapped circular log */
458  u64 first_chunk_record_index = h->number_of_records &
459  (h->file_size_in_records - 1);
460  u64 first_chunk_number_of_records = records_this_file -
461  first_chunk_record_index;
462  u8 *chunk_baseva = file_baseva +
463  first_chunk_record_index * h->record_size_in_cachelines *
464  h->cacheline_size;
465  (*fp) (h, chunk_baseva, first_chunk_number_of_records);
466  (*fp) (h, file_baseva,
467  records_this_file - first_chunk_number_of_records);
468  }
469 
470  if (munmap (file_baseva, file_size_in_bytes) < 0)
471  {
472  clib_warning ("munmap");
473  rv = -5;
474  /* but don't stop... */
475  }
476  records_left -= records_this_file;
477  if (records_left == 0)
478  break;
479  }
480 
481 out:
482  if (fd >= 0)
483  (void) close (fd);
484 
485  vec_free (this_filename);
486  vec_free (header_filename);
487  return rv;
488 }
489 
490 
491 /*
492  * fd.io coding-style-patch-verification: ON
493  *
494  * Local Variables:
495  * eval: (c-set-style "gnu")
496  * End:
497  */
vec_reset_length
#define vec_reset_length(v)
Reset vector length to zero NULL-pointer tolerant.
Definition: vec_bootstrap.h:194
CLIB_MAPLOG_FLAG_WRAPPED
#define CLIB_MAPLOG_FLAG_WRAPPED
Definition: maplog.h:94
MAPLOG_PATCH_VERSION
#define MAPLOG_PATCH_VERSION
Definition: maplog.h:64
clib_maplog_main_t::file_baseva
volatile u8 * file_baseva[2]
active segment base addresses
Definition: maplog.h:84
clib_maplog_header_t
Maplog log file header segment.
Definition: maplog.h:43
clib_maplog_main_t::header_filename
u8 * header_filename
log header file name
Definition: maplog.h:88
clib_maplog_main_t::flags
volatile u32 flags
flags, currently just "init" or not
Definition: maplog.h:77
h
h
Definition: flowhash_template.h:372
clib_unix_warning
#define clib_unix_warning(format, args...)
Definition: error.h:68
max_log2
static uword max_log2(uword x)
Definition: clib.h:223
count
u8 count
Definition: dhcp.api:208
vec_len
#define vec_len(v)
Number of elements in vector (rvalue-only, NULL tolerant)
Definition: vec_bootstrap.h:142
clib_maplog_close
__clib_export void clib_maplog_close(clib_maplog_main_t *mm)
Close a mapped log, and update the log header file.
Definition: maplog.c:299
vec_add1
#define vec_add1(V, E)
Add 1 element to end of vector (unspecified alignment).
Definition: vec.h:606
MAPLOG_MAJOR_VERSION
#define MAPLOG_MAJOR_VERSION
Definition: maplog.h:62
ARRAY_LEN
#define ARRAY_LEN(x)
Definition: clib.h:70
clib_maplog_init_args_t
log initialization structure
Definition: maplog.h:97
clib_maplog_process
__clib_export int clib_maplog_process(char *file_basename, void *fp_arg)
Process a complete maplog.
Definition: maplog.c:384
if
if(node->flags &VLIB_NODE_FLAG_TRACE) vnet_interface_output_trace(vm
maplog.h
mmap-based thread-safe fixed-size record double-buffered logging.
clib_maplog_main_t::file_size_in_records
u64 file_size_in_records
file size in records, rounded to a power of two
Definition: maplog.h:74
CLIB_CACHE_LINE_BYTES
#define CLIB_CACHE_LINE_BYTES
Definition: cache.h:58
clib_maplog_main_t::filenames
u8 * filenames[2]
active segment file names
Definition: maplog.h:85
clib_maplog_main_t::current_file_index
volatile u32 current_file_index
current file index
Definition: maplog.h:76
data
u8 data[128]
Definition: ipsec_types.api:95
vec_free
#define vec_free(V)
Free vector's memory (no header).
Definition: vec.h:395
clib_maplog_main_t
Process-private main data structure.
Definition: maplog.h:68
u64
unsigned long u64
Definition: types.h:89
format
description fragment has unexpected format
Definition: map.api:433
ASSERT
#define ASSERT(truth)
Definition: error_bootstrap.h:69
u32
unsigned int u32
Definition: types.h:88
clib_maplog_main_t::next_record_index
volatile u64 next_record_index
rw cache line: atomic ticket-counter, file index
Definition: maplog.h:72
clib_memset
clib_memset(h->entries, 0, sizeof(h->entries[0]) *entries)
u8
unsigned char u8
Definition: types.h:56
a
a
Definition: bitmap.h:525
format_maplog_header
__clib_export u8 * format_maplog_header(u8 *s, va_list *args)
format a log header
Definition: maplog.c:335
i
int i
Definition: flowhash_template.h:376
clib_maplog_main_t::record_size_in_cachelines
u32 record_size_in_cachelines
record size in cache lines
Definition: maplog.h:81
clib_warning
#define clib_warning(format, args...)
Definition: error.h:59
rv
int __clib_unused rv
Definition: application.c:491
CLIB_MAPLOG_FLAG_INIT
#define CLIB_MAPLOG_FLAG_INIT
Definition: maplog.h:92
clib_maplog_init
__clib_export int clib_maplog_init(clib_maplog_init_args_t *a)
Initialize a maplog object.
Definition: maplog.c:28
clib_maplog_update_header
void clib_maplog_update_header(clib_maplog_main_t *mm)
Update a mapped log header file.
Definition: maplog.c:246
CLIB_MAPLOG_FLAG_CIRCULAR
#define CLIB_MAPLOG_FLAG_CIRCULAR
Definition: maplog.h:93
clib_maplog_main_t::log2_file_size_in_records
u32 log2_file_size_in_records
lg file size in records
Definition: maplog.h:75
MAPLOG_MINOR_VERSION
#define MAPLOG_MINOR_VERSION
Definition: maplog.h:63
clib_maplog_main_t::file_basename
u8 * file_basename
basename, e.g.
Definition: maplog.h:87