net: cnss_prealloc: Add snapshot of cnss_prealloc driver

This is a snapshot of cnss_prealloc driver and associated files as of
msm-4.19 commit 94d53f998edd
("cnss_prealloc: Update prealloc memory table").

Change-Id: Ic6e853bf651e2a0e2b672bc68d42929c3b55ef93
CRs-Fixed: 2500800
Signed-off-by: Yuanyuan Liu <yuanliu@codeaurora.org>
This commit is contained in:
Yuanyuan Liu 2019-07-31 13:42:04 -07:00 • committed by Gerrit - the friendly Code Review server
commit 60450b0d00
6 changed files with 334 additions and 0 deletions

View file

@ -122,8 +122,22 @@ config VIRT_WIFI
This option adds support for ethernet connections to appear as if they
are wifi connections through a special rtnetlink device.
config CLD_LL_CORE
tristate "QTI core WLAN driver for QCA6174 chipset"
select NL80211_TESTMODE
select WEXT_CORE
select WEXT_PRIV
select WEXT_SPY
select WIRELESS_EXT
help
This section contains the necessary modules needed to enable the
core WLAN driver for QTI QCA6174 chipset.
Select Y to compile the driver in order to have WLAN functionality
support.
source "drivers/net/wireless/cnss2/Kconfig"
source "drivers/net/wireless/cnss_utils/Kconfig"
source "drivers/net/wireless/cnss_genl/Kconfig"
source "drivers/net/wireless/cnss_prealloc/Kconfig"
endif # WLAN

View file

@ -33,3 +33,4 @@ obj-$(CONFIG_VIRT_WIFI) += virt_wifi.o
obj-$(CONFIG_CNSS2) += cnss2/
obj-$(CONFIG_CNSS_UTILS) += cnss_utils/
obj-$(CONFIG_CNSS_GENL) += cnss_genl/
obj-$(CONFIG_WCNSS_MEM_PRE_ALLOC) += cnss_prealloc/

View file

@ -0,0 +1,9 @@
# SPDX-License-Identifier: GPL-2.0-only
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.

View file

@ -0,0 +1,3 @@
# SPDX-License-Identifier: GPL-2.0-only
obj-$(CONFIG_WCNSS_MEM_PRE_ALLOC) += cnss_prealloc.o

View file

@ -0,0 +1,293 @@
// SPDX-License-Identifier: GPL-2.0-only
/* Copyright (c) 2012,2014-2017,2019 The Linux Foundation. All rights reserved. */
#include <linux/module.h>
#include <linux/slab.h>
#include <linux/seq_file.h>
#include <linux/err.h>
#include <linux/stacktrace.h>
#include <linux/spinlock.h>
#include <linux/debugfs.h>
#ifdef CONFIG_WCNSS_SKB_PRE_ALLOC
#include <linux/skbuff.h>
#endif
#include <net/cnss_prealloc.h>
static DEFINE_SPINLOCK(alloc_lock);
#ifdef CONFIG_SLUB_DEBUG
#define WCNSS_MAX_STACK_TRACE 64
#endif
#define PRE_ALLOC_DEBUGFS_DIR "cnss-prealloc"
#define PRE_ALLOC_DEBUGFS_FILE_OBJ "status"
static struct dentry *debug_base;
struct wcnss_prealloc {
int occupied;
size_t 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, 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, 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, 16 * 1024, NULL},
{0, 16 * 1024, NULL},
{0, 16 * 1024, NULL},
{0, 16 * 1024, NULL},
{0, 16 * 1024, NULL},
{0, 16 * 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},
};
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)
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);
}
#else
static inline
void wcnss_prealloc_save_stack_trace(struct wcnss_prealloc *entry) {}
#endif
void *wcnss_prealloc_get(size_t 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);
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: %zu, addr: %pK, backtrace:\n",
wcnss_allocs[i].size, wcnss_allocs[i].ptr);
print_stack_trace(&wcnss_allocs[i].trace, 1);
}
}
#else
void wcnss_prealloc_check_memory_leak(void) {}
#endif
EXPORT_SYMBOL(wcnss_prealloc_check_memory_leak);
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;
}
EXPORT_SYMBOL(wcnss_pre_alloc_reset);
static int prealloc_memory_stats_show(struct seq_file *fp, void *data)
{
int i = 0;
int used_slots = 0, free_slots = 0;
unsigned int tsize = 0, tused = 0, size = 0;
seq_puts(fp, "\nSlot_Size(Kb)\t\t[Used : Free]\n");
for (i = 0; i < ARRAY_SIZE(wcnss_allocs); i++) {
tsize += wcnss_allocs[i].size;
if (size != wcnss_allocs[i].size) {
if (size) {
seq_printf(fp, "[%d : %d]\n",
used_slots, free_slots);
}
size = wcnss_allocs[i].size;
used_slots = 0;
free_slots = 0;
seq_printf(fp, "%d Kb\t\t\t", size / 1024);
}
if (wcnss_allocs[i].occupied) {
tused += wcnss_allocs[i].size;
++used_slots;
} else {
++free_slots;
}
}
seq_printf(fp, "[%d : %d]\n", used_slots, free_slots);
/* Convert byte to Kb */
if (tsize)
tsize = tsize / 1024;
if (tused)
tused = tused / 1024;
seq_printf(fp, "\nMemory Status:\nTotal Memory: %dKb\n", tsize);
seq_printf(fp, "Used: %dKb\nFree: %dKb\n", tused, tsize - tused);
return 0;
}
static int prealloc_memory_stats_open(struct inode *inode, struct file *file)
{
return single_open(file, prealloc_memory_stats_show, NULL);
}
static const struct file_operations prealloc_memory_stats_fops = {
.owner = THIS_MODULE,
.open = prealloc_memory_stats_open,
.read = seq_read,
.llseek = seq_lseek,
.release = single_release,
};
static int __init wcnss_pre_alloc_init(void)
{
int ret;
ret = wcnss_prealloc_init();
if (ret) {
pr_err("%s: Failed to init the prealloc pool\n", __func__);
return ret;
}
debug_base = debugfs_create_dir(PRE_ALLOC_DEBUGFS_DIR, NULL);
if (IS_ERR_OR_NULL(debug_base)) {
pr_err("%s: Failed to create debugfs dir\n", __func__);
} else if (IS_ERR_OR_NULL(debugfs_create_file
(PRE_ALLOC_DEBUGFS_FILE_OBJ,
0644, debug_base, NULL,
&prealloc_memory_stats_fops))) {
pr_err("%s: Failed to create debugfs file\n", __func__);
debugfs_remove_recursive(debug_base);
}
return ret;
}
static void __exit wcnss_pre_alloc_exit(void)
{
wcnss_prealloc_deinit();
debugfs_remove_recursive(debug_base);
}
module_init(wcnss_pre_alloc_init);
module_exit(wcnss_pre_alloc_exit);
MODULE_LICENSE("GPL v2");
MODULE_DESCRIPTION("WCNSS Prealloc Driver");

View file

@ -0,0 +1,14 @@
/* SPDX-License-Identifier: GPL-2.0-only */
/* Copyright (c) 2015-2016,2019 The Linux Foundation. All rights reserved. */
#ifndef _NET_CNSS_PREALLOC_H_
#define _NET_CNSS_PREALLOC_H_
#define WCNSS_PRE_ALLOC_GET_THRESHOLD (4*1024)
extern void *wcnss_prealloc_get(size_t 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_ */