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