X-Git-Url: http://git.droids-corp.org/?a=blobdiff_plain;f=drivers%2Fnet%2Fvirtio%2Fvirtio_ethdev.c;h=74c00ee37ea8ea0a4c00f1222f2a3d705eed9442;hb=1c07ecb41379b4105c492d4629900d26e231dbe6;hp=f74e4130deb80b526fbe6d70c0cd4ded29dfbcbd;hpb=6c3169a3dc041cbdbef506f19e3779940caa4b29;p=dpdk.git diff --git a/drivers/net/virtio/virtio_ethdev.c b/drivers/net/virtio/virtio_ethdev.c index f74e4130de..74c00ee37e 100644 --- a/drivers/net/virtio/virtio_ethdev.c +++ b/drivers/net/virtio/virtio_ethdev.c @@ -1,7 +1,7 @@ /*- * BSD LICENSE * - * Copyright(c) 2010-2014 Intel Corporation. All rights reserved. + * Copyright(c) 2010-2015 Intel Corporation. All rights reserved. * All rights reserved. * * Redistribution and use in source and binary forms, with or without @@ -51,6 +51,7 @@ #include #include #include +#include #include #include @@ -60,9 +61,11 @@ #include "virtio_pci.h" #include "virtio_logs.h" #include "virtqueue.h" +#include "virtio_rxtx.h" static int eth_virtio_dev_init(struct rte_eth_dev *eth_dev); +static int eth_virtio_dev_uninit(struct rte_eth_dev *eth_dev); static int virtio_dev_configure(struct rte_eth_dev *dev); static int virtio_dev_start(struct rte_eth_dev *dev); static void virtio_dev_stop(struct rte_eth_dev *dev); @@ -78,10 +81,10 @@ static int virtio_dev_link_update(struct rte_eth_dev *dev, static void virtio_set_hwaddr(struct virtio_hw *hw); static void virtio_get_hwaddr(struct virtio_hw *hw); -static void virtio_dev_rx_queue_release(__rte_unused void *rxq); -static void virtio_dev_tx_queue_release(__rte_unused void *txq); - -static void virtio_dev_stats_get(struct rte_eth_dev *dev, struct rte_eth_stats *stats); +static void virtio_dev_stats_get(struct rte_eth_dev *dev, + struct rte_eth_stats *stats); +static int virtio_dev_xstats_get(struct rte_eth_dev *dev, + struct rte_eth_xstats *xstats, unsigned n); static void virtio_dev_stats_reset(struct rte_eth_dev *dev); static void virtio_dev_free_mbufs(struct rte_eth_dev *dev); static int virtio_vlan_filter_set(struct rte_eth_dev *dev, @@ -110,23 +113,49 @@ static const struct rte_pci_id pci_id_virtio_map[] = { { .vendor_id = 0, /* sentinel */ }, }; +struct rte_virtio_xstats_name_off { + char name[RTE_ETH_XSTATS_NAME_SIZE]; + unsigned offset; +}; + +/* [rt]x_qX_ is prepended to the name string here */ +static const struct rte_virtio_xstats_name_off rte_virtio_q_stat_strings[] = { + {"good_packets", offsetof(struct virtqueue, packets)}, + {"good_bytes", offsetof(struct virtqueue, bytes)}, + {"errors", offsetof(struct virtqueue, errors)}, + {"multicast_packets", offsetof(struct virtqueue, multicast)}, + {"broadcast_packets", offsetof(struct virtqueue, broadcast)}, + {"undersize_packets", offsetof(struct virtqueue, size_bins[0])}, + {"size_64_packets", offsetof(struct virtqueue, size_bins[1])}, + {"size_65_127_packets", offsetof(struct virtqueue, size_bins[2])}, + {"size_128_255_packets", offsetof(struct virtqueue, size_bins[3])}, + {"size_256_511_packets", offsetof(struct virtqueue, size_bins[4])}, + {"size_512_1023_packets", offsetof(struct virtqueue, size_bins[5])}, + {"size_1024_1517_packets", offsetof(struct virtqueue, size_bins[6])}, + {"size_1518_max_packets", offsetof(struct virtqueue, size_bins[7])}, +}; + +#define VIRTIO_NB_Q_XSTATS (sizeof(rte_virtio_q_stat_strings) / \ + sizeof(rte_virtio_q_stat_strings[0])) + static int virtio_send_command(struct virtqueue *vq, struct virtio_pmd_ctrl *ctrl, int *dlen, int pkt_num) { - uint16_t head = vq->vq_desc_head_idx, i; + uint32_t head, i; int k, sum = 0; virtio_net_ctrl_ack status = ~0; struct virtio_pmd_ctrl result; ctrl->status = status; - if (!vq->hw->cvq) { + if (!(vq && vq->hw->cvq)) { PMD_INIT_LOG(ERR, "%s(): Control queue is not supported.", __func__); return -1; } + head = vq->vq_desc_head_idx; PMD_INIT_LOG(DEBUG, "vq->vq_desc_head_idx = %d, status = %d, " "vq->hw->cvq = %p vq = %p", @@ -228,7 +257,6 @@ virtio_set_multiple_queues(struct rte_eth_dev *dev, uint16_t nb_queues) dlen[0] = sizeof(uint16_t); ret = virtio_send_command(hw->cvq, &ctrl, dlen, 1); - if (ret) { PMD_INIT_LOG(ERR, "Multiqueue configured but send command " "failed, this is too late now..."); @@ -238,43 +266,53 @@ virtio_set_multiple_queues(struct rte_eth_dev *dev, uint16_t nb_queues) return 0; } +void +virtio_dev_queue_release(struct virtqueue *vq) { + struct virtio_hw *hw; + + if (vq) { + hw = vq->hw; + /* Select and deactivate the queue */ + VIRTIO_WRITE_REG_2(hw, VIRTIO_PCI_QUEUE_SEL, vq->queue_id); + VIRTIO_WRITE_REG_4(hw, VIRTIO_PCI_QUEUE_PFN, 0); + + rte_free(vq->sw_ring); + rte_free(vq); + } +} + int virtio_dev_queue_setup(struct rte_eth_dev *dev, int queue_type, uint16_t queue_idx, - uint16_t vtpci_queue_idx, + uint16_t vtpci_queue_idx, uint16_t nb_desc, unsigned int socket_id, struct virtqueue **pvq) { char vq_name[VIRTQUEUE_MAX_NAME_SZ]; const struct rte_memzone *mz; - uint16_t vq_size; - int size; + unsigned int vq_size, size; struct virtio_hw *hw = dev->data->dev_private; - struct virtqueue *vq = NULL; + struct virtqueue *vq = NULL; /* Write the virtqueue index to the Queue Select Field */ VIRTIO_WRITE_REG_2(hw, VIRTIO_PCI_QUEUE_SEL, vtpci_queue_idx); - PMD_INIT_LOG(DEBUG, "selecting queue: %d", vtpci_queue_idx); + PMD_INIT_LOG(DEBUG, "selecting queue: %u", vtpci_queue_idx); /* * Read the virtqueue size from the Queue Size field * Always power of 2 and if 0 virtqueue does not exist */ vq_size = VIRTIO_READ_REG_2(hw, VIRTIO_PCI_QUEUE_NUM); - PMD_INIT_LOG(DEBUG, "vq_size: %d nb_desc:%d", vq_size, nb_desc); - if (nb_desc == 0) - nb_desc = vq_size; + PMD_INIT_LOG(DEBUG, "vq_size: %u nb_desc:%u", vq_size, nb_desc); if (vq_size == 0) { PMD_INIT_LOG(ERR, "%s: virtqueue does not exist", __func__); return -EINVAL; - } else if (!rte_is_power_of_2(vq_size)) { + } + + if (!rte_is_power_of_2(vq_size)) { PMD_INIT_LOG(ERR, "%s: virtqueue size is not powerof 2", __func__); return -EINVAL; - } else if (nb_desc != vq_size) { - PMD_INIT_LOG(ERR, "Warning: nb_desc(%d) is not equal to vq size (%d), fall to vq size", - nb_desc, vq_size); - nb_desc = vq_size; } if (queue_type == VTNET_RQ) { @@ -282,6 +320,9 @@ int virtio_dev_queue_setup(struct rte_eth_dev *dev, dev->data->port_id, queue_idx); vq = rte_zmalloc(vq_name, sizeof(struct virtqueue) + vq_size * sizeof(struct vq_desc_extra), RTE_CACHE_LINE_SIZE); + vq->sw_ring = rte_zmalloc_socket("rxq->sw_ring", + (RTE_PMD_VIRTIO_RX_MAX_BURST + vq_size) * + sizeof(vq->sw_ring[0]), RTE_CACHE_LINE_SIZE, socket_id); } else if (queue_type == VTNET_TQ) { snprintf(vq_name, sizeof(vq_name), "port%d_tvq%d", dev->data->port_id, queue_idx); @@ -298,13 +339,22 @@ int virtio_dev_queue_setup(struct rte_eth_dev *dev, PMD_INIT_LOG(ERR, "%s: Can not allocate virtqueue", __func__); return (-ENOMEM); } + if (queue_type == VTNET_RQ && vq->sw_ring == NULL) { + PMD_INIT_LOG(ERR, "%s: Can not allocate RX soft ring", + __func__); + rte_free(vq); + return -ENOMEM; + } vq->hw = hw; vq->port_id = dev->data->port_id; vq->queue_id = queue_idx; vq->vq_queue_index = vtpci_queue_idx; vq->vq_nentries = vq_size; - vq->vq_free_cnt = vq_size; + + if (nb_desc == 0 || nb_desc > vq_size) + nb_desc = vq_size; + vq->vq_free_cnt = nb_desc; /* * Reserve a memzone for vring elements @@ -316,8 +366,12 @@ int virtio_dev_queue_setup(struct rte_eth_dev *dev, mz = rte_memzone_reserve_aligned(vq_name, vq->vq_ring_size, socket_id, 0, VIRTIO_PCI_VRING_ALIGN); if (mz == NULL) { - rte_free(vq); - return -ENOMEM; + if (rte_errno == EEXIST) + mz = rte_memzone_lookup(vq_name); + if (mz == NULL) { + rte_free(vq); + return -ENOMEM; + } } /* @@ -350,8 +404,13 @@ int virtio_dev_queue_setup(struct rte_eth_dev *dev, vq_size * hw->vtnet_hdr_size, socket_id, 0, RTE_CACHE_LINE_SIZE); if (vq->virtio_net_hdr_mz == NULL) { - rte_free(vq); - return -ENOMEM; + if (rte_errno == EEXIST) + vq->virtio_net_hdr_mz = + rte_memzone_lookup(vq_name); + if (vq->virtio_net_hdr_mz == NULL) { + rte_free(vq); + return -ENOMEM; + } } vq->virtio_net_hdr_mem = vq->virtio_net_hdr_mz->phys_addr; @@ -364,8 +423,13 @@ int virtio_dev_queue_setup(struct rte_eth_dev *dev, vq->virtio_net_hdr_mz = rte_memzone_reserve_aligned(vq_name, PAGE_SIZE, socket_id, 0, RTE_CACHE_LINE_SIZE); if (vq->virtio_net_hdr_mz == NULL) { - rte_free(vq); - return -ENOMEM; + if (rte_errno == EEXIST) + vq->virtio_net_hdr_mz = + rte_memzone_lookup(vq_name); + if (vq->virtio_net_hdr_mz == NULL) { + rte_free(vq); + return -ENOMEM; + } } vq->virtio_net_hdr_mem = vq->virtio_net_hdr_mz->phys_addr; @@ -387,14 +451,12 @@ virtio_dev_cq_queue_setup(struct rte_eth_dev *dev, uint16_t vtpci_queue_idx, uint32_t socket_id) { struct virtqueue *vq; - uint16_t nb_desc = 0; int ret; struct virtio_hw *hw = dev->data->dev_private; PMD_INIT_FUNC_TRACE(); ret = virtio_dev_queue_setup(dev, VTNET_CQ, VTNET_SQ_CQ_QUEUE_IDX, - vtpci_queue_idx, nb_desc, socket_id, &vq); - + vtpci_queue_idx, 0, socket_id, &vq); if (ret < 0) { PMD_INIT_LOG(ERR, "control vq initialization failed"); return ret; @@ -404,6 +466,22 @@ virtio_dev_cq_queue_setup(struct rte_eth_dev *dev, uint16_t vtpci_queue_idx, return 0; } +static void +virtio_free_queues(struct rte_eth_dev *dev) +{ + unsigned int i; + + for (i = 0; i < dev->data->nb_rx_queues; i++) + virtio_dev_rx_queue_release(dev->data->rx_queues[i]); + + dev->data->nb_rx_queues = 0; + + for (i = 0; i < dev->data->nb_tx_queues; i++) + virtio_dev_tx_queue_release(dev->data->tx_queues[i]); + + dev->data->nb_tx_queues = 0; +} + static void virtio_dev_close(struct rte_eth_dev *dev) { @@ -418,6 +496,7 @@ virtio_dev_close(struct rte_eth_dev *dev) vtpci_reset(hw); hw->started = 0; virtio_dev_free_mbufs(dev); + virtio_free_queues(dev); } static void @@ -428,13 +507,17 @@ virtio_dev_promiscuous_enable(struct rte_eth_dev *dev) int dlen[1]; int ret; + if (!vtpci_with_feature(hw, VIRTIO_NET_F_CTRL_RX)) { + PMD_INIT_LOG(INFO, "host does not support rx control\n"); + return; + } + ctrl.hdr.class = VIRTIO_NET_CTRL_RX; ctrl.hdr.cmd = VIRTIO_NET_CTRL_RX_PROMISC; ctrl.data[0] = 1; dlen[0] = 1; ret = virtio_send_command(hw->cvq, &ctrl, dlen, 1); - if (ret) PMD_INIT_LOG(ERR, "Failed to enable promisc"); } @@ -447,13 +530,17 @@ virtio_dev_promiscuous_disable(struct rte_eth_dev *dev) int dlen[1]; int ret; + if (!vtpci_with_feature(hw, VIRTIO_NET_F_CTRL_RX)) { + PMD_INIT_LOG(INFO, "host does not support rx control\n"); + return; + } + ctrl.hdr.class = VIRTIO_NET_CTRL_RX; ctrl.hdr.cmd = VIRTIO_NET_CTRL_RX_PROMISC; ctrl.data[0] = 0; dlen[0] = 1; ret = virtio_send_command(hw->cvq, &ctrl, dlen, 1); - if (ret) PMD_INIT_LOG(ERR, "Failed to disable promisc"); } @@ -466,13 +553,17 @@ virtio_dev_allmulticast_enable(struct rte_eth_dev *dev) int dlen[1]; int ret; + if (!vtpci_with_feature(hw, VIRTIO_NET_F_CTRL_RX)) { + PMD_INIT_LOG(INFO, "host does not support rx control\n"); + return; + } + ctrl.hdr.class = VIRTIO_NET_CTRL_RX; ctrl.hdr.cmd = VIRTIO_NET_CTRL_RX_ALLMULTI; ctrl.data[0] = 1; dlen[0] = 1; ret = virtio_send_command(hw->cvq, &ctrl, dlen, 1); - if (ret) PMD_INIT_LOG(ERR, "Failed to enable allmulticast"); } @@ -485,13 +576,17 @@ virtio_dev_allmulticast_disable(struct rte_eth_dev *dev) int dlen[1]; int ret; + if (!vtpci_with_feature(hw, VIRTIO_NET_F_CTRL_RX)) { + PMD_INIT_LOG(INFO, "host does not support rx control\n"); + return; + } + ctrl.hdr.class = VIRTIO_NET_CTRL_RX; ctrl.hdr.cmd = VIRTIO_NET_CTRL_RX_ALLMULTI; ctrl.data[0] = 0; dlen[0] = 1; ret = virtio_send_command(hw->cvq, &ctrl, dlen, 1); - if (ret) PMD_INIT_LOG(ERR, "Failed to disable allmulticast"); } @@ -511,13 +606,13 @@ static const struct eth_dev_ops virtio_eth_dev_ops = { .dev_infos_get = virtio_dev_info_get, .stats_get = virtio_dev_stats_get, + .xstats_get = virtio_dev_xstats_get, .stats_reset = virtio_dev_stats_reset, + .xstats_reset = virtio_dev_stats_reset, .link_update = virtio_dev_link_update, .rx_queue_setup = virtio_dev_rx_queue_setup, - /* meaningfull only to multiple queue */ .rx_queue_release = virtio_dev_rx_queue_release, .tx_queue_setup = virtio_dev_tx_queue_setup, - /* meaningfull only to multiple queue */ .tx_queue_release = virtio_dev_tx_queue_release, /* collect stats per queue */ .queue_stats_mapping_set = virtio_dev_queue_stats_mapping_set, @@ -568,7 +663,7 @@ virtio_dev_atomic_write_link_status(struct rte_eth_dev *dev, } static void -virtio_dev_stats_get(struct rte_eth_dev *dev, struct rte_eth_stats *stats) +virtio_update_stats(struct rte_eth_dev *dev, struct rte_eth_stats *stats) { unsigned i; @@ -605,6 +700,64 @@ virtio_dev_stats_get(struct rte_eth_dev *dev, struct rte_eth_stats *stats) stats->rx_nombuf = dev->data->rx_mbuf_alloc_failed; } +static int +virtio_dev_xstats_get(struct rte_eth_dev *dev, struct rte_eth_xstats *xstats, + unsigned n) +{ + unsigned i; + unsigned count = 0; + + unsigned nstats = dev->data->nb_tx_queues * VIRTIO_NB_Q_XSTATS + + dev->data->nb_rx_queues * VIRTIO_NB_Q_XSTATS; + + if (n < nstats) + return nstats; + + for (i = 0; i < dev->data->nb_rx_queues; i++) { + struct virtqueue *rxvq = dev->data->rx_queues[i]; + + if (rxvq == NULL) + continue; + + unsigned t; + + for (t = 0; t < VIRTIO_NB_Q_XSTATS; t++) { + snprintf(xstats[count].name, sizeof(xstats[count].name), + "rx_q%u_%s", i, + rte_virtio_q_stat_strings[t].name); + xstats[count].value = *(uint64_t *)(((char *)rxvq) + + rte_virtio_q_stat_strings[t].offset); + count++; + } + } + + for (i = 0; i < dev->data->nb_tx_queues; i++) { + struct virtqueue *txvq = dev->data->tx_queues[i]; + + if (txvq == NULL) + continue; + + unsigned t; + + for (t = 0; t < VIRTIO_NB_Q_XSTATS; t++) { + snprintf(xstats[count].name, sizeof(xstats[count].name), + "tx_q%u_%s", i, + rte_virtio_q_stat_strings[t].name); + xstats[count].value = *(uint64_t *)(((char *)txvq) + + rte_virtio_q_stat_strings[t].offset); + count++; + } + } + + return count; +} + +static void +virtio_dev_stats_get(struct rte_eth_dev *dev, struct rte_eth_stats *stats) +{ + virtio_update_stats(dev, stats); +} + static void virtio_dev_stats_reset(struct rte_eth_dev *dev) { @@ -618,6 +771,9 @@ virtio_dev_stats_reset(struct rte_eth_dev *dev) txvq->packets = 0; txvq->bytes = 0; txvq->errors = 0; + txvq->multicast = 0; + txvq->broadcast = 0; + memset(txvq->size_bins, 0, sizeof(txvq->size_bins[0]) * 8); } for (i = 0; i < dev->data->nb_rx_queues; i++) { @@ -628,6 +784,9 @@ virtio_dev_stats_reset(struct rte_eth_dev *dev) rxvq->packets = 0; rxvq->bytes = 0; rxvq->errors = 0; + rxvq->multicast = 0; + rxvq->broadcast = 0; + memset(rxvq->size_bins, 0, sizeof(rxvq->size_bins[0]) * 8); } dev->data->rx_mbuf_alloc_failed = 0; @@ -654,7 +813,7 @@ virtio_get_hwaddr(struct virtio_hw *hw) } } -static int +static void virtio_mac_table_set(struct virtio_hw *hw, const struct virtio_net_ctrl_mac *uc, const struct virtio_net_ctrl_mac *mc) @@ -662,6 +821,11 @@ virtio_mac_table_set(struct virtio_hw *hw, struct virtio_pmd_ctrl ctrl; int err, len[2]; + if (!vtpci_with_feature(hw, VIRTIO_NET_F_CTRL_MAC_ADDR)) { + PMD_DRV_LOG(INFO, "host does not support mac table\n"); + return; + } + ctrl.hdr.class = VIRTIO_NET_CTRL_MAC; ctrl.hdr.cmd = VIRTIO_NET_CTRL_MAC_TABLE_SET; @@ -674,8 +838,6 @@ virtio_mac_table_set(struct virtio_hw *hw, err = virtio_send_command(hw->cvq, &ctrl, len, 2); if (err != 0) PMD_DRV_LOG(NOTICE, "mac table set failed: %d", err); - - return err; } static void @@ -782,23 +944,10 @@ virtio_vlan_filter_set(struct rte_eth_dev *dev, uint16_t vlan_id, int on) static void virtio_negotiate_features(struct virtio_hw *hw) { - uint32_t host_features, mask; - - /* checksum offload not implemented */ - mask = VIRTIO_NET_F_CSUM | VIRTIO_NET_F_GUEST_CSUM; - - /* TSO and LRO are only available when their corresponding - * checksum offload feature is also negotiated. - */ - mask |= VIRTIO_NET_F_HOST_TSO4 | VIRTIO_NET_F_HOST_TSO6 | VIRTIO_NET_F_HOST_ECN; - mask |= VIRTIO_NET_F_GUEST_TSO4 | VIRTIO_NET_F_GUEST_TSO6 | VIRTIO_NET_F_GUEST_ECN; - mask |= VTNET_LRO_FEATURES; - - /* not negotiating INDIRECT descriptor table support */ - mask |= VIRTIO_RING_F_INDIRECT_DESC; + uint32_t host_features; /* Prepare guest_features: feature that driver wants to support */ - hw->guest_features = VTNET_FEATURES & ~mask; + hw->guest_features = VIRTIO_PMD_GUEST_FEATURES; PMD_INIT_LOG(DEBUG, "guest_features before negotiate = %x", hw->guest_features); @@ -1117,7 +1266,6 @@ eth_virtio_dev_init(struct rte_eth_dev *eth_dev) struct virtio_hw *hw = eth_dev->data->dev_private; struct virtio_net_config *config; struct virtio_net_config local_config; - uint32_t offset_conf = sizeof(config->mac); struct rte_pci_device *pci_dev; RTE_BUILD_BUG_ON(RTE_PKTMBUF_HEADROOM < sizeof(struct virtio_net_hdr)); @@ -1131,15 +1279,18 @@ eth_virtio_dev_init(struct rte_eth_dev *eth_dev) } /* Allocate memory for storing MAC addresses */ - eth_dev->data->mac_addrs = rte_zmalloc("virtio", ETHER_ADDR_LEN, 0); + eth_dev->data->mac_addrs = rte_zmalloc("virtio", VIRTIO_MAX_MAC_ADDRS * ETHER_ADDR_LEN, 0); if (eth_dev->data->mac_addrs == NULL) { PMD_INIT_LOG(ERR, "Failed to allocate %d bytes needed to store MAC addresses", - ETHER_ADDR_LEN); + VIRTIO_MAX_MAC_ADDRS * ETHER_ADDR_LEN); return -ENOMEM; } pci_dev = eth_dev->pci_dev; + + rte_eth_copy_pci_info(eth_dev, pci_dev); + if (virtio_resource_init(pci_dev) < 0) return -1; @@ -1156,6 +1307,10 @@ eth_virtio_dev_init(struct rte_eth_dev *eth_dev) vtpci_set_status(hw, VIRTIO_CONFIG_STATUS_DRIVER); virtio_negotiate_features(hw); + /* If host does not support status then disable LSC */ + if (!vtpci_with_feature(hw, VIRTIO_NET_F_STATUS)) + pci_dev->driver->drv_flags &= ~RTE_PCI_DRV_INTR_LSC; + rx_func_get(eth_dev); /* Setting up rx_header size for the device */ @@ -1176,8 +1331,14 @@ eth_virtio_dev_init(struct rte_eth_dev *eth_dev) if (vtpci_with_feature(hw, VIRTIO_NET_F_CTRL_VQ)) { config = &local_config; + vtpci_read_dev_config(hw, + offsetof(struct virtio_net_config, mac), + &config->mac, sizeof(config->mac)); + if (vtpci_with_feature(hw, VIRTIO_NET_F_STATUS)) { - offset_conf += sizeof(config->status); + vtpci_read_dev_config(hw, + offsetof(struct virtio_net_config, status), + &config->status, sizeof(config->status)); } else { PMD_INIT_LOG(DEBUG, "VIRTIO_NET_F_STATUS is not supported"); @@ -1185,15 +1346,16 @@ eth_virtio_dev_init(struct rte_eth_dev *eth_dev) } if (vtpci_with_feature(hw, VIRTIO_NET_F_MQ)) { - offset_conf += sizeof(config->max_virtqueue_pairs); + vtpci_read_dev_config(hw, + offsetof(struct virtio_net_config, max_virtqueue_pairs), + &config->max_virtqueue_pairs, + sizeof(config->max_virtqueue_pairs)); } else { PMD_INIT_LOG(DEBUG, "VIRTIO_NET_F_MQ is not supported"); config->max_virtqueue_pairs = 1; } - vtpci_read_dev_config(hw, 0, (uint8_t *)config, offset_conf); - hw->max_rx_queues = (VIRTIO_MAX_RX_QUEUES < config->max_virtqueue_pairs) ? VIRTIO_MAX_RX_QUEUES : config->max_virtqueue_pairs; @@ -1237,12 +1399,51 @@ eth_virtio_dev_init(struct rte_eth_dev *eth_dev) return 0; } +static int +eth_virtio_dev_uninit(struct rte_eth_dev *eth_dev) +{ + struct rte_pci_device *pci_dev; + struct virtio_hw *hw = eth_dev->data->dev_private; + + PMD_INIT_FUNC_TRACE(); + + if (rte_eal_process_type() == RTE_PROC_SECONDARY) + return -EPERM; + + if (hw->started == 1) { + virtio_dev_stop(eth_dev); + virtio_dev_close(eth_dev); + } + pci_dev = eth_dev->pci_dev; + + eth_dev->dev_ops = NULL; + eth_dev->tx_pkt_burst = NULL; + eth_dev->rx_pkt_burst = NULL; + + virtio_dev_queue_release(hw->cvq); + + rte_free(eth_dev->data->mac_addrs); + eth_dev->data->mac_addrs = NULL; + + /* reset interrupt callback */ + if (pci_dev->driver->drv_flags & RTE_PCI_DRV_INTR_LSC) + rte_intr_callback_unregister(&pci_dev->intr_handle, + virtio_interrupt_handler, + eth_dev); + + PMD_INIT_LOG(DEBUG, "dev_uninit completed"); + + return 0; +} + static struct eth_driver rte_virtio_pmd = { - { + .pci_drv = { .name = "rte_virtio_pmd", .id_table = pci_id_virtio_map, + .drv_flags = RTE_PCI_DRV_DETACHABLE, }, .eth_dev_init = eth_virtio_dev_init, + .eth_dev_uninit = eth_virtio_dev_uninit, .dev_private_size = sizeof(struct virtio_hw), }; @@ -1265,19 +1466,6 @@ rte_virtio_pmd_init(const char *name __rte_unused, return 0; } -/* - * Only 1 queue is supported, no queue release related operation - */ -static void -virtio_dev_rx_queue_release(__rte_unused void *rxq) -{ -} - -static void -virtio_dev_tx_queue_release(__rte_unused void *txq) -{ -} - /* * Configure virtio device * It returns 0 on success. @@ -1323,9 +1511,8 @@ virtio_dev_start(struct rte_eth_dev *dev) struct rte_pci_device *pci_dev = dev->pci_dev; /* check if lsc interrupt feature is enabled */ - if ((dev->data->dev_conf.intr_conf.lsc) && - (pci_dev->driver->drv_flags & RTE_PCI_DRV_INTR_LSC)) { - if (!vtpci_with_feature(hw, VIRTIO_NET_F_STATUS)) { + if (dev->data->dev_conf.intr_conf.lsc) { + if (!(pci_dev->driver->drv_flags & RTE_PCI_DRV_INTR_LSC)) { PMD_DRV_LOG(ERR, "link status not supported by host"); return -ENOTSUP; } @@ -1385,6 +1572,8 @@ static void virtio_dev_free_mbufs(struct rte_eth_dev *dev) "Before freeing rxq[%d] used and unused buf", i); VIRTQUEUE_DUMP((struct virtqueue *)dev->data->rx_queues[i]); + PMD_INIT_LOG(DEBUG, "rx_queues[%d]=%p", + i, dev->data->rx_queues[i]); while ((buf = (struct rte_mbuf *)virtqueue_detatch_unused( dev->data->rx_queues[i])) != NULL) { rte_pktmbuf_free(buf);