ANDROID: dma-buf: heaps: Add a sysfs file to report total pool size.

In order to help with memory accounting, expose the total pool size of
all DMA-BUF heaps at /sys/kernel/dma_heap/total_pools_kb.

This information will be exposed as part of Android Bugreport[1].

[1]: https://android-review.googlesource.com/q/topic:%22b%252F167709539%22+(status:open%20OR%20status:merged)

Bug: 167709539
Change-Id: I6a1b52517e73103122690f6567f4f295db9ca1ad
Signed-off-by: Hridya Valsaraju <hridya@google.com>
diff --git a/Documentation/ABI/testing/sysfs-kernel-dmaheap b/Documentation/ABI/testing/sysfs-kernel-dmaheap
new file mode 100644
index 0000000..f496181
--- /dev/null
+++ b/Documentation/ABI/testing/sysfs-kernel-dmaheap
@@ -0,0 +1,7 @@
+What:		/sys/kernel/dma_heap/total_pools_kb
+Date:		Feb 2021
+KernelVersion:	5.10
+Contact:	Hridya Valsaraju <hridya@google.com>,
+Description:
+		The total_pools_kb file is read-only and specifies how much
+		memory in Kb is allocated to DMA-BUF heap pools.
diff --git a/drivers/dma-buf/dma-heap.c b/drivers/dma-buf/dma-heap.c
index 0699c4b..4627d44 100644
--- a/drivers/dma-buf/dma-heap.c
+++ b/drivers/dma-buf/dma-heap.c
@@ -370,21 +370,81 @@ static char *dma_heap_devnode(struct device *dev, umode_t *mode)
 	return kasprintf(GFP_KERNEL, "dma_heap/%s", dev_name(dev));
 }
 
+static ssize_t total_pools_kb_show(struct kobject *kobj,
+				   struct kobj_attribute *attr, char *buf)
+{
+	struct dma_heap *heap;
+	u64 total_pool_size = 0;
+
+	mutex_lock(&heap_list_lock);
+	list_for_each_entry(heap, &heap_list, list) {
+		if (heap->ops->get_pool_size)
+			total_pool_size += heap->ops->get_pool_size(heap);
+	}
+	mutex_unlock(&heap_list_lock);
+
+	return sysfs_emit(buf, "%llu\n", total_pool_size / 1024);
+}
+
+static struct kobj_attribute total_pools_kb_attr =
+	__ATTR_RO(total_pools_kb);
+
+static struct attribute *dma_heap_sysfs_attrs[] = {
+	&total_pools_kb_attr.attr,
+	NULL,
+};
+
+ATTRIBUTE_GROUPS(dma_heap_sysfs);
+
+static struct kobject *dma_heap_kobject;
+
+static int dma_heap_sysfs_setup(void)
+{
+	int ret;
+
+	dma_heap_kobject = kobject_create_and_add("dma_heap", kernel_kobj);
+	if (!dma_heap_kobject)
+		return -ENOMEM;
+
+	ret = sysfs_create_groups(dma_heap_kobject, dma_heap_sysfs_groups);
+	if (ret) {
+		kobject_put(dma_heap_kobject);
+		return ret;
+	}
+
+	return 0;
+}
+
+static void dma_heap_sysfs_teardown(void)
+{
+	kobject_put(dma_heap_kobject);
+}
+
 static int dma_heap_init(void)
 {
 	int ret;
 
-	ret = alloc_chrdev_region(&dma_heap_devt, 0, NUM_HEAP_MINORS, DEVNAME);
+	ret = dma_heap_sysfs_setup();
 	if (ret)
 		return ret;
 
+	ret = alloc_chrdev_region(&dma_heap_devt, 0, NUM_HEAP_MINORS, DEVNAME);
+	if (ret)
+		goto err_chrdev;
+
 	dma_heap_class = class_create(THIS_MODULE, DEVNAME);
 	if (IS_ERR(dma_heap_class)) {
-		unregister_chrdev_region(dma_heap_devt, NUM_HEAP_MINORS);
-		return PTR_ERR(dma_heap_class);
+		ret = PTR_ERR(dma_heap_class);
+		goto err_class;
 	}
 	dma_heap_class->devnode = dma_heap_devnode;
 
 	return 0;
+
+err_class:
+	unregister_chrdev_region(dma_heap_devt, NUM_HEAP_MINORS);
+err_chrdev:
+	dma_heap_sysfs_teardown();
+	return ret;
 }
 subsys_initcall(dma_heap_init);
diff --git a/include/linux/dma-heap.h b/include/linux/dma-heap.h
index 23ca21a..cfd47a6 100644
--- a/include/linux/dma-heap.h
+++ b/include/linux/dma-heap.h
@@ -17,6 +17,7 @@ struct dma_heap;
 /**
  * struct dma_heap_ops - ops to operate on a given heap
  * @allocate:		allocate dmabuf and return struct dma_buf ptr
+ * @get_pool_size:	if heap maintains memory pools, get pool size in bytes
  *
  * allocate returns dmabuf on success, ERR_PTR(-errno) on error.
  */
@@ -25,6 +26,7 @@ struct dma_heap_ops {
 			unsigned long len,
 			unsigned long fd_flags,
 			unsigned long heap_flags);
+	long (*get_pool_size)(struct dma_heap *heap);
 };
 
 /**