qcacmn: Add support for WBUFF module

Introduce a new module WBUFF, which maintains a
pre-allocated pool of skbs for each registered
module and allocates skbs when requeted from
these pre-allocated pools.

The module exposes the following APIs:
 1) wbuff_module_init
 2) wbuff_module_deinit
 3) wbuff_module_register
 4) wbuff_module_deregister
 5) wbuff_buff_get
 6) wbuff_buff_put

Change-Id: Icab723ce29a9a364c822756dee78a0dae1006180
CRs-Fixed: 2313443
This commit is contained in:
Rakshith Suresh Patkar
2018-09-12 12:00:44 +05:30
committed by nshrivas
parent 2c3e194509
commit 9cee4926f9
3 changed files with 611 additions and 0 deletions

151
wbuff/inc/wbuff.h Normal file
View File

@@ -0,0 +1,151 @@
/*
* Copyright (c) 2018 The Linux Foundation. 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
* above copyright notice and this permission notice appear in all
* copies.
*
* THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL
* WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED
* WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE
* AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL
* DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR
* PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER
* TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
* PERFORMANCE OF THIS SOFTWARE.
*/
/**
* DOC: wbuff.h
* wbuff buffer management APIs
*/
#ifndef _WBUFF_H
#define _WBUFF_H
#include <qdf_status.h>
#include <qdf_nbuf.h>
/* wbuff available pools */
/* Pool of nbuf size 256 bytes */
#define WBUFF_POOL_0 0
/* Pool of nbuf size 512 bytes */
#define WBUFF_POOL_1 1
/* Pool of nbuf size 1024 bytes */
#define WBUFF_POOL_2 2
/* Pool of nbuf 2048 bytes */
#define WBUFF_POOL_3 3
/**
* struct wbuff_alloc_request - allocation structure for registering each
* pool for wbuff module.
* @slot: pool_slot identifier
* @size: number of buffers for @pool_slot
*/
struct wbuff_alloc_request {
uint8_t slot;
uint16_t size;
};
/* Opaque handle for wbuff */
struct wbuff_mod_handle;
#ifdef WLAN_FEATURE_WBUFF
/**
* wbuff_module_init() - Initializes the wbuff module
*
* Return: QDF_STATUS_SUCCESS - init success
* QDF_STATUS_E_NOSUPPORT - init failure
*/
QDF_STATUS wbuff_module_init(void);
/**
* wbuff_module_deinit() - De-initializes the wbuff module
*
* Return: QDF_STATUS_SUCCESS - de-init success
* QDF_STATUS_E_INVAL - de-init failure (wbuff not initialized)
*/
QDF_STATUS wbuff_module_deinit(void);
/**
* wbuff_module_register() - Registers a module with wbuff
* @req: allocation request from registered module
* @num: number of pools required
* @reserve: nbuf headroom to start with
* @align: alignment for the nbuf
*
* Return: Handle if registration success
* NULL if registration failure
*/
struct wbuff_mod_handle *
wbuff_module_register(struct wbuff_alloc_request *req, uint8_t num,
int reserve, int align);
/**
* wbuff_module_deregister() - De-registers a module with wbuff
* @hdl: wbuff_handle corresponding to the module
*
* Return: QDF_STATUS_SUCCESS - deregistration success
* QDF_STATUS_E_INVAL - deregistration failure
*/
QDF_STATUS wbuff_module_deregister(struct wbuff_mod_handle *hdl);
/**
* wbuff_buff_get() - return buffer to the requester
* @handle: wbuff_handle corresponding to the module
* @len: length of buffer requested
*
* Return: Network buffer if success
* NULL if failure
*/
qdf_nbuf_t wbuff_buff_get(struct wbuff_mod_handle *hdl, uint32_t len);
/**
* wbuff_buff_put() - put the buffer back to wbuff pool
* @hdl: wbuff_handle corresponding to the module
* @buf: pointer to network buffer
*
* Return: NULL if success (buffer consumed)
* @buf if failure (buffer not consumed)
*/
qdf_nbuf_t wbuff_buff_put(qdf_nbuf_t buf);
#else
static inline QDF_STATUS wbuff_module_init(void)
{
return QDF_STATUS_E_NOSUPPORT;
}
static inline QDF_STATUS wbuff_module_deinit(void)
{
return QDF_STATUS_E_NOSUPPORT;
}
static inline struct wbuff_mod_handle *
wbuff_module_register(struct wbuff_alloc_request *req, uint8_t num,
int reserve, int align)
{
return NULL;
}
static inline QDF_STATUS wbuff_module_deregister(struct wbuff_mod_handle *hdl)
{
return QDF_STATUS_E_NOSUPPORT;
}
static inline qdf_nbuf_t
wbuff_buff_get(struct wbuff_mod_handle *hdl, uint32_t len)
{
return NULL;
}
static inline qdf_nbuf_t
wbuff_buff_put(qdf_nbuf_t buf)
{
return buf;
}
#endif
#endif /* _WBUFF_H */

103
wbuff/src/i_wbuff.h Normal file
View File

@@ -0,0 +1,103 @@
/*
* Copyright (c) 2018 The Linux Foundation. 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
* above copyright notice and this permission notice appear in all
* copies.
*
* THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL
* WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED
* WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE
* AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL
* DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR
* PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER
* TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
* PERFORMANCE OF THIS SOFTWARE.
*/
/**
* DOC: i_wbuff.h
* wbuff private
*/
#ifndef _I_WBUFF_H
#define _I_WBUFF_H
#include <qdf_nbuf.h>
/* Number of modules supported by wbuff */
#define WBUFF_MAX_MODULES 4
/* Number of pools supported per module */
#define WBUFF_MAX_POOLS 4
/* Max buffer size supported by wbuff in bytes */
#define WBUFF_MAX_BUFFER_SIZE 2048
/* wbuff pool buffer lengths in bytes*/
#define WBUFF_LEN_POOL0 256
#define WBUFF_LEN_POOL1 512
#define WBUFF_LEN_POOL2 1024
#define WBUFF_LEN_POOL3 2048
/* wbuff max pool sizes */
/* Allocation of size 256 bytes */
#define WBUFF_POOL_0_MAX 256
/* Allocation of size 512 bytes */
#define WBUFF_POOL_1_MAX 128
/* Allocation of size 1024 bytes */
#define WBUFF_POOL_2_MAX 64
/* Allocation of size 2048 bytes */
#define WBUFF_POOL_3_MAX 32
#define WBUFF_MSLOT_SHIFT 4
#define WBUFF_MSLOT_BITMASK 0xF0
#define WBUFF_PSLOT_SHIFT 1
#define WBUFF_PSLOT_BITMASK 0xE
/* Comparison array for maximum allocation per pool*/
uint16_t wbuff_alloc_max[WBUFF_MAX_POOLS] = {WBUFF_POOL_0_MAX,
WBUFF_POOL_1_MAX,
WBUFF_POOL_2_MAX,
WBUFF_POOL_3_MAX};
/**
* struct wbuff_handle - wbuff handle to the registered module
* @id: the identifier for the registered module.
*/
struct wbuff_handle {
uint8_t id;
};
/**
* struct wbuff_module - allocation holder for wbuff registered module
* @registered: To identify whether module is registered
* @pending_returns: Number of buffers pending to be returned to
* wbuff by the module
* @lock: Lock for accessing per module buffer slots
* @handle: wbuff handle for the registered module
* @reserve: nbuf headroom to start with
* @align: alignment for the nbuf
* @pool[]: pools for all available buffers for the module
*/
struct wbuff_module {
bool registered;
uint16_t pending_returns;
qdf_spinlock_t lock;
struct wbuff_handle handle;
int reserve;
int align;
qdf_nbuf_t pool[WBUFF_MAX_POOLS];
};
/**
* struct wbuff_holder - allocation holder for wbuff
* @initialized: to identified whether module is initialized
*/
struct wbuff_holder {
bool initialized;
struct wbuff_module mod[WBUFF_MAX_MODULES];
};
#endif /* _WBUFF_H */

357
wbuff/src/wbuff.c Normal file
View File

@@ -0,0 +1,357 @@
/*
* Copyright (c) 2018 The Linux Foundation. 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
* above copyright notice and this permission notice appear in all
* copies.
*
* THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL
* WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED
* WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE
* AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL
* DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR
* PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER
* TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
* PERFORMANCE OF THIS SOFTWARE.
*/
/**
* DOC: wbuff.c
* wbuff buffer management APIs
*/
#include <wbuff.h>
#include "i_wbuff.h"
/**
* Allocation holder array for all wbuff registered modules
*/
struct wbuff_holder wbuff;
/**
* wbuff_get_pool_slot_from_len() - get pool_slot from length
* @len: length of the buffer
*
* Return: pool slot
*/
static uint8_t wbuff_get_pool_slot_from_len(uint16_t len)
{
if ((len > 0) && (len <= WBUFF_LEN_POOL0))
return WBUFF_POOL_0;
else if ((len > WBUFF_LEN_POOL0) && (len <= WBUFF_LEN_POOL1))
return WBUFF_POOL_1;
else if ((len > WBUFF_LEN_POOL1) && (len <= WBUFF_LEN_POOL2))
return WBUFF_POOL_2;
else
return WBUFF_POOL_3;
}
/**
* wbuff_get_len_from_pool_slot() - get len from pool slot
* @pool_slot: wbuff pool_slot
*
* Return: nbuf length from pool slot
*/
static uint32_t wbuff_get_len_from_pool_slot(uint16_t pool_slot)
{
uint32_t len = 0;
switch (pool_slot) {
case 0:
len = WBUFF_LEN_POOL0;
break;
case 1:
len = WBUFF_LEN_POOL1;
break;
case 2:
len = WBUFF_LEN_POOL2;
break;
case 3:
len = WBUFF_LEN_POOL3;
break;
default:
len = 0;
}
return len;
}
/**
* wbuff_get_free_mod_slot() - get free module slot
*
* Return: module slot
*/
static uint8_t wbuff_get_free_mod_slot(void)
{
uint8_t mslot = 0;
for (mslot = 0; mslot < WBUFF_MAX_MODULES; mslot++) {
qdf_spin_lock_bh(&wbuff.mod[mslot].lock);
if (!wbuff.mod[mslot].registered) {
wbuff.mod[mslot].registered = true;
qdf_spin_unlock_bh(&wbuff.mod[mslot].lock);
break;
}
qdf_spin_unlock_bh(&wbuff.mod[mslot].lock);
}
return mslot;
}
/**
* wbuff_is_valid_alloc_req() - validate alloc request
* @req: allocation request from registered module
* @num: number of pools required
*
* Return: true if valid wbuff_alloc_request
* false if invalid wbuff_alloc_request
*/
static bool wbuff_is_valid_alloc_req(struct wbuff_alloc_request *req,
uint8_t num)
{
uint16_t psize = 0;
uint8_t alloc = 0, pslot = 0;
for (alloc = 0; alloc < num; alloc++) {
pslot = req[alloc].slot;
psize = req[alloc].size;
if ((pslot > WBUFF_MAX_POOLS - 1) ||
(psize > wbuff_alloc_max[pslot]))
return false;
}
return true;
}
/**
* wbuff_prepare_nbuf() - allocate nbuf
* @mslot: module slot
* @pslot: pool slot
* @len: length of the buffer
* @reserve: nbuf headroom to start with
* @align: alignment for the nbuf
*
* Return: nbuf if success
* NULL if failure
*/
static qdf_nbuf_t wbuff_prepare_nbuf(uint8_t mslot, uint8_t pslot,
uint32_t len, int reserve, int align)
{
qdf_nbuf_t buf;
unsigned long dev_scratch = 0;
buf = qdf_nbuf_alloc(NULL, roundup(len + reserve, align), reserve,
align, false);
if (!buf)
return NULL;
dev_scratch = mslot;
dev_scratch <<= WBUFF_MSLOT_SHIFT;
dev_scratch |= ((pslot << WBUFF_PSLOT_SHIFT) | 1);
qdf_nbuf_set_dev_scratch(buf, dev_scratch);
return buf;
}
/**
* wbuff_is_valid_handle() - validate wbuff handle
* @handle: wbuff handle passed by module
*
* Return: true - valid wbuff_handle
* false - invalid wbuff_handle
*/
static bool wbuff_is_valid_handle(struct wbuff_handle *handle)
{
if ((handle) && (handle->id < WBUFF_MAX_MODULES) &&
(wbuff.mod[handle->id].registered))
return true;
return false;
}
QDF_STATUS wbuff_module_init(void)
{
struct wbuff_module *mod = NULL;
uint8_t mslot = 0, pslot = 0;
if (!qdf_nbuf_is_dev_scratch_supported()) {
wbuff.initialized = false;
return QDF_STATUS_E_NOSUPPORT;
}
for (mslot = 0; mslot < WBUFF_MAX_MODULES; mslot++) {
mod = &wbuff.mod[mslot];
qdf_spinlock_create(&mod->lock);
for (pslot = 0; pslot < WBUFF_MAX_POOLS; pslot++)
mod->pool[pslot] = NULL;
mod->registered = false;
}
wbuff.initialized = true;
return QDF_STATUS_SUCCESS;
}
QDF_STATUS wbuff_module_deinit(void)
{
struct wbuff_module *mod = NULL;
uint8_t mslot = 0;
if (!wbuff.initialized)
return QDF_STATUS_E_INVAL;
wbuff.initialized = false;
for (mslot = 0; mslot < WBUFF_MAX_MODULES; mslot++) {
mod = &wbuff.mod[mslot];
if (mod->registered)
wbuff_module_deregister((struct wbuff_mod_handle *)
&mod->handle);
qdf_spinlock_destroy(&mod->lock);
}
return QDF_STATUS_SUCCESS;
}
struct wbuff_mod_handle *
wbuff_module_register(struct wbuff_alloc_request *req, uint8_t num,
int reserve, int align)
{
struct wbuff_module *mod = NULL;
qdf_nbuf_t buf = NULL;
uint32_t len = 0;
uint16_t idx = 0, psize = 0;
uint8_t alloc = 0, mslot = 0, pslot = 0;
if (!wbuff.initialized)
return NULL;
if ((num == 0) || (num > WBUFF_MAX_POOLS))
return NULL;
if (!wbuff_is_valid_alloc_req(req, num))
return NULL;
mslot = wbuff_get_free_mod_slot();
if (mslot == WBUFF_MAX_MODULES)
return NULL;
mod = &wbuff.mod[mslot];
mod->handle.id = mslot;
for (alloc = 0; alloc < num; alloc++) {
pslot = req[alloc].slot;
psize = req[alloc].size;
len = wbuff_get_len_from_pool_slot(pslot);
/**
* Allocate pool_cnt number of buffers for
* the pool given by pslot
*/
for (idx = 0; idx < psize; idx++) {
buf = wbuff_prepare_nbuf(mslot, pslot, len, reserve,
align);
if (!buf)
continue;
if (!mod->pool[pslot]) {
qdf_nbuf_set_next(buf, NULL);
mod->pool[pslot] = buf;
} else {
qdf_nbuf_set_next(buf, mod->pool[pslot]);
mod->pool[pslot] = buf;
}
}
}
mod->reserve = reserve;
mod->align = align;
return (struct wbuff_mod_handle *)&mod->handle;
}
QDF_STATUS wbuff_module_deregister(struct wbuff_mod_handle *hdl)
{
struct wbuff_handle *handle;
struct wbuff_module *mod = NULL;
uint8_t mslot = 0, pslot = 0;
qdf_nbuf_t first = NULL, buf = NULL;
handle = (struct wbuff_handle *)hdl;
if ((!wbuff.initialized) || (!wbuff_is_valid_handle(handle)))
return QDF_STATUS_E_INVAL;
mslot = handle->id;
mod = &wbuff.mod[mslot];
qdf_spin_lock_bh(&mod->lock);
for (pslot = 0; pslot < WBUFF_MAX_POOLS; pslot++) {
first = mod->pool[pslot];
while (first) {
buf = first;
first = qdf_nbuf_next(buf);
qdf_nbuf_free(buf);
}
}
mod->registered = false;
qdf_spin_unlock_bh(&mod->lock);
return QDF_STATUS_SUCCESS;
}
qdf_nbuf_t wbuff_buff_get(struct wbuff_mod_handle *hdl, uint32_t len)
{
struct wbuff_handle *handle;
struct wbuff_module *mod = NULL;
uint8_t mslot = 0;
uint8_t pslot = 0;
qdf_nbuf_t buf = NULL;
handle = (struct wbuff_handle *)hdl;
if ((!wbuff.initialized) || (!wbuff_is_valid_handle(handle)) || !len ||
(len > WBUFF_MAX_BUFFER_SIZE))
return NULL;
mslot = handle->id;
pslot = wbuff_get_pool_slot_from_len(len);
mod = &wbuff.mod[mslot];
qdf_spin_lock_bh(&mod->lock);
if (mod->pool[pslot]) {
buf = mod->pool[pslot];
mod->pool[pslot] = qdf_nbuf_next(buf);
mod->pending_returns++;
}
qdf_spin_unlock_bh(&mod->lock);
if (buf)
qdf_nbuf_set_next(buf, NULL);
return buf;
}
qdf_nbuf_t wbuff_buff_put(qdf_nbuf_t buf)
{
qdf_nbuf_t buffer = buf;
unsigned long slot_info = 0;
uint8_t mslot = 0, pslot = 0;
if (!wbuff.initialized)
return buffer;
slot_info = qdf_nbuf_get_dev_scratch(buf);
if (!slot_info)
return buffer;
mslot = (slot_info & WBUFF_MSLOT_BITMASK) >> WBUFF_MSLOT_SHIFT;
pslot = (slot_info & WBUFF_PSLOT_BITMASK) >> WBUFF_PSLOT_SHIFT;
qdf_nbuf_reset(buffer, wbuff.mod[mslot].reserve, wbuff.mod[mslot].
align);
qdf_spin_lock_bh(&wbuff.mod[mslot].lock);
if (wbuff.mod[mslot].registered) {
qdf_nbuf_set_next(buffer, wbuff.mod[mslot].pool[pslot]);
wbuff.mod[mslot].pool[pslot] = buffer;
wbuff.mod[mslot].pending_returns--;
buffer = NULL;
}
qdf_spin_unlock_bh(&wbuff.mod[mslot].lock);
return buffer;
}