Fixing console UART on Quartz64 Model B

Recently, I've obtained Quartz64 Model B single board computer (the board revision is 1.4 2022.06.06). It is equipped with so-called RasbperryPi-compatible 40-pin extension header, which provides access to UART console. The board is powered by Rockchip RK3566 Quad-Core ARM Processor and default UART baudrate is 1500000 as it was for RK3328 for instance.

Surprisingly, I had nothing on the console when I powered the board up. Even though HDMI output works fine, and the board was booting successfully. It took some time to realize that something is wrong with the board itself. Then it appeared that other people also suffer from the similar issue: https://forum.pine64.org/showthread.php?tid=17131&pid=115539#pid115539

At some point I attached an oscilloscope to the UART TX pin:

These saw teeth are supposed to be rectangular for not-disturbed signal. So, the hardware origin of the issue became more evident. Unfortunately, Pine64 support didn't help me with this issue, they only adviced to try different OS, different power supply, etc. All of that didn't helped.

The board schematic is not very complicated for UART TX (the same is valid for RX):

The resistors can be probed relatively easily, it appeared that all of them have correct nominals. So, the final decision appeared to replace Q1 (for RX line) and Q2 (for TX line) SOT-323 MOSFETs. There are only two components of SOT-323 size on the board and they can be easily located with the help of components placement:

The MOSFETs have KN on their case and three pins and each located at its own side of the board. The pars are relatively big to be manually resoldered. I used Pine64 Pencil with ILS soldering tip. It seems that high-temperature lead-free soldering was used on the factory, so the temperature close to 400C was required to unsolder the parts. After the parts were replaced with the spares supplied by local electronic distributor, UART console has been working as initially expected.

Использование chrony для синхронизации времени с GPS

Возможность использования систем спутниковой навигации в качестве источника точного времени востребована в настоящее время, во многом благодаря обилию доступных по цене приёмников. Сервер точного времени chrony, развиваемый как легковесная замена ntpd, выбран во многих дистрибутивах в качестве сервера времени по умолчанию, и, конечно, поддерживает такую возможность.

Де-факто стандартом приёмников является, пришедший с водного транспорта, протокол NMEA 0183, зачастую используемый поверх RS232. Для целей синхронизации времени как правило используется отдельная линия с TTL сигналом с частотой 1 Гц. При использовании линий RS232 для передачи TTL сигнала используется DCD (первый пин разъема DB9). На популярных в последнее время одноплатных компьютерах для приема сигнала 1 Гц используется интерфейс GPIO.

На стороне ядра Linux сигнал обрабатывается подсистемой PPS: модулем pps-ldisc в случае DCD, или модулем pps-gpio для GPIO. Пользовательские приложения получают доступ к таблице событий через символьные устройства /dev/ppsX.

Данные, передаваемые по протоколу NMEA, доступны для чтения через соответсвующее устройство: /dev/ttyY. В отличии от ntpd, chrony не содержит код для разбора протокола NMEA, поэтому полагается на gpsd, который, в свою очередь, передает chrony данные о точном времени через unix сокет.

gpsd обладает скудными возможностями конфигурации, а собственные способности автоконфигурации его зачасную подводят. При запуске gpsd необходимо использовать следующие аргументы командной строки:

  • -n начать приём данных сразу после запуска; по умолчанию, gpsd ждет клиентских подключений для начала работы;
  • -s 9600 указывается скорость обмена для приёмника;
  • /dev/ttyY устройсто с NMEA.

При использовании устройств с именами вида /dev/ttyY gpsd пытается активировать на них линейную дисциплину PPS для получения сигнала с линии DCD. Сигнала, да и самой линии, может не быть, особенно в случаях с одноплатными компьютерами, а сообщить gpsd об этом явно возможности не предусмотрено. Однако, если устройство имеет имя /dev/gpsd0, то gpsd пытается открыть уже готовое устройство /dev/pps0, которое очень удачно может обслуживаться модулем pps-gpio. В последнем случае, придётся пойти на поклон к udev и попросить его делать символическую ссылку для нужного нам tty устройства. Примерное содержимое /etc/udev/rules.d/98-gpsd.rules:

ACTION=="remove", GOTO="gpsd_end"

KERNEL=="ttyS2", SYMLINK+="gpsd0", MODE="0600", OWNER="root", GROUP="root"

LABEL="gpsd_end"

Для chrony следует указать источник точного времени в виде сокета следующим образом. Примерное содержимое /etc/chrony.d/gps.conf:

refclock SOCK /run/chrony.gpsd0.sock refid GPS

Имя сокета для подключения не может быть задано произвольно. Дело в том, что gpsd нельзя указать, куда он должен передавать информацию о времени, вместо этого он самостоятельно начинает запись в сокет с именем /run/chrony.gpsd0.sock если его обнаружит. Следует убедиться, что gpsd запускается после chronyd. Текст gpsd0 после первой точки в имени файла соответсвует имени устройства используемого gpsd для чтения NMEA.

С помощью утилиты chronyc можно проверить, что всё работает корректно.

chronyc> sources
MS Name/IP address         Stratum Poll Reach LastRx Last sample
===============================================================================
#* GPS                           0   4   377    21   -721ns[-2140ns] +/- 1333ns
^- 2a00:ab00:203:9::1000:1       2   6   337     2  +9783us[+9783us] +/-   65ms
^- ftpshare1.corbina.net         2   6   377     2  -4050us[-4050us] +/-   39ms
^- tms04.deltatelesystems.ru     2   6   377    63    +11ms[  +11ms] +/-  104ms
^- 2a00:ab00:203:9::1000:6       2   6   177     1    +12ms[  +12ms] +/-   63ms

Возможные значения колнки "S":

  • * — используется в качестве основного источника,
  • + — используется,
  • - — не используется,
  • ? — не используется, идёт сбор информации о стабильности источника.

Изменение размера шард для InfluxDB 1.7

Хранилище сервера баз данных временных рядов InfluxDB 1.x использует шарды фиксированного по времени размера независимо от количества хранящихся в них записей. Шарды не могут быть слиты или разделены. При устаревании данные удаляются отдельными шардами. Использование слишком маленького размера шарда приводит к проблемам с производительностью и избыточному потреблению памяти: на каждый шард требуется хранить в памяти отдельный дескриптор, даже если используется TSI1 индексирование.

Размер шарда является свойством политики хранения (retention policy), и, хотя настройка может быть изменена в онлайне, её изменение не приводит к перебалансировке уже существующих шард. Это приводит к проблемам для баз данных где требуется бесконечный или достаточно длительный срок хранения, т.е. шарды удаляются медленно, так как, несмотря на собственные рекомендации, настройки политики хранения по умолчанию состоят в том чтобы хранить данные бесконечно и разделять на шарды по 1 неделе.

Одним из возможных способов перебалансировки шардов в онлайн является создание новой политики хранения с нужными параметрами, и последующего копирования туда данных запросом (что скорее всего приведет к избыточному потреблению оперативной памяти и потребует копирования по частям). Проблема этого подхода состоит в том, что невозможно переименовать политику хранения, поэтому потребуется перенастройка всех непрерывных запросов (continuous queries) и, возможно, перенастройка клиентских приложений.

В качестве альтернативного решения предлагается следующая оффлайн (при остановленном сервере) процедура: экспорт данных из существующей политики хранения в бинарном виде, удаление политики хранения, создание новой политики хранения (с прежним именем и новыми настройками) и импорт сохраненных данных. Для процедуры используется модифицированная (добавлена команда droprp) утилита influx_tools:

git clone -b influx_tools/droprp https://github.com/matwey/influxdb.git
cd influxdb/cmd/influx_tool
go build .

Экспорт данных производится следующим образом:

./influx_tools export -config /etc/influxdb/config.toml -no-conflict-path -database ${DB_NAME} -rp ${RP_NAME} -duration 8736h -format binary | xz -3 -c - > /tmp/ifx_dump

здесь 8736h — новый размер шарда, в данном случае 52 недели. Сжатие можно опустить, но оно весьма эффективно работает в данном месте.

Удалим политику хранения ${RP_NAME}:

./influx_tools droprp -config /etc/influxdb/config.toml -database ${DB_NAME} -rp ${RP_NAME}

В этот момент будут удалены файлы старых шард, удалены файлы журналов упреждающей записи, и модифицирован файл meta/meta.db, который представляет из себя сериализованный документ в формате ProtoBuf, хранящий список политик хранения и шард.

Загрузим данные на прежнее место с новыми настройками политики хранения:

xzcat /tmp/ifx_dump | ./influx_tools import -config /etc/influxdb/config.toml -database ${DB_NAME} -rp ${RP_NAME} -replace -shard-duration 8736h

Пересобирем индекс для новых шард:

influx_inspect buildtsi -datadir /var/lib/influxdb/data/ -waldir /var/lib/influxdb/wal -v

Восстановим права доступа:

chown -R influxdb:influxdb /var/lib/influxdb/*

Миграция InfluxDB 0.9 до InfluxDB 1.1

Между 0.9 и 1.1 нет прямой совместимости, поэтому прежде всего понадобится какая-нибудь промежуточная версия на короткий промежуток времени:
# wget https://dl.influxdata.com/influxdb/releases/influxdb-0.11.1-1_linux_amd64.tar.gz

Сначала рекомендуется сбросить WAL (write-ahead log), для этого рекомендуется изолировать сервер InfluxDB от записи и выполнить его restart.
Далее, останавливаем InfluxDB 0.9 и с помощью утилиты influx_tsm из пакета InfluxDB версии 1.1 конвертируем директорию data.
Предполагается, что резервную копию данных мы уже сделали внешним образом.
# influx_tsm -parallel -nobackup /var/lib/influxdb/data

Утилита выводит список всех найденных файлов устаревшего формата b1 или bz1 и спрашивает Proceed? y/N:
После подтверждения данные преобразуются в новый формат tsm.

Далее осталось преобразовать в новый формат /var/lib/influxdb/meta. Для этого на некоторое время придется запустить InfluxDB версии 0.11
# ./influxdb-0.11.1-1/usr/bin/influxd -config /etc/influxdb/config.toml

И попросить его сделать резервную копию:
# influxd backup /tmp/backup/
2016/11/27 17:41:37 backing up metastore to /tmp/backup/meta.00
2016/11/27 17:41:37 backup complete

После этого сервер 0.11 можно выключить и загрузить резервную копию назад, но уже используя InfluxDB версии 1.1:
# influxd restore -metadir=/var/lib/influxdb/meta/ /tmp/backup/
Using metastore snapshot: /tmp/backup/meta.00

После этого восстановим права доступа
# chown -R influxdb:influxdb /var/lib/influxdb/

И можно запускать сервер InfluxDB 1.1
# systemctl start influxd

Открываем консоль influx и проверяем что все наши данные доступны.

Настройка звука в конфигурации с несколькими одинаковыми звуковыми картами

Имеется следующая конфигурация. Два звуковых устройства, оба управляются одним и тем же модулем snd-hda-intel. Первое устройство отвечает за звуковой канал в HDMI, встроенной в процессор графической системы. Второй устройство — обычный интегрированный на материнскую плату аудио-контроллер.
00:03.0 Audio device [0403]: Intel Corporation Xeon E3-1200 v3/4th Gen Core Processor HD Audio Controller [8086:0c0c] (rev 06)
00:1b.0 Audio device [0403]: Intel Corporation 9 Series Chipset Family HD Audio Controller [8086:8ca0]
Пользователи жалуются, что звук не работает. Проблема состоит в том, что YaST создаёт следующую конфигурацию в 50-sound.conf:
options snd slots=snd-hda-intel,snd-hda-intel
alias snd-card-0 snd-hda-intel
alias snd-card-1 snd-hda-intel
Здесь snd-card-0 — звуковое устройство "по умолчанию". Очевидно, что в таком подходе ситуация, когда один и тот же модуль отвечает за два устройства, обрабатывается не корректно. Кто из устройств первый попал в функцию azx_probe модуля, тот и станет snd-card-0. В нашем случае, это не то устройство, которое нам хотелось бы. Это можно проверить, сделав
ls -l /sys/class/sound/card0/device
lrwxrwxrwx 1 root root 0 окт  9 21:36 /sys/class/sound/card0/device -> ../../../0000:00:03.0

Для восстановления правильного порядка предлагается делать следующее.
Во-первых, внимательно изучив исходники модуля, добавляем в 50-sound.conf:
options snd-hda-intel index=1,0
Во-вторых, надеемся, что при следующих загрузках порядок инициализации не будет меняться случайным образом, потому-что кто первый попадет в функцию azx_probe, тот теперь и будет card1. И это всё происходит в 2015 году, когда повсюду systemd и прочий udev, а звуковую карту за её DeviceId привязать нельзя.

Настройка вложенной виртуализации в ESXi 6.0

Иногда в гостевой системе требуется в свою очередь запускать виртуальные машины. В нашем случае, гипервизор VMWare ESXi 6.0 (он же известен под названием vSphere Hypervisor) используется для запуска различных операционных систем, сервисы в одной из которых, в свою очередь, динамически запускают qemu-kvm. Для того, чтобы kvm в гостевой системе работал, необходимо включить вложенную (nested) виртуализацию. Делается это следующим замысловатым образом.

Во-первых, в vmx-файл настроенной гостевой системы дописывается директива vhv.enable = "TRUE". Включить эту настройку из клиентского приложения, видимо, нельзя. Для редактирования файла можно сначала загрузить его на локальную машину, а затем отредактированную версию загрузить назад, используя браузер датастора гипервизора.
Во-вторых, нужно попросить гипервизор перечитать vmx-файл, как описано в тикете 1026043 базы знаний VMWare. А именно, используя ssh для доступа к командной строке гипервизора выполнить команды:
# vim-cmd vmsvc/getallvms
# vim-cmd vmsvc/reload Vmid
Первая команда просто отображает список всех виртуальных машин гипервизора, она нужна чтобы определить Vmid интересующей нас машины, это число используется во второй команде.

После этого стартуем виртуальную машину и проверяем, что аппаратная виртуализация стала доступна нам в гостевой системе:
# cat /proc/cpuinfo | grep vmx
flags           : fpu vme de pse tsc msr pae mce cx8 apic sep mtrr pge mca cmov pat pse36 clflush dts mmx fxsr sse sse2 ss ht syscall nx rdtscp lm constant_tsc arch_perfmon pebs bts nopl xtopology tsc_reliable nonstop_tsc aperfmperf pni pclmulqdq vmx ssse3 cx16 sse4_1 sse4_2 popcnt aes hypervisor lahf_lm ida arat epb dtherm tpr_shadow vnmi ept vpid
flags           : fpu vme de pse tsc msr pae mce cx8 apic sep mtrr pge mca cmov pat pse36 clflush dts mmx fxsr sse sse2 ss ht syscall nx rdtscp lm constant_tsc arch_perfmon pebs bts nopl xtopology tsc_reliable nonstop_tsc aperfmperf pni pclmulqdq vmx ssse3 cx16 sse4_1 sse4_2 popcnt aes hypervisor lahf_lm ida arat epb dtherm tpr_shadow vnmi ept vpid
# lsmod | grep kvm
kvm_intel             151704  3 
kvm                   493230  1 kvm_intel

Удаленная сетевая консоль ядра Linux

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

Полное описание сетевой консоли находится в документации ядра. Для настройки достаточно добавить в modprobe.d/99-local.conf:
options netconsole netconsole=@/,514@192.168.10.7/00:0C:29:F3:92:97
Здесь, 514 - номер UDP порта, 192.168.10.7 - IP удаленного хоста куда будет отсылаться информация, 00:0C:29:F3:92:97 - его MAC адрес, если его не указать явно то будет использоваться широковещательный адрес FF:FF:FF:FF:FF:FF, что может затруднить передачу информации в зависимости от настроек сетевого оборудования.
И загрузить модуль netconsole или, по желанию, поставить его на автозагрузку при старте системы.

После загрузки модуля netconsole, сетевая консоль начинает немедленно функционировать, а в системном журнале можно увидеть примерно следующее:
May  1 18:57:51 192.168.10.4 kernel: [162255.522603] netconsole: local port 6665
May  1 18:57:51 192.168.10.4 kernel: [162255.522673] netconsole: local IP 0.0.0.0
May  1 18:57:51 192.168.10.4 kernel: [162255.522710] netconsole: interface eth0
May  1 18:57:51 192.168.10.4 kernel: [162255.522746] netconsole: remote port 514
May  1 18:57:51 192.168.10.4 kernel: [162255.522784] netconsole: remote IP 192.168.10.7
May  1 18:57:51 192.168.10.4 kernel: [162255.522826] netconsole: remote ethernet address 00:0c:29:f3:92:97
May  1 18:57:51 192.168.10.4 kernel: [162255.522881] netconsole: local IP 192.168.10.4
May  1 18:57:51 192.168.10.4 kernel: [162255.523032] console [netcon0] enabled
May  1 18:57:51 192.168.10.4 kernel: [162255.523349] netconsole: network logging started

Данные приходят в самом простом текстовом виде и их можно читать самым простым способом:
netcat -u -l 514
Если у нас в наличии есть syslog-ng — можно использовать его следующим образом:
source s_remote_udp {
        network(transport("udp") ip(0.0.0.0) port(514));
};
filter f_remote_remhost {
        netmask(192.168.10.4);
};
destination d_remote_remhost {
        file("/var/log/remote/remhost.log");
};
log {
        source(s_remote_udp);
        filter(f_remote_remhost);
        destination(d_remote_remhost);
};

Проверить, что всё работает можно следующим образом:
echo '<7>Hello world!' > /dev/kmsg
dmesg -n 8
Сообщение должно появиться в журнале и быть передано по сети на удаленный хост.