FD.io VPP  v18.01-8-g0eacf49
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;
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 
58  a->file_size_in_bytes = file_size_in_records * record_size_in_cache_lines
60 
61  mm->file_basename = format (0, "%s", a->file_basename);
63  {
64  vec_free (mm->file_basename);
65  return -11;
66  }
67 
68  mm->file_size_in_records = file_size_in_records;
70  mm->record_size_in_cachelines = record_size_in_cache_lines;
71 
72  /* Map two files */
73  for (i = 0; i < 2; i++)
74  {
75  mm->filenames[i] = format (0, "%v_%d", mm->file_basename,
76  mm->current_file_index++);
77  vec_add1 (mm->filenames[i], 0);
78 
79  fd = open ((char *) mm->filenames[i], O_CREAT | O_RDWR | O_TRUNC, 0600);
80  if (fd < 0)
81  {
82  rv = -3;
83  goto fail;
84  }
85 
86  if (lseek (fd, a->file_size_in_bytes - 1, SEEK_SET) == (off_t) - 1)
87  {
88  rv = -4;
89  goto fail;
90  }
91  if (write (fd, &zero, 1) != 1)
92  {
93  rv = -5;
94  goto fail;
95  }
96 
97  mm->file_baseva[i] = mmap (0, a->file_size_in_bytes,
98  PROT_READ | PROT_WRITE, MAP_SHARED, fd, 0);
99  if (mm->file_baseva[i] == (u8 *) MAP_FAILED)
100  {
101  clib_unix_warning ("mmap");
102  goto fail;
103  }
104  (void) close (fd);
105  }
106 
107  memset (h, 0, sizeof (*h));
115  h->record_size_in_cachelines = record_size_in_cache_lines;
117  h->file_size_in_records = file_size_in_records;
118  h->number_of_records = ~0ULL;
119  h->number_of_files = ~0ULL;
120  memcpy (h->file_basename, mm->file_basename, vec_len (mm->file_basename));
121 
122  mm->header_filename = format (0, "%v_header", mm->file_basename);
123  vec_add1 (mm->header_filename, 0);
124 
125  fd = open ((char *) mm->header_filename, O_CREAT | O_RDWR | O_TRUNC, 0600);
126  if (fd < 0)
127  {
128  clib_unix_warning ("header create");
129  rv = -6;
130  goto fail;
131  }
132  rv = write (fd, h, sizeof (*h));
133  if (rv != sizeof (*h))
134  {
135  clib_unix_warning ("header write");
136  rv = -7;
137  goto fail;
138  }
139  (void) close (fd);
140  return 0;
141 
142 fail:
143  if (fd >= 0)
144  (void) close (fd);
145 
146  for (i = 0; i < 2; i++)
147  {
148  if (mm->file_baseva[i])
149  (void) munmap ((u8 *) mm->file_baseva[i], a->file_size_in_bytes);
150  if (mm->filenames[i])
151  (void) unlink ((char *) mm->filenames[i]);
152  vec_free (mm->filenames[i]);
153  }
154  if (mm->header_filename)
155  {
156  (void) unlink ((char *) mm->header_filename);
158  }
159  return rv;
160 }
161 
162 /* slow path: unmap a full log segment, and replace it */
163 
164 u8 *
165 _clib_maplog_get_entry_slowpath (clib_maplog_main_t * mm, u64 my_record_index)
166 {
167  int fd;
168  u8 *rv;
169  u8 zero = 0;
170  u32 unmap_index = (mm->current_file_index) & 1;
171  u64 file_size_in_bytes = mm->file_size_in_records
173 
174  /*
175  * Kill some time by calling format before we make the previous log
176  * segment disappear. Obviously it won't do to call clib_maplog_get_entry(),
177  * wait 100ms, and then fill in the log entry.
178  */
179  vec_reset_length (mm->filenames[unmap_index]);
180  mm->filenames[unmap_index] = format (mm->filenames[unmap_index],
181  "%v_%d", mm->file_basename,
182  mm->current_file_index++);
183 
184  /* Unmap the previous (full) segment */
185  (void) munmap ((u8 *) mm->file_baseva[unmap_index], file_size_in_bytes);
186 
187  /* Create a new segment */
188  fd = open ((char *) mm->filenames[unmap_index],
189  O_CREAT | O_RDWR | O_TRUNC, 0600);
190 
191  /* This is not real error recovery... */
192  if (fd < 0)
193  {
194  clib_unix_warning ("creat");
195  abort ();
196  }
197 
198  if (lseek (fd, file_size_in_bytes - 1, SEEK_SET) == (off_t) - 1)
199  {
200  clib_unix_warning ("lseek");
201  abort ();
202  }
203  if (write (fd, &zero, 1) != 1)
204  {
205  clib_unix_warning ("set-size write");
206  abort ();
207  }
208 
209  mm->file_baseva[unmap_index] =
210  mmap (0, file_size_in_bytes, PROT_READ | PROT_WRITE, MAP_SHARED, fd, 0);
211  if (mm->file_baseva[unmap_index] == (u8 *) MAP_FAILED)
212  {
213  clib_unix_warning ("mmap");
214  abort ();
215  }
216  (void) close (fd);
217 
218  rv = (u8 *)
219  mm->file_baseva[(my_record_index >> mm->log2_file_size_in_records) & 1] +
220  (my_record_index & (mm->file_size_in_records - 1))
222 
223  return rv;
224 }
225 
226 /**
227  * @brief Update a mapped log header file
228  *
229  * Read the log header. Update the number of records, and number of files
230  * @param[in/out] mm mapped log object
231  */
232 void
234 {
235  int fd, rv;
236  clib_maplog_header_t _h, *h = &_h;
237 
238  if (!(mm->flags & CLIB_MAPLOG_FLAG_INIT))
239  return;
240 
241  /* Open the log header */
242  fd = open ((char *) mm->header_filename, O_RDWR, 0600);
243  if (fd < 0)
244  {
245  clib_unix_warning ("reopen maplog header");
246  goto out;
247  }
248 
249  /* Read the log */
250  rv = read (fd, h, sizeof (*h));
251  if (rv != sizeof (*h))
252  {
253  clib_unix_warning ("read maplog header");
254  goto out;
255  }
256  /* Fix the header... */
259 
260  /* Back to the beginning of the log header... */
261  if (lseek (fd, 0, SEEK_SET) < 0)
262  {
263  clib_unix_warning ("lseek to rewrite header");
264  goto out;
265  }
266  /* Rewrite the log header */
267  rv = write (fd, h, sizeof (*h));
268  if (rv != sizeof (*h))
269  clib_unix_warning ("rewrite header");
270 
271 out:
272  if (fd >= 0)
273  (void) close (fd);
274 }
275 
276 /**
277  * @brief Close a mapped log, and update the log header file
278  *
279  * Unmap the current log segments.
280  * Read the log header. Update the number of records, and number of files
281  *
282  * @param[in/out] mm mapped log object
283  */
284 void
286 {
287  int i;
288  u64 file_size_in_bytes;
289 
290  if (!(mm->flags & CLIB_MAPLOG_FLAG_INIT))
291  return;
292 
294 
295  file_size_in_bytes =
298 
299  /* unmap current + next segments */
300  for (i = 0; i < 2; i++)
301  {
302  (void) munmap ((u8 *) mm->file_baseva[i], file_size_in_bytes);
303  vec_free (mm->filenames[i]);
304  }
305 
306  vec_free (mm->file_basename);
308  memset (mm, 0, sizeof (*mm));
309 }
310 
311 /**
312  * @brief format a log header
313  *
314  * Usage: s = format (0, "%U", format_maplog_header, headerp, verbose);
315  * @param [in] h clib_maplog_header_t pointer
316  * @param [in] verbose self-explanatory
317  */
318 u8 *
319 format_maplog_header (u8 * s, va_list * args)
320 {
321  clib_maplog_header_t *h = va_arg (*args, clib_maplog_header_t *);
322  int verbose = va_arg (*args, int);
323 
324  if (!verbose)
325  goto brief;
326  s = format (s, "basename %s ", h->file_basename);
327  s = format (s, "log ver %d.%d.%d app id %u ver %d.%d.%d\n",
331  h->application_id,
334  s = format (s, " records are %d %d-byte cachelines\n",
336  s = format (s, " files are %lld records long, %lld files\n",
338  s = format (s, " %lld records total\n", h->number_of_records);
339  return s;
340 
341 brief:
342  s = format (s, "%s %lld records %lld files %lld records/file",
345  return s;
346 }
347 
348 /**
349  * @brief Process a complete maplog
350  *
351  * Reads the maplog header. Map and process all log segments in order.
352  * Calls the callback function once per file with a record count.
353  *
354  * Note: if the file header isn't updated by calling
355  * clib_maplog_close(), it will appear to have an infinite
356  * number of records in an infinite number of files.
357  *
358  * So long as the callback function understands that possibility
359  * - by simply ignoring NULL records - the scheme still
360  * works...
361  *
362  * @param [in] file_basename Same basename supplied to clib_maplog_init
363  * @param [in] fp_arg Callback function pointer
364  */
365 int
366 clib_maplog_process (char *file_basename, void *fp_arg)
367 {
368  clib_maplog_header_t _h, *h = &_h;
369  int fd, rv = 0;
370  u64 file_index;
371  u64 file_size_in_bytes;
372  u8 *header_filename, *this_filename = 0;
373  u8 *file_baseva;
374  int (*fp) (clib_maplog_header_t *, void *data, u64 count);
375  u64 records_this_file, records_left;
376  ASSERT (fp_arg);
377 
378  fp = fp_arg;
379 
380  header_filename = format (0, "%s_header%c", file_basename, 0);
381 
382  fd = open ((char *) header_filename, O_RDONLY, 0600);
383  if (fd < 0)
384  {
385  clib_unix_warning ("open maplog header");
386  rv = -1;
387  goto out;
388  }
389  rv = read (fd, h, sizeof (*h));
390  if (rv != sizeof (*h))
391  {
392  clib_unix_warning ("read maplog header");
393  rv = -2;
394  goto out;
395  }
396  (void) close (fd);
397  fd = -1;
398 
399  file_size_in_bytes = h->file_size_in_records
401 
402  records_left = h->number_of_records;
403 
404  for (file_index = 0; file_index < h->number_of_files; file_index++)
405  {
406  vec_reset_length (this_filename);
407  this_filename = format (this_filename, "%s_%llu%c", file_basename,
408  file_index, 0);
409  fd = open ((char *) this_filename, O_RDONLY, 0600);
410  if (fd < 0)
411  {
412  rv = -3;
413  goto out;
414  }
415 
416  file_baseva =
417  mmap (0, file_size_in_bytes, PROT_READ, MAP_SHARED, fd, 0);
418  (void) close (fd);
419  fd = -1;
420  if (file_baseva == (u8 *) MAP_FAILED)
421  {
422  clib_unix_warning ("mmap");
423  rv = -4;
424  goto out;
425  }
426 
427  records_this_file = (records_left > h->file_size_in_records) ?
428  h->file_size_in_records : records_left;
429 
430  (*fp) (h, file_baseva, records_this_file);
431 
432  if (munmap (file_baseva, file_size_in_bytes) < 0)
433  {
434  clib_warning ("munmap");
435  rv = -5;
436  /* but don't stop... */
437  }
438  records_left -= records_this_file;
439  if (records_left == 0)
440  break;
441  }
442 
443 out:
444  if (fd >= 0)
445  (void) close (fd);
446 
447  vec_free (this_filename);
448  vec_free (header_filename);
449  return rv;
450 }
451 
452 
453 /*
454  * fd.io coding-style-patch-verification: ON
455  *
456  * Local Variables:
457  * eval: (c-set-style "gnu")
458  * End:
459  */
sll srl srl sll sra u16x4 i
Definition: vector_sse2.h:337
u8 application_patch_version
applcation patch version number
Definition: maplog.h:104
a
Definition: bitmap.h:516
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
void clib_maplog_update_header(clib_maplog_main_t *mm)
Update a mapped log header file.
Definition: maplog.c:233
#define vec_add1(V, E)
Add 1 element to end of vector (unspecified alignment).
Definition: vec.h:518
u8 maplog_minor_version
library minor version number
Definition: maplog.h:46
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
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.
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 long u64
Definition: types.h:89
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 * 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:319
u32 application_id
application identifier
Definition: maplog.h:49
#define MAPLOG_MAJOR_VERSION
Definition: maplog.h:62
u8 application_minor_version
applcation minor version number
Definition: maplog.h:103
vec_header_t h
Definition: buffer.c:282
#define vec_free(V)
Free vector&#39;s memory (no header).
Definition: vec.h:336
#define clib_warning(format, args...)
Definition: error.h:59
u32 application_id
application identifier
Definition: maplog.h:101
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:59
u8 application_major_version
applcation major version number
Definition: maplog.h:102
volatile u8 * file_baseva[2]
active segment base addresses
Definition: maplog.h:84
#define ASSERT(truth)
unsigned int u32
Definition: types.h:88
#define MAPLOG_MINOR_VERSION
Definition: maplog.h:63
char * file_basename
file base name
Definition: maplog.h:98
u32 record_size_in_bytes
record size in bytes
Definition: maplog.h:100
u64 number_of_records
number of records in entire log
Definition: maplog.h:57
size_t count
Definition: vapi.c:42
u8 * header_filename
log header file name
Definition: maplog.h:88
#define vec_len(v)
Number of elements in vector (rvalue-only, NULL tolerant)
unsigned char u8
Definition: types.h:56
static uword max_log2(uword x)
Definition: clib.h:236
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:285
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:99
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:366
#define CLIB_CACHE_LINE_BYTES
Definition: cache.h:67
u8 * filenames[2]
active segment file names
Definition: maplog.h:85
clib_maplog_main_t * mm
pointer to the main structure
Definition: maplog.h:97
log initialization structure
Definition: maplog.h:95
#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