Posix compliant CLOCK_PROCESS/THREAD_CPUTIME_ID V5

From: Christoph Lameter
Date: Wed Sep 29 2004 - 14:41:32 EST


This version uses an offset field in the task and signal structs
to store offsets for the thread and process clock. It also includes
the check in timer_create for REALTIME or MONOTONIC. The
clocks are now independent and therefore the thread clock is not
clearedby clock_settime(CLOCK_PROCESS_CPUTIME_ID) in the testrun:

Single Thread Testing
CLOCK_THREAD_CPUTIME_ID= 0.374943000 resolution= 0.000999848
CLOCK_PROCESS_CPUTIME_ID= 0.374943000 resolution= 0.000999848
Multi Thread Testing
Starting Thread: 0 1 2 3 4 5 6 7 8 9
Joining Thread: 0 1 2 3 4 5 6 7 8 9
0 Cycles= 0 Thread= 0.000000000ns Process= 0.000999848ns
1 Cycles=1000000 Thread= 0.036994376ns Process= 0.315951968ns
2 Cycles=2000000 Thread= 0.073988752ns Process= 0.086986776ns
3 Cycles=3000000 Thread= 0.109983280ns Process= 0.517921264ns
4 Cycles=4000000 Thread= 0.146977656ns Process= 0.563914272ns
5 Cycles=5000000 Thread= 0.180972488ns Process= 1.043841312ns
6 Cycles=6000000 Thread= 0.218966712ns Process= 1.112830824ns
7 Cycles=7000000 Thread= 0.254961240ns Process= 1.620753608ns
8 Cycles=8000000 Thread= 0.290955768ns Process= 1.490773368ns
9 Cycles=9000000 Thread= 0.326950296ns Process= 1.641750416ns

Clock status at the end of the timer tests:
Gettimeofday() = 1096484938.561351000
CLOCK_REALTIME= 1096484938.561363000 resolution= 0.000999848
CLOCK_MONOTONIC= 157.633669432 resolution= 0.000999848
CLOCK_PROCESS_CPUTIME_ID= 1.641750416 resolution= 0.000999848
CLOCK_THREAD_CPUTIME_ID= 0.375942848 resolution= 0.000999848Index: linux-2.6.9-rc2/kernel/posix-timers.c
===================================================================
--- linux-2.6.9-rc2.orig/kernel/posix-timers.c 2004-09-12 22:32:48.000000000 -0700
+++ linux-2.6.9-rc2/kernel/posix-timers.c 2004-09-29 11:48:54.000000000 -0700
@@ -10,6 +10,10 @@
* 2004-06-01 Fix CLOCK_REALTIME clock/timer TIMER_ABSTIME bug.
* Copyright (C) 2004 Boris Hu
*
+ * 2004-07-27 Provide POSIX compliant clocks
+ * CLOCK_PROCESS_CPUTIME_ID and CLOCK_THREAD_CPUTIME_ID.
+ * by Christoph Lameter
+ *
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or (at
@@ -133,18 +137,10 @@
* resolution. Here we define the standard CLOCK_REALTIME as a
* 1/HZ resolution clock.
*
- * CPUTIME & THREAD_CPUTIME: We are not, at this time, definding these
- * two clocks (and the other process related clocks (Std
- * 1003.1d-1999). The way these should be supported, we think,
- * is to use large negative numbers for the two clocks that are
- * pinned to the executing process and to use -pid for clocks
- * pinned to particular pids. Calls which supported these clock
- * ids would split early in the function.
- *
* RESOLUTION: Clock resolution is used to round up timer and interval
* times, NOT to report clock times, which are reported with as
* much resolution as the system can muster. In some cases this
- * resolution may depend on the underlaying clock hardware and
+ * resolution may depend on the underlying clock hardware and
* may not be quantifiable until run time, and only then is the
* necessary code is written. The standard says we should say
* something about this issue in the documentation...
@@ -162,7 +158,7 @@
*
* At this time all functions EXCEPT clock_nanosleep can be
* redirected by the CLOCKS structure. Clock_nanosleep is in
- * there, but the code ignors it.
+ * there, but the code ignores it.
*
* Permissions: It is assumed that the clock_settime() function defined
* for each clock will take care of permission checks. Some
@@ -198,6 +194,10 @@
struct timespec *tp, struct timespec *mo);
int do_posix_clock_monotonic_gettime(struct timespec *tp);
int do_posix_clock_monotonic_settime(struct timespec *tp);
+int do_posix_clock_process_gettime(struct timespec *tp);
+int do_posix_clock_process_settime(struct timespec *tp);
+int do_posix_clock_thread_gettime(struct timespec *tp);
+int do_posix_clock_thread_settime(struct timespec *tp);
static struct k_itimer *lock_timer(timer_t timer_id, unsigned long *flags);

static inline void unlock_timer(struct k_itimer *timr, unsigned long flags)
@@ -218,6 +218,16 @@
.clock_get = do_posix_clock_monotonic_gettime,
.clock_set = do_posix_clock_monotonic_settime
};
+ struct k_clock clock_thread = {.res = CLOCK_REALTIME_RES,
+ .abs_struct = NULL,
+ .clock_get = do_posix_clock_thread_gettime,
+ .clock_set = do_posix_clock_thread_settime
+ };
+ struct k_clock clock_process = {.res = CLOCK_REALTIME_RES,
+ .abs_struct = NULL,
+ .clock_get = do_posix_clock_process_gettime,
+ .clock_set = do_posix_clock_process_settime
+ };

#ifdef CONFIG_TIME_INTERPOLATION
/* Clocks are more accurate with time interpolators */
@@ -226,6 +236,8 @@

register_posix_clock(CLOCK_REALTIME, &clock_realtime);
register_posix_clock(CLOCK_MONOTONIC, &clock_monotonic);
+ register_posix_clock(CLOCK_PROCESS_CPUTIME_ID, &clock_process);
+ register_posix_clock(CLOCK_THREAD_CPUTIME_ID, &clock_thread);

posix_timers_cache = kmem_cache_create("posix_timers_cache",
sizeof (struct k_itimer), 0, 0, NULL, NULL);
@@ -573,8 +585,8 @@
sigevent_t event;
int it_id_set = IT_ID_NOT_SET;

- if ((unsigned) which_clock >= MAX_CLOCKS ||
- !posix_clocks[which_clock].res)
+ if ((unsigned) which_clock != CLOCK_REALTIME &&
+ (unsigned) which_clock != CLOCK_MONOTONIC)
return -EINVAL;

new_timer = alloc_posix_timer();
@@ -1227,6 +1239,69 @@
return -EINVAL;
}

+/*
+ * Single Unix Specification V3:
+ *
+ * Implementations shall also support the special clockid_t value
+ * CLOCK_THREAD_CPUTIME_ID, which represents the CPU-time clock of the calling
+ * thread when invoking one of the clock_*() or timer_*() functions. For these
+ * clock IDs, the values returned by clock_gettime() and specified by
+ * clock_settime() shall represent the amount of execution time of the thread
+ * associated with the clock.
+ */
+int do_posix_clock_thread_gettime(struct timespec *tp)
+{
+ jiffies_to_timespec(current->utime + current->stime
+ + current->thread_clock_offset, tp);
+ return 0;
+}
+
+int do_posix_clock_thread_settime(struct timespec *tp)
+{
+ current->thread_clock_offset = timespec_to_jiffies(tp)
+ - current->utime - current->stime;
+ return 0;
+}
+
+/*
+ * Single Unix Specification V3:
+ *
+ * Implementations shall also support the special clockid_t value
+ * CLOCK_PROCESS_CPUTIME_ID, which represents the CPU-time clock of the
+ * calling process when invoking one of the clock_*() or timer_*() functions.
+ * For these clock IDs, the values returned by clock_gettime() and specified
+ * by clock_settime() represent the amount of execution time of the process
+ * associated with the clock.
+ */
+
+unsigned long process_ticks(void) {
+ unsigned long ticks;
+ task_t *t;
+
+ /* The signal structure is shared between all threads */
+ ticks = current->signal->utime + current->signal->stime;
+
+ /* Add up the cpu time for all the still running threads of this process */
+ t = current;
+ do {
+ ticks += t->utime + t->stime;
+ t = next_thread(t);
+ } while (t != current);
+ return ticks;
+}
+
+int do_posix_clock_process_gettime(struct timespec *tp)
+{
+ jiffies_to_timespec(current->signal->process_clock_offset + process_ticks(), tp);
+ return 0;
+}
+
+int do_posix_clock_process_settime(struct timespec *tp)
+{
+ current->signal->process_clock_offset = timespec_to_jiffies(tp) - process_ticks();
+ return 0;
+}
+
asmlinkage long
sys_clock_settime(clockid_t which_clock, const struct timespec __user *tp)
{
Index: linux-2.6.9-rc2/include/linux/sched.h
===================================================================
--- linux-2.6.9-rc2.orig/include/linux/sched.h 2004-09-28 17:20:55.000000000 -0700
+++ linux-2.6.9-rc2/include/linux/sched.h 2004-09-29 11:41:27.000000000 -0700
@@ -293,6 +293,7 @@

/* POSIX.1b Interval Timers */
struct list_head posix_timers;
+ int process_clock_offset; /* for CLOCK_PROCESS_CPUTIME_ID */

/* job control IDs */
pid_t pgrp;
@@ -509,6 +510,7 @@
unsigned long utime, stime;
unsigned long nvcsw, nivcsw; /* context switch counts */
u64 start_time;
+ int thread_clock_offset; /* offset to thread_clock for CLOCK_THREAD_CPUTIME_ID */
/* mm fault and swap info: this can arguably be seen as either mm-specific or thread-specific */
unsigned long min_flt, maj_flt;
/* process credentials */
#include <stdio.h>
#include <stdlib.h>
#include <time.h>
#include <errno.h>
#include <asm/unistd.h>
#include <pthread.h>

#define clock_getres(x,y) syscall(__NR_clock_getres, x,y)
#define clock_gettime(x,y) syscall(__NR_clock_gettime, x, y)
#define clock_settime(x,y) syscall(__NR_clock_settime, x, y)

void pr(int clock,const char *n)
{
struct timespec tv = {1,2};
struct timespec res = {3,4};
int rc;


rc=clock_getres(clock,&res);
if (rc) {
printf("getres return code on %s=%d errno=%d\n",n,rc,errno);
}
rc=clock_gettime(clock,&tv);
if (rc) {
printf("gettime return code on %s=%d errno=%d\n",n,rc, errno);
}
else
printf("%25s=% 11d.%09d resolution=% 2d.%09d\n",n,tv.tv_sec,tv.tv_nsec,res.tv_sec,res.tv_nsec);
}

int y;

void kx(long long x) {
y=x;
};

struct timespec zero;

pthread_t thread[10];

struct tinfo {
int i;
struct timespec ttime,ptime;
} tinf[10];

void *thread_function(void *x) {
struct tinfo *t=x;
int i;

for(i=1;i< t->i;i++) kx(1000000000000LL/i);
clock_gettime(CLOCK_THREAD_CPUTIME_ID,&t->ttime);
clock_gettime(CLOCK_PROCESS_CPUTIME_ID,&t->ptime);
}

int main(char argc, char *argv[])
{
struct timespec tv;
int i;

/* Waste some time */
printf("Single Thread Testing\n");

for(i=1;i<10000000;i++) kx(1000000000000LL/i);
pr(CLOCK_THREAD_CPUTIME_ID,"CLOCK_THREAD_CPUTIME_ID");
pr(CLOCK_PROCESS_CPUTIME_ID,"CLOCK_PROCESS_CPUTIME_ID");
/* Waste some more time in threads */
printf("Multi Thread Testing\nStarting Thread:");
clock_settime(CLOCK_PROCESS_CPUTIME_ID,&zero);
for(i=0;i<10;i++) {
tinf[i].i=i*1000000;
if (pthread_create(&thread[i], NULL, thread_function, tinf+i))
perror("thread");
else
printf(" %d",i);
}
printf("\n Joining Thread:");
for(i=0;i<10;i++) if (pthread_join( thread[i], NULL)) perror("join"); else printf(" %d",i);
printf("\n");
for(i=0;i<10;i++) {
printf("%d Cycles=%7d Thread=% 3d.%09dns Process=% 3d.%09dns\n",i,tinf[i].i,tinf[i].ttime.tv_sec,tinf[i].ttime.tv_nsec,tinf[i].ptime.tv_sec,tinf[i].ptime.tv_nsec);
}
gettimeofday((struct timeval *)&tv);
tv.tv_nsec = tv.tv_nsec*1000;
printf("\nClock status at the end of the timer tests:\n");
printf(" Gettimeofday() =% 11d.%09d\n",tv.tv_sec,tv.tv_nsec);
pr(CLOCK_REALTIME,"CLOCK_REALTIME");
pr(CLOCK_MONOTONIC,"CLOCK_MONOTONIC");
pr(CLOCK_PROCESS_CPUTIME_ID,"CLOCK_PROCESS_CPUTIME_ID");
pr(CLOCK_THREAD_CPUTIME_ID,"CLOCK_THREAD_CPUTIME_ID");
printf("\n");
}