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;
 }