Annotation of nono/vm/scsidomain.cpp, revision 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.