Annotation of nono/vm/virtio_block.cpp, revision 1.1.1.4

1.1       root        1: //
                      2: // nono
                      3: // Copyright (C) 2024 nono project
                      4: // Licensed under nono-license.txt
                      5: //
                      6: 
                      7: //
                      8: // VirtIO ブロックデバイス
                      9: //
                     10: 
                     11: #include "virtio_block.h"
                     12: #include "virtio_def.h"
                     13: #include "config.h"
1.1.1.2   root       14: #include "mainram.h"
1.1       root       15: #include "memorystream.h"
1.1.1.3   root       16: #include "monitor.h"
1.1       root       17: 
                     18: // デバイス構成レイアウト
                     19: class VirtIOBlkConfigWriter
                     20: {
                     21:  public:
                     22:        le64 capacity {};
                     23:        le32 size_max {};
                     24:        le32 seg_max {};
                     25:        struct {
                     26:                le16 cylinders {};
                     27:                u8   heads {};
                     28:                u8   sectors {};
                     29:        } geometry;
                     30:        le32 blk_size {};
                     31:        struct {
                     32:                u8   physical_block_exp {};
                     33:                u8   alignment_offset {};
                     34:                le16 min_io_size {};
                     35:                le32 opt_io_size {};
                     36:        } topology;
                     37:        u8   writeback {};
                     38:        le32 max_discard_sectors {};
                     39:        le32 max_discard_seg {};
                     40:        le32 discard_sector_alignment {};
                     41:        le32 max_write_zeroes_sectors {};
                     42:        le32 max_write_zeroes_seg {};
                     43:        u8   write_zeroes_may_unmap {};
                     44: 
                     45:  public:
                     46:        void WriteTo(uint8 *dst) const;
                     47: };
                     48: 
                     49: // コンストラクタ
1.1.1.2   root       50: VirtIOBlockDevice::VirtIOBlockDevice(uint slot_, uint id_)
1.1       root       51:        : inherited(OBJ_VIRTIO_BLOCK(id_), slot_)
                     52: {
                     53:        id = id_;
                     54: 
                     55:        // 短縮形
1.1.1.2   root       56:        AddAlias(string_format("vblk%u", id));
1.1       root       57: 
                     58:        device_id = VirtIO::DEVICE_ID_BLOCK;
                     59:        int num_max = 32;
1.1.1.3   root       60:        vqueues.emplace_back(this, 0, "RequestQ", num_max);
1.1       root       61:        blocksize = 512;
                     62: 
1.1.1.2   root       63:        // 割り込み名
                     64:        snprintf(intrname, sizeof(intrname), "VIOBlk%u", id);
                     65:        // 完了通知メッセージ
                     66:        msgid = MessageID::VIRTIO_BLOCK_DONE(id);
                     67: 
1.1.1.3   root       68:        monitor = gMonitorManager->Regist(ID_MONITOR_VIRTIO_BLOCK(id), this);
                     69:        monitor->func = ToMonitorCallback(&VirtIOBlockDevice::MonitorUpdate);
                     70:        monitor->SetSize(MONITOR_WIDTH, 2 + 4 * 1 + num_max + 3);
1.1       root       71: }
                     72: 
                     73: // デストラクタ
                     74: VirtIOBlockDevice::~VirtIOBlockDevice()
                     75: {
                     76: }
                     77: 
                     78: // 初期化
                     79: bool
                     80: VirtIOBlockDevice::Init()
                     81: {
                     82:        if (inherited::Init() == false) {
                     83:                return false;
                     84:        }
                     85: 
1.1.1.2   root       86:        const std::string imgkey = string_format("virtio-block%u-image", id);
                     87:        const std::string wikey = string_format("virtio-block%u-writeignore", id);
1.1       root       88: 
                     89:        // オープン前に writeignore をチェック。see scsidev.cpp
                     90:        write_ignore = gConfig->Find(wikey).AsInt();
                     91: 
                     92:        // オープン (値が空ならここには来ない)
                     93:        const ConfigItem& itemimg = gConfig->Find(imgkey);
                     94:        assert(itemimg.AsString().empty() == false);
                     95:        std::string path = gMainApp.NormalizePath(itemimg.AsString());
                     96:        if (LoadDisk(path) == false) {
                     97:                return false;
                     98:        }
                     99: 
                    100:        uint seg_max = vqueues[0].num_max - 2;
                    101: 
                    102:        // DEVICE_FEATURES と構成レイアウトを用意。
                    103:        VirtIOBlkConfigWriter cfg;
                    104:        SetDeviceFeatures(VIRTIO_BLK_F_SEG_MAX), cfg.seg_max = seg_max;
                    105:        SetDeviceFeatures(VIRTIO_BLK_F_BLK_SIZE), cfg.blk_size = blocksize;
                    106:        if (GetWriteMode() >= SCSIDisk::RW::WriteProtect) {
                    107:                SetDeviceFeatures(VIRTIO_BLK_F_RO);
                    108:        }
                    109: 
                    110:        cfg.capacity = image.GetSize() / 512; // capacity は 512 バイト単位
                    111:        cfg.WriteTo(&device_config[0]);
                    112: 
                    113:        return true;
                    114: }
                    115: 
                    116: // バックエンドのディスクイメージを開く。
                    117: // 成功すれば true、失敗すれば false を返す。
                    118: bool
                    119: VirtIOBlockDevice::LoadDisk(const std::string& pathname_)
                    120: {
                    121:        off_t size;
1.1.1.2   root      122:        int w_ok;
1.1       root      123: 
                    124:        pathname = pathname_;
                    125:        if (image.CreateHandler(pathname) == false) {
                    126:                return false;
                    127:        }
                    128: 
1.1.1.2   root      129:        w_ok = image.IsWriteable();
                    130:        if (w_ok < 0) {
1.1       root      131:                return false;
                    132:        }
                    133: 
                    134:        // 書き込みモードをここで確定
                    135:        if (w_ok) {
                    136:                if (write_ignore) {
                    137:                        write_mode = SCSIDisk::RW::WriteIgnore;
                    138:                } else {
                    139:                        write_mode = SCSIDisk::RW::Writeable;
                    140:                }
                    141:        } else {
                    142:                write_mode = SCSIDisk::RW::WriteProtect;
                    143:        }
                    144: 
                    145:        // ここでオープン
                    146:        bool read_only = (GetWriteMode() != SCSIDisk::RW::Writeable);
                    147:        if (image.Open(read_only, write_ignore) == false) {
                    148:                return false;
                    149:        }
                    150: 
1.1.1.2   root      151:        // セクタ単位になっていること
                    152:        size = image.GetSize();
                    153:        if (size % blocksize != 0) {
                    154:                warnx("%s: Bad image size(%ju): Not a multiple of blocksize(%u)",
                    155:                        pathname.c_str(), (uintmax_t)size, blocksize);
                    156:                image.Close();
                    157:                return false;
                    158:        }
                    159:        // 最大サイズの制約ある?
                    160: 
1.1       root      161:        // 書き込み無視なら一応ログ出力
                    162:        if (GetWriteMode() == SCSIDisk::RW::WriteIgnore) {
                    163:                putmsg(0, "write is ignored");
                    164:        }
                    165: 
                    166:        return true;
                    167: }
                    168: 
                    169: void
                    170: VirtIOBlockDevice::MonitorUpdate(Monitor *, TextScreen& screen)
                    171: {
                    172:        int y = 0;
                    173: 
                    174:        screen.Clear();
                    175: 
                    176:        y = MonitorUpdateDev(screen, y);
                    177:        y++;
                    178:        y = MonitorUpdateVirtQueue(screen, y, vqueues[0]);
                    179:        y++;
                    180:        y = MonitorUpdateVirtQDesc(screen, y, vqueues[0]);
                    181: }
                    182: 
                    183: // ディスクリプタを一つ処理する。
                    184: void
1.1.1.2   root      185: VirtIOBlockDevice::ProcessDesc(VirtIOReq& req)
1.1       root      186: {
1.1.1.2   root      187:        VirtQueue *q = req.q;
                    188:        uint32 type;
                    189:        uint64 sector;
1.1       root      190:        uint32 status;
                    191: 
1.1.1.2   root      192:        // ヘッダ部は 16 バイト。
                    193:        // +0.L: type (コマンド)
                    194:        // +4.L: (reserved)
                    195:        // +8.L: 開始セクタ番号
                    196: 
                    197:        if (ReqReadLE32(req, &type) == false) {
1.1.1.4 ! root      198:                putlog(0, "Cannot read type in header");
1.1.1.2   root      199:                return;
                    200:        }
                    201:        ReqReadLE32(req); // skip
                    202:        if (ReqReadLE64(req, &sector) == false) {
1.1.1.4 ! root      203:                putlog(0, "Cannot read sector in header");
1.1.1.2   root      204:                return;
                    205:        }
                    206:        putlog(3, "%s req.idx=%u type=%x sec=$%x'%08x",
                    207:                __func__, req.idx, type, (uint32)(sector >> 32), (uint32)sector);
                    208: 
                    209:        // 転送データ長は、ディスクリプタの全長から
                    210:        // ヘッダ(16バイト) とステータスバイト(1バイト) を引いた部分。
                    211:        // という求め方しかない。
                    212:        uint32 datalen;
1.1.1.4 ! root      213:        if (req.totallen() >= 16 + 1) {
        !           214:                datalen = req.totallen() - (16 + 1);
1.1.1.2   root      215:        } else {
                    216:                datalen = 0;
                    217:        }
                    218: 
                    219:        switch (type) {
1.1       root      220:         case VIRTIO_BLK_T_IN:
1.1.1.2   root      221:                status = CmdRead(req, sector, datalen);
1.1       root      222:                access_read = q->last_avail_idx;
                    223:                break;
                    224: 
                    225:         case VIRTIO_BLK_T_OUT:
1.1.1.2   root      226:                status = CmdWrite(req, sector, datalen);
1.1       root      227:                access_write = q->last_avail_idx;
                    228:                break;
                    229: 
                    230:         default:
1.1.1.2   root      231:                putlog(0, "%s req.type=%u (NOT IMPLEMENTED)", __func__, type);
1.1       root      232:                status = VIRTIO_BLK_S_UNSUPP;
                    233:                break;
                    234:        }
1.1.1.2   root      235:        ReqWriteU8(req, status);
1.1       root      236: }
                    237: 
                    238: // 読み込みコマンド (BLK_T_IN) 実行。
                    239: uint32
1.1.1.2   root      240: VirtIOBlockDevice::CmdRead(VirtIOReq& req, uint64 sector, uint32 datalen)
1.1       root      241: {
1.1.1.4 ! root      242:        std::vector<uint8> databuf(datalen);
1.1       root      243: 
1.1.1.4 ! root      244:        if (image.Read(databuf.data(), sector * 512, databuf.size()) == false) {
        !           245:                return VIRTIO_BLK_S_IOERR;
        !           246:        }
        !           247:        if (ReqWriteRegion(req, databuf.data(), datalen) != 0) {
        !           248:                return VIRTIO_BLK_S_IOERR;
1.1       root      249:        }
1.1.1.4 ! root      250: 
1.1       root      251:        return VIRTIO_BLK_S_OK;
                    252: }
                    253: 
                    254: // 書き込みコマンド (BLK_T_OUT) 実行。
                    255: uint32
1.1.1.2   root      256: VirtIOBlockDevice::CmdWrite(VirtIOReq& req, uint64 sector, uint32 datalen)
1.1       root      257: {
1.1.1.4 ! root      258:        std::vector<uint8> databuf(datalen);
1.1       root      259: 
1.1.1.4 ! root      260:        if (ReqReadRegion(req, databuf.data(), datalen) != 0) {
        !           261:                return VIRTIO_BLK_S_IOERR;
        !           262:        }
        !           263:        if (image.Write(databuf.data(), sector * 512, databuf.size()) == false) {
        !           264:                return VIRTIO_BLK_S_IOERR;
1.1       root      265:        }
                    266: 
1.1.1.2   root      267:        return VIRTIO_BLK_S_OK;
1.1       root      268: }
                    269: 
                    270: const char *
1.1.1.2   root      271: VirtIOBlockDevice::GetFeatureName(uint feature) const
1.1       root      272: {
                    273:        static std::pair<uint, const char *> names[] = {
                    274:                { VIRTIO_BLK_F_SIZE_MAX,        "SIZE_MAX" },
                    275:                { VIRTIO_BLK_F_SEG_MAX,         "SEG_MAX" },
                    276:                { VIRTIO_BLK_F_GEOMETRY,        "GEOMETRY" },
                    277:                { VIRTIO_BLK_F_RO,                      "RO" },
                    278:                { VIRTIO_BLK_F_BLK_SIZE,        "BLK_SIZE" },
                    279:                { VIRTIO_BLK_F_FLUSH,           "FLUSH" },
                    280:                { VIRTIO_BLK_F_TOPOLOGY,        "TOPOLOGY" },
                    281:                { VIRTIO_BLK_F_CONFIG_WCE,      "CFG_WCE" },
                    282:                { VIRTIO_BLK_F_DISCARD,         "DISCARD" },
                    283:                { VIRTIO_BLK_F_WRITE_ZEROES,"WZEROES" },
                    284:        };
                    285: 
                    286:        for (auto& p : names) {
                    287:                if (feature == p.first) {
                    288:                        return p.second;
                    289:                }
                    290:        }
                    291:        return inherited::GetFeatureName(feature);
                    292: }
                    293: 
                    294: // virtio_blk_config 構造体を書き出す。
                    295: void
                    296: VirtIOBlkConfigWriter::WriteTo(uint8 *dst) const
                    297: {
                    298:        MemoryStreamLE mem(dst);
                    299: 
1.1.1.2   root      300:        mem.Write8(capacity);
                    301:        mem.Write4(size_max);
                    302:        mem.Write4(seg_max);
                    303:        mem.Write2(geometry.cylinders);
                    304:        mem.Write1(geometry.heads);
                    305:        mem.Write1(geometry.sectors);
                    306:        mem.Write4(blk_size);
                    307:        mem.Write1(topology.physical_block_exp);
                    308:        mem.Write1(topology.alignment_offset);
                    309:        mem.Write2(topology.min_io_size);
                    310:        mem.Write4(topology.opt_io_size);
                    311:        mem.Write1(writeback);
                    312:        mem.Write1(0);
                    313:        mem.Write1(0);
                    314:        mem.Write1(0);
                    315:        mem.Write4(max_discard_sectors);
                    316:        mem.Write4(max_discard_seg);
                    317:        mem.Write4(discard_sector_alignment);
                    318:        mem.Write4(max_write_zeroes_sectors);
                    319:        mem.Write4(max_write_zeroes_seg);
                    320:        mem.Write1(write_zeroes_may_unmap);
                    321:        mem.Write1(0);
                    322:        mem.Write1(0);
                    323:        mem.Write1(0);
1.1       root      324: }

unix.superglobalmegacorp.com

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