/* * 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; }