public inbox for linux-kernel@vger.kernel.org
 help / color / mirror / Atom feed
From: Kyle Moffett <mrmacman_g4@mac.com>
To: Jan Engelhardt <jengelh@linux01.gwdg.de>
Cc: Linux Kernel Mailing List <linux-kernel@vger.kernel.org>
Subject: Re: gettimeofday call
Date: Sat, 26 Mar 2005 10:35:56 -0500	[thread overview]
Message-ID: <4de75d4e913f9c7fc93bde5ee6ec57b0@mac.com> (raw)
In-Reply-To: <Pine.LNX.4.61.0503261139490.3958@yvahk01.tjqt.qr>

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);
}


  reply	other threads:[~2005-03-26 15:36 UTC|newest]

Thread overview: 4+ messages / expand[flat|nested]  mbox.gz  Atom feed  top
2005-03-26 10:40 gettimeofday call Jan Engelhardt
2005-03-26 15:35 ` Kyle Moffett [this message]
2005-03-26 17:42   ` Jan Engelhardt
2005-03-27  1:23 ` Chris Wedgwood

Reply instructions:

You may reply publicly to this message via plain-text email
using any one of the following methods:

* Save the following mbox file, import it into your mail client,
  and reply-to-all from there: mbox

  Avoid top-posting and favor interleaved quoting:
  https://en.wikipedia.org/wiki/Posting_style#Interleaved_style

* Reply using the --to, --cc, and --in-reply-to
  switches of git-send-email(1):

  git send-email \
    --in-reply-to=4de75d4e913f9c7fc93bde5ee6ec57b0@mac.com \
    --to=mrmacman_g4@mac.com \
    --cc=jengelh@linux01.gwdg.de \
    --cc=linux-kernel@vger.kernel.org \
    /path/to/YOUR_REPLY

  https://kernel.org/pub/software/scm/git/docs/git-send-email.html

* If your mail client supports setting the In-Reply-To header
  via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line before the message body.
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox