pps_capture() and pps_fetch()

From: Sebastian Huber <sebastian.huber_at_embedded-brains.de>
Date: Wed, 01 Jun 2022 07:08:13 UTC
Hello,

I try to understand how the PPS synchronization works in FreeBSD. It 
seems that pps_capture() starts a transaction and pps_event() completes 
the transaction if nothing interfered in the meantime.

void
pps_capture(struct pps_state *pps)
{
	struct timehands *th;

	KASSERT(pps != NULL, ("NULL pps pointer in pps_capture"));
	th = timehands;
	pps->capgen = atomic_load_acq_int(&th->th_generation);
	pps->capth = th;
#ifdef FFCLOCK
	pps->capffth = fftimehands;
#endif
	pps->capcount = th->th_counter->tc_get_timecount(th->th_counter);
	atomic_thread_fence_acq();
	if (pps->capgen != th->th_generation)
		pps->capgen = 0;
}

This function uses the current timehand to capture the time measured by 
the timecounter of the timehand. The timehand generation is used to 
ensure a consistent transaction.

We have

void
pps_event(struct pps_state *pps, int event)
{
[...]
	/* If the timecounter was wound up underneath us, bail out. */
	if (pps->capgen == 0 || pps->capgen !=
	    atomic_load_acq_int(&pps->capth->th_generation))
		return;
[...]
	/*
	 * If the timecounter changed, we cannot compare the count values, so
	 * we have to drop the rest of the PPS-stuff until the next event.
	 */
	if (pps->ppstc != pps->capth->th_counter) {
		pps->ppstc = pps->capth->th_counter;
		*pcount = pps->capcount;
		pps->ppscount[2] = pps->capcount;
		return;
	}

	/* Convert the count to a timespec. */
	tcount = pps->capcount - pps->capth->th_offset_count;
	tcount &= pps->capth->th_counter->tc_counter_mask;
	bt = pps->capth->th_bintime;
	bintime_addx(&bt, pps->capth->th_scale * tcount);
	bintime2timespec(&bt, &ts);

	/* If the timecounter was wound up underneath us, bail out. */
	atomic_thread_fence_acq();
	if (pps->capgen != pps->capth->th_generation)
		return;
[...]
}

Why do we need the

	atomic_thread_fence_acq();
	if (pps->capgen != th->th_generation)
		pps->capgen = 0;

in pps_capture()?

Why do we need the early

	/* If the timecounter was wound up underneath us, bail out. */
	if (pps->capgen == 0 || pps->capgen !=
	    atomic_load_acq_int(&pps->capth->th_generation))
		return;

in pps_event()?

Shouldn't a single

	if (pps->capgen == 0 || pps->capgen !=
	    atomic_load_acq_int(&pps->capth->th_generation))
		return;

after the bintime_addx(&bt, pps->capth->th_scale * tcount); in 
pps_event() be sufficient? The capture timehand may change anytime, so 
why is the generation checked multiple times?

-- 
embedded brains GmbH
Herr Sebastian HUBER
Dornierstr. 4
82178 Puchheim
Germany
email: sebastian.huber@embedded-brains.de
phone: +49-89-18 94 741 - 16
fax:   +49-89-18 94 741 - 08

Registergericht: Amtsgericht München
Registernummer: HRB 157899
Vertretungsberechtigte Geschäftsführer: Peter Rasmussen, Thomas Dörfler
Unsere Datenschutzerklärung finden Sie hier:
https://embedded-brains.de/datenschutzerklaerung/