--- include/linux/mm.h | 4 +- mm/mmap.c | 89 +++++++++++++++++++++++++++++++---------------------- 2 files changed, 55 insertions(+), 38 deletions(-) Index: linux-2.6/mm/mmap.c =================================================================== --- linux-2.6.orig/mm/mmap.c 2008-04-21 14:31:42.000000000 -0700 +++ linux-2.6/mm/mmap.c 2008-04-21 21:37:25.000000000 -0700 @@ -2241,29 +2241,33 @@ int install_special_mapping(struct mm_st static int cmp_rwsem(const void *a, const void *b) { - if (a == b) + struct rw_semaphore * const *pa = a; + struct rw_semaphore * const *pb = b; + struct rw_semaphore *va = *pa; + struct rw_semaphore *vb = *pb; + + if (va == vb) return 0; - if (a > b) + if (va > vb) return 1; return -1; } -static void acquire_locks(struct mm_struct *mm, - struct rw_semaphore **locks, int anon) +static void __scan_locks(struct mm_struct *mm, struct rw_semaphore **locks, + int anon, int lock) { struct vm_area_struct *vma; + struct rw_semaphore *last; int i = 0; for (vma = mm->mmap; vma; vma = vma->vm_next) { - struct address_space *mapping = vma->vm_file->f_mapping; - - if (mapping) { - if (!anon) - locks[i++] = &mapping->i_mmap_sem; - } else { - if (anon) + if (anon) { + if (vma->anon_vma) locks[i++] = &vma->anon_vma->sem; + } else { + if (vma->vm_file && vma->vm_file->f_mapping) + locks[i++] = &vma->vm_file->f_mapping->i_mmap_sem; } } @@ -2272,9 +2276,34 @@ static void acquire_locks(struct mm_stru sort(locks, i, sizeof(struct rw_semaphore *), cmp_rwsem, NULL); - while (i-- > 0) - down_read(locks[i]); + last = NULL; + while (i-- > 0) { + if (last != locks[i]) { + if (lock) + down_write(locks[i]); + else + up_write(locks[i]); + + last = locks[i]; + } + } +} + +static int scan_locks(struct mm_struct *mm, int lock) +{ + struct rw_semaphore **locks; + + locks = vmalloc(sizeof(struct rw_semaphore *) * mm->map_count); + if (!locks) + return -ENOMEM; + + __scan_locks(mm, locks, 0, lock); + __scan_locks(mm, locks, 1, lock); + + vfree(locks); + return 0; } + /* * This operation locks against the VM for all pte/vma/mm related * operations that could ever happen on a certain mm. This includes @@ -2284,36 +2313,24 @@ static void acquire_locks(struct mm_stru */ int mm_lock(struct mm_struct * mm) { - struct rw_semaphore **locks; + int rc; down_write(&mm->mmap_sem); - locks = vmalloc(sizeof(struct rw_semaphore *) * mm->map_count); - if (!locks) { - up_write(&mm->mmap_sem); - return -ENOMEM; - } - acquire_locks(mm, locks, 0); - acquire_locks(mm, locks, 1); + rc = scan_locks(mm, 1); + if (rc) + up_write(&mm->mmap_sem); - vfree(locks); - return 0; + return rc; } -void mm_unlock(struct mm_struct *mm) +int mm_unlock(struct mm_struct *mm) { - struct vm_area_struct *vma; + int rc; - for (vma = mm->mmap; vma; vma = vma->vm_next) { - struct address_space *mapping = vma->vm_file->f_mapping; - struct rw_semaphore *sem; - - if (mapping) - sem = &mapping->i_mmap_sem; - else - sem = &vma->anon_vma->sem; + rc = scan_locks(mm, 0); + if (!rc) + up_write(&mm->mmap_sem); - up_read(sem); - } - up_write(&mm->mmap_sem); + return rc; } Index: linux-2.6/include/linux/mm.h =================================================================== --- linux-2.6.orig/include/linux/mm.h 2008-04-21 21:27:32.000000000 -0700 +++ linux-2.6/include/linux/mm.h 2008-04-21 21:27:48.000000000 -0700 @@ -1055,13 +1055,13 @@ extern int install_special_mapping(struc * vmaske all mapping to lockout any scans of pages of this address space. * This can be used to effectively holding off reclaim from the address space. * - * mm_lock() can fail if there is not enough memory to store a pointer + * mm_lock/mm_unlock() can fail if there is not enough memory to store a pointer * array to all vmas. * * mm_lock and mm_unlock are expensive operations that may take a long time. */ extern int mm_lock(struct mm_struct *mm); -extern void mm_unlock(struct mm_struct *mm); +extern int mm_unlock(struct mm_struct *mm); extern unsigned long get_unmapped_area(struct file *, unsigned long, unsigned long, unsigned long, unsigned long);