The Design and Implementation of the FreeBSD Operating System, Second Edition
Now available: The Design and Implementation of the FreeBSD Operating System (Second Edition)


[ source navigation ] [ diff markup ] [ identifier search ] [ freetext search ] [ file search ] [ list types ] [ track identifier ]

FreeBSD/Linux Kernel Cross Reference
sys/mm/prio_tree.c

Version: -  FREEBSD  -  FREEBSD-13-STABLE  -  FREEBSD-13-0  -  FREEBSD-12-STABLE  -  FREEBSD-12-0  -  FREEBSD-11-STABLE  -  FREEBSD-11-0  -  FREEBSD-10-STABLE  -  FREEBSD-10-0  -  FREEBSD-9-STABLE  -  FREEBSD-9-0  -  FREEBSD-8-STABLE  -  FREEBSD-8-0  -  FREEBSD-7-STABLE  -  FREEBSD-7-0  -  FREEBSD-6-STABLE  -  FREEBSD-6-0  -  FREEBSD-5-STABLE  -  FREEBSD-5-0  -  FREEBSD-4-STABLE  -  FREEBSD-3-STABLE  -  FREEBSD22  -  l41  -  OPENBSD  -  linux-2.6  -  MK84  -  PLAN9  -  xnu-8792 
SearchContext: -  none  -  3  -  10 

    1 /*
    2  * mm/prio_tree.c - priority search tree for mapping->i_mmap
    3  *
    4  * Copyright (C) 2004, Rajesh Venkatasubramanian <vrajesh@umich.edu>
    5  *
    6  * This file is released under the GPL v2.
    7  *
    8  * Based on the radix priority search tree proposed by Edward M. McCreight
    9  * SIAM Journal of Computing, vol. 14, no.2, pages 257-276, May 1985
   10  *
   11  * 02Feb2004    Initial version
   12  */
   13 
   14 #include <linux/mm.h>
   15 #include <linux/prio_tree.h>
   16 #include <linux/prefetch.h>
   17 
   18 /*
   19  * See lib/prio_tree.c for details on the general radix priority search tree
   20  * code.
   21  */
   22 
   23 /*
   24  * The following #defines are mirrored from lib/prio_tree.c. They're only used
   25  * for debugging, and should be removed (along with the debugging code using
   26  * them) when switching also VMAs to the regular prio_tree code.
   27  */
   28 
   29 #define RADIX_INDEX(vma)  ((vma)->vm_pgoff)
   30 #define VMA_SIZE(vma)     (((vma)->vm_end - (vma)->vm_start) >> PAGE_SHIFT)
   31 /* avoid overflow */
   32 #define HEAP_INDEX(vma)   ((vma)->vm_pgoff + (VMA_SIZE(vma) - 1))
   33 
   34 /*
   35  * Radix priority search tree for address_space->i_mmap
   36  *
   37  * For each vma that map a unique set of file pages i.e., unique [radix_index,
   38  * heap_index] value, we have a corresponding priority search tree node. If
   39  * multiple vmas have identical [radix_index, heap_index] value, then one of
   40  * them is used as a tree node and others are stored in a vm_set list. The tree
   41  * node points to the first vma (head) of the list using vm_set.head.
   42  *
   43  * prio_tree_root
   44  *      |
   45  *      A       vm_set.head
   46  *     / \      /
   47  *    L   R -> H-I-J-K-M-N-O-P-Q-S
   48  *    ^   ^    <-- vm_set.list -->
   49  *  tree nodes
   50  *
   51  * We need some way to identify whether a vma is a tree node, head of a vm_set
   52  * list, or just a member of a vm_set list. We cannot use vm_flags to store
   53  * such information. The reason is, in the above figure, it is possible that
   54  * vm_flags' of R and H are covered by the different mmap_sems. When R is
   55  * removed under R->mmap_sem, H replaces R as a tree node. Since we do not hold
   56  * H->mmap_sem, we cannot use H->vm_flags for marking that H is a tree node now.
   57  * That's why some trick involving shared.vm_set.parent is used for identifying
   58  * tree nodes and list head nodes.
   59  *
   60  * vma radix priority search tree node rules:
   61  *
   62  * vma->shared.vm_set.parent != NULL    ==> a tree node
   63  *      vma->shared.vm_set.head != NULL ==> list of others mapping same range
   64  *      vma->shared.vm_set.head == NULL ==> no others map the same range
   65  *
   66  * vma->shared.vm_set.parent == NULL
   67  *      vma->shared.vm_set.head != NULL ==> list head of vmas mapping same range
   68  *      vma->shared.vm_set.head == NULL ==> a list node
   69  */
   70 
   71 /*
   72  * Add a new vma known to map the same set of pages as the old vma:
   73  * useful for fork's dup_mmap as well as vma_prio_tree_insert below.
   74  * Note that it just happens to work correctly on i_mmap_nonlinear too.
   75  */
   76 void vma_prio_tree_add(struct vm_area_struct *vma, struct vm_area_struct *old)
   77 {
   78         /* Leave these BUG_ONs till prio_tree patch stabilizes */
   79         BUG_ON(RADIX_INDEX(vma) != RADIX_INDEX(old));
   80         BUG_ON(HEAP_INDEX(vma) != HEAP_INDEX(old));
   81 
   82         vma->shared.vm_set.head = NULL;
   83         vma->shared.vm_set.parent = NULL;
   84 
   85         if (!old->shared.vm_set.parent)
   86                 list_add(&vma->shared.vm_set.list,
   87                                 &old->shared.vm_set.list);
   88         else if (old->shared.vm_set.head)
   89                 list_add_tail(&vma->shared.vm_set.list,
   90                                 &old->shared.vm_set.head->shared.vm_set.list);
   91         else {
   92                 INIT_LIST_HEAD(&vma->shared.vm_set.list);
   93                 vma->shared.vm_set.head = old;
   94                 old->shared.vm_set.head = vma;
   95         }
   96 }
   97 
   98 void vma_prio_tree_insert(struct vm_area_struct *vma,
   99                           struct prio_tree_root *root)
  100 {
  101         struct prio_tree_node *ptr;
  102         struct vm_area_struct *old;
  103 
  104         vma->shared.vm_set.head = NULL;
  105 
  106         ptr = raw_prio_tree_insert(root, &vma->shared.prio_tree_node);
  107         if (ptr != (struct prio_tree_node *) &vma->shared.prio_tree_node) {
  108                 old = prio_tree_entry(ptr, struct vm_area_struct,
  109                                         shared.prio_tree_node);
  110                 vma_prio_tree_add(vma, old);
  111         }
  112 }
  113 
  114 void vma_prio_tree_remove(struct vm_area_struct *vma,
  115                           struct prio_tree_root *root)
  116 {
  117         struct vm_area_struct *node, *head, *new_head;
  118 
  119         if (!vma->shared.vm_set.head) {
  120                 if (!vma->shared.vm_set.parent)
  121                         list_del_init(&vma->shared.vm_set.list);
  122                 else
  123                         raw_prio_tree_remove(root, &vma->shared.prio_tree_node);
  124         } else {
  125                 /* Leave this BUG_ON till prio_tree patch stabilizes */
  126                 BUG_ON(vma->shared.vm_set.head->shared.vm_set.head != vma);
  127                 if (vma->shared.vm_set.parent) {
  128                         head = vma->shared.vm_set.head;
  129                         if (!list_empty(&head->shared.vm_set.list)) {
  130                                 new_head = list_entry(
  131                                         head->shared.vm_set.list.next,
  132                                         struct vm_area_struct,
  133                                         shared.vm_set.list);
  134                                 list_del_init(&head->shared.vm_set.list);
  135                         } else
  136                                 new_head = NULL;
  137 
  138                         raw_prio_tree_replace(root, &vma->shared.prio_tree_node,
  139                                         &head->shared.prio_tree_node);
  140                         head->shared.vm_set.head = new_head;
  141                         if (new_head)
  142                                 new_head->shared.vm_set.head = head;
  143 
  144                 } else {
  145                         node = vma->shared.vm_set.head;
  146                         if (!list_empty(&vma->shared.vm_set.list)) {
  147                                 new_head = list_entry(
  148                                         vma->shared.vm_set.list.next,
  149                                         struct vm_area_struct,
  150                                         shared.vm_set.list);
  151                                 list_del_init(&vma->shared.vm_set.list);
  152                                 node->shared.vm_set.head = new_head;
  153                                 new_head->shared.vm_set.head = node;
  154                         } else
  155                                 node->shared.vm_set.head = NULL;
  156                 }
  157         }
  158 }
  159 
  160 /*
  161  * Helper function to enumerate vmas that map a given file page or a set of
  162  * contiguous file pages. The function returns vmas that at least map a single
  163  * page in the given range of contiguous file pages.
  164  */
  165 struct vm_area_struct *vma_prio_tree_next(struct vm_area_struct *vma,
  166                                         struct prio_tree_iter *iter)
  167 {
  168         struct prio_tree_node *ptr;
  169         struct vm_area_struct *next;
  170 
  171         if (!vma) {
  172                 /*
  173                  * First call is with NULL vma
  174                  */
  175                 ptr = prio_tree_next(iter);
  176                 if (ptr) {
  177                         next = prio_tree_entry(ptr, struct vm_area_struct,
  178                                                 shared.prio_tree_node);
  179                         prefetch(next->shared.vm_set.head);
  180                         return next;
  181                 } else
  182                         return NULL;
  183         }
  184 
  185         if (vma->shared.vm_set.parent) {
  186                 if (vma->shared.vm_set.head) {
  187                         next = vma->shared.vm_set.head;
  188                         prefetch(next->shared.vm_set.list.next);
  189                         return next;
  190                 }
  191         } else {
  192                 next = list_entry(vma->shared.vm_set.list.next,
  193                                 struct vm_area_struct, shared.vm_set.list);
  194                 if (!next->shared.vm_set.head) {
  195                         prefetch(next->shared.vm_set.list.next);
  196                         return next;
  197                 }
  198         }
  199 
  200         ptr = prio_tree_next(iter);
  201         if (ptr) {
  202                 next = prio_tree_entry(ptr, struct vm_area_struct,
  203                                         shared.prio_tree_node);
  204                 prefetch(next->shared.vm_set.head);
  205                 return next;
  206         } else
  207                 return NULL;
  208 }

Cache object: 1fed1dccbb823a02dcc4daa44e7ebf7f


[ source navigation ] [ diff markup ] [ identifier search ] [ freetext search ] [ file search ] [ list types ] [ track identifier ]


This page is part of the FreeBSD/Linux Linux Kernel Cross-Reference, and was automatically generated using a modified version of the LXR engine.