fed up with those stupid warnings
[mmotm.git] / mm / prio_tree.c
blobc297a46735725454fd81b87ac9c18210786bd199
1 /*
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
14 #include <linux/mm.h>
15 #include <linux/prio_tree.h>
18 * See lib/prio_tree.c for details on the general radix priority search tree
19 * code.
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)
30 /* avoid overflow */
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.
42 * prio_tree_root
43 * |
44 * A vm_set.head
45 * / \ /
46 * L R -> H-I-J-K-M-N-O-P-Q-S
47 * ^ ^ <-- vm_set.list -->
48 * tree nodes
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;
73 int i;
75 printk("vm_area_struct at %p:", ptr);
76 for (i = 0; i < sizeof(*vma)/sizeof(*ptr); i++, ptr++) {
77 if (!(i & 3))
78 printk("\n");
79 printk(" %p", *ptr);
81 printk("\n");
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.
100 dump_vma(vma);
101 dump_vma(old);
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);
113 else {
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);
144 else
145 raw_prio_tree_remove(root, &vma->shared.prio_tree_node);
146 } else {
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,
155 shared.vm_set.list);
156 list_del_init(&head->shared.vm_set.list);
157 } else
158 new_head = NULL;
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;
163 if (new_head)
164 new_head->shared.vm_set.head = head;
166 } else {
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,
172 shared.vm_set.list);
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;
176 } else
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;
193 if (!vma) {
195 * First call is with NULL vma
197 ptr = prio_tree_next(iter);
198 if (ptr) {
199 next = prio_tree_entry(ptr, struct vm_area_struct,
200 shared.prio_tree_node);
201 prefetch(next->shared.vm_set.head);
202 return next;
203 } else
204 return NULL;
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);
211 return next;
213 } else {
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);
218 return next;
222 ptr = prio_tree_next(iter);
223 if (ptr) {
224 next = prio_tree_entry(ptr, struct vm_area_struct,
225 shared.prio_tree_node);
226 prefetch(next->shared.vm_set.head);
227 return next;
228 } else
229 return NULL;