Пример #1
0
/*
 * Called from imagereclaim, to try to release Images.
 * The argument shows the preferred image to release pages from.
 * All images will be tried, from lru to mru.
 */
uint64_t
pagereclaim(Image *i)
{
	Page *p;
	uint64_t ticks;

	lock(&pga.l);
	ticks = fastticks(nil);

	/*
	 * All the pages with images backing them are at the
	 * end of the list (see putpage) so start there and work
	 * backward.
	 */
	for(p = pga.pgsza[0].tail; p && p->image == i; p = p->prev){
		if(p->ref == 0 && canlock(&p->l)){
			if(p->ref == 0) {
				uncachepage(p);
			}
			unlock(&p->l);
		}
	}
	ticks = fastticks(nil) - ticks;
	unlock(&pga.l);

	return ticks;
}
Пример #2
0
static int64_t
tadd(Timers *tt, Timer *nt)
{
	int64_t when;
	Timer *t, **last;

	/* Called with tt locked */
	assert(nt->tt == nil);
	switch(nt->tmode){
	default:
		panic("timer");
		break;
	case Trelative:
		if(nt->tns <= 0)
			nt->tns = 1;
		nt->twhen = fastticks(nil) + ns2fastticks(nt->tns);
		break;
	case Tperiodic:
		/*
		 * Periodic timers must have a period of at least 100µs.
		 */
		assert(nt->tns >= 100000);
		if(nt->twhen == 0){
			/*
			 * Look for another timer at the
			 * same frequency for combining.
			 */
			for(t = tt->head; t; t = t->tnext){
				if(t->tmode == Tperiodic && t->tns == nt->tns)
					break;
			}
			if(t)
				nt->twhen = t->twhen;
			else
				nt->twhen = fastticks(nil);
		}

		/*
		 * The new time must be in the future.
		 * ns2fastticks() can return 0 if the tod clock
		 * has been adjusted by, e.g. timesync.
		 */
		when = ns2fastticks(nt->tns);
		if(when == 0)
			when = 1;
		nt->twhen += when;
		break;
	}

	for(last = &tt->head; t = *last; last = &t->tnext){
		if(t->twhen > nt->twhen)
			break;
	}
	nt->tnext = *last;
	*last = nt;
	nt->tt = tt;
	if(last == &tt->head)
		return nt->twhen;
	return 0;
}
Пример #3
0
ulong
µs(void)
{
	if(SystimerFreq != 1*Mhz)
		return fastticks2us(fastticks(nil));
	return fastticks(nil);
}
Пример #4
0
/*
 * Rendezvous with other cores. Set roles for those that came
 * up online, and wait until they are initialized.
 * Sync TSC with them.
 * We assume other processors that could boot had time to
 * set online to 1 by now.
 */
static void
nixsquids(void)
{
	Mach *mp;
	int i;
	uvlong now, start;

	for(i = 1; i < MACHMAX; i++)
		if((mp = sys->machptr[i]) != nil && mp->online != 0){
			/*
			 * Inter-core calls. A ensure *mp->iccall and mp->icargs
			 * go into different cache lines.
			 */
			mp->icc = mallocalign(sizeof *m->icc, ICCLNSZ, 0, 0);
			mp->icc->fn = nil;
			if(i < initialTCs){
				conf.nmach++;
				mp->nixtype = NIXTC;
			}
			ainc(&active.nbooting);
		}
	sys->epoch = rdtsc();
	mfence();
	wrmsr(0x10, sys->epoch);
	m->rdtsc = rdtsc();
	active.thunderbirdsarego = 1;
	start = fastticks2us(fastticks(nil));
	do{
		now = fastticks2us(fastticks(nil));
	}while(active.nbooting > 0 && now - start < 1000000)
		;
	if(active.nbooting > 0)
		print("cpu0: %d cores couldn't start\n", active.nbooting);
	active.nbooting = 0;
}
Пример #5
0
static int
chanwait(Ep *ep, Ctlr *ctlr, Hostchan *hc, int mask)
{
	int intr, n, ointr;
	ulong start, now;
	Dwcregs *r;

	r = ctlr->regs;
	n = hc - r->hchan;
	for(;;){
restart:
		filock(ctlr);
		r->haintmsk |= 1<<n;
		hc->hcintmsk = mask;
		fiunlock(ctlr);
		tsleep(&ctlr->chanintr[n], chandone, hc, 1000);
		if((intr = hc->hcint) == 0)
			goto restart;
		hc->hcintmsk = 0;
		if(intr & Chhltd)
			return intr;
		start = fastticks(0);
		ointr = intr;
		now = start;
		do{
			intr = hc->hcint;
			if(intr & Chhltd){
				if((ointr != Ack && ointr != (Ack|Xfercomp)) ||
				   intr != (Ack|Chhltd|Xfercomp) ||
				   (now - start) > 60)
					dprint("await %x after %ldµs %x -> %x\n",
						mask, now - start, ointr, intr);
				return intr;
			}
			if((intr & mask) == 0){
				if(intr != Nak)
					dprint("ep%d.%d await %x after %ldµs intr %x -> %x\n",
						ep->dev->nb, ep->nb, mask, now - start, ointr, intr);
				goto restart;
			}
			now = fastticks(0);
		}while(now - start < 100);
		dprint("ep%d.%d halting channel %8.8ux hcchar %8.8ux "
			"grxstsr %8.8ux gnptxsts %8.8ux hptxsts %8.8ux\n",
			ep->dev->nb, ep->nb, intr, hc->hcchar, r->grxstsr,
			r->gnptxsts, r->hptxsts);
		mask = Chhltd;
		hc->hcchar |= Chdis;
		start = m->ticks;
		while(hc->hcchar & Chen){
			if(m->ticks - start >= 100){
				print("ep%d.%d channel won't halt hcchar %8.8ux\n",
					ep->dev->nb, ep->nb, hc->hcchar);
				break;
			}
		}
		logdump(ep);
	}
}
Пример #6
0
static void
imagereclaim(void)
{
	Image *i;
	uvlong ticks0, ticks;

	irstats.calls++;
	/* Somebody is already cleaning the page cache */
	if(!canqlock(&imagealloc.ireclaim))
		return;
	DBG("imagereclaim maxt %ulld noluck %d nolock %d\n",
		irstats.maxt, irstats.noluck, irstats.nolock);
	ticks0 = fastticks(nil);
	if(!canlock(&imagealloc)){
		/* never happen in the experiments I made */
		qunlock(&imagealloc.ireclaim);
		return;
	}

	for(i = imagealloc.lru; i != nil; i = i->prev){
		if(canlock(i)){
			i->ref++;	/* make sure it does not go away */
			unlock(i);
			pagereclaim(i);
			lock(i);
			DBG("imagereclaim: image %p(c%p, r%d)\n", i, i->c, i->ref);
			if(i->ref == 1){	/* no pages referring to it, it's ours */
				unlock(i);
				unlock(&imagealloc);
				putimage(i);
				break;
			}else
				--i->ref;
			unlock(i);
		}
	}

	if(i == nil){
		irstats.noluck++;
		unlock(&imagealloc);
	}
	irstats.loops++;
	ticks = fastticks(nil) - ticks0;
	irstats.ticks += ticks;
	if(ticks > irstats.maxt)
		irstats.maxt = ticks;
	//print("T%llud+", ticks);
	qunlock(&imagealloc.ireclaim);
}
Пример #7
0
void
timerset(uvlong next)
{
	Systimers *tn;
	vlong now, period;

	tn = (Systimers*)SYSTIMERS;
	now = fastticks(nil);
	period = next - fastticks(nil);
	if(period < MinPeriod)
		next = now + MinPeriod;
	else if(period > MaxPeriod)
		next = now + MaxPeriod;
	tn->c3 = (ulong)next;
}
Пример #8
0
/*
 *  Set the time of day struct
 */
void
todset(vlong t, vlong delta, int n)
{
	if(!tod.init)
		todinit();

	ilock(&tod);
	if(t >= 0){
		tod.off = t;
		tod.last = fastticks(nil);
		tod.lasttime = 0;
		tod.delta = 0;
		tod.sstart = tod.send;
	} else {
		if(n <= 0)
			n = 1;
		n *= HZ;
		if(delta < 0 && n > -delta)
			n = -delta;
		if(delta > 0 && n > delta)
			n = delta;
		if (n == 0) {
			iprint("todset: n == 0, delta == %lld\n", delta);
			delta = 0;
		} else
			delta /= n;
		tod.sstart = MACHP(0)->ticks;
		tod.send = tod.sstart + n;
		tod.delta = delta;
	}
	iunlock(&tod);
}
Пример #9
0
ulong
µs(void)
{
	if(SystimerFreq != 1*Mhz)
		return fastticks2us(fastticks(nil));
	return ((Systimers*)SYSTIMERS)->clo;
}
Пример #10
0
/*
 *  called regularly to avoid calculation overflows
 */
static void
todfix(void)
{
	vlong ticks, diff;
	uvlong x;

	ticks = fastticks(nil);
	diff = ticks - tod.last;
	if(diff <= tod.hz)
		return;

	ilock(&tod);
	diff = ticks - tod.last;
	if(diff > tod.hz){
		/* convert to epoch */
		mul64fract(&x, diff, tod.multiplier);
if(x > 30000000000ULL) iprint("todfix %llud\n", x);
		x += tod.off;

		/* protect against overflows */
		tod.last = ticks;
		tod.off = x;
	}
	iunlock(&tod);
}
Пример #11
0
/*
 *  read binary time info.  all numbers are little endian.
 *  ticks and nsec are syncronized.
 */
static int
readbintime(char *buf, int n)
{
	int i;
	vlong nsec, ticks;
	uchar *b = (uchar*)buf;

	i = 0;
	if(fasthz == 0LL)
		fastticks((uvlong*)&fasthz);
	nsec = todget(&ticks);
	if(n >= 3*sizeof(uvlong)){
		vlong2le(b+2*sizeof(uvlong), fasthz);
		i += sizeof(uvlong);
	}
	if(n >= 2*sizeof(uvlong)){
		vlong2le(b+sizeof(uvlong), ticks);
		i += sizeof(uvlong);
	}
	if(n >= 8){
		vlong2le(b, nsec);
		i += sizeof(vlong);
	}
	return i;
}
Пример #12
0
/*
 *  read binary time info.  all numbers are little endian.
 *  ticks and nsec are syncronized.
 */
static int
readbintime(char *buf, int n)
{
	int i;
	int64_t nsec, ticks;
	uint8_t *b = (uint8_t*)buf;

	i = 0;
	if(fasthz == 0LL)
		fastticks((uint64_t*)&fasthz);
	nsec = todget(&ticks);
	if(n >= 3*sizeof(uint64_t)){
		int64_t2le(b+2*sizeof(uint64_t), fasthz);
		i += sizeof(uint64_t);
	}
	if(n >= 2*sizeof(uint64_t)){
		int64_t2le(b+sizeof(uint64_t), ticks);
		i += sizeof(uint64_t);
	}
	if(n >= 8){
		int64_t2le(b, nsec);
		i += sizeof(int64_t);
	}
	return i;
}
Пример #13
0
/*
 *  Set the time of day struct
 */
void
todset(int64_t t, int64_t delta, int n)
{
	if(!tod.init)
		todinit();

	ilock(&tod.Lock);
	if(t >= 0){
		tod.off = t;
		tod.last = fastticks(nil);
		tod.lasttime = 0;
		tod.delta = 0;
		tod.sstart = tod.send;
	} else {
		if(n <= 0)
			n = 1;
		n *= HZ;
		if(delta < 0 && n > -delta)
			n = -delta;
		if(delta > 0 && n > delta)
			n = delta;
		delta = delta/n;
		tod.sstart = sys->ticks;
		tod.send = tod.sstart + n;
		tod.delta = delta;
	}
	iunlock(&tod.Lock);
}
Пример #14
0
void
acsyscall(void)
{
	panic("acsyscall");
#if 0
	Proc *p;

	/*
	 * If we saved the Ureg into m->proc->dbgregs,
	 * There's nothing else we have to do.
	 * Otherwise, we should m->proc->dbgregs = u;
	 */
	DBG("acsyscall: cpu%d\n", machp()->machno);

	_pmcupdate(m);
	p = m->proc;
	p->actime1 = fastticks(nil);
	m->syscall++;	/* would also count it in the TS core */
	m->icc->rc = ICCSYSCALL;
	m->cr2 = cr2get();
	fpuprocsave(p);
	_pmcupdate(m);
	mfence();
	m->icc->fn = nil;
	ready(p);
	/*
	 * The next call is probably going to make us jmp
	 * into user code, forgetting all our state in this
	 * stack, upon the next syscall.
	 * We don't nest calls in the current stack for too long.
	 */
	acsched();
#endif
}
Пример #15
0
/*
 *  read binary time info.  all numbers are little endian.
 *  ticks and nsec are syncronized.
 */
static int readbintime(char *buf, int n)
{
    int i;
    int64_t nsec, ticks;
    uint8_t *b = (uint8_t *) buf;

    i = 0;
    if (fasthz == 0LL)
        fasthz = system_timing.tsc_freq;
#if 0
    fastticks((uint64_t *) & fasthz);
    nsec = todget(&ticks);
#endif
    ticks = read_tsc();
    nsec = tsc2nsec(ticks);
    if (n >= 3 * sizeof(uint64_t)) {
        int64_t2le(b + 2 * sizeof(uint64_t), fasthz);
        i += sizeof(uint64_t);
    }
    if (n >= 2 * sizeof(uint64_t)) {
        int64_t2le(b + sizeof(uint64_t), ticks);
        i += sizeof(uint64_t);
    }
    if (n >= 8) {
        int64_t2le(b, nsec);
        i += sizeof(int64_t);
    }
    return i;
}
Пример #16
0
static vlong
tadd(Timers *tt, Timer *nt)
{
	Timer *t, **last;

	/* Called with tt locked */
	assert(nt->tt == nil);
	switch(nt->tmode){
	default:
		panic("timer");
		break;
	case Trelative:
		if(nt->tns <= 0)
			nt->tns = 1;
		nt->twhen = fastticks(nil) + ns2fastticks(nt->tns);
		break;
	case Tperiodic:
		assert(nt->tns >= 100000);	/* At least 100 µs period */
		if(nt->twhen == 0){
			/* look for another timer at same frequency for combining */
			for(t = tt->head; t; t = t->tnext){
				if(t->tmode == Tperiodic && t->tns == nt->tns)
					break;
			}
			if (t)
				nt->twhen = t->twhen;
			else
				nt->twhen = fastticks(nil);
		}
		nt->twhen += ns2fastticks(nt->tns);
		break;
	}

	for(last = &tt->head; t = *last; last = &t->tnext){
		if(t->twhen > nt->twhen)
			break;
	}
	nt->tnext = *last;
	*last = nt;
	nt->tt = tt;
	if(last == &tt->head)
		return nt->twhen;
	return 0;
}
Пример #17
0
Файл: time.c Проект: 0intro/vx32
void
timerkproc(void *v)
{
	sigset_t sigs;
	Timers *tt;
	Timer *t;
	uvlong when, now;
	struct timespec ts;
	Ureg u;
	int signo;
	
	memset(&u, 0, sizeof u);
	timer_pid = pthread_self();
	
	tt = &timers;
	ilock(&tt->lk);
	for(;;){
		if((t = tt->head) == nil){
			iunlock(&tt->lk);
			sigemptyset(&sigs);
			sigaddset(&sigs, SIGURG);
			sigwait(&sigs, &signo);
			ilock(&tt->lk);
			continue;
		}
		/*
		 * No need to ilock t here: any manipulation of t
		 * requires tdel(t) and this must be done with a
		 * lock to tt held.  We have tt, so the tdel will
		 * wait until we're done
		 */
		now = fastticks(nil);
		when = t->twhen;
		if(when > now){
			iunlock(&tt->lk);
			when -= now;
			ts.tv_sec = when/1000000000;
			ts.tv_nsec = when%1000000000;
			pthread_sigmask(SIG_SETMASK, nil, &sigs);
			sigdelset(&sigs, SIGURG);
			pselect(0, nil, nil, nil, &ts, &sigs);
			ilock(&tt->lk);
			continue;
		}
		
		tt->head = t->tnext;
		assert(t->tt == tt);
		t->tt = nil;
		iunlock(&tt->lk);
		(*t->tf)(&u, t);
		ilock(&tt->lk);
		if(t->tmode == Tperiodic)
			tadd(tt, t);
	}			
}
Пример #18
0
/*
 * Called in AP core context, to return from system call.
 */
void
acsysret(void)
{
panic("acsysret");
#if 0
	DBG("acsysret\n");
	if(m->proc != nil)
		m->proc->actime += fastticks2us(fastticks(nil) - m->proc->actime1);
	_acsysret();
#endif
}
Пример #19
0
void
randominit(void)
{
#ifdef USE_RANDOM
	srandom(getpid()+fastticks(nil)+ticks());
#else
	if((randfd = open("/dev/urandom", OREAD)) < 0)
	if((randfd = open("/dev/random", OREAD)) < 0)
		panic("open /dev/random: %r");
#endif
}
Пример #20
0
void
todinit(void)
{
	if(tod.init)
		return;
	ilock(&tod);
	tod.init = 1;			/* prevent reentry via fastticks */
	tod.last = fastticks((uvlong *)&tod.hz);
	iunlock(&tod);
	todsetfreq(tod.hz);
	addclock0link(todfix, 100);
}
Пример #21
0
void
todinit(void)
{
	if(tod.init)
		return;
	ilock(&tod.Lock);
	tod.last = fastticks((uint64_t *)&tod.hz);
	iunlock(&tod.Lock);
	todsetfreq(tod.hz);
	tod.init = 1;
	addclock0link(todfix, 100);
}
Пример #22
0
/*
 * Rendezvous with other cores. Set roles for those that came
 * up online, and wait until they are initialized.
 * Sync TSC with them.
 * We assume other processors that could boot had time to
 * set online to 1 by now.
 */
static void
nixsquids(void)
{
	Mach *m = machp();
	Mach *mp;
	int i;
	uint64_t now, start;

	/* Not AC for now :-) */
	for(i = 1; i <= MACHMAX; i++)
	//for(i = 1; i < MACHMAX; i++)
		if((mp = sys->machptr[i]) != nil && mp->online){
			/*
			 * Inter-core calls. A ensure *mp->iccall and mp->icargs
			 * go into different cache lines.
			 */
			mp->icc = mallocalign(sizeof *m->icc, ICCLNSZ, 0, 0);
			mp->icc->fn = nil;
			if(i < numtcs){
				sys->nmach++;
				mp->nixtype = NIXTC;
				sys->nc[NIXTC]++;
			}//else
				//sys->nc[NIXAC]++;
			ainc(&active.nbooting);
		}
	sys->epoch = rdtsc();
	mfence();
	wrmsr(0x10, sys->epoch);
	m->rdtsc = rdtsc();
	active.thunderbirdsarego = 1;
	start = fastticks2us(fastticks(nil));
	do{
		now = fastticks2us(fastticks(nil));
	}while(active.nbooting > 0 && now - start < 1000000)
		;
	if(active.nbooting > 0)
		print("cpu0: %d cores couldn't start\n", active.nbooting);
	active.nbooting = 0;
}
Пример #23
0
void
timerintr(Ureg *u, Tval)
{
	Timer *t;
	Timers *tt;
	uvlong when, now;
	int count, callhzclock;

	intrcount[m->machno]++;
	callhzclock = 0;
	tt = &timers[m->machno];
	now = fastticks(nil);
	if(now == 0)
		panic("timerintr: zero fastticks()");
	ilock(tt);
	count = Maxtimerloops;
	while((t = tt->head) != nil){
		/*
		 * No need to ilock t here: any manipulation of t
		 * requires tdel(t) and this must be done with a
		 * lock to tt held.  We have tt, so the tdel will
		 * wait until we're done
		 */
		when = t->twhen;
		if(when > now){
			timerset(when);
			iunlock(tt);
			if(callhzclock)
				hzclock(u);
			return;
		}
		tt->head = t->tnext;
		assert(t->tt == tt);
		t->tt = nil;
		fcallcount[m->machno]++;
		iunlock(tt);
		if(t->tf)
			(*t->tf)(u, t);
		else
			callhzclock++;
		ilock(tt);
		if(t->tmode == Tperiodic)
			tadd(tt, t);
		if (--count <= 0) {
			count = Maxtimerloops;
			iprint("timerintr: probably stuck in while loop; "
				"scrutinise clock.c or use faster cycle "
				"counter\n");
		}
	}
	iunlock(tt);
}
Пример #24
0
static void
imagereclaim(void)
{
	int n;
	Page *p;
	uvlong ticks;

	irstats.calls++;
	/* Somebody is already cleaning the page cache */
	if(!canqlock(&imagealloc.ireclaim))
		return;

	lock(&palloc);
	ticks = fastticks(nil);
	n = 0;
	/*
	 * All the pages with images backing them are at the
	 * end of the list (see putpage) so start there and work
	 * backward.
	 */
	for(p = palloc.tail; p && p->image && (n<1000 || !imagealloc.free); p = p->prev) {
		if(p->ref == 0 && canlock(p)) {
			if(p->ref == 0 && p->image && !p->image->notext) {
				n++;
				uncachepage(p);
			}
			unlock(p);
		}
	}
	ticks = fastticks(nil) - ticks;
	unlock(&palloc);
	irstats.loops++;
	irstats.ticks += ticks;
	if(ticks > irstats.maxt)
		irstats.maxt = ticks;
	//print("T%llud+", ticks);
	qunlock(&imagealloc.ireclaim);
}
Пример #25
0
/*
 *  get time of day
 */
vlong
todget(vlong *ticksp)
{
	uvlong x;
	vlong ticks, diff;
	ulong t;

	if(!tod.init)
		todinit();

	/*
	 * we don't want time to pass twixt the measuring of fastticks
	 * and grabbing tod.last.  Also none of the vlongs are atomic so
	 * we have to look at them inside the lock.
	 */
	ilock(&tod);
	tod.cnt++;
	ticks = fastticks(nil);

	/* add in correction */
	if(tod.sstart != tod.send){
		t = MACHP(0)->ticks;
		if(t >= tod.send)
			t = tod.send;
		tod.off = tod.off + tod.delta*(t - tod.sstart);
		tod.sstart = t;
	}

	/* convert to epoch */
	diff = ticks - tod.last;
	if(diff < 0)
		diff = 0;
	mul64fract(&x, diff, tod.multiplier);
	x += tod.off;

	/* time can't go backwards */
	if(x < tod.lasttime)
		x = tod.lasttime;
	else
		tod.lasttime = x;

	iunlock(&tod);

	if(ticksp != nil)
		*ticksp = ticks;

	return x;
}
Пример #26
0
/* go to user space */
void
kexit(Ureg*)
{
	uvlong t;
	Tos *tos;

	/* precise time accounting, kernel exit */
	tos = (Tos*)(USTKTOP-sizeof(Tos));
	t = fastticks(nil);
	tos->kcycles += t - up->kentry;
	tos->pcycles = up->pcycles;
	tos->cyclefreq = Frequency;
	tos->pid = up->pid;

	/* make visible immediately to user proc */
	cachedwbinvse(tos, sizeof *tos);
}
Пример #27
0
void
timerset(Tval next)
{
	long offset;
	Timerregs *tn = (Timerregs *)Tn1;
	static Lock setlck;

	ilock(&setlck);
	offset = next - fastticks(nil);
	if(offset < MinPeriod)
		offset = MinPeriod;
	else if(offset > MaxPeriod)
		offset = MaxPeriod;
	tn->tcrr = -offset;
	coherence();
	iunlock(&setlck);
}
Пример #28
0
/*
 * Tval is supposed to be in fastticks units.
 * One fasttick unit is 1/Basetickfreq seconds.
 */
void
timerset(Tval next)
{
	int x;
	long period;

	if(next == 0)
		return;
	x = splhi();			/* don't let us get scheduled */
	period = next - fastticks(nil);
	if(period > m->maxperiod - m->minperiod)
		period = m->maxperiod;
	else if(period < m->minperiod)
		period = m->minperiod;
	wrcompare(rdcount()+period);
	splx(x);
}
Пример #29
0
/*
 *  like the old #c/time but with added info.  Return
 *
 *	secs	nanosecs	fastticks	fasthz
 */
static int
readtime(uint32_t off, char *buf, int n)
{
	int64_t nsec, ticks;
	int32_t sec;
	char str[7*NUMSIZE];

	nsec = todget(&ticks);
	if(fasthz == 0LL)
		fastticks((uint64_t*)&fasthz);
	sec = nsec/1000000000ULL;
	snprint(str, sizeof(str), "%*lu %*llu %*llu %*llu ",
		NUMSIZE-1, sec,
		VLNUMSIZE-1, nsec,
		VLNUMSIZE-1, ticks,
		VLNUMSIZE-1, fasthz);
	return readstr(off, buf, n, str);
}
Пример #30
0
/*
 *  like the old #c/time but with added info.  Return
 *
 *	secs	nanosecs	fastticks	fasthz
 */
static int
readtime(ulong off, char *buf, int n)
{
	vlong	nsec, ticks;
	long sec;
	char str[7*NUMSIZE];

	nsec = todget(&ticks);
	if(fasthz == 0LL)
		fastticks((uvlong*)&fasthz);
	sec = nsec/1000000000ULL;
	snprint(str, sizeof(str), "%*lud %*llud %*llud %*llud ",
		NUMSIZE-1, sec,
		VLNUMSIZE-1, nsec,
		VLNUMSIZE-1, ticks,
		VLNUMSIZE-1, fasthz);
	return readstr(off, buf, n, str);
}