qcacmn: hold large memory for DBR ring

To avoid memory allocation failure during idle restart,
hold the memories with large size for DBR ring during
the entire lifetime of PSOC.

Change-Id: Idfe5f0e895cc088509b5a62ae07191aed32864f2
CRs-Fixed: 3099522
This commit is contained in:
Yu Wang 2021-12-28 16:22:09 +08:00 committed by Madan Koyyalamudi
parent 412a969aeb
commit 384fc6eb86
2 changed files with 328 additions and 13 deletions

View File

@ -1,5 +1,6 @@
/*
* Copyright (c) 2017-2021 The Linux Foundation. All rights reserved.
* Copyright (c) 2021 Qualcomm Innovation Center, Inc. All rights reserved.
*
* Permission to use, copy, modify, and/or distribute this software for
* any purpose with or without fee is hereby granted, provided that the
@ -494,6 +495,302 @@ QDF_STATUS target_if_direct_buf_rx_pdev_destroy_handler(
return status;
}
#if defined(DBR_HOLD_LARGE_MEM) && defined(CNSS_MEM_PRE_ALLOC)
/**
* struct direct_buf_rx_large_mem - large memory for DBR
* @node: large memory node
* @size: Size of the memory
* @vaddr_unaligned: unaligned base address of the memory
* @offset: offset between unaligned vaddr and aligned vaddr
* @align: Base address alignment
*/
struct direct_buf_rx_large_mem {
qdf_list_node_t node;
uint32_t size;
void *vaddr_unaligned;
uint8_t offset;
uint32_t align;
};
/* check if the actual buffer_size/base_address_alignment match the request */
#define DBR_MEM_NODE_MATCH(_actual_align, _actual_size, _req_align, _req_size) \
((_actual_align) == (_req_align) && \
((_actual_size) == (_req_size) || \
(_actual_size) == ((_req_size) + (_req_align) - 1)))
/*
* Memory with a size(in bytes) equal or larger than this threshold will be
* hold during the entire PSOC lifetime.
* MUST equal or larger than the allocation threshold in cnss_prealloc module.
*/
#define TARGET_IF_DBR_HOLD_MEM_THRESHOLD (8 * 1024)
/**
* target_if_dbr_init_mem_list() - init the large memory list for DBR
* @dbr_psoc_obj: pointer to direct buffer rx module psoc obj
*
* Return: None
*/
static void
target_if_dbr_init_mem_list(struct direct_buf_rx_psoc_obj *dbr_psoc_obj)
{
int i;
qdf_spinlock_create(&dbr_psoc_obj->mem_list_lock);
qdf_spin_lock_bh(&dbr_psoc_obj->mem_list_lock);
for (i = 0; i < QDF_ARRAY_SIZE(dbr_psoc_obj->mem_list); i++)
qdf_list_create(&dbr_psoc_obj->mem_list[i], 0);
qdf_spin_unlock_bh(&dbr_psoc_obj->mem_list_lock);
}
/**
* target_if_dbr_deinit_mem_list() - deinit the large memory list for DBR
* @dbr_psoc_obj: pointer to direct buffer rx module psoc obj
*
* Return: None
*/
static void
target_if_dbr_deinit_mem_list(struct direct_buf_rx_psoc_obj *dbr_psoc_obj)
{
struct direct_buf_rx_large_mem *cur, *next;
qdf_list_t *mem_list;
int i;
qdf_spin_lock_bh(&dbr_psoc_obj->mem_list_lock);
for (i = 0; i < QDF_ARRAY_SIZE(dbr_psoc_obj->mem_list); i++) {
mem_list = &dbr_psoc_obj->mem_list[i];
qdf_list_for_each_del(mem_list, cur, next, node) {
qdf_mem_free(cur->vaddr_unaligned);
qdf_list_remove_node(mem_list, &cur->node);
}
qdf_list_destroy(mem_list);
}
qdf_spin_unlock_bh(&dbr_psoc_obj->mem_list_lock);
qdf_spinlock_destroy(&dbr_psoc_obj->mem_list_lock);
}
/**
* target_if_dbr_mem_add() - allocate a new element for large memory list
* @dbr_psoc_obj: pointer to direct buffer rx module psoc obj
* @pdev_id: PDEV id
* @size: Size of the memory to be assigned to the new element
* @vaddr_unaligned: unaligned base address of the memory
* @offset: offset between unaligned vaddr and aligned vaddr
* @align: Base address alignment
*
* Return: None
*/
static void
target_if_dbr_mem_add(struct direct_buf_rx_psoc_obj *dbr_psoc_obj,
uint8_t pdev_id, uint32_t size, void *vaddr_unaligned,
uint8_t offset, uint32_t align)
{
struct direct_buf_rx_large_mem *new_node;
uint32_t list_size;
new_node = vaddr_unaligned;
qdf_mem_zero(new_node, sizeof(*new_node));
new_node->size = size;
new_node->vaddr_unaligned = vaddr_unaligned;
new_node->offset = offset;
new_node->align = align;
qdf_spin_lock_bh(&dbr_psoc_obj->mem_list_lock);
qdf_list_insert_back(&dbr_psoc_obj->mem_list[pdev_id],
&new_node->node);
list_size = qdf_list_size(&dbr_psoc_obj->mem_list[pdev_id]);
qdf_spin_unlock_bh(&dbr_psoc_obj->mem_list_lock);
}
/**
* target_if_dbr_mem_get() - get aligned memory
* @pdev: pointer to pdev object
* @size: Size to be allocated
* @offset: offset between unaligned vaddr and aligned vaddr
* @align: Base address alignment
* @mod_id: DBR module id (enum DBR_MODULE)
*
* If size to be allocated is equal or smaller than the threshold, this
* function will allocate the aligned memory dynamically;
* If NOT, it will search the saved memory list, return the one which meet the
* requirement, otherwise, allocate the aligned memory dynamically.
*
* Return:
* Unaligned base address of the memory on succeed, NULL otherwise.
*/
static void *
target_if_dbr_mem_get(struct wlan_objmgr_pdev *pdev, uint32_t *size,
uint8_t *offset, uint32_t align, uint32_t mod_id)
{
struct direct_buf_rx_psoc_obj *dbr_psoc_obj;
struct wlan_objmgr_psoc *psoc;
struct direct_buf_rx_large_mem *cur, *next;
void *vaddr_unaligned = NULL, *vaddr_aligned;
dma_addr_t paddr_aligned, paddr_unaligned;
QDF_STATUS status;
qdf_list_t *mem_list;
uint8_t pdev_id;
if (*size < TARGET_IF_DBR_HOLD_MEM_THRESHOLD) {
vaddr_aligned = qdf_aligned_malloc(size, &vaddr_unaligned,
&paddr_unaligned,
&paddr_aligned, align);
if (!vaddr_aligned)
return NULL;
*offset = vaddr_aligned - vaddr_unaligned;
return vaddr_unaligned;
}
if (!pdev) {
direct_buf_rx_err("pdev context passed is null");
return vaddr_unaligned;
}
psoc = wlan_pdev_get_psoc(pdev);
if (!psoc) {
direct_buf_rx_err("psoc is null");
return vaddr_unaligned;
}
dbr_psoc_obj = wlan_objmgr_psoc_get_comp_private_obj(psoc,
WLAN_TARGET_IF_COMP_DIRECT_BUF_RX);
if (!dbr_psoc_obj) {
direct_buf_rx_err("dir buf rx psoc object is null");
return vaddr_unaligned;
}
pdev_id = wlan_objmgr_pdev_get_pdev_id(pdev);
qdf_spin_lock_bh(&dbr_psoc_obj->mem_list_lock);
mem_list = &dbr_psoc_obj->mem_list[pdev_id];
qdf_list_for_each_del(mem_list, cur, next, node) {
if (DBR_MEM_NODE_MATCH(cur->align, cur->size, align, *size)) {
status = qdf_list_remove_node(mem_list, &cur->node);
if (QDF_IS_STATUS_ERROR(status)) {
direct_buf_rx_err("failed to remove node: %d",
status);
break;
}
*offset = cur->offset;
*size = cur->size;
vaddr_unaligned = cur->vaddr_unaligned;
break;
}
}
qdf_spin_unlock_bh(&dbr_psoc_obj->mem_list_lock);
if (vaddr_unaligned) {
qdf_mem_zero(vaddr_unaligned, *size);
return vaddr_unaligned;
}
vaddr_aligned =
qdf_aligned_malloc(size, &vaddr_unaligned, &paddr_unaligned,
&paddr_aligned, align);
if (!vaddr_aligned)
return NULL;
*offset = vaddr_aligned - vaddr_unaligned;
return vaddr_unaligned;
}
/**
* target_if_dbr_mem_put() - put aligned memory
* @pdev: pointer to pdev object
* @size: size of the memory to be put
* @vaddr_unaligned: unaligned base address of the memory
* @offset: offset between unaligned vaddr and aligned vaddr
* @align: Base address alignment
* @mod_id: DBR module id (enum DBR_MODULE)
*
* If size to be allocated is equal or smaller than the threshold, this
* function will free the memory directly;
* If NOT, it will search the saved memory list, mark the one which meet the
* requirement as NOT in use; and if no element is found, free the memory.
*
* Return: None
*/
static void
target_if_dbr_mem_put(struct wlan_objmgr_pdev *pdev, uint32_t size,
void *vaddr_unaligned, uint8_t offset,
uint32_t align, uint32_t mod_id)
{
struct direct_buf_rx_psoc_obj *dbr_psoc_obj;
struct wlan_objmgr_psoc *psoc;
if (!vaddr_unaligned)
return;
if (size < TARGET_IF_DBR_HOLD_MEM_THRESHOLD) {
qdf_mem_free(vaddr_unaligned);
return;
}
if (!pdev) {
direct_buf_rx_err("pdev context passed is null");
return;
}
psoc = wlan_pdev_get_psoc(pdev);
if (!psoc) {
direct_buf_rx_err("psoc is null");
return;
}
dbr_psoc_obj = wlan_objmgr_psoc_get_comp_private_obj(psoc,
WLAN_TARGET_IF_COMP_DIRECT_BUF_RX);
if (!dbr_psoc_obj) {
direct_buf_rx_err("dir buf rx psoc object is null");
return;
}
target_if_dbr_mem_add(dbr_psoc_obj, wlan_objmgr_pdev_get_pdev_id(pdev),
size, vaddr_unaligned, offset, align);
}
#else
static inline void
target_if_dbr_init_mem_list(struct direct_buf_rx_psoc_obj *dbr_psoc_obj)
{
}
static inline void
target_if_dbr_deinit_mem_list(struct direct_buf_rx_psoc_obj *dbr_psoc_obj)
{
}
static void *
target_if_dbr_mem_get(struct wlan_objmgr_pdev *pdev, uint32_t *size,
uint8_t *offset, uint32_t align, uint32_t mod_id)
{
void *vaddr_unaligned = NULL, *vaddr_aligned;
dma_addr_t paddr_aligned, paddr_unaligned;
vaddr_aligned = qdf_aligned_malloc(size, &vaddr_unaligned,
&paddr_unaligned, &paddr_aligned,
align);
if (!vaddr_aligned)
return NULL;
*offset = vaddr_aligned - vaddr_unaligned;
return vaddr_unaligned;
}
static inline void
target_if_dbr_mem_put(struct wlan_objmgr_pdev *pdev, uint32_t size,
void *vaddr_unaligned, uint8_t offset,
uint32_t align, uint32_t mod_id)
{
qdf_mem_free(vaddr_unaligned);
}
#endif /* DBR_HOLD_LARGE_MEM */
QDF_STATUS target_if_direct_buf_rx_psoc_create_handler(
struct wlan_objmgr_psoc *psoc, void *data)
{
@ -514,6 +811,8 @@ QDF_STATUS target_if_direct_buf_rx_psoc_create_handler(
direct_buf_rx_debug("Dbr psoc obj %pK", dbr_psoc_obj);
target_if_dbr_init_mem_list(dbr_psoc_obj);
status = wlan_objmgr_psoc_component_obj_attach(psoc,
WLAN_TARGET_IF_COMP_DIRECT_BUF_RX, dbr_psoc_obj,
QDF_STATUS_SUCCESS);
@ -548,6 +847,7 @@ QDF_STATUS target_if_direct_buf_rx_psoc_destroy_handler(
return QDF_STATUS_E_FAILURE;
}
target_if_dbr_deinit_mem_list(dbr_psoc_obj);
status = wlan_objmgr_psoc_component_obj_detach(psoc,
WLAN_TARGET_IF_COMP_DIRECT_BUF_RX,
dbr_psoc_obj);
@ -1082,7 +1382,9 @@ static QDF_STATUS target_if_dbr_fill_ring(struct wlan_objmgr_pdev *pdev,
struct direct_buf_rx_ring_cfg *dbr_ring_cfg;
struct direct_buf_rx_ring_cap *dbr_ring_cap;
struct direct_buf_rx_buf_info *dbr_buf_pool;
void *buf_vaddr_unaligned, *buf_vaddr_aligned;
QDF_STATUS status;
uint8_t offset;
direct_buf_rx_enter();
@ -1091,28 +1393,29 @@ static QDF_STATUS target_if_dbr_fill_ring(struct wlan_objmgr_pdev *pdev,
dbr_buf_pool = mod_param->dbr_buf_pool;
for (idx = 0; idx < dbr_ring_cfg->num_ptr - 1; idx++) {
void *buf_vaddr_unaligned = NULL, *buf_vaddr_aligned;
dma_addr_t buf_paddr_aligned, buf_paddr_unaligned;
buf_vaddr_aligned = qdf_aligned_malloc(
&dbr_ring_cap->min_buf_size, &buf_vaddr_unaligned,
&buf_paddr_unaligned, &buf_paddr_aligned,
dbr_ring_cap->min_buf_align);
if (!buf_vaddr_aligned) {
buf_vaddr_unaligned =
target_if_dbr_mem_get(pdev, &dbr_ring_cap->min_buf_size,
&offset,
dbr_ring_cap->min_buf_align,
mod_param->mod_id);
if (!buf_vaddr_unaligned) {
direct_buf_rx_err("dir buf rx ring alloc failed");
return QDF_STATUS_E_NOMEM;
}
dbr_buf_pool[idx].vaddr = buf_vaddr_unaligned;
dbr_buf_pool[idx].offset = buf_vaddr_aligned -
buf_vaddr_unaligned;
dbr_buf_pool[idx].offset = offset;
dbr_buf_pool[idx].cookie = idx;
buf_vaddr_aligned = buf_vaddr_unaligned + offset;
status = target_if_dbr_replenish_ring(pdev, mod_param,
buf_vaddr_aligned, idx);
if (QDF_IS_STATUS_ERROR(status)) {
direct_buf_rx_err("replenish failed with status : %d",
status);
qdf_mem_free(buf_vaddr_unaligned);
target_if_dbr_mem_put(pdev, dbr_ring_cap->min_buf_size,
buf_vaddr_unaligned, offset,
dbr_ring_cap->min_buf_align,
mod_param->mod_id);
return QDF_STATUS_E_FAILURE;
}
}
@ -1940,7 +2243,11 @@ static QDF_STATUS target_if_dbr_empty_ring(struct wlan_objmgr_pdev *pdev,
(qdf_dma_addr_t)dbr_buf_pool[idx].paddr,
QDF_DMA_FROM_DEVICE,
dbr_ring_cap->min_buf_size);
qdf_mem_free(dbr_buf_pool[idx].vaddr);
target_if_dbr_mem_put(pdev, dbr_ring_cap->min_buf_size,
dbr_buf_pool[idx].vaddr,
dbr_buf_pool[idx].offset,
dbr_ring_cap->min_buf_align,
mod_param->mod_id);
}
return QDF_STATUS_SUCCESS;

View File

@ -1,5 +1,6 @@
/*
* Copyright (c) 2017-2019 The Linux Foundation. All rights reserved.
* Copyright (c) 2021 Qualcomm Innovation Center, Inc. All rights reserved.
*
* Permission to use, copy, modify, and/or distribute this software for
* any purpose with or without fee is hereby granted, provided that the
@ -211,11 +212,18 @@ struct direct_buf_rx_pdev_obj {
* @hal_soc: Opaque HAL SOC handle
* @osdev: QDF os device handle
* @dbr_pdev_objs: array of DBR pdev objects
* @mem_list: list for holding the large memories during the entire
* PSOC lifetime
* @mem_list_lock: spin lock for the memory list
*/
struct direct_buf_rx_psoc_obj {
void *hal_soc;
qdf_device_t osdev;
struct direct_buf_rx_pdev_obj *dbr_pdev_obj[WLAN_UMAC_MAX_PDEVS];
#if defined(DBR_HOLD_LARGE_MEM) && defined(CNSS_MEM_PRE_ALLOC)
qdf_list_t mem_list[WLAN_UMAC_MAX_PDEVS];
qdf_spinlock_t mem_list_lock;
#endif
};
/**