Engineer Shares: CPU Control in Embedded Linux Systems

01Test EnvironmentEngineer Shares: CPU Control in Embedded Linux Systems

Xilinx ZCU106 Single BoardXilinx VCU TRD2020.1

02IntroductionEngineer Shares: CPU Control in Embedded Linux Systems

In embedded Linux systems, Linux directly manages all CPUs. By default, the system aims to maximize throughput rather than real-time performance. To ensure real-time performance, more precise control over the CPU can be implemented based on the application scenario. Common methods include process CPU isolation, CPU affinity, interrupt CPU affinity, and process priority.

03ToolsEngineer Shares: CPU Control in Embedded Linux Systems

In embedded Linux systems, tools such as ps and top from busybox are commonly used. They are lightweight but have limited functionality. For more powerful tools, they can be extracted from the Ubuntu filesystem ubuntu-base-20.04.1-base-arm64.tar.gz (Click “Read the original text” to view the link).

In this test, tools like ps and top were extracted from the Ubuntu filesystem and renamed to u-ps and u-top to distinguish them from the busybox versions.

04CPU IsolationEngineer Shares: CPU Control in Embedded Linux Systems

Linux may still schedule a process on any CPU, which can lead to ordinary processes affecting the performance of real-time processes. The Linux kernel command line parameter isolcpus can be used to achieve CPU isolation, completely preventing Linux from scheduling processes on certain CPUs, thus ensuring the response time of real-time processes.

In U-Boot, executing the following command can prevent Linux from scheduling processes on CPU2 and CPU3.

setenv bootargs "earlycon clk_ignore_unused consoleblank=0 cma=1700M uio_pdrv_genirq.of_id=generic-uio isolcpus=2,3"

After Linux starts, you can check the Linux kernel command line parameters using the command “cat /proc/cmdline”.

root@vcu_trd:~# cat /proc/cmdline
earlycon clk_ignore_unused consoleblank=0 cma=1700M uio_pdrv_genirq.of_id=generic-uio isolcpus=2,3

Then, using the ps tool from the Ubuntu filesystem with the psr option, you can view the CPU on which all system processes are running. The second column of the output indicates the CPU number. It can be seen that most processes run on CPU0 and CPU1. The processes running on CPU2 and CPU3 are those that have their CPU affinity set via taskset. The u-ps tool is from the package [ubuntu-base-20.04.1-base-arm64.tar.gz].

root@vcu_trd:~# u-ps -axo pid,psr,cmd,ni
  PID PSR CMD                          NI
    1   1 init                          0
    2   1 [kthreadd]                    0
    3   0 [rcu_gp]                    -20
    4   0 [rcu_par_gp]                -20
    6   0 [kworker/0:0H-kblockd]      -20
    8   0 [mm_percpu_wq]              -20
    9   0 [ksoftirqd/0]                 0
   10   1 [rcu_sched]                   0
   11   0 [migration/0]                 -
   12   0 [cpuhp/0]                     0
   13   1 [cpuhp/1]                     0
   14   1 [migration/1]                 -
   15   1 [ksoftirqd/1]                 0
   17   1 [kworker/1:0H-kblockd]      -20
   18   2 [cpuhp/2]                     0
   19   2 [migration/2]                 -
   20   2 [ksoftirqd/2]                 0
   21   2 [kworker/2:0-events]          0
   22   2 [kworker/2:0H]              -20
   23   3 [cpuhp/3]                     0
   24   3 [migration/3]                 -
   25   3 [ksoftirqd/3]                 0
   26   3 [kworker/3:0-events]          0
   27   3 [kworker/3:0H]              -20
   28   0 [kdevtmpfs]                   0
   29   1 [netns]                     -20
   30   1 [kauditd]                     0
   32   1 [oom_reaper]                  0
   33   0 [writeback]                 -20
   34   1 [kcompactd0]                  0
   35   1 [khugepaged]                 19
   37   0 [kworker/0:1-events]          0
   38   1 [kworker/u8:1-events_unboun   0
   87   1 [kblockd]                   -20
   88   0 [blkcg_punt_bio]            -20
   89   0 [edac-poller]               -20
   90   1 [watchdogd]                   -
   91   1 [rpciod]                    -20
   92   0 [kworker/u9:0]              -20
   93   1 [xprtiod]                   -20
   94   1 [cfg80211]                  -20
   95   1 [kswapd0]                     0
   96   1 [ecryptfs-kthrea]             0
   97   0 [nfsiod]                    -20
  100   0 [irq/60-a00d0000]             -
  107   1 [ion_system_heap]             -
  108   2 [irq/61-a00d1000]             -
  109   0 [kpktgend_0]                  0
  110   1 [kpktgend_1]                  0
  111   2 [kpktgend_2]                  0
  112   3 [kpktgend_3]                  0
  113   1 [ipv6_addrconf]             -20
  114   0 [krfcommd]                  -10
  115   2 [kworker/2:1-events]          0
  116   3 [kworker/3:1-events]          0
  117   0 [kworker/0:2-events_power_e   0
  118   0 [irq/47-fd4a0000]             -
  120   1 [scsi_eh_0]                   0
  121   1 [scsi_tmf_0]                -20
  122   1 [scsi_eh_1]                   0
  123   1 [scsi_tmf_1]                -20
  125   1 [spi0]                        0
  128   1 [sdhci]                     -20
  129   0 [irq/41-mmc0]                 -
  134   0 [mmc_complete]              -20
  135   0 [kworker/0:1H-mmc_complete] -20
  165   1 [kworker/1:1H-kblockd]      -20
  181   0 /sbin/udevd -d                0
  231   1 [irq/63-xilinx-v]             -
  238   1 [xilinx-hdmi-rx]            -20
  242   1 [irq/54-xilinx-h]             -
  246   1 [irq/52-xilinx-h]             -
  419   0 [irq/62-a0220000]             -
  420   2 [irq/62-a0200000]             -
  808   0 udhcpc -R -b -p /var/run/ud   0
  815   1 /usr/bin/dbus-daemon --syst   0
  818   0 /usr/sbin/haveged -w 1024 -   0
  827   1 xinit /etc/X11/Xsession --    0
  831   0 /usr/bin/Xorg :0 -br -pn     -1
  836   1 matchbox-window-manager -th   0
  841   1 dbus-launch --sh-syntax --e   0
  842   1 /usr/bin/dbus-daemon --sysl   0
  862   0 /usr/sbin/dropbear -r /etc/   0
  864   0 /usr/libexec/at-spi-bus-lau   0
  874   0 /usr/libexec/gconfd-2         0
  882   0 /usr/bin/dbus-daemon --conf   0
  885   1 /usr/sbin/inetd               0
  888   0 /usr/bin/settings-daemon      0
  898   0 /sbin/syslogd -n -O /var/lo   0
  901   0 /sbin/klogd -n                0
  910   0 matchbox-desktop              0
  911   0 matchbox-panel --start-appl   0
  921   0 /usr/sbin/tcf-agent -d -L-    0
  929   0 /bin/sh /bin/start_getty 11   0
  930   0 /sbin/getty 38400 tty1        0
  934   1 /usr/libexec/at-spi2-regist   0
  940   1 /usr/sbin/console-kit-daemo   0
 1025   0 /bin/login --                 0
 1050   1 -sh                           0
 1055   0 /usr/sbin/dropbear -r /etc/   0
 1057   0 -sh                           0
 1063   0 /usr/sbin/dropbear -r /etc/   0
 1065   0 -sh                           0
 1071   0 top                           0
 7174   0 /usr/sbin/dropbear -r /etc/   0
 7176   0 -sh                           0
22378   0 /usr/sbin/dropbear -r /etc/   0
22380   0 -sh                           0
22588   0 /usr/sbin/dropbear -r /etc/   0
22590   0 -sh                           0
22600   1 [kworker/1:0-events]          0
22601   1 [kworker/u8:0-events_unboun   0
22602   1 [kworker/1:2-events_power_e   0
22603   1 [kworker/u8:2-events_unboun   0
22606   0 u-ps -axo pid,psr,cmd,ni      0

05Process CPU AffinityEngineer Shares: CPU Control in Embedded Linux Systems

When setting process CPU affinity, it is necessary to know the process ID (PID). Tools like ps and top can be used to view the process ID (PID). The taskset tool can be used to view and control the CPU affinity of processes. By using the ‘-p’ option, you can specify the process ID (PID) to view the corresponding process’s CPU affinity.

root@vcu_trd:~# taskset -p 815
pid 815's current affinity mask: 1

The following script can check the CPU affinity of all processes.

#!/bin/sh

u-ps -axo pid,psr,cmd,ni | grep -v "gst"  | grep -v "xilinx" | grep -v "irq" | grep -v "kworker" | grep -v "grep" | grep -v "awk" | awk '{print $1}' > process_list.txt

echo -e "\nRead process list file:" ;
cat process_list.txt | while read line
do
    # echo "CPU affinity for process ID: $line"
taskset -p $line
done

In Linux systems with a large number of processes, the following script can be used to set the CPU affinity for all processes.

#!/bin/sh

u-ps -axo pid,psr,cmd,ni | grep -v "grep" | grep -v "awk" | awk '{print $1}' > process_list.txt

cat process_list.txt | while read line
do
   	echo -e "\nCheck process ID: $line"
   
	if [ $line -gt 500 ]; then
		
		# echo "Original CPU affinity for process ID: $line"
		# taskset -p $line    
		
		echo "Set priority for process ID: $line"
		taskset -a -p 1 $line    
		
		# echo "New CPU affinity for process ID: $line"
		# taskset -p $line 
		
	fi

done

For new tasks, you can specify the process CPU affinity at startup. The help information for taskset is as follows:

taskset [options] [mask | cpu-list] [pid|cmd [args...]]

To specify the process CPU affinity, you can use the following command to start a new task.

taskset -a cpu-list  cmd

For example, executing the command “taskset -a 8 top” will show that it indeed runs on CPU-3.

root@vcu_trd:~# u-ps -axo pid,psr,cmd,ni  | grep top | grep -v grep | grep -v match
22629   3 top

06Interrupt CPU AffinityEngineer Shares: CPU Control in Embedded Linux Systems

By default, Linux uses CPU0 to handle interrupts from ordinary peripherals. By changing /proc/irq/irq_number/smp_affinity, you can change the CPU that handles the interrupts. You can also view /proc/interrupts to display the number of interrupts handled by each CPU in the system.

There are also many interrupts in Linux systems, and the following script can be used to set the CPU affinity for all interrupts. The mapping of interrupts to CPUs can be changed based on the scenario.

#!/bin/sh

cat /proc/interrupts > interrupts_list_all.txt
cat /proc/interrupts  | grep -v "CPU"  | grep -v "IPI" | grep -v "Err" | awk '{print $1}'  > interrupts_list.txt

echo -e "\nRead interrupts list file:" ;
cat interrupts_list.txt | while read line
do
	# remove colon :
    line_new=${line/:/} 
   
	echo -e "\nCheck interrupt: $line_new"
	ls -l -h /proc/irq/$line_new/smp_affinity 

    # 48:  GICv2 122 Level     xilinx_framebuffer
    # 52:  GICv2 123 Level     xilinx-hdmi-rx
    # 54:  GICv2 125 Level     xilinx-hdmitxss
    # 55:  GICv2 127 Level     xlnx-mixer
    # 61:  GICv2 139 Level     a00d1000.sync_ip
    # 62:  GICv2 128 Level     a0200000.al5e, a0220000.al5d
    # 63:  GICv2 124 Level     xilinx-vphy
 
    if [ $line_new -eq 48 ]; then	   
		echo -e "\nSet CPU:1 affinity for interrupt: $line_new"
		echo 2 > /proc/irq/$line_new/smp_affinity 
    elif [ $line_new -eq 52 ]; then	   
		echo -e "\nSet CPU:1 affinity for interrupt: $line_new" 
		echo 2 > /proc/irq/$line_new/smp_affinity 
    elif [ $line_new -eq 54 ]; then	   
		echo -e "\nSet CPU:1 affinity for interrupt: $line_new" 
		echo 2 > /proc/irq/$line_new/smp_affinity 
    elif [ $line_new -eq 55 ]; then	   
		echo -e "\nSet CPU:1 affinity for interrupt: $line_new"
		echo 2 > /proc/irq/$line_new/smp_affinity  
    elif [ $line_new -eq 61 ]; then	   
		echo -e "\nSet CPU:2 affinity for interrupt: $line_new" 
		echo 4 > /proc/irq/$line_new/smp_affinity 
    elif [ $line_new -eq 62 ]; then	   
		echo -e "\nSet CPU:2 affinity for interrupt: $line_new" 
		echo 4 > /proc/irq/$line_new/smp_affinity 
    elif [ $line_new -eq 63 ]; then	   
		echo -e "\nSet CPU:2 affinity for interrupt: $line_new" 
		echo 2 > /proc/irq/$line_new/smp_affinity 
    else 
		echo -e "\nSet CPU:0 affinity for interrupt: $line_new" 
		echo 1 > /proc/irq/$line_new/smp_affinity 
    fi
	
	echo -e "\nNew CPU affinity for interrupt: $line_new" 
	cat /proc/irq/$line_new/smp_affinity 
done

After setting the interrupts, you can check /proc/interrupts to see that CPU2/CPU3 handled interrupts 48, 52, 54, 55, 61, and 62.

root@vcu_trd:~# cat /proc/interrupts
           CPU0       CPU1       CPU2       CPU3
  3:     115462     135783      31811     204151     GICv2  30 Level     arch_timer
  6:          0          0          0          0     GICv2  67 Level     zynqmp_ipi
  7:          0          0          0          0     GICv2 175 Level     arm-pmu
  8:          0          0          0          0     GICv2 176 Level     arm-pmu
  9:          0          0          0          0     GICv2 177 Level     arm-pmu
 10:          0          0          0          0     GICv2 178 Level     arm-pmu
 12:     349750          0          0          0     GICv2 156 Level     zynqmp-dma
 13:          0          0          0          0     GICv2 157 Level     zynqmp-dma
 14:          0          0          0          0     GICv2 158 Level     zynqmp-dma
 15:          0          0          0          0     GICv2 159 Level     zynqmp-dma
 16:          0          0          0          0     GICv2 160 Level     zynqmp-dma
 17:          0          0          0          0     GICv2 161 Level     zynqmp-dma
 18:          0          0          0          0     GICv2 162 Level     zynqmp-dma
 19:          0          0          0          0     GICv2 163 Level     zynqmp-dma
 20:          0          0          0          0     GICv2 164 Level     Mali_GP_MMU, Mali_GP, Mali_PP0_MMU, Mali_PP0, Mali_PP1_MMU, Mali_PP1
 21:          0          0          0          0     GICv2 109 Level     zynqmp-dma
 22:          0          0          0          0     GICv2 110 Level     zynqmp-dma
 23:          0          0          0          0     GICv2 111 Level     zynqmp-dma
 24:          0          0          0          0     GICv2 112 Level     zynqmp-dma
 25:          0          0          0          0     GICv2 113 Level     zynqmp-dma
 26:          0          0          0          0     GICv2 114 Level     zynqmp-dma
 27:          0          0          0          0     GICv2 115 Level     zynqmp-dma
 28:          0          0          0          0     GICv2 116 Level     zynqmp-dma
 30:     463312          0          0          0     GICv2  95 Level     eth0, eth0
 32:        525          0          0          0     GICv2  49 Level     cdns-i2c
 33:        113          0          0          0     GICv2  50 Level     cdns-i2c
 34:          0          0          0          0     GICv2  42 Level     ff960000.memory-controller
 35:          0          0          0          0     GICv2  57 Level     axi-pmon, axi-pmon
 36:        181          0          0          0     GICv2 155 Level     axi-pmon, axi-pmon
 37:         28          0          0          0     GICv2  47 Level     ff0f0000.spi
 38:          0          0          0          0     GICv2  58 Level     ffa60000.rtc
 39:          0          0          0          0     GICv2  59 Level     ffa60000.rtc
 40:          0          0          0          0     GICv2 165 Level     ahci-ceva[fd0c0000.ahci]
 41:        233          0          0          0     GICv2  81 Level     mmc0
 42:        133          0          0          0     GICv2  53 Level     xuartps
 44:          0          0          0          0     GICv2  84 Edge      ff150000.watchdog
 45:          0          0          0          0     GICv2  88 Level     ams-irq
 46:         12          0          0          0     GICv2 154 Level     fd4c0000.dma
 47:          0          0          0          0     GICv2 151 Level     fd4a0000.zynqmp-display
 48:          0      34920          0          0     GICv2 122 Level     xilinx_framebuffer
 49:          0          0          0          0     GICv2 141 Level     xilinx_framebuffer
 50:          0          0          0          0     GICv2 142 Level     xilinx_framebuffer
 51:          0          0          0          0     GICv2 143 Level     xilinx_framebuffer
 52:          0    1142094          0          0     GICv2 123 Level     xilinx-hdmi-rx
 53:          0          0          0          0     GICv2 121 Level     xilinx_framebuffer
 54:      17669     151552          0          0     GICv2 125 Level     xilinx-hdmitxss
 55:      17672     151552          0          0     GICv2 127 Level     xlnx-mixer
 56:          0          0          0          0     GICv2 136 Level     xilinx-dma-controller
 57:          0          0          0          0     GICv2 137 Level     xilinx-dma-controller
 58:          0          0          0          0     GICv2 138 Level     xilinx-dma-controller
 59:          0          0          0          0     GICv2 140 Level     xilinx-dma-controller
 60:         81          0          0          0     GICv2 126 Level     a00d0000.i2c
 61:          0          0      69841          0     GICv2 139 Level     a00d1000.sync_ip
 62:          4          0     279353          0     GICv2 128 Level     a0220000.al5d, a0200000.al5e
 63:       1184        163          0          0     GICv2 124 Level     xilinx-vphy
 64:          0          0          0          0     GICv2  97 Level     xhci-hcd:usb1
 67:          0          0          0          0  zynq-gpio  22 Edge      sw19
IPI0:     64845      46081         35     663483       Rescheduling interrupts
IPI1:        19         58         29         29       Function call interrupts
IPI2:         0          0          0          0       CPU stop interrupts
IPI3:         0          0          0          0       CPU stop (for crash dump) interrupts
IPI4:         0          0          0          0       Timer broadcast interrupts
IPI5:         0          0          0          0       IRQ work interrupts
IPI6:         0          0          0          0       CPU wake-up interrupts
Err:          0

07Process PriorityEngineer Shares: CPU Control in Embedded Linux Systems

The concept of process priority in Linux is quite complex. Generally, you can change a process’s priority by setting its Nice value using the renice tool. A higher Nice value means a lower priority for the process. The common format for renice is renice PRIORITY -p pid, where PRIORITY is the Nice value and pid is the process ID.

The following script can set the priority of all processes containing a keyword (the first parameter of the script, $1) to the value of the second parameter ($2).

#!/bin/sh

u-ps -axo pid,psr,cmd,ni
u-ps -axo pid,psr,cmd,ni | grep $1  | grep -v "grep" | grep -v "awk" | awk '{print $1}' > process_list.txt

echo -e "\nRead process list file:" ;
cat process_list.txt | while read line
do   
echo -e "\nSet PID: $line to priority-nice value: $2\n\n"  	renice $2 -p $line
done

08OthersEngineer Shares: CPU Control in Embedded Linux Systems

To further improve real-time performance, you may consider adding the Linux RT Patch (Click “Read the original text” to view the link).

Engineer Shares: CPU Control in Embedded Linux SystemsEngineer Shares: CPU Control in Embedded Linux SystemsEngineer Shares: CPU Control in Embedded Linux SystemsEngineer Shares: CPU Control in Embedded Linux SystemsFollow Us

Engineer Shares: CPU Control in Embedded Linux Systems

Leave a Comment