blob: d85522936cfaa34de58d798af4ff138121fb9eb7 [file] [log] [blame]
From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
From: Sandeep Patil <sspatil@google.com>
Date: Mon, 12 Aug 2019 17:23:25 -0700
Subject: ANDROID: staging: ion: make system and contig heaps modular
Add symmetric heap add/remove APIs and export symbols from
ion core that are needed to make both heaps modular.
Bug: 133508579
Test: ion-unit-test, rmmod, modprobe
Change-Id: Ic5f40936531936c9bfab48ea147108c019d28e2c
Signed-off-by: Sandeep Patil <sspatil@google.com>
[maennich: fold the following patch into this patch
b395fa76dedd ("ANDROID: staging: ion: fix sparse warning in ion system heap")]
Signed-off-by: Matthias Maennich <maennich@google.com>
---
drivers/staging/android/ion/heaps/Kconfig | 4 +-
drivers/staging/android/ion/heaps/Makefile | 4 +-
.../ion/heaps/ion_system_contig_heap.c | 36 ++++------
.../android/ion/heaps/ion_system_heap.c | 67 +++++++++----------
drivers/staging/android/ion/ion.c | 54 ++++++++++++---
drivers/staging/android/ion/ion_buffer.c | 13 ++++
drivers/staging/android/ion/ion_heap.c | 39 ++++++++++-
drivers/staging/android/ion/ion_private.h | 3 +
include/linux/ion.h | 24 ++++++-
9 files changed, 169 insertions(+), 75 deletions(-)
diff --git a/drivers/staging/android/ion/heaps/Kconfig b/drivers/staging/android/ion/heaps/Kconfig
index f1c7a6b79c01..af6fce69e61a 100644
--- a/drivers/staging/android/ion/heaps/Kconfig
+++ b/drivers/staging/android/ion/heaps/Kconfig
@@ -1,13 +1,13 @@
# SPDX-License-Identifier: GPL-2.0
config ION_SYSTEM_HEAP
- bool "Ion system heap"
+ tristate "Ion system heap"
depends on ION
help
Choose this option to enable the Ion system heap. The system heap
is backed by pages from the buddy allocator. If in doubt, say Y.
config ION_SYSTEM_CONTIG_HEAP
- bool "Ion system contig heap"
+ tristate "Ion system contig heap"
depends on ION
help
Choose this option to enable Ion system contig heap. The system contig heap
diff --git a/drivers/staging/android/ion/heaps/Makefile b/drivers/staging/android/ion/heaps/Makefile
index e2ee931100d0..449879035877 100644
--- a/drivers/staging/android/ion/heaps/Makefile
+++ b/drivers/staging/android/ion/heaps/Makefile
@@ -1,4 +1,6 @@
# SPDX-License-Identifier: GPL-2.0
-obj-$(CONFIG_ION_SYSTEM_HEAP) += ion_system_heap.o ion_page_pool.o
+obj-$(CONFIG_ION_SYSTEM_HEAP) += ion_sys_heap.o
+ion_sys_heap-y := ion_system_heap.o ion_page_pool.o
+
obj-$(CONFIG_ION_SYSTEM_CONTIG_HEAP) += ion_system_contig_heap.o
obj-$(CONFIG_ION_CMA_HEAP) += ion_cma_heap.o
diff --git a/drivers/staging/android/ion/heaps/ion_system_contig_heap.c b/drivers/staging/android/ion/heaps/ion_system_contig_heap.c
index 3a07ef931c2e..9cb18e38dc96 100644
--- a/drivers/staging/android/ion/heaps/ion_system_contig_heap.c
+++ b/drivers/staging/android/ion/heaps/ion_system_contig_heap.c
@@ -11,6 +11,7 @@
#include <linux/highmem.h>
#include <linux/ion.h>
#include <linux/mm.h>
+#include <linux/module.h>
#include <linux/scatterlist.h>
#include <linux/slab.h>
@@ -81,31 +82,22 @@ static struct ion_heap_ops kmalloc_ops = {
.map_user = ion_heap_map_user,
};
-static struct ion_heap *__ion_system_contig_heap_create(void)
-{
- struct ion_heap *heap;
-
- heap = kzalloc(sizeof(*heap), GFP_KERNEL);
- if (!heap)
- return ERR_PTR(-ENOMEM);
- heap->ops = &kmalloc_ops;
- heap->type = ION_HEAP_TYPE_SYSTEM_CONTIG;
- heap->name = "ion_system_contig_heap";
+static struct ion_heap contig_heap = {
+ .ops = &kmalloc_ops,
+ .type = ION_HEAP_TYPE_SYSTEM_CONTIG,
+ .name = "ion_system_contig_heap",
+};
- return heap;
+static int __init ion_system_contig_heap_init(void)
+{
+ return ion_device_add_heap(&contig_heap);
}
-static int ion_system_contig_heap_create(void)
+static void __exit ion_system_contig_heap_exit(void)
{
- struct ion_heap *heap;
-
- heap = __ion_system_contig_heap_create();
- if (IS_ERR(heap))
- return PTR_ERR(heap);
-
- ion_device_add_heap(heap);
-
- return 0;
+ ion_device_remove_heap(&contig_heap);
}
-device_initcall(ion_system_contig_heap_create);
+module_init(ion_system_contig_heap_init);
+module_exit(ion_system_contig_heap_exit);
+MODULE_LICENSE("GPL v2");
diff --git a/drivers/staging/android/ion/heaps/ion_system_heap.c b/drivers/staging/android/ion/heaps/ion_system_heap.c
index 25ea31757578..0b24652b77f2 100644
--- a/drivers/staging/android/ion/heaps/ion_system_heap.c
+++ b/drivers/staging/android/ion/heaps/ion_system_heap.c
@@ -11,6 +11,7 @@
#include <linux/highmem.h>
#include <linux/ion.h>
#include <linux/mm.h>
+#include <linux/module.h>
#include <linux/scatterlist.h>
#include <linux/slab.h>
#include <linux/vmalloc.h>
@@ -204,15 +205,6 @@ static int ion_system_heap_shrink(struct ion_heap *heap, gfp_t gfp_mask,
return nr_total;
}
-static struct ion_heap_ops system_heap_ops = {
- .allocate = ion_system_heap_allocate,
- .free = ion_system_heap_free,
- .map_kernel = ion_heap_map_kernel,
- .unmap_kernel = ion_heap_unmap_kernel,
- .map_user = ion_heap_map_user,
- .shrink = ion_system_heap_shrink,
-};
-
static void ion_system_heap_destroy_pools(struct ion_page_pool **pools)
{
int i;
@@ -246,38 +238,39 @@ static int ion_system_heap_create_pools(struct ion_page_pool **pools)
return -ENOMEM;
}
-static struct ion_heap *__ion_system_heap_create(void)
-{
- struct ion_system_heap *heap;
-
- heap = kzalloc(sizeof(*heap), GFP_KERNEL);
- if (!heap)
- return ERR_PTR(-ENOMEM);
- heap->heap.ops = &system_heap_ops;
- heap->heap.type = ION_HEAP_TYPE_SYSTEM;
- heap->heap.flags = ION_HEAP_FLAG_DEFER_FREE;
+static struct ion_heap_ops system_heap_ops = {
+ .allocate = ion_system_heap_allocate,
+ .free = ion_system_heap_free,
+ .map_kernel = ion_heap_map_kernel,
+ .unmap_kernel = ion_heap_unmap_kernel,
+ .map_user = ion_heap_map_user,
+ .shrink = ion_system_heap_shrink,
+};
- if (ion_system_heap_create_pools(heap->pools))
- goto free_heap;
+static struct ion_system_heap system_heap = {
+ .heap = {
+ .ops = &system_heap_ops,
+ .type = ION_HEAP_TYPE_SYSTEM,
+ .flags = ION_HEAP_FLAG_DEFER_FREE,
+ .name = "ion_system_heap",
+ }
+};
- return &heap->heap;
+static int __init ion_system_heap_init(void)
+{
+ int ret = ion_system_heap_create_pools(system_heap.pools);
+ if (ret)
+ return ret;
-free_heap:
- kfree(heap);
- return ERR_PTR(-ENOMEM);
+ return ion_device_add_heap(&system_heap.heap);
}
-static int ion_system_heap_create(void)
+static void __exit ion_system_heap_exit(void)
{
- struct ion_heap *heap;
-
- heap = __ion_system_heap_create();
- if (IS_ERR(heap))
- return PTR_ERR(heap);
- heap->name = "ion_system_heap";
-
- ion_device_add_heap(heap);
-
- return 0;
+ ion_device_remove_heap(&system_heap.heap);
+ ion_system_heap_destroy_pools(system_heap.pools);
}
-device_initcall(ion_system_heap_create);
+
+module_init(ion_system_heap_init);
+module_exit(ion_system_heap_exit);
+MODULE_LICENSE("GPL v2");
diff --git a/drivers/staging/android/ion/ion.c b/drivers/staging/android/ion/ion.c
index b862bdfeca89..548d65dbd3ac 100644
--- a/drivers/staging/android/ion/ion.c
+++ b/drivers/staging/android/ion/ion.c
@@ -222,28 +222,36 @@ static int debug_shrink_get(void *data, u64 *val)
DEFINE_SIMPLE_ATTRIBUTE(debug_shrink_fops, debug_shrink_get,
debug_shrink_set, "%llu\n");
-void ion_device_add_heap(struct ion_heap *heap)
+int __ion_device_add_heap(struct ion_heap *heap, struct module *owner)
{
struct ion_device *dev = internal_dev;
int ret;
struct dentry *heap_root;
char debug_name[64];
- if (!heap->ops->allocate || !heap->ops->free)
- pr_err("%s: can not add heap with invalid ops struct.\n",
- __func__);
+ if (!heap || !heap->ops || !heap->ops->allocate || !heap->ops->free) {
+ pr_err("%s: invalid heap or heap_ops\n", __func__);
+ ret = -EINVAL;
+ goto out;
+ }
+ heap->owner = owner;
spin_lock_init(&heap->free_lock);
spin_lock_init(&heap->stat_lock);
heap->free_list_size = 0;
- if (heap->flags & ION_HEAP_FLAG_DEFER_FREE)
- ion_heap_init_deferred_free(heap);
+ if (heap->flags & ION_HEAP_FLAG_DEFER_FREE) {
+ ret = ion_heap_init_deferred_free(heap);
+ if (ret)
+ goto out_heap_cleanup;
+ }
if ((heap->flags & ION_HEAP_FLAG_DEFER_FREE) || heap->ops->shrink) {
ret = ion_heap_init_shrinker(heap);
- if (ret)
+ if (ret) {
pr_err("%s: Failed to register shrinker\n", __func__);
+ goto out_heap_cleanup;
+ }
}
heap->num_of_buffers = 0;
@@ -273,6 +281,7 @@ void ion_device_add_heap(struct ion_heap *heap)
&debug_shrink_fops);
}
+ heap->debugfs_dir = heap_root;
down_write(&dev->lock);
heap->id = heap_id++;
/*
@@ -284,8 +293,37 @@ void ion_device_add_heap(struct ion_heap *heap)
dev->heap_cnt++;
up_write(&dev->lock);
+
+ return 0;
+
+out_heap_cleanup:
+ ion_heap_cleanup(heap);
+out:
+ return ret;
+}
+EXPORT_SYMBOL_GPL(__ion_device_add_heap);
+
+void ion_device_remove_heap(struct ion_heap *heap)
+{
+ struct ion_device *dev = internal_dev;
+
+ if (!heap) {
+ pr_err("%s: Invalid argument\n", __func__);
+ return;
+ }
+
+ // take semaphore and remove the heap from dev->heap list
+ down_write(&dev->lock);
+ /* So no new allocations can happen from this heap */
+ plist_del(&heap->node, &dev->heaps);
+ if (ion_heap_cleanup(heap) != 0) {
+ pr_warn("%s: failed to cleanup heap (%s)\n",
+ __func__, heap->name);
+ }
+ debugfs_remove_recursive(heap->debugfs_dir);
+ up_write(&dev->lock);
}
-EXPORT_SYMBOL(ion_device_add_heap);
+EXPORT_SYMBOL(ion_device_remove_heap);
static int ion_device_create(void)
{
diff --git a/drivers/staging/android/ion/ion_buffer.c b/drivers/staging/android/ion/ion_buffer.c
index ef5fbf45c554..961605a35c59 100644
--- a/drivers/staging/android/ion/ion_buffer.c
+++ b/drivers/staging/android/ion/ion_buffer.c
@@ -6,6 +6,7 @@
*/
#include <linux/mm.h>
+#include <linux/module.h>
#include <linux/slab.h>
#include <linux/vmalloc.h>
@@ -77,6 +78,14 @@ static struct ion_buffer *ion_buffer_create(struct ion_heap *heap,
heap->num_of_alloc_bytes += len;
if (heap->num_of_alloc_bytes > heap->alloc_bytes_wm)
heap->alloc_bytes_wm = heap->num_of_alloc_bytes;
+ if (heap->num_of_buffers == 1) {
+ /* This module reference lasts as long as at least one
+ * buffer is allocated from the heap. We are protected
+ * against ion_device_remove_heap() with dev->lock, so we can
+ * safely assume the module reference is going to* succeed.
+ */
+ __module_get(heap->owner);
+ }
spin_unlock(&heap->stat_lock);
INIT_LIST_HEAD(&buffer->attachments);
@@ -185,6 +194,7 @@ int ion_buffer_zero(struct ion_buffer *buffer)
return ion_sglist_zero(table->sgl, table->nents, pgprot);
}
+EXPORT_SYMBOL_GPL(ion_buffer_zero);
void ion_buffer_release(struct ion_buffer *buffer)
{
@@ -197,7 +207,10 @@ void ion_buffer_release(struct ion_buffer *buffer)
spin_lock(&buffer->heap->stat_lock);
buffer->heap->num_of_buffers--;
buffer->heap->num_of_alloc_bytes -= buffer->size;
+ if (buffer->heap->num_of_buffers == 0)
+ module_put(buffer->heap->owner);
spin_unlock(&buffer->heap->stat_lock);
+ /* drop reference to the heap module */
kfree(buffer);
}
diff --git a/drivers/staging/android/ion/ion_heap.c b/drivers/staging/android/ion/ion_heap.c
index 755548a4382d..e102f6a2ecf6 100644
--- a/drivers/staging/android/ion/ion_heap.c
+++ b/drivers/staging/android/ion/ion_heap.c
@@ -101,12 +101,15 @@ static int ion_heap_deferred_free(void *data)
struct ion_buffer *buffer;
wait_event_freezable(heap->waitqueue,
- ion_heap_freelist_size(heap) > 0);
+ (ion_heap_freelist_size(heap) > 0 ||
+ kthread_should_stop()));
spin_lock(&heap->free_lock);
if (list_empty(&heap->free_list)) {
spin_unlock(&heap->free_lock);
- continue;
+ if (!kthread_should_stop())
+ continue;
+ break;
}
buffer = list_first_entry(&heap->free_list, struct ion_buffer,
list);
@@ -156,12 +159,14 @@ void *ion_heap_map_kernel(struct ion_heap *heap,
return vaddr;
}
+EXPORT_SYMBOL_GPL(ion_heap_map_kernel);
void ion_heap_unmap_kernel(struct ion_heap *heap,
struct ion_buffer *buffer)
{
vunmap(buffer->vaddr);
}
+EXPORT_SYMBOL_GPL(ion_heap_unmap_kernel);
int ion_heap_map_user(struct ion_heap *heap, struct ion_buffer *buffer,
struct vm_area_struct *vma)
@@ -198,6 +203,7 @@ int ion_heap_map_user(struct ion_heap *heap, struct ion_buffer *buffer,
return 0;
}
+EXPORT_SYMBOL_GPL(ion_heap_map_user);
void ion_heap_freelist_add(struct ion_heap *heap, struct ion_buffer *buffer)
{
@@ -256,3 +262,32 @@ int ion_heap_init_shrinker(struct ion_heap *heap)
return register_shrinker(&heap->shrinker);
}
+
+int ion_heap_cleanup(struct ion_heap *heap)
+{
+ int ret;
+
+ if (heap->flags & ION_HEAP_FLAG_DEFER_FREE &&
+ !IS_ERR_OR_NULL(heap->task)) {
+ size_t free_list_size = ion_heap_freelist_size(heap);
+ size_t total_drained = ion_heap_freelist_drain(heap, 0);
+
+ if (total_drained != free_list_size) {
+ pr_err("%s: %s heap drained %zu bytes, requested %zu\n",
+ __func__, heap->name, free_list_size,
+ total_drained);
+ return -EBUSY;
+ }
+ ret = kthread_stop(heap->task);
+ if (ret < 0) {
+ pr_err("%s: failed to stop heap free thread\n",
+ __func__);
+ return ret;
+ }
+ }
+
+ if ((heap->flags & ION_HEAP_FLAG_DEFER_FREE) || heap->ops->shrink)
+ unregister_shrinker(&heap->shrinker);
+
+ return 0;
+}
diff --git a/drivers/staging/android/ion/ion_private.h b/drivers/staging/android/ion/ion_private.h
index ed7ed39d0df1..5cc09fbfbefe 100644
--- a/drivers/staging/android/ion/ion_private.h
+++ b/drivers/staging/android/ion/ion_private.h
@@ -51,4 +51,7 @@ extern struct dma_buf *ion_dmabuf_alloc(struct ion_device *dev, size_t len,
unsigned int flags);
extern int ion_free(struct ion_buffer *buffer);
+/* ion heap helpers */
+extern int ion_heap_cleanup(struct ion_heap *heap);
+
#endif /* _ION_PRIVATE_H */
diff --git a/include/linux/ion.h b/include/linux/ion.h
index 775e948a362c..fe0d2cb1ef25 100644
--- a/include/linux/ion.h
+++ b/include/linux/ion.h
@@ -12,6 +12,7 @@
#include <linux/dma-direction.h>
#include <linux/kref.h>
#include <linux/mm_types.h>
+#include <linux/module.h>
#include <linux/mutex.h>
#include <linux/rbtree.h>
#include <linux/sched.h>
@@ -105,6 +106,7 @@ struct ion_heap_ops {
* allocating. These are specified by platform data and
* MUST be unique
* @name: used for debugging
+ * @owner: kernel module that implements this heap
* @shrinker: a shrinker for the heap
* @free_list: free list head if deferred free is used
* @free_list_size size of the deferred free list in bytes
@@ -127,6 +129,7 @@ struct ion_heap {
unsigned long flags;
unsigned int id;
const char *name;
+ struct module *owner;
/* deferred free support */
struct shrinker shrinker;
@@ -143,16 +146,30 @@ struct ion_heap {
/* protect heap statistics */
spinlock_t stat_lock;
+
+ /* heap's debugfs root */
+ struct dentry *debugfs_dir;
};
+#define ion_device_add_heap(heap) __ion_device_add_heap(heap, THIS_MODULE)
+
#ifdef CONFIG_ION
/**
- * ion_device_add_heap - adds a heap to the ion device
+ * __ion_device_add_heap - adds a heap to the ion device
*
* @heap: the heap to add
+ *
+ * Returns 0 on success, negative error otherwise.
+ */
+int __ion_device_add_heap(struct ion_heap *heap, struct module *owner);
+
+/**
+ * ion_device_remove_heap - removes a heap from ion device
+ *
+ * @heap: pointer to the heap to be removed
*/
-void ion_device_add_heap(struct ion_heap *heap);
+void ion_device_remove_heap(struct ion_heap *heap);
/**
* ion_heap_init_shrinker
@@ -283,7 +300,8 @@ struct dma_buf *ion_alloc(size_t len, unsigned int heap_id_mask,
#else
-static inline int ion_device_add_heap(struct ion_heap *heap)
+static inline int __ion_device_add_heap(struct ion_heap *heap,
+ struct module *owner)
{
return -ENODEV;
}