diff --git a/qdf/linux/src/qdf_trace.c b/qdf/linux/src/qdf_trace.c index 87e61c7e85..920f627230 100644 --- a/qdf/linux/src/qdf_trace.c +++ b/qdf/linux/src/qdf_trace.c @@ -3937,11 +3937,13 @@ void qdf_logging_init(void) { wlan_logging_sock_init_svc(); nl_srv_init(NULL, WLAN_NLINK_PROTO_FAMILY); + wlan_logging_notifier_init(qdf_log_dump_at_kernel_enable); wlan_logging_set_flush_timer(qdf_log_flush_timer_period); } void qdf_logging_exit(void) { + wlan_logging_notifier_deinit(qdf_log_dump_at_kernel_enable); nl_srv_exit(); wlan_logging_sock_deinit_svc(); } diff --git a/utils/logging/inc/wlan_logging_sock_svc.h b/utils/logging/inc/wlan_logging_sock_svc.h index 7db67a2666..5245e60298 100644 --- a/utils/logging/inc/wlan_logging_sock_svc.h +++ b/utils/logging/inc/wlan_logging_sock_svc.h @@ -1,5 +1,5 @@ /* - * Copyright (c) 2014-2019 The Linux Foundation. All rights reserved. + * Copyright (c) 2014-2020 The Linux Foundation. All rights reserved. * * Permission to use, copy, modify, and/or distribute this software for * any purpose with or without fee is hereby granted, provided that the @@ -46,6 +46,28 @@ int wlan_log_to_user(QDF_TRACE_LEVEL log_level, char *to_be_sent, int length); */ int wlan_logging_set_flush_timer(uint32_t milliseconds); +/** + * wlan_logging_notifier_init() - registers to panic notifier chain + * @dump_at_kernel_enable: qdf logging at kernel level enabled + * + * This function registers an handler to panic notifier chain if + * qdf logging at kernel level is disabled. + * + * Return: 0 on success + */ +int wlan_logging_notifier_init(bool dump_at_kernel_enable); + +/** + * wlan_logging_notifier_deinit() - unregisters to panic notifier chain + * @dump_at_kernel_enable: qdf logging at kernel level enabled + * + * This function unregisters an handler to panic notifier chain if + * qdf logging at kernel level is disabled. + * + * Return: 0 on success + */ +int wlan_logging_notifier_deinit(bool dump_at_kernel_enable); + #ifdef WLAN_LOGGING_SOCK_SVC_ENABLE void wlan_logging_set_per_pkt_stats(void); void wlan_logging_set_fw_flush_complete(void); diff --git a/utils/logging/src/wlan_logging_sock_svc.c b/utils/logging/src/wlan_logging_sock_svc.c index d6366fb883..168db1b34a 100644 --- a/utils/logging/src/wlan_logging_sock_svc.c +++ b/utils/logging/src/wlan_logging_sock_svc.c @@ -98,6 +98,15 @@ #define HOST_LOG_FW_FLUSH_COMPLETE 0x003 #define DIAG_TYPE_LOGS 1 #define PTT_MSG_DIAG_CMDS_TYPE 0x5050 +#define MAX_LOG_LINE 500 + +/* default rate limit period - 2sec */ +#define PANIC_WIFILOG_PRINT_RATE_LIMIT_PERIOD (2*HZ) +/* default burst for rate limit */ +#define PANIC_WIFILOG_PRINT_RATE_LIMIT_BURST_DEFAULT 250 +DEFINE_RATELIMIT_STATE(panic_wifilog_ratelimit, + PANIC_WIFILOG_PRINT_RATE_LIMIT_PERIOD, + PANIC_WIFILOG_PRINT_RATE_LIMIT_BURST_DEFAULT); struct log_msg { struct list_head node; @@ -151,6 +160,8 @@ struct wlan_logging { struct list_head free_list; /* Holds the filled nodes which needs to be indicated to APP */ struct list_head filled_list; + /* Holds nodes for console printing in case of kernel panic */ + struct list_head panic_list; /* Wait queue for Logger thread */ wait_queue_head_t wait_queue; /* Logger thread */ @@ -919,6 +930,127 @@ int wlan_logging_set_flush_timer(uint32_t milliseconds) return 0; } +static int panic_wifilog_ratelimit_print(void) +{ + return __ratelimit(&panic_wifilog_ratelimit); +} + +/** + * wlan_logging_dump_last_logs() - Panic notifier callback's helper function + * + * This function prints buffered logs in chunks of MAX_LOG_LINE. + */ +static void wlan_logging_dump_last_logs(void) +{ + char *log; + struct log_msg *plog_msg; + char textbuf[MAX_LOG_LINE]; + unsigned int filled_length; + unsigned int text_len; + unsigned long flags; + + /* Iterate over panic list */ + pr_err("\n"); + while (!list_empty(&gwlan_logging.panic_list)) { + plog_msg = (struct log_msg *) + (gwlan_logging.panic_list.next); + list_del_init(gwlan_logging.panic_list.next); + log = &plog_msg->logbuf[sizeof(tAniHdr)]; + filled_length = plog_msg->filled_length; + while (filled_length) { + text_len = scnprintf(textbuf, + sizeof(textbuf), + "%s", log); + if (panic_wifilog_ratelimit_print()) + pr_err("%s\n", textbuf); + log += text_len; + filled_length -= text_len; + } + spin_lock_irqsave(&gwlan_logging.spin_lock, flags); + list_add_tail(&plog_msg->node, + &gwlan_logging.free_list); + spin_unlock_irqrestore(&gwlan_logging.spin_lock, flags); + } +} + +/** + * wlan_logging_panic_handler() - Panic notifier callback + * + * This function extracts log buffers in filled list and + * current node.Sends them to helper function for printing. + */ +static int wlan_logging_panic_handler(struct notifier_block *this, + unsigned long event, void *ptr) +{ + char *log; + struct log_msg *plog_msg; + unsigned long flags; + + spin_lock_irqsave(&gwlan_logging.spin_lock, flags); + /* Iterate over nodes queued for app */ + while (!list_empty(&gwlan_logging.filled_list)) { + plog_msg = (struct log_msg *) + (gwlan_logging.filled_list.next); + list_del_init(gwlan_logging.filled_list.next); + list_add_tail(&plog_msg->node, + &gwlan_logging.panic_list); + } + /* Check current node */ + if (gwlan_logging.pcur_node && + gwlan_logging.pcur_node->filled_length) { + plog_msg = gwlan_logging.pcur_node; + log = &plog_msg->logbuf[sizeof(tAniHdr)]; + log[plog_msg->filled_length] = '\0'; + list_add_tail(&gwlan_logging.pcur_node->node, + &gwlan_logging.panic_list); + if (!list_empty(&gwlan_logging.free_list)) { + gwlan_logging.pcur_node = + (struct log_msg *)(gwlan_logging.free_list.next); + list_del_init(gwlan_logging.free_list.next); + gwlan_logging.pcur_node->filled_length = 0; + } else + gwlan_logging.pcur_node = NULL; + } + spin_unlock_irqrestore(&gwlan_logging.spin_lock, flags); + + wlan_logging_dump_last_logs(); + + return NOTIFY_DONE; +} + +static struct notifier_block panic_nb = { + .notifier_call = wlan_logging_panic_handler, +}; + +int wlan_logging_notifier_init(bool dump_at_kernel_enable) +{ + int ret; + + if (gwlan_logging.is_active && + !dump_at_kernel_enable) { + ret = atomic_notifier_chain_register(&panic_notifier_list, + &panic_nb); + if (ret) { + QDF_TRACE_ERROR(QDF_MODULE_ID_QDF, + "Failed to register panic notifier"); + return -EINVAL; + } + } + + return 0; +} + +int wlan_logging_notifier_deinit(bool dump_at_kernel_enable) +{ + if (gwlan_logging.is_active && + !dump_at_kernel_enable) { + atomic_notifier_chain_unregister(&panic_notifier_list, + &panic_nb); + } + + return 0; +} + static void flush_timer_init(void) { qdf_spinlock_create(&gwlan_logging.flush_timer_lock); @@ -951,6 +1083,7 @@ int wlan_logging_sock_init_svc(void) spin_lock_irqsave(&gwlan_logging.spin_lock, irq_flag); INIT_LIST_HEAD(&gwlan_logging.free_list); INIT_LIST_HEAD(&gwlan_logging.filled_list); + INIT_LIST_HEAD(&gwlan_logging.panic_list); for (i = 0; i < gwlan_logging.num_buf; i++) { list_add(&gplog_msg[i].node, &gwlan_logging.free_list);