+static int
+eth_vhost_update_intr(struct rte_eth_dev *eth_dev, uint16_t rxq_idx)
+{
+ struct rte_intr_handle *handle = eth_dev->intr_handle;
+ struct rte_epoll_event rev, *elist;
+ int epfd, ret;
+
+ if (handle == NULL)
+ return 0;
+
+ elist = rte_intr_elist_index_get(handle, rxq_idx);
+ if (rte_intr_efds_index_get(handle, rxq_idx) == elist->fd)
+ return 0;
+
+ VHOST_LOG(INFO, "kickfd for rxq-%d was changed, updating handler.\n",
+ rxq_idx);
+
+ if (elist->fd != -1)
+ VHOST_LOG(ERR, "Unexpected previous kickfd value (Got %d, expected -1).\n",
+ elist->fd);
+
+ /*
+ * First remove invalid epoll event, and then install
+ * the new one. May be solved with a proper API in the
+ * future.
+ */
+ epfd = elist->epfd;
+ rev = *elist;
+ ret = rte_epoll_ctl(epfd, EPOLL_CTL_DEL, rev.fd,
+ elist);
+ if (ret) {
+ VHOST_LOG(ERR, "Delete epoll event failed.\n");
+ return ret;
+ }
+
+ rev.fd = rte_intr_efds_index_get(handle, rxq_idx);
+ if (rte_intr_elist_index_set(handle, rxq_idx, rev))
+ return -rte_errno;
+
+ elist = rte_intr_elist_index_get(handle, rxq_idx);
+ ret = rte_epoll_ctl(epfd, EPOLL_CTL_ADD, rev.fd, elist);
+ if (ret) {
+ VHOST_LOG(ERR, "Add epoll event failed.\n");
+ return ret;
+ }
+
+ return 0;
+}
+
+static int
+eth_rxq_intr_enable(struct rte_eth_dev *dev, uint16_t qid)
+{
+ struct rte_vhost_vring vring;
+ struct vhost_queue *vq;
+ int old_intr_enable, ret = 0;
+
+ vq = dev->data->rx_queues[qid];
+ if (!vq) {
+ VHOST_LOG(ERR, "rxq%d is not setup yet\n", qid);
+ return -1;
+ }
+
+ rte_spinlock_lock(&vq->intr_lock);
+ old_intr_enable = vq->intr_enable;
+ vq->intr_enable = 1;
+ ret = eth_vhost_update_intr(dev, qid);
+ rte_spinlock_unlock(&vq->intr_lock);
+
+ if (ret < 0) {
+ VHOST_LOG(ERR, "Failed to update rxq%d's intr\n", qid);
+ vq->intr_enable = old_intr_enable;
+ return ret;
+ }
+
+ ret = rte_vhost_get_vhost_vring(vq->vid, (qid << 1) + 1, &vring);
+ if (ret < 0) {
+ VHOST_LOG(ERR, "Failed to get rxq%d's vring\n", qid);
+ return ret;
+ }
+ VHOST_LOG(INFO, "Enable interrupt for rxq%d\n", qid);
+ rte_vhost_enable_guest_notification(vq->vid, (qid << 1) + 1, 1);
+ rte_wmb();
+
+ return ret;
+}
+
+static int
+eth_rxq_intr_disable(struct rte_eth_dev *dev, uint16_t qid)
+{
+ struct rte_vhost_vring vring;
+ struct vhost_queue *vq;
+ int ret = 0;
+
+ vq = dev->data->rx_queues[qid];
+ if (!vq) {
+ VHOST_LOG(ERR, "rxq%d is not setup yet\n", qid);
+ return -1;
+ }
+
+ ret = rte_vhost_get_vhost_vring(vq->vid, (qid << 1) + 1, &vring);
+ if (ret < 0) {
+ VHOST_LOG(ERR, "Failed to get rxq%d's vring", qid);
+ return ret;
+ }
+ VHOST_LOG(INFO, "Disable interrupt for rxq%d\n", qid);
+ rte_vhost_enable_guest_notification(vq->vid, (qid << 1) + 1, 0);
+ rte_wmb();
+
+ vq->intr_enable = 0;
+
+ return 0;
+}
+
+static void
+eth_vhost_uninstall_intr(struct rte_eth_dev *dev)
+{
+ struct rte_intr_handle *intr_handle = dev->intr_handle;
+
+ if (intr_handle != NULL) {
+ rte_intr_vec_list_free(intr_handle);
+ rte_intr_instance_free(intr_handle);
+ }
+ dev->intr_handle = NULL;
+}
+
+static int
+eth_vhost_install_intr(struct rte_eth_dev *dev)
+{
+ struct rte_vhost_vring vring;
+ struct vhost_queue *vq;
+ int nb_rxq = dev->data->nb_rx_queues;
+ int i;
+ int ret;
+
+ /* uninstall firstly if we are reconnecting */
+ if (dev->intr_handle != NULL)
+ eth_vhost_uninstall_intr(dev);
+
+ dev->intr_handle = rte_intr_instance_alloc(RTE_INTR_INSTANCE_F_PRIVATE);
+ if (dev->intr_handle == NULL) {
+ VHOST_LOG(ERR, "Fail to allocate intr_handle\n");
+ return -ENOMEM;
+ }
+ if (rte_intr_efd_counter_size_set(dev->intr_handle, sizeof(uint64_t)))
+ return -rte_errno;
+
+ if (rte_intr_vec_list_alloc(dev->intr_handle, NULL, nb_rxq)) {
+ VHOST_LOG(ERR,
+ "Failed to allocate memory for interrupt vector\n");
+ rte_intr_instance_free(dev->intr_handle);
+ return -ENOMEM;
+ }
+
+
+ VHOST_LOG(INFO, "Prepare intr vec\n");
+ for (i = 0; i < nb_rxq; i++) {
+ if (rte_intr_vec_list_index_set(dev->intr_handle, i, RTE_INTR_VEC_RXTX_OFFSET + i))
+ return -rte_errno;
+ if (rte_intr_efds_index_set(dev->intr_handle, i, -1))
+ return -rte_errno;
+ vq = dev->data->rx_queues[i];
+ if (!vq) {
+ VHOST_LOG(INFO, "rxq-%d not setup yet, skip!\n", i);
+ continue;
+ }
+
+ ret = rte_vhost_get_vhost_vring(vq->vid, (i << 1) + 1, &vring);
+ if (ret < 0) {
+ VHOST_LOG(INFO,
+ "Failed to get rxq-%d's vring, skip!\n", i);
+ continue;
+ }
+
+ if (vring.kickfd < 0) {
+ VHOST_LOG(INFO,
+ "rxq-%d's kickfd is invalid, skip!\n", i);
+ continue;
+ }
+
+ if (rte_intr_efds_index_set(dev->intr_handle, i, vring.kickfd))
+ continue;
+ VHOST_LOG(INFO, "Installed intr vec for rxq-%d\n", i);
+ }
+
+ if (rte_intr_nb_efd_set(dev->intr_handle, nb_rxq))
+ return -rte_errno;
+
+ if (rte_intr_max_intr_set(dev->intr_handle, nb_rxq + 1))
+ return -rte_errno;
+
+ if (rte_intr_type_set(dev->intr_handle, RTE_INTR_HANDLE_VDEV))
+ return -rte_errno;
+
+ return 0;
+}
+