gpu: ion: Add scan for leaked userspace fds
The ION_IOC_MAP/ION_IOC_SHARE ioctls take a reference to
an ion buffer. If the fd generated by the ioctl is never
closed, the buffer will never be freed. This is a pain to
track down in userspace. Add a debugfs entry to do a scan
of all the buffers and check which ones have no handle
associated with them. If a buffer has no handle associated
with it, that means the last reference is an fd. There
isn't a good way to directly associate an fd with a buffer,
but this can at least be a starting point for tracking down
leaks.
Change-Id: I64eedd6a30d8298b2aa75532c7fad506542910f6
Signed-off-by: Laura Abbott <lauraa@codeaurora.org>
diff --git a/drivers/gpu/ion/ion.c b/drivers/gpu/ion/ion.c
index 4248d20..60bc276 100644
--- a/drivers/gpu/ion/ion.c
+++ b/drivers/gpu/ion/ion.c
@@ -1337,6 +1337,83 @@
mutex_unlock(&dev->lock);
}
+static int ion_debug_leak_show(struct seq_file *s, void *unused)
+{
+ struct ion_device *dev = s->private;
+ struct rb_node *n;
+ struct rb_node *n2;
+
+ /* mark all buffers as 1 */
+ seq_printf(s, "%16.s %16.s %16.s %16.s\n", "buffer", "heap", "size",
+ "ref cnt");
+ mutex_lock(&dev->lock);
+ for (n = rb_first(&dev->buffers); n; n = rb_next(n)) {
+ struct ion_buffer *buf = rb_entry(n, struct ion_buffer,
+ node);
+
+ buf->marked = 1;
+ }
+
+ /* now see which buffers we can access */
+ for (n = rb_first(&dev->kernel_clients); n; n = rb_next(n)) {
+ struct ion_client *client = rb_entry(n, struct ion_client,
+ node);
+
+ mutex_lock(&client->lock);
+ for (n2 = rb_first(&client->handles); n2; n2 = rb_next(n2)) {
+ struct ion_handle *handle = rb_entry(n2,
+ struct ion_handle, node);
+
+ handle->buffer->marked = 0;
+
+ }
+ mutex_unlock(&client->lock);
+
+ }
+
+ for (n = rb_first(&dev->user_clients); n; n = rb_next(n)) {
+ struct ion_client *client = rb_entry(n, struct ion_client,
+ node);
+
+ mutex_lock(&client->lock);
+ for (n2 = rb_first(&client->handles); n2; n2 = rb_next(n2)) {
+ struct ion_handle *handle = rb_entry(n2,
+ struct ion_handle, node);
+
+ handle->buffer->marked = 0;
+
+ }
+ mutex_unlock(&client->lock);
+
+ }
+ /* And anyone still marked as a 1 means a leaked handle somewhere */
+ for (n = rb_first(&dev->buffers); n; n = rb_next(n)) {
+ struct ion_buffer *buf = rb_entry(n, struct ion_buffer,
+ node);
+
+ if (buf->marked == 1)
+ seq_printf(s, "%16.x %16.s %16.x %16.d\n",
+ (int)buf, buf->heap->name, buf->size,
+ atomic_read(&buf->ref.refcount));
+ }
+ mutex_unlock(&dev->lock);
+ return 0;
+}
+
+static int ion_debug_leak_open(struct inode *inode, struct file *file)
+{
+ return single_open(file, ion_debug_leak_show, inode->i_private);
+}
+
+static const struct file_operations debug_leak_fops = {
+ .open = ion_debug_leak_open,
+ .read = seq_read,
+ .llseek = seq_lseek,
+ .release = single_release,
+};
+
+
+
struct ion_device *ion_device_create(long (*custom_ioctl)
(struct ion_client *client,
unsigned int cmd,
@@ -1369,6 +1446,8 @@
idev->heaps = RB_ROOT;
idev->user_clients = RB_ROOT;
idev->kernel_clients = RB_ROOT;
+ debugfs_create_file("check_leaked_fds", 0664, idev->debug_root, idev,
+ &debug_leak_fops);
return idev;
}