今回は、最初の記事 (第 1 回の図 3) で説明した専用仮想化 (パーティショニング) の概念に対応した実装について説明します。共有仮想化デバイス (第 1 回の図 4) の概念の開発については、この記事では取り上げません。
システムの実装を説明するこの記事には、以下の内容が含まれます。
- spufs の仮想化: すべてのマウント・ポイントに同じ root-inode を使用させないようにします。
- sysfs の調整: 実行中にエントリーを適応させます。
- SPU スケジューラーの変更: 仮想化を実現するためのステップを追加します。
- OpenVZ ツールの変更: 新しい機能を利用します。
デフォルトでは、マウントした spufs はすべて同じ root-inode を使用するように設定されています。例えば 2 つの chroot 環境 (A と B) があり、どちらの環境でもマウントされた spufs は /spu に設定されているとします。環境 A で SPE スレッドを作成して /spu に含まれる内容を表示すると、作成した SPE スレッドが表示されます。一方、環境 B で /spu の内容を表示すると環境 A と同じものが表示されます。これは、環境 A と同じ root-inode にアクセスしているためです。
この振る舞いを変更し、両方の環境でそれぞれに作成した SPE スレッドだけが表示されるようにするには、spufs がすべてのマウント・ポイントに同じ root-inode を使用するという事態を変えなければなりません。spufs はマウント時に、スーパー・ブロックを返すカーネル関数を内部で呼び出します。通常、この関数は get_sb_single() で、常に同じスーパー・ブロックを返します。そこで get_sb_nodev() 関数を使えば毎回異なるスーパー・ブロックを返すので、目的の振る舞いを達成することができます (図 1 に、仮想化を行う前と後の spufs を示します)。
図 1. 仮想化する前と後の spufs
OpenVZ では、コンテナー内に少数のファイル・システムしかマウントできません。それぞれのファイル・システムは struct file_system_type インスタンスを実装し、このインスタンスによってファイル固有の特徴が定義されます。OpenVZ は (数ある構造体のうち) この構造体を、ファイル・システムがマウント可能かどうかを定義するメンバーで拡張します。したがって、.fs_flags メンバーは FS_VIRTUALIZED に設定する必要があります。
コンテナー内部で作成されるプロセスには、2 つの PID が設定されます (第 1 回で説明したように、各コンテナーにはカーネルによって提供される独自のリソース・セットがあります。プロセス・ツリーは 1 つのリソース・セットで、そのなかでコンテナーが見られるのは仮想化 PID を持つ独自のプロセス・セットだけです)。コンテナー内部での PID は仮想化 PID ですが、ホスト・システム上では同じプロセスがもう 1 つの PID (グローバル PID) で表されます。前述したように、spufs ディレクトリーには spethread-<PID>-<thread-ID> という形式の名前が付きます。<PID> は SPE スレッドを保持するプロセスのプロセス ID、<thread-ID>> は対応するスレッド ID です。コンテナー環境では仮想化 PID であるため、コンテナー内に含まれる spufs のリストには、グローバル PID ではなく仮想化 PID が示されなければなりません。このような振る舞いは、幸い OpenVZ にすでに実装されています。
Linux® カーネルの arch/powerpc/platforms/cell/spufs/inode.c ファイルを変更してください。
sysfs はすでに仮想化されているので、コンテナー内で使用することができます。この sysfs はホスト・システムで表示される sysfs のコピーではなく、そのごく一部です。ここで目標となるのは、実行中に SPU をコンテナーに割り当て、コンテナーから解放することです。つまり、実行中に sysfs のエントリーも適応させなければなりません。この作業を行う前に、まずは SPU がリストされるディレクトリーを作成します。
このディレクトリーは /sys/devices/system/spu です。デフォルトでは、コンテナー環境内の sysfs には /sys/devices/system/spu、/sys/devices/system、/sys/devices のいずれのディレクトリーもないため、この 3 つのエントリーをコンテナーの初期化 (起動) 中に作成しなければなりません。/sys/devices/system/spu のサブディレクトリーは spu0 から spu<N> にすることができます。ここで、<N> はシステム内で利用可能な SPU の数から 1 を引いた値です。これらのディレクトリーは、SPU がコンテナーに割り当てられるときに作成され、コンテナーから解放されるときに削除される必要があります。
sysfs のリストに含まれる各ディレクトリーには、それぞれに対応する kobject インスタンスがカーネル空間内にあります。kobject とは、ディレクトリーの名前とその親 kobject (言い換えると、対応する親ディレクトリー) を定義する構造体のことです。例えば、sys/devices/system/spu/spu3 ディレクトリーとして後で表示される kobject については以下の 2 つの設定を調整します。
- 名前 spu3
- 親 (/sys/devices/system/spu を表す kobject へのポインター)
subsystem_register() を呼び出すことで kobject を sysfs に登録すると、kobject はユーザー空間内に表示されるようになります。反対に、sysfs からディレクトリーを削除するには、削除対象の kobject を指定して subsystem_unregister() 関数を呼び出します。図 2 に一例を示します。
図 2. sysfs および kobject
名前が spu3 となっている kobject には、名前が spu の親 kobject へのポインターがあります。
主な変更はカーネル・ファイル、kernel/ve/vecalls.c で行います。OpenVZ に固有のこのファイルには、初期化およびコンテナーのパラメーター設定を実行している間に呼び出されるほとんどの関数が実装されます。
システム内にあるそれぞれの物理 SPU は、カーネル空間内では spu 構造体のインスタンスによって表されます。この構造体には、以下をはじめとする情報が含まれます。
- SPU の ID (番号)
- 所属先の Cell/B.E. ノード
- SPU の LS へのポインター
新しいメンバー変数は SPU のオーナーをコンテナー ID という形で保存します。SPU スケジューラーが実装する spu_alloc() 関数は、SPU スレッドを実行できる空き SPU を見つけるために、システム内で使用可能な (すぐに実行される SPU スレッドがない) SPU のリストを検索します。
通常の振る舞いでは、リストに含まれる最初の SPU で SPE スレッドが実行されます。仮想化の振る舞いを実現するには、この spu_alloc() 関数で、空き SPU と実行対象の SPE スレッドが同じコンテナー ID を持っていることをチェックしなければなりません。図 3 に、変更を行う前と後の spu_alloc() の動作を示します。
図 3. 変更前と変更後の spu_alloc()
この追加チェックの結果が真でない場合、関数は空き SPU のリストにある次の SPU をチェックします。SPE スレッドを起動するコンテナーに使用可能な空き SPU がない場合には、SPU スケジューラーはリストが空であるかのように振る舞い、SPU が空きになるまで待機します。
この spu_alloc() 関数を実装する場所は、Linux カーネルのソース・ファイル、arch/powerpc/platforms/cell/spu_base.c です。
必要な関数のほとんどはすでに存在していますが、新しい機能を使用するために OpenVZ ツールを変更する必要があります。vzctl は実行中に SPU 割り当てを管理するツールです。OpenVZ でコンテナーのパラメーターを設定するには、主にこのツールを使用します。コンテナーに割り当てる SPU の数を設定するための新規パラメーターは、--spus <nr_spus> です。
<nr_spus> の値は、コンテナーに割り当てられる SPU の数を表します。この値は絶対数を表しているので、6 の設定値で 8 基の SPU がコンテナーに割り当てられている場合、6 基の SPU が追加されるのではなく、2 基の SPU がコンテナーから解放されます (8 - 6 = 2)。
以下のコマンド・ラインの出力例では、ID が 101 に設定されたコンテナーが 8 基の SPU を取得しています。
[root@c02b12-0 ~]# vzctl set 101 --spus 8 Setting SPUs: 8 Configure meminfo: 1024000 WARNING: Settings were not saved and will be reset to original values on next start (use --save flag) [root@c02b12-0 ~]# |
この振る舞いを完成するには、vzctl ツールがユーザー空間の境界線を越えてカーネル空間で管理を行う必要があります。また、まだ他のコンテナーで使用されていない SPU を見つけることも必要です。vzctl ツールは使用可能な SPU のリストを検索し、spu 構造体内に新しく実装されたコンテナー ID をチェックします (SPU スケジューラーの変更についてのセクションで説明)。その値が 0 であれば、SPU を要求側コンテナーに割り当てることができます。0 という値が使用される理由は、コンテナー ID の値は 0 より大きいことが条件であるため、値 が 0 の場合には SPU が他のコンテナーに割り当てられていないことを意味するからです。関数が要求を完了できるだけの空き SPU を見つけられない場合、この手順は終了し、コンテナーに SPU は割り当てられません。コンテナーにすでに割り当てられている SPU の数が要求された SPU 数を上回る場合には、その差の分だけ SPU が解放されます。
ユーザー空間とカーネル空間との間の境界線を超えるには、さまざまな実装モデルを使用することができます (実装モデルの詳細については、「参考文献」で紹介している Arnd Bergmann の「How to not invent kernel interfaces」を参照してください)。そのうち最も単純な方法は、システム・コールのパラメーターに <containerID> および <nr_spus> パラメーターをマッピングする新しいシステム・コールを実装することです。
カーネル・モジュールとしてビルド可能なカーネルの一部には、コンテナーの SPU パラメーターの設定を操作する関数を実装する必要がありますが、これには大きな問題が伴います。カーネル・モジュールがロードされない場合にはカーネル空間内のシステム・コール・ハンドラー関数は何も実行せず、カーネル・モジュールがロードされた場合にはモジュール内に実装された関数が呼び出されるようにしなければなりません。しかし、システム・コール・テーブル (システム・コール・ハンドラー関数への関数ポインターが常駐するテーブル) は静的カーネル・ビルドの一部であるため、そう簡単には行きません。
このモジュールは静的関数の一部ではないことから、静的組み込みシステム・コール・ハンドラー関数がモジュールの一部である関数を呼び出すことは不可能です。この問題に対するソリューションは、モジュール内の関数へのポインターを静的組み込みシステム・コール・ハンドラー関数の変数にコピーする関数ラッパーを実装し、静的に組み込まれたシステム・コール・ハンドラーがモジュール内の関数を呼び出せるようにすることです。この関数ラッパーを、モジュールの初期化とクリーンアップの際に呼び出すようにします。図 4 では、関数ラッパー・メソッドを黒の矢印で示しています。
図 4. システム・コールおよびモジュールの関数
上記の図から、モジュール内部に実装された関数の関数ポインターが静的組み込みカーネル空間にコピーされる仕組みがわかるはずです。点線の矢印は、ユーザー空間のアプリケーションが静的組み込みシステム・コール・ハンドラー関数を渡すことによって、モジュール内の関数を呼び出していることを示しています。
変更が必要なカーネルのソース・コード・ファイルは以下のとおりです。
- include/asm-powerpc/systbl.h
- include/asm-powerpc/unistd.h
- include/linux/syscalls.h
- kernel/sys.c
- kernel/sys_ni.c
- kernel/ve/vecalls.c
また、OpenVZ の vzctl ソースに含まれる以下のファイルも更新してください。
- include/res.h
- include/vzctl_param.h
- include/vzsyscalls.h
- src/lib/config.c
- src/lib/res.c
- src/vzctl.c
さらに以下の 2 つのファイルを導入し、OpenVZ ビルド・システムに組み込みます。
- include/spu.h
- src/lib/spu.c
第 3 回では、システムを使用してテストする方法を説明し、コンテナー仮想化のパフォーマンスを疑似仮想化や完全仮想化などの他のソフトウェア仮想化方法と比較して分析します。
学ぶために
-
RSS フィードを利用して、連載の今後の記事についての通知を受け取れるようにしてください (developerWorks コンテンツの RSS フィードについての詳細を調べてください)。
- OpenVZ と仮想化については、「Virtualization in Linux」(2006年9月) を読んでください。3 つの主要な仮想化方法であるエミュレーション、疑似仮想化、そして OS レベルの仮想化を OpenVZ と関連させて解説しています。
- Cell/B.E. プラットフォーム上で Linux を実行できるようにする SPU ファイル・システム・インターフェースについての詳細は、Arnd Bergmann の「Spufs: The Cell Synergistic Processing Unit as a virtual file system」(developerWorks、2005年6月) を読んでください。同著者による論文「How to not invent kernel interfaces」(LinuxConf Europe、2007年7月) では、それぞれのカーネル・コードに適したユーザー空間インターフェースの選び方について解説しています。さらに、Linux に関するCell/B.E. プラットフォーム上の「仮想」クラスについての資料では、スレッド化モデル、Linux ランタイム・ストラテジー、PPU/SPU ランタイム要件、spufs、信号処理などについて取り上げています。
- マトリックス乗算、XDR DMA 帯域幅、SPE と SPE DMA 間の帯域幅などのパフォーマンス分析測定については、Daniel Hackenberg のプレゼンテーション「Performance Measurements on Cell SMP Systems」(Center for Information Services and High Performance Computing の Cell/B.E. クラスター会議用、2007年5月) を読んでください。
- PPE 中心と SPE 中心のプログラミング・モデルの違い、関数の負荷軽減、DMA のオーバーラップ、異種のマルチスレッドなど、Cell/B.E. プログラミング・モデルの課題については、Duc Vianney のプレゼンテーション「Cell Software Solutions Programming Model」(2006年3月) を読んでください。
-
「仮想 Linux」(developerWorks、2006年12月) では、さまざまな形式での仮想化 (そして現在の仮想化プロジェクト) について Linux の視点から説明しています。
- 疑似仮想化についての詳細は、「coLinuxによる仮想化」(developerWorks、2007年3月) および「QEMU によるシステムのエミュレーション」(2007年9月) を参照してください。
- SDK 3.0 を話題にしたブログ、Infobombs には、すぐに知識が身につく新連載が掲載されています。最初の 3 つの記事では、ALF (Accelerated Library Framework) の概要、ALF で最も重要な 10 のコンセプト、そして DaCS (Data Communication and Synchronization) ライブラリー・サービスの概要を紹介しています。
- libspe2 の概念についての詳細、そして libspe2 で基本 SPE プロセスの管理と通信を行う方法については、「libspe2 の変更内容: libspe2 が Cell Broadband Engine プログラミングに与える影響」(developerWorks、2007年7月) を参照してください。
- 「Introduction to the Cell Multiprocessor」(IBM Journal of Research and Development、2005年) では、Cell/B.E. マルチプロセッサーの歴史、このプログラムの目標と課題、設計コンセプト、アーキテクチャーおよびプログラミング・モデル、そして実装についてわかりやすく概説しています。
- Cell/B.E. プログラミングの詳細を学ぶには、developerWorks の以下の連載を読んでください。
- 「Cell BE プロセッサーでのハイパフォーマンス・アプリケーションのプログラミング」
- 「PS3 を既製品から実験装置に」
- 「The little broadband engine that could」
- IBM Semiconductor Solutions Technical Library の Cell Broadband Engine documentation セクションから豊富なマニュアル、仕様などをダウンロードできます。
-
developerWorks ニュースレターに登録すると、最新の開発者向けニュースと Cell/B.E. に関する最新情報を毎週メールで受け取れます。ニュースレターで Cell/B.E. 関連のニュースを受信するには、登録する際に Power Architecture にチェック・マークを付けてください。
-
Linux ゾーンと Open source ゾーンをはじめ、他の developerWorks リソースにもアクセスして仮想化関連のリソースを探してください。
製品や技術を入手するために
- SWsoft から「OpenVZ User's Guide」Version 2.7.0-8 を入手してください。
- パフォーマンス分析用のツールを集めた LMbench を試してみてください。このスイートには、さまざまな UNIX® システムのパフォーマンスを比較する移植可能なベンチマークが用意されています。
- Power.org のサンプル・コードを使用してください。このサンプル・コードは、Cell/B.E. 環境内で 4Way SIMD 単精度浮動小数点演算の複合 FFT を実行します。
- IBM developerWorks Cell Broadband Engine resource center は、Cell/B.E. に関する最も信頼できる情報源です。ここには、Cell/B.E. 関連の記事、ディスカッション・フォーラム、ダウンロードなどが満載されています。
-
カスタム Cell/B.E. ベースまたはカスタム・プロセッサー・ベースのソリューションについては IBM にお問い合わせください。
議論するために
-
ディスカッション・フォーラムに参加してください。
-
Cell Broadband Engine Architecture forum で、このプロセッサーの技術に関する疑問の答えを見つけてください。「Forum watch」ブログ・シリーズには、フォーラムからの興味深い問題とその答えが定期的に要約されて記載されます。
-
Power Architecture ブログにアクセスして、Cell/B.E. やその他の Power Architecture 関連技術に関するニュース、ダウンロード、資料、イベント通知を参照してください。人気の高い「Forum watch」ブログ・シリーズ (Q&A 要約) と「FixIt」技術更新情報、それに Infobomb の技術要約も用意されています。
Christian Kaiser は、ドイツ・アーヘンにある RWTH University でコンピューター工学を学び、2007年に研修生としてボーブリンゲンの IBM Germany Research Lab に勤務しました。IBM での研修期間中に研究したのは、Cell Broadband Engine プロセッサーを対象とした仮想化の手法です。この研修期間の後、彼は RWTH Aachen の Chair for Operating Systems で論文に取り組むため大学に戻りました。論文のテーマは、「Analysis of asynchronous collective communication in memory-coupled high-speed networks」です。
Christian Rund は、ドイツ・ボーブリンゲンの IBM Development Laboratory に勤務しています。彼は University of Stuttgart とスウェーデンの Uppsala Universitet でコンピューター・サイエンスを学び、1997年に卒業しました。学生時代にドイツのヘレンベルグとシュトュットガルトの IBM で研修生として勤務した経験を経て、1998年にシュトュットガルト所在の Landeszentralbank (Deutsche Bundesbank) システム開発部門の一員となりました。zSeries FCP チャネル開発チームの研究開発技術者として IBM に入社したのは 2001年です。その後、2006年中頃からは Cell/B.E. ベースのサーバー向けホスト・ファームウェアの研究開発技術者として活躍しています。