aboutsummaryrefslogtreecommitdiffstats
path: root/fs/btrfs
diff options
context:
space:
mode:
authorWang Shilong <wangsl.fnst@cn.fujitsu.com>2014-01-28 11:25:34 -0500
committerChris Mason <clm@fb.com>2014-01-29 10:06:27 -0500
commit4c7a6f74ceeafd738b55d1c57349327f7ea8e895 (patch)
treeeb3d4c6e6406490e6c31ee4da3512506daddc4b8 /fs/btrfs
parentf05c474688762f186b16a26366755b6ef0bfed0c (diff)
Btrfs: rework ulist with list+rb_tree
We are really suffering from now ulist's implementation, some developers gave their try, and i just gave some of my ideas for things: 1. use list+rb_tree instead of arrary+rb_tree 2. add cur_list to iterator rather than ulist structure. 3. add seqnum into every node when they are added, this is used to do selfcheck when iterating node. I noticed Zach Brown's comments before, long term is to kick off ulist implementation, however, for now, we need at least avoid arrary from ulist. Cc: Liu Bo <bo.li.liu@oracle.com> Cc: Josef Bacik <jbacik@fb.com> Cc: Zach Brown <zab@redhat.com> Signed-off-by: Wang Shilong <wangsl.fnst@cn.fujitsu.com> Signed-off-by: Josef Bacik <jbacik@fb.com> Signed-off-by: Chris Mason <clm@fb.com>
Diffstat (limited to 'fs/btrfs')
-rw-r--r--fs/btrfs/ulist.c105
-rw-r--r--fs/btrfs/ulist.h38
2 files changed, 55 insertions, 88 deletions
diff --git a/fs/btrfs/ulist.c b/fs/btrfs/ulist.c
index 35f5de9dd498..8dd0e8dfdaf4 100644
--- a/fs/btrfs/ulist.c
+++ b/fs/btrfs/ulist.c
@@ -7,6 +7,7 @@
7#include <linux/slab.h> 7#include <linux/slab.h>
8#include <linux/export.h> 8#include <linux/export.h>
9#include "ulist.h" 9#include "ulist.h"
10#include "ctree.h"
10 11
11/* 12/*
12 * ulist is a generic data structure to hold a collection of unique u64 13 * ulist is a generic data structure to hold a collection of unique u64
@@ -14,10 +15,6 @@
14 * enumerating it. 15 * enumerating it.
15 * It is possible to store an auxiliary value along with the key. 16 * It is possible to store an auxiliary value along with the key.
16 * 17 *
17 * The implementation is preliminary and can probably be sped up
18 * significantly. A first step would be to store the values in an rbtree
19 * as soon as ULIST_SIZE is exceeded.
20 *
21 * A sample usage for ulists is the enumeration of directed graphs without 18 * A sample usage for ulists is the enumeration of directed graphs without
22 * visiting a node twice. The pseudo-code could look like this: 19 * visiting a node twice. The pseudo-code could look like this:
23 * 20 *
@@ -50,10 +47,9 @@
50 */ 47 */
51void ulist_init(struct ulist *ulist) 48void ulist_init(struct ulist *ulist)
52{ 49{
53 ulist->nnodes = 0; 50 INIT_LIST_HEAD(&ulist->nodes);
54 ulist->nodes = ulist->int_nodes;
55 ulist->nodes_alloced = ULIST_SIZE;
56 ulist->root = RB_ROOT; 51 ulist->root = RB_ROOT;
52 ulist->nnodes = 0;
57} 53}
58EXPORT_SYMBOL(ulist_init); 54EXPORT_SYMBOL(ulist_init);
59 55
@@ -66,14 +62,14 @@ EXPORT_SYMBOL(ulist_init);
66 */ 62 */
67void ulist_fini(struct ulist *ulist) 63void ulist_fini(struct ulist *ulist)
68{ 64{
69 /* 65 struct ulist_node *node;
70 * The first ULIST_SIZE elements are stored inline in struct ulist. 66 struct ulist_node *next;
71 * Only if more elements are alocated they need to be freed. 67
72 */ 68 list_for_each_entry_safe(node, next, &ulist->nodes, list) {
73 if (ulist->nodes_alloced > ULIST_SIZE) 69 kfree(node);
74 kfree(ulist->nodes); 70 }
75 ulist->nodes_alloced = 0; /* in case ulist_fini is called twice */
76 ulist->root = RB_ROOT; 71 ulist->root = RB_ROOT;
72 INIT_LIST_HEAD(&ulist->nodes);
77} 73}
78EXPORT_SYMBOL(ulist_fini); 74EXPORT_SYMBOL(ulist_fini);
79 75
@@ -192,57 +188,29 @@ int ulist_add(struct ulist *ulist, u64 val, u64 aux, gfp_t gfp_mask)
192int ulist_add_merge(struct ulist *ulist, u64 val, u64 aux, 188int ulist_add_merge(struct ulist *ulist, u64 val, u64 aux,
193 u64 *old_aux, gfp_t gfp_mask) 189 u64 *old_aux, gfp_t gfp_mask)
194{ 190{
195 int ret = 0; 191 int ret;
196 struct ulist_node *node = NULL; 192 struct ulist_node *node;
193
197 node = ulist_rbtree_search(ulist, val); 194 node = ulist_rbtree_search(ulist, val);
198 if (node) { 195 if (node) {
199 if (old_aux) 196 if (old_aux)
200 *old_aux = node->aux; 197 *old_aux = node->aux;
201 return 0; 198 return 0;
202 } 199 }
200 node = kmalloc(sizeof(*node), gfp_mask);
201 if (!node)
202 return -ENOMEM;
203 203
204 if (ulist->nnodes >= ulist->nodes_alloced) { 204 node->val = val;
205 u64 new_alloced = ulist->nodes_alloced + 128; 205 node->aux = aux;
206 struct ulist_node *new_nodes; 206#ifdef CONFIG_BTRFS_DEBUG
207 void *old = NULL; 207 node->seqnum = ulist->nnodes;
208 int i; 208#endif
209
210 /*
211 * if nodes_alloced == ULIST_SIZE no memory has been allocated
212 * yet, so pass NULL to krealloc
213 */
214 if (ulist->nodes_alloced > ULIST_SIZE)
215 old = ulist->nodes;
216 209
217 new_nodes = krealloc(old, sizeof(*new_nodes) * new_alloced, 210 ret = ulist_rbtree_insert(ulist, node);
218 gfp_mask); 211 ASSERT(!ret);
219 if (!new_nodes) 212 list_add_tail(&node->list, &ulist->nodes);
220 return -ENOMEM; 213 ulist->nnodes++;
221
222 if (!old)
223 memcpy(new_nodes, ulist->int_nodes,
224 sizeof(ulist->int_nodes));
225
226 ulist->nodes = new_nodes;
227 ulist->nodes_alloced = new_alloced;
228
229 /*
230 * krealloc actually uses memcpy, which does not copy rb_node
231 * pointers, so we have to do it ourselves. Otherwise we may
232 * be bitten by crashes.
233 */
234 ulist->root = RB_ROOT;
235 for (i = 0; i < ulist->nnodes; i++) {
236 ret = ulist_rbtree_insert(ulist, &ulist->nodes[i]);
237 if (ret < 0)
238 return ret;
239 }
240 }
241 ulist->nodes[ulist->nnodes].val = val;
242 ulist->nodes[ulist->nnodes].aux = aux;
243 ret = ulist_rbtree_insert(ulist, &ulist->nodes[ulist->nnodes]);
244 BUG_ON(ret);
245 ++ulist->nnodes;
246 214
247 return 1; 215 return 1;
248} 216}
@@ -266,11 +234,26 @@ EXPORT_SYMBOL(ulist_add);
266 */ 234 */
267struct ulist_node *ulist_next(struct ulist *ulist, struct ulist_iterator *uiter) 235struct ulist_node *ulist_next(struct ulist *ulist, struct ulist_iterator *uiter)
268{ 236{
269 if (ulist->nnodes == 0) 237 struct ulist_node *node;
238
239 if (list_empty(&ulist->nodes))
270 return NULL; 240 return NULL;
271 if (uiter->i < 0 || uiter->i >= ulist->nnodes) 241 if (uiter->cur_list && uiter->cur_list->next == &ulist->nodes)
272 return NULL; 242 return NULL;
273 243 if (uiter->cur_list) {
274 return &ulist->nodes[uiter->i++]; 244 uiter->cur_list = uiter->cur_list->next;
245 } else {
246 uiter->cur_list = ulist->nodes.next;
247#ifdef CONFIG_BTRFS_DEBUG
248 uiter->i = 0;
249#endif
250 }
251 node = list_entry(uiter->cur_list, struct ulist_node, list);
252#ifdef CONFIG_BTRFS_DEBUG
253 ASSERT(node->seqnum == uiter->i);
254 ASSERT(uiter->i >= 0 && uiter->i < ulist->nnodes);
255 uiter->i++;
256#endif
257 return node;
275} 258}
276EXPORT_SYMBOL(ulist_next); 259EXPORT_SYMBOL(ulist_next);
diff --git a/fs/btrfs/ulist.h b/fs/btrfs/ulist.h
index fb36731074b5..2be7102d8073 100644
--- a/fs/btrfs/ulist.h
+++ b/fs/btrfs/ulist.h
@@ -17,18 +17,12 @@
17 * enumerating it. 17 * enumerating it.
18 * It is possible to store an auxiliary value along with the key. 18 * It is possible to store an auxiliary value along with the key.
19 * 19 *
20 * The implementation is preliminary and can probably be sped up
21 * significantly. A first step would be to store the values in an rbtree
22 * as soon as ULIST_SIZE is exceeded.
23 */ 20 */
24
25/*
26 * number of elements statically allocated inside struct ulist
27 */
28#define ULIST_SIZE 16
29
30struct ulist_iterator { 21struct ulist_iterator {
22#ifdef CONFIG_BTRFS_DEBUG
31 int i; 23 int i;
24#endif
25 struct list_head *cur_list; /* hint to start search */
32}; 26};
33 27
34/* 28/*
@@ -37,6 +31,12 @@ struct ulist_iterator {
37struct ulist_node { 31struct ulist_node {
38 u64 val; /* value to store */ 32 u64 val; /* value to store */
39 u64 aux; /* auxiliary value saved along with the val */ 33 u64 aux; /* auxiliary value saved along with the val */
34
35#ifdef CONFIG_BTRFS_DEBUG
36 int seqnum; /* sequence number this node is added */
37#endif
38
39 struct list_head list; /* used to link node */
40 struct rb_node rb_node; /* used to speed up search */ 40 struct rb_node rb_node; /* used to speed up search */
41}; 41};
42 42
@@ -46,24 +46,8 @@ struct ulist {
46 */ 46 */
47 unsigned long nnodes; 47 unsigned long nnodes;
48 48
49 /* 49 struct list_head nodes;
50 * number of nodes we already have room for
51 */
52 unsigned long nodes_alloced;
53
54 /*
55 * pointer to the array storing the elements. The first ULIST_SIZE
56 * elements are stored inline. In this case the it points to int_nodes.
57 * After exceeding ULIST_SIZE, dynamic memory is allocated.
58 */
59 struct ulist_node *nodes;
60
61 struct rb_root root; 50 struct rb_root root;
62
63 /*
64 * inline storage space for the first ULIST_SIZE entries
65 */
66 struct ulist_node int_nodes[ULIST_SIZE];
67}; 51};
68 52
69void ulist_init(struct ulist *ulist); 53void ulist_init(struct ulist *ulist);
@@ -77,6 +61,6 @@ int ulist_add_merge(struct ulist *ulist, u64 val, u64 aux,
77struct ulist_node *ulist_next(struct ulist *ulist, 61struct ulist_node *ulist_next(struct ulist *ulist,
78 struct ulist_iterator *uiter); 62 struct ulist_iterator *uiter);
79 63
80#define ULIST_ITER_INIT(uiter) ((uiter)->i = 0) 64#define ULIST_ITER_INIT(uiter) ((uiter)->cur_list = NULL)
81 65
82#endif 66#endif