From: Andrea Righi <andrea@betterlinux.com>
To: Andrew Morton <akpm@linux-foundation.org>
Cc: "Minchan Kim" <minchan.kim@gmail.com>,
"Peter Zijlstra" <a.p.zijlstra@chello.nl>,
"Johannes Weiner" <jweiner@redhat.com>,
"KAMEZAWA Hiroyuki" <kamezawa.hiroyu@jp.fujitsu.com>,
"KOSAKI Motohiro" <kosaki.motohiro@jp.fujitsu.com>,
"Rik van Riel" <riel@redhat.com>,
"Hugh Dickins" <hughd@google.com>,
"Alexander Viro" <viro@zeniv.linux.org.uk>,
"Shaohua Li" <shaohua.li@intel.com>,
"Pádraig Brady" <P@draigBrady.com>,
"John Stultz" <john.stultz@linaro.org>,
"Jerry James" <jamesjer@betterlinux.com>,
"Julius Plenz" <julius@plenz.com>, linux-mm <linux-mm@kvack.org>,
linux-fsdevel@vger.kernel.org,
LKML <linux-kernel@vger.kernel.org>
Subject: Re: [PATCH v5 1/3] kinterval: routines to manipulate generic intervals
Date: Mon, 13 Feb 2012 01:48:15 +0100 [thread overview]
Message-ID: <20120213004815.GA2052@thinkpad> (raw)
In-Reply-To: <1329006098-5454-2-git-send-email-andrea@betterlinux.com>
On Sun, Feb 12, 2012 at 01:21:36AM +0100, Andrea Righi wrote:
> Add a generic infrastructure to efficiently keep track of intervals.
>
> An interval is represented by a triplet (start, end, type). The values
> (start, end) define the bounds of the range. The type is a generic
> property associated to the interval. The interpretation of the type is
> left to the user.
>
> Multiple intervals associated to the same object are stored in an
> interval tree (augmented rbtree) [1], with tree ordered on starting
> address. The tree cannot contain multiple entries of different
> interval types which overlap; in case of overlapping intervals new
> inserted intervals overwrite the old ones (completely or in part, in the
> second case the old interval is shrunk or split accordingly).
>
> Reference:
> [1] "Introduction to Algorithms" by Cormen, Leiserson, Rivest and Stein
>
> Signed-off-by: Andrea Righi <andrea@betterlinux.com>
> ---
For those who are interested, I've an extra patch for this part (must be
applied on top of the previous one): there's a bug fix and some
improvements.
I'll include the following fix in the next version of the
POSIX_FADV_NOREUSE patch set (sorry for this, but the whole patch set is
still totally experimental, especially the kinterval part, I also plan
to add a proper documentation and a sample test case in the samples/ dir
if we think it'll be useful).
Thanks,
-Andrea
---
From: Andrea Righi <andrea@betterlinux.com>
Subject: [PATCH] kinterval: fix interval boundaries and optimize insertion/removal
Use the right notation [start, end) instead of [start, end] for interval
boundaries, so now an interval does not include its endpoint. This is
the natural way to define a memory range (i.e, 0-4096 = all bytes
between 0 and 4095 included) and it makes easier to reuse this code also
for other similar cases.
Moreover, optimize insertion and removal code to be actually O(log(n)).
Signed-off-by: Andrea Righi <andrea@betterlinux.com>
---
include/linux/kinterval.h | 2 +-
lib/kinterval.c | 135 ++++++++++++++++++++++++++++-----------------
2 files changed, 86 insertions(+), 51 deletions(-)
diff --git a/include/linux/kinterval.h b/include/linux/kinterval.h
index 8152265..7a505f4 100644
--- a/include/linux/kinterval.h
+++ b/include/linux/kinterval.h
@@ -114,7 +114,7 @@ long kinterval_lookup_range(struct rb_root *root, u64 start, u64 end);
*/
static inline long kinterval_lookup(struct rb_root *root, u64 addr)
{
- return kinterval_lookup_range(root, addr, addr);
+ return kinterval_lookup_range(root, addr, addr + 1);
}
/**
diff --git a/lib/kinterval.c b/lib/kinterval.c
index 2a9d463..28ee627 100644
--- a/lib/kinterval.c
+++ b/lib/kinterval.c
@@ -31,7 +31,7 @@ static struct kmem_cache *kinterval_cachep __read_mostly;
static bool is_interval_overlapping(struct kinterval *node, u64 start, u64 end)
{
- return !(node->start > end || node->end < start);
+ return !(node->start >= end || node->end <= start);
}
static u64 get_subtree_max_end(struct rb_node *node)
@@ -76,23 +76,65 @@ static struct kinterval *
kinterval_rb_lowest_match(struct rb_root *root, u64 start, u64 end)
{
struct rb_node *node = root->rb_node;
- struct kinterval *lower_range = NULL;
+ struct kinterval *lowest_match = NULL;
while (node) {
struct kinterval *range = rb_entry(node, struct kinterval, rb);
if (get_subtree_max_end(node->rb_left) > start) {
+ /* Lowest overlap if any must be on the left side */
node = node->rb_left;
} else if (is_interval_overlapping(range, start, end)) {
- lower_range = range;
+ lowest_match = range;
break;
} else if (start >= range->start) {
+ /* Lowest overlap if any must be on the right side */
node = node->rb_right;
} else {
break;
}
}
- return lower_range;
+ return lowest_match;
+}
+
+/*
+ * Merge two adjacent intervals, if they can be merged next is removed from the
+ * tree.
+ */
+static void kinterval_rb_merge_node(struct rb_root *root,
+ struct kinterval *prev, struct kinterval *next)
+{
+ struct rb_node *deepest;
+
+ if (prev && prev->type == next->type && prev->end == next->start) {
+ prev->end = next->end;
+ deepest = rb_augment_erase_begin(&next->rb);
+ rb_erase(&next->rb, root);
+ rb_augment_erase_end(deepest,
+ kinterval_rb_augment_cb, NULL);
+ kmem_cache_free(kinterval_cachep, next);
+ }
+}
+
+/*
+ * Try to merge a new inserted interval with the previous and the next
+ * interval.
+ */
+static void kinterval_rb_merge(struct rb_root *root, struct kinterval *new)
+{
+ struct kinterval *next, *prev;
+ struct rb_node *node;
+
+ node = rb_prev(&new->rb);
+ prev = node ? rb_entry(node, struct kinterval, rb) : NULL;
+
+ node = rb_next(&new->rb);
+ next = node ? rb_entry(node, struct kinterval, rb) : NULL;
+
+ if (next)
+ kinterval_rb_merge_node(root, new, next);
+ if (prev)
+ kinterval_rb_merge_node(root, prev, new);
}
static void
@@ -114,32 +156,8 @@ kinterval_rb_insert(struct rb_root *root, struct kinterval *new)
rb_link_node(&new->rb, parent, node);
rb_insert_color(&new->rb, root);
rb_augment_insert(&new->rb, kinterval_rb_augment_cb, NULL);
-}
-/* Merge adjacent intervals */
-static void kinterval_rb_merge(struct rb_root *root)
-{
- struct kinterval *next, *prev = NULL;
- struct rb_node *node, *deepest;
-
- node = rb_first(root);
- while (node) {
- next = rb_entry(node, struct kinterval, rb);
- node = rb_next(&next->rb);
-
- if (prev && prev->type == next->type &&
- prev->end == (next->start - 1) &&
- prev->end < next->start) {
- prev->end = next->end;
- deepest = rb_augment_erase_begin(&next->rb);
- rb_erase(&next->rb, root);
- rb_augment_erase_end(deepest,
- kinterval_rb_augment_cb, NULL);
- kmem_cache_free(kinterval_cachep, next);
- } else {
- prev = next;
- }
- }
+ kinterval_rb_merge(root, new);
}
static int kinterval_rb_check_add(struct rb_root *root,
@@ -148,16 +166,17 @@ static int kinterval_rb_check_add(struct rb_root *root,
struct kinterval *old;
struct rb_node *node, *deepest;
- node = rb_first(root);
+ old = kinterval_rb_lowest_match(root, new->start, new->end);
+ node = old ? &old->rb : NULL;
+
while (node) {
old = rb_entry(node, struct kinterval, rb);
+ node = rb_next(&old->rb);
+
/* Check all the possible matches within the range */
- if (old->start > new->end)
+ if (old->start >= new->end)
break;
- node = rb_next(&old->rb);
- if (!is_interval_overlapping(old, new->start, new->end))
- continue;
/*
* Interval is overlapping another one, shrink the old interval
* accordingly.
@@ -206,8 +225,12 @@ static int kinterval_rb_check_add(struct rb_root *root,
* new old
* |___________|_______|
*/
+ deepest = rb_augment_erase_begin(&old->rb);
rb_erase(&old->rb, root);
+ rb_augment_erase_end(deepest, kinterval_rb_augment_cb,
+ NULL);
old->start = new->end + 1;
+ old->subtree_max_end = old->end;
kinterval_rb_insert(root, old);
break;
} else if (new->start >= old->start && new->end >= old->end) {
@@ -230,7 +253,7 @@ static int kinterval_rb_check_add(struct rb_root *root,
rb_erase(&old->rb, root);
rb_augment_erase_end(deepest, kinterval_rb_augment_cb,
NULL);
- old->end = new->start - 1;
+ old->end = new->start;
old->subtree_max_end = old->end;
kinterval_rb_insert(root, old);
} else if (new->start >= old->start && new->end <= old->end) {
@@ -261,13 +284,17 @@ static int kinterval_rb_check_add(struct rb_root *root,
if (unlikely(!prev))
return -ENOMEM;
+ deepest = rb_augment_erase_begin(&old->rb);
rb_erase(&old->rb, root);
+ rb_augment_erase_end(deepest, kinterval_rb_augment_cb,
+ NULL);
prev->start = old->start;
- old->start = new->end + 1;
- prev->end = new->start - 1;
+ old->start = new->end;
+ prev->end = new->start;
prev->type = old->type;
+ old->subtree_max_end = old->end;
kinterval_rb_insert(root, old);
new->subtree_max_end = new->end;
@@ -280,7 +307,6 @@ static int kinterval_rb_check_add(struct rb_root *root,
}
new->subtree_max_end = new->end;
kinterval_rb_insert(root, new);
- kinterval_rb_merge(root);
return 0;
}
@@ -291,7 +317,7 @@ int kinterval_add(struct rb_root *root, u64 start, u64 end,
struct kinterval *range;
int ret;
- if (end < start)
+ if (end <= start)
return -EINVAL;
range = kmem_cache_zalloc(kinterval_cachep, flags);
if (unlikely(!range))
@@ -314,16 +340,17 @@ static int kinterval_rb_check_del(struct rb_root *root,
struct kinterval *old;
struct rb_node *node, *deepest;
- node = rb_first(root);
+ old = kinterval_rb_lowest_match(root, start, end);
+ node = old ? &old->rb : NULL;
+
while (node) {
old = rb_entry(node, struct kinterval, rb);
+ node = rb_next(&old->rb);
+
/* Check all the possible matches within the range */
- if (old->start > end)
+ if (old->start >= end)
break;
- node = rb_next(&old->rb);
- if (!is_interval_overlapping(old, start, end))
- continue;
if (start <= old->start && end >= old->end) {
/*
* Completely erase the old range:
@@ -354,8 +381,12 @@ static int kinterval_rb_check_del(struct rb_root *root,
* old
* |_______|
*/
+ deepest = rb_augment_erase_begin(&old->rb);
rb_erase(&old->rb, root);
- old->start = end + 1;
+ rb_augment_erase_end(deepest, kinterval_rb_augment_cb,
+ NULL);
+ old->start = end;
+ old->subtree_max_end = old->end;
kinterval_rb_insert(root, old);
break;
} else if (start >= old->start && end >= old->end) {
@@ -378,7 +409,7 @@ static int kinterval_rb_check_del(struct rb_root *root,
rb_erase(&old->rb, root);
rb_augment_erase_end(deepest, kinterval_rb_augment_cb,
NULL);
- old->end = start - 1;
+ old->end = start;
old->subtree_max_end = old->end;
kinterval_rb_insert(root, old);
} else if (start >= old->start && end <= old->end) {
@@ -403,13 +434,17 @@ static int kinterval_rb_check_del(struct rb_root *root,
if (unlikely(!prev))
return -ENOMEM;
+ deepest = rb_augment_erase_begin(&old->rb);
rb_erase(&old->rb, root);
+ rb_augment_erase_end(deepest, kinterval_rb_augment_cb,
+ NULL);
prev->start = old->start;
- old->start = end + 1;
- prev->end = start - 1;
+ old->start = end;
+ prev->end = start;
prev->type = old->type;
+ old->subtree_max_end = old->end;
kinterval_rb_insert(root, old);
prev->subtree_max_end = prev->end;
@@ -422,7 +457,7 @@ static int kinterval_rb_check_del(struct rb_root *root,
int kinterval_del(struct rb_root *root, u64 start, u64 end, gfp_t flags)
{
- if (end < start)
+ if (end <= start)
return -EINVAL;
return kinterval_rb_check_del(root, start, end, flags);
}
@@ -451,7 +486,7 @@ long kinterval_lookup_range(struct rb_root *root, u64 start, u64 end)
{
struct kinterval *range;
- if (end < start)
+ if (end <= start)
return -EINVAL;
range = kinterval_rb_lowest_match(root, start, end);
return range ? range->type : -ENOENT;
--
1.7.5.4
next prev parent reply other threads:[~2012-02-13 0:49 UTC|newest]
Thread overview: 27+ messages / expand[flat|nested] mbox.gz Atom feed top
2012-02-12 0:21 [RFC] [PATCH v5 0/3] fadvise: support POSIX_FADV_NOREUSE Andrea Righi
2012-02-12 0:21 ` [PATCH v5 1/3] kinterval: routines to manipulate generic intervals Andrea Righi
2012-02-13 0:48 ` Andrea Righi [this message]
2012-02-12 0:21 ` [PATCH v5 2/3] mm: filemap: introduce mark_page_usedonce Andrea Righi
2012-02-12 0:21 ` [PATCH v5 3/3] fadvise: implement POSIX_FADV_NOREUSE Andrea Righi
2012-02-13 16:22 ` KOSAKI Motohiro
2012-02-13 16:22 ` KOSAKI Motohiro
[not found] ` <CAHGf_=qs8-nE6y6EzNYUzgjGo0sMP5zvCc3=GNZmHct6mPecqg@mail.gmail.com>
2012-02-13 18:00 ` Andrea Righi
2012-02-15 23:35 ` Arun Sharma
2012-02-15 23:47 ` Andrea Righi
2012-02-15 23:57 ` Arun Sharma
2012-02-16 0:56 ` Andrea Righi
2012-02-16 2:10 ` Arun Sharma
2012-02-16 10:39 ` Andrea Righi
2012-02-16 18:43 ` Arun Sharma
2012-02-16 18:57 ` Andrea Righi
2012-02-16 19:07 ` Arun Sharma
2012-02-27 2:33 ` KAMEZAWA Hiroyuki
2012-02-27 10:46 ` Andrea Righi
2012-02-14 21:33 ` [RFC] [PATCH v5 0/3] fadvise: support POSIX_FADV_NOREUSE Andrew Morton
2012-02-14 22:06 ` John Stultz
2012-02-14 22:59 ` Andrea Righi
2012-02-14 23:22 ` Andrew Morton
2012-02-15 1:35 ` Andrea Righi
2012-02-15 23:48 ` KAMEZAWA Hiroyuki
2012-02-16 0:43 ` Andrea Righi
2014-01-02 21:25 ` Phillip Susi
Reply instructions:
You may reply publicly to this message via plain-text email
using any one of the following methods:
* Save the following mbox file, import it into your mail client,
and reply-to-all from there: mbox
Avoid top-posting and favor interleaved quoting:
https://en.wikipedia.org/wiki/Posting_style#Interleaved_style
* Reply using the --to, --cc, and --in-reply-to
switches of git-send-email(1):
git send-email \
--in-reply-to=20120213004815.GA2052@thinkpad \
--to=andrea@betterlinux.com \
--cc=P@draigBrady.com \
--cc=a.p.zijlstra@chello.nl \
--cc=akpm@linux-foundation.org \
--cc=hughd@google.com \
--cc=jamesjer@betterlinux.com \
--cc=john.stultz@linaro.org \
--cc=julius@plenz.com \
--cc=jweiner@redhat.com \
--cc=kamezawa.hiroyu@jp.fujitsu.com \
--cc=kosaki.motohiro@jp.fujitsu.com \
--cc=linux-fsdevel@vger.kernel.org \
--cc=linux-kernel@vger.kernel.org \
--cc=linux-mm@kvack.org \
--cc=minchan.kim@gmail.com \
--cc=riel@redhat.com \
--cc=shaohua.li@intel.com \
--cc=viro@zeniv.linux.org.uk \
/path/to/YOUR_REPLY
https://kernel.org/pub/software/scm/git/docs/git-send-email.html
* If your mail client supports setting the In-Reply-To header
via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line
before the message body.
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).