📄 mmap.c
字号:
/* same locking considerations of the above case */ area->vm_start = end; lock_vma_mappings(area); spin_lock(&mm->page_table_lock); } else { /* Unmapping a hole: area->vm_start < addr <= end < area->vm_end */ /* Add end mapping -- leave beginning for below */ mpnt = extra; extra = NULL; mpnt->vm_mm = area->vm_mm; mpnt->vm_start = end; mpnt->vm_end = area->vm_end; mpnt->vm_page_prot = area->vm_page_prot; mpnt->vm_flags = area->vm_flags; mpnt->vm_raend = 0; mpnt->vm_ops = area->vm_ops; mpnt->vm_pgoff = area->vm_pgoff + ((end - area->vm_start) >> PAGE_SHIFT); mpnt->vm_file = area->vm_file; mpnt->vm_private_data = area->vm_private_data; if (mpnt->vm_file) get_file(mpnt->vm_file); if (mpnt->vm_ops && mpnt->vm_ops->open) mpnt->vm_ops->open(mpnt); area->vm_end = addr; /* Truncate area */ /* Because mpnt->vm_file == area->vm_file this locks * things correctly. */ lock_vma_mappings(area); spin_lock(&mm->page_table_lock); __insert_vm_struct(mm, mpnt); } __insert_vm_struct(mm, area); spin_unlock(&mm->page_table_lock); unlock_vma_mappings(area); return extra;}/* * Try to free as many page directory entries as we can, * without having to work very hard at actually scanning * the page tables themselves. * * Right now we try to free page tables if we have a nice * PGDIR-aligned area that got free'd up. We could be more * granular if we want to, but this is fast and simple, * and covers the bad cases. * * "prev", if it exists, points to a vma before the one * we just free'd - but there's no telling how much before. */static void free_pgtables(struct mm_struct * mm, struct vm_area_struct *prev, unsigned long start, unsigned long end){ unsigned long first = start & PGDIR_MASK; unsigned long last = end + PGDIR_SIZE - 1; unsigned long start_index, end_index; if (!prev) { prev = mm->mmap; if (!prev) goto no_mmaps; if (prev->vm_end > start) { if (last > prev->vm_start) last = prev->vm_start; goto no_mmaps; } } for (;;) { struct vm_area_struct *next = prev->vm_next; if (next) { if (next->vm_start < start) { prev = next; continue; } if (last > next->vm_start) last = next->vm_start; } if (prev->vm_end > first) first = prev->vm_end + PGDIR_SIZE - 1; break; }no_mmaps: /* * If the PGD bits are not consecutive in the virtual address, the * old method of shifting the VA >> by PGDIR_SHIFT doesn't work. */ start_index = pgd_index(first); if (start_index < FIRST_USER_PGD_NR) start_index = FIRST_USER_PGD_NR; end_index = pgd_index(last); if (end_index > start_index) { clear_page_tables(mm, start_index, end_index - start_index); flush_tlb_pgtables(mm, first & PGDIR_MASK, last & PGDIR_MASK); }}/* Munmap is split into 2 main parts -- this part which finds * what needs doing, and the areas themselves, which do the * work. This now handles partial unmappings. * Jeremy Fitzhardine <jeremy@sw.oz.au> */int do_munmap(struct mm_struct *mm, unsigned long addr, size_t len){ struct vm_area_struct *mpnt, *prev, **npp, *free, *extra; if ((addr & ~PAGE_MASK) || addr > TASK_SIZE || len > TASK_SIZE-addr) return -EINVAL; if ((len = PAGE_ALIGN(len)) == 0) return -EINVAL; /* Check if this memory area is ok - put it on the temporary * list if so.. The checks here are pretty simple -- * every area affected in some way (by any overlap) is put * on the list. If nothing is put on, nothing is affected. */ mpnt = find_vma_prev(mm, addr, &prev); if (!mpnt) return 0; /* we have addr < mpnt->vm_end */ if (mpnt->vm_start >= addr+len) return 0; /* If we'll make "hole", check the vm areas limit */ if ((mpnt->vm_start < addr && mpnt->vm_end > addr+len) && mm->map_count >= MAX_MAP_COUNT) return -ENOMEM; /* * We may need one additional vma to fix up the mappings ... * and this is the last chance for an easy error exit. */ extra = kmem_cache_alloc(vm_area_cachep, SLAB_KERNEL); if (!extra) return -ENOMEM; npp = (prev ? &prev->vm_next : &mm->mmap); free = NULL; spin_lock(&mm->page_table_lock); for ( ; mpnt && mpnt->vm_start < addr+len; mpnt = *npp) { *npp = mpnt->vm_next; mpnt->vm_next = free; free = mpnt; rb_erase(&mpnt->vm_rb, &mm->mm_rb); } mm->mmap_cache = NULL; /* Kill the cache. */ spin_unlock(&mm->page_table_lock); /* Ok - we have the memory areas we should free on the 'free' list, * so release them, and unmap the page range.. * If the one of the segments is only being partially unmapped, * it will put new vm_area_struct(s) into the address space. * In that case we have to be careful with VM_DENYWRITE. */ while ((mpnt = free) != NULL) { unsigned long st, end, size; struct file *file = NULL; free = free->vm_next; st = addr < mpnt->vm_start ? mpnt->vm_start : addr; end = addr+len; end = end > mpnt->vm_end ? mpnt->vm_end : end; size = end - st; if (mpnt->vm_flags & VM_DENYWRITE && (st != mpnt->vm_start || end != mpnt->vm_end) && (file = mpnt->vm_file) != NULL) { atomic_dec(&file->f_dentry->d_inode->i_writecount); } remove_shared_vm_struct(mpnt); mm->map_count--; zap_page_range(mm, st, size); /* * Fix the mapping, and free the old area if it wasn't reused. */ extra = unmap_fixup(mm, mpnt, st, size, extra); if (file) atomic_inc(&file->f_dentry->d_inode->i_writecount); } validate_mm(mm); /* Release the extra vma struct if it wasn't used */ if (extra) kmem_cache_free(vm_area_cachep, extra); free_pgtables(mm, prev, addr, addr+len); return 0;}asmlinkage long sys_munmap(unsigned long addr, size_t len){ int ret; struct mm_struct *mm = current->mm; down_write(&mm->mmap_sem); ret = do_munmap(mm, addr, len); up_write(&mm->mmap_sem); return ret;}/* * this is really a simplified "do_mmap". it only handles * anonymous maps. eventually we may be able to do some * brk-specific accounting here. */unsigned long do_brk(unsigned long addr, unsigned long len){ struct mm_struct * mm = current->mm; struct vm_area_struct * vma, * prev; unsigned long flags; rb_node_t ** rb_link, * rb_parent; len = PAGE_ALIGN(len); if (!len) return addr; /* * mlock MCL_FUTURE? */ if (mm->def_flags & VM_LOCKED) { unsigned long locked = mm->locked_vm << PAGE_SHIFT; locked += len; if (locked > current->rlim[RLIMIT_MEMLOCK].rlim_cur) return -EAGAIN; } /* * Clear old maps. this also does some error checking for us */ munmap_back: vma = find_vma_prepare(mm, addr, &prev, &rb_link, &rb_parent); if (vma && vma->vm_start < addr + len) { if (do_munmap(mm, addr, len)) return -ENOMEM; goto munmap_back; } /* Check against address space limits *after* clearing old maps... */ if ((mm->total_vm << PAGE_SHIFT) + len > current->rlim[RLIMIT_AS].rlim_cur) return -ENOMEM; if (mm->map_count > MAX_MAP_COUNT) return -ENOMEM; if (!vm_enough_memory(len >> PAGE_SHIFT)) return -ENOMEM; flags = calc_vm_flags(PROT_READ|PROT_WRITE|PROT_EXEC, MAP_FIXED|MAP_PRIVATE) | mm->def_flags; flags |= VM_MAYREAD | VM_MAYWRITE | VM_MAYEXEC; /* Can we just expand an old anonymous mapping? */ if (rb_parent && vma_merge(mm, prev, rb_parent, addr, addr + len, flags)) goto out; /* * create a vma struct for an anonymous mapping */ vma = kmem_cache_alloc(vm_area_cachep, SLAB_KERNEL); if (!vma) return -ENOMEM; vma->vm_mm = mm; vma->vm_start = addr; vma->vm_end = addr + len; vma->vm_flags = flags; vma->vm_page_prot = protection_map[flags & 0x0f]; vma->vm_ops = NULL; vma->vm_pgoff = 0; vma->vm_file = NULL; vma->vm_private_data = NULL; vma_link(mm, vma, prev, rb_link, rb_parent);out: mm->total_vm += len >> PAGE_SHIFT; if (flags & VM_LOCKED) { mm->locked_vm += len >> PAGE_SHIFT; make_pages_present(addr, addr + len); } return addr;}/* Build the RB tree corresponding to the VMA list. */void build_mmap_rb(struct mm_struct * mm){ struct vm_area_struct * vma; rb_node_t ** rb_link, * rb_parent; mm->mm_rb = RB_ROOT; rb_link = &mm->mm_rb.rb_node; rb_parent = NULL; for (vma = mm->mmap; vma; vma = vma->vm_next) { __vma_link_rb(mm, vma, rb_link, rb_parent); rb_parent = &vma->vm_rb; rb_link = &rb_parent->rb_right; }}/* Release all mmaps. */void exit_mmap(struct mm_struct * mm){ mmu_gather_t *tlb; struct vm_area_struct * mpnt; release_segments(mm); spin_lock(&mm->page_table_lock); tlb = tlb_gather_mmu(mm); flush_cache_mm(mm); mpnt = mm->mmap; while (mpnt) { unsigned long start = mpnt->vm_start; unsigned long end = mpnt->vm_end; mm->map_count--; remove_shared_vm_struct(mpnt); unmap_page_range(tlb, mm, start, end); mpnt = mpnt->vm_next; } /* This is just debugging */ if (mm->map_count) BUG(); tlb_finish_mmu(tlb, 0, TASK_SIZE); mpnt = mm->mmap; mm->mmap = mm->mmap_cache = NULL; mm->mm_rb = RB_ROOT; mm->rss = 0; mm->total_vm = 0; mm->locked_vm = 0; spin_unlock(&mm->page_table_lock); clear_page_tables(mm, FIRST_USER_PGD_NR, USER_PTRS_PER_PGD); /* * Walk the list again, actually closing and freeing it * without holding any MM locks. */ while (mpnt) { struct vm_area_struct * next = mpnt->vm_next; if (mpnt->vm_ops) { if (mpnt->vm_ops->close) mpnt->vm_ops->close(mpnt); } if (mpnt->vm_file) fput(mpnt->vm_file); kmem_cache_free(vm_area_cachep, mpnt); mpnt = next; }}/* Insert vm structure into process list sorted by address * and into the inode's i_mmap ring. If vm_file is non-NULL * then the i_shared_lock must be held here. */void __insert_vm_struct(struct mm_struct * mm, struct vm_area_struct * vma){ struct vm_area_struct * __vma, * prev; rb_node_t ** rb_link, * rb_parent; __vma = find_vma_prepare(mm, vma->vm_start, &prev, &rb_link, &rb_parent); if (__vma && __vma->vm_start < vma->vm_end) BUG(); __vma_link(mm, vma, prev, rb_link, rb_parent); mm->map_count++; validate_mm(mm);}void insert_vm_struct(struct mm_struct * mm, struct vm_area_struct * vma){ struct vm_area_struct * __vma, * prev; rb_node_t ** rb_link, * rb_parent; __vma = find_vma_prepare(mm, vma->vm_start, &prev, &rb_link, &rb_parent); if (__vma && __vma->vm_start < vma->vm_end) BUG(); vma_link(mm, vma, prev, rb_link, rb_parent); validate_mm(mm);}
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -