diff options
author | Yue Ma <yuem@codeaurora.org> | 2016-01-26 17:46:14 -0800 |
---|---|---|
committer | David Keitel <dkeitel@codeaurora.org> | 2016-03-22 11:16:40 -0700 |
commit | 0c00aa87c1b8f3e28e9839b4970d078d47f1a469 (patch) | |
tree | 25854ce848632bbf990df684ddecceb7c333db7b | |
parent | ffe5134f23b5ba5d7ee8d24fa4b6c95104677b5f (diff) |
net: cnss_prealloc: Add snapshot of cnss prealloc driver
This is a snapshot of the cnss prealloc driver and associated files
as of msm-3.18 commit:
e70ad0cd5efdd9dc91a77dcdac31d6132e1315c1 (Promotion of kernel.lnx.
3.18-151201.)
Signed-off-by: Yue Ma <yuem@codeaurora.org>
-rw-r--r-- | drivers/net/wireless/Kconfig | 8 | ||||
-rw-r--r-- | drivers/net/wireless/Makefile | 2 | ||||
-rw-r--r-- | drivers/net/wireless/cnss_prealloc/Makefile | 2 | ||||
-rw-r--r-- | drivers/net/wireless/cnss_prealloc/cnss_prealloc.c | 247 | ||||
-rw-r--r-- | include/net/cnss_prealloc.h | 23 |
5 files changed, 282 insertions, 0 deletions
diff --git a/drivers/net/wireless/Kconfig b/drivers/net/wireless/Kconfig index 80177e0cf08e..057b5fb07d8d 100644 --- a/drivers/net/wireless/Kconfig +++ b/drivers/net/wireless/Kconfig @@ -295,6 +295,14 @@ config WCNSS_REGISTER_DUMP_ON_BITE register access failures. So this feature is to enable/disable the register dump on WCNSS WDOG bite. +config WCNSS_MEM_PRE_ALLOC + tristate "WCNSS pre-alloc memory support" + ---help--- + Pre-allocate memory for the WLAN driver module. + This feature enable cld wlan driver to use pre allocated memory + for it's internal usage and release it to back to pre allocated pool. + This memory is allocated at the cold boot time. + source "drivers/net/wireless/ath/Kconfig" source "drivers/net/wireless/b43/Kconfig" source "drivers/net/wireless/b43legacy/Kconfig" diff --git a/drivers/net/wireless/Makefile b/drivers/net/wireless/Makefile index 2a97a0e9ee43..25d33bd9cde8 100644 --- a/drivers/net/wireless/Makefile +++ b/drivers/net/wireless/Makefile @@ -62,3 +62,5 @@ obj-$(CONFIG_CW1200) += cw1200/ obj-$(CONFIG_RSI_91X) += rsi/ obj-$(CONFIG_WCNSS_CORE) += wcnss/ + +obj-$(CONFIG_WCNSS_MEM_PRE_ALLOC) += cnss_prealloc/ diff --git a/drivers/net/wireless/cnss_prealloc/Makefile b/drivers/net/wireless/cnss_prealloc/Makefile new file mode 100644 index 000000000000..a93da4900470 --- /dev/null +++ b/drivers/net/wireless/cnss_prealloc/Makefile @@ -0,0 +1,2 @@ +cnssprealloccore-objs += cnss_prealloc.o ../wcnss/qcomwlan_secif.o +obj-$(CONFIG_WCNSS_MEM_PRE_ALLOC) += cnssprealloccore.o diff --git a/drivers/net/wireless/cnss_prealloc/cnss_prealloc.c b/drivers/net/wireless/cnss_prealloc/cnss_prealloc.c new file mode 100644 index 000000000000..8cf48edbe592 --- /dev/null +++ b/drivers/net/wireless/cnss_prealloc/cnss_prealloc.c @@ -0,0 +1,247 @@ +/* Copyright (c) 2012,2014-2015 The Linux Foundation. All rights reserved. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 and + * only version 2 as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + */ +#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 */ +static struct wcnss_prealloc wcnss_allocs[] = { + {0, 8 * 1024, NULL}, + {0, 8 * 1024, NULL}, + {0, 8 * 1024, NULL}, + {0, 8 * 1024, NULL}, + {0, 8 * 1024, NULL}, + {0, 8 * 1024, NULL}, + {0, 8 * 1024, NULL}, + {0, 8 * 1024, NULL}, + {0, 12 * 1024, NULL}, + {0, 12 * 1024, NULL}, + {0, 12 * 1024, NULL}, + {0, 12 * 1024, NULL}, + {0, 12 * 1024, NULL}, + {0, 12 * 1024, NULL}, + {0, 12 * 1024, NULL}, + {0, 12 * 1024, NULL}, + {0, 12 * 1024, NULL}, + {0, 12 * 1024, NULL}, + {0, 12 * 1024, NULL}, + {0, 12 * 1024, NULL}, + {0, 12 * 1024, NULL}, + {0, 12 * 1024, NULL}, + {0, 12 * 1024, NULL}, + {0, 12 * 1024, NULL}, + {0, 12 * 1024, NULL}, + {0, 12 * 1024, NULL}, + {0, 12 * 1024, NULL}, + {0, 12 * 1024, NULL}, + {0, 12 * 1024, NULL}, + {0, 12 * 1024, NULL}, + {0, 12 * 1024, NULL}, + {0, 12 * 1024, NULL}, + {0, 12 * 1024, NULL}, + {0, 12 * 1024, NULL}, + {0, 12 * 1024, NULL}, + {0, 12 * 1024, NULL}, + {0, 12 * 1024, NULL}, + {0, 12 * 1024, NULL}, + {0, 12 * 1024, NULL}, + {0, 12 * 1024, NULL}, + {0, 12 * 1024, NULL}, + {0, 12 * 1024, NULL}, + {0, 12 * 1024, NULL}, + {0, 12 * 1024, NULL}, + {0, 16 * 1024, NULL}, + {0, 16 * 1024, NULL}, + {0, 16 * 1024, NULL}, + {0, 16 * 1024, NULL}, + {0, 16 * 1024, NULL}, + {0, 16 * 1024, NULL}, + {0, 24 * 1024, NULL}, + {0, 24 * 1024, NULL}, + {0, 32 * 1024, NULL}, + {0, 32 * 1024, NULL}, + {0, 32 * 1024, NULL}, + {0, 32 * 1024, NULL}, + {0, 32 * 1024, NULL}, + {0, 32 * 1024, NULL}, + {0, 32 * 1024, NULL}, + {0, 32 * 1024, NULL}, + {0, 64 * 1024, NULL}, + {0, 64 * 1024, NULL}, + {0, 64 * 1024, NULL}, + {0, 64 * 1024, NULL}, + {0, 76 * 1024, NULL}, +}; + +int wcnss_prealloc_init(void) +{ + int i; + + for (i = 0; i < ARRAY_SIZE(wcnss_allocs); i++) { + wcnss_allocs[i].occupied = 0; + wcnss_allocs[i].ptr = kmalloc(wcnss_allocs[i].size, GFP_KERNEL); + if (wcnss_allocs[i].ptr == NULL) + return -ENOMEM; + } + + return 0; +} + +void wcnss_prealloc_deinit(void) +{ + int i = 0; + + for (i = 0; i < ARRAY_SIZE(wcnss_allocs); i++) { + kfree(wcnss_allocs[i].ptr); + wcnss_allocs[i].ptr = NULL; + } +} + +#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; + unsigned long flags; + + spin_lock_irqsave(&alloc_lock, flags); + for (i = 0; i < ARRAY_SIZE(wcnss_allocs); i++) { + if (wcnss_allocs[i].occupied) + continue; + + if (wcnss_allocs[i].size > 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); + + return NULL; +} +EXPORT_SYMBOL(wcnss_prealloc_get); + +int wcnss_prealloc_put(void *ptr) +{ + int i = 0; + unsigned long flags; + + spin_lock_irqsave(&alloc_lock, flags); + for (i = 0; i < ARRAY_SIZE(wcnss_allocs); i++) { + if (wcnss_allocs[i].ptr == ptr) { + wcnss_allocs[i].occupied = 0; + spin_unlock_irqrestore(&alloc_lock, flags); + return 1; + } + } + spin_unlock_irqrestore(&alloc_lock, flags); + + return 0; +} +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(); +} + +static void __exit wcnss_pre_alloc_exit(void) +{ + wcnss_prealloc_deinit(); +} + +module_init(wcnss_pre_alloc_init); +module_exit(wcnss_pre_alloc_exit); + +MODULE_LICENSE("GPL v2"); +MODULE_DESCRIPTION(DEVICE "WCNSS Prealloc Driver"); diff --git a/include/net/cnss_prealloc.h b/include/net/cnss_prealloc.h new file mode 100644 index 000000000000..734b4b69ecbb --- /dev/null +++ b/include/net/cnss_prealloc.h @@ -0,0 +1,23 @@ +/* Copyright (c) 2015, The Linux Foundation. All rights reserved. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 and + * only version 2 as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + */ + +#ifndef _NET_CNSS_PREALLOC_H_ +#define _NET_CNSS_PREALLOC_H_ + +#define WCNSS_PRE_ALLOC_GET_THRESHOLD (4*1024) + +extern void *wcnss_prealloc_get(unsigned int size); +extern int wcnss_prealloc_put(void *ptr); +extern int wcnss_pre_alloc_reset(void); +void wcnss_prealloc_check_memory_leak(void); + +#endif /* _NET_CNSS__PREALLOC_H_ */ |