diff --git a/qdf/Kbuild b/qdf/Kbuild index 3592c4b1d1..4f5dd58741 100644 --- a/qdf/Kbuild +++ b/qdf/Kbuild @@ -50,6 +50,7 @@ linux/src/qdf_crypto.o \ linux/src/qdf_module.o \ linux/src/qdf_net_if.o \ linux/src/qdf_nbuf.o \ +linux/src/qdf_nbuf_frag.o \ linux/src/qdf_perf.o \ linux/src/qdf_status.o \ linux/src/qdf_threads.o \ diff --git a/qdf/inc/qdf_nbuf.h b/qdf/inc/qdf_nbuf.h index 1feaa9a5c2..ca2eefd009 100644 --- a/qdf/inc/qdf_nbuf.h +++ b/qdf/inc/qdf_nbuf.h @@ -3667,6 +3667,86 @@ static inline void qdf_nbuf_orphan(qdf_nbuf_t buf) return __qdf_nbuf_orphan(buf); } +/** + * qdf_nbuf_get_frag_size_by_idx() - Get size of nbuf frag at index idx + * @nbuf: qdf_nbuf_t + * @idx: Frag index for which frag size is requested + * + * Return: Frag size + */ +static inline unsigned int qdf_nbuf_get_frag_size_by_idx(qdf_nbuf_t nbuf, + uint8_t idx) +{ + return __qdf_nbuf_get_frag_size_by_idx(nbuf, idx); +} + +/** + * qdf_nbuf_get_frag_addr() - Get nbuf frag address at index idx + * @nbuf: qdf_nbuf_t + * @idx: Frag index for which frag address is requested + * + * Return: Frag address + */ +static inline qdf_frag_t qdf_nbuf_get_frag_addr(qdf_nbuf_t nbuf, uint8_t idx) +{ + return __qdf_nbuf_get_frag_addr(nbuf, idx); +} + +/** + * qdf_nbuf_trim_add_frag_size() - Increase/Decrease frag_size by size + * @nbuf: qdf_nbuf_t + * @idx: Frag index + * @size: Size by which frag_size needs to be increased/decreased + * +Ve means increase, -Ve means decrease + * @truesize: truesize + */ +static inline void qdf_nbuf_trim_add_frag_size(qdf_nbuf_t nbuf, uint8_t idx, + int size, unsigned int truesize) +{ + __qdf_nbuf_trim_add_frag_size(nbuf, idx, size, truesize); +} + +/** + * qdf_nbuf_move_frag_page_offset() - Move frag page_offset by size + * and adjust length by size. + * @nbuf: qdf_nbuf_t + * @idx: Frag index + * @offset: Frag page offset should be moved by offset. + * +Ve - Move offset forward. + * -Ve - Move offset backward. + */ +static inline QDF_STATUS qdf_nbuf_move_frag_page_offset(qdf_nbuf_t nbuf, + uint8_t idx, + int offset) +{ + return __qdf_nbuf_move_frag_page_offset(nbuf, idx, offset); +} + +/** + * qdf_nbuf_add_rx_frag() - Add frag to nbuf at index frag_idx + * @buf: Frag pointer needs to be added in nbuf frag + * @nbuf: qdf_nbuf_t where frag will be added + * @offset: Offset in frag to be added to nbuf_frags + * @frag_len: Frag length + * @truesize: truesize + * @take_frag_ref: Whether to take ref for frag or not + * This bool must be set as per below comdition: + * 1. False: If this frag is being added in any nbuf + * for the first time after allocation + * 2. True: If frag is already attached part of any + * nbuf + * + * qdf_nbuf_add_rx_frag takes ref_count based on boolean flag take_frag_ref + */ +static inline void qdf_nbuf_add_rx_frag(qdf_frag_t buf, qdf_nbuf_t nbuf, + int offset, int frag_len, + unsigned int truesize, + bool take_frag_ref) +{ + __qdf_nbuf_add_rx_frag(buf, nbuf, offset, + frag_len, truesize, take_frag_ref); +} + #ifdef CONFIG_NBUF_AP_PLATFORM #include #else diff --git a/qdf/inc/qdf_nbuf_frag.h b/qdf/inc/qdf_nbuf_frag.h new file mode 100644 index 0000000000..b19e390eb8 --- /dev/null +++ b/qdf/inc/qdf_nbuf_frag.h @@ -0,0 +1,89 @@ +/* + * Copyright (c) 2020 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: qdf_nbuf_frag.h + * This file defines the nbuf frag abstraction. + */ + +#ifndef _QDF_NBUF_FRAG_H +#define _QDF_NBUF_FRAG_H + +#include +#include + +/* + * typedef qdf_frag_t - Platform independent frag address abstraction + */ +typedef __qdf_frag_t qdf_frag_t; + +/** + * Maximum number of frags an SKB can hold + */ +#define QDF_NBUF_MAX_FRAGS __QDF_NBUF_MAX_FRAGS + +/** + * qdf_mem_map_page() - Map Page + * @osdev: qdf_device_t + * @buf: Virtual page address to be mapped + * @dir: qdf_dma_dir_t + * @nbytes: Size of memory to be mapped + * @paddr: Corresponding mapped physical address + * + * Return: QDF_STATUS + */ +static inline QDF_STATUS qdf_mem_map_page(qdf_device_t osdev, qdf_frag_t buf, + qdf_dma_dir_t dir, size_t nbytes, + qdf_dma_addr_t *phy_addr) +{ + return __qdf_mem_map_page(osdev, buf, dir, nbytes, phy_addr); +} + +/** + * qdf_mem_unmap_page() - Unmap Page + * @osdev: qdf_device_t + * @paddr: Physical memory to be unmapped + * @nbytes: Size of memory to be unmapped + * @dir: qdf_dma_dir_t + */ +static inline void qdf_mem_unmap_page(qdf_device_t osdev, qdf_dma_addr_t paddr, + size_t nbytes, qdf_dma_dir_t dir) +{ + __qdf_mem_unmap_page(osdev, paddr, nbytes, dir); +} + +/** + * qdf_frag_free() - Free allocated frag memory + * @vaddr: Frag address to be freed. + */ +static inline void qdf_frag_free(qdf_frag_t vaddr) +{ + __qdf_frag_free(vaddr); +} + +/** + * qdf_frag_alloc() - Allocate frag memory + * @fragsz: Size of frag memory to be allocated + * + * Return: Allcated frag address + */ +static inline qdf_frag_t qdf_frag_alloc(unsigned int fragsz) +{ + return __qdf_frag_alloc(fragsz); +} +#endif /* _QDF_NBUF_FRAG_H */ diff --git a/qdf/linux/src/i_qdf_nbuf.h b/qdf/linux/src/i_qdf_nbuf.h index ea0418428b..2d864f8114 100644 --- a/qdf/linux/src/i_qdf_nbuf.h +++ b/qdf/linux/src/i_qdf_nbuf.h @@ -37,6 +37,7 @@ #include #include #include +#include /* * Use socket buffer as the underlying implementation as skbuf . @@ -2347,6 +2348,89 @@ void __qdf_nbuf_queue_head_unlock(struct sk_buff_head *skb_queue_head) spin_unlock_bh(&skb_queue_head->lock); } +/** + * __qdf_nbuf_get_frag_size_by_idx() - Get nbuf frag size at index idx + * @nbuf: qdf_nbuf_t + * @idx: Index for which frag size is requested + * + * Return: Frag size + */ +static inline unsigned int __qdf_nbuf_get_frag_size_by_idx(__qdf_nbuf_t nbuf, + uint8_t idx) +{ + unsigned int size = 0; + + if (likely(idx < __QDF_NBUF_MAX_FRAGS)) + size = skb_frag_size(&skb_shinfo(nbuf)->frags[idx]); + return size; +} + +/** + * __qdf_nbuf_get_frag_address() - Get nbuf frag address at index idx + * @nbuf: qdf_nbuf_t + * @idx: Index for which frag address is requested + * + * Return: Frag address in success, else NULL + */ +static inline __qdf_frag_t __qdf_nbuf_get_frag_addr(__qdf_nbuf_t nbuf, + uint8_t idx) +{ + __qdf_frag_t frag_addr = NULL; + + if (likely(idx < __QDF_NBUF_MAX_FRAGS)) + frag_addr = skb_frag_address(&skb_shinfo(nbuf)->frags[idx]); + return frag_addr; +} + +/** + * __qdf_nbuf_trim_add_frag_size() - Increase/Decrease frag_size by size + * @nbuf: qdf_nbuf_t + * @idx: Frag index + * @size: Size by which frag_size needs to be increased/decreased + * +Ve means increase, -Ve means decrease + * @truesize: truesize + */ +static inline void __qdf_nbuf_trim_add_frag_size(__qdf_nbuf_t nbuf, uint8_t idx, + int size, + unsigned int truesize) +{ + skb_coalesce_rx_frag(nbuf, idx, size, truesize); +} + +/** + * __qdf_nbuf_move_frag_page_offset() - Move frag page_offset by size + * and adjust length by size. + * @nbuf: qdf_nbuf_t + * @idx: Frag index + * @offset: Frag page offset should be moved by offset. + * +Ve - Move offset forward. + * -Ve - Move offset backward. + * + * Return: QDF_STATUS + */ +QDF_STATUS __qdf_nbuf_move_frag_page_offset(__qdf_nbuf_t nbuf, uint8_t idx, + int offset); + +/** + * __qdf_nbuf_add_rx_frag() - Add frag to nbuf at nr_frag index + * @buf: Frag pointer needs to be added in nbuf frag + * @nbuf: qdf_nbuf_t where frag will be added + * @offset: Offset in frag to be added to nbuf_frags + * @frag_len: Frag length + * @truesize: truesize + * @take_frag_ref: Whether to take ref for frag or not + * This bool must be set as per below comdition: + * 1. False: If this frag is being added in any nbuf + * for the first time after allocation. + * 2. True: If frag is already attached part of any + * nbuf. + * + * It takes ref_count based on boolean flag take_frag_ref + */ +void __qdf_nbuf_add_rx_frag(__qdf_frag_t buf, __qdf_nbuf_t nbuf, + int offset, int frag_len, + unsigned int truesize, bool take_frag_ref); + #ifdef CONFIG_NBUF_AP_PLATFORM #include #else diff --git a/qdf/linux/src/i_qdf_nbuf_frag.h b/qdf/linux/src/i_qdf_nbuf_frag.h new file mode 100644 index 0000000000..434d71847f --- /dev/null +++ b/qdf/linux/src/i_qdf_nbuf_frag.h @@ -0,0 +1,83 @@ +/* + * Copyright (c) 2020 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_qdf_nbuf_frag.h + * This file provides OS dependent nbuf frag API's. + */ + +#ifndef _I_QDF_NBUF_FRAG_H +#define _I_QDF_NBUF_FRAG_H + +#include +#include + +/** + * typedef __qdf_frag_t - Abstraction for void * for frag address + */ +typedef void *__qdf_frag_t; + +/** + * Maximum number of frags an SKB can hold + */ +#define __QDF_NBUF_MAX_FRAGS MAX_SKB_FRAGS + +/** + * __qdf_mem_unmap_page() - Unmap frag memory + * @osdev: qdf_device_t + * @paddr: Address to be unmapped + * @nbytes: Number of bytes to be unmapped + * @dir: qdf_dma_dir_t + */ +void __qdf_mem_unmap_page(qdf_device_t osdev, qdf_dma_addr_t paddr, + size_t nbytes, qdf_dma_dir_t dir); + +/** + * __qdf_mem_map_page() - Map frag memory + * @osdev: qdf_device_t + * @buf: Vaddr to be mapped + * @dir: qdf_dma_dir_t + * @nbytes: Number of bytes to be mapped + * @paddr: Mapped physical address + * + * Return: QDF_STATUS + */ +QDF_STATUS __qdf_mem_map_page(qdf_device_t osdev, __qdf_frag_t buf, + qdf_dma_dir_t dir, size_t nbytes, + qdf_dma_addr_t *phy_addr); + +/** + * __qdf_frag_free() - Free allocated frag memory + * @vaddr: Frag address to be freed + */ +static inline void __qdf_frag_free(__qdf_frag_t vaddr) +{ + skb_free_frag(vaddr); +} + +/** + * __qdf_frag_alloc() - Allocate frag Memory + * @fragsz: Size of frag memory to be allocated + * + * Return: Allocated frag addr. + */ +static inline __qdf_frag_t __qdf_frag_alloc(unsigned int fragsz) +{ + return netdev_alloc_frag(fragsz); +} +#endif /* _I_QDF_NBUF_FRAG_H */ diff --git a/qdf/linux/src/qdf_nbuf.c b/qdf/linux/src/qdf_nbuf.c index f2985a2a2a..60b7ff2962 100644 --- a/qdf/linux/src/qdf_nbuf.c +++ b/qdf/linux/src/qdf_nbuf.c @@ -4574,3 +4574,71 @@ void __qdf_nbuf_mod_exit(void) { } #endif + +#if (LINUX_VERSION_CODE >= KERNEL_VERSION(5, 4, 0)) +QDF_STATUS __qdf_nbuf_move_frag_page_offset(__qdf_nbuf_t nbuf, uint8_t idx, + int offset) +{ + unsigned int frag_offset; + skb_frag_t *frag; + + if (qdf_unlikely(idx >= __qdf_nbuf_get_nr_frags(nbuf))) + return QDF_STATUS_E_FAILURE; + + frag = &skb_shinfo(nbuf)->frags[idx]; + frag_offset = skb_frag_off(frag); + + frag_offset += offset; + skb_frag_off_set(frag, frag_offset); + + __qdf_nbuf_trim_add_frag_size(nbuf, idx, -(offset), 0); + + return QDF_STATUS_SUCCESS; +} + +#else +QDF_STATUS __qdf_nbuf_move_frag_page_offset(__qdf_nbuf_t nbuf, uint8_t idx, + int offset) +{ + uint16_t frag_offset; + skb_frag_t *frag; + + if (qdf_unlikely(idx >= __qdf_nbuf_get_nr_frags(nbuf))) + return QDF_STATUS_E_FAILURE; + + frag = &skb_shinfo(nbuf)->frags[idx]; + frag_offset = frag->page_offset; + + frag_offset += offset; + frag->page_offset = frag_offset; + + __qdf_nbuf_trim_add_frag_size(nbuf, idx, -(offset), 0); + + return QDF_STATUS_SUCCESS; +} +#endif + +qdf_export_symbol(__qdf_nbuf_move_frag_page_offset); + +void __qdf_nbuf_add_rx_frag(__qdf_frag_t buf, __qdf_nbuf_t nbuf, + int offset, int frag_len, + unsigned int truesize, bool take_frag_ref) +{ + struct page *page; + int frag_offset; + uint8_t nr_frag; + + nr_frag = __qdf_nbuf_get_nr_frags(nbuf); + + page = virt_to_head_page(buf); + frag_offset = buf - page_address(page); + + skb_add_rx_frag(nbuf, nr_frag, page, + (frag_offset + offset), + frag_len, truesize); + + if (unlikely(take_frag_ref)) + skb_frag_ref(nbuf, nr_frag); +} + +qdf_export_symbol(__qdf_nbuf_add_rx_frag); diff --git a/qdf/linux/src/qdf_nbuf_frag.c b/qdf/linux/src/qdf_nbuf_frag.c new file mode 100644 index 0000000000..135fadc1ea --- /dev/null +++ b/qdf/linux/src/qdf_nbuf_frag.c @@ -0,0 +1,68 @@ +/* + * Copyright (c) 2020 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: qdf_nbuf_frag.c + * QCA driver framework(QDF) network nbuf frag management APIs + */ + +#include +#include + +#if defined(HIF_PCI) +QDF_STATUS __qdf_mem_map_page(qdf_device_t osdev, __qdf_frag_t buf, + qdf_dma_dir_t dir, size_t nbytes, + qdf_dma_addr_t *phy_addr) +{ + struct page *page; + unsigned long offset; + + page = virt_to_head_page(buf); + offset = buf - page_address(page); + *phy_addr = dma_map_page(osdev->dev, page, offset, nbytes, + __qdf_dma_dir_to_os(dir)); + + return dma_mapping_error(osdev->dev, *phy_addr) ? + QDF_STATUS_E_FAILURE : QDF_STATUS_SUCCESS; +} +#else +QDF_STATUS __qdf_mem_map_page(qdf_device_t osdev, __qdf_frag_t buf, + qdf_dma_dir_t dir, size_t nbytes, + qdf_dma_addr_t *phy_addr) +{ + return QDF_STATUS_SUCCESS; +} +#endif + +qdf_export_symbol(__qdf_mem_map_page); + +#if defined(HIF_PCI) +void __qdf_mem_unmap_page(qdf_device_t osdev, qdf_dma_addr_t paddr, + size_t nbytes, qdf_dma_dir_t dir) +{ + dma_unmap_page(osdev->dev, paddr, nbytes, + __qdf_dma_dir_to_os(dir)); +} +#else +void __qdf_mem_unmap_page(qdf_device_t osdev, qdf_dma_addr_t paddr, + size_t nbytes, qdf_dma_dir_t dir) +{ +} +#endif + +qdf_export_symbol(__qdf_mem_unmap_page);