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_sched.c b/arch/arm/mach-msm/ocmem_sched.c
index 54510c9..c95728e 100644
--- a/arch/arm/mach-msm/ocmem_sched.c
+++ b/arch/arm/mach-msm/ocmem_sched.c
@@ -244,6 +244,15 @@
return ret_addr;
}
+static inline struct ocmem_zone *zone_of(struct ocmem_req *req)
+{
+ int owner;
+ if (!req)
+ return NULL;
+ owner = req->owner;
+ return get_zone(owner);
+}
+
static int insert_region(struct ocmem_region *region)
{
@@ -838,7 +847,6 @@
if (matched_req != req)
goto invalid_op_error;
-
ret = zone->z_ops->free(zone,
matched_req->req_start, matched_req->req_sz);
@@ -1317,6 +1325,8 @@
if (rc < 0)
return -EINVAL;
+ inc_ocmem_stat(zone_of(req), NR_FREES);
+
ocmem_destroy_req(req);
handle->req = NULL;
@@ -1393,14 +1403,16 @@
goto transfer_out_error;
}
+ inc_ocmem_stat(zone_of(req), NR_TRANSFERS_TO_DDR);
+
rc = queue_transfer(req, handle, list, TO_DDR);
if (rc < 0) {
pr_err("Failed to queue rdm transfer to DDR\n");
+ inc_ocmem_stat(zone_of(req), NR_TRANSFER_FAILS);
goto transfer_out_error;
}
-
return 0;
transfer_out_error:
@@ -1429,10 +1441,13 @@
goto transfer_in_error;
}
+ inc_ocmem_stat(zone_of(req), NR_TRANSFERS_TO_OCMEM);
+
rc = queue_transfer(req, handle, list, TO_OCMEM);
if (rc < 0) {
pr_err("Failed to queue rdm transfer to OCMEM\n");
+ inc_ocmem_stat(zone_of(req), NR_TRANSFER_FAILS);
goto transfer_in_error;
}
@@ -1469,6 +1484,8 @@
if (is_tcm(req->owner))
do_unmap(req);
+ inc_ocmem_stat(zone_of(req), NR_SHRINKS);
+
if (size == 0) {
pr_info("req %p being shrunk to zero\n", req);
rc = do_free(req);
@@ -1551,6 +1568,8 @@
req->edata = edata;
buffer.addr = req->req_start;
buffer.len = 0x0;
+ inc_ocmem_stat(zone_of(req),
+ NR_EVICTIONS);
dispatch_notification(req->owner,
OCMEM_ALLOC_SHRINK, &buffer);
}
@@ -1580,8 +1599,10 @@
rc = __sched_allocate(req, can_block, can_wait);
mutex_unlock(&sched_mutex);
- if (rc == OP_FAIL)
+ if (rc == OP_FAIL) {
+ inc_ocmem_stat(zone_of(req), NR_ALLOCATION_FAILS);
goto err_allocate_fail;
+ }
if (rc == OP_RESCHED) {
buffer->addr = 0x0;
@@ -1591,6 +1612,7 @@
} else if (rc == OP_PARTIAL) {
buffer->addr = device_address(req->owner, req->req_start);
buffer->len = req->req_sz;
+ inc_ocmem_stat(zone_of(req), NR_RANGE_ALLOCATIONS);
pr_debug("ocmem: Enqueuing req %p\n", req);
sched_enqueue(req);
} else if (rc == OP_COMPLETE) {
@@ -1622,6 +1644,7 @@
list_del(&req->sched_list);
req->op = SCHED_ALLOCATE;
sched_enqueue(req);
+ inc_ocmem_stat(zone_of(req), NR_RESTORES);
}
}
kfree(edata);
@@ -1668,11 +1691,15 @@
req->op = SCHED_ALLOCATE;
req->buffer = buffer;
+ inc_ocmem_stat(zone_of(req), NR_REQUESTS);
+
rc = do_allocate(req, can_block, can_wait);
if (rc < 0)
goto do_allocate_error;
+ inc_ocmem_stat(zone_of(req), NR_SYNC_ALLOCATIONS);
+
handle->req = req;
if (is_tcm(id)) {
@@ -1717,10 +1744,11 @@
rc = do_allocate(req, true, false);
-
if (rc < 0)
goto do_allocate_error;
+ inc_ocmem_stat(zone_of(req), NR_ASYNC_ALLOCATIONS);
+
if (is_tcm(id)) {
rc = process_map(req, req->req_start, req->req_end);
if (rc < 0)
@@ -1788,10 +1816,51 @@
return;
}
-int ocmem_sched_init(void)
+static int ocmem_allocations_show(struct seq_file *f, void *dummy)
+{
+ struct rb_node *rb_node = NULL;
+ struct ocmem_req *req = NULL;
+ unsigned j;
+ mutex_lock(&sched_mutex);
+ for (rb_node = rb_first(&sched_tree); rb_node;
+ rb_node = rb_next(rb_node)) {
+ struct ocmem_region *tmp_region = NULL;
+ tmp_region = rb_entry(rb_node, struct ocmem_region, region_rb);
+ for (j = MAX_OCMEM_PRIO - 1; j > NO_PRIO; j--) {
+ req = find_req_match(j, tmp_region);
+ if (req) {
+ seq_printf(f,
+ "owner: %s 0x%lx -- 0x%lx size 0x%lx [state: %2lx]\n",
+ get_name(req->owner),
+ req->req_start, req->req_end,
+ req->req_sz, req->state);
+ }
+ }
+ }
+ mutex_unlock(&sched_mutex);
+ return 0;
+}
+
+static int ocmem_allocations_open(struct inode *inode, struct file *file)
+{
+ return single_open(file, ocmem_allocations_show, inode->i_private);
+}
+
+static const struct file_operations allocations_show_fops = {
+ .open = ocmem_allocations_open,
+ .read = seq_read,
+ .llseek = seq_lseek,
+ .release = seq_release,
+};
+
+int ocmem_sched_init(struct platform_device *pdev)
{
int i = 0;
+ struct ocmem_plat_data *pdata = NULL;
+ struct device *dev = &pdev->dev;
+
sched_tree = RB_ROOT;
+ pdata = platform_get_drvdata(pdev);
mutex_init(&sched_mutex);
mutex_init(&sched_queue_mutex);
for (i = MIN_PRIO; i < MAX_OCMEM_PRIO; i++)
@@ -1805,5 +1874,11 @@
ocmem_eviction_wq = alloc_workqueue("ocmem_eviction_wq", 0, 0);
if (!ocmem_eviction_wq)
return -ENOMEM;
+
+ if (!debugfs_create_file("allocations", S_IRUGO, pdata->debug_node,
+ NULL, &allocations_show_fops)) {
+ dev_err(dev, "Unable to create debugfs node for scheduler\n");
+ return -EBUSY;
+ }
return 0;
}