# topology.h スケジューラを実装する上で必要なCpuクラスやTopologyクラスなどが実装されたヘッダファイル。 ## [MAX_CPUS](https://github.com/google/ghost-userspace/blob/9ca0a1fb6ed88f0c4b0b40a5a35502938efa567f/lib/topology.h#L27) 扱えるCPUの最大数をマクロで定義している。値は512。 CPUごとに情報を管理するときは、MAX_CPUSの数だけの配列を用意することが多々ある([FIFOスケジューラの例](https://github.com/google/ghost-userspace/blob/9ca0a1fb6ed88f0c4b0b40a5a35502938efa567f/schedulers/fifo/per_cpu/fifo_scheduler.h#L151))。 ## [Cpu](https://github.com/google/ghost-userspace/blob/9ca0a1fb6ed88f0c4b0b40a5a35502938efa567f/lib/topology.h#L44-L115) 1つの論理CPUを表すクラス。 CPUの番号・コアの番号・同時スレッディングの番号などが管理される。 このクラスのインスタンスは直接作成するのではなく、**Topologyクラスを経由して**作成することになっている。実際、コンストラクタはprivateに実装されており、friend定義されているTopologyからしか使うことができないようになっている。 ```cpp class Cpu { ... private: struct CpuRep { // CPUの設定情報をまとめた構造体 int cpu; // CPU番号 int core; // コアの番号 int smt_idx; // siblingsにおける自身の位置(コアなら0) std::unique_ptr<CpuList> siblings; // コアを共有しているCPUのリスト std::unique_ptr<CpuList> l3_siblings; // L3キャッシュを共有しているCPUのリスト int numa_node; // NUMAの番号 }; explicit Cpu(const CpuRep* rep) : rep_(rep) { CHECK_NE(rep, nullptr); } const CpuRep* rep_; // 唯一のメンバ変数 friend Topology; }; ``` 例えば、CPU番号がcpu_idのCpuクラスを作成するには以下のようにする。 ```cpp auto cpu = MachineTopology()->cpu(cpu_id); ``` このクラスは[CpuRep](https://github.com/google/ghost-userspace/blob/9ca0a1fb6ed88f0c4b0b40a5a35502938efa567f/lib/topology.h#L100-L107)というクラス内定義された構造体のインスタンスへのポインタのみを保持する。そのため、**クラスサイズは8bytes**であり、<u>Cpuクラスのコピーは重い処理にはならない</u>。 ```cpp class Cpu { ... private: ... struct CpuRep { int cpu; // CPU番号 int core; int smt_idx; std::unique_ptr<CpuList> siblings; std::unique_ptr<CpuList> l3_siblings; int numa_node; }; const CpuRep* rep_; // 唯一のメンバ変数 ... }; ``` ## CpuMap 複数のCPUを管理するためのクラス。例えば、CPU3〜CPU7の集まりを管理するクラスなど。独自のイテレータを実装しており、以下のような処理が可能。 ```cpp for (const Cpu& cpu : cpus()) { ... } ``` これは抽象基底クラスであり、派生クラスが2つ存在する。 ## CpuList CpuMapをmutexで保護したクラス。CPUの情報はビットマップで管理される。 ### イテレータ 以下のような使い方ができる。 ```cpp for (const Cpu& cpu : cpus()) { ... } ``` ### [Set](https://github.com/google/ghost-userspace/blob/9ca0a1fb6ed88f0c4b0b40a5a35502938efa567f/lib/topology.h#L376-L381) ```cpp void Set(uint32_t id); ``` 引数で指定したCPUのbitをセットする。 ### [Clear](https://github.com/google/ghost-userspace/blob/9ca0a1fb6ed88f0c4b0b40a5a35502938efa567f/lib/topology.h#L385-L390) ```cpp void Clear(uint32_t id); ``` 引数で指定したCPUのbitをクリアする。 ### [GetNthCpu](https://github.com/google/ghost-userspace/blob/9ca0a1fb6ed88f0c4b0b40a5a35502938efa567f/lib/topology.cc#L53-L72) ```cpp Cpu CpuList::GetNthCpu(uint32_t n); ``` n番目のCpuインスタンスを返す関数。 ## AtomicCpuMap CpuMapをアトミック命令で扱うクラス。 :::success :lightning: アトミック処理でできる操作は、例えばn番目のビットをセットする、などの小さな変更。逆にmutexが必要になるのは、64bitを超える範囲をマスクしたりする操作など。用途に応じてそれぞれのクラスを使い分ける。 ::: ## [Topology](https://github.com/google/ghost-userspace/blob/9ca0a1fb6ed88f0c4b0b40a5a35502938efa567f/lib/topology.h#L584-L811) CPU、コア、NUMA、などのマシントポロジーを表現するクラス。 シングルトンで実装してあり、[MachineTopology](https://github.com/google/ghost-userspace/blob/9ca0a1fb6ed88f0c4b0b40a5a35502938efa567f/lib/topology.cc#L546-L549)関数からインスタンスにアクセスできる。 基本的には、Cpuインスタンスを取得したり、CpuListを取得したり、などはこのクラスを経由して行われる。 このクラスが保持しているメンバ変数は以下の通り。 ```cpp class Topology { ... private: // テスト用のメンバ変数は省略 ... const uint32_t num_cpus_; // CPUの数 CpuList all_cpus_ = EmptyCpuList(); // 全てのCpuのリスト std::vector<Cpu::CpuRep> cpus_; // Cpu::CpuRep用のデータストレージ int highest_node_idx_ = -1; std::vector<CpuList> cpus_on_node_; }; ``` ### [コンストラクタ](https://github.com/google/ghost-userspace/blob/9ca0a1fb6ed88f0c4b0b40a5a35502938efa567f/lib/topology.cc#L169-L234) コンストラクタはMachineTopology関数へ最初にアクセスしたときに一度だけ呼ばれる。 コンストラクタ内では、/sys/fs/devices/cpu/ディレクトリ配下のファイルからトポロジーの情報を読み出してきて、各メンバ変数を初期化している。特にcpus_メンバの初期化では、idの設定、siblingsの設定、l3_siblingsの設定、などがされる。 ### [cpu](https://github.com/google/ghost-userspace/blob/9ca0a1fb6ed88f0c4b0b40a5a35502938efa567f/lib/topology.h#L667-L671) ```cpp Cpu cpu(int cpu) const; ``` Cpuのインスタンスを返す関数。 引数でCPUの番号を指定してあげる。 なお、CpuインスタンスはCpuクラスから直接作成することはできないので、このメンバ関数などによって作成するしか方法はない。 ### [all_cpus](https://github.com/google/ghost-userspace/blob/9ca0a1fb6ed88f0c4b0b40a5a35502938efa567f/lib/topology.h#L674) ```cpp const CpuList& all_cpus() const; ``` このマシン上の全てのCPUのリスト(CpuList)を返す関数。 ### EmptyCpuList ```cpp CpuList EmptyCpuList() const { return CpuList(*this); } ``` 空のCpuListを作成する。 トポロジーの情報を伝えているので、マシンに備わっているCPUの数などが設定される。 作成後は `Set(i)` メンバ関数などを用いてCPU情報をセットしていく。 ### ★ ToCpuList ```cpp CpuList ToCpuList(const std::vector<int>& cpus) const; CpuList ToCpuList(const std::vector<Cpu>& cpus) const; ``` CpuList のインスタンスを生成するメンバ関数。vectorとして渡された個別のCpuインスタンスを元に、それらの集合を表すCpuListを作成する関数。以下のようにして使う。 ```cpp CpuList cpus = MachineTopology()->ToCpuList(std::vector<int>{0, 1, 4}); for (auto cpu: cpus) { std::cout << "cpu: " << cpu << std:: endl; } ``` ``` cpu: 0 cpu: 1 cpu: 4 ``` ### ★ ParseCpuStr ```cpp CpuList ParseCpuStr(const std::string& str) const; ``` 文字列からCPUのリストを作成する関数。 文字列としては `"0-5"` のように指定したり、`"1, 3, 5"` のように指定したりできる。 ```cpp CpuList cpus = MachineTopology()->ParseCpuStr("0-1,4"); ``` # enclave.h このヘッダファイルには主に以下の2つのデータ構造が定義されている。 * **AgentConfig**:agentの設定内容をまとめたクラス。FullAgentに渡されるデータ構造 * **Enclave**:enclaveのクラス。なお、抽象基底クラスとなっており、派生クラスとしては**LocalEnclave**のみが定義されている ## AgentConfig agentの設定情報をまとめたクラス。 FullAgentのコンストラクタに渡され、そのままLocalEnlcaveのコンストラクタにも渡される。 CPU情報やトポロジー情報などが渡されることになっている。 ```cpp class AgentConfig { public: Topology* topology_; // トポロジー CpuList cpus_; // 管理するCPUのリスト int numa_node_ = 0; // NUMA関連の情報 int enclave_fd_ = -1; // ghOStFSのenclaveディレクトリのfd CpuTickConfig tick_config_ = CpuTickConfig::kNoTicks; // CPU_TICKメッセージを発行するかどうか int stderr_fd_ = 2; bool mlockall_ = false; ... }; ``` AgentProcessやFullAgentは、**AgentConfigType**という型引数をテンプレートで受け取っている。ここにはAgentConfigか、AgentConfigの派生クラスを渡してあげることになっている。 ++Q. AgentConfigの派生クラスを実装するのはどういうときか?++ A. コマンドライン引数によってチューニング可能なスケジューラの設定項目を増やしたいとき。スケジューラの諸設定は、このクラスのインスタンスによってFullAgentに渡す必要がある。なので、既存のメンバ変数では物足りないなら、AgentConfigの派生クラスを実装してあげるべきである(e.g. [CfsConfig](https://github.com/google/ghost-userspace/blob/9ca0a1fb6ed88f0c4b0b40a5a35502938efa567f/schedulers/cfs/cfs_scheduler.h#L664-L677), ...)。 ## [Enclave](https://github.com/google/ghost-userspace/blob/9ca0a1fb6ed88f0c4b0b40a5a35502938efa567f/lib/enclave.h#L64-L223) ```cpp class Enclave { // めっちゃ色々な関数が宣言されている ... protected: const AgentConfig config_; Topology* topology_; CpuList enclave_cpus_; // 管理しているCPU private: absl::Mutex mu_; // === スケジューラのリスト === std::list<Scheduler*> schedulers_ ABSL_GUARDED_BY(mu_); // === agentのリスト === // 管理するCPUの数だけ存在する。 std::list<Agent*> agents_ ABSL_GUARDED_BY(mu_); bool is_online_ = false; }; ``` enclaveを抽象化したクラス。 ghOStFSのenclaveディレクトリを介した処理のラッパー関数などが用意されている。 Enclaveクラスは抽象基底クラスであり、LocalEnclaveによって継承される。各メンバ関数の実装は、**ほとんどLocalEnclaveに実装されている**ので、そちらを確認してほしい。 ### Ready enclaveの初期化処理が実装されている。 FIFOスケジューラのところでまとめているので、そちらを参照([リンク](https://hackmd.io/@cwtjjkgpTyeVG_KcZmF3LQ/HJNj83_ma/https%3A%2F%2Fhackmd.io%2F%40cwtjjkgpTyeVG_KcZmF3LQ%2FBkiN0cDGa#%E2%91%A3-%E5%88%9D%E6%9C%9F%E5%8C%96%E5%87%A6%E7%90%86%E3%81%AE%E6%9C%80%E5%BE%8C%EF%BC%88Enclave-%E3%81%AE%E5%87%A6%E7%90%86%EF%BC%89))。 ### GetAgent ```cpp virtual Agent* GetAgent(const Cpu& cpu) = 0; ``` cpuに対応するAgentインスタンスを取得する関数。 ### [topology() / cpus()](https://github.com/google/ghost-userspace/blob/9ca0a1fb6ed88f0c4b0b40a5a35502938efa567f/lib/enclave.h#L193-L194) enclaveの管理するCPUやトポロジーの情報を取得する。 ### [GetRunRequest](https://github.com/google/ghost-userspace/blob/9ca0a1fb6ed88f0c4b0b40a5a35502938efa567f/lib/enclave.h#L70) ```cpp virtual RunRequest* GetRunRequest(const Cpu& cpu) = 0; ``` cpuに合致したRunRequestを取得する関数。 これは純粋仮想関数であり、実装の本体はLocalEnclaveに実装されている。 ### CommitRunRequest 1つのトランザクションをコミットする関数。純粋仮想関数。LocalEnclaveに実装がある。 ### CommitRunRequests 複数のトランザクションをコミットする関数。Enclaveに実装はあるが、愚直な実装のため効率が悪い。LocalEncalveの方により効率的な実装があるので、そっちを参照。 ## LocalEnclave LocalEnclaveはEnclaveの派生クラスなので、Enclaveのメンバ変数を継承する。また、新しく追加されたメンバ変数は以下のようになっていた。 ```cpp class LocalEnclave final : public Enclave { ... private: // 各CPUに紐付いたagent関連のデータをまとめた配列。 CpuRep cpus_[MAX_CPUS]; // agent ➝ kernel の通信用の共有メモリを指すポインタ。 // この共有メモリには、ghost_txnデータ構造が置いてある。 ghost_cpu_data* data_region_ = nullptr; size_t data_region_size_ = 0; // 共有メモリのサイズ bool destroy_when_destructed_; // /sys/fs/ghost/enclave_<id>/配下のファイルの記述子 int dir_fd_ = -1; int ctl_fd_ = -1; int agent_online_fd_ = -1; }; ``` [CpuRep構造体](https://github.com/google/ghost-userspace/blob/9ca0a1fb6ed88f0c4b0b40a5a35502938efa567f/lib/enclave.h#L611-L614)は、LocalEnclaveのクラス内で定義されている。CPUごとに管理するデータをまとめた構造体である。 ```cpp struct CpuRep { Agent* agent; LocalRunRequest req; } ABSL_CACHELINE_ALIGNED; ``` ### コンストラクタ(初期化時) LocalEnclaveインスタンスを作るときには、①既存のenclaveディレクトリに対応するインスタンスを作成するか、②新しくghOStFSにenclaveディレクトリを作成してそれに対応するインスタンスを生成するか、のどちらかを選ぶ。 ```cpp // 既存のenclaveを利用する場合は、AgentConfigにenclaveディレクトリのfdを渡しておく。 LocalEnclave::LocalEnclave(AgentConfig config) : Enclave(config), dir_fd_(config.enclave_fd_) { if (dir_fd_ == -1) { CreateAndAttachToEnclave(); // 新しくenclaveを作成する } else { AttachToExistingEnclave(); // 既存のenclaveを利用 } // enclaveが管理するCPUごとに初期化処理を行う。 // ここでどんな処理がなされているかは後で説明する。 BuildCpuReps(); // AgentConfigのtick_config_の値がCpuTickConfig::kAllTicksのときは、 // CPU_TICKメッセージが発行されるように設定する。 if (config_.tick_config_ == CpuTickConfig::kAllTicks) { SetDeliverTicks(true); } CHECK_EQ(agent_bpf_init(), 0); } ``` ### [CreateAndAttachToEnclave](https://github.com/google/ghost-userspace/blob/9ca0a1fb6ed88f0c4b0b40a5a35502938efa567f/lib/enclave.cc#L418-L461)(初期化時) 新しくenclaveディレクトリを作成し、そこに自身をattachする関数。 ```cpp void LocalEnclave::CreateAndAttachToEnclave() { destroy_when_destructed_ = true; // ① /sys/fs/ghost/ctlに対し"create n 83"を書き込み、enclaveディレクトリを作成する ctl_fd_ = LocalEnclave::MakeNextEnclave(); // ② 作成されたenclave_nディレクトリのfdをdir_fd_にセットする dir_fd_ = LocalEnclave::GetEnclaveDirectory(ctl_fd_); // ③ 新しくenclaveを作るかに関係なく行う初期化処理 //   カーネルとユーザーで共有するcpu_dataのmmapとSW領域の設定を行う CommonInit(); // ④ enclave_dir/cpumaskに管理するCPUのリストを渡して設定する //   成功するまで何回か試す。 int cpumask_fd = openat(dir_fd_, "cpumask", O_RDWR); std::string cpumask = enclave_cpus_.CpuMaskStr(); constexpr int kMaxRetries = 10; int retries = 0; do { int ret = write(cpumask_fd, cpumask.c_str(), cpumask.length()); if (ret == cpumask.length()) { break; } absl::SleepFor(absl::Milliseconds(50)); } while (++retries < kMaxRetries); close(cpumask_fd); } ``` ### [CommonInit](https://github.com/google/ghost-userspace/blob/9ca0a1fb6ed88f0c4b0b40a5a35502938efa567f/lib/enclave.cc#L358-L379)(初期化時) LocalEnclaveのコンストラクタから呼ばれる関数。 agentとカーネルで共有するメモリ領域の設定などを行う。 具体的には、cpu_data領域とstatus word領域である。 ```cpp void LocalEnclave::CommonInit() { // ① enclave_n/cpu_dataファイルを読み書き可でopenする int data_fd = LocalEnclave::GetCpuDataRegion(dir_fd_); // ② cpu_dataをmmapする data_region_size_ = GetFileSize(data_fd); data_region_ = static_cast<ghost_cpu_data*>( mmap(/*addr=*/nullptr, data_region_size_, PROT_READ | PROT_WRITE, MAP_SHARED, data_fd, /*offset=*/0)); close(data_fd); // ③ SW領域も初期化する GhostHelper()->SetGlobalStatusWordTable( new LocalStatusWordTable(dir_fd_, /*id=*/0, config_.numa_node_)); } ``` ### [BuildCpuReps](https://github.com/google/ghost-userspace/blob/9ca0a1fb6ed88f0c4b0b40a5a35502938efa567f/lib/enclave.cc#L382-L395)(初期化時) cpus_メンバの初期化を行う関数。 ```cpp void LocalEnclave::BuildCpuReps() { cpu_set_t cpuset = topology_->ToCpuSet(*cpus()); for (int i = 0; i < topology_->num_cpus(); ++i) { if (!CPU_ISSET(i, &cpuset)) { continue; } ghost_txn* txn = &data_region_[i].txn; Cpu cpu = topology_->cpu(txn->cpu); CpuRep* rep = &cpus_[cpu.id()]; rep->req.Init(this, cpu, txn); // LocalRunRequest::Initの実行 rep->agent = nullptr; } } ``` ### [デストラクタ](https://github.com/google/ghost-userspace/blob/9ca0a1fb6ed88f0c4b0b40a5a35502938efa567f/lib/enclave.cc#L479-L492)(終了時) enclaveディレクトリをcloseしたりする。 ### [GetRunRequest](https://github.com/google/ghost-userspace/blob/9ca0a1fb6ed88f0c4b0b40a5a35502938efa567f/lib/enclave.h#L496-L498) そのCPUのLocalRunRequestへのポインタを返す関数。 実装は以下のようになっている。 ```cpp LocalRunRequest* GetRunRequest(const Cpu& cpu) final { return &cpus_[cpu.id()].req; } ``` ### [CommitRunRequest](https://github.com/google/ghost-userspace/blob/9ca0a1fb6ed88f0c4b0b40a5a35502938efa567f/lib/enclave.cc#L559-L562) CommitRunRequestは、SubmitRunRequestとCompleteRunRequestを順に行うだけの関数。 ```cpp bool LocalEnclave::CommitRunRequest(RunRequest* req) { SubmitRunRequest(req); return CompleteRunRequest(req); } ``` SubmitRunRequest は、TXN_COMMITをenclaveに対して発行する。この処理で呼び出したシステムコール内で、次のタスクがスケジュールされるので、この処理が終了したときには、次のスケジューリング契機になったときである。 CompleteRunReqeustは、**トランザクションのstateを見て、そのトランザクションが成功したのかどうかをbool値で返す関数である**。 ### [CommitRunRequests](https://github.com/google/ghost-userspace/blob/9ca0a1fb6ed88f0c4b0b40a5a35502938efa567f/lib/enclave.cc#L502-L511) ```cpp bool LocalEnclave::CommitRunRequests(const CpuList& cpu_list); ``` 複数のトランザクションをまとめてコミットする関数。コミットは一度のシステムコールで行えるため、効率がいい。 コミットした**全てのトランザクションが成功だった場合はtrue**を、**1つでも失敗したトランザクションがあった場合はfalse**を返す。 ### [SubmitRunRequest](https://github.com/google/ghost-userspace/blob/9ca0a1fb6ed88f0c4b0b40a5a35502938efa567f/lib/enclave.cc#L564-L573) トランザクションがopenしていれば、**GhostHelper()->Commit(cpu)を実行**する。 ### [SubmitRunRequests](https://github.com/google/ghost-userspace/blob/9ca0a1fb6ed88f0c4b0b40a5a35502938efa567f/lib/enclave.cc#L513-L516) トランザクションがopenしていれば、**GhostHelper()->Commit(cpu)を実行**する。 ### [CompleteRunRequest](https://github.com/google/ghost-userspace/blob/9ca0a1fb6ed88f0c4b0b40a5a35502938efa567f/lib/enclave.cc#L575-L635) トランザクションが完了するまで待ち、トランザクションが成功すればtrueを、失敗すればfalseを返す関数。 ### CommitSyncRequests sync グループのコミットをするための関数。 実装は以下のようなっていて、複数のCPUでのトランザクションのコミットをまとめて行う。 ```cpp bool LocalEnclave::CommitSyncRequests(const CpuList& cpu_list) { if (SubmitSyncRequests(cpu_list)) { // コミットに成功した場合は所有権がカーネルによって解放されている。 // そのため特に何もせずに終了。 return true; } // コミットに失敗した場合はユーザー側が所有権を開放する。 bool failure_detected = false; for (const Cpu& cpu : cpu_list) { RunRequest* req = GetRunRequest(cpu); if (!CompleteRunRequest(req)) { // Cannot bail early even though the return value has been finalized // because we must call CompleteRunRequest() on all cpus in the list. failure_detected = true; } } CHECK(failure_detected); ReleaseSyncRequests(cpu_list); return false; } ``` ### [PingRunRequest](https://github.com/google/ghost-userspace/blob/9ca0a1fb6ed88f0c4b0b40a5a35502938efa567f/lib/enclave.cc#L651-L658) ```cpp bool PingRunRequest(const RunRequest* req); ``` 引数にRunRequestを受け取り、そのRunRequestのCPUへ向けてPingを飛ばす、というもの。 ## [RunRequestOptions](https://github.com/google/ghost-userspace/blob/9ca0a1fb6ed88f0c4b0b40a5a35502938efa567f/lib/enclave.h#L237-L272) ```cpp struct RunRequestOptions { Gtid target = Gtid(0); BarrierToken target_barrier = StatusWord::NullBarrierToken(); BarrierToken agent_barrier = StatusWord::NullBarrierToken(); int commit_flags = 0; int run_flags = 0; int sync_group_owner = kSyncGroupNotOwned; bool allow_txn_target_on_cpu = false; }; ``` RunRequest::Openの引数として使われるデータ構造。 この構造体の各フィールドは[ghost_txn構造体](https://hackmd.io/@cwtjjkgpTyeVG_KcZmF3LQ/HJNj83_ma/https%3A%2F%2Fhackmd.io%2F%40cwtjjkgpTyeVG_KcZmF3LQ%2FBkry_h2ba#ghost_txn)の各フィールドに対応している。 各フィールドの説明は以下の通り。 ||| |-|-| | target | ghost_txt.gtidにセットされる値。次に実行状態にするタスクのgtidをセットする。 | target_barrier | ghost_txt.task_barrierにセットされる値。カーネルはここの値を比較することで、そのタスクに関するメッセージを全て処理したのかを検証する。 | agent_barrier | ghost_txt.agent_barrierにセットされる値。ここの値は、ローカルコミットでは確認されるが、リモートコミットでは確認されないことになっている。ここの値をもとに、最新の情報が全て処理されたのかをカーネルは検証する。 | commit_flags | ghost_txt.commit_flagsにセットされる値。デフォルト値は0であり、コミットはgreedyに行われる。詳しくは、ghost_txnの説明を参照。 | commit_flags | ghost_txt.commit_flagsにセットされる値。詳しくは、ghost_txnの説明を参照。 | run_flags | ghost_txt.run_flagsにセットされる値。詳しくは、ghost_txnの説明を参照。 | sync_group_owner | ghost_txt.sync_group_ownerにセットされる値。デフォルト値はkSyncGroupNotOwned(-1)。もし、この値と異なる値がセットされていた場合、RunRequestをopenしたときに、所有権が放棄されるまで待つようになるらしい。 ## [RunRequest](https://github.com/google/ghost-userspace/blob/9ca0a1fb6ed88f0c4b0b40a5a35502938efa567f/lib/enclave.h#L282-L407) Ghost::Run()のAPIを実装したクラス。 一度オープンすれば、非同期にリクエストを発行できる。 また、まとめてリクエストを発行することもできる。 ```cpp class RunRequest { [ ... ] protected: Enclave* enclave_ = nullptr; Cpu cpu_; }; ``` いくつかのメソッドが定義されているので、それらを見ていく。 ### [Init](https://github.com/google/ghost-userspace/blob/9ca0a1fb6ed88f0c4b0b40a5a35502938efa567f/lib/enclave.h#L287-L290) ```cpp void Init(Enclave* enclave, const Cpu& cpu); ``` 初期化関数。[LocalEnclave::BuildCpuReps()](#BuildCpuReps)から呼び出される。 enclave_とcpu_の2つのメンバ変数を初期化する。 ### Open ```cpp virtual void Open(const RunRequestOptions& options) = 0; ``` トランザクションの設定をして、 これは純粋仮想関数であり、実装の詳細はLocalRunRequest::Openを参照してほしい。 1つのトランザクションをコミットするには以下のようにする。 ```cpp RunRequest* req = Enclave::GetRunRequest(cpu); req->Open({.target = ..., .target_barrier = ..., .agent_barrier = ..., .commit_flags = ...}); Enclave::CommitRunRequests({cpu}); ``` なお、複数のトランザクションを一度にコミットすることも可能である。その場合は、それぞれのトランザクションの内容をOpenを使って設定しておき、Enclave::CommitRunRequestsの引数にコミットするCPUのリストを渡せばよい。 ### [LocalYield](https://github.com/google/ghost-userspace/blob/9ca0a1fb6ed88f0c4b0b40a5a35502938efa567f/lib/enclave.h#L325-L327) agentがローカルCPUを明け渡すときに発行するリクエスト。 コメントによると、スケジューリングモデルによって用途は変わってくるらしい。 * Centralized型:satellite agentはこのリクエストをする。 * Per-CPU型:スケジュールするタスクがなくなったり、CFSタスクなどにCPUを譲ったりするときにこのリクエストをする。 このリクエストを送ったときに呼ばれるカーネル側の処理は ghost_run である。 ### [Commit](https://github.com/google/ghost-userspace/blob/9ca0a1fb6ed88f0c4b0b40a5a35502938efa567f/lib/enclave.h#L339) このリクエストをコミットする。 やってる処理はEnclave::CommitRunRequest。 ### [open](https://github.com/google/ghost-userspace/blob/9ca0a1fb6ed88f0c4b0b40a5a35502938efa567f/lib/enclave.h#L346) TXNがOpenしているときはtrueを返す。 ### [committed](https://github.com/google/ghost-userspace/blob/9ca0a1fb6ed88f0c4b0b40a5a35502938efa567f/lib/enclave.h#L348) TXNのコミット処理が**終了**していればtrueを返す。 正常終了でなくてもいい。 ### [succeeded](https://github.com/google/ghost-userspace/blob/9ca0a1fb6ed88f0c4b0b40a5a35502938efa567f/lib/enclave.h#L350) TXNのコミット処理が**正常終了**していればtrueを返す。 ### [failed](https://github.com/google/ghost-userspace/blob/9ca0a1fb6ed88f0c4b0b40a5a35502938efa567f/lib/enclave.h#L349) TXNのコミット処理が**異常終了**していればtrueを返す。 ## [LocalRunRequest](https://github.com/google/ghost-userspace/blob/9ca0a1fb6ed88f0c4b0b40a5a35502938efa567f/lib/enclave.h#L409-L488) ```cpp class LocalRunRequest : public RunRequest { [ ... ] private: ghost_txn* txn_ = nullptr; bool allow_txn_target_on_cpu_ = false; }; ``` RunRequestの派生クラス。 メンバ関数の具体的な実装を伴う。特にghost_txnへのポインタを持っており、ghost_txnを介したトランザクションの発行などを行う。 ### [Open](https://github.com/google/ghost-userspace/blob/9ca0a1fb6ed88f0c4b0b40a5a35502938efa567f/lib/enclave.cc#L757-L796) 引数optionsで指定された内容をtxn_に書き込み、txn_->stateにGHOST_TXN_READYを設定する関数。 この関数によってトランザクションをopenしたあとは**必ずコミット処理を行う**必要がある。 ```cpp void LocalRunRequest::Open(const RunRequestOptions& options) { ... // ghost_txnの各フィールド値を設定する txn_->agent_barrier = options.agent_barrier; txn_->gtid = options.target.id(); txn_->task_barrier = options.target_barrier; txn_->run_flags = options.run_flags; txn_->commit_flags = options.commit_flags; ... // TXNの状態を"GHOST_TXN_READY"に設定する txn_->state.store(GHOST_TXN_READY, std::memory_order_release); } ``` ### [Abort](https://github.com/google/ghost-userspace/blob/9ca0a1fb6ed88f0c4b0b40a5a35502938efa567f/lib/enclave.cc#L798-L813) 現在オープンしているトランザクションをAbortする。 実装では txn_->state に GHOST_TXN_ABORTED を書き込むことになっている。なお、stateがREADY状態出ない場合は何もしない。 アボート処理が成功すれば true を返す。失敗すれば false を返す。 失敗するケースとして考えられるのは、 * このトランザクションがすでにカーネルによってコミットされたか * 他のCPUからトランザクションの請求が来ているか のどちらか。