xref: /wlan-dirver/qca-wifi-host-cmn/qdf/src/qdf_flex_mem.c (revision 4865edfd190c086bbe2c69aae12a8226f877b91e)
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