1 /* 2 * Copyright (c) 2018 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_trace.h" 25 #include "qdf_util.h" 26 27 static struct qdf_flex_mem_segment * 28 qdf_flex_mem_seg_alloc(struct qdf_flex_mem_pool *pool) 29 { 30 struct qdf_flex_mem_segment *seg; 31 size_t total_size = sizeof(struct qdf_flex_mem_segment) + 32 pool->item_size * QDF_FM_BITMAP_BITS; 33 34 seg = qdf_mem_malloc(total_size); 35 if (!seg) 36 return NULL; 37 38 seg->dynamic = true; 39 seg->bytes = (uint8_t *)(seg + 1); 40 seg->used_bitmap = 0; 41 qdf_list_insert_back(&pool->seg_list, &seg->node); 42 43 return seg; 44 } 45 46 void qdf_flex_mem_init(struct qdf_flex_mem_pool *pool) 47 { 48 int i; 49 50 qdf_spinlock_create(&pool->lock); 51 52 for (i = 0; i < pool->reduction_limit; i++) 53 qdf_flex_mem_seg_alloc(pool); 54 } 55 qdf_export_symbol(qdf_flex_mem_init); 56 57 void qdf_flex_mem_deinit(struct qdf_flex_mem_pool *pool) 58 { 59 qdf_flex_mem_release(pool); 60 QDF_BUG(!qdf_list_size(&pool->seg_list)); 61 62 qdf_spinlock_destroy(&pool->lock); 63 } 64 qdf_export_symbol(qdf_flex_mem_deinit); 65 66 static void *__qdf_flex_mem_alloc(struct qdf_flex_mem_pool *pool) 67 { 68 struct qdf_flex_mem_segment *seg; 69 70 qdf_list_for_each(&pool->seg_list, seg, node) { 71 int index; 72 void *ptr; 73 74 index = qdf_ffz(seg->used_bitmap); 75 if (index < 0) 76 continue; 77 78 QDF_BUG(index < QDF_FM_BITMAP_BITS); 79 80 seg->used_bitmap ^= (QDF_FM_BITMAP)1 << index; 81 ptr = &seg->bytes[index * pool->item_size]; 82 qdf_mem_zero(ptr, pool->item_size); 83 84 return ptr; 85 } 86 87 seg = qdf_flex_mem_seg_alloc(pool); 88 if (!seg) 89 return NULL; 90 91 seg->used_bitmap = 1; 92 93 return seg->bytes; 94 } 95 96 void *qdf_flex_mem_alloc(struct qdf_flex_mem_pool *pool) 97 { 98 void *ptr; 99 100 QDF_BUG(pool); 101 if (!pool) 102 return NULL; 103 104 qdf_spin_lock_bh(&pool->lock); 105 ptr = __qdf_flex_mem_alloc(pool); 106 qdf_spin_unlock_bh(&pool->lock); 107 108 return ptr; 109 } 110 qdf_export_symbol(qdf_flex_mem_alloc); 111 112 static void qdf_flex_mem_seg_free(struct qdf_flex_mem_pool *pool, 113 struct qdf_flex_mem_segment *seg) 114 { 115 if (!seg->dynamic) 116 return; 117 118 if (qdf_list_size(&pool->seg_list) <= pool->reduction_limit) 119 return; 120 121 122 qdf_list_remove_node(&pool->seg_list, &seg->node); 123 qdf_mem_free(seg); 124 } 125 126 static void __qdf_flex_mem_free(struct qdf_flex_mem_pool *pool, void *ptr) 127 { 128 struct qdf_flex_mem_segment *seg; 129 void *low_addr; 130 void *high_addr; 131 unsigned long index; 132 133 qdf_list_for_each(&pool->seg_list, seg, node) { 134 low_addr = seg->bytes; 135 high_addr = low_addr + pool->item_size * QDF_FM_BITMAP_BITS; 136 137 if (ptr < low_addr || ptr > high_addr) 138 continue; 139 140 index = (ptr - low_addr) / pool->item_size; 141 QDF_BUG(index < QDF_FM_BITMAP_BITS); 142 143 seg->used_bitmap ^= (QDF_FM_BITMAP)1 << index; 144 if (!seg->used_bitmap) 145 qdf_flex_mem_seg_free(pool, seg); 146 147 return; 148 } 149 150 qdf_err("Failed to find pointer in segment pool"); 151 QDF_DEBUG_PANIC(); 152 } 153 154 void qdf_flex_mem_free(struct qdf_flex_mem_pool *pool, void *ptr) 155 { 156 QDF_BUG(pool); 157 if (!pool) 158 return; 159 160 QDF_BUG(ptr); 161 if (!ptr) 162 return; 163 164 qdf_spin_lock_bh(&pool->lock); 165 __qdf_flex_mem_free(pool, ptr); 166 qdf_spin_unlock_bh(&pool->lock); 167 } 168 qdf_export_symbol(qdf_flex_mem_free); 169 170 static void __qdf_flex_mem_release(struct qdf_flex_mem_pool *pool) 171 { 172 struct qdf_flex_mem_segment *seg; 173 struct qdf_flex_mem_segment *next; 174 175 qdf_list_for_each_del(&pool->seg_list, seg, next, node) { 176 if (!seg->dynamic) 177 continue; 178 179 if (seg->used_bitmap != 0) 180 continue; 181 182 qdf_list_remove_node(&pool->seg_list, &seg->node); 183 qdf_mem_free(seg); 184 } 185 } 186 187 void qdf_flex_mem_release(struct qdf_flex_mem_pool *pool) 188 { 189 QDF_BUG(pool); 190 if (!pool) 191 return; 192 193 qdf_spin_lock_bh(&pool->lock); 194 __qdf_flex_mem_release(pool); 195 qdf_spin_unlock_bh(&pool->lock); 196 } 197 qdf_export_symbol(qdf_flex_mem_release); 198 199