2 * mm/prio_tree.c - priority search tree for mapping->i_mmap
4 * Copyright (C) 2004, Rajesh Venkatasubramanian <vrajesh@umich.edu>
6 * This file is released under the GPL v2.
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
11 * 02Feb2004 Initial version
15 #include <linux/prio_tree.h>
18 * See lib/prio_tree.c for details on the general radix priority search tree
23 * The following #defines are mirrored from lib/prio_tree.c. They're only used
24 * for debugging, and should be removed (along with the debugging code using
25 * them) when switching also VMAs to the regular prio_tree code.
28 #define RADIX_INDEX(vma) ((vma)->vm_pgoff)
29 #define VMA_SIZE(vma) (((vma)->vm_end - (vma)->vm_start) >> PAGE_SHIFT)
31 #define HEAP_INDEX(vma) ((vma)->vm_pgoff + (VMA_SIZE(vma) - 1))
34 * Radix priority search tree for address_space->i_mmap
36 * For each vma that map a unique set of file pages i.e., unique [radix_index,
37 * heap_index] value, we have a corresponding priority search tree node. If
38 * multiple vmas have identical [radix_index, heap_index] value, then one of
39 * them is used as a tree node and others are stored in a vm_set list. The tree
40 * node points to the first vma (head) of the list using vm_set.head.
46 * L R -> H-I-J-K-M-N-O-P-Q-S
47 * ^ ^ <-- vm_set.list -->
50 * We need some way to identify whether a vma is a tree node, head of a vm_set
51 * list, or just a member of a vm_set list. We cannot use vm_flags to store
52 * such information. The reason is, in the above figure, it is possible that
53 * vm_flags' of R and H are covered by the different mmap_sems. When R is
54 * removed under R->mmap_sem, H replaces R as a tree node. Since we do not hold
55 * H->mmap_sem, we cannot use H->vm_flags for marking that H is a tree node now.
56 * That's why some trick involving shared.vm_set.parent is used for identifying
57 * tree nodes and list head nodes.
59 * vma radix priority search tree node rules:
61 * vma->shared.vm_set.parent != NULL ==> a tree node
62 * vma->shared.vm_set.head != NULL ==> list of others mapping same range
63 * vma->shared.vm_set.head == NULL ==> no others map the same range
65 * vma->shared.vm_set.parent == NULL
66 * vma->shared.vm_set.head != NULL ==> list head of vmas mapping same range
67 * vma->shared.vm_set.head == NULL ==> a list node
70 static void dump_vma(struct vm_area_struct
*vma
)
72 void **ptr
= (void **) vma
;
75 printk("vm_area_struct at %p:", ptr
);
76 for (i
= 0; i
< sizeof(*vma
)/sizeof(*ptr
); i
++, ptr
++) {
85 * Add a new vma known to map the same set of pages as the old vma:
86 * useful for fork's dup_mmap as well as vma_prio_tree_insert below.
87 * Note that it just happens to work correctly on i_mmap_nonlinear too.
89 void vma_prio_tree_add(struct vm_area_struct
*vma
, struct vm_area_struct
*old
)
91 vma
->shared
.vm_set
.head
= NULL
;
92 vma
->shared
.vm_set
.parent
= NULL
;
94 if (WARN_ON(RADIX_INDEX(vma
) != RADIX_INDEX(old
) ||
95 HEAP_INDEX(vma
) != HEAP_INDEX(old
))) {
97 * This should never happen, yet it has been seen a few times:
98 * we cannot say much about it without seeing the vma contents.
103 * Don't try to link this (corrupt?) vma into the (corrupt?)
104 * prio_tree, but arrange for its removal to succeed later.
106 INIT_LIST_HEAD(&vma
->shared
.vm_set
.list
);
107 } else if (!old
->shared
.vm_set
.parent
)
108 list_add(&vma
->shared
.vm_set
.list
,
109 &old
->shared
.vm_set
.list
);
110 else if (old
->shared
.vm_set
.head
)
111 list_add_tail(&vma
->shared
.vm_set
.list
,
112 &old
->shared
.vm_set
.head
->shared
.vm_set
.list
);
114 INIT_LIST_HEAD(&vma
->shared
.vm_set
.list
);
115 vma
->shared
.vm_set
.head
= old
;
116 old
->shared
.vm_set
.head
= vma
;
120 void vma_prio_tree_insert(struct vm_area_struct
*vma
,
121 struct prio_tree_root
*root
)
123 struct prio_tree_node
*ptr
;
124 struct vm_area_struct
*old
;
126 vma
->shared
.vm_set
.head
= NULL
;
128 ptr
= raw_prio_tree_insert(root
, &vma
->shared
.prio_tree_node
);
129 if (ptr
!= (struct prio_tree_node
*) &vma
->shared
.prio_tree_node
) {
130 old
= prio_tree_entry(ptr
, struct vm_area_struct
,
131 shared
.prio_tree_node
);
132 vma_prio_tree_add(vma
, old
);
136 void vma_prio_tree_remove(struct vm_area_struct
*vma
,
137 struct prio_tree_root
*root
)
139 struct vm_area_struct
*node
, *head
, *new_head
;
141 if (!vma
->shared
.vm_set
.head
) {
142 if (!vma
->shared
.vm_set
.parent
)
143 list_del_init(&vma
->shared
.vm_set
.list
);
145 raw_prio_tree_remove(root
, &vma
->shared
.prio_tree_node
);
147 /* Leave this BUG_ON till prio_tree patch stabilizes */
148 BUG_ON(vma
->shared
.vm_set
.head
->shared
.vm_set
.head
!= vma
);
149 if (vma
->shared
.vm_set
.parent
) {
150 head
= vma
->shared
.vm_set
.head
;
151 if (!list_empty(&head
->shared
.vm_set
.list
)) {
152 new_head
= list_entry(
153 head
->shared
.vm_set
.list
.next
,
154 struct vm_area_struct
,
156 list_del_init(&head
->shared
.vm_set
.list
);
160 raw_prio_tree_replace(root
, &vma
->shared
.prio_tree_node
,
161 &head
->shared
.prio_tree_node
);
162 head
->shared
.vm_set
.head
= new_head
;
164 new_head
->shared
.vm_set
.head
= head
;
167 node
= vma
->shared
.vm_set
.head
;
168 if (!list_empty(&vma
->shared
.vm_set
.list
)) {
169 new_head
= list_entry(
170 vma
->shared
.vm_set
.list
.next
,
171 struct vm_area_struct
,
173 list_del_init(&vma
->shared
.vm_set
.list
);
174 node
->shared
.vm_set
.head
= new_head
;
175 new_head
->shared
.vm_set
.head
= node
;
177 node
->shared
.vm_set
.head
= NULL
;
183 * Helper function to enumerate vmas that map a given file page or a set of
184 * contiguous file pages. The function returns vmas that at least map a single
185 * page in the given range of contiguous file pages.
187 struct vm_area_struct
*vma_prio_tree_next(struct vm_area_struct
*vma
,
188 struct prio_tree_iter
*iter
)
190 struct prio_tree_node
*ptr
;
191 struct vm_area_struct
*next
;
195 * First call is with NULL vma
197 ptr
= prio_tree_next(iter
);
199 next
= prio_tree_entry(ptr
, struct vm_area_struct
,
200 shared
.prio_tree_node
);
201 prefetch(next
->shared
.vm_set
.head
);
207 if (vma
->shared
.vm_set
.parent
) {
208 if (vma
->shared
.vm_set
.head
) {
209 next
= vma
->shared
.vm_set
.head
;
210 prefetch(next
->shared
.vm_set
.list
.next
);
214 next
= list_entry(vma
->shared
.vm_set
.list
.next
,
215 struct vm_area_struct
, shared
.vm_set
.list
);
216 if (!next
->shared
.vm_set
.head
) {
217 prefetch(next
->shared
.vm_set
.list
.next
);
222 ptr
= prio_tree_next(iter
);
224 next
= prio_tree_entry(ptr
, struct vm_area_struct
,
225 shared
.prio_tree_node
);
226 prefetch(next
->shared
.vm_set
.head
);