blob: 1900aa8f03b4ca8489171a79afc6125b55e04ec1 [file] [log] [blame]
Dave Barache9d91702017-11-29 16:59:01 -05001/*
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
Dave Barach04fee312017-12-01 16:20:32 -050018/**
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 */
Dave Barache9d91702017-11-29 16:59:01 -050027int
Dave Barach04fee312017-12-01 16:20:32 -050028clib_maplog_init (clib_maplog_init_args_t * a)
Dave Barache9d91702017-11-29 16:59:01 -050029{
Dave Barach6ddede72018-03-22 10:54:45 -040030 int i, fd, limit;
Dave Barache9d91702017-11-29 16:59:01 -050031 int rv = 0;
32 u8 zero = 0;
33 u32 record_size_in_cache_lines;
34 u64 file_size_in_records;
Dave Barach04fee312017-12-01 16:20:32 -050035 clib_maplog_main_t *mm;
36 clib_maplog_header_t _h, *h = &_h;
37
38 ASSERT (a && a->mm);
39 mm = a->mm;
Dave Barache9d91702017-11-29 16:59:01 -050040
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 =
Dave Barach04fee312017-12-01 16:20:32 -050048 (a->record_size_in_bytes + CLIB_CACHE_LINE_BYTES -
Dave Barache9d91702017-11-29 16:59:01 -050049 1) / CLIB_CACHE_LINE_BYTES;
50
Dave Barach04fee312017-12-01 16:20:32 -050051 file_size_in_records = a->file_size_in_bytes
Dave Barache9d91702017-11-29 16:59:01 -050052 / (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);
Dave Barach04fee312017-12-01 16:20:32 -050057 a->file_size_in_bytes = file_size_in_records * record_size_in_cache_lines
Dave Barache9d91702017-11-29 16:59:01 -050058 * CLIB_CACHE_LINE_BYTES;
59
Dave Barach04fee312017-12-01 16:20:32 -050060 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
Dave Barache9d91702017-11-29 16:59:01 -050067 mm->file_size_in_records = file_size_in_records;
68 mm->flags |= CLIB_MAPLOG_FLAG_INIT;
69 mm->record_size_in_cachelines = record_size_in_cache_lines;
Dave Barach6ddede72018-03-22 10:54:45 -040070 limit = 2;
71 if (a->maplog_is_circular)
72 {
73 mm->log2_file_size_in_records = 63;
74 mm->flags |= CLIB_MAPLOG_FLAG_CIRCULAR;
75 limit = 1;
76 }
Dave Barache9d91702017-11-29 16:59:01 -050077
Dave Barach6ddede72018-03-22 10:54:45 -040078 /*
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++)
Dave Barache9d91702017-11-29 16:59:01 -050083 {
84 mm->filenames[i] = format (0, "%v_%d", mm->file_basename,
85 mm->current_file_index++);
Dave Barach04fee312017-12-01 16:20:32 -050086 vec_add1 (mm->filenames[i], 0);
Dave Barache9d91702017-11-29 16:59:01 -050087
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
Dave Barach04fee312017-12-01 16:20:32 -050095 if (lseek (fd, a->file_size_in_bytes - 1, SEEK_SET) == (off_t) - 1)
Dave Barache9d91702017-11-29 16:59:01 -050096 {
97 rv = -4;
98 goto fail;
99 }
100 if (write (fd, &zero, 1) != 1)
101 {
102 rv = -5;
103 goto fail;
104 }
105
Dave Barach04fee312017-12-01 16:20:32 -0500106 mm->file_baseva[i] = mmap (0, a->file_size_in_bytes,
Dave Barache9d91702017-11-29 16:59:01 -0500107 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 }
Dave Barach04fee312017-12-01 16:20:32 -0500115
116 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;
Dave Barach6ddede72018-03-22 10:54:45 -0400129 h->maplog_flag_circular = a->maplog_is_circular;
Dave Barach04fee312017-12-01 16:20:32 -0500130 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;
Dave Barache9d91702017-11-29 16:59:01 -0500151
152fail:
Dave Barach7a640192017-12-01 10:24:28 -0500153 if (fd >= 0)
Dave Barache9d91702017-11-29 16:59:01 -0500154 (void) close (fd);
155
Dave Barach6ddede72018-03-22 10:54:45 -0400156 for (i = 0; i < limit; i++)
Dave Barache9d91702017-11-29 16:59:01 -0500157 {
158 if (mm->file_baseva[i])
Dave Barach04fee312017-12-01 16:20:32 -0500159 (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);
167 vec_free (mm->header_filename);
Dave Barache9d91702017-11-29 16:59:01 -0500168 }
169 return rv;
170}
171
Dave Barach04fee312017-12-01 16:20:32 -0500172/* slow path: unmap a full log segment, and replace it */
173
Dave Barache9d91702017-11-29 16:59:01 -0500174u8 *
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
182 * mm->record_size_in_cachelines * CLIB_CACHE_LINE_BYTES;
183
Dave Barach6ddede72018-03-22 10:54:45 -0400184 /* This should never happen */
185 ASSERT ((mm->flags & CLIB_MAPLOG_FLAG_CIRCULAR) == 0);
186
Dave Barach04fee312017-12-01 16:20:32 -0500187 /*
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 */
Dave Barache9d91702017-11-29 16:59:01 -0500192 vec_reset_length (mm->filenames[unmap_index]);
Dave Barache9d91702017-11-29 16:59:01 -0500193 mm->filenames[unmap_index] = format (mm->filenames[unmap_index],
194 "%v_%d", mm->file_basename,
195 mm->current_file_index++);
196
Dave Barach04fee312017-12-01 16:20:32 -0500197 /* Unmap the previous (full) segment */
198 (void) munmap ((u8 *) mm->file_baseva[unmap_index], file_size_in_bytes);
199
200 /* Create a new segment */
Dave Barache9d91702017-11-29 16:59:01 -0500201 fd = open ((char *) mm->filenames[unmap_index],
202 O_CREAT | O_RDWR | O_TRUNC, 0600);
Dave Barach04fee312017-12-01 16:20:32 -0500203
204 /* This is not real error recovery... */
Dave Barache9d91702017-11-29 16:59:01 -0500205 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))
234 * mm->record_size_in_cachelines * CLIB_CACHE_LINE_BYTES;
235
236 return rv;
237}
238
Dave Barach04fee312017-12-01 16:20:32 -0500239/**
Dave Barach55c79e92017-12-05 14:48:56 -0500240 * @brief Update a mapped log header file
Dave Barach04fee312017-12-01 16:20:32 -0500241 *
Dave Barach04fee312017-12-01 16:20:32 -0500242 * Read the log header. Update the number of records, and number of files
Dave Barach04fee312017-12-01 16:20:32 -0500243 * @param[in/out] mm mapped log object
244 */
Dave Barache9d91702017-11-29 16:59:01 -0500245void
Dave Barach55c79e92017-12-05 14:48:56 -0500246clib_maplog_update_header (clib_maplog_main_t * mm)
Dave Barache9d91702017-11-29 16:59:01 -0500247{
Dave Barach55c79e92017-12-05 14:48:56 -0500248 int fd, rv;
Dave Barach04fee312017-12-01 16:20:32 -0500249 clib_maplog_header_t _h, *h = &_h;
Dave Barache9d91702017-11-29 16:59:01 -0500250
251 if (!(mm->flags & CLIB_MAPLOG_FLAG_INIT))
252 return;
253
Dave Barach04fee312017-12-01 16:20:32 -0500254 /* 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;
Dave Barach6ddede72018-03-22 10:54:45 -0400272 h->maplog_flag_wrapped = (mm->flags & CLIB_MAPLOG_FLAG_WRAPPED) ? 1 : 0;
Dave Barach04fee312017-12-01 16:20:32 -0500273
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
285out:
286 if (fd >= 0)
287 (void) close (fd);
Dave Barach55c79e92017-12-05 14:48:56 -0500288}
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 */
298void
299clib_maplog_close (clib_maplog_main_t * mm)
300{
Dave Barach6ddede72018-03-22 10:54:45 -0400301 int i, limit;
Dave Barach55c79e92017-12-05 14:48:56 -0500302 u64 file_size_in_bytes;
303
304 if (!(mm->flags & CLIB_MAPLOG_FLAG_INIT))
305 return;
306
307 clib_maplog_update_header (mm);
308
309 file_size_in_bytes =
310 mm->file_size_in_records * mm->record_size_in_cachelines *
311 CLIB_CACHE_LINE_BYTES;
312
Dave Barach6ddede72018-03-22 10:54:45 -0400313 limit = (mm->flags & CLIB_MAPLOG_FLAG_CIRCULAR) ? 1 : 2;
314
Dave Barach55c79e92017-12-05 14:48:56 -0500315 /* unmap current + next segments */
Dave Barach6ddede72018-03-22 10:54:45 -0400316 for (i = 0; i < limit; i++)
Dave Barach55c79e92017-12-05 14:48:56 -0500317 {
318 (void) munmap ((u8 *) mm->file_baseva[i], file_size_in_bytes);
319 vec_free (mm->filenames[i]);
320 }
Dave Barach04fee312017-12-01 16:20:32 -0500321
Dave Barache9d91702017-11-29 16:59:01 -0500322 vec_free (mm->file_basename);
Dave Barach04fee312017-12-01 16:20:32 -0500323 vec_free (mm->header_filename);
Dave Barache9d91702017-11-29 16:59:01 -0500324 memset (mm, 0, sizeof (*mm));
325}
326
Dave Barach04fee312017-12-01 16:20:32 -0500327/**
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 */
334u8 *
335format_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);
Dave Barach6ddede72018-03-22 10:54:45 -0400343 s = format (s, "log ver %d.%d.%d app id %u ver %d.%d.%d %s %s\n",
Dave Barach04fee312017-12-01 16:20:32 -0500344 h->maplog_major_version,
345 h->maplog_minor_version,
346 h->maplog_patch_version,
347 h->application_id,
348 h->application_major_version,
Dave Barach6ddede72018-03-22 10:54:45 -0400349 h->application_minor_version, h->application_patch_version,
350 h->maplog_flag_circular ? "circular" : "linear",
351 h->maplog_flag_wrapped ? "wrapped" : "not wrapped");
Dave Barach04fee312017-12-01 16:20:32 -0500352 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
359brief:
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 *
Dave Barach55c79e92017-12-05 14:48:56 -0500372 * 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 *
Dave Barach04fee312017-12-01 16:20:32 -0500380 * @param [in] file_basename Same basename supplied to clib_maplog_init
381 * @param [in] fp_arg Callback function pointer
382 */
383int
384clib_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;
Dave Barach55c79e92017-12-05 14:48:56 -0500392 int (*fp) (clib_maplog_header_t *, void *data, u64 count);
Dave Barach04fee312017-12-01 16:20:32 -0500393 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);
Dave Barach545d9ea2017-12-13 11:43:13 -0500415 fd = -1;
Dave Barach04fee312017-12-01 16:20:32 -0500416
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 {
Dave Barach04fee312017-12-01 16:20:32 -0500430 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);
Dave Barach129e80e2017-12-12 08:29:42 -0500437 fd = -1;
Dave Barach04fee312017-12-01 16:20:32 -0500438 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
Dave Barach6ddede72018-03-22 10:54:45 -0400448 /*
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 }
Dave Barach04fee312017-12-01 16:20:32 -0500469
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
481out:
Dave Barach129e80e2017-12-12 08:29:42 -0500482 if (fd >= 0)
Dave Barach04fee312017-12-01 16:20:32 -0500483 (void) close (fd);
484
485 vec_free (this_filename);
486 vec_free (header_filename);
487 return rv;
488}
489
490
Dave Barache9d91702017-11-29 16:59:01 -0500491/*
492 * fd.io coding-style-patch-verification: ON
493 *
494 * Local Variables:
495 * eval: (c-set-style "gnu")
496 * End:
497 */