blob: 158361b0bdac41650f67ee26fe57aa667af742a8 [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{
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;
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);
57
Dave Barach04fee312017-12-01 16:20:32 -050058 a->file_size_in_bytes = file_size_in_records * record_size_in_cache_lines
Dave Barache9d91702017-11-29 16:59:01 -050059 * CLIB_CACHE_LINE_BYTES;
60
Dave Barach04fee312017-12-01 16:20:32 -050061 mm->file_basename = format (0, "%s", a->file_basename);
62 if (vec_len (mm->file_basename) > ARRAY_LEN (h->file_basename))
63 {
64 vec_free (mm->file_basename);
65 return -11;
66 }
67
Dave Barache9d91702017-11-29 16:59:01 -050068 mm->file_size_in_records = file_size_in_records;
69 mm->flags |= CLIB_MAPLOG_FLAG_INIT;
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++);
Dave Barach04fee312017-12-01 16:20:32 -050077 vec_add1 (mm->filenames[i], 0);
Dave Barache9d91702017-11-29 16:59:01 -050078
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
Dave Barach04fee312017-12-01 16:20:32 -050086 if (lseek (fd, a->file_size_in_bytes - 1, SEEK_SET) == (off_t) - 1)
Dave Barache9d91702017-11-29 16:59:01 -050087 {
88 rv = -4;
89 goto fail;
90 }
91 if (write (fd, &zero, 1) != 1)
92 {
93 rv = -5;
94 goto fail;
95 }
96
Dave Barach04fee312017-12-01 16:20:32 -050097 mm->file_baseva[i] = mmap (0, a->file_size_in_bytes,
Dave Barache9d91702017-11-29 16:59:01 -050098 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 }
Dave Barach04fee312017-12-01 16:20:32 -0500106
107 memset (h, 0, sizeof (*h));
108 h->maplog_major_version = MAPLOG_MAJOR_VERSION;
109 h->maplog_minor_version = MAPLOG_MINOR_VERSION;
110 h->maplog_patch_version = MAPLOG_PATCH_VERSION;
111 h->application_id = a->application_id;
112 h->application_major_version = a->application_major_version;
113 h->application_minor_version = a->application_minor_version;
114 h->application_patch_version = a->application_patch_version;
115 h->record_size_in_cachelines = record_size_in_cache_lines;
116 h->cacheline_size = CLIB_CACHE_LINE_BYTES;
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;
Dave Barache9d91702017-11-29 16:59:01 -0500141
142fail:
Dave Barach7a640192017-12-01 10:24:28 -0500143 if (fd >= 0)
Dave Barache9d91702017-11-29 16:59:01 -0500144 (void) close (fd);
145
146 for (i = 0; i < 2; i++)
147 {
148 if (mm->file_baseva[i])
Dave Barach04fee312017-12-01 16:20:32 -0500149 (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);
157 vec_free (mm->header_filename);
Dave Barache9d91702017-11-29 16:59:01 -0500158 }
159 return rv;
160}
161
Dave Barach04fee312017-12-01 16:20:32 -0500162/* slow path: unmap a full log segment, and replace it */
163
Dave Barache9d91702017-11-29 16:59:01 -0500164u8 *
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
172 * mm->record_size_in_cachelines * CLIB_CACHE_LINE_BYTES;
173
Dave Barach04fee312017-12-01 16:20:32 -0500174 /*
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 */
Dave Barache9d91702017-11-29 16:59:01 -0500179 vec_reset_length (mm->filenames[unmap_index]);
Dave Barache9d91702017-11-29 16:59:01 -0500180 mm->filenames[unmap_index] = format (mm->filenames[unmap_index],
181 "%v_%d", mm->file_basename,
182 mm->current_file_index++);
183
Dave Barach04fee312017-12-01 16:20:32 -0500184 /* Unmap the previous (full) segment */
185 (void) munmap ((u8 *) mm->file_baseva[unmap_index], file_size_in_bytes);
186
187 /* Create a new segment */
Dave Barache9d91702017-11-29 16:59:01 -0500188 fd = open ((char *) mm->filenames[unmap_index],
189 O_CREAT | O_RDWR | O_TRUNC, 0600);
Dave Barach04fee312017-12-01 16:20:32 -0500190
191 /* This is not real error recovery... */
Dave Barache9d91702017-11-29 16:59:01 -0500192 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))
221 * mm->record_size_in_cachelines * CLIB_CACHE_LINE_BYTES;
222
223 return rv;
224}
225
Dave Barach04fee312017-12-01 16:20:32 -0500226/**
Dave Barach55c79e92017-12-05 14:48:56 -0500227 * @brief Update a mapped log header file
Dave Barach04fee312017-12-01 16:20:32 -0500228 *
Dave Barach04fee312017-12-01 16:20:32 -0500229 * Read the log header. Update the number of records, and number of files
Dave Barach04fee312017-12-01 16:20:32 -0500230 * @param[in/out] mm mapped log object
231 */
Dave Barache9d91702017-11-29 16:59:01 -0500232void
Dave Barach55c79e92017-12-05 14:48:56 -0500233clib_maplog_update_header (clib_maplog_main_t * mm)
Dave Barache9d91702017-11-29 16:59:01 -0500234{
Dave Barach55c79e92017-12-05 14:48:56 -0500235 int fd, rv;
Dave Barach04fee312017-12-01 16:20:32 -0500236 clib_maplog_header_t _h, *h = &_h;
Dave Barache9d91702017-11-29 16:59:01 -0500237
238 if (!(mm->flags & CLIB_MAPLOG_FLAG_INIT))
239 return;
240
Dave Barach04fee312017-12-01 16:20:32 -0500241 /* 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... */
257 h->number_of_records = mm->next_record_index;
258 h->number_of_files = mm->current_file_index;
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
271out:
272 if (fd >= 0)
273 (void) close (fd);
Dave Barach55c79e92017-12-05 14:48:56 -0500274}
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 */
284void
285clib_maplog_close (clib_maplog_main_t * mm)
286{
287 int i;
288 u64 file_size_in_bytes;
289
290 if (!(mm->flags & CLIB_MAPLOG_FLAG_INIT))
291 return;
292
293 clib_maplog_update_header (mm);
294
295 file_size_in_bytes =
296 mm->file_size_in_records * mm->record_size_in_cachelines *
297 CLIB_CACHE_LINE_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 }
Dave Barach04fee312017-12-01 16:20:32 -0500305
Dave Barache9d91702017-11-29 16:59:01 -0500306 vec_free (mm->file_basename);
Dave Barach04fee312017-12-01 16:20:32 -0500307 vec_free (mm->header_filename);
Dave Barache9d91702017-11-29 16:59:01 -0500308 memset (mm, 0, sizeof (*mm));
309}
310
Dave Barach04fee312017-12-01 16:20:32 -0500311/**
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 */
318u8 *
319format_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",
328 h->maplog_major_version,
329 h->maplog_minor_version,
330 h->maplog_patch_version,
331 h->application_id,
332 h->application_major_version,
333 h->application_minor_version, h->application_patch_version);
334 s = format (s, " records are %d %d-byte cachelines\n",
335 h->record_size_in_cachelines, h->cacheline_size);
336 s = format (s, " files are %lld records long, %lld files\n",
337 h->file_size_in_records, h->number_of_files);
338 s = format (s, " %lld records total\n", h->number_of_records);
339 return s;
340
341brief:
342 s = format (s, "%s %lld records %lld files %lld records/file",
343 h->file_basename, h->number_of_records, h->number_of_files,
344 h->file_size_in_records);
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 *
Dave Barach55c79e92017-12-05 14:48:56 -0500354 * 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 *
Dave Barach04fee312017-12-01 16:20:32 -0500362 * @param [in] file_basename Same basename supplied to clib_maplog_init
363 * @param [in] fp_arg Callback function pointer
364 */
365int
366clib_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;
Dave Barach55c79e92017-12-05 14:48:56 -0500374 int (*fp) (clib_maplog_header_t *, void *data, u64 count);
Dave Barach04fee312017-12-01 16:20:32 -0500375 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
398 file_size_in_bytes = h->file_size_in_records
399 * h->record_size_in_cachelines * CLIB_CACHE_LINE_BYTES;
400
401 records_left = h->number_of_records;
402
403 for (file_index = 0; file_index < h->number_of_files; file_index++)
404 {
405 vec_reset_length (this_filename);
406 this_filename = format (this_filename, "%s_%llu%c", file_basename,
407 file_index, 0);
408 fd = open ((char *) this_filename, O_RDONLY, 0600);
409 if (fd < 0)
410 {
Dave Barach04fee312017-12-01 16:20:32 -0500411 rv = -3;
412 goto out;
413 }
414
415 file_baseva =
416 mmap (0, file_size_in_bytes, PROT_READ, MAP_SHARED, fd, 0);
417 (void) close (fd);
Dave Barach129e80e2017-12-12 08:29:42 -0500418 fd = -1;
Dave Barach04fee312017-12-01 16:20:32 -0500419 if (file_baseva == (u8 *) MAP_FAILED)
420 {
421 clib_unix_warning ("mmap");
422 rv = -4;
423 goto out;
424 }
425
426 records_this_file = (records_left > h->file_size_in_records) ?
427 h->file_size_in_records : records_left;
428
429 (*fp) (h, file_baseva, records_this_file);
430
431 if (munmap (file_baseva, file_size_in_bytes) < 0)
432 {
433 clib_warning ("munmap");
434 rv = -5;
435 /* but don't stop... */
436 }
437 records_left -= records_this_file;
438 if (records_left == 0)
439 break;
440 }
441
442out:
Dave Barach129e80e2017-12-12 08:29:42 -0500443 if (fd >= 0)
Dave Barach04fee312017-12-01 16:20:32 -0500444 (void) close (fd);
445
446 vec_free (this_filename);
447 vec_free (header_filename);
448 return rv;
449}
450
451
Dave Barache9d91702017-11-29 16:59:01 -0500452/*
453 * fd.io coding-style-patch-verification: ON
454 *
455 * Local Variables:
456 * eval: (c-set-style "gnu")
457 * End:
458 */