linux-kernel.vger.kernel.org archive mirror
 help / color / mirror / Atom feed
* [PATCH 0/2] [RFC] Volatile ranges (v4)
@ 2012-03-16 22:51 John Stultz
  2012-03-16 22:51 ` [PATCH 1/2] [RFC] Range tree implementation John Stultz
                   ` (2 more replies)
  0 siblings, 3 replies; 31+ messages in thread
From: John Stultz @ 2012-03-16 22:51 UTC (permalink / raw)
  To: linux-kernel
  Cc: John Stultz, Andrew Morton, Android Kernel Team, Robert Love,
	Mel Gorman, Hugh Dickins, Dave Hansen, Rik van Riel,
	Dmitry Adamushko, Dave Chinner, Neil Brown, Andrea Righi,
	Aneesh Kumar K.V

Ok. So here's another iteration of the fadvise volatile range code.

I realize this is still a way off from being ready, but I wanted to post
what I have to share with folks working on the various range/interval
management ideas as well as update folks who've provided feedback on the
volatile range code.

So just on the premise: Ideally, I want delayed reclaim based hole
punching.

Application has a possibly shared mmapped cache file, which it can mark
chunks of which volatile or nonvolatile as it uses it.  If the kernel
needs memory, it can zap any ranges that are currently marked volatile. 

Some examples would be rendering of images or web pages that are not
on-screen. This allows the application to volunteer memory for
reclaiming, and the kernel to grab it only when it needs. This differs
from some of the memory notification schemes, in that it allows the
kernel to immediately reclaim what it needs, rather then having to
request applications to give up memory (which may add further memory
load) until enough is free. However, unlike the notification scheme,
it does require applications to mark and unmark pages as volatile as
they use them.

Current use cases (ie: users of Android's ashmem) only use shmfs/tmpfs.
However, I don't see right off why it should be limited to shm. As long
as punching a hole in a file can be done w/ minimal memory overhead
this could be useful and have somewhat sane behavior. 

We could also only zap the page cache, not writing any dirty data out.
However, w/ non-shm files, discarding dirty data without hole punching
would obviously leave persistent files in a non-coherent state. This
may further re-inforce that the design should be shm only if we don't
do hole punching.

On the topic of hole punching, the kernel doesn't seem completely
unified in this as well. As I understand, there are two methods to do
hole punching: FALLOCATE_FL_PUNCH_HOLE vs MADV_REMOVE, and they don't
necessarily overlap in support. For the most part, it seems persistent
filesystems require FALLOCATE_FL_PUNCH, where as shmfs/tmpfs uses
MADV_REMOVE. But I may be misunderstanding the subtle difference here,
so if anyone wants to clarify this, it would be great.

One concern was that if the design is shm only, fadvise might not be
the right interface, as it should be generic. The
madvise(MADV_REMOVE,...) interface gives some precedence to shmfs/tmpfs
only calls, but I'd like to get some further feedback as to what folks
think of this. If we are shm/tmpfs only, I could rework this design to
use madvise instead of fadvise if folks would prefer.

Also, there's still the issue that lockdep doesn't like me calling
vmtruncate_range from the shrinker due to any allocations being done
while the i_mutex is taken could cause the shrinker to run and need the
i_mutex again.  I did try using invalidate_inode_pages2_range() but it
always returns EBUSY in this context, so I suspect I want something
else. I'm currently reading shmem_truncate_range() and zap_page_range()
to get a better idea of how to this might be best accomplished.

Regarding feedback suggesting dropping the LRU ranges, and instead
keeping the volatile/purged data in radix tags and to manage things at
writeout time. My concern there is having the LRU behavior on the
entire range from when it was marked volatile instead of the actual
last page access (you might have ranges that have frequent use areas
and non-frequent use).  Also sorting out how to evict the entire range
when one page is dropped might be funky.  However, I'll likely revisit
this soon, but for this iteration I didn't get to it.

I still also realize I have the issue of bloating the address_space
structure to handle, and I suspect if I continue w/ this approach
I'll use a separate hash table to store the range-tree roots in my
next revision.

Anyway, thanks for the continued advice and feedback!
-john

CC: Andrew Morton <akpm@linux-foundation.org>
CC: Android Kernel Team <kernel-team@android.com>
CC: Robert Love <rlove@google.com>
CC: Mel Gorman <mel@csn.ul.ie>
CC: Hugh Dickins <hughd@google.com>
CC: Dave Hansen <dave@linux.vnet.ibm.com>
CC: Rik van Riel <riel@redhat.com>
CC: Dmitry Adamushko <dmitry.adamushko@gmail.com>
CC: Dave Chinner <david@fromorbit.com>
CC: Neil Brown <neilb@suse.de>
CC: Andrea Righi <andrea@betterlinux.com>
CC: Aneesh Kumar K.V <aneesh.kumar@linux.vnet.ibm.com>

John Stultz (2):
  [RFC] Range tree implementation
  [RFC] fadvise: Add _VOLATILE,_ISVOLATILE, and _NONVOLATILE flags

 fs/inode.c                |    4 +
 include/linux/fadvise.h   |    5 +
 include/linux/fs.h        |    2 +
 include/linux/rangetree.h |   53 ++++++++
 include/linux/volatile.h  |   14 ++
 lib/Makefile              |    2 +-
 lib/rangetree.c           |  105 +++++++++++++++
 mm/Makefile               |    2 +-
 mm/fadvise.c              |   16 ++-
 mm/volatile.c             |  313 +++++++++++++++++++++++++++++++++++++++++++++
 10 files changed, 513 insertions(+), 3 deletions(-)
 create mode 100644 include/linux/rangetree.h
 create mode 100644 include/linux/volatile.h
 create mode 100644 lib/rangetree.c
 create mode 100644 mm/volatile.c

-- 
1.7.3.2.146.gca209


^ permalink raw reply	[flat|nested] 31+ messages in thread
* [PATCH 0/2][RFC] Volatile Ranges (v7)
@ 2012-04-14  1:07 John Stultz
  2012-04-14  1:08 ` [PATCH 2/2] [RFC] fadvise: Add _VOLATILE,_ISVOLATILE, and _NONVOLATILE flags John Stultz
  0 siblings, 1 reply; 31+ messages in thread
From: John Stultz @ 2012-04-14  1:07 UTC (permalink / raw)
  To: linux-kernel
  Cc: John Stultz, Andrew Morton, Android Kernel Team, Robert Love,
	Mel Gorman, Hugh Dickins, Dave Hansen, Rik van Riel,
	Dmitry Adamushko, Dave Chinner, Neil Brown, Andrea Righi,
	Aneesh Kumar K.V

Another week, another volatile range patch iteration.

So I think this is starting to shape up, and given the muted response
to the last few iterations, next time I may need to drop the RFC to
scare folks into taking a serious look at this.

This round tries to address the outstanding lockdep issue of calling
vmtruncate_range form a shrinker. My solution here is to call
shmem_truncate_range directly, which results in this functionality
being a tmpfs only feature for now. I know there was some concern
over using a generic fadvise interface for a tmpfs only feature,
and while I'd like this to be more generic, it may really only make
sense for tmpfs files. Also the MADV_REMOVE interface provides
similar effective tmpfs only (well, nilfs2 supports it too) precedent.
Thoughts here about what would be the most appropriate interface
would be appreciated (does madvise make more sense for tmpfs only?).

Also I reworked the code so the volatile ranges won't persist if
all the fds have been closed. I think this avoids possible
surprising effects of volatile pages if they were allowed to
persist across multiple non-concurrent opens.

Finally Dmitry Adamushko pointed out a race and some other minor
fixes that I corrected.

As always, your feedback is greatly appreciated.

thanks
-john

CC: Andrew Morton <akpm@linux-foundation.org>
CC: Android Kernel Team <kernel-team@android.com>
CC: Robert Love <rlove@google.com>
CC: Mel Gorman <mel@csn.ul.ie>
CC: Hugh Dickins <hughd@google.com>
CC: Dave Hansen <dave@linux.vnet.ibm.com>
CC: Rik van Riel <riel@redhat.com>
CC: Dmitry Adamushko <dmitry.adamushko@gmail.com>
CC: Dave Chinner <david@fromorbit.com>
CC: Neil Brown <neilb@suse.de>
CC: Andrea Righi <andrea@betterlinux.com>
CC: Aneesh Kumar K.V <aneesh.kumar@linux.vnet.ibm.com>


John Stultz (2):
  [RFC] Range tree implementation
  [RFC] fadvise: Add _VOLATILE,_ISVOLATILE, and _NONVOLATILE flags

 fs/file_table.c           |    4 +
 include/linux/fadvise.h   |    5 +
 include/linux/rangetree.h |   56 ++++++
 include/linux/volatile.h  |   12 ++
 lib/Makefile              |    2 +-
 lib/rangetree.c           |  128 +++++++++++++
 mm/Makefile               |    2 +-
 mm/fadvise.c              |   16 ++-
 mm/volatile.c             |  457 +++++++++++++++++++++++++++++++++++++++++++++
 9 files changed, 679 insertions(+), 3 deletions(-)
 create mode 100644 include/linux/rangetree.h
 create mode 100644 include/linux/volatile.h
 create mode 100644 lib/rangetree.c
 create mode 100644 mm/volatile.c

-- 
1.7.3.2.146.gca209


^ permalink raw reply	[flat|nested] 31+ messages in thread
* [PATCH 0/2] [RFC] Volatile Ranges (v6)
@ 2012-04-07  0:08 John Stultz
  2012-04-07  0:08 ` [PATCH 2/2] [RFC] fadvise: Add _VOLATILE,_ISVOLATILE, and _NONVOLATILE flags John Stultz
  0 siblings, 1 reply; 31+ messages in thread
From: John Stultz @ 2012-04-07  0:08 UTC (permalink / raw)
  To: linux-kernel
  Cc: John Stultz, Andrew Morton, Android Kernel Team, Robert Love,
	Mel Gorman, Hugh Dickins, Dave Hansen, Rik van Riel,
	Dmitry Adamushko, Dave Chinner, Neil Brown, Andrea Righi,
	Aneesh Kumar K.V

Just wanted to send out another iteration of the volatile range code
for review and comment. 

This revision handles some improved coalescing logic, fixes some bugs
in the range tree management, and also utilizes a hash tabe to avoid
bloating the address_space structure with range_tree pointers.

Still looking for guidence on what a better interface for this might be
as well as thoughts on how to best drop the ranges under memory pressure
(vmtruncate_region is pretty close to what I want, but lockdep makes it
clear that its not really safe to call from a shrinker).

Another detail is that by hanging the volatile ranges off of the
address_space, the volatility for tmpfs files persists even when no one
has an open fd on the file. This could cause some surprises if application
A marked some pages volatile and died, then application B opened the file
and had pages dropped out underneith it while it was being used. I suspect
I need to clean up the volatility when all fds are dropped. But any extra
insight would be useful here.


Thanks for the continued advice and feedback!
-john

CC: Andrew Morton <akpm@linux-foundation.org>
CC: Android Kernel Team <kernel-team@android.com>
CC: Robert Love <rlove@google.com>
CC: Mel Gorman <mel@csn.ul.ie>
CC: Hugh Dickins <hughd@google.com>
CC: Dave Hansen <dave@linux.vnet.ibm.com>
CC: Rik van Riel <riel@redhat.com>
CC: Dmitry Adamushko <dmitry.adamushko@gmail.com>
CC: Dave Chinner <david@fromorbit.com>
CC: Neil Brown <neilb@suse.de>
CC: Andrea Righi <andrea@betterlinux.com>
CC: Aneesh Kumar K.V <aneesh.kumar@linux.vnet.ibm.com>

John Stultz (2):
  [RFC] Range tree implementation
  [RFC] fadvise: Add _VOLATILE,_ISVOLATILE, and _NONVOLATILE flags

 fs/inode.c                |    2 +
 include/linux/fadvise.h   |    5 +
 include/linux/rangetree.h |   56 ++++++
 include/linux/volatile.h  |   14 ++
 lib/Makefile              |    2 +-
 lib/rangetree.c           |  128 +++++++++++++
 mm/Makefile               |    2 +-
 mm/fadvise.c              |   16 ++-
 mm/volatile.c             |  440 +++++++++++++++++++++++++++++++++++++++++++++
 9 files changed, 662 insertions(+), 3 deletions(-)
 create mode 100644 include/linux/rangetree.h
 create mode 100644 include/linux/volatile.h
 create mode 100644 lib/rangetree.c
 create mode 100644 mm/volatile.c

-- 
1.7.3.2.146.gca209


^ permalink raw reply	[flat|nested] 31+ messages in thread
* [PATCH 0/2] [RFC] fadivse volatile & range tree (v5)
@ 2012-03-21  4:15 John Stultz
  2012-03-21  4:15 ` [PATCH 2/2] [RFC] fadvise: Add _VOLATILE,_ISVOLATILE, and _NONVOLATILE flags John Stultz
  0 siblings, 1 reply; 31+ messages in thread
From: John Stultz @ 2012-03-21  4:15 UTC (permalink / raw)
  To: linux-kernel
  Cc: John Stultz, Andrew Morton, Android Kernel Team, Robert Love,
	Mel Gorman, Hugh Dickins, Dave Hansen, Rik van Riel,
	Dmitry Adamushko, Dave Chinner, Neil Brown, Andrea Righi,
	Aneesh Kumar K.V

Just another quick iteration here trying to address some of the comments
made on my last patchset.

I've merged most of the easy fixes and feedback in. Also I've improved
the volatile range coalescing/removing code to avoid re-starting lookups
every time from the root node. We also avoid coalescing with neighboring
ranges that have been purged. Although since we coalesce overlapping
purged ranges, there are still some semantics to work out there (ie:
do we immediately zap the newly volatile range if we coalesce w/ a 
purged range? or break it into smaller un-purged sub-ranges?).

I haven't yet been able to really digest the prio_tree code, that Dmitry
suggested, but it seem like it might be applicable here. One issue there
is that the start,last tuples are longs, so changes there might be
necessary to handle 64bit file ranges. We'll see.

Also, I still want to try implementing DaveC's suggested radix tree
tag method. It would be significantly different from this, so I wanted
to get all the feedback for this "branch" of investigation merged and
published, so I can come back to it if the radix tree tag idea doesn't
pan out.

Thanks again for all the great feedback!
-john

CC: Andrew Morton <akpm@linux-foundation.org>
CC: Android Kernel Team <kernel-team@android.com>
CC: Robert Love <rlove@google.com>
CC: Mel Gorman <mel@csn.ul.ie>
CC: Hugh Dickins <hughd@google.com>
CC: Dave Hansen <dave@linux.vnet.ibm.com>
CC: Rik van Riel <riel@redhat.com>
CC: Dmitry Adamushko <dmitry.adamushko@gmail.com>
CC: Dave Chinner <david@fromorbit.com>
CC: Neil Brown <neilb@suse.de>
CC: Andrea Righi <andrea@betterlinux.com>
CC: Aneesh Kumar K.V <aneesh.kumar@linux.vnet.ibm.com>


John Stultz (2):
  [RFC] Range tree implementation
  [RFC] fadvise: Add _VOLATILE,_ISVOLATILE, and _NONVOLATILE flags

 fs/inode.c                |    4 +
 include/linux/fadvise.h   |    5 +
 include/linux/fs.h        |    2 +
 include/linux/rangetree.h |   56 ++++++++
 include/linux/volatile.h  |   14 ++
 lib/Makefile              |    2 +-
 lib/rangetree.c           |  124 ++++++++++++++++
 mm/Makefile               |    2 +-
 mm/fadvise.c              |   16 ++-
 mm/volatile.c             |  342 +++++++++++++++++++++++++++++++++++++++++++++
 10 files changed, 564 insertions(+), 3 deletions(-)
 create mode 100644 include/linux/rangetree.h
 create mode 100644 include/linux/volatile.h
 create mode 100644 lib/rangetree.c
 create mode 100644 mm/volatile.c

-- 
1.7.3.2.146.gca209


^ permalink raw reply	[flat|nested] 31+ messages in thread
* [PATCH 1/2] [RFC] Range tree implementation
@ 2012-02-10  0:16 John Stultz
  2012-02-10  0:16 ` [PATCH 2/2] [RFC] fadvise: Add _VOLATILE,_ISVOLATILE, and _NONVOLATILE flags John Stultz
  0 siblings, 1 reply; 31+ messages in thread
From: John Stultz @ 2012-02-10  0:16 UTC (permalink / raw)
  To: linux-kernel
  Cc: John Stultz, Andrew Morton, Android Kernel Team, Robert Love,
	Mel Gorman, Hugh Dickins, Dave Hansen, Rik van Riel

After Andrew suggested something like his mumbletree idea
to better store a list of ranges, I worked on a few different
approaches, and this is what I've finally managed to get working.

I suspect range-tree isn't a totally accurate name, but I
couldn't quite make out the difference between range trees
and interval trees, so I just picked one to call it. Do
let me know if you have a better name.

The idea of storing ranges in a tree is nice, but has a number
of complications. When adding a range, its possible that a
large range will consume and merge a number of smaller ranges.
When removing a range, its possible you may end up splitting an
existing range, causing one range to become two. This makes it
very difficult to provide generic list_head like behavior, as
the parent structures would need to be duplicated and removed,
and that has lots of memory ownership issues.

So, this is a much simplified and more list_head like
implementation. You can add a node to a tree, or remove a node
to a tree, but the generic implementation doesn't do the
merging or splitting for you. But it does provide helpers to
find overlapping and adjacent ranges.

I made the tree self-balancing via splaying as it seemed easier
to handle with the merging/splitting cases I originally tried to
make the generic code handle, but since I've dropped that, I
suspect it can be reworked to use a rbtree. I just wanted to get
this out for initial review.

Andrew also really wanted this range-tree implementation to be
resuable so we don't duplicate the file locking logic. I'm not
totally convinced that the requirements between the volatile
ranges and file locking are really equivelent, but this reduced
impelementation may make it possible.

Do let me know what you think or if you have other ideas for
better ways to do the same.

thanks
-john

CC: Andrew Morton <akpm@linux-foundation.org>
CC: Android Kernel Team <kernel-team@android.com>
CC: Robert Love <rlove@google.com>
CC: Mel Gorman <mel@csn.ul.ie>
CC: Hugh Dickins <hughd@google.com>
CC: Dave Hansen <dave@linux.vnet.ibm.com>
CC: Rik van Riel <riel@redhat.com>
Signed-off-by: John Stultz <john.stultz@linaro.org>
---
 include/linux/rangetree.h |   35 +++++
 lib/Makefile              |    2 +-
 lib/rangetree.c           |  325 +++++++++++++++++++++++++++++++++++++++++++++
 3 files changed, 361 insertions(+), 1 deletions(-)
 create mode 100644 include/linux/rangetree.h
 create mode 100644 lib/rangetree.c

diff --git a/include/linux/rangetree.h b/include/linux/rangetree.h
new file mode 100644
index 0000000..998ebcc
--- /dev/null
+++ b/include/linux/rangetree.h
@@ -0,0 +1,35 @@
+#ifndef _LINUX_RANGETREE_H
+#define _LINUX_RANGETREE_H
+
+#include <linux/types.h>
+#include <linux/fs.h>
+
+struct range_tree_node {
+	struct range_tree_node *parent;
+	struct range_tree_node *left;
+	struct range_tree_node *right;
+	u64 start;
+	u64 end;
+};
+
+static inline void range_tree_node_init(struct range_tree_node *node)
+{
+	node->parent = NULL;
+	node->left = NULL;
+	node->right = NULL;
+	node->start = 0;
+	node->end = 0;
+}
+
+extern struct range_tree_node *range_tree_in_range(struct range_tree_node *root,
+							 u64 start, u64 end);
+extern struct range_tree_node *range_tree_in_range_adjacent(
+						struct range_tree_node *root,
+							 u64 start, u64 end);
+extern struct range_tree_node *range_tree_add(struct range_tree_node *root,
+						struct range_tree_node *node);
+extern struct range_tree_node *range_tree_remove(struct range_tree_node *root,
+						struct range_tree_node *node);
+#endif
+
+
diff --git a/lib/Makefile b/lib/Makefile
index 18515f0..f43ef0d 100644
--- a/lib/Makefile
+++ b/lib/Makefile
@@ -12,7 +12,7 @@ lib-y := ctype.o string.o vsprintf.o cmdline.o \
 	 idr.o int_sqrt.o extable.o prio_tree.o \
 	 sha1.o md5.o irq_regs.o reciprocal_div.o argv_split.o \
 	 proportions.o prio_heap.o ratelimit.o show_mem.o \
-	 is_single_threaded.o plist.o decompress.o
+	 is_single_threaded.o plist.o decompress.o rangetree.o
 
 lib-$(CONFIG_MMU) += ioremap.o
 lib-$(CONFIG_SMP) += cpumask.o
diff --git a/lib/rangetree.c b/lib/rangetree.c
new file mode 100644
index 0000000..db20665
--- /dev/null
+++ b/lib/rangetree.c
@@ -0,0 +1,325 @@
+#include <linux/rangetree.h>
+#include <linux/kernel.h>
+#include <linux/slab.h>
+
+/**
+ * rotate_right - Splay tree helper
+ * @node: node to be rotated right
+ * @root: tree root
+ *
+ * Returns the tree root after rotating the node right
+ */
+static struct range_tree_node *rotate_right(struct range_tree_node *node,
+						struct range_tree_node *root)
+{
+	struct range_tree_node *new_root, *new_right;
+
+	if (!node->left)
+		return root;
+
+	new_root = node->left;
+	new_right = node;
+
+	if (root == node)
+		root = new_root;
+
+	new_right->left = new_root->right;
+	new_root->parent = new_right->parent;
+	if (new_root->parent) {
+		if (new_root->parent->right == new_right)
+			new_root->parent->right = new_root;
+		else
+			new_root->parent->left = new_root;
+	}
+	new_right->parent = new_root;
+
+	new_root->right = new_right;
+
+	if (new_right->left)
+		new_right->left->parent = new_right;
+
+
+	/* Paranoid sanity checking */
+	if (new_root->left)
+		BUG_ON(new_root->left->parent != new_root);
+	if (new_root->right)
+		BUG_ON(new_root->right->parent != new_root);
+	if (new_right->left)
+		BUG_ON(new_right->left->parent != new_right);
+	if (new_right->right)
+		BUG_ON(new_right->right->parent != new_right);
+
+
+	return root;
+
+}
+
+/**
+ * rotate_left - Splay tree helper
+ * @node: node to be rotated left
+ * @root: tree root
+ *
+ * Returns the tree root after rotating the node left
+ */
+static struct range_tree_node *rotate_left(struct range_tree_node *node,
+						struct range_tree_node *root)
+{
+	struct range_tree_node *new_root, *new_left;
+
+	if (!node->right)
+		return root;
+
+	new_root = node->right;
+	new_left = node;
+
+	if (root == node)
+		root = new_root;
+
+	new_left->right = new_root->left;
+	if (new_left->right)
+		new_left->right->parent = new_left;
+	new_root->parent = new_left->parent;
+	if (new_root->parent) {
+		if (new_root->parent->left == new_left)
+			new_root->parent->left = new_root;
+		else
+			new_root->parent->right = new_root;
+	}
+	new_left->parent = new_root;
+	new_root->left = new_left;
+
+
+	/* Paranoid sanity checking */
+	if (new_root->left)
+		BUG_ON(new_root->left->parent != new_root);
+	if (new_root->right)
+		BUG_ON(new_root->right->parent != new_root);
+	if (new_left->left)
+		BUG_ON(new_left->left->parent != new_left);
+	if (new_left->right)
+		BUG_ON(new_left->right->parent != new_left);
+
+	return root;
+}
+
+/**
+ * splay_tree Splays a node to the top of a tree
+ * @root: root of the splay tree
+ * @node: node to be splayed to the root
+ *
+ * Returns the root of a tree after splaying
+ */
+static struct range_tree_node *splay_tree(struct range_tree_node *root,
+						struct range_tree_node *node)
+{
+restart:
+	if (root == node)
+		return root;
+
+	if (node->parent == root) {
+		if (root->left == node)
+			root = rotate_right(root, root);
+		else
+			root = rotate_left(root, root);
+		return root;
+	} else {
+		struct range_tree_node *parent, *grandparent;
+
+		parent = node->parent;
+		grandparent = parent->parent;
+
+		if ((node == parent->left) && (parent == grandparent->left)) {
+			root = rotate_right(grandparent, root);
+			root = rotate_right(parent, root);
+		} else if ((node == parent->right) &&
+					(parent == grandparent->right)) {
+			root = rotate_left(grandparent, root);
+			root = rotate_left(parent, root);
+		} else if ((node == parent->right) &&
+					(parent == grandparent->left)) {
+			root = rotate_left(parent, root);
+			root = rotate_right(grandparent, root);
+		} else if ((node == parent->left) &&
+					(parent == grandparent->right)) {
+			root = rotate_right(parent, root);
+			root = rotate_left(grandparent, root);
+		} else {
+			BUG_ON(1); /* Something is odd */
+		}
+		goto restart;
+	}
+}
+
+
+/**
+ * range_tree_in_range - Returns the first node that overlaps with the
+ *                       given range
+ * @root: range_tree root
+ * @start: range start
+ * @end: range end
+ *
+ */
+struct range_tree_node *range_tree_in_range(struct range_tree_node *root,
+						u64 start, u64 end)
+{
+	struct range_tree_node *candidate = root;
+
+	if (!candidate)
+		return 0;
+restart:
+	if (end < candidate->start) {
+		if (candidate->left) {
+			candidate = candidate->left;
+			goto restart;
+		}
+	} else if (start > candidate->end) {
+		if (candidate->right) {
+			candidate = candidate->right;
+			goto restart;
+		}
+	} else
+		return candidate;
+	return 0;
+}
+
+
+/**
+ * range_tree_in_range - Returns the first node that overlaps or is adjacent
+ *                       with the given range
+ * @root: range_tree root
+ * @start: range start
+ * @end: range end
+ *
+ */
+struct range_tree_node *range_tree_in_range_adjacent(
+					struct range_tree_node *root,
+					u64 start, u64 end)
+{
+	struct range_tree_node *candidate = root;
+
+	if (!candidate)
+		return 0;
+restart:
+	if (end + 1 < candidate->start) {
+		if (candidate->left) {
+			candidate = candidate->left;
+			goto restart;
+		}
+	} else if (start > candidate->end + 1) {
+		if (candidate->right) {
+			candidate = candidate->right;
+			goto restart;
+		}
+	} else
+		return candidate;
+	return 0;
+}
+
+/**
+ * range_tree_add - Add a node to a range tree
+ * @root: range tree to be added to
+ * @node: range_tree_node to be added
+ *
+ * Adds a node to the range tree.
+ */
+struct range_tree_node *range_tree_add(struct range_tree_node *root,
+					struct range_tree_node *node)
+{
+	struct range_tree_node *candidate;
+	/* make sure its not connected */
+	BUG_ON(node->parent || node->left || node->right);
+
+	if (!root)
+		return node;
+
+	candidate = root;
+restart:
+	if (node->start < candidate->start) {
+		if (candidate->left) {
+			candidate = candidate->left;
+			goto restart;
+		}
+		candidate->left = node;
+		node->parent = candidate;
+	} else if (node->start > candidate->start) {
+		if (candidate->right) {
+			candidate = candidate->right;
+			goto restart;
+		}
+		candidate->right = node;
+		node->parent = candidate;
+	}
+
+	root = splay_tree(root, node);
+	return root;
+}
+
+/**
+ * range_tree_merge - Helper to merge two range sub-trees
+ * @left: left subtree to be merged
+ * @right: right subtree to be merged
+ *
+ * Returns a merged range tree of two subtrees. left subtree
+ * must be all less then the right subtree.
+ */
+static struct range_tree_node *range_tree_merge(struct range_tree_node *left,
+						struct range_tree_node *right)
+{
+	struct range_tree_node *merge;
+
+	if (!left)
+		return right;
+	if (!right)
+		return left;
+
+	merge = left;
+	/* grab the right-most node on the left side */
+	while (merge->right)
+		merge = merge->right;
+	merge->right = right;
+	if (right)
+		right->parent = merge;
+
+	return left;
+}
+
+/**
+ * null_node: Helper that clears node data
+ * @node: Node to be cleared
+ */
+static void null_node(struct range_tree_node *node)
+{
+	node->left = node->right = node->parent = NULL;
+	node->start = node->end = 0;
+}
+
+/**
+ * range_tree_remove: Removes a given node from the tree
+ * @root: root of tree
+ * @node: Node to be removed
+ *
+ * Removes a node and splays the tree
+ */
+struct range_tree_node *range_tree_remove(struct range_tree_node *root,
+						struct range_tree_node *node)
+{
+	struct range_tree_node *subtree;
+
+	subtree = range_tree_merge(node->left, node->right);
+
+	if (subtree)
+		subtree->parent = node->parent;
+
+	if (node->parent && node->parent->left == node)
+		node->parent->left = subtree;
+	if (node->parent && node->parent->right == node)
+		node->parent->right = subtree;
+
+	null_node(node);
+	if (node == root)
+		return subtree;
+
+	if (subtree)
+		root = splay_tree(root, subtree);
+	return root;
+}
-- 
1.7.3.2.146.gca209


^ permalink raw reply related	[flat|nested] 31+ messages in thread

end of thread, other threads:[~2012-07-21 11:17 UTC | newest]

Thread overview: 31+ messages (download: mbox.gz follow: Atom feed
-- links below jump to the message on this page --
2012-03-16 22:51 [PATCH 0/2] [RFC] Volatile ranges (v4) John Stultz
2012-03-16 22:51 ` [PATCH 1/2] [RFC] Range tree implementation John Stultz
2012-03-20 10:00   ` Dmitry Adamushko
2012-03-20 18:04     ` John Stultz
2012-03-20 16:44   ` Aneesh Kumar K.V
2012-03-16 22:51 ` [PATCH 2/2] [RFC] fadvise: Add _VOLATILE,_ISVOLATILE, and _NONVOLATILE flags John Stultz
2012-03-17  0:47   ` [PATCH] fadvise volatile fixes from Dmitry John Stultz
2012-03-17 16:21   ` [PATCH 2/2] [RFC] fadvise: Add _VOLATILE,_ISVOLATILE, and _NONVOLATILE flags Dmitry Adamushko
2012-03-18  9:13     ` Dmitry Adamushko
2012-03-20  0:18     ` John Stultz
2012-07-19 10:13 ` [PATCH 0/2] [RFC] Volatile ranges (v4) Dmitry Vyukov
     [not found]   ` <CAO6Zf6BSpq53UqYjCkq0b3pTPW=WDTnCorQ59tONnV7U-U6EOg@mail.gmail.com>
     [not found]     ` <CACT4Y+ZgBo9=HX5MHhmWBiQcdiGMss9RSS_reF4gJimivJx7sQ@mail.gmail.com>
2012-07-21 11:17       ` Dmitry Adamushko
  -- strict thread matches above, loose matches on Subject: below --
2012-04-14  1:07 [PATCH 0/2][RFC] Volatile Ranges (v7) John Stultz
2012-04-14  1:08 ` [PATCH 2/2] [RFC] fadvise: Add _VOLATILE,_ISVOLATILE, and _NONVOLATILE flags John Stultz
2012-04-07  0:08 [PATCH 0/2] [RFC] Volatile Ranges (v6) John Stultz
2012-04-07  0:08 ` [PATCH 2/2] [RFC] fadvise: Add _VOLATILE,_ISVOLATILE, and _NONVOLATILE flags John Stultz
2012-03-21  4:15 [PATCH 0/2] [RFC] fadivse volatile & range tree (v5) John Stultz
2012-03-21  4:15 ` [PATCH 2/2] [RFC] fadvise: Add _VOLATILE,_ISVOLATILE, and _NONVOLATILE flags John Stultz
2012-02-10  0:16 [PATCH 1/2] [RFC] Range tree implementation John Stultz
2012-02-10  0:16 ` [PATCH 2/2] [RFC] fadvise: Add _VOLATILE,_ISVOLATILE, and _NONVOLATILE flags John Stultz
2012-02-12 14:08   ` Dmitry Adamushko
2012-02-17  3:49     ` John Stultz
2012-02-14  5:16   ` Dave Chinner
2012-02-14  5:55     ` John Stultz
2012-02-14 23:51       ` Dave Chinner
2012-02-15  0:29         ` John Stultz
2012-02-15  1:37           ` NeilBrown
2012-02-17  4:45             ` Dave Chinner
2012-02-17  5:27               ` NeilBrown
2012-02-17  5:38               ` John Stultz
2012-02-17  5:21             ` John Stultz
2012-02-20  7:34               ` NeilBrown
2012-02-20 23:25                 ` Dave Hansen
     [not found]   ` <CAO6Zf6B6nGqsz5zpT3ixbO-+JWxMsScABasnwo-CVHuMKPqpLQ@mail.gmail.com>
2012-02-17  3:43     ` John Stultz
2012-02-17  5:24       ` John Stultz

This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for NNTP newsgroup(s).