lists.openwall.net   lists  /  announce  owl-users  owl-dev  john-users  john-dev  passwdqc-users  yescrypt  popa3d-users  /  oss-security  kernel-hardening  musl  sabotage  tlsify  passwords  /  crypt-dev  xvendor  /  Bugtraq  Full-Disclosure  linux-kernel  linux-netdev  linux-ext4  linux-hardening  linux-cve-announce  PHC 
Open Source and information security mailing list archives
 
Hash Suite: Windows password security audit tool. GUI, reports in PDF.
[<prev] [next>] [thread-next>] [day] [month] [year] [list]
Date:	Wed, 30 Sep 2009 16:05:31 -0500
From:	Russ Anderson <rja@....com>
To:	linux-kernel@...r.kernel.org
Cc:	mingo@...e.hu, hpa@...or.com, Cliff Wickman <cpw@....com>,
	Russ Anderson <rja@....com>
Subject: [PATCH 2/2] x86: UV hardware performance counter and topology access

Adds device named "/dev/uv_hwperf" that supports an ioctl interface
to call down into BIOS to read/write memory mapped performance
monitoring registers.
Adds /proc/sgi_uv/topology file, providing the node locations, etc.

Diffed against 2.6.31

Signed-off-by: Cliff Wickman <cpw@....com>
Acked-by: Russ Anderson <rja@....com>

---

This patch only effects UV systems.

 arch/x86/Kconfig            |    6 
 arch/x86/kernel/Makefile    |    1 
 arch/x86/kernel/uv_hwperf.c | 1095 ++++++++++++++++++++++++++++++++++++++++++++
 include/asm-x86/uv/geo.h    |  121 ++++
 include/asm-x86/uv/hwperf.h |  255 ++++++++++
 5 files changed, 1478 insertions(+)

Index: linux/arch/x86/kernel/Makefile
===================================================================
--- linux.orig/arch/x86/kernel/Makefile	2009-09-30 15:22:48.000000000 -0500
+++ linux/arch/x86/kernel/Makefile	2009-09-30 15:22:55.000000000 -0500
@@ -38,6 +38,7 @@ obj-$(CONFIG_X86_32)	+= probe_roms_32.o
 obj-$(CONFIG_X86_32)	+= sys_i386_32.o i386_ksyms_32.o
 obj-$(CONFIG_X86_64)	+= sys_x86_64.o x8664_ksyms_64.o
 obj-$(CONFIG_X86_64)	+= syscall_64.o vsyscall_64.o
+obj-$(CONFIG_UV_HWPERF)	+= uv_hwperf.o
 obj-y			+= bootflag.o e820.o
 obj-y			+= pci-dma.o quirks.o i8237.o topology.o kdebugfs.o
 obj-y			+= alternative.o i8253.o pci-nommu.o
Index: linux/arch/x86/kernel/uv_hwperf.c
===================================================================
--- /dev/null	1970-01-01 00:00:00.000000000 +0000
+++ linux/arch/x86/kernel/uv_hwperf.c	2009-09-30 15:22:55.000000000 -0500
@@ -0,0 +1,1095 @@
+/*
+ * 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) 2009 Silicon Graphics International Corp. All rights reserved.
+ *
+ * SGI UV hardware performance monitoring API.
+ * Cliff Wickman <cpw@....com>.
+ *
+ * Creates a dynamic device named "/dev/uv_hwperf" that supports an ioctl
+ * interface to call down into BIOS to read/write memory mapped registers,
+ * e.g. for performance monitoring.
+ * The "uv_hwperf" device is registered only after the sysfs
+ * file is first opened, i.e. only if/when it's needed.
+ *
+ * The /proc/sgi_uv/topology file provides the node locations, etc.
+ *
+ * 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 <linux/cpufreq.h>
+#include <linux/sysdev.h>
+#include <linux/proc_fs.h>
+#include <linux/sched.h>
+#include <asm/processor.h>
+#include <asm/topology.h>
+#include <asm/uaccess.h>
+#include <asm/types.h>
+#include <asm/uv/hwperf.h>
+#include <asm/uv/geo.h>
+#include <asm/uv/bios.h>
+#include <asm/uv/uv.h>
+#include <asm/uv/uv_hub.h>
+#include <asm/genapic.h>
+
+static void *uv_hwperf_biosheap;
+static int uv_hwperf_obj_cnt;
+static signed short uv_hwperf_master_nasid = INVALID_NASID;
+static int uv_hwperf_initial_bios_calls(void);
+static DEFINE_MUTEX(uv_hwperf_init_mutex);
+static int num_cnodes;
+
+struct kobject *uv_hwperf_kobj;
+
+#define cnode_possible(n)	((n) < num_cnodes)
+
+static int uv_hwperf_enum_objects(int *nobj, struct uv_hwperf_object_info **ret)
+{
+	s64 e;
+	u64 sz;
+	struct uv_hwperf_object_info *objbuf = NULL;
+
+	sz = uv_hwperf_obj_cnt * sizeof(struct uv_hwperf_object_info);
+	objbuf = vmalloc(sz);
+	if (objbuf == NULL) {
+		printk(KERN_ERR "uv_hwperf_enum_objects: vmalloc(%d) failed\n",
+			 (int)sz);
+		e = -ENOMEM;
+		goto out;
+	}
+
+	e = uv_bios_hwperf(uv_hwperf_master_nasid, UV_HWPERF_ENUM_OBJECTS, 0,
+		sz, (u64 *)objbuf, NULL);
+	if (e != BIOS_STATUS_SUCCESS) {
+		e = -EINVAL;
+		vfree(objbuf);
+	}
+
+out:
+	*nobj = uv_hwperf_obj_cnt;
+	*ret = objbuf;
+	return e;
+}
+
+static int uv_hwperf_location_to_bpos(char *location,
+	int *rack, int *slot, int *blade)
+{
+	char type;
+
+	/* first scan for a geoid string */
+	if (sscanf(location, "%03d%c%02db%d", rack, &type, slot, blade) != 4)
+		return -1;
+	return 0;
+}
+
+int
+cnodeid_to_nasid(int pnode)
+{
+	return UV_PNODE_TO_NASID(pnode);
+}
+
+union geoid_u
+cnodeid_get_geoid(int cnode)
+{
+	union geoid_u geoid;
+	/*
+	 * if per-node info from the pcfg (prom configuration table) has
+	 * already been located and squirreled away, return this nodes's
+	 * geoid.
+	 *
+	 * else
+	 *   read the pcfg from the bios
+	 *   for each cnode:
+	 *     convert cnode to nasid
+	 *     search the pcfg by nasid
+	 *     squirrel away the node's geoid
+	 *   return this cnode's geoid
+	 */
+
+	uv_bios_get_geoinfo(cnodeid_to_nasid(cnode), (u64)&geoid,
+			    sizeof(union geoid_u));
+	return geoid;
+}
+
+static int uv_hwperf_geoid_to_cnode(char *location)
+{
+	int cnode;
+	union geoid_u geoid;
+	int rack, slot, blade;
+	int this_rack, this_slot, this_blade;
+
+	if (uv_hwperf_location_to_bpos(location, &rack, &slot, &blade))
+		return -1;
+
+	for (cnode = 0; cnode < num_cnodes; cnode++) {
+		geoid = cnodeid_get_geoid(cnode);
+		this_rack = geo_rack(geoid);
+		this_slot = geo_slot(geoid);
+		this_blade = geo_blade(geoid);
+		if (rack == this_rack && slot == this_slot &&
+		    blade == this_blade) {
+			break;
+		}
+	}
+
+	return cnode_possible(cnode) ? cnode : -1;
+}
+
+static int uv_hwperf_obj_to_cnode(struct uv_hwperf_object_info *obj)
+{
+	if (!UV_HWPERF_IS_NODE(obj) && !UV_HWPERF_IS_IONODE(obj))
+		BUG();
+	if (UV_HWPERF_FOREIGN(obj))
+		return -1;
+	return uv_hwperf_geoid_to_cnode(obj->location);
+}
+
+static int uv_hwperf_generic_ordinal(struct uv_hwperf_object_info *obj,
+				struct uv_hwperf_object_info *objs)
+{
+	int ordinal;
+	struct uv_hwperf_object_info *p;
+
+	for (ordinal = 0, p = objs; p != obj; p++) {
+		if (UV_HWPERF_FOREIGN(p))
+			continue;
+		if (UV_HWPERF_SAME_OBJTYPE(p, obj))
+			ordinal++;
+	}
+
+	return ordinal;
+}
+
+static const char *slabname_node =	"node"; /* UVhub asic */
+static const char *slabname_ionode =	"ionode"; /* TIO asic */
+static const char *slabname_router =	"router"; /* NL5 */
+static const char *slabname_other =	"other"; /* unknown asic */
+
+static const char *uv_hwperf_get_slabname(struct uv_hwperf_object_info *obj,
+			struct uv_hwperf_object_info *objs, int *ordinal)
+{
+	int isnode;
+	const char *slabname = slabname_other;
+
+	isnode = UV_HWPERF_IS_NODE(obj);
+	if (isnode || UV_HWPERF_IS_IONODE(obj)) {
+		slabname = isnode ? slabname_node : slabname_ionode;
+		*ordinal = uv_hwperf_obj_to_cnode(obj);
+	} else {
+		*ordinal = uv_hwperf_generic_ordinal(obj, objs);
+		if (UV_HWPERF_IS_ROUTER(obj))
+			slabname = slabname_router;
+	}
+
+	return slabname;
+}
+
+static void print_pci_topology(struct seq_file *s)
+{
+	char *p;
+	size_t sz;
+	s64 e;
+
+	for (sz = PAGE_SIZE; sz < 16 * PAGE_SIZE; sz += PAGE_SIZE) {
+		p = kmalloc(sz, GFP_KERNEL);
+		if (!p)
+			break;
+		e = uv_bios_get_pci_topology((u64)p, sz);
+		if (e == BIOS_STATUS_SUCCESS)
+			seq_puts(s, p);
+		kfree(p);
+		if (e == BIOS_STATUS_SUCCESS || e == BIOS_STATUS_UNIMPLEMENTED)
+			break;
+	}
+}
+
+static inline int uv_hwperf_has_cpus(short node)
+{
+	return node < MAX_NUMNODES && node_online(node) && nr_cpus_node(node);
+}
+
+static inline int uv_hwperf_has_mem(short node)
+{
+	return node < MAX_NUMNODES && node_online(node) &&
+		NODE_DATA(node)->node_present_pages;
+}
+
+static struct uv_hwperf_object_info *
+uv_hwperf_findobj_id(struct uv_hwperf_object_info *objbuf,
+	int nobj, int id)
+{
+	int i;
+	struct uv_hwperf_object_info *p = objbuf;
+
+	for (i = 0; i < nobj; i++, p++) {
+		if (p->id == id)
+			return p;
+	}
+
+	return NULL;
+
+}
+
+static int uv_hwperf_get_nearest_node_objdata
+	(struct uv_hwperf_object_info *objbuf, int nobj, short node,
+	 short *near_mem_node, short *near_cpu_node)
+{
+	s64 e;
+	struct uv_hwperf_object_info *nodeobj = NULL;
+	struct uv_hwperf_object_info *op;
+	struct uv_hwperf_object_info *dest;
+	struct uv_hwperf_object_info *router;
+	struct uv_hwperf_port_info ptdata[16];
+	int sz, i, j;
+	short c;
+	int found_mem = 0;
+	int found_cpu = 0;
+
+	if (!cnode_possible(node))
+		return -EINVAL;
+
+	if (uv_hwperf_has_cpus(node)) {
+		if (near_cpu_node)
+			*near_cpu_node = node;
+		found_cpu++;
+	}
+
+	if (uv_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 (!UV_HWPERF_IS_NODE(op) && !UV_HWPERF_IS_IONODE(op))
+			continue;
+		if (node == uv_hwperf_obj_to_cnode(op)) {
+			nodeobj = op;
+			break;
+		}
+	}
+	if (!nodeobj) {
+		e = -ENOENT;
+		goto err;
+	}
+
+	/* get its interconnect topology */
+	sz = op->ports * sizeof(struct uv_hwperf_port_info);
+	if (sz > sizeof(ptdata))
+		BUG();
+	e = uv_bios_hwperf(uv_hwperf_master_nasid, UV_HWPERF_ENUM_PORTS,
+		nodeobj->id, sz, (u64 *)&ptdata, NULL);
+	if (e != BIOS_STATUS_SUCCESS) {
+		e = -EINVAL;
+		goto err;
+	}
+
+	/* find nearest node with cpus and nearest memory */
+	for (router = NULL, j = 0; j < op->ports; j++) {
+		dest = uv_hwperf_findobj_id(objbuf, nobj, ptdata[j].conn_id);
+		if (dest && UV_HWPERF_IS_ROUTER(dest))
+			router = dest;
+		if (!dest || UV_HWPERF_FOREIGN(dest) ||
+		    !UV_HWPERF_IS_NODE(dest) || UV_HWPERF_IS_IONODE(dest)) {
+			continue;
+		}
+		c = uv_hwperf_obj_to_cnode(dest);
+		if (!found_cpu && uv_hwperf_has_cpus(c)) {
+			if (near_cpu_node)
+				*near_cpu_node = c;
+			found_cpu++;
+		}
+		if (!found_mem && uv_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 uv_hwperf_port_info);
+		if (sz > sizeof(ptdata))
+			BUG();
+		e = uv_bios_hwperf(uv_hwperf_master_nasid, UV_HWPERF_ENUM_PORTS,
+				router->id, sz, (u64 *)&ptdata, NULL);
+		if (e != BIOS_STATUS_SUCCESS) {
+			e = -EINVAL;
+			goto err;
+		}
+		for (j = 0; j < router->ports; j++) {
+			dest = uv_hwperf_findobj_id(objbuf, nobj,
+				ptdata[j].conn_id);
+			if (!dest || dest->id == node ||
+			    UV_HWPERF_FOREIGN(dest) ||
+			    !UV_HWPERF_IS_NODE(dest) ||
+			    UV_HWPERF_IS_IONODE(dest)) {
+				continue;
+			}
+			c = uv_hwperf_obj_to_cnode(dest);
+			if (!found_cpu && uv_hwperf_has_cpus(c)) {
+				if (near_cpu_node)
+					*near_cpu_node = c;
+				found_cpu++;
+			}
+			if (!found_mem && uv_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 (UV_HWPERF_FOREIGN(op) ||
+			    UV_HWPERF_IS_IONODE(op) ||
+			    !UV_HWPERF_IS_NODE(op)) {
+				continue;
+			}
+			c = uv_hwperf_obj_to_cnode(op);
+			if (!found_cpu && uv_hwperf_has_cpus(c)) {
+				if (near_cpu_node)
+					*near_cpu_node = c;
+				found_cpu++;
+			}
+			if (!found_mem && uv_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 uv_topology_show(struct seq_file *s, void *d)
+{
+	int sz;
+	int pt;
+	int e = 0;
+	int i;
+	int j = 0;
+	const char *slabname;
+	int ordinal;
+	const struct cpumask *cpumask;
+	struct cpuinfo_x86 *c;
+	struct uv_hwperf_port_info *ptdata;
+	struct uv_hwperf_object_info *p;
+	struct uv_hwperf_object_info *obj = d;	/* this object */
+	struct uv_hwperf_object_info *objs = s->private; /* all objects */
+	int uv_type = 0;
+	long partid = 0;
+	long coher = 0;
+	long region_size = 0;
+	long system_serial_number = 0;
+	unsigned int freq;
+
+	if (obj == objs) {
+		seq_printf(s, "# uv_topology version 1\n");
+		seq_printf(s, "# objtype ordinal location partition"
+			" [attribute value [, ...]]\n");
+
+		if (uv_bios_get_sn_info(0, &uv_type, &partid, &coher,
+			&region_size, &system_serial_number))
+			BUG();
+		seq_printf(s, "partition %ld %s local "
+			"uvtype %s, "
+			"coherency_domain %ld, "
+			"region_size %ld, "
+			"system_serial_number %ld\n",
+			partid, utsname()->nodename,
+			uv_type ? "unknown" : "UVhub",
+			coher, region_size, system_serial_number);
+
+		print_pci_topology(s);
+	}
+
+	if (UV_HWPERF_FOREIGN(obj)) {
+		/* private in another partition: not interesting */
+		return 0;
+	}
+
+	for (i = 0; i < UV_HWPERF_MAXSTRING && obj->name[i]; i++) {
+		if (obj->name[i] == ' ')
+			obj->name[i] = '_';
+	}
+
+	slabname = uv_hwperf_get_slabname(obj, objs, &ordinal);
+	seq_printf(s, "%s %d %s %s asic %s", slabname, ordinal, obj->location,
+		obj->uv_hwp_this_part ? "local" : "shared", obj->name);
+
+	if (ordinal < 0 ||
+	    (!UV_HWPERF_IS_NODE(obj) && !UV_HWPERF_IS_IONODE(obj)))
+		seq_putc(s, '\n');
+	else {
+		short near_mem = -1;
+		short near_cpu = -1;
+
+		seq_printf(s, ", nasid 0x%x", cnodeid_to_nasid(ordinal));
+
+		if (uv_hwperf_get_nearest_node_objdata(objs, uv_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 (!UV_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 (!UV_HWPERF_IS_IONODE(obj)) {
+			cpumask = cpumask_of_node(ordinal);
+			for_each_online_cpu(i) {
+				if (cpu_isset(i, *cpumask)) {
+					c = (struct cpuinfo_x86 *)&cpu_data(i);
+					freq = cpufreq_quick_get(j);
+					if (!freq)
+						freq = cpu_khz;
+					seq_printf(s, "cpu %d %s%d local"
+						" freq %uMHz, arch UV",
+						i, obj->location,
+						c->cpu_core_id,
+						freq / 1000);
+					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 uv_hwperf_port_info);
+		ptdata = kmalloc(sz, GFP_KERNEL);
+		if (ptdata == NULL)
+			return -ENOMEM;
+		e = uv_bios_hwperf(uv_hwperf_master_nasid, UV_HWPERF_ENUM_PORTS,
+				obj->id, sz, (u64 *)ptdata, NULL);
+		if (e != BIOS_STATUS_SUCCESS)
+			return -EINVAL;
+		for (ordinal = 0, p = objs; p != obj; p++)
+			if (!UV_HWPERF_FOREIGN(p))
+				ordinal += p->ports;
+		for (pt = 0; pt < obj->ports; pt++) {
+			for (p = objs, i = 0; i < uv_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 >= uv_hwperf_obj_cnt) {
+				/* no connection */
+				seq_puts(s, " local endpoint disconnected"
+					    ", protocol unknown\n");
+				continue;
+			}
+
+			if (obj->uv_hwp_this_part && p->uv_hwp_this_part)
+				/* both ends local to this partition */
+				seq_puts(s, " local");
+			else if (UV_HWPERF_FOREIGN(p))
+				/* both ends of the link in foreign partiton */
+				seq_puts(s, " foreign");
+			else
+				/* link straddles a partition */
+				seq_puts(s, " shared");
+
+			seq_printf(s, " endpoint %s-%d, protocol %s\n",
+				p->location, ptdata[pt].conn_port,
+				(UV_HWPERF_IS_NL5ROUTER(p)) ?  "LLP5" : "LL??");
+		}
+		kfree(ptdata);
+	}
+
+	return 0;
+}
+
+static void *uv_topology_start(struct seq_file *s, loff_t * pos)
+{
+	struct uv_hwperf_object_info *objs = s->private;
+
+	if (*pos < uv_hwperf_obj_cnt)
+		return (void *)(objs + *pos);
+
+	return NULL;
+}
+
+static void *uv_topology_next(struct seq_file *s, void *v, loff_t * pos)
+{
+	++*pos;
+	return uv_topology_start(s, pos);
+}
+
+static void uv_topology_stop(struct seq_file *m, void *v)
+{
+	return;
+}
+
+/*
+ * /proc/sgi_uv/topology, read-only using seq_file
+ */
+static const struct seq_operations uv_topology_seq_ops = {
+	.start = uv_topology_start,
+	.next = uv_topology_next,
+	.stop = uv_topology_stop,
+	.show = uv_topology_show
+};
+
+struct uv_hwperf_op_info {
+	u64 op;
+	struct uv_hwperf_ioctl_args *a;
+	void *p;
+	int *v0;
+	int ret;
+};
+
+static void uv_hwperf_call_bios(void *info)
+{
+	struct uv_hwperf_op_info *op_info = info;
+	s64 r;
+
+	r = uv_bios_hwperf(uv_hwperf_master_nasid, op_info->op, op_info->a->arg,
+			 op_info->a->sz, (u64 *)op_info->p, NULL);
+	*op_info->v0 = r;
+	op_info->ret = r;
+}
+
+static int uv_hwperf_op_cpu(struct uv_hwperf_op_info *op_info)
+{
+	u32 cpu;
+	u32 use_ipi;
+	int r = 0;
+	cpumask_t save_allowed;
+
+	cpu = (op_info->a->arg & UV_HWPERF_ARG_CPU_MASK) >> 32;
+	use_ipi = op_info->a->arg & UV_HWPERF_ARG_USE_IPI_MASK;
+	op_info->a->arg &= UV_HWPERF_ARG_OBJID_MASK;
+
+	if (cpu != UV_HWPERF_ARG_ANY_CPU) {
+		if (!cpu_possible(cpu) || !cpu_online(cpu)) {
+			r = -EINVAL;
+			goto out;
+		}
+	}
+
+	if (cpu == UV_HWPERF_ARG_ANY_CPU || cpu == get_cpu()) {
+		/* don't care, or already on correct cpu */
+		uv_hwperf_call_bios(op_info);
+	} else {
+		if (use_ipi)
+			/* use an interprocessor interrupt to call BIOS */
+			{
+			smp_call_function_single(cpu, uv_hwperf_call_bios,
+				op_info, 1);
+			}
+		else {
+			/* migrate the task before calling BIOS */
+			save_allowed = current->cpus_allowed;
+			set_cpus_allowed_ptr(current, cpumask_of(cpu));
+			uv_hwperf_call_bios(op_info);
+			set_cpus_allowed_ptr(current, &save_allowed);
+		}
+	}
+	r = op_info->ret;
+
+out:
+	return r;
+}
+
+/* map BIOS hwperf error code to system error code */
+static int uv_hwperf_map_err(int hwperf_err)
+{
+	int e;
+
+	switch (hwperf_err) {
+	case BIOS_STATUS_SUCCESS:
+		e = 0;
+		break;
+
+	case UV_HWPERF_OP_NOMEM:
+		e = -ENOMEM;
+		break;
+
+	case UV_HWPERF_OP_NO_PERM:
+		e = -EPERM;
+		break;
+
+	case UV_HWPERF_OP_IO_ERROR:
+		e = -EIO;
+		break;
+
+	case UV_HWPERF_OP_BUSY:
+		e = -EBUSY;
+		break;
+
+	case UV_HWPERF_OP_RECONFIGURE:
+		e = -EAGAIN;
+		break;
+
+	case UV_HWPERF_OP_INVAL:
+	default:
+		e = -EINVAL;
+		break;
+	}
+
+	return e;
+}
+
+/*
+ * ioctl for "uv_hwperf" misc device
+ * (called via the uv_hwperf_dev and uv_hwperf_fops structures)
+ */
+static int
+uv_hwperf_ioctl(struct inode *in, struct file *fp, unsigned int op,
+		unsigned long arg)
+{
+	struct uv_hwperf_ioctl_args a;
+	struct uv_hwperf_object_info *objs;
+	struct uv_hwperf_object_info *cpuobj;
+	struct uv_hwperf_op_info op_info;
+	struct cpuinfo_x86 *cdata;
+	void *p = NULL;
+	int nobj;
+	int node = 0;
+	int r;
+	int v0;
+	int i;
+	int j;
+	unsigned int freq;
+
+	unlock_kernel();
+
+	/* only user requests are allowed here */
+	if ((op & UV_HWPERF_OP_MASK) <
+		(UV_HWPERF_OBJECT_COUNT & UV_HWPERF_OP_MASK)) {
+		r = -EINVAL;
+		goto error;
+	}
+	r = copy_from_user(&a, (const void __user *)arg,
+		sizeof(struct uv_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 & UV_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 UV_HWPERF_GET_CPU_INFO:
+		if (a.sz == sizeof(u64)) {
+			/* special case to get size needed */
+			*(u64 *) p =
+				(u64) num_online_cpus() *
+				sizeof(struct uv_hwperf_object_info);
+		} else if (a.sz < num_online_cpus() *
+				sizeof(struct uv_hwperf_object_info)) {
+			r = -ENOMEM;
+			goto error;
+		} else {
+			r = uv_hwperf_enum_objects(&nobj, &objs);
+			if (r == 0) {
+				int cpuobj_index = 0;
+
+				memset(p, 0, a.sz);
+				for (i = 0; i < nobj; i++) {
+					if (!UV_HWPERF_IS_NODE(objs + i))
+						continue;
+					node = uv_hwperf_obj_to_cnode(objs + i);
+					for_each_online_cpu(j) {
+						if (node != cpu_to_node(j))
+							continue;
+						cpuobj =
+						(struct uv_hwperf_object_info *)
+							 p + cpuobj_index++;
+						freq = cpufreq_quick_get(j);
+						if (!freq)
+							freq = cpu_khz;
+						cdata = (struct cpuinfo_x86 *)
+							 &cpu_data(j);
+						cpuobj->id = j;
+						snprintf(cpuobj->name,
+							 sizeof(cpuobj->name),
+							 "CPU %uMHz %s",
+							 freq / 1000,
+							 (char *)
+							 &cdata->x86_vendor_id);
+						snprintf(cpuobj->location,
+							 sizeof
+							 (cpuobj->location),
+							 "%s%d",
+							 objs[i].location,
+							 cdata->cpu_core_id);
+					}
+				}
+			}
+
+			vfree(objs);
+		}
+		break;
+
+	case UV_HWPERF_GET_NODE_NASID:
+		node = a.arg;
+		if (a.sz != sizeof(int) || node < 0 || !cnode_possible(node)) {
+			r = -EINVAL;
+			goto error;
+		}
+		*(int *)p = cnodeid_to_nasid(node);
+		break;
+
+	case UV_HWPERF_GET_OBJ_NODE:
+		if (a.sz != sizeof(u64) || a.arg < 0) {
+			r = -EINVAL;
+			goto error;
+		}
+		r = uv_hwperf_enum_objects(&nobj, &objs);
+		if (r == 0) {
+			if (a.arg >= nobj) {
+				r = -EINVAL;
+				vfree(objs);
+				goto error;
+			}
+			i = a.arg;
+			if (objs[i].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 (!UV_HWPERF_IS_NODE(objs + i) &&
+			    !UV_HWPERF_IS_IONODE(objs + i)) {
+				r = -ENOENT;
+				vfree(objs);
+				goto error;
+			}
+
+			*(u64 *)p = (u64)
+				uv_hwperf_obj_to_cnode(objs + i);
+			vfree(objs);
+		}
+		break;
+
+	case UV_HWPERF_GET_MMRS:
+	case UV_HWPERF_SET_MMRS:
+	case UV_HWPERF_OBJECT_DISTANCE:
+		op_info.p = p;
+		op_info.a = &a;
+		op_info.v0 = &v0;
+		op_info.op = op;
+		r = uv_hwperf_op_cpu(&op_info);
+		if (r) {
+			r = uv_hwperf_map_err(r);
+			a.v0 = v0;
+			goto error;
+		}
+		break;
+
+	default:
+		/* all other ops are a direct BIOS call */
+		r = uv_bios_hwperf(uv_hwperf_master_nasid, op, a.arg, a.sz,
+				   (u64 *)p, NULL);
+		if (r) {
+			r = uv_hwperf_map_err(r);
+			goto error;
+		}
+		a.v0 = r;
+		break;
+	}
+
+	if (op & UV_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 const struct file_operations uv_hwperf_fops = {
+	.ioctl = uv_hwperf_ioctl,
+};
+
+static struct miscdevice uv_hwperf_dev = {
+	MISC_DYNAMIC_MINOR,
+	UV_HWPERF_DEVICE_NAME,
+	&uv_hwperf_fops
+};
+
+static int uv_hwperf_initial_bios_calls(void)
+{
+	u64 v;
+	s64 biosr;
+	int e = 0;
+
+	/* single threaded, once-only initialization */
+	mutex_lock(&uv_hwperf_init_mutex);
+
+	if (uv_hwperf_biosheap) {
+		mutex_unlock(&uv_hwperf_init_mutex);
+		return e;
+	}
+
+	num_cnodes = uv_num_possible_blades();
+
+	/*
+	 * The PROM code needs a fixed reference node. For convenience the
+	 * same node as the console I/O is used.
+	 */
+	e = uv_bios_hwperf(0, UV_HWPERF_MASTER_NASID, 0,
+			   (u64)sizeof(uv_hwperf_master_nasid),
+			   (u64 *)&uv_hwperf_master_nasid, NULL);
+	if (e) {
+		e = -EINVAL;
+		goto out;
+	}
+
+	/*
+	 * Request the needed size and install the PROM scratch area.
+	 * The PROM keeps various tracking bits in this memory area.
+	 */
+	biosr = uv_bios_hwperf(uv_hwperf_master_nasid, UV_HWPERF_GET_HEAPSIZE,
+				0, (u64)sizeof(u64), (u64 *)&v, NULL);
+	if (biosr != BIOS_STATUS_SUCCESS) {
+		e = -EINVAL;
+		goto out;
+	}
+
+	uv_hwperf_biosheap = vmalloc(v);
+	if (uv_hwperf_biosheap == NULL) {
+		e = -ENOMEM;
+		goto out;
+	}
+	biosr = uv_bios_hwperf(uv_hwperf_master_nasid, UV_HWPERF_INSTALL_HEAP,
+				0, v, (u64 *)uv_hwperf_biosheap, NULL);
+	if (biosr != BIOS_STATUS_SUCCESS) {
+		e = -EINVAL;
+		goto out;
+	}
+
+	biosr = uv_bios_hwperf(uv_hwperf_master_nasid, UV_HWPERF_OBJECT_COUNT,
+				0, sizeof(u64), (u64 *)&v, NULL);
+	if (biosr != BIOS_STATUS_SUCCESS) {
+		e = -EINVAL;
+		goto out;
+	}
+	uv_hwperf_obj_cnt = (int)v;
+
+out:
+	if (e < 0 && uv_hwperf_biosheap) {
+		vfree(uv_hwperf_biosheap);
+		uv_hwperf_biosheap = NULL;
+		uv_hwperf_obj_cnt = 0;
+	}
+	mutex_unlock(&uv_hwperf_init_mutex);
+	return e;
+}
+
+#ifdef CONFIG_PROC_FS
+static const struct file_operations proc_uv_topo_fops = {
+	.open		= uv_topology_open,
+	.read		= seq_read,
+	.llseek		= seq_lseek,
+	.release	= uv_topology_release,
+};
+
+/*
+ * set up /proc/sgi_uv/topology
+ */
+void uv_hwperf_register_procfs(void)
+{
+	struct proc_dir_entry *sgi_topology;
+
+	sgi_topology = create_proc_entry("sgi_uv/topology", 0444, NULL);
+	if (!sgi_topology) {
+		printk(KERN_ERR "unable to create /proc/sgi_uv/topology\n");
+		return;
+	}
+	sgi_topology->proc_fops = &proc_uv_topo_fops;
+}
+
+/*
+ * take down the /proc/sgi_uv/topology file
+ */
+void uv_hwperf_deregister_procfs(void)
+{
+	remove_proc_entry("sgi_uv/topology", NULL);
+}
+
+/*
+ * called because of uv_hwperf_register_procfs
+ */
+int uv_topology_open(struct inode *inode, struct file *file)
+{
+	int e;
+	struct seq_file *seq;
+	struct uv_hwperf_object_info *objbuf;
+	int nobj;
+
+	e = uv_hwperf_enum_objects(&nobj, &objbuf);
+	if (e == 0) {
+		e = seq_open(file, &uv_topology_seq_ops);
+		seq = file->private_data;
+		seq->private = objbuf;
+	}
+
+	return e;
+}
+
+/*
+ * called because of uv_hwperf_register_procfs
+ */
+int uv_topology_release(struct inode *inode, struct file *file)
+{
+	struct seq_file *seq = file->private_data;
+
+	vfree(seq->private);
+	return seq_release(inode, file);
+}
+#endif
+
+/*
+ * Register a dynamic misc device for hwperf ioctls. Platforms
+ * supporting hotplug will create /dev/uv_hwperf, else user
+ * can to look up the minor number in /proc/misc.
+ */
+static int uv_hwperf_device_register(void)
+{
+	int e;
+
+	e = misc_register(&uv_hwperf_dev);
+	if (e != 0) {
+		printk(KERN_ERR "uv_hwperf_device_register: failed to "
+		"register misc device for \"%s\"\n", uv_hwperf_dev.name);
+	}
+	return e;
+}
+
+/*
+ * Register a dynamic misc device for hwperf ioctls. Platforms
+ * supporting hotplug will create /dev/uv_hwperf, else user
+ * can to look up the minor number in /proc/misc.
+ */
+static void uv_hwperf_device_deregister(void)
+{
+	int e;
+
+	e = misc_deregister(&uv_hwperf_dev);
+	if (e != 0) {
+		printk(KERN_ERR "uv_hwperf_device_deregister: failed to "
+		"deregister misc device \"%s\"\n", uv_hwperf_dev.name);
+	}
+
+	return;
+}
+
+/*
+ * entry to this module (at insmod time)
+ */
+static int uv_hwperf_entry(void)
+{
+	if (!is_uv_system())
+		return 0;
+
+	uv_hwperf_initial_bios_calls();
+	uv_hwperf_device_register();
+#ifdef CONFIG_PROC_FS
+	uv_hwperf_register_procfs();
+#endif
+	return 0;
+}
+
+/*
+ * exit from this module (at rmmod time)
+ */
+static void uv_hwperf_exit(void)
+{
+	if (!is_uv_system())
+		return;
+
+	uv_hwperf_device_deregister();
+	uv_hwperf_deregister_procfs();
+}
+
+module_init(uv_hwperf_entry);
+module_exit(uv_hwperf_exit);
+
+MODULE_AUTHOR("Silicon Graphics, Inc.");
+MODULE_DESCRIPTION("Driver for SGI UV hub counter access and topology");
+/* ?? should this be GPL? */
+MODULE_LICENSE("GPL");
Index: linux/include/asm-x86/uv/hwperf.h
===================================================================
--- /dev/null	1970-01-01 00:00:00.000000000 +0000
+++ linux/include/asm-x86/uv/hwperf.h	2009-09-30 15:22:55.000000000 -0500
@@ -0,0 +1,255 @@
+/*
+ * 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) 2009 Silicon Graphics International Corp. All rights reserved.
+ *
+ * Data types used by the UV_HWPERF_OP BIOS call for monitoring
+ * SGI UV node and router hardware
+ *
+ * Copyright (C) Cliff Wickman <cpw@....com>
+ */
+
+#ifndef UV_HWPERF_H
+#define UV_HWPERF_H
+
+#define UV_HWPERF_DEVICE_NAME "hwperf"
+
+/*
+ * object structure. UV_HWPERF_ENUM_OBJECTS and UV_HWPERF_GET_CPU_INFO
+ * return an array of these. Do not change this without also
+ * changing the corresponding BIOS code.
+ */
+#define UV_HWPERF_MAXSTRING		128
+struct uv_hwperf_object_info {
+	unsigned int id;
+	union {
+		struct {
+			unsigned long long this_part:1;
+			unsigned long long is_shared:1;
+		} fields;
+		struct {
+			unsigned long long flags;
+			unsigned long long reserved;
+		} b;
+	} f;
+	char name[UV_HWPERF_MAXSTRING];
+	char location[UV_HWPERF_MAXSTRING];
+	unsigned int ports;
+};
+
+#define uv_hwp_this_part	f.fields.this_part
+#define uv_hwp_is_shared	f.fields.is_shared
+#define uv_hwp_flags		f.b.flags
+
+/* macros for object classification */
+#define UV_HWPERF_IS_NODE(x)		((x) && strstr((x)->name, "Hub"))
+#define UV_HWPERF_IS_IONODE(x)		((x) && strstr((x)->name, "IORiser"))
+#define UV_HWPERF_IS_NL5ROUTER(x)	((x) && strstr((x)->name, "NL5Router"))
+#define UV_HWPERF_IS_OLDROUTER(x)	((x) && strstr((x)->name, "Router"))
+#define UV_HWPERF_IS_ROUTER(x)		UV_HWPERF_IS_NL5ROUTER(x)
+#define UV_HWPERF_FOREIGN(x)		((x) && !(x)->uv_hwp_this_part && \
+					!(x)->uv_hwp_is_shared)
+#define UV_HWPERF_SAME_OBJTYPE(x, y)	((UV_HWPERF_IS_NODE(x) && \
+					 UV_HWPERF_IS_NODE(y)) || \
+					(UV_HWPERF_IS_IONODE(x) && \
+					 UV_HWPERF_IS_IONODE(y)) || \
+					(UV_HWPERF_IS_ROUTER(x) && \
+					 UV_HWPERF_IS_ROUTER(y)))
+
+/* numa port structure, UV_HWPERF_ENUM_PORTS returns an array of these */
+struct uv_hwperf_port_info {
+	unsigned int port;
+	unsigned int conn_id;
+	unsigned int conn_port;
+};
+
+/* for HWPERF_{GET,SET}_MMRS */
+struct uv_hwperf_data {
+	unsigned long long addr;
+	unsigned long long data;
+};
+
+/* user ioctl() argument, see below */
+struct uv_hwperf_ioctl_args {
+	unsigned long long arg;		/* argument, usually an object id */
+	unsigned long long sz;		/* size of transfer */
+	void *ptr;			/* pointer to source/target */
+	unsigned int v0;		/* second return value */
+};
+
+/*
+ * For UV_HWPERF_{GET,SET}_MMRS and UV_HWPERF_OBJECT_DISTANCE,
+ * uv_hwperf_ioctl_args.arg can be used to specify a CPU on which
+ * to call BIOS, and whether to use an interprocessor interrupt
+ * or task migration in order to do so. If the CPU specified is
+ * UV_HWPERF_ARG_ANY_CPU, then the current CPU will be used.
+ */
+#define UV_HWPERF_ARG_ANY_CPU		0x7fffffffUL
+#define UV_HWPERF_ARG_CPU_MASK		0x7fffffff00000000ULL
+#define UV_HWPERF_ARG_USE_IPI_MASK	0x8000000000000000ULL
+#define UV_HWPERF_ARG_OBJID_MASK	0x00000000ffffffffULL
+
+/*
+ * ioctl requests on the "uv_hwperf" misc device that call BIOS.
+ */
+#define UV_HWPERF_OP_MEM_COPYIN		0x1000
+#define UV_HWPERF_OP_MEM_COPYOUT	0x2000
+#define UV_HWPERF_OP_MASK		0x0fff
+
+/*
+ * Determine mem requirement.
+ * arg	don't care
+ * sz	8
+ * p	pointer to unsigned long long integer
+ */
+#define	UV_HWPERF_GET_HEAPSIZE		1
+
+/*
+ * Install mem for BIOS driver
+ * arg	don't care
+ * sz	sizeof buffer pointed to by p
+ * p	pointer to buffer for scratch area
+ */
+#define UV_HWPERF_INSTALL_HEAP		2
+
+/*
+ * Get the master (console) nasid
+ * arg	don't care
+ * sz	sizeof nasid_t, pointed to by p
+ * p	pointer to nasid_t master nasid
+ */
+#define UV_HWPERF_MASTER_NASID		3
+
+/*
+ * Determine number of objects
+ * arg	don't care
+ * sz	8
+ * p	pointer to unsigned long long integer
+ */
+#define UV_HWPERF_OBJECT_COUNT		(10|UV_HWPERF_OP_MEM_COPYOUT)
+
+/*
+ * Determine object "distance", relative to a cpu. This operation can
+ * execute on a designated logical cpu number, using either an IPI or
+ * via task migration. If the cpu number is UV_HWPERF_ANY_CPU, then
+ * the current CPU is used. See the UV_HWPERF_ARG_* macros above.
+ *
+ * arg	bitmap of IPI flag, cpu number and object id
+ * sz	8
+ * p	pointer to unsigned long long integer
+ */
+#define UV_HWPERF_OBJECT_DISTANCE	(11|UV_HWPERF_OP_MEM_COPYOUT)
+
+/*
+ * Enumerate objects. Special case if sz == 8, returns the required
+ * buffer size.
+ * arg	don't care
+ * sz	sizeof buffer pointed to by p
+ * p	pointer to array of struct uv_hwperf_object_info
+ */
+#define UV_HWPERF_ENUM_OBJECTS		(12|UV_HWPERF_OP_MEM_COPYOUT)
+
+/*
+ * Enumerate NumaLink ports for an object. Special case if sz == 8,
+ * returns the required buffer size.
+ * arg	object id
+ * sz	sizeof buffer pointed to by p
+ * p	pointer to array of struct uv_hwperf_port_info
+ */
+#define UV_HWPERF_ENUM_PORTS		(13|UV_HWPERF_OP_MEM_COPYOUT)
+
+/*
+ * SET/GET memory mapped registers. These operations can execute
+ * on a designated logical cpu number, using either an IPI or via
+ * task migration. If the cpu number is UV_HWPERF_ANY_CPU, then
+ * the current CPU is used. See the UV_HWPERF_ARG_* macros above.
+ *
+ * arg	bitmap of ipi flag, cpu number and object id
+ * sz	sizeof buffer pointed to by p
+ * p	pointer to array of struct uv_hwperf_data
+ */
+#define UV_HWPERF_SET_MMRS		(14|UV_HWPERF_OP_MEM_COPYIN)
+#define UV_HWPERF_GET_MMRS		(15|UV_HWPERF_OP_MEM_COPYOUT| \
+					    UV_HWPERF_OP_MEM_COPYIN)
+/*
+ * Lock a shared object
+ * arg	object id
+ * sz	don't care
+ * p	don't care
+ */
+#define UV_HWPERF_ACQUIRE		16
+
+/*
+ * Unlock a shared object
+ * arg	object id
+ * sz	don't care
+ * p	don't care
+ */
+#define UV_HWPERF_RELEASE		17
+
+/*
+ * Break a lock on a shared object
+ * arg	object id
+ * sz	don't care
+ * p	don't care
+ */
+#define UV_HWPERF_FORCE_RELEASE		18
+
+/*
+ * ioctl requests on "uv_hwperf" that do not call BIOS
+ */
+
+/*
+ * get cpu info as an array of hwperf_object_info_t.
+ * id is logical CPU number, name is description, location
+ * is geoid (e.g. 001i04b1). Special case if sz == 8,
+ * returns the required buffer size.
+ *
+ * arg	don't care
+ * sz	sizeof buffer pointed to by p
+ * p	pointer to array of struct uv_hwperf_object_info
+ */
+#define UV_HWPERF_GET_CPU_INFO		(100|UV_HWPERF_OP_MEM_COPYOUT)
+
+/*
+ * Given an object id, return it's node number (aka cnode).
+ * arg	object id
+ * sz	8
+ * p	pointer to unsigned long long integer
+ */
+#define UV_HWPERF_GET_OBJ_NODE		(101|UV_HWPERF_OP_MEM_COPYOUT)
+
+/*
+ * Given a node number (cnode), return it's nasid.
+ * arg	ordinal node number (aka cnodeid)
+ * sz	8
+ * p	pointer to unsigned long long integer
+ */
+#define UV_HWPERF_GET_NODE_NASID	(102|UV_HWPERF_OP_MEM_COPYOUT)
+
+/*
+ * Given a node id, determine the id of the nearest node with CPUs
+ * and the id of the nearest node that has memory. The argument
+ * node would normally be a "headless" node, e.g. an "IO node".
+ * Return 0 on success.
+ */
+extern int uv_hwperf_get_nearest_node(short node, short *near_mem,
+					short *near_cpu);
+
+/* return codes */
+#define UV_HWPERF_OP_OK			0
+#define UV_HWPERF_OP_NOMEM		1
+#define UV_HWPERF_OP_NO_PERM		2
+#define UV_HWPERF_OP_IO_ERROR		3
+#define UV_HWPERF_OP_BUSY		4
+#define UV_HWPERF_OP_RECONFIGURE	253
+#define UV_HWPERF_OP_INVAL		254
+
+#ifdef CONFIG_PROC_FS
+int uv_topology_open(struct inode *inode, struct file *file);
+int uv_topology_release(struct inode *inode, struct file *file);
+#endif
+
+#endif				/* UV_HWPERF_H */
Index: linux/include/asm-x86/uv/geo.h
===================================================================
--- /dev/null	1970-01-01 00:00:00.000000000 +0000
+++ linux/include/asm-x86/uv/geo.h	2009-09-30 15:22:55.000000000 -0500
@@ -0,0 +1,121 @@
+/*
+ * 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) 2009 Silicon Graphics International Corp. All rights reserved.
+ */
+
+#ifndef _ASM_UV_GEO_H
+#define _ASM_UV_GEO_H
+
+/* The geoid_s implementation below is based loosely on the pcfg_t
+   implementation in sys/SN/promcfg.h. */
+
+/* Type declaractions */
+
+/* Size of a geoid_s structure (must be before decl. of geoid_u) */
+#define GEOID_SIZE	8	/* Would 16 be better?  The size can
+				   be different on different platforms. */
+
+/* Fields common to all substructures */
+struct geo_common_s {
+    unsigned int rack;
+    unsigned char type;		/* What type of h/w is named by this geoid_s */
+    unsigned char slot:4;	/* slot is IRU */
+    unsigned char blade:4;
+};
+
+/* Additional fields for particular types of hardware */
+struct geo_node_s {
+    struct geo_common_s common;		/* No additional fields needed */
+};
+
+struct geo_rtr_s {
+    struct geo_common_s common;		/* No additional fields needed */
+};
+
+struct geo_iocntl_s {
+    struct geo_common_s common;		/* No additional fields needed */
+};
+
+struct geo_pcicard_s {
+    struct geo_iocntl_s common;
+    char bus;				/* Bus/widget number */
+    char slot;				/* PCI slot number */
+};
+
+/* Subcomponents of a node */
+struct geo_cpu_s {
+    struct geo_node_s node;
+    char slice;				/* Which CPU on the node */
+};
+
+struct geo_mem_s {
+    struct geo_node_s node;
+    char membus;			/* The memory bus on the node */
+    char memslot;			/* The memory slot on the bus */
+};
+
+union geoid_u {
+    struct geo_common_s common;
+    struct geo_node_s node;
+    struct geo_iocntl_s iocntl;
+    struct geo_pcicard_s pcicard;
+    struct geo_rtr_s rtr;
+    struct geo_cpu_s cpu;
+    struct geo_mem_s mem;
+    char padsize[GEOID_SIZE];
+};
+
+/* Preprocessor macros */
+
+#define GEO_MAX_LEN	48	/* max. formatted length, plus some pad:
+				   module/001c07/slab/5/node/memory/2/slot/4 */
+
+#define GEO_TYPE_INVALID	0
+#define GEO_TYPE_MODULE		1
+#define GEO_TYPE_NODE		2
+#define GEO_TYPE_RTR		3
+#define GEO_TYPE_IOCNTL		4
+#define GEO_TYPE_IOCARD		5
+#define GEO_TYPE_CPU		6
+#define GEO_TYPE_MEM		7
+#define GEO_TYPE_MAX		(GEO_TYPE_MEM+1)
+
+/* Parameter for hwcfg_format_geoid_compt() */
+#define GEO_COMPT_MODULE	1
+#define GEO_COMPT_SLAB		2
+#define GEO_COMPT_IOBUS		3
+#define GEO_COMPT_IOSLOT	4
+#define GEO_COMPT_CPU		5
+#define GEO_COMPT_MEMBUS	6
+#define GEO_COMPT_MEMSLOT	7
+
+#define GEO_INVALID_STR		"<invalid>"
+
+#define INVALID_NASID           ((signed short)-1)
+#define INVALID_CNODEID         ((short)-1)
+#define INVALID_PNODEID         ((pnodeid_t)-1)
+#define INVALID_SLOT            ((unsigned char)-1)
+#define INVALID_MODULE          ((unsigned int)-1)
+
+static inline unsigned int geo_rack(union geoid_u g)
+{
+	return (g.common.type == GEO_TYPE_INVALID) ?
+		INVALID_MODULE : g.common.rack;
+}
+
+static inline unsigned char geo_slot(union geoid_u g)
+{
+	return (g.common.type == GEO_TYPE_INVALID) ?
+		INVALID_SLOT : g.common.slot;
+}
+
+static inline unsigned char geo_blade(union geoid_u g)
+{
+	return (g.common.type == GEO_TYPE_INVALID) ?
+		INVALID_SLOT : g.common.blade;
+}
+
+#endif /* _ASM_UV_GEO_H */
Index: linux/arch/x86/Kconfig
===================================================================
--- linux.orig/arch/x86/Kconfig	2009-09-30 15:22:48.000000000 -0500
+++ linux/arch/x86/Kconfig	2009-09-30 15:22:55.000000000 -0500
@@ -397,6 +397,12 @@ config X86_MRST
 	  nor standard legacy replacement devices/features. e.g. Moorestown does
 	  not contain i8259, i8254, HPET, legacy BIOS, most of the io ports.
 
+config UV_HWPERF
+	tristate "SGI UV hwperf: hub counters and topology"
+	help
+	If you have an SGI UV system and you want to enable access to hub
+	counters and topology, say Y here, otherwise say N.
+
 config X86_RDC321X
 	bool "RDC R-321x SoC"
 	depends on X86_32
-- 
Russ Anderson, OS RAS/Partitioning Project Lead  
SGI - Silicon Graphics Inc          rja@....com
--
To unsubscribe from this list: send the line "unsubscribe linux-kernel" in
the body of a message to majordomo@...r.kernel.org
More majordomo info at  http://vger.kernel.org/majordomo-info.html
Please read the FAQ at  http://www.tux.org/lkml/

Powered by blists - more mailing lists

Powered by Openwall GNU/*/Linux Powered by OpenVZ