Commit cd1b413c authored by Jan Schmidt's avatar Jan Schmidt

Btrfs: ulist realloc bugfix

ulist_next gets the pointer to the previously returned element to find the
next element from there. However, when we call ulist_add while iteration
with ulist_next is in progress (ulist explicitly supports this), we can
realloc the ulist internal memory, which makes the pointer to the previous
element useless.

Instead, we now use an iterator parameter that's independent from the
internal pointers.
Reported-by: default avatarAlexander Block <ablock84@googlemail.com>
Signed-off-by: default avatarJan Schmidt <list.btrfs@jan-o-sch.net>
parent b9fab919
...@@ -201,6 +201,7 @@ static int __resolve_indirect_refs(struct btrfs_fs_info *fs_info, ...@@ -201,6 +201,7 @@ static int __resolve_indirect_refs(struct btrfs_fs_info *fs_info,
struct __prelim_ref *new_ref; struct __prelim_ref *new_ref;
struct ulist *parents; struct ulist *parents;
struct ulist_node *node; struct ulist_node *node;
struct ulist_iterator uiter;
parents = ulist_alloc(GFP_NOFS); parents = ulist_alloc(GFP_NOFS);
if (!parents) if (!parents)
...@@ -225,11 +226,12 @@ static int __resolve_indirect_refs(struct btrfs_fs_info *fs_info, ...@@ -225,11 +226,12 @@ static int __resolve_indirect_refs(struct btrfs_fs_info *fs_info,
} }
/* we put the first parent into the ref at hand */ /* we put the first parent into the ref at hand */
node = ulist_next(parents, NULL); ULIST_ITER_INIT(&uiter);
node = ulist_next(parents, &uiter);
ref->parent = node ? node->val : 0; ref->parent = node ? node->val : 0;
/* additional parents require new refs being added here */ /* additional parents require new refs being added here */
while ((node = ulist_next(parents, node))) { while ((node = ulist_next(parents, &uiter))) {
new_ref = kmalloc(sizeof(*new_ref), GFP_NOFS); new_ref = kmalloc(sizeof(*new_ref), GFP_NOFS);
if (!new_ref) { if (!new_ref) {
ret = -ENOMEM; ret = -ENOMEM;
...@@ -788,6 +790,7 @@ int btrfs_find_all_roots(struct btrfs_trans_handle *trans, ...@@ -788,6 +790,7 @@ int btrfs_find_all_roots(struct btrfs_trans_handle *trans,
{ {
struct ulist *tmp; struct ulist *tmp;
struct ulist_node *node = NULL; struct ulist_node *node = NULL;
struct ulist_iterator uiter;
int ret; int ret;
tmp = ulist_alloc(GFP_NOFS); tmp = ulist_alloc(GFP_NOFS);
...@@ -799,6 +802,7 @@ int btrfs_find_all_roots(struct btrfs_trans_handle *trans, ...@@ -799,6 +802,7 @@ int btrfs_find_all_roots(struct btrfs_trans_handle *trans,
return -ENOMEM; return -ENOMEM;
} }
ULIST_ITER_INIT(&uiter);
while (1) { while (1) {
ret = find_parent_nodes(trans, fs_info, bytenr, seq, ret = find_parent_nodes(trans, fs_info, bytenr, seq,
tmp, *roots); tmp, *roots);
...@@ -807,7 +811,7 @@ int btrfs_find_all_roots(struct btrfs_trans_handle *trans, ...@@ -807,7 +811,7 @@ int btrfs_find_all_roots(struct btrfs_trans_handle *trans,
ulist_free(*roots); ulist_free(*roots);
return ret; return ret;
} }
node = ulist_next(tmp, node); node = ulist_next(tmp, &uiter);
if (!node) if (!node)
break; break;
bytenr = node->val; bytenr = node->val;
...@@ -1176,6 +1180,8 @@ int iterate_extent_inodes(struct btrfs_fs_info *fs_info, ...@@ -1176,6 +1180,8 @@ int iterate_extent_inodes(struct btrfs_fs_info *fs_info,
struct ulist_node *ref_node = NULL; struct ulist_node *ref_node = NULL;
struct ulist_node *root_node = NULL; struct ulist_node *root_node = NULL;
struct seq_list seq_elem; struct seq_list seq_elem;
struct ulist_iterator ref_uiter;
struct ulist_iterator root_uiter;
struct btrfs_delayed_ref_root *delayed_refs = NULL; struct btrfs_delayed_ref_root *delayed_refs = NULL;
pr_debug("resolving all inodes for extent %llu\n", pr_debug("resolving all inodes for extent %llu\n",
...@@ -1201,12 +1207,14 @@ int iterate_extent_inodes(struct btrfs_fs_info *fs_info, ...@@ -1201,12 +1207,14 @@ int iterate_extent_inodes(struct btrfs_fs_info *fs_info,
if (ret) if (ret)
goto out; goto out;
while (!ret && (ref_node = ulist_next(refs, ref_node))) { ULIST_ITER_INIT(&ref_uiter);
while (!ret && (ref_node = ulist_next(refs, &ref_uiter))) {
ret = btrfs_find_all_roots(trans, fs_info, ref_node->val, -1, ret = btrfs_find_all_roots(trans, fs_info, ref_node->val, -1,
seq_elem.seq, &roots); seq_elem.seq, &roots);
if (ret) if (ret)
break; break;
while (!ret && (root_node = ulist_next(roots, root_node))) { ULIST_ITER_INIT(&root_uiter);
while (!ret && (root_node = ulist_next(roots, &root_uiter))) {
pr_debug("root %llu references leaf %llu\n", pr_debug("root %llu references leaf %llu\n",
root_node->val, ref_node->val); root_node->val, ref_node->val);
ret = iterate_leaf_refs(fs_info, ref_node->val, ret = iterate_leaf_refs(fs_info, ref_node->val,
......
...@@ -23,9 +23,9 @@ ...@@ -23,9 +23,9 @@
* *
* ulist = ulist_alloc(); * ulist = ulist_alloc();
* ulist_add(ulist, root); * ulist_add(ulist, root);
* elem = NULL; * ULIST_ITER_INIT(&uiter);
* *
* while ((elem = ulist_next(ulist, elem)) { * while ((elem = ulist_next(ulist, &uiter)) {
* for (all child nodes n in elem) * for (all child nodes n in elem)
* ulist_add(ulist, n); * ulist_add(ulist, n);
* do something useful with the node; * do something useful with the node;
...@@ -188,33 +188,26 @@ EXPORT_SYMBOL(ulist_add); ...@@ -188,33 +188,26 @@ EXPORT_SYMBOL(ulist_add);
/** /**
* ulist_next - iterate ulist * ulist_next - iterate ulist
* @ulist: ulist to iterate * @ulist: ulist to iterate
* @prev: previously returned element or %NULL to start iteration * @uiter: iterator variable, initialized with ULIST_ITER_INIT(&iterator)
* *
* Note: locking must be provided by the caller. In case of rwlocks only read * Note: locking must be provided by the caller. In case of rwlocks only read
* locking is needed * locking is needed
* *
* This function is used to iterate an ulist. The iteration is started with * This function is used to iterate an ulist.
* @prev = %NULL. It returns the next element from the ulist or %NULL when the * It returns the next element from the ulist or %NULL when the
* end is reached. No guarantee is made with respect to the order in which * end is reached. No guarantee is made with respect to the order in which
* the elements are returned. They might neither be returned in order of * the elements are returned. They might neither be returned in order of
* addition nor in ascending order. * addition nor in ascending order.
* It is allowed to call ulist_add during an enumeration. Newly added items * It is allowed to call ulist_add during an enumeration. Newly added items
* are guaranteed to show up in the running enumeration. * are guaranteed to show up in the running enumeration.
*/ */
struct ulist_node *ulist_next(struct ulist *ulist, struct ulist_node *prev) struct ulist_node *ulist_next(struct ulist *ulist, struct ulist_iterator *uiter)
{ {
int next;
if (ulist->nnodes == 0) if (ulist->nnodes == 0)
return NULL; return NULL;
if (uiter->i < 0 || uiter->i >= ulist->nnodes)
if (!prev)
return &ulist->nodes[0];
next = (prev - ulist->nodes) + 1;
if (next < 0 || next >= ulist->nnodes)
return NULL; return NULL;
return &ulist->nodes[next]; return &ulist->nodes[uiter->i++];
} }
EXPORT_SYMBOL(ulist_next); EXPORT_SYMBOL(ulist_next);
...@@ -24,6 +24,10 @@ ...@@ -24,6 +24,10 @@
*/ */
#define ULIST_SIZE 16 #define ULIST_SIZE 16
struct ulist_iterator {
int i;
};
/* /*
* element of the list * element of the list
*/ */
...@@ -63,6 +67,9 @@ struct ulist *ulist_alloc(unsigned long gfp_mask); ...@@ -63,6 +67,9 @@ struct ulist *ulist_alloc(unsigned long gfp_mask);
void ulist_free(struct ulist *ulist); void ulist_free(struct ulist *ulist);
int ulist_add(struct ulist *ulist, u64 val, unsigned long aux, int ulist_add(struct ulist *ulist, u64 val, unsigned long aux,
unsigned long gfp_mask); unsigned long gfp_mask);
struct ulist_node *ulist_next(struct ulist *ulist, struct ulist_node *prev); struct ulist_node *ulist_next(struct ulist *ulist,
struct ulist_iterator *uiter);
#define ULIST_ITER_INIT(uiter) ((uiter)->i = 0)
#endif #endif
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment