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:

zatwierdzone przez
nshrivas

rodzic
3e8172d58b
commit
8bcf1b75d2
@@ -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);
|
||||
|
@@ -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);
|
||||
|
Reference in New Issue
Block a user