Annotation of nono/vm/scsidomain.cpp, revision 1.1.1.1

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: }

unix.superglobalmegacorp.com

This archive runs on limited infrastructure. Preserving old code on modern bandwidth. Automated agents are requested to crawl responsibly.