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 *
qdf_flex_mem_seg_alloc(struct qdf_flex_mem_pool * pool)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
qdf_flex_mem_init(struct qdf_flex_mem_pool * pool)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
qdf_flex_mem_deinit(struct qdf_flex_mem_pool * pool)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
__qdf_flex_mem_alloc(struct qdf_flex_mem_pool * pool)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
qdf_flex_mem_alloc(struct qdf_flex_mem_pool * pool)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
qdf_flex_mem_seg_free(struct qdf_flex_mem_pool * pool,struct qdf_flex_mem_segment * seg)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
__qdf_flex_mem_free(struct qdf_flex_mem_pool * pool,void * ptr)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
qdf_flex_mem_free(struct qdf_flex_mem_pool * pool,void * ptr)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