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

Commit a6193e1e 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: support optional messaging in event log" into msm-4.9

parents 1b8d055d 6ac61ccd
Loading
Loading
Loading
Loading
+80 −2
Original line number Diff line number Diff line
@@ -2436,7 +2436,7 @@ void sde_dbg_dump(bool queue_work, const char *name, ...)
	struct sde_dbg_reg_base **blk_arr;
	u32 blk_len;

	if (!sde_evtlog_is_enabled(sde_dbg_base.evtlog, SDE_EVTLOG_DEFAULT))
	if (!sde_evtlog_is_enabled(sde_dbg_base.evtlog, SDE_EVTLOG_ALWAYS))
		return;

	if (queue_work && work_pending(&sde_dbg_base.dump_work))
@@ -2558,6 +2558,82 @@ static const struct file_operations sde_evtlog_fops = {
	.write = sde_evtlog_dump_write,
};

/*
 * sde_evtlog_filter_show - read callback for evtlog filter
 * @s: pointer to seq_file object
 * @data: pointer to private data
 */
static int sde_evtlog_filter_show(struct seq_file *s, void *data)
{
	struct sde_dbg_evtlog *evtlog;
	char buffer[64];
	int i;

	if (!s || !s->private)
		return -EINVAL;

	evtlog = s->private;

	for (i = 0; !sde_evtlog_get_filter(
				evtlog, i, buffer, ARRAY_SIZE(buffer)); ++i)
		seq_printf(s, "*%s*\n", buffer);
	return 0;
}

/*
 * sde_evtlog_filter_open - debugfs open handler for evtlog filter
 * @inode: debugfs inode
 * @file: file handle
 * Returns: zero on success
 */
static int sde_evtlog_filter_open(struct inode *inode, struct file *file)
{
	if (!file)
		return -EINVAL;

	return single_open(file, sde_evtlog_filter_show, inode->i_private);
}

/*
 * sde_evtlog_filter_write - write callback for evtlog filter
 * @file: pointer to file structure
 * @user_buf: pointer to incoming user data
 * @count: size of incoming user buffer
 * @ppos: pointer to file offset
 */
static ssize_t sde_evtlog_filter_write(struct file *file,
	const char __user *user_buf, size_t count, loff_t *ppos)
{
	char *tmp_filter = NULL;
	ssize_t rc = 0;

	if (count > 0) {
		/* copy user provided string and null terminate it */
		tmp_filter = kzalloc(count + 1, GFP_KERNEL);
		if (!tmp_filter)
			rc = -ENOMEM;
		else if (copy_from_user(tmp_filter, user_buf, count))
			rc = -EFAULT;
	}

	/* update actual filter configuration on success */
	if (!rc) {
		sde_evtlog_set_filter(sde_dbg_base.evtlog, tmp_filter);
		rc = count;
	}
	kfree(tmp_filter);

	return rc;
}

static const struct file_operations sde_evtlog_filter_fops = {
	.open =		sde_evtlog_filter_open,
	.write =	sde_evtlog_filter_write,
	.read =		seq_read,
	.llseek =	seq_lseek,
	.release =	seq_release
};

/**
 * sde_dbg_reg_base_release - release allocated reg dump file private data
 * @inode: debugfs inode
@@ -2799,12 +2875,14 @@ int sde_dbg_debugfs_register(struct dentry *debugfs_root)
			&sde_evtlog_fops);
	debugfs_create_u32("enable", 0644, sde_dbg_base.root,
			&(sde_dbg_base.evtlog->enable));
	debugfs_create_file("filter", 0644, sde_dbg_base.root,
			sde_dbg_base.evtlog,
			&sde_evtlog_filter_fops);
	debugfs_create_u32("panic", 0644, sde_dbg_base.root,
			&sde_dbg_base.panic_on_err);
	debugfs_create_u32("reg_dump", 0644, sde_dbg_base.root,
			&sde_dbg_base.enable_reg_dump);


	if (dbg->dbgbus_sde.entries) {
		dbg->dbgbus_sde.cmn.name = DBGBUS_NAME_SDE;
		snprintf(debug_name, sizeof(debug_name), "%s_dbgbus",
+48 −6
Original line number Diff line number Diff line
@@ -24,9 +24,10 @@
#define SDE_DBG_DUMP_DATA_LIMITER (NULL)

enum sde_dbg_evtlog_flag {
	SDE_EVTLOG_DEFAULT = BIT(0),
	SDE_EVTLOG_CRITICAL = BIT(0),
	SDE_EVTLOG_IRQ = BIT(1),
	SDE_EVTLOG_ALL = BIT(7)
	SDE_EVTLOG_VERBOSE = BIT(2),
	SDE_EVTLOG_ALWAYS = -1
};

enum sde_dbg_dump_flag {
@@ -35,7 +36,7 @@ enum sde_dbg_dump_flag {
};

#ifdef CONFIG_DRM_SDE_EVTLOG_DEBUG
#define SDE_EVTLOG_DEFAULT_ENABLE 1
#define SDE_EVTLOG_DEFAULT_ENABLE SDE_EVTLOG_CRITICAL
#else
#define SDE_EVTLOG_DEFAULT_ENABLE 0
#endif
@@ -72,6 +73,9 @@ struct sde_dbg_evtlog_log {
	int pid;
};

/**
 * @filter_list: Linked list of currently active filter strings
 */
struct sde_dbg_evtlog {
	struct sde_dbg_evtlog_log logs[SDE_EVTLOG_ENTRY];
	u32 first;
@@ -80,6 +84,7 @@ struct sde_dbg_evtlog {
	u32 next;
	u32 enable;
	spinlock_t spin_lock;
	struct list_head filter_list;
};

extern struct sde_dbg_evtlog *sde_dbg_base_evtlog;
@@ -89,7 +94,15 @@ extern struct sde_dbg_evtlog *sde_dbg_base_evtlog;
 * ... - variable arguments
 */
#define SDE_EVT32(...) sde_evtlog_log(sde_dbg_base_evtlog, __func__, \
		__LINE__, SDE_EVTLOG_DEFAULT, ##__VA_ARGS__, \
		__LINE__, SDE_EVTLOG_ALWAYS, ##__VA_ARGS__, \
		SDE_EVTLOG_DATA_LIMITER)

/**
 * SDE_EVT32_VERBOSE - Write a list of 32bit values for verbose event logging
 * ... - variable arguments
 */
#define SDE_EVT32_VERBOSE(...) sde_evtlog_log(sde_dbg_base_evtlog, __func__, \
		__LINE__, SDE_EVTLOG_VERBOSE, ##__VA_ARGS__, \
		SDE_EVTLOG_DATA_LIMITER)

/**
@@ -244,6 +257,24 @@ void sde_dbg_reg_register_dump_range(const char *base_name,
		const char *range_name, u32 offset_start, u32 offset_end,
		uint32_t xin_id);

/**
 * sde_evtlog_set_filter - update evtlog filtering
 * @evtlog:	pointer to evtlog
 * @filter:     pointer to optional function name filter, set to NULL to disable
 */
void sde_evtlog_set_filter(struct sde_dbg_evtlog *evtlog, char *filter);

/**
 * sde_evtlog_get_filter - query configured evtlog filters
 * @evtlog:	pointer to evtlog
 * @index:	filter index to retrieve
 * @buf:	pointer to output filter buffer
 * @bufsz:	size of output filter buffer
 * Returns:	zero if a filter string was returned
 */
int sde_evtlog_get_filter(struct sde_dbg_evtlog *evtlog, int index,
		char *buf, size_t bufsz);

#else
static inline struct sde_dbg_evtlog *sde_evtlog_init(void)
{
@@ -275,7 +306,7 @@ static inline ssize_t sde_evtlog_dump_to_buffer(struct sde_dbg_evtlog *evtlog,
	return 0;
}

void sde_dbg_init_dbg_buses(u32 hwversion)
static inline void sde_dbg_init_dbg_buses(u32 hwversion)
{
}

@@ -285,7 +316,7 @@ static inline int sde_dbg_init(struct device *dev,
	return 0;
}

int sde_dbg_debugfs_register(struct dentry *debugfs_root)
static inline int sde_dbg_debugfs_register(struct dentry *debugfs_root)
{
	return 0;
}
@@ -310,6 +341,17 @@ static inline void sde_dbg_reg_register_dump_range(const char *base_name,
{
}

static inline void sde_evtlog_set_filter(
		struct sde_dbg_evtlog *evtlog, char *filter)
{
}

static inline int sde_evtlog_get_filter(struct sde_dbg_evtlog *evtlog,
		int index, char *buf, size_t bufsz)
{
	return -EINVAL;
}

#endif /* defined(CONFIG_DEBUG_FS) */


+147 −27
Original line number Diff line number Diff line
@@ -23,13 +23,40 @@
#include "sde_dbg.h"
#include "sde_trace.h"

bool sde_evtlog_is_enabled(struct sde_dbg_evtlog *evtlog, u32 flag)
#define SDE_EVTLOG_FILTER_STRSIZE	64

struct sde_evtlog_filter {
	struct list_head list;
	char filter[SDE_EVTLOG_FILTER_STRSIZE];
};

static bool _sde_evtlog_is_filtered_no_lock(
		struct sde_dbg_evtlog *evtlog, const char *str)
{
	if (!evtlog)
		return false;
	struct sde_evtlog_filter *filter_node;
	bool rc;

	if (!str)
		return true;

	/*
	 * Filter the incoming string IFF the list is not empty AND
	 * a matching entry is not in the list.
	 */
	rc = !list_empty(&evtlog->filter_list);
	list_for_each_entry(filter_node, &evtlog->filter_list, list)
		if (strnstr(str, filter_node->filter,
					SDE_EVTLOG_FILTER_STRSIZE - 1)) {
			rc = false;
			break;
		}

	return (flag & evtlog->enable) ||
		(flag == SDE_EVTLOG_ALL && evtlog->enable);
	return rc;
}

bool sde_evtlog_is_enabled(struct sde_dbg_evtlog *evtlog, u32 flag)
{
	return evtlog && (evtlog->enable & flag);
}

void sde_evtlog_log(struct sde_dbg_evtlog *evtlog, const char *name, int line,
@@ -47,6 +74,10 @@ void sde_evtlog_log(struct sde_dbg_evtlog *evtlog, const char *name, int line,
		return;

	spin_lock_irqsave(&evtlog->spin_lock, flags);

	if (_sde_evtlog_is_filtered_no_lock(evtlog, name))
		goto exit;

	log = &evtlog->logs[evtlog->curr];
	log->time = ktime_to_us(ktime_get());
	log->name = name;
@@ -70,27 +101,20 @@ void sde_evtlog_log(struct sde_dbg_evtlog *evtlog, const char *name, int line,

	trace_sde_evtlog(name, line, i > 0 ? log->data[0] : 0,
			i > 1 ? log->data[1] : 0);

exit:
	spin_unlock_irqrestore(&evtlog->spin_lock, flags);
}

/* always dump the last entries which are not dumped yet */
static bool _sde_evtlog_dump_calc_range(struct sde_dbg_evtlog *evtlog)
{
	bool need_dump = true;
	unsigned long flags;

	if (!evtlog)
		return false;

	spin_lock_irqsave(&evtlog->spin_lock, flags);

	evtlog->first = evtlog->next;

	if (evtlog->last == evtlog->first) {
		need_dump = false;
		goto dump_exit;
	}
	if (evtlog->last == evtlog->first)
		return false;

	if (evtlog->last < evtlog->first) {
		evtlog->first %= SDE_EVTLOG_ENTRY;
@@ -99,16 +123,14 @@ static bool _sde_evtlog_dump_calc_range(struct sde_dbg_evtlog *evtlog)
	}

	if ((evtlog->last - evtlog->first) > SDE_EVTLOG_PRINT_ENTRY) {
		pr_warn("evtlog buffer overflow before dump: %d\n",
			evtlog->last - evtlog->first);
		pr_info("evtlog skipping %d entries, last=%d\n",
			evtlog->last - evtlog->first - SDE_EVTLOG_PRINT_ENTRY,
			evtlog->last - 1);
		evtlog->first = evtlog->last - SDE_EVTLOG_PRINT_ENTRY;
	}
	evtlog->next = evtlog->first + 1;

dump_exit:
	spin_unlock_irqrestore(&evtlog->spin_lock, flags);

	return need_dump;
	return true;
}

ssize_t sde_evtlog_dump_to_buffer(struct sde_dbg_evtlog *evtlog,
@@ -122,16 +144,15 @@ ssize_t sde_evtlog_dump_to_buffer(struct sde_dbg_evtlog *evtlog,
	if (!evtlog || !evtlog_buf)
		return 0;

	spin_lock_irqsave(&evtlog->spin_lock, flags);

	/* update markers, exit if nothing to print */
	if (!_sde_evtlog_dump_calc_range(evtlog))
		return 0;

	spin_lock_irqsave(&evtlog->spin_lock, flags);
		goto exit;

	log = &evtlog->logs[evtlog->first % SDE_EVTLOG_ENTRY];

	prev_log = &evtlog->logs[(evtlog->first - 1) %
		SDE_EVTLOG_ENTRY];
	prev_log = &evtlog->logs[(evtlog->first - 1) % SDE_EVTLOG_ENTRY];

	off = snprintf((evtlog_buf + off), (evtlog_buf_size - off), "%s:%-4d",
		log->name, log->line);
@@ -150,7 +171,7 @@ ssize_t sde_evtlog_dump_to_buffer(struct sde_dbg_evtlog *evtlog,
			"%x ", log->data[i]);

	off += snprintf((evtlog_buf + off), (evtlog_buf_size - off), "\n");

exit:
	spin_unlock_irqrestore(&evtlog->spin_lock, flags);

	return off;
@@ -178,10 +199,109 @@ struct sde_dbg_evtlog *sde_evtlog_init(void)
	spin_lock_init(&evtlog->spin_lock);
	evtlog->enable = SDE_EVTLOG_DEFAULT_ENABLE;

	INIT_LIST_HEAD(&evtlog->filter_list);

	return evtlog;
}

int sde_evtlog_get_filter(struct sde_dbg_evtlog *evtlog, int index,
		char *buf, size_t bufsz)
{
	struct sde_evtlog_filter *filter_node;
	unsigned long flags;
	int rc = -EFAULT;

	if (!evtlog || !buf || !bufsz || index < 0)
		return -EINVAL;

	spin_lock_irqsave(&evtlog->spin_lock, flags);
	list_for_each_entry(filter_node, &evtlog->filter_list, list) {
		if (index--)
			continue;

		/* don't care about return value */
		(void)strlcpy(buf, filter_node->filter, bufsz);
		rc = 0;
		break;
	}
	spin_unlock_irqrestore(&evtlog->spin_lock, flags);

	return rc;
}

void sde_evtlog_set_filter(struct sde_dbg_evtlog *evtlog, char *filter)
{
	struct sde_evtlog_filter *filter_node, *tmp;
	struct list_head free_list;
	unsigned long flags;
	char *flt;

	if (!evtlog)
		return;

	INIT_LIST_HEAD(&free_list);

	/*
	 * Clear active filter list and cache filter_nodes locally
	 * to reduce memory fragmentation.
	 */
	spin_lock_irqsave(&evtlog->spin_lock, flags);
	list_for_each_entry_safe(filter_node, tmp, &evtlog->filter_list, list) {
		list_del_init(&filter_node->list);
		list_add_tail(&filter_node->list, &free_list);
	}
	spin_unlock_irqrestore(&evtlog->spin_lock, flags);

	/*
	 * Parse incoming filter request string and build up a new
	 * filter list. New filter nodes are taken from the local
	 * free list, if available, and allocated from the system
	 * heap once the free list is empty.
	 */
	while (filter && (flt = strsep(&filter, "|\r\n\t ")) != NULL) {
		if (!*flt)
			continue;

		if (list_empty(&free_list)) {
			filter_node = kzalloc(sizeof(*filter_node), GFP_KERNEL);
			if (!filter_node)
				break;

			INIT_LIST_HEAD(&filter_node->list);
		} else {
			filter_node = list_first_entry(&free_list,
					struct sde_evtlog_filter, list);
			list_del_init(&filter_node->list);
		}

		/* don't care if copy truncated */
		(void)strlcpy(filter_node->filter, flt,
				SDE_EVTLOG_FILTER_STRSIZE);

		spin_lock_irqsave(&evtlog->spin_lock, flags);
		list_add_tail(&filter_node->list, &evtlog->filter_list);
		spin_unlock_irqrestore(&evtlog->spin_lock, flags);
	}

	/*
	 * Free any unused filter_nodes back to the system.
	 */
	list_for_each_entry_safe(filter_node, tmp, &free_list, list) {
		list_del(&filter_node->list);
		kfree(filter_node);
	}
}

void sde_evtlog_destroy(struct sde_dbg_evtlog *evtlog)
{
	struct sde_evtlog_filter *filter_node, *tmp;

	if (!evtlog)
		return;

	list_for_each_entry_safe(filter_node, tmp, &evtlog->filter_list, list) {
		list_del(&filter_node->list);
		kfree(filter_node);
	}
	kfree(evtlog);
}