/* Copyright (c) 2002,2008-2013, 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 * only version 2 as published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * */ #include #include #include "kgsl.h" #include "kgsl_device.h" #include "kgsl_sharedmem.h" /*default log levels is error for everything*/ #define KGSL_LOG_LEVEL_DEFAULT 3 #define KGSL_LOG_LEVEL_MAX 7 struct dentry *kgsl_debugfs_dir; static struct dentry *pm_d_debugfs; struct dentry *proc_d_debugfs; static int pm_dump_set(void *data, u64 val) { struct kgsl_device *device = data; if (val) { mutex_lock(&device->mutex); kgsl_postmortem_dump(device, 1); mutex_unlock(&device->mutex); } return 0; } DEFINE_SIMPLE_ATTRIBUTE(pm_dump_fops, NULL, pm_dump_set, "%llu\n"); static int pm_regs_enabled_set(void *data, u64 val) { struct kgsl_device *device = data; device->pm_regs_enabled = val ? 1 : 0; return 0; } static int pm_regs_enabled_get(void *data, u64 *val) { struct kgsl_device *device = data; *val = device->pm_regs_enabled; return 0; } static int pm_ib_enabled_set(void *data, u64 val) { struct kgsl_device *device = data; device->pm_ib_enabled = val ? 1 : 0; return 0; } static int pm_ib_enabled_get(void *data, u64 *val) { struct kgsl_device *device = data; *val = device->pm_ib_enabled; return 0; } static int pm_enabled_set(void *data, u64 val) { struct kgsl_device *device = data; device->pm_dump_enable = val; return 0; } static int pm_enabled_get(void *data, u64 *val) { struct kgsl_device *device = data; *val = device->pm_dump_enable; return 0; } DEFINE_SIMPLE_ATTRIBUTE(pm_regs_enabled_fops, pm_regs_enabled_get, pm_regs_enabled_set, "%llu\n"); DEFINE_SIMPLE_ATTRIBUTE(pm_ib_enabled_fops, pm_ib_enabled_get, pm_ib_enabled_set, "%llu\n"); DEFINE_SIMPLE_ATTRIBUTE(pm_enabled_fops, pm_enabled_get, pm_enabled_set, "%llu\n"); static inline int kgsl_log_set(unsigned int *log_val, void *data, u64 val) { *log_val = min((unsigned int)val, (unsigned int)KGSL_LOG_LEVEL_MAX); return 0; } #define KGSL_DEBUGFS_LOG(__log) \ static int __log ## _set(void *data, u64 val) \ { \ struct kgsl_device *device = data; \ return kgsl_log_set(&device->__log, data, val); \ } \ static int __log ## _get(void *data, u64 *val) \ { \ struct kgsl_device *device = data; \ *val = device->__log; \ return 0; \ } \ DEFINE_SIMPLE_ATTRIBUTE(__log ## _fops, \ __log ## _get, __log ## _set, "%llu\n"); \ KGSL_DEBUGFS_LOG(drv_log); KGSL_DEBUGFS_LOG(cmd_log); KGSL_DEBUGFS_LOG(ctxt_log); KGSL_DEBUGFS_LOG(mem_log); KGSL_DEBUGFS_LOG(pwr_log); KGSL_DEBUGFS_LOG(ft_log); static int memfree_hist_print(struct seq_file *s, void *unused) { void *base = kgsl_driver.memfree_hist.base_hist_rb; struct kgsl_memfree_hist_elem *wptr = kgsl_driver.memfree_hist.wptr; struct kgsl_memfree_hist_elem *p; char str[16]; seq_printf(s, "%8s %8s %8s %11s\n", "pid", "gpuaddr", "size", "flags"); mutex_lock(&kgsl_driver.memfree_hist_mutex); p = wptr; for (;;) { kgsl_get_memory_usage(str, sizeof(str), p->flags); /* * if the ring buffer is not filled up yet * all its empty elems have size==0 * just skip them ... */ if (p->size) seq_printf(s, "%8d %08x %8d %11s\n", p->pid, p->gpuaddr, p->size, str); p++; if ((void *)p >= base + kgsl_driver.memfree_hist.size) p = (struct kgsl_memfree_hist_elem *) base; if (p == kgsl_driver.memfree_hist.wptr) break; } mutex_unlock(&kgsl_driver.memfree_hist_mutex); return 0; } static int memfree_hist_open(struct inode *inode, struct file *file) { return single_open(file, memfree_hist_print, inode->i_private); } static const struct file_operations memfree_hist_fops = { .open = memfree_hist_open, .read = seq_read, .llseek = seq_lseek, .release = single_release, }; void kgsl_device_debugfs_init(struct kgsl_device *device) { if (kgsl_debugfs_dir && !IS_ERR(kgsl_debugfs_dir)) device->d_debugfs = debugfs_create_dir(device->name, kgsl_debugfs_dir); if (!device->d_debugfs || IS_ERR(device->d_debugfs)) return; device->cmd_log = KGSL_LOG_LEVEL_DEFAULT; device->ctxt_log = KGSL_LOG_LEVEL_DEFAULT; device->drv_log = KGSL_LOG_LEVEL_DEFAULT; device->mem_log = KGSL_LOG_LEVEL_DEFAULT; device->pwr_log = KGSL_LOG_LEVEL_DEFAULT; device->ft_log = KGSL_LOG_LEVEL_DEFAULT; debugfs_create_file("log_level_cmd", 0644, device->d_debugfs, device, &cmd_log_fops); debugfs_create_file("log_level_ctxt", 0644, device->d_debugfs, device, &ctxt_log_fops); debugfs_create_file("log_level_drv", 0644, device->d_debugfs, device, &drv_log_fops); debugfs_create_file("log_level_mem", 0644, device->d_debugfs, device, &mem_log_fops); debugfs_create_file("log_level_pwr", 0644, device->d_debugfs, device, &pwr_log_fops); debugfs_create_file("memfree_history", 0444, device->d_debugfs, device, &memfree_hist_fops); debugfs_create_file("log_level_ft", 0644, device->d_debugfs, device, &ft_log_fops); /* Create postmortem dump control files */ pm_d_debugfs = debugfs_create_dir("postmortem", device->d_debugfs); if (IS_ERR(pm_d_debugfs)) return; debugfs_create_file("dump", 0600, pm_d_debugfs, device, &pm_dump_fops); debugfs_create_file("regs_enabled", 0644, pm_d_debugfs, device, &pm_regs_enabled_fops); debugfs_create_file("ib_enabled", 0644, pm_d_debugfs, device, &pm_ib_enabled_fops); device->pm_dump_enable = 0; debugfs_create_file("enable", 0644, pm_d_debugfs, device, &pm_enabled_fops); } static const char * const memtype_strings[] = { "gpumem", "pmem", "ashmem", "usermap", "ion", }; static const char *memtype_str(int memtype) { if (memtype < ARRAY_SIZE(memtype_strings)) return memtype_strings[memtype]; return "unknown"; } static char get_alignflag(const struct kgsl_memdesc *m) { int align = kgsl_memdesc_get_align(m); if (align >= ilog2(SZ_1M)) return 'L'; else if (align >= ilog2(SZ_64K)) return 'l'; return '-'; } static char get_cacheflag(const struct kgsl_memdesc *m) { static const char table[] = { [KGSL_CACHEMODE_WRITECOMBINE] = '-', [KGSL_CACHEMODE_UNCACHED] = 'u', [KGSL_CACHEMODE_WRITEBACK] = 'b', [KGSL_CACHEMODE_WRITETHROUGH] = 't', }; return table[kgsl_memdesc_get_cachemode(m)]; } static void print_mem_entry(struct seq_file *s, struct kgsl_mem_entry *entry) { char flags[6]; char usage[16]; struct kgsl_memdesc *m = &entry->memdesc; flags[0] = kgsl_memdesc_is_global(m) ? 'g' : '-'; flags[1] = m->flags & KGSL_MEMFLAGS_GPUREADONLY ? 'r' : '-'; flags[2] = get_alignflag(m); flags[3] = get_cacheflag(m); flags[4] = kgsl_memdesc_use_cpu_map(m) ? 'p' : '-'; flags[5] = '\0'; kgsl_get_memory_usage(usage, sizeof(usage), m->flags); seq_printf(s, "%08x %08lx %8d %5d %5s %10s %16s %5d\n", m->gpuaddr, m->useraddr, m->size, entry->id, flags, memtype_str(entry->memtype), usage, m->sglen); } static int process_mem_print(struct seq_file *s, void *unused) { struct kgsl_mem_entry *entry; struct rb_node *node; struct kgsl_process_private *private = s->private; int next = 0; seq_printf(s, "%8s %8s %8s %5s %5s %10s %16s %5s\n", "gpuaddr", "useraddr", "size", "id", "flags", "type", "usage", "sglen"); /* print all entries with a GPU address */ spin_lock(&private->mem_lock); for (node = rb_first(&private->mem_rb); node; node = rb_next(node)) { entry = rb_entry(node, struct kgsl_mem_entry, node); print_mem_entry(s, entry); } spin_unlock(&private->mem_lock); /* now print all the unbound entries */ while (1) { rcu_read_lock(); entry = idr_get_next(&private->mem_idr, &next); rcu_read_unlock(); if (entry == NULL) break; if (entry->memdesc.gpuaddr == 0) print_mem_entry(s, entry); next++; } return 0; } static int process_mem_open(struct inode *inode, struct file *file) { return single_open(file, process_mem_print, inode->i_private); } static const struct file_operations process_mem_fops = { .open = process_mem_open, .read = seq_read, .llseek = seq_lseek, .release = single_release, }; void kgsl_process_init_debugfs(struct kgsl_process_private *private) { unsigned char name[16]; snprintf(name, sizeof(name), "%d", private->pid); private->debug_root = debugfs_create_dir(name, proc_d_debugfs); debugfs_create_file("mem", 0400, private->debug_root, private, &process_mem_fops); } void kgsl_core_debugfs_init(void) { kgsl_debugfs_dir = debugfs_create_dir("kgsl", 0); proc_d_debugfs = debugfs_create_dir("proc", kgsl_debugfs_dir); } void kgsl_core_debugfs_close(void) { debugfs_remove_recursive(kgsl_debugfs_dir); }