基于Enclustra的Mars ZX3模块构建容错嵌入式Linux系统

由于专家的工作量,几年前,我们被迫对交易对手进行一次开发。 在Enclustra Mars ZX3模块上进行了开发,该模块使用SOC ARM + FPGA Zynq-7020。 为了构建Linux,我们使用了Enclustra的BSP(bsp-xilinx),对其进行了稍微的修改。

在测试开发的软件的过程中,关闭电源后,我们立即遇到了软件故障。 在分析过程中,发现通过网络发送到设备的配置命令被写入文件,当发生电源故障时,有时结果是空的或完全不存在。 这迫使我们重新考虑构建交付给我们的Linux程序集的思想。 系统本身的构建过程已在模块制造商的网站上进行了详细介绍,因此我不再赘述。 我只会描述什么使我们能够解决增加可靠性和防止故障的任务。

火星ZX3模块具有QSPI Flash和NAND Flash芯片。 在我们的例子中,该模块装有QSPI Flash,U-Boot已写入其中。 由于两个芯片都使用相同的Zynq-7020引脚,因此在加载U-Boot之后,将引脚切换到NAND Flash,在其上分别将启动脚本,设备树,Linux内核,ubifs文件系统和环境变量写入。 此外,除具有环境变量的部分外,所有部分都被保留(也就是说,有两个这样的部分)。 以下是设备树文件的一部分,该文件显示了承包商在向我们传输的版本中如何破坏NAND Flash:

partition@nand-linux { label = "nand-linux"; reg = <0x0 0x500000>; }; partition@nand-device-tree { label = "nand-device-tree"; reg = <0x500000 0x100000>; }; partition@nand-bootscript { label = "nand-bootscript"; reg = <0x600000 0x100000>; }; partition@nand-linux-second { label = "nand-linux-second"; reg = <0x700000 0x500000>; }; partition@nand-device-tree-second { label = "nand-device-tree"; reg = <0xC00000 0x100000>; }; partition@nand-bootscript-second { label = "nand-bootscript"; reg = <0xD00000 0x100000>; }; partition@nand-rootfs { label = "nand-rootfs"; reg = <0xE00000 0xF500000>; }; partition@nand-rootfs-second { label = "nand-rootfs"; reg = <0x10300000 0xF500000>; }; partition@boot-env { label = "nand-env"; reg = <0x1F800000 0x100000>; }; 

第二部分应该在主要部分出现故障时使用。 为此,Linux必须实现一个监视系统完整性的过程,并在发生故障的情况下将一个值写入环境变量,以指示从备份分区启动系统。 该算法应该在将来实现。

配置数据被记录在两个文件夹中以进行备份,但这并没有保存情况。 假定在没有配置文件的情况下,将使用默认设置自动再次创建它们。

问题在于NAND Flash一次只能将数据写入一页,并且擦除操作会成块出现。 因此,如果在数据记录期间发生电源故障,不仅此数据将被破坏,而且文件系统也可能被损坏。 启动备份系统只能延迟问题的发生。 尽管在这种情况下,可以实现从备份分区恢复主分区。

我们决定采取另一种方法,将rootfs挂载为只读文件系统,并写入配置文件以分离为读取和写入而挂载的数据备份部分。 在这种情况下,对备份分区的需求就消失了,但是我们将它们留给以后使用,因为内存量允许我们执行此操作。 如有必要,可以将其删除。

结果,完成了以下NAND闪存分区:

  partition@nand-linux { label = "nand-linux"; reg = <0x0 0x500000>; }; partition@nand-device-tree { label = "nand-device-tree"; reg = <0x500000 0x100000>; }; partition@nand-bootscript { label = "nand-bootscript"; reg = <0x600000 0x100000>; }; partition@nand-linux-second { label = "nand-linux-second"; reg = <0x700000 0x500000>; }; partition@nand-device-tree-second { label = "nand-device-tree"; reg = <0xC00000 0x100000>; }; partition@nand-bootscript-second { label = "nand-bootscript"; reg = <0xD00000 0x100000>; }; partition@nand-rootfs { label = "nand-rootfs"; reg = <0xE00000 0x9600000>; }; partition@nand-rootfs-second { label = "nand-rootfs"; reg = <0xA400000 0x9600000>; }; partition@nand-data { label = "nand-data"; reg = <0x13A00000 0x5F00000>; }; partition@nand-data-backup { label = "nand-data-backup"; reg = <0x19900000 0x5F00000>; }; partition@boot-env { label = "nand-env"; reg = <0x1F800000 0x100000>; }; 

使用U-Boot命令闪存NAND闪存时,我们会删除nand-datanand-data-backup部分:

 u-boot>nand erase.part nand-data u-boot>nand erase.part nand-data-backup 

在Linux启动脚本中,我们实现了将根文件系统安装为只读,从而替换了Linux程序集中/ etc / inittab文件中的行:

 ::sysinit:/bin/mount -o remount,rw / 



 ::sysinit:/bin/mount -o remount,ro / 

我们在/etc/init.d/文件夹中添加了一个启动脚本,该脚本安装了用于读取和写入的nand-datanand-data-backup部分。 如果出现安装错误(在首次引导期间或文件系统损坏),则将格式化并重新安装这些分区。 文件夹/ mnt / data // mnt / backup /必须事先在根文件系统中创建。

 #!/bin/sh # # datafs Mount datafs. # umask 077 start() { printf "Starting mount datafs..." /usr/sbin/ubiattach /dev/ubi_ctrl -m 8 /usr/sbin/ubiattach /dev/ubi_ctrl -m 9 mount_datafs_err=0; mount_datafs_backup_err=0; /bin/mount -t ubifs ubi1:datafs /mnt/data mount_datafs_err=$? if [ $mount_datafs_err -ne 0 ]; then /usr/sbin/ubidetach -p /dev/mtd8 /usr/sbin/ubiformat /dev/mtd8 -y /usr/sbin/ubiattach /dev/ubi_ctrl -m 8 /usr/sbin/ubimkvol /dev/ubi1 -N datafs -s 81MiB /bin/mount -t ubifs ubi1:datafs /mnt/data mount_datafs_err=$? fi /bin/mount -t ubifs ubi2:datafs /mnt/backup mount_datafs_backup_err=$? if [ $mount_datafs_backup_err -ne 0 ]; then /usr/sbin/ubidetach -p /dev/mtd9 /usr/sbin/ubiformat /dev/mtd9 -y /usr/sbin/ubiattach /dev/ubi_ctrl -m 9 /usr/sbin/ubimkvol /dev/ubi2 -N datafs -s 81MiB /bin/mount -t ubifs ubi2:datafs /mnt/backup mount_datafs_backup_err=$? fi printf "Mount datafs: " if [ $mount_datafs_err -ne 0 ]; then echo "Error" else echo "OK" fi printf "Mount backup datafs: " if [ $mount_datafs_backup_err -ne 0 ]; then echo "Error" else echo "OK" fi touch /var/lock/datafs } stop() { /bin/umount /mnt/data /bin/umount /mnt/backup rm -f /var/lock/datafs } restart() { stop sleep 1 start } case "$1" in start) start ;; stop) stop ;; restart|reload) restart ;; *) echo "Usage: $0 {start|stop|restart|reload}" exit 1 esac exit 0 

下载时,如果缺少/ mnt / data /文件夹中的配置文件,则会从/ mnt / backup /文件夹中下载它们。 如果/ mnt / backup /中没有配置文件,则会使用默认参数从软件自动创建它们。 如果文件存在于/ mnt / data /中 ,而不存在于/ mnt / backup /中 ,则它们将从/ mnt / data /复制到/ mnt / backup /中 。 所有这些操作均由用户软件执行。

在下一阶段,为了提高可靠性,我们拒绝为每个命令将配置写入文件中。 现在,整个配置都存储在RAM中,并且如果需要,可以通过单独的命令将其保存在/ mnt / data // mnt / backup /文件夹中的文件中。

如果在工作期间需要对根文件系统进行更改而又不刷新设备,则可以从控制台重新挂载系统以使用以下命令进行读写

 mount -o remount,rw / 

进行更改,然后重新安装为只读:

 mount -o remount,ro / 

Source: https://habr.com/ru/post/zh-CN483900/


All Articles