SimpleKernel 1.17.0
Loading...
Searching...
No Matches
virtio::SplitVirtqueue Class Referencefinal

Split Virtqueue 管理类 More...

#include <split.hpp>

Inheritance diagram for virtio::SplitVirtqueue:
Inheritance graph
Collaboration diagram for virtio::SplitVirtqueue:
Collaboration graph

Classes

struct  Avail
 Virtqueue Available Ring. More...
 
struct  Desc
 Virtqueue 描述符表条目 More...
 
struct  Used
 Virtqueue Used Ring. More...
 
struct  UsedElem
 Virtqueue Used Ring 元素 More...
 

Public Types

enum class  DescFlags : uint16_t { kDescFNext = 1 , kDescFWrite = 2 , kDescFIndirect = 4 }
 Descriptor Flags. More...
 
enum class  AvailFlags : uint16_t { kAvailFNoInterrupt = 1 }
 Available Ring Flags. More...
 
enum class  UsedFlags : uint16_t { kUsedFNoNotify = 1 }
 Used Ring Flags. More...
 

Public Member Functions

 SplitVirtqueue (const DmaRegion &dma, uint16_t queue_size, bool event_idx, size_t used_align=Used::kAlign)
 从预分配的 DMA 缓冲区构造 SplitVirtqueue
 
auto IsValid () const -> bool
 检查 virtqueue 是否成功初始化
 
auto AllocDesc () -> Expected< uint16_t >
 从空闲链表分配一个描述符
 
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 Submit (uint16_t head) -> void
 将描述符链提交到 Available Ring
 
auto HasUsed () const -> bool
 检查 Used Ring 中是否有已完成的缓冲区
 
auto PopUsed () -> Expected< UsedElem >
 Used Ring 弹出一个已完成的元素
 
auto SubmitChain (const IoVec *readable, size_t readable_count, const IoVec *writable, size_t writable_count) -> Expected< uint16_t >
 提交 Scatter-Gather 描述符链
 
auto FreeChain (uint16_t head) -> Expected< void >
 释放整条描述符链
 
auto DescPhys () const -> uint64_t
 获取描述符表的物理地址
 
auto AvailPhys () const -> uint64_t
 获取 Available Ring 的物理地址
 
auto UsedPhys () const -> uint64_t
 获取 Used Ring 的物理地址
 
auto Size () const -> uint16_t
 获取队列大小
 
auto NumFree () const -> uint16_t
 获取当前空闲描述符数量
 
auto AvailUsedEvent () -> volatile uint16_t *
 获取 Available Ring 的 used_event 字段
 
auto AvailUsedEvent () const -> const volatile uint16_t *
 
auto UsedAvailEvent () -> volatile uint16_t *
 获取 Used Ring 的 avail_event 字段
 
auto UsedAvailEvent () const -> const volatile uint16_t *
 
auto EventIdxEnabled () const -> bool
 检查是否启用了 VIRTIO_F_EVENT_IDX 特性
 
auto AvailIdx () const -> uint16_t
 获取当前 Available Ring 索引
 
auto LastUsedIdx () const -> uint16_t
 获取驱动程序上次处理到的 Used Ring 索引
 
构造/析构函数
 SplitVirtqueue ()=delete
 
 SplitVirtqueue (const SplitVirtqueue &)=delete
 
auto operator= (const SplitVirtqueue &) -> SplitVirtqueue &=delete
 
auto operator= (SplitVirtqueue &&) -> SplitVirtqueue &=delete
 
 SplitVirtqueue (SplitVirtqueue &&other) noexcept
 
 ~SplitVirtqueue ()=default
 
- Public Member Functions inherited from virtio::VirtqueueBase
auto SubmitChainWithBarrier (this auto &&self, const IoVec *readable, size_t readable_count, const IoVec *writable, size_t writable_count) -> Expected< uint16_t >
 提交 Scatter-Gather 链并通知设备
 
template<typename Callback >
auto ProcessUsedWithCallback (this auto &&self, Callback &&callback) -> uint32_t
 处理已完成的缓冲区并释放描述符链
 

Static Public Member Functions

static constexpr auto CalcSize (uint16_t queue_size, bool event_idx=true, size_t used_align=Used::kAlign) -> size_t
 计算给定队列大小所需的 DMA 内存字节数
 

Private Attributes

volatile Descdesc_ {nullptr}
 描述符表指针(指向 DMA 内存)
 
volatile Availavail_ {nullptr}
 Available Ring 指针(指向 DMA 内存)
 
volatile Usedused_ {nullptr}
 Used Ring 指针(指向 DMA 内存)
 
uint16_t queue_size_ {0}
 队列大小(描述符数量,必须为 2 的幂)
 
uint16_t free_head_ {0}
 空闲描述符链表头索引
 
uint16_t num_free_ {0}
 空闲描述符数量
 
uint16_t last_used_idx_ {0}
 上次处理到的 Used Ring 索引(用于 PopUsed)
 
uint64_t phys_base_ {0}
 DMA 内存物理基地址(客户机物理地址)
 
size_t desc_offset_ {0}
 描述符表在 DMA 内存中的偏移量(字节)
 
size_t avail_offset_ {0}
 Available Ring 在 DMA 内存中的偏移量(字节)
 
size_t used_offset_ {0}
 Used Ring 在 DMA 内存中的偏移量(字节)
 
bool event_idx_enabled_ {false}
 是否启用 VIRTIO_F_EVENT_IDX 特性
 
bool is_valid_ {false}
 初始化是否成功
 

Additional Inherited Members

- Protected Member Functions inherited from virtio::VirtqueueBase
 VirtqueueBase ()=default
 
 ~VirtqueueBase ()=default
 
 VirtqueueBase (VirtqueueBase &&) noexcept=default
 
auto operator= (VirtqueueBase &&) noexcept -> VirtqueueBase &=default
 
 VirtqueueBase (const VirtqueueBase &)=delete
 
auto operator= (const VirtqueueBase &) -> VirtqueueBase &=delete
 

Detailed Description

Split Virtqueue 管理类

管理 split virtqueue 的描述符分配/释放、缓冲区提交、已用缓冲区回收。 使用预分配的 DMA 内存,自身不进行任何堆内存分配。

内存布局(在 DMA 缓冲区中连续排列):

[Descriptor Table] aligned to 16
[Available Ring] aligned to 2
[Used Ring] aligned to 4
Virtqueue Used Ring.
Definition split.hpp:158
Warning
非线程安全:此类的所有方法均不是线程安全的。 如果多个线程/核需要访问同一个 virtqueue,调用者必须使用外部同步机制(如自旋锁或互斥锁)。
单生产者-单消费者:描述符分配和提交应由同一线程执行, 已用缓冲区回收应由另一线程执行(通常在中断处理程序中)。
See also
cpu_io::Wmb/Rmb for barrier semantics
virtio-v1.2#2.7

Definition at line 38 of file split.hpp.

Member Enumeration Documentation

◆ AvailFlags

enum class virtio::SplitVirtqueue::AvailFlags : uint16_t
strong

Available Ring Flags.

See also
virtio-v1.2#2.7.6 The Virtqueue Available Ring
Enumerator
kAvailFNoInterrupt 

设备应该不发送中断

Definition at line 57 of file split.hpp.

57 : uint16_t {
60 };
@ kAvailFNoInterrupt
设备应该不发送中断

◆ DescFlags

enum class virtio::SplitVirtqueue::DescFlags : uint16_t
strong

Descriptor Flags.

See also
virtio-v1.2#2.7.5 The Virtqueue Descriptor Table
Enumerator
kDescFNext 

标记缓冲区通过 next 字段继续

kDescFWrite 

标记缓冲区为设备只写(否则为设备只读)

kDescFIndirect 

标记缓冲区包含描述符列表(间接描述符)

Definition at line 44 of file split.hpp.

44 : uint16_t {
46 kDescFNext = 1,
48 kDescFWrite = 2,
51 };
@ kDescFNext
标记缓冲区通过 next 字段继续
@ kDescFWrite
标记缓冲区为设备只写(否则为设备只读)
@ kDescFIndirect
标记缓冲区包含描述符列表(间接描述符)

◆ UsedFlags

enum class virtio::SplitVirtqueue::UsedFlags : uint16_t
strong

Used Ring Flags.

See also
virtio-v1.2#2.7.8 The Virtqueue Used Ring
Enumerator
kUsedFNoNotify 

驱动不需要通知

Definition at line 66 of file split.hpp.

66 : uint16_t {
69 };
@ kUsedFNoNotify
驱动不需要通知

Constructor & Destructor Documentation

◆ SplitVirtqueue() [1/4]

virtio::SplitVirtqueue::SplitVirtqueue ( const DmaRegion dma,
uint16_t  queue_size,
bool  event_idx,
size_t  used_align = Used::kAlign 
)
inline

从预分配的 DMA 缓冲区构造 SplitVirtqueue

Parameters
dmaDMA 区域描述符(包含虚拟地址、物理地址和大小, 必须已清零,大小 >= CalcSize()
queue_size队列大小(必须为 2 的幂)
event_idx是否启用 VIRTIO_F_EVENT_IDX 特性
used_alignUsed Ring 的对齐要求(modern = 4,legacy MMIO = 4096)
See also
virtio-v1.2#2.7

Definition at line 240 of file split.hpp.

242 : queue_size_(queue_size),
243 phys_base_(dma.phys),
244 event_idx_enabled_(event_idx) {
245 if (!dma.IsValid() || !IsPowerOfTwo(queue_size)) {
246 return;
247 }
248
249 size_t desc_total = static_cast<size_t>(sizeof(Desc)) * queue_size;
250 size_t avail_total = sizeof(uint16_t) * (2 + queue_size);
251 if (event_idx) {
252 avail_total += sizeof(uint16_t);
253 }
254
255 desc_offset_ = 0;
256 avail_offset_ = AlignUp(desc_total, Avail::kAlign);
257 size_t used_total = sizeof(uint16_t) * 2 + sizeof(UsedElem) * queue_size;
258 if (event_idx) {
259 used_total += sizeof(uint16_t);
260 }
261 used_offset_ = AlignUp(avail_offset_ + avail_total, used_align);
262
263 auto* base = dma.Data();
264 desc_ = reinterpret_cast<volatile Desc*>(base + desc_offset_);
265 avail_ = reinterpret_cast<volatile Avail*>(base + avail_offset_);
266 used_ = reinterpret_cast<volatile Used*>(base + used_offset_);
267
268 for (uint16_t i = 0; i < queue_size; ++i) {
269 desc_[i].next = static_cast<uint16_t>(i + 1);
270 }
271 // 末尾描述符使用 sentinel 值,避免越界索引
272 desc_[queue_size - 1].next = 0xFFFF;
273 free_head_ = 0;
274 num_free_ = queue_size;
275 last_used_idx_ = 0;
276
277 is_valid_ = true;
278 }
size_t avail_offset_
Available Ring 在 DMA 内存中的偏移量(字节)
Definition split.hpp:688
size_t used_offset_
Used Ring 在 DMA 内存中的偏移量(字节)
Definition split.hpp:690
volatile Avail * avail_
Available Ring 指针(指向 DMA 内存)
Definition split.hpp:670
uint16_t queue_size_
队列大小(描述符数量,必须为 2 的幂)
Definition split.hpp:675
volatile Used * used_
Used Ring 指针(指向 DMA 内存)
Definition split.hpp:672
uint16_t num_free_
空闲描述符数量
Definition split.hpp:679
size_t desc_offset_
描述符表在 DMA 内存中的偏移量(字节)
Definition split.hpp:686
bool event_idx_enabled_
是否启用 VIRTIO_F_EVENT_IDX 特性
Definition split.hpp:692
volatile Desc * desc_
描述符表指针(指向 DMA 内存)
Definition split.hpp:668
bool is_valid_
初始化是否成功
Definition split.hpp:694
uint64_t phys_base_
DMA 内存物理基地址(客户机物理地址)
Definition split.hpp:684
uint16_t free_head_
空闲描述符链表头索引
Definition split.hpp:677
uint16_t last_used_idx_
上次处理到的 Used Ring 索引(用于 PopUsed)
Definition split.hpp:681
constexpr auto IsPowerOfTwo(size_t value) -> bool
检查值是否为 2 的幂
Definition misc.hpp:32
constexpr auto AlignUp(size_t value, size_t align) -> size_t
将值向上对齐到指定边界
Definition misc.hpp:20
auto Data() const -> uint8_t *
获取虚拟基地址的类型化指针
Definition io_buffer.hpp:52
auto IsValid() const -> bool
检查区域是否有效(非空指针且大小非零)
Definition io_buffer.hpp:46
uintptr_t phys
物理(总线/DMA)基地址
Definition io_buffer.hpp:41
static constexpr size_t kAlign
Available Ring 对齐要求(字节)
Definition split.hpp:105
uint16_t next
下一个描述符的索引(当 flags & kDescFNext 时有效) (little-endian)
Definition split.hpp:90
Here is the call graph for this function:

◆ SplitVirtqueue() [2/4]

virtio::SplitVirtqueue::SplitVirtqueue ( )
delete

◆ SplitVirtqueue() [3/4]

virtio::SplitVirtqueue::SplitVirtqueue ( const SplitVirtqueue )
delete

◆ SplitVirtqueue() [4/4]

virtio::SplitVirtqueue::SplitVirtqueue ( SplitVirtqueue &&  other)
inlinenoexcept

Definition at line 643 of file split.hpp.

644 : VirtqueueBase(std::move(other)),
645 desc_(other.desc_),
646 avail_(other.avail_),
647 used_(other.used_),
648 queue_size_(other.queue_size_),
649 free_head_(other.free_head_),
650 num_free_(other.num_free_),
651 last_used_idx_(other.last_used_idx_),
652 phys_base_(other.phys_base_),
653 desc_offset_(other.desc_offset_),
654 avail_offset_(other.avail_offset_),
655 used_offset_(other.used_offset_),
656 event_idx_enabled_(other.event_idx_enabled_),
657 is_valid_(other.is_valid_) {
658 other.is_valid_ = false;
659 other.desc_ = nullptr;
660 other.avail_ = nullptr;
661 other.used_ = nullptr;
662 }

◆ ~SplitVirtqueue()

virtio::SplitVirtqueue::~SplitVirtqueue ( )
default

Member Function Documentation

◆ AllocDesc()

auto virtio::SplitVirtqueue::AllocDesc ( ) -> Expected<uint16_t>
inline

从空闲链表分配一个描述符

从空闲描述符链表中取出一个描述符,供上层使用。 调用者必须填充描述符的 addr、len、flags 和 next 字段。

Returns
成功返回描述符索引(range: 0 ~ queue_size-1); 空闲链表为空时返回 ErrorCode::kNoFreeDescriptors
Warning
非线程安全:多个线程同时调用可能导致竞态条件
See also
virtio-v1.2#2.7.5 The Virtqueue Descriptor Table
virtio-v1.2#2.7.13 Supplying Buffers to The Device

Definition at line 298 of file split.hpp.

298 {
299 if (num_free_ == 0) {
300 return std::unexpected(Error{ErrorCode::kNoFreeDescriptors});
301 }
302
303 uint16_t idx = free_head_;
305 --num_free_;
306
307 return idx;
308 }
@ kNoFreeDescriptors
没有空闲描述符
错误类型,用于 std::expected
Definition expected.hpp:343

◆ AvailIdx()

auto virtio::SplitVirtqueue::AvailIdx ( ) const -> uint16_t
inline

获取当前 Available Ring 索引

Definition at line 630 of file split.hpp.

630{ return avail_->idx; }
uint16_t idx
驱动程序将下一个描述符条目放入环中的位置(模 queue_size) (little-endian)
Definition split.hpp:109

◆ AvailPhys()

auto virtio::SplitVirtqueue::AvailPhys ( ) const -> uint64_t
inline

获取 Available Ring 的物理地址

See also
virtio-v1.2#2.7.6

Definition at line 570 of file split.hpp.

570 {
571 return phys_base_ + avail_offset_;
572 }

◆ AvailUsedEvent() [1/2]

auto virtio::SplitVirtqueue::AvailUsedEvent ( ) -> volatile uint16_t*
inline

获取 Available Ring 的 used_event 字段

Returns
used_event 字段指针,未启用 EVENT_IDX 则返回 nullptr
See also
virtio-v1.2#2.7.10

Definition at line 598 of file split.hpp.

598 {
599 return event_idx_enabled_ ? avail_->used_event(queue_size_) : nullptr;
600 }
auto used_event(uint16_t queue_size) volatile -> volatile uint16_t *
获取 used_event 字段的指针
Definition split.hpp:123
Here is the call graph for this function:

◆ AvailUsedEvent() [2/2]

auto virtio::SplitVirtqueue::AvailUsedEvent ( ) const -> const volatile uint16_t*
inline

Definition at line 602 of file split.hpp.

602 {
603 return event_idx_enabled_ ? avail_->used_event(queue_size_) : nullptr;
604 }
Here is the call graph for this function:

◆ CalcSize()

static constexpr auto virtio::SplitVirtqueue::CalcSize ( uint16_t  queue_size,
bool  event_idx = true,
size_t  used_align = Used::kAlign 
) -> size_t
inlinestaticconstexpr

计算给定队列大小所需的 DMA 内存字节数

Parameters
queue_size队列大小(必须为 2 的幂)
event_idx是否启用 VIRTIO_F_EVENT_IDX 特性
used_alignUsed Ring 的对齐要求
Returns
所需的 DMA 内存字节数
See also
virtio-v1.2#2.6 Split Virtqueues

Definition at line 203 of file split.hpp.

206 {
207 // Descriptor Table: sizeof(Desc) * queue_size
208 size_t desc_total = static_cast<size_t>(sizeof(Desc)) * queue_size;
209
210 // Available Ring: flags(2) + idx(2) + ring[N](2*N) + used_event(2, 可选)
211 size_t avail_total = sizeof(uint16_t) * (2 + queue_size);
212 if (event_idx) {
213 avail_total += sizeof(uint16_t);
214 }
215
216 // Used Ring: flags(2) + idx(2) + ring[N](sizeof(UsedElem)*N) +
217 // avail_event(2, 可选)
218 size_t used_total = sizeof(uint16_t) * 2 + sizeof(UsedElem) * queue_size;
219 if (event_idx) {
220 used_total += sizeof(uint16_t);
221 }
222
223 // 按对齐要求排列
224 size_t avail_off = AlignUp(desc_total, Avail::kAlign);
225 size_t used_off = AlignUp(avail_off + avail_total, used_align);
226
227 return used_off + used_total;
228 }
Here is the call graph for this function:

◆ DescPhys()

auto virtio::SplitVirtqueue::DescPhys ( ) const -> uint64_t
inline

获取描述符表的物理地址

See also
virtio-v1.2#2.7.5

Definition at line 562 of file split.hpp.

562 {
563 return phys_base_ + desc_offset_;
564 }

◆ EventIdxEnabled()

auto virtio::SplitVirtqueue::EventIdxEnabled ( ) const -> bool
inline

检查是否启用了 VIRTIO_F_EVENT_IDX 特性

Definition at line 623 of file split.hpp.

623 {
624 return event_idx_enabled_;
625 }

◆ FreeChain()

auto virtio::SplitVirtqueue::FreeChain ( uint16_t  head) -> Expected<void>
inline

释放整条描述符链

从链头开始,沿 next 指针遍历并释放所有描述符,直到遇到 不含 kDescFNext 标志的描述符为止。

典型用法:在 PopUsed() 获取已完成请求的 head 后, 用此方法一次性归还整条链的描述符。

Parameters
head描述符链头索引
Returns
成功或失败(如 head 索引无效)
Warning
非线程安全
See also
virtio-v1.2#2.7.14 Receiving Used Buffers From The Device

Definition at line 530 of file split.hpp.

530 {
531 if (head >= queue_size_) {
532 return std::unexpected(Error{ErrorCode::kInvalidDescriptor});
533 }
534
535 uint16_t idx = head;
536 while (true) {
537 if (idx >= queue_size_) {
538 return std::unexpected(Error{ErrorCode::kInvalidDescriptor});
539 }
540
541 uint16_t next = desc_[idx].next;
542 bool has_next =
543 (desc_[idx].flags & std::to_underlying(DescFlags::kDescFNext)) != 0;
544
545 desc_[idx].next = free_head_;
546 free_head_ = idx;
547 ++num_free_;
548
549 if (!has_next) {
550 break;
551 }
552 idx = next;
553 }
554
555 return {};
556 }
@ kInvalidDescriptor
无效的描述符索引
uint16_t flags
标志位: DescFlags (little-endian)
Definition split.hpp:88

◆ FreeDesc()

auto virtio::SplitVirtqueue::FreeDesc ( uint16_t  idx) -> Expected<void>
inline

归还描述符到空闲链表

将不再使用的描述符放回空闲链表,供后续分配使用。 对于描述符链,调用者必须按正确的顺序释放链中的每个描述符。

Parameters
idx要释放的描述符索引(必须为之前分配的有效索引)
Warning
非线程安全:多个线程同时调用可能导致竞态条件
释放已释放的描述符或无效索引会导致空闲链表损坏
See also
virtio-v1.2#2.7.14 Receiving Used Buffers From The Device

Definition at line 322 of file split.hpp.

322 {
323 if (idx >= queue_size_) {
324 return std::unexpected(Error{ErrorCode::kInvalidDescriptor});
325 }
326 desc_[idx].next = free_head_;
327 free_head_ = idx;
328 ++num_free_;
329 return {};
330 }

◆ GetDesc() [1/2]

auto virtio::SplitVirtqueue::GetDesc ( uint16_t  idx) -> Expected<volatile Desc*>
inline

获取描述符的可变引用

用于设置描述符的 addr、len、flags 和 next 字段。 调用者必须确保索引有效(通过 AllocDesc() 分配)。

Parameters
idx描述符索引(必须 < queue_size)
Returns
描述符的 volatile 引用(用于与设备共享内存)
See also
virtio-v1.2#2.7.5 The Virtqueue Descriptor Table

Definition at line 342 of file split.hpp.

342 {
343 if (idx >= queue_size_) {
344 return std::unexpected(Error{ErrorCode::kInvalidDescriptor});
345 }
346 return &desc_[idx];
347 }

◆ GetDesc() [2/2]

auto virtio::SplitVirtqueue::GetDesc ( uint16_t  idx) const -> Expected<const volatile Desc*>
inline

获取描述符的只读引用

Parameters
idx描述符索引(必须 < queue_size)
Returns
描述符的 const volatile 指针,失败返回错误

Definition at line 355 of file split.hpp.

356 {
357 if (idx >= queue_size_) {
358 return std::unexpected(Error{ErrorCode::kInvalidDescriptor});
359 }
360 return &desc_[idx];
361 }

◆ HasUsed()

auto virtio::SplitVirtqueue::HasUsed ( ) const -> bool
inline

检查 Used Ring 中是否有已完成的缓冲区

通过比较驱动程序上次处理的 idx 与设备当前的 idx, 判断是否有新的已处理缓冲区可供回收。

Returns
true 表示有已完成的缓冲区可用,false 表示没有
See also
virtio-v1.2#2.7.14 Receiving Used Buffers From The Device

Definition at line 399 of file split.hpp.

399 {
400 return last_used_idx_ != used_->idx;
401 }
uint16_t idx
设备将下一个描述符条目放入环中的位置(模 queue_size) (little-endian)
Definition split.hpp:164
Here is the caller graph for this function:

◆ IsValid()

auto virtio::SplitVirtqueue::IsValid ( ) const -> bool
inline

检查 virtqueue 是否成功初始化

Definition at line 283 of file split.hpp.

283{ return is_valid_; }

◆ LastUsedIdx()

auto virtio::SplitVirtqueue::LastUsedIdx ( ) const -> uint16_t
inline

获取驱动程序上次处理到的 Used Ring 索引

Definition at line 635 of file split.hpp.

635{ return last_used_idx_; }

◆ NumFree()

auto virtio::SplitVirtqueue::NumFree ( ) const -> uint16_t
inline

获取当前空闲描述符数量

Definition at line 590 of file split.hpp.

590{ return num_free_; }

◆ operator=() [1/2]

auto virtio::SplitVirtqueue::operator= ( const SplitVirtqueue ) -> SplitVirtqueue &=delete
delete

◆ operator=() [2/2]

auto virtio::SplitVirtqueue::operator= ( SplitVirtqueue &&  ) -> SplitVirtqueue &=delete
delete

◆ PopUsed()

auto virtio::SplitVirtqueue::PopUsed ( ) -> Expected<UsedElem>
inline

Used Ring 弹出一个已完成的元素

Used Ring 中取出下一个设备已处理完成的缓冲区。 返回的 UsedElem 包含:

  • id: 描述符链头索引(对应之前提交的 head)
  • len: 设备写入的字节数(仅对 Device-writable 缓冲区有意义)
Returns
成功返回 UsedElem{id, len}; 无可用元素时返回 ErrorCode::kNoUsedBuffers
Warning
非线程安全:多个线程同时调用可能导致竞态条件
See also
virtio-v1.2#2.7.14 Receiving Used Buffers From The Device

Definition at line 417 of file split.hpp.

417 {
418 if (!HasUsed()) {
419 return std::unexpected(Error{ErrorCode::kNoUsedBuffers});
420 }
421
422 uint16_t idx = last_used_idx_ % queue_size_;
423 UsedElem elem;
424 elem.id = used_->ring[idx].id;
425 elem.len = used_->ring[idx].len;
426
428
429 return elem;
430 }
auto HasUsed() const -> bool
检查 Used Ring 中是否有已完成的缓冲区
Definition split.hpp:399
@ kNoUsedBuffers
没有已使用的缓冲区可回收
uint32_t len
设备写入描述符链的总字节数 (little-endian)
Definition split.hpp:145
uint32_t id
描述符链头的索引 (little-endian)
Definition split.hpp:143
UsedElem ring[]
已用描述符元素数组 ring[queue_size]
Definition split.hpp:168
Here is the call graph for this function:

◆ Size()

auto virtio::SplitVirtqueue::Size ( ) const -> uint16_t
inline

获取队列大小

Definition at line 585 of file split.hpp.

585{ return queue_size_; }

◆ Submit()

auto virtio::SplitVirtqueue::Submit ( uint16_t  head) -> void
inline

将描述符链提交到 Available Ring

将描述符链的头部索引放入 Available Ring,使其对设备可见。 调用此方法后,调用者应使用内存屏障确保 idx 更新对设备可见, 然后通过 Transport::NotifyQueue() 通知设备。

Parameters
head描述符链头部索引(必须为有效的已分配描述符)
Note
调用者必须在调用此方法前确保描述符写入已完成
调用者必须在调用此方法后通知设备(如 Transport::NotifyQueue())
See also
cpu_io::Wmb() 用于确保 ring 写入在 idx 更新之前对设备可见
Warning
非线程安全:多个线程同时调用可能导致竞态条件
See also
virtio-v1.2#2.7.13 Supplying Buffers to The Device

Definition at line 380 of file split.hpp.

380 {
381 uint16_t idx = avail_->idx;
382 avail_->ring[idx % queue_size_] = head;
383
384 // 写屏障:确保 ring 写入在 idx 更新之前对设备可见
385 cpu_io::Wmb();
386
387 avail_->idx = idx + 1;
388 }
void Wmb()
Definition cpu_io.h:58
uint16_t ring[]
可用描述符头索引数组 ring[queue_size] (little-endian)
Definition split.hpp:113
Here is the call graph for this function:
Here is the caller graph for this function:

◆ SubmitChain()

auto virtio::SplitVirtqueue::SubmitChain ( const IoVec readable,
size_t  readable_count,
const IoVec writable,
size_t  writable_count 
) -> Expected<uint16_t>
inline

提交 Scatter-Gather 描述符链

从空闲链表分配描述符,按顺序组装 readable(设备只读)和 writable(设备可写) 缓冲区为描述符链,自动设置 NEXT 标志串联,并提交到 Available Ring。

描述符链顺序:[readable_0, ..., readable_N, writable_0, ..., writable_M]

  • readable 部分:flags = kDescFNext(无 kDescFWrite)
  • writable 部分:flags = kDescFNext | kDescFWrite
  • 最后一个描述符清除 kDescFNext

调用者在调用此方法后仍需调用内存屏障 + Transport::NotifyQueue() 通知设备。

Parameters
readable设备只读缓冲区数组(如请求头、写入数据)
readable_countreadable 数组中的元素数量
writable设备可写缓冲区数组(如读取数据、状态字节)
writable_countwritable 数组中的元素数量
Returns
成功返回描述符链头索引(可用作 token);失败返回错误
Precondition
readable_count + writable_count > 0
readable_count + writable_count <= NumFree()
Postcondition
描述符链已提交到 Available Ring
Warning
非线程安全
See also
virtio-v1.2#2.7.13 Supplying Buffers to The Device
架构文档 §3 Scatter-Gather

Definition at line 460 of file split.hpp.

462 {
463 size_t total = readable_count + writable_count;
464 if (total == 0) {
465 return std::unexpected(Error{ErrorCode::kInvalidArgument});
466 }
467 if (num_free_ < static_cast<uint16_t>(total)) {
468 return std::unexpected(Error{ErrorCode::kNoFreeDescriptors});
469 }
470
471 uint16_t head = free_head_;
472 uint16_t prev_idx = 0xFFFF;
473
474 for (size_t i = 0; i < readable_count; ++i) {
475 uint16_t idx = free_head_;
477 --num_free_;
478
479 desc_[idx].addr = readable[i].phys_addr;
480 desc_[idx].len = static_cast<uint32_t>(readable[i].len);
481 desc_[idx].flags = std::to_underlying(DescFlags::kDescFNext);
482
483 if (prev_idx != 0xFFFF) {
484 desc_[prev_idx].next = idx;
485 }
486 prev_idx = idx;
487 }
488
489 for (size_t i = 0; i < writable_count; ++i) {
490 uint16_t idx = free_head_;
492 --num_free_;
493
494 desc_[idx].addr = writable[i].phys_addr;
495 desc_[idx].len = static_cast<uint32_t>(writable[i].len);
496 desc_[idx].flags = std::to_underlying(DescFlags::kDescFNext) |
497 std::to_underlying(DescFlags::kDescFWrite);
498 if (prev_idx != 0xFFFF) {
499 desc_[prev_idx].next = idx;
500 }
501 prev_idx = idx;
502 }
503
504 desc_[prev_idx].flags =
505 desc_[prev_idx].flags & ~std::to_underlying(DescFlags::kDescFNext);
506
507 // 写屏障:确保描述符写入在 Available Ring 更新之前对设备可见
508 cpu_io::Wmb();
509
510 Submit(head);
511
512 return head;
513 }
auto Submit(uint16_t head) -> void
将描述符链提交到 Available Ring
Definition split.hpp:380
@ kInvalidArgument
uint64_t addr
缓冲区的客户机物理地址 (little-endian)
Definition split.hpp:84
uint32_t len
缓冲区长度(字节) (little-endian)
Definition split.hpp:86
Here is the call graph for this function:

◆ UsedAvailEvent() [1/2]

auto virtio::SplitVirtqueue::UsedAvailEvent ( ) -> volatile uint16_t*
inline

获取 Used Ring 的 avail_event 字段

Returns
avail_event 字段指针,未启用 EVENT_IDX 则返回 nullptr
See also
virtio-v1.2#2.7.10

Definition at line 612 of file split.hpp.

612 {
613 return event_idx_enabled_ ? used_->avail_event(queue_size_) : nullptr;
614 }
auto avail_event(uint16_t queue_size) volatile -> volatile uint16_t *
获取 avail_event 字段的指针
Definition split.hpp:178
Here is the call graph for this function:

◆ UsedAvailEvent() [2/2]

auto virtio::SplitVirtqueue::UsedAvailEvent ( ) const -> const volatile uint16_t*
inline

Definition at line 616 of file split.hpp.

616 {
617 return event_idx_enabled_ ? used_->avail_event(queue_size_) : nullptr;
618 }
Here is the call graph for this function:

◆ UsedPhys()

auto virtio::SplitVirtqueue::UsedPhys ( ) const -> uint64_t
inline

获取 Used Ring 的物理地址

See also
virtio-v1.2#2.7.8

Definition at line 578 of file split.hpp.

578 {
579 return phys_base_ + used_offset_;
580 }

Member Data Documentation

◆ avail_

volatile Avail* virtio::SplitVirtqueue::avail_ {nullptr}
private

Available Ring 指针(指向 DMA 内存)

Definition at line 670 of file split.hpp.

670{nullptr};

◆ avail_offset_

size_t virtio::SplitVirtqueue::avail_offset_ {0}
private

Available Ring 在 DMA 内存中的偏移量(字节)

Definition at line 688 of file split.hpp.

688{0};

◆ desc_

volatile Desc* virtio::SplitVirtqueue::desc_ {nullptr}
private

描述符表指针(指向 DMA 内存)

Definition at line 668 of file split.hpp.

668{nullptr};

◆ desc_offset_

size_t virtio::SplitVirtqueue::desc_offset_ {0}
private

描述符表在 DMA 内存中的偏移量(字节)

Definition at line 686 of file split.hpp.

686{0};

◆ event_idx_enabled_

bool virtio::SplitVirtqueue::event_idx_enabled_ {false}
private

是否启用 VIRTIO_F_EVENT_IDX 特性

Definition at line 692 of file split.hpp.

692{false};

◆ free_head_

uint16_t virtio::SplitVirtqueue::free_head_ {0}
private

空闲描述符链表头索引

Definition at line 677 of file split.hpp.

677{0};

◆ is_valid_

bool virtio::SplitVirtqueue::is_valid_ {false}
private

初始化是否成功

Definition at line 694 of file split.hpp.

694{false};

◆ last_used_idx_

uint16_t virtio::SplitVirtqueue::last_used_idx_ {0}
private

上次处理到的 Used Ring 索引(用于 PopUsed)

Definition at line 681 of file split.hpp.

681{0};

◆ num_free_

uint16_t virtio::SplitVirtqueue::num_free_ {0}
private

空闲描述符数量

Definition at line 679 of file split.hpp.

679{0};

◆ phys_base_

uint64_t virtio::SplitVirtqueue::phys_base_ {0}
private

DMA 内存物理基地址(客户机物理地址)

Definition at line 684 of file split.hpp.

684{0};

◆ queue_size_

uint16_t virtio::SplitVirtqueue::queue_size_ {0}
private

队列大小(描述符数量,必须为 2 的幂)

Definition at line 675 of file split.hpp.

675{0};

◆ used_

volatile Used* virtio::SplitVirtqueue::used_ {nullptr}
private

Used Ring 指针(指向 DMA 内存)

Definition at line 672 of file split.hpp.

672{nullptr};

◆ used_offset_

size_t virtio::SplitVirtqueue::used_offset_ {0}
private

Used Ring 在 DMA 内存中的偏移量(字节)

Definition at line 690 of file split.hpp.

690{0};

The documentation for this class was generated from the following file: