mirror of
https://github.com/BobTheBlinker/android_kernel_motorola_sm6375.git
synced 2026-10-24 21:54:34 -04:00
qcacmn: Add nbuf map/unmap tracking
When MEMORY_DEBUG is enabled, track calls to nbuf map/unmap to detect the following usage problems: * double map * double unmap * unmap without map * never unmap Change-Id: I7326bb628d4cf0be9004398fd396907b1a06bf22 CRs-Fixed: 2155599
This commit is contained in:
parent
71ab9ef0b7
commit
4082a0951f
2 changed files with 325 additions and 17 deletions
|
|
@ -522,6 +522,96 @@ qdf_nbuf_set_send_complete_flag(qdf_nbuf_t buf, bool flag)
|
|||
__qdf_nbuf_set_send_complete_flag(buf, flag);
|
||||
}
|
||||
|
||||
#ifdef MEMORY_DEBUG
|
||||
/**
|
||||
* qdf_nbuf_map_check_for_leaks() - check for nbut map leaks
|
||||
*
|
||||
* Check for net buffers that have been mapped, but never unmapped.
|
||||
*
|
||||
* Returns: None
|
||||
*/
|
||||
void qdf_nbuf_map_check_for_leaks(void);
|
||||
|
||||
QDF_STATUS qdf_nbuf_map_debug(qdf_device_t osdev,
|
||||
qdf_nbuf_t buf,
|
||||
qdf_dma_dir_t dir,
|
||||
const char *file,
|
||||
uint32_t line);
|
||||
|
||||
#define qdf_nbuf_map(osdev, buf, dir) \
|
||||
qdf_nbuf_map_debug(osdev, buf, dir, __FILE__, __LINE__)
|
||||
|
||||
void qdf_nbuf_unmap_debug(qdf_device_t osdev,
|
||||
qdf_nbuf_t buf,
|
||||
qdf_dma_dir_t dir,
|
||||
const char *file,
|
||||
uint32_t line);
|
||||
|
||||
#define qdf_nbuf_unmap(osdev, buf, dir) \
|
||||
qdf_nbuf_unmap_debug(osdev, buf, dir, __FILE__, __LINE__)
|
||||
|
||||
QDF_STATUS qdf_nbuf_map_single_debug(qdf_device_t osdev,
|
||||
qdf_nbuf_t buf,
|
||||
qdf_dma_dir_t dir,
|
||||
const char *file,
|
||||
uint32_t line);
|
||||
|
||||
#define qdf_nbuf_map_single(osdev, buf, dir) \
|
||||
qdf_nbuf_map_single_debug(osdev, buf, dir, __FILE__, __LINE__)
|
||||
|
||||
void qdf_nbuf_unmap_single_debug(qdf_device_t osdev,
|
||||
qdf_nbuf_t buf,
|
||||
qdf_dma_dir_t dir,
|
||||
const char *file,
|
||||
uint32_t line);
|
||||
|
||||
#define qdf_nbuf_unmap_single(osdev, buf, dir) \
|
||||
qdf_nbuf_unmap_single_debug(osdev, buf, dir, __FILE__, __LINE__)
|
||||
|
||||
QDF_STATUS qdf_nbuf_map_nbytes_debug(qdf_device_t osdev,
|
||||
qdf_nbuf_t buf,
|
||||
qdf_dma_dir_t dir,
|
||||
int nbytes,
|
||||
const char *file,
|
||||
uint32_t line);
|
||||
|
||||
#define qdf_nbuf_map_nbytes(osdev, buf, dir, nbytes) \
|
||||
qdf_nbuf_map_nbytes_debug(osdev, buf, dir, nbytes, __FILE__, __LINE__)
|
||||
|
||||
void qdf_nbuf_unmap_nbytes_debug(qdf_device_t osdev,
|
||||
qdf_nbuf_t buf,
|
||||
qdf_dma_dir_t dir,
|
||||
int nbytes,
|
||||
const char *file,
|
||||
uint32_t line);
|
||||
|
||||
#define qdf_nbuf_unmap_nbytes(osdev, buf, dir, nbytes) \
|
||||
qdf_nbuf_unmap_nbytes_debug(osdev, buf, dir, nbytes, __FILE__, __LINE__)
|
||||
|
||||
QDF_STATUS qdf_nbuf_map_nbytes_single_debug(qdf_device_t osdev,
|
||||
qdf_nbuf_t buf,
|
||||
qdf_dma_dir_t dir,
|
||||
int nbytes,
|
||||
const char *file,
|
||||
uint32_t line);
|
||||
|
||||
#define qdf_nbuf_map_nbytes_single(osdev, buf, dir, nbytes) \
|
||||
qdf_nbuf_map_nbytes_single_debug(osdev, buf, dir, nbytes, \
|
||||
__FILE__, __LINE__)
|
||||
|
||||
void qdf_nbuf_unmap_nbytes_single_debug(qdf_device_t osdev,
|
||||
qdf_nbuf_t buf,
|
||||
qdf_dma_dir_t dir,
|
||||
int nbytes,
|
||||
const char *file,
|
||||
uint32_t line);
|
||||
|
||||
#define qdf_nbuf_unmap_nbytes_single(osdev, buf, dir, nbytes) \
|
||||
qdf_nbuf_unmap_nbytes_single_debug(osdev, buf, dir, nbytes, \
|
||||
__FILE__, __LINE__)
|
||||
|
||||
#else /* MEMORY_DEBUG */
|
||||
|
||||
static inline QDF_STATUS
|
||||
qdf_nbuf_map(qdf_device_t osdev, qdf_nbuf_t buf, qdf_dma_dir_t dir)
|
||||
{
|
||||
|
|
@ -534,6 +624,18 @@ qdf_nbuf_unmap(qdf_device_t osdev, qdf_nbuf_t buf, qdf_dma_dir_t dir)
|
|||
__qdf_nbuf_unmap(osdev, buf, dir);
|
||||
}
|
||||
|
||||
static inline QDF_STATUS
|
||||
qdf_nbuf_map_single(qdf_device_t osdev, qdf_nbuf_t buf, qdf_dma_dir_t dir)
|
||||
{
|
||||
return __qdf_nbuf_map_single(osdev, buf, dir);
|
||||
}
|
||||
|
||||
static inline void
|
||||
qdf_nbuf_unmap_single(qdf_device_t osdev, qdf_nbuf_t buf, qdf_dma_dir_t dir)
|
||||
{
|
||||
__qdf_nbuf_unmap_single(osdev, buf, dir);
|
||||
}
|
||||
|
||||
static inline QDF_STATUS
|
||||
qdf_nbuf_map_nbytes(qdf_device_t osdev, qdf_nbuf_t buf,
|
||||
qdf_dma_dir_t dir, int nbytes)
|
||||
|
|
@ -548,18 +650,6 @@ qdf_nbuf_unmap_nbytes(qdf_device_t osdev,
|
|||
__qdf_nbuf_unmap_nbytes(osdev, buf, dir, nbytes);
|
||||
}
|
||||
|
||||
static inline void
|
||||
qdf_nbuf_sync_for_cpu(qdf_device_t osdev, qdf_nbuf_t buf, qdf_dma_dir_t dir)
|
||||
{
|
||||
__qdf_nbuf_sync_for_cpu(osdev, buf, dir);
|
||||
}
|
||||
|
||||
static inline QDF_STATUS
|
||||
qdf_nbuf_map_single(qdf_device_t osdev, qdf_nbuf_t buf, qdf_dma_dir_t dir)
|
||||
{
|
||||
return __qdf_nbuf_map_single(osdev, buf, dir);
|
||||
}
|
||||
|
||||
static inline QDF_STATUS
|
||||
qdf_nbuf_map_nbytes_single(
|
||||
qdf_device_t osdev, qdf_nbuf_t buf, qdf_dma_dir_t dir, int nbytes)
|
||||
|
|
@ -567,18 +657,19 @@ qdf_nbuf_map_nbytes_single(
|
|||
return __qdf_nbuf_map_nbytes_single(osdev, buf, dir, nbytes);
|
||||
}
|
||||
|
||||
static inline void
|
||||
qdf_nbuf_unmap_single(qdf_device_t osdev, qdf_nbuf_t buf, qdf_dma_dir_t dir)
|
||||
{
|
||||
__qdf_nbuf_unmap_single(osdev, buf, dir);
|
||||
}
|
||||
|
||||
static inline void
|
||||
qdf_nbuf_unmap_nbytes_single(
|
||||
qdf_device_t osdev, qdf_nbuf_t buf, qdf_dma_dir_t dir, int nbytes)
|
||||
{
|
||||
return __qdf_nbuf_unmap_nbytes_single(osdev, buf, dir, nbytes);
|
||||
}
|
||||
#endif /* MEMORY_DEBUG */
|
||||
|
||||
static inline void
|
||||
qdf_nbuf_sync_for_cpu(qdf_device_t osdev, qdf_nbuf_t buf, qdf_dma_dir_t dir)
|
||||
{
|
||||
__qdf_nbuf_sync_for_cpu(osdev, buf, dir);
|
||||
}
|
||||
|
||||
static inline int qdf_nbuf_get_num_frags(qdf_nbuf_t buf)
|
||||
{
|
||||
|
|
|
|||
|
|
@ -30,6 +30,7 @@
|
|||
* QCA driver framework(QDF) network buffer management APIs
|
||||
*/
|
||||
|
||||
#include <linux/hashtable.h>
|
||||
#include <linux/kernel.h>
|
||||
#include <linux/version.h>
|
||||
#include <linux/skbuff.h>
|
||||
|
|
@ -468,6 +469,220 @@ void __qdf_nbuf_free(struct sk_buff *skb)
|
|||
|
||||
EXPORT_SYMBOL(__qdf_nbuf_free);
|
||||
|
||||
#ifdef MEMORY_DEBUG
|
||||
#define QDF_NBUF_MAP_HT_BITS 10 /* 1024 buckets */
|
||||
static DECLARE_HASHTABLE(qdf_nbuf_map_ht, QDF_NBUF_MAP_HT_BITS);
|
||||
static qdf_spinlock_t qdf_nbuf_map_lock;
|
||||
|
||||
struct qdf_nbuf_map_metadata {
|
||||
struct hlist_node node;
|
||||
qdf_nbuf_t nbuf;
|
||||
const char *file;
|
||||
uint32_t line;
|
||||
};
|
||||
|
||||
static void qdf_nbuf_map_tracking_init(void)
|
||||
{
|
||||
hash_init(qdf_nbuf_map_ht);
|
||||
qdf_spinlock_create(&qdf_nbuf_map_lock);
|
||||
}
|
||||
|
||||
void qdf_nbuf_map_check_for_leaks(void)
|
||||
{
|
||||
struct qdf_nbuf_map_metadata *meta;
|
||||
int bucket;
|
||||
uint32_t count = 0;
|
||||
bool is_empty;
|
||||
|
||||
qdf_spin_lock_irqsave(&qdf_nbuf_map_lock);
|
||||
is_empty = hash_empty(qdf_nbuf_map_ht);
|
||||
qdf_spin_unlock_irqrestore(&qdf_nbuf_map_lock);
|
||||
|
||||
if (is_empty)
|
||||
return;
|
||||
|
||||
qdf_err("Nbuf map without unmap events detected!");
|
||||
qdf_err("------------------------------------------------------------");
|
||||
|
||||
qdf_spin_lock_irqsave(&qdf_nbuf_map_lock);
|
||||
hash_for_each(qdf_nbuf_map_ht, bucket, meta, node) {
|
||||
qdf_spin_unlock_irqrestore(&qdf_nbuf_map_lock);
|
||||
|
||||
count++;
|
||||
qdf_err("0x%pk @ %s:%u", meta->nbuf, kbasename(meta->file),
|
||||
meta->line);
|
||||
|
||||
qdf_spin_lock_irqsave(&qdf_nbuf_map_lock);
|
||||
}
|
||||
qdf_spin_unlock_irqrestore(&qdf_nbuf_map_lock);
|
||||
|
||||
panic("%u fatal nbuf map without unmap events detected!", count);
|
||||
}
|
||||
|
||||
static void qdf_nbuf_map_tracking_deinit(void)
|
||||
{
|
||||
qdf_nbuf_map_check_for_leaks();
|
||||
qdf_spinlock_destroy(&qdf_nbuf_map_lock);
|
||||
}
|
||||
|
||||
static struct qdf_nbuf_map_metadata *qdf_nbuf_meta_get(qdf_nbuf_t nbuf)
|
||||
{
|
||||
struct qdf_nbuf_map_metadata *meta;
|
||||
|
||||
hash_for_each_possible(qdf_nbuf_map_ht, meta, node, (size_t)nbuf) {
|
||||
if (meta->nbuf == nbuf)
|
||||
return meta;
|
||||
}
|
||||
|
||||
return NULL;
|
||||
}
|
||||
|
||||
static void
|
||||
qdf_nbuf_track_map(qdf_nbuf_t nbuf, const char *file, uint32_t line)
|
||||
{
|
||||
struct qdf_nbuf_map_metadata *meta;
|
||||
|
||||
QDF_BUG(nbuf);
|
||||
if (!nbuf) {
|
||||
qdf_err("Cannot map null nbuf");
|
||||
return;
|
||||
}
|
||||
|
||||
qdf_spin_lock_irqsave(&qdf_nbuf_map_lock);
|
||||
meta = qdf_nbuf_meta_get(nbuf);
|
||||
qdf_spin_unlock_irqrestore(&qdf_nbuf_map_lock);
|
||||
if (meta)
|
||||
panic("Double nbuf map detected @ %s:%u",
|
||||
kbasename(file), line);
|
||||
|
||||
meta = qdf_mem_malloc(sizeof(*meta));
|
||||
if (!meta) {
|
||||
qdf_err("Failed to allocate nbuf map tracking metadata");
|
||||
return;
|
||||
}
|
||||
|
||||
meta->nbuf = nbuf;
|
||||
meta->file = file;
|
||||
meta->line = line;
|
||||
|
||||
qdf_spin_lock_irqsave(&qdf_nbuf_map_lock);
|
||||
hash_add(qdf_nbuf_map_ht, &meta->node, (size_t)nbuf);
|
||||
qdf_spin_unlock_irqrestore(&qdf_nbuf_map_lock);
|
||||
}
|
||||
|
||||
static void
|
||||
qdf_nbuf_untrack_map(qdf_nbuf_t nbuf, const char *file, uint32_t line)
|
||||
{
|
||||
struct qdf_nbuf_map_metadata *meta;
|
||||
|
||||
QDF_BUG(nbuf);
|
||||
if (!nbuf) {
|
||||
qdf_err("Cannot unmap null nbuf");
|
||||
return;
|
||||
}
|
||||
|
||||
qdf_spin_lock_irqsave(&qdf_nbuf_map_lock);
|
||||
meta = qdf_nbuf_meta_get(nbuf);
|
||||
|
||||
if (!meta)
|
||||
panic("Double nbuf unmap or unmap without map detected @%s:%u",
|
||||
kbasename(file), line);
|
||||
|
||||
hash_del(&meta->node);
|
||||
qdf_spin_unlock_irqrestore(&qdf_nbuf_map_lock);
|
||||
|
||||
qdf_mem_free(meta);
|
||||
}
|
||||
|
||||
QDF_STATUS qdf_nbuf_map_debug(qdf_device_t osdev,
|
||||
qdf_nbuf_t buf,
|
||||
qdf_dma_dir_t dir,
|
||||
const char *file,
|
||||
uint32_t line)
|
||||
{
|
||||
qdf_nbuf_track_map(buf, file, line);
|
||||
|
||||
return __qdf_nbuf_map(osdev, buf, dir);
|
||||
}
|
||||
|
||||
void qdf_nbuf_unmap_debug(qdf_device_t osdev,
|
||||
qdf_nbuf_t buf,
|
||||
qdf_dma_dir_t dir,
|
||||
const char *file,
|
||||
uint32_t line)
|
||||
{
|
||||
qdf_nbuf_untrack_map(buf, file, line);
|
||||
__qdf_nbuf_unmap_single(osdev, buf, dir);
|
||||
}
|
||||
|
||||
QDF_STATUS qdf_nbuf_map_single_debug(qdf_device_t osdev,
|
||||
qdf_nbuf_t buf,
|
||||
qdf_dma_dir_t dir,
|
||||
const char *file,
|
||||
uint32_t line)
|
||||
{
|
||||
qdf_nbuf_track_map(buf, file, line);
|
||||
|
||||
return __qdf_nbuf_map_single(osdev, buf, dir);
|
||||
}
|
||||
|
||||
void qdf_nbuf_unmap_single_debug(qdf_device_t osdev,
|
||||
qdf_nbuf_t buf,
|
||||
qdf_dma_dir_t dir,
|
||||
const char *file,
|
||||
uint32_t line)
|
||||
{
|
||||
qdf_nbuf_untrack_map(buf, file, line);
|
||||
__qdf_nbuf_unmap_single(osdev, buf, dir);
|
||||
}
|
||||
|
||||
QDF_STATUS qdf_nbuf_map_nbytes_debug(qdf_device_t osdev,
|
||||
qdf_nbuf_t buf,
|
||||
qdf_dma_dir_t dir,
|
||||
int nbytes,
|
||||
const char *file,
|
||||
uint32_t line)
|
||||
{
|
||||
qdf_nbuf_track_map(buf, file, line);
|
||||
|
||||
return __qdf_nbuf_map_nbytes(osdev, buf, dir, nbytes);
|
||||
}
|
||||
|
||||
void qdf_nbuf_unmap_nbytes_debug(qdf_device_t osdev,
|
||||
qdf_nbuf_t buf,
|
||||
qdf_dma_dir_t dir,
|
||||
int nbytes,
|
||||
const char *file,
|
||||
uint32_t line)
|
||||
{
|
||||
qdf_nbuf_untrack_map(buf, file, line);
|
||||
__qdf_nbuf_unmap_nbytes(osdev, buf, dir, nbytes);
|
||||
}
|
||||
|
||||
QDF_STATUS qdf_nbuf_map_nbytes_single_debug(qdf_device_t osdev,
|
||||
qdf_nbuf_t buf,
|
||||
qdf_dma_dir_t dir,
|
||||
int nbytes,
|
||||
const char *file,
|
||||
uint32_t line)
|
||||
{
|
||||
qdf_nbuf_track_map(buf, file, line);
|
||||
|
||||
return __qdf_nbuf_map_nbytes_single(osdev, buf, dir, nbytes);
|
||||
}
|
||||
|
||||
void qdf_nbuf_unmap_nbytes_single_debug(qdf_device_t osdev,
|
||||
qdf_nbuf_t buf,
|
||||
qdf_dma_dir_t dir,
|
||||
int nbytes,
|
||||
const char *file,
|
||||
uint32_t line)
|
||||
{
|
||||
qdf_nbuf_untrack_map(buf, file, line);
|
||||
__qdf_nbuf_unmap_nbytes_single(osdev, buf, dir, nbytes);
|
||||
}
|
||||
#endif /* MEMORY_DEBUG */
|
||||
|
||||
/**
|
||||
* __qdf_nbuf_map() - map a buffer to local bus address space
|
||||
* @osdev: OS device
|
||||
|
|
@ -1647,6 +1862,7 @@ void qdf_net_buf_debug_init(void)
|
|||
{
|
||||
uint32_t i;
|
||||
|
||||
qdf_nbuf_map_tracking_init();
|
||||
qdf_nbuf_track_memory_manager_create();
|
||||
|
||||
for (i = 0; i < QDF_NET_BUF_TRACK_MAX_SIZE; i++) {
|
||||
|
|
@ -1689,6 +1905,7 @@ void qdf_net_buf_debug_exit(void)
|
|||
}
|
||||
|
||||
qdf_nbuf_track_memory_manager_destroy();
|
||||
qdf_nbuf_map_tracking_deinit();
|
||||
|
||||
#ifdef CONFIG_HALT_KMEMLEAK
|
||||
if (count) {
|
||||
|
|
|
|||
Loading…
Reference in a new issue