pciutilsにオレオレパッチをportageのuser-patch経由で当ててリンク速度を気楽に見る

- 2019/12/16 改造後のコマンド実行結果の修正(コピペミス、構文ミスetc)

#要約:PCIeのリンク速度を手軽に見たかった

これは[IPFactory Advent Calendar 2019](https://qiita.com/advent-calendar/2019/ipfactory)の15日目の記事です。  

IPFactory所属のLagryzと申します。  

普段は部室でゴソゴソケーブル弄ったりラックと戦ったり故障品の交換に走り回っているような人間です。
なのであまりコード書いたりしていないので他の方のように強いという訳でもありませんが、
サークルで始めてのAdventCalendarという事で軽く記事を書かせて頂きたいと思います。

QiitaもAdvent Calendarも始めてとなりますのでどうかお手柔かにお願い致します。


##みんなだいすきPCI Express

<img src="https://upload.wikimedia.org/wikipedia/commons/e/e5/PCI_Express.svg" alt="ここにPCI-Expressの画像を入力" width=40%>
ある程度PCに詳しい方ならだいたいご存じの[PCI Express](https://ja.wikipedia.org/wiki/PCI_Express)。
特に設定もせず挿すだけで数十Gbpsの帯域が手に入る素晴しい接続規格です。[^1]
名前聞いて思い出せない方もグラボの端子とか言えばわかるかもしれません。[こんなのです](https://www.google.com/search?q=pci+express&tbm=isch)。
最近だと[Thunderbolt3って名前でPCから外出し始めたり](https://ja.wikipedia.org/wiki/Thunderbolt#Thunderbolt%E3%81%A8USB%E3%81%AE%E6%AF%94%E8%BC%83)、[RasPiにも生えていたり](http://mloduchowski.com/en/blog/raspberry-pi-4-b-pci-express/)と活躍の場が広がっている様ですね。
新MacProにも~~魔改造~~[少し拡張された](https://www.apple.com/jp/mac-pro/#section-graphics)カードが導入されましたし。

自分もサーバにNICやHCAをあれこれ入れて検証している関係でよくお世話になっております。  

PCI Expressはプロトコル的にも柔軟で、レーンという単位(x4やx16等の表記)で帯域を分割したり纏めて太くしたりできるという特徴があります。
後方互換性も素晴らしく、最新のカードでもPCIe1.1スロットに刺さり通信できます。[^2]  

また高速シリアル通信規格の例に漏れず中身はパケット通信であったり、
物理層(LVDS)や符号化(8b10b/128b130b)なんかも調べてみると高速通信における敵が見えてくるので面白いです。
他の通信規格(USB等)にも参考とされているだけはあります。  

## lspci/pciutils
こんな素晴しいPCIeですが、後方互換性がしっかりしすぎている為に物理故障やスロットが無効化されている等という訳でもなければヘンな組み合わせでも大抵問題なく動作してしまいます。

この為、x16ポートに刺していたと思ったのに実際のリンクはx8やx4だったり[^3]、システムの対応バージョンがわからず
実際にはどの速度で動いているか把握できずに遅い速度で動いてしまう事があります。[^4]
またこの場合にも特に能動的な通知がある訳でもないので余計気づきにくいです。

Windowsマシンならドライバが動作しているデバイスであればデバイスマネージャからバージョンやリンク幅を参照できますが、
Linuxシステムの場合は珍しく手間がかかり、[pciutilsというパッケージ](https://github.com/pciutils/pciutils)で提供されている`lspci`というコマンドを用いる事で
デバイスの情報を取得できます。[^5]  

```
root@system ~ # lspci
00:00.0 Host bridge: Intel Corporation Xeon E7 v4/Xeon E5 v4/Xeon E3 v4/Xeon D DMI2 (rev 01)
00:01.0 PCI bridge: Intel Corporation Xeon E7 v4/Xeon E5 v4/Xeon E3 v4/Xeon D PCI Express Root Port 1 (rev 01)
00:01.1 PCI bridge: Intel Corporation Xeon E7 v4/Xeon E5 v4/Xeon E3 v4/Xeon D PCI Express Root Port 1 (rev 01)
...
```

こんなある意味で最大の相棒であり最後の砦でもある`lspci`コマンドなのですが、一つ個人的に気に食わない事があります。  
それは

### リンク速度見るのが面倒

という事です。  

どういう事かというと**lspciにはリンク速度を羅列するオプションは存在しないのです。**  

「じゃあどうやって見るっていうんだ」とか「Windowsに負けてるぞ」とか聞こえてきそうですが、一応方法はあります。  

それは**`-vv`オプションを用いて全ての情報を吐き出した上でsedやgrepで整形することです。**
いちいちコマンド打つのが面倒なら**古き良きシェルスクリプトに固めましょう。**

しかしこの方法も万能ではありません。
それは**新しい環境で使用するのが難しい**という事です。

開発や運用でもコンテナやVMを使うなんて当たり前な時代、なんなら物理デバイスの検証だって今時はVMやコンテナ経由で行なうような時代です。[^6] [^7]
開発環境と同じく一度作った環境を後生大事に保守して検証...なんて事はあまりせずに作って使ったらとっとと捨てるような環境においては
オレオレな`.sh`を作成したところで各環境に用意するのが面倒だったり不可能だったりします。  

実際自分で一回スクリプトを作って`~/`や`/usr/bin/`に放り込んでいた事もあったのですが、
普段使わないので存在自体を忘れたまま必死に`lspci -vv`をシェル上で整形してばかりでした。  

そこで私は思いました。
**欲しいなら機能を付け足せばいいのでは？**と。

## lspciを改造する
前置きが長くなってしまいましたがやると決めたら早速弄っていきたいと思います。

### 環境

#### OS:Gentoo/Linux
みんなだいすきジェンツーペンギン。
言わずと知れた(?)ディストリビューションですね。  
私もサブマシンにインストールしてデスクトップ環境として使っているのですがコンパイルの手間に目を瞑れば
portageという柔軟かつ強力なパッケージシステムと十分にメンテナンスされた山ほどのパッケージにより
非常に軽量で快適な環境が作れて非常に気に入っています。  

実は言う程怖くないとか案外ハマれば他のディストリなんかより楽とかいう話もしたいのですが時間が無いので今回はあまり説明しません。  

ただし今からこいつを新規にインストールするような時間はないので[^8]出来合いのGentooを使っていきます。[^9]
出来合いというのはLXDコンテナの事で、なんとGentooなんかのディストリビューションまで[コンテナイメージが作成され配布されている](https://us.images.linuxcontainers.org/)のです。
普通のアプリを動かす程度ならこのイメージで問題ないのでサクっとコンテナを立てちゃいましょう。
今回使用したイメージは`gentoo-current-default_20190718_amd64.tar.xz`です。
またリポジトリのsyncは一切行わないまま使用します。

#### コンテナホスト:Proxmox VE
コンテナ起き場についてはたまたま個人で[Proxmox](https://pve.proxmox.com/wiki/Main_Page)というKVMベースのハイパーバイザを使用しておりこれが丁度LXDをサポートしているのでこれを使用します。
正直LXDが動けばいいので他のホストLinux環境で動かしても問題ないと思います。  

ただデバイスを直接扱うので念の為に`Privileged`コンテナとして動かして`nested=true`に設定しておきました。無くてもいいかもしれませんが。
WindowsやMacの方はなんかがんばってください。  

#### Portageと`/etc/portage/patches`
ご存じの方も居るかと思いますがGentooのソフトウェアインストールはソースコードを用意してローカルでビルドを行いシステムにインストールするという工程になります。  
この為インストールに非常に時間がかかる反面、自分でビルドオプションやコンパイラオプションを指定する事によりバイナリの軽量化や依存関係のコントロールを自分で行う事ができるメリットがあります。[^11]  

また自前でソースからコンパイルするという特性上、**[パッケージマネージャ管理下のソフトウェアに自前のパッチを適用する事も可能](https://wiki.gentoo.org/wiki//etc/portage/patches)**となっております。

少し前までは全てのパッケージで提供されておらず、パッケージマネージャのhookを用いたりパッケージごとに任意で提供されている手法で適用していたようですが
最近(EAPI6以降)のパッケージであれば`/etc/portage/patches/[パッケージカテゴリ]/[パッケージ名]`というディレクトリを作成し、
そこに`.patch`ファイルを放り込めばインストール時に自動で適用してくれます。
またフォルダ名でバージョンを指定する事により特定のバージョンのみで適用する等の選択が行えます。

今回は常に適用して欲しいので`/etc/portage/patches/sys-apps/pciutils`ディレクトリを作成しました。

### パッチを作っていく
あまり書いてる時間もないので[^10]簡単な説明しかできません。

改造する元のソースコードについてはGentooが落としてくるものをそのまま使用します。

```
# emerge -f sys-apps/pciutils
```
ダウンロードされたファイルはデフォルトでは`/var/cache/distfiles/`に放り込まれます。
今回は`pciutils-3.5.6.tar.xz`をここからコピーして回答して改造していきました。

#### パッチ内容
本当はもっと内容について解説したかったのですが、とりあえず投稿したいので後で解説追記する予定です。  
改造についてはlspci自体は機能が綺麗に分割されており非常に上手く纏められたコードになっています。
綺麗に分割されすぎており、メインプログラムの`lspci.c`のみの変更だけでなく
PCI Expressの情報を表示している`ls-caps.c`まで改造が必要になりそうで震えておりましたが、
~~泥臭い~~努力の結果なんとか1ファイルのパッチのみで済ませる事ができました。
パッチは[ここ](https://github.com/laguryz/pciutils-mod)で公開しております。  

では放り込んでいきましょう。  

### 適用
```
# mkdir /etc/portage/patches/sys-apps/pciutils -p
# curl -o /etc/portage/patches/sys-apps/pciutils/lspci.patch -L https://raw.githubusercontent.com/laguryz/pciutils-mod/master/lspci.patch
  % Total    % Received % Xferd  Average Speed   Time    Time     Time  Current
                                 Dload  Upload   Total   Spent    Left  Speed
100  4090  100  4090    0     0  17184      0 --:--:-- --:--:-- --:--:-- 18423
# emerge sys-apps/pci-utils
...
>>> Emerging (1 of 1) sys-apps/pciutils-3.5.6::gentoo
 * pciutils-3.5.6.tar.gz BLAKE2B SHA512 size ;-) ...                                                                                                                                                       [ ok ]
>>> Unpacking source...
>>> Unpacking pciutils-3.5.6.tar.gz to /var/tmp/portage/sys-apps/pciutils-3.5.6/work
>>> Source unpacked in /var/tmp/portage/sys-apps/pciutils-3.5.6/work
>>> Preparing source in /var/tmp/portage/sys-apps/pciutils-3.5.6/work/pciutils-3.5.6 ...
 * Applying pciutils-3.1.9-static-pc.patch ...                                                                                                                                                             [ ok ]
 * Applying lspci.patch ...                                                                                                                                                                                [ ok ]
 * User patches applied.
 * Will copy sources from /var/tmp/portage/sys-apps/pciutils-3.5.6/work/pciutils-3.5.6
 * abi_x86_64.amd64: copying to /var/tmp/portage/sys-apps/pciutils-3.5.6/work/pciutils-3.5.6-abi_x86_64.amd64
>>> Source prepared.
>>> Configuring source in /var/tmp/portage/sys-apps/pciutils-3.5.6/work/pciutils-3.5.6 ...
 * abi_x86_64.amd64: running multilib-minimal_abi_src_configure
...
>>> Installing (1 of 1) sys-apps/pciutils-3.5.6::gentoo

 * Messages for package sys-apps/pciutils-3.5.6:

 * User patches applied.
>>> Auto-cleaning packages...

>>> No outdated packages were found on your system.

 * GNU info directory index is up-to-date.

 * IMPORTANT: 6 news items need reading for repository 'gentoo'.
 * Use eselect news read to view new items.
```

### 実行

```
# lspci -l
...
06:00.0 Ethernet controller: Intel Corporation I211 Gigabit Network Connection (rev 03)
        Link Info: 
                Capability: Port #0, Speed Gen1(2.5GT/s), Width(lanes) x1
                LinkStatus: Speed Gen1(2.5GT/s), Width(lanes) x1
07:00.0 Network controller: Intel Corporation Wireless 3160 (rev 83)
        Link Info: 
                Capability: Port #0, Speed Gen1(2.5GT/s), Width(lanes) x1
                LinkStatus: Speed Gen1(2.5GT/s), Width(lanes) x1
08:00.0 USB controller: ASMedia Technology Inc. ASM1142 USB 3.1 Host Controller
        Link Info: 
                Capability: Port #1, Speed Gen2(5GT/s), Width(lanes) x2
                LinkStatus: Speed Gen2(5GT/s), Width(lanes) x2
...
```

あっさりと適用できました。

##さいごに
「折角ソースからコンパイルする環境に生きているのにデフォルト設定ばかりだな」とか思っていたところにカレンダーの話がきたのでやってみました。
なにげなく使っているツールや環境に対する自分の理解を深める為にちょっとだけのつもりでやってみた改造でしたが、思いの他大事になっった印象でした。
ただ得られたモノは大きく、また曖昧だった部分も自分で中身を探った事により理解が深まった気がします。  
多分しばらくやらないとは思いますが良い経験になりましたので、皆さまも是非自分の環境でOSSを改造していってみてください！


***
[^1]: ドライバやデバイス自体の設計コストについては考えないものとする
[^2]: 電気的に問題ないのとPCIeレイヤでの通信が行えるだけでシステムとして動作するかは別
[^3]: 一般ユースでは[あまり気にする必要はない](https://pc.watch.impress.co.jp/docs/topic/feature/1082967.html)(x16なグラボをx8で動かしても程度ならベンチに差が出る程度)
[^4]: これも重いゲームしたりストレージぶら下がってたりGPGPUしたりInfiniband/10/40/100GEでも使わなきゃあまり変わらない
[^5]: 自分はこれしか知りませんので他に良いものがありましたら教えてください
[^6]: 最近はPCIパススルーやRawデバイスマッピング、VirtIOやSR-IOV等の仮想化支援機能が充実し熟れてきたので軽い気持で使えます。自分も1台のサーバにInfinibandのHCAを2枚押しこんだ上でPCIパススルーにてVMにHCAを渡してやってRDMAの検証環境を構築していますが以前に踏んだバグ(RDMA通信のみ行えない)もしっかり直っており関心しました。  
[^7]: [code43](https://www.google.com/search?q=geforce+code+43)なんてなかった
[^8]: ほぼ全てソースからコンパイルなので非常に時間がかかる 自分がラップトップにインストールした時には16時間かかりました
[^9]: 怒られそうな表現
[^10]: 15日越えそう
[^11]: これがGentooの提供する「選択」です。