For a TSN-aware communication protocol stack and framework, I need to be able to execute real-time threads not only running at very exact intervals, but starting at precise timeslots deducted from ptpd.
Currently, I did that by delaying / yielding the task until the start time. For a PoC of my TSN communication this was sufficient, but with the Linux real-time extensions, there must be a better / more effective way. The jitter introduced was about 800us between two Beaglebones (running Linux 4.14.71-ti-r80 #1 SMP PREEMPT).
Using a cyclic pthread, I synchronised the start of my network producer task (Note: vos_ prefix denotes my OS abstraction methods, actually matching POSIX calls):
for (;; )
{
/* Synchronize with starttime */
vos_getTime(&now); /* get initial time */
vos_subTime(&now, &startTime);
/* Wait for multiples of interval */
execTime = ((UINT32)now.tv_usec % interval);
waitingTime = interval - execTime;
if (waitingTime >= interval)
{
...error checks omitted
}
/* Idle for the difference */
vos_threadDelay(waitingTime); /* pthread_testcancel() inside */
vos_getTime(&priorCall); /* get initial time */
pFunction(pArguments); /* thread function: send data */
vos_getTime(&afterCall); /* get time after function has returned */
/* get the runtime of pFunction() */
vos_subTime(&afterCall, &priorCall);
/* afterCall holds now the time difference... */
if (afterCall.tv_sec <= MAXSEC_FOR_USECPRESENTATION)
{
...runtime / error handling omitted
}
}
The above code produces a jitter of about 800us at interval times of 5ms (using Linux 4.14 without RT-extension and policy Round-Robin), which is fairly good - but ineffective when it comes to multiple threads all doing scheduled traffic...
With Linux 4.1.15-ti-rt-r43 #1 SMP PREEMPT RT, I'm planning to use SCHED_DEADLINE as policy, but struct sched_attr provides no start-time-parameter. The runtime-checking in the above code is not needed, then. But:
How can I effectively use Linux's real-time extensions to have a thread running at exact absolute times, say for example at every 1ms starting at absolute real time 'now' + 0.0075s (e.g. now.0085, now.0095, now.0105 ...), without the overhead of the above code?
The clock_nanosleep()
POSIX function has an absolute deadline mode:
#define NSEC_PER_SEC 1000000000
/* Initial delay, 7.5ms */
const long start_delay_ns = 7500000;
/* Cycle time, 1ms */
const long cycle_time_ns = 1000000;
struct timespec deadline;
clock_gettime(CLOCK_MONOTONIC, &deadline);
deadline.tv_nsec += start_delay_ns;
deadline.tv_sec += deadline.tv_nsec / NSEC_PER_SEC;
deadline.tv_nsec %= NSEC_PER_SEC;
for (;;)
{
struct timespec now;
/* Sleep until deadline */
while (clock_nanosleep(CLOCK_MONOTONIC, TIMER_ABSTIME, &deadline, NULL) != 0)
if (errno != EINTR)
return; /* error handling here */
pFunction(pArguments); /* thread function: send data */
/* Calculate next deadline */
deadline.tv_nsec += cycle_time_ns;
deadline.tv_sec += deadline.tv_nsec / NSEC_PER_SEC;
deadline.tv_nsec %= NSEC_PER_SEC;
clock_gettime(CLOCK_MONOTONIC, &now);
if (now.tv_sec > deadline.tv_sec || (now.tv_sec == deadline.tv_sec && deadline.tv_nsec > now.tv_nsec))
return; /* time overrun error handling here */
}