Open Source силён, когда это улица с двусторонним движением.

В основе uStor — нашей программно‑определяемой системы хранения — используется несколько Open Source‑компонентов. Один из них — SPDK (Storage Performance Development Kit), на котором построен транспортный слой: высокоскоростной target классических протоколов доступа к томам (iSCSI и NVMe‑oF поверх TCP) в пользовательском пространстве.

Когда в продукте используется внешний код, рано или поздно возникает вопрос: что делать с найденными в нём проблемами. Можно исправлять их у себя в форке и тихо жить дальше. А можно возвращать исправления в upstream, и тогда выигрывают все, кто пользуется SPDK, включая нас же в следующем релизе.

Мы выбираем второй путь. За последнее время мы отправили в upstream SPDK три исправления — все приняты мейнтейнерами проекта.

Что мы исправили

У всех трёх багов общая черта: они не проявляются на штатной нагрузке. Такой код спокойно компилируется, проходит обычные прогоны и выглядит рабочим. Проблема выстреливает в граничных условиях, а для системы хранения это как раз те режимы, где нужна предсказуемость, потому что за ней данные.

iSCSI: неверный порядок аргументов в проверке redirect‑портала

В коде проверки перенаправления портала функция поиска вызывалась с переставленными аргументами: сигнатура ожидает (pg, host, port), а на вход уходило (pg, port, host).

Внешне код компилируется и работает без ошибок — до тех пор, пока перенаправление реально не понадобится. В этот момент логика поиска портала начинает вести себя непредсказуемо. Мы вернули корректный порядок аргументов.

Патч: https://review.spdk.io/c/spdk/spdk/+/28152

blobstore: потенциальный double‑free при ошибке выделения памяти

При загрузке информации о занятых кластерах, если не удавалось изменить размер битового массива, буфер ctx→mask освобождался дважды: сначала в колбэке, затем повторно в обработчике ошибки.

Классический double‑free в нештатной ветке — при нехватке памяти, когда система и без того работает на пределе. Мы убрали повторное освобождение.

Патч: https://review.spdk.io/c/spdk/spdk/+/28357

bdev_opal: разыменование NULL при нехватке памяти

В RPC создания opal‑bdev результат spdk_sprintf_alloc() использовался без проверки на NULL. Если выделить память под имя устройства не удавалось, код продолжал работу с нулевым указателем.

Теперь при сбое ошибка логируется, клиенту возвращается корректный internal error, а функция безопасно завершает обработку.

Патч: https://review.spdk.io/c/spdk/spdk/+/28153

Инженерная культура: мы читаем код, от которого зависим

Мы не относимся к SPDK как к чёрному ящику, который «просто работает». Код, который попадает в uStor, мы читаем и гоняем на собственных нагрузочных и отказных стендах через fault injection: искусственно создаём нехватку памяти, обрывы и отказы в самый неподходящий момент. Именно там такие баги и находятся — у нас, до того, как что‑то дойдёт до заказчика.

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

Отвечая за разработку в MIND Software, я считаю такой подход рабочим правилом: если зависишь от чужого кода в проде, умей его читать, чинить и возвращать фиксы обратно. И это касается не только SPDK, а всего Open Source, который мы используем: находим у себя, исправляем, отправляем в upstream. Три патча — это не разовая история для поста на Habr, а обычная практика работы с чужим кодом в наших продуктах.