本文将mark下virtio per-virtqueue reset特性的相关notes,主要内容转载自virtio 1.2 来了!

Motivation

引入这个特性的目的是解决virtio-net不支持队列级别的reset操作的问题。这在很多现代化的网卡中是一个比较常见的功能, 是实现很多功能的基础能力, 为了让 virtio-net 支持更多能力, 这个特性的引入是必须的。但是 Per-virtqueue reset 并不只限于 virtio-net 这一种设备,它是一个 virtio 的基础能力,相信其它的 virtio 设备也会慢慢支持这个 feature。

Implementation Process

Per-virtqueue reset 由 driver 针对某一个队列发起,基于某一种 transport(比如 PCIe) 通知 device。device 停止使用队列,driver 在 reset 之后可以重新 re-enable 队列。virtio spec 定义了这个过程中详细的交互流程和信息。
以下是 virtio spec 中定义的详细流程:

  • driver 基于 transport 通知 device 某个指定的队列要 reset。
  • device 收到请求之后设置 reset 状态为 1,停止此队列的所有操作,包括中断等,并设置队列的所有的状态到初始值。在 device 完成 reset 操作之前,返回给 driver 的 reset 状态都是 1,直到 reset 操作完成。reset 完成之后 reset 及 enable 的值都要设置成 0。
  • driver 在检查到队列的 reset 状态变成 0 之后,就表示device reset 操作已经完成了。这个时候开始,driver 就可以安全地回收队列占用的相关资源了。

到此 driver 对于队列的 reset 操作就已经完成了。

  • 之后 virtio driver 可选地进行 re-enable 操作,在操作的过程中,driver 可以给 device 新的参数来 re-enable 这个队列。比如新的队列大小。

以上是一个完整的 reset & re-enable 的过程,理论上 re-enable 是可选的。

Usage

对于现代的很多硬件设备来讲,对于队列进行 reset 是一个比较常见的功能,所以这个功能的引入让 virtio 设备更加现代化。早期 virtio 的出现是伴随着高性能的需求而来的,我们原来更加关注它在性能上的基本功能,一些高级功能并不重视。per-virtqueue reset 让 virtio 对于队列的使用更加灵活,譬如我们可以基于 per-vertqueue reset 实现下面两个功能:

  1. 调整virtio-net 网卡队列的ring size。在 virtio-net 的场景下,基于 per-virtqueue reset 我们可以实现网卡队列 ring size 的调整。目前一般的网卡都支持使用ethtool -G eth0 rx <new size> tx <new size>来调整队列的大小,但是原来的 virtio-net 一直是不支持这样一个简单的功能的,现在基于 per-virtqueue reset,我们很快就可以在 Linux 下使用这个命令来调整队列的大小。
  2. 支持AF_XDP,扩展云上应用的边界。除了应用于上述简单的场景之外,我们还可以在更高级的场景应用到这个功能。per-virtqueue reset 也可以视作一种资源的快速回收机制。比如在 virtio-net 的情况下,我们必须要等待收到新的数据包或者硬件完成数据包的发送才能完成对于 buffer 资源的回收。而现在基于 per-virtqueue reset,driver 可以不用被动地等待而是可以主动调用 reset 快速地让 device 释放对于某个队列上的 buffer 资源的占用,实现资源的快速回收。这可以让 virtio-net 支持 AF_XDP 这样的高级功能,实现在 linux 内核框架下的高性能收发包。

Spec Details

per-virtqueue reset的细节请参考virtio 1.2 spec

2.6.1 Virtqueue Reset

VIRTIO_F_RING_RESET(40) This feature indicates that the driver can reset a queue individually.

2.6.1.1.2 Driver Requirements: Virtqueue Reset
After the driver tells the device to reset a queue, the driver MUST verify that the queue has actually been reset.
After the queue has been successfully reset, the driver MAY release any resource associated with that virtqueue.

2.6.1.2.1 Device Requirements: Virtqueue Re-enable
The device MUST observe any queue configuration that may have been changed by the driver, like the maximum queue size.

1
2
3
4
5
6
7
8
9
10
11
struct virtio_pci_common_cfg { 
...

/* About a specific virtqueue. */
le16 queue_select; /* read-write */
le16 queue_size; /* read-write */
le16 queue_msix_vector; /* read-write */
le16 queue_enable; /* read-write */
...
le16 queue_reset; /* read-write */
};

queue_size
Queue Size. On reset, specifies the maximum queue size supported by the device. This can be modified by the driver to reduce memory requirements.

queue_reset
The driver uses this to selectively reset the queue.

4.1.4.3.1 Device Requirements: Common configuration structure layout
If VIRTIO_F_RING_RESET has been negotiated, the device MUST present a 0 in queue_reset on reset.
If VIRTIO_F_RING_RESET has been negotiated, the device MUST present a 0 in queue_reset after the virtqueue is enabled with queue_enable.
The device MUST reset the queue when 1 is written to queue_reset. The device MUST continue to present 1 in queue_reset as long as the queue reset is ongoing. The device MUST present 0 in both queue_reset and queue_enable when queue reset has completed.

4.1.4.3.2 Driver Requirements: Common configuration structure layout
If VIRTIO_F_RING_RESET has been negotiated, after the driver writes 1 to queue_reset to reset the queue, the driver MUST NOT consider queue reset to be complete until it reads back 0 in queue_reset. The driver MAY re-enable the queue by writing 1 to queue_enable after ensuring that other virtqueue fields have been set up correctly. The driver MAY set driver-writeable queue configuration values to different values than those that were used before the queue reset.


参考资料:

  1. Virtio 1.2 is Coming!
  2. virtio 1.2 来了!
  3. virtio 1.2 spec
  4. virtio pci support VIRTIO_F_RING_RESET
  5. virtio: pci support virtqueue reset