aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorPhilipp Hachtmann <phacht@linux.vnet.ibm.com>2014-03-06 12:39:39 -0500
committerMartin Schwidefsky <schwidefsky@de.ibm.com>2015-08-03 12:40:26 -0400
commite8054b654bf5d4f549f4f24b708acce6d2718b1b (patch)
treeeac76bd64bf06f509903d6006f2ae1f1160c928a
parent3a368f742da13955bed4a2efed85ed7c1d826bcc (diff)
s390/numa: add topology tree infrastructure
NUMA emulation needs proper means to mangle the book/mc/core topology of the machine. The topology tree (toptree) consistently maintains cpu masks for the root, each node, and all leaves of the tree while the user may use the toptree functions to rearrange the tree in various ways. This patch contains several changes from Michael Holzheu. Signed-off-by: Philipp Hachtmann <phacht@linux.vnet.ibm.com> Signed-off-by: Michael Holzheu <holzheu@linux.vnet.ibm.com> Signed-off-by: Martin Schwidefsky <schwidefsky@de.ibm.com>
-rw-r--r--arch/s390/numa/Makefile1
-rw-r--r--arch/s390/numa/toptree.c342
-rw-r--r--arch/s390/numa/toptree.h60
3 files changed, 403 insertions, 0 deletions
diff --git a/arch/s390/numa/Makefile b/arch/s390/numa/Makefile
index 7e94c8f491f7..31372293b62e 100644
--- a/arch/s390/numa/Makefile
+++ b/arch/s390/numa/Makefile
@@ -1 +1,2 @@
1obj-y += numa.o 1obj-y += numa.o
2obj-y += toptree.o
diff --git a/arch/s390/numa/toptree.c b/arch/s390/numa/toptree.c
new file mode 100644
index 000000000000..902d350d859a
--- /dev/null
+++ b/arch/s390/numa/toptree.c
@@ -0,0 +1,342 @@
1/*
2 * NUMA support for s390
3 *
4 * A tree structure used for machine topology mangling
5 *
6 * Copyright IBM Corp. 2015
7 */
8
9#include <linux/kernel.h>
10#include <linux/cpumask.h>
11#include <linux/list.h>
12#include <linux/list_sort.h>
13#include <linux/slab.h>
14#include <asm/numa.h>
15
16#include "toptree.h"
17
18/**
19 * toptree_alloc - Allocate and initialize a new tree node.
20 * @level: The node's vertical level; level 0 contains the leaves.
21 * @id: ID number, explicitly not unique beyond scope of node's siblings
22 *
23 * Allocate a new tree node and initialize it.
24 *
25 * RETURNS:
26 * Pointer to the new tree node or NULL on error
27 */
28struct toptree *toptree_alloc(int level, int id)
29{
30 struct toptree *res = kzalloc(sizeof(struct toptree), GFP_KERNEL);
31
32 if (!res)
33 return res;
34
35 INIT_LIST_HEAD(&res->children);
36 INIT_LIST_HEAD(&res->sibling);
37 cpumask_clear(&res->mask);
38 res->level = level;
39 res->id = id;
40 return res;
41}
42
43/**
44 * toptree_remove - Remove a tree node from a tree
45 * @cand: Pointer to the node to remove
46 *
47 * The node is detached from its parent node. The parent node's
48 * masks will be updated to reflect the loss of the child.
49 */
50static void toptree_remove(struct toptree *cand)
51{
52 struct toptree *oldparent;
53
54 list_del_init(&cand->sibling);
55 oldparent = cand->parent;
56 cand->parent = NULL;
57 toptree_update_mask(oldparent);
58}
59
60/**
61 * toptree_free - discard a tree node
62 * @cand: Pointer to the tree node to discard
63 *
64 * Checks if @cand is attached to a parent node. Detaches it
65 * cleanly using toptree_remove. Possible children are freed
66 * recursively. In the end @cand itself is freed.
67 */
68void toptree_free(struct toptree *cand)
69{
70 struct toptree *child, *tmp;
71
72 if (cand->parent)
73 toptree_remove(cand);
74 toptree_for_each_child_safe(child, tmp, cand)
75 toptree_free(child);
76 kfree(cand);
77}
78
79/**
80 * toptree_update_mask - Update node bitmasks
81 * @cand: Pointer to a tree node
82 *
83 * The node's cpumask will be updated by combining all children's
84 * masks. Then toptree_update_mask is called recursively for the
85 * parent if applicable.
86 *
87 * NOTE:
88 * This must not be called on leaves. If called on a leaf, its
89 * CPU mask is cleared and lost.
90 */
91void toptree_update_mask(struct toptree *cand)
92{
93 struct toptree *child;
94
95 cpumask_clear(&cand->mask);
96 list_for_each_entry(child, &cand->children, sibling)
97 cpumask_or(&cand->mask, &cand->mask, &child->mask);
98 if (cand->parent)
99 toptree_update_mask(cand->parent);
100}
101
102/**
103 * toptree_insert - Insert a tree node into tree
104 * @cand: Pointer to the node to insert
105 * @target: Pointer to the node to which @cand will added as a child
106 *
107 * Insert a tree node into a tree. Masks will be updated automatically.
108 *
109 * RETURNS:
110 * 0 on success, -1 if NULL is passed as argument or the node levels
111 * don't fit.
112 */
113static int toptree_insert(struct toptree *cand, struct toptree *target)
114{
115 if (!cand || !target)
116 return -1;
117 if (target->level != (cand->level + 1))
118 return -1;
119 list_add_tail(&cand->sibling, &target->children);
120 cand->parent = target;
121 toptree_update_mask(target);
122 return 0;
123}
124
125/**
126 * toptree_move_children - Move all child nodes of a node to a new place
127 * @cand: Pointer to the node whose children are to be moved
128 * @target: Pointer to the node to which @cand's children will be attached
129 *
130 * Take all child nodes of @cand and move them using toptree_move.
131 */
132static void toptree_move_children(struct toptree *cand, struct toptree *target)
133{
134 struct toptree *child, *tmp;
135
136 toptree_for_each_child_safe(child, tmp, cand)
137 toptree_move(child, target);
138}
139
140/**
141 * toptree_unify - Merge children with same ID
142 * @cand: Pointer to node whose direct children should be made unique
143 *
144 * When mangling the tree it is possible that a node has two or more children
145 * which have the same ID. This routine merges these children into one and
146 * moves all children of the merged nodes into the unified node.
147 */
148void toptree_unify(struct toptree *cand)
149{
150 struct toptree *child, *tmp, *cand_copy;
151
152 /* Threads cannot be split, cores are not split */
153 if (cand->level < 2)
154 return;
155
156 cand_copy = toptree_alloc(cand->level, 0);
157 toptree_for_each_child_safe(child, tmp, cand) {
158 struct toptree *tmpchild;
159
160 if (!cpumask_empty(&child->mask)) {
161 tmpchild = toptree_get_child(cand_copy, child->id);
162 toptree_move_children(child, tmpchild);
163 }
164 toptree_free(child);
165 }
166 toptree_move_children(cand_copy, cand);
167 toptree_free(cand_copy);
168
169 toptree_for_each_child(child, cand)
170 toptree_unify(child);
171}
172
173/**
174 * toptree_move - Move a node to another context
175 * @cand: Pointer to the node to move
176 * @target: Pointer to the node where @cand should go
177 *
178 * In the easiest case @cand is exactly on the level below @target
179 * and will be immediately moved to the target.
180 *
181 * If @target's level is not the direct parent level of @cand,
182 * nodes for the missing levels are created and put between
183 * @cand and @target. The "stacking" nodes' IDs are taken from
184 * @cand's parents.
185 *
186 * After this it is likely to have redundant nodes in the tree
187 * which are addressed by means of toptree_unify.
188 */
189void toptree_move(struct toptree *cand, struct toptree *target)
190{
191 struct toptree *stack_target, *real_insert_point, *ptr, *tmp;
192
193 if (cand->level + 1 == target->level) {
194 toptree_remove(cand);
195 toptree_insert(cand, target);
196 return;
197 }
198
199 real_insert_point = NULL;
200 ptr = cand;
201 stack_target = NULL;
202
203 do {
204 tmp = stack_target;
205 stack_target = toptree_alloc(ptr->level + 1,
206 ptr->parent->id);
207 toptree_insert(tmp, stack_target);
208 if (!real_insert_point)
209 real_insert_point = stack_target;
210 ptr = ptr->parent;
211 } while (stack_target->level < (target->level - 1));
212
213 toptree_remove(cand);
214 toptree_insert(cand, real_insert_point);
215 toptree_insert(stack_target, target);
216}
217
218/**
219 * toptree_get_child - Access a tree node's child by its ID
220 * @cand: Pointer to tree node whose child is to access
221 * @id: The desired child's ID
222 *
223 * @cand's children are searched for a child with matching ID.
224 * If no match can be found, a new child with the desired ID
225 * is created and returned.
226 */
227struct toptree *toptree_get_child(struct toptree *cand, int id)
228{
229 struct toptree *child;
230
231 toptree_for_each_child(child, cand)
232 if (child->id == id)
233 return child;
234 child = toptree_alloc(cand->level-1, id);
235 toptree_insert(child, cand);
236 return child;
237}
238
239/**
240 * toptree_first - Find the first descendant on specified level
241 * @context: Pointer to tree node whose descendants are to be used
242 * @level: The level of interest
243 *
244 * RETURNS:
245 * @context's first descendant on the specified level, or NULL
246 * if there is no matching descendant
247 */
248struct toptree *toptree_first(struct toptree *context, int level)
249{
250 struct toptree *child, *tmp;
251
252 if (context->level == level)
253 return context;
254
255 if (!list_empty(&context->children)) {
256 list_for_each_entry(child, &context->children, sibling) {
257 tmp = toptree_first(child, level);
258 if (tmp)
259 return tmp;
260 }
261 }
262 return NULL;
263}
264
265/**
266 * toptree_next_sibling - Return next sibling
267 * @cur: Pointer to a tree node
268 *
269 * RETURNS:
270 * If @cur has a parent and is not the last in the parent's children list,
271 * the next sibling is returned. Or NULL when there are no siblings left.
272 */
273static struct toptree *toptree_next_sibling(struct toptree *cur)
274{
275 if (cur->parent == NULL)
276 return NULL;
277
278 if (cur == list_last_entry(&cur->parent->children,
279 struct toptree, sibling))
280 return NULL;
281 return (struct toptree *) list_next_entry(cur, sibling);
282}
283
284/**
285 * toptree_next - Tree traversal function
286 * @cur: Pointer to current element
287 * @context: Pointer to the root node of the tree or subtree to
288 * be traversed.
289 * @level: The level of interest.
290 *
291 * RETURNS:
292 * Pointer to the next node on level @level
293 * or NULL when there is no next node.
294 */
295struct toptree *toptree_next(struct toptree *cur, struct toptree *context,
296 int level)
297{
298 struct toptree *cur_context, *tmp;
299
300 if (!cur)
301 return NULL;
302
303 if (context->level == level)
304 return NULL;
305
306 tmp = toptree_next_sibling(cur);
307 if (tmp != NULL)
308 return tmp;
309
310 cur_context = cur;
311 while (cur_context->level < context->level - 1) {
312 /* Step up */
313 cur_context = cur_context->parent;
314 /* Step aside */
315 tmp = toptree_next_sibling(cur_context);
316 if (tmp != NULL) {
317 /* Step down */
318 tmp = toptree_first(tmp, level);
319 if (tmp != NULL)
320 return tmp;
321 }
322 }
323 return NULL;
324}
325
326/**
327 * toptree_count - Count descendants on specified level
328 * @context: Pointer to node whose descendants are to be considered
329 * @level: Only descendants on the specified level will be counted
330 *
331 * RETURNS:
332 * Number of descendants on the specified level
333 */
334int toptree_count(struct toptree *context, int level)
335{
336 struct toptree *cur;
337 int cnt = 0;
338
339 toptree_for_each(cur, context, level)
340 cnt++;
341 return cnt;
342}
diff --git a/arch/s390/numa/toptree.h b/arch/s390/numa/toptree.h
new file mode 100644
index 000000000000..bdf502027af4
--- /dev/null
+++ b/arch/s390/numa/toptree.h
@@ -0,0 +1,60 @@
1/*
2 * NUMA support for s390
3 *
4 * A tree structure used for machine topology mangling
5 *
6 * Copyright IBM Corp. 2015
7 */
8#ifndef S390_TOPTREE_H
9#define S390_TOPTREE_H
10
11#include <linux/cpumask.h>
12#include <linux/list.h>
13
14struct toptree {
15 int level;
16 int id;
17 cpumask_t mask;
18 struct toptree *parent;
19 struct list_head sibling;
20 struct list_head children;
21};
22
23struct toptree *toptree_alloc(int level, int id);
24void toptree_free(struct toptree *cand);
25void toptree_update_mask(struct toptree *cand);
26void toptree_unify(struct toptree *cand);
27struct toptree *toptree_get_child(struct toptree *cand, int id);
28void toptree_move(struct toptree *cand, struct toptree *target);
29int toptree_count(struct toptree *context, int level);
30
31struct toptree *toptree_first(struct toptree *context, int level);
32struct toptree *toptree_next(struct toptree *cur, struct toptree *context,
33 int level);
34
35#define toptree_for_each_child(child, ptree) \
36 list_for_each_entry(child, &ptree->children, sibling)
37
38#define toptree_for_each_child_safe(child, ptmp, ptree) \
39 list_for_each_entry_safe(child, ptmp, &ptree->children, sibling)
40
41#define toptree_is_last(ptree) \
42 ((ptree->parent == NULL) || \
43 (ptree->parent->children.prev == &ptree->sibling))
44
45#define toptree_for_each(ptree, cont, ttype) \
46 for (ptree = toptree_first(cont, ttype); \
47 ptree != NULL; \
48 ptree = toptree_next(ptree, cont, ttype))
49
50#define toptree_for_each_safe(ptree, tmp, cont, ttype) \
51 for (ptree = toptree_first(cont, ttype), \
52 tmp = toptree_next(ptree, cont, ttype); \
53 ptree != NULL; \
54 ptree = tmp, \
55 tmp = toptree_next(ptree, cont, ttype))
56
57#define toptree_for_each_sibling(ptree, start) \
58 toptree_for_each(ptree, start->parent, start->level)
59
60#endif /* S390_TOPTREE_H */