SimpleKernel 1.17.0
Loading...
Searching...
No Matches
virtio_blk.hpp
Go to the documentation of this file.
1
5#pragma once
6
7#include <cpu_io.h>
8
9#include <array>
10#include <cstddef>
11#include <cstdint>
12#include <type_traits>
13#include <utility>
14
15#include "expected.hpp"
16#include "io_buffer.hpp"
17#include "kernel_log.hpp"
18#include "virtio/defs.h"
23
24namespace virtio::blk {
25
48template <typename TransportT = MmioTransport,
49 typename VirtqueueT = SplitVirtqueue>
50class VirtioBlk {
51 public:
53 using UserData = void*;
54
56 static constexpr uint16_t kMaxInflight = 64;
57
59 static constexpr size_t kMaxSgElements = 18;
60
71 [[nodiscard]] static constexpr auto GetRequiredVqMemSize(uint16_t queue_count,
72 uint32_t queue_size)
73 -> std::pair<size_t, size_t> {
74 // 始终按 event_idx=true 分配,因为特性协商在分配之后
75 size_t per_queue =
76 VirtqueueT::CalcSize(static_cast<uint16_t>(queue_size), true);
77 return {per_queue * queue_count, 4096};
78 }
79
86 [[nodiscard]] static constexpr auto CalcDmaSize(uint16_t queue_size = 128)
87 -> size_t {
88 // 始终按 event_idx=true 分配,确保空间充足
89 return VirtqueueT::CalcSize(queue_size, true);
90 }
91
97 [[nodiscard]] static constexpr auto GetRequiredSlotMemSize()
98 -> std::pair<size_t, size_t> {
99 return {sizeof(RequestSlot) * kMaxInflight, alignof(RequestSlot)};
100 }
101
122 [[nodiscard]] static auto Create(
123 uint64_t mmio_base, const DmaRegion& vq_dma, const DmaRegion& slot_dma,
124 VirtToPhysFunc virt_to_phys = IdentityVirtToPhys,
125 uint16_t queue_count = 1, uint32_t queue_size = 128,
126 uint64_t driver_features = 0) -> Expected<VirtioBlk> {
127 if (queue_count == 0) {
128 return std::unexpected(Error{ErrorCode::kInvalidArgument});
129 }
130 if (queue_count > 1) {
131 }
132
133 // 1. 创建传输层
134 TransportT transport(mmio_base);
135 if (!transport.IsValid()) {
136 return std::unexpected(Error{ErrorCode::kTransportNotInitialized});
137 }
138
139 // 2. 设备初始化序列
140 DeviceInitializer<TransportT> initializer(transport);
141
142 uint64_t wanted_features =
143 static_cast<uint64_t>(ReservedFeature::kVersion1) |
144 static_cast<uint64_t>(ReservedFeature::kEventIdx) | driver_features;
145 auto negotiated_result = initializer.Init(wanted_features);
146 if (!negotiated_result) {
147 return std::unexpected(negotiated_result.error());
148 }
149 uint64_t negotiated = *negotiated_result;
150
151 if ((negotiated & static_cast<uint64_t>(ReservedFeature::kVersion1)) == 0) {
152 return std::unexpected(Error{ErrorCode::kFeatureNegotiationFailed});
153 }
154
155 // 根据协商结果决定是否启用 Event Index
156 bool event_idx =
157 (negotiated & static_cast<uint64_t>(ReservedFeature::kEventIdx)) != 0;
158 if (event_idx) {
159 }
160 // 3. 创建 Virtqueue
161 VirtqueueT vq(vq_dma, static_cast<uint16_t>(queue_size), event_idx);
162 if (!vq.IsValid()) {
163 return std::unexpected(Error{ErrorCode::kInvalidArgument});
164 }
165
166 // 4. 配置队列
167 const uint32_t queue_idx = 0;
168 auto setup_result = initializer.SetupQueue(
169 queue_idx, vq.DescPhys(), vq.AvailPhys(), vq.UsedPhys(), vq.Size());
170 if (!setup_result) {
171 return std::unexpected(setup_result.error());
172 }
173
174 // 5. 激活设备
175 auto activate_result = initializer.Activate();
176 if (!activate_result) {
177 return std::unexpected(activate_result.error());
178 }
179
180 return VirtioBlk(std::move(transport), std::move(vq), negotiated, slot_dma,
181 virt_to_phys);
182 }
183
184 // ======== 异步 IO 接口 (Enqueue/Kick/HandleInterrupt) ========
185
200 [[nodiscard]] auto EnqueueRead(uint16_t queue_index, uint64_t sector,
201 const IoVec* buffers, size_t buffer_count,
202 UserData token = nullptr) -> Expected<void> {
203 return DoEnqueue(ReqType::kIn, queue_index, sector, buffers, buffer_count,
204 token);
205 }
206
224 [[nodiscard]] auto EnqueueWrite(uint16_t queue_index, uint64_t sector,
225 const IoVec* buffers, size_t buffer_count,
226 UserData token = nullptr) -> Expected<void> {
227 return DoEnqueue(ReqType::kOut, queue_index, sector, buffers, buffer_count,
228 token);
229 }
230
240 auto Kick(uint16_t queue_index) -> void {
241 if (queue_index != 0) {
242 return;
243 }
244 // 写屏障:确保 Available Ring 更新对设备可见
245 cpu_io::Wmb();
246
247 if (vq_.EventIdxEnabled()) {
248 auto* avail_event_ptr = vq_.UsedAvailEvent();
249 if (avail_event_ptr != nullptr) {
250 uint16_t avail_event = *avail_event_ptr;
251 uint16_t new_idx = vq_.AvailIdx();
252 if (VringNeedEvent(avail_event, new_idx, old_avail_idx_)) {
253 transport_.NotifyQueue(queue_index);
254 } else {
256 }
257 old_avail_idx_ = new_idx;
258 } else {
259 transport_.NotifyQueue(queue_index);
260 }
261 } else {
262 transport_.NotifyQueue(queue_index);
263 }
264 }
265
278 template <typename CompletionCallback>
279 auto HandleInterrupt(CompletionCallback&& on_complete) -> void {
280 // 确认中断
281 uint32_t isr_status = transport_.GetInterruptStatus();
282 if (isr_status != 0) {
283 transport_.AckInterrupt(isr_status);
284 }
286
287 ProcessCompletions(static_cast<CompletionCallback&&>(on_complete));
289 }
290
300 auto HandleInterrupt() -> void {
301 uint32_t status = transport_.GetInterruptStatus();
302 if (status != 0) {
303 transport_.AckInterrupt(status);
304 }
306 request_completed_ = true;
307 cpu_io::Wmb();
308 }
309
310 // ======== 同步便捷方法 ========
311
323 [[nodiscard]] auto Read(uint64_t sector, uint8_t* data) -> Expected<void> {
324 if (data == nullptr) {
325 return std::unexpected(Error{ErrorCode::kInvalidArgument});
326 }
327 IoVec data_iov{virt_to_phys_(reinterpret_cast<uintptr_t>(data)),
329 return SubmitSyncRequest(ReqType::kIn, sector, &data_iov, 1);
330 }
331
343 [[nodiscard]] auto Write(uint64_t sector, const uint8_t* data)
344 -> Expected<void> {
345 if (data == nullptr) {
346 return std::unexpected(Error{ErrorCode::kInvalidArgument});
347 }
348 IoVec data_iov{
349 virt_to_phys_(reinterpret_cast<uintptr_t>(const_cast<uint8_t*>(data))),
351 return SubmitSyncRequest(ReqType::kOut, sector, &data_iov, 1);
352 }
353
354 // ======== 配置与监控 ========
355
365 [[nodiscard]] auto ReadConfig() const -> BlkConfig {
366 BlkConfig config{};
367
368 config.capacity = transport_.ReadConfigU64(
369 static_cast<uint32_t>(BlkConfigOffset::kCapacity));
370 config.size_max = transport_.ReadConfigU32(
371 static_cast<uint32_t>(BlkConfigOffset::kSizeMax));
372 config.seg_max = transport_.ReadConfigU32(
373 static_cast<uint32_t>(BlkConfigOffset::kSegMax));
374
376 static_cast<uint64_t>(BlkFeatureBit::kGeometry)) != 0) {
377 config.geometry.cylinders = transport_.ReadConfigU16(
378 static_cast<uint32_t>(BlkConfigOffset::kGeometryCylinders));
379 config.geometry.heads = transport_.ReadConfigU8(
380 static_cast<uint32_t>(BlkConfigOffset::kGeometryHeads));
381 config.geometry.sectors = transport_.ReadConfigU8(
382 static_cast<uint32_t>(BlkConfigOffset::kGeometrySectors));
383 }
384
386 static_cast<uint64_t>(BlkFeatureBit::kBlkSize)) != 0) {
387 config.blk_size = transport_.ReadConfigU32(
388 static_cast<uint32_t>(BlkConfigOffset::kBlkSize));
389 }
390
392 static_cast<uint64_t>(BlkFeatureBit::kTopology)) != 0) {
393 config.topology.physical_block_exp = transport_.ReadConfigU8(
394 static_cast<uint32_t>(BlkConfigOffset::kTopologyPhysBlockExp));
395 config.topology.alignment_offset = transport_.ReadConfigU8(
396 static_cast<uint32_t>(BlkConfigOffset::kTopologyAlignOffset));
397 config.topology.min_io_size = transport_.ReadConfigU16(
398 static_cast<uint32_t>(BlkConfigOffset::kTopologyMinIoSize));
399 config.topology.opt_io_size = transport_.ReadConfigU32(
400 static_cast<uint32_t>(BlkConfigOffset::kTopologyOptIoSize));
401 }
402
404 static_cast<uint64_t>(BlkFeatureBit::kConfigWce)) != 0) {
405 config.writeback = transport_.ReadConfigU8(
406 static_cast<uint32_t>(BlkConfigOffset::kWriteback));
407 }
408
410 static_cast<uint64_t>(BlkFeatureBit::kDiscard)) != 0) {
411 config.max_discard_sectors = transport_.ReadConfigU32(
412 static_cast<uint32_t>(BlkConfigOffset::kMaxDiscardSectors));
413 config.max_discard_seg = transport_.ReadConfigU32(
414 static_cast<uint32_t>(BlkConfigOffset::kMaxDiscardSeg));
415 config.discard_sector_alignment = transport_.ReadConfigU32(
416 static_cast<uint32_t>(BlkConfigOffset::kDiscardSectorAlignment));
417 }
418
420 static_cast<uint64_t>(BlkFeatureBit::kWriteZeroes)) != 0) {
421 config.max_write_zeroes_sectors = transport_.ReadConfigU32(
422 static_cast<uint32_t>(BlkConfigOffset::kMaxWriteZeroesSectors));
423 config.max_write_zeroes_seg = transport_.ReadConfigU32(
424 static_cast<uint32_t>(BlkConfigOffset::kMaxWriteZeroesSeg));
425 config.write_zeroes_may_unmap = transport_.ReadConfigU8(
426 static_cast<uint32_t>(BlkConfigOffset::kWriteZeroesMayUnmap));
427 }
428
430 static_cast<uint64_t>(BlkFeatureBit::kSecureErase)) != 0) {
431 config.max_secure_erase_sectors = transport_.ReadConfigU32(
432 static_cast<uint32_t>(BlkConfigOffset::kMaxSecureEraseSectors));
433 config.max_secure_erase_seg = transport_.ReadConfigU32(
434 static_cast<uint32_t>(BlkConfigOffset::kMaxSecureEraseSeg));
435 config.secure_erase_sector_alignment = transport_.ReadConfigU32(
436 static_cast<uint32_t>(BlkConfigOffset::kSecureEraseSectorAlignment));
437 }
438
439 if ((negotiated_features_ & static_cast<uint64_t>(BlkFeatureBit::kMq)) !=
440 0) {
441 config.num_queues = transport_.ReadConfigU16(
442 static_cast<uint32_t>(BlkConfigOffset::kNumQueues));
443 }
444
445 return config;
446 }
447
454 [[nodiscard]] auto GetCapacity() const -> uint64_t {
455 return transport_.ReadConfigU64(
456 static_cast<uint32_t>(BlkConfigOffset::kCapacity));
457 }
458
464 [[nodiscard]] auto GetNegotiatedFeatures() const -> uint64_t {
466 }
467
474 [[nodiscard]] auto GetStats() const -> VirtioStats { return stats_; }
475
478 VirtioBlk() = delete;
479 VirtioBlk(VirtioBlk&& other) noexcept
480 : transport_(std::move(other.transport_)),
481 vq_(std::move(other.vq_)),
482 negotiated_features_(other.negotiated_features_),
483 slot_dma_(other.slot_dma_),
484 slots_(other.slots_),
485 virt_to_phys_(other.virt_to_phys_),
486 stats_(other.stats_),
487 slot_bitmap_(other.slot_bitmap_),
488 old_avail_idx_(other.old_avail_idx_),
489 request_completed_(other.request_completed_) {
490 other.slots_ = nullptr;
491 other.slot_bitmap_ = 0;
492 }
493 auto operator=(VirtioBlk&& other) noexcept -> VirtioBlk& {
494 if (this != &other) {
495 transport_ = std::move(other.transport_);
496 vq_ = std::move(other.vq_);
497 negotiated_features_ = other.negotiated_features_;
498 slot_dma_ = other.slot_dma_;
499 slots_ = other.slots_;
500 virt_to_phys_ = other.virt_to_phys_;
501 stats_ = other.stats_;
502 slot_bitmap_ = other.slot_bitmap_;
503 old_avail_idx_ = other.old_avail_idx_;
504 request_completed_ = other.request_completed_;
505 other.slots_ = nullptr;
506 other.slot_bitmap_ = 0;
507 }
508 return *this;
509 }
510 VirtioBlk(const VirtioBlk&) = delete;
511 auto operator=(const VirtioBlk&) -> VirtioBlk& = delete;
512 ~VirtioBlk() = default;
514
515 private:
523 struct RequestSlot {
525 alignas(16) BlkReqHeader header;
527 alignas(4) volatile uint8_t status;
531 uint16_t desc_head;
532 };
533
539 VirtioBlk(TransportT transport, VirtqueueT vq, uint64_t features,
540 const DmaRegion& slot_dma, VirtToPhysFunc v2p)
541 : transport_(std::move(transport)),
542 vq_(std::move(vq)),
543 negotiated_features_(features),
544 slot_dma_(slot_dma),
545 slots_(reinterpret_cast<RequestSlot*>(slot_dma.Data())),
546 virt_to_phys_(v2p),
547 stats_{},
548 slot_bitmap_(0),
550 request_completed_(false) {}
551
566 [[nodiscard]] auto DoEnqueue(ReqType type, uint16_t queue_index,
567 uint64_t sector, const IoVec* buffers,
568 size_t buffer_count, UserData token)
569 -> Expected<void> {
570 if (queue_index != 0) {
571 return std::unexpected(Error{ErrorCode::kInvalidArgument});
572 }
573
574 if (buffer_count + 2 > kMaxSgElements) {
575 return std::unexpected(Error{ErrorCode::kInvalidArgument});
576 }
577
578 auto slot_result = AllocRequestSlot();
579 if (!slot_result) {
581 return std::unexpected(slot_result.error());
582 }
583 uint16_t slot_idx = *slot_result;
584 auto& slot = slots_[slot_idx];
585
586 slot.header.type = static_cast<uint32_t>(type);
587 slot.header.reserved = 0;
588 slot.header.sector = sector;
589 slot.status = 0xFF; // sentinel:设备完成后会覆写
590 slot.token = token;
591
592 std::array<IoVec, kMaxSgElements> readable_iovs{};
593 std::array<IoVec, kMaxSgElements> writable_iovs{};
594 size_t readable_count = 0;
595 size_t writable_count = 0;
596
597 auto slot_base_phys =
598 slot_dma_.phys + static_cast<size_t>(slot_idx) * sizeof(RequestSlot);
599 readable_iovs[readable_count++] = {
600 slot_base_phys + offsetof(RequestSlot, header), sizeof(BlkReqHeader)};
601
602 if (type == ReqType::kIn) {
603 for (size_t i = 0; i < buffer_count; ++i) {
604 writable_iovs[writable_count++] = buffers[i];
605 }
606 } else {
607 for (size_t i = 0; i < buffer_count; ++i) {
608 readable_iovs[readable_count++] = buffers[i];
609 }
610 }
611
612 // 状态字节始终为 device-writable
613 writable_iovs[writable_count++] = {
614 slot_base_phys + offsetof(RequestSlot, status), sizeof(uint8_t)};
615
616 cpu_io::Wmb();
617
618 auto chain_result = vq_.SubmitChain(readable_iovs.data(), readable_count,
619 writable_iovs.data(), writable_count);
620 if (!chain_result) {
621 FreeRequestSlot(slot_idx);
623 return std::unexpected(chain_result.error());
624 }
625
626 slot.desc_head = *chain_result;
627
628 return {};
629 }
630
643 template <typename CompletionCallback>
644 auto ProcessCompletions(CompletionCallback&& on_complete) -> void {
645 cpu_io::Rmb();
646
647 while (vq_.HasUsed()) {
648 auto elem_result = vq_.PopUsed();
649 if (!elem_result) {
650 break;
651 }
652
653 auto elem = *elem_result;
654 auto head = static_cast<uint16_t>(elem.id);
655
656 uint16_t slot_idx = FindSlotByDescHead(head);
657 if (slot_idx < kMaxInflight) {
658 auto& slot = slots_[slot_idx];
659
660 cpu_io::Rmb();
661
662 ErrorCode ec = MapBlkStatus(slot.status);
663 on_complete(slot.token, ec);
664 stats_.bytes_transferred += elem.len;
665 FreeRequestSlot(slot_idx);
666 }
667
668 (void)vq_.FreeChain(head);
669 }
670 }
671
679 [[nodiscard]] auto AllocRequestSlot() -> Expected<uint16_t> {
680 uint64_t free_bits = ~slot_bitmap_;
681 if (free_bits == 0) {
682 return std::unexpected(Error{ErrorCode::kNoFreeDescriptors});
683 }
684 auto idx = static_cast<uint16_t>(__builtin_ctzll(free_bits));
685 if (idx >= kMaxInflight) {
686 return std::unexpected(Error{ErrorCode::kNoFreeDescriptors});
687 }
688 slot_bitmap_ |= (uint64_t{1} << idx);
689 return idx;
690 }
691
697 auto FreeRequestSlot(uint16_t idx) -> void {
698 if (idx < kMaxInflight) {
699 slot_bitmap_ &= ~(uint64_t{1} << idx);
700 }
701 }
702
709 [[nodiscard]] auto FindSlotByDescHead(uint16_t desc_head) const -> uint16_t {
710 uint64_t used = slot_bitmap_;
711 while (used != 0) {
712 auto i = static_cast<uint16_t>(__builtin_ctzll(used));
713 if (slots_[i].desc_head == desc_head) {
714 return i;
715 }
716 used &= used - 1; // clear lowest set bit
717 }
718 return kMaxInflight;
719 }
720
727 [[nodiscard]] static auto MapBlkStatus(uint8_t status) -> ErrorCode {
728 switch (status) {
729 case static_cast<uint8_t>(BlkStatus::kOk):
730 return ErrorCode::kSuccess;
731 case static_cast<uint8_t>(BlkStatus::kIoErr):
732 return ErrorCode::kIoError;
733 case static_cast<uint8_t>(BlkStatus::kUnsupp):
735 default:
737 }
738 }
739
752 [[nodiscard]] static auto VringNeedEvent(uint16_t event_idx, uint16_t new_idx,
753 uint16_t old_idx) -> bool {
754 return static_cast<uint16_t>(new_idx - event_idx - 1) <
755 static_cast<uint16_t>(new_idx - old_idx);
756 }
757
766 auto UpdateUsedEvent() -> void {
767 if (vq_.EventIdxEnabled()) {
768 auto* used_event_ptr = vq_.AvailUsedEvent();
769 if (used_event_ptr != nullptr) {
770 *used_event_ptr = vq_.LastUsedIdx();
771 cpu_io::Wmb();
772 }
773 }
774 }
775
788 [[nodiscard]] auto SubmitSyncRequest(ReqType type, uint64_t sector,
789 const IoVec* buffers,
790 size_t buffer_count) -> Expected<void> {
791 auto enq = DoEnqueue(type, 0, sector, buffers, buffer_count, nullptr);
792 if (!enq) {
793 return std::unexpected(enq.error());
794 }
795
796 Kick(0);
797
798 uint32_t spin_limit = 100000000U;
799
800 for (uint32_t i = 0; i < spin_limit; ++i) {
801 cpu_io::Rmb();
802 if (vq_.HasUsed()) {
803 break;
804 }
805 }
806
807 if (!vq_.HasUsed()) {
808 klog::Warn("Sync request timeout: sector={}", sector);
809 return std::unexpected(Error{ErrorCode::kTimeout});
810 }
811
813 bool done = false;
814 ProcessCompletions([&done, &result](UserData, ErrorCode status) {
815 done = true;
816 result = status;
817 });
819
820 if (!done) {
821 return std::unexpected(Error{ErrorCode::kTimeout});
822 }
823 if (result != ErrorCode::kSuccess) {
824 return std::unexpected(Error{result});
825 }
826 return {};
827 }
829 TransportT transport_;
831 VirtqueueT vq_;
843 uint64_t slot_bitmap_{};
845 uint16_t old_avail_idx_{0};
847 volatile bool request_completed_{false};
848};
849
850} // namespace virtio::blk
Virtio 设备初始化器
auto Activate() -> Expected< void >
激活设备,开始正常运行
auto SetupQueue(uint32_t queue_idx, uint64_t desc_phys, uint64_t avail_phys, uint64_t used_phys, uint32_t queue_size) -> Expected< void >
配置并激活指定的 virtqueue
auto Init(uint64_t driver_features) -> Expected< uint64_t >
执行 virtio 设备初始化序列
Virtio MMIO 传输层
Definition mmio.hpp:61
Split Virtqueue 管理类
Definition split.hpp:38
Virtio 块设备驱动
auto Kick(uint16_t queue_index) -> void
批量触发硬件通知
auto GetCapacity() const -> uint64_t
获取设备容量
VirtioBlk(TransportT transport, VirtqueueT vq, uint64_t features, const DmaRegion &slot_dma, VirtToPhysFunc v2p)
私有构造函数
auto EnqueueWrite(uint16_t queue_index, uint64_t sector, const IoVec *buffers, size_t buffer_count, UserData token=nullptr) -> Expected< void >
异步提交写请求(仅入队描述符,不触发硬件通知)
auto operator=(const VirtioBlk &) -> VirtioBlk &=delete
auto Write(uint64_t sector, const uint8_t *data) -> Expected< void >
同步写入一个扇区
auto operator=(VirtioBlk &&other) noexcept -> VirtioBlk &
static auto VringNeedEvent(uint16_t event_idx, uint16_t new_idx, uint16_t old_idx) -> bool
判断是否需要发送通知(处理 wrap-around)
auto ProcessCompletions(CompletionCallback &&on_complete) -> void
处理 Used Ring 中已完成的请求
auto AllocRequestSlot() -> Expected< uint16_t >
从请求槽池中分配一个空闲槽(O(1) 位图算法)
VirtToPhysFunc virt_to_phys_
Address translation callback.
void * UserData
异步 IO 回调中使用的用户自定义上下文指针类型
volatile bool request_completed_
请求完成标志(由简化版 HandleInterrupt 在中断上下文中设置)
RequestSlot * slots_
Pointer to request slot array (lives in slot_dma_ memory)
static auto Create(uint64_t mmio_base, const DmaRegion &vq_dma, const DmaRegion &slot_dma, VirtToPhysFunc virt_to_phys=IdentityVirtToPhys, uint16_t queue_count=1, uint32_t queue_size=128, uint64_t driver_features=0) -> Expected< VirtioBlk >
创建并初始化块设备
auto DoEnqueue(ReqType type, uint16_t queue_index, uint64_t sector, const IoVec *buffers, size_t buffer_count, UserData token) -> Expected< void >
异步入队请求的内部实现
static constexpr auto CalcDmaSize(uint16_t queue_size=128) -> size_t
计算单个 Virtqueue DMA 缓冲区所需的字节数(向后兼容)
auto GetNegotiatedFeatures() const -> uint64_t
获取协商后的特性位
VirtqueueT vq_
Virtqueue 实例(当前支持单队列)
static constexpr auto GetRequiredVqMemSize(uint16_t queue_count, uint32_t queue_size) -> std::pair< size_t, size_t >
获取多队列所需的总 DMA 内存大小
uint16_t old_avail_idx_
上次 Kick 时的 avail idx(用于 Event Index 通知抑制)
auto FreeRequestSlot(uint16_t idx) -> void
释放请求槽
auto SubmitSyncRequest(ReqType type, uint64_t sector, const IoVec *buffers, size_t buffer_count) -> Expected< void >
同步提交请求的内部实现
auto ReadConfig() const -> BlkConfig
读取块设备配置空间
static constexpr size_t kMaxSgElements
每个 Scatter-Gather 请求的最大 IoVec 数量(含请求头和状态字节)
VirtioBlk(const VirtioBlk &)=delete
auto FindSlotByDescHead(uint16_t desc_head) const -> uint16_t
根据描述符链头索引查找请求槽
static constexpr auto GetRequiredSlotMemSize() -> std::pair< size_t, size_t >
计算 RequestSlot DMA 内存所需字节数
static constexpr uint16_t kMaxInflight
每个设备的最大并发(in-flight)请求数
uint64_t negotiated_features_
协商后的特性位掩码
auto GetStats() const -> VirtioStats
获取性能监控统计数据
auto UpdateUsedEvent() -> void
更新 avail->used_event 字段
TransportT transport_
传输层实例
VirtioStats stats_
性能统计数据
auto HandleInterrupt(CompletionCallback &&on_complete) -> void
中断处理(带完成回调)
auto Read(uint64_t sector, uint8_t *data) -> Expected< void >
同步读取一个扇区
uint64_t slot_bitmap_
请求槽占用位图(bit i = 1 表示 slots_[i] 被占用)
DmaRegion slot_dma_
DMA region backing the request slot pool.
auto EnqueueRead(uint16_t queue_index, uint64_t sector, const IoVec *buffers, size_t buffer_count, UserData token=nullptr) -> Expected< void >
异步提交读请求(仅入队描述符,不触发硬件通知)
static auto MapBlkStatus(uint8_t status) -> ErrorCode
将设备 BlkStatus 映射为 ErrorCode
auto HandleInterrupt() -> void
中断处理(简化版,无回调)
VirtioBlk(VirtioBlk &&other) noexcept
ErrorCode
内核错误码
Definition expected.hpp:11
@ kTransportNotInitialized
传输层未正确初始化
@ kTimeout
操作超时
@ kFeatureNegotiationFailed
特性协商失败
@ kInvalidArgument
@ kNoFreeDescriptors
没有空闲描述符
@ kIoError
IO 操作错误
@ kDeviceError
通用设备报告错误
@ kNotSupported
不支持的操作(通用,非设备特定)
std::expected< T, Error > Expected
std::expected 别名模板
Definition expected.hpp:365
auto IdentityVirtToPhys(uintptr_t virt) -> uintptr_t
恒等映射:物理地址 == 虚拟地址(早期启动 / 无 MMU 时的默认实现)
Definition io_buffer.hpp:25
auto(*)(uintptr_t virt) -> uintptr_t VirtToPhysFunc
虚拟地址到物理地址转换回调类型
Definition io_buffer.hpp:18
void Wmb()
Definition cpu_io.h:58
void Rmb()
Definition cpu_io.h:57
auto Warn(etl::format_string< Args... > fmt, Args &&... args) -> void
以 WARN 级别记录日志
ReqType
块设备请求类型
@ kOut
写入 (VIRTIO_BLK_T_OUT)
@ kIn
读取 (VIRTIO_BLK_T_IN)
constexpr size_t kSectorSize
标准扇区大小(字节)
@ kUnsupp
不支持的操作 (VIRTIO_BLK_S_UNSUPP)
@ kIoErr
IO 错误 (VIRTIO_BLK_S_IOERR)
@ kOk
操作成功 (VIRTIO_BLK_S_OK)
@ kWriteZeroes
设备支持 write zeroes 命令 (VIRTIO_BLK_F_WRITE_ZEROES)
@ kDiscard
设备支持 discard 命令 (VIRTIO_BLK_F_DISCARD)
@ kTopology
设备配置空间中 topology 字段有效 (VIRTIO_BLK_F_TOPOLOGY)
@ kMq
设备支持多队列 (VIRTIO_BLK_F_MQ)
@ kConfigWce
设备可在回写和直写缓存模式间切换 (VIRTIO_BLK_F_CONFIG_WCE)
@ kBlkSize
设备配置空间中 blk_size 字段有效 (VIRTIO_BLK_F_BLK_SIZE)
@ kSecureErase
设备支持 secure erase 命令 (VIRTIO_BLK_F_SECURE_ERASE)
@ kGeometry
设备配置空间中 geometry 字段有效 (VIRTIO_BLK_F_GEOMETRY)
@ kEventIdx
设备支持 avail_event 和 used_event 字段 (VIRTIO_F_EVENT_IDX) [1 << 29]
@ kVersion1
设备符合 virtio 1.0+ 规范 (VIRTIO_F_VERSION_1) [1 << 32]
DMA 可访问内存区域的非拥有描述符
Definition io_buffer.hpp:37
uintptr_t phys
物理(总线/DMA)基地址
Definition io_buffer.hpp:41
错误类型,用于 std::expected
Definition expected.hpp:343
Scatter-Gather IO 物理内存向量
Definition misc.hpp:43
块设备配置空间布局
uint64_t capacity
设备容量(以 512 字节扇区为单位)
uint32_t type
请求类型 (ReqType)
uint16_t desc_head
描述符链头索引(用于在 Used Ring 中匹配)
BlkReqHeader header
请求头(DMA 可访问,设备只读)
UserData token
用户自定义上下文指针
volatile uint8_t status
状态字节(DMA 可访问,设备只写)
VirtIO 设备性能监控统计数据
uint64_t interrupts_handled
已处理的中断次数
uint64_t queue_full_errors
队列满导致入队失败的次数
uint64_t kicks_elided
借助 Event Index 省略的 Kick 次数
uint64_t bytes_transferred
已传输字节数