LCOV - code coverage report
Current view: top level - vppinfra - maplog.c (source / functions) Hit Total Coverage
Test: coverage-filtered.info Lines: 0 240 0.0 %
Date: 2023-07-05 22:20:52 Functions: 0 6 0.0 %

          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             :  */

Generated by: LCOV version 1.14