/*
* 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 <linux/mutex.h>
#include <asm/processor.h>
#include <asm/topology.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 DEFINE_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,