diff --git a/drivers/net/wireless/Kconfig b/drivers/net/wireless/Kconfig index 57d93e5e60ca..e99233bf9e53 100644 --- a/drivers/net/wireless/Kconfig +++ b/drivers/net/wireless/Kconfig @@ -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 diff --git a/drivers/net/wireless/Makefile b/drivers/net/wireless/Makefile index e755261a241d..ced606e8d2ab 100644 --- a/drivers/net/wireless/Makefile +++ b/drivers/net/wireless/Makefile @@ -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/ diff --git a/drivers/net/wireless/cnss_prealloc/Kconfig b/drivers/net/wireless/cnss_prealloc/Kconfig new file mode 100644 index 000000000000..8c24cd55844c --- /dev/null +++ b/drivers/net/wireless/cnss_prealloc/Kconfig @@ -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. diff --git a/drivers/net/wireless/cnss_prealloc/Makefile b/drivers/net/wireless/cnss_prealloc/Makefile new file mode 100644 index 000000000000..a8b06956441e --- /dev/null +++ b/drivers/net/wireless/cnss_prealloc/Makefile @@ -0,0 +1,3 @@ +# SPDX-License-Identifier: GPL-2.0-only + +obj-$(CONFIG_WCNSS_MEM_PRE_ALLOC) += cnss_prealloc.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..066cb0f4f18f --- /dev/null +++ b/drivers/net/wireless/cnss_prealloc/cnss_prealloc.c @@ -0,0 +1,293 @@ +// SPDX-License-Identifier: GPL-2.0-only +/* Copyright (c) 2012,2014-2017,2019 The Linux Foundation. All rights reserved. */ + +#include +#include +#include +#include +#include +#include +#include +#ifdef CONFIG_WCNSS_SKB_PRE_ALLOC +#include +#endif +#include + +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"); diff --git a/include/net/cnss_prealloc.h b/include/net/cnss_prealloc.h new file mode 100644 index 000000000000..74aafe27492d --- /dev/null +++ b/include/net/cnss_prealloc.h @@ -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_ */