Pull to refresh

Построение отказоустойчивой системы Embedded Linux на базе модуля Mars ZX3 фирмы Enclustra

Reading time5 min
Views5.4K
В связи с загруженностью специалистов, несколько лет назад мы вынуждены были отдать одну разработку контрагентам. Разработка велась на модуле Mars ZX3 фирмы Enclustra, в котором используется SOC ARM+FPGA Zynq-7020. Для сборки Linux использовался BSP от Enclustra (bsp-xilinx), который был немного модифицирован.

В процессе тестирования разработанного программного обеспечения, мы сразу же столкнулись с отказами ПО при выключении питания. При анализе, обнаружилось, что команды конфигурирования, отправляемые на устройство по сети, записывались в файлы, которые, при сбое питания, иногда оказывались пустыми или отсутствовали совсем. Это вынудило нас пересмотреть идеологию построения переданной нам сборки Linux. Сам процесс построения системы хорошо описан на сайте изготовителя модуля, поэтому не буду на нем останавливаться. Опишу только то, что позволило решить стоящую перед нами задачу повышения надежности и предотвращения отказов.

На модуле Mars ZX3 установлены микросхемы QSPI Flash и NAND Flash. В нашем случае, загрузка модуля происходит с QSPI Flash, в которую был записан U-Boot. Так как обе микросхемы используют одинаковые выводы Zynq-7020, после загрузки U-Boot переключает выводы на NAND Flash, на котором в отдельных разделах записаны загрузочный скрипт, Device Tree, ядро Linux, файловая система ubifs, переменные окружения. При этом все разделы, кроме раздела с переменными окружения были резервированы (то есть таких разделов было по два). Ниже приведен фрагмент файла Device Tree, где показано, как был разбит 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 как Read-Only файловую систему, а файлы конфигурации записывать в отдельные разделы data и backup, которые монтировались для чтения и записи. В этом случае надобность в резервных разделах отпадает, но мы оставили их на будущее, так как объем памяти позволял нам это сделать. При необходимости, их можно удалить.

В итоге, было сделано следующее разбиение 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 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>;
	};

При прошивке NAND Flash с помощью команды U-Boot мы стираем разделы nand-data и nand-data-backup:

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

В загрузочном скрипте Linux мы реализовали монтирование корневой файловой системы как Read-Only, заменив в файле /etc/inittab в сборке Linux строку:

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

на

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

Добавили загрузочный скрипт в папку /etc/init.d/, который монтирует разделы nand-data и nand-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/. Все эти операции выполняет пользовательское ПО.

На следующем этапе, для повышения надежности мы отказались от записи конфигурации в файл по каждой команде. Вся конфигурация теперь хранится в оперативной памяти и, при необходимости, по отдельной команде может быть сохранена в файлах в папках /mnt/data/ и /mnt/backup/.

Если в процессе работы потребуется внести изменения в корневую файловую систему, не перепрошивая устройство, можно из консоли перемонтировать систему для чтения и записи командой

mount -o remount,rw /

Выполнить изменения и, затем, перемонтировать в Read-Only:

mount -o remount,ro /
Tags:
Hubs:
Total votes 10: ↑8 and ↓2+6
Comments5

Articles