123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978979980981982983984985986987988989990991992993994995996997998999 |
- /*
- * This file is subject to the terms and conditions of the GNU General Public
- * License. See the file "COPYING" in the main directory of this archive
- * for more details.
- *
- * Copyright (C) 2004-2006 Silicon Graphics, Inc. All rights reserved.
- *
- * SGI Altix topology and hardware performance monitoring API.
- * Mark Goodwin <markgw@sgi.com>.
- *
- * Creates /proc/sgi_sn/sn_topology (read-only) to export
- * info about Altix nodes, routers, CPUs and NumaLink
- * interconnection/topology.
- *
- * Also creates a dynamic misc device named "sn_hwperf"
- * that supports an ioctl interface to call down into SAL
- * to discover hw objects, topology and to read/write
- * memory mapped registers, e.g. for performance monitoring.
- * The "sn_hwperf" device is registered only after the procfs
- * file is first opened, i.e. only if/when it's needed.
- *
- * This API is used by SGI Performance Co-Pilot and other
- * tools, see http://oss.sgi.com/projects/pcp
- */
- #include <linux/fs.h>
- #include <linux/slab.h>
- #include <linux/vmalloc.h>
- #include <linux/seq_file.h>
- #include <linux/miscdevice.h>
- #include <linux/utsname.h>
- #include <linux/cpumask.h>
- #include <linux/smp_lock.h>
- #include <linux/nodemask.h>
- #include <asm/processor.h>
- #include <asm/topology.h>
- #include <asm/smp.h>
- #include <asm/semaphore.h>
- #include <asm/uaccess.h>
- #include <asm/sal.h>
- #include <asm/sn/io.h>
- #include <asm/sn/sn_sal.h>
- #include <asm/sn/module.h>
- #include <asm/sn/geo.h>
- #include <asm/sn/sn2/sn_hwperf.h>
- #include <asm/sn/addrs.h>
- static void *sn_hwperf_salheap = NULL;
- static int sn_hwperf_obj_cnt = 0;
- static nasid_t sn_hwperf_master_nasid = INVALID_NASID;
- static int sn_hwperf_init(void);
- static DECLARE_MUTEX(sn_hwperf_init_mutex);
- static int sn_hwperf_enum_objects(int *nobj, struct sn_hwperf_object_info **ret)
- {
- int e;
- u64 sz;
- struct sn_hwperf_object_info *objbuf = NULL;
- if ((e = sn_hwperf_init()) < 0) {
- printk(KERN_ERR "sn_hwperf_init failed: err %d\n", e);
- goto out;
- }
- sz = sn_hwperf_obj_cnt * sizeof(struct sn_hwperf_object_info);
- if ((objbuf = (struct sn_hwperf_object_info *) vmalloc(sz)) == NULL) {
- printk("sn_hwperf_enum_objects: vmalloc(%d) failed\n", (int)sz);
- e = -ENOMEM;
- goto out;
- }
- e = ia64_sn_hwperf_op(sn_hwperf_master_nasid, SN_HWPERF_ENUM_OBJECTS,
- 0, sz, (u64) objbuf, 0, 0, NULL);
- if (e != SN_HWPERF_OP_OK) {
- e = -EINVAL;
- vfree(objbuf);
- }
- out:
- *nobj = sn_hwperf_obj_cnt;
- *ret = objbuf;
- return e;
- }
- static int sn_hwperf_location_to_bpos(char *location,
- int *rack, int *bay, int *slot, int *slab)
- {
- char type;
- /* first scan for an old style geoid string */
- if (sscanf(location, "%03d%c%02d#%d",
- rack, &type, bay, slab) == 4)
- *slot = 0;
- else /* scan for a new bladed geoid string */
- if (sscanf(location, "%03d%c%02d^%02d#%d",
- rack, &type, bay, slot, slab) != 5)
- return -1;
- /* success */
- return 0;
- }
- static int sn_hwperf_geoid_to_cnode(char *location)
- {
- int cnode;
- geoid_t geoid;
- moduleid_t module_id;
- int rack, bay, slot, slab;
- int this_rack, this_bay, this_slot, this_slab;
- if (sn_hwperf_location_to_bpos(location, &rack, &bay, &slot, &slab))
- return -1;
- /*
- * FIXME: replace with cleaner for_each_XXX macro which addresses
- * both compute and IO nodes once ACPI3.0 is available.
- */
- for (cnode = 0; cnode < num_cnodes; cnode++) {
- geoid = cnodeid_get_geoid(cnode);
- module_id = geo_module(geoid);
- this_rack = MODULE_GET_RACK(module_id);
- this_bay = MODULE_GET_BPOS(module_id);
- this_slot = geo_slot(geoid);
- this_slab = geo_slab(geoid);
- if (rack == this_rack && bay == this_bay &&
- slot == this_slot && slab == this_slab) {
- break;
- }
- }
- return node_possible(cnode) ? cnode : -1;
- }
- static int sn_hwperf_obj_to_cnode(struct sn_hwperf_object_info * obj)
- {
- if (!SN_HWPERF_IS_NODE(obj) && !SN_HWPERF_IS_IONODE(obj))
- BUG();
- if (!obj->sn_hwp_this_part)
- return -1;
- return sn_hwperf_geoid_to_cnode(obj->location);
- }
- static int sn_hwperf_generic_ordinal(struct sn_hwperf_object_info *obj,
- struct sn_hwperf_object_info *objs)
- {
- int ordinal;
- struct sn_hwperf_object_info *p;
- for (ordinal=0, p=objs; p != obj; p++) {
- if (SN_HWPERF_FOREIGN(p))
- continue;
- if (SN_HWPERF_SAME_OBJTYPE(p, obj))
- ordinal++;
- }
- return ordinal;
- }
- static const char *slabname_node = "node"; /* SHub asic */
- static const char *slabname_ionode = "ionode"; /* TIO asic */
- static const char *slabname_router = "router"; /* NL3R or NL4R */
- static const char *slabname_other = "other"; /* unknown asic */
- static const char *sn_hwperf_get_slabname(struct sn_hwperf_object_info *obj,
- struct sn_hwperf_object_info *objs, int *ordinal)
- {
- int isnode;
- const char *slabname = slabname_other;
- if ((isnode = SN_HWPERF_IS_NODE(obj)) || SN_HWPERF_IS_IONODE(obj)) {
- slabname = isnode ? slabname_node : slabname_ionode;
- *ordinal = sn_hwperf_obj_to_cnode(obj);
- }
- else {
- *ordinal = sn_hwperf_generic_ordinal(obj, objs);
- if (SN_HWPERF_IS_ROUTER(obj))
- slabname = slabname_router;
- }
- return slabname;
- }
- static void print_pci_topology(struct seq_file *s)
- {
- char *p;
- size_t sz;
- int e;
- for (sz = PAGE_SIZE; sz < 16 * PAGE_SIZE; sz += PAGE_SIZE) {
- if (!(p = (char *)kmalloc(sz, GFP_KERNEL)))
- break;
- e = ia64_sn_ioif_get_pci_topology(__pa(p), sz);
- if (e == SALRET_OK)
- seq_puts(s, p);
- kfree(p);
- if (e == SALRET_OK || e == SALRET_NOT_IMPLEMENTED)
- break;
- }
- }
- static inline int sn_hwperf_has_cpus(cnodeid_t node)
- {
- return node_online(node) && nr_cpus_node(node);
- }
- static inline int sn_hwperf_has_mem(cnodeid_t node)
- {
- return node_online(node) && NODE_DATA(node)->node_present_pages;
- }
- static struct sn_hwperf_object_info *
- sn_hwperf_findobj_id(struct sn_hwperf_object_info *objbuf,
- int nobj, int id)
- {
- int i;
- struct sn_hwperf_object_info *p = objbuf;
- for (i=0; i < nobj; i++, p++) {
- if (p->id == id)
- return p;
- }
- return NULL;
- }
- static int sn_hwperf_get_nearest_node_objdata(struct sn_hwperf_object_info *objbuf,
- int nobj, cnodeid_t node, cnodeid_t *near_mem_node, cnodeid_t *near_cpu_node)
- {
- int e;
- struct sn_hwperf_object_info *nodeobj = NULL;
- struct sn_hwperf_object_info *op;
- struct sn_hwperf_object_info *dest;
- struct sn_hwperf_object_info *router;
- struct sn_hwperf_port_info ptdata[16];
- int sz, i, j;
- cnodeid_t c;
- int found_mem = 0;
- int found_cpu = 0;
- if (!node_possible(node))
- return -EINVAL;
- if (sn_hwperf_has_cpus(node)) {
- if (near_cpu_node)
- *near_cpu_node = node;
- found_cpu++;
- }
- if (sn_hwperf_has_mem(node)) {
- if (near_mem_node)
- *near_mem_node = node;
- found_mem++;
- }
- if (found_cpu && found_mem)
- return 0; /* trivially successful */
- /* find the argument node object */
- for (i=0, op=objbuf; i < nobj; i++, op++) {
- if (!SN_HWPERF_IS_NODE(op) && !SN_HWPERF_IS_IONODE(op))
- continue;
- if (node == sn_hwperf_obj_to_cnode(op)) {
- nodeobj = op;
- break;
- }
- }
- if (!nodeobj) {
- e = -ENOENT;
- goto err;
- }
- /* get it's interconnect topology */
- sz = op->ports * sizeof(struct sn_hwperf_port_info);
- if (sz > sizeof(ptdata))
- BUG();
- e = ia64_sn_hwperf_op(sn_hwperf_master_nasid,
- SN_HWPERF_ENUM_PORTS, nodeobj->id, sz,
- (u64)&ptdata, 0, 0, NULL);
- if (e != SN_HWPERF_OP_OK) {
- e = -EINVAL;
- goto err;
- }
- /* find nearest node with cpus and nearest memory */
- for (router=NULL, j=0; j < op->ports; j++) {
- dest = sn_hwperf_findobj_id(objbuf, nobj, ptdata[j].conn_id);
- if (!dest || SN_HWPERF_FOREIGN(dest) ||
- !SN_HWPERF_IS_NODE(dest) || SN_HWPERF_IS_IONODE(dest)) {
- continue;
- }
- c = sn_hwperf_obj_to_cnode(dest);
- if (!found_cpu && sn_hwperf_has_cpus(c)) {
- if (near_cpu_node)
- *near_cpu_node = c;
- found_cpu++;
- }
- if (!found_mem && sn_hwperf_has_mem(c)) {
- if (near_mem_node)
- *near_mem_node = c;
- found_mem++;
- }
- if (SN_HWPERF_IS_ROUTER(dest))
- router = dest;
- }
- if (router && (!found_cpu || !found_mem)) {
- /* search for a node connected to the same router */
- sz = router->ports * sizeof(struct sn_hwperf_port_info);
- if (sz > sizeof(ptdata))
- BUG();
- e = ia64_sn_hwperf_op(sn_hwperf_master_nasid,
- SN_HWPERF_ENUM_PORTS, router->id, sz,
- (u64)&ptdata, 0, 0, NULL);
- if (e != SN_HWPERF_OP_OK) {
- e = -EINVAL;
- goto err;
- }
- for (j=0; j < router->ports; j++) {
- dest = sn_hwperf_findobj_id(objbuf, nobj,
- ptdata[j].conn_id);
- if (!dest || dest->id == node ||
- SN_HWPERF_FOREIGN(dest) ||
- !SN_HWPERF_IS_NODE(dest) ||
- SN_HWPERF_IS_IONODE(dest)) {
- continue;
- }
- c = sn_hwperf_obj_to_cnode(dest);
- if (!found_cpu && sn_hwperf_has_cpus(c)) {
- if (near_cpu_node)
- *near_cpu_node = c;
- found_cpu++;
- }
- if (!found_mem && sn_hwperf_has_mem(c)) {
- if (near_mem_node)
- *near_mem_node = c;
- found_mem++;
- }
- if (found_cpu && found_mem)
- break;
- }
- }
- if (!found_cpu || !found_mem) {
- /* resort to _any_ node with CPUs and memory */
- for (i=0, op=objbuf; i < nobj; i++, op++) {
- if (SN_HWPERF_FOREIGN(op) ||
- SN_HWPERF_IS_IONODE(op) ||
- !SN_HWPERF_IS_NODE(op)) {
- continue;
- }
- c = sn_hwperf_obj_to_cnode(op);
- if (!found_cpu && sn_hwperf_has_cpus(c)) {
- if (near_cpu_node)
- *near_cpu_node = c;
- found_cpu++;
- }
- if (!found_mem && sn_hwperf_has_mem(c)) {
- if (near_mem_node)
- *near_mem_node = c;
- found_mem++;
- }
- if (found_cpu && found_mem)
- break;
- }
- }
- if (!found_cpu || !found_mem)
- e = -ENODATA;
- err:
- return e;
- }
- static int sn_topology_show(struct seq_file *s, void *d)
- {
- int sz;
- int pt;
- int e = 0;
- int i;
- int j;
- const char *slabname;
- int ordinal;
- cpumask_t cpumask;
- char slice;
- struct cpuinfo_ia64 *c;
- struct sn_hwperf_port_info *ptdata;
- struct sn_hwperf_object_info *p;
- struct sn_hwperf_object_info *obj = d; /* this object */
- struct sn_hwperf_object_info *objs = s->private; /* all objects */
- u8 shubtype;
- u8 system_size;
- u8 sharing_size;
- u8 partid;
- u8 coher;
- u8 nasid_shift;
- u8 region_size;
- u16 nasid_mask;
- int nasid_msb;
- if (obj == objs) {
- seq_printf(s, "# sn_topology version 2\n");
- seq_printf(s, "# objtype ordinal location partition"
- " [attribute value [, ...]]\n");
- if (ia64_sn_get_sn_info(0,
- &shubtype, &nasid_mask, &nasid_shift, &system_size,
- &sharing_size, &partid, &coher, ®ion_size))
- BUG();
- for (nasid_msb=63; nasid_msb > 0; nasid_msb--) {
- if (((u64)nasid_mask << nasid_shift) & (1ULL << nasid_msb))
- break;
- }
- seq_printf(s, "partition %u %s local "
- "shubtype %s, "
- "nasid_mask 0x%016lx, "
- "nasid_bits %d:%d, "
- "system_size %d, "
- "sharing_size %d, "
- "coherency_domain %d, "
- "region_size %d\n",
- partid, system_utsname.nodename,
- shubtype ? "shub2" : "shub1",
- (u64)nasid_mask << nasid_shift, nasid_msb, nasid_shift,
- system_size, sharing_size, coher, region_size);
- print_pci_topology(s);
- }
- if (SN_HWPERF_FOREIGN(obj)) {
- /* private in another partition: not interesting */
- return 0;
- }
- for (i = 0; i < SN_HWPERF_MAXSTRING && obj->name[i]; i++) {
- if (obj->name[i] == ' ')
- obj->name[i] = '_';
- }
- slabname = sn_hwperf_get_slabname(obj, objs, &ordinal);
- seq_printf(s, "%s %d %s %s asic %s", slabname, ordinal, obj->location,
- obj->sn_hwp_this_part ? "local" : "shared", obj->name);
- if (!SN_HWPERF_IS_NODE(obj) && !SN_HWPERF_IS_IONODE(obj))
- seq_putc(s, '\n');
- else {
- cnodeid_t near_mem = -1;
- cnodeid_t near_cpu = -1;
- seq_printf(s, ", nasid 0x%x", cnodeid_to_nasid(ordinal));
- if (sn_hwperf_get_nearest_node_objdata(objs, sn_hwperf_obj_cnt,
- ordinal, &near_mem, &near_cpu) == 0) {
- seq_printf(s, ", near_mem_nodeid %d, near_cpu_nodeid %d",
- near_mem, near_cpu);
- }
- if (!SN_HWPERF_IS_IONODE(obj)) {
- for_each_online_node(i) {
- seq_printf(s, i ? ":%d" : ", dist %d",
- node_distance(ordinal, i));
- }
- }
- seq_putc(s, '\n');
- /*
- * CPUs on this node, if any
- */
- cpumask = node_to_cpumask(ordinal);
- for_each_online_cpu(i) {
- if (cpu_isset(i, cpumask)) {
- slice = 'a' + cpuid_to_slice(i);
- c = cpu_data(i);
- seq_printf(s, "cpu %d %s%c local"
- " freq %luMHz, arch ia64",
- i, obj->location, slice,
- c->proc_freq / 1000000);
- for_each_online_cpu(j) {
- seq_printf(s, j ? ":%d" : ", dist %d",
- node_distance(
- cpu_to_node(i),
- cpu_to_node(j)));
- }
- seq_putc(s, '\n');
- }
- }
- }
- if (obj->ports) {
- /*
- * numalink ports
- */
- sz = obj->ports * sizeof(struct sn_hwperf_port_info);
- if ((ptdata = vmalloc(sz)) == NULL)
- return -ENOMEM;
- e = ia64_sn_hwperf_op(sn_hwperf_master_nasid,
- SN_HWPERF_ENUM_PORTS, obj->id, sz,
- (u64) ptdata, 0, 0, NULL);
- if (e != SN_HWPERF_OP_OK)
- return -EINVAL;
- for (ordinal=0, p=objs; p != obj; p++) {
- if (!SN_HWPERF_FOREIGN(p))
- ordinal += p->ports;
- }
- for (pt = 0; pt < obj->ports; pt++) {
- for (p = objs, i = 0; i < sn_hwperf_obj_cnt; i++, p++) {
- if (ptdata[pt].conn_id == p->id) {
- break;
- }
- }
- seq_printf(s, "numalink %d %s-%d",
- ordinal+pt, obj->location, ptdata[pt].port);
- if (i >= sn_hwperf_obj_cnt) {
- /* no connection */
- seq_puts(s, " local endpoint disconnected"
- ", protocol unknown\n");
- continue;
- }
- if (obj->sn_hwp_this_part && p->sn_hwp_this_part)
- /* both ends local to this partition */
- seq_puts(s, " local");
- else if (!obj->sn_hwp_this_part && !p->sn_hwp_this_part)
- /* both ends of the link in foreign partiton */
- seq_puts(s, " foreign");
- else
- /* link straddles a partition */
- seq_puts(s, " shared");
- /*
- * Unlikely, but strictly should query the LLP config
- * registers because an NL4R can be configured to run
- * NL3 protocol, even when not talking to an NL3 router.
- * Ditto for node-node.
- */
- seq_printf(s, " endpoint %s-%d, protocol %s\n",
- p->location, ptdata[pt].conn_port,
- (SN_HWPERF_IS_NL3ROUTER(obj) ||
- SN_HWPERF_IS_NL3ROUTER(p)) ? "LLP3" : "LLP4");
- }
- vfree(ptdata);
- }
- return 0;
- }
- static void *sn_topology_start(struct seq_file *s, loff_t * pos)
- {
- struct sn_hwperf_object_info *objs = s->private;
- if (*pos < sn_hwperf_obj_cnt)
- return (void *)(objs + *pos);
- return NULL;
- }
- static void *sn_topology_next(struct seq_file *s, void *v, loff_t * pos)
- {
- ++*pos;
- return sn_topology_start(s, pos);
- }
- static void sn_topology_stop(struct seq_file *m, void *v)
- {
- return;
- }
- /*
- * /proc/sgi_sn/sn_topology, read-only using seq_file
- */
- static struct seq_operations sn_topology_seq_ops = {
- .start = sn_topology_start,
- .next = sn_topology_next,
- .stop = sn_topology_stop,
- .show = sn_topology_show
- };
- struct sn_hwperf_op_info {
- u64 op;
- struct sn_hwperf_ioctl_args *a;
- void *p;
- int *v0;
- int ret;
- };
- static void sn_hwperf_call_sal(void *info)
- {
- struct sn_hwperf_op_info *op_info = info;
- int r;
- r = ia64_sn_hwperf_op(sn_hwperf_master_nasid, op_info->op,
- op_info->a->arg, op_info->a->sz,
- (u64) op_info->p, 0, 0, op_info->v0);
- op_info->ret = r;
- }
- static int sn_hwperf_op_cpu(struct sn_hwperf_op_info *op_info)
- {
- u32 cpu;
- u32 use_ipi;
- int r = 0;
- cpumask_t save_allowed;
-
- cpu = (op_info->a->arg & SN_HWPERF_ARG_CPU_MASK) >> 32;
- use_ipi = op_info->a->arg & SN_HWPERF_ARG_USE_IPI_MASK;
- op_info->a->arg &= SN_HWPERF_ARG_OBJID_MASK;
- if (cpu != SN_HWPERF_ARG_ANY_CPU) {
- if (cpu >= NR_CPUS || !cpu_online(cpu)) {
- r = -EINVAL;
- goto out;
- }
- }
- if (cpu == SN_HWPERF_ARG_ANY_CPU || cpu == get_cpu()) {
- /* don't care, or already on correct cpu */
- sn_hwperf_call_sal(op_info);
- }
- else {
- if (use_ipi) {
- /* use an interprocessor interrupt to call SAL */
- smp_call_function_single(cpu, sn_hwperf_call_sal,
- op_info, 1, 1);
- }
- else {
- /* migrate the task before calling SAL */
- save_allowed = current->cpus_allowed;
- set_cpus_allowed(current, cpumask_of_cpu(cpu));
- sn_hwperf_call_sal(op_info);
- set_cpus_allowed(current, save_allowed);
- }
- }
- r = op_info->ret;
- out:
- return r;
- }
- /* map SAL hwperf error code to system error code */
- static int sn_hwperf_map_err(int hwperf_err)
- {
- int e;
- switch(hwperf_err) {
- case SN_HWPERF_OP_OK:
- e = 0;
- break;
- case SN_HWPERF_OP_NOMEM:
- e = -ENOMEM;
- break;
- case SN_HWPERF_OP_NO_PERM:
- e = -EPERM;
- break;
- case SN_HWPERF_OP_IO_ERROR:
- e = -EIO;
- break;
- case SN_HWPERF_OP_BUSY:
- e = -EBUSY;
- break;
- case SN_HWPERF_OP_RECONFIGURE:
- e = -EAGAIN;
- break;
- case SN_HWPERF_OP_INVAL:
- default:
- e = -EINVAL;
- break;
- }
- return e;
- }
- /*
- * ioctl for "sn_hwperf" misc device
- */
- static int
- sn_hwperf_ioctl(struct inode *in, struct file *fp, u32 op, u64 arg)
- {
- struct sn_hwperf_ioctl_args a;
- struct cpuinfo_ia64 *cdata;
- struct sn_hwperf_object_info *objs;
- struct sn_hwperf_object_info *cpuobj;
- struct sn_hwperf_op_info op_info;
- void *p = NULL;
- int nobj;
- char slice;
- int node;
- int r;
- int v0;
- int i;
- int j;
- unlock_kernel();
- /* only user requests are allowed here */
- if ((op & SN_HWPERF_OP_MASK) < 10) {
- r = -EINVAL;
- goto error;
- }
- r = copy_from_user(&a, (const void __user *)arg,
- sizeof(struct sn_hwperf_ioctl_args));
- if (r != 0) {
- r = -EFAULT;
- goto error;
- }
- /*
- * Allocate memory to hold a kernel copy of the user buffer. The
- * buffer contents are either copied in or out (or both) of user
- * space depending on the flags encoded in the requested operation.
- */
- if (a.ptr) {
- p = vmalloc(a.sz);
- if (!p) {
- r = -ENOMEM;
- goto error;
- }
- }
- if (op & SN_HWPERF_OP_MEM_COPYIN) {
- r = copy_from_user(p, (const void __user *)a.ptr, a.sz);
- if (r != 0) {
- r = -EFAULT;
- goto error;
- }
- }
- switch (op) {
- case SN_HWPERF_GET_CPU_INFO:
- if (a.sz == sizeof(u64)) {
- /* special case to get size needed */
- *(u64 *) p = (u64) num_online_cpus() *
- sizeof(struct sn_hwperf_object_info);
- } else
- if (a.sz < num_online_cpus() * sizeof(struct sn_hwperf_object_info)) {
- r = -ENOMEM;
- goto error;
- } else
- if ((r = sn_hwperf_enum_objects(&nobj, &objs)) == 0) {
- memset(p, 0, a.sz);
- for (i = 0; i < nobj; i++) {
- int cpuobj_index = 0;
- if (!SN_HWPERF_IS_NODE(objs + i))
- continue;
- node = sn_hwperf_obj_to_cnode(objs + i);
- for_each_online_cpu(j) {
- if (node != cpu_to_node(j))
- continue;
- cpuobj = (struct sn_hwperf_object_info *) p + cpuobj_index++;
- slice = 'a' + cpuid_to_slice(j);
- cdata = cpu_data(j);
- cpuobj->id = j;
- snprintf(cpuobj->name,
- sizeof(cpuobj->name),
- "CPU %luMHz %s",
- cdata->proc_freq / 1000000,
- cdata->vendor);
- snprintf(cpuobj->location,
- sizeof(cpuobj->location),
- "%s%c", objs[i].location,
- slice);
- }
- }
- vfree(objs);
- }
- break;
- case SN_HWPERF_GET_NODE_NASID:
- if (a.sz != sizeof(u64) ||
- (node = a.arg) < 0 || !node_possible(node)) {
- r = -EINVAL;
- goto error;
- }
- *(u64 *)p = (u64)cnodeid_to_nasid(node);
- break;
- case SN_HWPERF_GET_OBJ_NODE:
- if (a.sz != sizeof(u64) || a.arg < 0) {
- r = -EINVAL;
- goto error;
- }
- if ((r = sn_hwperf_enum_objects(&nobj, &objs)) == 0) {
- if (a.arg >= nobj) {
- r = -EINVAL;
- vfree(objs);
- goto error;
- }
- if (objs[(i = a.arg)].id != a.arg) {
- for (i = 0; i < nobj; i++) {
- if (objs[i].id == a.arg)
- break;
- }
- }
- if (i == nobj) {
- r = -EINVAL;
- vfree(objs);
- goto error;
- }
- if (!SN_HWPERF_IS_NODE(objs + i) &&
- !SN_HWPERF_IS_IONODE(objs + i)) {
- r = -ENOENT;
- vfree(objs);
- goto error;
- }
- *(u64 *)p = (u64)sn_hwperf_obj_to_cnode(objs + i);
- vfree(objs);
- }
- break;
- case SN_HWPERF_GET_MMRS:
- case SN_HWPERF_SET_MMRS:
- case SN_HWPERF_OBJECT_DISTANCE:
- op_info.p = p;
- op_info.a = &a;
- op_info.v0 = &v0;
- op_info.op = op;
- r = sn_hwperf_op_cpu(&op_info);
- if (r) {
- r = sn_hwperf_map_err(r);
- a.v0 = v0;
- goto error;
- }
- break;
- default:
- /* all other ops are a direct SAL call */
- r = ia64_sn_hwperf_op(sn_hwperf_master_nasid, op,
- a.arg, a.sz, (u64) p, 0, 0, &v0);
- if (r) {
- r = sn_hwperf_map_err(r);
- goto error;
- }
- a.v0 = v0;
- break;
- }
- if (op & SN_HWPERF_OP_MEM_COPYOUT) {
- r = copy_to_user((void __user *)a.ptr, p, a.sz);
- if (r != 0) {
- r = -EFAULT;
- goto error;
- }
- }
- error:
- vfree(p);
- lock_kernel();
- return r;
- }
- static struct file_operations sn_hwperf_fops = {
- .ioctl = sn_hwperf_ioctl,
- };
- static struct miscdevice sn_hwperf_dev = {
- MISC_DYNAMIC_MINOR,
- "sn_hwperf",
- &sn_hwperf_fops
- };
- static int sn_hwperf_init(void)
- {
- u64 v;
- int salr;
- int e = 0;
- /* single threaded, once-only initialization */
- down(&sn_hwperf_init_mutex);
- if (sn_hwperf_salheap) {
- up(&sn_hwperf_init_mutex);
- return e;
- }
- /*
- * The PROM code needs a fixed reference node. For convenience the
- * same node as the console I/O is used.
- */
- sn_hwperf_master_nasid = (nasid_t) ia64_sn_get_console_nasid();
- /*
- * Request the needed size and install the PROM scratch area.
- * The PROM keeps various tracking bits in this memory area.
- */
- salr = ia64_sn_hwperf_op(sn_hwperf_master_nasid,
- (u64) SN_HWPERF_GET_HEAPSIZE, 0,
- (u64) sizeof(u64), (u64) &v, 0, 0, NULL);
- if (salr != SN_HWPERF_OP_OK) {
- e = -EINVAL;
- goto out;
- }
- if ((sn_hwperf_salheap = vmalloc(v)) == NULL) {
- e = -ENOMEM;
- goto out;
- }
- salr = ia64_sn_hwperf_op(sn_hwperf_master_nasid,
- SN_HWPERF_INSTALL_HEAP, 0, v,
- (u64) sn_hwperf_salheap, 0, 0, NULL);
- if (salr != SN_HWPERF_OP_OK) {
- e = -EINVAL;
- goto out;
- }
- salr = ia64_sn_hwperf_op(sn_hwperf_master_nasid,
- SN_HWPERF_OBJECT_COUNT, 0,
- sizeof(u64), (u64) &v, 0, 0, NULL);
- if (salr != SN_HWPERF_OP_OK) {
- e = -EINVAL;
- goto out;
- }
- sn_hwperf_obj_cnt = (int)v;
- out:
- if (e < 0 && sn_hwperf_salheap) {
- vfree(sn_hwperf_salheap);
- sn_hwperf_salheap = NULL;
- sn_hwperf_obj_cnt = 0;
- }
- up(&sn_hwperf_init_mutex);
- return e;
- }
- int sn_topology_open(struct inode *inode, struct file *file)
- {
- int e;
- struct seq_file *seq;
- struct sn_hwperf_object_info *objbuf;
- int nobj;
- if ((e = sn_hwperf_enum_objects(&nobj, &objbuf)) == 0) {
- e = seq_open(file, &sn_topology_seq_ops);
- seq = file->private_data;
- seq->private = objbuf;
- }
- return e;
- }
- int sn_topology_release(struct inode *inode, struct file *file)
- {
- struct seq_file *seq = file->private_data;
- vfree(seq->private);
- return seq_release(inode, file);
- }
- int sn_hwperf_get_nearest_node(cnodeid_t node,
- cnodeid_t *near_mem_node, cnodeid_t *near_cpu_node)
- {
- int e;
- int nobj;
- struct sn_hwperf_object_info *objbuf;
- if ((e = sn_hwperf_enum_objects(&nobj, &objbuf)) == 0) {
- e = sn_hwperf_get_nearest_node_objdata(objbuf, nobj,
- node, near_mem_node, near_cpu_node);
- vfree(objbuf);
- }
- return e;
- }
- static int __devinit sn_hwperf_misc_register_init(void)
- {
- int e;
- if (!ia64_platform_is("sn2"))
- return 0;
- sn_hwperf_init();
- /*
- * Register a dynamic misc device for hwperf ioctls. Platforms
- * supporting hotplug will create /dev/sn_hwperf, else user
- * can to look up the minor number in /proc/misc.
- */
- if ((e = misc_register(&sn_hwperf_dev)) != 0) {
- printk(KERN_ERR "sn_hwperf_misc_register_init: failed to "
- "register misc device for \"%s\"\n", sn_hwperf_dev.name);
- }
- return e;
- }
- device_initcall(sn_hwperf_misc_register_init); /* after misc_init() */
- EXPORT_SYMBOL(sn_hwperf_get_nearest_node);
|