PERFORCE change 175383 for review

John Baldwin jhb at FreeBSD.org
Fri Mar 5 21:10:01 UTC 2010


http://p4web.freebsd.org/chv.cgi?CH=175383

Change 175383 by jhb at jhb_jhbbsd on 2010/03/05 21:09:24

	First set of patches to port mcelog(8) to FreeBSD.  It can parse
	MCA error messages from dmesg or /var/log/messages via --ascii
	and it can also pull events from the running kernel via hw.mca
	sysctls.
	
	Known limitations, todo:
	- The client/server stuff is not tested.
	- The client will not work because it doesn't do a sendmsg() with
	  a SCM_CREDS control message.
	- The server does not notice new events.  On Linux /dev/mcelog
	  becomes readable when a new event is logged.  For FreeBSD we
	  would need to poll hw.mca.count instead I think.
	- Triggers are not currently supported.  We would need to switch to
	  kqueue and use EVFILT_PROC events I think (or implement ppoll()).
	- The code that tries to track bad pages is not supported.
	
	Other changes:
	- Install to /usr/local rather than /usr.
	- Include a version of open_memstream(3) implemented via
	  funopen(3).
	- Add a getline(3) stub using fgets(3).
	- Use 'cpuid' instruction values to extract several things that
	  the Linux code reads out of /proc.
	- Use FreeBSD-specific SCM_CREDS rather than Linux-specific
	  SCM_CREDENTIALS.

Affected files ...

.. //depot/projects/mcelog/Makefile#2 edit
.. //depot/projects/mcelog/cache.c#2 edit
.. //depot/projects/mcelog/client.c#2 edit
.. //depot/projects/mcelog/config.c#2 edit
.. //depot/projects/mcelog/eventloop.c#2 edit
.. //depot/projects/mcelog/intel.c#2 edit
.. //depot/projects/mcelog/mcelog.c#2 edit
.. //depot/projects/mcelog/mcelog.h#2 edit
.. //depot/projects/mcelog/memdb.c#2 edit
.. //depot/projects/mcelog/memstream.c#1 add
.. //depot/projects/mcelog/p4.c#2 edit
.. //depot/projects/mcelog/server.c#2 edit
.. //depot/projects/mcelog/tsc.c#2 edit

Differences ...

==== //depot/projects/mcelog/Makefile#2 (text) ====

@@ -1,5 +1,5 @@
 CFLAGS := -g -Os
-prefix := /usr
+prefix := /usr/local
 etcprefix :=
 # Define appropiately for your distribution
 # DOCDIR := /usr/share/doc/packages/mcelog
@@ -30,8 +30,14 @@
 
 OBJ := p4.o k8.o mcelog.o dmi.o tsc.o core2.o bitfield.o intel.o \
        nehalem.o dunnington.o tulsa.o config.o memutil.o msg.o   \
-       eventloop.o leaky-bucket.o memdb.o server.o trigger.o 	 \
-       client.o cache.o sysfs.o yellow.o page.o rbtree.o
+       eventloop.o leaky-bucket.o memdb.o server.o client.o 	 \
+       cache.o rbtree.o
+ifndef FREEBSD
+OBJ += page.o trigger.o sysfs.o yellow.o
+endif
+ifdef FREEBSD
+OBJ += memstream.o
+endif
 DISKDB_OBJ := diskdb.o dimm.o db.o
 CLEAN := mcelog dmi tsc dbquery .depend .depend.X dbquery.o ${DISKDB_OBJ}
 DOC := mce.pdf

==== //depot/projects/mcelog/cache.c#2 (text) ====

@@ -27,6 +27,7 @@
 #include "sysfs.h"
 #include "cache.h"
 
+#ifdef __Linux__
 struct cache { 
 	unsigned level;
 	/* Numerical values must match MCACOD */
@@ -164,6 +165,15 @@
 	Wprintf("Cannot find sysfs cache for CPU %d", cpu);
 	return -1;
 }
+#endif
+
+#ifdef __FreeBSD__
+int cache_to_cpus(int cpu, unsigned level, unsigned type, 
+		   int *cpulen, unsigned **cpumap)
+{
+	return -1;
+}
+#endif
 
 #ifdef TEST
 main()

==== //depot/projects/mcelog/client.c#2 (text) ====

@@ -18,6 +18,7 @@
 #include <stdio.h>
 #include <sys/socket.h>
 #include <sys/un.h>
+#include <string.h>
 #include <unistd.h>
 #include "mcelog.h"
 #include "client.h"
@@ -48,6 +49,9 @@
 			sizeof(struct sockaddr_un)) < 0)
 		SYSERRprintf("client connect");
 	
+#ifdef __FreeBSD__
+	/* XXX: Need to use sendmsg() to send a SCM_CREDS control message. */
+#endif
 	n = strlen(command);
 	if (write(fd, command, n) != n)
 		SYSERRprintf("client command write");

==== //depot/projects/mcelog/config.c#2 (text) ====

@@ -126,6 +126,24 @@
 	return s;
 }
 
+#ifdef __FreeBSD__
+/*
+ * Newer versions do have getline(), so this should use a version test
+ * at some point.
+ */
+static ssize_t getline(char **cp, size_t *lenp, FILE *f)
+{
+
+	if (*cp == NULL) {
+		*cp = malloc(4096);
+		*lenp = 4096;
+	}
+	if (fgets(*cp, *lenp, f) == NULL)
+		return (0);
+	return (strlen(*cp));
+}
+#endif
+
 int parse_config_file(const char *fn)
 {
 	FILE *f;

==== //depot/projects/mcelog/eventloop.c#2 (text) ====

@@ -38,7 +38,9 @@
 static struct pollfd pollfds[MAX_POLLFD];
 static struct pollcb pollcbs[MAX_POLLFD];	
 
+#ifdef __Linux__
 static sigset_t event_sigs;
+#endif
 
 static int closeonexec(int fd)
 {
@@ -97,6 +99,7 @@
 }
 
 /* Run signal handler only directly after event loop */
+#ifdef __Linux__
 int event_signal(int sig)
 {
 	static int first = 1;
@@ -111,11 +114,17 @@
 		return -1;
 	return 0;
 }
+#endif
 
 void eventloop(void)
 {
 	for (;;) { 
+#ifdef __Linux__
 		int n = ppoll(pollfds, max_pollfd, NULL, &event_sigs);
+#endif
+#ifdef __FreeBSD__
+		int n = poll(pollfds, max_pollfd, -1);
+#endif
 		if (n <= 0) {
 			if (n < 0 && errno != EINTR)
 				SYSERRprintf("poll error");

==== //depot/projects/mcelog/intel.c#2 (text) ====

@@ -79,7 +79,9 @@
  			corr_err_cnt = EXTRACT(m->status, 38, 52);
 		memory_error(m, channel, dimm, corr_err_cnt, recordlen);
 
+#ifdef __Linux__
 		account_page_error(m, channel, dimm, corr_err_cnt);
+#endif
 
 		return 1;
 	}

==== //depot/projects/mcelog/mcelog.c#2 (text) ====

@@ -20,8 +20,18 @@
 #define _GNU_SOURCE 1
 #include <sys/fcntl.h>
 #include <sys/ioctl.h>
+#ifdef __Linux__
 #include <asm/types.h>
 #include <asm/ioctls.h>
+#endif
+#ifdef __FreeBSD__
+#include <sys/types.h>
+#include <sys/sysctl.h>
+#include <machine/cpufunc.h>
+#include <machine/specialreg.h>
+#include <machine/mca.h>
+#include <err.h>
+#endif
 #include <stdlib.h>
 #include <stdio.h>
 #include <string.h>
@@ -59,7 +69,9 @@
 
 enum cputype cputype = CPU_GENERIC;	
 
+#ifdef __Linux__
 char *logfn = LOG_DEV_FILENAME; 
+#endif
 
 int ignore_nodev;
 int filter_bogus = 1;
@@ -70,7 +82,9 @@
 int dump_raw_ascii;
 int daemon_mode;
 static char *inputfile;
+#ifdef __Linux__
 char *processor_flags;
+#endif
 static int foreground;
 int filter_memory_errors;
 static struct config_cred runcred = { .uid = -1U, .gid = -1U };
@@ -388,6 +402,7 @@
 	Wprintf("\n");
 }
 
+#ifdef __Linux__
 void check_cpu(void)
 { 
 	enum { 
@@ -455,7 +470,44 @@
 	} else
 		Eprintf("warning: Cannot open /proc/cpuinfo\n");
 } 
+#endif
+
+#ifdef __FreeBSD__
+void check_cpu(void)
+{
+	char vendor[20];
+	u_int regs[4];
+	u_int cpu_id;
+	int family, model;
+	static int checked;
+
+	if (checked)
+		return;
+	checked = 1;
+
+	do_cpuid(0, regs);
+	((u_int *)vendor)[0] = regs[1];
+	((u_int *)vendor)[1] = regs[3];
+	((u_int *)vendor)[2] = regs[2];
+	vendor[12] = 0;
 
+	do_cpuid(1, regs);
+	cpu_id = regs[0];
+	family = CPUID_TO_FAMILY(cpu_id);
+	model = CPUID_TO_MODEL(cpu_id);
+
+	if (cpu_forced) 
+		;
+	else if (!strcmp(vendor,"AuthenticAMD") && 
+	    (family == 15 || family == 16 || family == 17))
+		cputype = CPU_K8;
+	else if (!strcmp(vendor,"GenuineIntel"))
+		cputype = select_intel_cputype(family, model);
+	/* Add checks for other CPUs here */	
+}
+#endif
+
+#ifdef __Linux__
 static char *skipspace(char *s)
 {
 	while (isspace(*s))
@@ -479,6 +531,7 @@
 	}
 	return skipspace(s);
 }
+#endif
 
 static void dump_mce_final(struct mce *m, char *symbol, int missing, int recordlen, 
 			   int dseen)
@@ -501,6 +554,7 @@
 	if (recordlen < endof_field(struct mce, f)) \
 		recordlen = endof_field(struct mce, f)
 
+#ifdef __Linux__
 /* Decode ASCII input for fatal messages */
 static void decodefatal(FILE *inf)
 {
@@ -646,7 +700,148 @@
 	if (data)
 		dump_mce_final(&m, symbol, missing, recordlen, disclaimer_seen);
 }
+#endif
 
+#ifdef __FreeBSD__
+/* Used to map cpuid vendor strings to Linux cpuvendor values. */
+static struct {
+	char *name;
+	u_char cpuvendor;
+} vendor_ids[] = {
+	{ "GenuineIntel", 0 },
+	{ "CyrixInstead", 1 },
+	{ "AuthenticAMD", 2 },
+	{ "UMC UMC UMC ", 3 },
+	{ "CentaurHauls", 5 },
+	{ "GenuineTMx86", 7 },
+	{ "Geode by NSC", 8 },
+};
+	  
+/* Convert FreeBSD's struct mca_record into a struct mce. */
+static void convert_mca(struct mca_record *mr, struct mce *mce, int live)
+{
+	memset(mce, 0, sizeof(*mce));
+	mce->status = mr->mr_status;
+	mce->misc = mr->mr_misc;
+	mce->addr = mr->mr_addr;
+	mce->tsc = mr->mr_tsc;
+	mce->bank = mr->mr_bank;
+	mce->finished = 1;
+	mce->apicid = mr->mr_apic_id;
+
+	/*
+	 * For live records (from sysctl), fill in some fields using
+	 * registers from the current CPU.
+	 */
+	if (live) {
+		char vendor[20];
+		u_int i, regs[4];
+
+		do_cpuid(0, regs);
+		((u_int *)vendor)[0] = regs[1];
+		((u_int *)vendor)[1] = regs[3];
+		((u_int *)vendor)[2] = regs[2];
+		vendor[12] = 0;
+		mce->cpuvendor = 0xff;
+		for (i = 0; i < sizeof(vendor_ids) / sizeof(vendor_ids[0]); i++)
+			if (strcmp(vendor, vendor_ids[i].name) == 0)
+				mce->cpuvendor = vendor_ids[i].cpuvendor;
+
+		do_cpuid(1, regs);
+		mce->cpuid = regs[0];
+		//mce->mcgcap = rdmsr(MSR_MCG_CAP);
+	}
+}
+
+/* Decode ASCII input for fatal messages */
+static void decodefatal(FILE *inf)
+{
+	struct mca_record mr;
+	struct mce m;
+	long long val;
+	char line[100], *s, symbol[1];
+	int data, missing;
+	enum rows {
+		BANK = 1,
+		CPU = 2,
+		ADDR = 4,
+		MISC = 8,
+	};
+
+	symbol[0] = '\0';
+	data = 0;
+	missing = 0;
+	memset(&mr, 0, sizeof(mr));
+	while ((s = fgets(line, sizeof(line), inf)) != NULL) {
+		s = strstr(s, "MCA: ");
+		if (s == NULL)
+			continue;
+		s += strlen("MCA: ");
+
+		if (strncmp(s, "bank", 4) == 0) {
+			/* Start of a new record, dump the previous one. */
+			if (data != 0) {
+				/* Require some minimum data. */
+				if (data & BANK) {
+					if (mr.mr_status & MC_STATUS_ADDRV &&
+					    !(data & ADDR))
+						missing = 1;
+					if (mr.mr_status & MC_STATUS_MISCV &&
+					    !(data & MISC))
+						missing = 1;
+					convert_mca(&mr, &m, 0);
+					dump_mce_final(&m, symbol, missing,
+					    sizeof(struct mce), 0);
+				}
+				data = 0;
+				missing = 0;
+				memset(&mr, 0, sizeof(mr));
+			}
+
+			if (sscanf(s, "bank %d, status 0x%llx", &mr.mr_bank,
+				&val) != 2)
+				missing = 1;
+			else {
+				data |= BANK;
+				mr.mr_status = val;
+			}
+		}
+		if (strncmp(s, "CPU", 3) == 0) {
+			if (sscanf(s, "CPU %d ", &mr.mr_apic_id) != 1)
+				missing = 1;
+			else
+				data |= CPU;
+		}
+		if (strncmp(s, "Address", 7) == 0) {
+			if (sscanf(s, "Address 0x%llx", &val) != 1)
+				missing = 1;
+			else {
+				data |= ADDR;
+				mr.mr_addr = val;
+			}
+		}
+		if (strncmp(s, "Misc", 4) == 0) {
+			if (sscanf(s, "Misc 0x%llx", &val) != 1)
+				missing = 1;
+			else {
+				data |= MISC;
+				mr.mr_misc = val;
+			}
+		}
+	}
+
+	/* Dump the last record. */
+	if (data & BANK) {
+		if (mr.mr_status & MC_STATUS_ADDRV && !(data & ADDR))
+			missing = 1;
+		if (mr.mr_status & MC_STATUS_MISCV && !(data & MISC))
+			missing = 1;
+		convert_mca(&mr, &m, 0);
+		dump_mce_final(&m, symbol, missing, sizeof(struct mce), 0);
+	}
+}
+#endif
+
 static void remove_pidfile(void)
 {
 	unlink(pidfile);
@@ -900,8 +1095,10 @@
 
 static void general_setup(void)
 {
+#ifdef __Linux__
 	trigger_setup();
 	yellow_setup();
+#endif
 	config_cred("global", "run-credentials", &runcred);
 	if (config_bool("global", "filter-memory-errors") == 1)
 		filter_memory_errors = 1;
@@ -924,6 +1121,7 @@
 	}
 }
 
+#ifdef __Linux__
 static void process(int fd, unsigned recordlen, unsigned loglen, char *buf)
 {	
 	int i; 
@@ -964,6 +1162,51 @@
 	if (finish)
 		exit(0);
 }
+#endif
+
+#ifdef __FreeBSD__
+static void process(int fd __unused, unsigned recordlen,
+    unsigned loglen, char *buf __unused)
+{
+	struct mca_record mr;
+	struct mce mce;
+	int mib[4];
+	size_t len;
+	int finish, i;
+
+	len = 4;
+	if (sysctlnametomib("hw.mca.records", mib, &len) < 0)
+		return;
+
+	finish = 0;
+	recordlen = sizeof(struct mce);
+	for (i = 0; i < (int)loglen; i++) {
+		mib[3] = i;
+		len = sizeof(mr);
+		if (sysctl(mib, 4, &mr, &len, NULL, 0) < 0) {
+			warn("sysctl(hw.mca.records.%d)", i);
+			continue;
+		}
+
+		convert_mca(&mr, &mce, 1);
+		mce_prepare(&mce);
+		if (numerrors > 0 && --numerrors == 0)
+			finish = 1;
+		if (!mce_filter(&mce, recordlen)) 
+			continue;
+		if (!dump_raw_ascii) {
+			disclaimer();
+			Wprintf("MCE %d\n", i);
+			dump_mce(&mce, recordlen);
+		} else
+			dump_mce_raw_ascii(&mce, recordlen);
+		flushlog();
+	}
+
+	if (finish)
+		exit(0);
+}
+#endif
 
 static void noargs(int ac, char **av)
 {
@@ -1022,12 +1265,28 @@
 	char *buf;
 };
 
+#ifdef __Linux__
 static void process_mcefd(struct pollfd *pfd, void *data)
 {
 	struct mcefd_data *d = (struct mcefd_data *)data;
 	assert((pfd->revents & POLLIN) != 0);
 	process(pfd->fd, d->recordlen, d->loglen, d->buf);
 }
+#endif
+
+#ifdef __FreeBSD__
+/* Fetch current MCA records using sysctls. */
+static void fetch_records_info(struct mcefd_data *d)
+{
+	size_t len;
+	int count;
+
+	memset(d, 0, sizeof(*d));
+	len = sizeof(count);
+	if (sysctlbyname("hw.mca.count", &count, &len, NULL, 0) == 0)
+		d->loglen = count;
+}
+#endif
 
 int main(int ac, char **av) 
 { 
@@ -1057,13 +1316,16 @@
 		} else if (opt == 0)
 			break;		    
 	} 
+#ifdef __Linux__
 	if (av[optind])
 		logfn = av[optind++];
+#endif
 	if (av[optind])
 		usage();
 	checkdmi();
 	general_setup();
 		
+#ifdef __Linux__
 	fd = open(logfn, O_RDONLY); 
 	if (fd < 0) {
 		if (ignore_nodev) 
@@ -1078,15 +1340,24 @@
 		err("MCE_GET_LOG_LEN");
 
 	d.buf = xalloc(d.recordlen * d.loglen); 
+#endif
+#ifdef __FreeBSD__
+	fetch_records_info(&d);
+	fd = -1;
+#endif
 	if (daemon_mode) {
 		check_cpu();
 		prefill_memdb();
 		if (!do_dmi)
 			closedmi();
 		server_setup();
+#ifdef __Linux__
 		page_setup();
+#endif
 		drop_cred();
+#ifdef __Linux__
 		register_pollcb(fd, POLLIN, process_mcefd, &d);
+#endif
 		if (!foreground && daemon(0, need_stdout()) < 0)
 			err("daemon");
 		if (pidfile)
@@ -1095,7 +1366,9 @@
 	} else {
 		process(fd, d.recordlen, d.loglen, d.buf);
 	}
+#ifdef __Linux__
 	trigger_wait();
+#endif
 		
 	exit(0); 
 } 

==== //depot/projects/mcelog/mcelog.h#2 (text) ====

@@ -64,9 +64,11 @@
 #define MCI_STATUS_ADDRV (1ULL<<58)  /* addr reg. valid */
 #define MCI_STATUS_PCC   (1ULL<<57)  /* processor context corrupt */
 
+#ifndef MCG_STATUS_RIPV
 #define MCG_STATUS_RIPV  (1ULL<<0)   /* restart ip valid */
 #define MCG_STATUS_EIPV  (1ULL<<1)   /* eip points to correct instruction */
 #define MCG_STATUS_MCIP  (1ULL<<2)   /* machine check in progress */
+#endif
 
 #define MCG_CMCI_P		(1ULL<<10)   /* CMCI supported */
 #define MCG_TES_P		(1ULL<<11)   /* Yellow bit cache threshold supported */
@@ -89,6 +91,10 @@
 #define PRINTFLIKE 
 #endif
 
+#if defined(__FreeBSD__) && defined(_STDIO_H_)
+FILE *open_memstream(char **cp, size_t *lenp);
+#endif
+
 int Wprintf(char *fmt, ...) PRINTFLIKE;
 void Eprintf(char *fmt, ...) PRINTFLIKE;
 void SYSERRprintf(char *fmt, ...) PRINTFLIKE;

==== //depot/projects/mcelog/memdb.c#2 (text) ====

@@ -170,7 +170,9 @@
 	asprintf(&env[ei++], "THRESHOLD_COUNT=%d", bucket->count + bucket->excess);
 	env[ei] = NULL;	
 	assert(ei < MAX_ENV);
+#ifdef __Linux__
 	run_trigger(bc->trigger, NULL, env);
+#endif
 	for (i = 0; i < ei; i++)
 		free(env[i]);
 out:

==== //depot/projects/mcelog/p4.c#2 (text) ====

@@ -175,8 +175,10 @@
 		Wprintf("%s CACHE %s %s Error\n", type, level,
 				get_RRRR_str((mca & CACHE_RRRR_MASK) >> 
 					      CACHE_RRRR_SHIFT));
+#ifdef __Linux__
 		if (track == 2)
 			run_yellow_trigger(cpu, typenum, levelnum, type, level, socket);
+#endif
 	} else if (test_prefix(10, mca)) {
 		if (mca == 0x400)
 			Wprintf("Internal Timer error\n");

==== //depot/projects/mcelog/server.c#2 (text) ====

@@ -101,7 +101,9 @@
 
 static void dispatch_pages(FILE *fh)
 {
+#ifdef __Linux__
 	dump_page_errors(fh);
+#endif
 	fprintf(fh, "done\n");
 }
 
@@ -137,6 +139,7 @@
 		Enomem();
 }
 
+#ifdef __Linux__
 /* check if client is allowed to access */
 static int access_check(int fd, struct msghdr *msg)
 {
@@ -162,6 +165,35 @@
 	sendstring(fd, "permission denied\n");
 	return -1;
 }
+#endif
+
+#ifdef __FreeBSD__
+/* check if client is allowed to access */
+static int access_check(int fd, struct msghdr *msg)
+{
+	struct cmsghdr *cmsg;
+	struct cmsgcred *cr;
+
+	/* check credentials */
+	cmsg = CMSG_FIRSTHDR(msg);
+	if (cmsg == NULL || 
+		cmsg->cmsg_level != SOL_SOCKET ||
+		cmsg->cmsg_type != SCM_CREDS) { 
+		Eprintf("Did not receive credentials over client unix socket %p\n",
+			cmsg);
+		return -1;
+	}
+	cr = (struct cmsgcred *)CMSG_DATA(cmsg);
+	if (cr->cmcred_uid == 0 || 
+		(acc.uid != -1U && cr->cmcred_uid == acc.uid) ||
+		(acc.gid != -1U && cr->cmcred_gid == acc.gid))
+		return 0;
+	Eprintf("rejected client access from pid:%u uid:%u gid:%u\n",
+		cr->cmcred_pid, cr->cmcred_uid, cr->cmcred_gid);
+	sendstring(fd, "permission denied\n");
+	return -1;
+}
+#endif
 
 /* retrieve commands from client */
 static int client_input(int fd, struct clientcon *cc)
@@ -242,18 +274,22 @@
 {
 	struct clientcon *cc = NULL;
 	int nfd = accept(pfd->fd, NULL, 0);	
+#ifdef __Linux__
 	int on;
+#endif
 
 	if (nfd < 0) {
 		SYSERRprintf("accept failed on client socket");
 		return;
 	}
 
+#ifdef __Linux__
 	on = 1;
 	if (setsockopt(nfd, SOL_SOCKET, SO_PASSCRED, &on, sizeof(on)) < 0) {
 		SYSERRprintf("Cannot enable credentials passing on client socket");
 		goto cleanup;
 	}
+#endif
 
 	cc = xalloc(sizeof(struct clientcon));
 	if (register_pollcb(nfd, POLLIN, client_event, cc) < 0) {

==== //depot/projects/mcelog/tsc.c#2 (text) ====

@@ -15,6 +15,12 @@
    on your Linux system; if not, write to the Free Software Foundation,
    Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */
 #define _GNU_SOURCE 1
+#ifdef __FreeBSD__
+#include <sys/types.h>
+#include <sys/sysctl.h>
+#include <machine/cpufunc.h>
+#include <machine/specialreg.h>
+#endif
 #include <string.h>
 #include <stdio.h>
 #include <stdlib.h>
@@ -46,6 +52,7 @@
 	return 0;
 }
 
+#ifdef __Linux__
 static double cpufreq_mhz(int cpu, double infomhz)
 {
 	double mhz;
@@ -68,12 +75,29 @@
 	fclose(f);
 	return mhz;
 }
+#endif
+
+#ifdef __FreeBSD__
+static double cpufreq_mhz(int cpu, double infomhz)
+{
+	double mhz;
+	uint64_t freq;
+	size_t len;
+
+	len = sizeof(freq);
+	if (sysctlbyname("machdep.tsc_freq", &freq, &len, NULL, 0) < 0)
+		return infomhz;
+	mhz = freq / 1000000.0;
+	return mhz;
+}
+#endif
 
 int decode_tsc_forced(char **buf, double mhz, u64 tsc)
 {
 	return fmt_tsc(buf, tsc, mhz);
 }
 
+#ifdef __Linux__
 static int deep_sleep_states(int cpu)
 {
 	int ret;
@@ -132,6 +156,41 @@
 		return 0;
 	return 1;
 }
+#endif
+
+#ifdef __FreeBSD__
+/* Try to figure out if this CPU has a somewhat reliable TSC clock */
+static int tsc_reliable(int cputype, int cpunum)
+{
+	u_int regs[4];
+	u_int cpu_id, amd_pminfo;
+
+	if (cputype != CPU_K8 && !is_intel_cpu(cputype))
+		return 0;
+
+	do_cpuid(0, regs);
+	cpu_id = regs[1];
+	do_cpuid(0x80000000, regs);
+	if (regs[0] >= 0x80000007) {
+		do_cpuid(0x80000007, regs);
+		amd_pminfo = regs[3];
+	} else
+		amd_pminfo = 0;
+
+	if (amd_pminfo & AMDPM_TSC_INVARIANT)
+		return 1;
+	if (is_intel_cpu(cputype)) {
+		if (CPUID_TO_FAMILY(cpu_id) >= 0x10 ||
+		    cpu_id == 0x60fb2)
+			return 1;
+	} else if ((CPUID_TO_FAMILY(cpu_id) == 0x6 &&
+		CPUID_TO_MODEL(cpu_id) >= 0xe) ||
+	    (CPUID_TO_FAMILY(cpu_id) == 0xf && CPUID_TO_MODEL(cpu_id) >= 0x3))
+		return 1;
+
+	return 0;
+}
+#endif
 
 int decode_tsc_current(char **buf, int cpunum, enum cputype cputype, double mhz, 
 		       unsigned long long tsc)


More information about the p4-projects mailing list