Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit 40cd6a4a authored by Linux Build Service Account's avatar Linux Build Service Account Committed by Gerrit - the friendly Code Review server
Browse files

Merge "cnss: Add prealloc memory leak detection logic"

parents 80db6b41 e2210a33
Loading
Loading
Loading
Loading
+3 −0
Original line number Diff line number Diff line
@@ -1691,6 +1691,9 @@ void cnss_wlan_unregister_driver(struct cnss_wlan_driver *driver)
	if (wdrv->remove)
		wdrv->remove(pdev);

	wcnss_prealloc_check_memory_leak();
	wcnss_pre_alloc_reset();

	if (penv->pcie_link_state && !penv->pcie_link_down_ind) {
		pci_save_state(pdev);
		penv->saved_state = pci_store_saved_state(pdev);
+70 −0
Original line number Diff line number Diff line
@@ -12,15 +12,24 @@
#include <linux/module.h>
#include <linux/slab.h>
#include <linux/err.h>
#include <linux/stacktrace.h>
#include <linux/wcnss_wlan.h>
#include <linux/spinlock.h>

static DEFINE_SPINLOCK(alloc_lock);

#ifdef CONFIG_SLUB_DEBUG
#define WCNSS_MAX_STACK_TRACE			64
#endif

struct wcnss_prealloc {
	int occupied;
	unsigned int size;
	void *ptr;
#ifdef CONFIG_SLUB_DEBUG
	unsigned long stack_trace[WCNSS_MAX_STACK_TRACE];
	struct stack_trace trace;
#endif
};

/* pre-alloced mem for WLAN driver */
@@ -116,6 +125,28 @@ void wcnss_prealloc_deinit(void)
	}
}

#ifdef CONFIG_SLUB_DEBUG
static void wcnss_prealloc_save_stack_trace(struct wcnss_prealloc *entry)
{
	struct stack_trace *trace = &entry->trace;

	memset(&entry->stack_trace, 0, sizeof(entry->stack_trace));
	trace->nr_entries = 0;
	trace->max_entries = WCNSS_MAX_STACK_TRACE;
	trace->entries = entry->stack_trace;
	trace->skip = 2;

	save_stack_trace(trace);

	return;
}
#else
static inline void wcnss_prealloc_save_stack_trace(struct wcnss_prealloc *entry)
{
	return;
}
#endif

void *wcnss_prealloc_get(unsigned int size)
{
	int i = 0;
@@ -130,10 +161,12 @@ void *wcnss_prealloc_get(unsigned int size)
			/* we found the slot */
			wcnss_allocs[i].occupied = 1;
			spin_unlock_irqrestore(&alloc_lock, flags);
			wcnss_prealloc_save_stack_trace(&wcnss_allocs[i]);
			return wcnss_allocs[i].ptr;
		}
	}
	spin_unlock_irqrestore(&alloc_lock, flags);

	pr_err("wcnss: %s: prealloc not available for size: %d\n",
			__func__, size);

@@ -160,6 +193,43 @@ int wcnss_prealloc_put(void *ptr)
}
EXPORT_SYMBOL(wcnss_prealloc_put);

#ifdef CONFIG_SLUB_DEBUG
void wcnss_prealloc_check_memory_leak(void)
{
	int i, j = 0;

	for (i = 0; i < ARRAY_SIZE(wcnss_allocs); i++) {
		if (!wcnss_allocs[i].occupied)
			continue;

		if (j == 0) {
			pr_err("wcnss_prealloc: Memory leak detected\n");
			j++;
		}

		pr_err("Size: %u, addr: %pK, backtrace:\n",
				wcnss_allocs[i].size, wcnss_allocs[i].ptr);
		print_stack_trace(&wcnss_allocs[i].trace, 1);
	}

}
#endif

int wcnss_pre_alloc_reset(void)
{
	int i, n = 0;

	for (i = 0; i < ARRAY_SIZE(wcnss_allocs); i++) {
		if (!wcnss_allocs[i].occupied)
			continue;

		wcnss_allocs[i].occupied = 0;
		n++;
	}

	return n;
}

static int __init wcnss_pre_alloc_init(void)
{
	return wcnss_prealloc_init();
+10 −0
Original line number Diff line number Diff line
@@ -152,7 +152,17 @@ extern void cnss_set_driver_status(enum cnss_driver_status driver_status);
#ifdef CONFIG_WCNSS_MEM_PRE_ALLOC
extern void *wcnss_prealloc_get(unsigned int size);
extern int wcnss_prealloc_put(void *ptr);
extern int wcnss_pre_alloc_reset(void);
#else
static inline int wcnss_pre_alloc_reset(void) { return 0; }
#endif

#if defined(CONFIG_WCNSS_MEM_PRE_ALLOC) && defined(CONFIG_SLUB_DEBUG)
void wcnss_prealloc_check_memory_leak(void);
#else
static inline void wcnss_prealloc_check_memory_leak(void) {}
#endif


extern int msm_pcie_enumerate(u32 rc_idx);
#endif /* _NET_CNSS_H_ */