+#define virtio_user_get_dev(hwp) container_of(hwp, struct virtio_user_dev, hw)
+
+static void
+virtio_user_reset_queues_packed(struct rte_eth_dev *eth_dev)
+{
+ struct virtio_user_dev *dev = eth_dev->data->dev_private;
+ struct virtio_hw *hw = &dev->hw;
+ struct virtnet_rx *rxvq;
+ struct virtnet_tx *txvq;
+ uint16_t i;
+
+ /* Add lock to avoid queue contention. */
+ rte_spinlock_lock(&hw->state_lock);
+ hw->started = 0;
+
+ /*
+ * Waitting for datapath to complete before resetting queues.
+ * 1 ms should be enough for the ongoing Tx/Rx function to finish.
+ */
+ rte_delay_ms(1);
+
+ /* Vring reset for each Tx queue and Rx queue. */
+ for (i = 0; i < eth_dev->data->nb_rx_queues; i++) {
+ rxvq = eth_dev->data->rx_queues[i];
+ virtqueue_rxvq_reset_packed(rxvq->vq);
+ virtio_dev_rx_queue_setup_finish(eth_dev, i);
+ }
+
+ for (i = 0; i < eth_dev->data->nb_tx_queues; i++) {
+ txvq = eth_dev->data->tx_queues[i];
+ virtqueue_txvq_reset_packed(txvq->vq);
+ }
+
+ hw->started = 1;
+ rte_spinlock_unlock(&hw->state_lock);
+}
+
+
+static int
+virtio_user_server_reconnect(struct virtio_user_dev *dev)
+{
+ int ret, connectfd, old_status;
+ struct rte_eth_dev *eth_dev = &rte_eth_devices[dev->port_id];
+ struct virtio_hw *hw = &dev->hw;
+ uint64_t protocol_features;
+
+ connectfd = accept(dev->listenfd, NULL, NULL);
+ if (connectfd < 0)
+ return -1;
+
+ dev->vhostfd = connectfd;
+ old_status = dev->status;
+
+ virtio_reset(hw);
+
+ virtio_set_status(hw, VIRTIO_CONFIG_STATUS_ACK);
+
+ virtio_set_status(hw, VIRTIO_CONFIG_STATUS_DRIVER);
+
+ if (dev->ops->send_request(dev, VHOST_USER_GET_FEATURES,
+ &dev->device_features) < 0) {
+ PMD_INIT_LOG(ERR, "get_features failed: %s",
+ strerror(errno));
+ return -1;
+ }
+
+ if (dev->device_features &
+ (1ULL << VHOST_USER_F_PROTOCOL_FEATURES)) {
+ if (dev->ops->send_request(dev,
+ VHOST_USER_GET_PROTOCOL_FEATURES,
+ &protocol_features))
+ return -1;
+
+ /* Offer VHOST_USER_PROTOCOL_F_STATUS */
+ dev->protocol_features |=
+ (1ULL << VHOST_USER_PROTOCOL_F_STATUS);
+ dev->protocol_features &= protocol_features;
+
+ if (dev->ops->send_request(dev,
+ VHOST_USER_SET_PROTOCOL_FEATURES,
+ &dev->protocol_features))
+ return -1;
+
+ if (!(dev->protocol_features &
+ (1ULL << VHOST_USER_PROTOCOL_F_MQ)))
+ dev->unsupported_features |= (1ull << VIRTIO_NET_F_MQ);
+ }
+
+ dev->device_features |= dev->frontend_features;
+
+ /* umask vhost-user unsupported features */
+ dev->device_features &= ~(dev->unsupported_features);
+
+ dev->features &= dev->device_features;
+
+ /* For packed ring, resetting queues is required in reconnection. */
+ if (virtio_with_packed_queue(hw) &&
+ (old_status & VIRTIO_CONFIG_STATUS_DRIVER_OK)) {
+ PMD_INIT_LOG(NOTICE, "Packets on the fly will be dropped"
+ " when packed ring reconnecting.");
+ virtio_user_reset_queues_packed(eth_dev);
+ }
+
+ virtio_set_status(hw, VIRTIO_CONFIG_STATUS_FEATURES_OK);
+
+ /* Start the device */
+ virtio_set_status(hw, VIRTIO_CONFIG_STATUS_DRIVER_OK);
+ if (!dev->started)
+ return -1;
+
+ if (dev->queue_pairs > 1) {
+ ret = virtio_user_handle_mq(dev, dev->queue_pairs);
+ if (ret != 0) {
+ PMD_INIT_LOG(ERR, "Fails to enable multi-queue pairs!");
+ return -1;
+ }
+ }
+ if (eth_dev->data->dev_flags & RTE_ETH_DEV_INTR_LSC) {
+ if (rte_intr_disable(eth_dev->intr_handle) < 0) {
+ PMD_DRV_LOG(ERR, "interrupt disable failed");
+ return -1;
+ }
+ rte_intr_callback_unregister(eth_dev->intr_handle,
+ virtio_interrupt_handler,
+ eth_dev);
+ eth_dev->intr_handle->fd = connectfd;
+ rte_intr_callback_register(eth_dev->intr_handle,
+ virtio_interrupt_handler, eth_dev);
+
+ if (rte_intr_enable(eth_dev->intr_handle) < 0) {
+ PMD_DRV_LOG(ERR, "interrupt enable failed");
+ return -1;
+ }
+ }
+ PMD_INIT_LOG(NOTICE, "server mode virtio-user reconnection succeeds!");
+ return 0;
+}
+
+static void
+virtio_user_delayed_handler(void *param)
+{
+ struct virtio_hw *hw = (struct virtio_hw *)param;
+ struct rte_eth_dev *eth_dev = &rte_eth_devices[hw->port_id];
+ struct virtio_user_dev *dev = virtio_user_get_dev(hw);
+
+ if (rte_intr_disable(eth_dev->intr_handle) < 0) {
+ PMD_DRV_LOG(ERR, "interrupt disable failed");
+ return;
+ }
+ rte_intr_callback_unregister(eth_dev->intr_handle,
+ virtio_interrupt_handler, eth_dev);
+ if (dev->is_server) {
+ if (dev->vhostfd >= 0) {
+ close(dev->vhostfd);
+ dev->vhostfd = -1;
+ /* Until the featuers are negotiated again, don't assume
+ * the backend supports VHOST_USER_PROTOCOL_F_STATUS
+ */
+ dev->protocol_features &=
+ ~(1ULL << VHOST_USER_PROTOCOL_F_STATUS);
+ }
+ eth_dev->intr_handle->fd = dev->listenfd;
+ rte_intr_callback_register(eth_dev->intr_handle,
+ virtio_interrupt_handler, eth_dev);
+ if (rte_intr_enable(eth_dev->intr_handle) < 0) {
+ PMD_DRV_LOG(ERR, "interrupt enable failed");
+ return;
+ }
+ }
+}