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