deb07aa9b7670630720a9b8037491434718105cb
[vpp.git] / src / vppinfra / maplog.c
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
28 clib_maplog_init (clib_maplog_init_args_t * a)
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;
35   clib_maplog_main_t *mm;
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 =
48     (a->record_size_in_bytes + CLIB_CACHE_LINE_BYTES -
49      1) / CLIB_CACHE_LINE_BYTES;
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
59     * CLIB_CACHE_LINE_BYTES;
60
61   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
68   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++);
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));
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;
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);
157       vec_free (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
172     * mm->record_size_in_cachelines * CLIB_CACHE_LINE_BYTES;
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))
221     * mm->record_size_in_cachelines * CLIB_CACHE_LINE_BYTES;
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
233 clib_maplog_update_header (clib_maplog_main_t * mm)
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... */
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
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
285 clib_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     }
305
306   vec_free (mm->file_basename);
307   vec_free (mm->header_filename);
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",
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
341 brief:
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  *
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
400     * h->record_size_in_cachelines * CLIB_CACHE_LINE_BYTES;
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  */