SPI and realtime

Hello eyeryone,

I’m actually porting a bare-metal-realtime-application to linux.
That application needs to exchange 256 bytes of data with slave processors via spi every 500us.
On an oscilloscope I see that the actual transfer (the clock-signal) takes about 308us while the ioctl()-call takes about 435us.
Because of that I do the ioctl() in a separate realtime-thread of high priority.
To see the begin and end of the ioctl()-call my thread switches a gpio-pin immediately before and after the ioctl():

In the oscillogram the blue signal is the clock of the spi while the pink one is the gpio indicating the start and end of the ioctl().

Most of the time everything works fine, but in one of a thousand times the ioctl() takes more than 500us breaking my realtime.

I found a 2 1/2 years old thread about a similar issue. Like described in the post by Garyio I turned the “message pump” into a realtime thread.

diff -c spi-imx.c.orig spi-imx.c
*** spi-imx.c.orig	2018-07-10 11:52:06.579519394 +0200
--- spi-imx.c	2018-10-16 15:47:30.807011186 +0200
*** 1192,1197 ****
--- 1192,1198 ----
  	master->bits_per_word_mask = SPI_BPW_RANGE_MASK(1, 32);
  	master->bus_num = np ? -1 : pdev->id;
+ 	master->rt = 1; // DM 16.10.2018
  	spi_imx = spi_master_get_devdata(master);
  	spi_imx->bitbang.master = master;

After this with top I can see the “message pump” running as realtime thread:

top - 17:14:27 up  1:14,  2 users,  load average: 1.06, 1.04, 1.00
Tasks: 119 total,   1 running, 118 sleeping,   0 stopped,   0 zombie
%Cpu(s):  0.6 us,  6.4 sy,  0.0 ni, 93.1 id,  0.0 wa,  0.0 hi,  0.0 si,  0.0 st
KiB Mem :   251580 total,   187476 free,    17904 used,    46200 buff/cache
KiB Swap:        0 total,        0 free,        0 used.   227320 avail Mem 

  PID USER      PR  NI    VIRT    RES    SHR S %CPU %MEM     TIME+ COMMAND     
  614 root      20   0   29156   1456   1288 S 32.1  0.6  13:59.36 asx-linux   
   76 root     -51   0       0      0      0 S  6.5  0.0   3:05.27 irq/64-sdma 
  265 root      rt   0       0      0      0 S  4.5  0.0   2:05.07 spi0        
  612 root      20   0    4428   2072   1716 R  1.3  0.8   0:34.66 top         

It now looks somewhat better but still in 0.4% of the cases it takes more than 450us giving me too little time to handle the received data and to initiate the next transfer.

Is there something like a realtime-patch for the spi-driver or any way to shorten the time-gap (90us in best cases) between the end of the actual transfer (clock-signal) and the return of the ioctl()-call?


In the thread you’ve referenced, worst case latency was almost 200us (and on a bit faster CPU).
There a few things you can do. First make sure that cpu frequency scaling is off and clock is at max.
Go through /proc/interrupts to see if you can see drivers generating interrupts on subsystems you’re not using in you’re application. I assume you’re using spidev, transfer ioctls in spidev are not optimal in the RT case(they involve kmalloc and copy_to\from_user). You can use mmap to share memory with the data and ioctl only to trigger transfer, this will decrease time needed (no copying) and remove possibility of page fault occurring. Those changes may buy you enough time and decreased latency to fit under 500us.