📄 sn_hwperf.c
字号:
/* * 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 <linux/smp.h>#include <asm/processor.h>#include <asm/topology.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);#define cnode_possible(n) ((n) < num_cnodes)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); objbuf = vmalloc(sz); if (objbuf == 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 cnode_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 (SN_HWPERF_FOREIGN(obj)) 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 = 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 < MAX_NUMNODES && node_online(node) && nr_cpus_node(node);}static inline int sn_hwperf_has_mem(cnodeid_t node){ return node < MAX_NUMNODES && 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 (!cnode_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_IS_ROUTER(dest)) router = dest; 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 (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, 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 (ordinal < 0 || (!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 */ if (!SN_HWPERF_IS_IONODE(obj)) { 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 = kmalloc(sz, GFP_KERNEL)) == NULL) return -ENOMEM; e = ia64_sn_hwperf_op(sn_hwperf_master_nasid,
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -