aboutsummaryrefslogtreecommitdiffstats
path: root/arch/ia64
diff options
context:
space:
mode:
Diffstat (limited to 'arch/ia64')
-rw-r--r--arch/ia64/sn/kernel/sn2/sn_hwperf.c266
1 files changed, 246 insertions, 20 deletions
diff --git a/arch/ia64/sn/kernel/sn2/sn_hwperf.c b/arch/ia64/sn/kernel/sn2/sn_hwperf.c
index 58e48fdf5b47..0261452d08df 100644
--- a/arch/ia64/sn/kernel/sn2/sn_hwperf.c
+++ b/arch/ia64/sn/kernel/sn2/sn_hwperf.c
@@ -59,7 +59,7 @@ static int sn_hwperf_enum_objects(int *nobj, struct sn_hwperf_object_info **ret)
59 struct sn_hwperf_object_info *objbuf = NULL; 59 struct sn_hwperf_object_info *objbuf = NULL;
60 60
61 if ((e = sn_hwperf_init()) < 0) { 61 if ((e = sn_hwperf_init()) < 0) {
62 printk("sn_hwperf_init failed: err %d\n", e); 62 printk(KERN_ERR "sn_hwperf_init failed: err %d\n", e);
63 goto out; 63 goto out;
64 } 64 }
65 65
@@ -111,7 +111,7 @@ static int sn_hwperf_geoid_to_cnode(char *location)
111 if (sn_hwperf_location_to_bpos(location, &rack, &bay, &slot, &slab)) 111 if (sn_hwperf_location_to_bpos(location, &rack, &bay, &slot, &slab))
112 return -1; 112 return -1;
113 113
114 for (cnode = 0; cnode < numionodes; cnode++) { 114 for_each_node(cnode) {
115 geoid = cnodeid_get_geoid(cnode); 115 geoid = cnodeid_get_geoid(cnode);
116 module_id = geo_module(geoid); 116 module_id = geo_module(geoid);
117 this_rack = MODULE_GET_RACK(module_id); 117 this_rack = MODULE_GET_RACK(module_id);
@@ -124,11 +124,13 @@ static int sn_hwperf_geoid_to_cnode(char *location)
124 } 124 }
125 } 125 }
126 126
127 return cnode < numionodes ? cnode : -1; 127 return node_possible(cnode) ? cnode : -1;
128} 128}
129 129
130static int sn_hwperf_obj_to_cnode(struct sn_hwperf_object_info * obj) 130static int sn_hwperf_obj_to_cnode(struct sn_hwperf_object_info * obj)
131{ 131{
132 if (!SN_HWPERF_IS_NODE(obj) && !SN_HWPERF_IS_IONODE(obj))
133 BUG();
132 if (!obj->sn_hwp_this_part) 134 if (!obj->sn_hwp_this_part)
133 return -1; 135 return -1;
134 return sn_hwperf_geoid_to_cnode(obj->location); 136 return sn_hwperf_geoid_to_cnode(obj->location);
@@ -192,6 +194,181 @@ static void print_pci_topology(struct seq_file *s)
192 } 194 }
193} 195}
194 196
197static inline int sn_hwperf_has_cpus(cnodeid_t node)
198{
199 return node_online(node) && nr_cpus_node(node);
200}
201
202static inline int sn_hwperf_has_mem(cnodeid_t node)
203{
204 return node_online(node) && NODE_DATA(node)->node_present_pages;
205}
206
207static struct sn_hwperf_object_info *
208sn_hwperf_findobj_id(struct sn_hwperf_object_info *objbuf,
209 int nobj, int id)
210{
211 int i;
212 struct sn_hwperf_object_info *p = objbuf;
213
214 for (i=0; i < nobj; i++, p++) {
215 if (p->id == id)
216 return p;
217 }
218
219 return NULL;
220
221}
222
223static int sn_hwperf_get_nearest_node_objdata(struct sn_hwperf_object_info *objbuf,
224 int nobj, cnodeid_t node, cnodeid_t *near_mem_node, cnodeid_t *near_cpu_node)
225{
226 int e;
227 struct sn_hwperf_object_info *nodeobj = NULL;
228 struct sn_hwperf_object_info *op;
229 struct sn_hwperf_object_info *dest;
230 struct sn_hwperf_object_info *router;
231 struct sn_hwperf_port_info ptdata[16];
232 int sz, i, j;
233 cnodeid_t c;
234 int found_mem = 0;
235 int found_cpu = 0;
236
237 if (!node_possible(node))
238 return -EINVAL;
239
240 if (sn_hwperf_has_cpus(node)) {
241 if (near_cpu_node)
242 *near_cpu_node = node;
243 found_cpu++;
244 }
245
246 if (sn_hwperf_has_mem(node)) {
247 if (near_mem_node)
248 *near_mem_node = node;
249 found_mem++;
250 }
251
252 if (found_cpu && found_mem)
253 return 0; /* trivially successful */
254
255 /* find the argument node object */
256 for (i=0, op=objbuf; i < nobj; i++, op++) {
257 if (!SN_HWPERF_IS_NODE(op) && !SN_HWPERF_IS_IONODE(op))
258 continue;
259 if (node == sn_hwperf_obj_to_cnode(op)) {
260 nodeobj = op;
261 break;
262 }
263 }
264 if (!nodeobj) {
265 e = -ENOENT;
266 goto err;
267 }
268
269 /* get it's interconnect topology */
270 sz = op->ports * sizeof(struct sn_hwperf_port_info);
271 if (sz > sizeof(ptdata))
272 BUG();
273 e = ia64_sn_hwperf_op(sn_hwperf_master_nasid,
274 SN_HWPERF_ENUM_PORTS, nodeobj->id, sz,
275 (u64)&ptdata, 0, 0, NULL);
276 if (e != SN_HWPERF_OP_OK) {
277 e = -EINVAL;
278 goto err;
279 }
280
281 /* find nearest node with cpus and nearest memory */
282 for (router=NULL, j=0; j < op->ports; j++) {
283 dest = sn_hwperf_findobj_id(objbuf, nobj, ptdata[j].conn_id);
284 if (!dest || SN_HWPERF_FOREIGN(dest) ||
285 !SN_HWPERF_IS_NODE(dest) || SN_HWPERF_IS_IONODE(dest)) {
286 continue;
287 }
288 c = sn_hwperf_obj_to_cnode(dest);
289 if (!found_cpu && sn_hwperf_has_cpus(c)) {
290 if (near_cpu_node)
291 *near_cpu_node = c;
292 found_cpu++;
293 }
294 if (!found_mem && sn_hwperf_has_mem(c)) {
295 if (near_mem_node)
296 *near_mem_node = c;
297 found_mem++;
298 }
299 if (SN_HWPERF_IS_ROUTER(dest))
300 router = dest;
301 }
302
303 if (router && (!found_cpu || !found_mem)) {
304 /* search for a node connected to the same router */
305 sz = router->ports * sizeof(struct sn_hwperf_port_info);
306 if (sz > sizeof(ptdata))
307 BUG();
308 e = ia64_sn_hwperf_op(sn_hwperf_master_nasid,
309 SN_HWPERF_ENUM_PORTS, router->id, sz,
310 (u64)&ptdata, 0, 0, NULL);
311 if (e != SN_HWPERF_OP_OK) {
312 e = -EINVAL;
313 goto err;
314 }
315 for (j=0; j < router->ports; j++) {
316 dest = sn_hwperf_findobj_id(objbuf, nobj,
317 ptdata[j].conn_id);
318 if (!dest || dest->id == node ||
319 SN_HWPERF_FOREIGN(dest) ||
320 !SN_HWPERF_IS_NODE(dest) ||
321 SN_HWPERF_IS_IONODE(dest)) {
322 continue;
323 }
324 c = sn_hwperf_obj_to_cnode(dest);
325 if (!found_cpu && sn_hwperf_has_cpus(c)) {
326 if (near_cpu_node)
327 *near_cpu_node = c;
328 found_cpu++;
329 }
330 if (!found_mem && sn_hwperf_has_mem(c)) {
331 if (near_mem_node)
332 *near_mem_node = c;
333 found_mem++;
334 }
335 if (found_cpu && found_mem)
336 break;
337 }
338 }
339
340 if (!found_cpu || !found_mem) {
341 /* resort to _any_ node with CPUs and memory */
342 for (i=0, op=objbuf; i < nobj; i++, op++) {
343 if (SN_HWPERF_FOREIGN(op) ||
344 SN_HWPERF_IS_IONODE(op) ||
345 !SN_HWPERF_IS_NODE(op)) {
346 continue;
347 }
348 c = sn_hwperf_obj_to_cnode(op);
349 if (!found_cpu && sn_hwperf_has_cpus(c)) {
350 if (near_cpu_node)
351 *near_cpu_node = c;
352 found_cpu++;
353 }
354 if (!found_mem && sn_hwperf_has_mem(c)) {
355 if (near_mem_node)
356 *near_mem_node = c;
357 found_mem++;
358 }
359 if (found_cpu && found_mem)
360 break;
361 }
362 }
363
364 if (!found_cpu || !found_mem)
365 e = -ENODATA;
366
367err:
368 return e;
369}
370
371
195static int sn_topology_show(struct seq_file *s, void *d) 372static int sn_topology_show(struct seq_file *s, void *d)
196{ 373{
197 int sz; 374 int sz;
@@ -265,11 +442,24 @@ static int sn_topology_show(struct seq_file *s, void *d)
265 if (!SN_HWPERF_IS_NODE(obj) && !SN_HWPERF_IS_IONODE(obj)) 442 if (!SN_HWPERF_IS_NODE(obj) && !SN_HWPERF_IS_IONODE(obj))
266 seq_putc(s, '\n'); 443 seq_putc(s, '\n');
267 else { 444 else {
445 cnodeid_t near_mem = -1;
446 cnodeid_t near_cpu = -1;
447
268 seq_printf(s, ", nasid 0x%x", cnodeid_to_nasid(ordinal)); 448 seq_printf(s, ", nasid 0x%x", cnodeid_to_nasid(ordinal));
269 for (i=0; i < numionodes; i++) { 449
270 seq_printf(s, i ? ":%d" : ", dist %d", 450 if (sn_hwperf_get_nearest_node_objdata(objs, sn_hwperf_obj_cnt,
271 node_distance(ordinal, i)); 451 ordinal, &near_mem, &near_cpu) == 0) {
452 seq_printf(s, ", near_mem_nodeid %d, near_cpu_nodeid %d",
453 near_mem, near_cpu);
454 }
455
456 if (!SN_HWPERF_IS_IONODE(obj)) {
457 for_each_online_node(i) {
458 seq_printf(s, i ? ":%d" : ", dist %d",
459 node_distance(ordinal, i));
460 }
272 } 461 }
462
273 seq_putc(s, '\n'); 463 seq_putc(s, '\n');
274 464
275 /* 465 /*
@@ -554,6 +744,8 @@ sn_hwperf_ioctl(struct inode *in, struct file *fp, u32 op, u64 arg)
554 if ((r = sn_hwperf_enum_objects(&nobj, &objs)) == 0) { 744 if ((r = sn_hwperf_enum_objects(&nobj, &objs)) == 0) {
555 memset(p, 0, a.sz); 745 memset(p, 0, a.sz);
556 for (i = 0; i < nobj; i++) { 746 for (i = 0; i < nobj; i++) {
747 if (!SN_HWPERF_IS_NODE(objs + i))
748 continue;
557 node = sn_hwperf_obj_to_cnode(objs + i); 749 node = sn_hwperf_obj_to_cnode(objs + i);
558 for_each_online_cpu(j) { 750 for_each_online_cpu(j) {
559 if (node != cpu_to_node(j)) 751 if (node != cpu_to_node(j))
@@ -580,7 +772,7 @@ sn_hwperf_ioctl(struct inode *in, struct file *fp, u32 op, u64 arg)
580 772
581 case SN_HWPERF_GET_NODE_NASID: 773 case SN_HWPERF_GET_NODE_NASID:
582 if (a.sz != sizeof(u64) || 774 if (a.sz != sizeof(u64) ||
583 (node = a.arg) < 0 || node >= numionodes) { 775 (node = a.arg) < 0 || !node_possible(node)) {
584 r = -EINVAL; 776 r = -EINVAL;
585 goto error; 777 goto error;
586 } 778 }
@@ -609,6 +801,14 @@ sn_hwperf_ioctl(struct inode *in, struct file *fp, u32 op, u64 arg)
609 vfree(objs); 801 vfree(objs);
610 goto error; 802 goto error;
611 } 803 }
804
805 if (!SN_HWPERF_IS_NODE(objs + i) &&
806 !SN_HWPERF_IS_IONODE(objs + i)) {
807 r = -ENOENT;
808 vfree(objs);
809 goto error;
810 }
811
612 *(u64 *)p = (u64)sn_hwperf_obj_to_cnode(objs + i); 812 *(u64 *)p = (u64)sn_hwperf_obj_to_cnode(objs + i);
613 vfree(objs); 813 vfree(objs);
614 } 814 }
@@ -674,6 +874,7 @@ static int sn_hwperf_init(void)
674 874
675 /* single threaded, once-only initialization */ 875 /* single threaded, once-only initialization */
676 down(&sn_hwperf_init_mutex); 876 down(&sn_hwperf_init_mutex);
877
677 if (sn_hwperf_salheap) { 878 if (sn_hwperf_salheap) {
678 up(&sn_hwperf_init_mutex); 879 up(&sn_hwperf_init_mutex);
679 return e; 880 return e;
@@ -724,19 +925,6 @@ out:
724 sn_hwperf_salheap = NULL; 925 sn_hwperf_salheap = NULL;
725 sn_hwperf_obj_cnt = 0; 926 sn_hwperf_obj_cnt = 0;
726 } 927 }
727
728 if (!e) {
729 /*
730 * Register a dynamic misc device for ioctl. Platforms
731 * supporting hotplug will create /dev/sn_hwperf, else
732 * user can to look up the minor number in /proc/misc.
733 */
734 if ((e = misc_register(&sn_hwperf_dev)) != 0) {
735 printk(KERN_ERR "sn_hwperf_init: misc register "
736 "for \"sn_hwperf\" failed, err %d\n", e);
737 }
738 }
739
740 up(&sn_hwperf_init_mutex); 928 up(&sn_hwperf_init_mutex);
741 return e; 929 return e;
742} 930}
@@ -764,3 +952,41 @@ int sn_topology_release(struct inode *inode, struct file *file)
764 vfree(seq->private); 952 vfree(seq->private);
765 return seq_release(inode, file); 953 return seq_release(inode, file);
766} 954}
955
956int sn_hwperf_get_nearest_node(cnodeid_t node,
957 cnodeid_t *near_mem_node, cnodeid_t *near_cpu_node)
958{
959 int e;
960 int nobj;
961 struct sn_hwperf_object_info *objbuf;
962
963 if ((e = sn_hwperf_enum_objects(&nobj, &objbuf)) == 0) {
964 e = sn_hwperf_get_nearest_node_objdata(objbuf, nobj,
965 node, near_mem_node, near_cpu_node);
966 vfree(objbuf);
967 }
968
969 return e;
970}
971
972static int __devinit sn_hwperf_misc_register_init(void)
973{
974 int e;
975
976 sn_hwperf_init();
977
978 /*
979 * Register a dynamic misc device for hwperf ioctls. Platforms
980 * supporting hotplug will create /dev/sn_hwperf, else user
981 * can to look up the minor number in /proc/misc.
982 */
983 if ((e = misc_register(&sn_hwperf_dev)) != 0) {
984 printk(KERN_ERR "sn_hwperf_misc_register_init: failed to "
985 "register misc device for \"%s\"\n", sn_hwperf_dev.name);
986 }
987
988 return e;
989}
990
991device_initcall(sn_hwperf_misc_register_init); /* after misc_init() */
992EXPORT_SYMBOL(sn_hwperf_get_nearest_node);