getcpu_cacheシステムコール追加パッチを試してみました(前編: pwclientでパッチ適用まで)
今日(2/24)、LKMLで以下のパッチが流れていました。
「getcpu_cache()システムコールを追加する」パッチで、Linux 4.6に向けたものなので、
もう少し先のパッチです(現在は4.5のrc期間)。
まだ、パッチの中身を完全に理解したわけでは無いのですが、
調べたり、試してみたりしたことをまとめてみます。
今回やったことは、以下の3つです。
1. getcpu_cache()について、主にメール本文やコミットメッセージからだいたい理解
2. パッチを適用し、QEMU上でカーネルを動かしてみる
3. パッチに付属しているサンプルを動かしてみる(詳細は次回の記事)
1. getcpu_cache()について
そもそも、getcpu()というシステムコールがあり、
これは呼び出し元スレッドが動作しているCPUを判別するもので、
SMPやNUMAといったマルチコアのアーキテクチャのためのものです。
getcpu_cache()は、getcpu()にキャッシュ機構を追加して高速化しています。
上記のメールの本文に、
「ARM上での、このアプローチとシステムコールに基づくgetcpuとの比較は44倍の高速化を示した。
x86-64上において、glibcを介したvDSOからのlsl実行と比較して14倍の高速化を示した。」
と書かれていることから、キャッシュが効力を発揮する条件においては、
高速化が期待できるパッチのようです。
なお、man 2 getcpuを見ると、getcpu()自体にtcacheという引数があり、
2.6.24より以前では、この引数がNULL以外の場合はgetcpu()用のキャッシュ機構が動作するように実装されていた
とのことです。
そのため、過去に一度実装されていたものが、形を変えて再実装された様に見えます。
2. パッチを適用し、QEMU上でカーネルを動かしてみる
2.1. パッチの適用
LKML等のML上で流れているパッチは、patchwork.kernel.orgで管理されています。
そして、pwclientというコマンドライン上で動作するPythonスクリプトを公開しており、
これを使用すると、「パッチをダウンロードし、git amで適用」といった操作がコマンド一発で行えます。
手順は以下のとおりです。
なお、LinuxのGitのツリーをcloneする手順は省略します。
私は、Linusの木をcloneし、試してみました。
1. pwclientの準備
以下のページからpwclientをダウンロードし、PATHの通った場所に配置、実行権限を付与
また、pwclient実行の度にMLを指定しなくても良いように、以下のページからLKML用の.pwclientrcをダウンロードし、
ホームディレクトリに配置
2. パッチを適用したいLinuxカーネルのGitツリーにブランチを作成
$ git checkout -b add_getcpu_cache
3. 適用したいパッチのPatchwork上のIDを確認
$ pwclient list getcpu_cache ID State Name -- ----- ---- 7952981 New [RFC,1/3] getcpu_cache system call: cache CPU number of running thread 7953001 New [RFC,2/3] getcpu_cache: wire up ARM system call 7952991 New [RFC,3/3] getcpu_cache: wire up x86 32/64 system call 8135861 New [RFC,v2,1/3] getcpu_cache system call: cache CPU number of running thread 8135881 New [RFC,v2,2/3] getcpu_cache: wire up ARM system call 8135871 New [RFC,v2,3/3] getcpu_cache: wire up x86 32/64 system call 8153941 New [RFC,v3,1/5] getcpu_cache system call: cache CPU number of running thread 8153901 New [RFC,v3,2/5] getcpu_cache: ARM resume notifier 8153931 New [RFC,v3,3/5] getcpu_cache: wire up ARM system call 8153911 New [RFC,v3,4/5] getcpu_cache: x86 32/64 resume notifier 8153921 New [RFC,v3,5/5] getcpu_cache: wire up x86 32/64 system call 8154741 New [RFC,v3,1/5] getcpu_cache system call: cache CPU number of running thread 8164781 New [RFC,v3,1/5] getcpu_cache system call: cache CPU number of running thread 8397411 New [v4,1/5] getcpu_cache system call: cache CPU number of running thread 8397451 New [v4,2/5] getcpu_cache: ARM resume notifier 8397421 New [v4,3/5] getcpu_cache: wire up ARM system call 8397431 New [v4,4/5] getcpu_cache: x86 32/64 resume notifier 8397441 New [v4,5/5] getcpu_cache: wire up x86 32/64 system call 8397691 New [v4,(updated)] getcpu_cache: wire up ARM system call
上記のコマンドでは、"getcpu_cache"でpatchwork.kernel.org内を検索し、結果をリスト表示しています。
"v4"が一番新しく、
> 8397421 New [v4,3/5] getcpu_cache: wire up ARM system call
については、
> 8397691 New [v4,(updated)] getcpu_cache: wire up ARM system call
の更新版が出ているので、今回は以下の5つのパッチを適用してみます。
> 8397411 New [v4,1/5] getcpu_cache system call: cache CPU number of running thread
> 8397451 New [v4,2/5] getcpu_cache: ARM resume notifier
> 8397691 New [v4,(updated)] getcpu_cache: wire up ARM system call
> 8397431 New [v4,4/5] getcpu_cache: x86 32/64 resume notifier
> 8397441 New [v4,5/5] getcpu_cache: wire up x86 32/64 system call
4. パッチを適用
pwclient git-am ID
で、「パッチをダウンロードし、git am」を一括で行ってくれます。
今回の場合、以下の5つのコマンドを実行しました。
$ pwclient git-am 8397411 $ pwclient git-am 8397451 $ pwclient git-am 8397691 $ pwclient git-am 8397431 $ pwclient git-am 8397441
> 62dda9b getcpu_cache: wire up x86 32/64 system call
がHEADである時点のLinusツリーにおいては、コンフリクト無く適用できました。
なお、pwclientについて詳しくは、
pwclientをオプション無しで実行すると表示されるUsageを見てみてください。
2.2. カーネルのビルド
今回は、x86_64_defconfig のデフォルトコンフィギュレーションでビルドしてみます。
また、今回は、カーネルイメージ(bzImage)と初期RAMディスク(initrd)のみで、簡易的に動作確認してみますので、
上記ではbzImageターゲットを指定しています。(initrdはPC上(Debian Jessie)のものを流用します。)
$ make x86_64_defconfig
$ make -j4 bzImage
ビルドが完了すると、arch/x86/boot/bzImage にカーネルイメージが生成されます。
加えて、以降でサンプルをビルドするときのために、
カーネルのヘッダーファイルを適当なディレクトリに生成しておきます。
$ mkdir ~/test_linux_headers $ make headers_install INSTALL_HDR_PATH=~/test_linux_headers
2.3. QEMUで起動してみる
以下のコマンドで、先ほどビルドしたカーネルとinitrdを使用してQEMU上で起動してみることができます。
"-kernel"オプションと"-initrd"オプションは、自身の環境に合わせて書き換えてください。
$ qemu-system-x86_64 -kernel ~/git/linux/arch/x86/boot/bzImage -initrd /boot/initrd.img-4.4.0 -append "root=/dev/ram rdinit=/bin/sh"
QEMUのウィンドウが立ち上がり、カーネルのブートログが表示された後、
Enterキーを押下すると、"\ #"というプロンプトが表示され、コマンド実行ができるようになります。
なお、以下の例のように実行することで、QEMUのウィンドウを立ち上げずに、シェル上で実行できます。
qemu-system-x86_64 -kernel ~/git/linux/arch/x86/boot/bzImage -initrd /boot/initrd.img-4.4.0 -curses -append "root=/dev/ram rdinit=/bin/sh"
■ カーネルのブートパラメータでttyS0をコンソールとする例
$ qemu-system-x86_64 -kernel ~/git/linux/arch/x86/boot/bzImage -initrd /boot/initrd.img-4.4.0 -nographic -append "console=ttyS0 root=/dev/ram rdinit=/bin/sh"