1 /* 2 * Copyright (c) 2018-2019 The Linux Foundation. All rights reserved. 3 * 4 * Permission to use, copy, modify, and/or distribute this software for 5 * any purpose with or without fee is hereby granted, provided that the 6 * above copyright notice and this permission notice appear in all 7 * copies. 8 * 9 * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL 10 * WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED 11 * WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE 12 * AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL 13 * DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR 14 * PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER 15 * TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR 16 * PERFORMANCE OF THIS SOFTWARE. 17 */ 18 19 #include "qdf_flex_mem.h" 20 #include "qdf_list.h" 21 #include "qdf_lock.h" 22 #include "qdf_mem.h" 23 #include "qdf_module.h" 24 #include "qdf_talloc.h" 25 #include "qdf_trace.h" 26 #include "qdf_util.h" 27 28 static struct qdf_flex_mem_segment * 29 qdf_flex_mem_seg_alloc(struct qdf_flex_mem_pool *pool) 30 { 31 struct qdf_flex_mem_segment *seg; 32 size_t total_size = sizeof(struct qdf_flex_mem_segment) + 33 pool->item_size * QDF_FM_BITMAP_BITS; 34 35 seg = qdf_talloc(pool, total_size); 36 if (!seg) 37 return NULL; 38 39 seg->dynamic = true; 40 seg->bytes = (uint8_t *)(seg + 1); 41 seg->used_bitmap = 0; 42 qdf_list_insert_back(&pool->seg_list, &seg->node); 43 44 return seg; 45 } 46 47 void qdf_flex_mem_init(struct qdf_flex_mem_pool *pool) 48 { 49 int i; 50 51 qdf_spinlock_create(&pool->lock); 52 53 for (i = 0; i < pool->reduction_limit; i++) 54 qdf_flex_mem_seg_alloc(pool); 55 } 56 qdf_export_symbol(qdf_flex_mem_init); 57 58 void qdf_flex_mem_deinit(struct qdf_flex_mem_pool *pool) 59 { 60 struct qdf_flex_mem_segment *seg, *next; 61 62 qdf_spinlock_destroy(&pool->lock); 63 64 qdf_list_for_each_del(&pool->seg_list, seg, next, node) { 65 QDF_BUG(!seg->used_bitmap); 66 if (seg->used_bitmap) 67 continue; 68 69 qdf_list_remove_node(&pool->seg_list, &seg->node); 70 if (seg->dynamic) 71 qdf_tfree(seg); 72 } 73 } 74 qdf_export_symbol(qdf_flex_mem_deinit); 75 76 static void *__qdf_flex_mem_alloc(struct qdf_flex_mem_pool *pool) 77 { 78 struct qdf_flex_mem_segment *seg; 79 80 qdf_list_for_each(&pool->seg_list, seg, node) { 81 int index; 82 void *ptr; 83 84 index = qdf_ffz(seg->used_bitmap); 85 if (index < 0) 86 continue; 87 88 QDF_BUG(index < QDF_FM_BITMAP_BITS); 89 90 seg->used_bitmap ^= (QDF_FM_BITMAP)1 << index; 91 ptr = &seg->bytes[index * pool->item_size]; 92 qdf_mem_zero(ptr, pool->item_size); 93 94 return ptr; 95 } 96 97 seg = qdf_flex_mem_seg_alloc(pool); 98 if (!seg) 99 return NULL; 100 101 seg->used_bitmap = 1; 102 103 return seg->bytes; 104 } 105 106 void *qdf_flex_mem_alloc(struct qdf_flex_mem_pool *pool) 107 { 108 void *ptr; 109 110 QDF_BUG(pool); 111 if (!pool) 112 return NULL; 113 114 qdf_spin_lock_bh(&pool->lock); 115 ptr = __qdf_flex_mem_alloc(pool); 116 qdf_spin_unlock_bh(&pool->lock); 117 118 return ptr; 119 } 120 qdf_export_symbol(qdf_flex_mem_alloc); 121 122 static void qdf_flex_mem_seg_free(struct qdf_flex_mem_pool *pool, 123 struct qdf_flex_mem_segment *seg) 124 { 125 if (!seg->dynamic) 126 return; 127 128 if (qdf_list_size(&pool->seg_list) <= pool->reduction_limit) 129 return; 130 131 qdf_list_remove_node(&pool->seg_list, &seg->node); 132 qdf_tfree(seg); 133 } 134 135 static void __qdf_flex_mem_free(struct qdf_flex_mem_pool *pool, void *ptr) 136 { 137 struct qdf_flex_mem_segment *seg; 138 void *low_addr; 139 void *high_addr; 140 unsigned long index; 141 142 qdf_list_for_each(&pool->seg_list, seg, node) { 143 low_addr = seg->bytes; 144 high_addr = low_addr + pool->item_size * QDF_FM_BITMAP_BITS; 145 146 if (ptr < low_addr || ptr > high_addr) 147 continue; 148 149 index = (ptr - low_addr) / pool->item_size; 150 QDF_BUG(index < QDF_FM_BITMAP_BITS); 151 152 seg->used_bitmap ^= (QDF_FM_BITMAP)1 << index; 153 if (!seg->used_bitmap) 154 qdf_flex_mem_seg_free(pool, seg); 155 156 return; 157 } 158 159 QDF_DEBUG_PANIC("Failed to find pointer in segment pool"); 160 } 161 162 void qdf_flex_mem_free(struct qdf_flex_mem_pool *pool, void *ptr) 163 { 164 QDF_BUG(pool); 165 if (!pool) 166 return; 167 168 QDF_BUG(ptr); 169 if (!ptr) 170 return; 171 172 qdf_spin_lock_bh(&pool->lock); 173 __qdf_flex_mem_free(pool, ptr); 174 qdf_spin_unlock_bh(&pool->lock); 175 } 176 qdf_export_symbol(qdf_flex_mem_free); 177 178