|
|
1.1 root 1: //
2: // nono
3: // Copyright (C) 2025 nono project
4: // Licensed under nono-license.txt
5: //
6:
7: //
8: // SCSI ドメイン
9: //
10:
11: // SCSI ドメインは、1台のイニシエータとそれに繋がるターゲットからなるシステム
12: // 一式を指すことにする。ここには SCSIBus は含まない。
13: //
14: // HBA (SPCとか)
15: // |
16: // +----> .domain ................................................
17: // | (SCSIDomain) :
18: // | : +----------------+ +------------+ +------------+ :
19: // | .parent |.initiator | |.target[0] | |.target[6] | :
20: // |<--------|(SCSIHostDevice)| |(SCSITarget)| |(SCSITarget)| :
21: // | : +----------------+ +------------+ +------------+ :
22: // | : ^ | ^ | ^ | :
23: // | ..........|.|...............|.|.............|.|.........
24: // | | | | | | |
25: // | | |.bus | |.bus | |.bus
26: // +----> .bus .....|.|...............|.|.............|.|.........
27: // (SCSIBus) | v | v | v :
28: // : .device[7] .device[0] .device[6] :
29: // : (SCSIDevice) (SCSIDevice) (SCSIDevice) :
30: // ........................................................
31: //
32: // 初期化 (Create) 時に SCSIDomain は設定ファイルから自分のドメインの
33: // 各 .target[] と .initiator を生成する。
34: // HBA が SCSIBus を持つタイプであれば Init 時に
35: // SCSIDomain::AttachTargets(bus)、SCSIBus::Refresh() ですべてのターゲットを
36: // バスに参加させる。
37: // HBA が SCSIBus を持たないタイプであれば GetDevices() でターゲット情報を
38: // 手元に持っておく。
39: //
40: // 電源オン後、イニシエータの SCSI ID が確定したら HBA は
41: // SCSIDomain::AttachInitiator(bus, id)、SCSIBus::Refresh() でイニシエータを
42: // バスに参加させる。
43:
44: #include "scsidomain.h"
45: #include "scsi.h"
46: #include "scsibus.h"
47: #include "scsidev.h"
48: #include "config.h"
49: #include "monitor.h"
50:
51: // コンストラクタ
1.1.1.3 ! root 52: SCSIDomain::SCSIDomain(IODevice *parent_,
! 53: const char *domain_name_, const char *config_name_)
1.1 root 54: : inherited(OBJ_SCSI)
55: {
56: parent = parent_;
1.1.1.3 ! root 57: domain_name = domain_name_;
1.1 root 58: config_name = config_name_;
59:
60: monitor = gMonitorManager->Regist(ID_MONITOR_SCSIDEVS, this);
1.1.1.3 ! root 61: monitor->SetCallback(&SCSIDomain::MonitorScreen);
1.1 root 62: // サイズは Create で決まる
63: }
64:
65: // デストラクタ
66: SCSIDomain::~SCSIDomain()
67: {
68: }
69:
70: // 動的なコンストラクション
71: bool
72: SCSIDomain::Create()
73: {
74: // 設定を読み込む。
75: for (int id = 0; id < MaxDevices; id++) {
76: const std::string key = string_format("%s-id%u-image",
77: config_name, id);
78: const ConfigItem& item = gConfig->Find(key);
79: const std::string& image = item.AsString();
80:
81: // 空ならデバイスなし
82: if (image.empty()) {
83: // 未接続部分のパラメータは減らしておくか。
84: // --show-config するとさすがに無駄に多くて邪魔なので。
85: const std::string wikey = string_format("%s-id%u-writeignore",
86: config_name, id);
87: gConfig->Delete(wikey);
88:
89: // -seektime は virtio-scsi にはない。
90: if (strncmp(config_name, "virtio", 6) != 0) {
91: const std::string stkey = string_format("%s-id%u-seektime",
92: config_name, id);
93: gConfig->Delete(stkey);
94: }
95: continue;
96: }
97:
98: // ',' より前がデバイス種別 (ここではパスは不要)
99: auto v = string_split(image, ',');
100: // 比較のため前後の空白を取り除いて小文字にする
101: std::string type = string_trim(v[0]);
102: type = string_tolower(type);
103:
104: SCSI::DevType target_devtype;
105: if (type == "hd") {
106: target_devtype = SCSI::DevType::HD;
107: } else if (type == "cd") {
108: target_devtype = SCSI::DevType::CD;
109: } else if (type == "mo") {
110: target_devtype = SCSI::DevType::MO;
111: } else {
112: item.Err("Invaild device type '%s'", type.c_str());
113: return false;
114: }
115:
116: try {
117: target[id].reset(new SCSIDisk(this, id, target_devtype));
118: } catch (...) { }
119: if ((bool)target[id] == false) {
120: warnx("Failed to initialize target[%u] at %s", id, __method__);
121: return false;
122: }
123: putlog(3, "Attach #%u", id);
124: }
125:
126: // イニシエータを生成。
127: try {
128: initiator.reset(new SCSIHostDevice(this, parent));
129: } catch (...) { }
130: if ((bool)initiator == false) {
131: warnx("Failed to initialize SCSHostDevice at %s", __method__);
132: return false;
133: }
134:
135: // この時点ではイニシエータに ID がないため失敗しないはず。
136: bool r = RefreshDevices();
137: assert(r == true);
138:
139: // イニシエータだけ必要な行数が異なるが、この時点の
140: // connected_devices はイニシエータを含まないので都合がいい。
141: monitor->SetSize(75, connected_devices.size() * 4 + 1);
142:
143: return true;
144: }
145:
146: // 初期化。
147: bool
148: SCSIDomain::Init()
149: {
150: // 初期化ではないけど、ログが使えるようになったここでデバッグ表示。
151: if (loglevel >= 1) {
152: const auto& conn = GetConnectedDevices();
153: for (const auto dev : conn) {
1.1.1.3 ! root 154: putmsgn("%s target #%u %s", domain_name, dev->GetMyID(),
1.1 root 155: SCSI::GetDevTypeName(dev->GetDevType()));
156: }
157: }
158:
159: return true;
160: }
161:
162: // すべてのターゲットをバスに参加させる。
163: // バスを持つホストデバイスが Init() 時に呼ぶ。
164: void
165: SCSIDomain::AttachTargets(SCSIBus *bus)
166: {
167: for (uint id = 0; id < MaxDevices; id++) {
1.1.1.2 root 168: if ((bool)target[id]) {
1.1 root 169: target[id]->Attach(bus);
170: }
171: }
172: }
173:
174: // イニシエータに ID を付与し、(あれば)バスに参加させる。
175: // bus は NULL でもよい。
176: // 成功すれば true を返す。
177: // 失敗すればイニシエータの ID を -1 に設定して false を返す。
178: bool
179: SCSIDomain::AttachInitiator(SCSIBus *bus, uint id_)
180: {
181: initiator->SetID(id_);
182:
183: // ドメインに反映させる。
184: if (RefreshDevices() == false) {
185: // 失敗したらイニシエータを切り離しておく。
186: initiator->SetID(-1);
187: RefreshDevices();
188: return false;
189: }
190:
191: if (bus) {
192: initiator->Attach(bus);
193: }
194:
195: return true;
196: }
197:
198: // initiator と target[] から device[] と connected_devices を作り直す。
199: // target[] はイニシエータを含まないターゲットデバイス。
200: // device[] はターゲットと ID の確定したイニシエータを指す穴あきの配列。
201: // connected_devices はイニシエータも含む ID 順の可変長リスト。
202: // initiator か target[] に増減があるか、initiator の ID が確定した時に
203: // 呼ぶこと。
204: // initiator の ID が他と衝突すれば false を返す。この場合 device[] と
205: // connected_devices の状態は不定。
206: bool
207: SCSIDomain::RefreshDevices()
208: {
209: // initiator と taget[] から device[] を作る。
210: device.fill(NULL);
211: for (int id = 0; id < MaxDevices; id++) {
212: if ((bool)target[id]) {
213: device[id] = target[id].get();
214: }
215: if (initiator->GetMyID() == id) {
216: if (device[id]) {
217: return false;
218: }
219: device[id] = initiator.get();
220: }
221: }
222:
223: // device[] から connected_devices を作る。
224: connected_devices.clear();
225: for (uint id = 0; id < MaxDevices; id++) {
226: if (device[id]) {
227: connected_devices.push_back(device[id]);
228: }
229: }
230:
231: return true;
232: }
233:
234: // (イニシエータを除く) デバイス数を返す。
235: uint
236: SCSIDomain::GetTargetCount() const
237: {
238: uint count = 0;
239:
240: for (const auto& t : target) {
241: if ((bool)t) {
242: count++;
243: }
244: }
245:
246: return count;
247: }
248:
249: // SCSI デバイスモニタ
250: // (どこでやるのがいいか分からんけど、SCSI デバイスを一括して表示したいので
251: // とりあえずここで)
252: void
1.1.1.3 ! root 253: SCSIDomain::MonitorScreen(Monitor *, TextScreen& screen)
1.1 root 254: {
255: int y;
256:
257: // 012345678901234567890123456789012345678901234567890123456789012345678901234
258: // ID6: HD(2048MB)
259: // MediumLoaded Off LogicalBlock 2048 ImageSize 2,147,483,648
260: // RemovalPrevent Off WriteProtected
261:
262:
263: screen.Clear();
264:
265: y = 0;
266: for (const auto dev : connected_devices) {
267: std::string desc = SCSI::GetDevTypeName(dev->GetDevType());
268: std::string pathname;
269:
270: const SCSIDisk *disk = dynamic_cast<const SCSIDisk*>(dev);
271: if (disk && disk->IsMediumLoaded()) {
272: desc += string_format("(%uMB)",
273: (uint)(disk->GetSize() / 1024 / 1024));
274: pathname = disk->GetPathName();
275: }
276: screen.Print(0, y, "ID%c: %s", '0' + dev->GetMyID(), desc.c_str());
277:
278: #if 1
279: (void)pathname;
280: #else
281: // パス名の非ASCIIどうするか
282: if (!pathname.empty()) {
283: if (pathname.size() > screen.GetCol() - 16 - 3) {
284: pathname = pathname.substr(0, screen.GetCol() - 16 - 3);
285: pathname += "...";
286: }
287: screen.Puts(16, y, pathname.c_str());
288: }
289: #endif
290: y++;
291:
292: // イニシエータならここまで。(今の所イニシエータとディスクしかない)
293: if (disk == NULL) {
294: continue;
295: }
296:
297: const int x1 = 5;
298: const int x2 = 29;
299: const int x3 = 50;
300:
301: // 1行目
302: screen.Print(x1, y,
303: (disk->IsRemovableDevice() ? TA::Normal : TA::Disable),
304: "MediumLoaded %s",
305: disk->IsMediumLoaded() ? "On" : "Off");
306: screen.Print(x2, y, "LogicalBlock %u", disk->GetBlocksize());
307: std::string imagesize = format_number(disk->GetSize());
308: screen.Print(x3, y, "ImageSize %15s", imagesize.c_str());
309: y++;
310:
311: // 2行目
312: screen.Print(x1, y,
313: (disk->IsRemovableDevice() ? TA::Normal : TA::Disable),
314: "RemovalPrevent %s",
315: disk->IsMediumRemovalPrevented() ? "On" : "Off");
316:
317: screen.Puts(x2, y,
318: (disk->IsMediumLoaded() ? TA::Normal : TA::Disable),
319: disk->GetWriteModeStr());
320: y++;
321:
322: y++;
323: }
324: }
This archive runs on limited infrastructure. Preserving old code on modern bandwidth. Automated agents are requested to crawl responsibly.