Re: [PATCH] radix_tree: take radix_tree_path off stack

From: Hugh Dickins
Date: Wed Dec 21 2011 - 01:53:36 EST


On Wed, 21 Dec 2011, Dave Chinner wrote:
> On Sun, Dec 18, 2011 at 10:41:39PM -0800, Hugh Dickins wrote:
>
> > and once radix_tree_tag_if_tagged() has set
> > tag on a node and its ancestors, it need not ascend from that node again.
>
> I'm not sure I really follow this. I think I know what you mean, but
> I can't quite get it straight and the comment in the code doesn't
> help me get it straight. Can you describe it a bit more - I think
> I'm just being dense at the moment....

Below...

>
> Not sure about the page cache, but other users of the radix tree
> definitely do delete objects with tags still set. For example, when
> XFS is reclaiming inodes it will delete the inode from it's internal
> radix trees with the reclaim tag still set on the index. This
> happens for every single inode that is reclaimed, so it's anything
> but seldom and should really be considered a common operation....

Thanks for that info: it was the pagecache case's deep stack that
was worrying me, and I'm only dimly aware of its other uses.

>
> Couple more comments below.
>
> > @@ -274,18 +273,23 @@ static int radix_tree_extend(struct radi
> > if (!(node = radix_tree_node_alloc(root)))
> > return -ENOMEM;
> >
> > - /* Increase the height. */
> > - node->slots[0] = indirect_to_ptr(root->rnode);
> > -
> > /* Propagate the aggregated tag info into the new root */
> > for (tag = 0; tag < RADIX_TREE_MAX_TAGS; tag++) {
> > if (root_tag_get(root, tag))
> > tag_set(node, tag, 0);
> > }
> >
> > + /* Increase the height. */
> > newheight = root->height+1;
>
> While touching this code, fixing the adjacent whitespace damage
> would be good.

I didn't notice any: do you mean "root->height+1" instead of
"root->height + 1"? I don't care much, and checkpatch didn't complain.

>
> > node->height = newheight;
> > node->count = 1;
> > + node->parent = NULL;
> > + slot = root->rnode;
> > + if (newheight > 1) {
> > + slot = indirect_to_ptr(slot);
> > + slot->parent = node;
> > + }
> > + node->slots[0] = slot;
>
> This would be much more obvious in function if it separated the two
> different cases completely:
>
> if (newheight > 1) {
> slot = indirect_to_ptr(root->rnode);
> slot->parent = node;
> } else {
> slot = root->rnode;
> node->parent = NULL;
> }
> node->slots[0] = slot;

We do need to set node->parent NULL in all cases (and cannot clear
it when freeing). I chose the "slot = blah(slot)" style to follow the
"newptr = blah(newptr)" over in radix_tree_shrink(), thought it helped
to keep those blocks alike.

>
> > @@ -701,15 +691,21 @@ unsigned long radix_tree_range_tag_if_ta
> > tag_set(slot, settag, offset);
> >
> > /* walk back up the path tagging interior nodes */
> > - pathp = &path[0];
> > - while (pathp->node) {
> > + upindex = index;
> > + while (node) {
> > + upindex >>= RADIX_TREE_MAP_SHIFT;
> > + offset = upindex & RADIX_TREE_MAP_MASK;
> > +
> > /* stop if we find a node with the tag already set */
> > - if (tag_get(pathp->node, settag, pathp->offset))
> > + if (tag_get(node, settag, offset))
> > break;
> > - tag_set(pathp->node, settag, pathp->offset);
> > - pathp++;
> > + tag_set(node, settag, offset);
> > + node = node->parent;
> > }
> >
> > + /* optimization: no need to walk up from this node again */
> > + node = NULL;
>
> As per my query above: why? That's the question the comment needs to
> answer....

At the top of the hunk, we can see the tag_set(slot, settag, offset)
where it sets the tag in the leafnode "slot"; then it loops up to parent
"node" of slot, to parent of parent, etc, setting tag in those, but
breaking as soon as it finds the tag already set - it can be sure that
the tag must already be set on all nodes above.

If afterwards it comes to set tag at another offset (most likely the
very next) in this same leafnode, we know that it has already set tag
on the parent, the parent's parent etc., so need not bother to tag_get
from the level above to discover that. And since we happen to have a
variable "node" which stops the loop when it's NULL, let's set it to
NULL now to stop the loop immediately in future.

Hugh
--
To unsubscribe from this list: send the line "unsubscribe linux-kernel" in
the body of a message to majordomo@xxxxxxxxxxxxxxx
More majordomo info at http://vger.kernel.org/majordomo-info.html
Please read the FAQ at http://www.tux.org/lkml/