|
|
1.1 root 1: //
2: // nono
3: // Copyright (C) 2021 nono project
4: // Licensed under nono-license.txt
5: //
6:
7: //
8: // スレッドを持つデバイス
9: //
10:
11: #include "thread.h"
1.1.1.3 root 12: #include "mainapp.h"
1.1 root 13: #include "mythread.h"
1.1.1.6 root 14: #include <time.h>
15: #include <algorithm>
16:
17: static void PrintSuggestion(int) __unused;
1.1 root 18:
19: // コンストラクタ
1.1.1.3 root 20: ThreadDevice::ThreadDevice(uint objid_)
1.1.1.2 root 21: : inherited(objid_)
1.1 root 22: {
1.1.1.5 root 23: // SetName() はこのクラスでオーバーライドしているので
24: // ここで(改めて)実行する必要がある。
25: SetName(GetName());
1.1 root 26: }
27:
28: // デストラクタ
29: ThreadDevice::~ThreadDevice()
30: {
31: // ここで virtual の Terminate() は呼べないので、
32: // 継承側のデストラクタで TerminateThread() をそれぞれ呼ぶこと。
33: }
34:
1.1.1.5 root 35: // オブジェクト名を設定(更新)するとともにスレッド名も更新する。
36: // Object::SetName() のオーバーロード。
1.1 root 37: void
1.1.1.5 root 38: ThreadDevice::SetName(const std::string& newname)
39: {
40: inherited::SetName(newname);
41: SetThreadName(newname);
42: }
43:
44: // スレッドにつけるスレッド名を覚えておく
45: // (このスレッドにスレッド名を設定する、ではない)。
46: void
47: ThreadDevice::SetThreadName(const std::string& threadname_)
1.1 root 48: {
49: threadname = threadname_;
50: }
51:
52: // スレッド開始
53: bool
54: ThreadDevice::StartThread()
55: {
1.1.1.5 root 56: // ここまでに名前はセットしておくこと。
57: assert(threadname.empty() == false);
58:
1.1.1.6 root 59: // スレッド起動。
1.1 root 60: std::lock_guard<std::mutex> lock(thread_starter);
1.1.1.4 root 61: try {
1.1.1.6 root 62: thread.reset(new std::thread(&ThreadDevice::OnStart, this));
1.1.1.4 root 63: } catch (...) { }
1.1 root 64: if ((bool)thread == false) {
1.1.1.5 root 65: warnx("Failed to initialize thread(%s) at %s",
66: threadname.c_str(), __method__);
1.1 root 67: return false;
68: }
69: return true;
70: }
71:
1.1.1.6 root 72: // 開始されたスレッドでのエントリポイント。
73: void
74: ThreadDevice::OnStart()
75: {
76: // このスレッドにスレッド名を設定。
77: PTHREAD_SETNAME(threadname.c_str());
78:
79: // パフォーマンス計測のためのリストに追加。
80: // デバッガスレッドはなくてもいいか。
81: if (GetId() != OBJ_DEBUGGER && GetId() != OBJ_HOSTCOM_DBG) {
82: auto thman = gMainApp.GetThreadManager();
83: thman->RegistThread(this, threadname);
84: }
85:
86: // 実行開始。
87: std::lock_guard<std::mutex> lock_sub(this->thread_starter);
88: ThreadRun();
89: }
90:
1.1 root 91: // スレッドに終了を要求し、その終了を待つ。
92: // スレッド外から呼ぶこと。
93: void
94: ThreadDevice::TerminateThread()
95: {
96: if ((bool)thread) {
1.1.1.6 root 97: auto thman = gMainApp.GetThreadManager();
98: thman->UnregistThread(this);
99:
1.1 root 100: Terminate();
101:
102: if (thread->joinable()) {
103: thread->join();
104: }
105:
106: thread.reset();
107: }
108: }
1.1.1.3 root 109:
1.1.1.6 root 110: // このスレッドのスレッドアフィニティを設定する。
1.1.1.5 root 111: /*static*/ void
112: ThreadBase::SetThreadAffinityHint(AffinityClass hint)
1.1.1.3 root 113: {
114: #if defined(HAVE_PTHREAD_SETAFFINITY_NP) && defined(HAVE_MICPUSET)
1.1.1.6 root 115: std::lock_guard<std::mutex> lock(exlock);
1.1.1.3 root 116:
1.1.1.6 root 117: // policy, hint から cpuset を作成する。
1.1.1.3 root 118: //
1.1.1.6 root 119: // policy hint 動作
120: // ------- ----- ----
121: // Neutral * nop
122: // Binding Light set Lo (if 高性能コア数 < 3)
123: // Binding Light nop (if 高性能コア数 >= 3)
124: // Binding Heavy set Hi
125:
126: if (gMainApp.affinity_policy == MainApp::AffinityPolicy::Neutral) {
1.1.1.3 root 127: return;
128: }
129:
1.1.1.6 root 130: const std::vector<bool>& fastcore = gMainApp.fastcore;
131:
132: // 高性能コアが少ない場合、Light スレッドに高性能コアを奪われたくないので
133: // Light スレッドを低性能コアに縛っておきたい。
134: // 高性能コアの数が多ければ、そこまでする必要もないか。
135: if (hint == AffinityClass::Light) {
136: uint nhigh = std::count(fastcore.begin(), fastcore.end(), true);
137: if (nhigh >= 3) {
138: return;
139: }
140: }
141:
142: // ここまで来ると、このスレッドが Heavy なら高性能コアに、
143: // Light なら低性能コアにバインドすることが確定。
1.1.1.3 root 144: MICPUSet cset;
1.1.1.6 root 145: bool target = (hint == AffinityClass::Heavy);
146: for (uint i = 0, end = fastcore.size(); i < end; i++) {
147: if (fastcore[i] == target) {
148: cset.Set(i);
1.1.1.3 root 149: }
150: }
151:
1.1.1.6 root 152: // 設定。
1.1.1.3 root 153: int r = PTHREAD_SETAFFINITY(pthread_self(), cset);
154:
155: if (gMainApp.hostcpu->loglevel >= 1) {
156: std::string csetstr;
1.1.1.6 root 157: for (uint i = 0, end = fastcore.size(); i < end; i++) {
158: // 読みやすさのための区切り。先頭の1つは表示時に捨てる。
159: uint n = i % 16;
160: if (n == 0) {
161: csetstr += '\'';
162: }
163: if (cset.Get(i)) {
164: csetstr += "0123456789ABCDEF"[n];
165: } else {
166: csetstr += '_';
167: }
1.1.1.3 root 168: }
169: gMainApp.hostcpu->putmsgn("SetAffinity:%-12s(%s) %s%s%s",
170: PTHREAD_GETNAME().c_str(),
171: (hint == AffinityClass::Light ? "Light" : "Heavy"),
1.1.1.6 root 172: csetstr.c_str() + 1,
1.1.1.3 root 173: (r == 0 ? "" : " = "),
174: (r == 0 ? "" : strerror(r)));
1.1.1.6 root 175:
176: // この後のエラーメッセージより先にログスレッドが動いたら嬉しい。
177: usleep(1);
1.1.1.3 root 178: }
179:
180: // エラーなら errno ではなく戻り値にエラー番号が返ってくる。
1.1.1.6 root 181: // NetBSD では sysctl で許可しないと一般ユーザはこの機能を使えない。
182: if (r != 0) {
183: errno = r;
184: warn("PTHREAD_SETAFFINITY");
185: PrintSuggestion(r);
186: // 本当は終了したいが、この時点からではちょっと面倒なので放置。
187: // 以降は何もしない。
188: gMainApp.affinity_policy = MainApp::AffinityPolicy::Neutral;
1.1.1.3 root 189: }
190: #else
191: // なければ無視する。
192: #endif
193: }
1.1.1.6 root 194:
195: /*static*/ std::mutex ThreadBase::exlock;
196:
197:
198: //
199: // スレッドマネージャ
200: //
201:
202: // コンストラクタ
203: ThreadManager::ThreadManager()
204: : inherited(OBJ_NONE)
205: {
206: SetName("ThreadManager");
207: }
208:
209: // デストラクタ
210: ThreadManager::~ThreadManager()
211: {
212: }
213:
214: // 表示順のスレッド名
215: static const std::vector<const char *>
216: disp_order = {
217: "Main",
218: "Logger",
219: "SignalThread",
220: "VM",
221: "VirtIOBlock0",
222: "VirtIOBlock1",
223: "VirtIOBlock2",
224: "VirtIOBlock3",
225: "VirtIOBlock4",
226: "VirtIOBlock5",
227: "VirtIOBlock6",
228: "VirtIOBlock7",
229: "VirtIONet",
230: "VirtIOSCSI",
231: "VirtIOEntropy",
232: "VideoRenderer",
233: "SoundRenderer",
234: "HostNet0",
235: "HostNet1",
236: "HostCOM0",
237: "HostCOM1",
238: "HostCOM2",
239: "HostSound",
240: };
241:
242: // スレッドリストに登録する。
243: // 登録したいスレッド自身から呼ぶこと。
244: void
245: ThreadManager::RegistThread(Object *obj, const std::string& name)
246: {
247: // パフォーマンス計測のため自スレッドの clockid を取得。
248: clockid_t clockid;
249: pthread_getcpuclockid(pthread_self(), &clockid);
250:
251: std::lock_guard<std::mutex> lock(threads_mutex);
252: threads.emplace_back(obj, name, clockid);
253: // 都度表示順にソートする。
254: std::sort(threads.begin(), threads.end(),
255: [](const ThreadInfo& a, const ThreadInfo& b) {
256: auto ia = std::find(disp_order.begin(), disp_order.end(), a.name);
257: auto ib = std::find(disp_order.begin(), disp_order.end(), b.name);
258: return ia < ib;
259: }
260: );
261: }
262:
263: // スレッドリストから obj を削除する。
264: void
265: ThreadManager::UnregistThread(Object *obj)
266: {
267: std::lock_guard<std::mutex> lock(threads_mutex);
268:
269: for (auto it = threads.begin(); it != threads.end(); ++it) {
270: if ((*it).obj == obj) {
271: threads.erase(it);
272: break;
273: }
274: }
275: }
276:
277: // スレッドの CPU 利用率を記録する。
278: void
279: ThreadManager::CalcPerf(uint64 rtime)
280: {
281: std::lock_guard<std::mutex> lock(threads_mutex);
282:
283: for (auto& info : threads) {
284: struct timespec ts;
285: clock_gettime(info.clockid, &ts);
1.1.1.7 ! root 286: uint64 t = nsec_to_tsec(sec_to_nsec(ts.tv_sec) + ts.tv_nsec);
1.1.1.6 root 287: if (__predict_true(last_rtime != 0)) {
288: // 2回目以降
289: uint percent = (t - info.last_clock) * 100 / (rtime - last_rtime);
290: info.load->EnqueueForce(percent);
291: }
292: info.last_clock = t;
293: }
294: last_rtime = rtime;
295: }
296:
297: #define hostcpu_putmsg(lv, fmt...) do { \
298: if (hostcpu->loglevel >= (lv)) \
299: hostcpu->putmsgn(fmt); \
300: } while (0)
301:
302: #if defined(__x86_64__)
1.1.1.7 ! root 303: #include <cpuid.h>
1.1.1.6 root 304: // x86_64 CPU の P コア、E コアを調べて fastcore 配列に返す。
305: // fastcore は事前にコア数分確保してあること。
306: // 検出できれば fastcore を更新して true を返す。
307: // 検出できなければ fastcore は不定で false を返す。
308: // 失敗ならエラーメッセージも表示する。
309: // MainApp から呼ばれるが、スレッド関連なのでこっちに置く。
310: /*static*/ bool
311: ThreadManager::DetectCPUAffinity_x86(std::vector<bool>& fastcore)
312: {
313: Object *hostcpu = gMainApp.hostcpu.get();
314:
315: // CPUID(0x1a, 0) は Intel Core 12世代以降でハイブリッドアーキテクチャ
316: // 情報を返す。eax の bit31-24 (最上位バイト) が 0x40 なら P コア、
317: // 0x20 なら E コア。0 ならたぶんハイブリッド構成でない CPU。
318: //
319: // まずどのコアでもいいので現在のコアで実行してみる。
320: // ここでハイブリッド構成だと分かれば、個別のコアについて調べるのだが、
321: // ただしそのためのスレッドアフィニティ指定には特権が必要な場合があるので、
322: // 特権を要求する作業が必要になるかどうかを先に調べておきたいということ。
323:
324: #if defined(HAVE_PTHREAD_SETAFFINITY_NP) && defined(HAVE_MICPUSET)
325: // まず現在のコアで。
326: uint32 eax, ebx, ecx, edx;
327: __cpuid_count(0x1a, 0, eax, ebx, ecx, edx);
328:
329: if ((eax >> 24) == 0) {
330: // CPU はハイブリッド構成ではない。
331: hostcpu_putmsg(1, "%s: Not a hybrid type core", __func__);
332: return false;
333: } else {
334: // CPU はハイブリッド構成なので、個別のコアについて調べる。
335: for (uint i = 0, end = fastcore.size(); i < end; i++) {
336: int r;
337: std::thread th([&] {
338: MICPUSet cset;
339: cset.Set(i);
340: r = PTHREAD_SETAFFINITY(pthread_self(), cset);
341: __cpuid_count(0x1a, 0, eax, ebx, ecx, edx);
342: if ((eax >> 24) == 0x40) {
343: fastcore[i] = true;
344: }
345: });
346: th.join();
347:
348: if (r != 0) {
349: errno = r;
350: warn("PTHREAD_SETAFFINITY");
351: PrintSuggestion(r);
352: return false;
353: }
354: hostcpu_putmsg(1, "%s: Core%u is %c",
355: __func__, i, (fastcore[i] ? 'P' : 'E'));
356: }
357: return true;
358: }
359: #else
360: hostcpu_putmsg(1, "%s: No PTHREAD_SETAFFINITY", __func__);
361: return false;
362: #endif
363: }
364: #endif // __x86_64__
365:
366: // PTHREAD_SETAFFINITY() が EPERM になった時に表示するお知らせ。
367: // 2箇所から呼ばれるので関数にしてある。
368: static void
369: PrintSuggestion(int errorcode)
370: {
371: #if defined(__NetBSD__)
372: if (errorcode == EPERM) {
373: fprintf(stderr,
374: " ** To allow non-privileged users to set thread affinity on NetBSD:\n"
375: " ** # sysctl -w security.models.extensions.user_set_cpu_affinity=1\n"
376: );
377: }
378: #endif
379: }
This archive runs on limited infrastructure. Preserving old code on modern bandwidth. Automated agents are requested to crawl responsibly.