Linux Audio

Check our new training course

Loading...
Note: File does not exist in v6.9.4.
  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 */
 76void 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
 98void 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
114void 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 */
165struct 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}