Discussion:
gettimeofday call
(too old to reply)
Jan Engelhardt
2005-03-26 10:43:59 UTC
Permalink
Hello list,

I suppose that calling gettimeofday() repeatedly (to add a timestamp to
some data) within the kernel is cheaper than doing it in userspace, is it?

Jan Engelhardt
--
No TOFU for me, please.
-
To unsubscribe from this list: send the line "unsubscribe linux-kernel" in
the body of a message to ***@vger.kernel.org
More majordomo info at http://vger.kernel.org/majordomo-info.html
Please read the FAQ at http://www.tux.org/lkml/



-------------------------------------------------------------------------------
Achtung: diese Newsgruppe ist eine unidirektional gegatete Mailingliste.
Antworten nur per Mail an die im Reply-To-Header angegebene Adresse.
Fragen zum Gateway -> ***@inka.de.
-------------------------------------------------------------------------------
Kyle Moffett
2005-03-26 15:38:09 UTC
Permalink
Post by Jan Engelhardt
Hello list,
I suppose that calling gettimeofday() repeatedly (to add a timestamp to
some data) within the kernel is cheaper than doing it in userspace, is it?
Well, the following daemon works on most archs that support mmap at only
the cost of a couple read-barriers per gettimeofday(), as opposed to a
whole context switch. OTOH, some archs are getting a vDSO that also
supports a fully-userspace gettimeofday() automatically. libc doesn't
have support code for that, though, and it isn't so portable.

Cheers,
Kyle Moffett

-----BEGIN GEEK CODE BLOCK-----
Version: 3.12
GCM/CS/IT/U d- s++: a18 C++++>$ UB/L/X/*++++(+)>$ P+++(++++)>$
L++++(+++) E W++(+) N+++(++) o? K? w--- O? M++ V? PS+() PE+(-) Y+
PGP+++ t+(+++) 5 X R? tv-(--) b++++(++) DI+ D+ G e->++++$ h!*()>++$ r
!y?(-)
------END GEEK CODE BLOCK------

/*
* NOTE: Make sure you set read_memory_barrier() and
write_memory_barrier()
* properly for your architecture, otherwise this code is not likely to
work
*/

#include <stdarg.h>
#include <stdio.h>
#include <stdlib.h>
#include <errno.h>
#include <string.h>
#include <fcntl.h>
#include <unistd.h>
#include <signal.h>
#include <sys/types.h>
#include <sys/time.h>
#include <sys/mman.h>

static void handler(int signal);

static void usage(const char *arg0, int err, const char *fmt, ...)
__attribute__((__noreturn__));

int main(int argc, char **argv);

enum status_t {
STATUS_RUNNING,
STATUS_STOPPED,
STATUS_RESTART,
};

static enum status_t status = STATUS_STOPPED;
static int last_signal = 0;

static void usage(const char *arg0, int err, const char *fmt, ...) {
va_list ap;
va_start(ap,fmt);
if (fmt) {
if (err) {
char buf[41] = { };
strerror_r(err, buf, 40);
fprintf(stderr,"Error: %s: ",buf);
err = 0;
} else {
fprintf(stderr,"Error: ");
}
vfprintf(stderr,fmt,ap);
fprintf(stderr,"\n");
}
va_end(ap);

if (err) {
char buf[41] = { };
strerror_r(err, buf, 40);
fprintf(stderr,"Error: %s\n",buf);
err = 0;
}

if (!arg0) arg0 = "[" __FILE__ "]";

fprintf(stderr,
"Usage: %s ( -h | <timefile> ) \n"
" -h: Display this help text.\n"
" timefile: The name of the file in which to provide\n"
" shared access to a monotonic time via mmap.\n",
arg0);
exit(1);
}

static void handler (int signal) {
last_signal = signal;
}

/* These are for PPC only, fix for your arch */
#define read_memory_barrier() __asm__ __volatile__ ("sync": : :"memory")
#define write_memory_barrier() __asm__ __volatile__ ("eieio": :
:"memory")

/*
* The "old_time" is the currently stored value. NOTE: This value is
* designed to be read and written locklessly, assuming that reading
* and writing a "long" is atomic on your platform:
* To read:
* do {
* sec1 = time[2];
* read_memory_barrier();
* usec = time[1];
* read_memory_barrier();
* sec2 = time[0];
* read_memory_barrier();
* } while(sec1 != sec2);
* To write:
* time[0] = sec;
* write_memory_barrier();
* time[1] = usec;
* write_memory_barrier();
* time[2] = sec;
*/
#if 0
struct timeval read_time_nolk(volatile long *time) {
struct timeval res;
long lastsecs;

do {
res.tv_sec = time[2];
read_memory_barrier();
res.tv_usec = time[1];
read_memory_barrier();
lastsecs = time[0];
read_memory_barrier();
} while (lastsecs != res.tv_sec);

return res;
}
#endif

static inline void write_time_nolk(volatile long *time, struct timeval
val) {
time[0] = val.tv_sec;
write_memory_barrier();
time[1] = val.tv_usec;
write_memory_barrier();
time[2] = val.tv_sec;
write_memory_barrier();
}

int main(int argc, char **argv) {
int fd; void *mem;
long nulltimebuf[3] = { 0, 0, 0 };

volatile long *oldtime = NULL;
struct timeval newtime = { 0, 0 };
struct timeval zerotime = { 0, 0 };

if (argc <= 0)
usage(NULL,0,"Missing first argument!");

if (argc != 2)
usage(argv[0],0,"Invalid arguments!");

if (strlen(argv[1]) == 0)
usage(argv[0],0,"Invalid argument!");

if (!strcmp(argv[1],"-h"))
usage(argv[0],0,NULL);

/* Trap most signals */
signal(SIGHUP,&handler);
signal(SIGINT,&handler);
signal(SIGQUIT,&handler);
signal(SIGTERM,&handler);

startup:
/* Open the file */
fd = open(argv[1], O_RDWR|O_CREAT|O_EXCL, 0666);
if (fd < 0)
usage(argv[0],errno,"Could not open file: '%s'",argv[1]);

/* Extend the file */
if (0 > write(fd, nulltimebuf, 3*sizeof(long)))
usage(argv[0],errno,"Could not resize file: '%s'",argv[1]);

/* Mmap the file */
mem = mmap(NULL,4096,PROT_READ|PROT_WRITE,MAP_SHARED,fd,0);
if (mem == MAP_FAILED)
usage(argv[0],errno,"Could not map file: '%s'",argv[1]);

oldtime = (volatile long *)mem;
write_time_nolk(oldtime,zerotime);

/* Enter the run loop */
status = STATUS_RUNNING;
while(status == STATUS_RUNNING) {
/* Get a new timestamp */
int err = gettimeofday(&newtime,NULL);
if (err) usage(argv[0],errno,"Could not get the time");

/* Bound the microseconds within 10^6 */
newtime.tv_usec %= 1000000;

/* Check for the time going backwards. Since we're the only
* writer, we can afford to ignore memory barriers here */
if (oldtime[0] > newtime.tv_sec || (
oldtime[0] == newtime.tv_sec &&
oldtime[1] > newtime.tv_usec
)) {
/* Ahh, crud, just spew a warning */
fprintf(stderr,"WARNING: Time regression: "
"%lu seconds, %lu microseconds\n",
oldtime[0] - newtime.tv_sec,
oldtime[1] - newtime.tv_usec);
} else {
/* Ok, the time is fine, so store it in memory */
write_time_nolk(oldtime,newtime);
}

/* Check our signal status, if we've received one, then we *
* need to handle it and restart or clean up and quit. */
switch(last_signal) {
case 0:
/* Since we've got no signal, sleep and repeat */
usleep(1000);
break;

case SIGHUP:
status = STATUS_RESTART;
break;

case SIGQUIT:
case SIGINT:
case SIGTERM:
status = STATUS_STOPPED;
break;

default:
fprintf(stderr,"WARNING: Unknown signal: %d\n",
last_signal);
status = STATUS_STOPPED;
break;
}
}

fprintf(stderr,"Caught signal, cleaning up...\n");

/* First prevent new accesses */
if (unlink(argv[1]))
usage(argv[0],errno,"Could not delete file: '%s'",argv[1]);

/* Now tell listening processes that we're stopped */
write_time_nolk(oldtime,zerotime);

if (munmap(mem,4096))
usage(argv[0],errno,"Could not unmap file: '%s'",argv[1]);

if (close(fd))
usage(argv[0],errno,"Could not close file: '%s'",argv[1]);

if (status == STATUS_RESTART) {
fprintf(stderr,"Restarting...\n");
goto startup;
}

fprintf(stderr,"Quitting...\n");
exit(0);
}

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



-------------------------------------------------------------------------------
Achtung: diese Newsgruppe ist eine unidirektional gegatete Mailingliste.
Antworten nur per Mail an die im Reply-To-Header angegebene Adresse.
Fragen zum Gateway -> ***@inka.de.
-------------------------------------------------------------------------------
Jan Engelhardt
2005-03-26 17:44:11 UTC
Permalink
Post by Kyle Moffett
Post by Jan Engelhardt
I suppose that calling gettimeofday() repeatedly (to add a timestamp to
some data) within the kernel is cheaper than doing it in userspace, is it?
Well, the following daemon works on most archs that support mmap at only
[...]

Ah, it does not need to be that complex.

Just comparing two approaches:

--1--
/* KERNEL: Calling read() on a character device */
static int u_read(...) {
...
gettimeofday(tv);
enqueue_in_buffer(tv);
...
}

+nothing needed in userspace

--2--
/* KERNEL: No gettimeofday */

Userspace:
while(read(fd, buf, sizeof(buf)) {
gettimeofday(&buf.tv);
...
}

(In either case, at some point, userspace has a timestamp.)
I think that -1- is faster it does not require an additional syscall from
userspace to sys_gettimeofday().

Jan Engelhardt
--
No TOFU for me, please.
-
To unsubscribe from this list: send the line "unsubscribe linux-kernel" in
the body of a message to ***@vger.kernel.org
More majordomo info at http://vger.kernel.org/majordomo-info.html
Please read the FAQ at http://www.tux.org/lkml/



-------------------------------------------------------------------------------
Achtung: diese Newsgruppe ist eine unidirektional gegatete Mailingliste.
Antworten nur per Mail an die im Reply-To-Header angegebene Adresse.
Fragen zum Gateway -> ***@inka.de.
-------------------------------------------------------------------------------
Chris Wedgwood
2005-03-27 01:26:45 UTC
Permalink
Post by Jan Engelhardt
I suppose that calling gettimeofday() repeatedly (to add a timestamp
to some data) within the kernel is cheaper than doing it in
userspace, is it?
Calls to do_gettimeofday are used in various places for this already.
See sock_get_timestamp for example.
-
To unsubscribe from this list: send the line "unsubscribe linux-kernel" in
the body of a message to ***@vger.kernel.org
More majordomo info at http://vger.kernel.org/majordomo-info.html
Please read the FAQ at http://www.tux.org/lkml/



-------------------------------------------------------------------------------
Achtung: diese Newsgruppe ist eine unidirektional gegatete Mailingliste.
Antworten nur per Mail an die im Reply-To-Header angegebene Adresse.
Fragen zum Gateway -> ***@inka.de.
-------------------------------------------------------------------------------
Continue reading on narkive:
Loading...