Linux Kernel  3.7.1
 All Data Structures Namespaces Files Functions Variables Typedefs Enumerations Enumerator Macros Groups Pages
sn_hwperf.c
Go to the documentation of this file.
1 /*
2  * This file is subject to the terms and conditions of the GNU General Public
3  * License. See the file "COPYING" in the main directory of this archive
4  * for more details.
5  *
6  * Copyright (C) 2004-2006 Silicon Graphics, Inc. All rights reserved.
7  *
8  * SGI Altix topology and hardware performance monitoring API.
9  * Mark Goodwin <[email protected]>.
10  *
11  * Creates /proc/sgi_sn/sn_topology (read-only) to export
12  * info about Altix nodes, routers, CPUs and NumaLink
13  * interconnection/topology.
14  *
15  * Also creates a dynamic misc device named "sn_hwperf"
16  * that supports an ioctl interface to call down into SAL
17  * to discover hw objects, topology and to read/write
18  * memory mapped registers, e.g. for performance monitoring.
19  * The "sn_hwperf" device is registered only after the procfs
20  * file is first opened, i.e. only if/when it's needed.
21  *
22  * This API is used by SGI Performance Co-Pilot and other
23  * tools, see http://oss.sgi.com/projects/pcp
24  */
25 
26 #include <linux/fs.h>
27 #include <linux/slab.h>
28 #include <linux/export.h>
29 #include <linux/vmalloc.h>
30 #include <linux/seq_file.h>
31 #include <linux/miscdevice.h>
32 #include <linux/utsname.h>
33 #include <linux/cpumask.h>
34 #include <linux/nodemask.h>
35 #include <linux/smp.h>
36 #include <linux/mutex.h>
37 
38 #include <asm/processor.h>
39 #include <asm/topology.h>
40 #include <asm/uaccess.h>
41 #include <asm/sal.h>
42 #include <asm/sn/io.h>
43 #include <asm/sn/sn_sal.h>
44 #include <asm/sn/module.h>
45 #include <asm/sn/geo.h>
46 #include <asm/sn/sn2/sn_hwperf.h>
47 #include <asm/sn/addrs.h>
48 
49 static void *sn_hwperf_salheap = NULL;
50 static int sn_hwperf_obj_cnt = 0;
51 static nasid_t sn_hwperf_master_nasid = INVALID_NASID;
52 static int sn_hwperf_init(void);
53 static DEFINE_MUTEX(sn_hwperf_init_mutex);
54 
55 #define cnode_possible(n) ((n) < num_cnodes)
56 
57 static int sn_hwperf_enum_objects(int *nobj, struct sn_hwperf_object_info **ret)
58 {
59  int e;
60  u64 sz;
61  struct sn_hwperf_object_info *objbuf = NULL;
62 
63  if ((e = sn_hwperf_init()) < 0) {
64  printk(KERN_ERR "sn_hwperf_init failed: err %d\n", e);
65  goto out;
66  }
67 
68  sz = sn_hwperf_obj_cnt * sizeof(struct sn_hwperf_object_info);
69  objbuf = vmalloc(sz);
70  if (objbuf == NULL) {
71  printk("sn_hwperf_enum_objects: vmalloc(%d) failed\n", (int)sz);
72  e = -ENOMEM;
73  goto out;
74  }
75 
76  e = ia64_sn_hwperf_op(sn_hwperf_master_nasid, SN_HWPERF_ENUM_OBJECTS,
77  0, sz, (u64) objbuf, 0, 0, NULL);
78  if (e != SN_HWPERF_OP_OK) {
79  e = -EINVAL;
80  vfree(objbuf);
81  }
82 
83 out:
84  *nobj = sn_hwperf_obj_cnt;
85  *ret = objbuf;
86  return e;
87 }
88 
89 static int sn_hwperf_location_to_bpos(char *location,
90  int *rack, int *bay, int *slot, int *slab)
91 {
92  char type;
93 
94  /* first scan for an old style geoid string */
95  if (sscanf(location, "%03d%c%02d#%d",
96  rack, &type, bay, slab) == 4)
97  *slot = 0;
98  else /* scan for a new bladed geoid string */
99  if (sscanf(location, "%03d%c%02d^%02d#%d",
100  rack, &type, bay, slot, slab) != 5)
101  return -1;
102  /* success */
103  return 0;
104 }
105 
106 static int sn_hwperf_geoid_to_cnode(char *location)
107 {
108  int cnode;
109  geoid_t geoid;
111  int rack, bay, slot, slab;
112  int this_rack, this_bay, this_slot, this_slab;
113 
114  if (sn_hwperf_location_to_bpos(location, &rack, &bay, &slot, &slab))
115  return -1;
116 
117  /*
118  * FIXME: replace with cleaner for_each_XXX macro which addresses
119  * both compute and IO nodes once ACPI3.0 is available.
120  */
121  for (cnode = 0; cnode < num_cnodes; cnode++) {
122  geoid = cnodeid_get_geoid(cnode);
123  module_id = geo_module(geoid);
124  this_rack = MODULE_GET_RACK(module_id);
125  this_bay = MODULE_GET_BPOS(module_id);
126  this_slot = geo_slot(geoid);
127  this_slab = geo_slab(geoid);
128  if (rack == this_rack && bay == this_bay &&
129  slot == this_slot && slab == this_slab) {
130  break;
131  }
132  }
133 
134  return cnode_possible(cnode) ? cnode : -1;
135 }
136 
137 static int sn_hwperf_obj_to_cnode(struct sn_hwperf_object_info * obj)
138 {
139  if (!SN_HWPERF_IS_NODE(obj) && !SN_HWPERF_IS_IONODE(obj))
140  BUG();
141  if (SN_HWPERF_FOREIGN(obj))
142  return -1;
143  return sn_hwperf_geoid_to_cnode(obj->location);
144 }
145 
146 static int sn_hwperf_generic_ordinal(struct sn_hwperf_object_info *obj,
147  struct sn_hwperf_object_info *objs)
148 {
149  int ordinal;
150  struct sn_hwperf_object_info *p;
151 
152  for (ordinal=0, p=objs; p != obj; p++) {
153  if (SN_HWPERF_FOREIGN(p))
154  continue;
155  if (SN_HWPERF_SAME_OBJTYPE(p, obj))
156  ordinal++;
157  }
158 
159  return ordinal;
160 }
161 
162 static const char *slabname_node = "node"; /* SHub asic */
163 static const char *slabname_ionode = "ionode"; /* TIO asic */
164 static const char *slabname_router = "router"; /* NL3R or NL4R */
165 static const char *slabname_other = "other"; /* unknown asic */
166 
167 static const char *sn_hwperf_get_slabname(struct sn_hwperf_object_info *obj,
168  struct sn_hwperf_object_info *objs, int *ordinal)
169 {
170  int isnode;
171  const char *slabname = slabname_other;
172 
173  if ((isnode = SN_HWPERF_IS_NODE(obj)) || SN_HWPERF_IS_IONODE(obj)) {
174  slabname = isnode ? slabname_node : slabname_ionode;
175  *ordinal = sn_hwperf_obj_to_cnode(obj);
176  }
177  else {
178  *ordinal = sn_hwperf_generic_ordinal(obj, objs);
179  if (SN_HWPERF_IS_ROUTER(obj))
180  slabname = slabname_router;
181  }
182 
183  return slabname;
184 }
185 
186 static void print_pci_topology(struct seq_file *s)
187 {
188  char *p;
189  size_t sz;
190  int e;
191 
192  for (sz = PAGE_SIZE; sz < 16 * PAGE_SIZE; sz += PAGE_SIZE) {
193  if (!(p = kmalloc(sz, GFP_KERNEL)))
194  break;
195  e = ia64_sn_ioif_get_pci_topology(__pa(p), sz);
196  if (e == SALRET_OK)
197  seq_puts(s, p);
198  kfree(p);
199  if (e == SALRET_OK || e == SALRET_NOT_IMPLEMENTED)
200  break;
201  }
202 }
203 
204 static inline int sn_hwperf_has_cpus(cnodeid_t node)
205 {
206  return node < MAX_NUMNODES && node_online(node) && nr_cpus_node(node);
207 }
208 
209 static inline int sn_hwperf_has_mem(cnodeid_t node)
210 {
211  return node < MAX_NUMNODES && node_online(node) && NODE_DATA(node)->node_present_pages;
212 }
213 
214 static struct sn_hwperf_object_info *
215 sn_hwperf_findobj_id(struct sn_hwperf_object_info *objbuf,
216  int nobj, int id)
217 {
218  int i;
219  struct sn_hwperf_object_info *p = objbuf;
220 
221  for (i=0; i < nobj; i++, p++) {
222  if (p->id == id)
223  return p;
224  }
225 
226  return NULL;
227 
228 }
229 
230 static int sn_hwperf_get_nearest_node_objdata(struct sn_hwperf_object_info *objbuf,
231  int nobj, cnodeid_t node, cnodeid_t *near_mem_node, cnodeid_t *near_cpu_node)
232 {
233  int e;
234  struct sn_hwperf_object_info *nodeobj = NULL;
235  struct sn_hwperf_object_info *op;
236  struct sn_hwperf_object_info *dest;
237  struct sn_hwperf_object_info *router;
238  struct sn_hwperf_port_info ptdata[16];
239  int sz, i, j;
240  cnodeid_t c;
241  int found_mem = 0;
242  int found_cpu = 0;
243 
244  if (!cnode_possible(node))
245  return -EINVAL;
246 
247  if (sn_hwperf_has_cpus(node)) {
248  if (near_cpu_node)
249  *near_cpu_node = node;
250  found_cpu++;
251  }
252 
253  if (sn_hwperf_has_mem(node)) {
254  if (near_mem_node)
255  *near_mem_node = node;
256  found_mem++;
257  }
258 
259  if (found_cpu && found_mem)
260  return 0; /* trivially successful */
261 
262  /* find the argument node object */
263  for (i=0, op=objbuf; i < nobj; i++, op++) {
264  if (!SN_HWPERF_IS_NODE(op) && !SN_HWPERF_IS_IONODE(op))
265  continue;
266  if (node == sn_hwperf_obj_to_cnode(op)) {
267  nodeobj = op;
268  break;
269  }
270  }
271  if (!nodeobj) {
272  e = -ENOENT;
273  goto err;
274  }
275 
276  /* get it's interconnect topology */
277  sz = op->ports * sizeof(struct sn_hwperf_port_info);
278  BUG_ON(sz > sizeof(ptdata));
279  e = ia64_sn_hwperf_op(sn_hwperf_master_nasid,
280  SN_HWPERF_ENUM_PORTS, nodeobj->id, sz,
281  (u64)&ptdata, 0, 0, NULL);
282  if (e != SN_HWPERF_OP_OK) {
283  e = -EINVAL;
284  goto err;
285  }
286 
287  /* find nearest node with cpus and nearest memory */
288  for (router=NULL, j=0; j < op->ports; j++) {
289  dest = sn_hwperf_findobj_id(objbuf, nobj, ptdata[j].conn_id);
290  if (dest && SN_HWPERF_IS_ROUTER(dest))
291  router = dest;
292  if (!dest || SN_HWPERF_FOREIGN(dest) ||
293  !SN_HWPERF_IS_NODE(dest) || SN_HWPERF_IS_IONODE(dest)) {
294  continue;
295  }
296  c = sn_hwperf_obj_to_cnode(dest);
297  if (!found_cpu && sn_hwperf_has_cpus(c)) {
298  if (near_cpu_node)
299  *near_cpu_node = c;
300  found_cpu++;
301  }
302  if (!found_mem && sn_hwperf_has_mem(c)) {
303  if (near_mem_node)
304  *near_mem_node = c;
305  found_mem++;
306  }
307  }
308 
309  if (router && (!found_cpu || !found_mem)) {
310  /* search for a node connected to the same router */
311  sz = router->ports * sizeof(struct sn_hwperf_port_info);
312  BUG_ON(sz > sizeof(ptdata));
313  e = ia64_sn_hwperf_op(sn_hwperf_master_nasid,
314  SN_HWPERF_ENUM_PORTS, router->id, sz,
315  (u64)&ptdata, 0, 0, NULL);
316  if (e != SN_HWPERF_OP_OK) {
317  e = -EINVAL;
318  goto err;
319  }
320  for (j=0; j < router->ports; j++) {
321  dest = sn_hwperf_findobj_id(objbuf, nobj,
322  ptdata[j].conn_id);
323  if (!dest || dest->id == node ||
324  SN_HWPERF_FOREIGN(dest) ||
325  !SN_HWPERF_IS_NODE(dest) ||
326  SN_HWPERF_IS_IONODE(dest)) {
327  continue;
328  }
329  c = sn_hwperf_obj_to_cnode(dest);
330  if (!found_cpu && sn_hwperf_has_cpus(c)) {
331  if (near_cpu_node)
332  *near_cpu_node = c;
333  found_cpu++;
334  }
335  if (!found_mem && sn_hwperf_has_mem(c)) {
336  if (near_mem_node)
337  *near_mem_node = c;
338  found_mem++;
339  }
340  if (found_cpu && found_mem)
341  break;
342  }
343  }
344 
345  if (!found_cpu || !found_mem) {
346  /* resort to _any_ node with CPUs and memory */
347  for (i=0, op=objbuf; i < nobj; i++, op++) {
348  if (SN_HWPERF_FOREIGN(op) ||
349  SN_HWPERF_IS_IONODE(op) ||
350  !SN_HWPERF_IS_NODE(op)) {
351  continue;
352  }
353  c = sn_hwperf_obj_to_cnode(op);
354  if (!found_cpu && sn_hwperf_has_cpus(c)) {
355  if (near_cpu_node)
356  *near_cpu_node = c;
357  found_cpu++;
358  }
359  if (!found_mem && sn_hwperf_has_mem(c)) {
360  if (near_mem_node)
361  *near_mem_node = c;
362  found_mem++;
363  }
364  if (found_cpu && found_mem)
365  break;
366  }
367  }
368 
369  if (!found_cpu || !found_mem)
370  e = -ENODATA;
371 
372 err:
373  return e;
374 }
375 
376 
377 static int sn_topology_show(struct seq_file *s, void *d)
378 {
379  int sz;
380  int pt;
381  int e = 0;
382  int i;
383  int j;
384  const char *slabname;
385  int ordinal;
386  char slice;
387  struct cpuinfo_ia64 *c;
388  struct sn_hwperf_port_info *ptdata;
389  struct sn_hwperf_object_info *p;
390  struct sn_hwperf_object_info *obj = d; /* this object */
391  struct sn_hwperf_object_info *objs = s->private; /* all objects */
392  u8 shubtype;
393  u8 system_size;
394  u8 sharing_size;
395  u8 partid;
396  u8 coher;
397  u8 nasid_shift;
398  u8 region_size;
399  u16 nasid_mask;
400  int nasid_msb;
401 
402  if (obj == objs) {
403  seq_printf(s, "# sn_topology version 2\n");
404  seq_printf(s, "# objtype ordinal location partition"
405  " [attribute value [, ...]]\n");
406 
407  if (ia64_sn_get_sn_info(0,
408  &shubtype, &nasid_mask, &nasid_shift, &system_size,
409  &sharing_size, &partid, &coher, &region_size))
410  BUG();
411  for (nasid_msb=63; nasid_msb > 0; nasid_msb--) {
412  if (((u64)nasid_mask << nasid_shift) & (1ULL << nasid_msb))
413  break;
414  }
415  seq_printf(s, "partition %u %s local "
416  "shubtype %s, "
417  "nasid_mask 0x%016llx, "
418  "nasid_bits %d:%d, "
419  "system_size %d, "
420  "sharing_size %d, "
421  "coherency_domain %d, "
422  "region_size %d\n",
423 
424  partid, utsname()->nodename,
425  shubtype ? "shub2" : "shub1",
426  (u64)nasid_mask << nasid_shift, nasid_msb, nasid_shift,
427  system_size, sharing_size, coher, region_size);
428 
429  print_pci_topology(s);
430  }
431 
432  if (SN_HWPERF_FOREIGN(obj)) {
433  /* private in another partition: not interesting */
434  return 0;
435  }
436 
437  for (i = 0; i < SN_HWPERF_MAXSTRING && obj->name[i]; i++) {
438  if (obj->name[i] == ' ')
439  obj->name[i] = '_';
440  }
441 
442  slabname = sn_hwperf_get_slabname(obj, objs, &ordinal);
443  seq_printf(s, "%s %d %s %s asic %s", slabname, ordinal, obj->location,
444  obj->sn_hwp_this_part ? "local" : "shared", obj->name);
445 
446  if (ordinal < 0 || (!SN_HWPERF_IS_NODE(obj) && !SN_HWPERF_IS_IONODE(obj)))
447  seq_putc(s, '\n');
448  else {
449  cnodeid_t near_mem = -1;
450  cnodeid_t near_cpu = -1;
451 
452  seq_printf(s, ", nasid 0x%x", cnodeid_to_nasid(ordinal));
453 
454  if (sn_hwperf_get_nearest_node_objdata(objs, sn_hwperf_obj_cnt,
455  ordinal, &near_mem, &near_cpu) == 0) {
456  seq_printf(s, ", near_mem_nodeid %d, near_cpu_nodeid %d",
457  near_mem, near_cpu);
458  }
459 
460  if (!SN_HWPERF_IS_IONODE(obj)) {
462  seq_printf(s, i ? ":%d" : ", dist %d",
463  node_distance(ordinal, i));
464  }
465  }
466 
467  seq_putc(s, '\n');
468 
469  /*
470  * CPUs on this node, if any
471  */
472  if (!SN_HWPERF_IS_IONODE(obj)) {
473  for_each_cpu_and(i, cpu_online_mask,
474  cpumask_of_node(ordinal)) {
475  slice = 'a' + cpuid_to_slice(i);
476  c = cpu_data(i);
477  seq_printf(s, "cpu %d %s%c local"
478  " freq %luMHz, arch ia64",
479  i, obj->location, slice,
480  c->proc_freq / 1000000);
482  seq_printf(s, j ? ":%d" : ", dist %d",
484  cpu_to_node(i),
485  cpu_to_node(j)));
486  }
487  seq_putc(s, '\n');
488  }
489  }
490  }
491 
492  if (obj->ports) {
493  /*
494  * numalink ports
495  */
496  sz = obj->ports * sizeof(struct sn_hwperf_port_info);
497  if ((ptdata = kmalloc(sz, GFP_KERNEL)) == NULL)
498  return -ENOMEM;
499  e = ia64_sn_hwperf_op(sn_hwperf_master_nasid,
500  SN_HWPERF_ENUM_PORTS, obj->id, sz,
501  (u64) ptdata, 0, 0, NULL);
502  if (e != SN_HWPERF_OP_OK)
503  return -EINVAL;
504  for (ordinal=0, p=objs; p != obj; p++) {
505  if (!SN_HWPERF_FOREIGN(p))
506  ordinal += p->ports;
507  }
508  for (pt = 0; pt < obj->ports; pt++) {
509  for (p = objs, i = 0; i < sn_hwperf_obj_cnt; i++, p++) {
510  if (ptdata[pt].conn_id == p->id) {
511  break;
512  }
513  }
514  seq_printf(s, "numalink %d %s-%d",
515  ordinal+pt, obj->location, ptdata[pt].port);
516 
517  if (i >= sn_hwperf_obj_cnt) {
518  /* no connection */
519  seq_puts(s, " local endpoint disconnected"
520  ", protocol unknown\n");
521  continue;
522  }
523 
524  if (obj->sn_hwp_this_part && p->sn_hwp_this_part)
525  /* both ends local to this partition */
526  seq_puts(s, " local");
527  else if (SN_HWPERF_FOREIGN(p))
528  /* both ends of the link in foreign partiton */
529  seq_puts(s, " foreign");
530  else
531  /* link straddles a partition */
532  seq_puts(s, " shared");
533 
534  /*
535  * Unlikely, but strictly should query the LLP config
536  * registers because an NL4R can be configured to run
537  * NL3 protocol, even when not talking to an NL3 router.
538  * Ditto for node-node.
539  */
540  seq_printf(s, " endpoint %s-%d, protocol %s\n",
541  p->location, ptdata[pt].conn_port,
542  (SN_HWPERF_IS_NL3ROUTER(obj) ||
543  SN_HWPERF_IS_NL3ROUTER(p)) ? "LLP3" : "LLP4");
544  }
545  kfree(ptdata);
546  }
547 
548  return 0;
549 }
550 
551 static void *sn_topology_start(struct seq_file *s, loff_t * pos)
552 {
553  struct sn_hwperf_object_info *objs = s->private;
554 
555  if (*pos < sn_hwperf_obj_cnt)
556  return (void *)(objs + *pos);
557 
558  return NULL;
559 }
560 
561 static void *sn_topology_next(struct seq_file *s, void *v, loff_t * pos)
562 {
563  ++*pos;
564  return sn_topology_start(s, pos);
565 }
566 
567 static void sn_topology_stop(struct seq_file *m, void *v)
568 {
569  return;
570 }
571 
572 /*
573  * /proc/sgi_sn/sn_topology, read-only using seq_file
574  */
575 static const struct seq_operations sn_topology_seq_ops = {
576  .start = sn_topology_start,
577  .next = sn_topology_next,
578  .stop = sn_topology_stop,
579  .show = sn_topology_show
580 };
581 
585  void *p;
586  int *v0;
587  int ret;
588 };
589 
590 static void sn_hwperf_call_sal(void *info)
591 {
592  struct sn_hwperf_op_info *op_info = info;
593  int r;
594 
595  r = ia64_sn_hwperf_op(sn_hwperf_master_nasid, op_info->op,
596  op_info->a->arg, op_info->a->sz,
597  (u64) op_info->p, 0, 0, op_info->v0);
598  op_info->ret = r;
599 }
600 
601 static int sn_hwperf_op_cpu(struct sn_hwperf_op_info *op_info)
602 {
603  u32 cpu;
604  u32 use_ipi;
605  int r = 0;
606  cpumask_t save_allowed;
607 
608  cpu = (op_info->a->arg & SN_HWPERF_ARG_CPU_MASK) >> 32;
609  use_ipi = op_info->a->arg & SN_HWPERF_ARG_USE_IPI_MASK;
610  op_info->a->arg &= SN_HWPERF_ARG_OBJID_MASK;
611 
612  if (cpu != SN_HWPERF_ARG_ANY_CPU) {
613  if (cpu >= nr_cpu_ids || !cpu_online(cpu)) {
614  r = -EINVAL;
615  goto out;
616  }
617  }
618 
619  if (cpu == SN_HWPERF_ARG_ANY_CPU) {
620  /* don't care which cpu */
621  sn_hwperf_call_sal(op_info);
622  } else if (cpu == get_cpu()) {
623  /* already on correct cpu */
624  sn_hwperf_call_sal(op_info);
625  put_cpu();
626  } else {
627  put_cpu();
628  if (use_ipi) {
629  /* use an interprocessor interrupt to call SAL */
630  smp_call_function_single(cpu, sn_hwperf_call_sal,
631  op_info, 1);
632  }
633  else {
634  /* migrate the task before calling SAL */
635  save_allowed = current->cpus_allowed;
636  set_cpus_allowed_ptr(current, cpumask_of(cpu));
637  sn_hwperf_call_sal(op_info);
638  set_cpus_allowed_ptr(current, &save_allowed);
639  }
640  }
641  r = op_info->ret;
642 
643 out:
644  return r;
645 }
646 
647 /* map SAL hwperf error code to system error code */
648 static int sn_hwperf_map_err(int hwperf_err)
649 {
650  int e;
651 
652  switch(hwperf_err) {
653  case SN_HWPERF_OP_OK:
654  e = 0;
655  break;
656 
657  case SN_HWPERF_OP_NOMEM:
658  e = -ENOMEM;
659  break;
660 
662  e = -EPERM;
663  break;
664 
666  e = -EIO;
667  break;
668 
669  case SN_HWPERF_OP_BUSY:
670  e = -EBUSY;
671  break;
672 
674  e = -EAGAIN;
675  break;
676 
677  case SN_HWPERF_OP_INVAL:
678  default:
679  e = -EINVAL;
680  break;
681  }
682 
683  return e;
684 }
685 
686 /*
687  * ioctl for "sn_hwperf" misc device
688  */
689 static long sn_hwperf_ioctl(struct file *fp, u32 op, unsigned long arg)
690 {
691  struct sn_hwperf_ioctl_args a;
692  struct cpuinfo_ia64 *cdata;
693  struct sn_hwperf_object_info *objs;
694  struct sn_hwperf_object_info *cpuobj;
695  struct sn_hwperf_op_info op_info;
696  void *p = NULL;
697  int nobj;
698  char slice;
699  int node;
700  int r;
701  int v0;
702  int i;
703  int j;
704 
705  /* only user requests are allowed here */
706  if ((op & SN_HWPERF_OP_MASK) < 10) {
707  r = -EINVAL;
708  goto error;
709  }
710  r = copy_from_user(&a, (const void __user *)arg,
711  sizeof(struct sn_hwperf_ioctl_args));
712  if (r != 0) {
713  r = -EFAULT;
714  goto error;
715  }
716 
717  /*
718  * Allocate memory to hold a kernel copy of the user buffer. The
719  * buffer contents are either copied in or out (or both) of user
720  * space depending on the flags encoded in the requested operation.
721  */
722  if (a.ptr) {
723  p = vmalloc(a.sz);
724  if (!p) {
725  r = -ENOMEM;
726  goto error;
727  }
728  }
729 
730  if (op & SN_HWPERF_OP_MEM_COPYIN) {
731  r = copy_from_user(p, (const void __user *)a.ptr, a.sz);
732  if (r != 0) {
733  r = -EFAULT;
734  goto error;
735  }
736  }
737 
738  switch (op) {
740  if (a.sz == sizeof(u64)) {
741  /* special case to get size needed */
742  *(u64 *) p = (u64) num_online_cpus() *
743  sizeof(struct sn_hwperf_object_info);
744  } else
745  if (a.sz < num_online_cpus() * sizeof(struct sn_hwperf_object_info)) {
746  r = -ENOMEM;
747  goto error;
748  } else
749  if ((r = sn_hwperf_enum_objects(&nobj, &objs)) == 0) {
750  int cpuobj_index = 0;
751 
752  memset(p, 0, a.sz);
753  for (i = 0; i < nobj; i++) {
754  if (!SN_HWPERF_IS_NODE(objs + i))
755  continue;
756  node = sn_hwperf_obj_to_cnode(objs + i);
758  if (node != cpu_to_node(j))
759  continue;
760  cpuobj = (struct sn_hwperf_object_info *) p + cpuobj_index++;
761  slice = 'a' + cpuid_to_slice(j);
762  cdata = cpu_data(j);
763  cpuobj->id = j;
764  snprintf(cpuobj->name,
765  sizeof(cpuobj->name),
766  "CPU %luMHz %s",
767  cdata->proc_freq / 1000000,
768  cdata->vendor);
769  snprintf(cpuobj->location,
770  sizeof(cpuobj->location),
771  "%s%c", objs[i].location,
772  slice);
773  }
774  }
775 
776  vfree(objs);
777  }
778  break;
779 
781  if (a.sz != sizeof(u64) ||
782  (node = a.arg) < 0 || !cnode_possible(node)) {
783  r = -EINVAL;
784  goto error;
785  }
786  *(u64 *)p = (u64)cnodeid_to_nasid(node);
787  break;
788 
790  i = a.arg;
791  if (a.sz != sizeof(u64) || i < 0) {
792  r = -EINVAL;
793  goto error;
794  }
795  if ((r = sn_hwperf_enum_objects(&nobj, &objs)) == 0) {
796  if (i >= nobj) {
797  r = -EINVAL;
798  vfree(objs);
799  goto error;
800  }
801  if (objs[i].id != a.arg) {
802  for (i = 0; i < nobj; i++) {
803  if (objs[i].id == a.arg)
804  break;
805  }
806  }
807  if (i == nobj) {
808  r = -EINVAL;
809  vfree(objs);
810  goto error;
811  }
812 
813  if (!SN_HWPERF_IS_NODE(objs + i) &&
814  !SN_HWPERF_IS_IONODE(objs + i)) {
815  r = -ENOENT;
816  vfree(objs);
817  goto error;
818  }
819 
820  *(u64 *)p = (u64)sn_hwperf_obj_to_cnode(objs + i);
821  vfree(objs);
822  }
823  break;
824 
825  case SN_HWPERF_GET_MMRS:
826  case SN_HWPERF_SET_MMRS:
828  op_info.p = p;
829  op_info.a = &a;
830  op_info.v0 = &v0;
831  op_info.op = op;
832  r = sn_hwperf_op_cpu(&op_info);
833  if (r) {
834  r = sn_hwperf_map_err(r);
835  a.v0 = v0;
836  goto error;
837  }
838  break;
839 
840  default:
841  /* all other ops are a direct SAL call */
842  r = ia64_sn_hwperf_op(sn_hwperf_master_nasid, op,
843  a.arg, a.sz, (u64) p, 0, 0, &v0);
844  if (r) {
845  r = sn_hwperf_map_err(r);
846  goto error;
847  }
848  a.v0 = v0;
849  break;
850  }
851 
852  if (op & SN_HWPERF_OP_MEM_COPYOUT) {
853  r = copy_to_user((void __user *)a.ptr, p, a.sz);
854  if (r != 0) {
855  r = -EFAULT;
856  goto error;
857  }
858  }
859 
860 error:
861  vfree(p);
862 
863  return r;
864 }
865 
866 static const struct file_operations sn_hwperf_fops = {
867  .unlocked_ioctl = sn_hwperf_ioctl,
868  .llseek = noop_llseek,
869 };
870 
871 static struct miscdevice sn_hwperf_dev = {
873  "sn_hwperf",
874  &sn_hwperf_fops
875 };
876 
877 static int sn_hwperf_init(void)
878 {
879  u64 v;
880  int salr;
881  int e = 0;
882 
883  /* single threaded, once-only initialization */
884  mutex_lock(&sn_hwperf_init_mutex);
885 
886  if (sn_hwperf_salheap) {
887  mutex_unlock(&sn_hwperf_init_mutex);
888  return e;
889  }
890 
891  /*
892  * The PROM code needs a fixed reference node. For convenience the
893  * same node as the console I/O is used.
894  */
895  sn_hwperf_master_nasid = (nasid_t) ia64_sn_get_console_nasid();
896 
897  /*
898  * Request the needed size and install the PROM scratch area.
899  * The PROM keeps various tracking bits in this memory area.
900  */
901  salr = ia64_sn_hwperf_op(sn_hwperf_master_nasid,
903  (u64) sizeof(u64), (u64) &v, 0, 0, NULL);
904  if (salr != SN_HWPERF_OP_OK) {
905  e = -EINVAL;
906  goto out;
907  }
908 
909  if ((sn_hwperf_salheap = vmalloc(v)) == NULL) {
910  e = -ENOMEM;
911  goto out;
912  }
913  salr = ia64_sn_hwperf_op(sn_hwperf_master_nasid,
915  (u64) sn_hwperf_salheap, 0, 0, NULL);
916  if (salr != SN_HWPERF_OP_OK) {
917  e = -EINVAL;
918  goto out;
919  }
920 
921  salr = ia64_sn_hwperf_op(sn_hwperf_master_nasid,
923  sizeof(u64), (u64) &v, 0, 0, NULL);
924  if (salr != SN_HWPERF_OP_OK) {
925  e = -EINVAL;
926  goto out;
927  }
928  sn_hwperf_obj_cnt = (int)v;
929 
930 out:
931  if (e < 0 && sn_hwperf_salheap) {
932  vfree(sn_hwperf_salheap);
933  sn_hwperf_salheap = NULL;
934  sn_hwperf_obj_cnt = 0;
935  }
936  mutex_unlock(&sn_hwperf_init_mutex);
937  return e;
938 }
939 
940 int sn_topology_open(struct inode *inode, struct file *file)
941 {
942  int e;
943  struct seq_file *seq;
944  struct sn_hwperf_object_info *objbuf;
945  int nobj;
946 
947  if ((e = sn_hwperf_enum_objects(&nobj, &objbuf)) == 0) {
948  e = seq_open(file, &sn_topology_seq_ops);
949  seq = file->private_data;
950  seq->private = objbuf;
951  }
952 
953  return e;
954 }
955 
956 int sn_topology_release(struct inode *inode, struct file *file)
957 {
958  struct seq_file *seq = file->private_data;
959 
960  vfree(seq->private);
961  return seq_release(inode, file);
962 }
963 
965  cnodeid_t *near_mem_node, cnodeid_t *near_cpu_node)
966 {
967  int e;
968  int nobj;
969  struct sn_hwperf_object_info *objbuf;
970 
971  if ((e = sn_hwperf_enum_objects(&nobj, &objbuf)) == 0) {
972  e = sn_hwperf_get_nearest_node_objdata(objbuf, nobj,
973  node, near_mem_node, near_cpu_node);
974  vfree(objbuf);
975  }
976 
977  return e;
978 }
979 
980 static int __devinit sn_hwperf_misc_register_init(void)
981 {
982  int e;
983 
984  if (!ia64_platform_is("sn2"))
985  return 0;
986 
987  sn_hwperf_init();
988 
989  /*
990  * Register a dynamic misc device for hwperf ioctls. Platforms
991  * supporting hotplug will create /dev/sn_hwperf, else user
992  * can to look up the minor number in /proc/misc.
993  */
994  if ((e = misc_register(&sn_hwperf_dev)) != 0) {
995  printk(KERN_ERR "sn_hwperf_misc_register_init: failed to "
996  "register misc device for \"%s\"\n", sn_hwperf_dev.name);
997  }
998 
999  return e;
1000 }
1001 
1002 device_initcall(sn_hwperf_misc_register_init); /* after misc_init() */