X-Git-Url: https://gerrit.fd.io/r/gitweb?a=blobdiff_plain;f=src%2Fvppinfra%2Fmaplog.c;h=689133651307b58586422853ad0180e88671fc34;hb=b7b929931a07fbb27b43d5cd105f366c3e29807e;hp=94cc9ac11a8d4ac2711a504301982157df05e7ef;hpb=7a64019a768438cfca4cc9e34d9d8e4c34f54d47;p=vpp.git diff --git a/src/vppinfra/maplog.c b/src/vppinfra/maplog.c index 94cc9ac11a8..68913365130 100644 --- a/src/vppinfra/maplog.c +++ b/src/vppinfra/maplog.c @@ -15,46 +15,75 @@ #include +/** + * @brief Initialize a maplog object + * + * Compute record and file size parameters + * Create and map two log segments to seed the process + * + * @param[in/out] a init args structure + * @return 0 => success, <0 => failure + */ int -clib_maplog_init (clib_maplog_main_t * mm, char *file_basename, - u64 file_size_in_bytes, u32 record_size_in_bytes) +clib_maplog_init (clib_maplog_init_args_t * a) { - int i, fd; + int i, fd, limit; int rv = 0; u8 zero = 0; u32 record_size_in_cache_lines; u64 file_size_in_records; + clib_maplog_main_t *mm; + clib_maplog_header_t _h, *h = &_h; + + ASSERT (a && a->mm); + mm = a->mm; /* Already initialized? */ if (mm->flags & CLIB_MAPLOG_FLAG_INIT) return (-2); - memset (mm, 0, sizeof (*mm)); + clib_memset (mm, 0, sizeof (*mm)); record_size_in_cache_lines = - (record_size_in_bytes + CLIB_CACHE_LINE_BYTES - + (a->record_size_in_bytes + CLIB_CACHE_LINE_BYTES - 1) / CLIB_CACHE_LINE_BYTES; - file_size_in_records = file_size_in_bytes + file_size_in_records = a->file_size_in_bytes / (record_size_in_cache_lines * CLIB_CACHE_LINE_BYTES); /* Round up file size in records to a power of 2, for speed... */ mm->log2_file_size_in_records = max_log2 (file_size_in_records); file_size_in_records = 1ULL << (mm->log2_file_size_in_records); - - file_size_in_bytes = file_size_in_records * record_size_in_cache_lines + a->file_size_in_bytes = file_size_in_records * record_size_in_cache_lines * CLIB_CACHE_LINE_BYTES; - mm->file_basename = format (0, "%s", file_basename); + mm->file_basename = format (0, "%s", a->file_basename); + if (vec_len (mm->file_basename) > ARRAY_LEN (h->file_basename)) + { + vec_free (mm->file_basename); + return -11; + } + mm->file_size_in_records = file_size_in_records; mm->flags |= CLIB_MAPLOG_FLAG_INIT; mm->record_size_in_cachelines = record_size_in_cache_lines; + limit = 2; + if (a->maplog_is_circular) + { + mm->log2_file_size_in_records = 63; + mm->flags |= CLIB_MAPLOG_FLAG_CIRCULAR; + limit = 1; + } - /* Map two files */ - for (i = 0; i < 2; i++) + /* + * Map the one and only file for a circular log, + * two files for a normal log. + */ + for (i = 0; i < limit; i++) { mm->filenames[i] = format (0, "%v_%d", mm->file_basename, mm->current_file_index++); + vec_add1 (mm->filenames[i], 0); fd = open ((char *) mm->filenames[i], O_CREAT | O_RDWR | O_TRUNC, 0600); if (fd < 0) @@ -63,7 +92,7 @@ clib_maplog_init (clib_maplog_main_t * mm, char *file_basename, goto fail; } - if (lseek (fd, file_size_in_bytes - 1, SEEK_SET) == (off_t) - 1) + if (lseek (fd, a->file_size_in_bytes - 1, SEEK_SET) == (off_t) - 1) { rv = -4; goto fail; @@ -74,7 +103,7 @@ clib_maplog_init (clib_maplog_main_t * mm, char *file_basename, goto fail; } - mm->file_baseva[i] = mmap (0, file_size_in_bytes, + mm->file_baseva[i] = mmap (0, a->file_size_in_bytes, PROT_READ | PROT_WRITE, MAP_SHARED, fd, 0); if (mm->file_baseva[i] == (u8 *) MAP_FAILED) { @@ -83,20 +112,65 @@ clib_maplog_init (clib_maplog_main_t * mm, char *file_basename, } (void) close (fd); } - return rv; + + clib_memset (h, 0, sizeof (*h)); + h->maplog_major_version = MAPLOG_MAJOR_VERSION; + h->maplog_minor_version = MAPLOG_MINOR_VERSION; + h->maplog_patch_version = MAPLOG_PATCH_VERSION; + h->application_id = a->application_id; + h->application_major_version = a->application_major_version; + h->application_minor_version = a->application_minor_version; + h->application_patch_version = a->application_patch_version; + h->record_size_in_cachelines = record_size_in_cache_lines; + h->cacheline_size = CLIB_CACHE_LINE_BYTES; + h->file_size_in_records = file_size_in_records; + h->number_of_records = ~0ULL; + h->number_of_files = ~0ULL; + h->maplog_flag_circular = a->maplog_is_circular; + memcpy (h->file_basename, mm->file_basename, vec_len (mm->file_basename)); + + mm->header_filename = format (0, "%v_header", mm->file_basename); + vec_add1 (mm->header_filename, 0); + + fd = open ((char *) mm->header_filename, O_CREAT | O_RDWR | O_TRUNC, 0600); + if (fd < 0) + { + clib_unix_warning ("header create"); + rv = -6; + goto fail; + } + rv = write (fd, h, sizeof (*h)); + if (rv != sizeof (*h)) + { + clib_unix_warning ("header write"); + rv = -7; + goto fail; + } + (void) close (fd); + return 0; fail: if (fd >= 0) (void) close (fd); - for (i = 0; i < 2; i++) + for (i = 0; i < limit; i++) { if (mm->file_baseva[i]) - (void) munmap ((u8 *) mm->file_baseva[i], file_size_in_bytes); + (void) munmap ((u8 *) mm->file_baseva[i], a->file_size_in_bytes); + if (mm->filenames[i]) + (void) unlink ((char *) mm->filenames[i]); + vec_free (mm->filenames[i]); + } + if (mm->header_filename) + { + (void) unlink ((char *) mm->header_filename); + vec_free (mm->header_filename); } return rv; } +/* slow path: unmap a full log segment, and replace it */ + u8 * _clib_maplog_get_entry_slowpath (clib_maplog_main_t * mm, u64 my_record_index) { @@ -107,16 +181,27 @@ _clib_maplog_get_entry_slowpath (clib_maplog_main_t * mm, u64 my_record_index) u64 file_size_in_bytes = mm->file_size_in_records * mm->record_size_in_cachelines * CLIB_CACHE_LINE_BYTES; - (void) munmap ((u8 *) mm->file_baseva[unmap_index], file_size_in_bytes); - vec_reset_length (mm->filenames[unmap_index]); + /* This should never happen */ + ASSERT ((mm->flags & CLIB_MAPLOG_FLAG_CIRCULAR) == 0); + /* + * Kill some time by calling format before we make the previous log + * segment disappear. Obviously it won't do to call clib_maplog_get_entry(), + * wait 100ms, and then fill in the log entry. + */ + vec_reset_length (mm->filenames[unmap_index]); mm->filenames[unmap_index] = format (mm->filenames[unmap_index], "%v_%d", mm->file_basename, mm->current_file_index++); + /* Unmap the previous (full) segment */ + (void) munmap ((u8 *) mm->file_baseva[unmap_index], file_size_in_bytes); + + /* Create a new segment */ fd = open ((char *) mm->filenames[unmap_index], O_CREAT | O_RDWR | O_TRUNC, 0600); - /* $$$ this is not real error recovery... */ + + /* This is not real error recovery... */ if (fd < 0) { clib_unix_warning ("creat"); @@ -151,29 +236,258 @@ _clib_maplog_get_entry_slowpath (clib_maplog_main_t * mm, u64 my_record_index) return rv; } +/** + * @brief Update a mapped log header file + * + * Read the log header. Update the number of records, and number of files + * @param[in/out] mm mapped log object + */ +void +clib_maplog_update_header (clib_maplog_main_t * mm) +{ + int fd, rv; + clib_maplog_header_t _h, *h = &_h; + + if (!(mm->flags & CLIB_MAPLOG_FLAG_INIT)) + return; + + /* Open the log header */ + fd = open ((char *) mm->header_filename, O_RDWR, 0600); + if (fd < 0) + { + clib_unix_warning ("reopen maplog header"); + goto out; + } + + /* Read the log */ + rv = read (fd, h, sizeof (*h)); + if (rv != sizeof (*h)) + { + clib_unix_warning ("read maplog header"); + goto out; + } + /* Fix the header... */ + h->number_of_records = mm->next_record_index; + h->number_of_files = mm->current_file_index; + h->maplog_flag_wrapped = (mm->flags & CLIB_MAPLOG_FLAG_WRAPPED) ? 1 : 0; + + /* Back to the beginning of the log header... */ + if (lseek (fd, 0, SEEK_SET) < 0) + { + clib_unix_warning ("lseek to rewrite header"); + goto out; + } + /* Rewrite the log header */ + rv = write (fd, h, sizeof (*h)); + if (rv != sizeof (*h)) + clib_unix_warning ("rewrite header"); + +out: + if (fd >= 0) + (void) close (fd); +} + +/** + * @brief Close a mapped log, and update the log header file + * + * Unmap the current log segments. + * Read the log header. Update the number of records, and number of files + * + * @param[in/out] mm mapped log object + */ void clib_maplog_close (clib_maplog_main_t * mm) { - int i; + int i, limit; u64 file_size_in_bytes; if (!(mm->flags & CLIB_MAPLOG_FLAG_INIT)) return; + clib_maplog_update_header (mm); + file_size_in_bytes = mm->file_size_in_records * mm->record_size_in_cachelines * CLIB_CACHE_LINE_BYTES; - for (i = 0; i < 2; i++) + limit = (mm->flags & CLIB_MAPLOG_FLAG_CIRCULAR) ? 1 : 2; + + /* unmap current + next segments */ + for (i = 0; i < limit; i++) { (void) munmap ((u8 *) mm->file_baseva[i], file_size_in_bytes); vec_free (mm->filenames[i]); } vec_free (mm->file_basename); - memset (mm, 0, sizeof (*mm)); + vec_free (mm->header_filename); + clib_memset (mm, 0, sizeof (*mm)); } +/** + * @brief format a log header + * + * Usage: s = format (0, "%U", format_maplog_header, headerp, verbose); + * @param [in] h clib_maplog_header_t pointer + * @param [in] verbose self-explanatory + */ +u8 * +format_maplog_header (u8 * s, va_list * args) +{ + clib_maplog_header_t *h = va_arg (*args, clib_maplog_header_t *); + int verbose = va_arg (*args, int); + + if (!verbose) + goto brief; + s = format (s, "basename %s ", h->file_basename); + s = format (s, "log ver %d.%d.%d app id %u ver %d.%d.%d %s %s\n", + h->maplog_major_version, + h->maplog_minor_version, + h->maplog_patch_version, + h->application_id, + h->application_major_version, + h->application_minor_version, h->application_patch_version, + h->maplog_flag_circular ? "circular" : "linear", + h->maplog_flag_wrapped ? "wrapped" : "not wrapped"); + s = format (s, " records are %d %d-byte cachelines\n", + h->record_size_in_cachelines, h->cacheline_size); + s = format (s, " files are %lld records long, %lld files\n", + h->file_size_in_records, h->number_of_files); + s = format (s, " %lld records total\n", h->number_of_records); + return s; + +brief: + s = format (s, "%s %lld records %lld files %lld records/file", + h->file_basename, h->number_of_records, h->number_of_files, + h->file_size_in_records); + return s; +} + +/** + * @brief Process a complete maplog + * + * Reads the maplog header. Map and process all log segments in order. + * Calls the callback function once per file with a record count. + * + * Note: if the file header isn't updated by calling + * clib_maplog_close(), it will appear to have an infinite + * number of records in an infinite number of files. + * + * So long as the callback function understands that possibility + * - by simply ignoring NULL records - the scheme still + * works... + * + * @param [in] file_basename Same basename supplied to clib_maplog_init + * @param [in] fp_arg Callback function pointer + */ +int +clib_maplog_process (char *file_basename, void *fp_arg) +{ + clib_maplog_header_t _h, *h = &_h; + int fd, rv = 0; + u64 file_index; + u64 file_size_in_bytes; + u8 *header_filename, *this_filename = 0; + u8 *file_baseva; + int (*fp) (clib_maplog_header_t *, void *data, u64 count); + u64 records_this_file, records_left; + ASSERT (fp_arg); + + fp = fp_arg; + + header_filename = format (0, "%s_header%c", file_basename, 0); + + fd = open ((char *) header_filename, O_RDONLY, 0600); + if (fd < 0) + { + clib_unix_warning ("open maplog header"); + rv = -1; + goto out; + } + rv = read (fd, h, sizeof (*h)); + if (rv != sizeof (*h)) + { + clib_unix_warning ("read maplog header"); + rv = -2; + goto out; + } + (void) close (fd); + fd = -1; + + file_size_in_bytes = h->file_size_in_records + * h->record_size_in_cachelines * CLIB_CACHE_LINE_BYTES; + + records_left = h->number_of_records; + + for (file_index = 0; file_index < h->number_of_files; file_index++) + { + vec_reset_length (this_filename); + this_filename = format (this_filename, "%s_%llu%c", file_basename, + file_index, 0); + fd = open ((char *) this_filename, O_RDONLY, 0600); + if (fd < 0) + { + rv = -3; + goto out; + } + + file_baseva = + mmap (0, file_size_in_bytes, PROT_READ, MAP_SHARED, fd, 0); + (void) close (fd); + fd = -1; + if (file_baseva == (u8 *) MAP_FAILED) + { + clib_unix_warning ("mmap"); + rv = -4; + goto out; + } + + records_this_file = (records_left > h->file_size_in_records) ? + h->file_size_in_records : records_left; + + /* + * Normal log, or a circular non-wrapped log, or a circular + * wrapped log which happens to be exactly linear + */ + if (h->maplog_flag_circular == 0 || h->maplog_flag_wrapped == 0 || + ((h->number_of_records % h->file_size_in_records) == 0)) + (*fp) (h, file_baseva, records_this_file); + else + { + /* "Normal" wrapped circular log */ + u64 first_chunk_record_index = h->number_of_records & + (h->file_size_in_records - 1); + u64 first_chunk_number_of_records = records_this_file - + first_chunk_record_index; + u8 *chunk_baseva = file_baseva + + first_chunk_record_index * h->record_size_in_cachelines * + h->cacheline_size; + (*fp) (h, chunk_baseva, first_chunk_number_of_records); + (*fp) (h, file_baseva, + records_this_file - first_chunk_number_of_records); + } + + if (munmap (file_baseva, file_size_in_bytes) < 0) + { + clib_warning ("munmap"); + rv = -5; + /* but don't stop... */ + } + records_left -= records_this_file; + if (records_left == 0) + break; + } + +out: + if (fd >= 0) + (void) close (fd); + + vec_free (this_filename); + vec_free (header_filename); + return rv; +} + + /* * fd.io coding-style-patch-verification: ON *