続・玩式草子 ―戯れせんとや生まれけん―

第56回Plamo LinuxとNVIDIA GPU

前回までの「あそび方」シリーズでは、少し踏み込んだPlamo Linux内部の仕組みをあれこれ取りあげ、最終的にはインストーラの基盤となっているinitramfsについてかなり詳しく紹介しました。現在、こうして作り直したインストーラを用いたPlamo-8.2の開発作業が進んでいるものの、正式公開にはもう少し時間がかかりそうなので、今回はPlamo-8.2の予告を兼ねて、新機能のひとつであるNVIDIA GPUへの対応状況を紹介してみましょう。

LinuxとNVIDIA

最近の生成AIブームの追い風に乗って株価や収益が爆上げ中のNVIDIAですが、筆者のような古くからのLinuxユーザはあまりいい印象を持っていません。というのも、かってのNVIDIAはOSSに非協力的で、GPUに関する情報をほとんど公開せず、OSSなドライバの開発が困難だったためです。当時のLinuxユーザの間では「グラボを買うならNVIDIA以外」とまで言われていました。

Linusさん自身がNVIDIAを名指しでののしったことすらあります(笑)

この状況が大きく変わったのが2022年です。この年、NVIDIAは従来の方針を大きく変更し、Linux用ドライバのソースコードを公開すると発表、"open-gpu-kernel-module"という名称でGitHub上にリポジトリを公開しました。

後述するようにNVIDIAのGPUを利用するにはこのドライバ・モジュールだけでは不十分で、ソースコード非公開の共有ライブラリやツール類が必要となるものの、カーネルの任意のバージョンに合わせてドライバ・モジュールを作れるようになったので、NVIDIAのグラボやそれが提供するCUDA(Compute Unified Device Architecture)もLinux環境の選択肢の一つとなりました。

一方、同じ2022年にStableDiffusionという画像生成AIがOSSとして公開され、一大センセーションを巻き起こしました。"StableDiffusion"は、従来は大規模なクラウドサービス経由でしか実現できなかったキーワードからの画像生成を、そこそこの性能のグラボを持つPCで実行可能にした画期的なソフトウェアで、ブラウザ経由で簡単に操作できる"StableDiffusion-webui"相俟あいまって急速に普及しました。

"StableDiffusion"自身は特定のグラボを前提とはしていないものの、現状ではNVIDIAが提供するCUDA環境をPyTorch経由で利用するのが一番簡単そうです。そこで筆者も、StableDiffusionで遊ぶために型落ちのGeForce RTX 3060を入手し、それを使うためにPlamo LinuxをNVIDIAに対応させることにしたのでした(苦笑⁠⁠。

Open-GPU-kernel-module

さて、それではNVIDIAが公開している"Open-GPU-kernel-module"を見てみましょう。GitHubで公開されている"Open-GPU-kernel-module"は、"555.42.02"や"535.179"といった複数のバージョンが公開日と共に列挙されているだけで、どれが開発版でどれが安定版か、といった説明は見当りません。

GitHub上に公開されたNVIDIAのドライバ
GitHub上に公開されたNVIDIAのドライバ

どれを使うのがいいのかな、としばらく悩んだものの、NVIDIA自身で公開しているUNIX用ドライバのアーカイブ・サイトを見ると、最新の製品版が"550.78"、新機能のテスト版が"545.29.06"、ベータ版が"555.42.02"と紹介されていたので、とりあえず"550.78"を使うことにしました。

GitHubからダウンロードした"550.78.tar.gz"を展開すると、"open-gpu-kernel-modules-550.78"というディレクトリにソースコード一式が取り出されます。

$ ls
550.78.tar.gz  open-gpu-kernel-modules-550.78/
$ ls open-gpu-kernel-modules-550.78/
CHANGELOG.md        COPYING    SECURITY.md   nv-compiler.sh*  version.mk
CODE_OF_CONDUCT.md  Makefile   kernel-open/  src/
CONTRIBUTING.md     README.md  nouveau/      utils.mk

これらの中にはMakefileも含まれており、"make"を実行するだけでカーネル・モジュール(*.ko)がビルドできました。

$ make -j4
make -C src/nvidia
make -C src/nvidia-modeset
...
 [ nvidia-modeset    ]  XZ           _out/Linux_x86_64/pascal_shaders.xz
 [ nvidia-modeset    ]  XZ           _out/Linux_x86_64/maxwell_shaders.xz
...
  LD [M]  ./open-gpu-kernel-modules-550.78/kernel-open/nvidia-peermem.ko
make[2]: ディレクトリ '/usr/src/linux-6.6.30' から出ます
make[1]: ディレクトリ './open-gpu-kernel-modules-550.78/kernel-open' から出ます

$ ls open-gpu-kernel-modules-550.78/kernel-open/*ko
open-gpu-kernel-modules-550.78/kernel-open/nvidia-drm.ko
open-gpu-kernel-modules-550.78/kernel-open/nvidia-modeset.ko
open-gpu-kernel-modules-550.78/kernel-open/nvidia-peermem.ko
open-gpu-kernel-modules-550.78/kernel-open/nvidia-uvm.ko
open-gpu-kernel-modules-550.78/kernel-open/nvidia.ko

作成したモジュールをインストールするルールも定義されており、"make modules_install"で適切な場所にインストールされるようです。

$ make -n modules_install
make -C kernel-open modules_install
make[1]: ディレクトリ './open-gpu-kernel-modules-550.78/kernel-open' に入ります
make "LD=ld" "CC=cc" "OBJDUMP=objdump" KBUILD_OUTPUT=/lib/modules/6.6.30-plamo64/build
 -C /lib/modules/6.6.30-plamo64/source
...
NV_KERNEL_OUTPUT=/lib/modules/6.6.30-plamo64/build
NV_KERNEL_MODULES="nvidia nvidia-uvm nvidia-modeset nvidia-drm nvidia-peermem"
INSTALL_MOD_DIR="kernel/drivers/video" NV_SPECTRE_V2=0 modules_install
...

このようにソースコードからのビルド、インストールは簡単にできそうなものの、これらモジュールをコンパイル済みのパッケージとして提供するには工夫が必要です。というのも、カーネル・モジュールはカーネルのバージョンと密接に結びついていて、異なるバージョンのカーネルでは利用できないからです。

当初は対応するカーネルのバージョンをパッケージ名に併記して、"nvidia_modules_for_kernel_6.6.30-550.78..." みたいな形にしようか、と考えましたが、ずいぶん長くなってしまうし、そもそもこれらモジュールは特定のカーネルバージョンでしか使えません。そのため、あらかじめカーネルパッケージに組み込んでしまう方がいい、と判断しました。

詳細は省きますが、カーネルパッケージ作成用のPlamoBuildスクリプトを改造し、カーネルをビルド後、指定したバージョンのNVIDIA用ドライバ・モジュールをビルド、これらNVIDIA用モジュールはカーネルのソースコード由来のモジュールと区別するためにextramodules/というディレクトリに収めることにしました。

こうしておけばカーネルパッケージをインストールすれば対応するNVIDIA用モジュールもインストールされるので、バージョンの不一致等を気にする必要はなくなります。一方、NVIDIA用モジュールのバージョン(今回だと550.78)はパッケージ名等からは分からないので、インストール後にmodinfoで確認することになります。

$ sudo modinfo nvidia
[sudo] kojima のパスワード:
filename:       /lib/modules/6.6.30-plamo64/extramodules/nvidia.ko.xz
import_ns:      DMA_BUF
alias:          char-major-195-*
version:        550.78
supported:      external
license:        Dual MIT/GPL
firmware:       nvidia/550.78/gsp_tu10x.bin
firmware:       nvidia/550.78/gsp_ga10x.bin
softdep:        pre: ecdh_generic,ecdsa_generic
...

ドライバ・モジュールをカーネルパッケージに組み込んだ結果、ドライバのみの更新パッケージは作成しづらくなったものの、上述のようにソースコードから手動でビルド、インストールすることは可能なので、ドライバのみの更新はユーザに手動で対応してもらうことにしておきましょう(苦笑⁠⁠。

NVIDIA-Linux-x86_64など

さて、NVIDIA GPU用のカーネル・モジュールは用意できたものの、実際にグラボを操作するにはバイナリ配布されているNVIDIA製ライブラリ類が必要となります。これらはNVIDIAのダウンロードサイトで公開されており、カーネルモジュールと同じバージョンのものが必要です。

バイナリ配布されているNVIDIA製ライブラリ
バイナリ配布されているNVIDIA製ライブラリ

ここで提供されている"NVIDIA-Linux-x86_64-550.78.run"は、シェルスクリプト化された自己展開型のアーカイブファイルで、オプション無しで実行すれば展開からインストールまで自動実行します。しかし、直接インストールしてしまうとパッケージ管理ツールで管理できなくなるので、まずは「アーカイブの展開」のみを行なう"-x"オプションを指定し、中身を取り出してみます。

$ sh ./NVIDIA-Linux-x86_64-550.78.run -x
Creating directory NVIDIA-Linux-x86_64-550.78
Verifying archive integrity... OK
Uncompressing NVIDIA Accelerated Graphics Driver for Linux-x86_64 550.78....
...
$ ls NVIDIA-Linux-x86_64-550.78
10_nvidia.json                    libnvidia-opticalflow.so.550.78*
10_nvidia_wayland.json            libnvidia-pkcs11-openssl3.so.550.78*
15_nvidia_gbm.json                libnvidia-pkcs11.so.550.78*
...
libEGL.so.550.78*                 nvidia-application-profiles-550.78-rc
libEGL_nvidia.so.550.78*          nvidia-bug-report.sh*
libGL.so.1.7.0*                   nvidia-cuda-mps-control*
...

上記のように、このアーカイブの中にはOpenGLやlibGLXといったX Window System用の共有ライブラリに加え、設定ファイルや各種ツール、OpenCL、CUDAといったGPUの機能を使うためのファイル類が収められています。

lddでこれらライブラリの依存関係を調べたところ、Plamo Linuxのデフォルト環境内で解決できました。そこでArch Linux等を参考に、これらファイルを適切な場所に配置するスクリプトを書き、"nvidia_utils"という名前でPlamo Linux用パッケージとしてまとめ直すことにしました。こうしておけば削除やバージョンアップにパッケージ管理ツールが使えます。

このアーカイブに含まれているツールの中にはNVIDIAからOSSとして公開されているものもあります。"nvidia-settings"は動作中のGPUの各種情報を確認するためのツール、"nvidia-persistenced"はGPUの初期化や動作状態を管理するためのツールです。

両者をざっと試したところ、"nvidia-settings"はlibX11を参照する一般的なX用アプリ、"nvidia-persistenced"はシステム起動時に自動起動が必要なデーモンでしたので、これらは通常のソフトウェア同様、PlamoBuildスクリプトでビルド、パッケージ化して、"nvidia_utils"とは別に提供することにしました。

以上をまとめると、Plamo-8.2ではカーネルパッケージに組み込んだモジュールドライバと、ソースコード非公開の"nvidia_utils"、OSSな"nvidia_settings"と"nvidia_persistenced"という3つのパッケージでNVIDIAなGPUに対応することになりました。現在、カーネルパッケージを除くNVIDIA専用の3つのパッケージはcontrib/Nvidia/ディレクトリに収めています。

$ ls /home/ftp/pub/Plamo-8.x/x86_64/contrib/Nvidia/*tzst
/home/ftp/pub/Plamo-8.x/x86_64/contrib/Nvidia/nvidia_persistenced-550.78-x86_64-B1.tzst
/home/ftp/pub/Plamo-8.x/x86_64/contrib/Nvidia/nvidia_settings-550.78-x86_64-B1.tzst
/home/ftp/pub/Plamo-8.x/x86_64/contrib/Nvidia/nvidia_utils-550.78-x86_64-B2.tzst

NVIDIA GPUの使用例

さて、これらパッケージをインストールしてNVIDIAグラボの動作を確認してみます。手配したのはGigaByte製のGeForce RTX 3060です。装着したマシンのCPUはGPU内蔵のAMD Ryzen 3400Gなので、ビデオ回りが競合するかも、と心配したものの、問題なく認識、利用できました。

$ lspci -v
...
1:00.0 VGA compatible controller: NVIDIA Corporation GA104 [GeForce RTX 3060] (rev a1) (prog-if 00VGA controller])
      Subsystem: Gigabyte Technology Co., Ltd GA104 [GeForce RTX 3060]
      Flags: bus master, fast devsel, latency 0, IRQ 77, IOMMU group 8
      Memory at f5000000 (32-bit, non-prefetchable) [size=16M]
      Memory at c0000000 (64-bit, prefetchable) [size=256M]
      Memory at d0000000 (64-bit, prefetchable) [size=32M]
      I/O ports at f000 [size=128]
      Expansion ROM at 000c0000 [virtual] [disabled] [size=128K]
      Capabilities: <access denied>
      Kernel driver in use: nvidia
      Kernel modules: nouveau, nvidia_drm, nvidia
...

カーネルモジュールも問題なく組み込まれています。

$ dmesg
...
[   10.137411] loop: module loaded
[   10.342824] nvidia: loading out-of-tree module taints kernel.
[   10.397797] nvidia-nvlink: Nvlink Core is being initialized, major device number 239
[   10.398713] nvidia 0000:01:00.0: vgaarb: VGA decodes changed: olddecodes=io+mem,decodes=none:owns=io+mem
[   10.441252] NVRM: loading NVIDIA UNIX Open Kernel Module for x86_64  550.78  Release Build  (kojima@pl82_b1.linet.gr.jp)  2024年  5月 17日 金曜日 21:52:33 JST
[   10.649463] nvidia-uvm: Loaded the UVM driver, major device number 237.
...

Xも無事立ち上がり、nvidia-settingsも動作しました。

nvidia-settingによるGPUの情報表示
nvidia-settingによるGPUの情報表示

さて、それでは"StableDiffusion"も試してみましょう。"nvidia-settings"で動作状況を調べつつ、"StableDiffusion-webui"から「沙漠にいるペンギン」の画像をあれこれ作ってみたところ、今回指定した512x512サイズの画像ならば1枚5秒前後で生成でき、消費電力は160W程度のようです。

StableDiffusion-webuiの動作
StableDiffusion-webuiの動作

"nvidia_utils"パッケージに含まれているnvidia-smiというツールで動作中のGPUの使用状況を確認すると、GPUのメモリの使用量は4GB程度で、まだまだ余裕がある感じでした。

$ nvidia-smi 
Tue May 28 11:58:21 2024       
+-----------------------------------------------------------------------------------------+
| NVIDIA-SMI 550.78                 Driver Version: 550.78         CUDA Version: 12.4     |
|-----------------------------------------+------------------------+----------------------+
| GPU  Name                 Persistence-M | Bus-Id          Disp.A | Volatile Uncorr. ECC |
| Fan  Temp   Perf          Pwr:Usage/Cap |           Memory-Usage | GPU-Util  Compute M. |
|                                         |                        |               MIG M. |
|=========================================+========================+======================|
|   0  NVIDIA GeForce RTX 3060        On  |   00000000:01:00.0  On |                  N/A |
| 83%   57C    P2            162W /  170W |    4068MiB /  12288MiB |     98%      Default |
|                                         |                        |                  N/A |
+-----------------------------------------+------------------------+----------------------+
                                                                                         
+-----------------------------------------------------------------------------------------+
| Processes:                                                                              |
|  GPU   GI   CI        PID   Type   Process name                              GPU Memory |
|        ID   ID                                                               Usage      |
|=========================================================================================|
|    0   N/A  N/A      1922      G   /usr/libexec/Xorg                             235MiB |
|    0   N/A  N/A      2311      G   firefox                                         0MiB |
|    0   N/A  N/A      9873      C   python3                                      3348MiB |
|    0   N/A  N/A     10571      G   nvidia-settings                                 2MiB |
+-----------------------------------------------------------------------------------------+

今回は"StableDiffusion"を動かしたい一心で(苦笑⁠⁠、Plamo LinuxをNVIDIA GPUに対応させた話を紹介しました。無事当初の目的は果せたものの、⁠Xを動かすだけならグラボは不要」と安価なGPU内蔵タイプのCPUばかり使っていた結果、GPUを使う分野へのPlamo Linuxの対応がずいぶんお粗末だったことを痛感しています。

まずはNVIDIAを対象にしたものの、当初からOSSに親和的だったAMDやIntelでは、OSSのみで同等の機能が実現できるはずです。ROCmやOneAPIなど、調べないといけないことは多々あるものの、今後はAMDやIntelのGPUにも対応していきたい思っています。

おすすめ記事

記事・ニュース一覧