在虛擬化場景,中斷的開銷更大,所以要儘可能的少用中斷,我們從中斷處理函數開始看。
skb_recv_done函數
static void skb_recv_done(struct virtqueue *rvq)
{
struct virtnet_info *vi = rvq->vdev->priv;
struct receive_queue *rq = &vi->rq[vq2rxq(rvq)];
/* Schedule NAPI, Suppress further interrupts if successful. */
if (napi_schedule_prep(&rq->napi)) {
virtqueue_disable_cb(rvq); //關閉中斷,vhost不會通知前端
__napi_schedule(&rq->napi); //註冊napi,觸發軟中斷
}
}
觸發軟中斷前,關閉vhost中斷通知,此時通過poll機制收包。 收包軟中斷最終會調用napi定義的poll函數,virtio_net定義的是virtnet_poll函數。
virtnet_poll函數
static int virtnet_poll(struct napi_struct *napi, int budget)
{
struct receive_queue *rq =
container_of(napi, struct receive_queue, napi);
unsigned int r, received;
received = virtnet_receive(rq, budget); //接收報文,並上送協議棧
/* Out of packets? */
if (received < budget) { //接收報文小於budget,說明報文已經全部接收完成
r = virtqueue_enable_cb_prepare(rq->vq); //啓動vhost觸發中斷,後端把報文發送到共享環後觸發中斷
napi_complete(napi); //napi處理完成
if (unlikely(virtqueue_poll(rq->vq, r)) && //判斷是否有報文,如果此時有報文則要準備註冊napi
napi_schedule_prep(napi)) {
virtqueue_disable_cb(rq->vq); //關閉vhost觸發中斷
__napi_schedule(napi); //註冊napi,並觸發軟中斷,後續可以繼續收包
}
}
return received; //如果接收報文數超過budget,則主動退出,等到下一次軟中處理時再收包
}
如果報文接收完成,會重新開啓中斷;否則處理完budget數量的報文後,讓出CPU,等待下次處理軟中斷時再次接收報文。
關閉和開啓中斷的函數是通過設置共享環中的flag實現的:
virtqueue_disable_cb函數
void virtqueue_disable_cb(struct virtqueue *_vq)
{
struct vring_virtqueue *vq = to_vvq(_vq);
vq->vring.avail->flags |= cpu_to_virtio16(_vq->vdev, VRING_AVAIL_F_NO_INTERRUPT); //關閉中斷
}
virtqueue_enable_cb_prepare函數
unsigned virtqueue_enable_cb_prepare(struct virtqueue *_vq)
{
struct vring_virtqueue *vq = to_vvq(_vq);
u16 last_used_idx;
START_USE(vq);
/* We optimistically turn back on interrupts, then check if there was
* more to do. */
/* Depending on the VIRTIO_RING_F_EVENT_IDX feature, we need to
* either clear the flags bit or point the event index at the next
* entry. Always do both to keep code simple. */
vq->vring.avail->flags &= cpu_to_virtio16(_vq->vdev, ~VRING_AVAIL_F_NO_INTERRUPT); //該標記位置0,開啓中斷
vring_used_event(&vq->vring) = cpu_to_virtio16(_vq->vdev, last_used_idx = vq->last_used_idx);
END_USE(vq);
return last_used_idx;
}
原文鏈接:https://blog.csdn.net/one_clouder/article/details/53327362