prio_tree generalization
Subsystems (4)
cover prio_tree generalization
This patch set for 2.6.10-rc3-bk10 (*) generalizes the prio_tree code such that other subsystems than just VMA can use it.
(*) I'm not sure which tree is the most useful base for this.
Should I use 2.6.10-rc3-mm* instead of -bk* ? (Or is it already too late for 2.6.10 anyway ?)
While there are currently no other in-tree users than VMA, the prio_tree algorithm will be useful for detecting overlapping disk IO requests, which is a prerequisite for efficient handling of barriers, which - besides being a good idea in general - in turn is a prerequisite for useful disk IO priorities.
The three patches that follow are incremental and must be applied in the order specified.
- Werner
Signature · 3 lines
_________________________________________________________________________ / Werner Almesberger, Buenos Aires, Argentina [email protected] / /_http://www.almesberger.net/____________________________________________/
PATCH 1/3 prio_tree: roll call to prio_tree_first into prio_tree_next
Allow prio_tree_next to be used as the only function for tree traversal, similar to how vma_prio_tree_next works.
This patch isn't needed for the generalization, but since it affects the API, it's better to include it first.
- Werner
---------------------------------- cut here -----------------------------------
include/linux/prio_tree.h renamed from include/linux/prio_tree.h.orig +1 -0
@@ -29,6 +29,7 @@ static inline void prio_tree_iter_init(s iter->root = root; iter->r_index = r_index; iter->h_index = h_index; + iter->cur =NULL ; }#define INIT_PRIO_TREE_ROOT (ptr) \
mm/prio_tree.c renamed from mm/prio_tree.c.orig +4 -1
@@ -457,6 +457,9 @@ static struct prio_tree_node *prio_tree_ {unsigned long r_index, h_index; +if (iter->cur ==NULL ) +return prio_tree_first (iter); + repeat:while (prio_tree_left (iter, &r_index, &h_index))if (overlap (iter, r_index, h_index))
@@ -620,7 +623,7 @@ struct vm_area_struct *vma_prio_tree_nex/* * First call is with NULL vma */ - ptr =prio_tree_first (iter); + ptr =prio_tree_next (iter);if (ptr) { next = prio_tree_entry(ptr, struct vm_area_struct, shared.prio_tree_node);
Signature · 3 lines
_________________________________________________________________________ / Werner Almesberger, Buenos Aires, Argentina [email protected] / /_http://www.almesberger.net/____________________________________________/
PATCH 2/3 prio_tree: generalization
In reply to Werner AlmesbergerExport prio_tree functions such that they can be used by other subsystems than only VMAs. Also adds a mode to prio_tree to use it with keys explicitly included in the prio_tree meta-data.
The plan is to also consider converting VMAs to use explicit keys, so that the old "raw" mode can be removed.
- Werner
---------------------------------- cut here -----------------------------------
include/linux/prio_tree.h renamed from include/linux/prio_tree.h.roll_first +45 -2
@@ -1,15 +1,38 @@#ifndef _LINUX_PRIO_TREE_H#define _LINUX_PRIO_TREE_H +/* +* K&R 2nd ed. A8.3 somewhat obliquely hints that initial sequences of struct +* fields with identical types should end up at the same location. We'll use +* this until we can scrap struct raw_prio_tree_node. +* +* Note: all this could be done more elegantly by using unnamed union/struct +* fields. However, gcc 2.95.3 and apparently also gcc 3.0.4 don't support this +* language extension. +*/ + +struct raw_prio_tree_node { +struct prio_tree_node *left; +struct prio_tree_node *right; +struct prio_tree_node *parent; +}; +struct prio_tree_node {struct prio_tree_node *left;struct prio_tree_node *right;struct prio_tree_node *parent; +unsigned long start; +unsigned long last;/* last location _in_ interval */ };struct prio_tree_root {struct prio_tree_node *prio_tree_node; -unsigned int index_bits; +unsigned short index_bits; +unsigned short raw; +/* +* 0: nodes are of type struct prio_tree_node +* 1: nodes are of type raw_prio_tree_node +*/ }; struct prio_tree_iter {
@@ -32,12 +55,16 @@ static inline void prio_tree_iter_init(s iter->cur =NULL ; } -#define INIT_PRIO_TREE_ROOT (ptr) \ +#define __INIT_PRIO_TREE_ROOT (ptr, _raw) \ do { \ (ptr)->prio_tree_node = NULL; \ (ptr)->index_bits = 1; \ + (ptr)->raw = (_raw); \ } while (0) +#define INIT_PRIO_TREE_ROOT (ptr) __INIT_PRIO_TREE_ROOT(ptr, 0) +#define INIT_RAW_PRIO_TREE_ROOT (ptr) __INIT_PRIO_TREE_ROOT(ptr, 1) +#define INIT_PRIO_TREE_NODE (ptr) \ do { \ (ptr)->left = (ptr)->right = (ptr)->parent = (ptr); \
@@ -74,4 +101,20 @@ static inline int prio_tree_right_empty( return node->right == node; } + +struct prio_tree_node *prio_tree_replace (struct prio_tree_root *root, +struct prio_tree_node *old,struct prio_tree_node *node); +struct prio_tree_node *prio_tree_insert (struct prio_tree_root *root, +struct prio_tree_node *node); +void prio_tree_remove (struct prio_tree_root *root,struct prio_tree_node *node); +struct prio_tree_node *prio_tree_next (struct prio_tree_iter *iter); + +#define raw_prio_tree_replace (root, old, node) \ + prio_tree_replace(root, (struct prio_tree_node *) (old), \ + (struct prio_tree_node *) (node)) +#define raw_prio_tree_insert (root, node) \ + prio_tree_insert(root, (struct prio_tree_node *) (node)) +#define raw_prio_tree_remove (root, node) \ + prio_tree_remove(root, (struct prio_tree_node *) (node)) +#endif /* _LINUX_PRIO_TREE_H */
include/linux/mm.h renamed from include/linux/mm.h.orig +1 -1
@@ -85,7 +85,7 @@ struct vm_area_struct {struct vm_area_struct *head; } vm_set; -struct prio_tree_node prio_tree_node; +struct raw_prio_tree_node prio_tree_node; } shared;/*
mm/prio_tree.c renamed from mm/prio_tree.c.roll_first +33 -32
173 lines not on this page · raw message
fs/inode.c renamed from fs/inode.c.orig +1 -1
@@ -201,7 +201,7 @@ void inode_init_once(struct inode *inodeatomic_set (&inode->i_data.truncate_count,0 );INIT_LIST_HEAD (&inode->i_data.private_list);spin_lock_init (&inode->i_data.private_lock); -INIT_PRIO_TREE_ROOT (&inode->i_data.i_mmap); +INIT_RAW_PRIO_TREE_ROOT (&inode->i_data.i_mmap);INIT_LIST_HEAD (&inode->i_data.i_mmap_nonlinear);spin_lock_init (&inode->i_lock);i_size_ordered_init (inode);
Signature · 3 lines
_________________________________________________________________________ / Werner Almesberger, Buenos Aires, Argentina [email protected] / /_http://www.almesberger.net/____________________________________________/
PATCH 3/3 prio_tree: move general code from mm/ to lib/
In reply to Werner AlmesbergerLast but not least, move the general prio_tree code from mm/ to lib/. This patch also duplicates some macros, which are used in the VMA code for debugging purposes, so we can't properly separate them yet.
Note that this patch conflicts with a patch in 2.6.10-rc3-mm1 (frv-better-mmap-support-in-uclinux.patch), which removes mm/prio_tree in systems without an MMU. Not making that other patch provide a dummy for prio_tree_init should resolve the conflict. (That's just from reading the patch - I haven't actually tried this.)
- Werner
---------------------------------- cut here -----------------------------------
mm/prio_tree.c renamed from mm/prio_tree.c.gen +6 -459
@@ -11,477 +11,24 @@* 02Feb2004 Initial version */ -#include <linux/init.h> #include <linux/mm.h> #include <linux/prio_tree.h> /* -* A clever mix of heap and radix trees forms a radix priority search tree (PST) -* which is useful for storing intervals, e.g, we can consider a vma as a closed -* interval of file pages [offset_begin, offset_end], and store all vmas that -* map a file in a PST. Then, using the PST, we can answer a stabbing query, -* i.e., selecting a set of stored intervals (vmas) that overlap with (map) a -* given input interval X (a set of consecutive file pages), in "O(log n + m)" -* time where 'log n' is the height of the PST, and 'm' is the number of stored -* intervals (vmas) that overlap (map) with the input interval X (the set of -* consecutive file pages). -* -* In our implementation, we store closed intervals of the form [radix_index, -* heap_index]. We assume that always radix_index <= heap_index. McCreight's PST -* is designed for storing intervals with unique radix indices, i.e., each -* interval have different radix_index. However, this limitation can be easily -* overcome by using the size, i.e., heap_index - radix_index, as part of the -* index, so we index the tree using [(radix_index,size), heap_index]. -* -* When the above-mentioned indexing scheme is used, theoretically, in a 32 bit -* machine, the maximum height of a PST can be 64. We can use a balanced version -* of the priority search tree to optimize the tree height, but the balanced -* tree proposed by McCreight is too complex and memory-hungry for our purpose. +* See lib/prio_tree.c for details on the general radix priority search tree +* code. */ /* -* The following macros are used for implementing prio_tree for i_mmap +* The following #defines are mirrored from lib/prio_tree.c. They're only used +* for debugging, and should be removed (along with the debugging code using +* them) when switching also VMAs to the regular prio_tree code. */ #define RADIX_INDEX (vma) ((vma)->vm_pgoff)#define VMA_SIZE (vma) (((vma)->vm_end - (vma)->vm_start) >> PAGE_SHIFT)/* avoid overflow */ -#define HEAP_INDEX (vma) ((vma)->vm_pgoff + (VMA_SIZE(vma) - 1)) - - -static void get_index (const struct prio_tree_root *root, -const struct prio_tree_node *node, -unsigned long *radix,unsigned long *heap) -{ -if (root->raw) { -struct vm_area_struct *vma =prio_tree_entry ( - node, struct vm_area_struct, shared.prio_tree_node); - - *radix =RADIX_INDEX (vma); - *heap =HEAP_INDEX (vma); - } -else { - *radix = node->start; - *heap = node->last; - } -} - -static unsigned long index_bits_to_maxindex[BITS_PER_LONG ]; - -void __init prio_tree_init (void ) -{ -unsigned int i; - -for (i =0 ; i <ARRAY_SIZE (index_bits_to_maxindex) -1 ; i++) - index_bits_to_maxindex[i] = (1UL << (i +1 )) -1 ; - index_bits_to_maxindex[ARRAY_SIZE (index_bits_to_maxindex) -1 ] = ~0UL ; -} - -/* -* Maximum heap_index that can be stored in a PST with index_bits bits -*/ -static inline unsigned long prio_tree_maxindex (unsigned int bits) -{ -return index_bits_to_maxindex[bits -1 ]; -} - -/* -* Extend a priority search tree so that it can store a node with heap_index -* max_heap_index. In the worst case, this algorithm takes O((log n)^2). -* However, this function is used rarely and the common case performance is -* not bad. -*/ -static struct prio_tree_node *prio_tree_expand (struct prio_tree_root *root, -struct prio_tree_node *node,unsigned long max_heap_index) -{ -struct prio_tree_node *first =NULL , *prev, *last =NULL ; - -if (max_heap_index >prio_tree_maxindex (root->index_bits)) - root->index_bits++; - -while (max_heap_index >prio_tree_maxindex (root->index_bits)) { - root->index_bits++; - -if (prio_tree_empty (root)) -continue ; - -if (first ==NULL ) { - first = root->prio_tree_node; -prio_tree_remove (root, root->prio_tree_node); -INIT_PRIO_TREE_NODE (first); - last = first; - }else { - prev = last; - last = root->prio_tree_node; -prio_tree_remove (root, root->prio_tree_node); -INIT_PRIO_TREE_NODE (last); - prev->left = last; - last->parent = prev; - } - } - -INIT_PRIO_TREE_NODE (node); - -if (first) { - node->left = first; - first->parent = node; - }else - last = node; - -if (!prio_tree_empty (root)) { - last->left = root->prio_tree_node; - last->left->parent = last; - } - - root->prio_tree_node = node; -return node; -} - -/* -* Replace a prio_tree_node with a new node and return the old node -*/ -struct prio_tree_node *prio_tree_replace (struct prio_tree_root *root, -struct prio_tree_node *old,struct prio_tree_node *node) -{ -INIT_PRIO_TREE_NODE (node); - -if (prio_tree_root (old)) { -BUG_ON (root->prio_tree_node != old); -/* -* We can reduce root->index_bits here. However, it is complex -* and does not help much to improve performance (IMO). -*/ - node->parent = node; - root->prio_tree_node = node; - }else { - node->parent = old->parent; -if (old->parent->left == old) - old->parent->left = node; -else - old->parent->right = node; - } - -if (!prio_tree_left_empty (old)) { - node->left = old->left; - old->left->parent = node; - } - -if (!prio_tree_right_empty (old)) { - node->right = old->right; - old->right->parent = node; - } - -return old; -} - -/* -* Insert a prio_tree_node @node into a radix priority search tree @root. The -* algorithm typically takes O(log n) time where 'log n' is the number of bits -* required to represent the maximum heap_index. In the worst case, the algo -* can take O((log n)^2) - check prio_tree_expand. -* -* If a prior node with same radix_index and heap_index is already found in -* the tree, then returns the address of the prior node. Otherwise, inserts -* @node into the tree and returns @node. -*/ -struct prio_tree_node *prio_tree_insert (struct prio_tree_root *root, -struct prio_tree_node *node) -{ -struct prio_tree_node *cur, *res = node; -unsigned long radix_index, heap_index; -unsigned long r_index, h_index, index, mask; -int size_flag =0 ; - -get_index (root, node, &radix_index, &heap_index); - -if (prio_tree_empty (root) || - heap_index >prio_tree_maxindex (root->index_bits)) -return prio_tree_expand (root, node, heap_index); - - cur = root->prio_tree_node; - mask =1UL << (root->index_bits -1 ); - -while (mask) { -get_index (root, cur, &r_index, &h_index); - -if (r_index == radix_index && h_index == heap_index) -return cur; - -if (h_index < heap_index || - (h_index == heap_index && r_index > radix_index)) { -struct prio_tree_node *tmp = node; - node =prio_tree_replace (root, cur, node); - cur = tmp; -/* swap indices */ - index = r_index; - r_index = radix_index; - radix_index = index; - index = h_index; - h_index = heap_index; - heap_index = index; - } - -if (size_flag) - index = heap_index - radix_index; -else - index = radix_index; - -if (index & mask) { -if (prio_tree_right_empty (cur)) { -INIT_PRIO_TREE_NODE (node); - cur->right = node; - node->parent = cur; -return res; - }else - cur = cur->right; - }else { -if (prio_tree_left_empty (cur)) { -INIT_PRIO_TREE_NODE (node); - cur->left = node; - node->parent = cur; -return res; - }else - cur = cur->left; - } - - mask >>=1 ; - -if (!mask) { - mask =1UL << (BITS_PER_LONG -1 ); - size_flag =1 ; - } - } -/* Should not reach here */ -BUG (); -return NULL ; -} - -/* -* Remove a prio_tree_node @node from a radix priority search tree @root. The -* algorithm takes O(log n) time where 'log n' is the number of bits required -* to represent the maximum heap_index. -*/ -void prio_tree_remove (struct prio_tree_root *root,struct prio_tree_node *node) -{ -struct prio_tree_node *cur; -unsigned long r_index, h_index_right, h_index_left; - - cur = node; - -while (!prio_tree_left_empty (cur) || !prio_tree_right_empty (cur)) { -if (!prio_tree_left_empty (cur)) -get_index (root, cur->left, &r_index, &h_index_left); -else { - cur = cur->right; -continue ; - } - -if (!prio_tree_right_empty (cur)) -get_index (root, cur->right, &r_index, &h_index_right); -else { - cur = cur->left; -continue ; - } - -/* both h_index_left and h_index_right cannot be 0 */ -if (h_index_left >= h_index_right) - cur = cur->left; -else - cur = cur->right; - } - -if (prio_tree_root (cur)) { -BUG_ON (root->prio_tree_node != cur); -__INIT_PRIO_TREE_ROOT (root, root->raw); -return ; - } - -if (cur->parent->right == cur) - cur->parent->right = cur->parent; -else - cur->parent->left = cur->parent; - -while (cur != node) - cur =prio_tree_replace (root, cur->parent, cur); -} - -/* -* Following functions help to enumerate all prio_tree_nodes in the tree that -* overlap with the input interval X [radix_index, heap_index]. The enumeration -* takes O(log n + m) time where 'log n' is the height of the tree (which is -* proportional to # of bits required to represent the maximum heap_index) and -* 'm' is the number of prio_tree_nodes that overlap the interval X. -*/ - -static struct prio_tree_node *prio_tree_left (struct prio_tree_iter *iter, -unsigned long *r_index,unsigned long *h_index) -{ -if (prio_tree_left_empty (iter->cur)) -return NULL ; - -get_index (iter->root, iter->cur->left, r_index, h_index); - -if (iter->r_index <= *h_index) { - iter->cur = iter->cur->left; - iter->mask >>=1 ; -if (iter->mask) { -if (iter->size_level) - iter->size_level++; - }else { -if (iter->size_level) { -BUG_ON (!prio_tree_left_empty (iter->cur)); -BUG_ON (!prio_tree_right_empty (iter->cur)); - iter->size_level++; - iter->mask =ULONG_MAX ; - }else { - iter->size_level =1 ; - iter->mask =1UL << (BITS_PER_LONG -1 ); - } - } -return iter->cur; - } - -return NULL ; -} - -static struct prio_tree_node *prio_tree_right (struct prio_tree_iter *iter, -unsigned long *r_index,unsigned long *h_index) -{ -unsigned long value; - -if (prio_tree_right_empty (iter->cur)) -return NULL ; - -if (iter->size_level) - value = iter->value; -else - value = iter->value | iter->mask; - -if (iter->h_index < value) -return NULL ; - -get_index (iter->root, iter->cur->right, r_index, h_index); - -if (iter->r_index <= *h_index) { - iter->cur = iter->cur->right; - iter->mask >>=1 ; - iter->value = value; -if (iter->mask) { -if (iter->size_level) - iter->size_level++; - }else { -if (iter->size_level) { -BUG_ON (!prio_tree_left_empty (iter->cur)); -BUG_ON (!prio_tree_right_empty (iter->cur)); - iter->size_level++; - iter->mask =ULONG_MAX ; - }else { - iter->size_level =1 ; - iter->mask =1UL << (BITS_PER_LONG -1 ); - } - } -return iter->cur; - } - -return NULL ; -} - -static struct prio_tree_node *prio_tree_parent (struct prio_tree_iter *iter) -{ - iter->cur = iter->cur->parent; -if (iter->mask ==ULONG_MAX ) - iter->mask =1UL ; -else if (iter->size_level ==1 ) - iter->mask =1UL ; -else - iter->mask <<=1 ; -if (iter->size_level) - iter->size_level--; -if (!iter->size_level && (iter->value & iter->mask)) - iter->value ^= iter->mask; -return iter->cur; -} - -static inline int overlap (struct prio_tree_iter *iter, -unsigned long r_index,unsigned long h_index) -{ -return iter->h_index >= r_index && iter->r_index <= h_index; -} - -/* -* prio_tree_first: -* -* Get the first prio_tree_node that overlaps with the interval [radix_index, -* heap_index]. Note that always radix_index <= heap_index. We do a pre-order -* traversal of the tree. -*/ -static struct prio_tree_node *prio_tree_first (struct prio_tree_iter *iter) -{ -struct prio_tree_root *root; -unsigned long r_index, h_index; - -INIT_PRIO_TREE_ITER (iter); - - root = iter->root; -if (prio_tree_empty (root)) -return NULL ; - -get_index (root, root->prio_tree_node, &r_index, &h_index); - -if (iter->r_index > h_index) -return NULL ; - - iter->mask =1UL << (root->index_bits -1 ); - iter->cur = root->prio_tree_node; - -while (1 ) { -if (overlap (iter, r_index, h_index)) -return iter->cur; - -if (prio_tree_left (iter, &r_index, &h_index)) -continue ; - -if (prio_tree_right (iter, &r_index, &h_index)) -continue ; - -break ; - } -return NULL ; -} - -/* -* prio_tree_next: -* -* Get the next prio_tree_node that overlaps with the input interval in iter -*/ -struct prio_tree_node *prio_tree_next (struct prio_tree_iter *iter) -{ -unsigned long r_index, h_index; - -if (iter->cur ==NULL ) -return prio_tree_first (iter); - -repeat: -while (prio_tree_left (iter, &r_index, &h_index)) -if (overlap (iter, r_index, h_index)) -return iter->cur; - -while (!prio_tree_right (iter, &r_index, &h_index)) { -while (!prio_tree_root (iter->cur) && - iter->cur->parent->right == iter->cur) -prio_tree_parent (iter); - -if (prio_tree_root (iter->cur)) -return NULL ; - -prio_tree_parent (iter); - } - -if (overlap (iter, r_index, h_index)) -return iter->cur; - - goto repeat; -} +#define HEAP_INDEX (vma) ((vma)->vm_pgoff + (VMA_SIZE(vma) - 1))/* * Radix priority search tree for address_space->i_mmap
lib/Makefile renamed from lib/Makefile.orig +1 -1
@@ -5,7 +5,7 @@ lib-y := errno.o ctype.o string.o vsprintf.o cmdline.o \ bust_spinlocks.o rbtree.o radix-tree.o dump_stack.o \ kobject.o kref.o idr.o div64.o parser.o int_sqrt.o \ - bitmap.o extable.o kobject_uevent.o + bitmap.o extable.o kobject_uevent.o prio_tree.o ifeq ($(CONFIG_DEBUG_KOBJECT),y) CFLAGS_kobject.o += -DDEBUG
lib/prio_tree.c new file +484 -0
484 lines not on this page · raw message
Signature · 3 lines
_________________________________________________________________________ / Werner Almesberger, Buenos Aires, Argentina [email protected] / /_http://www.almesberger.net/____________________________________________/
Re: [PATCH 3/3] prio_tree: move general code from mm/ to lib/
Quoted 7 lines · Werner Almesberger
Note that this patch conflicts with a patch in 2.6.10-rc3-mm1 (frv-better-mmap-support-in-uclinux.patch), which removes
mm/prio_treein systems without an MMU. Not making that other patch provide a dummy forprio_tree_initshould resolve the conflict. (That's just from reading the patch - I haven't actually tried this.)
The prio_tree stuff can go back in for the nommu stuff. I've given Andrew a patch to that effect (copied to LKML).
Subject: [PATCH] Cross-reference nommu VMAs with mappings Date: Wed, 15 Dec 2004 15:55:35 +0000
David
Re: [PATCH 3/3] prio_tree: move general code from mm/ to lib/
Quoted 2 lines · David Howells
The
prio_treestuff can go back in for the nommu stuff. I've given Andrew a patch to that effect (copied to LKML).
Ah, found it. Great, so there should be no conflict anymore.
Thanks,
- Werner
Signature · 3 lines
_________________________________________________________________________ / Werner Almesberger, Buenos Aires, Argentina [email protected] / /_http://www.almesberger.net/____________________________________________/
Quoted 7 lines · Werner Almesberger
This patch set for 2.6.10-rc3-bk10 (*) generalizes the
prio_tree
code such that other subsystems than just VMA can use it.(*) I'm not sure which tree is the most useful base for this.
Should I use 2.6.10-rc3-mm* instead of -bk* ? (Or is it already too late for 2.6.10 anyway ?)
Yes, 2.6.10 is in locked-down-try-to-get-the-bugs-out mode. I'll queue these up for post-2.6.10.