diff --git a/qdf/inc/qdf_nbuf.h b/qdf/inc/qdf_nbuf.h index 5570b8ea91..de97deb6a7 100644 --- a/qdf/inc/qdf_nbuf.h +++ b/qdf/inc/qdf_nbuf.h @@ -3900,6 +3900,22 @@ void qdf_net_buf_debug_acquire_frag(qdf_nbuf_t buf, const char *func, void qdf_net_buf_debug_release_frag(qdf_nbuf_t buf, const char *func, uint32_t line); +/** + * qdf_nbuf_frag_count_inc() - Increment global frag counter + * @buf: qdf_nbuf_t + * + * Return: none + */ +void qdf_nbuf_frag_count_inc(qdf_nbuf_t buf); + +/** + * qdf_nbuf_frag_count_dec() - Decrement global frag counter + * @buf: qdf_nbuf_t + * + * Return: none + */ +void qdf_nbuf_frag_count_dec(qdf_nbuf_t buf); + #else /* NBUF_FRAG_MEMORY_DEBUG */ /** @@ -3954,6 +3970,15 @@ static inline void qdf_net_buf_debug_release_frag(qdf_nbuf_t buf, uint32_t line) { } + +static inline void qdf_nbuf_frag_count_inc(qdf_nbuf_t buf) +{ +} + +static inline void qdf_nbuf_frag_count_dec(qdf_nbuf_t buf) +{ +} + #endif /* NBUF_FRAG_MEMORY_DEBUG */ #ifdef MEMORY_DEBUG diff --git a/qdf/linux/src/i_qdf_nbuf.h b/qdf/linux/src/i_qdf_nbuf.h index 01e18a2e32..03ac69ccf6 100644 --- a/qdf/linux/src/i_qdf_nbuf.h +++ b/qdf/linux/src/i_qdf_nbuf.h @@ -772,6 +772,20 @@ __qdf_nbuf_alloc(__qdf_device_t osdev, size_t size, int reserve, int align, __qdf_nbuf_t __qdf_nbuf_alloc_no_recycler(size_t size, int reserve, int align, const char *func, uint32_t line); +/** + * __qdf_nbuf_clone() - clone the nbuf (copy is readonly) + * @skb: Pointer to network buffer + * + * if GFP_ATOMIC is overkill then we can check whether its + * called from interrupt context and then do it or else in + * normal case use GFP_KERNEL + * + * example use "in_irq() || irqs_disabled()" + * + * Return: cloned skb + */ +__qdf_nbuf_t __qdf_nbuf_clone(__qdf_nbuf_t nbuf); + void __qdf_nbuf_free(struct sk_buff *skb); QDF_STATUS __qdf_nbuf_map(__qdf_device_t osdev, struct sk_buff *skb, qdf_dma_dir_t dir); @@ -1039,30 +1053,6 @@ static inline size_t __qdf_nbuf_get_nr_frags(struct sk_buff *skb) */ #define __qdf_nbuf_pool_delete(osdev) -/** - * __qdf_nbuf_clone() - clone the nbuf (copy is readonly) - * @skb: Pointer to network buffer - * - * if GFP_ATOMIC is overkill then we can check whether its - * called from interrupt context and then do it or else in - * normal case use GFP_KERNEL - * - * example use "in_irq() || irqs_disabled()" - * - * Return: cloned skb - */ -static inline struct sk_buff *__qdf_nbuf_clone(struct sk_buff *skb) -{ - struct sk_buff *skb_new = NULL; - - skb_new = skb_clone(skb, GFP_ATOMIC); - if (skb_new) { - __qdf_frag_count_inc(__qdf_nbuf_get_nr_frags(skb_new)); - __qdf_nbuf_count_inc(skb_new); - } - return skb_new; -} - /** * __qdf_nbuf_copy() - returns a private copy of the skb * @skb: Pointer to network buffer @@ -1078,7 +1068,6 @@ static inline struct sk_buff *__qdf_nbuf_copy(struct sk_buff *skb) skb_new = skb_copy(skb, GFP_ATOMIC); if (skb_new) { - __qdf_frag_count_inc(__qdf_nbuf_get_nr_frags(skb_new)); __qdf_nbuf_count_inc(skb_new); } return skb_new; diff --git a/qdf/linux/src/qdf_nbuf.c b/qdf/linux/src/qdf_nbuf.c index b0c99be128..dc67ff8177 100644 --- a/qdf/linux/src/qdf_nbuf.c +++ b/qdf/linux/src/qdf_nbuf.c @@ -445,6 +445,64 @@ void __qdf_nbuf_count_dec(__qdf_nbuf_t nbuf) qdf_export_symbol(__qdf_nbuf_count_dec); #endif +#ifdef NBUF_FRAG_MEMORY_DEBUG +void qdf_nbuf_frag_count_inc(qdf_nbuf_t nbuf) +{ + qdf_nbuf_t ext_list; + uint32_t num_nr_frags; + uint32_t total_num_nr_frags; + + num_nr_frags = qdf_nbuf_get_nr_frags(nbuf); + qdf_assert_always(num_nr_frags <= QDF_NBUF_MAX_FRAGS); + + total_num_nr_frags = num_nr_frags; + + /* Take into account the frags attached to frag_list */ + ext_list = qdf_nbuf_get_ext_list(nbuf); + while (ext_list) { + num_nr_frags = qdf_nbuf_get_nr_frags(ext_list); + qdf_assert_always(num_nr_frags <= QDF_NBUF_MAX_FRAGS); + total_num_nr_frags += num_nr_frags; + ext_list = qdf_nbuf_queue_next(ext_list); + } + + qdf_frag_count_inc(total_num_nr_frags); +} + +qdf_export_symbol(qdf_nbuf_frag_count_inc); + +void qdf_nbuf_frag_count_dec(qdf_nbuf_t nbuf) +{ + qdf_nbuf_t ext_list; + uint32_t num_nr_frags; + uint32_t total_num_nr_frags; + + if (qdf_nbuf_get_users(nbuf) > 1) + return; + + num_nr_frags = qdf_nbuf_get_nr_frags(nbuf); + qdf_assert_always(num_nr_frags <= QDF_NBUF_MAX_FRAGS); + + total_num_nr_frags = num_nr_frags; + + /* Take into account the frags attached to frag_list */ + ext_list = qdf_nbuf_get_ext_list(nbuf); + while (ext_list) { + if (qdf_nbuf_get_users(ext_list) == 1) { + num_nr_frags = qdf_nbuf_get_nr_frags(ext_list); + qdf_assert_always(num_nr_frags <= QDF_NBUF_MAX_FRAGS); + total_num_nr_frags += num_nr_frags; + } + ext_list = qdf_nbuf_queue_next(ext_list); + } + + qdf_frag_count_dec(total_num_nr_frags); +} + +qdf_export_symbol(qdf_nbuf_frag_count_dec); + +#endif + #if defined(CONFIG_WIFI_EMULATION_WIFI_3_0) && defined(BUILD_X86) && \ !defined(QCA_WIFI_QCN9000) struct sk_buff *__qdf_nbuf_alloc(qdf_device_t osdev, size_t size, int reserve, @@ -636,13 +694,7 @@ void __qdf_nbuf_free(struct sk_buff *skb) if (pld_nbuf_pre_alloc_free(skb)) return; - /** - * Decrement global frag counter only when last user of nbuf - * does free so as to avoid decrementing count on every free - * expect the last one in case where nbuf has multiple users - */ - if (qdf_nbuf_get_users(skb) == 1) - qdf_frag_count_dec(qdf_nbuf_get_nr_frags(skb)); + qdf_nbuf_frag_count_dec(skb); qdf_nbuf_count_dec(skb); qdf_mem_skb_dec(skb->truesize); @@ -654,6 +706,20 @@ void __qdf_nbuf_free(struct sk_buff *skb) qdf_export_symbol(__qdf_nbuf_free); +__qdf_nbuf_t __qdf_nbuf_clone(__qdf_nbuf_t skb) +{ + qdf_nbuf_t skb_new = NULL; + + skb_new = skb_clone(skb, GFP_ATOMIC); + if (skb_new) { + qdf_nbuf_frag_count_inc(skb_new); + qdf_nbuf_count_inc(skb_new); + } + return skb_new; +} + +qdf_export_symbol(__qdf_nbuf_clone); + #ifdef NBUF_MEMORY_DEBUG enum qdf_nbuf_event_type { QDF_NBUF_ALLOC, @@ -2885,11 +2951,24 @@ void qdf_nbuf_free_debug(qdf_nbuf_t nbuf, const char *func, uint32_t line) idx++; } - /* Take care to delete the debug entries for frag_list */ + /** + * Take care to update the debug entries for frag_list and also + * for the frags attached to frag_list + */ ext_list = qdf_nbuf_get_ext_list(nbuf); while (ext_list) { if (qdf_nbuf_get_users(ext_list) == 1) { qdf_nbuf_panic_on_free_if_mapped(ext_list, func, line); + idx = 0; + num_nr_frags = qdf_nbuf_get_nr_frags(ext_list); + qdf_assert_always(num_nr_frags <= QDF_NBUF_MAX_FRAGS); + while (idx < num_nr_frags) { + p_frag = qdf_nbuf_get_frag_addr(ext_list, idx); + if (qdf_likely(p_frag)) + qdf_frag_debug_refcount_dec(p_frag, + func, line); + idx++; + } qdf_net_buf_debug_delete_node(ext_list); } @@ -2905,6 +2984,7 @@ qdf_nbuf_t qdf_nbuf_clone_debug(qdf_nbuf_t buf, const char *func, uint32_t line) { uint32_t num_nr_frags; uint32_t idx = 0; + qdf_nbuf_t ext_list; qdf_frag_t p_frag; qdf_nbuf_t cloned_buf = __qdf_nbuf_clone(buf); @@ -2927,6 +3007,23 @@ qdf_nbuf_t qdf_nbuf_clone_debug(qdf_nbuf_t buf, const char *func, uint32_t line) idx++; } + /* Take care to update debug entries for frags attached to frag_list */ + ext_list = qdf_nbuf_get_ext_list(cloned_buf); + while (ext_list) { + idx = 0; + num_nr_frags = qdf_nbuf_get_nr_frags(ext_list); + + qdf_assert_always(num_nr_frags <= QDF_NBUF_MAX_FRAGS); + + while (idx < num_nr_frags) { + p_frag = qdf_nbuf_get_frag_addr(ext_list, idx); + if (qdf_likely(p_frag)) + qdf_frag_debug_refcount_inc(p_frag, func, line); + idx++; + } + ext_list = qdf_nbuf_queue_next(ext_list); + } + /* Store SKB in internal QDF tracking table */ qdf_net_buf_debug_add_node(cloned_buf, 0, func, line); qdf_nbuf_history_add(cloned_buf, func, line, QDF_NBUF_ALLOC_CLONE); @@ -4878,6 +4975,7 @@ void qdf_net_buf_debug_acquire_frag(qdf_nbuf_t buf, const char *func, { uint32_t num_nr_frags; uint32_t idx = 0; + qdf_nbuf_t ext_list; qdf_frag_t p_frag; if (qdf_unlikely(!buf)) @@ -4894,6 +4992,26 @@ void qdf_net_buf_debug_acquire_frag(qdf_nbuf_t buf, const char *func, qdf_frag_debug_refcount_inc(p_frag, func, line); idx++; } + + /** + * Take care to update the refcount in the debug entries for the + * frags attached to frag_list + */ + ext_list = qdf_nbuf_get_ext_list(buf); + while (ext_list) { + idx = 0; + num_nr_frags = qdf_nbuf_get_nr_frags(ext_list); + + qdf_assert_always(num_nr_frags <= QDF_NBUF_MAX_FRAGS); + + while (idx < num_nr_frags) { + p_frag = qdf_nbuf_get_frag_addr(ext_list, idx); + if (qdf_likely(p_frag)) + qdf_frag_debug_refcount_inc(p_frag, func, line); + idx++; + } + ext_list = qdf_nbuf_queue_next(ext_list); + } } qdf_export_symbol(qdf_net_buf_debug_acquire_frag); @@ -4902,6 +5020,7 @@ void qdf_net_buf_debug_release_frag(qdf_nbuf_t buf, const char *func, uint32_t line) { uint32_t num_nr_frags; + qdf_nbuf_t ext_list; uint32_t idx = 0; qdf_frag_t p_frag; @@ -4928,6 +5047,24 @@ void qdf_net_buf_debug_release_frag(qdf_nbuf_t buf, const char *func, qdf_frag_debug_refcount_dec(p_frag, func, line); idx++; } + + /* Take care to update debug entries for frags attached to frag_list */ + ext_list = qdf_nbuf_get_ext_list(buf); + while (ext_list) { + if (qdf_nbuf_get_users(ext_list) == 1) { + idx = 0; + num_nr_frags = qdf_nbuf_get_nr_frags(ext_list); + qdf_assert_always(num_nr_frags <= QDF_NBUF_MAX_FRAGS); + while (idx < num_nr_frags) { + p_frag = qdf_nbuf_get_frag_addr(ext_list, idx); + if (qdf_likely(p_frag)) + qdf_frag_debug_refcount_dec(p_frag, + func, line); + idx++; + } + } + ext_list = qdf_nbuf_queue_next(ext_list); + } } qdf_export_symbol(qdf_net_buf_debug_release_frag);