FD.io VPP  v18.10-34-gcce845e
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 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  memset (mm, 0, sizeof (*mm));
46 
47  record_size_in_cache_lines =
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);
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  memset (h, 0, sizeof (*h));
124  h->record_size_in_cachelines = record_size_in_cache_lines;
126  h->file_size_in_records = file_size_in_records;
127  h->number_of_records = ~0ULL;
128  h->number_of_files = ~0ULL;
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 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... */
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 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  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 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",
347  h->application_id,
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",
354  s = format (s, " files are %lld records long, %lld files\n",
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",
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 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
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  */
u8 application_patch_version
application patch version number
Definition: maplog.h:106
a
Definition: bitmap.h:538
int clib_maplog_init(clib_maplog_init_args_t *a)
Initialize a maplog object.
Definition: maplog.c:28
volatile u64 next_record_index
rw cache line: atomic ticket-counter, file index
Definition: maplog.h:72
unsigned long u64
Definition: types.h:89
void clib_maplog_update_header(clib_maplog_main_t *mm)
Update a mapped log header file.
Definition: maplog.c:246
#define vec_add1(V, E)
Add 1 element to end of vector (unspecified alignment).
Definition: vec.h:523
u8 maplog_minor_version
library minor version number
Definition: maplog.h:46
int i
#define CLIB_MAPLOG_FLAG_WRAPPED
Definition: maplog.h:94
u64 file_size_in_records
file size in records, rounded to a power of two
Definition: maplog.h:74
u8 * format(u8 *s, const char *fmt,...)
Definition: format.c:419
u32 record_size_in_cachelines
record size in cache lines
Definition: maplog.h:54
u64 file_size_in_records
file size in records
Definition: maplog.h:56
unsigned char u8
Definition: types.h:56
volatile u32 current_file_index
current file index
Definition: maplog.h:76
Maplog log file header segment.
Definition: maplog.h:43
#define vec_reset_length(v)
Reset vector length to zero NULL-pointer tolerant.
memset(h->entries, 0, sizeof(h->entries[0])*entries)
u32 record_size_in_cachelines
record size in cache lines
Definition: maplog.h:81
u8 application_minor_version
application minor version number
Definition: maplog.h:51
#define CLIB_MAPLOG_FLAG_INIT
Definition: maplog.h:92
unsigned int u32
Definition: types.h:88
u32 log2_file_size_in_records
lg file size in records
Definition: maplog.h:75
u32 cacheline_size
cache line size
Definition: maplog.h:55
mmap-based thread-safe fixed-size record double-buffered logging.
u8 maplog_flag_wrapped
log has wrapped
Definition: maplog.h:48
u8 * file_basename
basename, e.g.
Definition: maplog.h:87
u8 * format_maplog_header(u8 *s, va_list *args)
format a log header
Definition: maplog.c:335
u32 application_id
application identifier
Definition: maplog.h:49
#define CLIB_MAPLOG_FLAG_CIRCULAR
Definition: maplog.h:93
#define MAPLOG_MAJOR_VERSION
Definition: maplog.h:62
u8 application_minor_version
application minor version number
Definition: maplog.h:105
#define vec_free(V)
Free vector&#39;s memory (no header).
Definition: vec.h:339
#define clib_warning(format, args...)
Definition: error.h:59
u32 application_id
application identifier
Definition: maplog.h:103
volatile u32 flags
flags, currently just "init" or not
Definition: maplog.h:77
u8 maplog_major_version
library major version number
Definition: maplog.h:45
#define ARRAY_LEN(x)
Definition: clib.h:61
u8 application_major_version
application major version number
Definition: maplog.h:104
volatile u8 * file_baseva[2]
active segment base addresses
Definition: maplog.h:84
#define ASSERT(truth)
#define MAPLOG_MINOR_VERSION
Definition: maplog.h:63
char * file_basename
file base name
Definition: maplog.h:100
u32 record_size_in_bytes
record size in bytes
Definition: maplog.h:102
u64 number_of_records
number of records in entire log
Definition: maplog.h:57
size_t count
Definition: vapi.c:46
u8 maplog_flag_circular
log is circular
Definition: maplog.h:53
u8 * header_filename
log header file name
Definition: maplog.h:88
#define vec_len(v)
Number of elements in vector (rvalue-only, NULL tolerant)
static uword max_log2(uword x)
Definition: clib.h:187
Process-private main data structure.
Definition: maplog.h:68
void clib_maplog_close(clib_maplog_main_t *mm)
Close a mapped log, and update the log header file.
Definition: maplog.c:299
u8 application_patch_version
application patch version number
Definition: maplog.h:52
#define clib_unix_warning(format, args...)
Definition: error.h:68
u64 file_size_in_bytes
file size in bytes
Definition: maplog.h:101
u8 application_major_version
application major version number
Definition: maplog.h:50
u64 number_of_files
number of files in entire log
Definition: maplog.h:58
int clib_maplog_process(char *file_basename, void *fp_arg)
Process a complete maplog.
Definition: maplog.c:384
#define CLIB_CACHE_LINE_BYTES
Definition: cache.h:59
u8 maplog_is_circular
single, circular log
Definition: maplog.h:107
u8 * filenames[2]
active segment file names
Definition: maplog.h:85
clib_maplog_main_t * mm
pointer to the main structure
Definition: maplog.h:99
log initialization structure
Definition: maplog.h:97
#define MAPLOG_PATCH_VERSION
Definition: maplog.h:64
u8 file_basename[256]
file basename
Definition: maplog.h:59
u8 maplog_patch_version
library patch version number
Definition: maplog.h:47