diff --git a/drivers/net/wireless/cnss_prealloc/cnss_prealloc.c b/drivers/net/wireless/cnss_prealloc/cnss_prealloc.c index 913f756f9520..e93416ebd343 100644 --- a/drivers/net/wireless/cnss_prealloc/cnss_prealloc.c +++ b/drivers/net/wireless/cnss_prealloc/cnss_prealloc.c @@ -11,10 +11,15 @@ */ #include #include +#include #include #include #include #include +#include +#ifdef CONFIG_WCNSS_SKB_PRE_ALLOC +#include +#endif static DEFINE_SPINLOCK(alloc_lock); @@ -22,6 +27,11 @@ static DEFINE_SPINLOCK(alloc_lock); #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; unsigned int size; @@ -216,6 +226,8 @@ void wcnss_prealloc_check_memory_leak(void) } } +#else +void wcnss_prealloc_check_memory_leak(void) {} #endif int wcnss_pre_alloc_reset(void) @@ -233,14 +245,89 @@ int wcnss_pre_alloc_reset(void) return n; } +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; +} + +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) { - return wcnss_prealloc_init(); + 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); diff --git a/drivers/soc/qcom/icnss.c b/drivers/soc/qcom/icnss.c index 70874da9d176..020cccaa5f98 100644 --- a/drivers/soc/qcom/icnss.c +++ b/drivers/soc/qcom/icnss.c @@ -48,6 +48,11 @@ #include #include +#ifdef CONFIG_WCNSS_MEM_PRE_ALLOC +#include +#endif + + #include "wlan_firmware_service_v01.h" #ifdef CONFIG_ICNSS_DEBUG @@ -1959,6 +1964,8 @@ static int icnss_call_driver_probe(struct icnss_priv *priv) if (ret < 0) { icnss_pr_err("Driver probe failed: %d, state: 0x%lx\n", ret, priv->state); + wcnss_prealloc_check_memory_leak(); + wcnss_pre_alloc_reset(); goto out; } @@ -2088,6 +2095,8 @@ static int icnss_driver_event_register_driver(void *data) if (ret) { icnss_pr_err("Driver probe failed: %d, state: 0x%lx\n", ret, penv->state); + wcnss_prealloc_check_memory_leak(); + wcnss_pre_alloc_reset(); goto power_off; } @@ -2113,6 +2122,8 @@ static int icnss_driver_event_unregister_driver(void *data) penv->ops->remove(&penv->pdev->dev); clear_bit(ICNSS_DRIVER_PROBED, &penv->state); + wcnss_prealloc_check_memory_leak(); + wcnss_pre_alloc_reset(); penv->ops = NULL; @@ -2137,6 +2148,8 @@ static int icnss_call_driver_remove(struct icnss_priv *priv) penv->ops->remove(&priv->pdev->dev); clear_bit(ICNSS_DRIVER_PROBED, &priv->state); + wcnss_prealloc_check_memory_leak(); + wcnss_pre_alloc_reset(); icnss_hw_power_off(penv);