diff --git a/qdf/Kbuild b/qdf/Kbuild index 2c1225b0db..3463afda20 100644 --- a/qdf/Kbuild +++ b/qdf/Kbuild @@ -45,6 +45,7 @@ linux/src/qdf_perf.o \ linux/src/qdf_threads.o \ linux/src/qdf_trace.o \ linux/src/qdf_file.o \ +src/qdf_flex_mem.o \ src/qdf_parse.o \ src/qdf_str.o \ src/qdf_types.o diff --git a/qdf/inc/qdf_flex_mem.h b/qdf/inc/qdf_flex_mem.h index 9827020a2f..6eca0b4416 100644 --- a/qdf/inc/qdf_flex_mem.h +++ b/qdf/inc/qdf_flex_mem.h @@ -44,11 +44,13 @@ * qdf_flex_mem_pool - a pool of memory segments * @seg_list: the list containing the memory segments * @lock: spinlock for protecting internal data structures + * @reduction_limit: the minimum number of segments to keep during reduction * @item_size: the size of the items the pool will allocate */ struct qdf_flex_mem_pool { qdf_list_t seg_list; struct qdf_spinlock lock; + uint16_t reduction_limit; uint16_t item_size; }; @@ -70,8 +72,9 @@ struct qdf_flex_mem_segment { * DEFINE_QDF_FLEX_MEM_POOL() - define a new flex mem pool with one segment * @name: the name of the pool variable * @size_of_item: size of the items the pool will allocate + * @rm_limit: min number of segments to keep during reduction */ -#define DEFINE_QDF_FLEX_MEM_POOL(name, size_of_item) \ +#define DEFINE_QDF_FLEX_MEM_POOL(name, size_of_item, rm_limit) \ struct qdf_flex_mem_pool name; \ uint8_t __ ## name ## _head_bytes[QDF_FM_BITMAP_BITS * (size_of_item)];\ struct qdf_flex_mem_segment __ ## name ## _head = { \ @@ -81,6 +84,7 @@ struct qdf_flex_mem_segment { }; \ struct qdf_flex_mem_pool name = { \ .seg_list = QDF_LIST_INIT_SINGLE(__ ## name ## _head.node), \ + .reduction_limit = (rm_limit), \ .item_size = (size_of_item), \ } @@ -126,4 +130,14 @@ void *qdf_flex_mem_alloc(struct qdf_flex_mem_pool *pool); */ void qdf_flex_mem_free(struct qdf_flex_mem_pool *pool, void *ptr); +/** + * qdf_flex_mem_release() - release unused segments + * @pool: the pool to operate against + * + * This function physically releases as much unused pool memory as possible. + * + * Return: None + */ +void qdf_flex_mem_release(struct qdf_flex_mem_pool *pool); + #endif /* __QDF_FLEX_MEM_H */ diff --git a/qdf/inc/qdf_list.h b/qdf/inc/qdf_list.h index 219a6c4080..7423efdec1 100644 --- a/qdf/inc/qdf_list.h +++ b/qdf/inc/qdf_list.h @@ -109,6 +109,9 @@ static inline void qdf_list_create(__qdf_list_t *list, uint32_t max_size) #define qdf_list_for_each(list_ptr, cursor, node_field) \ __qdf_list_for_each(list_ptr, cursor, node_field) +#define qdf_list_for_each_del(list_ptr, cursor, next, node_field) \ + __qdf_list_for_each_del(list_ptr, cursor, next, node_field) + /** * qdf_init_list_head() - initialize list head * @list_head: pointer to list head diff --git a/qdf/linux/src/i_qdf_list.h b/qdf/linux/src/i_qdf_list.h index 51a6a18553..2923070b81 100644 --- a/qdf/linux/src/i_qdf_list.h +++ b/qdf/linux/src/i_qdf_list.h @@ -79,6 +79,9 @@ static inline void __qdf_list_create(__qdf_list_t *list, uint32_t max_size) #define __qdf_list_for_each(list_ptr, cursor, node_field) \ list_for_each_entry(cursor, &(list_ptr)->anchor, node_field) +#define __qdf_list_for_each_del(list_ptr, cursor, next, node_field) \ + list_for_each_entry_safe(cursor, next, &(list_ptr)->anchor, node_field) + /** * __qdf_init_list_head() - initialize list head * @list_head: pointer to list head diff --git a/qdf/src/qdf_flex_mem.c b/qdf/src/qdf_flex_mem.c index 075ec7649f..136acd7db8 100644 --- a/qdf/src/qdf_flex_mem.c +++ b/qdf/src/qdf_flex_mem.c @@ -20,6 +20,7 @@ #include "qdf_list.h" #include "qdf_lock.h" #include "qdf_mem.h" +#include "qdf_module.h" #include "qdf_trace.h" #include "qdf_util.h" @@ -27,17 +28,20 @@ void qdf_flex_mem_init(struct qdf_flex_mem_pool *pool) { qdf_spinlock_create(&pool->lock); } +qdf_export_symbol(qdf_flex_mem_init); void qdf_flex_mem_deinit(struct qdf_flex_mem_pool *pool) { qdf_spinlock_destroy(&pool->lock); } +qdf_export_symbol(qdf_flex_mem_deinit); -static struct qdf_flex_mem_segment *qdf_flex_mem_seg_alloc(uint16_t item_size) +static struct qdf_flex_mem_segment * +qdf_flex_mem_seg_alloc(struct qdf_flex_mem_pool *pool) { - size_t bytes_size = item_size * QDF_FM_BITMAP_BITS; - size_t total_size = sizeof(struct qdf_flex_mem_segment) + bytes_size; struct qdf_flex_mem_segment *seg; + size_t total_size = sizeof(struct qdf_flex_mem_segment) + + pool->item_size * QDF_FM_BITMAP_BITS; seg = qdf_mem_malloc(total_size); if (!seg) @@ -70,7 +74,7 @@ static void *__qdf_flex_mem_alloc(struct qdf_flex_mem_pool *pool) return ptr; } - seg = qdf_flex_mem_seg_alloc(pool->item_size); + seg = qdf_flex_mem_seg_alloc(pool); if (!seg) return NULL; @@ -94,10 +98,18 @@ void *qdf_flex_mem_alloc(struct qdf_flex_mem_pool *pool) return ptr; } +qdf_export_symbol(qdf_flex_mem_alloc); static void qdf_flex_mem_seg_free(struct qdf_flex_mem_pool *pool, struct qdf_flex_mem_segment *seg) { + if (!seg->dynamic) + return; + + if (qdf_list_size(&pool->seg_list) <= pool->reduction_limit) + return; + + qdf_list_remove_node(&pool->seg_list, &seg->node); qdf_mem_free(seg); } @@ -120,7 +132,7 @@ static void __qdf_flex_mem_free(struct qdf_flex_mem_pool *pool, void *ptr) QDF_BUG(index < QDF_FM_BITMAP_BITS); seg->used_bitmap ^= (QDF_FM_BITMAP)1 << index; - if (!seg->used_bitmap && seg->dynamic) + if (!seg->used_bitmap) qdf_flex_mem_seg_free(pool, seg); return; @@ -144,4 +156,33 @@ void qdf_flex_mem_free(struct qdf_flex_mem_pool *pool, void *ptr) __qdf_flex_mem_free(pool, ptr); qdf_spin_unlock_bh(&pool->lock); } +qdf_export_symbol(qdf_flex_mem_free); +static void __qdf_flex_mem_release(struct qdf_flex_mem_pool *pool) +{ + struct qdf_flex_mem_segment *seg; + struct qdf_flex_mem_segment *next; + + qdf_list_for_each_del(&pool->seg_list, seg, next, node) { + if (!seg->dynamic) + continue; + + if (seg->used_bitmap != 0) + continue; + + qdf_list_remove_node(&pool->seg_list, &seg->node); + qdf_mem_free(seg); + } +} + +void qdf_flex_mem_release(struct qdf_flex_mem_pool *pool) +{ + QDF_BUG(pool); + if (!pool) + return; + + qdf_spin_lock_bh(&pool->lock); + __qdf_flex_mem_release(pool); + qdf_spin_unlock_bh(&pool->lock); +} +qdf_export_symbol(qdf_flex_mem_release);