/* This marks a buffer as continuing via the next field. * 表示该buffer之后还有buffer,所有buffer可以通过next连成一个Descriptor chain */ #define VRING_DESC_F_NEXT 1 /* This marks a buffer as write-only (otherwise read-only). * 表示该buffer只能写,当buffer用于接收数据时,需要向Host提供buffer,这个时候就标记buffer为写。反之是发送数据,标记为读 */ #define VRING_DESC_F_WRITE 2 /* This means the buffer contains a list of buffer descriptors. * 不做讨论 */ #define VRING_DESC_F_INDIRECT 4
/* __virtio_le32 is used here for ids for padding reasons. */ structvring_used_elem { /* Index of start of used descriptor chain. */ __virtio_le32 id; /* Total length of the descriptor chain which was written to. */ __virtio_le32 len; };
structvring_used { __virtio_le16 flags; __virtio_le16 idx; structvring_used_elemring[]; /* Only if VIRTIO_F_EVENT_IDX: __virtio_le16 avail_event; */ };
flags:用于指示Guest当它添加完buffer,将Descriptor index写入Avail Ring之后,是否发送notification通知Host。**如果flags设置为0,Guest每增加一次buffer就会通知Host,如果flags为1,不通知Host。**Used Ring flags的含义和Avail Ring flags的含义类似,都是指示前后端数据处理完后是否通知对方。同样的,当VIRTIO_F_EVENT_IDX特性开启时,flags必须被设置成0,Guest使用avail_event方式通知Host
/** * Structure to describe the virtqueue. */ structvirtqueue { /* Reference the virtio_dev it belong to */ structvirtio_dev *vdev; /* Virtqueue identifier */ __u16 queue_id; /* Notify to the host */ virtqueue_notify_host_t vq_notify_host; /* Callback from the virtqueue */ virtqueue_callback_t vq_callback; /* Next entry of the queue */ UK_TAILQ_ENTRY(struct virtqueue) next; /* Private data structure used by the driver of the queue */ void *priv; };
avail_idx = vrq->vring.avail->idx & (vrq->vring.num - 1); /* Adding the idx to available ring */ vrq->vring.avail->ring[avail_idx] = idx; /** * Write barrier to make sure we push the descriptor on the available * descriptor and then increment available index. */ wmb(); vrq->vring.avail->idx++; // 回到最开始,这就是available last idx }
/* No new descriptor since last dequeue operation */ if (!virtqueue_hasdata(vq)) return -ENOMSG; used_idx = vrq->last_used_desc_idx++ & (vrq->vring.num - 1); elem = &vrq->vring.used->ring[used_idx]; // 根据used ring提供的下标,取出对应vring /** * We are reading from the used descriptor information updated by the * host. */ rmb(); head_idx = elem->id; if (len) *len = elem->len; *cookie = vrq->vq_info[head_idx].cookie; virtqueue_detach_desc(vrq, head_idx); // vrq->vq_info[head_idx].cookie = NULL; return (vrq->vring.num - vrq->desc_avail); // 返回的是下一个available_idx in vrq }