73 -> std::pair<size_t, size_t> {
76 VirtqueueT::CalcSize(
static_cast<uint16_t
>(queue_size),
true);
77 return {per_queue * queue_count, 4096};
86 [[nodiscard]]
static constexpr auto CalcDmaSize(uint16_t queue_size = 128)
89 return VirtqueueT::CalcSize(queue_size,
true);
98 -> std::pair<size_t, size_t> {
125 uint16_t queue_count = 1, uint32_t queue_size = 128,
127 if (queue_count == 0) {
130 if (queue_count > 1) {
134 TransportT transport(mmio_base);
135 if (!transport.IsValid()) {
142 uint64_t wanted_features =
145 auto negotiated_result = initializer.
Init(wanted_features);
146 if (!negotiated_result) {
147 return std::unexpected(negotiated_result.error());
149 uint64_t negotiated = *negotiated_result;
161 VirtqueueT vq(vq_dma,
static_cast<uint16_t
>(queue_size), event_idx);
167 const uint32_t queue_idx = 0;
169 queue_idx, vq.DescPhys(), vq.AvailPhys(), vq.UsedPhys(), vq.Size());
171 return std::unexpected(setup_result.error());
175 auto activate_result = initializer.
Activate();
176 if (!activate_result) {
177 return std::unexpected(activate_result.error());
180 return VirtioBlk(std::move(transport), std::move(vq), negotiated, slot_dma,
200 [[nodiscard]]
auto EnqueueRead(uint16_t queue_index, uint64_t sector,
201 const IoVec* buffers,
size_t buffer_count,
225 const IoVec* buffers,
size_t buffer_count,
240 auto Kick(uint16_t queue_index) ->
void {
241 if (queue_index != 0) {
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();
278 template <
typename CompletionCallback>
281 uint32_t isr_status =
transport_.GetInterruptStatus();
282 if (isr_status != 0) {
301 uint32_t status =
transport_.GetInterruptStatus();
324 if (data ==
nullptr) {
343 [[nodiscard]]
auto Write(uint64_t sector,
const uint8_t* data)
345 if (data ==
nullptr) {
349 virt_to_phys_(
reinterpret_cast<uintptr_t
>(
const_cast<uint8_t*
>(data))),
377 config.geometry.cylinders =
transport_.ReadConfigU16(
379 config.geometry.heads =
transport_.ReadConfigU8(
381 config.geometry.sectors =
transport_.ReadConfigU8(
393 config.topology.physical_block_exp =
transport_.ReadConfigU8(
395 config.topology.alignment_offset =
transport_.ReadConfigU8(
397 config.topology.min_io_size =
transport_.ReadConfigU16(
399 config.topology.opt_io_size =
transport_.ReadConfigU32(
411 config.max_discard_sectors =
transport_.ReadConfigU32(
413 config.max_discard_seg =
transport_.ReadConfigU32(
415 config.discard_sector_alignment =
transport_.ReadConfigU32(
421 config.max_write_zeroes_sectors =
transport_.ReadConfigU32(
423 config.max_write_zeroes_seg =
transport_.ReadConfigU32(
425 config.write_zeroes_may_unmap =
transport_.ReadConfigU8(
431 config.max_secure_erase_sectors =
transport_.ReadConfigU32(
433 config.max_secure_erase_seg =
transport_.ReadConfigU32(
435 config.secure_erase_sector_alignment =
transport_.ReadConfigU32(
481 vq_(std::move(other.vq_)),
490 other.slots_ =
nullptr;
491 other.slot_bitmap_ = 0;
494 if (
this != &other) {
496 vq_ = std::move(other.vq_);
505 other.slots_ =
nullptr;
506 other.slot_bitmap_ = 0;
539 VirtioBlk(TransportT transport, VirtqueueT vq, uint64_t features,
567 uint64_t sector,
const IoVec* buffers,
568 size_t buffer_count,
UserData token)
570 if (queue_index != 0) {
581 return std::unexpected(slot_result.error());
583 uint16_t slot_idx = *slot_result;
584 auto& slot =
slots_[slot_idx];
586 slot.
header.
type =
static_cast<uint32_t
>(type);
587 slot.header.reserved = 0;
588 slot.header.sector = sector;
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;
597 auto slot_base_phys =
599 readable_iovs[readable_count++] = {
603 for (
size_t i = 0; i < buffer_count; ++i) {
604 writable_iovs[writable_count++] = buffers[i];
607 for (
size_t i = 0; i < buffer_count; ++i) {
608 readable_iovs[readable_count++] = buffers[i];
613 writable_iovs[writable_count++] = {
614 slot_base_phys + offsetof(
RequestSlot, status),
sizeof(uint8_t)};
618 auto chain_result =
vq_.SubmitChain(readable_iovs.data(), readable_count,
619 writable_iovs.data(), writable_count);
623 return std::unexpected(chain_result.error());
626 slot.desc_head = *chain_result;
643 template <
typename CompletionCallback>
647 while (
vq_.HasUsed()) {
648 auto elem_result =
vq_.PopUsed();
653 auto elem = *elem_result;
654 auto head =
static_cast<uint16_t
>(elem.id);
658 auto& slot =
slots_[slot_idx];
663 on_complete(slot.token, ec);
668 (void)
vq_.FreeChain(head);
680 uint64_t free_bits = ~slot_bitmap_;
681 if (free_bits == 0) {
684 auto idx =
static_cast<uint16_t
>(__builtin_ctzll(free_bits));
712 auto i =
static_cast<uint16_t
>(__builtin_ctzll(used));
713 if (
slots_[i].desc_head == desc_head) {
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);
767 if (
vq_.EventIdxEnabled()) {
768 auto* used_event_ptr =
vq_.AvailUsedEvent();
769 if (used_event_ptr !=
nullptr) {
770 *used_event_ptr =
vq_.LastUsedIdx();
789 const IoVec* buffers,
791 auto enq =
DoEnqueue(type, 0, sector, buffers, buffer_count,
nullptr);
793 return std::unexpected(enq.error());
798 uint32_t spin_limit = 100000000U;
800 for (uint32_t i = 0; i < spin_limit; ++i) {
807 if (!
vq_.HasUsed()) {
808 klog::Warn(
"Sync request timeout: sector={}", sector);
824 return std::unexpected(
Error{result});
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 设备初始化序列
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_
传输层实例
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
@ kTransportNotInitialized
传输层未正确初始化
@ kFeatureNegotiationFailed
特性协商失败
@ kNoFreeDescriptors
没有空闲描述符
@ kNotSupported
不支持的操作(通用,非设备特定)
std::expected< T, Error > Expected
std::expected 别名模板
auto IdentityVirtToPhys(uintptr_t virt) -> uintptr_t
恒等映射:物理地址 == 虚拟地址(早期启动 / 无 MMU 时的默认实现)
auto(*)(uintptr_t virt) -> uintptr_t VirtToPhysFunc
虚拟地址到物理地址转换回调类型
auto Warn(etl::format_string< Args... > fmt, Args &&... args) -> void
以 WARN 级别记录日志
@ kOut
写入 (VIRTIO_BLK_T_OUT)
@ kIn
读取 (VIRTIO_BLK_T_IN)
constexpr size_t kSectorSize
标准扇区大小(字节)
@ kSecureEraseSectorAlignment
@ kDiscardSectorAlignment
@ 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]
uintptr_t phys
物理(总线/DMA)基地址
uint64_t capacity
设备容量(以 512 字节扇区为单位)
uint16_t desc_head
描述符链头索引(用于在 Used Ring 中匹配)
BlkReqHeader header
请求头(DMA 可访问,设备只读)
volatile uint8_t status
状态字节(DMA 可访问,设备只写)
uint64_t interrupts_handled
已处理的中断次数
uint64_t queue_full_errors
队列满导致入队失败的次数
uint64_t kicks_elided
借助 Event Index 省略的 Kick 次数
uint64_t bytes_transferred
已传输字节数