/* * Copyright (c) 2017 Cisco and/or its affiliates. * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at: * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ #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_init_args_t * a) { 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); clib_memset (mm, 0, sizeof (*mm)); record_size_in_cache_lines = (a->record_size_in_bytes + CLIB_CACHE_LINE_BYTES - 1) / CLIB_CACHE_LINE_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); a->file_size_in_bytes = file_size_in_records * record_size_in_cache_lines * CLIB_CACHE_LINE_BYTES; 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 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) { rv = -3; goto fail; } if (lseek (fd, a->file_size_in_bytes - 1, SEEK_SET) == (off_t) - 1) { rv = -4; goto fail; } if (write (fd, &zero, 1) != 1) { rv = -5; goto fail; } 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) { clib_unix_warning ("mmap"); goto fail; } (void) close (fd); } 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 < limit; i++) { if (mm->file_baseva[i]) (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) { int fd; u8 *rv; u8 zero = 0; u32 unmap_index = (mm->current_file_index) & 1; u64 file_size_in_bytes = mm->file_size_in_records * mm->record_size_in_cachelines * CLIB_CACHE_LINE_BYTES; /* 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_i