Scenario
In alcune particolari condizioni, abbiamo avuto modo di verificare, io ed il mio collaboratore Senior Luca, dei Kernel Panic sollecitati dalla lettura del modulo 1wire.Il kernel panic si verifica:
- su kernel 3.18.9+, Raspian armv6l (non ho informazioni circa versioni differenti, più o meno recenti rispetto a questa)
- solo su CPU mono core (quindi RaspBerry Pi 1 e non RaspBerry Pi 2)
- quando istruzioni pygame sono in esecuzione
- quando si verifica un overload della CPU (tuttavia questo non è estremamente rilevante)
- quando il numero di processi python aumenta
- a prescindere dalle sonde (ne abbiamo cambiate 10 e abbiamo cambiato 5 raspberry, riproducendo il problema anche con hardware completamente differente)
Cosa Accade
Inizialmente una serie di letture delle sonde w1 danno errori di CRC:
May 11 20:11:16 AS15C3101B kernel: [ 392.757953] w1_slave_driver 28-000005a01d7f: Read failed CRC check
May 11 20:11:43 AS15C3101B kernel: [ 419.917527] w1_slave_driver 28-00000696eb99: Read failed CRC check
May 11 20:12:30 AS15C3101B kernel: [ 466.413438] w1_slave_driver 28-00000696eb99: Read failed CRC check
May 11 20:12:44 AS15C3101B kernel: [ 480.799177] w1_slave_driver 28-0000054ebd49: Read failed CRC check
May 11 20:12:50 AS15C3101B kernel: [ 486.936962] w1_slave_driver 28-000005a01d7f: Read failed CRC check
May 11 20:13:06 AS15C3101B kernel: [ 502.939939] w1_slave_driver 28-0000054ebd49: Read failed CRC check
May 11 20:13:14 AS15C3101B kernel: [ 510.917800] w1_slave_driver 28-000005a01d7f: Read failed CRC check
May 11 20:13:28 AS15C3101B kernel: [ 524.425806] w1_slave_driver 28-0000054ebd49: Read failed CRC check
May 11 20:13:36 AS15C3101B kernel: [ 532.149026] w1_slave_driver 28-000005a01d7f: Read failed CRC check
May 11 20:13:51 AS15C3101B kernel: [ 547.247455] w1_slave_driver 28-0000054ebd49: Read failed CRC check
May 11 20:22:55 AS15C3101B kernel: [ 394.467714] w1_slave_driver 28-0000054e5d76: Read failed CRC check
May 11 20:23:35 AS15C3101B kernel: [ 433.749331] w1_slave_driver 28-00000696eb99: Read failed CRC check
May 11 20:23:49 AS15C3101B kernel: [ 448.129373] w1_slave_driver 28-0000054e5d76: Read failed CRC check
May 11 20:23:59 AS15C3101B kernel: [ 457.658434] w1_slave_driver 28-00000696eb99: Read failed CRC check
May 11 20:24:55 AS15C3101B kernel: [ 513.698751] w1_slave_driver 28-000005a137c6: Read failed CRC check
May 11 20:27:48 AS15C3101B kernel: [ 687.187439] w1_slave_driver 28-000005a137c6: Read failed CRC check
May 11 20:28:02 AS15C3101B kernel: [ 701.376795] w1_slave_driver 28-000005a137c6: Read failed CRC check
May 11 20:33:18 AS15C3101B kernel: [ 1017.072088] w1_slave_driver 28-00000696eb99: Read failed CRC check
Successivamente tendono persino a scomparire per poi ricomparire a breve e, per finire, il kernel va in panic dando messaggi di errore anche in broadcast.
Message from syslogd@AS15C1302B at May 11 20:39:56 ...
kernel:[ 2614.966264] 5f00: 5550f5f9 37b02fc0 00001a62 dcef5f78 dcef5f3c dba05320 01825000 dcef4000
Message from syslogd@AS15C1302B at May 11 20:39:56 ...
kernel:[ 2614.966286] 5f20: dcef5f78 00008000 01825000 00000000 dcef5f74 dcef5f40 c0137e6c c01a9a38
Segmentation fault
kernel:[ 2614.966264] 5f00: 5550f5f9 37b02fc0 00001a62 dcef5f78 dcef5f3c dba05320 01825000 dcef4000
Message from syslogd@AS15C1302B at May 11 20:39:56 ...
kernel:[ 2614.966286] 5f20: dcef5f78 00008000 01825000 00000000 dcef5f74 dcef5f40 c0137e6c c01a9a38
Segmentation fault
Nei /var/log/messages non c'è molto, a dire il vero:
May 11 20:34:56 AS15C3101B kernel: [ 1115.363581] Modules linked in: i2c_dev stmpe_ts snd_bcm2835 snd_pcm snd_seq snd_seq_device snd_timer snd evdev uinput fb_ili9340(C) fbtft(C) syscopyarea sysfillrect sysimgblt fb_sys_fops spi_bcm2708 i2c_bcm2708 8192cu w1_therm lirc_rpi(C) w1_gpio wire lirc_dev cn rc_core uio_pdrv_genirq uio
May 11 20:34:56 AS15C3101B kernel: [ 1115.363724] CPU: 0 PID: 24237 Comm: cat Tainted: G C 3.18.9+ #768
May 11 20:34:56 AS15C3101B kernel: [ 1115.363742] task: dbb0b600 ti: daf0e000 task.ti: daf0e000
May 11 20:34:56 AS15C3101B kernel: [ 1115.363778] PC is at w1_slave_show+0x2d8/0x398 [w1_therm]
May 11 20:34:56 AS15C3101B kernel: [ 1115.363804] LR is at vsnprintf+0x27c/0x3e0
May 11 20:34:56 AS15C3101B kernel: [ 1115.363820] pc : [<bf04336c>] lr : [<c02fb36c>] psr: 20000113
May 11 20:34:56 AS15C3101B kernel: [ 1115.363820] sp : daf0fe08 ip : bf0435c0 fp : daf0fe54
May 11 20:34:56 AS15C3101B kernel: [ 1115.363840] r10: 0000001f r9 : daf0fe27 r8 : daf0fe27
May 11 20:34:56 AS15C3101B kernel: [ 1115.363854] r7 : dcf70a50 r6 : dcfb8000 r5 : 00000fe5 r4 : 00000000
May 11 20:34:56 AS15C3101B kernel: [ 1115.363867] r3 : 00000000 r2 : 1002ff7f r1 : 464b01de r0 : 0000000d
May 11 20:34:56 AS15C3101B kernel: [ 1115.363882] Flags: nzCv IRQs on FIQs on Mode SVC_32 ISA ARM Segment user
May 11 20:34:56 AS15C3101B kernel: [ 1115.363896] Control: 00c5387d Table: 1ad54008 DAC: 00000015
May 11 20:34:56 AS15C3101B kernel: [ 1115.364356] [<bf04336c>] (w1_slave_show [w1_therm]) from [<c0361650>] (dev_attr_show+0x2c/0x58)
May 11 20:34:56 AS15C3101B kernel: [ 1115.364394] [<c0361650>] (dev_attr_show) from [<c01aa838>] (sysfs_kf_seq_show+0x9c/0x104)
May 11 20:34:56 AS15C3101B kernel: [ 1115.364424] [<c01aa838>] (sysfs_kf_seq_show) from [<c01a9168>] (kernfs_seq_show+0x34/0x38)
May 11 20:34:56 AS15C3101B kernel: [ 1115.364452] [<c01a9168>] (kernfs_seq_show) from [<c015c310>] (seq_read+0x1b8/0x488)
May 11 20:34:56 AS15C3101B kernel: [ 1115.364481] [<c015c310>] (seq_read) from [<c01a9b50>] (kernfs_fop_read+0x124/0x16c)
May 11 20:34:56 AS15C3101B kernel: [ 1115.364511] [<c01a9b50>] (kernfs_fop_read) from [<c0137e6c>] (vfs_read+0x98/0x188)
May 11 20:34:56 AS15C3101B kernel: [ 1115.364538] [<c0137e6c>] (vfs_read) from [<c0138580>] (SyS_read+0x4c/0xa0)
May 11 20:34:56 AS15C3101B kernel: [ 1115.364573] [<c0138580>] (SyS_read) from [<c000e800>] (ret_fast_syscall+0x0/0x48)
May 11 20:34:56 AS15C3101B kernel: [ 1115.364679] ---[ end trace d47c3c19eebad7da ]---
Da questo momento in poi qualsiasi interrogazione sulle sonde w1 rimarrà in hang e bloccherà la shell o qualsiasi script che richiami questa informazione. Anche un semplice cat del valore di w1_slave.
rmmod e modprobe dei moduli w1 rimarranno in hang esattamente come tutto ciò che riguarda anche il modulo w1_thermal w1_slave e w1_bus.
Scatenare l'errore
Più interrogazioni si fanno sulle sonde w1, più avremo probabilità di scatenare il Kernel Panic.
Un modo efficace per mandare in hang il sistema può essere questo:
(bash)# while [ true ]; do cat /sys/bus/w1/devices/28-*/w1_slave; sleep 1; done
Se il sistema è affetto dal problema, nel giro di 60 minuti diventerà instabile.
Ipotesi
Una delle ipotesi che abbiamo formulato si basa sull'assenza di clock hardware nel Raspberry. Tale mancanza, emulata a livello software tramite timer interrupt, potrebbe produrre (in determinate e rare circostanze di carico e utilizzo di risorse) una dilatazione del clock, causando difficoltà nella gestione dell'hardware comunicante in half-duplex (che quindi utilizza dei timing precisi) come quello w1.
E' solo un'ipotesi, ma nel paragrafo successivo vedremo come lavorando sui nice del processore si producano dei miglioramenti.
Soluzioni (più o meno artigianali)
Intendiamoci, può accadere qualsiasi cosa, ma un Kernel deve gestire questo tipo di problema. Il fatto che si pianti (non del tutto, perchè riusciamo ancora a riavviare mediante un reset di tipo echo 1 > /proc/sys/kernel/sysrq ), non è comunque normale. Di fatto tutta la parte w1 entra irrimediabilmente in hang fino al riavvio del sistema.
Perdendoci la testa, correlando l'errore non presente sui sistemi multicore e seguendo la traccia dell'ottimizzazione dell'utilizzo della singola risorsa CPU, ci siamo accorti di una cosa:
# ps -lfax | grep w1_bus_master
F UID PID PPID PRI NI VSZ RSS WCHAN STAT TTY TIME COMMAND
1 0 247 2 20 0 0 0 w1_pro S ? 0:17 \_ [w1_bus_master1]
# nice
0
(Per chi non lo sapesse, nice è il comando linux in grado di forzare una priorità di CPU durante l'esecuzione di un processo. Renice è il suo omonimo in grado di cambiarla ad un processo esistente.)
Nonostante il nice di default fosse 0, w1_bus_master veniva eseguito con nice 20. In situazioni di high-cpu-load, quindi, un processo "tradizionale" parrebbe essere eseguito con una priorità maggiore di un processo di sistema così delicato.
Tanto per iniziare, quindi, abbiamo dato una priorità di utilizzo CPU a -20 (il massimo impostabile "tradizionalmente"):
# renice -n -20 $( ps -aux | grep w1_bus_master | grep -v "grep" | sed -n 1p | awk '{ print $2 }' );
Successivamente abbiamo trovato una profonda correlazione scatenante con l'esecuzione di uno script python contenente librerie pygame. Abbiamo, quindi, startato lo script con nice -n 39 python script.py
Questi due accorgimenti, insieme ad un'ottimizzazione dello script python e al numero di processi che esso generava, sono bastati a ridurre drasticamente i kernel panic e gli errori CRC in lettura del bus w1. Ad ora, dopo quasi 4.000 interrogazioni, non ho avuto nemmeno un CRC error e, ovviamente, di conseguenza nessun Kernel Panic o Kernel Mod Hang.
Senza gli accorgimenti di cui sopra, ricevevo circa il 20% di errori CRC, prima di causare (prima o poi) un prevedibile crash del Kernel e del suo modulo di gestione w1.