Ejemplo n.º 1
0
static struct rb_node *
__get_cached_rbnode(struct iova_domain *iovad, unsigned long *limit_pfn)
{
	if ((*limit_pfn != iovad->dma_32bit_pfn) ||
		(iovad->cached32_node == NULL))
		return rb_last(&iovad->rbroot);
	else {
		struct rb_node *prev_node = rb_prev(iovad->cached32_node);
		struct iova *curr_iova =
			container_of(iovad->cached32_node, struct iova, node);
		*limit_pfn = curr_iova->pfn_lo - 1;
		return prev_node;
	}
}
Ejemplo n.º 2
0
/*
 * look for a given offset in the tree, and if it can't be found return the
 * first lesser offset
 */
static struct rb_node *__tree_search(struct rb_root *root, u64 file_offset,
				     struct rb_node **prev_ret)
{
	struct rb_node *n = root->rb_node;
	struct rb_node *prev = NULL;
	struct rb_node *test;
	struct btrfs_ordered_extent *entry;
	struct btrfs_ordered_extent *prev_entry = NULL;

	while (n) {
		entry = rb_entry(n, struct btrfs_ordered_extent, rb_node);
		prev = n;
		prev_entry = entry;

		if (file_offset < entry->file_offset)
			n = n->rb_left;
		else if (file_offset >= entry_end(entry))
			n = n->rb_right;
		else
			return n;
	}
	if (!prev_ret)
		return NULL;

	while (prev && file_offset >= entry_end(prev_entry)) {
		test = rb_next(prev);
		if (!test)
			break;
		prev_entry = rb_entry(test, struct btrfs_ordered_extent,
				      rb_node);
		if (file_offset < entry_end(prev_entry))
			break;

		prev = test;
	}
	if (prev)
		prev_entry = rb_entry(prev, struct btrfs_ordered_extent,
				      rb_node);
	while (prev && file_offset < entry_end(prev_entry)) {
		test = rb_prev(prev);
		if (!test)
			break;
		prev_entry = rb_entry(test, struct btrfs_ordered_extent,
				      rb_node);
		prev = test;
	}
	*prev_ret = prev;
	return NULL;
}
Ejemplo n.º 3
0
/* 取得<=key的元素 */
const h_rb_cursor_st *h_rbtree_upper_bound(const h_rbtree_st *tree,
        const void *key, uint32_t ksize)
{
    rbtree_node_st *parent;
    int is_left;
    rbtree_node_st *ret = do_lookup(tree, key, ksize, &parent, &is_left);
    if (ret)
        return &(ret->cs);

    while (parent
            && tree->cmp_fn(parent->cs.key, parent->cs.ksize, key, ksize) > 0) {
        parent = rb_prev(parent);
    }

    return parent == NULL ? NULL : &(parent->cs);
}
Ejemplo n.º 4
0
static int tree_insert(struct rb_root *root, struct extent_map *em)
{
	struct rb_node **p = &root->rb_node;
	struct rb_node *parent = NULL;
	struct extent_map *entry = NULL;
	struct rb_node *orig_parent = NULL;
	u64 end = range_end(em->start, em->len);

	while (*p) {
		parent = *p;
		entry = rb_entry(parent, struct extent_map, rb_node);

		WARN_ON(!entry->in_tree);

		if (em->start < entry->start)
			p = &(*p)->rb_left;
		else if (em->start >= extent_map_end(entry))
			p = &(*p)->rb_right;
		else
			return -EEXIST;
	}

	orig_parent = parent;
	while (parent && em->start >= extent_map_end(entry)) {
		parent = rb_next(parent);
		entry = rb_entry(parent, struct extent_map, rb_node);
	}
	if (parent)
		if (end > entry->start && em->start < extent_map_end(entry))
			return -EEXIST;

	parent = orig_parent;
	entry = rb_entry(parent, struct extent_map, rb_node);
	while (parent && em->start < entry->start) {
		parent = rb_prev(parent);
		entry = rb_entry(parent, struct extent_map, rb_node);
	}
	if (parent)
		if (end > entry->start && em->start < extent_map_end(entry))
			return -EEXIST;

	em->in_tree = 1;
	rb_link_node(&em->rb_node, orig_parent, p);
	rb_insert_color(&em->rb_node, root);
	return 0;
}
Ejemplo n.º 5
0
/*
 * search through the tree for an extent_map with a given offset.  If
 * it can't be found, try to find some neighboring extents
 */
static struct rb_node *__tree_search(struct rb_root *root, u64 offset,
				     struct rb_node **prev_ret,
				     struct rb_node **next_ret)
{
	struct rb_node *n = root->rb_node;
	struct rb_node *prev = NULL;
	struct rb_node *orig_prev = NULL;
	struct extent_map *entry;
	struct extent_map *prev_entry = NULL;

	while (n) {
		entry = rb_entry(n, struct extent_map, rb_node);
		prev = n;
		prev_entry = entry;

		WARN_ON(!entry->in_tree);

		if (offset < entry->start)
			n = n->rb_left;
		else if (offset >= extent_map_end(entry))
			n = n->rb_right;
		else
			return n;
	}

	if (prev_ret) {
		orig_prev = prev;
		while (prev && offset >= extent_map_end(prev_entry)) {
			prev = rb_next(prev);
			prev_entry = rb_entry(prev, struct extent_map, rb_node);
		}
		*prev_ret = prev;
		prev = orig_prev;
	}

	if (next_ret) {
		prev_entry = rb_entry(prev, struct extent_map, rb_node);
		while (prev && offset < prev_entry->start) {
			prev = rb_prev(prev);
			prev_entry = rb_entry(prev, struct extent_map, rb_node);
		}
		*next_ret = prev;
	}
	return NULL;
}
Ejemplo n.º 6
0
/* Needs the lock */
static void __ocfs2_extent_map_drop(struct inode *inode,
				    u32 new_clusters,
				    struct rb_node **free_head,
				    struct ocfs2_extent_map_entry **tail_ent)
{
	struct rb_node *node, *next;
	struct ocfs2_extent_map *em = &OCFS2_I(inode)->ip_map;
	struct ocfs2_extent_map_entry *ent;

	*free_head = NULL;

	ent = NULL;
	node = rb_last(&em->em_extents);
	while (node)
	{
		next = rb_prev(node);

		ent = rb_entry(node, struct ocfs2_extent_map_entry,
			       e_node);
		if (le32_to_cpu(ent->e_rec.e_cpos) < new_clusters)
			break;

		rb_erase(&ent->e_node, &em->em_extents);

		node->rb_right = *free_head;
		*free_head = node;

		ent = NULL;
		node = next;
	}

	/* Do we have an entry straddling new_clusters? */
	if (tail_ent) {
		if (ent &&
		    ((le32_to_cpu(ent->e_rec.e_cpos) +
		      le32_to_cpu(ent->e_rec.e_clusters)) > new_clusters))
			*tail_ent = ent;
		else
			*tail_ent = NULL;
	}
}
Ejemplo n.º 7
0
/**
 * bfq_idle_extract - extract an entity from the idle tree.
 * @st: the service tree of the owning @entity.
 * @entity: the entity being removed.
 */
static void bfq_idle_extract(struct bfq_service_tree *st,
			     struct bfq_entity *entity)
{
	struct bfq_queue *bfqq = bfq_entity_to_bfqq(entity);
	struct rb_node *next;

	BUG_ON(entity->tree != &st->idle);

	if (entity == st->first_idle) {
		next = rb_next(&entity->rb_node);
		st->first_idle = bfq_entity_of(next);
	}

	if (entity == st->last_idle) {
		next = rb_prev(&entity->rb_node);
		st->last_idle = bfq_entity_of(next);
	}

	bfq_extract(&st->idle, entity);

	if (bfqq != NULL)
		list_del(&bfqq->bfqq_list);
}
Ejemplo n.º 8
0
static struct ext2_reserve_window_node *
search_reserve_window(struct rb_root *root, ext2_fsblk_t goal)
{
	struct rb_node *n = root->rb_node;
	struct ext2_reserve_window_node *rsv;

	if (!n)
		return NULL;

	do {
		rsv = rb_entry(n, struct ext2_reserve_window_node, rsv_node);

		if (goal < rsv->rsv_start)
			n = n->rb_left;
		else if (goal > rsv->rsv_end)
			n = n->rb_right;
		else
			return rsv;
	} while (n);
	if (rsv->rsv_start > goal) {
		n = rb_prev(&rsv->rsv_node);
		rsv = rb_entry(n, struct ext2_reserve_window_node, rsv_node);
	}
Ejemplo n.º 9
0
/**
 * add_extent_mapping - add new extent map to the extent tree
 * @tree:	tree to insert new map in
 * @em:		map to insert
 *
 * Insert @em into @tree or perform a simple forward/backward merge with
 * existing mappings.  The extent_map struct passed in will be inserted
 * into the tree directly, with an additional reference taken, or a
 * reference dropped if the merge attempt was sucessfull.
 */
int add_extent_mapping(struct extent_map_tree *tree,
		       struct extent_map *em)
{
	int ret = 0;
	struct extent_map *merge = NULL;
	struct rb_node *rb;
	struct extent_map *exist;

	exist = lookup_extent_mapping(tree, em->start, em->len);
	if (exist) {
		free_extent_map(exist);
		ret = -EEXIST;
		goto out;
	}
	assert_spin_locked(&tree->lock);
	rb = tree_insert(&tree->map, em->start, &em->rb_node);
	if (rb) {
		ret = -EEXIST;
		free_extent_map(merge);
		goto out;
	}
	atomic_inc(&em->refs);
	if (em->start != 0) {
		rb = rb_prev(&em->rb_node);
		if (rb)
			merge = rb_entry(rb, struct extent_map, rb_node);
		if (rb && mergable_maps(merge, em)) {
			em->start = merge->start;
			em->len += merge->len;
			em->block_len += merge->block_len;
			em->block_start = merge->block_start;
			merge->in_tree = 0;
			rb_erase(&merge->rb_node, &tree->map);
			free_extent_map(merge);
		}
	 }
Ejemplo n.º 10
0
static void try_merge_map(struct extent_map_tree *tree, struct extent_map *em)
{
	struct extent_map *merge = NULL;
	struct rb_node *rb;

	if (em->start != 0) {
		rb = rb_prev(&em->rb_node);
		if (rb)
			merge = rb_entry(rb, struct extent_map, rb_node);
		if (rb && mergable_maps(merge, em)) {
			em->start = merge->start;
			em->orig_start = merge->orig_start;
			em->len += merge->len;
			em->block_len += merge->block_len;
			em->block_start = merge->block_start;
			merge->in_tree = 0;
			em->mod_len = (em->mod_len + em->mod_start) - merge->mod_start;
			em->mod_start = merge->mod_start;
			em->generation = max(em->generation, merge->generation);

			rb_erase(&merge->rb_node, &tree->map);
			free_extent_map(merge);
		}
	}
Ejemplo n.º 11
0
/*
 * lookup extent at @fofs, if hit, return the extent
 * if not, return NULL and
 * @prev_ex: extent before fofs
 * @next_ex: extent after fofs
 * @insert_p: insert point for new extent at fofs
 * in order to simpfy the insertion after.
 * tree must stay unchanged between lookup and insertion.
 */
static struct extent_node *__lookup_extent_tree_ret(struct extent_tree *et,
				unsigned int fofs,
				struct extent_node **prev_ex,
				struct extent_node **next_ex,
				struct rb_node ***insert_p,
				struct rb_node **insert_parent)
{
	struct rb_node **pnode = &et->root.rb_node;
	struct rb_node *parent = NULL, *tmp_node;
	struct extent_node *en = et->cached_en;

	*insert_p = NULL;
	*insert_parent = NULL;
	*prev_ex = NULL;
	*next_ex = NULL;

	if (RB_EMPTY_ROOT(&et->root))
		return NULL;

	if (en) {
		struct extent_info *cei = &en->ei;

		if (cei->fofs <= fofs && cei->fofs + cei->len > fofs)
			goto lookup_neighbors;
	}

	while (*pnode) {
		parent = *pnode;
		en = rb_entry(*pnode, struct extent_node, rb_node);

		if (fofs < en->ei.fofs)
			pnode = &(*pnode)->rb_left;
		else if (fofs >= en->ei.fofs + en->ei.len)
			pnode = &(*pnode)->rb_right;
		else
			goto lookup_neighbors;
	}

	*insert_p = pnode;
	*insert_parent = parent;

	en = rb_entry(parent, struct extent_node, rb_node);
	tmp_node = parent;
	if (parent && fofs > en->ei.fofs)
		tmp_node = rb_next(parent);
	*next_ex = tmp_node ?
		rb_entry(tmp_node, struct extent_node, rb_node) : NULL;

	tmp_node = parent;
	if (parent && fofs < en->ei.fofs)
		tmp_node = rb_prev(parent);
	*prev_ex = tmp_node ?
		rb_entry(tmp_node, struct extent_node, rb_node) : NULL;
	return NULL;

lookup_neighbors:
	if (fofs == en->ei.fofs) {
		/* lookup prev node for merging backward later */
		tmp_node = rb_prev(&en->rb_node);
		*prev_ex = tmp_node ?
			rb_entry(tmp_node, struct extent_node, rb_node) : NULL;
	}
Ejemplo n.º 12
0
/* 删除key */
int h_rbtree_delete(h_rbtree_st *tree, const void *key, uint32_t ksize)
{
    rbtree_node_st *parent, *left, *right, *next;
    rb_color_t color;
    int is_left;
    rbtree_node_st *oldnode = do_lookup(tree, key, ksize, &parent, &is_left);
    rbtree_node_st *node;

    if (!oldnode)
        return -1;

    node = oldnode;
    left = node->left;
    right = node->right;

    if (node == tree->first)
        tree->first = rb_next(node);
    if (node == tree->last)
        tree->last = rb_prev(node);

    if (!left)
        next = right;
    else if (!right)
        next = left;
    else
        next = get_first(right);

    if (parent)
        set_child(next, parent, parent->left == node);
    else
        tree->root = next;

    if (left && right) {
        color = get_color(next);
        set_color(get_color(node), next);

        next->left = left;
        set_parent(next, left);

        if (next != right) {
            parent = get_parent(next);
            set_parent(get_parent(node), next);

            node = next->right;
            parent->left = node;

            next->right = right;
            set_parent(next, right);
        } else {
            set_parent(parent, next);
            parent = next;
            node = next->right;
        }
    } else {
        color = get_color(node);
        node = next;
    }
    /*
     * 'node' is now the sole successor's child and 'parent' its
     * new parent (since the successor can have been moved).
     */
    if (node)
        set_parent(parent, node);

    /*
     * The 'easy' cases.
     */
    if (color == RB_RED)
        goto end_delete;
    if (node && is_red(node)) {
        set_color(RB_BLACK, node);
        goto end_delete;
    }

    do {
        if (node == tree->root)
            break;

        if (node == parent->left) {
            rbtree_node_st *sibling = parent->right;

            if (is_red(sibling)) {
                set_color(RB_BLACK, sibling);
                set_color(RB_RED, parent);
                rotate_left(tree, parent);
                sibling = parent->right;
            }
            if ((!sibling->left  || is_black(sibling->left)) &&
                (!sibling->right || is_black(sibling->right))) {
                set_color(RB_RED, sibling);
                node = parent;
                parent = get_parent(parent);
                continue;
            }
            if (!sibling->right || is_black(sibling->right)) {
                set_color(RB_BLACK, sibling->left);
                set_color(RB_RED, sibling);
                rotate_right(tree, sibling);
                sibling = parent->right;
            }
            set_color(get_color(parent), sibling);
            set_color(RB_BLACK, parent);
            set_color(RB_BLACK, sibling->right);
            rotate_left(tree, parent);
            node = tree->root;
            break;
        } else {
            rbtree_node_st *sibling = parent->left;

            if (is_red(sibling)) {
                set_color(RB_BLACK, sibling);
                set_color(RB_RED, parent);
                rotate_right(tree, parent);
                sibling = parent->left;
            }
            if ((!sibling->left  || is_black(sibling->left)) &&
                (!sibling->right || is_black(sibling->right))) {
                set_color(RB_RED, sibling);
                node = parent;
                parent = get_parent(parent);
                continue;
            }
            if (!sibling->left || is_black(sibling->left)) {
                set_color(RB_BLACK, sibling->right);
                set_color(RB_RED, sibling);
                rotate_left(tree, sibling);
                sibling = parent->left;
            }
            set_color(get_color(parent), sibling);
            set_color(RB_BLACK, parent);
            set_color(RB_BLACK, sibling->left);
            rotate_right(tree, parent);
            node = tree->root;
            break;
        }
    } while (is_black(node));

    if (node)
        set_color(RB_BLACK, node);

end_delete:
    if ((void *)tree->data_free)
        tree->data_free(oldnode->cs.data);

    h_free(oldnode);
    tree->nelem--;

    return 0;
}
Ejemplo n.º 13
0
/*
 * add all currently queued delayed refs from this head whose seq nr is
 * smaller or equal that seq to the list
 */
static int __add_delayed_refs(struct btrfs_delayed_ref_head *head, u64 seq,
			      struct list_head *prefs)
{
	struct btrfs_delayed_extent_op *extent_op = head->extent_op;
	struct rb_node *n = &head->node.rb_node;
	struct btrfs_key key;
	struct btrfs_key op_key = {0};
	int sgn;
	int ret = 0;

	if (extent_op && extent_op->update_key)
		btrfs_disk_key_to_cpu(&op_key, &extent_op->key);

	while ((n = rb_prev(n))) {
		struct btrfs_delayed_ref_node *node;
		node = rb_entry(n, struct btrfs_delayed_ref_node,
				rb_node);
		if (node->bytenr != head->node.bytenr)
			break;
		WARN_ON(node->is_head);

		if (node->seq > seq)
			continue;

		switch (node->action) {
		case BTRFS_ADD_DELAYED_EXTENT:
		case BTRFS_UPDATE_DELAYED_HEAD:
			WARN_ON(1);
			continue;
		case BTRFS_ADD_DELAYED_REF:
			sgn = 1;
			break;
		case BTRFS_DROP_DELAYED_REF:
			sgn = -1;
			break;
		default:
			BUG_ON(1);
		}
		switch (node->type) {
		case BTRFS_TREE_BLOCK_REF_KEY: {
			struct btrfs_delayed_tree_ref *ref;

			ref = btrfs_delayed_node_to_tree_ref(node);
			ret = __add_prelim_ref(prefs, ref->root, &op_key,
					       ref->level + 1, 0, node->bytenr,
					       node->ref_mod * sgn);
			break;
		}
		case BTRFS_SHARED_BLOCK_REF_KEY: {
			struct btrfs_delayed_tree_ref *ref;

			ref = btrfs_delayed_node_to_tree_ref(node);
			ret = __add_prelim_ref(prefs, ref->root, NULL,
					       ref->level + 1, ref->parent,
					       node->bytenr,
					       node->ref_mod * sgn);
			break;
		}
		case BTRFS_EXTENT_DATA_REF_KEY: {
			struct btrfs_delayed_data_ref *ref;
			ref = btrfs_delayed_node_to_data_ref(node);

			key.objectid = ref->objectid;
			key.type = BTRFS_EXTENT_DATA_KEY;
			key.offset = ref->offset;
			ret = __add_prelim_ref(prefs, ref->root, &key, 0, 0,
					       node->bytenr,
					       node->ref_mod * sgn);
			break;
		}
		case BTRFS_SHARED_DATA_REF_KEY: {
			struct btrfs_delayed_data_ref *ref;

			ref = btrfs_delayed_node_to_data_ref(node);

			key.objectid = ref->objectid;
			key.type = BTRFS_EXTENT_DATA_KEY;
			key.offset = ref->offset;
			ret = __add_prelim_ref(prefs, ref->root, &key, 0,
					       ref->parent, node->bytenr,
					       node->ref_mod * sgn);
			break;
		}
		default:
			WARN_ON(1);
		}
		if (ret)
			return ret;
	}

	return 0;
}
Ejemplo n.º 14
0
/* 取得当前itor的上一结点 */
const h_rb_cursor_st *h_rbtree_prev(const h_rb_cursor_st *_itor)
{
    rbtree_node_st *prev_node = rb_prev(cursor_node(_itor));
    return prev_node == NULL ? NULL : &(prev_node->cs);
}
Ejemplo n.º 15
0
int main(int argc, char *argv[])
{
    rb_tree_t rb_tree;
    rb_node_t *node;
    unsigned int key_pool[KEY_POOL_CAPS];
    unsigned int key;
    for (int i = 0; i < KEY_POOL_CAPS; ++i) {
add_key:
        key = arc4random() % 3000;
        for (int j = i - 1; j >= 0; j--) {
            if (key == key_pool[j]) {
                goto add_key;
            }
        }
        key_pool[i] = key;
        printf("Add %u into key pool.\n", key_pool[i]);
    }
    
    rb_tree_init(&rb_tree, data_compare, data_destroy);
redo:
    for (int i = 0; i < KEY_POOL_CAPS; ++i) {
        data_node_t *data = (data_node_t *)malloc(sizeof(*data));
        data->key = key_pool[i];
        if (rb_insert(&(data->rb_node), &rb_tree)) {
            printf("Key %u already exist.\n", data->key);
        } else {
            printf("Add %u into tree.\n", data->key);
        }
    }
    printf("INIT: ************\n");
    printf("Min:%u\n", 
        rb_entry(rb_first(&rb_tree), data_node_t, rb_node)->key);
    printf("Max:%u\n", 
        rb_entry(rb_last(&rb_tree), data_node_t, rb_node)->key);
    printf("Trav with rb_next:\n");
    node = rb_first(&rb_tree);
    while (node) {
        printf("%u, ",rb_entry(node, data_node_t, rb_node)->key); 
        node = rb_next(node);
    }
    printf("\n");
    printf("Trav with rb_prev:\n");
    node = rb_last(&rb_tree);
    while (node) {
        printf("%u, ",rb_entry(node, data_node_t, rb_node)->key); 
        node = rb_prev(node);
    }
    printf("\n");

    printf("ERASE: ************\n");
    data_node_t search_node;
    for (int i = 0; i < 5; i++) {
        unsigned int j = arc4random() % KEY_POOL_CAPS;
        search_node.key = key_pool[j];
        rb_node_t *to_remove = rb_find(&(search_node.rb_node), &rb_tree);
        if (to_remove) {
            printf("Remove %u from tree.\n", search_node.key);
            rb_remove(to_remove, &rb_tree);
            free(rb_entry(to_remove, data_node_t, rb_node));
        } else {
            printf("Key %u not in tree.\n", search_node.key);
        }
    }
    printf("PRINT: ************\n");
    printf("Min:%u\n", 
        rb_entry(rb_first(&rb_tree), data_node_t, rb_node)->key);
    printf("Max:%u\n", 
        rb_entry(rb_last(&rb_tree), data_node_t, rb_node)->key);
    printf("Trav with rb_next:\n");
    node = rb_first(&rb_tree);
    while (node) {
        printf("%u, ",rb_entry(node, data_node_t, rb_node)->key); 
        node = rb_next(node);
    }
    printf("\n");
    printf("Trav with rb_prev:\n");
    node = rb_last(&rb_tree);
    while (node) {
        printf("%u, ",rb_entry(node, data_node_t, rb_node)->key); 
        node = rb_prev(node);
    }
    printf("\n");
    rb_clear(&rb_tree);
    printf("PRINT2: ************\n");
    node = rb_first(&rb_tree);
    if (node) {
        printf("Min:%u\n", 
        rb_entry(node, data_node_t, rb_node)->key);
    }

    node = rb_last(&rb_tree);
    if (node) {
        printf("Max:%u\n", 
        rb_entry(node, data_node_t, rb_node)->key);
    }
    printf("Trav with rb_next:\n");
    node = rb_first(&rb_tree);
    while (node) {
        printf("%u, ",rb_entry(node, data_node_t, rb_node)->key); 
        node = rb_next(node);
    }
    printf("\n");
    printf("Trav with rb_prev:\n");
    node = rb_last(&rb_tree);
    while (node) {
        printf("%u, ",rb_entry(node, data_node_t, rb_node)->key); 
        node = rb_prev(node);
    }
    printf("\n");
    goto redo;
    return 0;
}
Ejemplo n.º 16
0
int defrag_file_interactive(struct defrag_ctx *c)
{
	struct rb_node *n = rb_last(&c->free_tree_by_size);
	struct free_extent *f = rb_entry(n, struct free_extent, size_rb);
	struct inode *inode;
	e2_blkcnt_t biggest_size = 0;
	int ret, num_biggest = 0;
	ext2_ino_t inode_nr;
	char improve;

	print_fragged_inodes(c);
	if (n)
		biggest_size = f->end_block - f->start_block + 1;
	while (n && biggest_size == f->end_block - f->start_block + 1) {
		num_biggest++;
		n = rb_prev(n);
		f = rb_entry(n, struct free_extent, size_rb);
	}
	printf("Biggest free space: %llu blocks (%d)\n", biggest_size,
	       num_biggest);
	do {
		long number;
		printf("WARNING: UNTESTED!\n");
		printf("Specify inode number.\n");
		printf("You can enter -1 for free space consolidation, or 0 to exit.\n");
		printf("You can also precede the inode number by an 'i' to improve rather than defragment the file.\n");
		printf("Inode number: ");
		fflush(stdout);
		number = getchar();
		if (number == 'i' || number == 'I') {
			improve = 1;
		} else {
			improve = 0;
			ungetc(number, stdin);
		}
		if (getnumber(&number, -2, c->sb.s_inodes_count) < 0) {
			switch (errno) {
			case EDOM:
				printf("Not a valid number\n");
				break;
			case ERANGE:
				printf("Inode number out of range\n");
				break;
			default:
				printf("Error: %s\n", strerror(errno));
				return -1;
			}
			number = LONG_MIN;
		}
		switch (number) {
		case 0:
			return 1;
		case -1:
			ret = consolidate_free_space(c);
			if (ret)
				printf("Error: %s\n", strerror(errno));
			if (ret && errno != ENOSPC)
				return ret;
			return 0;
		case -2:
			ret = 0;
			while (!ret)
				ret = consolidate_free_space(c);
			if (errno == ENOSPC)
				return 0;
			return ret;
		case LONG_MIN:
			inode_nr = 0;
			break;
		default:
			inode_nr = number;
		}
	} while (inode_nr < EXT2_FIRST_INO(&c->sb));
	inode = c->inodes[inode_nr];
	if (inode == NULL || inode->data->extent_count == 0) {
		printf("Inode has no data associated\n");
		return 0;
	}
	if (!improve)
		ret = do_one_inode(c, inode_nr);
	else
		while ((ret = try_improve_inode(c, inode_nr)) > 0);
	if (ret < 0)
		printf("Error: %s\n", strerror(errno));
	printf("Inode now has %llu fragments\n",
	       c->inodes[inode_nr]->data->extent_count);
	if (ret && errno != ENOSPC)
		return ret;
	else
		return 0;
}
/*
 * Mark a range of blocks as belonging to the "system zone" --- that
 * is, filesystem metadata blocks which should never be used by
 * inodes.
 */
static int add_system_zone(struct ext4_sb_info *sbi,
			   ext4_fsblk_t start_blk,
			   unsigned int count)
{
	struct ext4_system_zone *new_entry = NULL, *entry;
	struct rb_node **n = &sbi->system_blks.rb_node, *node;
	struct rb_node *parent = NULL, *new_node = NULL;

	while (*n) {
		parent = *n;
		entry = rb_entry(parent, struct ext4_system_zone, node);
		if (start_blk < entry->start_blk)
			n = &(*n)->rb_left;
		else if (start_blk >= (entry->start_blk + entry->count))
			n = &(*n)->rb_right;
		else {
			if (start_blk + count > (entry->start_blk + 
						 entry->count))
				entry->count = (start_blk + count - 
						entry->start_blk);
			new_node = *n;
			new_entry = rb_entry(new_node, struct ext4_system_zone,
					     node);
			break;
		}
	}

	if (!new_entry) {
		new_entry = kmem_cache_alloc(ext4_system_zone_cachep,
					     GFP_KERNEL);
		if (!new_entry)
			return -ENOMEM;
		new_entry->start_blk = start_blk;
		new_entry->count = count;
		new_node = &new_entry->node;

		rb_link_node(new_node, parent, n);
		rb_insert_color(new_node, &sbi->system_blks);
	}

	/* Can we merge to the left? */
	node = rb_prev(new_node);
	if (node) {
		entry = rb_entry(node, struct ext4_system_zone, node);
		if (can_merge(entry, new_entry)) {
			new_entry->start_blk = entry->start_blk;
			new_entry->count += entry->count;
			rb_erase(node, &sbi->system_blks);
			kmem_cache_free(ext4_system_zone_cachep, entry);
		}
	}

	/* Can we merge to the right? */
	node = rb_next(new_node);
	if (node) {
		entry = rb_entry(node, struct ext4_system_zone, node);
		if (can_merge(new_entry, entry)) {
			new_entry->count += entry->count;
			rb_erase(node, &sbi->system_blks);
			kmem_cache_free(ext4_system_zone_cachep, entry);
		}
	}
	return 0;
}