80 struct [[gnu::packed]]
Desc {
82 static constexpr size_t kAlign = 16;
105 static constexpr size_t kAlign = 2;
111#pragma GCC diagnostic push
112#pragma GCC diagnostic ignored "-Wpedantic"
114#pragma GCC diagnostic pop
124 ->
volatile uint16_t* {
125 return ring + queue_size;
128 [[nodiscard]]
auto used_event(uint16_t queue_size)
const volatile ->
const
130 return ring + queue_size;
160 static constexpr size_t kAlign = 4;
166#pragma GCC diagnostic push
167#pragma GCC diagnostic ignored "-Wpedantic"
169#pragma GCC diagnostic pop
179 ->
volatile uint16_t* {
181 auto* byte_ptr =
reinterpret_cast<volatile uint8_t*
>(ring);
182 return reinterpret_cast<volatile uint16_t*
>(byte_ptr +
sizeof(
UsedElem) *
186 [[nodiscard]]
auto avail_event(uint16_t queue_size)
const volatile ->
const
188 const auto* byte_ptr =
reinterpret_cast<const volatile uint8_t*
>(ring);
189 return reinterpret_cast<const volatile uint16_t*
>(
190 byte_ptr +
sizeof(
UsedElem) * queue_size);
203 [[nodiscard]]
static constexpr auto CalcSize(uint16_t queue_size,
204 bool event_idx =
true,
208 size_t desc_total =
static_cast<size_t>(
sizeof(
Desc)) * queue_size;
211 size_t avail_total =
sizeof(uint16_t) * (2 + queue_size);
213 avail_total +=
sizeof(uint16_t);
218 size_t used_total =
sizeof(uint16_t) * 2 +
sizeof(
UsedElem) * queue_size;
220 used_total +=
sizeof(uint16_t);
225 size_t used_off =
AlignUp(avail_off + avail_total, used_align);
227 return used_off + used_total;
249 size_t desc_total =
static_cast<size_t>(
sizeof(
Desc)) * queue_size;
250 size_t avail_total =
sizeof(uint16_t) * (2 + queue_size);
252 avail_total +=
sizeof(uint16_t);
257 size_t used_total =
sizeof(uint16_t) * 2 +
sizeof(
UsedElem) * queue_size;
259 used_total +=
sizeof(uint16_t);
263 auto* base = dma.
Data();
268 for (uint16_t i = 0; i < queue_size; ++i) {
269 desc_[i].
next =
static_cast<uint16_t
>(i + 1);
355 [[nodiscard]]
auto GetDesc(uint16_t idx)
const
461 const IoVec* writable,
size_t writable_count)
463 size_t total = readable_count + writable_count;
467 if (
num_free_ <
static_cast<uint16_t
>(total)) {
472 uint16_t prev_idx = 0xFFFF;
474 for (
size_t i = 0; i < readable_count; ++i) {
480 desc_[idx].
len =
static_cast<uint32_t
>(readable[i].len);
483 if (prev_idx != 0xFFFF) {
489 for (
size_t i = 0; i < writable_count; ++i) {
495 desc_[idx].
len =
static_cast<uint32_t
>(writable[i].len);
498 if (prev_idx != 0xFFFF) {
658 other.is_valid_ =
false;
659 other.desc_ =
nullptr;
660 other.avail_ =
nullptr;
661 other.used_ =
nullptr;
size_t avail_offset_
Available Ring 在 DMA 内存中的偏移量(字节)
auto EventIdxEnabled() const -> bool
检查是否启用了 VIRTIO_F_EVENT_IDX 特性
SplitVirtqueue(const SplitVirtqueue &)=delete
size_t used_offset_
Used Ring 在 DMA 内存中的偏移量(字节)
auto AvailUsedEvent() -> volatile uint16_t *
获取 Available Ring 的 used_event 字段
volatile Avail * avail_
Available Ring 指针(指向 DMA 内存)
uint16_t queue_size_
队列大小(描述符数量,必须为 2 的幂)
auto SubmitChain(const IoVec *readable, size_t readable_count, const IoVec *writable, size_t writable_count) -> Expected< uint16_t >
提交 Scatter-Gather 描述符链
UsedFlags
Used Ring Flags.
static constexpr auto CalcSize(uint16_t queue_size, bool event_idx=true, size_t used_align=Used::kAlign) -> size_t
计算给定队列大小所需的 DMA 内存字节数
auto FreeDesc(uint16_t idx) -> Expected< void >
归还描述符到空闲链表
auto GetDesc(uint16_t idx) -> Expected< volatile Desc * >
获取描述符的可变引用
auto GetDesc(uint16_t idx) const -> Expected< const volatile Desc * >
获取描述符的只读引用
auto AvailIdx() const -> uint16_t
获取当前 Available Ring 索引
auto LastUsedIdx() const -> uint16_t
获取驱动程序上次处理到的 Used Ring 索引
volatile Used * used_
Used Ring 指针(指向 DMA 内存)
SplitVirtqueue(SplitVirtqueue &&other) noexcept
auto AllocDesc() -> Expected< uint16_t >
从空闲链表分配一个描述符
auto operator=(SplitVirtqueue &&) -> SplitVirtqueue &=delete
uint16_t num_free_
空闲描述符数量
auto IsValid() const -> bool
检查 virtqueue 是否成功初始化
auto DescPhys() const -> uint64_t
获取描述符表的物理地址
~SplitVirtqueue()=default
SplitVirtqueue(const DmaRegion &dma, uint16_t queue_size, bool event_idx, size_t used_align=Used::kAlign)
从预分配的 DMA 缓冲区构造 SplitVirtqueue
size_t desc_offset_
描述符表在 DMA 内存中的偏移量(字节)
bool event_idx_enabled_
是否启用 VIRTIO_F_EVENT_IDX 特性
DescFlags
Descriptor Flags.
@ kDescFNext
标记缓冲区通过 next 字段继续
@ kDescFWrite
标记缓冲区为设备只写(否则为设备只读)
@ kDescFIndirect
标记缓冲区包含描述符列表(间接描述符)
auto Size() const -> uint16_t
获取队列大小
volatile Desc * desc_
描述符表指针(指向 DMA 内存)
auto operator=(const SplitVirtqueue &) -> SplitVirtqueue &=delete
auto HasUsed() const -> bool
检查 Used Ring 中是否有已完成的缓冲区
AvailFlags
Available Ring Flags.
@ kAvailFNoInterrupt
设备应该不发送中断
auto UsedAvailEvent() -> volatile uint16_t *
获取 Used Ring 的 avail_event 字段
uint64_t phys_base_
DMA 内存物理基地址(客户机物理地址)
auto NumFree() const -> uint16_t
获取当前空闲描述符数量
auto UsedPhys() const -> uint64_t
获取 Used Ring 的物理地址
auto UsedAvailEvent() const -> const volatile uint16_t *
uint16_t free_head_
空闲描述符链表头索引
auto Submit(uint16_t head) -> void
将描述符链提交到 Available Ring
auto PopUsed() -> Expected< UsedElem >
从 Used Ring 弹出一个已完成的元素
uint16_t last_used_idx_
上次处理到的 Used Ring 索引(用于 PopUsed)
auto FreeChain(uint16_t head) -> Expected< void >
释放整条描述符链
auto AvailUsedEvent() const -> const volatile uint16_t *
auto AvailPhys() const -> uint64_t
获取 Available Ring 的物理地址
Virtqueue 基类(C++23 Deducing this 编译期多态)
@ kNoFreeDescriptors
没有空闲描述符
@ kInvalidDescriptor
无效的描述符索引
@ kNoUsedBuffers
没有已使用的缓冲区可回收
std::expected< T, Error > Expected
std::expected 别名模板
constexpr auto IsPowerOfTwo(size_t value) -> bool
检查值是否为 2 的幂
constexpr auto AlignUp(size_t value, size_t align) -> size_t
将值向上对齐到指定边界
auto Data() const -> uint8_t *
获取虚拟基地址的类型化指针
auto IsValid() const -> bool
检查区域是否有效(非空指针且大小非零)
Virtqueue Available Ring.
uint16_t ring[]
可用描述符头索引数组 ring[queue_size] (little-endian)
static constexpr size_t kAlign
Available Ring 对齐要求(字节)
auto used_event(uint16_t queue_size) volatile -> volatile uint16_t *
获取 used_event 字段的指针
uint16_t flags
标志位: AvailFlags (little-endian)
uint16_t idx
驱动程序将下一个描述符条目放入环中的位置(模 queue_size) (little-endian)
auto used_event(uint16_t queue_size) const volatile -> const volatile uint16_t *
uint64_t addr
缓冲区的客户机物理地址 (little-endian)
uint16_t flags
标志位: DescFlags (little-endian)
uint16_t next
下一个描述符的索引(当 flags & kDescFNext 时有效) (little-endian)
uint32_t len
缓冲区长度(字节) (little-endian)
uint32_t len
设备写入描述符链的总字节数 (little-endian)
uint32_t id
描述符链头的索引 (little-endian)
static constexpr size_t kAlign
Used Ring 对齐要求(字节)
UsedElem ring[]
已用描述符元素数组 ring[queue_size]
auto avail_event(uint16_t queue_size) const volatile -> const volatile uint16_t *
uint16_t idx
设备将下一个描述符条目放入环中的位置(模 queue_size) (little-endian)
auto avail_event(uint16_t queue_size) volatile -> volatile uint16_t *
获取 avail_event 字段的指针
uint16_t flags
标志位: UsedFlags (little-endian)