diff --git a/wbuff/inc/wbuff.h b/wbuff/inc/wbuff.h new file mode 100644 index 0000000000..4a7143582c --- /dev/null +++ b/wbuff/inc/wbuff.h @@ -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 +#include + +/* 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 */ diff --git a/wbuff/src/i_wbuff.h b/wbuff/src/i_wbuff.h new file mode 100644 index 0000000000..ce7b69ec71 --- /dev/null +++ b/wbuff/src/i_wbuff.h @@ -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 + +/* 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 */ diff --git a/wbuff/src/wbuff.c b/wbuff/src/wbuff.c new file mode 100644 index 0000000000..ca3516d152 --- /dev/null +++ b/wbuff/src/wbuff.c @@ -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 +#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; +}