Line data Source code
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
28 0 : clib_maplog_init (clib_maplog_init_args_t *a)
29 : {
30 : int i, fd, limit;
31 0 : int rv = 0;
32 0 : u8 zero = 0;
33 : u32 record_size_in_cache_lines;
34 : u64 file_size_in_records;
35 : clib_maplog_main_t *mm;
36 0 : clib_maplog_header_t _h, *h = &_h;
37 :
38 0 : ASSERT (a && a->mm);
39 0 : mm = a->mm;
40 :
41 : /* Already initialized? */
42 0 : if (mm->flags & CLIB_MAPLOG_FLAG_INIT)
43 0 : return (-2);
44 :
45 0 : clib_memset (mm, 0, sizeof (*mm));
46 :
47 0 : record_size_in_cache_lines =
48 0 : (a->record_size_in_bytes + CLIB_CACHE_LINE_BYTES -
49 : 1) / CLIB_CACHE_LINE_BYTES;
50 :
51 0 : file_size_in_records = a->file_size_in_bytes
52 0 : / (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 0 : mm->log2_file_size_in_records = max_log2 (file_size_in_records);
56 0 : file_size_in_records = 1ULL << (mm->log2_file_size_in_records);
57 0 : a->file_size_in_bytes = file_size_in_records * record_size_in_cache_lines
58 0 : * CLIB_CACHE_LINE_BYTES;
59 :
60 0 : mm->file_basename = format (0, "%s", a->file_basename);
61 0 : if (vec_len (mm->file_basename) > ARRAY_LEN (h->file_basename))
62 : {
63 0 : vec_free (mm->file_basename);
64 0 : return -11;
65 : }
66 :
67 0 : mm->file_size_in_records = file_size_in_records;
68 0 : mm->flags |= CLIB_MAPLOG_FLAG_INIT;
69 0 : mm->record_size_in_cachelines = record_size_in_cache_lines;
70 0 : limit = 2;
71 0 : if (a->maplog_is_circular)
72 : {
73 0 : mm->log2_file_size_in_records = 63;
74 0 : mm->flags |= CLIB_MAPLOG_FLAG_CIRCULAR;
75 0 : 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 0 : for (i = 0; i < limit; i++)
83 : {
84 0 : mm->filenames[i] = format (0, "%v_%d", mm->file_basename,
85 0 : mm->current_file_index++);
86 0 : vec_add1 (mm->filenames[i], 0);
87 :
88 0 : fd = open ((char *) mm->filenames[i], O_CREAT | O_RDWR | O_TRUNC, 0600);
89 0 : if (fd < 0)
90 : {
91 0 : rv = -3;
92 0 : goto fail;
93 : }
94 :
95 0 : if (lseek (fd, a->file_size_in_bytes - 1, SEEK_SET) == (off_t) - 1)
96 : {
97 0 : rv = -4;
98 0 : goto fail;
99 : }
100 0 : if (write (fd, &zero, 1) != 1)
101 : {
102 0 : rv = -5;
103 0 : goto fail;
104 : }
105 :
106 0 : mm->file_baseva[i] = mmap (0, a->file_size_in_bytes,
107 : PROT_READ | PROT_WRITE, MAP_SHARED, fd, 0);
108 0 : if (mm->file_baseva[i] == (u8 *) MAP_FAILED)
109 : {
110 0 : clib_unix_warning ("mmap");
111 0 : goto fail;
112 : }
113 0 : (void) close (fd);
114 : }
115 :
116 0 : clib_memset (h, 0, sizeof (*h));
117 0 : h->maplog_major_version = MAPLOG_MAJOR_VERSION;
118 0 : h->maplog_minor_version = MAPLOG_MINOR_VERSION;
119 0 : h->maplog_patch_version = MAPLOG_PATCH_VERSION;
120 0 : h->application_id = a->application_id;
121 0 : h->application_major_version = a->application_major_version;
122 0 : h->application_minor_version = a->application_minor_version;
123 0 : h->application_patch_version = a->application_patch_version;
124 0 : h->record_size_in_cachelines = record_size_in_cache_lines;
125 0 : h->cacheline_size = CLIB_CACHE_LINE_BYTES;
126 0 : h->file_size_in_records = file_size_in_records;
127 0 : h->number_of_records = ~0ULL;
128 0 : h->number_of_files = ~0ULL;
129 0 : h->maplog_flag_circular = a->maplog_is_circular;
130 0 : memcpy (h->file_basename, mm->file_basename, vec_len (mm->file_basename));
131 :
132 0 : mm->header_filename = format (0, "%v_header", mm->file_basename);
133 0 : vec_add1 (mm->header_filename, 0);
134 :
135 0 : fd = open ((char *) mm->header_filename, O_CREAT | O_RDWR | O_TRUNC, 0600);
136 0 : if (fd < 0)
137 : {
138 0 : clib_unix_warning ("header create");
139 0 : rv = -6;
140 0 : goto fail;
141 : }
142 0 : rv = write (fd, h, sizeof (*h));
143 0 : if (rv != sizeof (*h))
144 : {
145 0 : clib_unix_warning ("header write");
146 0 : rv = -7;
147 0 : goto fail;
148 : }
149 0 : (void) close (fd);
150 0 : return 0;
151 :
152 0 : fail:
153 0 : if (fd >= 0)
154 0 : (void) close (fd);
155 :
156 0 : for (i = 0; i < limit; i++)
157 : {
158 0 : if (mm->file_baseva[i])
159 0 : (void) munmap ((u8 *) mm->file_baseva[i], a->file_size_in_bytes);
160 0 : if (mm->filenames[i])
161 0 : (void) unlink ((char *) mm->filenames[i]);
162 0 : vec_free (mm->filenames[i]);
163 : }
164 0 : if (mm->header_filename)
165 : {
166 0 : (void) unlink ((char *) mm->header_filename);
167 0 : vec_free (mm->header_filename);
168 : }
169 0 : return rv;
170 : }
171 :
172 : /* slow path: unmap a full log segment, and replace it */
173 :
174 : __clib_export u8 *
175 0 : _clib_maplog_get_entry_slowpath (clib_maplog_main_t *mm, u64 my_record_index)
176 : {
177 : int fd;
178 : u8 *rv;
179 0 : u8 zero = 0;
180 0 : u32 unmap_index = (mm->current_file_index) & 1;
181 0 : u64 file_size_in_bytes = mm->file_size_in_records
182 0 : * mm->record_size_in_cachelines * CLIB_CACHE_LINE_BYTES;
183 :
184 : /* This should never happen */
185 0 : 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 0 : vec_reset_length (mm->filenames[unmap_index]);
193 0 : mm->filenames[unmap_index] = format (mm->filenames[unmap_index],
194 : "%v_%d", mm->file_basename,
195 0 : mm->current_file_index++);
196 :
197 : /* Unmap the previous (full) segment */
198 0 : (void) munmap ((u8 *) mm->file_baseva[unmap_index], file_size_in_bytes);
199 :
200 : /* Create a new segment */
201 0 : fd = open ((char *) mm->filenames[unmap_index],
202 : O_CREAT | O_RDWR | O_TRUNC, 0600);
203 :
204 : /* This is not real error recovery... */
205 0 : if (fd < 0)
206 : {
207 0 : clib_unix_warning ("creat");
208 0 : abort ();
209 : }
210 :
211 0 : if (lseek (fd, file_size_in_bytes - 1, SEEK_SET) == (off_t) - 1)
212 : {
213 0 : clib_unix_warning ("lseek");
214 0 : abort ();
215 : }
216 0 : if (write (fd, &zero, 1) != 1)
217 : {
218 0 : clib_unix_warning ("set-size write");
219 0 : abort ();
220 : }
221 :
222 0 : mm->file_baseva[unmap_index] =
223 0 : mmap (0, file_size_in_bytes, PROT_READ | PROT_WRITE, MAP_SHARED, fd, 0);
224 0 : if (mm->file_baseva[unmap_index] == (u8 *) MAP_FAILED)
225 : {
226 0 : clib_unix_warning ("mmap");
227 0 : abort ();
228 : }
229 0 : (void) close (fd);
230 :
231 0 : rv = (u8 *)
232 0 : mm->file_baseva[(my_record_index >> mm->log2_file_size_in_records) & 1] +
233 0 : (my_record_index & (mm->file_size_in_records - 1))
234 0 : * mm->record_size_in_cachelines * CLIB_CACHE_LINE_BYTES;
235 :
236 0 : 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
246 0 : clib_maplog_update_header (clib_maplog_main_t * mm)
247 : {
248 : int fd, rv;
249 0 : clib_maplog_header_t _h, *h = &_h;
250 :
251 0 : if (!(mm->flags & CLIB_MAPLOG_FLAG_INIT))
252 0 : return;
253 :
254 : /* Open the log header */
255 0 : fd = open ((char *) mm->header_filename, O_RDWR, 0600);
256 0 : if (fd < 0)
257 : {
258 0 : clib_unix_warning ("reopen maplog header");
259 0 : goto out;
260 : }
261 :
262 : /* Read the log */
263 0 : rv = read (fd, h, sizeof (*h));
264 0 : if (rv != sizeof (*h))
265 : {
266 0 : clib_unix_warning ("read maplog header");
267 0 : goto out;
268 : }
269 : /* Fix the header... */
270 0 : h->number_of_records = mm->next_record_index;
271 0 : h->number_of_files = mm->current_file_index;
272 0 : h->maplog_flag_wrapped = (mm->flags & CLIB_MAPLOG_FLAG_WRAPPED) ? 1 : 0;
273 :
274 : /* Back to the beginning of the log header... */
275 0 : if (lseek (fd, 0, SEEK_SET) < 0)
276 : {
277 0 : clib_unix_warning ("lseek to rewrite header");
278 0 : goto out;
279 : }
280 : /* Rewrite the log header */
281 0 : rv = write (fd, h, sizeof (*h));
282 0 : if (rv != sizeof (*h))
283 0 : clib_unix_warning ("rewrite header");
284 :
285 0 : out:
286 0 : if (fd >= 0)
287 0 : (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
299 0 : clib_maplog_close (clib_maplog_main_t *mm)
300 : {
301 : int i, limit;
302 : u64 file_size_in_bytes;
303 :
304 0 : if (!(mm->flags & CLIB_MAPLOG_FLAG_INIT))
305 0 : return;
306 :
307 0 : clib_maplog_update_header (mm);
308 :
309 0 : file_size_in_bytes =
310 0 : mm->file_size_in_records * mm->record_size_in_cachelines *
311 : CLIB_CACHE_LINE_BYTES;
312 :
313 0 : limit = (mm->flags & CLIB_MAPLOG_FLAG_CIRCULAR) ? 1 : 2;
314 :
315 : /* unmap current + next segments */
316 0 : for (i = 0; i < limit; i++)
317 : {
318 0 : (void) munmap ((u8 *) mm->file_baseva[i], file_size_in_bytes);
319 0 : vec_free (mm->filenames[i]);
320 : }
321 :
322 0 : vec_free (mm->file_basename);
323 0 : vec_free (mm->header_filename);
324 0 : 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 0 : format_maplog_header (u8 *s, va_list *args)
336 : {
337 0 : clib_maplog_header_t *h = va_arg (*args, clib_maplog_header_t *);
338 0 : int verbose = va_arg (*args, int);
339 :
340 0 : if (!verbose)
341 0 : goto brief;
342 0 : s = format (s, "basename %s ", h->file_basename);
343 0 : s = format (s, "log ver %d.%d.%d app id %u ver %d.%d.%d %s %s\n",
344 0 : h->maplog_major_version,
345 0 : h->maplog_minor_version,
346 0 : h->maplog_patch_version,
347 : h->application_id,
348 0 : h->application_major_version,
349 0 : h->application_minor_version, h->application_patch_version,
350 0 : h->maplog_flag_circular ? "circular" : "linear",
351 0 : h->maplog_flag_wrapped ? "wrapped" : "not wrapped");
352 0 : s = format (s, " records are %d %d-byte cachelines\n",
353 : h->record_size_in_cachelines, h->cacheline_size);
354 0 : s = format (s, " files are %lld records long, %lld files\n",
355 : h->file_size_in_records, h->number_of_files);
356 0 : s = format (s, " %lld records total\n", h->number_of_records);
357 0 : return s;
358 :
359 0 : brief:
360 0 : s = format (s, "%s %lld records %lld files %lld records/file",
361 0 : h->file_basename, h->number_of_records, h->number_of_files,
362 : h->file_size_in_records);
363 0 : 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 0 : clib_maplog_process (char *file_basename, void *fp_arg)
385 : {
386 0 : clib_maplog_header_t _h, *h = &_h;
387 0 : int fd, rv = 0;
388 : u64 file_index;
389 : u64 file_size_in_bytes;
390 0 : 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 0 : ASSERT (fp_arg);
395 :
396 0 : fp = fp_arg;
397 :
398 0 : header_filename = format (0, "%s_header%c", file_basename, 0);
399 :
400 0 : fd = open ((char *) header_filename, O_RDONLY, 0600);
401 0 : if (fd < 0)
402 : {
403 0 : clib_unix_warning ("open maplog header");
404 0 : rv = -1;
405 0 : goto out;
406 : }
407 0 : rv = read (fd, h, sizeof (*h));
408 0 : if (rv != sizeof (*h))
409 : {
410 0 : clib_unix_warning ("read maplog header");
411 0 : rv = -2;
412 0 : goto out;
413 : }
414 0 : (void) close (fd);
415 0 : fd = -1;
416 :
417 0 : file_size_in_bytes = h->file_size_in_records
418 0 : * h->record_size_in_cachelines * CLIB_CACHE_LINE_BYTES;
419 :
420 0 : records_left = h->number_of_records;
421 :
422 0 : for (file_index = 0; file_index < h->number_of_files; file_index++)
423 : {
424 0 : vec_reset_length (this_filename);
425 0 : this_filename = format (this_filename, "%s_%llu%c", file_basename,
426 : file_index, 0);
427 0 : fd = open ((char *) this_filename, O_RDONLY, 0600);
428 0 : if (fd < 0)
429 : {
430 0 : rv = -3;
431 0 : goto out;
432 : }
433 :
434 : file_baseva =
435 0 : mmap (0, file_size_in_bytes, PROT_READ, MAP_SHARED, fd, 0);
436 0 : (void) close (fd);
437 0 : fd = -1;
438 0 : if (file_baseva == (u8 *) MAP_FAILED)
439 : {
440 0 : clib_unix_warning ("mmap");
441 0 : rv = -4;
442 0 : goto out;
443 : }
444 :
445 0 : 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 0 : if (h->maplog_flag_circular == 0 || h->maplog_flag_wrapped == 0 ||
453 0 : ((h->number_of_records % h->file_size_in_records) == 0))
454 0 : (*fp) (h, file_baseva, records_this_file);
455 : else
456 : {
457 : /* "Normal" wrapped circular log */
458 0 : u64 first_chunk_record_index = h->number_of_records &
459 0 : (h->file_size_in_records - 1);
460 0 : u64 first_chunk_number_of_records = records_this_file -
461 : first_chunk_record_index;
462 0 : u8 *chunk_baseva = file_baseva +
463 0 : first_chunk_record_index * h->record_size_in_cachelines *
464 0 : h->cacheline_size;
465 0 : (*fp) (h, chunk_baseva, first_chunk_number_of_records);
466 0 : (*fp) (h, file_baseva,
467 : records_this_file - first_chunk_number_of_records);
468 : }
469 :
470 0 : if (munmap (file_baseva, file_size_in_bytes) < 0)
471 : {
472 0 : clib_warning ("munmap");
473 0 : rv = -5;
474 : /* but don't stop... */
475 : }
476 0 : records_left -= records_this_file;
477 0 : if (records_left == 0)
478 0 : break;
479 : }
480 :
481 0 : out:
482 0 : if (fd >= 0)
483 0 : (void) close (fd);
484 :
485 0 : vec_free (this_filename);
486 0 : vec_free (header_filename);
487 0 : 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 : */
|