<style> u { text-decoration-color: gray; text-decoration-style: wavy; /* 波線 */ } </style> # Status Word Status Word(以降SW)とは、カーネルから agent に対して公開されるデータ構造の1つであり、agent スレッドや ghOSt スレッドの状態に関する情報が含まれる。 スレッド1つにつき SW が1つ割り当てられる。 SW は [**ghost_status_word**](https://github.com/google/ghost-kernel/blob/edd5f9490d82df24c16f90a62f7be05c6c389867/include/uapi/linux/ghost.h#L191-L197) という構造体で表現される。フォーマットは以下の通り。 ![F53E348F-E2EA-4E2F-A3D1-2335290A2820](https://hackmd.io/_uploads/Bynu_viEp.jpg) **barrier** には、**バリア値**が格納されている。これはメッセージが書き込まれるたびにインクリメントされていく値である。詳しくは別の資料にまとめた(バリア)。 **flags** には、この SW 自体の状態(使われているかなど)・agent 固有のフラグ・タスクの状態、などが含まれる。この flags に使われるフラグは **GHOST_SW_XXX** という名前でマクロ定義されている([ソースコード](https://github.com/google/ghost-kernel/blob/edd5f9490d82df24c16f90a62f7be05c6c389867/include/uapi/linux/ghost.h#L199-L212))。 **gtid** には、そのスレッドの GTID が格納されている。 **switch_time** には、そのスレッドが行なったコンテキストスイッチの回数が格納されている。 **runtime** には、そのスレッドの実行に費やしたCPU時間が格納されている。単位はナノ秒。 :::success ghost-userspace では [LocalStatusWord](https://github.com/google/ghost-userspace/blob/9ca0a1fb6ed88f0c4b0b40a5a35502938efa567f/lib/ghost.h#L485-L503) というクラスでラップされている。 ::: # SW関連のデータ構造 <u>複数の SW が1つの共有メモリにまとめられて agent に公開される</u>。このような共有メモリはカーネルでは **SW領域**と呼ばれている(ユーザー空間ライブラリでは [**StatusWordTable**](https://github.com/google/ghost-userspace/blob/9ca0a1fb6ed88f0c4b0b40a5a35502938efa567f/lib/ghost.h#L41-L79) と呼ばれている)。agent はその共有メモリをファイルとして mmap することでアクセスできるようになる。 SW領域は、ヘッダーとSWの配列からなる。 ![10EFE31C-D556-430C-8AF7-C3973EF77935](https://hackmd.io/_uploads/BkVhGdsVa.jpg) # SWへのアクセス SW領域は複数存在することができる。そのため、SW領域を識別するための**ID**が割り振られている。従って、<u>あるSWの位置を示すには、**SW領域のIDとそのSW領域内のSW配列のインデックスという2つの情報が必要**である</u>(これは [ghost_sw_info](https://github.com/google/ghost-kernel/blob/edd5f9490d82df24c16f90a62f7be05c6c389867/include/uapi/linux/ghost.h#L89-L92) 構造体でまとめて管理される)。 以下の図はSWの場所を ghost_sw_info によって特定する図を表している。 青色の ghost_sw_info は、2番目のSWテーブルの上から3つ目のSWを指定している。 ![5AE3773F-7EC6-44FD-ABBD-CD347BE4B221](https://hackmd.io/_uploads/B1TOL6-Np.jpg) (図はカーネル側のデータ構造で、ピンク色で塗られた部分はユーザー空間にも公開される) :::warning :dart: SW領域は複数存在することができると書いたが、google-userspace の実装はこれに対応できていない。1つのグローバルなSW領域のみを管理することになっている。もし、大量のタスクを扱う必要があるのならば、ここの実装を書き換える必要がありそう。 ⇣SW領域内の全てのSWを走査する関数の実装だが、1つのSW領域内のSWしか走査できていない。 ```cpp void LocalEnclave::ForEachTaskStatusWord( const std::function<void(ghost_status_word* sw, uint32_t region_id, uint32_t idx)> l) { // TODO: Need to support more than one SW region. StatusWordTable* tbl = GhostHelper()->GetGlobalStatusWordTable(); CHECK_NE(tbl, nullptr); tbl->ForEachTaskStatusWord(l); } ``` ::: # SWの取得方法 SW にアクセスするには ghost_sw_info の情報が必要になる。この情報を取得方法は、agent と ghOSt スレッドとで異なる。 agentのSWは、**専用のシステムコールを発行**することで取得する。 ghOStスレッドのSWは、**TASK_NEWメッセージが発行されたときに、メッセージと一緒に渡される**。 # SW領域の初期化 ghOStFS において enclave ディレクトリの ctl ファイルに "**create sw_region \<id> \<numa_node>**" というコマンドを書き込むことで作成する。 作成されたSW領域は、/sys/fs/ghost/enclave_\<n>/sw_region/sw_\<id> というファイルでユーザー空間に公開され、mmapによってマップできるようになる。 カーネル側の実装を詳しく見ていく(カーネルの実装に興味がない場合は飛ばしていい)。 上記のコマンドを ctl ファイルに書き込んだときに実行されるカーネル側の関数は [create_sw_region](https://github.com/google/ghost-kernel/blob/edd5f9490d82df24c16f90a62f7be05c6c389867/kernel/sched/ghost.c#L7635-L7678) である。 ```c static int create_sw_region(struct kernfs_open_file *ctl_of, unsigned int id, unsigned int numa_node) { ... // ① enclave_n/sw_regionsディレクトリをopenする dir = kernfs_find_and_get(e->enclave_dir, "sw_regions"); ... // ② 新しく作成するファイルの名前"sw_<id>"を作成する if (snprintf(name, sizeof(name), "sw_%u", id) >= sizeof(name)) { ... } ... // ③ enclave_n/sw_regions/sw_<id>というファイルをRead-Onlyで作成する swr_kn = kernfs_create_file_ns(dir, name, 0440, e->uid, e->gid, 0, &gf_ops_e_swr, e, NULL); ... // ④ ファイルの中身をsw_regionとして初期化する(詳しくは後ほど) swr = ghost_create_sw_region(e, id, numa_node); ... // ⑤ SW領域を有効化する swr_kn->attr.size = swr->mmap_sz; swr_kn->priv = swr; kernfs_activate(swr_kn); ... } ``` [ghost_create_sw_region](https://github.com/google/ghost-kernel/blob/edd5f9490d82df24c16f90a62f7be05c6c389867/kernel/sched/ghost.c#L3077-L3120) の実装を詳しく見ていく。 この関数ではSW領域用のメモリをカーネル空間に確保し、その領域に対応する [ghost_sw_region](https://github.com/google/ghost-kernel/blob/edd5f9490d82df24c16f90a62f7be05c6c389867/kernel/sched/ghost.c#L126-L132) のインスタンスへのポインタを返す。一度作成されたSW領域は encalve が削除されるまでメモリに残り続ける。 ```c static struct ghost_sw_region *ghost_create_sw_region(struct ghost_enclave *e, unsigned int id, unsigned int node) { struct ghost_sw_region *swr; struct ghost_sw_region_header *h; ... // ① ghost_sw_region構造体用のメモリ領域を確保する //   この構造体はSW領域を管理するためのメタデータで、カーネル側にのみ見えている swr = kzalloc_node(sizeof(struct ghost_sw_region), GFP_KERNEL, node); ... //   SW領域は複数アロケートすることができる。それらの領域はリストで連結して管理される INIT_LIST_HEAD(&swr->list); // ② SW領域に必要なメモリ領域のサイズを計算する(手元の環境では0x200000) swr->mmap_sz = calculate_sw_region_size(); // ③ カーネルとユーザーで共有されるメモリをアロケートする //   これがSW領域 swr->header = vmalloc_user_node_flags(swr->mmap_sz, node, GFP_KERNEL); ... // ④ SW領域のヘッダ部分を初期化する h = swr->header; h->id = id; h->numa_node = node; h->version = GHOST_SW_REGION_VERSION; h->start = sizeof(struct ghost_sw_region_header); h->capacity = (swr->mmap_sz - h->start) / sizeof(struct ghost_status_word); h->available = h->capacity; // ⑤ enclaveが管理するSW領域のリストの末尾に加える enclave_add_sw_region(e, swr); swr->enclave = e; return swr; } ``` # SWのフラグ SWのflagsに設定できるフラグは[ここ](https://github.com/google/ghost-kernel/blob/edd5f9490d82df24c16f90a62f7be05c6c389867/include/uapi/linux/ghost.h#L199-L212)で定義されている。 使われるフラグは、以下の3種類に分けられる。 * agentスレッド専用のフラグ * ghOStスレッド専用のフラグ * スレッドの種類によらずに使われるフラグ ## スレッドの種類によらずに使われるフラグ ### GHOST_SW_F_INUSE そのステータスワードが**使用中のとき**にセットされるフラグ。 ステータスワード領域には使われているステータスワードと使われてい**ない**ステータスワード(これから使われる)が存在する。これらを見分けるために使われるフラグである。 なお、このフラグは、新しいghOStスレッドができたときに呼ばれる[ghost_initialize_status_word関数の中](https://github.com/google/ghost-kernel/blob/edd5f9490d82df24c16f90a62f7be05c6c389867/kernel/sched/ghost.c#L2771)でセットされる。 ### GHOST_SW_F_CANFREE このステータスワードがもう使われていないときにセットされるフラグ。 ghOStスレッドがghOStの管理下から抜けるときなどにうセットされる(例えば[release_from_ghost関数の内部](https://github.com/google/ghost-kernel/blob/edd5f9490d82df24c16f90a62f7be05c6c389867/kernel/sched/ghost.c#L4918))。 ### GHOST_SW_F_ALLOCATED このステータスワードをあるスレッド用に使うために割り当てるときにセットされるフラグ。 ## agentスレッド専用のフラグ ### GHOST_SW_CPU_AVAIL そのCPU上の**rqにghOStタスク以外のタスクが存在している場合にセット**されるフラグ。 Centralized型のスケジューラでは、GlobalAgentを変えるときに使ったりする。 カーネルの実装では、このフラグは[handle_cpu_availability](https://github.com/google/ghost-kernel/blob/edd5f9490d82df24c16f90a62f7be05c6c389867/kernel/sched/ghost.c#L254-L274)の内部でセット・クリアされる。 ### GHOST_SW_BOOST_PRIO agentの実行優先度がboostされているときにセットされるフラグ。 ++Q. **実行優先度がboostされている**、とはどういうことか?++ ghost-kernelでは、ghOStスレッドはIDLEスレッドの次に優先度が低く設定されている。そのため、ghOStスレッドはCFSスレッドなどによってプリエンプトされてしまう。しかし、agentに関しては、専用のスケジューリングクラスを実装することで、CFSクラスやRTクラスのスレッドが実行可能状態になっていようとも、最高優先度でスケジュールされるようになっている。 このように、**ghOStクラスよりも優先度の高いスケジューリングクラスのスレッドが1つ以上実行可能状態になっている状況で、 agentスレッドが実行状態になっていることを「優先度がboostされている」としている**のだ。 agent スレッドの優先度ブーストのイメージ図を以下に示す。 ![DD1BB8D8-F6B4-42F8-90F1-D33E23F4A025](https://hackmd.io/_uploads/B1S3ksE4T.jpg) :::warning :dart: ここらへんの詳しい内容については、カーネル側の[pick_next_ghost_agent](https://github.com/google/ghost-kernel/blob/edd5f9490d82df24c16f90a62f7be05c6c389867/kernel/sched/ghost.c#L1510-L1608)関数の実装を見るとよい。 :::