X-Git-Url: http://git.droids-corp.org/?a=blobdiff_plain;f=drivers%2Fnet%2Fvirtio%2Fvirtio_user%2Fvirtio_user_dev.c;h=6a6145583b77044fddcfd9b227045b69c375061f;hb=6c31a8c20a5af8f8b7929d7637a6a9d414ccfb31;hp=215819d46d2f4c9c78f281f51ad5d72a65b1774c;hpb=748e5ea58a57ac0368d65e358ceafc388f1c0be1;p=dpdk.git diff --git a/drivers/net/virtio/virtio_user/virtio_user_dev.c b/drivers/net/virtio/virtio_user/virtio_user_dev.c index 215819d46d..6a6145583b 100644 --- a/drivers/net/virtio/virtio_user/virtio_user_dev.c +++ b/drivers/net/virtio/virtio_user/virtio_user_dev.c @@ -13,6 +13,7 @@ #include #include +#include #include #include @@ -43,7 +44,7 @@ virtio_user_create_queue(struct virtio_user_dev *dev, uint32_t queue_sel) file.fd = dev->callfds[queue_sel]; ret = dev->ops->set_vring_call(dev, &file); if (ret < 0) { - PMD_INIT_LOG(ERR, "(%s) Failed to create queue %u\n", dev->path, queue_sel); + PMD_INIT_LOG(ERR, "(%s) Failed to create queue %u", dev->path, queue_sel); return -1; } @@ -107,7 +108,7 @@ virtio_user_kick_queue(struct virtio_user_dev *dev, uint32_t queue_sel) return 0; err: - PMD_INIT_LOG(ERR, "(%s) Failed to kick queue %u\n", dev->path, queue_sel); + PMD_INIT_LOG(ERR, "(%s) Failed to kick queue %u", dev->path, queue_sel); return -1; } @@ -213,7 +214,7 @@ error: pthread_mutex_unlock(&dev->mutex); rte_mcfg_mem_read_unlock(); - PMD_INIT_LOG(ERR, "(%s) Failed to start device\n", dev->path); + PMD_INIT_LOG(ERR, "(%s) Failed to start device", dev->path); /* TODO: free resource here or caller to check */ return -1; @@ -254,26 +255,89 @@ out: err: pthread_mutex_unlock(&dev->mutex); - PMD_INIT_LOG(ERR, "(%s) Failed to stop device\n", dev->path); + PMD_INIT_LOG(ERR, "(%s) Failed to stop device", dev->path); return -1; } -static inline void -parse_mac(struct virtio_user_dev *dev, const char *mac) +int +virtio_user_dev_set_mac(struct virtio_user_dev *dev) { - struct rte_ether_addr tmp; + int ret = 0; - if (!mac) - return; + if (!(dev->device_features & (1ULL << VIRTIO_NET_F_MAC))) + return -ENOTSUP; + + if (!dev->ops->set_config) + return -ENOTSUP; + + ret = dev->ops->set_config(dev, dev->mac_addr, + offsetof(struct virtio_net_config, mac), + RTE_ETHER_ADDR_LEN); + if (ret) + PMD_DRV_LOG(ERR, "(%s) Failed to set MAC address in device", dev->path); + + return ret; +} + +int +virtio_user_dev_get_mac(struct virtio_user_dev *dev) +{ + int ret = 0; + + if (!(dev->device_features & (1ULL << VIRTIO_NET_F_MAC))) + return -ENOTSUP; + + if (!dev->ops->get_config) + return -ENOTSUP; + + ret = dev->ops->get_config(dev, dev->mac_addr, + offsetof(struct virtio_net_config, mac), + RTE_ETHER_ADDR_LEN); + if (ret) + PMD_DRV_LOG(ERR, "(%s) Failed to get MAC address from device", dev->path); + + return ret; +} + +static void +virtio_user_dev_init_mac(struct virtio_user_dev *dev, const char *mac) +{ + struct rte_ether_addr cmdline_mac; + char buf[RTE_ETHER_ADDR_FMT_SIZE]; + int ret; - if (rte_ether_unformat_addr(mac, &tmp) == 0) { - memcpy(dev->mac_addr, &tmp, RTE_ETHER_ADDR_LEN); + if (mac && rte_ether_unformat_addr(mac, &cmdline_mac) == 0) { + /* + * MAC address was passed from command-line, try to store + * it in the device if it supports it. Otherwise try to use + * the device one. + */ + memcpy(dev->mac_addr, &cmdline_mac, RTE_ETHER_ADDR_LEN); dev->mac_specified = 1; + + /* Setting MAC may fail, continue to get the device one in this case */ + virtio_user_dev_set_mac(dev); + ret = virtio_user_dev_get_mac(dev); + if (ret == -ENOTSUP) + goto out; + + if (memcmp(&cmdline_mac, dev->mac_addr, RTE_ETHER_ADDR_LEN)) + PMD_DRV_LOG(INFO, "(%s) Device MAC update failed", dev->path); } else { - /* ignore the wrong mac, use random mac */ - PMD_DRV_LOG(ERR, "wrong format of mac: %s", mac); + ret = virtio_user_dev_get_mac(dev); + if (ret) { + PMD_DRV_LOG(ERR, "(%s) No valid MAC in devargs or device, use random", + dev->path); + return; + } + + dev->mac_specified = 1; } +out: + rte_ether_format_addr(buf, RTE_ETHER_ADDR_FMT_SIZE, + (struct rte_ether_addr *)dev->mac_addr); + PMD_DRV_LOG(INFO, "(%s) MAC %s specified", dev->path, buf); } static int @@ -283,13 +347,7 @@ virtio_user_dev_init_notify(struct virtio_user_dev *dev) int callfd; int kickfd; - for (i = 0; i < VIRTIO_MAX_VIRTQUEUES; ++i) { - if (i >= dev->max_queue_pairs * 2) { - dev->kickfds[i] = -1; - dev->callfds[i] = -1; - continue; - } - + for (i = 0; i < dev->max_queue_pairs * 2; i++) { /* May use invalid flag, but some backend uses kickfd and * callfd as criteria to judge if dev is alive. so finally we * use real event_fd. @@ -297,35 +355,56 @@ virtio_user_dev_init_notify(struct virtio_user_dev *dev) callfd = eventfd(0, EFD_CLOEXEC | EFD_NONBLOCK); if (callfd < 0) { PMD_DRV_LOG(ERR, "(%s) callfd error, %s", dev->path, strerror(errno)); - break; + goto err; } kickfd = eventfd(0, EFD_CLOEXEC | EFD_NONBLOCK); if (kickfd < 0) { close(callfd); PMD_DRV_LOG(ERR, "(%s) kickfd error, %s", dev->path, strerror(errno)); - break; + goto err; } dev->callfds[i] = callfd; dev->kickfds[i] = kickfd; } - if (i < VIRTIO_MAX_VIRTQUEUES) { - for (j = 0; j < i; ++j) { - close(dev->callfds[j]); + return 0; +err: + for (j = 0; j < i; j++) { + if (dev->kickfds[j] >= 0) { close(dev->kickfds[j]); + dev->kickfds[j] = -1; + } + if (dev->callfds[j] >= 0) { + close(dev->callfds[j]); + dev->callfds[j] = -1; } - - return -1; } - return 0; + return -1; +} + +static void +virtio_user_dev_uninit_notify(struct virtio_user_dev *dev) +{ + uint32_t i; + + for (i = 0; i < dev->max_queue_pairs * 2; ++i) { + if (dev->kickfds[i] >= 0) { + close(dev->kickfds[i]); + dev->kickfds[i] = -1; + } + if (dev->callfds[i] >= 0) { + close(dev->callfds[i]); + dev->callfds[i] = -1; + } + } } static int virtio_user_fill_intr_handle(struct virtio_user_dev *dev) { uint32_t i; - struct rte_eth_dev *eth_dev = &rte_eth_devices[dev->port_id]; + struct rte_eth_dev *eth_dev = &rte_eth_devices[dev->hw.port_id]; if (!eth_dev->intr_handle) { eth_dev->intr_handle = malloc(sizeof(*eth_dev->intr_handle)); @@ -337,17 +416,13 @@ virtio_user_fill_intr_handle(struct virtio_user_dev *dev) } for (i = 0; i < dev->max_queue_pairs; ++i) - eth_dev->intr_handle->efds[i] = dev->callfds[i]; + eth_dev->intr_handle->efds[i] = dev->callfds[2 * i]; eth_dev->intr_handle->nb_efd = dev->max_queue_pairs; eth_dev->intr_handle->max_intr = dev->max_queue_pairs + 1; eth_dev->intr_handle->type = RTE_INTR_HANDLE_VDEV; /* For virtio vdev, no need to read counter for clean */ eth_dev->intr_handle->efd_counter_size = 0; - eth_dev->intr_handle->fd = -1; - if (dev->vhostfd >= 0) - eth_dev->intr_handle->fd = dev->vhostfd; - else if (dev->is_server) - eth_dev->intr_handle->fd = dev->listenfd; + eth_dev->intr_handle->fd = dev->ops->get_intr_fd(dev); return 0; } @@ -396,18 +471,12 @@ exit: pthread_mutex_unlock(&dev->mutex); if (ret < 0) - PMD_DRV_LOG(ERR, "(%s) Failed to update memory table\n", dev->path); + PMD_DRV_LOG(ERR, "(%s) Failed to update memory table", dev->path); } static int virtio_user_dev_setup(struct virtio_user_dev *dev) { - uint32_t q; - - dev->vhostfd = -1; - dev->vhostfds = NULL; - dev->tapfds = NULL; - if (dev->is_server) { if (dev->backend_type != VIRTIO_USER_BACKEND_VHOST_USER) { PMD_DRV_LOG(ERR, "Server mode only supports vhost-user!"); @@ -415,50 +484,44 @@ virtio_user_dev_setup(struct virtio_user_dev *dev) } } - if (dev->backend_type == VIRTIO_USER_BACKEND_VHOST_USER) { + switch (dev->backend_type) { + case VIRTIO_USER_BACKEND_VHOST_USER: dev->ops = &virtio_ops_user; - } else if (dev->backend_type == - VIRTIO_USER_BACKEND_VHOST_KERNEL) { + break; + case VIRTIO_USER_BACKEND_VHOST_KERNEL: dev->ops = &virtio_ops_kernel; - - dev->vhostfds = malloc(dev->max_queue_pairs * - sizeof(int)); - dev->tapfds = malloc(dev->max_queue_pairs * - sizeof(int)); - if (!dev->vhostfds || !dev->tapfds) { - PMD_INIT_LOG(ERR, "(%s) Failed to allocate FDs", dev->path); - return -1; - } - - for (q = 0; q < dev->max_queue_pairs; ++q) { - dev->vhostfds[q] = -1; - dev->tapfds[q] = -1; - } - } else if (dev->backend_type == - VIRTIO_USER_BACKEND_VHOST_VDPA) { + break; + case VIRTIO_USER_BACKEND_VHOST_VDPA: dev->ops = &virtio_ops_vdpa; - } else { + break; + default: PMD_DRV_LOG(ERR, "(%s) Unknown backend type", dev->path); return -1; } - if (dev->ops->setup(dev) < 0) { - PMD_INIT_LOG(ERR, "(%s) Failed to setup backend\n", dev->path); + PMD_INIT_LOG(ERR, "(%s) Failed to setup backend", dev->path); return -1; } if (virtio_user_dev_init_notify(dev) < 0) { - PMD_INIT_LOG(ERR, "(%s) Failed to init notifiers\n", dev->path); - return -1; + PMD_INIT_LOG(ERR, "(%s) Failed to init notifiers", dev->path); + goto destroy; } if (virtio_user_fill_intr_handle(dev) < 0) { - PMD_INIT_LOG(ERR, "(%s) Failed to init interrupt handler\n", dev->path); - return -1; + PMD_INIT_LOG(ERR, "(%s) Failed to init interrupt handler", dev->path); + goto uninit; } return 0; + +uninit: + virtio_user_dev_uninit_notify(dev); +destroy: + dev->ops->destroy(dev); + + return -1; } /* Use below macro to filter features from vhost backend */ @@ -489,9 +552,16 @@ virtio_user_dev_init(struct virtio_user_dev *dev, char *path, int queues, enum virtio_user_backend_type backend_type) { uint64_t backend_features; + int i; pthread_mutex_init(&dev->mutex, NULL); strlcpy(dev->path, path, PATH_MAX); + + for (i = 0; i < VIRTIO_MAX_VIRTQUEUES; i++) { + dev->kickfds[i] = -1; + dev->callfds[i] = -1; + } + dev->started = 0; dev->max_queue_pairs = queues; dev->queue_pairs = 1; /* mq disabled by default */ @@ -502,8 +572,6 @@ virtio_user_dev_init(struct virtio_user_dev *dev, char *path, int queues, dev->unsupported_features = 0; dev->backend_type = backend_type; - parse_mac(dev, mac); - if (*ifname) { dev->ifname = *ifname; *ifname = NULL; @@ -531,6 +599,8 @@ virtio_user_dev_init(struct virtio_user_dev *dev, char *path, int queues, return -1; } + virtio_user_dev_init_mac(dev, mac); + if (!mrg_rxbuf) dev->unsupported_features |= (1ull << VIRTIO_NET_F_MRG_RXBUF); @@ -566,17 +636,13 @@ virtio_user_dev_init(struct virtio_user_dev *dev, char *path, int queues, if (dev->backend_type == VIRTIO_USER_BACKEND_VHOST_USER) dev->frontend_features |= (1ull << VIRTIO_NET_F_STATUS); - /* - * Device features = - * (frontend_features | backend_features) & ~unsupported_features; - */ - dev->device_features |= dev->frontend_features; + dev->frontend_features &= ~dev->unsupported_features; dev->device_features &= ~dev->unsupported_features; if (rte_mem_event_callback_register(VIRTIO_USER_MEM_EVENT_CLB_NAME, virtio_user_mem_event_cb, dev)) { if (rte_errno != ENOTSUP) { - PMD_INIT_LOG(ERR, "(%s) Failed to register mem event callback\n", + PMD_INIT_LOG(ERR, "(%s) Failed to register mem event callback", dev->path); return -1; } @@ -588,34 +654,18 @@ virtio_user_dev_init(struct virtio_user_dev *dev, char *path, int queues, void virtio_user_dev_uninit(struct virtio_user_dev *dev) { - uint32_t i; - - virtio_user_stop_device(dev); + struct rte_eth_dev *eth_dev = &rte_eth_devices[dev->hw.port_id]; - rte_mem_event_callback_unregister(VIRTIO_USER_MEM_EVENT_CLB_NAME, dev); - - for (i = 0; i < dev->max_queue_pairs * 2; ++i) { - close(dev->callfds[i]); - close(dev->kickfds[i]); + if (eth_dev->intr_handle) { + free(eth_dev->intr_handle); + eth_dev->intr_handle = NULL; } - if (dev->vhostfd >= 0) - close(dev->vhostfd); + virtio_user_stop_device(dev); - if (dev->is_server && dev->listenfd >= 0) { - close(dev->listenfd); - dev->listenfd = -1; - } + rte_mem_event_callback_unregister(VIRTIO_USER_MEM_EVENT_CLB_NAME, dev); - if (dev->vhostfds) { - for (i = 0; i < dev->max_queue_pairs; ++i) { - close(dev->vhostfds[i]); - if (dev->tapfds[i] >= 0) - close(dev->tapfds[i]); - } - free(dev->vhostfds); - free(dev->tapfds); - } + virtio_user_dev_uninit_notify(dev); free(dev->ifname); @@ -637,15 +687,11 @@ virtio_user_handle_mq(struct virtio_user_dev *dev, uint16_t q_pairs) return -1; } - /* Server mode can't enable queue pairs if vhostfd is invalid, - * always return 0 in this case. - */ - if (!dev->is_server || dev->vhostfd >= 0) { - for (i = 0; i < q_pairs; ++i) - ret |= dev->ops->enable_qp(dev, i, 1); - for (i = q_pairs; i < dev->max_queue_pairs; ++i) - ret |= dev->ops->enable_qp(dev, i, 0); - } + for (i = 0; i < q_pairs; ++i) + ret |= dev->ops->enable_qp(dev, i, 1); + for (i = q_pairs; i < dev->max_queue_pairs; ++i) + ret |= dev->ops->enable_qp(dev, i, 0); + dev->queue_pairs = q_pairs; return ret; @@ -820,7 +866,7 @@ virtio_user_dev_set_status(struct virtio_user_dev *dev, uint8_t status) dev->status = status; ret = dev->ops->set_status(dev, status); if (ret && ret != -ENOTSUP) - PMD_INIT_LOG(ERR, "(%s) Failed to set backend status\n", dev->path); + PMD_INIT_LOG(ERR, "(%s) Failed to set backend status", dev->path); pthread_mutex_unlock(&dev->mutex); return ret; @@ -844,7 +890,7 @@ virtio_user_dev_update_status(struct virtio_user_dev *dev) "\t-DRIVER_OK: %u\n" "\t-FEATURES_OK: %u\n" "\t-DEVICE_NEED_RESET: %u\n" - "\t-FAILED: %u\n", + "\t-FAILED: %u", dev->status, (dev->status == VIRTIO_CONFIG_STATUS_RESET), !!(dev->status & VIRTIO_CONFIG_STATUS_ACK), @@ -854,9 +900,193 @@ virtio_user_dev_update_status(struct virtio_user_dev *dev) !!(dev->status & VIRTIO_CONFIG_STATUS_DEV_NEED_RESET), !!(dev->status & VIRTIO_CONFIG_STATUS_FAILED)); } else if (ret != -ENOTSUP) { - PMD_INIT_LOG(ERR, "(%s) Failed to get backend status\n", dev->path); + PMD_INIT_LOG(ERR, "(%s) Failed to get backend status", dev->path); } pthread_mutex_unlock(&dev->mutex); return ret; } + +int +virtio_user_dev_update_link_state(struct virtio_user_dev *dev) +{ + if (dev->ops->update_link_state) + return dev->ops->update_link_state(dev); + + return 0; +} + +static void +virtio_user_dev_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; + + /* + * Waiting 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(virtnet_rxq_to_vq(rxvq)); + 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(virtnet_txq_to_vq(txvq)); + } + + hw->started = 1; + rte_spinlock_unlock(&hw->state_lock); +} + +void +virtio_user_dev_delayed_disconnect_handler(void *param) +{ + struct virtio_user_dev *dev = param; + struct rte_eth_dev *eth_dev = &rte_eth_devices[dev->hw.port_id]; + + if (rte_intr_disable(eth_dev->intr_handle) < 0) { + PMD_DRV_LOG(ERR, "interrupt disable failed"); + return; + } + PMD_DRV_LOG(DEBUG, "Unregistering intr fd: %d", + eth_dev->intr_handle->fd); + if (rte_intr_callback_unregister(eth_dev->intr_handle, + virtio_interrupt_handler, + eth_dev) != 1) + PMD_DRV_LOG(ERR, "interrupt unregister failed"); + + if (dev->is_server) { + if (dev->ops->server_disconnect) + dev->ops->server_disconnect(dev); + + eth_dev->intr_handle->fd = dev->ops->get_intr_fd(dev); + + PMD_DRV_LOG(DEBUG, "Registering intr fd: %d", + eth_dev->intr_handle->fd); + + if (rte_intr_callback_register(eth_dev->intr_handle, + virtio_interrupt_handler, + eth_dev)) + PMD_DRV_LOG(ERR, "interrupt register failed"); + + if (rte_intr_enable(eth_dev->intr_handle) < 0) { + PMD_DRV_LOG(ERR, "interrupt enable failed"); + return; + } + } +} + +static void +virtio_user_dev_delayed_intr_reconfig_handler(void *param) +{ + struct virtio_user_dev *dev = param; + struct rte_eth_dev *eth_dev = &rte_eth_devices[dev->hw.port_id]; + + PMD_DRV_LOG(DEBUG, "Unregistering intr fd: %d", + eth_dev->intr_handle->fd); + + if (rte_intr_callback_unregister(eth_dev->intr_handle, + virtio_interrupt_handler, + eth_dev) != 1) + PMD_DRV_LOG(ERR, "interrupt unregister failed"); + + eth_dev->intr_handle->fd = dev->ops->get_intr_fd(dev); + + PMD_DRV_LOG(DEBUG, "Registering intr fd: %d", eth_dev->intr_handle->fd); + + if (rte_intr_callback_register(eth_dev->intr_handle, + virtio_interrupt_handler, eth_dev)) + PMD_DRV_LOG(ERR, "interrupt register failed"); + + if (rte_intr_enable(eth_dev->intr_handle) < 0) + PMD_DRV_LOG(ERR, "interrupt enable failed"); +} + +int +virtio_user_dev_server_reconnect(struct virtio_user_dev *dev) +{ + int ret, old_status; + struct rte_eth_dev *eth_dev = &rte_eth_devices[dev->hw.port_id]; + struct virtio_hw *hw = &dev->hw; + + if (!dev->ops->server_reconnect) { + PMD_DRV_LOG(ERR, "(%s) Missing server reconnect callback", dev->path); + return -1; + } + + if (dev->ops->server_reconnect(dev)) { + PMD_DRV_LOG(ERR, "(%s) Reconnect callback call failed", dev->path); + return -1; + } + + 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->get_features(dev, &dev->device_features) < 0) { + PMD_INIT_LOG(ERR, "get_features failed: %s", + strerror(errno)); + return -1; + } + + /* unmask vhost-user unsupported features */ + dev->device_features &= ~(dev->unsupported_features); + + dev->features &= (dev->device_features | dev->frontend_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_dev_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; + } + /* + * This function can be called from the interrupt handler, so + * we can't unregister interrupt handler here. Setting + * alarm to do that later. + */ + rte_eal_alarm_set(1, + virtio_user_dev_delayed_intr_reconfig_handler, + (void *)dev); + } + PMD_INIT_LOG(NOTICE, "server mode virtio-user reconnection succeeds!"); + return 0; +}