TL;DR. Подключил NVMe накопитель к Repka Pi 5 через PCIe-адаптер Geekworm X1002. По документации Geekworm этот адаптер поддерживает только PCIe Gen 2.0, но на практике он стабильно держит Gen3 ×1 без единой ошибки линка — и выдаёт те же ~720 МБ/с, что и более дорогой кейс с NVMe. Под катом — как проверить, на какой скорости реально поднялся линк, и прогнать стресс-тест, чтобы убедиться, что Gen3 здесь не «на грани», а уверенно.
Что вообще собирал и зачем #
Хочу быстрое хранилище на Repka Pi 5. До этого гнал NVMe через кейс Argon NEO 5 (с отдельной M.2-HAT-платой), который я разбирал в отдельном посте — всё работало на Gen3 ×1. Теперь решил попробовать Geekworm X1002 — отдельную PCIe-платку под один M.2 NVMe, тоже через родной PCIe-разъём платы. Вопрос простой: заведётся ли, и на какой скорости?
| Компонент | Модель |
|---|---|
| Платформа | Repka Pi 5 (Rockchip RK3588) |
| ОС | Repka OS (Ubuntu 25.10), ядро 6.1.115.1-repka-pi5 |
| Адаптер | Geekworm X1002 (PCIe Peripheral Board, M.2 NVMe) |
| SSD | CUSU CV3500Q 1TB, PCIe 3.0 ×4, контроллер Maxio, QLC |
💡 Что такое X1002. Это плата-переходник от Geekworm: на одном конце — разъём под M.2 NVMe-диск (размеры 2230/2242/2260/2280), на другом — шлейф FFC в родной PCIe-разъём Raspberry Pi 5 / Repka Pi 5. По сути, то же самое, что M.2-HAT, только отдельной платкой. Заявлена строго под NVMe (SATA и AHCI не поддерживает).
Сборка: шлейф, защёлка и пины питания (грабли) #
Прежде чем радоваться цифрам, пара слов о том, как это физически собирать — тут есть два неочевидных места, на которые я лично наступил.
Шлейф FFC и защёлка разъёма #

PCIe-разъём на Repka Pi 5 — это ZIF-коннектор с откидной защёлкой (flip-lock): чтобы вставить шлейф, защёлку надо сначала аккуратно приподнять, вставить ленту до упора, потом опустить обратно. У X1002 защёлка немного другой конструкции, чем у привычных M.2-HAT, и именно тут кроется грабель:
⚠️ Не открывайте защёлку с силой и не тяните за шлейф, не отпустив замок. Пластиковый язычок защёлки хрупкий и при избыточном усилии ломается — а без него шлейф не прижимается к контактам, линк не поднимается, и вы потом долго ищете причину в софте, как я когда-то. Открывать — плавно, ногтём или пластиковой лопаткой, без рычага.
Шлейф вставляется контактами в нужную сторону (у FFC медные «пятаки» только с одной стороны). Этому меня уже научила история с перевёрнутым кабелем на другом адаптере — повторять не хочется. Вставил до упора, опустил защёлку, проверил, что лента сидит плотно и не выпадает при лёгком потягивании.


Pogo pins — дополнительное питание через GPIO #

А вот это — самая важная часть всей сборки, и именно её легко пропустить. У X1002 есть pogo pins: подпружиненные штырьковые контакты на углу платы, которые при установке прижимаются прямо к штырькам GPIO платы и заводят на адаптер дополнительное питание 5 В. Ничего паять не надо — плата просто встаёт так, что пого-пины упираются в нужные GPIO.
Зачем они нужны. По спецификации через сам шлейф FFC можно снять максимум 5 В / 1 А = 5 Вт, а на накопитель приходит 3,3 В (до 3,5 А). Для нетребовательного SSD в простое хватает. Но NVMe под нагрузкой (особенно при пиках записи и сбросе кэша на QLC-памяти) может кратковременно потреблять больше — и тогда по тонкому шлейфу садится напряжение. Симптом я наблюдал лично на Raspberry Pi: без pogo pins, на одном только FFC, диск периодически отваливался — пропадал из системы, в логах появлялись ошибки контроллера. Стоило прижать pogo pins к GPIO — отваливания прекратились.
💡 Почему так. Когда по шлейфу не хватает тока, на SSD проваливается напряжение ниже допустимого, и его контроллер уходит в аварийный сброс или отключается. Это та же физика, что и просадки напряжения на слабом БП — только вместо общего питания страдает линия 3,3 В на накопитель. Pogo pins дают второй, независимый путь подвода 5 В, разгружая тонкий FFC-шлейф.
⚠️ Поэтому при сборке следите, чтобы pogo pins действительно упирались в GPIO, а не висели в воздухе и не замыкали соседние контакты. Плата должна стоять ровно, на штатных стойках, чтобы прижим был плотным и стабильным. «Диск отваливается под нагрузкой» — почти всегда симптом именно плохого пита, а не софта.

Подключил. Работает? #
Собрал по этой инструкции (шлейф до упора в правильной ориентации, pogo pins прижаты к GPIO), загрузился. Первым делом — видит ли система диск:
lsblk -o NAME,SIZE,MODEL,TRAN,MOUNTPOINT /dev/nvme0n1
NAME SIZE MODEL TRAN MOUNTPOINT
nvme0n1 953,9G CUSU CV3500Q 1TB nvme /mnt/nvme
└─nvme0n1p1 953,9G nvme /mnt/nvme
Диск определился, раздел на месте, примонтирован. Пока всё хорошо. Но «определился» — это полдела. Главное — на какой скорости поднялся PCIe-линк.
Лирическое отступление: Gen2 и Gen3 — в чём вообще разница #
Прежде чем смотреть цифры, проговорим, что значит «Gen2» и «Gen3» применительно к PCIe.
PCIe (PCI Express) — это высокоскоростная шина, по которой быстрые устройства общаются с процессором. Она делится на поколения (generations), и каждое следующее быстрее предыдущего:
| Поколение | Пропускная способность на 1 линию | Год |
|---|---|---|
| Gen 2.0 | 5.0 ГТ/с ≈ ~500 МБ/с реальных | 2007 |
| Gen 3.0 | 8.0 ГТ/с ≈ ~985 МБ/с реальных | 2010 |
Ключевое слово — «на 1 линию». Repka Pi 5 выводит на свой PCIe-разъём одну линию (×1), поэтому потолок для нас — это пропускная способность одной линии. На Gen2 это ~500 МБ/с, на Gen3 ~985 МБ/с. Разница — примерно вдвое. Для NVMe это разница между «быстро» и «очень быстро».
💡 Как линк выбирает поколение. PCIe-линк поднимается на младшем из поддерживаемых speeds: если хост умеет Gen3, диск умеет Gen3, но плата-переходник между ними рассчитана только на Gen2 — линк встанет на Gen2. То есть одно «узкое» звено режет всю цепочку. Именно поэтому спецификация X1002 «Gen2-only» имела значение: если она честная, мой NVMe должен был бы работать на ~500 МБ/с, а не на ~985.
Главный вопрос: на какой скорости реально поднялся линк? #
Скорость линка ядро честно пишет в sysfs. Смотрим:
cat /sys/class/nvme/nvme0/device/current_link_speed # 8.0 GT/s PCIe
cat /sys/class/nvme/nvme0/device/current_link_width # 1
cat /sys/class/nvme/nvme0/device/max_link_speed # 8.0 GT/s PCIe
И вот тут — сюрприз. 8.0 GT/s — это Gen3, а не Gen2 (Gen2 было бы 5.0 GT/s). То есть линк встал на Gen3 ×1, хотя спецификация X1002 это прямо запрещает.
Как ядро подняло линк — смотрим в логе:
dmesg | grep -iE "rk-pcie|gen\."
rk-pcie fe150000.pcie: PCIe Link up, LTSSM is 0x230011
rk-pcie fe150000.pcie: PCIe Gen.3 x1 link up
Чистый подъём на Gen3, без единой попытки деградировать. Спойлер: это не значит, что всё хорошо — это значит, что нужно копать дальше. Линк-то поднялся, но «Gen3 на Gen2-плате» — подозрительно. Может, он держится на грани и сыплет ошибками, которые компенсируются повторами? Это надо проверять, а не радоваться цифре.
Проверка стабильности: ошибки линка и стресс-тест #
Раз адаптер работает выше спецификации, главный вопрос — не ценой ли ошибок это достигнуто. Проверяю по двум направлениям: счётчики ошибок PCIe и поведение под нагрузкой.
Шаг 1. Ошибки PCIe AER #
PCIe умеет сам сообщать об ошибках через механизм AER (Advanced Error Reporting). Делятся они на corrected (исправимые — линк сам перестраховался, переотправил пакет) и uncorrectable (неисправимые — данные потеряны). Много corrected — уже тревожно, любые uncorrectable — плохо.
Сначала — нет ли ошибок в логе ядра:
dmesg | grep -iE "aer|pcie.*error|corrected|uncorrectable"
У меня пусто — ни одной ошибки с момента загрузки. Хорошо, но мало: AER-события попадают в dmesg только если они есть, а счётчики накопленных ошибок это ядро тут не экспортирует. Поэтому переходим к более надёжной проверке — реальной нагрузкой.
💡 Почему одной проверки ошибок мало. Можно «прочитать dmesg, увидеть ноль ошибок и успокоиться» — но если диск только что загрузился и на нём ничего не писали, ошибкам было просто неоткуда взяться. Стабильность PCIe проверяют именно под нагрузкой: продолжительной записью и чтением. Если линк «на грани», под нагрузкой он начнёт сыпать corrected-ошибками, ретрейниться (пересогласовывать скорость) или вовсе падать до Gen2.
Шаг 2. Стресс-тест: 3 цикла записи по 1 ГБ #
Пишу гигабайт прямыми операциями ввода-вывода (oflag=direct, в обход кэша — так честнее, видим реальную скорость диска, а не RAM):
for i in 1 2 3; do
dd if=/dev/zero of=/mnt/nvme/.stress bs=1M count=1024 oflag=direct
done
Результаты трёх циклов:
| Цикл | Время записи 1 ГБ | Скорость |
|---|---|---|
| 1 | ~1,97 с | ~520 МБ/с |
| 2 | ~1,79 с | ~570 МБ/с |
| 3 | ~1,93 с | ~520 МБ/с |
Стабильно, без провалов. Чтение того же файла:
dd if=/mnt/nvme/.stress of=/dev/null bs=1M
# ~1,33 с на 1 ГБ ≈ 770 МБ/с
И классический замер:
hdparm -t /dev/nvme0n1
# Timing buffered disk reads: 2152 MB in 3.00 seconds = 717 MB/sec
Шаг 3. Линк не упал после нагрузки #
Самая важная проверка — повторно смотрю скорость линка и логи после стресс-теста:
cat /sys/class/nvme/nvme0/device/current_link_speed # 8.0 GT/s PCIe — всё ещё Gen3
dmesg | grep -iE "aer|pcie.*error|link.*(down|fail)|ltssm" | tail
Линк остался на Gen3 ×1, новых сообщений об ошибках или пересогласовании (LTSSM-ретрейнах) в логе не появилось. Файловая система отвечает на запись после нагрузки — данные не повредились.
Шаг 4. Не только dd — реальная нагрузка #
dd — хороший синтетический тест, но он не ответит на главный вопрос: выдержит ли адаптер длительную хаотичную нагрузку, когда на диск одновременно пишут база данных, объектное хранилище и очередь сообщений? Чтобы проверить, я поднял на Repka Pi 5 полноценный Docker-стек из 13 контейнеров:
- PostgreSQL — постоянные WAL-записи, индексы, транзакции
- MinIO — объектное хранилище, запись/чтение изображений (JPG 5–7 МБ каждый)
- Apache Kafka — очередь сообщений, постоянная запись сегментов
- ML-inference — 4 параллельных классификатора, читают модели из MinIO и пишут результаты
Docker data-root — прямо на NVMe через X1002. Все тома (postgres_data, minio_data, kafka_data) — на NVMe. Это профильная нагрузка, ради которой NVMe и нужен: смешанные случайные чтение/запись, много мелких операций + крупные файлы, 24/7 без перерыва.
Результат за 30+ минут непрерывной работы:
dmesg | grep -c "controller is down" # 0 — ни одного сбоя
cat /sys/class/nvme/nvme0/device/current_link_speed # 8.0 GT/s — Gen3, не деградировал
Ноль падений контроллера, ноль ретрейнов линка, ноль ошибок AER. В то время как на RPi5 тот же стек падал за полторы минуты (об этом ниже).
💡 Почему это важно. Синтетический
ddпишет один большой файл последовательно — это самая лёгкая нагрузка для контроллера. Реальная работа базы данных и хранилища — это тысячи мелких случайных операций в секунду, и именно они сильнее всего нагружают PCIe-интерфейс. Если адаптер держит такую нагрузку — он держит вообще всё.
Сравнение с предыдущим сетапом #
До X1002 гнал тот же диск через кейс Argon NEO 5. Сравнение:
| Параметр | Argon NEO 5 | Geekworm X1002 |
|---|---|---|
| Заявленный максимум | Gen3 (не ограничено) | Gen2 (по спеке) |
| Реальная скорость линка | Gen3 ×1 | Gen3 ×1 (вопреки спеке) |
| Чтение | ~757 МБ/с | ~717–770 МБ/с |
| Ошибки линка | нет | нет |
| Форм-фактор | Кейс с HAT-платой | Отдельная платка |
По факту — паритет. X1002 не уступает, хотя формально «хуже» по спецификации. Разница в скорости (~40 МБ/с) — в пределах погрешности и теплового состояния диска.
А что на Raspberry Pi 5? (спойлер: не работает) #
Пока собирался с Repka Pi 5, параллельно гонял тот же CUSU CV3500Q на Raspberry Pi 5 — через её родной PCIe-разъём (M.2 HAT, без X1002). И вот там всё пошло совсем по-другому.
Через ~100 секунд после загрузки, под I/O-нагрузкой Docker, ядро начинало сыпать:
nvme nvme0: controller is down; will reset: CSTS=0xffffffff
nvme nvme0: Does your device have a faulty power saving mode enabled?
nvme nvme0: Try "nvme_core.default_ps_max_latency_us=0 pcie_aspm=off pcie_port_pm=off"
Дальше — ext4 переводил файловую систему в read-only (Remounting filesystem read-only), journal падал, Docker зависал. Pi переставала отвечать. Перезагрузка — и снова те же ~100 секунд до падения.
Я перепробовал всё, что рекомендует сообщество (Raspberry Pi Forums, тред t=395256; Kuron's blog):
| Фикс | Результат |
|---|---|
Force PCIe Gen 2 (pciex1_gen=2) |
❌ упал через 105 сек |
Disable ASPM (pcie_aspm=off) |
❌ упал |
Disable NVMe APST (default_ps_max_latency_us=0) |
❌ упал |
| Force PCIe Gen 1 (2.5 GT/s, самая медленная) | ⚠️ проработал чуть дольше, но всё равно упал |
| Все четыре вместе | ❌ упал |
Диск при этом абсолютно здоров — SMART: critical_warning:0, media_errors:0, percentage_used:0%. Проблема не в SSD, а в PCIe-контроллере Raspberry Pi 5 — он фундаментально нестабилен с некоторыми накопителями (особенно бюджетными: CUSU, некоторые партии Samsung). Это не баг конкретного экземпляра, а класс проблем — на форумах сотни таких отчётов.
💡 Вывод. Тот же диск, который каждые 100 секунд падал на RPi5, на Repka Pi 5 + X1002 работает часами без единого сбоя. Разница — реализация PCIe-контроллера: RK3588 (Repka) стабильнее, чем Broadcom BCM2712 (RPi5), по крайней мере с бюджетными NVMe. Если планируете держать данные на NVMe — Repka Pi 5 надёжнее.
Это не значит, что RPi5 вообще нельзя использовать — с SD-картой она работает штатно (просто медленнее). Но NVMe на RPi5 — это лотерея: повезёт с конкретным экземпляром SSD или нет. С X1002 на Repka — не лотерея, а стабильный результат.
Бонус: корпус P580 #
Всё это хозяйство у меня живёт в корпусе P580 — это специальный корпус от Geekworm, спроектированный как раз под связку «плата + X1002» (адаптер встаёт внутрь на штатное место, всё прячется). Под Repka Pi 5 он почти полностью совместим: посадочные, стойки, вырезы под GPIO и pogo pins — совпадают.
Единственный нюанс, на который стоит обратить внимание при сборке, — вырезы под видеовыходы. Корпус проектировался под Raspberry Pi 5, а у неё и у Repka Pi 5 разъёмы отличаются: в P580 вырезы сделаны под HDMI, тогда как нужно под micro-HDMI. Функционально это ни на что не влияет — просто отверстия не совпадают по форме «один в один», при необходимости подгоняются надфилем или не используются (если монитор подключён иначе).
Подробный разбор корпусов под Repka Pi 5 — что подходит из «малинковой» экосистемы, что нужно подпиливать, а что встаёт идеально — я соберу в отдельном посте. Здесь упомянул только как факт: P580 с X1002 внутри — рабочий и аккуратный вариант.
Итоги #
| Параметр | Значение |
|---|---|
| Адаптер | Geekworm X1002 |
| Статус | ✅ работает |
| Линк по спецификации | Gen2-only |
| Линк реально | Gen3 ×1 (8.0 GT/s) — выше спецификации |
| Запись (direct I/O) | ~520–570 МБ/с |
| Чтение | ~720–770 МБ/с |
| Ошибки AER | 0 |
| Ретрейны / падения линка | 0 |
| Износ (SMART) | 0%, ошибок носителя нет |
| Реальная нагрузка (Docker, 13 контейнеров) | ✅ 30+ мин, 0 сбоев контроллера |
Geekworm X1002 подключился, определился и стабильно держит Gen3 ×1 под нагрузкой, хотя заявлен как Gen2-only. Выдаёт ~720 МБ/с чтения — столько же, сколько более дорогой кейс с отдельной HAT-платой. Ошибок линка и пересогласований за весь стресс-тест не возникло, то есть Gen3 тут не «на грани», а уверенно.
Пять уроков, которые я вынес #
- Спецификация — не приговор. «Gen2-only» у X1002, видимо, перестраховка производителя (длинный шлейф, трассировка). На практике Gen3 встаёт чисто. Но это не повод доверять слепо — проверять надо.
- «Линк поднялся» ≠ «всё хорошо». На скорости линка легко успокоиться и пропустить, что он держится ценой потока corrected-ошибок. Стабильность PCIe проверяют под нагрузкой: записью, чтением и контролем ошибок после.
- Сравнивайте с альтернативой. Когда есть второй сетап (тут — Argon NEO 5), прогон одного и того же диска на обоих сразу даёт честную картину, кто кого. X1002 оказался не хуже — а значит, более дешёвая платка имеет право на жизнь.
- «Диск отваливается» — сначала питание, потом софт. Периодическое пропадание NVMe под нагрузкой почти всегда означает, что по FFC-шлейфу не хватает тока, а не баг драйвера. Проверяйте pogo pins (плотный прижим к GPIO) и БП — и только потом лезьте в логи и конфиги.
- Стабильный адаптер не спасёт от выдёргивания шнура. На том же стенде накопилось 23 unsafe shutdown в SMART — от выдёргиваний питания без
poweroff. Каждый незавершённый shutdown повреждает journal ext4 и может перевести файловую систему в read-only. Для устройства «в чужие руки» обязателен UPS (хотя бы power bank с passthrough) +commit=1в fstab. Стабильность X1002 — это половина дела; питание без рывков — вторая половина.
Шпаргалка команд #
# Диск виден?
lsblk
ls /dev/nvme*
# Скорость и ширина линка (Gen3 = 8.0 GT/s, Gen2 = 5.0 GT/s)
cat /sys/class/nvme/nvme0/device/current_link_speed
cat /sys/class/nvme/nvme0/device/current_link_width
# Как ядро подняло линк
dmesg | grep -iE "rk-pcie|gen\."
# Ошибки PCIe AER
dmesg | grep -iE "aer|corrected|uncorrectable"
# Стресс-тест записи (1 ГБ, прямой I/O — в обход кэша)
dd if=/dev/zero of=/mnt/nvme/.stress bs=1M count=1024 oflag=direct
# Замер скорости чтения
sudo hdparm -t /dev/nvme0n1
# SMART и здоровье
sudo nvme smart-log /dev/nvme0
# Следить за отваливанием диска под нагрузкой в реальном времени
# (запустите в одном окне, стресс-тест — в другом)
dmesg -w | grep -iE "nvme|pcie|aer|link|reset"
Ссылки #
- Geekworm X1002 — wiki
- Repka Pi — официальный сайт
- Raspberry Pi PCIe FPC Connector Standard (datasheet) — пин-аут FFC-разъёма, который Repka Pi 5 повторяет