Bug 78787 - [libc] [patch] sysconf(_SC_CLK_TCK) may return incorrect value
Summary: [libc] [patch] sysconf(_SC_CLK_TCK) may return incorrect value
Status: Open
Alias: None
Product: Base System
Classification: Unclassified
Component: kern (show other bugs)
Version: 4.11-RELEASE
Hardware: Any Any
: Normal Affects Only Me
Assignee: freebsd-bugs mailing list
URL:
Keywords:
Depends on:
Blocks:
 
Reported: 2005-03-13 14:40 UTC by Pete French
Modified: 2018-01-03 05:14 UTC (History)
0 users

See Also:


Attachments

Note You need to log in before you can comment on or make changes to this bug.
Description Pete French 2005-03-13 14:40:02 UTC
sysconf(_SC_CLK_TCK) returns a contstant value defined in
src/sys/i386/include/ansi.h this is not correct as the actual value of
the statistics clock is not a constnat. In particular it is 100Hz on
my system, but the constant returned is 128. The stat clock may be 100Hz
is apm is enabled with flags 0x20 in the kernel file

Fix: 

The call to sysconf(_SC_CLK_TCK) should actuallyy query the kernel to
get the correct value. Note that this is how /usr/bin/time gets the
frequency of the clock.

I have included below an analysis of the problem done by someone else on
the FreeBSD stable mailing lists, which follwos the code through in more
depth. I do not know if this problem still exists in 5 or CURRENT.

Response from Jon Noack <noackjr@alumni.rice.edu> to my query on -stable

-------------------------------------------------------------------

I infer from your kern.clockrate output that you are running 4.x.

Why does sysconf(_SC_CLK_TCK) always returns 128?  Check out sysconf() 
in src/lib/libc/gen/sysconf.c (lines 83-84 of rev. 1.10):
**********************************************************************
	case _SC_CLK_TCK:
		return (CLK_TCK);
**********************************************************************

CLK_TCK is defined in src/include/time.h (line 52 of rev. 1.15):
**********************************************************************
#define	CLK_TCK		_BSD_CLK_TCK_
**********************************************************************

_BSD_CLK_TCK_ is defined in src/sys/i386/include/ansi.h (line 101 of 
rev. 1.18.2.4):
**********************************************************************
#define        _BSD_CLK_TCK_           128
**********************************************************************

So on i386 (and FreeBSD 4.x), sysconf(_SC_CLK_TCK) will always return 
128.  If you look in src/sys/alpha/include/ansi.h, you'll see that on 
alpha it will always return 100.

To determine how stathz can vary, we'll have to dig deeper.  Check out 
initclocks() in src/sys/kern/kern_clock.c (lines 196-213 of rev. 
1.105.2.11):
**********************************************************************
	/*
	 * Set divisors to 1 (normal case) and let the machine-specific
	 * code do its bit.
	 */
	psdiv = pscnt = 1;
	cpu_initclocks();

#ifdef DEVICE_POLLING
	init_device_poll();
#endif

	/*
	 * Compute profhz/stathz, and fix profhz if needed.
	 */
	i = stathz ? stathz : hz;
	if (profhz == 0)
		profhz = i;
	psratio = profhz / i;
**********************************************************************

stathz and profhz are originally set by cpu_initclocks() (which is MD). 
  If they are not set at this point, hz is used.  This must be the case 
for some of your machines as both stathz and profhz equal hz.  So why 
aren't stathz and profhz being set earlier?  Check out cpu_initclocks() 
in src/sys/i386/isa/clock.c (lines 995-1008 of rev. 1.149.2.6):
**********************************************************************			if 
(statclock_disable) {
		/*
		 * The stat interrupt mask is different without the
		 * statistics clock.  Also, don't set the interrupt
		 * flag which would normally cause the RTC to generate
		 * interrupts.
		 */
		stat_imask = HWI_MASK | SWI_MASK;
		rtc_statusb = RTCSB_24HR;
	} else {
	        /* Setting stathz to nonzero early helps avoid races. */
		stathz = RTC_NOPROFRATE;
		profhz = RTC_PROFRATE;
         }
**********************************************************************

If you look in src/sys/isa/rtc.h, you'll see that RTC_NOPROFRATE=128 and 
RTC_PROFRATE=1024.  The only way stathz and profhz will not be set here 
is if statclock_disable is true.  Where is statclock_disable set?  Check 
out apm_attach() in src/sys/i386/apm/apm.c (lines 1032-1033 of rev. 
1.114.2.6):
**********************************************************************
	if (flags & 0x20)
		statclock_disable = 1;
**********************************************************************

Thus, you must have "device apm0 flags 0x20" or something similar in 
your kernel config file for you to get stathz and profhz as 100.  apm is 
disabled in GENERIC, so by default this is not a problem; apm_attach 
won't be called if apm is disabled, so the fact that GENERIC includes 
"flags 0x20" is irrelevant.

Jon
How-To-Repeat: 
Build a kernel which has a statistics clock other tahn 128 and observe that
sysconf(_SC_CLK_TCK) returns the wrong value!
Comment 1 swell.k 2011-01-06 12:52:08 UTC
The patch below borrows code from NetBSD and partially solves the issue.
It does not address all bde's concerns though

   http://docs.freebsd.org/cgi/mid.cgi?20020701142849.U6692-100000

Also, on my amd64 box stathz != 128

  kern.clockrate: { hz = 1000, tick = 1000, profhz = 8126, stathz = 127 }

--- a.diff begins here ---
Index: include/time.h
===================================================================
--- include/time.h	(revision 216514)
+++ include/time.h	(working copy)
@@ -50,8 +50,12 @@
  * Frequency of the clock ticks reported by times().  Deprecated - use
  * sysconf(_SC_CLK_TCK) instead.  (Removed in 1003.1-2001.)
  */
-#define	CLK_TCK		128
+#ifndef __lint
+__weak_reference(sysconf, __sysconf);
+long __sysconf(int);
 #endif
+#define	CLK_TCK		(__sysconf(3))
+#endif
 
 /* Frequency of the clock ticks reported by clock().  */
 #define	CLOCKS_PER_SEC	128
Index: lib/libc/gen/sysconf.c
===================================================================
--- lib/libc/gen/sysconf.c	(revision 216473)
+++ lib/libc/gen/sysconf.c	(working copy)
@@ -75,6 +75,8 @@ sysconf(name)
 	int mib[2], sverrno, value;
 	long lvalue, defaultresult;
 	const char *path;
+	struct clockinfo tmpclock;
+	static int clk_tck;
 
 	defaultresult = -1;
 
@@ -94,7 +96,21 @@ sysconf(name)
 		}
 		return ((long)rl.rlim_cur);
 	case _SC_CLK_TCK:
-		return (CLK_TCK);
+		/*
+		 * Has to be handled specially because it returns a
+		 * struct clockinfo instead of an integer. Also, since
+		 * this might be called often by some things that
+		 * don't grok CLK_TCK can be a macro expanding to a
+		 * function, cache the value.
+		 */
+		if (clk_tck == 0) {
+			mib[0] = CTL_KERN;
+			mib[1] = KERN_CLOCKRATE;
+			len = sizeof(struct clockinfo);
+			clk_tck = sysctl(mib, 2, &tmpclock, &len, NULL, 0)
+			    == -1 ? -1 : tmpclock.stathz;
+		}
+		return(clk_tck);
 	case _SC_NGROUPS_MAX:
 		mib[0] = CTL_KERN;
 		mib[1] = KERN_NGROUPS;
Index: usr.sbin/timed/timed/timed.c
===================================================================
--- usr.sbin/timed/timed/timed.c	(revision 216514)
+++ usr.sbin/timed/timed/timed.c	(working copy)
@@ -759,7 +759,8 @@
 	int force;
 {
 # define NG_DELAY (30*60*CLK_TCK)	/* 30 minutes */
-	static unsigned long last_update = -NG_DELAY;
+	static unsigned long last_update;
+	static int firsttime;
 	unsigned long new_update;
 	struct goodhost *ghp, **ghpp;
 #ifdef HAVENIS
@@ -768,6 +769,10 @@
 #endif /* HAVENIS */
 	struct tms tm;
 
+	if (firsttime == 0) {
+		last_update = -NG_DELAY;
+		firsttime++;
+	}
 
 	/* if no netgroup, then we are finished */
 	if (goodgroup == 0 || !Mflag)
--- a.diff ends here ---
Comment 2 Eitan Adler freebsd_committer freebsd_triage 2017-12-31 07:58:24 UTC
For bugs matching the following criteria:

Status: In Progress Changed: (is less than) 2014-06-01

Reset to default assignee and clear in-progress tags.

Mail being skipped