The Probabilistic Skiplist is a O(lgn) data structure, and
we want to apply this for later use, mainly for RCU-skiplist.

Note:
a) The skiplist is probabilistic, and it is the distribution of node sizes
   that is maintained, but the strict order is not required[1].

b) This skiplist cannot be resized once it is created,
   so here is a level limit 16 and an associated (and fixed) probability 0.25
   that determines the distribution of nodes[1].

c) The level limit may need to be adjusted.
   I know it is a magic number, but now for simplicity we just keep it at 16,
   and then each skiplist is able to contain (2^32-1)/3 nodes at most.

[1] 
http://www.csee.umbc.edu/courses/undergraduate/341/fall01/Lectures/SkipLists/skip_lists/skip_lists.html

Signed-off-by: Liu Bo <liubo2...@cn.fujitsu.com>
---
 fs/btrfs/Makefile   |    2 +-
 fs/btrfs/skiplist.c |   98 ++++++++++++++++++++++++
 fs/btrfs/skiplist.h |  210 +++++++++++++++++++++++++++++++++++++++++++++++++++
 3 files changed, 309 insertions(+), 1 deletions(-)
 create mode 100644 fs/btrfs/skiplist.c
 create mode 100644 fs/btrfs/skiplist.h

diff --git a/fs/btrfs/Makefile b/fs/btrfs/Makefile
index c0ddfd2..3284462 100644
--- a/fs/btrfs/Makefile
+++ b/fs/btrfs/Makefile
@@ -8,6 +8,6 @@ btrfs-y += super.o ctree.o extent-tree.o print-tree.o 
root-tree.o dir-item.o \
           extent_io.o volumes.o async-thread.o ioctl.o locking.o orphan.o \
           export.o tree-log.o free-space-cache.o zlib.o lzo.o \
           compression.o delayed-ref.o relocation.o delayed-inode.o scrub.o \
-          reada.o backref.o
+          reada.o backref.o skiplist.o
 
 btrfs-$(CONFIG_BTRFS_FS_POSIX_ACL) += acl.o
diff --git a/fs/btrfs/skiplist.c b/fs/btrfs/skiplist.c
new file mode 100644
index 0000000..c803478
--- /dev/null
+++ b/fs/btrfs/skiplist.c
@@ -0,0 +1,98 @@
+/*
+  The Probabilistic Skiplist
+  (C) 2011  Liu Bo <liubo2...@cn.fujitsu.com>
+
+  Based on the skiplist experiments of Con Kolivas <ker...@kolivas.org>
+  for BFS cpu scheduler.
+
+  This program is free software; you can redistribute it and/or modify
+  it under the terms of the GNU General Public License as published by
+  the Free Software Foundation; either version 2 of the License, or
+  (at your option) any later version.
+
+  This program is distributed in the hope that it will be useful,
+  but WITHOUT ANY WARRANTY; without even the implied warranty of
+  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+  GNU General Public License for more details.
+
+  You should have received a copy of the GNU General Public License
+  along with this program; if not, write to the Free Software
+  Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+*/
+
+#include <linux/random.h>
+#include <linux/slab.h>
+#include "skiplist.h"
+
+inline int sl_fill_node(struct sl_node *node, int level, gfp_t mask)
+{
+       struct sl_node **p;
+       struct sl_node **q;
+       int num;
+
+       BUG_ON(level > MAXLEVEL);
+
+       num = level + 1;
+       p = kmalloc(sizeof(*p) * num, mask);
+       BUG_ON(!p);
+       if (!p)
+               return -ENOMEM;
+       q = kmalloc(sizeof(*q) * num, mask);
+       BUG_ON(!q);
+       if (!q) {
+               kfree(p);
+               return -ENOMEM;
+       }
+
+       node->next = p;
+       node->prev = q;
+       node->level = level;
+       return 0;
+}
+
+inline void sl_link_node(struct sl_node *node, struct sl_node **backlook,
+                        int level)
+{
+       struct sl_node *p, *q;
+       int i = 0;
+
+       do {
+               p = backlook[i];
+               q = p->next[i];
+
+               node->next[i] = q;
+               node->prev[i] = p;
+               p->next[i] = node;
+               q->prev[i] = node;
+
+               i++;
+       } while (i <= level);
+}
+
+void sl_erase(struct sl_node *node, struct sl_list *list)
+{
+       struct sl_node *prev, *next;
+       struct sl_node *head;
+       int level;
+       int i;
+
+       level = node->level;
+
+       for (i = 0; i <= level; i++) {
+               prev = node->prev[i];
+               next = node->next[i];
+
+               prev->next[i] = next;
+               next->prev[i] = prev;
+               node->next[i] = node;
+               node->prev[i] = node;
+       }
+
+       head = list->head;
+       if (level == list->level) {
+               while (head->next[level] == head &&
+                      head->prev[level] == head && level > 0)
+                       level--;
+               list->level = level;
+       }
+}
diff --git a/fs/btrfs/skiplist.h b/fs/btrfs/skiplist.h
new file mode 100644
index 0000000..3e414b5
--- /dev/null
+++ b/fs/btrfs/skiplist.h
@@ -0,0 +1,210 @@
+/*
+  The Probabilistic Skiplist
+  (C) 2011  Liu Bo <liubo2...@cn.fujitsu.com>
+
+  Based on the skiplist experiments of Con Kolivas <ker...@kolivas.org>
+  for BFS cpu scheduler.
+
+  This program is free software; you can redistribute it and/or modify
+  it under the terms of the GNU General Public License as published by
+  the Free Software Foundation; either version 2 of the License, or
+  (at your option) any later version.
+
+  This program is distributed in the hope that it will be useful,
+  but WITHOUT ANY WARRANTY; without even the implied warranty of
+  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+  GNU General Public License for more details.
+
+  You should have received a copy of the GNU General Public License
+  along with this program; if not, write to the Free Software
+  Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+
+  To use skiplist you'll have to implement your own insert and search cores to
+  aviod us to use callbacks and to drop drammatically perfromances.
+
+  Here is some examples of insert and search:
+
+-------------------------------------------------------------------------------
+static inline
+struct extent_map *next_entry(struct sl_node *p, int l, struct sl_node **q)
+{
+       struct extent_map *ret;
+       struct sl_node *next;
+
+       next = p->next[l];
+       ret = sl_entry(next, struct extent_map, sl_node);
+       *q = next;
+       return ret;
+}
+
+static inline
+struct sl_node *sl_search(struct sl_list *list, u64 offset)
+{
+       struct sl_node *p, *q;
+       struct extent_map *entry;
+       int level;
+
+       level = list->level;
+       p = list->head;
+       do {
+               while (entry = next_entry(p, level, &q), entry->off <= offset)
+                       p = q;
+
+               entry = sl_entry(p, struct sl_node, sl_node);
+               if (entry->off == offset)
+                       return p;
+
+               level--;
+       } while (level >= 0);
+       return NULL;
+}
+
+static
+struct sl_node *sl_insert_node(struct sl_list *list, u64 offset,
+                              struct sl_node *node)
+{
+       struct extent_map *entry;
+       struct sl_node *backlook[MAXLEVEL], *p, *q;
+       u64 randseed;
+       int level;
+       int ret;
+
+       level = list->level;
+       p = list->head;
+       do {
+               while (entry = next_entry(p, level, &q), entry->off <= offset)
+                       p = q;
+
+               entry = sl_entry(p, struct sl_node, sl_node);
+               if (entry->off == offset)
+                       return p;
+
+               level--;
+       } while (level >= 0);
+
+       get_random_bytes(&randseed, sizeof(u64));
+       level = generate_node_level(randseed);
+       if (level > list->level) {
+               list->level++;
+               level = list->level;
+               backlook[level] = list->head;
+       }
+
+       if (ret = sl_fill_node(node, level, GFP_ATOMIC))
+               return ERR_PTR(ret);
+       sl_link_node(node, backlook, level);
+       return NULL;
+}
+-------------------------------------------------------------------------------
+*/
+
+#ifndef _SKIPLIST_H
+#define _SKIPLIST_H
+
+#include <linux/random.h>
+
+#define MAXLEVEL 16
+/* double p = 0.25; */
+
+struct sl_node {
+       struct sl_node **next;
+       struct sl_node **prev;
+       unsigned int level;
+       unsigned int head:1;
+};
+
+struct sl_list {
+       struct sl_node *head;
+       struct sl_node *h_next[MAXLEVEL];
+       struct sl_node *h_prev[MAXLEVEL];
+       unsigned int level;
+};
+
+#define sl_entry(ptr, type, member) container_of(ptr, type, member)
+
+static inline int sl_empty(const struct sl_node *head)
+{
+       return head->next[0] == head;
+}
+
+static inline struct sl_node *__sl_next_with_level(struct sl_node *node,
+                                                  int level)
+{
+       return node->next[level];
+}
+
+static inline struct sl_node *__sl_prev_with_level(struct sl_node *node,
+                                                  int level)
+{
+       return node->prev[level];
+}
+
+static inline struct sl_node *sl_next(struct sl_node *node)
+{
+       struct sl_node *ret;
+
+       ret = __sl_next_with_level(node, 0);
+       if (ret->head)
+               return NULL;
+
+       return ret;
+}
+
+static inline struct sl_node *sl_prev(struct sl_node *node)
+{
+       struct sl_node *ret;
+
+       ret = __sl_prev_with_level(node, 0);
+       if (ret->head)
+               return NULL;
+
+       return ret;
+}
+
+static inline void sl_init_list(struct sl_list *list, struct sl_node *head)
+{
+       int i;
+
+       list->head = head;
+       list->level = 0;
+       for (i = 0; i < MAXLEVEL; i++)
+               list->h_next[i] = list->h_prev[i] = list->head;
+
+       head->next = list->h_next;
+       head->prev = list->h_prev;
+       head->head = 1;
+}
+
+static inline void sl_init_node(struct sl_node *node)
+{
+       node->head = 0;
+       node->level = 0;
+       node->next = NULL;
+       node->prev = NULL;
+}
+
+static inline void sl_free_node(struct sl_node *node)
+{
+       kfree(node->next);
+       kfree(node->prev);
+}
+
+static inline int generate_node_level(u64 randseed)
+{
+       int level = 0;
+
+       while (randseed && !(randseed & 3)) {
+               randseed >>= 2;
+               level++;
+       }
+
+       return (level > MAXLEVEL ? MAXLEVEL : level);
+}
+
+
+inline int sl_fill_node(struct sl_node *node, int level, gfp_t mask);
+inline void sl_link_node(struct sl_node *node, struct sl_node **backlook,
+                        int level);
+void sl_erase(struct sl_node *node, struct sl_list *list);
+
+#endif /* _SKIPLIST_H */
-- 
1.6.5.2

--
To unsubscribe from this list: send the line "unsubscribe linux-btrfs" in
the body of a message to majord...@vger.kernel.org
More majordomo info at  http://vger.kernel.org/majordomo-info.html

Reply via email to