From: wafer Xie <[email protected]>

For the used ring, based on the state of the SVQ descriptor,
if it is indirect, retrieve the corresponding indirect buffer by index
and then increment the freeing counter.
Once all descriptors in this buffer have been released,
update the current buffer state to SVQ_INDIRECT_BUF_FREED.

If the current indirect buffer borrows descriptors from the next indirect 
buffer,
the borrowed descriptors must be returned and the corresponding borrow counter 
updated.

Suggested-by: Eugenio PĂ©rez <[email protected]>
Signed-off-by: wafer Xie <[email protected]>
---
 hw/virtio/vhost-shadow-virtqueue.c | 64 ++++++++++++++++++++++++++++--
 1 file changed, 61 insertions(+), 3 deletions(-)

diff --git a/hw/virtio/vhost-shadow-virtqueue.c 
b/hw/virtio/vhost-shadow-virtqueue.c
index f80266fc03..4f564f514c 100644
--- a/hw/virtio/vhost-shadow-virtqueue.c
+++ b/hw/virtio/vhost-shadow-virtqueue.c
@@ -443,7 +443,7 @@ static VirtQueueElement 
*vhost_svq_get_buf(VhostShadowVirtqueue *svq,
 {
     const vring_used_t *used = svq->vring.used;
     vring_used_elem_t used_elem;
-    uint16_t last_used, last_used_chain, num;
+    uint16_t last_used, last_used_chain, num, act_num;
 
     if (!vhost_svq_more_used(svq)) {
         return NULL;
@@ -469,12 +469,70 @@ static VirtQueueElement 
*vhost_svq_get_buf(VhostShadowVirtqueue *svq,
         return NULL;
     }
 
+    /* Check if indirect descriptors were used */
+    int indirect_buf_idx = svq->desc_state[used_elem.id].indirect_buf_idx;
+    bool used_indirect = (indirect_buf_idx >= 0);
+
     num = svq->desc_state[used_elem.id].ndescs;
     svq->desc_state[used_elem.id].ndescs = 0;
-    last_used_chain = vhost_svq_last_desc_of_chain(svq, num, used_elem.id);
+
+    /* Update indirect buffer state if it was used */
+    if (used_indirect) {
+        SVQIndirectDescBuf *buf = &svq->indirect.bufs[indirect_buf_idx];
+
+        /* Increment freeing_descs for descriptors returned from used ring */
+        buf->freeing_descs += num;
+
+        /* Check if all descs are accounted for (freed + freeing == num) */
+        if (buf->freed_descs + buf->freeing_descs >= buf->num_descs) {
+            /*
+             * Return borrowed descriptors to next buffer first, if any.
+             */
+            if (buf->borrowed_descs > 0) {
+                int next_idx = indirect_buf_idx + 1;
+                assert(next_idx < SVQ_NUM_INDIRECT_BUFS);
+                SVQIndirectDescBuf *next_buf = &svq->indirect.bufs[next_idx];
+                uint16_t borrowed = buf->borrowed_descs;
+
+                /* Return borrowed descriptors to next buffer */
+                next_buf->start_idx -= borrowed;
+                next_buf->num_descs += borrowed;
+                next_buf->freeing_descs += borrowed;
+
+                /*
+                 * Adjust freed_head only if next_buf has already started using
+                 * descriptors. If freed_head == 0, the returned descriptors
+                 * are at the beginning and freed_head stays at 0.
+                 */
+                if (next_buf->freed_head > 0) {
+                    next_buf->freed_head += borrowed;
+                }
+
+                /* increase the borrowed num */
+                buf->num_descs -= borrowed;
+                buf->freeing_descs -= borrowed;
+                buf->borrowed_descs = 0;
+            }
+
+            /* Reset buffer to freed state */
+            buf->state = SVQ_INDIRECT_BUF_FREED;
+            buf->freed_descs = buf->num_descs;
+            buf->freeing_descs = 0;
+            buf->freed_head = 0;
+        }
+
+        svq->desc_state[used_elem.id].indirect_buf_idx = -1;
+    }
+
+    /*
+     * If using indirect descriptors, only 1 main descriptor is used.
+     * Otherwise, we used 'num' descriptors in a chain.
+     */
+    act_num = used_indirect ? 1 : num;
+    last_used_chain = vhost_svq_last_desc_of_chain(svq, act_num, used_elem.id);
     svq->desc_next[last_used_chain] = svq->free_head;
     svq->free_head = used_elem.id;
-    svq->num_free += num;
+    svq->num_free += act_num;
 
     *len = used_elem.len;
     return g_steal_pointer(&svq->desc_state[used_elem.id].elem);
-- 
2.34.1


Reply via email to