Начало

Всё началось с того, что пришлось портировать Carbon Reductor на популярные платформы типа Centos и Debian. Часть кода в результате изменилась, синхронизировать именения между ветками стало слишком сложно, плюс появилась ещё и userspace часть, используящая этот модуль. Иными словами, процесс существования Carbon Reductor выглядел так.

Реализация и отладка алгоритма в userspace

Всё просто - вместо модуля ядра мы пишем простую C’шную программу, которая использует другой способ получения данных. (в моём случае я просто константно объявил char[] black_url=”скопированный из tshark http пакет” вместо того, чтобы получать его из skbuff’ов).После реализации алгоритма всё гонялось valgrindом и grpofом до посинения и избавления от малейших warning’ов. Когда скорость работы алгоритма стала приемлемой даже для 10гбитного канала пришло время портировать всё в модуль ядра.

В ядре Ideco ACP 3

Чтобы один код компилировался и в ядре и в userspace пришлось создать следующую конструкцию#ifdef __KERNEL__пачка ядреных инклудов#elseпачка юзерспэйсных инклудов#endifи доводить список инклюдов ядра до ума. В принципе работает и ладно, но постепенно оно стало очень плохо читаться. В таком виде оно начало портироваться в ядро Carbon 5.

В Carbon 5

При портировании на ядро 2.6 возникло много затыков, пришлось сильно переписывать функцию match, менять кучу include’ов итд, конструции в виде#ifdef KERNEL#if LINUX_VERSION_CODE < KERNEL_VERSION(2,4,38)действия в ядре 2.4#endif#if LINUX_VERSION_CODE > KERNEL_VERSION(2,6,31)действия в ядре 2.6#endif#elseдействия в userspace #endifвстречались чуть ли не через каждые 100 строк. Читаемость кода адски упала, но тем не менее всё удалось завести.

Centos и Debian

Что приятно - в 2.6.32 ядре CentOS и Debian Carbon Reductor завёлся вообще без проблем, за исключением подгрузки самого модуля. Через хаки в виде создания chroot-jail с нужными /lib/modules/ и сборкой с отключенной проверкой версии всё заработало и там и там.Дальше изменения вносились в основном в userspace часть - подгрузку списков, автоматическое обновление, итд. Ядерную часть трогали очень редко, поэтому изменения опять же не синхронизировались, исправления в Ideco ACP не попадали, а я от всего этого понемногу впадал в депрессию.Вдобавок, чтобы хоть как-то понимать что происходит в написанном достаточно давно коде, пришлось пойти на жесткие меры - удалить из Centos версии все упоминания о userspace и ideco acp коде.

Имеющийся беспорядок

Что я имел в результате относительно недавно?4 разных ветки эволюции одного кода, которые предстояло скрестить.Carbon reductor работающий в userspace, служащий для тестов основных алгоритмов разбора URL.Carbon reductor работающий в ядре Ideco ACP, несколько заброшенный, так как внимание уделялось CentOSCarbon Reductor для Debian, отлично работающий у пары клиентов, так что трогать его было страшно.Carbon Reductor для CentOS, самая идеальная версия, содержащая самый актуальный код, новые фичи и оптимизации.Наводим порядок в море хаосаВсе алгоритмы связанные с разбором URL вынесены в common_reductor.c, все include/define итд вынесены в common_reductor.c и удалены из ipt_reductor.c и xt_reductor.c. Потом они (ipt_reductor.c и xt_reductor.c) приведены к максимально схожему состоянию и в начале добавлен include “common_reductor.c”. На самом деле это достаточно криворукий подход, но “делать нужно было завтра” и это уже гораздо лучше того что было.

GITуем

В результате удалось получить универсальные common_reductor.c и common_reductor.h, которые работают и в userspace и во всех ядрах. Они разместились в отдельном репозитории, common_reductor, который при каждой сборке на любой из систем сборки (debian, ideco, carbon, centos) подтягивается на неё и кладётся в нужное место, в результате изменения  в алгоритме автоматически попадают во все ветки.

Результат

Жизнь стала легче, жизнь стала веселее. В скором времени допишу что случилось с зависимыми от версий ядра исходниками, как изменения кочуют между ними и как системы сборки сами стали отдельным копируемым и легко настраиваемым “продуктом”. :)

Мораль

Используйте GIT. Разделяйте код на разные уровни - связанный с платформой и ваши алгоритмы.Выносите повторяющийся код в библиотеки, лучше только основные алгоритмы не привязанные к платформе исполнения.Вместо вложенных #if LINUX_VERSION_CODE < KERNEL_VERSION лучше разнесите отличающийся код в разные исходники и хедеры, относящиеся к конкретным версиям ядра.Имейте userspace версию и не забрасывайте её. Вне ядра исправлять алгоритмы значительно легче.