msm: ocmem: Add debugfs support
Add support for viewing ocmem allocations, zones,
zone specific statistics and power states via debugfs.
Change-Id: I7c3d4d4bccfcd7a1825023d860ce491ae2a7ef5f
Signed-off-by: Naveen Ramaraj <nramaraj@codeaurora.org>
diff --git a/arch/arm/mach-msm/ocmem.c b/arch/arm/mach-msm/ocmem.c
index 82fe2f8..51445aa 100644
--- a/arch/arm/mach-msm/ocmem.c
+++ b/arch/arm/mach-msm/ocmem.c
@@ -63,6 +63,27 @@
"other_os",
};
+/* Must be in sync with enum ocmem_zstat_item */
+static const char *zstat_names[NR_OCMEM_ZSTAT_ITEMS] = {
+ "Allocation requests",
+ "Synchronous allocations",
+ "Ranged allocations",
+ "Asynchronous allocations",
+ "Allocation failures",
+ "Allocations grown",
+ "Allocations freed",
+ "Allocations shrunk",
+ "OCMEM maps",
+ "Map failures",
+ "OCMEM unmaps",
+ "Unmap failures",
+ "Transfers to OCMEM",
+ "Transfers to DDR",
+ "Transfer failures",
+ "Evictions",
+ "Restorations",
+};
+
struct ocmem_quota_table {
const char *name;
int id;
@@ -135,6 +156,23 @@
return 0;
}
+inline void inc_ocmem_stat(struct ocmem_zone *z,
+ enum ocmem_zstat_item item)
+{
+ if (!z)
+ return;
+ atomic_long_inc(&z->z_stat[item]);
+}
+
+inline unsigned long get_ocmem_stat(struct ocmem_zone *z,
+ enum ocmem_zstat_item item)
+{
+ if (!z)
+ return 0;
+ else
+ return atomic_long_read(&z->z_stat[item]);
+}
+
static struct ocmem_plat_data *parse_static_config(struct platform_device *pdev)
{
struct ocmem_plat_data *pdata = NULL;
@@ -473,6 +511,60 @@
return NULL;
}
+static int ocmem_zones_show(struct seq_file *f, void *dummy)
+{
+ unsigned i = 0;
+ for (i = OCMEM_GRAPHICS; i < OCMEM_CLIENT_MAX; i++) {
+ struct ocmem_zone *z = get_zone(i);
+ if (z && z->active == true)
+ seq_printf(f, "zone %s\t:0x%08lx - 0x%08lx (%4ld KB)\n",
+ get_name(z->owner), z->z_start, z->z_end - 1,
+ (z->z_end - z->z_start)/SZ_1K);
+ }
+ return 0;
+}
+
+static int ocmem_zones_open(struct inode *inode, struct file *file)
+{
+ return single_open(file, ocmem_zones_show, inode->i_private);
+}
+
+static const struct file_operations zones_show_fops = {
+ .open = ocmem_zones_open,
+ .read = seq_read,
+ .llseek = seq_lseek,
+ .release = seq_release,
+};
+
+static int ocmem_stats_show(struct seq_file *f, void *dummy)
+{
+ unsigned i = 0;
+ unsigned j = 0;
+ for (i = OCMEM_GRAPHICS; i < OCMEM_CLIENT_MAX; i++) {
+ struct ocmem_zone *z = get_zone(i);
+ if (z && z->active == true) {
+ seq_printf(f, "zone %s:\n", get_name(z->owner));
+ for (j = 0 ; j < ARRAY_SIZE(zstat_names); j++) {
+ seq_printf(f, "\t %s: %lu\n", zstat_names[j],
+ get_ocmem_stat(z, j));
+ }
+ }
+ }
+ return 0;
+}
+
+static int ocmem_stats_open(struct inode *inode, struct file *file)
+{
+ return single_open(file, ocmem_stats_show, inode->i_private);
+}
+
+static const struct file_operations stats_show_fops = {
+ .open = ocmem_stats_open,
+ .read = seq_read,
+ .llseek = seq_lseek,
+ .release = seq_release,
+};
+
static int ocmem_zone_init(struct platform_device *pdev)
{
@@ -549,6 +641,8 @@
z_ops->allocate = allocate_head;
z_ops->free = free_head;
}
+ /* zap the counters */
+ memset(zone->z_stat, 0 , sizeof(zone->z_stat));
zone->active = true;
active_zones++;
@@ -557,7 +651,19 @@
pr_info(" zone %s\t: 0x%08lx - 0x%08lx (%4ld KB)\n",
client_names[part->id], zone->z_start,
- zone->z_end, part->p_size/SZ_1K);
+ zone->z_end - 1, part->p_size/SZ_1K);
+ }
+
+ if (!debugfs_create_file("zones", S_IRUGO, pdata->debug_node,
+ NULL, &zones_show_fops)) {
+ dev_err(dev, "Unable to create debugfs node for zones\n");
+ return -EBUSY;
+ }
+
+ if (!debugfs_create_file("stats", S_IRUGO, pdata->debug_node,
+ NULL, &stats_show_fops)) {
+ dev_err(dev, "Unable to create debugfs node for stats\n");
+ return -EBUSY;
}
dev_dbg(dev, "Total active zones = %d\n", active_zones);
@@ -587,6 +693,27 @@
return 0;
}
+static int __devinit ocmem_debugfs_init(struct platform_device *pdev)
+{
+ struct dentry *debug_dir = NULL;
+ struct ocmem_plat_data *pdata = platform_get_drvdata(pdev);
+
+ debug_dir = debugfs_create_dir("ocmem", NULL);
+ if (!debug_dir || IS_ERR(debug_dir)) {
+ pr_err("ocmem: Unable to create debugfs root\n");
+ return PTR_ERR(debug_dir);
+ }
+
+ pdata->debug_node = debug_dir;
+ return 0;
+}
+
+static void __devexit ocmem_debugfs_exit(struct platform_device *pdev)
+{
+ struct ocmem_plat_data *pdata = platform_get_drvdata(pdev);
+ debugfs_remove_recursive(pdata->debug_node);
+}
+
static int __devinit msm_ocmem_probe(struct platform_device *pdev)
{
struct device *dev = &pdev->dev;
@@ -635,6 +762,9 @@
platform_set_drvdata(pdev, ocmem_pdata);
+ if (ocmem_debugfs_init(pdev))
+ return -EBUSY;
+
if (ocmem_core_init(pdev))
return -EBUSY;
@@ -644,7 +774,7 @@
if (ocmem_notifier_init())
return -EBUSY;
- if (ocmem_sched_init())
+ if (ocmem_sched_init(pdev))
return -EBUSY;
if (ocmem_rdm_init(pdev))
@@ -661,6 +791,7 @@
static int __devexit msm_ocmem_remove(struct platform_device *pdev)
{
+ ocmem_debugfs_exit(pdev);
return 0;
}