[RFC 1/6] fs: add installed and uninstalled file_operations
These optional file_operations notify a file implementation when it is installed or uninstalled from a task's fd table. This can be used for accounting of file-backed shared resources like dma-buf. This involves some changes to the __fd_install() and __close_fd() APIs to actually pass along the responsible task_struct. These are low-level APIs with only two in-tree callers, both adjusted in this patch. Signed-off-by: Greg Hackmann Signed-off-by: Ruchi Kandoi --- drivers/android/binder.c | 4 ++-- fs/file.c| 38 +- fs/open.c| 2 +- include/linux/fdtable.h | 4 ++-- include/linux/fs.h | 2 ++ 5 files changed, 36 insertions(+), 14 deletions(-) diff --git a/drivers/android/binder.c b/drivers/android/binder.c index 562af94..0bb174e 100644 --- a/drivers/android/binder.c +++ b/drivers/android/binder.c @@ -398,7 +398,7 @@ static void task_fd_install( struct binder_proc *proc, unsigned int fd, struct file *file) { if (proc->files) - __fd_install(proc->files, fd, file); + __fd_install(proc->tsk, fd, file); } /* @@ -411,7 +411,7 @@ static long task_close_fd(struct binder_proc *proc, unsigned int fd) if (proc->files == NULL) return -ESRCH; - retval = __close_fd(proc->files, fd); + retval = __close_fd(proc->tsk, fd); /* can't restart close syscall because file table entry was cleared */ if (unlikely(retval == -ERESTARTSYS || retval == -ERESTARTNOINTR || diff --git a/fs/file.c b/fs/file.c index 69d6990..19c5fad 100644 --- a/fs/file.c +++ b/fs/file.c @@ -282,6 +282,24 @@ static unsigned int count_open_files(struct fdtable *fdt) return i; } +static inline void fdt_install(struct fdtable *fdt, int fd, struct file *file, + struct task_struct *task) +{ + if (file->f_op->installed) + file->f_op->installed(file, task); + rcu_assign_pointer(fdt->fd[fd], file); +} + +static inline void fdt_uninstall(struct fdtable *fdt, int fd, + struct task_struct *task) +{ + struct file *old_file = fdt->fd[fd]; + + if (old_file->f_op->uninstalled) + old_file->f_op->uninstalled(old_file, task); + rcu_assign_pointer(fdt->fd[fd], NULL); +} + /* * Allocate a new files structure and copy contents from the * passed in files structure. @@ -543,7 +561,7 @@ int __alloc_fd(struct files_struct *files, /* Sanity check */ if (rcu_access_pointer(fdt->fd[fd]) != NULL) { printk(KERN_WARNING "alloc_fd: slot %d not NULL!\n", fd); - rcu_assign_pointer(fdt->fd[fd], NULL); + fdt_uninstall(fdt, fd, current); } #endif @@ -601,10 +619,11 @@ EXPORT_SYMBOL(put_unused_fd); * fd_install() instead. */ -void __fd_install(struct files_struct *files, unsigned int fd, +void __fd_install(struct task_struct *task, unsigned int fd, struct file *file) { struct fdtable *fdt; + struct files_struct *files = task->files; might_sleep(); rcu_read_lock_sched(); @@ -618,13 +637,13 @@ void __fd_install(struct files_struct *files, unsigned int fd, smp_rmb(); fdt = rcu_dereference_sched(files->fdt); BUG_ON(fdt->fd[fd] != NULL); - rcu_assign_pointer(fdt->fd[fd], file); + fdt_install(fdt, fd, file, task); rcu_read_unlock_sched(); } void fd_install(unsigned int fd, struct file *file) { - __fd_install(current->files, fd, file); + __fd_install(current, fd, file); } EXPORT_SYMBOL(fd_install); @@ -632,10 +651,11 @@ EXPORT_SYMBOL(fd_install); /* * The same warnings as for __alloc_fd()/__fd_install() apply here... */ -int __close_fd(struct files_struct *files, unsigned fd) +int __close_fd(struct task_struct *task, unsigned fd) { struct file *file; struct fdtable *fdt; + struct files_struct *files = task->files; spin_lock(&files->file_lock); fdt = files_fdtable(files); @@ -644,7 +664,7 @@ int __close_fd(struct files_struct *files, unsigned fd) file = fdt->fd[fd]; if (!file) goto out_unlock; - rcu_assign_pointer(fdt->fd[fd], NULL); + fdt_uninstall(fdt, fd, task); __clear_close_on_exec(fd, fdt); __put_unused_fd(files, fd); spin_unlock(&files->file_lock); @@ -679,7 +699,7 @@ void do_close_on_exec(struct files_struct *files) file = fdt->fd[fd]; if (!file) continue; - rcu_assign_pointer(fdt->fd[fd], NULL); + fdt_uninstall(fdt, fd, current); __put_unused_fd(files, fd); spin_unlock(&files->fi
[RFC 0/6] Module for tracking/accounting shared memory buffers
This patchstack introduces a new "memtrack" module for tracking and accounting memory exported to userspace as shared buffers, like dma-buf fds or GEM handles. Any process holding a reference to these buffers will keep the kernel from reclaiming its backing pages. mm counters don't provide a complete picture of these allocations, since they only account for pages that are mapped into a process's address space. This problem is especially bad for systems like Android that use dma-buf fds to share graphics and multimedia buffers between processes: these allocations are often large, have complex sharing patterns, and are rarely mapped into every process that holds a reference to them. memtrack maintains a per-process list of shared buffer references, which is exported to userspace as /proc/[pid]/memtrack. Buffers can be optionally "tagged" with a short string: for example, Android userspace would use this tag to identify whether buffers were allocated on behalf of the camera stack, GL, etc. memtrack also exports the VMAs associated with these buffers so that pages already included in the process's mm counters aren't double-counted. Shared-buffer allocators can hook into memtrack by embedding struct memtrack_buffer in their buffer metadata, calling memtrack_buffer_{init,remove} at buffer allocation and free time, and memtrack_buffer_{install,uninstall} when a userspace process takes or drops a reference to the buffer. For fd-backed buffers like dma-bufs, hooks in fdtable.c and fork.c automatically notify memtrack when references are added or removed from a process's fd table. This patchstack adds memtrack hooks into dma-buf and ion. If there's upstream interest in memtrack, it can be extended to other memory allocators as well, such as GEM implementations. Greg Hackmann (1): drivers: staging: ion: add ION_IOC_TAG ioctl Ruchi Kandoi (5): fs: add installed and uninstalled file_operations drivers: misc: add memtrack dma-buf: add memtrack support memtrack: Adds the accounting to keep track of all mmaped/unmapped pages. memtrack: Add memtrack accounting for forked processes. drivers/android/binder.c| 4 +- drivers/dma-buf/dma-buf.c | 37 +++ drivers/misc/Kconfig| 16 + drivers/misc/Makefile | 1 + drivers/misc/memtrack.c | 516 drivers/staging/android/ion/ion-ioctl.c | 17 ++ drivers/staging/android/ion/ion.c | 60 +++- drivers/staging/android/ion/ion_priv.h | 2 + drivers/staging/android/uapi/ion.h | 25 ++ fs/file.c | 38 ++- fs/open.c | 2 +- fs/proc/base.c | 4 + include/linux/dma-buf.h | 5 + include/linux/fdtable.h | 4 +- include/linux/fs.h | 2 + include/linux/memtrack.h| 130 include/linux/mm.h | 3 + include/linux/sched.h | 3 + kernel/fork.c | 23 +- 19 files changed, 875 insertions(+), 17 deletions(-) create mode 100644 drivers/misc/memtrack.c create mode 100644 include/linux/memtrack.h -- 2.8.0.rc3.226.g39d4020 ___ devel mailing list de...@linuxdriverproject.org http://driverdev.linuxdriverproject.org/mailman/listinfo/driverdev-devel
[RFC 5/6] memtrack: Add memtrack accounting for forked processes.
When a process is forked, all the buffers are shared with the forked process too. Adds the functionality to add memtrack accounting for the forked processes. Forked process gets a copy of the mapped pages of the parent process. This patch makes sure that the new mapped pages are attributed to the child process instead of the parent. Signed-off-by: Ruchi Kandoi --- drivers/misc/memtrack.c | 45 +++ drivers/staging/android/ion/ion.c | 45 +-- include/linux/memtrack.h | 19 +++-- include/linux/mm.h| 3 +++ kernel/fork.c | 19 +++-- 5 files changed, 117 insertions(+), 14 deletions(-) diff --git a/drivers/misc/memtrack.c b/drivers/misc/memtrack.c index 4b2d17f..fa2601a 100644 --- a/drivers/misc/memtrack.c +++ b/drivers/misc/memtrack.c @@ -204,12 +204,13 @@ EXPORT_SYMBOL(memtrack_buffer_uninstall); * @buffer: the buffer's memtrack entry * * @vma: vma being opened + * @task: task which mapped the pages */ void memtrack_buffer_vm_open(struct memtrack_buffer *buffer, - const struct vm_area_struct *vma) + const struct vm_area_struct *vma, struct task_struct *task) { unsigned long flags; - struct task_struct *leader = current->group_leader; + struct task_struct *leader = task->group_leader; struct memtrack_vma_list *vma_list; vma_list = kmalloc(sizeof(*vma_list), GFP_KERNEL); @@ -228,12 +229,13 @@ EXPORT_SYMBOL(memtrack_buffer_vm_open); * * @buffer: the buffer's memtrack entry * @vma: the vma being closed + * @task: task that mmaped the pages */ void memtrack_buffer_vm_close(struct memtrack_buffer *buffer, - const struct vm_area_struct *vma) + const struct vm_area_struct *vma, struct task_struct *task) { unsigned long flags; - struct task_struct *leader = current->group_leader; + struct task_struct *leader = task->group_leader; write_lock_irqsave(&leader->memtrack_lock, flags); memtrack_buffer_vm_close_locked(&leader->memtrack_rb, buffer, vma); @@ -241,6 +243,41 @@ void memtrack_buffer_vm_close(struct memtrack_buffer *buffer, } EXPORT_SYMBOL(memtrack_buffer_vm_close); +/** + * memtrack_buffer_install_fork - Install all parent's handles into + * child. + * + * @parent: parent task + * @child: child task + */ +void memtrack_buffer_install_fork(struct task_struct *parent, + struct task_struct *child) +{ + struct task_struct *leader, *leader_child; + struct rb_root *root; + struct rb_node *node; + unsigned long flags; + + if (!child || !parent) + return; + + leader = parent->group_leader; + leader_child = child->group_leader; + write_lock_irqsave(&leader->memtrack_lock, flags); + root = &leader->memtrack_rb; + node = rb_first(root); + while (node) { + struct memtrack_handle *handle; + + handle = rb_entry(node, struct memtrack_handle, node); + memtrack_buffer_install_locked(&leader_child->memtrack_rb, + handle->buffer); + node = rb_next(node); + } + write_unlock_irqrestore(&leader->memtrack_lock, flags); +} +EXPORT_SYMBOL(memtrack_buffer_install_fork); + static int memtrack_id_alloc(struct memtrack_buffer *buffer) { int ret; diff --git a/drivers/staging/android/ion/ion.c b/drivers/staging/android/ion/ion.c index c32d520..451aa0f 100644 --- a/drivers/staging/android/ion/ion.c +++ b/drivers/staging/android/ion/ion.c @@ -906,7 +906,7 @@ static void ion_vm_open(struct vm_area_struct *vma) list_add(&vma_list->list, &buffer->vmas); mutex_unlock(&buffer->lock); pr_debug("%s: adding %p\n", __func__, vma); - memtrack_buffer_vm_open(&buffer->memtrack_buffer, vma); + memtrack_buffer_vm_open(&buffer->memtrack_buffer, vma, current); } static void ion_vm_close(struct vm_area_struct *vma) @@ -925,13 +925,51 @@ static void ion_vm_close(struct vm_area_struct *vma) break; } mutex_unlock(&buffer->lock); - memtrack_buffer_vm_close(&buffer->memtrack_buffer, vma); + memtrack_buffer_vm_close(&buffer->memtrack_buffer, vma, current); +} + +void vm_track(struct vm_area_struct *vma, struct task_struct *task) +{ + struct ion_buffer *buffer = vma->vm_private_data; + + memtrack_buffer_vm_open(&buffer->memtrack_buffer, vma, task); +} + +void vm_untrack(struct vm_area_struct *vma, struct task_struct *task) +{ + struct ion_buffer *buffer = vma->vm_private_data; + + memtrack_buffer_vm_close(&buffer->memtrack_buffer, vma, task); } static const struct vm_operations_struct