Loading drivers/gpu/drm/msm/sde_dbg.c +80 −2 Original line number Diff line number Diff line Loading @@ -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)) Loading Loading @@ -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 Loading Loading @@ -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", Loading drivers/gpu/drm/msm/sde_dbg.h +48 −6 Original line number Diff line number Diff line Loading @@ -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 { Loading @@ -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 Loading Loading @@ -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; Loading @@ -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; Loading @@ -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) /** Loading Loading @@ -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) { Loading Loading @@ -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) { } Loading @@ -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; } Loading @@ -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) */ Loading drivers/gpu/drm/msm/sde_dbg_evtlog.c +147 −27 Original line number Diff line number Diff line Loading @@ -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, Loading @@ -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; Loading @@ -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; Loading @@ -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, Loading @@ -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); Loading @@ -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; Loading Loading @@ -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); } Loading
drivers/gpu/drm/msm/sde_dbg.c +80 −2 Original line number Diff line number Diff line Loading @@ -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)) Loading Loading @@ -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 Loading Loading @@ -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", Loading
drivers/gpu/drm/msm/sde_dbg.h +48 −6 Original line number Diff line number Diff line Loading @@ -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 { Loading @@ -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 Loading Loading @@ -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; Loading @@ -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; Loading @@ -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) /** Loading Loading @@ -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) { Loading Loading @@ -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) { } Loading @@ -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; } Loading @@ -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) */ Loading
drivers/gpu/drm/msm/sde_dbg_evtlog.c +147 −27 Original line number Diff line number Diff line Loading @@ -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, Loading @@ -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; Loading @@ -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; Loading @@ -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, Loading @@ -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); Loading @@ -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; Loading Loading @@ -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); }