Messages in this thread Patch in this message | | | Date | Sat, 3 Apr 2004 03:26:12 +0200 | From | Andrea Arcangeli <> | Subject | anon-vma (and now filebacked-mappings too) mprotect vma merging [Re: 2.6.5-rc2-aa vma merging] |
| |
On Fri, Apr 02, 2004 at 05:35:22PM +0200, Andrea Arcangeli wrote: > On Fri, Apr 02, 2004 at 12:34:29PM +0100, Hugh Dickins wrote: > > Sorry to be boring, Andrea, but 2.6.5-rc3-aa2 is now out, and you > > have still not fixed the vma merging issue: I don't believe you can. > > Hugh, it will get fixed perfectly, so please give me the time to fix > swap suspend, then I can care about minor issues.
here we go with the mprotect merging, try this on top of 2.6.5-rc3-aa2.
Your testcase output:
[..] 981140 kB 981144 kB 981148 kB 981152 kB 981156 kB 981160 kB 981164 kB 981168 kB 981172 kB 981176 kB 981180 kB 981184 kB 981188 kB 981192 kB 981196 kB
hanging now in a oom condition (it's 1G box) because I had no swap (looks like the VM is totally broken w/o swap, nothing gets killed in a obvious oom scenario that would be resolved by the 2.4 vm in a second [I mean, without swap!], anyways I could break it with C^c after waiting half a minute with some faith in the vm not being completely locked up w/o swap).
Now I did mkswap /dev/sda1 and swapon -a, so I'll try again now with swap (it didn't get swap because a previous swap-restore crashed).
2095720 kB 2095724 kB 2095728 kB 2095732 kB 2095736 kB 2095740 kB 2095744 kB 2095748 kB 2095752 kB 2095756 kB 2095760 kB 2095764 kB 2095768 kB 2095772 kB 2095776 kB 2095780 kB 2095784 kB 2095788 kB 2095792 kB 2095796 kB 2095800 kB 2095804 kB 2095808 kB 2095812 kB mmap: Cannot allocate memory Type <Return> to exit xeon:~ #
it worked better this time after some swap, the oom was because the address space was finished.
I run it another time stopping it in the middle to verify the number of mappings and the merging:
[..] 1315 pts/0 T 0:03 ./mprotect-merge 1316 pts/0 R 0:00 ps x xeon:~ # cat /proc/1315/maps 08048000-08049000 r-xp 00000000 08:02 611014 /root/mprotect-merge 08049000-0804a000 rw-p 00000000 08:02 611014 /root/mprotect-merge 40000000-40014000 r-xp 00000000 08:02 443256 /lib/ld-2.3.2.so 40014000-40015000 rw-p 00014000 08:02 443256 /lib/ld-2.3.2.so 40015000-40016000 r--p 40015000 00:00 0 40016000-40017000 rw-p 40016000 00:00 0 40017000-40028000 r--p 40017000 00:00 0 40028000-40157000 r-xp 00000000 08:02 443262 /lib/libc.so.6 40157000-4015b000 rw-p 0012f000 08:02 443262 /lib/libc.so.6 4015b000-4015e000 rw-p 4015b000 00:00 0 4015e000-6f80d000 r--p 4015e000 00:00 0 ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ 1 vma for all bfffd000-c0000000 rwxp bfffd000 00:00 0 ffffe000-fffff000 ---p 00000000 00:00 0 xeon:~ #
here the last seconds of swapout before I press the key to exit after the address space is finished:
1 3 1101252 9860 740 5280 0 33224 0 33228 2231 2193 3 8 8 80 4 1 1120564 4360 744 5368 340 18960 476 19164 1730 3866 2 8 44 45 0 0 1143692 6220 588 4544 88 23912 156 23912 2820 2160 1 5 54 41 0 0 1143692 6296 588 4544 0 0 0 0 1064 23 0 0 100 0 0 0 1143692 6296 596 4536 0 0 0 32 1008 37 0 0 100 0 0 0 1143692 6296 596 4536 0 0 0 0 1004 27 0 0 100 0 0 0 1143692 6308 596 4536 0 0 0 0 1005 29 0 0 100 0 0 0 1143692 6308 596 4536 0 0 0 0 1004 31 0 0 100 0 0 0 1143692 6308 596 4536 0 0 0 0 1005 29 0 0 100 0 0 0 1143692 6308 596 4536 0 0 0 0 1004 31 0 0 100 0 0 0 1143692 6388 596 4536 0 0 0 0 1005 33 0 0 100 0 0 0 1143692 6388 596 4536 0 0 0 0 1004 29 0 0 100 0 0 0 13268 998296 596 4476 132 0 152 0 1023 59 0 7 92 1 0 0 13268 998296 596 4476 0 0 0 0 1004 25 0 0 100 0 0 0 13268 998336 612 4444 16 0 56 0 1013 51 0 0 99 1
Ok now that the basic testing is over and it's all running successful on the test box I'll load the thing on my desktop. Note: I will keep this at the very end of my patchset (if you see it's also incremental with prio-tree) because this is an add-on and it had zero testing yet, kernel is perfectly stable even without this (partly new for file mappings) merging-feature. I encountered no bugs while writing this. However I did some cleanup in the process of creating some better helper API to use in common with mmap.c.
Most important: now file mappings gets merged perfectly through mprotect too (still untested though, but if anon-vma merging works file merging will most certainly too), that has never been possible in any linux yet (I believe this is more useful than the anon-vma merging infact, I know some big app using mprotect on filebacked mappings).
The spinlocking is cleaned up as well, compared to mainline, now only the mmap_sem semaphore (in write mode) protects the vma merging, no need of page_table_lock spinlock anymore except while walking the pagetables.
--- x/include/linux/mm.h.~1~ 2004-04-02 20:37:14.000000000 +0200 +++ x/include/linux/mm.h 2004-04-03 03:02:07.362304072 +0200 @@ -751,6 +751,7 @@ extern int do_munmap(struct mm_struct *, extern unsigned long do_brk(unsigned long, unsigned long); +/* vma merging helpers */ static inline void __vma_unlink(struct mm_struct *mm, struct vm_area_struct *vma, struct vm_area_struct *prev) @@ -761,14 +762,48 @@ __vma_unlink(struct mm_struct *mm, struc mm->mmap_cache = prev; } -static inline int -can_vma_merge(struct vm_area_struct *vma, unsigned long vm_flags) +extern void __remove_shared_vm_struct(struct vm_area_struct *, struct inode *, + struct address_space *); + +/* + * If the vma has a ->close operation then the driver probably needs to release + * per-vma resources, so we don't attempt to merge those. + */ +#define VM_SPECIAL (VM_IO | VM_DONTCOPY | VM_DONTEXPAND | VM_RESERVED) + +static inline int is_mergeable_vma(struct vm_area_struct *vma, + struct file *file, + unsigned long vm_flags, + unsigned long pgoff, + anon_vma_t ** anon_vma_cache) { -#ifdef CONFIG_MMU - if (!vma->vm_file && vma->vm_flags == vm_flags) - return 1; -#endif - return 0; + if (vma->vm_ops && vma->vm_ops->close) + return 0; + if (vma->vm_file != file) + return 0; + if (vma->vm_pgoff != pgoff) + return 0; + if (vma->vm_private_data) + return 0; + if (vma->vm_flags != vm_flags) { + /* + * If the only difference between two adiacent + * vmas is the page protection we try to + * share the same anon_vma to maximize the + * merging in mprotect. + */ + if (anon_vma_cache && !*anon_vma_cache) + *anon_vma_cache = vma->anon_vma; + return 0; + } + return 1; +} + +static inline int is_mergeable_anon_vma(struct vm_area_struct *prev, + struct vm_area_struct *next) +{ + return ((!next->anon_vma || !prev->anon_vma) || + (next->anon_vma == prev->anon_vma)); } /* filemap.c */ --- x/mm/mmap.c.~1~ 2004-04-02 20:37:14.000000000 +0200 +++ x/mm/mmap.c 2004-04-03 02:59:15.959361280 +0200 @@ -74,7 +74,7 @@ EXPORT_SYMBOL(vm_committed_space); /* * Requires inode->i_mapping->i_shared_sem */ -static void +void __remove_shared_vm_struct(struct vm_area_struct *vma, struct inode *inode, struct address_space * mapping) { @@ -343,26 +343,6 @@ __insert_vm_struct(struct mm_struct * mm } /* - * If the vma has a ->close operation then the driver probably needs to release - * per-vma resources, so we don't attempt to merge those. - */ -#define VM_SPECIAL (VM_IO | VM_DONTCOPY | VM_DONTEXPAND | VM_RESERVED) - -static inline int is_mergeable_vma(struct vm_area_struct *vma, - struct file *file, unsigned long vm_flags) -{ - if (vma->vm_ops && vma->vm_ops->close) - return 0; - if (vma->vm_file != file) - return 0; - if (vma->vm_flags != vm_flags) - return 0; - if (vma->vm_private_data) - return 0; - return 1; -} - -/* * Return true if we can merge this (vm_flags,file,vm_pgoff,size) * in front of (at a lower virtual address and file offset than) the vma. * @@ -373,26 +353,25 @@ static inline int is_mergeable_vma(struc static int can_vma_merge_before(struct vm_area_struct *prev, struct vm_area_struct *vma, unsigned long vm_flags, - struct file *file, unsigned long vm_pgoff, unsigned long size) + struct file *file, unsigned long vm_pgoff, + anon_vma_t ** anon_vma_cache) { - if (is_mergeable_vma(vma, file, vm_flags)) - if (vma->vm_pgoff == vm_pgoff + size) { - if (prev) { - /* - * We can fill an hole only if the two - * anonymous mappings are queued in the same - * anon_vma, or if one of them is "direct" - * and it can be queued in the existing - * anon_vma. - * - * Must check this even if file != NULL - * for MAP_PRIVATE mappings. - */ - return ((!vma->anon_vma || !prev->anon_vma) || - (vma->anon_vma == prev->anon_vma)); - } - return 1; + if (is_mergeable_vma(vma, file, vm_flags, vm_pgoff, anon_vma_cache)) { + if (prev) { + /* + * We can fill an hole only if the two + * anonymous mappings are queued in the same + * anon_vma, or if one of them is "direct" + * and it can be queued in the existing + * anon_vma. + * + * Must check this even if file != NULL + * for MAP_PRIVATE mappings. + */ + return is_mergeable_anon_vma(prev, vma); } + return 1; + } return 0; } @@ -402,16 +381,11 @@ can_vma_merge_before(struct vm_area_stru */ static int can_vma_merge_after(struct vm_area_struct *vma, unsigned long vm_flags, - struct file *file, unsigned long vm_pgoff) + struct file *file, unsigned long vm_pgoff, + anon_vma_t ** anon_vma_cache) { - if (is_mergeable_vma(vma, file, vm_flags)) { - unsigned long vma_size; - - vma_size = (vma->vm_end - vma->vm_start) >> PAGE_SHIFT; - if (vma->vm_pgoff + vma_size == vm_pgoff) - return 1; - } - return 0; + unsigned long vma_size = (vma->vm_end - vma->vm_start) >> PAGE_SHIFT; + return is_mergeable_vma(vma, file, vm_flags, vm_pgoff - vma_size, anon_vma_cache); } /* @@ -420,14 +394,15 @@ can_vma_merge_after(struct vm_area_struc * both (it neatly fills a hole). */ static int vma_merge(struct mm_struct *mm, struct vm_area_struct *prev, - struct rb_node *rb_parent, unsigned long addr, - unsigned long end, unsigned long vm_flags, - struct file *file, unsigned long pgoff) + struct rb_node *rb_parent, unsigned long addr, + unsigned long end, unsigned long vm_flags, + struct file *file, unsigned long pgoff, + anon_vma_t ** anon_vma_cache) { - struct inode *inode = file ? file->f_dentry->d_inode : NULL; - struct address_space *mapping = file ? file->f_mapping : NULL; + struct inode *inode; + struct address_space *mapping; struct semaphore *i_shared_sem; - struct prio_tree_root *root = NULL; + struct prio_tree_root *root; /* * We later require that vma->vm_flags == vm_flags, so this tests @@ -436,14 +411,24 @@ static int vma_merge(struct mm_struct *m if (vm_flags & VM_SPECIAL) return 0; - i_shared_sem = file ? &file->f_mapping->i_shared_sem : NULL; + /* + * Only "root" and "inode" have to be NULL too if "file" is null, + * however mapping and i_shared_sem would cause gcc to warn about + * uninitialized usage so we set them to NULL too. + */ + inode = NULL; + root = NULL; + i_shared_sem = NULL; + mapping = NULL; + if (file) { + inode = file->f_dentry->d_inode; + mapping = file->f_mapping; + i_shared_sem = &mapping->i_shared_sem; - if (mapping) { if (vm_flags & VM_SHARED) { if (likely(!(vm_flags & VM_NONLINEAR))) root = &mapping->i_mmap_shared; - } - else + } else root = &mapping->i_mmap; } @@ -456,7 +441,7 @@ static int vma_merge(struct mm_struct *m * Can it merge with the predecessor? */ if (prev->vm_end == addr && - can_vma_merge_after(prev, vm_flags, file, pgoff)) { + can_vma_merge_after(prev, vm_flags, file, pgoff, anon_vma_cache)) { struct vm_area_struct *next; /* @@ -465,8 +450,9 @@ static int vma_merge(struct mm_struct *m next = prev->vm_next; /* next cannot change under us, it's serialized by the mmap_sem */ if (next && end == next->vm_start && - can_vma_merge_before(prev, next, vm_flags, file, - pgoff, (end - addr) >> PAGE_SHIFT)) { + can_vma_merge_before(prev, next, vm_flags, file, + pgoff + ((end - addr) >> PAGE_SHIFT), + anon_vma_cache)) { /* serialized by the mmap_sem */ __vma_unlink(mm, next, prev); @@ -516,10 +502,10 @@ static int vma_merge(struct mm_struct *m prev = prev->vm_next; if (prev) { merge_next: - if (!can_vma_merge_before(NULL, prev, vm_flags, file, - pgoff, (end - addr) >> PAGE_SHIFT)) - return 0; - if (end == prev->vm_start) { + if (end == prev->vm_start && + can_vma_merge_before(NULL, prev, vm_flags, file, + pgoff + ((end - addr) >> PAGE_SHIFT), + anon_vma_cache)) { if (file) down(i_shared_sem); anon_vma_lock(prev); @@ -551,6 +537,7 @@ unsigned long do_mmap_pgoff(struct file int error; struct rb_node ** rb_link, * rb_parent; unsigned long charged = 0; + anon_vma_t * anon_vma_cache; if (file) { if (!file->f_op || !file->f_op->mmap) @@ -686,9 +673,10 @@ munmap_back: } /* Can we just expand an old anonymous mapping? */ + anon_vma_cache = NULL; if (!file && !(vm_flags & VM_SHARED) && rb_parent) if (vma_merge(mm, prev, rb_parent, addr, addr + len, - vm_flags, NULL, pgoff)) + vm_flags, NULL, pgoff, &anon_vma_cache)) goto out; /* @@ -712,7 +700,6 @@ munmap_back: vma->vm_private_data = NULL; vma->vm_next = NULL; INIT_VMA_SHARED(vma); - vma->anon_vma = NULL; if (file) { error = -EINVAL; @@ -751,7 +738,9 @@ munmap_back: addr = vma->vm_start; if (!file || !rb_parent || !vma_merge(mm, prev, rb_parent, addr, - addr + len, vma->vm_flags, file, pgoff)) { + addr + len, vma->vm_flags, file, pgoff, + &anon_vma_cache)) { + vma->anon_vma = anon_vma_cache; vma_link(mm, vma, prev, rb_link, rb_parent); if (correct_wcount) atomic_inc(&inode->i_writecount); @@ -1409,6 +1398,7 @@ unsigned long do_brk(unsigned long addr, unsigned long flags; struct rb_node ** rb_link, * rb_parent; unsigned long pgoff; + anon_vma_t * anon_vma_cache; len = PAGE_ALIGN(len); if (!len) @@ -1454,8 +1444,9 @@ unsigned long do_brk(unsigned long addr, pgoff = addr >> PAGE_SHIFT; /* Can we just expand an old anonymous mapping? */ + anon_vma_cache = NULL; if (rb_parent && vma_merge(mm, prev, rb_parent, addr, addr + len, - flags, NULL, pgoff)) + flags, NULL, pgoff, &anon_vma_cache)) goto out; /* @@ -1477,7 +1468,7 @@ unsigned long do_brk(unsigned long addr, vma->vm_file = NULL; vma->vm_private_data = NULL; INIT_VMA_SHARED(vma); - vma->anon_vma = NULL; + vma->anon_vma = anon_vma_cache; vma_link(mm, vma, prev, rb_link, rb_parent); --- x/mm/fremap.c.~1~ 2004-04-02 20:37:14.000000000 +0200 +++ x/mm/fremap.c 2004-04-02 20:51:11.873652616 +0200 @@ -61,9 +61,8 @@ int install_page(struct mm_struct *mm, s pmd_t *pmd; pte_t pte_val; - spin_lock(&mm->page_table_lock); pgd = pgd_offset(mm, addr); - + spin_lock(&mm->page_table_lock); pmd = pmd_alloc(mm, pgd, addr); if (!pmd) goto err_unlock; @@ -103,9 +102,8 @@ int install_file_pte(struct mm_struct *m pmd_t *pmd; pte_t pte_val; - spin_lock(&mm->page_table_lock); pgd = pgd_offset(mm, addr); - + spin_lock(&mm->page_table_lock); pmd = pmd_alloc(mm, pgd, addr); if (!pmd) goto err_unlock; --- x/mm/mprotect.c.~1~ 2004-04-02 20:37:13.928039712 +0200 +++ x/mm/mprotect.c 2004-04-03 01:46:44.987809496 +0200 @@ -16,6 +16,8 @@ #include <linux/fs.h> #include <linux/highmem.h> #include <linux/security.h> +#include <linux/objrmap.h> +#include <linux/file.h> #include <asm/uaccess.h> #include <asm/pgalloc.h> @@ -107,7 +109,6 @@ change_protection(struct vm_area_struct return; } -#if VMA_MERGING_FIXUP /* * Try to merge a vma with the previous flag, return 1 if successful or 0 if it * was impossible. @@ -116,42 +117,174 @@ static int mprotect_attempt_merge(struct vm_area_struct *vma, struct vm_area_struct *prev, unsigned long end, int newflags) { - struct mm_struct * mm = vma->vm_mm; + unsigned long prev_pgoff; + struct file *file; + struct inode *inode; + struct address_space *mapping; + struct semaphore *i_shared_sem; + struct prio_tree_root *root; - if (!prev || !vma) + if (newflags & VM_SPECIAL) + return 0; + if (!prev) return 0; if (prev->vm_end != vma->vm_start) return 0; - if (!can_vma_merge(prev, newflags)) + + prev_pgoff = vma->vm_pgoff - ((prev->vm_end - prev->vm_start) >> PAGE_SHIFT); + file = vma->vm_file; + if (!is_mergeable_vma(prev, file, newflags, prev_pgoff, NULL)) return 0; - if (vma->vm_file || (vma->vm_flags & VM_SHARED)) + if (!is_mergeable_anon_vma(prev, vma)) return 0; /* + * Only "root" and "inode" have to be NULL too if "file" is null, + * however mapping and i_shared_sem would cause gcc to warn about + * uninitialized usage so we set them to NULL too. + */ + inode = NULL; + root = NULL; + i_shared_sem = NULL; + mapping = NULL; + if (file) { + inode = file->f_dentry->d_inode; + mapping = file->f_mapping; + i_shared_sem = &mapping->i_shared_sem; + + if (vma->vm_flags & VM_SHARED) { + if (likely(!(vma->vm_flags & VM_NONLINEAR))) + root = &mapping->i_mmap_shared; + } else + root = &mapping->i_mmap; + } + + /* * If the whole area changes to the protection of the previous one * we can just get rid of it. */ if (end == vma->vm_end) { - spin_lock(&mm->page_table_lock); - prev->vm_end = end; + struct mm_struct * mm = vma->vm_mm; + + /* serialized by the mmap_sem */ __vma_unlink(mm, vma, prev); - spin_unlock(&mm->page_table_lock); - kmem_cache_free(vm_area_cachep, vma); + if (file) + down(i_shared_sem); + __vma_modify(root, prev, prev->vm_start, + end, prev->vm_pgoff); + + __remove_shared_vm_struct(vma, inode, mapping); + if (file) + up(i_shared_sem); + + /* + * The anon_vma_lock is taken inside and + * we can race with the vm_end move on the right, + * that will not be a problem, moves on the right + * of vm_end are controlled races. + */ + anon_vma_merge(prev, vma); + + if (file) + fput(file); + mm->map_count--; + kmem_cache_free(vm_area_cachep, vma); return 1; } /* * Otherwise extend it. */ - spin_lock(&mm->page_table_lock); - prev->vm_end = end; - vma->vm_start = end; - spin_unlock(&mm->page_table_lock); + if (file) + down(i_shared_sem); + __vma_modify(root, prev, prev->vm_start, end, prev->vm_pgoff); + __vma_modify(root, vma, end, vma->vm_end, + vma->vm_pgoff + ((end - vma->vm_start) >> PAGE_SHIFT)); + if (file) + up(i_shared_sem); return 1; } -#endif + +static void +mprotect_attempt_merge_final(struct vm_area_struct *prev, + struct vm_area_struct *next) +{ + unsigned long next_pgoff; + struct file * file; + struct inode *inode; + struct address_space *mapping; + struct semaphore *i_shared_sem; + struct prio_tree_root *root; + struct mm_struct * mm; + unsigned int newflags; + + if (!next) + return; + if (prev->vm_end != next->vm_start) + return; + newflags = prev->vm_flags; + if (newflags & VM_SPECIAL) + return; + + next_pgoff = prev->vm_pgoff + ((prev->vm_end - prev->vm_start) >> PAGE_SHIFT); + file = prev->vm_file; + if (!is_mergeable_vma(next, file, newflags, next_pgoff, NULL)) + return; + if (!is_mergeable_anon_vma(prev, next)) + return; + + + /* + * Only "root" and "inode" have to be NULL too if "file" is null, + * however mapping and i_shared_sem would cause gcc to warn about + * uninitialized usage so we set them to NULL too. + */ + inode = NULL; + root = NULL; + i_shared_sem = NULL; + mapping = NULL; + if (file) { + inode = file->f_dentry->d_inode; + mapping = file->f_mapping; + i_shared_sem = &mapping->i_shared_sem; + + if (next->vm_flags & VM_SHARED) { + if (likely(!(next->vm_flags & VM_NONLINEAR))) + root = &mapping->i_mmap_shared; + } else + root = &mapping->i_mmap; + } + + mm = next->vm_mm; + + /* serialized by the mmap_sem */ + __vma_unlink(mm, next, prev); + + if (file) + down(i_shared_sem); + __vma_modify(root, prev, prev->vm_start, + next->vm_end, prev->vm_pgoff); + + __remove_shared_vm_struct(next, inode, mapping); + if (file) + up(i_shared_sem); + + /* + * The anon_vma_lock is taken inside and + * we can race with the vm_end move on the right, + * that will not be a problem, moves on the right + * of vm_end are controlled races. + */ + anon_vma_merge(prev, next); + + if (file) + fput(file); + + mm->map_count--; + kmem_cache_free(vm_area_cachep, next); +} static int mprotect_fixup(struct vm_area_struct *vma, struct vm_area_struct **pprev, @@ -187,7 +320,6 @@ mprotect_fixup(struct vm_area_struct *vm newprot = protection_map[newflags & 0xf]; if (start == vma->vm_start) { -#if VMA_MERGING_FIXUP /* * Try to merge with the previous vma. */ @@ -195,7 +327,6 @@ mprotect_fixup(struct vm_area_struct *vm vma = *pprev; goto success; } -#endif } else { error = split_vma(mm, vma, start, 1); if (error) @@ -213,13 +344,13 @@ mprotect_fixup(struct vm_area_struct *vm goto fail; } - spin_lock(&mm->page_table_lock); + /* + * vm_flags and vm_page_prot are protected by the mmap_sem + * hold in write mode. + */ vma->vm_flags = newflags; vma->vm_page_prot = newprot; - spin_unlock(&mm->page_table_lock); -#if VMA_MERGING_FIXUP success: -#endif change_protection(vma, start, end, newprot); return 0; @@ -321,19 +452,7 @@ sys_mprotect(unsigned long start, size_t } } -#if VMA_MERGING_FIXUP - if (next && prev->vm_end == next->vm_start && - can_vma_merge(next, prev->vm_flags) && - !prev->vm_file && !(prev->vm_flags & VM_SHARED)) { - spin_lock(&prev->vm_mm->page_table_lock); - prev->vm_end = next->vm_end; - __vma_unlink(prev->vm_mm, next, prev); - spin_unlock(&prev->vm_mm->page_table_lock); - - kmem_cache_free(vm_area_cachep, next); - prev->vm_mm->map_count--; - } -#endif + mprotect_attempt_merge_final(prev, next); out: up_write(¤t->mm->mmap_sem); return error; - To unsubscribe from this list: send the line "unsubscribe linux-kernel" in the body of a message to majordomo@vger.kernel.org More majordomo info at http://vger.kernel.org/majordomo-info.html Please read the FAQ at http://www.tux.org/lkml/
| |