/*-
* 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
#include <rte_pci.h>
#include <rte_ether.h>
#include <rte_common.h>
+#include <rte_errno.h>
#include <rte_memory.h>
#include <rte_eal.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);
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_reset(struct rte_eth_dev *dev);
static void virtio_dev_free_mbufs(struct rte_eth_dev *dev);
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",
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);
+ vq = NULL;
+ }
+}
+
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)
uint16_t vq_size;
int 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);
*/
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;
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) {
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
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;
+ }
}
/*
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;
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;
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;
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)
{
vtpci_reset(hw);
hw->started = 0;
virtio_dev_free_mbufs(dev);
+ virtio_free_queues(dev);
}
static void
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;
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;
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;
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;
.stats_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,
}
}
-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)
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;
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
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);
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),
};
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.
"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);