public inbox for linux-kernel@vger.kernel.org
 help / color / mirror / Atom feed
* gettimeofday call
@ 2005-03-26 10:40 Jan Engelhardt
  2005-03-26 15:35 ` Kyle Moffett
  2005-03-27  1:23 ` Chris Wedgwood
  0 siblings, 2 replies; 4+ messages in thread
From: Jan Engelhardt @ 2005-03-26 10:40 UTC (permalink / raw)
  To: Linux Kernel Mailing List

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.

^ permalink raw reply	[flat|nested] 4+ messages in thread

* Re: gettimeofday call
  2005-03-26 10:40 gettimeofday call Jan Engelhardt
@ 2005-03-26 15:35 ` Kyle Moffett
  2005-03-26 17:42   ` Jan Engelhardt
  2005-03-27  1:23 ` Chris Wedgwood
  1 sibling, 1 reply; 4+ messages in thread
From: Kyle Moffett @ 2005-03-26 15:35 UTC (permalink / raw)
  To: Jan Engelhardt; +Cc: Linux Kernel Mailing List

On Mar 26, 2005, at 05:40, Jan Engelhardt wrote:
> 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);
}


^ permalink raw reply	[flat|nested] 4+ messages in thread

* Re: gettimeofday call
  2005-03-26 15:35 ` Kyle Moffett
@ 2005-03-26 17:42   ` Jan Engelhardt
  0 siblings, 0 replies; 4+ messages in thread
From: Jan Engelhardt @ 2005-03-26 17:42 UTC (permalink / raw)
  To: Kyle Moffett; +Cc: Linux Kernel Mailing 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
[...]

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.

^ permalink raw reply	[flat|nested] 4+ messages in thread

* Re: gettimeofday call
  2005-03-26 10:40 gettimeofday call Jan Engelhardt
  2005-03-26 15:35 ` Kyle Moffett
@ 2005-03-27  1:23 ` Chris Wedgwood
  1 sibling, 0 replies; 4+ messages in thread
From: Chris Wedgwood @ 2005-03-27  1:23 UTC (permalink / raw)
  To: Jan Engelhardt; +Cc: Linux Kernel Mailing List

On Sat, Mar 26, 2005 at 11:40:27AM +0100, Jan Engelhardt wrote:

> 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.

^ permalink raw reply	[flat|nested] 4+ messages in thread

end of thread, other threads:[~2005-03-27  1:24 UTC | newest]

Thread overview: 4+ messages (download: mbox.gz follow: Atom feed
-- links below jump to the message on this page --
2005-03-26 10:40 gettimeofday call Jan Engelhardt
2005-03-26 15:35 ` Kyle Moffett
2005-03-26 17:42   ` Jan Engelhardt
2005-03-27  1:23 ` Chris Wedgwood

This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox