Solaris Cluster OS™ システムは、メモリーベース相互接続 (Dolphin-SCI など) と階層化システムソフトウェア構成要素で構成できます。このような構成要素は、リモートノード上に存在するメモリーへの直接アクセスに基づいて、ユーザーレベルのノード間メッセージング用メカニズムを実装します。このメカニズムのことを「リモート共有メモリー (RSM)」と呼びます。この章では、RSM アプリケーションプログラミングインタフェース (RSMAPI) について説明します。
API フレームワークでは、RSMAPI フレームワークについて説明します。
API ライブラリ関数では、RSMAPI ライブラリ関数について説明します。
RSMAPI の使用例では、RSMAPI の使用例について説明します。
共有メモリーモデルでは、まず、あるアプリケーションプロセスがプロセスのローカルアドレス空間から RSM エクスポートセグメントを作成します。次に、1 つまたは複数のリモートアプリケーションプロセスが相互接続上のエクスポートセグメントとインポートセグメント間の仮想接続を使用して、RSM インポートセグメントを作成します。共有セグメントのメモリー参照を行うときには、どのアプリケーションプロセスもローカルなアドレス空間のアドレスを使用します。
アプリケーションプロセスは、ローカルでアドレス可能なメモリーをエクスポートセグメントに割り当てることによって、RSM エクスポートセグメントを作成します。この割り当てには、System V Shared Memory、mmap(2)、valloc(3C) などの標準の Solaris インタフェースの 1 つを使用します。次に、アプリケーションプロセスはセグメントを作成する RSMAPI を呼び出して、割り当てられたメモリーに参照ハンドルを提供します。RSM セグメントは 1 つまたは複数の相互接続コントローラを通じて公開されます。公開されたセグメントは、リモートからアクセスできるようになります。セグメントをインポートすることが許可されたノードのアクセス権リストも公開されます。
エクスポートされるセグメントにはセグメント ID が割り当てられます。このセグメント ID (および、作成するプロセスのクラスタノード ID) を使用すると、インポートしているプロセス (インポータ) はエクスポートセグメントを一意に指定できます。エクスポートセグメントが正常に作成されると、後続のセグメント操作で使用するための RSM エクスポートセグメントハンドルがプロセスに返されます。
アプリケーションプロセスは RSMAPI を使用して、公開されたセグメントへのアクセス権を取得し、インポートセグメントを作成します。インポートセグメントを作成した後、アプリケーションプロセスは相互接続間に仮想接続を確立します。インポートセグメントが正常に作成されると、後続のセグメントインポート操作で使用するための RSM インポートセグメントハンドルがアプリケーションプロセスに返されます。メモリーマッピングが相互接続によってサポートされている場合、仮想接続を確立した後、アプリケーションは RSMAPI を要求して、ローカルアクセス用にメモリーマップを提供できます。メモリーマッピングがサポートされていない場合、アプリケーションは RSMAPI が提供するメモリーアクセスプリミティブを使用できます。
RSMAPI は、リモートアクセスエラー検出をサポートし、書き込み順番メモリーモデルに関する問題を解決するための機構を提供します。この機構のことを「barrier」と呼びます。
RSMAPI が提供する通知メカニズムを使用すると、ローカルアクセスとリモートアクセスの同期をとることができます。つまり、インポートプロセスがデータ書き込み操作を終了するまで、エクスポートプロセスはデータの処理をブロックする関数を呼び出すことができます。書き込み操作が終了すると、インポートプロセスはシグナル関数を呼び出してエクスポートプロセスのブロックを解除します。ブロックが解除された後、エクスポートプロセスはデータを処理できます。
RSM アプリケーションサポート構成要素は次のソフトウェアパッケージで配信されます。
RSMAPI 関数をエクスポートする共有ライブラリ (/usr/lib/librsm.so)
ユーザーライブラリの代わりに RSMAPI インタフェースを通じてメモリー相互接続ドライバとインタフェースをとる Kernel Agent (KA) 仮想ドライバ (/usr/kernel/drv/rsm)
相互接続トポロジを取得するためのクラスタインタフェースモジュール
相互接続ドライバサービスモジュール (/kernel/misc/rsmops)
API 関数とデータ構造体のプロトタイプを提供するヘッダーファイル (/opt/SUNWrsmdk/include)
システムに構成されている固有な相互接続の RSM サポートを提供する librsm.so へのオプション拡張。拡張はライブラリ (librsm interconnect.so) の形式で提供されます。

API ライブラリ関数は次の操作をサポートします。
相互接続コントローラ操作
クラスタトポロジ操作
メモリーセグメント操作 (セグメント管理とデータアクセスを含む)
バリア操作
イベント操作
コントローラ操作は、コントローラへのアクセスを取得するメカニズムを提供します。コントローラ操作はまた、配下の相互接続の特性も決定します。相互接続コントローラ操作には、次のような操作が含まれます。
コントローラの取得
コントローラ属性の取得
コントローラの解放
rsm_get_controller は、指定されたコントローラのインスタンス (sci0 や loopback など) のコントローラハンドルを取得します。返されるコントローラハンドルは後続の RSM ライブラリ呼び出しに使用されます。
戻り値: 成功した場合、0 を返します。そうでない場合、エラー値を返します。
コントローラハンドルが無効です。
コントローラが存在しません。
メモリーが不足しています。
ライブラリのバージョンが無効です。
アドレスが不正です。
この関数は、指定されたコントローラハンドルに関連するコントローラを解放します。rsm_release_controller の呼び出しごとに対応する rsm_get_controller が存在する必要があります。つまり、コントローラに関連付けられたコントローラハンドルをすべて解放すると、コントローラに関連付けられたシステム資源が解放されます。コントローラハンドルにアクセスしたり、解放されたコントローラハンドル上のインポートセグメントまたはエクスポートセグメントにアクセスしたりすることは不正です。このような場合の結果は定義されていません。
戻り値:成功した場合、0 を返します。そうでない場合、エラー値を返します。
コントローラハンドルが無効です。
この関数は、指定されたコントローラハンドルの属性を取得します。この関数に現在定義されている属性は次のとおりです。
typedef struct {
uint_t attr_direct_access_sizes;
uint_t attr_atomic_sizes;
size_t attr_page_size;
size_t attr_max_export_segment_size;
size_t attr_tot_export_segment_size;
ulong_t attr_max_export_segments;
size_t attr_max_import_map_size;
size_t attr_tot_import_map_size;
ulong_t attr_max_import_segments;
} rsmapi_controller_attr_t;
戻り値:成功した場合、0 を返します。そうでない場合、エラー値を返します。
コントローラハンドルが無効です。
アドレスが不正です。
エクスポート操作とインポート操作に必要な鍵となる相互接続データは次のとおりです。
エクスポートクラスタのノード ID
インポートクラスタのノード ID
コントローラ名
メモリーをエクスポートするアプリケーション構成要素は、インタフェースが提供するデータを使用して、既存のローカルコントローラセットを発見します。インタフェースが提供するデータはまた、セグメントを作成および発行するためのコントローラを正しく割り当てるために使用できます。アプリケーション構成要素は、ハードウェア相互接続とアプリケーションソフトウェアディストリビューションに整合性があるコントローラセットを使用して、エクスポートされたセグメントを効率的に分散できます。
メモリーをインポートするアプリケーション構成要素は、メモリーのエクスポートで使用されるセグメント ID とコントローラを通知する必要があります。この情報は通常、事前定義されているセグメントとコントローラのペアによって伝達されます。メモリーをインポートしている構成要素はトポロジデータを使用して、セグメントインポート操作に適切なコントローラを決定できます。
この関数は、アプリケーションポインタによって指定された場所にあるトポロジデータへのポインタを返します。トポロジデータ構造体は次のように定義されます。
戻り値:成功した場合、0 を返します。そうでない場合、エラー値を返します。
トポロジポインタが無効です。
メモリーが不足しています。
メモリーが不足しています。
rsm_free_interconnect_topology 操作は、rsm_get_interconnect_topology で割り当てられたメモリーを解放します。
戻り値: ありません。
rsm_get_topology_data から返されるポインタは rsm_topology_t structure を参照します。この構造体は、各ローカルコントローラのローカルノード ID と connections_t 構造体へのポインタの配列を提供します。
typedef struct rsm_topology {
rsm_nodeid_t local_nodeid;
uint_t local_cntrl_count;
connections_t *connections[1];
} rsm_topology_t;
RSM セグメント ID はアプリケーションが指定するか、システムが rsm_memseg_export_publish() 関数を使用して生成します。セグメント ID を指定するアプリケーションは、予約されたセグメント ID の範囲を使用する必要があります。セグメント ID の範囲を予約するには、rsm_get_segmentid_range 関数を使用して、予約されたセグメント ID の範囲をセグメント ID 構成ファイル /etc/rsm/rsm.segmentid に定義します。rsm_get_segmentid_range 関数を使用すると、アプリケーションは自分用に予約されたセグメントID の範囲を取得できます。この関数は、指定されたアプリケーション ID の /etc/rsm/rsm.segmentid ファイルに定義されているセグメント ID の範囲を読み取ります。
アプリケーション ID はアプリケーションを識別するための NULL で終了する文字列です。アプリケーションは baseid 以上で baseid+length 未満の値を使用できます。baseid または length が変更された場合、アプリケーションに返されるセグメント ID は予約された範囲内ではない場合がありますので、セグメント ID を取得するときには、予約されたセグメント ID の範囲内のオフセットを使用してください。
/etc/rsm/rsm.segmentid ファイル内のエントリは次のような形式です。
#keyword appid baseid length reserve SUNWfoo 0x600000 100 |
エントリを構成する文字列は、タブまたは空白で区切ることができます。この文字列は先頭から、キーワード reserve、アプリケーション識別子 (空白を含まない文字列)、baseid (予約された範囲の開始セグメントID (16 進数))、および、length (予約されたセグメントID の数) から構成されます。コメント行には、最初の列に # を指定します。このファイルには、空白 (空白の行) があってはなりません。システムに予約されたセグメントID は/usr/include/rsm/rsm_common.h ヘッダーファイルに定義されています。アプリケーションはシステムに予約されたセグメント ID を使用できません。
成功した場合、rsm_get_segmentid_range 関数は 0 を返します。失敗した場合、この関数は次のエラー値のうちの 1 つを返します。
渡されたアドレスが無効です。
アプリケーション ID が /etc/rsm/rsm.segmentid ファイルに定義されていません。
構成ファイル /etc/rsm/rsm.segmentid が存在しないか、読み取ることができません。構成ファイルの書式が正しくありません。
RSM セグメントは、連続する仮想アドレスの範囲にマッピングされた (一般的に) 連続しない物理メモリーページセットを表します。RSM セグメントのエクスポート操作とインポート操作によって、相互接続のシステム間で物理メモリー領域を共有できるようになります。物理メモリーページが存在するノードのプロセスのことをメモリーの「エクスポータ」と呼びます。リモートアクセス用に公開するためにエクスポートされたセグメントは、指定されたノードに固有なセグメント識別子を持ちます。セグメント ID はエクスポータが指定するか、RSMAPI フレームワークが割り当てます。
エクスポートされたメモリーへのアクセスを取得するために、相互接続のノードのプロセスは RSM インポートセグメントを作成します。この RSM インポートセグメントは、ローカルの物理ページではなく、エクスポートされたセグメントと接続しています。相互接続がメモリーマッピングをサポートする場合、インポータはインポートセグメントのメモリーマッピングされたアドレスを使用して、エクスポートされたメモリーを読み書きできます。相互接続がメモリーマッピングをサポートしない場合、インポートしているプロセス (インポータ) はメモリーアクセスプリミティブを使用します。
メモリーセグメントをエクスポートするとき、アプリケーションはまず、通常のオペレーティングシステムインタフェース (System V Shared Memory Interface、mmap、または valloc など) を使用して、自分の仮想アドレス空間にメモリーを割り当てます。メモリーを割り当てた後、アプリケーションは RSMAPI ライブラリインタフェースを呼び出して、セグメントを作成して、ラベルを付けます。セグメントにラベルを付けた後、RSMAPI ライブラリインタフェースは割り当てた仮想アドレスの範囲に物理ページをバインドします。物理ページをバインドした後、RSMAPI ライブラリインタフェースはセグメントを公開して、インポートしているプロセス (インポータ) がアクセスできるようにします。
mmap を使用して仮想アドレス空間を取得した場合、マッピングは MAP_PRIVATE である必要があります。
エクスポート側のメモリーセグメント操作には、次のような操作が含まれます。
メモリーセグメントの作成および破壊
メモリーセグメントの公開および公開解除
メモリーセグメント用のバッキングストアの再バインド
rsm_memseg_export_create を使用して新しいメモリーセグメントを確立すると、セグメントを作成するときに物理メモリーを関連付けることができます。この操作は、エクスポート側のメモリーセグメントハンドルを新しいメモリーセグメントに戻します。セグメントは作成するプロセスが動作している間、または、rsm_memseg_export_destroy を使用して破壊するまで存在します。
インポート側が切断する前に破壊操作が行われた場合、切断が強制的に行われます。
この関数はセグメントハンドルを作成します。セグメントハンドルを作成した後、この関数はセグメントハンドルを指定された仮想アドレス範囲 [vaddr..vaddr+size] にバインドします。この範囲は有効であり、コントローラの alignment プロパティ上に整列している必要があります。flags 引数はビットマスクで、次の操作を有効にします。
セグメント上のバインド解除
セグメント上の再バインド
RSM_ALLOW_REBIND の flags への引き渡し
ロック操作のサポート
RSM_LOCK_OPS の flags への引き渡し
RSM_LOCK_OPS フラグは RSMAPI の初期リリースには含まれません。
戻り値:成功した場合、0 を返します。そうでない場合、エラー値を返します。
コントローラハンドルが無効です。
コントローラが存在しません。
セグメントハンドルが無効です。
コントローラの長さが 0 あるいは、制限を超えています。
アドレスが無効です。
アクセス権がありません。
メモリーが不足しています。
リソースが不足しています。
アドレスがページ境界に整列されていません。
シグナルによって操作が割り込まれました。
この関数はセグメントとその空きリソースの割り当てを解除します。インポートしているプロセス (インポータ) はすべて強制的に切断されます。
戻り値:成功した場合、0 を返します。そうでない場合、エラー値を返します。
セグメントハンドルが無効です。
pollfd は使用中です。
発行操作によって、相互接続上にあるほかのノードがメモリーセグメントをインポートできます。エクスポートセグメントは複数の相互接続アダプタ上で発行できます。
セグメント ID は承認された範囲または 0 を指定した場合、有効なセグメント ID が RSMAPI フレームワークによって生成され、返されます。
セグメントアクセス制御リストはノード ID とアクセス権のペアから構成されます。リストでは、指定したノード ID ごとに、Solaris のファイルアクセス権とともに所有者、グループ、およびその他のユーザーの 3 つの 8 進数によって関連する読み取り権と書き込み権が示されます。アクセス制御リストでは、各 8 進数は次の値を持ちます。
書き込みアクセス
読み取り専用アクセス
読み取りおよび書き込みアクセス
たとえば、0624 というアクセス権は次のことを意味します。
エクスポータと同じ uid を持つインポータは、読み取りと書き込み両方のアクセス権を持つ
エクスポータと同じ gid を持つインポータは、書き込みアクセス権だけを持つ
その他すべてのインポータは、読み取り専用アクセス権だけを持つ
アクセス制御リストが提供される場合、リストに含まれないノードはセグメントをインポートできません。ただし、アクセス制御リストが NULL の場合は、すべてのノードがセグメントをインポートできます。すべてのノードのアクセス権は、エクスポートするプロセス (エクスポータ) の所有者、グループ、およびその他のファイル作成権と同じになります。
ノードアプリケーションはセグメント識別子の割り当てを管理し、エクスポートするノード上で一意性を保証する義務があります。
typedef struct {
rsm_node_id_t ae_node; /* 資源へのアクセスを許可されたリモートノード ID */
rsm_permission_t ae_permissions; /* 許可されたアクセスモード */
}rsmapi_access_entry_t;.
戻り値:成功した場合、0 を返します。そうでない場合、エラー値を返します。
セグメントハンドルが無効です。
セグメントはすでに公開されています。
アクセス制御リストが無効です。
セグメント ID が無効です。
セグメント ID は使用中です。
セグメント ID は予約されています。
セグメントの作成者ではありません。
アドレスが不正です。
メモリーが不足しています。
リソースが不足しています。
認可されたセグメント ID の範囲:
0
0x0FFFFF
0x100000
0x1FFFFF
0x200000
0x2FFFFF
0x300000
0x3FFFFF
0x400000
0x4FFFFF
次に示す範囲は、公開値が 0 の場合、システムによる割り当て用に予約されています。
0x80000000
0xFFFFFFF
この関数は、ノードのアクセス (制御) リストとセグメントのアクセスモードを新たに確立します。これらの変更は将来のインポート呼び出しだけに影響し、すでに許可されているインポート要求は取り消しません。
戻り値:成功した場合、0 を返します。そうでない場合、エラー値を返します。
セグメントハンドルが無効です。
セグメントが公開されていません。
アクセス制御リストが無効です。
セグメントの作成者ではありません。
メモリーが不足しています。
リソースが不足しています。
シグナルによって操作が割り込まれました。
戻り値:成功した場合、0 を返します。そうでない場合、エラー値を返します。
セグメントハンドルが無効です。
セグメントが公開されていません。
セグメントの作成者ではありません。
シグナルによって操作が割り込まれました。
再バインド操作は、エクスポートセグメントの現在のバッキングストアを解放します。現在のバッキングストアを解放した後、再バインド操作は、新しいバッキングストアを割り当てます。まず始めにアプリケーションは、セグメント用の新しい仮想メモリー割り当てを取得する必要があります。この操作はセグメントのインポータに透過的です。
アプリケーションは、再バインド操作が完了するまで、セグメントデータへアクセスしてはいけません。再バインド中にセグメントからデータを取得しようとしてもシステムエラーにはなりませんが、このような操作の結果は定義されていません。
戻り値:成功した場合、0 を返します。そうでない場合、エラー値を返します。
セグメントハンドルが無効です。
長さが無効です。
アドレスが無効です。
再バインドは許可されていません。
セグメントの作成者ではありません。
アクセス権がありません。
メモリーが不足しています。
リソースが不足しています。
シグナルによって操作が割り込まれました。
インポート側の操作には、次の操作が含まれます。
メモリーセグメントの接続および切断
インポートされたメモリーセグメントへのアクセス
バリア操作を使用したデータアクセス操作の順番の決定、およびアクセスエラーの検出
接続操作は、RSM インポートセグメントを作成して、エクスポートされたセグメントとの論理的な接続を形成するときに使用します。
インポートされたセグメントメモリーへのアクセスは、次の 3 つのインタフェースカテゴリによって実現されます。
セグメントアクセス
データ転送
セグメントメモリーマッピング
この関数は、指定されたアクセス権 perm を使用してリモートノード node_id 上にある セグメント segment_id に接続します。セグメントに接続した後、この関数はセグメントハンドルを返します。
引数 perm は、当該接続のインポータによって要求されるアクセスモードを指定します。接続を確立するとき、エクスポータが指定したアクセス権とインポータが使用するアクセスモード、ユーザー要求されるアクセスモードが無効な場合、接続要求は拒否されます。なお、perm 引数は次の 8 進数値に制限されます。
読み取りモード
書き込みモード
読み取りおよび書き込みモード
指定されたコントローラは、セグメントのエクスポートに使用されるコントローラと物理的に接続されている必要があります。
戻り値:成功した場合、0 を返します。そうでない場合、エラー値を返します。
コントローラハンドルが無効です。
コントローラが存在しません。
セグメントハンドルが無効です。
アクセス権がありません。
セグメントがノードに公開されていません。
セグメントが公開されていません。
リモートノードに到達できません。
接続が割り込まれました。
メモリーが不足しています。
リソースが不足しています。
アドレスが不正です。
この関数はセグメントを切断します。セグメントを切断した後、この関数はセグメントのリソースを解放します。切断されたセグメントへの既存のマッピングはすべて削除されます。ハンドル im_memseg は解放されます。
戻り値:成功した場合、0 を返します。そうでない場合、エラー値を返します。
セグメントハンドルが無効です。
セグメントがマッピングされたままになっています。
pollfd は使用中です。
次のインタフェースは、8 ビットから64 ビットまでのデータを転送するためのメカニズムを提供します。get インタフェースは、プロセスがメモリー上の連続するデータから読みとるべき、与えられたサイズのデータ項目の数を示すリピートカウント (rep_cnt) を使用します。メモリー上の連続するデータは、インポートされたセグメントのオフセット (offset) から始まります。データは datap から始まる連続する場所に書き込まれます。put インタフェースは、リピートカウント (rep_cnt) を使用して、プロセスが読み取るべきデータ項目数を指定します。連続する場所は、datap から始まります。データは次に、インポートされたセグメントの offset から始まる連続する場所に書き込まれます。
これらのインタフェースはまた、読み取り元と書き込み先のエンディアン特性に互換性がない場合にバイトを交換するメカニズムも提供します。
関数のプロトタイプ:
int rsm_memseg_import_get8(rsm_memseg_import_handle_t im_memseg, off_t offset, uint8_t *datap, ulong_t rep_cnt);次のインタフェースは、セグメントアクセス操作がサポートするデータよりも大きなデータを転送するときに使用します。
int rsm_memseg_import_put( rsm_memseg_import_handle_t im_memseg, off_t offset , void *src_addr, size_t length );この関数は、src_addr と length で指定されたローカルメモリーから、対応するインポートされたセグメントのハンドルとオフセットで指定された場所にデータを書き込みます。
int rsm_memseg_import_get( rsm_memseg_import_handle_t im_memseg, off_t offset , void *dst_addr, size_t length );この関数は rsm_memseg_import_put() と似ていますが、データはインポートされたセグメントから dest_vec で定義されたローカル領域に移動します。
put ルーチンと get ルーチンは、引数 offset で指定したバイトオフセット位置から、指定された量のデータを書き込みまたは読み込みます。これらのルーチンはセグメントのベースから開始します。オフセットは適切な境界に整列している必要があります。たとえば、rsm_memseg_import_get64() の場合、offset と datap はダブルワード境界に整列している必要がありますが、rsm_memseg_import_put32() の場合、offset はワード境界に整列している必要があります。
デフォルトでは、セグメントのバリアモード属性は暗黙的 (implicit) です。暗黙的なバリアモードは、操作から戻ってきたときにはデータ転送が完了または失敗していると呼び出し元が仮定していることを意味します。デフォルトのバリアモードは暗黙的であるため、アプリケーションはバリアを初期化する必要があります。デフォルトのバリアモードを使用するとき、put ルーチンまたは get ルーチンを呼び出す前に、アプリケーションは rsm_memseg_import_init_barrier() 関数を使用してバリアを初期化します。明示的な操作モードを使用するには、呼び出し元はバリア操作を使用して転送を強制的に完了させる必要があります。転送を強制的に完了させた後、結果としてエラーが発生したかどうかを判断する必要があります。
オフセットを rsm_memseg_import_map() ルーチンに渡すことによって、インポートセグメントは部分的にマッピングできます。インポートセグメントを部分的にマッピングする場合、put ルーチンまたは get ルーチンの offset 引数はセグメントのベースからです。ユーザーは、正しいバイトオフセットが put ルーチンまたは get ルーチンに渡されていることを確認する必要があります。
戻り値:成功した場合、0 を返します。そうでない場合、エラー値を返します。
セグメントハンドルが無効です。
アドレスが不正です。
メモリー整列が無効です。
オフセットが無効です。
長さが無効です。
アクセス権がありません。
バリアが初期化されていません。
入出力完了エラー
接続が中断されました。
リソースが不足しています。
rsm_memseg_import_putv() と rsm_memseg_import_getv () 関数を使用すると、単一の読み取り元アドレスや単一の書き込み先アドレスではなく、入出力要求のリストを使用できます。
関数のプロトタイプ:
int rsm_memseg_import_putv(rsm_scat_gath_t *sg_io);Scatter-Gather リスト (sg_io) の入出力ベクトル構成要素を使用すると、ローカル仮想アドレスまたは local_memory_handles を指定できます。ハンドルはローカルアドレス範囲を繰り返して使用するための効率的な方法です。割り当てられたシステムリソース (ロックダウンされたローカルメモリーなど) はハンドルが解放されるまで保持されます。ハンドルをサポートする関数は rsm_create_localmemory_handle() と rsm_free_localmemory_handle() です。
仮想アドレスやハンドルは、ベクトルに集めて、単一のリモートセグメントに書き込むことができます。この結果はまた、単一のリモートセグメントから読み取って、仮想アドレスまたはハンドルのベクトルに分散できます。
ベクトル全体の入出力は関数が返る前に初期化されます。インポートセグメントのバリアモード属性は、関数が返る前に入出力が完了しているかどうかを判断します。バリアモード属性を implicit (暗黙的) に設定すると、ベクトルに入った順番でデータ転送が完了することが保証されます。リストの各エントリは、暗黙的なバリアの開く操作と閉じる操作によって囲まれます。エラーが検出された場合、ベクトルの入出力は中断され、関数はすぐに返ります。残りのカウントは、入出力が完了または初期化されなかったエントリの数を示します。
putv 操作または getv 操作が正常に完了した場合に通知イベントをターゲットセグメントに送信することを指定できます。通知イベントを送信することを指定するには、rsm_scat_gath_t 構造体の flags エントリに RSM_IMPLICIT_SIGPOST 値を指定します。また、flags エントリに RSM_SIGPOST_NO_ACCUMULATE を指定しておくと、RSM_IMPLICIT_SIGPOST が設定されたときに、この値がシグナルポスト操作に渡されます。
戻り値:成功した場合、0 を返します。そうでない場合、エラー値を返します。
Scatter-Gather 構造体ポインタが無効です。
セグメントハンドルが無効です。
コントローラハンドルが無効です。
アドレスが不正です。
オフセットが無効です。
長さが無効です。
アクセス権がありません。
入出力完了エラー
接続が中断されました。
リソースが不足しています。
シグナルによって操作が割り込まれました。
この関数は、後続の putv または getv への呼び出しの入出力ベクトルで使用するためのローカルハンドルを取得します。ロックダウンの可能性があるので、メモリーがローカルハンドルによってスパンされている場合は特に、可能な限りハンドルを解放して、システムリソースを節約してください。
戻り値:成功した場合、0 を返します。そうでない場合、エラー値を返します。
コントローラハンドルが無効です。
ローカルメモリーハンドルが無効です。
長さが無効です。
アドレスが無効です。
メモリーが不足しています。
この関数は、ローカルハンドルに関連するシステムリソースを解放します。プロセスが終了するときにはプロセスに属するすべてのハンドルが解放されますが、この関数を呼び出すことでシステムリソースを節約できます。
戻り値:成功した場合、0 を返します。そうでない場合、エラー値を返します。
コントローラハンドルが無効です。
ローカルメモリーハンドルが無効です。
次の例に、プライマリデータ構造体の定義を示します。
typedef void *rsm_localmemory_handle_t
typedef struct {
ulong_t io_request_count; number of rsm_iovec_t entries
ulong_t io_residual_count; rsm_iovec_t entries not completed
in flags;
rsm_memseg_import_handle_t remote_handle; opaque handle for
import segment
rsm_iovec_t *iovec; pointer to
array of io_vec_t
} rsm_scat_gath_t;
typedef struct {
int io_type; HANDLE or VA_IMMEDIATE
union {
rsm_localmemory_handle_t handle; used with HANDLE
caddr_t virtual_addr; used with
VA_IMMEDIATE
} local;
size_t local_offset; offset from handle base vaddr
size_t import_segment_offset; offset from segment base vaddr
size_t transfer_length;
} rsm_iovec_t;
マッピング操作は、ネイティブなアーキテクチャの相互接続 (Dolphin-SCI や NewLink など) だけで利用できます。セグメントをマッピングすることによって CPU メモリー操作がそのセグメントにアクセスできるようになるので、メモリーアクセスプリミティブを呼び出すオーバーヘッドを省くことができます。
int rsm_memseg_import_map(rsm_memseg_import_handle_t im_memseg, void **address, rsm_attribute_t attr, rsm_permission_t perm, off_t offset, size_t length);この関数は、インポートされたセグメントを呼び出し元のアドレス空間にマッピングします。属性 RSM_MAP_FIXED が指定されている場合、この関数は **address に指定された値にあるセグメントをマッピングします。
typedef enum {
RSM_MAP_NONE = 0x0, /* システムは使用できる仮想アドレスを選択する */
RSM_MAP_FIXED = 0x1, /* セグメントを指定された仮想アドレスにマッピングする */
} rsm_map_attr_t;
戻り値:成功した場合、0 を返します。そうでない場合、エラー値を返します。
セグメントハンドルが無効です。
アドレスが無効です。
長さが無効です。
オフセットが無効です。
アクセス権がありません。
セグメントはすでにマッピングされています。
セグメントは接続されていません。
接続が中断されました。
マッピング中にエラーが発生しました。
アドレスがページ境界に整列されていません。
セグメントのマッピング解除
int rsm_memseg_import_unmap (rsm_memseg_import_handle_t im_memseg );この関数は、ユーザーの仮想アドレス空間からインポートされたセグメントをマッピング解除します。
戻り値:成功した場合、0 を返します。そうでない場合、エラー値を返します。
セグメントハンドルが無効です。
バリア操作は、書き込みアクセス順番メモリーモデルに関する問題を解決するときに使用します。バリア操作は、リモートメモリーアクセスエラーを検出することもできます。
バリア初期化
バリアを開く
バリアを閉じる
書き込みの順番を決定する
閉じる操作を正常に実行することによって、バリアを開いてから閉じるまでの間に発生するアクセス操作が正常に完了することが保証されます。バリアを開いた後、個々のデータアクセス操作 (読み取りと書き込みの両方) が失敗しても、バリアを閉じるまでは報告されません。
バリアの有効範囲内で書き込みの順番を決定するには、明示的なバリア順番決定操作を使用します。バリア順番決定操作の前に発行された書き込み操作は、バリア順番決定操作後に発行された操作よりも前に完了します。あるバリアの有効範囲内の書き込み操作の順番は別のバリアの有効範囲を基準にして決定されます。
バリアの初期化
int rsm_memseg_import_init_barrier (rsm_memseg_import_handle_t im_memseg , rsm_barrier_type_t type, rsmapi_barrier_t *barrier);現在のところ、サポートされるタイプは RSM_BAR_DEFAULT だけです。
戻り値:成功した場合、0 を返します。そうでない場合、エラー値を返します。
セグメントハンドルが無効です。
バリアポインタが無効です。
メモリーが不足しています。
バリアを開く
int rsm_memseg_import_open_barrier (rsmapi_barrier_t *barrier);戻り値:成功した場合、0 を返します。そうでない場合、エラー値を返します。
セグメントハンドルが無効です。
バリアポインタが無効です。
バリアを閉じる
int rsm_memseg_import_close_barrier (rsmapi_barrier_t *barrier);この関数はバリアを閉じて、すべてのストアバッファーをフラッシュします。この関数は、rsm_memseg_import_close_barrier() の呼び出しが失敗した場合、最後の rsm_memseg_import_open_barrier 呼び出しまで、呼び出し元プロセスがすべてのリモートメモリー操作を再試行することを前提にして呼び出されます。
戻り値:成功した場合、0 を返します。そうでない場合、エラー値を返します。
セグメントハンドルが無効です。
バリアポインタが無効です。
バリアが初期化されていません。
バリアが開かれていません。
メモリーアクセスエラー
接続が中断されました。
バリアの順番決定
int rsm_memseg_import_order_barrier (rsmapi_barrier_t *barrier);この関数は、すべてのストアバッファーをフラッシュします。
戻り値:成功した場合、0 を返します。そうでない場合、エラー値を返します。
セグメントハンドルが無効です。
バリアポインタが無効です。
バリアが初期化されていません。
バリアが開かれていません。
メモリーアクセスエラー
接続が中断されました。
バリアの破壊
int rsm_memseg_import_destroy_barrier(rsmapi_barrier_t *barrier);この関数は、すべてのバリアリソースの割り当てを解除します。
戻り値:成功した場合、0 を返します。そうでない場合、エラー値を返します。
セグメントハンドルが無効です。
バリアポインタが無効です。
モードの設定
int rsm_memseg_import_set_mode (rsm_memseg_import_handle_t im_memseg, rsm_barrier_mode_t mode);この関数は、put ルーチンで利用できるオプションの明示的なバリアの有効範囲決定をサポートします。有効なバリアモードは、RSM_BARRIER_MODE_EXPLICIT と RSM_BARRIER_MODE_IMPLICIT の 2 つです。バリアモードのデフォルト値は RSM_BARRIER_MODE_IMPLICIT です。暗黙モードでは、put 操作ごとに暗黙的なバリアの開く操作と閉じる操作が適用されます。バリアモードを RSM_BARRIER_MODE_EXPLICIT に設定する前は、rsm_memseg_import_init_barrier ルーチンを使用して、インポートされたセグメント im_memseg 用のバリアを初期化する必要があります。
戻り値:成功した場合、0 を返します。そうでない場合、エラー値を返します。
セグメントハンドルが無効です。
モードの取得
int rsm_memseg_import_get_mode (rsm_memseg_import_handle_t im_memseg , rsm_barrier_mode_t *mode);この関数は、put ルーチンにおける現在のバリアの有効範囲決定のモード値を取得します。
戻り値:成功した場合、0 を返します。そうでない場合、エラー値を返します。
セグメントハンドルが無効です。
イベント操作によって、プロセスはメモリーアクセスイベントと同期をとることができます。rsm_intr_signal_wait() 関数を使用できない場合、 rsm_memseg_get_pollfd() でポーリング記述子を取得し、poll システムコールを使用することによって、プロセスはイベント待機を多重送信できます。
rsm_intr_signal_post() 操作と rsm_intr_signal_wait() 操作を使用すると、カーネルへの ioctl 呼び出しを処理する必要が生まれます。
シグナルの送信
int rsm_intr_signal_post (void *memseg, uint_t flags);ボイドポインタ *memseg を使用すると、インポートセグメントハンドルまたはエクスポートセグメントハンドルのどちらでもタイプキャスト (型変換) できます。*memseg がインポートセグメントハンドルを参照している場合、この関数はエクスポートしているプロセス (エクスポータ) にシグナルを送信します。*memseg がエクスポートセグメントハンドルを参照している場合、この関数はそのセグメントのすべてのインポータにシグナルを送信します。flags 引数に RSM_SIGPOST_NO_ACCUMULATE を設定すると、あるイベントがすでにターゲットセグメントに対して保留中である場合、当該イベントを破棄します。
戻り値:成功した場合、0 を返します。そうでない場合、エラー値を返します。
セグメントハンドルが無効です。
リモートノードに到達できません。
シグナルの待機
int rsm_intr_signal_wait (void * memseg, int timeout);ボイドポインタ *memseg を使用すると、インポートセグメントハンドルまたはエクスポートセグメントハンドルのどちらでもタイプキャスト (型変換) できます。プロセスは timeout ミリ秒まで、あるいは、イベントが発生するまでブロックされます。値が -1 の場合、プロセスはイベントが発生するまで、あるいは、割り込みが発生するまでブロックされます。
戻り値:成功した場合、0 を返します。そうでない場合、エラー値を返します。
セグメントハンドルが無効です。
タイマーが満了しました。
待機中に割り込みが発生しました。
pollfd の取得
int rsm_memseg_get_pollfd (void *memseg, struct pollfd *pollfd );この関数は、指定された pollfd 構造体を、指定されたセグメントの記述子と rsm_intr_signal_post() で生成された単一固定イベントで初期化します。pollfd 構造体を poll システムコールで使用すると、rsm_intr_signal_post によってシグナル送信されるイベントを待機します。メモリーセグメントがまだ公開されていない場合、poll システムコールは有効な pollfd を返しません。呼び出しが成功するたびに、指定されたセグメントの pollfd 参照カウントがインクリメントします。
戻り値:成功した場合、0 を返します。そうでない場合、エラー値を返します。
セグメントハンドルが無効です。
pollfd の解放
int rsm_memseg_release_pollfd(oid *memseg);この呼び出しは、指定されたセグメントの pollfd 参照カウントをデクリメントします。参照カウントが 0 以外の場合、セグメントを公開解除、破壊、またはマッピング解除する操作は失敗します。
戻り値:成功した場合、0 を返します。そうでない場合、エラー値を返します。
セグメントハンドルが無効です。
この節では、共有メモリー操作のエクスポート側とインポート側における一般的な注意点について説明します。この節ではまた、セグメント、ファイル記述子、および RSM 構成可能パラメータに関する一般的な情報についても説明します。
システムはエクスポート操作またはインポート操作ごとにファイル記述子を割り当てますが、メモリーをインポートまたはエクスポートしているアプリケーションはこの記述子にアクセスできません。プロセスごとのファイル記述子割り当てのデフォルトの制限は 256 です。インポートまたはエクスポートしているアプリケーションは割り当ての制限を適切に調節する必要があります。アプリケーションがファイル記述子の制限値を 256 より大 きく設定した場合、エクスポートセグメントとインポートセグメントに割り当てられるファイル記述子は 256 から始まります。このようなファイル記述子の値が選択されるのは、アプリケーションが通常のファイル記述子を割り当てるのを妨害しないようにするためです。この動作によって、256 より小さなファイル記述子を処理できない 32 ビットアプリケーションが特定の libc 関数を使用できるようになります。
アプリケーションは、再バインド操作が完了するまで、セグメントデータにアクセスしないようにする必要があります。再バインド中にセグメントからデータを取得しようとしてもシステムエラーにはなりませんが、このような操作の結果は定義されていません。仮想アドレス空間はすでにマッピングされており、有効である必要があります。
インポートセグメント用に指定されたコントローラは、セグメントのエクスポートに使用されるコントローラと物理的に接続されている必要があります。
SUNWrsm ソフトウェアパッケージには rsm.conf ファイルがあります。このファイルは /usr/kernel/drv にあります。このファイルは RSM 用の構成ファイルです。rsm.conf ファイルを使用すると、特定の構成可能な RSM プロパティの値を指定できます。現在 rsm.conf ファイルに定義されている構成可能なパラメータには max-exported-memory と enable-dynamic-reconfiguration があります。
エクスポート可能なメモリー量の上限を指定します。この上限は、利用可能なメモリーの合計に対するパー センテージで表現されます。このプロパティの値が 0 の場合、エクスポート可能なメ モリーに上限がないことを示します。
動的再構成が有効であるかどうかを示します。このプロパティの値が 0 の場合、動的再構成が無効であることを示します。1 の場合、動的再構成が有効であることを示します。このプロパティのデフォルトの値は 1 です。
この節では、簡単なプログラムを使用して RSMAPI の使用例を示します。このプログラムはエクスポータノードとインポータノードの 2 つのノード上で動作します。エクスポータノードはメモリーセグメントを作成および公開し、メッセージがセグメントに書き込まれるまで待機します。インポータノードはエクスポートされたセグメントに接続し、メッセージを書き込み、次に、エクスポータにシグナルを送信します。
/*
* Copyright (c) 1998 by Sun Microsystems, Inc.
* All rights reserved.
*/
#include <stdio.h>
#include <rsm/rsmpai.h>
#include <errno.h>
/*
このプログラムを実行するには次の手順を行う:
1 つ目のノード (ノード ID = 1):
rsmtest -e -n 2
2 つ目のノード (ノード ID = 2):
rsmtest -i -n 1
プログラムはコンソールでインポータにメッセージをプロンプト
表示する。任意のメッセージを入力してリターンキーを入力する。
エクスポートコンソールにメッセージが表示される。
*/
typedef struct {
char out;
char in;
char data[1];
}msg_t;
#define SEG_ID 0x400000
#define EXPORT 0
#define IMPORT 1
#define BUFSIZE (1024 * 8)
#define DEFAULT_SEGSZ BUFSIZE
#define RSM_PERM_READ 0400
#define RSM_PERM_WRITE 0200
#define RSM_PERM_RDWR (RSM_PERM_READ|RSM_PERM_WRITE)
#define
RSM_ACCESS_TRUSTED
0666
rsm_topology_t *tp;
int iterations = 10;
int mode = EXPORT;
int test = 0;
char *buf;
int buflen = BUFSIZE;
int offset = 0;
volatile char *iva;
int status;
rsm_memseg_id_t segid;
rsmapi_controller_handle_t ctrl;
rsmapi_controller_attr_t attr;
rsm_memseg_export_handle_t seg;
rsm_memseg_import_handle_t imseg;
rsm_access_entry_t list[2];
rsm_node_id_t dest;
extern void *valloc(size_t);
extern void exit();
extern void sleep();
extern int atoi(const char *);
/* 次の関数はセグメントをエクスポートし、それを発行する
*/
static int
export()
{
int i;
/* メモリーを割り当て、クリアする */
buf = (char *)valloc(buflen);
if (!buf) {
(void) fprintf(stderr, "Unable to allocate memory\n");
exit (1);
}
for (i = 0; i < buflen; i++)
buf[i] = 0;
/* エクスポートメモリーセグメントを作成する */
status = rsm_memseg_export_create(ctrl, &seg, (void *)buf, buflen);
if (status != 0) {
(void) fprintf(stderr,
"unable to create an exported segment %d\n", status);
exit(1);
}
/* ノード 1 とノード 2 に発行するアクロスリストを設定する */
list[0].ae_node = tp->topology_hdr.local_nodeid ;
/* 読み取り権と書き込み権を許可する */
list[0].ae_permission = RSM_ACCESS_TRUSTED;
list[1].ae_node = tp->topology_hdr.local_nodeid + 1;
/* 読み取り権とアクセス権を許可する */
list[1].ae_permission = RSM_ACCESS_TRUSTED;
/* 作成されたエクスポートセグメントを発行する */
status = rsm_memseg_export_publish(seg, &segid, list, 0);
if (status != 0) {
(void) fprintf(stderr, "unable to pub segment %d\n", status);
exit(1);
}
return (0);
}
/* 次の関数はエクスポートされたメモリーセグメントへの接続に使用される */
static void
import()
{
/* ローカルの仮想アドレスを介してアクセスするために
* エクスポートされたセグメントに接続してマッピングを設定する
*/
again:
status = rsm_memseg_import_connect(ctrl, dest, segid, RSM_PERM_RDWR,
&imseg);
if (status != 0) {
(void) fprintf(stderr,
"unable to conect to segment %x err %x\n",
segid, status);
sleep(1);
goto again;
}
iva = NULL;
status = rsm_memseg_import_map(imseg, (void **)&iva,
RSM_MAP_NONE, RSM_PERM_RDWR, 0, buflen);
if (status != 0) {
(void) fprintf(stderr, "unable to mmap segment %d\n", status);
exit(1);
}
}
/* エクスポートセグメントの発行を取り消して破壊する */
static void
export_close()
{
again:
status = rsm_memseg_export_unpublish(seg);
if (status != 0) {
(void) fprintf(stderr,
"unable to create an unpub segment %d\n", status);
sleep(10);
goto again;
}
status = rsm_memseg_export_destroy(seg);
if (status != 0) {
(void) fprintf(stderr, "unable to destroy segment %d\n",
status);
exit(1);
}
}
/* 仮想アドレスのマッピングを取り消してセグメントの接続を解除する */
static void
import_close()
{
status = rsm_memseg_import_unmap(imseg);
if (status != 0) {
(void) fprintf(stderr, "unable to unmap segment %d\n", status);
exit(1);
}
status = rsm_memseg_import_disconnect(imseg);
if (status != 0) {
(void) fprintf(stderr,
"unable to disconnect segment %d\n", status);
exit(1);
}
}
static void
test0()
{
volatile msg_t *mbuf;
/* 報告エラーへのバリア */
rsmapi_barrier_t bar;
int i;
if (mode == EXPORT) {
(void) export();
mbuf = (msg_t *)(buf + offset);
mbuf->in = mbuf->out = 0;
} else {
import();
mbuf = (msg_t *)(iva + offset);
rsm_memseg_import_init_barrier(imseg, RSM_BARRIER_NODE, &bar);
}
(void) printf("Mbuf is %x\n", (uint_t)mbuf);
while (iterations--> 0) {
int e;
switch (mode) {
case EXPORT:
while (mbuf->out == mbuf->in) {
(void) rsm_intr_signal_wait(seg, 1000);
}
(void) printf("msg [0x%x %d %d] ",
(uint_t)mbuf, (int)mbuf->out, mbuf->in);
for (i = 0; mbuf->data[i] != '\0' && i < buflen; i++) {
(void) putchar(mbuf->data[i]);
mbuf->data[i] = '?';
}
(void) putchar('\n');
mbuf->out++;
break;
case IMPORT:
(void) printf("Enter msg [0x%x %d]: ",
(uint_t)mbuf, mbuf->out, mbuf->in);
retry:
e = rsm_memseg_import_open_barrier(&bar);
if (e != 0) {
(void) printf("Barrier open failed %x\n", e);
exit(1);
}
for (i = 0; (mbuf->data[i] = getchar()) != '\n'; i++)
;
mbuf->data[i] = '\0';
rsm_memseg_import_order_barrier(&bar);
mbuf->in++;
e = rsm_memseg_import_close_barrier(&bar);
if (e != 0) {
(void) printf("Barrier close failed, %d\n", e);
goto retry;
}
(void)rsm_intr_signal_post(imseg);
break;
}
}
if (mode == IMPORT) {
import_close();
} else {
export_close();
}
}
void
main(int argc, char *argv[])
{
int unit = 0;
char *device = "sci0";
int i;
segid = SEG_ID;
buflen = DEFAULT_SEGSZ;
while ((i = getopt(argc, argv, "OCGeid:b:sl:n:k:t:c:u:v")) != -1) {
switch (i) {
case 'e':
mode = EXPORT;
break;
case 'i':
mode = IMPORT;
break;
case 'n':
dest = atoi(optarg);
if ((int)dest < 0) dest = 0;
break;
default:
(void) fprintf(stderr, "Usage: %s -ei -n dest\n",
argv[0]);
exit(1);
}
}
status = rsm_get_controller(device, &ctrl);
if (status != 0) {
(void) fprintf(stderr, "Unable to get controller\n");
exit(1);
}
status = rsm_get_controller_attr(ctrl, &attr);
status = rsm_get_interconnect_topology(&tp);
if (status != 0) {
(void) fprintf(stderr, "Unable to get topology\n");
exit(1);
} else {
(void) printf("Local node id = %d\n",
tp->topology_hdr.local_nodeid);
}
if (dest == 0) {
dest = tp->topology_hdr.local_nodeid;
(void) printf("Dest is adjusted to %d\n", dest);
}
switch (test) {
case 0:
test0();
break;
default:
(void) printf("No test executed\n");
break;
}
}