Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit 0cf11fbe authored by Linux Build Service Account's avatar Linux Build Service Account Committed by Gerrit - the friendly Code Review server
Browse files

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

parents af36d13a 3c6964a0
Loading
Loading
Loading
Loading
+15 −3
Original line number Diff line number Diff line
@@ -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_callback *cb;
	unsigned long irq_flags;
	bool cb_tbl_error = false;
	int enable_counts = 0;

	pr_debug("irq_idx=%d\n", irq_idx);

	if (list_empty(&irq_obj->irq_cb_tbl[irq_idx]))
		SDE_ERROR("irq_idx=%d has no registered callback\n", irq_idx);
	spin_lock_irqsave(&sde_kms->irq_obj.cb_lock, irq_flags);
	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]);

	/*
	 * 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)
		if (cb->func)
			cb->func(cb->arg, irq_idx);
	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.
	 * NOTE: sde_core_irq_callback_handler is protected by top-level
+7 −1
Original line number Diff line number Diff line
/*
 * 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
 * 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");
	atomic_inc(&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");
}

+17 −1
Original line number Diff line number Diff line
/* 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
 * 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)
);

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,
	TP_PROTO(int pid, const char *name, bool trace_begin),
	TP_ARGS(pid, name, trace_begin),
+184 −11
Original line number Diff line number Diff line
/* 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
 * 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 */
#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
@@ -162,6 +165,7 @@ struct sde_dbg_vbif_debug_bus {
 * @enable_reg_dump: whether to dump registers into memory, kernel log, or both
 * @dbgbus_sde: debug bus structure for the sde
 * @dbgbus_vbif_rt: debug bus structure for the realtime vbif
 * @dump_all: dump all entries in register dump
 */
static struct sde_dbg_base {
	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_vbif_debug_bus dbgbus_vbif_rt;
	bool dump_all;
	u32 debugfs_ctrl;
} sde_dbg_base;

/* 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[],
	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;

@@ -1460,6 +1466,7 @@ static void _sde_dump_array(struct sde_dbg_reg_base *blk_arr[],
				sde_dbg_base.enable_reg_dump);
	}

	if (dump_all)
		sde_evtlog_dump_all(sde_dbg_base.evtlog);

	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),
		sde_dbg_base.work_panic, "evtlog_workitem",
		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, ...)
@@ -1493,6 +1501,7 @@ void sde_dbg_dump(bool queue_work, const char *name, ...)
	bool do_panic = false;
	bool dump_dbgbus_sde = false;
	bool dump_dbgbus_vbif_rt = false;
	bool dump_all = false;
	va_list args;
	char *blk_name = 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,
			sizeof(sde_dbg_base.req_dump_blks));
	sde_dbg_base.dump_all = false;

	va_start(args, name);
	i = 0;
@@ -1531,6 +1541,8 @@ void sde_dbg_dump(bool queue_work, const char *name, ...)
						blk_name);
			}
		}
		if (!strcmp(blk_name, "all"))
			dump_all = true;

		if (!strcmp(blk_name, "dbg_bus"))
			dump_dbgbus_sde = true;
@@ -1550,13 +1562,53 @@ void sde_dbg_dump(bool queue_work, const char *name, ...)
				dump_dbgbus_sde;
		sde_dbg_base.dbgbus_vbif_rt.cmn.include_in_deferred_work =
				dump_dbgbus_vbif_rt;
		sde_dbg_base.dump_all = dump_all;
		schedule_work(&sde_dbg_base.dump_work);
	} else {
		_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
 * @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)
{
	if (!inode || !file)
		return -EINVAL;

	/* non-seekable */
	file->f_mode &= ~(FMODE_LSEEK | FMODE_PREAD | FMODE_PWRITE);
	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;
	char evtlog_buf[SDE_EVTLOG_BUF_MAX];

	if (!buff || !ppos)
		return -EINVAL;

	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))
		return -EFAULT;
	*ppos += len;
@@ -1621,6 +1679,82 @@ static const struct file_operations sde_evtlog_fops = {
	.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)
{
	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++)
		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,
						&sde_evtlog_fops);
	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)
{
	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);
	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,
		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 cnt = DEFAULT_BASE_REG_CNT;
	char buf[24];
	ssize_t rc = count;

	if (!file)
		return -EINVAL;

	dbg = file->private_data;
	if (!dbg)
		return -ENODEV;

@@ -1799,6 +1946,9 @@ static ssize_t sde_dbg_reg_base_offset_write(struct file *file,
		goto exit;
	}

	if (cnt == 0)
		return -EINVAL;

	dbg->off = off;
	dbg->cnt = cnt;

@@ -1819,17 +1969,29 @@ exit:
static ssize_t sde_dbg_reg_base_offset_read(struct file *file,
			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;
	char buf[24] = {'\0'};

	if (!file)
		return -EINVAL;

	dbg = file->private_data;
	if (!dbg)
		return -ENODEV;

	if (!ppos)
		return -EINVAL;

	if (*ppos)
		return 0;	/* the end */

	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);
	if (len < 0 || len >= sizeof(buf)) {
		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,
		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;
	u32 data, cnt;
	char buf[24];

	if (!file)
		return -EINVAL;

	dbg = file->private_data;
	if (!dbg)
		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,
			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;

	if (!file)
		return -EINVAL;

	dbg = file->private_data;
	if (!dbg) {
		pr_err("invalid handle\n");
		return -ENODEV;
	}

	if (!ppos)
		return -EINVAL;

	mutex_lock(&sde_dbg_base.mutex);
	if (!dbg->buf) {
		char *hwbuf;
+30 −5
Original line number Diff line number Diff line
/* 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
 * it under the terms of the GNU General Public License version 2 and
@@ -17,9 +17,10 @@
#include <linux/debugfs.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_EXIT	0x2222
#define SDE_EVTLOG_ERROR	0xebad

#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
 * 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_BUF_MAX 512
#define SDE_EVTLOG_BUF_ALIGN 32
@@ -77,6 +78,7 @@ struct sde_dbg_evtlog {
	struct sde_dbg_evtlog_log logs[SDE_EVTLOG_ENTRY];
	u32 first;
	u32 last;
	u32 last_dump;
	u32 curr;
	u32 next;
	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__, \
		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)

/**
@@ -172,10 +181,12 @@ bool sde_evtlog_is_enabled(struct sde_dbg_evtlog *evtlog, u32 flag);
 * @evtlog:		pointer to evtlog
 * @evtlog_buf:		target buffer to print into
 * @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
 */
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
@@ -213,6 +224,15 @@ void sde_dbg_destroy(void);
 */
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
 *	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,
		char *evtlog_buf, ssize_t evtlog_buf_size)
		char *evtlog_buf, ssize_t evtlog_buf_size,
		bool update_last_entry)
{
	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,
		void __iomem *base, size_t max_offset)
{
Loading