rdlkbr 发表于 2016-05-19 17:36

关于线性区合并函数vma_merge中case4的疑问

case4的情况是当新的线性区不能与前面合并,但可以与后面合并,且新线性区的开始地址add < prev->vm_end,如下图:

这里case4合并后,按意思来理解,应该是将prev缩减x,然后next与new_area合并,但是vma_adjust实际代码却只是将next的空间向前扩展x,而不是到new_vm_area的vm_start(addr)
vma_merge
mm/mmap.c(linux kernel 2.6.11.7)
代码如下:
struct vm_area_struct *vma_merge(struct mm_struct *mm,
                        struct vm_area_struct *prev, unsigned long addr,
                        unsigned long end, unsigned long vm_flags,
                        struct anon_vma *anon_vma, struct file *file,
                        pgoff_t pgoff, struct mempolicy *policy)
{
         pgoff_t pglen = (end - addr) >> PAGE_SHIFT;
         struct vm_area_struct *area, *next;

if (vm_flags & VM_SPECIAL)
               return NULL;

         if (prev)
               next = prev->vm_next;
         else
               next = mm->mmap;
         area = next;
         if (next && next->vm_end == end)                /* cases 6, 7, 8 */
               next = next->vm_next;
      if (prev && prev->vm_end == addr &&
                     mpol_equal(vma_policy(prev), policy) &&
                        can_vma_merge_after(prev, vm_flags,
                                             anon_vma, file, pgoff)) {
            
                if (next && end == next->vm_start &&
                              mpol_equal(policy, vma_policy(next)) &&
                               can_vma_merge_before(next, vm_flags,
                                        anon_vma, file, pgoff+pglen) &&
                              is_mergeable_anon_vma(prev->anon_vma,
                                                      next->anon_vma)) {
                         /* cases 1, 6 */
                        vma_adjust(prev, prev->vm_start,
                              next->vm_end, prev->vm_pgoff, NULL);
               } else                                  /* cases 2, 5, 7 */
                     vma_adjust(prev, prev->vm_start,
                              end, prev->vm_pgoff, NULL);
                return prev;
      }

               if (next && end == next->vm_start &&
                        mpol_equal(policy, vma_policy(next)) &&
                        can_vma_merge_before(next, vm_flags,
                                        anon_vma, file, pgoff+pglen)) {
               if (prev && addr < prev->vm_end)      /* case 4 */
                        vma_adjust(prev, prev->vm_start,
                              addr, prev->vm_pgoff, NULL);
                else                                    /* cases 3, 8 */
                        vma_adjust(area, addr, next->vm_end,
                              next->vm_pgoff - pglen, NULL);
                return area;
      }

       return NULL;
}
vma_adjust
mm/mmap.c(linux kernel 2.6.11.7)
代码如下:
void vma_adjust(struct vm_area_struct *vma, unsigned long start,
        unsigned long end, pgoff_t pgoff, struct vm_area_struct *insert)
{
        struct mm_struct *mm = vma->vm_mm;
        struct vm_area_struct *next = vma->vm_next;
        struct vm_area_struct *importer = NULL;
        struct address_space *mapping = NULL;
        struct prio_tree_root *root = NULL;
        struct file *file = vma->vm_file;
        struct anon_vma *anon_vma = NULL;
        long adjust_next = 0;
        int remove_next = 0;

        /*insert为NULL 查看vma和next合并*/
        if (next && !insert) {
                if (end >= next->vm_end) {
                        /*
                       * vma expands, overlapping all the next, and
                       * perhaps the one after too (mprotect case 6).
                       */
                        /* case 1,6*/
again:                        remove_next = 1 + (end > next->vm_end);
                        end = next->vm_end;
                        anon_vma = next->anon_vma;
                        importer = vma;
                } else if (end > next->vm_start) {
                        /*
                       * vma expands, overlapping part of the next:
                       * mprotect case 5 shifting the boundary up.
                       */
                        /*case 5*/
                        adjust_next = (end - next->vm_start) >> PAGE_SHIFT;
                        anon_vma = next->anon_vma;
                        importer = vma;
                } else if (end < vma->vm_end) {
                        /*
                       * vma shrinks, and !insert tells it's not
                       * split_vma inserting another: so it must be
                       * mprotect case 4 shifting the boundary down.
                       */
                        adjust_next = - ((vma->vm_end - end) >> PAGE_SHIFT); /*case4就是这里adjust_next*/
                        anon_vma = next->anon_vma;
                        importer = next;
                }
        }

        if (file) {
                mapping = file->f_mapping;
                if (!(vma->vm_flags & VM_NONLINEAR))
                        root = &mapping->i_mmap;
                spin_lock(&mapping->i_mmap_lock);
                if (importer &&
                  vma->vm_truncate_count != next->vm_truncate_count) {
                        /*
                       * unmap_mapping_range might be in progress:
                       * ensure that the expanding vma is rescanned.
                       */
                        importer->vm_truncate_count = 0;
                }
                if (insert) {
                        insert->vm_truncate_count = vma->vm_truncate_count;
                        /*
                       * Put into prio_tree now, so instantiated pages
                       * are visible to arm/parisc __flush_dcache_page
                       * throughout; but we cannot insert into address
                       * space until vma start or end is updated.
                       */
                        __vma_link_file(insert);
                }
        }

        /*
       * When changing only vma->vm_end, we don't really need
       * anon_vma lock: but is that case worth optimizing out?
       */
        if (vma->anon_vma)
                anon_vma = vma->anon_vma;
        if (anon_vma) {
                spin_lock(&anon_vma->lock);
                /*
               * Easily overlooked: when mprotect shifts the boundary,
               * make sure the expanding vma has anon_vma set if the
               * shrinking vma had, to cover any anon pages imported.
               */
                if (importer && !importer->anon_vma) {
                        importer->anon_vma = anon_vma;
                        __anon_vma_link(importer);
                }
        }

        if (root) {
                flush_dcache_mmap_lock(mapping);
                vma_prio_tree_remove(vma, root);
                if (adjust_next)
                        vma_prio_tree_remove(next, root);
        }

        vma->vm_start = start;
        vma->vm_end = end;
        vma->vm_pgoff = pgoff;
        if (adjust_next) {/*case4就是这里adjust_next调整next->vm_start 仅仅扩展了上图的x而已*/
                next->vm_start += adjust_next << PAGE_SHIFT;
                next->vm_pgoff += adjust_next;
        }

        if (root) {
                if (adjust_next)
                        vma_prio_tree_insert(next, root);
                vma_prio_tree_insert(vma, root);
                flush_dcache_mmap_unlock(mapping);
        }

        if (remove_next) {
                /*
               * vma_merge has merged next into vma, and needs
               * us to remove next before dropping the locks.
               */
                /*从红黑树和链表中删除*/
                __vma_unlink(mm, next, vma);
                if (file)
                        __remove_shared_vm_struct(next, file, mapping);
                if (next->anon_vma)
                        __anon_vma_merge(vma, next);
        } else if (insert) {
                /*
               * split_vma has split insert from vma, and needs
               * us to insert it before dropping the locks
               * (it may either follow vma or precede it).
               */
                __insert_vm_struct(mm, insert);
        }

        if (anon_vma)
                spin_unlock(&anon_vma->lock);
        if (mapping)
                spin_unlock(&mapping->i_mmap_lock);

        if (remove_next) {
                if (file)
                        fput(file);
                mm->map_count--;
                mpol_free(vma_policy(next));
                kmem_cache_free(vm_area_cachep, next);
                /*
               * In mprotect's case 6 (see comments on vma_merge),
               * we must remove another next too. It would clutter
               * up the code too much to do both in one go.
               */
                if (remove_next == 2) {
                        next = vma->vm_next;
                        goto again;
                }
        }

        validate_mm(mm);
}

rdlkbr 发表于 2016-05-20 18:05

本帖最后由 rdlkbr 于 2016-05-20 21:09 编辑

。。。。。。。。。。。。。
页: [1]
查看完整版本: 关于线性区合并函数vma_merge中case4的疑问