xref: /wlan-dirver/qca-wifi-host-cmn/qdf/src/qdf_flex_mem.c (revision 6ecd284e5a94a1c96e26d571dd47419ac305990d)
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_trace.h"
24 #include "qdf_util.h"
25 
26 void qdf_flex_mem_init(struct qdf_flex_mem_pool *pool)
27 {
28 	qdf_spinlock_create(&pool->lock);
29 }
30 
31 void qdf_flex_mem_deinit(struct qdf_flex_mem_pool *pool)
32 {
33 	qdf_spinlock_destroy(&pool->lock);
34 }
35 
36 static struct qdf_flex_mem_segment *qdf_flex_mem_seg_alloc(uint16_t item_size)
37 {
38 	size_t bytes_size = item_size * QDF_FM_BITMAP_BITS;
39 	size_t total_size = sizeof(struct qdf_flex_mem_segment) + bytes_size;
40 	struct qdf_flex_mem_segment *seg;
41 
42 	seg = qdf_mem_malloc(total_size);
43 	if (!seg)
44 		return NULL;
45 
46 	seg->dynamic = true;
47 	seg->bytes = (uint8_t *)(seg + 1);
48 
49 	return seg;
50 }
51 
52 static void *__qdf_flex_mem_alloc(struct qdf_flex_mem_pool *pool)
53 {
54 	struct qdf_flex_mem_segment *seg;
55 
56 	qdf_list_for_each(&pool->seg_list, seg, node) {
57 		int index;
58 		void *ptr;
59 
60 		index = qdf_ffz(seg->used_bitmap);
61 		if (index < 0)
62 			continue;
63 
64 		QDF_BUG(index < QDF_FM_BITMAP_BITS);
65 
66 		seg->used_bitmap ^= (QDF_FM_BITMAP)1 << index;
67 		ptr = &seg->bytes[index * pool->item_size];
68 		qdf_mem_zero(ptr, pool->item_size);
69 
70 		return ptr;
71 	}
72 
73 	seg = qdf_flex_mem_seg_alloc(pool->item_size);
74 	if (!seg)
75 		return NULL;
76 
77 	seg->used_bitmap = 1;
78 	qdf_list_insert_back(&pool->seg_list, &seg->node);
79 
80 	return seg->bytes;
81 }
82 
83 void *qdf_flex_mem_alloc(struct qdf_flex_mem_pool *pool)
84 {
85 	void *ptr;
86 
87 	QDF_BUG(pool);
88 	if (!pool)
89 		return NULL;
90 
91 	qdf_spin_lock_bh(&pool->lock);
92 	ptr = __qdf_flex_mem_alloc(pool);
93 	qdf_spin_unlock_bh(&pool->lock);
94 
95 	return ptr;
96 }
97 
98 static void qdf_flex_mem_seg_free(struct qdf_flex_mem_pool *pool,
99 				  struct qdf_flex_mem_segment *seg)
100 {
101 	qdf_list_remove_node(&pool->seg_list, &seg->node);
102 	qdf_mem_free(seg);
103 }
104 
105 static void __qdf_flex_mem_free(struct qdf_flex_mem_pool *pool, void *ptr)
106 {
107 	struct qdf_flex_mem_segment *seg;
108 	void *low_addr;
109 	void *high_addr;
110 	unsigned long index;
111 
112 	qdf_list_for_each(&pool->seg_list, seg, node) {
113 		low_addr = seg->bytes;
114 		high_addr = low_addr + pool->item_size * QDF_FM_BITMAP_BITS;
115 
116 		if (ptr < low_addr || ptr > high_addr)
117 			continue;
118 
119 		index = (ptr - low_addr) / pool->item_size;
120 		QDF_BUG(index < QDF_FM_BITMAP_BITS);
121 
122 		seg->used_bitmap ^= (QDF_FM_BITMAP)1 << index;
123 		if (!seg->used_bitmap && seg->dynamic)
124 			qdf_flex_mem_seg_free(pool, seg);
125 
126 		return;
127 	}
128 
129 	qdf_err("Failed to find pointer in segment pool");
130 	QDF_DEBUG_PANIC();
131 }
132 
133 void qdf_flex_mem_free(struct qdf_flex_mem_pool *pool, void *ptr)
134 {
135 	QDF_BUG(pool);
136 	if (!pool)
137 		return;
138 
139 	QDF_BUG(ptr);
140 	if (!ptr)
141 		return;
142 
143 	qdf_spin_lock_bh(&pool->lock);
144 	__qdf_flex_mem_free(pool, ptr);
145 	qdf_spin_unlock_bh(&pool->lock);
146 }
147 
148