Merge ssh://master.kernel.org/pub/scm/linux/kernel/git/linville/wireless-next-2.6...
[pandora-kernel.git] / mm / prio_tree.c
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 }