[OpenAFS-devel] fileserver profiling

Kyle Moffett mrmacman_g4@mac.com
Mon, 7 Mar 2005 22:06:42 -0500


On Mar 07, 2005, at 14:20, Derrick J Brashear wrote:
> The mmap() code presumably needs another thread, and would need to be
> different for lwp. Why have 2 sets of code, and use up another thread?

Err, how about this little C program.  It needs a read_memory_barrier() 
and
write_memory_barrier() definition for each architecture, but other than 
that,
it's pretty portable to most ANSI/POSIX-type systems, and reading it is
lock-free and syscall-free to boot.  You can use it anywhere you can 
get in
an "open(); mmap();" at program start, read memory, and use memory 
barriers,
in other words, basically everywhere.

The code should be pretty self explanatory, so enjoy :-D

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


#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; the read memory barrier does too much 
anyways */
#define read_memory_barrier()	__asm__ __volatile__ ("eieio": : 
:"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);
}