Proxmox ve 8.3.2、カーネル 6.8.12-5-pveで起動後にマザーボード内蔵NICが消失する

結論としてはRealtek RTL8111Hのr8168ドライバを最新版ビルドして入れれば良い

# 前提; システム構成

- Host OS: Proxmox VE 8.3.2
- Kernel: 6.8.12-4-pve
- マザーボード: ASRock B550M Pro4
- NIC: Realtek RTL8111Hチップ


```
# pveversion 
pve-manager/8.3.2/3e76eec21c4a14a7 (running kernel: 6.8.12-5-pve)
```


## 発生再現

ゲストOS側でUSBパススルーをしたいが、リソースマッピングを簡単にするため単独でIFが欲しく、USB3.0インタフェースPCIeカードをAmazonで適当に買ったので、刺して追加して起動すると、何故かProxmox veが立ち上がると同じタイミングでNICのLINK UPが消失し、LEDすら光らない。

この状態では、管理画面すら入れないので、なにもできなくなる。まず追加USBインターフェースカードを取るとNICはLINK UPしたままの状態が維持される（消失されない）、カードの指すPCIeスロットを変えても挙動に変化なし。

ちなみにProxmox veじゃなくてDebianとかUbuntuでも起こるんじゃないかな。

## 問題点

そもそもカニさんチップ乗ってるマザーボード使ってる方が悪い。

…とはいえ内蔵NICがこれなんだから仕方ない。なんかどうもaptで勝手に入るデフォルトの`r8169`ドライバくんは最新カーネルちゃんと対応できてないらしい。なんじゃそら、NICごときにドライバを手動ビルドとは20年前のdebian woodyでもいじってる気分だ。

- `ip a`でNICが認識されるものの、リンクが確立せず通信不可。
- `dmesg`に`r8169`に関連するエラーやリンク確立失敗のログ。

これらがあったらそうかもね. `lspci`では以下のように認識されている（この文字列はProxmox ve管理画面のリソースマッピングからも確認できると思う）

```
# lspci |grep Realtek
07:00.0 Ethernet controller: Realtek Semiconductor Co., Ltd. RTL8111/8168/8411 PCI Express Gigabit Ethernet Controller (rev 15)
```

ちゃんとデバイスとしては動いているが以下のように

```
# ip a
1: lo: <LOOPBACK,UP,LOWER_UP> mtu 65536 qdisc noqueue state UNKNOWN group default qlen 1000
    link/loopback 00:00:00:00:00:00 brd 00:00:00:00:00:00
    inet 127.0.0.1/8 scope host lo
       valid_lft forever preferred_lft forever
    inet6 ::1/128 scope host noprefixroute 
       valid_lft forever preferred_lft forever
2: enp7s0: <BROADCAST,MULTICAST> mtu 1500 qdisc noop state DOWN group default qlen 1000
    link/ether 9c:6b:00:36:e9:27 brd ff:ff:ff:ff:ff:ff
```

意味不明にDOWNのまま何しても立ち上がらない

```
# lsmod | grep r816
r8169                 110592  0
```

ドライバは当たってそうな感じを醸し出す（しかしこれはでまかせだ）


## マザーボードの設定での回避策

UEFI(BIOS)で, PCIe リソースの自動割り当てを設定したり、`Above 4G Decoding`をEnableにする（自動的に伝統的制御を排除するが、そもそもIOMMUによるパススルーを仮想環境上で行うならEnableであるべき、詳しくは↓こちらの記事を参照されたい）

https://qiita.com/Mine02C4/items/8c543b7f700cad82d77a

## 管理画面への迂回ルートを作っとけ

べつにProxmox ve的にはNICを消失しているだけで、起動はちゃんとできている（が管理画面に行けないので困っている）これを回避するため、なんでもいいからUSB-NICをつけておき、NICとして認識させておくと良い。

もちろん物理的にモニタやキーボードをつなげとくのも良いが、ゲストOS側でPCIeパススルーを使ってGPUなどをゲストOS側で占有する場合、ホストOS側の情報は途中で出なくなるし、起動後に乗っ取ることもできないので注意されたい

# 0. `apt update && apt full-upgrade`してもいいかもね

これだけで解決する場合もあるらしい？？


# 1. 既存のr8169ドライバを無効化

r8169ドライバを無効化するため、モジュールをブラックリストに登録

```
# echo "blacklist r8169" >> /etc/modprobe.d/blacklist.conf
# update-initramfs -u
```

# 2. debian標準r8168ドライバを削除

aptではいる`r8168-dkms`をremoved.

```
# apt remove r8168-dkms
# reboot
```

念の為、再起動後に`lsmod`して確認したほうがいいかも。

```
# lsmod |grep r8168
```

これ打って何も出なければいいが, 出てくるようなら以下でdkms全消しできる

```
# dkms remove -m r8168 --all
```


# 3. 最新のr8168ドライバをget

ここから先は以下の記事を参考にした

https://text.yusukesakai.com/entry/20240114/1705237997

そんでドライバソースだが、公式からダウンロードしてもいいけど、github上でパッケージ配布しているところがあるので、そこのリリースからいま現段階で最新の8.054.00をチョイス。

https://github.com/sbwml/package_kernel_r8168/

```
# wget https://github.com/sbwml/package_kernel_r8168/releases/download/8.054.00/r8168-8.054.00.tar.bz2
# tar -xvf r8168-*.tar.bz2
# cd r8168-8.054.00/
```

# 4. /usr/src/へcpしてdkms.confつくる

```
# mkdir /usr/src/r8168-8.054.00
# cp -v * /usr/src/r8168-8.054.00/
# vi /usr/src/r8168-8.054.00/dkms.conf
```

当たり前だけどディレクトリ名のバージョン数に注意な

```dkms.conf
PACKAGE_NAME="r8168"
PACKAGE_VERSION="8.053.00"
BUILT_MODULE_NAME[0]="$PACKAGE_NAME"
DEST_MODULE_LOCATION[0]="/updates/dkms"
AUTOINSTALL="YES"
REMAKE_INITRD="YES"
```

なんか`REMAKE_INITRD`構文が非推奨化したなどNoticeがどっかで出るけど、dkms.confはこのままで問題ない

# 5. dkms addしてbuildしてinstallしてmodprobe

```
# dkms add -m r8168 -v 8.054.00
# dkms build -m r8168 -v 8.054.00 -k $(uname -r)
# dkms install -m r8168 -v 8.054.00 -k $(uname -r)
# modprobe r8168
# update-initramfs -u
```

`dkms install`したら`modprobe`相当な動作してると思うけど、あんま信用してないからやっといた。この段階で`lsmod`すると

```
# lsmod |grep r8168
r8168                 667648  0
```

まあなんか入ってるはず。`dkms status`でも確認できる；

```
# dkms status
Deprecated feature: REMAKE_INITRD (/var/lib/dkms/r8168/8.054.00/source/dkms.conf)
r8168/8.054.00, 6.8.12-5-pve, x86_64: installed
```

ちなみにbuild失敗したとき


```
# apt install pve-headers-$(uname -r)
```
なんかこの辺をちゃんといれることで解決するかも


# 6. 再起動して`ip a`でインタフェース名を確認する

別に再起動せずとも, ↓これでも良い
```
# systemctl restart networking
```

インタフェース名が変わってるかもね。`/etc/network/interfaces`あたりを調整して、`vmbr0`の物理NIC指定先をちゃんと改めないと行けないはずだぞ。

うちの環境では`/etc/network/internfaces`は最終的に、以下のようになった；

```conf:/etc/network/internfaces
auto lo
iface lo inet loopback

# Main NIC: enp7s0 (bridged to vmbr0)
auto enp7s0
iface enp7s0 inet manual

auto vmbr0
iface vmbr0 inet static
    address 192.168.100.50/24
    gateway 192.168.100.1
    bridge-ports enp7s0
    bridge-stp off
    bridge-fd 0

# Backup USB-NIC: enx5c857e3dddc6 (static IP)
auto enx5c857e3dddc6
iface enx5c857e3dddc6 inet static
    address 192.168.100.51/24


source /etc/network/interfaces.d/*
```

あ、なんか名前引けないとかトラブルでたら、`/etc/resolve.conf`に適当に引き先書けばいいよ

```
# cat /etc/resolv.conf
nameserver 8.8.8.8
```