| // SPDX-License-Identifier: GPL-2.0 |
| /* |
| * NUMA support for s390 |
| * |
| * A tree structure used for machine topology mangling |
| * |
| * Copyright IBM Corp. 2015 |
| */ |
| |
| #include <linux/kernel.h> |
| #include <linux/bootmem.h> |
| #include <linux/cpumask.h> |
| #include <linux/list.h> |
| #include <linux/list_sort.h> |
| #include <linux/slab.h> |
| #include <asm/numa.h> |
| |
| #include "toptree.h" |
| |
| /** |
| * toptree_alloc - Allocate and initialize a new tree node. |
| * @level: The node's vertical level; level 0 contains the leaves. |
| * @id: ID number, explicitly not unique beyond scope of node's siblings |
| * |
| * Allocate a new tree node and initialize it. |
| * |
| * RETURNS: |
| * Pointer to the new tree node or NULL on error |
| */ |
| struct toptree __ref *toptree_alloc(int level, int id) |
| { |
| struct toptree *res; |
| |
| if (slab_is_available()) |
| res = kzalloc(sizeof(*res), GFP_KERNEL); |
| else |
| res = memblock_virt_alloc(sizeof(*res), 8); |
| if (!res) |
| return res; |
| |
| INIT_LIST_HEAD(&res->children); |
| INIT_LIST_HEAD(&res->sibling); |
| cpumask_clear(&res->mask); |
| res->level = level; |
| res->id = id; |
| return res; |
| } |
| |
| /** |
| * toptree_remove - Remove a tree node from a tree |
| * @cand: Pointer to the node to remove |
| * |
| * The node is detached from its parent node. The parent node's |
| * masks will be updated to reflect the loss of the child. |
| */ |
| static void toptree_remove(struct toptree *cand) |
| { |
| struct toptree *oldparent; |
| |
| list_del_init(&cand->sibling); |
| oldparent = cand->parent; |
| cand->parent = NULL; |
| toptree_update_mask(oldparent); |
| } |
| |
| /** |
| * toptree_free - discard a tree node |
| * @cand: Pointer to the tree node to discard |
| * |
| * Checks if @cand is attached to a parent node. Detaches it |
| * cleanly using toptree_remove. Possible children are freed |
| * recursively. In the end @cand itself is freed. |
| */ |
| void __ref toptree_free(struct toptree *cand) |
| { |
| struct toptree *child, *tmp; |
| |
| if (cand->parent) |
| toptree_remove(cand); |
| toptree_for_each_child_safe(child, tmp, cand) |
| toptree_free(child); |
| if (slab_is_available()) |
| kfree(cand); |
| else |
| memblock_free_early((unsigned long)cand, sizeof(*cand)); |
| } |
| |
| /** |
| * toptree_update_mask - Update node bitmasks |
| * @cand: Pointer to a tree node |
| * |
| * The node's cpumask will be updated by combining all children's |
| * masks. Then toptree_update_mask is called recursively for the |
| * parent if applicable. |
| * |
| * NOTE: |
| * This must not be called on leaves. If called on a leaf, its |
| * CPU mask is cleared and lost. |
| */ |
| void toptree_update_mask(struct toptree *cand) |
| { |
| struct toptree *child; |
| |
| cpumask_clear(&cand->mask); |
| list_for_each_entry(child, &cand->children, sibling) |
| cpumask_or(&cand->mask, &cand->mask, &child->mask); |
| if (cand->parent) |
| toptree_update_mask(cand->parent); |
| } |
| |
| /** |
| * toptree_insert - Insert a tree node into tree |
| * @cand: Pointer to the node to insert |
| * @target: Pointer to the node to which @cand will added as a child |
| * |
| * Insert a tree node into a tree. Masks will be updated automatically. |
| * |
| * RETURNS: |
| * 0 on success, -1 if NULL is passed as argument or the node levels |
| * don't fit. |
| */ |
| static int toptree_insert(struct toptree *cand, struct toptree *target) |
| { |
| if (!cand || !target) |
| return -1; |
| if (target->level != (cand->level + 1)) |
| return -1; |
| list_add_tail(&cand->sibling, &target->children); |
| cand->parent = target; |
| toptree_update_mask(target); |
| return 0; |
| } |
| |
| /** |
| * toptree_move_children - Move all child nodes of a node to a new place |
| * @cand: Pointer to the node whose children are to be moved |
| * @target: Pointer to the node to which @cand's children will be attached |
| * |
| * Take all child nodes of @cand and move them using toptree_move. |
| */ |
| static void toptree_move_children(struct toptree *cand, struct toptree *target) |
| { |
| struct toptree *child, *tmp; |
| |
| toptree_for_each_child_safe(child, tmp, cand) |
| toptree_move(child, target); |
| } |
| |
| /** |
| * toptree_unify - Merge children with same ID |
| * @cand: Pointer to node whose direct children should be made unique |
| * |
| * When mangling the tree it is possible that a node has two or more children |
| * which have the same ID. This routine merges these children into one and |
| * moves all children of the merged nodes into the unified node. |
| */ |
| void toptree_unify(struct toptree *cand) |
| { |
| struct toptree *child, *tmp, *cand_copy; |
| |
| /* Threads cannot be split, cores are not split */ |
| if (cand->level < 2) |
| return; |
| |
| cand_copy = toptree_alloc(cand->level, 0); |
| toptree_for_each_child_safe(child, tmp, cand) { |
| struct toptree *tmpchild; |
| |
| if (!cpumask_empty(&child->mask)) { |
| tmpchild = toptree_get_child(cand_copy, child->id); |
| toptree_move_children(child, tmpchild); |
| } |
| toptree_free(child); |
| } |
| toptree_move_children(cand_copy, cand); |
| toptree_free(cand_copy); |
| |
| toptree_for_each_child(child, cand) |
| toptree_unify(child); |
| } |
| |
| /** |
| * toptree_move - Move a node to another context |
| * @cand: Pointer to the node to move |
| * @target: Pointer to the node where @cand should go |
| * |
| * In the easiest case @cand is exactly on the level below @target |
| * and will be immediately moved to the target. |
| * |
| * If @target's level is not the direct parent level of @cand, |
| * nodes for the missing levels are created and put between |
| * @cand and @target. The "stacking" nodes' IDs are taken from |
| * @cand's parents. |
| * |
| * After this it is likely to have redundant nodes in the tree |
| * which are addressed by means of toptree_unify. |
| */ |
| void toptree_move(struct toptree *cand, struct toptree *target) |
| { |
| struct toptree *stack_target, *real_insert_point, *ptr, *tmp; |
| |
| if (cand->level + 1 == target->level) { |
| toptree_remove(cand); |
| toptree_insert(cand, target); |
| return; |
| } |
| |
| real_insert_point = NULL; |
| ptr = cand; |
| stack_target = NULL; |
| |
| do { |
| tmp = stack_target; |
| stack_target = toptree_alloc(ptr->level + 1, |
| ptr->parent->id); |
| toptree_insert(tmp, stack_target); |
| if (!real_insert_point) |
| real_insert_point = stack_target; |
| ptr = ptr->parent; |
| } while (stack_target->level < (target->level - 1)); |
| |
| toptree_remove(cand); |
| toptree_insert(cand, real_insert_point); |
| toptree_insert(stack_target, target); |
| } |
| |
| /** |
| * toptree_get_child - Access a tree node's child by its ID |
| * @cand: Pointer to tree node whose child is to access |
| * @id: The desired child's ID |
| * |
| * @cand's children are searched for a child with matching ID. |
| * If no match can be found, a new child with the desired ID |
| * is created and returned. |
| */ |
| struct toptree *toptree_get_child(struct toptree *cand, int id) |
| { |
| struct toptree *child; |
| |
| toptree_for_each_child(child, cand) |
| if (child->id == id) |
| return child; |
| child = toptree_alloc(cand->level-1, id); |
| toptree_insert(child, cand); |
| return child; |
| } |
| |
| /** |
| * toptree_first - Find the first descendant on specified level |
| * @context: Pointer to tree node whose descendants are to be used |
| * @level: The level of interest |
| * |
| * RETURNS: |
| * @context's first descendant on the specified level, or NULL |
| * if there is no matching descendant |
| */ |
| struct toptree *toptree_first(struct toptree *context, int level) |
| { |
| struct toptree *child, *tmp; |
| |
| if (context->level == level) |
| return context; |
| |
| if (!list_empty(&context->children)) { |
| list_for_each_entry(child, &context->children, sibling) { |
| tmp = toptree_first(child, level); |
| if (tmp) |
| return tmp; |
| } |
| } |
| return NULL; |
| } |
| |
| /** |
| * toptree_next_sibling - Return next sibling |
| * @cur: Pointer to a tree node |
| * |
| * RETURNS: |
| * If @cur has a parent and is not the last in the parent's children list, |
| * the next sibling is returned. Or NULL when there are no siblings left. |
| */ |
| static struct toptree *toptree_next_sibling(struct toptree *cur) |
| { |
| if (cur->parent == NULL) |
| return NULL; |
| |
| if (cur == list_last_entry(&cur->parent->children, |
| struct toptree, sibling)) |
| return NULL; |
| return (struct toptree *) list_next_entry(cur, sibling); |
| } |
| |
| /** |
| * toptree_next - Tree traversal function |
| * @cur: Pointer to current element |
| * @context: Pointer to the root node of the tree or subtree to |
| * be traversed. |
| * @level: The level of interest. |
| * |
| * RETURNS: |
| * Pointer to the next node on level @level |
| * or NULL when there is no next node. |
| */ |
| struct toptree *toptree_next(struct toptree *cur, struct toptree *context, |
| int level) |
| { |
| struct toptree *cur_context, *tmp; |
| |
| if (!cur) |
| return NULL; |
| |
| if (context->level == level) |
| return NULL; |
| |
| tmp = toptree_next_sibling(cur); |
| if (tmp != NULL) |
| return tmp; |
| |
| cur_context = cur; |
| while (cur_context->level < context->level - 1) { |
| /* Step up */ |
| cur_context = cur_context->parent; |
| /* Step aside */ |
| tmp = toptree_next_sibling(cur_context); |
| if (tmp != NULL) { |
| /* Step down */ |
| tmp = toptree_first(tmp, level); |
| if (tmp != NULL) |
| return tmp; |
| } |
| } |
| return NULL; |
| } |
| |
| /** |
| * toptree_count - Count descendants on specified level |
| * @context: Pointer to node whose descendants are to be considered |
| * @level: Only descendants on the specified level will be counted |
| * |
| * RETURNS: |
| * Number of descendants on the specified level |
| */ |
| int toptree_count(struct toptree *context, int level) |
| { |
| struct toptree *cur; |
| int cnt = 0; |
| |
| toptree_for_each(cur, context, level) |
| cnt++; |
| return cnt; |
| } |