qcacmn: Register for panic notification

If wlan logging is enabled then register a notifier block
to kernel's panic notifier chain. Notifier block contains a
callback function to print buffered wlan logs in the event
of kernel panic.

Change-Id: Id8cae651afe45e411e1ddf122c2bc9b5f6688327
CRs-Fixed: 2692163
This commit is contained in:
Jeevan Kukkalli
2020-05-14 19:32:47 +05:30
zatwierdzone przez nshrivas
rodzic 3e8172d58b
commit 8bcf1b75d2
3 zmienionych plików z 158 dodań i 1 usunięć

Wyświetl plik

@@ -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);

Wyświetl plik

@@ -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);