Linuxカーネルの独自パッチのメンテに関する検討メモ

@satoru_takeuchiさんの記事に触発されて、不足分を補う意味で書きました。
[linuxカーネルに関する独自コードをメンテナンスするコスト](https://qiita.com/satoru_takeuchi/items/98b6115b8ff271f8b0d1)


# 独自パッチのメンテについて

Linuxカーネルに限らず、OSSあるいは他人のソフトウェアへの改変部分を独自に持ち続ける場合、元になったソフトウェアの更新に追従するかどうかを検討する必要があります。

## 更新に追従しないリスクの検討

追従しないというのも一つの選択肢ではあります。むしろ「売り切り」で「保守しない」商売をしている場合、追従コストを度外視するのが当たり前に行われてきました。追従しない場合は追従しないことによるリスク（損失も含めて）を検討することになるでしょう。ざっくり考えると以下のようなものがあります。

- 脆弱性に対するセキュリティリスク
- バグによる不具合の発生リスク
- ハードウェア更新が出来ないリスク
- 新機能が使えないことでビジネスが不利になるリスク
- 新しいバージョンへの乗り換えコストが増加するリスク

個々のリスクを詳しく見ていきましょう。

#### 脆弱性に対するセキュリティリスク

これは説明するまでもないですが、ソフトウェアには脆弱性が潜んでいます。OSSも例外ではなく、[日々新しい脆弱性が見つかっています](https://blog.honto.info/)。そして日々修正されているわけです。アップストリーム追従をしないということは、これらの修正を受け入れないということです。ちなみにLinuxカーネルには年間大体100件程度の脆弱性が見つかっています。1年前のLinuxを放置し続けるということは、この1年間に見つかった脆弱性を晒し続けるということになります。
セキュリティについては、このリスクがどれだけ波及的損失をもたらすかを検討する必要もあります。特に客先に設置するような場合、客先で発生したセキュリティ侵害の責任を取る必要が出てきます。自社で使うシステムでも、顧客から預かっているデータが流出する可能性も検討しなければなりません。

#### バグによる不具合の発生リスク

また、バグ報告はほぼ毎日のように上がってきています。バグ修正しかしないStable Kernelでは、[3ヶ月で1100程度のパッチ](https://lwn.net/Articles/750054/)が入っているので、一日大体十数個の修正パッチが入っているということになります。アプリケーションが異常な動きをした時に、システムごとダウンする可能性も残されているということです。サービスレベルの大幅な低下を招いても良いでしょうか？

#### ハードウェア更新が出来ないリスク

アップストリームでは新しいハードウェアへの対応が日々追加されています。標準化されているサーバであっても新しいプロセッサへの対応コードが入っていなければ、効率良く動かし続けることは出来ないでしょう。標準化されていない組み込み機器はなおさら問題です。
例えばサーバ機器を同じカーネルで3年間使い続け、さて性能が足りなくなったので乗り換えようとした時に、同じカーネルを使い続けるでしょうか？3年ならまだしも5年たったら？

#### 新機能が使えないことでビジネスが不利になるリスク

Linuxカーネルは日々更新されており、新機能がどんどん追加されています。独自機能に固執するあまり、その他の機能への追従が出来ないままでは、他社との比較時に不利になりませんか？　差別化機能が、いつの間にか他社から差別される要因になり、足を引っ張るリスクというのも考えないといけません。

#### 新しいバージョンへの乗り換えコストが増加するリスク

では数年ごとに新しいバージョンへ乗り換えればいいのではないかということになりますが、それはそんなに簡単なことでしょうか？アップストリームのコードは日々更新されています。独自パッチと衝突するような修正が入っていないとも限りませんし、独自パッチが前提にしていた機能がなくなっている場合や、度重なる修正によってAPIの意味が変わっている可能性もあります。
また完全な乗り換えが可能ならまだ良いですが、旧バージョンのサポートも終わっていない場合、二重開発が必要になり、どんどんコストは膨らんでいくことになります。

### リスクを検討しないでいい場合

取り敢えずリスクを考えなくていい場合というのは、以下のような条件ではないでしょうか。

- 試作品であって製品版ではない、あるいは一時的に使うもの
    - 例）研究開発の過程での開発機など
- 期間限定的に導入する機械とセットになっていて、外部からの侵入経路は物理的に存在せず、完全にテストされた動作を繰り返すようなもの
    - 例）秘密工場にある遠心分離機など（ただしUSBキーなども挿せなくすること）
- リスクを全て個人の責任で引き受けられるもの
    - 例）逸般人の誤家庭用サーバなど

### アップストリームに追従するコストよりリスクが安いと思う場合

リスクは損失ｘ機会で期待値を出すことが出来ますが、機会が少なくても一発で受ける損失が大きい場合は賭け事になります。逆に機会が多くてもなあなあで済ませられる自信があるなら、リスクは主観的に小さく出来ます。これは完全に主観なので、読者がどう思うかに任せられます。

前述の通り、Linuxカーネルも日々問題が発見され、修正されています。その中のどれがあなたのシステムにヒットするかどうかはわかりませんが、一年後も安心して使い続けられる確率は結構低いのではないかと思います。

## メンテナンスのトレードオフ

Linuxカーネルの独自パッチ・独自ドライバをメンテナンスする条件として、以下のようなものが考えられるでしょう。

- 独自パッチをメンテナンスしていい場合
    - メンテナンスの期間が決まっている
    - バージョンアップが多少遅れても大目に見られる
    - メンテナンスコストを負担する余裕がある
    - メンテナンスが可能なスキルを持っている
    - そこそこ枯れたサブシステムで独立性が高いモジュールである
- 独自パッチを独自にメンテナンスすべきではない場合
    - 上記以外の全て

### メンテナンスコストの算出

メンテナンスのコストはベースのソフトウェアによって変わります。ベースになるソフトウェア（サブシステム）の規模×更新頻度と、独自パッチの規模をかけ合わせたものに比例することになります。
また、独自パッチの独立性は非常に重要な要素になります。元のソフトウェアのAPIやコードに対して、どれだけ変更を加えるか、あるいは呼び出しを行うかによって、メンテナンスコストは増加します。

ベースのLoC：SLoC
毎月の更新LoC：dLoC
独自パッチから呼び出すベースの関数・マクロ数：CF

（SLoC）× dLoC × CF

ざっくりとこんな感じでコストの規模感の予想が出来るでしょう。

実際のコストは、このメンテナンスをするエンジニアがどれだけスキルを持っていて、どれだけ人件費がかかるか、対応するエンジニアが居なくなる確率×期間、独自パッチ及びベースソフトウェアのテストの網羅率にも依存します。

- メンテナンス期間が長くなれば長くなるほど、エンジニアの離職率が上がるためリスクも増えるでしょう
- 機能テストとして網羅率の高いテストセットを持っていれば、エンジニアへの依存度を低下させることが出来ます
- 但しテストセットが大きくなればなるほど、テストセットのメンテナンスコストもかかることになります

ちなみに、この計算は複数バージョンへの追従コストは勘案せず、更にEOL（End of Life）になったバージョンのメンテナンスコストも計算に入れていません。EOLカーネルのメンテナンスコストについては今回の範囲外としますが、基本的に人外のスキルが必要になると考えてください（ざっくり言うと世界でも100人ぐらいしかいないスキルセットとマインドセットのエンジニアが必要です）。

### 独自パッチの価値の算出

独自パッチをいつまでも抱え込む場合には、コスト・リスクの他に、パッチの価値の算出をする必要があります。これは個々のビジネスに依存するのでなんともいえないところですが、算出はしておくことと、パッチの価値は時間と共に減少することを考えておきましょう。

**もしその機能が価値のある重要な機能なのであれば、遅かれ早かれ他の人もそれを作るのです。**

ですから、よっぽどのことがない限り、独自パッチを価値の源泉として囲い込もうとするのはやめましょう。むしろ、先にアップストリームに入れ、自らアップストリームのメンテナンスを行うことで、独自機能をベースにしたサービスなどに力を注ぎ、その分野での露出を高めて顧客にアピールすることを考えたほうがお得でしょう。


## メンテナンスの戦略

### ベース選択によるメンテナンスコストの変化

独自パッチのメンテナンスにおいて重要なのが、ベースとするリリース（バージョン）の選択です。Linuxカーネルは安定版とベータ版と開発版がありますが、基本的に安定版をベースにすれば日々のメンテナンスコストは減らすことが出来ます。
ただしこの方法は「脆弱性」と「バグ対策」のリスクは無くせるものの、「新機能対策」や「新ハードウェアへの対策」についてのリスクは減らせないので注意が必要です。

#### Linuxカーネルの安定版リリース

Linuxカーネルはリリースされると安定版となり、バグや脆弱性の修正のみがバックポートされるようになります。リリースされるまでは4.19-rc1など、"-rcX"が付いています。リリースされた後は、4.18.4など、リビジョン番号が追加されるので、見分けることが簡単に出来ます。
Linuxカーネルの安定版には、長期メンテナンスされるリリース（LTS：Long Time Support）と、そうでないリリースが存在しています。Linuxカーネルは年に5回、大体2ヶ月半毎にリリースが行われるのですが、通常新しい安定版がリリースされると、2つ前の安定版はEOL（サポート終了）になります（1つの安定版は5ヶ月程度メンテされることになる）。
しかし、年に1つの安定版リリースがLTSとして選ばれ、最低2年間のメンテナンスが行われます。過去には、3.18, 4.1, 4.4, 4.9, 4.14が[LTSカーネルとして選ばれました](https://www.kernel.org/category/releases.html)。

|Version |Maintainer|Released|Projected EOL|
|:------:|---------:|--------:|----------:|
|4.14	|Greg Kroah-Hartman|2017-11-12|	Jan, 2020|
|4.9	|Greg Kroah-Hartman|2016-12-11|	Jan, 2019|
|4.4	|Greg Kroah-Hartman|2016-01-10|	Feb, 2022|
|3.16	|Ben Hutchings|2014-08-03|Apr, 2020|

最低2年というのは、中には4.4のように6年間の長期保守が予定されているものや、Greg K.H.以外のメンテナが自発的に長期サポートを続けているカーネルなどがあるためです。

#### Linuxカーネルの開発サイクル

Linuxカーネルの開発版は-rcXだけではありません。-rcXは既に3ヶ月後にはリリースされることが決まっているパッチだけが入っていると考えてください。そして基本的には-rcXカーネルは「ベータ版」です。つまり出来る限りバグを出して修正していくためのプロセスの途上のカーネルです。では本当の開発版はどこにあるかと言うと、個々のカーネルメンテナがサブシステム毎に持っているgitツリーの中にあります。このgitツリーから-rcXに対してPull Requestを出してLinusのツリーにマージしてもらうのですが、実は基本のルールとしては以下のようになっています。

- 安定版リリースと次の-rc1リリースの間に2週間あるから、その間にPRを投げる。
- -rc1をリリースした後は、基本的にはバグ修正しか受け付けない
- 個々の開発者が-rc1後に機能追加を出した場合、**その次の安定版リリース後**にLinusツリーに送られる

更に言うと、いきなり-rc1で色んな所からPRが来てマージしたら衝突しまくりますよね。それを事前に回避するため、linux-nextという実験的マージツリーも用意されていて、こちらの方でLinusツリーに行く前のパッチをマージして衝突を確認しています。

前述の通り、安定版リリースの間隔は約3ヶ月ですので、機能追加パッチについてはサブシステムのメンテナのツリーにマージされても、安定版リリースに入るのは最短で3ヶ月、最長で半年近くかかることになります。

### ベースバージョン選択の戦略

今後独自パッチをどのように扱うかを決めた上で、ベースバージョンをどれにするかを決めることになります。

- 独自パッチはもう独自でメンテする
- 独自パッチをアップストリームに入れず短期には製品に出したい
- 独自パッチのアップストリーム化をしたいけど製品も出したい

#### 独自パッチはもう独自でメンテする

茨の道を選ばれたあなたに必要なのはLTSカーネルです。どのくらいの期間メンテが必要ですか？LTSカーネルのEOLを越えてメンテするなら、相当の覚悟が必要です。なぜなら自ら修正パッチをバックポートし、テストを行いリリースしていかねばならないのですから・・・。
LTSカーネルでは短いという場合は、長期サポートをしているディストリビューションのカーネルをベースにすることも検討していいと思います。でもあなたの代わりに頑張っているディストリビュータへの貢献はしましょう。またディストリビュータがEOLにした後のことも考えないといけません。

#### 独自パッチをアップストリームに入れず短期には製品に出したい

売り切り製品によくある悩みです。この場合、製品のライフサイクルをカバーできるLTSカーネルを選択して移植し、その上でメンテナンスし続けることになります。ただしLTSカーネルがEOLを迎えると同時に、独自パッチを諦めるか、あるいは新しいLTSカーネルに移植するコストをかけるかを考えないといけません。
LTSカーネルの寿命も2年と短いので、出来る限り新しいLTSを選択し、製品化直前にも最新のLTSに移植することを考えたほうがいいでしょう。

#### 独自パッチのアップストリーム化をしたいけど製品も出したい

おめでとうございます。独自パッチのアップストリーム化を目指す心意気やよしです。製品にはその時最新のLTSを採用しましょう。大変ですが、同時に最新の開発版カーネルにも移植し、日々更新をしながらアップストリームに投稿しましょう。LTSのメンテ期間が尽きるまでにはアップストリーム化を終える意気込みで進めるのが良いでしょう。

# おわりに
個人的には独自パッチをアップストリームに入れないなんてとんでもない、という思いではあるのですが、アップストリーム活動に時間とコストがかかることもまた事実です。最低でもメンテナンスはきっちり行って欲しい、メンテナンスをしないことによるリスクも十分大きいんだよ、ということを明確にしたいと思ってこの文を書きました。