Merge "drm/msm/sde: take irq callback lock before reading cb list"

This commit is contained in:
Linux Build Service Account 2018-03-19 03:35:36 -07:00 committed by Gerrit - the friendly Code Review server
commit 0cf11fbe38
6 changed files with 277 additions and 34 deletions

View file

@ -31,23 +31,35 @@ static void sde_core_irq_callback_handler(void *arg, int irq_idx)
struct sde_irq *irq_obj = &sde_kms->irq_obj; struct sde_irq *irq_obj = &sde_kms->irq_obj;
struct sde_irq_callback *cb; struct sde_irq_callback *cb;
unsigned long irq_flags; unsigned long irq_flags;
bool cb_tbl_error = false;
int enable_counts = 0;
pr_debug("irq_idx=%d\n", irq_idx); pr_debug("irq_idx=%d\n", irq_idx);
if (list_empty(&irq_obj->irq_cb_tbl[irq_idx])) spin_lock_irqsave(&sde_kms->irq_obj.cb_lock, irq_flags);
SDE_ERROR("irq_idx=%d has no registered callback\n", irq_idx); if (list_empty(&irq_obj->irq_cb_tbl[irq_idx])) {
/* print error outside lock */
cb_tbl_error = true;
enable_counts = atomic_read(
&sde_kms->irq_obj.enable_counts[irq_idx]);
}
atomic_inc(&irq_obj->irq_counts[irq_idx]); atomic_inc(&irq_obj->irq_counts[irq_idx]);
/* /*
* Perform registered function callback * Perform registered function callback
*/ */
spin_lock_irqsave(&sde_kms->irq_obj.cb_lock, irq_flags);
list_for_each_entry(cb, &irq_obj->irq_cb_tbl[irq_idx], list) list_for_each_entry(cb, &irq_obj->irq_cb_tbl[irq_idx], list)
if (cb->func) if (cb->func)
cb->func(cb->arg, irq_idx); cb->func(cb->arg, irq_idx);
spin_unlock_irqrestore(&sde_kms->irq_obj.cb_lock, irq_flags); spin_unlock_irqrestore(&sde_kms->irq_obj.cb_lock, irq_flags);
if (cb_tbl_error) {
SDE_ERROR("irq has no registered callback, idx %d enables %d\n",
irq_idx, enable_counts);
SDE_EVT32_IRQ(irq_idx, enable_counts, SDE_EVTLOG_ERROR);
}
/* /*
* Clear pending interrupt status in HW. * Clear pending interrupt status in HW.
* NOTE: sde_core_irq_callback_handler is protected by top-level * NOTE: sde_core_irq_callback_handler is protected by top-level

View file

@ -1,5 +1,5 @@
/* /*
* Copyright (c) 2014-2017, The Linux Foundation. All rights reserved. * Copyright (c) 2014-2018, The Linux Foundation. All rights reserved.
* Copyright (C) 2013 Red Hat * Copyright (C) 2013 Red Hat
* Author: Rob Clark <robdclark@gmail.com> * Author: Rob Clark <robdclark@gmail.com>
* *
@ -602,6 +602,12 @@ static void sde_encoder_underrun_callback(struct drm_encoder *drm_enc,
SDE_ATRACE_BEGIN("encoder_underrun_callback"); SDE_ATRACE_BEGIN("encoder_underrun_callback");
atomic_inc(&phy_enc->underrun_cnt); atomic_inc(&phy_enc->underrun_cnt);
SDE_EVT32(DRMID(drm_enc), atomic_read(&phy_enc->underrun_cnt)); SDE_EVT32(DRMID(drm_enc), atomic_read(&phy_enc->underrun_cnt));
trace_sde_encoder_underrun(DRMID(drm_enc),
atomic_read(&phy_enc->underrun_cnt));
SDE_DBG_CTRL("stop_ftrace");
SDE_DBG_CTRL("panic_underrun");
SDE_ATRACE_END("encoder_underrun_callback"); SDE_ATRACE_END("encoder_underrun_callback");
} }

View file

@ -1,4 +1,4 @@
/* Copyright (c) 2014-2016, The Linux Foundation. All rights reserved. /* Copyright (c) 2014-2018, The Linux Foundation. All rights reserved.
* *
* This program is free software; you can redistribute it and/or modify * This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2 and * it under the terms of the GNU General Public License version 2 and
@ -125,6 +125,22 @@ TRACE_EVENT(sde_cmd_release_bw,
TP_printk("crtc:%d", __entry->crtc_id) TP_printk("crtc:%d", __entry->crtc_id)
); );
TRACE_EVENT(sde_encoder_underrun,
TP_PROTO(u32 enc_id, u32 underrun_cnt),
TP_ARGS(enc_id, underrun_cnt),
TP_STRUCT__entry(
__field(u32, enc_id)
__field(u32, underrun_cnt)
),
TP_fast_assign(
__entry->enc_id = enc_id;
__entry->underrun_cnt = underrun_cnt;
),
TP_printk("enc:%d underrun_cnt:%d", __entry->enc_id,
__entry->underrun_cnt)
);
TRACE_EVENT(sde_mark_write, TRACE_EVENT(sde_mark_write,
TP_PROTO(int pid, const char *name, bool trace_begin), TP_PROTO(int pid, const char *name, bool trace_begin),
TP_ARGS(pid, name, trace_begin), TP_ARGS(pid, name, trace_begin),

View file

@ -1,4 +1,4 @@
/* Copyright (c) 2009-2017, The Linux Foundation. All rights reserved. /* Copyright (c) 2009-2018, The Linux Foundation. All rights reserved.
* *
* This program is free software; you can redistribute it and/or modify * This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2 and * it under the terms of the GNU General Public License version 2 and
@ -57,6 +57,9 @@
/* print debug ranges in groups of 4 u32s */ /* print debug ranges in groups of 4 u32s */
#define REG_DUMP_ALIGN 16 #define REG_DUMP_ALIGN 16
#define DBG_CTRL_STOP_FTRACE BIT(0)
#define DBG_CTRL_PANIC_UNDERRUN BIT(1)
#define DBG_CTRL_MAX BIT(2)
/** /**
* struct sde_dbg_reg_offset - tracking for start and end of region * struct sde_dbg_reg_offset - tracking for start and end of region
@ -162,6 +165,7 @@ struct sde_dbg_vbif_debug_bus {
* @enable_reg_dump: whether to dump registers into memory, kernel log, or both * @enable_reg_dump: whether to dump registers into memory, kernel log, or both
* @dbgbus_sde: debug bus structure for the sde * @dbgbus_sde: debug bus structure for the sde
* @dbgbus_vbif_rt: debug bus structure for the realtime vbif * @dbgbus_vbif_rt: debug bus structure for the realtime vbif
* @dump_all: dump all entries in register dump
*/ */
static struct sde_dbg_base { static struct sde_dbg_base {
struct sde_dbg_evtlog *evtlog; struct sde_dbg_evtlog *evtlog;
@ -180,6 +184,8 @@ static struct sde_dbg_base {
struct sde_dbg_sde_debug_bus dbgbus_sde; struct sde_dbg_sde_debug_bus dbgbus_sde;
struct sde_dbg_vbif_debug_bus dbgbus_vbif_rt; struct sde_dbg_vbif_debug_bus dbgbus_vbif_rt;
bool dump_all;
u32 debugfs_ctrl;
} sde_dbg_base; } sde_dbg_base;
/* sde_dbg_base_evtlog - global pointer to main sde event log for macro use */ /* sde_dbg_base_evtlog - global pointer to main sde event log for macro use */
@ -1448,7 +1454,7 @@ static void _sde_dbg_dump_vbif_dbg_bus(struct sde_dbg_vbif_debug_bus *bus)
*/ */
static void _sde_dump_array(struct sde_dbg_reg_base *blk_arr[], static void _sde_dump_array(struct sde_dbg_reg_base *blk_arr[],
u32 len, bool do_panic, const char *name, bool dump_dbgbus_sde, u32 len, bool do_panic, const char *name, bool dump_dbgbus_sde,
bool dump_dbgbus_vbif_rt) bool dump_dbgbus_vbif_rt, bool dump_all)
{ {
int i; int i;
@ -1460,6 +1466,7 @@ static void _sde_dump_array(struct sde_dbg_reg_base *blk_arr[],
sde_dbg_base.enable_reg_dump); sde_dbg_base.enable_reg_dump);
} }
if (dump_all)
sde_evtlog_dump_all(sde_dbg_base.evtlog); sde_evtlog_dump_all(sde_dbg_base.evtlog);
if (dump_dbgbus_sde) if (dump_dbgbus_sde)
@ -1484,7 +1491,8 @@ static void _sde_dump_work(struct work_struct *work)
ARRAY_SIZE(sde_dbg_base.req_dump_blks), ARRAY_SIZE(sde_dbg_base.req_dump_blks),
sde_dbg_base.work_panic, "evtlog_workitem", sde_dbg_base.work_panic, "evtlog_workitem",
sde_dbg_base.dbgbus_sde.cmn.include_in_deferred_work, sde_dbg_base.dbgbus_sde.cmn.include_in_deferred_work,
sde_dbg_base.dbgbus_vbif_rt.cmn.include_in_deferred_work); sde_dbg_base.dbgbus_vbif_rt.cmn.include_in_deferred_work,
sde_dbg_base.dump_all);
} }
void sde_dbg_dump(bool queue_work, const char *name, ...) void sde_dbg_dump(bool queue_work, const char *name, ...)
@ -1493,6 +1501,7 @@ void sde_dbg_dump(bool queue_work, const char *name, ...)
bool do_panic = false; bool do_panic = false;
bool dump_dbgbus_sde = false; bool dump_dbgbus_sde = false;
bool dump_dbgbus_vbif_rt = false; bool dump_dbgbus_vbif_rt = false;
bool dump_all = false;
va_list args; va_list args;
char *blk_name = NULL; char *blk_name = NULL;
struct sde_dbg_reg_base *blk_base = NULL; struct sde_dbg_reg_base *blk_base = NULL;
@ -1510,6 +1519,7 @@ void sde_dbg_dump(bool queue_work, const char *name, ...)
memset(sde_dbg_base.req_dump_blks, 0, memset(sde_dbg_base.req_dump_blks, 0,
sizeof(sde_dbg_base.req_dump_blks)); sizeof(sde_dbg_base.req_dump_blks));
sde_dbg_base.dump_all = false;
va_start(args, name); va_start(args, name);
i = 0; i = 0;
@ -1531,6 +1541,8 @@ void sde_dbg_dump(bool queue_work, const char *name, ...)
blk_name); blk_name);
} }
} }
if (!strcmp(blk_name, "all"))
dump_all = true;
if (!strcmp(blk_name, "dbg_bus")) if (!strcmp(blk_name, "dbg_bus"))
dump_dbgbus_sde = true; dump_dbgbus_sde = true;
@ -1550,13 +1562,53 @@ void sde_dbg_dump(bool queue_work, const char *name, ...)
dump_dbgbus_sde; dump_dbgbus_sde;
sde_dbg_base.dbgbus_vbif_rt.cmn.include_in_deferred_work = sde_dbg_base.dbgbus_vbif_rt.cmn.include_in_deferred_work =
dump_dbgbus_vbif_rt; dump_dbgbus_vbif_rt;
sde_dbg_base.dump_all = dump_all;
schedule_work(&sde_dbg_base.dump_work); schedule_work(&sde_dbg_base.dump_work);
} else { } else {
_sde_dump_array(blk_arr, blk_len, do_panic, name, _sde_dump_array(blk_arr, blk_len, do_panic, name,
dump_dbgbus_sde, dump_dbgbus_vbif_rt); dump_dbgbus_sde, dump_dbgbus_vbif_rt, dump_all);
} }
} }
void sde_dbg_ctrl(const char *name, ...)
{
int i = 0;
va_list args;
char *blk_name = NULL;
/* no debugfs controlled events are enabled, just return */
if (!sde_dbg_base.debugfs_ctrl)
return;
va_start(args, name);
while ((blk_name = va_arg(args, char*))) {
if (i++ >= SDE_EVTLOG_MAX_DATA) {
pr_err("could not parse all dbg arguments\n");
break;
}
if (IS_ERR_OR_NULL(blk_name))
break;
if (!strcmp(blk_name, "stop_ftrace") &&
sde_dbg_base.debugfs_ctrl &
DBG_CTRL_STOP_FTRACE) {
pr_debug("tracing off\n");
tracing_off();
}
if (!strcmp(blk_name, "panic_underrun") &&
sde_dbg_base.debugfs_ctrl &
DBG_CTRL_PANIC_UNDERRUN) {
pr_debug("panic underrun\n");
panic("underrun");
}
}
}
/* /*
* sde_dbg_debugfs_open - debugfs open handler for evtlog dump * sde_dbg_debugfs_open - debugfs open handler for evtlog dump
* @inode: debugfs inode * @inode: debugfs inode
@ -1564,6 +1616,9 @@ void sde_dbg_dump(bool queue_work, const char *name, ...)
*/ */
static int sde_dbg_debugfs_open(struct inode *inode, struct file *file) static int sde_dbg_debugfs_open(struct inode *inode, struct file *file)
{ {
if (!inode || !file)
return -EINVAL;
/* non-seekable */ /* non-seekable */
file->f_mode &= ~(FMODE_LSEEK | FMODE_PREAD | FMODE_PWRITE); file->f_mode &= ~(FMODE_LSEEK | FMODE_PREAD | FMODE_PWRITE);
file->private_data = inode->i_private; file->private_data = inode->i_private;
@ -1583,8 +1638,11 @@ static ssize_t sde_evtlog_dump_read(struct file *file, char __user *buff,
ssize_t len = 0; ssize_t len = 0;
char evtlog_buf[SDE_EVTLOG_BUF_MAX]; char evtlog_buf[SDE_EVTLOG_BUF_MAX];
if (!buff || !ppos)
return -EINVAL;
len = sde_evtlog_dump_to_buffer(sde_dbg_base.evtlog, evtlog_buf, len = sde_evtlog_dump_to_buffer(sde_dbg_base.evtlog, evtlog_buf,
SDE_EVTLOG_BUF_MAX); SDE_EVTLOG_BUF_MAX, true);
if (copy_to_user(buff, evtlog_buf, len)) if (copy_to_user(buff, evtlog_buf, len))
return -EFAULT; return -EFAULT;
*ppos += len; *ppos += len;
@ -1621,6 +1679,82 @@ static const struct file_operations sde_evtlog_fops = {
.write = sde_evtlog_dump_write, .write = sde_evtlog_dump_write,
}; };
/**
* sde_dbg_ctrl_read - debugfs read handler for debug ctrl read
* @file: file handler
* @buff: user buffer content for debugfs
* @count: size of user buffer
* @ppos: position offset of user buffer
*/
static ssize_t sde_dbg_ctrl_read(struct file *file, char __user *buff,
size_t count, loff_t *ppos)
{
ssize_t len = 0;
char buf[24] = {'\0'};
if (!buff || !ppos)
return -EINVAL;
if (*ppos)
return 0; /* the end */
len = snprintf(buf, sizeof(buf), "0x%x\n", sde_dbg_base.debugfs_ctrl);
pr_debug("%s: ctrl:0x%x len:0x%zx\n",
__func__, sde_dbg_base.debugfs_ctrl, len);
if ((count < sizeof(buf)) || copy_to_user(buff, buf, len)) {
pr_err("error copying the buffer! count:0x%zx\n", count);
return -EFAULT;
}
*ppos += len; /* increase offset */
return len;
}
/**
* sde_dbg_ctrl_write - debugfs read handler for debug ctrl write
* @file: file handler
* @user_buf: user buffer content from debugfs
* @count: size of user buffer
* @ppos: position offset of user buffer
*/
static ssize_t sde_dbg_ctrl_write(struct file *file,
const char __user *user_buf, size_t count, loff_t *ppos)
{
u32 dbg_ctrl = 0;
char buf[24];
if (!file) {
pr_err("DbgDbg: %s: error no file --\n", __func__);
return -EINVAL;
}
if (count >= sizeof(buf))
return -EFAULT;
if (copy_from_user(buf, user_buf, count))
return -EFAULT;
buf[count] = 0; /* end of string */
if (kstrtouint(buf, 0, &dbg_ctrl)) {
pr_err("%s: error in the number of bytes\n", __func__);
return -EFAULT;
}
pr_debug("dbg_ctrl_read:0x%x\n", dbg_ctrl);
sde_dbg_base.debugfs_ctrl = dbg_ctrl;
return count;
}
static const struct file_operations sde_dbg_ctrl_fops = {
.open = sde_dbg_debugfs_open,
.read = sde_dbg_ctrl_read,
.write = sde_dbg_ctrl_write,
};
void sde_dbg_init_dbg_buses(u32 hwversion) void sde_dbg_init_dbg_buses(u32 hwversion)
{ {
static struct sde_dbg_base *dbg = &sde_dbg_base; static struct sde_dbg_base *dbg = &sde_dbg_base;
@ -1695,6 +1829,8 @@ int sde_dbg_init(struct dentry *debugfs_root, struct device *dev,
for (i = 0; i < SDE_EVTLOG_ENTRY; i++) for (i = 0; i < SDE_EVTLOG_ENTRY; i++)
sde_dbg_base.evtlog->logs[i].counter = i; sde_dbg_base.evtlog->logs[i].counter = i;
debugfs_create_file("dbg_ctrl", 0600, sde_dbg_base.root, NULL,
&sde_dbg_ctrl_fops);
debugfs_create_file("dump", 0600, sde_dbg_base.root, NULL, debugfs_create_file("dump", 0600, sde_dbg_base.root, NULL,
&sde_evtlog_fops); &sde_evtlog_fops);
debugfs_create_u32("enable", 0600, sde_dbg_base.root, debugfs_create_u32("enable", 0600, sde_dbg_base.root,
@ -1736,7 +1872,14 @@ void sde_dbg_destroy(void)
*/ */
static int sde_dbg_reg_base_release(struct inode *inode, struct file *file) static int sde_dbg_reg_base_release(struct inode *inode, struct file *file)
{ {
struct sde_dbg_reg_base *dbg = file->private_data; struct sde_dbg_reg_base *dbg;
if (!file)
return -EINVAL;
dbg = file->private_data;
if (!dbg)
return -ENODEV;
mutex_lock(&sde_dbg_base.mutex); mutex_lock(&sde_dbg_base.mutex);
if (dbg && dbg->buf) { if (dbg && dbg->buf) {
@ -1760,12 +1903,16 @@ static int sde_dbg_reg_base_release(struct inode *inode, struct file *file)
static ssize_t sde_dbg_reg_base_offset_write(struct file *file, static ssize_t sde_dbg_reg_base_offset_write(struct file *file,
const char __user *user_buf, size_t count, loff_t *ppos) const char __user *user_buf, size_t count, loff_t *ppos)
{ {
struct sde_dbg_reg_base *dbg = file->private_data; struct sde_dbg_reg_base *dbg;
u32 off = 0; u32 off = 0;
u32 cnt = DEFAULT_BASE_REG_CNT; u32 cnt = DEFAULT_BASE_REG_CNT;
char buf[24]; char buf[24];
ssize_t rc = count; ssize_t rc = count;
if (!file)
return -EINVAL;
dbg = file->private_data;
if (!dbg) if (!dbg)
return -ENODEV; return -ENODEV;
@ -1799,6 +1946,9 @@ static ssize_t sde_dbg_reg_base_offset_write(struct file *file,
goto exit; goto exit;
} }
if (cnt == 0)
return -EINVAL;
dbg->off = off; dbg->off = off;
dbg->cnt = cnt; dbg->cnt = cnt;
@ -1819,17 +1969,29 @@ exit:
static ssize_t sde_dbg_reg_base_offset_read(struct file *file, static ssize_t sde_dbg_reg_base_offset_read(struct file *file,
char __user *buff, size_t count, loff_t *ppos) char __user *buff, size_t count, loff_t *ppos)
{ {
struct sde_dbg_reg_base *dbg = file->private_data; struct sde_dbg_reg_base *dbg;
int len = 0; int len = 0;
char buf[24] = {'\0'}; char buf[24] = {'\0'};
if (!file)
return -EINVAL;
dbg = file->private_data;
if (!dbg) if (!dbg)
return -ENODEV; return -ENODEV;
if (!ppos)
return -EINVAL;
if (*ppos) if (*ppos)
return 0; /* the end */ return 0; /* the end */
mutex_lock(&sde_dbg_base.mutex); mutex_lock(&sde_dbg_base.mutex);
if (dbg->off % sizeof(u32)) {
mutex_unlock(&sde_dbg_base.mutex);
return -EFAULT;
}
len = snprintf(buf, sizeof(buf), "0x%08zx %zx\n", dbg->off, dbg->cnt); len = snprintf(buf, sizeof(buf), "0x%08zx %zx\n", dbg->off, dbg->cnt);
if (len < 0 || len >= sizeof(buf)) { if (len < 0 || len >= sizeof(buf)) {
mutex_unlock(&sde_dbg_base.mutex); mutex_unlock(&sde_dbg_base.mutex);
@ -1857,11 +2019,15 @@ static ssize_t sde_dbg_reg_base_offset_read(struct file *file,
static ssize_t sde_dbg_reg_base_reg_write(struct file *file, static ssize_t sde_dbg_reg_base_reg_write(struct file *file,
const char __user *user_buf, size_t count, loff_t *ppos) const char __user *user_buf, size_t count, loff_t *ppos)
{ {
struct sde_dbg_reg_base *dbg = file->private_data; struct sde_dbg_reg_base *dbg;
size_t off; size_t off;
u32 data, cnt; u32 data, cnt;
char buf[24]; char buf[24];
if (!file)
return -EINVAL;
dbg = file->private_data;
if (!dbg) if (!dbg)
return -ENODEV; return -ENODEV;
@ -1907,14 +2073,21 @@ static ssize_t sde_dbg_reg_base_reg_write(struct file *file,
static ssize_t sde_dbg_reg_base_reg_read(struct file *file, static ssize_t sde_dbg_reg_base_reg_read(struct file *file,
char __user *user_buf, size_t count, loff_t *ppos) char __user *user_buf, size_t count, loff_t *ppos)
{ {
struct sde_dbg_reg_base *dbg = file->private_data; struct sde_dbg_reg_base *dbg;
size_t len; size_t len;
if (!file)
return -EINVAL;
dbg = file->private_data;
if (!dbg) { if (!dbg) {
pr_err("invalid handle\n"); pr_err("invalid handle\n");
return -ENODEV; return -ENODEV;
} }
if (!ppos)
return -EINVAL;
mutex_lock(&sde_dbg_base.mutex); mutex_lock(&sde_dbg_base.mutex);
if (!dbg->buf) { if (!dbg->buf) {
char *hwbuf; char *hwbuf;

View file

@ -1,4 +1,4 @@
/* Copyright (c) 2016-2017, The Linux Foundation. All rights reserved. /* Copyright (c) 2016-2018, The Linux Foundation. All rights reserved.
* *
* This program is free software; you can redistribute it and/or modify * This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2 and * it under the terms of the GNU General Public License version 2 and
@ -17,9 +17,10 @@
#include <linux/debugfs.h> #include <linux/debugfs.h>
#include <linux/list.h> #include <linux/list.h>
#define SDE_EVTLOG_DATA_LIMITER (-1) #define SDE_EVTLOG_DATA_LIMITER (0xC0DEBEEF)
#define SDE_EVTLOG_FUNC_ENTRY 0x1111 #define SDE_EVTLOG_FUNC_ENTRY 0x1111
#define SDE_EVTLOG_FUNC_EXIT 0x2222 #define SDE_EVTLOG_FUNC_EXIT 0x2222
#define SDE_EVTLOG_ERROR 0xebad
#define SDE_DBG_DUMP_DATA_LIMITER (NULL) #define SDE_DBG_DUMP_DATA_LIMITER (NULL)
@ -52,7 +53,7 @@ enum sde_dbg_dump_flag {
* number must be greater than print entry to prevent out of bound evtlog * number must be greater than print entry to prevent out of bound evtlog
* entry array access. * entry array access.
*/ */
#define SDE_EVTLOG_ENTRY (SDE_EVTLOG_PRINT_ENTRY * 4) #define SDE_EVTLOG_ENTRY (SDE_EVTLOG_PRINT_ENTRY * 8)
#define SDE_EVTLOG_MAX_DATA 15 #define SDE_EVTLOG_MAX_DATA 15
#define SDE_EVTLOG_BUF_MAX 512 #define SDE_EVTLOG_BUF_MAX 512
#define SDE_EVTLOG_BUF_ALIGN 32 #define SDE_EVTLOG_BUF_ALIGN 32
@ -77,6 +78,7 @@ struct sde_dbg_evtlog {
struct sde_dbg_evtlog_log logs[SDE_EVTLOG_ENTRY]; struct sde_dbg_evtlog_log logs[SDE_EVTLOG_ENTRY];
u32 first; u32 first;
u32 last; u32 last;
u32 last_dump;
u32 curr; u32 curr;
u32 next; u32 next;
u32 enable; u32 enable;
@ -123,6 +125,13 @@ extern struct sde_dbg_evtlog *sde_dbg_base_evtlog;
#define SDE_DBG_DUMP_WQ(...) sde_dbg_dump(true, __func__, ##__VA_ARGS__, \ #define SDE_DBG_DUMP_WQ(...) sde_dbg_dump(true, __func__, ##__VA_ARGS__, \
SDE_DBG_DUMP_DATA_LIMITER) SDE_DBG_DUMP_DATA_LIMITER)
/**
* SDE_DBG_EVT_CTRL - trigger a different driver events
* event: event that trigger different behavior in the driver
*/
#define SDE_DBG_CTRL(...) sde_dbg_ctrl(__func__, ##__VA_ARGS__, \
SDE_DBG_DUMP_DATA_LIMITER)
#if defined(CONFIG_DEBUG_FS) #if defined(CONFIG_DEBUG_FS)
/** /**
@ -172,10 +181,12 @@ bool sde_evtlog_is_enabled(struct sde_dbg_evtlog *evtlog, u32 flag);
* @evtlog: pointer to evtlog * @evtlog: pointer to evtlog
* @evtlog_buf: target buffer to print into * @evtlog_buf: target buffer to print into
* @evtlog_buf_size: size of target buffer * @evtlog_buf_size: size of target buffer
* @update_last_entry:» whether or not to stop at most recent entry
* Returns: number of bytes written to buffer * Returns: number of bytes written to buffer
*/ */
ssize_t sde_evtlog_dump_to_buffer(struct sde_dbg_evtlog *evtlog, ssize_t sde_evtlog_dump_to_buffer(struct sde_dbg_evtlog *evtlog,
char *evtlog_buf, ssize_t evtlog_buf_size); char *evtlog_buf, ssize_t evtlog_buf_size,
bool update_last_entry);
/** /**
* sde_dbg_init_dbg_buses - initialize debug bus dumping support for the chipset * sde_dbg_init_dbg_buses - initialize debug bus dumping support for the chipset
@ -213,6 +224,15 @@ void sde_dbg_destroy(void);
*/ */
void sde_dbg_dump(bool queue_work, const char *name, ...); void sde_dbg_dump(bool queue_work, const char *name, ...);
/**
* sde_dbg_ctrl - trigger specific actions for the driver with debugging
* purposes. Those actions need to be enabled by the debugfs entry
* so the driver executes those actions in the corresponding calls.
* @va_args: list of actions to trigger
* Returns: none
*/
void sde_dbg_ctrl(const char *name, ...);
/** /**
* sde_dbg_reg_register_base - register a hw register address section for later * sde_dbg_reg_register_base - register a hw register address section for later
* dumping. call this before calling sde_dbg_reg_register_dump_range * dumping. call this before calling sde_dbg_reg_register_dump_range
@ -272,7 +292,8 @@ static inline bool sde_evtlog_is_enabled(struct sde_dbg_evtlog *evtlog,
} }
static inline ssize_t sde_evtlog_dump_to_buffer(struct sde_dbg_evtlog *evtlog, static inline ssize_t sde_evtlog_dump_to_buffer(struct sde_dbg_evtlog *evtlog,
char *evtlog_buf, ssize_t evtlog_buf_size) char *evtlog_buf, ssize_t evtlog_buf_size,
bool update_last_entry)
{ {
return 0; return 0;
} }
@ -295,6 +316,10 @@ static inline void sde_dbg_dump(bool queue_work, const char *name, ...)
{ {
} }
static inline void sde_dbg_ctrl(const char *name, ...)
{
}
static inline int sde_dbg_reg_register_base(const char *name, static inline int sde_dbg_reg_register_base(const char *name,
void __iomem *base, size_t max_offset) void __iomem *base, size_t max_offset)
{ {

View file

@ -1,4 +1,4 @@
/* Copyright (c) 2016-2017, The Linux Foundation. All rights reserved. /* Copyright (c) 2016-2018, The Linux Foundation. All rights reserved.
* *
* This program is free software; you can redistribute it and/or modify * This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2 and * it under the terms of the GNU General Public License version 2 and
@ -75,7 +75,8 @@ void sde_evtlog_log(struct sde_dbg_evtlog *evtlog, const char *name, int line,
} }
/* always dump the last entries which are not dumped yet */ /* always dump the last entries which are not dumped yet */
static bool _sde_evtlog_dump_calc_range(struct sde_dbg_evtlog *evtlog) static bool _sde_evtlog_dump_calc_range(struct sde_dbg_evtlog *evtlog,
bool update_last_entry)
{ {
bool need_dump = true; bool need_dump = true;
unsigned long flags; unsigned long flags;
@ -87,21 +88,26 @@ static bool _sde_evtlog_dump_calc_range(struct sde_dbg_evtlog *evtlog)
evtlog->first = evtlog->next; evtlog->first = evtlog->next;
if (evtlog->last == evtlog->first) { if (update_last_entry)
evtlog->last_dump = evtlog->last;
if (evtlog->last_dump == evtlog->first) {
need_dump = false; need_dump = false;
goto dump_exit; goto dump_exit;
} }
if (evtlog->last < evtlog->first) { if (evtlog->last_dump < evtlog->first) {
evtlog->first %= SDE_EVTLOG_ENTRY; evtlog->first %= SDE_EVTLOG_ENTRY;
if (evtlog->last < evtlog->first) if (evtlog->last_dump < evtlog->first)
evtlog->last += SDE_EVTLOG_ENTRY; evtlog->last_dump += SDE_EVTLOG_ENTRY;
} }
if ((evtlog->last - evtlog->first) > SDE_EVTLOG_PRINT_ENTRY) { if ((evtlog->last_dump - evtlog->first) > SDE_EVTLOG_PRINT_ENTRY) {
pr_warn("evtlog buffer overflow before dump: %d\n", pr_info("evtlog skipping %d entries, last=%d\n",
evtlog->last - evtlog->first); evtlog->last_dump - evtlog->first -
evtlog->first = evtlog->last - SDE_EVTLOG_PRINT_ENTRY; SDE_EVTLOG_PRINT_ENTRY,
evtlog->last_dump - 1);
evtlog->first = evtlog->last_dump - SDE_EVTLOG_PRINT_ENTRY;
} }
evtlog->next = evtlog->first + 1; evtlog->next = evtlog->first + 1;
@ -112,7 +118,8 @@ dump_exit:
} }
ssize_t sde_evtlog_dump_to_buffer(struct sde_dbg_evtlog *evtlog, ssize_t sde_evtlog_dump_to_buffer(struct sde_dbg_evtlog *evtlog,
char *evtlog_buf, ssize_t evtlog_buf_size) char *evtlog_buf, ssize_t evtlog_buf_size,
bool update_last_entry)
{ {
int i; int i;
ssize_t off = 0; ssize_t off = 0;
@ -123,7 +130,7 @@ ssize_t sde_evtlog_dump_to_buffer(struct sde_dbg_evtlog *evtlog,
return 0; return 0;
/* update markers, exit if nothing to print */ /* update markers, exit if nothing to print */
if (!_sde_evtlog_dump_calc_range(evtlog)) if (!_sde_evtlog_dump_calc_range(evtlog, update_last_entry))
return 0; return 0;
spin_lock_irqsave(&evtlog->spin_lock, flags); spin_lock_irqsave(&evtlog->spin_lock, flags);
@ -159,12 +166,16 @@ ssize_t sde_evtlog_dump_to_buffer(struct sde_dbg_evtlog *evtlog,
void sde_evtlog_dump_all(struct sde_dbg_evtlog *evtlog) void sde_evtlog_dump_all(struct sde_dbg_evtlog *evtlog)
{ {
char buf[SDE_EVTLOG_BUF_MAX]; char buf[SDE_EVTLOG_BUF_MAX];
bool update_last_entry = true;
if (!evtlog) if (!evtlog)
return; return;
while (sde_evtlog_dump_to_buffer(evtlog, buf, sizeof(buf))) while (sde_evtlog_dump_to_buffer(evtlog, buf, sizeof(buf),
update_last_entry)) {
pr_info("%s", buf); pr_info("%s", buf);
update_last_entry = false;
}
} }
struct sde_dbg_evtlog *sde_evtlog_init(void) struct sde_dbg_evtlog *sde_evtlog_init(void)