xref: /linux/drivers/of/of_numa.c (revision bb9ae1a66c85eeb626864efd812c62026e126ec0)
1 // SPDX-License-Identifier: GPL-2.0
2 /*
3  * OF NUMA Parsing support.
4  *
5  * Copyright (C) 2015 - 2016 Cavium Inc.
6  */
7 
8 #define pr_fmt(fmt) "OF: NUMA: " fmt
9 
10 #include <linux/of.h>
11 #include <linux/of_address.h>
12 #include <linux/nodemask.h>
13 #include <linux/numa_memblks.h>
14 
15 #include <asm/numa.h>
16 
17 /* define default numa node to 0 */
18 #define DEFAULT_NODE 0
19 
20 /*
21  * Even though we connect cpus to numa domains later in SMP
22  * init, we need to know the node ids now for all cpus.
23 */
24 static void __init of_numa_parse_cpu_nodes(void)
25 {
26 	u32 nid;
27 	int r;
28 	struct device_node *np;
29 
30 	for_each_of_cpu_node(np) {
31 		r = of_property_read_u32(np, "numa-node-id", &nid);
32 		if (r)
33 			continue;
34 
35 		pr_debug("CPU on %u\n", nid);
36 		if (nid >= MAX_NUMNODES)
37 			pr_warn("Node id %u exceeds maximum value\n", nid);
38 		else
39 			node_set(nid, numa_nodes_parsed);
40 	}
41 }
42 
43 static int __init of_numa_parse_memory_nodes(void)
44 {
45 	struct device_node *np = NULL;
46 	struct resource rsrc;
47 	u32 nid;
48 	int i, r = -EINVAL;
49 
50 	for_each_node_by_type(np, "memory") {
51 		r = of_property_read_u32(np, "numa-node-id", &nid);
52 		if (r == -EINVAL)
53 			/*
54 			 * property doesn't exist if -EINVAL, continue
55 			 * looking for more memory nodes with
56 			 * "numa-node-id" property
57 			 */
58 			continue;
59 
60 		if (nid >= MAX_NUMNODES) {
61 			pr_warn("Node id %u exceeds maximum value\n", nid);
62 			r = -EINVAL;
63 		}
64 
65 		for (i = 0; !r && !of_address_to_resource(np, i, &rsrc); i++)
66 			r = numa_add_memblk(nid, rsrc.start, rsrc.end + 1);
67 
68 		if (!i || r) {
69 			of_node_put(np);
70 			pr_err("bad property in memory node\n");
71 			return r ? : -EINVAL;
72 		}
73 	}
74 
75 	return r;
76 }
77 
78 static int __init of_numa_parse_distance_map_v1(struct device_node *map)
79 {
80 	const __be32 *matrix;
81 	int entry_count;
82 	int i;
83 
84 	pr_info("parsing numa-distance-map-v1\n");
85 
86 	matrix = of_get_property(map, "distance-matrix", NULL);
87 	if (!matrix) {
88 		pr_err("No distance-matrix property in distance-map\n");
89 		return -EINVAL;
90 	}
91 
92 	entry_count = of_property_count_u32_elems(map, "distance-matrix");
93 	if (entry_count <= 0) {
94 		pr_err("Invalid distance-matrix\n");
95 		return -EINVAL;
96 	}
97 
98 	for (i = 0; i + 2 < entry_count; i += 3) {
99 		u32 nodea, nodeb, distance;
100 
101 		nodea = of_read_number(matrix, 1);
102 		matrix++;
103 		nodeb = of_read_number(matrix, 1);
104 		matrix++;
105 		distance = of_read_number(matrix, 1);
106 		matrix++;
107 
108 		if ((nodea == nodeb && distance != LOCAL_DISTANCE) ||
109 		    (nodea != nodeb && distance <= LOCAL_DISTANCE)) {
110 			pr_err("Invalid distance[node%d -> node%d] = %d\n",
111 			       nodea, nodeb, distance);
112 			return -EINVAL;
113 		}
114 
115 		node_set(nodea, numa_nodes_parsed);
116 
117 		numa_set_distance(nodea, nodeb, distance);
118 
119 		/* Set default distance of node B->A same as A->B */
120 		if (nodeb > nodea)
121 			numa_set_distance(nodeb, nodea, distance);
122 	}
123 
124 	return 0;
125 }
126 
127 static int __init of_numa_parse_distance_map(void)
128 {
129 	int ret = 0;
130 	struct device_node *np;
131 
132 	np = of_find_compatible_node(NULL, NULL,
133 				     "numa-distance-map-v1");
134 	if (np)
135 		ret = of_numa_parse_distance_map_v1(np);
136 
137 	of_node_put(np);
138 	return ret;
139 }
140 
141 int of_node_to_nid(struct device_node *device)
142 {
143 	struct device_node *np;
144 	u32 nid;
145 	int r = -ENODATA;
146 
147 	np = of_node_get(device);
148 
149 	while (np) {
150 		r = of_property_read_u32(np, "numa-node-id", &nid);
151 		/*
152 		 * -EINVAL indicates the property was not found, and
153 		 *  we walk up the tree trying to find a parent with a
154 		 *  "numa-node-id".  Any other type of error indicates
155 		 *  a bad device tree and we give up.
156 		 */
157 		if (r != -EINVAL)
158 			break;
159 
160 		np = of_get_next_parent(np);
161 	}
162 	if (np && r)
163 		pr_warn("Invalid \"numa-node-id\" property in node %pOFn\n",
164 			np);
165 	of_node_put(np);
166 
167 	/*
168 	 * If numa=off passed on command line, or with a defective
169 	 * device tree, the nid may not be in the set of possible
170 	 * nodes.  Check for this case and return NUMA_NO_NODE.
171 	 */
172 	if (!r && nid < MAX_NUMNODES && node_possible(nid))
173 		return nid;
174 
175 	return NUMA_NO_NODE;
176 }
177 
178 int __init of_numa_init(void)
179 {
180 	int r;
181 
182 	of_numa_parse_cpu_nodes();
183 	r = of_numa_parse_memory_nodes();
184 	if (r)
185 		return r;
186 	return of_numa_parse_distance_map();
187 }
188