| /* |
| * Copyright (c) 2020 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 <vppinfra/clib.h> |
| #include <vppinfra/mem.h> |
| #include <vppinfra/time.h> |
| #include <vppinfra/format.h> |
| #include <vppinfra/clib_error.h> |
| |
| /* while usage of dlmalloc APIs is genrally discouraged, in this particular |
| * case there is significant benefit of calling them directly due to |
| * smaller memory consuption (no wwp and headroom space) */ |
| #include <vppinfra/dlmalloc.h> |
| |
| #define CLIB_MEM_BULK_DEFAULT_MIN_ELTS_PER_CHUNK 32 |
| |
| typedef struct clib_mem_bulk_chunk_hdr |
| { |
| u32 freelist; |
| u32 n_free; |
| struct clib_mem_bulk_chunk_hdr *prev, *next; |
| } clib_mem_bulk_chunk_hdr_t; |
| |
| typedef struct |
| { |
| u32 elt_sz; |
| u32 chunk_hdr_sz; |
| u32 elts_per_chunk; |
| u32 align; |
| u32 chunk_align; |
| void *mspace; |
| clib_mem_bulk_chunk_hdr_t *full_chunks, *avail_chunks; |
| } clib_mem_bulk_t; |
| |
| static inline uword |
| bulk_chunk_size (clib_mem_bulk_t *b) |
| { |
| return (uword) b->elts_per_chunk * b->elt_sz + b->chunk_hdr_sz; |
| } |
| |
| __clib_export clib_mem_bulk_handle_t |
| clib_mem_bulk_init (u32 elt_sz, u32 align, u32 min_elts_per_chunk) |
| { |
| clib_mem_heap_t *heap = clib_mem_get_heap (); |
| clib_mem_bulk_t *b; |
| uword sz; |
| |
| if ((b = mspace_memalign (heap->mspace, 16, sizeof (clib_mem_bulk_t))) == 0) |
| return 0; |
| |
| if (align < 16) |
| align = 16; |
| |
| if (min_elts_per_chunk == 0) |
| min_elts_per_chunk = CLIB_MEM_BULK_DEFAULT_MIN_ELTS_PER_CHUNK; |
| |
| clib_mem_unpoison (b, sizeof (clib_mem_bulk_t)); |
| clib_memset (b, 0, sizeof (clib_mem_bulk_t)); |
| b->mspace = heap->mspace; |
| b->align = align; |
| b->elt_sz = round_pow2 (elt_sz, align); |
| b->chunk_hdr_sz = round_pow2 (sizeof (clib_mem_bulk_chunk_hdr_t), align); |
| b->elts_per_chunk = min_elts_per_chunk; |
| sz = bulk_chunk_size (b); |
| b->chunk_align = max_pow2 (sz); |
| b->elts_per_chunk += (b->chunk_align - sz) / b->elt_sz; |
| return b; |
| } |
| |
| __clib_export void |
| clib_mem_bulk_destroy (clib_mem_bulk_handle_t h) |
| { |
| clib_mem_bulk_t *b = h; |
| clib_mem_bulk_chunk_hdr_t *c, *next; |
| void *ms = b->mspace; |
| |
| c = b->full_chunks; |
| |
| again: |
| while (c) |
| { |
| next = c->next; |
| clib_mem_poison (c, bulk_chunk_size (b)); |
| mspace_free (ms, c); |
| c = next; |
| } |
| |
| if (b->avail_chunks) |
| { |
| c = b->avail_chunks; |
| b->avail_chunks = 0; |
| goto again; |
| } |
| |
| clib_mem_poison (b, sizeof (clib_mem_bulk_t)); |
| mspace_free (ms, b); |
| } |
| |
| static inline void * |
| get_chunk_elt_ptr (clib_mem_bulk_t *b, clib_mem_bulk_chunk_hdr_t *c, u32 index) |
| { |
| return (u8 *) c + b->chunk_hdr_sz + index * b->elt_sz; |
| } |
| |
| static inline void |
| add_to_chunk_list (clib_mem_bulk_chunk_hdr_t **first, |
| clib_mem_bulk_chunk_hdr_t *c) |
| { |
| c->next = *first; |
| c->prev = 0; |
| if (c->next) |
| c->next->prev = c; |
| *first = c; |
| } |
| |
| static inline void |
| remove_from_chunk_list (clib_mem_bulk_chunk_hdr_t **first, |
| clib_mem_bulk_chunk_hdr_t *c) |
| { |
| if (c->next) |
| c->next->prev = c->prev; |
| if (c->prev) |
| c->prev->next = c->next; |
| else |
| *first = c->next; |
| } |
| |
| __clib_export void * |
| clib_mem_bulk_alloc (clib_mem_bulk_handle_t h) |
| { |
| clib_mem_bulk_t *b = h; |
| clib_mem_bulk_chunk_hdr_t *c = b->avail_chunks; |
| u32 elt_idx; |
| |
| if (b->avail_chunks == 0) |
| { |
| u32 i, sz = bulk_chunk_size (b); |
| c = mspace_memalign (b->mspace, b->chunk_align, sz); |
| clib_mem_unpoison (c, sz); |
| clib_memset (c, 0, sizeof (clib_mem_bulk_chunk_hdr_t)); |
| b->avail_chunks = c; |
| c->n_free = b->elts_per_chunk; |
| |
| /* populate freelist */ |
| for (i = 0; i < b->elts_per_chunk - 1; i++) |
| *((u32 *) get_chunk_elt_ptr (b, c, i)) = i + 1; |
| *((u32 *) get_chunk_elt_ptr (b, c, i)) = ~0; |
| } |
| |
| ASSERT (c->freelist != ~0); |
| elt_idx = c->freelist; |
| c->freelist = *((u32 *) get_chunk_elt_ptr (b, c, elt_idx)); |
| c->n_free--; |
| |
| if (c->n_free == 0) |
| { |
| /* chunk is full */ |
| ASSERT (c->freelist == ~0); |
| remove_from_chunk_list (&b->avail_chunks, c); |
| add_to_chunk_list (&b->full_chunks, c); |
| } |
| |
| return get_chunk_elt_ptr (b, c, elt_idx); |
| } |
| |
| __clib_export void |
| clib_mem_bulk_free (clib_mem_bulk_handle_t h, void *p) |
| { |
| clib_mem_bulk_t *b = h; |
| uword offset = (uword) p & (b->chunk_align - 1); |
| clib_mem_bulk_chunk_hdr_t *c = (void *) ((u8 *) p - offset); |
| u32 elt_idx = (offset - b->chunk_hdr_sz) / b->elt_sz; |
| |
| ASSERT (elt_idx < b->elts_per_chunk); |
| ASSERT (get_chunk_elt_ptr (b, c, elt_idx) == p); |
| |
| c->n_free++; |
| |
| if (c->n_free == b->elts_per_chunk) |
| { |
| /* chunk is empty - give it back */ |
| remove_from_chunk_list (&b->avail_chunks, c); |
| clib_mem_poison (c, bulk_chunk_size (b)); |
| mspace_free (b->mspace, c); |
| return; |
| } |
| |
| if (c->n_free == 1) |
| { |
| /* move chunk to avail chunks */ |
| remove_from_chunk_list (&b->full_chunks, c); |
| add_to_chunk_list (&b->avail_chunks, c); |
| } |
| |
| /* add elt to freelist */ |
| *(u32 *) p = c->freelist; |
| c->freelist = elt_idx; |
| } |
| |
| __clib_export u8 * |
| format_clib_mem_bulk (u8 *s, va_list *args) |
| { |
| clib_mem_bulk_t *b = va_arg (*args, clib_mem_bulk_handle_t); |
| clib_mem_bulk_chunk_hdr_t *c; |
| uword n_chunks = 0, n_free_elts = 0, n_elts, chunk_sz; |
| |
| c = b->full_chunks; |
| while (c) |
| { |
| n_chunks++; |
| c = c->next; |
| } |
| |
| c = b->avail_chunks; |
| while (c) |
| { |
| n_chunks++; |
| n_free_elts += c->n_free; |
| c = c->next; |
| } |
| |
| n_elts = n_chunks * b->elts_per_chunk; |
| chunk_sz = b->chunk_hdr_sz + (uword) b->elts_per_chunk * b->elt_sz; |
| |
| s = format (s, "%u bytes/elt, align %u, chunk-align %u, ", b->elt_sz, |
| b->align, b->chunk_align); |
| s = format (s, "%u elts-per-chunk, chunk size %lu bytes", b->elts_per_chunk, |
| chunk_sz); |
| |
| if (n_chunks == 0) |
| return format (s, "\nempty"); |
| |
| s = format (s, "\n%lu chunks allocated, ", n_chunks); |
| s = format (s, "%lu / %lu free elts (%.1f%%), ", n_free_elts, n_elts, |
| (f64) n_free_elts * 100 / n_elts); |
| s = format (s, "%lu bytes of memory consumed", n_chunks * chunk_sz); |
| |
| return s; |
| } |