Linux Radix Tree迭代器(radix_tree_next_chunk)如何工作?

时间:2019-05-02 21:02:55

标签: linux-kernel iterator tree-traversal radix-tree

我试图了解Linux内核中的radix_tree_next_chunk实现。从radix_tree_for_each_slot调用radix_tree_next_chunk,如以下代码所示。 radix_tree_next_chunk如何返回下一个块的第一个插槽?请解释步行。

#define radix_tree_for_each_slot(slot, root, iter, start)       \
for (slot = radix_tree_iter_init(iter, start) ;         \
     slot || (slot = radix_tree_next_chunk(root, iter, 0)) ;    \
     slot = radix_tree_next_slot(slot, iter, 0))

我已经阅读了Linux基数树象征https://lwn.net/Articles/175432/

这是radix_tree_next_chunk实现。

/**
 * radix_tree_next_chunk - find next chunk of slots for iteration
 *
 * @root:   radix tree root
 * @iter:   iterator state
 * @flags:  RADIX_TREE_ITER_* flags and tag index
 * Returns: pointer to chunk first slot, or NULL if iteration is over
 */
void __rcu **radix_tree_next_chunk(const struct radix_tree_root *root,
                 struct radix_tree_iter *iter, unsigned flags)
{
    unsigned tag = flags & RADIX_TREE_ITER_TAG_MASK;
    struct radix_tree_node *node, *child;
    unsigned long index, offset, maxindex;

    if ((flags & RADIX_TREE_ITER_TAGGED) && !root_tag_get(root, tag))
        return NULL;

    /*
     * Catch next_index overflow after ~0UL. iter->index never overflows
     * during iterating; it can be zero only at the beginning.
     * And we cannot overflow iter->next_index in a single step,
     * because RADIX_TREE_MAP_SHIFT < BITS_PER_LONG.
     *
     * This condition also used by radix_tree_next_slot() to stop
     * contiguous iterating, and forbid switching to the next chunk.
     */
    index = iter->next_index;
    if (!index && iter->index)
        return NULL;

 restart:
    radix_tree_load_root(root, &child, &maxindex);
    if (index > maxindex)
        return NULL;
    if (!child)
        return NULL;

    if (!radix_tree_is_internal_node(child)) {
        /* Single-slot tree */
        iter->index = index;
        iter->next_index = maxindex + 1;
        iter->tags = 1;
        iter->node = NULL;
        return (void __rcu **)&root->xa_head;
    }

    do {
        node = entry_to_node(child);
        offset = radix_tree_descend(node, &child, index);

        if ((flags & RADIX_TREE_ITER_TAGGED) ?
                !tag_get(node, tag, offset) : !child) {
            /* Hole detected */
            if (flags & RADIX_TREE_ITER_CONTIG)
                return NULL;

            if (flags & RADIX_TREE_ITER_TAGGED)
                offset = radix_tree_find_next_bit(node, tag,
                        offset + 1);
            else
                while (++offset < RADIX_TREE_MAP_SIZE) {
                    void *slot = rcu_dereference_raw(
                            node->slots[offset]);
                    if (slot)
                        break;
                }
            index &= ~node_maxindex(node);
            index += offset << node->shift;
            /* Overflow after ~0UL */
            if (!index)
                return NULL;
            if (offset == RADIX_TREE_MAP_SIZE)
                goto restart;
            child = rcu_dereference_raw(node->slots[offset]);
        }

        if (!child)
            goto restart;
        if (child == RADIX_TREE_RETRY)
            break;
    } while (node->shift && radix_tree_is_internal_node(child));

    /* Update the iterator state */
    iter->index = (index &~ node_maxindex(node)) | offset;
    iter->next_index = (index | node_maxindex(node)) + 1;
    iter->node = node;

    if (flags & RADIX_TREE_ITER_TAGGED)
        set_iter_tags(iter, node, offset, tag);

    return node->slots + offset;
}
EXPORT_SYMBOL(radix_tree_next_chunk);

0 个答案:

没有答案