[PATCH] make btime (in /proc/stat) stable and accurate within 1 jiffy

Mike Coleman (mkc@kc.net)
Sat, 4 Dec 1999 00:02:54 -0600 (CST)


While trying to figure out why process start times (as reported by 'ps')
jitter around, I noticed that the kernel's idea of boot time also jitters
around (between two adjacent time_t values). You can observe this with

while true; do fgrep btime /proc/stat; done

This patch makes the btime value stable and accurate to within one jiffy.
'Stable' here means that the same cached estimate is returned each time, until
the sync between jiffies and timeofday is changed (e.g., by settimeofday,
adjtimex, etc.).

A stable btime is useful because 'ps' calculates process start times based on
boot time (and it would like to use btime).

The patch is a bit ugly with all the unrolled timeval math. There's other
timeval math going on in the kernel, but it doesn't seem to have been pulled
together in one place (include/net/profile.h has a good start). Would this be
worth doing?

--Mike

--- linux-2.3.29/fs/proc/proc_misc.c.dist Thu Nov 25 23:07:06 1999
+++ linux-2.3.29/fs/proc/proc_misc.c Fri Dec 3 23:26:04 1999
@@ -9,6 +9,10 @@
* there. I took this into a separate file and switched the thing to generic
* proc_file_inode_operations, leaving in array.c only per-process stuff.
* Inumbers allocation made dynamic (via create_proc_entry()). AV, May 1999.
+ *
+ * Fixes:
+ * Mike Coleman: make btime stable and accurate to within one jiffy
+ * <mkc@acm.org>
*/

#include <linux/types.h>
@@ -279,14 +283,68 @@
}
#endif

+/* Get boot time accurate to within approximately one jiffy. The same cached
+ estimate is returned each time, changing only for timeofday changes. */
+static void get_boot_time(struct timeval *tv)
+{
+ unsigned long jiffies0;
+ struct timeval now, delta, currentboot;
+ unsigned long flags;
+ struct timespec jiffies_timespec;
+ static struct timeval oldboot;
+
+ /* grab timeofday and jiffies as close in time as possible */
+ local_irq_save(flags);
+ /* do this first, as it might wait on entry for a lock! */
+ do_gettimeofday(&now);
+ jiffies0 = jiffies;
+ local_irq_restore(flags);
+
+ jiffies_to_timespec(jiffies0, &jiffies_timespec);
+ jiffies_timespec.tv_nsec /= 1000; /* convert to usec */
+
+ /* currentboot = now - jiffies_timespec */
+ currentboot.tv_sec = now.tv_sec - jiffies_timespec.tv_sec;
+ while (now.tv_usec < jiffies_timespec.tv_nsec) {
+ now.tv_usec += 1000000;
+ currentboot.tv_sec--;
+ }
+ currentboot.tv_usec = now.tv_usec - jiffies_timespec.tv_nsec;
+
+ /* delta = currentboot - oldboot */
+ delta.tv_sec = currentboot.tv_sec - oldboot.tv_sec;
+ delta.tv_usec = currentboot.tv_usec - oldboot.tv_usec;
+
+ if (delta.tv_sec < 0) {
+ delta.tv_sec *= -1;
+ delta.tv_usec *= -1;
+ }
+ if (delta.tv_sec <= 1) {
+ delta.tv_usec += delta.tv_sec * 1000000;
+#define EPSILON 0
+ if (delta.tv_usec <= (1000000 / HZ) + EPSILON) {
+ /* close, so just use old value */
+ tv->tv_sec = oldboot.tv_sec;
+ tv->tv_usec = oldboot.tv_usec;
+ return;
+ }
+ }
+ /* not close, so store and use new value */
+ tv->tv_sec = oldboot.tv_sec = currentboot.tv_sec;
+ tv->tv_usec = oldboot.tv_usec = currentboot.tv_usec;
+}
+
static int kstat_read_proc(char *page, char **start, off_t off,
int count, int *eof, void *data)
{
int i, len;
unsigned sum = 0;
extern unsigned long total_forks;
+ struct timeval boot;
unsigned long jif = jiffies;

+ get_boot_time(&boot);
+
for (i = 0 ; i < NR_IRQS ; i++)
sum += kstat_irqs(i);

@@ -350,10 +408,10 @@
len += sprintf(page + len, " %u", kstat_irqs(i));
len += sprintf(page + len,
"\nctxt %u\n"
- "btime %lu\n"
+ "btime %lu.%06lu\n"
"processes %lu\n",
kstat.context_swtch,
- xtime.tv_sec - jif / HZ,
+ boot.tv_sec, boot.tv_usec,
total_forks);
if (len <= off+count) *eof = 1;
*start = page + off;

-
To unsubscribe from this list: send the line "unsubscribe linux-kernel" in
the body of a message to majordomo@vger.rutgers.edu
Please read the FAQ at http://www.tux.org/lkml/