/* * Copyright (c) 2018-2019 The Linux Foundation. All rights reserved. * * Permission to use, copy, modify, and/or distribute this software for * any purpose with or without fee is hereby granted, provided that the * above copyright notice and this permission notice appear in all * copies. * * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL * WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED * WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE * AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL * DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR * PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER * TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR * PERFORMANCE OF THIS SOFTWARE. */ #include "qdf_flex_mem.h" #include "qdf_list.h" #include "qdf_lock.h" #include "qdf_mem.h" #include "qdf_module.h" #include "qdf_talloc.h" #include "qdf_trace.h" #include "qdf_util.h" static struct qdf_flex_mem_segment * qdf_flex_mem_seg_alloc(struct qdf_flex_mem_pool *pool) { struct qdf_flex_mem_segment *seg; size_t total_size = sizeof(struct qdf_flex_mem_segment) + pool->item_size * QDF_FM_BITMAP_BITS; seg = qdf_talloc(pool, total_size); if (!seg) return NULL; seg->dynamic = true; seg->bytes = (uint8_t *)(seg + 1); seg->used_bitmap = 0; qdf_list_insert_back(&pool->seg_list, &seg->node); return seg; } void qdf_flex_mem_init(struct qdf_flex_mem_pool *pool) { int i; qdf_spinlock_create(&pool->lock); for (i = 0; i < pool->reduction_limit; i++) qdf_flex_mem_seg_alloc(pool); } qdf_export_symbol(qdf_flex_mem_init); void qdf_flex_mem_deinit(struct qdf_flex_mem_pool *pool) { struct qdf_flex_mem_segment *seg, *next; qdf_spinlock_destroy(&pool->lock); qdf_list_for_each_del(&pool->seg_list, seg, next, node) { QDF_BUG(!seg->used_bitmap); if (seg->used_bitmap) continue; qdf_list_remove_node(&pool->seg_list, &seg->node); if (seg->dynamic) qdf_tfree(seg); } } qdf_export_symbol(qdf_flex_mem_deinit); static void *__qdf_flex_mem_alloc(struct qdf_flex_mem_pool *pool) { struct qdf_flex_mem_segment *seg; qdf_list_for_each(&pool->seg_list, seg, node) { int index; void *ptr; index = qdf_ffz(seg->used_bitmap); if (index < 0) continue; QDF_BUG(index < QDF_FM_BITMAP_BITS); seg->used_bitmap ^= (QDF_FM_BITMAP)1 << index; ptr = &seg->bytes[index * pool->item_size]; qdf_mem_zero(ptr, pool->item_size); return ptr; } seg = qdf_flex_mem_seg_alloc(pool); if (!seg) return NULL; seg->used_bitmap = 1; return seg->bytes; } void *qdf_flex_mem_alloc(struct qdf_flex_mem_pool *pool) { void *ptr; QDF_BUG(pool); if (!pool) return NULL; qdf_spin_lock_bh(&pool->lock); ptr = __qdf_flex_mem_alloc(pool); qdf_spin_unlock_bh(&pool->lock); return ptr; } qdf_export_symbol(qdf_flex_mem_alloc); static void qdf_flex_mem_seg_free(struct qdf_flex_mem_pool *pool, struct qdf_flex_mem_segment *seg) { if (!seg->dynamic) return; if (qdf_list_size(&pool->seg_list) <= pool->reduction_limit) return; qdf_list_remove_node(&pool->seg_list, &seg->node); qdf_tfree(seg); } static void __qdf_flex_mem_free(struct qdf_flex_mem_pool *pool, void *ptr) { struct qdf_flex_mem_segment *seg; void *low_addr; void *high_addr; unsigned long index; qdf_list_for_each(&pool->seg_list, seg, node) { low_addr = seg->bytes; high_addr = low_addr + pool->item_size * QDF_FM_BITMAP_BITS; if (ptr < low_addr || ptr > high_addr) continue; index = (ptr - low_addr) / pool->item_size; QDF_BUG(index < QDF_FM_BITMAP_BITS); seg->used_bitmap ^= (QDF_FM_BITMAP)1 << index; if (!seg->used_bitmap) qdf_flex_mem_seg_free(pool, seg); return; } QDF_DEBUG_PANIC("Failed to find pointer in segment pool"); } void qdf_flex_mem_free(struct qdf_flex_mem_pool *pool, void *ptr) { QDF_BUG(pool); if (!pool) return; QDF_BUG(ptr); if (!ptr) return; qdf_spin_lock_bh(&pool->lock); __qdf_flex_mem_free(pool, ptr); qdf_spin_unlock_bh(&pool->lock); } qdf_export_symbol(qdf_flex_mem_free);