diff options
| -rw-r--r-- | fs/btrfs/ulist.c | 105 | ||||
| -rw-r--r-- | fs/btrfs/ulist.h | 38 |
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 | */ |
| 51 | void ulist_init(struct ulist *ulist) | 48 | void 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 | } |
| 58 | EXPORT_SYMBOL(ulist_init); | 54 | EXPORT_SYMBOL(ulist_init); |
| 59 | 55 | ||
| @@ -66,14 +62,14 @@ EXPORT_SYMBOL(ulist_init); | |||
| 66 | */ | 62 | */ |
| 67 | void ulist_fini(struct ulist *ulist) | 63 | void 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 | } |
| 78 | EXPORT_SYMBOL(ulist_fini); | 74 | EXPORT_SYMBOL(ulist_fini); |
| 79 | 75 | ||
| @@ -192,57 +188,29 @@ int ulist_add(struct ulist *ulist, u64 val, u64 aux, gfp_t gfp_mask) | |||
| 192 | int ulist_add_merge(struct ulist *ulist, u64 val, u64 aux, | 188 | int 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 | */ |
| 267 | struct ulist_node *ulist_next(struct ulist *ulist, struct ulist_iterator *uiter) | 235 | struct 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 | } |
| 276 | EXPORT_SYMBOL(ulist_next); | 259 | EXPORT_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 | |||
| 30 | struct ulist_iterator { | 21 | struct 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 { | |||
| 37 | struct ulist_node { | 31 | struct 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 | ||
| 69 | void ulist_init(struct ulist *ulist); | 53 | void ulist_init(struct ulist *ulist); |
| @@ -77,6 +61,6 @@ int ulist_add_merge(struct ulist *ulist, u64 val, u64 aux, | |||
| 77 | struct ulist_node *ulist_next(struct ulist *ulist, | 61 | struct 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 |
