2013-09-18 10:00:00 +00:00
|
|
|
/*-
|
|
|
|
* BSD LICENSE
|
2014-06-03 23:42:50 +00:00
|
|
|
*
|
2016-06-15 15:25:32 +00:00
|
|
|
* Copyright(c) 2010-2016 Intel Corporation. All rights reserved.
|
2013-09-18 10:00:00 +00:00
|
|
|
* All rights reserved.
|
2014-06-03 23:42:50 +00:00
|
|
|
*
|
2013-09-18 10:00:00 +00:00
|
|
|
* Redistribution and use in source and binary forms, with or without
|
|
|
|
* modification, are permitted provided that the following conditions
|
|
|
|
* are met:
|
2014-06-03 23:42:50 +00:00
|
|
|
*
|
2013-09-18 10:00:00 +00:00
|
|
|
* * Redistributions of source code must retain the above copyright
|
|
|
|
* notice, this list of conditions and the following disclaimer.
|
|
|
|
* * Redistributions in binary form must reproduce the above copyright
|
|
|
|
* notice, this list of conditions and the following disclaimer in
|
|
|
|
* the documentation and/or other materials provided with the
|
|
|
|
* distribution.
|
|
|
|
* * Neither the name of Intel Corporation nor the names of its
|
|
|
|
* contributors may be used to endorse or promote products derived
|
|
|
|
* from this software without specific prior written permission.
|
2014-06-03 23:42:50 +00:00
|
|
|
*
|
2013-09-18 10:00:00 +00:00
|
|
|
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
|
|
|
|
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
|
|
|
|
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
|
|
|
|
* A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
|
|
|
|
* OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
|
|
|
|
* SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
|
|
|
|
* LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
|
|
|
|
* DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
|
|
|
|
* THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
|
|
|
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
|
|
|
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
|
|
|
*/
|
|
|
|
|
|
|
|
#include <stdint.h>
|
|
|
|
#include <string.h>
|
|
|
|
#include <stdio.h>
|
|
|
|
#include <errno.h>
|
|
|
|
#include <unistd.h>
|
|
|
|
|
|
|
|
#include <rte_ethdev.h>
|
2017-04-11 15:44:24 +00:00
|
|
|
#include <rte_ethdev_pci.h>
|
2013-09-18 10:00:00 +00:00
|
|
|
#include <rte_memcpy.h>
|
|
|
|
#include <rte_string_fns.h>
|
|
|
|
#include <rte_memzone.h>
|
|
|
|
#include <rte_malloc.h>
|
|
|
|
#include <rte_atomic.h>
|
|
|
|
#include <rte_branch_prediction.h>
|
|
|
|
#include <rte_pci.h>
|
|
|
|
#include <rte_ether.h>
|
|
|
|
#include <rte_common.h>
|
2015-07-15 13:51:00 +00:00
|
|
|
#include <rte_errno.h>
|
2017-09-07 12:13:44 +00:00
|
|
|
#include <rte_cpuflags.h>
|
2013-09-18 10:00:00 +00:00
|
|
|
|
|
|
|
#include <rte_memory.h>
|
|
|
|
#include <rte_eal.h>
|
2014-04-21 14:59:37 +00:00
|
|
|
#include <rte_dev.h>
|
2013-09-18 10:00:00 +00:00
|
|
|
|
|
|
|
#include "virtio_ethdev.h"
|
|
|
|
#include "virtio_pci.h"
|
|
|
|
#include "virtio_logs.h"
|
|
|
|
#include "virtqueue.h"
|
2015-10-29 14:53:22 +00:00
|
|
|
#include "virtio_rxtx.h"
|
2013-09-18 10:00:00 +00:00
|
|
|
|
2015-07-15 13:51:00 +00:00
|
|
|
static int eth_virtio_dev_uninit(struct rte_eth_dev *eth_dev);
|
2013-09-18 10:00:00 +00:00
|
|
|
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);
|
2014-11-08 04:26:15 +00:00
|
|
|
static void virtio_dev_promiscuous_enable(struct rte_eth_dev *dev);
|
|
|
|
static void virtio_dev_promiscuous_disable(struct rte_eth_dev *dev);
|
|
|
|
static void virtio_dev_allmulticast_enable(struct rte_eth_dev *dev);
|
|
|
|
static void virtio_dev_allmulticast_disable(struct rte_eth_dev *dev);
|
2013-09-18 10:00:00 +00:00
|
|
|
static void virtio_dev_info_get(struct rte_eth_dev *dev,
|
|
|
|
struct rte_eth_dev_info *dev_info);
|
|
|
|
static int virtio_dev_link_update(struct rte_eth_dev *dev,
|
2017-05-12 10:33:03 +00:00
|
|
|
int wait_to_complete);
|
2013-09-18 10:00:00 +00:00
|
|
|
|
|
|
|
static void virtio_set_hwaddr(struct virtio_hw *hw);
|
|
|
|
static void virtio_get_hwaddr(struct virtio_hw *hw);
|
|
|
|
|
2017-10-10 20:20:18 +00:00
|
|
|
static int virtio_dev_stats_get(struct rte_eth_dev *dev,
|
2015-11-02 10:19:00 +00:00
|
|
|
struct rte_eth_stats *stats);
|
|
|
|
static int virtio_dev_xstats_get(struct rte_eth_dev *dev,
|
2016-06-15 15:25:33 +00:00
|
|
|
struct rte_eth_xstat *xstats, unsigned n);
|
2016-06-15 15:25:32 +00:00
|
|
|
static int virtio_dev_xstats_get_names(struct rte_eth_dev *dev,
|
|
|
|
struct rte_eth_xstat_name *xstats_names,
|
|
|
|
unsigned limit);
|
2013-09-18 10:00:00 +00:00
|
|
|
static void virtio_dev_stats_reset(struct rte_eth_dev *dev);
|
|
|
|
static void virtio_dev_free_mbufs(struct rte_eth_dev *dev);
|
2015-02-09 01:14:02 +00:00
|
|
|
static int virtio_vlan_filter_set(struct rte_eth_dev *dev,
|
|
|
|
uint16_t vlan_id, int on);
|
2017-05-05 00:40:00 +00:00
|
|
|
static int virtio_mac_addr_add(struct rte_eth_dev *dev,
|
2015-02-09 01:14:03 +00:00
|
|
|
struct ether_addr *mac_addr,
|
2017-05-12 10:33:03 +00:00
|
|
|
uint32_t index, uint32_t vmdq);
|
2015-02-09 01:14:03 +00:00
|
|
|
static void virtio_mac_addr_remove(struct rte_eth_dev *dev, uint32_t index);
|
2015-02-09 01:14:04 +00:00
|
|
|
static void virtio_mac_addr_set(struct rte_eth_dev *dev,
|
|
|
|
struct ether_addr *mac_addr);
|
2013-09-18 10:00:00 +00:00
|
|
|
|
2014-05-29 07:18:20 +00:00
|
|
|
static int virtio_dev_queue_stats_mapping_set(
|
2017-05-12 10:33:03 +00:00
|
|
|
struct rte_eth_dev *eth_dev,
|
|
|
|
uint16_t queue_id,
|
|
|
|
uint8_t stat_idx,
|
|
|
|
uint8_t is_rx);
|
2014-05-29 07:18:20 +00:00
|
|
|
|
2013-09-18 10:00:00 +00:00
|
|
|
/*
|
|
|
|
* The set of PCI devices this driver supports
|
|
|
|
*/
|
2015-04-16 23:23:39 +00:00
|
|
|
static const struct rte_pci_id pci_id_virtio_map[] = {
|
2016-09-28 08:25:11 +00:00
|
|
|
{ RTE_PCI_DEVICE(VIRTIO_PCI_VENDORID, VIRTIO_PCI_LEGACY_DEVICEID_NET) },
|
|
|
|
{ RTE_PCI_DEVICE(VIRTIO_PCI_VENDORID, VIRTIO_PCI_MODERN_DEVICEID_NET) },
|
2016-07-11 14:40:40 +00:00
|
|
|
{ .vendor_id = 0, /* sentinel */ },
|
2013-09-18 10:00:00 +00:00
|
|
|
};
|
|
|
|
|
2015-11-02 10:19:00 +00:00
|
|
|
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 */
|
2016-06-01 16:12:13 +00:00
|
|
|
static const struct rte_virtio_xstats_name_off rte_virtio_rxq_stat_strings[] = {
|
|
|
|
{"good_packets", offsetof(struct virtnet_rx, stats.packets)},
|
|
|
|
{"good_bytes", offsetof(struct virtnet_rx, stats.bytes)},
|
|
|
|
{"errors", offsetof(struct virtnet_rx, stats.errors)},
|
|
|
|
{"multicast_packets", offsetof(struct virtnet_rx, stats.multicast)},
|
|
|
|
{"broadcast_packets", offsetof(struct virtnet_rx, stats.broadcast)},
|
|
|
|
{"undersize_packets", offsetof(struct virtnet_rx, stats.size_bins[0])},
|
|
|
|
{"size_64_packets", offsetof(struct virtnet_rx, stats.size_bins[1])},
|
|
|
|
{"size_65_127_packets", offsetof(struct virtnet_rx, stats.size_bins[2])},
|
|
|
|
{"size_128_255_packets", offsetof(struct virtnet_rx, stats.size_bins[3])},
|
|
|
|
{"size_256_511_packets", offsetof(struct virtnet_rx, stats.size_bins[4])},
|
|
|
|
{"size_512_1023_packets", offsetof(struct virtnet_rx, stats.size_bins[5])},
|
2016-09-07 06:11:00 +00:00
|
|
|
{"size_1024_1518_packets", offsetof(struct virtnet_rx, stats.size_bins[6])},
|
|
|
|
{"size_1519_max_packets", offsetof(struct virtnet_rx, stats.size_bins[7])},
|
2015-11-02 10:19:00 +00:00
|
|
|
};
|
|
|
|
|
2016-06-01 16:12:13 +00:00
|
|
|
/* [rt]x_qX_ is prepended to the name string here */
|
|
|
|
static const struct rte_virtio_xstats_name_off rte_virtio_txq_stat_strings[] = {
|
|
|
|
{"good_packets", offsetof(struct virtnet_tx, stats.packets)},
|
|
|
|
{"good_bytes", offsetof(struct virtnet_tx, stats.bytes)},
|
|
|
|
{"errors", offsetof(struct virtnet_tx, stats.errors)},
|
|
|
|
{"multicast_packets", offsetof(struct virtnet_tx, stats.multicast)},
|
|
|
|
{"broadcast_packets", offsetof(struct virtnet_tx, stats.broadcast)},
|
|
|
|
{"undersize_packets", offsetof(struct virtnet_tx, stats.size_bins[0])},
|
|
|
|
{"size_64_packets", offsetof(struct virtnet_tx, stats.size_bins[1])},
|
|
|
|
{"size_65_127_packets", offsetof(struct virtnet_tx, stats.size_bins[2])},
|
|
|
|
{"size_128_255_packets", offsetof(struct virtnet_tx, stats.size_bins[3])},
|
|
|
|
{"size_256_511_packets", offsetof(struct virtnet_tx, stats.size_bins[4])},
|
|
|
|
{"size_512_1023_packets", offsetof(struct virtnet_tx, stats.size_bins[5])},
|
2016-09-07 06:11:00 +00:00
|
|
|
{"size_1024_1518_packets", offsetof(struct virtnet_tx, stats.size_bins[6])},
|
|
|
|
{"size_1519_max_packets", offsetof(struct virtnet_tx, stats.size_bins[7])},
|
2016-06-01 16:12:13 +00:00
|
|
|
};
|
|
|
|
|
|
|
|
#define VIRTIO_NB_RXQ_XSTATS (sizeof(rte_virtio_rxq_stat_strings) / \
|
|
|
|
sizeof(rte_virtio_rxq_stat_strings[0]))
|
|
|
|
#define VIRTIO_NB_TXQ_XSTATS (sizeof(rte_virtio_txq_stat_strings) / \
|
|
|
|
sizeof(rte_virtio_txq_stat_strings[0]))
|
2015-11-02 10:19:00 +00:00
|
|
|
|
net/virtio: store PCI operators pointer locally
We used to store the vtpci_ops at virtio_hw structure. The struct,
however, is stored in shared memory. That means only one value is
allowed. For the multiple process model, however, the address of
vtpci_ops should be different among different processes.
Take virtio PMD as example, the vtpci_ops is set by the primary
process, based on its own process space. If we access that address
from the secondary process, that would be an illegal memory access,
A crash then might happen.
To make the multiple process model work, we need store the vtpci_ops
in local memory but not in a shared memory. This is what the patch
does: a local virtio_hw_internal array of size RTE_MAX_ETHPORTS is
allocated. This new structure is used to store all these kind of
info in a non-shared memory. Current, we have:
- vtpci_ops
- rte_pci_ioport
- virtio pci mapped memory, such as common_cfg.
The later two will be done in coming patches. Later patches would also
set them correctly for secondary process, so that the multiple process
model could work.
Signed-off-by: Yuanhan Liu <yuanhan.liu@linux.intel.com>
2017-01-06 10:16:17 +00:00
|
|
|
struct virtio_hw_internal virtio_hw_internal[RTE_MAX_ETHPORTS];
|
|
|
|
|
2014-05-29 07:18:20 +00:00
|
|
|
static int
|
2016-06-01 16:12:13 +00:00
|
|
|
virtio_send_command(struct virtnet_ctl *cvq, struct virtio_pmd_ctrl *ctrl,
|
2014-05-29 07:18:20 +00:00
|
|
|
int *dlen, int pkt_num)
|
|
|
|
{
|
2015-05-25 10:20:52 +00:00
|
|
|
uint32_t head, i;
|
2014-05-29 07:18:20 +00:00
|
|
|
int k, sum = 0;
|
|
|
|
virtio_net_ctrl_ack status = ~0;
|
2017-08-11 02:13:18 +00:00
|
|
|
struct virtio_pmd_ctrl *result;
|
2016-06-01 16:12:13 +00:00
|
|
|
struct virtqueue *vq;
|
2014-05-29 07:18:20 +00:00
|
|
|
|
|
|
|
ctrl->status = status;
|
|
|
|
|
2016-07-05 11:42:59 +00:00
|
|
|
if (!cvq || !cvq->vq) {
|
2016-02-10 16:08:54 +00:00
|
|
|
PMD_INIT_LOG(ERR, "Control queue is not supported.");
|
2014-05-29 07:18:20 +00:00
|
|
|
return -1;
|
|
|
|
}
|
2016-06-01 16:12:13 +00:00
|
|
|
vq = cvq->vq;
|
2015-05-25 10:20:52 +00:00
|
|
|
head = vq->vq_desc_head_idx;
|
2014-05-29 07:18:20 +00:00
|
|
|
|
|
|
|
PMD_INIT_LOG(DEBUG, "vq->vq_desc_head_idx = %d, status = %d, "
|
2014-06-14 01:06:19 +00:00
|
|
|
"vq->hw->cvq = %p vq = %p",
|
2014-05-29 07:18:20 +00:00
|
|
|
vq->vq_desc_head_idx, status, vq->hw->cvq, vq);
|
|
|
|
|
|
|
|
if ((vq->vq_free_cnt < ((uint32_t)pkt_num + 2)) || (pkt_num < 1))
|
|
|
|
return -1;
|
|
|
|
|
2016-06-01 16:12:13 +00:00
|
|
|
memcpy(cvq->virtio_net_hdr_mz->addr, ctrl,
|
2014-05-29 07:18:20 +00:00
|
|
|
sizeof(struct virtio_pmd_ctrl));
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Format is enforced in qemu code:
|
|
|
|
* One TX packet for header;
|
|
|
|
* At least one TX packet per argument;
|
|
|
|
* One RX packet for ACK.
|
|
|
|
*/
|
|
|
|
vq->vq_ring.desc[head].flags = VRING_DESC_F_NEXT;
|
net/virtio: allow virtual address to fill vring descriptors
This patch is related to how to calculate relative address for vhost
backend.
The principle is that: based on one or multiple shared memory regions,
vhost maintains a reference system with the frontend start address,
backend start address, and length for each segment, so that each
frontend address (GPA, Guest Physical Address) can be translated into
vhost-recognizable backend address. To make the address translation
efficient, we need to maintain as few regions as possible. In the case
of VM, GPA is always locally continuous. But for some other case, like
virtio-user, GPA continuous is not guaranteed, therefore, we use virtual
address here.
It basically means:
a. when set_base_addr, VA address is used;
b. when preparing RX's descriptors, VA address is used;
c. when transmitting packets, VA is filled in TX's descriptors;
d. in TX and CQ's header, VA is used.
Signed-off-by: Huawei Xie <huawei.xie@intel.com>
Signed-off-by: Jianfeng Tan <jianfeng.tan@intel.com>
Acked-by: Neil Horman <nhorman@tuxdriver.com>
Acked-by: Yuanhan Liu <yuanhan.liu@linux.intel.com>
2016-06-15 09:03:21 +00:00
|
|
|
vq->vq_ring.desc[head].addr = cvq->virtio_net_hdr_mem;
|
2014-05-29 07:18:20 +00:00
|
|
|
vq->vq_ring.desc[head].len = sizeof(struct virtio_net_ctrl_hdr);
|
|
|
|
vq->vq_free_cnt--;
|
|
|
|
i = vq->vq_ring.desc[head].next;
|
|
|
|
|
|
|
|
for (k = 0; k < pkt_num; k++) {
|
|
|
|
vq->vq_ring.desc[i].flags = VRING_DESC_F_NEXT;
|
net/virtio: allow virtual address to fill vring descriptors
This patch is related to how to calculate relative address for vhost
backend.
The principle is that: based on one or multiple shared memory regions,
vhost maintains a reference system with the frontend start address,
backend start address, and length for each segment, so that each
frontend address (GPA, Guest Physical Address) can be translated into
vhost-recognizable backend address. To make the address translation
efficient, we need to maintain as few regions as possible. In the case
of VM, GPA is always locally continuous. But for some other case, like
virtio-user, GPA continuous is not guaranteed, therefore, we use virtual
address here.
It basically means:
a. when set_base_addr, VA address is used;
b. when preparing RX's descriptors, VA address is used;
c. when transmitting packets, VA is filled in TX's descriptors;
d. in TX and CQ's header, VA is used.
Signed-off-by: Huawei Xie <huawei.xie@intel.com>
Signed-off-by: Jianfeng Tan <jianfeng.tan@intel.com>
Acked-by: Neil Horman <nhorman@tuxdriver.com>
Acked-by: Yuanhan Liu <yuanhan.liu@linux.intel.com>
2016-06-15 09:03:21 +00:00
|
|
|
vq->vq_ring.desc[i].addr = cvq->virtio_net_hdr_mem
|
2014-05-29 07:18:20 +00:00
|
|
|
+ sizeof(struct virtio_net_ctrl_hdr)
|
|
|
|
+ sizeof(ctrl->status) + sizeof(uint8_t)*sum;
|
|
|
|
vq->vq_ring.desc[i].len = dlen[k];
|
|
|
|
sum += dlen[k];
|
|
|
|
vq->vq_free_cnt--;
|
|
|
|
i = vq->vq_ring.desc[i].next;
|
|
|
|
}
|
|
|
|
|
|
|
|
vq->vq_ring.desc[i].flags = VRING_DESC_F_WRITE;
|
net/virtio: allow virtual address to fill vring descriptors
This patch is related to how to calculate relative address for vhost
backend.
The principle is that: based on one or multiple shared memory regions,
vhost maintains a reference system with the frontend start address,
backend start address, and length for each segment, so that each
frontend address (GPA, Guest Physical Address) can be translated into
vhost-recognizable backend address. To make the address translation
efficient, we need to maintain as few regions as possible. In the case
of VM, GPA is always locally continuous. But for some other case, like
virtio-user, GPA continuous is not guaranteed, therefore, we use virtual
address here.
It basically means:
a. when set_base_addr, VA address is used;
b. when preparing RX's descriptors, VA address is used;
c. when transmitting packets, VA is filled in TX's descriptors;
d. in TX and CQ's header, VA is used.
Signed-off-by: Huawei Xie <huawei.xie@intel.com>
Signed-off-by: Jianfeng Tan <jianfeng.tan@intel.com>
Acked-by: Neil Horman <nhorman@tuxdriver.com>
Acked-by: Yuanhan Liu <yuanhan.liu@linux.intel.com>
2016-06-15 09:03:21 +00:00
|
|
|
vq->vq_ring.desc[i].addr = cvq->virtio_net_hdr_mem
|
2014-05-29 07:18:20 +00:00
|
|
|
+ sizeof(struct virtio_net_ctrl_hdr);
|
|
|
|
vq->vq_ring.desc[i].len = sizeof(ctrl->status);
|
|
|
|
vq->vq_free_cnt--;
|
|
|
|
|
|
|
|
vq->vq_desc_head_idx = vq->vq_ring.desc[i].next;
|
|
|
|
|
|
|
|
vq_update_avail_ring(vq, head);
|
|
|
|
vq_update_avail_idx(vq);
|
|
|
|
|
2014-06-14 01:06:19 +00:00
|
|
|
PMD_INIT_LOG(DEBUG, "vq->vq_queue_index = %d", vq->vq_queue_index);
|
2014-05-29 07:18:20 +00:00
|
|
|
|
|
|
|
virtqueue_notify(vq);
|
|
|
|
|
2015-02-09 01:14:05 +00:00
|
|
|
rte_rmb();
|
2016-06-19 17:48:52 +00:00
|
|
|
while (VIRTQUEUE_NUSED(vq) == 0) {
|
2015-02-09 01:14:05 +00:00
|
|
|
rte_rmb();
|
2014-05-29 07:18:20 +00:00
|
|
|
usleep(100);
|
2015-02-09 01:14:05 +00:00
|
|
|
}
|
2014-05-29 07:18:20 +00:00
|
|
|
|
2016-06-19 17:48:52 +00:00
|
|
|
while (VIRTQUEUE_NUSED(vq)) {
|
2014-05-29 07:18:20 +00:00
|
|
|
uint32_t idx, desc_idx, used_idx;
|
|
|
|
struct vring_used_elem *uep;
|
|
|
|
|
|
|
|
used_idx = (uint32_t)(vq->vq_used_cons_idx
|
|
|
|
& (vq->vq_nentries - 1));
|
|
|
|
uep = &vq->vq_ring.used->ring[used_idx];
|
|
|
|
idx = (uint32_t) uep->id;
|
|
|
|
desc_idx = idx;
|
|
|
|
|
|
|
|
while (vq->vq_ring.desc[desc_idx].flags & VRING_DESC_F_NEXT) {
|
|
|
|
desc_idx = vq->vq_ring.desc[desc_idx].next;
|
|
|
|
vq->vq_free_cnt++;
|
|
|
|
}
|
|
|
|
|
|
|
|
vq->vq_ring.desc[desc_idx].next = vq->vq_desc_head_idx;
|
|
|
|
vq->vq_desc_head_idx = idx;
|
|
|
|
|
|
|
|
vq->vq_used_cons_idx++;
|
|
|
|
vq->vq_free_cnt++;
|
|
|
|
}
|
|
|
|
|
2014-06-14 01:06:19 +00:00
|
|
|
PMD_INIT_LOG(DEBUG, "vq->vq_free_cnt=%d\nvq->vq_desc_head_idx=%d",
|
2014-05-29 07:18:20 +00:00
|
|
|
vq->vq_free_cnt, vq->vq_desc_head_idx);
|
|
|
|
|
2017-08-11 02:13:18 +00:00
|
|
|
result = cvq->virtio_net_hdr_mz->addr;
|
2014-05-29 07:18:20 +00:00
|
|
|
|
2017-08-11 02:13:18 +00:00
|
|
|
return result->status;
|
2014-05-29 07:18:20 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
static int
|
|
|
|
virtio_set_multiple_queues(struct rte_eth_dev *dev, uint16_t nb_queues)
|
|
|
|
{
|
2015-02-09 01:13:56 +00:00
|
|
|
struct virtio_hw *hw = dev->data->dev_private;
|
2014-05-29 07:18:20 +00:00
|
|
|
struct virtio_pmd_ctrl ctrl;
|
|
|
|
int dlen[1];
|
|
|
|
int ret;
|
|
|
|
|
|
|
|
ctrl.hdr.class = VIRTIO_NET_CTRL_MQ;
|
|
|
|
ctrl.hdr.cmd = VIRTIO_NET_CTRL_MQ_VQ_PAIRS_SET;
|
|
|
|
memcpy(ctrl.data, &nb_queues, sizeof(uint16_t));
|
|
|
|
|
|
|
|
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 "
|
2014-06-14 01:06:19 +00:00
|
|
|
"failed, this is too late now...");
|
2014-05-29 07:18:20 +00:00
|
|
|
return -EINVAL;
|
|
|
|
}
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
net/virtio: allocate queue at init stage
Queue allocation should be done once, since the queue related info (such
as vring addreess) will only be informed to the vhost-user backend once
without virtio device reset.
That means, if you allocate queues again after the vhost-user negotiation,
the vhost-user backend will not be informed any more. Leading to a state
that the vring info mismatches between virtio PMD driver and vhost-backend:
the driver switches to the new address has just been allocated, while the
vhost-backend still sticks to the old address has been assigned in the init
stage.
Unfortunately, that is exactly how the virtio driver is coded so far: queue
allocation is done at queue_setup stage (when rte_eth_tx/rx_queue_setup is
invoked). This is wrong, because queue_setup can be invoked several times.
For example,
$ start_testpmd.sh ... --txq=1 --rxq=1 ...
> port stop 0
> port config all txq 1 # just trigger the queue_setup callback again
> port config all rxq 1
> port start 0
The right way to do is allocate the queues in the init stage, so that the
vring info could be persistent with the vhost-user backend.
Besides that, we should allocate max_queue pairs the device supports, but
not nr queue pairs firstly configured, to make following case work.
$ start_testpmd.sh ... --txq=1 --rxq=1 ...
> port stop 0
> port config all txq 2
> port config all rxq 2
> port start 0
Since the allocation is switched to init stage, the free should also
moved from the rx/tx_queue_release to dev close stage. That leading we
could do nothing an empty rx/tx_queue_release() implementation.
Signed-off-by: Yuanhan Liu <yuanhan.liu@linux.intel.com>
2016-11-05 09:40:59 +00:00
|
|
|
static void
|
|
|
|
virtio_dev_queue_release(void *queue __rte_unused)
|
2016-04-29 00:48:46 +00:00
|
|
|
{
|
net/virtio: allocate queue at init stage
Queue allocation should be done once, since the queue related info (such
as vring addreess) will only be informed to the vhost-user backend once
without virtio device reset.
That means, if you allocate queues again after the vhost-user negotiation,
the vhost-user backend will not be informed any more. Leading to a state
that the vring info mismatches between virtio PMD driver and vhost-backend:
the driver switches to the new address has just been allocated, while the
vhost-backend still sticks to the old address has been assigned in the init
stage.
Unfortunately, that is exactly how the virtio driver is coded so far: queue
allocation is done at queue_setup stage (when rte_eth_tx/rx_queue_setup is
invoked). This is wrong, because queue_setup can be invoked several times.
For example,
$ start_testpmd.sh ... --txq=1 --rxq=1 ...
> port stop 0
> port config all txq 1 # just trigger the queue_setup callback again
> port config all rxq 1
> port start 0
The right way to do is allocate the queues in the init stage, so that the
vring info could be persistent with the vhost-user backend.
Besides that, we should allocate max_queue pairs the device supports, but
not nr queue pairs firstly configured, to make following case work.
$ start_testpmd.sh ... --txq=1 --rxq=1 ...
> port stop 0
> port config all txq 2
> port config all rxq 2
> port start 0
Since the allocation is switched to init stage, the free should also
moved from the rx/tx_queue_release to dev close stage. That leading we
could do nothing an empty rx/tx_queue_release() implementation.
Signed-off-by: Yuanhan Liu <yuanhan.liu@linux.intel.com>
2016-11-05 09:40:59 +00:00
|
|
|
/* do nothing */
|
|
|
|
}
|
2015-07-15 13:51:02 +00:00
|
|
|
|
net/virtio: allocate queue at init stage
Queue allocation should be done once, since the queue related info (such
as vring addreess) will only be informed to the vhost-user backend once
without virtio device reset.
That means, if you allocate queues again after the vhost-user negotiation,
the vhost-user backend will not be informed any more. Leading to a state
that the vring info mismatches between virtio PMD driver and vhost-backend:
the driver switches to the new address has just been allocated, while the
vhost-backend still sticks to the old address has been assigned in the init
stage.
Unfortunately, that is exactly how the virtio driver is coded so far: queue
allocation is done at queue_setup stage (when rte_eth_tx/rx_queue_setup is
invoked). This is wrong, because queue_setup can be invoked several times.
For example,
$ start_testpmd.sh ... --txq=1 --rxq=1 ...
> port stop 0
> port config all txq 1 # just trigger the queue_setup callback again
> port config all rxq 1
> port start 0
The right way to do is allocate the queues in the init stage, so that the
vring info could be persistent with the vhost-user backend.
Besides that, we should allocate max_queue pairs the device supports, but
not nr queue pairs firstly configured, to make following case work.
$ start_testpmd.sh ... --txq=1 --rxq=1 ...
> port stop 0
> port config all txq 2
> port config all rxq 2
> port start 0
Since the allocation is switched to init stage, the free should also
moved from the rx/tx_queue_release to dev close stage. That leading we
could do nothing an empty rx/tx_queue_release() implementation.
Signed-off-by: Yuanhan Liu <yuanhan.liu@linux.intel.com>
2016-11-05 09:40:59 +00:00
|
|
|
static int
|
|
|
|
virtio_get_queue_type(struct virtio_hw *hw, uint16_t vtpci_queue_idx)
|
|
|
|
{
|
|
|
|
if (vtpci_queue_idx == hw->max_queue_pairs * 2)
|
|
|
|
return VTNET_CQ;
|
|
|
|
else if (vtpci_queue_idx % 2 == 0)
|
|
|
|
return VTNET_RQ;
|
|
|
|
else
|
|
|
|
return VTNET_TQ;
|
|
|
|
}
|
2016-04-29 00:48:46 +00:00
|
|
|
|
net/virtio: allocate queue at init stage
Queue allocation should be done once, since the queue related info (such
as vring addreess) will only be informed to the vhost-user backend once
without virtio device reset.
That means, if you allocate queues again after the vhost-user negotiation,
the vhost-user backend will not be informed any more. Leading to a state
that the vring info mismatches between virtio PMD driver and vhost-backend:
the driver switches to the new address has just been allocated, while the
vhost-backend still sticks to the old address has been assigned in the init
stage.
Unfortunately, that is exactly how the virtio driver is coded so far: queue
allocation is done at queue_setup stage (when rte_eth_tx/rx_queue_setup is
invoked). This is wrong, because queue_setup can be invoked several times.
For example,
$ start_testpmd.sh ... --txq=1 --rxq=1 ...
> port stop 0
> port config all txq 1 # just trigger the queue_setup callback again
> port config all rxq 1
> port start 0
The right way to do is allocate the queues in the init stage, so that the
vring info could be persistent with the vhost-user backend.
Besides that, we should allocate max_queue pairs the device supports, but
not nr queue pairs firstly configured, to make following case work.
$ start_testpmd.sh ... --txq=1 --rxq=1 ...
> port stop 0
> port config all txq 2
> port config all rxq 2
> port start 0
Since the allocation is switched to init stage, the free should also
moved from the rx/tx_queue_release to dev close stage. That leading we
could do nothing an empty rx/tx_queue_release() implementation.
Signed-off-by: Yuanhan Liu <yuanhan.liu@linux.intel.com>
2016-11-05 09:40:59 +00:00
|
|
|
static uint16_t
|
|
|
|
virtio_get_nr_vq(struct virtio_hw *hw)
|
|
|
|
{
|
|
|
|
uint16_t nr_vq = hw->max_queue_pairs * 2;
|
|
|
|
|
|
|
|
if (vtpci_with_feature(hw, VIRTIO_NET_F_CTRL_VQ))
|
|
|
|
nr_vq += 1;
|
|
|
|
|
|
|
|
return nr_vq;
|
2015-07-15 13:51:02 +00:00
|
|
|
}
|
|
|
|
|
2016-11-05 09:41:00 +00:00
|
|
|
static void
|
|
|
|
virtio_init_vring(struct virtqueue *vq)
|
|
|
|
{
|
|
|
|
int size = vq->vq_nentries;
|
|
|
|
struct vring *vr = &vq->vq_ring;
|
|
|
|
uint8_t *ring_mem = vq->vq_ring_virt_mem;
|
|
|
|
|
|
|
|
PMD_INIT_FUNC_TRACE();
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Reinitialise since virtio port might have been stopped and restarted
|
|
|
|
*/
|
|
|
|
memset(ring_mem, 0, vq->vq_ring_size);
|
|
|
|
vring_init(vr, size, ring_mem, VIRTIO_PCI_VRING_ALIGN);
|
|
|
|
vq->vq_used_cons_idx = 0;
|
|
|
|
vq->vq_desc_head_idx = 0;
|
|
|
|
vq->vq_avail_idx = 0;
|
|
|
|
vq->vq_desc_tail_idx = (uint16_t)(vq->vq_nentries - 1);
|
|
|
|
vq->vq_free_cnt = vq->vq_nentries;
|
|
|
|
memset(vq->vq_descx, 0, sizeof(struct vq_desc_extra) * vq->vq_nentries);
|
|
|
|
|
|
|
|
vring_desc_init(vr->desc, size);
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Disable device(host) interrupting guest
|
|
|
|
*/
|
|
|
|
virtqueue_disable_intr(vq);
|
|
|
|
}
|
|
|
|
|
net/virtio: allocate queue at init stage
Queue allocation should be done once, since the queue related info (such
as vring addreess) will only be informed to the vhost-user backend once
without virtio device reset.
That means, if you allocate queues again after the vhost-user negotiation,
the vhost-user backend will not be informed any more. Leading to a state
that the vring info mismatches between virtio PMD driver and vhost-backend:
the driver switches to the new address has just been allocated, while the
vhost-backend still sticks to the old address has been assigned in the init
stage.
Unfortunately, that is exactly how the virtio driver is coded so far: queue
allocation is done at queue_setup stage (when rte_eth_tx/rx_queue_setup is
invoked). This is wrong, because queue_setup can be invoked several times.
For example,
$ start_testpmd.sh ... --txq=1 --rxq=1 ...
> port stop 0
> port config all txq 1 # just trigger the queue_setup callback again
> port config all rxq 1
> port start 0
The right way to do is allocate the queues in the init stage, so that the
vring info could be persistent with the vhost-user backend.
Besides that, we should allocate max_queue pairs the device supports, but
not nr queue pairs firstly configured, to make following case work.
$ start_testpmd.sh ... --txq=1 --rxq=1 ...
> port stop 0
> port config all txq 2
> port config all rxq 2
> port start 0
Since the allocation is switched to init stage, the free should also
moved from the rx/tx_queue_release to dev close stage. That leading we
could do nothing an empty rx/tx_queue_release() implementation.
Signed-off-by: Yuanhan Liu <yuanhan.liu@linux.intel.com>
2016-11-05 09:40:59 +00:00
|
|
|
static int
|
|
|
|
virtio_init_queue(struct rte_eth_dev *dev, uint16_t vtpci_queue_idx)
|
2013-09-18 10:00:00 +00:00
|
|
|
{
|
|
|
|
char vq_name[VIRTQUEUE_MAX_NAME_SZ];
|
2016-06-01 16:12:13 +00:00
|
|
|
char vq_hdr_name[VIRTQUEUE_MAX_NAME_SZ];
|
|
|
|
const struct rte_memzone *mz = NULL, *hdr_mz = NULL;
|
2015-08-28 16:23:38 +00:00
|
|
|
unsigned int vq_size, size;
|
2015-02-09 01:13:56 +00:00
|
|
|
struct virtio_hw *hw = dev->data->dev_private;
|
net/virtio: allow virtual address to fill vring descriptors
This patch is related to how to calculate relative address for vhost
backend.
The principle is that: based on one or multiple shared memory regions,
vhost maintains a reference system with the frontend start address,
backend start address, and length for each segment, so that each
frontend address (GPA, Guest Physical Address) can be translated into
vhost-recognizable backend address. To make the address translation
efficient, we need to maintain as few regions as possible. In the case
of VM, GPA is always locally continuous. But for some other case, like
virtio-user, GPA continuous is not guaranteed, therefore, we use virtual
address here.
It basically means:
a. when set_base_addr, VA address is used;
b. when preparing RX's descriptors, VA address is used;
c. when transmitting packets, VA is filled in TX's descriptors;
d. in TX and CQ's header, VA is used.
Signed-off-by: Huawei Xie <huawei.xie@intel.com>
Signed-off-by: Jianfeng Tan <jianfeng.tan@intel.com>
Acked-by: Neil Horman <nhorman@tuxdriver.com>
Acked-by: Yuanhan Liu <yuanhan.liu@linux.intel.com>
2016-06-15 09:03:21 +00:00
|
|
|
struct virtnet_rx *rxvq = NULL;
|
|
|
|
struct virtnet_tx *txvq = NULL;
|
|
|
|
struct virtnet_ctl *cvq = NULL;
|
2016-06-01 16:12:13 +00:00
|
|
|
struct virtqueue *vq;
|
2016-11-05 09:40:58 +00:00
|
|
|
size_t sz_hdr_mz = 0;
|
2016-06-01 16:12:13 +00:00
|
|
|
void *sw_ring = NULL;
|
net/virtio: allocate queue at init stage
Queue allocation should be done once, since the queue related info (such
as vring addreess) will only be informed to the vhost-user backend once
without virtio device reset.
That means, if you allocate queues again after the vhost-user negotiation,
the vhost-user backend will not be informed any more. Leading to a state
that the vring info mismatches between virtio PMD driver and vhost-backend:
the driver switches to the new address has just been allocated, while the
vhost-backend still sticks to the old address has been assigned in the init
stage.
Unfortunately, that is exactly how the virtio driver is coded so far: queue
allocation is done at queue_setup stage (when rte_eth_tx/rx_queue_setup is
invoked). This is wrong, because queue_setup can be invoked several times.
For example,
$ start_testpmd.sh ... --txq=1 --rxq=1 ...
> port stop 0
> port config all txq 1 # just trigger the queue_setup callback again
> port config all rxq 1
> port start 0
The right way to do is allocate the queues in the init stage, so that the
vring info could be persistent with the vhost-user backend.
Besides that, we should allocate max_queue pairs the device supports, but
not nr queue pairs firstly configured, to make following case work.
$ start_testpmd.sh ... --txq=1 --rxq=1 ...
> port stop 0
> port config all txq 2
> port config all rxq 2
> port start 0
Since the allocation is switched to init stage, the free should also
moved from the rx/tx_queue_release to dev close stage. That leading we
could do nothing an empty rx/tx_queue_release() implementation.
Signed-off-by: Yuanhan Liu <yuanhan.liu@linux.intel.com>
2016-11-05 09:40:59 +00:00
|
|
|
int queue_type = virtio_get_queue_type(hw, vtpci_queue_idx);
|
2016-06-01 16:12:13 +00:00
|
|
|
int ret;
|
2013-09-18 10:00:00 +00:00
|
|
|
|
2016-02-02 13:48:14 +00:00
|
|
|
PMD_INIT_LOG(DEBUG, "setting up queue: %u", vtpci_queue_idx);
|
2013-09-18 10:00:00 +00:00
|
|
|
|
|
|
|
/*
|
|
|
|
* Read the virtqueue size from the Queue Size field
|
|
|
|
* Always power of 2 and if 0 virtqueue does not exist
|
|
|
|
*/
|
net/virtio: store PCI operators pointer locally
We used to store the vtpci_ops at virtio_hw structure. The struct,
however, is stored in shared memory. That means only one value is
allowed. For the multiple process model, however, the address of
vtpci_ops should be different among different processes.
Take virtio PMD as example, the vtpci_ops is set by the primary
process, based on its own process space. If we access that address
from the secondary process, that would be an illegal memory access,
A crash then might happen.
To make the multiple process model work, we need store the vtpci_ops
in local memory but not in a shared memory. This is what the patch
does: a local virtio_hw_internal array of size RTE_MAX_ETHPORTS is
allocated. This new structure is used to store all these kind of
info in a non-shared memory. Current, we have:
- vtpci_ops
- rte_pci_ioport
- virtio pci mapped memory, such as common_cfg.
The later two will be done in coming patches. Later patches would also
set them correctly for secondary process, so that the multiple process
model could work.
Signed-off-by: Yuanhan Liu <yuanhan.liu@linux.intel.com>
2017-01-06 10:16:17 +00:00
|
|
|
vq_size = VTPCI_OPS(hw)->get_queue_num(hw, vtpci_queue_idx);
|
net/virtio: allocate queue at init stage
Queue allocation should be done once, since the queue related info (such
as vring addreess) will only be informed to the vhost-user backend once
without virtio device reset.
That means, if you allocate queues again after the vhost-user negotiation,
the vhost-user backend will not be informed any more. Leading to a state
that the vring info mismatches between virtio PMD driver and vhost-backend:
the driver switches to the new address has just been allocated, while the
vhost-backend still sticks to the old address has been assigned in the init
stage.
Unfortunately, that is exactly how the virtio driver is coded so far: queue
allocation is done at queue_setup stage (when rte_eth_tx/rx_queue_setup is
invoked). This is wrong, because queue_setup can be invoked several times.
For example,
$ start_testpmd.sh ... --txq=1 --rxq=1 ...
> port stop 0
> port config all txq 1 # just trigger the queue_setup callback again
> port config all rxq 1
> port start 0
The right way to do is allocate the queues in the init stage, so that the
vring info could be persistent with the vhost-user backend.
Besides that, we should allocate max_queue pairs the device supports, but
not nr queue pairs firstly configured, to make following case work.
$ start_testpmd.sh ... --txq=1 --rxq=1 ...
> port stop 0
> port config all txq 2
> port config all rxq 2
> port start 0
Since the allocation is switched to init stage, the free should also
moved from the rx/tx_queue_release to dev close stage. That leading we
could do nothing an empty rx/tx_queue_release() implementation.
Signed-off-by: Yuanhan Liu <yuanhan.liu@linux.intel.com>
2016-11-05 09:40:59 +00:00
|
|
|
PMD_INIT_LOG(DEBUG, "vq_size: %u", vq_size);
|
2013-09-18 10:00:00 +00:00
|
|
|
if (vq_size == 0) {
|
2016-02-10 16:08:54 +00:00
|
|
|
PMD_INIT_LOG(ERR, "virtqueue does not exist");
|
2014-06-13 01:32:40 +00:00
|
|
|
return -EINVAL;
|
2015-06-11 15:53:27 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
if (!rte_is_power_of_2(vq_size)) {
|
2016-02-10 16:08:54 +00:00
|
|
|
PMD_INIT_LOG(ERR, "virtqueue size is not powerof 2");
|
2014-06-13 01:32:40 +00:00
|
|
|
return -EINVAL;
|
2015-06-11 15:53:27 +00:00
|
|
|
}
|
|
|
|
|
2016-11-05 09:40:57 +00:00
|
|
|
snprintf(vq_name, sizeof(vq_name), "port%d_vq%d",
|
|
|
|
dev->data->port_id, vtpci_queue_idx);
|
2016-04-29 00:48:45 +00:00
|
|
|
|
2016-11-05 09:40:58 +00:00
|
|
|
size = RTE_ALIGN_CEIL(sizeof(*vq) +
|
2016-06-01 16:12:13 +00:00
|
|
|
vq_size * sizeof(struct vq_desc_extra),
|
|
|
|
RTE_CACHE_LINE_SIZE);
|
2016-11-05 09:40:58 +00:00
|
|
|
if (queue_type == VTNET_TQ) {
|
2016-06-01 16:12:13 +00:00
|
|
|
/*
|
|
|
|
* For each xmit packet, allocate a virtio_net_hdr
|
|
|
|
* and indirect ring elements
|
|
|
|
*/
|
|
|
|
sz_hdr_mz = vq_size * sizeof(struct virtio_tx_region);
|
|
|
|
} else if (queue_type == VTNET_CQ) {
|
|
|
|
/* Allocate a page for control vq command, data and status */
|
|
|
|
sz_hdr_mz = PAGE_SIZE;
|
2015-10-29 14:53:22 +00:00
|
|
|
}
|
2014-05-29 07:18:19 +00:00
|
|
|
|
net/virtio: allocate queue at init stage
Queue allocation should be done once, since the queue related info (such
as vring addreess) will only be informed to the vhost-user backend once
without virtio device reset.
That means, if you allocate queues again after the vhost-user negotiation,
the vhost-user backend will not be informed any more. Leading to a state
that the vring info mismatches between virtio PMD driver and vhost-backend:
the driver switches to the new address has just been allocated, while the
vhost-backend still sticks to the old address has been assigned in the init
stage.
Unfortunately, that is exactly how the virtio driver is coded so far: queue
allocation is done at queue_setup stage (when rte_eth_tx/rx_queue_setup is
invoked). This is wrong, because queue_setup can be invoked several times.
For example,
$ start_testpmd.sh ... --txq=1 --rxq=1 ...
> port stop 0
> port config all txq 1 # just trigger the queue_setup callback again
> port config all rxq 1
> port start 0
The right way to do is allocate the queues in the init stage, so that the
vring info could be persistent with the vhost-user backend.
Besides that, we should allocate max_queue pairs the device supports, but
not nr queue pairs firstly configured, to make following case work.
$ start_testpmd.sh ... --txq=1 --rxq=1 ...
> port stop 0
> port config all txq 2
> port config all rxq 2
> port start 0
Since the allocation is switched to init stage, the free should also
moved from the rx/tx_queue_release to dev close stage. That leading we
could do nothing an empty rx/tx_queue_release() implementation.
Signed-off-by: Yuanhan Liu <yuanhan.liu@linux.intel.com>
2016-11-05 09:40:59 +00:00
|
|
|
vq = rte_zmalloc_socket(vq_name, size, RTE_CACHE_LINE_SIZE,
|
|
|
|
SOCKET_ID_ANY);
|
2016-06-01 16:12:13 +00:00
|
|
|
if (vq == NULL) {
|
|
|
|
PMD_INIT_LOG(ERR, "can not allocate vq");
|
|
|
|
return -ENOMEM;
|
|
|
|
}
|
net/virtio: allocate queue at init stage
Queue allocation should be done once, since the queue related info (such
as vring addreess) will only be informed to the vhost-user backend once
without virtio device reset.
That means, if you allocate queues again after the vhost-user negotiation,
the vhost-user backend will not be informed any more. Leading to a state
that the vring info mismatches between virtio PMD driver and vhost-backend:
the driver switches to the new address has just been allocated, while the
vhost-backend still sticks to the old address has been assigned in the init
stage.
Unfortunately, that is exactly how the virtio driver is coded so far: queue
allocation is done at queue_setup stage (when rte_eth_tx/rx_queue_setup is
invoked). This is wrong, because queue_setup can be invoked several times.
For example,
$ start_testpmd.sh ... --txq=1 --rxq=1 ...
> port stop 0
> port config all txq 1 # just trigger the queue_setup callback again
> port config all rxq 1
> port start 0
The right way to do is allocate the queues in the init stage, so that the
vring info could be persistent with the vhost-user backend.
Besides that, we should allocate max_queue pairs the device supports, but
not nr queue pairs firstly configured, to make following case work.
$ start_testpmd.sh ... --txq=1 --rxq=1 ...
> port stop 0
> port config all txq 2
> port config all rxq 2
> port start 0
Since the allocation is switched to init stage, the free should also
moved from the rx/tx_queue_release to dev close stage. That leading we
could do nothing an empty rx/tx_queue_release() implementation.
Signed-off-by: Yuanhan Liu <yuanhan.liu@linux.intel.com>
2016-11-05 09:40:59 +00:00
|
|
|
hw->vqs[vtpci_queue_idx] = vq;
|
|
|
|
|
2013-09-18 10:00:00 +00:00
|
|
|
vq->hw = hw;
|
|
|
|
vq->vq_queue_index = vtpci_queue_idx;
|
|
|
|
vq->vq_nentries = vq_size;
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Reserve a memzone for vring elements
|
|
|
|
*/
|
|
|
|
size = vring_size(vq_size, VIRTIO_PCI_VRING_ALIGN);
|
|
|
|
vq->vq_ring_size = RTE_ALIGN_CEIL(size, VIRTIO_PCI_VRING_ALIGN);
|
2016-06-01 16:12:13 +00:00
|
|
|
PMD_INIT_LOG(DEBUG, "vring_size: %d, rounded_vring_size: %d",
|
|
|
|
size, vq->vq_ring_size);
|
2013-09-18 10:00:00 +00:00
|
|
|
|
net/virtio: allocate queue at init stage
Queue allocation should be done once, since the queue related info (such
as vring addreess) will only be informed to the vhost-user backend once
without virtio device reset.
That means, if you allocate queues again after the vhost-user negotiation,
the vhost-user backend will not be informed any more. Leading to a state
that the vring info mismatches between virtio PMD driver and vhost-backend:
the driver switches to the new address has just been allocated, while the
vhost-backend still sticks to the old address has been assigned in the init
stage.
Unfortunately, that is exactly how the virtio driver is coded so far: queue
allocation is done at queue_setup stage (when rte_eth_tx/rx_queue_setup is
invoked). This is wrong, because queue_setup can be invoked several times.
For example,
$ start_testpmd.sh ... --txq=1 --rxq=1 ...
> port stop 0
> port config all txq 1 # just trigger the queue_setup callback again
> port config all rxq 1
> port start 0
The right way to do is allocate the queues in the init stage, so that the
vring info could be persistent with the vhost-user backend.
Besides that, we should allocate max_queue pairs the device supports, but
not nr queue pairs firstly configured, to make following case work.
$ start_testpmd.sh ... --txq=1 --rxq=1 ...
> port stop 0
> port config all txq 2
> port config all rxq 2
> port start 0
Since the allocation is switched to init stage, the free should also
moved from the rx/tx_queue_release to dev close stage. That leading we
could do nothing an empty rx/tx_queue_release() implementation.
Signed-off-by: Yuanhan Liu <yuanhan.liu@linux.intel.com>
2016-11-05 09:40:59 +00:00
|
|
|
mz = rte_memzone_reserve_aligned(vq_name, vq->vq_ring_size,
|
|
|
|
SOCKET_ID_ANY,
|
2016-06-01 16:12:13 +00:00
|
|
|
0, VIRTIO_PCI_VRING_ALIGN);
|
2013-09-18 10:00:00 +00:00
|
|
|
if (mz == NULL) {
|
2015-07-15 13:51:00 +00:00
|
|
|
if (rte_errno == EEXIST)
|
|
|
|
mz = rte_memzone_lookup(vq_name);
|
|
|
|
if (mz == NULL) {
|
2016-06-01 16:12:13 +00:00
|
|
|
ret = -ENOMEM;
|
|
|
|
goto fail_q_alloc;
|
2015-07-15 13:51:00 +00:00
|
|
|
}
|
2013-09-18 10:00:00 +00:00
|
|
|
}
|
2014-05-29 07:18:19 +00:00
|
|
|
|
2017-06-12 04:34:30 +00:00
|
|
|
memset(mz->addr, 0, mz->len);
|
2016-06-01 16:12:13 +00:00
|
|
|
|
2013-09-18 10:00:00 +00:00
|
|
|
vq->vq_ring_mem = mz->phys_addr;
|
|
|
|
vq->vq_ring_virt_mem = mz->addr;
|
2016-06-01 16:12:13 +00:00
|
|
|
PMD_INIT_LOG(DEBUG, "vq->vq_ring_mem: 0x%" PRIx64,
|
|
|
|
(uint64_t)mz->phys_addr);
|
|
|
|
PMD_INIT_LOG(DEBUG, "vq->vq_ring_virt_mem: 0x%" PRIx64,
|
|
|
|
(uint64_t)(uintptr_t)mz->addr);
|
|
|
|
|
2016-11-05 09:41:00 +00:00
|
|
|
virtio_init_vring(vq);
|
|
|
|
|
2016-06-01 16:12:13 +00:00
|
|
|
if (sz_hdr_mz) {
|
2016-11-05 09:40:57 +00:00
|
|
|
snprintf(vq_hdr_name, sizeof(vq_hdr_name), "port%d_vq%d_hdr",
|
|
|
|
dev->data->port_id, vtpci_queue_idx);
|
2016-06-01 16:12:13 +00:00
|
|
|
hdr_mz = rte_memzone_reserve_aligned(vq_hdr_name, sz_hdr_mz,
|
net/virtio: allocate queue at init stage
Queue allocation should be done once, since the queue related info (such
as vring addreess) will only be informed to the vhost-user backend once
without virtio device reset.
That means, if you allocate queues again after the vhost-user negotiation,
the vhost-user backend will not be informed any more. Leading to a state
that the vring info mismatches between virtio PMD driver and vhost-backend:
the driver switches to the new address has just been allocated, while the
vhost-backend still sticks to the old address has been assigned in the init
stage.
Unfortunately, that is exactly how the virtio driver is coded so far: queue
allocation is done at queue_setup stage (when rte_eth_tx/rx_queue_setup is
invoked). This is wrong, because queue_setup can be invoked several times.
For example,
$ start_testpmd.sh ... --txq=1 --rxq=1 ...
> port stop 0
> port config all txq 1 # just trigger the queue_setup callback again
> port config all rxq 1
> port start 0
The right way to do is allocate the queues in the init stage, so that the
vring info could be persistent with the vhost-user backend.
Besides that, we should allocate max_queue pairs the device supports, but
not nr queue pairs firstly configured, to make following case work.
$ start_testpmd.sh ... --txq=1 --rxq=1 ...
> port stop 0
> port config all txq 2
> port config all rxq 2
> port start 0
Since the allocation is switched to init stage, the free should also
moved from the rx/tx_queue_release to dev close stage. That leading we
could do nothing an empty rx/tx_queue_release() implementation.
Signed-off-by: Yuanhan Liu <yuanhan.liu@linux.intel.com>
2016-11-05 09:40:59 +00:00
|
|
|
SOCKET_ID_ANY, 0,
|
2016-03-04 18:19:19 +00:00
|
|
|
RTE_CACHE_LINE_SIZE);
|
|
|
|
if (hdr_mz == NULL) {
|
2015-07-15 13:51:00 +00:00
|
|
|
if (rte_errno == EEXIST)
|
2016-06-01 16:12:13 +00:00
|
|
|
hdr_mz = rte_memzone_lookup(vq_hdr_name);
|
2016-03-04 18:19:19 +00:00
|
|
|
if (hdr_mz == NULL) {
|
2016-06-01 16:12:13 +00:00
|
|
|
ret = -ENOMEM;
|
|
|
|
goto fail_q_alloc;
|
2015-07-15 13:51:00 +00:00
|
|
|
}
|
2013-09-18 10:00:00 +00:00
|
|
|
}
|
2016-06-01 16:12:13 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
if (queue_type == VTNET_RQ) {
|
|
|
|
size_t sz_sw = (RTE_PMD_VIRTIO_RX_MAX_BURST + vq_size) *
|
|
|
|
sizeof(vq->sw_ring[0]);
|
|
|
|
|
|
|
|
sw_ring = rte_zmalloc_socket("sw_ring", sz_sw,
|
net/virtio: allocate queue at init stage
Queue allocation should be done once, since the queue related info (such
as vring addreess) will only be informed to the vhost-user backend once
without virtio device reset.
That means, if you allocate queues again after the vhost-user negotiation,
the vhost-user backend will not be informed any more. Leading to a state
that the vring info mismatches between virtio PMD driver and vhost-backend:
the driver switches to the new address has just been allocated, while the
vhost-backend still sticks to the old address has been assigned in the init
stage.
Unfortunately, that is exactly how the virtio driver is coded so far: queue
allocation is done at queue_setup stage (when rte_eth_tx/rx_queue_setup is
invoked). This is wrong, because queue_setup can be invoked several times.
For example,
$ start_testpmd.sh ... --txq=1 --rxq=1 ...
> port stop 0
> port config all txq 1 # just trigger the queue_setup callback again
> port config all rxq 1
> port start 0
The right way to do is allocate the queues in the init stage, so that the
vring info could be persistent with the vhost-user backend.
Besides that, we should allocate max_queue pairs the device supports, but
not nr queue pairs firstly configured, to make following case work.
$ start_testpmd.sh ... --txq=1 --rxq=1 ...
> port stop 0
> port config all txq 2
> port config all rxq 2
> port start 0
Since the allocation is switched to init stage, the free should also
moved from the rx/tx_queue_release to dev close stage. That leading we
could do nothing an empty rx/tx_queue_release() implementation.
Signed-off-by: Yuanhan Liu <yuanhan.liu@linux.intel.com>
2016-11-05 09:40:59 +00:00
|
|
|
RTE_CACHE_LINE_SIZE, SOCKET_ID_ANY);
|
2016-06-01 16:12:13 +00:00
|
|
|
if (!sw_ring) {
|
|
|
|
PMD_INIT_LOG(ERR, "can not allocate RX soft ring");
|
|
|
|
ret = -ENOMEM;
|
|
|
|
goto fail_q_alloc;
|
|
|
|
}
|
|
|
|
|
|
|
|
vq->sw_ring = sw_ring;
|
2016-11-05 09:40:58 +00:00
|
|
|
rxvq = &vq->rxq;
|
2016-06-01 16:12:13 +00:00
|
|
|
rxvq->vq = vq;
|
|
|
|
rxvq->port_id = dev->data->port_id;
|
|
|
|
rxvq->mz = mz;
|
|
|
|
} else if (queue_type == VTNET_TQ) {
|
2016-11-05 09:40:58 +00:00
|
|
|
txvq = &vq->txq;
|
2016-06-01 16:12:13 +00:00
|
|
|
txvq->vq = vq;
|
|
|
|
txvq->port_id = dev->data->port_id;
|
|
|
|
txvq->mz = mz;
|
|
|
|
txvq->virtio_net_hdr_mz = hdr_mz;
|
|
|
|
txvq->virtio_net_hdr_mem = hdr_mz->phys_addr;
|
net/virtio: allow virtual address to fill vring descriptors
This patch is related to how to calculate relative address for vhost
backend.
The principle is that: based on one or multiple shared memory regions,
vhost maintains a reference system with the frontend start address,
backend start address, and length for each segment, so that each
frontend address (GPA, Guest Physical Address) can be translated into
vhost-recognizable backend address. To make the address translation
efficient, we need to maintain as few regions as possible. In the case
of VM, GPA is always locally continuous. But for some other case, like
virtio-user, GPA continuous is not guaranteed, therefore, we use virtual
address here.
It basically means:
a. when set_base_addr, VA address is used;
b. when preparing RX's descriptors, VA address is used;
c. when transmitting packets, VA is filled in TX's descriptors;
d. in TX and CQ's header, VA is used.
Signed-off-by: Huawei Xie <huawei.xie@intel.com>
Signed-off-by: Jianfeng Tan <jianfeng.tan@intel.com>
Acked-by: Neil Horman <nhorman@tuxdriver.com>
Acked-by: Yuanhan Liu <yuanhan.liu@linux.intel.com>
2016-06-15 09:03:21 +00:00
|
|
|
} else if (queue_type == VTNET_CQ) {
|
2016-11-05 09:40:58 +00:00
|
|
|
cvq = &vq->cq;
|
net/virtio: allow virtual address to fill vring descriptors
This patch is related to how to calculate relative address for vhost
backend.
The principle is that: based on one or multiple shared memory regions,
vhost maintains a reference system with the frontend start address,
backend start address, and length for each segment, so that each
frontend address (GPA, Guest Physical Address) can be translated into
vhost-recognizable backend address. To make the address translation
efficient, we need to maintain as few regions as possible. In the case
of VM, GPA is always locally continuous. But for some other case, like
virtio-user, GPA continuous is not guaranteed, therefore, we use virtual
address here.
It basically means:
a. when set_base_addr, VA address is used;
b. when preparing RX's descriptors, VA address is used;
c. when transmitting packets, VA is filled in TX's descriptors;
d. in TX and CQ's header, VA is used.
Signed-off-by: Huawei Xie <huawei.xie@intel.com>
Signed-off-by: Jianfeng Tan <jianfeng.tan@intel.com>
Acked-by: Neil Horman <nhorman@tuxdriver.com>
Acked-by: Yuanhan Liu <yuanhan.liu@linux.intel.com>
2016-06-15 09:03:21 +00:00
|
|
|
cvq->vq = vq;
|
|
|
|
cvq->mz = mz;
|
|
|
|
cvq->virtio_net_hdr_mz = hdr_mz;
|
|
|
|
cvq->virtio_net_hdr_mem = hdr_mz->phys_addr;
|
|
|
|
memset(cvq->virtio_net_hdr_mz->addr, 0, PAGE_SIZE);
|
net/virtio: allocate queue at init stage
Queue allocation should be done once, since the queue related info (such
as vring addreess) will only be informed to the vhost-user backend once
without virtio device reset.
That means, if you allocate queues again after the vhost-user negotiation,
the vhost-user backend will not be informed any more. Leading to a state
that the vring info mismatches between virtio PMD driver and vhost-backend:
the driver switches to the new address has just been allocated, while the
vhost-backend still sticks to the old address has been assigned in the init
stage.
Unfortunately, that is exactly how the virtio driver is coded so far: queue
allocation is done at queue_setup stage (when rte_eth_tx/rx_queue_setup is
invoked). This is wrong, because queue_setup can be invoked several times.
For example,
$ start_testpmd.sh ... --txq=1 --rxq=1 ...
> port stop 0
> port config all txq 1 # just trigger the queue_setup callback again
> port config all rxq 1
> port start 0
The right way to do is allocate the queues in the init stage, so that the
vring info could be persistent with the vhost-user backend.
Besides that, we should allocate max_queue pairs the device supports, but
not nr queue pairs firstly configured, to make following case work.
$ start_testpmd.sh ... --txq=1 --rxq=1 ...
> port stop 0
> port config all txq 2
> port config all rxq 2
> port start 0
Since the allocation is switched to init stage, the free should also
moved from the rx/tx_queue_release to dev close stage. That leading we
could do nothing an empty rx/tx_queue_release() implementation.
Signed-off-by: Yuanhan Liu <yuanhan.liu@linux.intel.com>
2016-11-05 09:40:59 +00:00
|
|
|
|
|
|
|
hw->cvq = cvq;
|
net/virtio: allow virtual address to fill vring descriptors
This patch is related to how to calculate relative address for vhost
backend.
The principle is that: based on one or multiple shared memory regions,
vhost maintains a reference system with the frontend start address,
backend start address, and length for each segment, so that each
frontend address (GPA, Guest Physical Address) can be translated into
vhost-recognizable backend address. To make the address translation
efficient, we need to maintain as few regions as possible. In the case
of VM, GPA is always locally continuous. But for some other case, like
virtio-user, GPA continuous is not guaranteed, therefore, we use virtual
address here.
It basically means:
a. when set_base_addr, VA address is used;
b. when preparing RX's descriptors, VA address is used;
c. when transmitting packets, VA is filled in TX's descriptors;
d. in TX and CQ's header, VA is used.
Signed-off-by: Huawei Xie <huawei.xie@intel.com>
Signed-off-by: Jianfeng Tan <jianfeng.tan@intel.com>
Acked-by: Neil Horman <nhorman@tuxdriver.com>
Acked-by: Yuanhan Liu <yuanhan.liu@linux.intel.com>
2016-06-15 09:03:21 +00:00
|
|
|
}
|
|
|
|
|
2016-12-23 15:58:02 +00:00
|
|
|
/* For virtio_user case (that is when hw->dev is NULL), we use
|
net/virtio: allow virtual address to fill vring descriptors
This patch is related to how to calculate relative address for vhost
backend.
The principle is that: based on one or multiple shared memory regions,
vhost maintains a reference system with the frontend start address,
backend start address, and length for each segment, so that each
frontend address (GPA, Guest Physical Address) can be translated into
vhost-recognizable backend address. To make the address translation
efficient, we need to maintain as few regions as possible. In the case
of VM, GPA is always locally continuous. But for some other case, like
virtio-user, GPA continuous is not guaranteed, therefore, we use virtual
address here.
It basically means:
a. when set_base_addr, VA address is used;
b. when preparing RX's descriptors, VA address is used;
c. when transmitting packets, VA is filled in TX's descriptors;
d. in TX and CQ's header, VA is used.
Signed-off-by: Huawei Xie <huawei.xie@intel.com>
Signed-off-by: Jianfeng Tan <jianfeng.tan@intel.com>
Acked-by: Neil Horman <nhorman@tuxdriver.com>
Acked-by: Yuanhan Liu <yuanhan.liu@linux.intel.com>
2016-06-15 09:03:21 +00:00
|
|
|
* virtual address. And we need properly set _offset_, please see
|
2016-07-19 12:31:59 +00:00
|
|
|
* VIRTIO_MBUF_DATA_DMA_ADDR in virtqueue.h for more information.
|
net/virtio: allow virtual address to fill vring descriptors
This patch is related to how to calculate relative address for vhost
backend.
The principle is that: based on one or multiple shared memory regions,
vhost maintains a reference system with the frontend start address,
backend start address, and length for each segment, so that each
frontend address (GPA, Guest Physical Address) can be translated into
vhost-recognizable backend address. To make the address translation
efficient, we need to maintain as few regions as possible. In the case
of VM, GPA is always locally continuous. But for some other case, like
virtio-user, GPA continuous is not guaranteed, therefore, we use virtual
address here.
It basically means:
a. when set_base_addr, VA address is used;
b. when preparing RX's descriptors, VA address is used;
c. when transmitting packets, VA is filled in TX's descriptors;
d. in TX and CQ's header, VA is used.
Signed-off-by: Huawei Xie <huawei.xie@intel.com>
Signed-off-by: Jianfeng Tan <jianfeng.tan@intel.com>
Acked-by: Neil Horman <nhorman@tuxdriver.com>
Acked-by: Yuanhan Liu <yuanhan.liu@linux.intel.com>
2016-06-15 09:03:21 +00:00
|
|
|
*/
|
2017-01-12 05:37:00 +00:00
|
|
|
if (!hw->virtio_user_dev)
|
net/virtio: allow virtual address to fill vring descriptors
This patch is related to how to calculate relative address for vhost
backend.
The principle is that: based on one or multiple shared memory regions,
vhost maintains a reference system with the frontend start address,
backend start address, and length for each segment, so that each
frontend address (GPA, Guest Physical Address) can be translated into
vhost-recognizable backend address. To make the address translation
efficient, we need to maintain as few regions as possible. In the case
of VM, GPA is always locally continuous. But for some other case, like
virtio-user, GPA continuous is not guaranteed, therefore, we use virtual
address here.
It basically means:
a. when set_base_addr, VA address is used;
b. when preparing RX's descriptors, VA address is used;
c. when transmitting packets, VA is filled in TX's descriptors;
d. in TX and CQ's header, VA is used.
Signed-off-by: Huawei Xie <huawei.xie@intel.com>
Signed-off-by: Jianfeng Tan <jianfeng.tan@intel.com>
Acked-by: Neil Horman <nhorman@tuxdriver.com>
Acked-by: Yuanhan Liu <yuanhan.liu@linux.intel.com>
2016-06-15 09:03:21 +00:00
|
|
|
vq->offset = offsetof(struct rte_mbuf, buf_physaddr);
|
|
|
|
else {
|
|
|
|
vq->vq_ring_mem = (uintptr_t)mz->addr;
|
|
|
|
vq->offset = offsetof(struct rte_mbuf, buf_addr);
|
|
|
|
if (queue_type == VTNET_TQ)
|
|
|
|
txvq->virtio_net_hdr_mem = (uintptr_t)hdr_mz->addr;
|
|
|
|
else if (queue_type == VTNET_CQ)
|
|
|
|
cvq->virtio_net_hdr_mem = (uintptr_t)hdr_mz->addr;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (queue_type == VTNET_TQ) {
|
|
|
|
struct virtio_tx_region *txr;
|
|
|
|
unsigned int i;
|
|
|
|
|
2016-03-04 18:19:19 +00:00
|
|
|
txr = hdr_mz->addr;
|
|
|
|
memset(txr, 0, vq_size * sizeof(*txr));
|
|
|
|
for (i = 0; i < vq_size; i++) {
|
|
|
|
struct vring_desc *start_dp = txr[i].tx_indir;
|
|
|
|
|
|
|
|
vring_desc_init(start_dp, RTE_DIM(txr[i].tx_indir));
|
|
|
|
|
|
|
|
/* first indirect descriptor is always the tx header */
|
2016-06-01 16:12:13 +00:00
|
|
|
start_dp->addr = txvq->virtio_net_hdr_mem
|
2016-03-04 18:19:19 +00:00
|
|
|
+ i * sizeof(*txr)
|
|
|
|
+ offsetof(struct virtio_tx_region, tx_hdr);
|
|
|
|
|
2016-06-01 16:12:13 +00:00
|
|
|
start_dp->len = hw->vtnet_hdr_size;
|
2016-03-04 18:19:19 +00:00
|
|
|
start_dp->flags = VRING_DESC_F_NEXT;
|
|
|
|
}
|
2013-09-18 10:00:00 +00:00
|
|
|
}
|
|
|
|
|
net/virtio: store PCI operators pointer locally
We used to store the vtpci_ops at virtio_hw structure. The struct,
however, is stored in shared memory. That means only one value is
allowed. For the multiple process model, however, the address of
vtpci_ops should be different among different processes.
Take virtio PMD as example, the vtpci_ops is set by the primary
process, based on its own process space. If we access that address
from the secondary process, that would be an illegal memory access,
A crash then might happen.
To make the multiple process model work, we need store the vtpci_ops
in local memory but not in a shared memory. This is what the patch
does: a local virtio_hw_internal array of size RTE_MAX_ETHPORTS is
allocated. This new structure is used to store all these kind of
info in a non-shared memory. Current, we have:
- vtpci_ops
- rte_pci_ioport
- virtio pci mapped memory, such as common_cfg.
The later two will be done in coming patches. Later patches would also
set them correctly for secondary process, so that the multiple process
model could work.
Signed-off-by: Yuanhan Liu <yuanhan.liu@linux.intel.com>
2017-01-06 10:16:17 +00:00
|
|
|
if (VTPCI_OPS(hw)->setup_queue(hw, vq) < 0) {
|
2016-06-15 09:03:20 +00:00
|
|
|
PMD_INIT_LOG(ERR, "setup_queue failed");
|
|
|
|
return -EINVAL;
|
|
|
|
}
|
|
|
|
|
2014-05-29 07:18:19 +00:00
|
|
|
return 0;
|
2016-06-01 16:12:13 +00:00
|
|
|
|
|
|
|
fail_q_alloc:
|
|
|
|
rte_free(sw_ring);
|
|
|
|
rte_memzone_free(hdr_mz);
|
|
|
|
rte_memzone_free(mz);
|
|
|
|
rte_free(vq);
|
|
|
|
|
|
|
|
return ret;
|
2013-09-18 10:00:00 +00:00
|
|
|
}
|
|
|
|
|
net/virtio: allocate queue at init stage
Queue allocation should be done once, since the queue related info (such
as vring addreess) will only be informed to the vhost-user backend once
without virtio device reset.
That means, if you allocate queues again after the vhost-user negotiation,
the vhost-user backend will not be informed any more. Leading to a state
that the vring info mismatches between virtio PMD driver and vhost-backend:
the driver switches to the new address has just been allocated, while the
vhost-backend still sticks to the old address has been assigned in the init
stage.
Unfortunately, that is exactly how the virtio driver is coded so far: queue
allocation is done at queue_setup stage (when rte_eth_tx/rx_queue_setup is
invoked). This is wrong, because queue_setup can be invoked several times.
For example,
$ start_testpmd.sh ... --txq=1 --rxq=1 ...
> port stop 0
> port config all txq 1 # just trigger the queue_setup callback again
> port config all rxq 1
> port start 0
The right way to do is allocate the queues in the init stage, so that the
vring info could be persistent with the vhost-user backend.
Besides that, we should allocate max_queue pairs the device supports, but
not nr queue pairs firstly configured, to make following case work.
$ start_testpmd.sh ... --txq=1 --rxq=1 ...
> port stop 0
> port config all txq 2
> port config all rxq 2
> port start 0
Since the allocation is switched to init stage, the free should also
moved from the rx/tx_queue_release to dev close stage. That leading we
could do nothing an empty rx/tx_queue_release() implementation.
Signed-off-by: Yuanhan Liu <yuanhan.liu@linux.intel.com>
2016-11-05 09:40:59 +00:00
|
|
|
static void
|
|
|
|
virtio_free_queues(struct virtio_hw *hw)
|
2013-09-18 10:00:00 +00:00
|
|
|
{
|
net/virtio: allocate queue at init stage
Queue allocation should be done once, since the queue related info (such
as vring addreess) will only be informed to the vhost-user backend once
without virtio device reset.
That means, if you allocate queues again after the vhost-user negotiation,
the vhost-user backend will not be informed any more. Leading to a state
that the vring info mismatches between virtio PMD driver and vhost-backend:
the driver switches to the new address has just been allocated, while the
vhost-backend still sticks to the old address has been assigned in the init
stage.
Unfortunately, that is exactly how the virtio driver is coded so far: queue
allocation is done at queue_setup stage (when rte_eth_tx/rx_queue_setup is
invoked). This is wrong, because queue_setup can be invoked several times.
For example,
$ start_testpmd.sh ... --txq=1 --rxq=1 ...
> port stop 0
> port config all txq 1 # just trigger the queue_setup callback again
> port config all rxq 1
> port start 0
The right way to do is allocate the queues in the init stage, so that the
vring info could be persistent with the vhost-user backend.
Besides that, we should allocate max_queue pairs the device supports, but
not nr queue pairs firstly configured, to make following case work.
$ start_testpmd.sh ... --txq=1 --rxq=1 ...
> port stop 0
> port config all txq 2
> port config all rxq 2
> port start 0
Since the allocation is switched to init stage, the free should also
moved from the rx/tx_queue_release to dev close stage. That leading we
could do nothing an empty rx/tx_queue_release() implementation.
Signed-off-by: Yuanhan Liu <yuanhan.liu@linux.intel.com>
2016-11-05 09:40:59 +00:00
|
|
|
uint16_t nr_vq = virtio_get_nr_vq(hw);
|
|
|
|
struct virtqueue *vq;
|
|
|
|
int queue_type;
|
|
|
|
uint16_t i;
|
2013-09-18 10:00:00 +00:00
|
|
|
|
2017-02-20 14:04:46 +00:00
|
|
|
if (hw->vqs == NULL)
|
|
|
|
return;
|
|
|
|
|
net/virtio: allocate queue at init stage
Queue allocation should be done once, since the queue related info (such
as vring addreess) will only be informed to the vhost-user backend once
without virtio device reset.
That means, if you allocate queues again after the vhost-user negotiation,
the vhost-user backend will not be informed any more. Leading to a state
that the vring info mismatches between virtio PMD driver and vhost-backend:
the driver switches to the new address has just been allocated, while the
vhost-backend still sticks to the old address has been assigned in the init
stage.
Unfortunately, that is exactly how the virtio driver is coded so far: queue
allocation is done at queue_setup stage (when rte_eth_tx/rx_queue_setup is
invoked). This is wrong, because queue_setup can be invoked several times.
For example,
$ start_testpmd.sh ... --txq=1 --rxq=1 ...
> port stop 0
> port config all txq 1 # just trigger the queue_setup callback again
> port config all rxq 1
> port start 0
The right way to do is allocate the queues in the init stage, so that the
vring info could be persistent with the vhost-user backend.
Besides that, we should allocate max_queue pairs the device supports, but
not nr queue pairs firstly configured, to make following case work.
$ start_testpmd.sh ... --txq=1 --rxq=1 ...
> port stop 0
> port config all txq 2
> port config all rxq 2
> port start 0
Since the allocation is switched to init stage, the free should also
moved from the rx/tx_queue_release to dev close stage. That leading we
could do nothing an empty rx/tx_queue_release() implementation.
Signed-off-by: Yuanhan Liu <yuanhan.liu@linux.intel.com>
2016-11-05 09:40:59 +00:00
|
|
|
for (i = 0; i < nr_vq; i++) {
|
|
|
|
vq = hw->vqs[i];
|
|
|
|
if (!vq)
|
|
|
|
continue;
|
|
|
|
|
|
|
|
queue_type = virtio_get_queue_type(hw, i);
|
|
|
|
if (queue_type == VTNET_RQ) {
|
|
|
|
rte_free(vq->sw_ring);
|
|
|
|
rte_memzone_free(vq->rxq.mz);
|
|
|
|
} else if (queue_type == VTNET_TQ) {
|
|
|
|
rte_memzone_free(vq->txq.mz);
|
|
|
|
rte_memzone_free(vq->txq.virtio_net_hdr_mz);
|
|
|
|
} else {
|
|
|
|
rte_memzone_free(vq->cq.mz);
|
|
|
|
rte_memzone_free(vq->cq.virtio_net_hdr_mz);
|
|
|
|
}
|
|
|
|
|
|
|
|
rte_free(vq);
|
2017-02-20 14:04:46 +00:00
|
|
|
hw->vqs[i] = NULL;
|
2013-09-18 10:00:00 +00:00
|
|
|
}
|
|
|
|
|
net/virtio: allocate queue at init stage
Queue allocation should be done once, since the queue related info (such
as vring addreess) will only be informed to the vhost-user backend once
without virtio device reset.
That means, if you allocate queues again after the vhost-user negotiation,
the vhost-user backend will not be informed any more. Leading to a state
that the vring info mismatches between virtio PMD driver and vhost-backend:
the driver switches to the new address has just been allocated, while the
vhost-backend still sticks to the old address has been assigned in the init
stage.
Unfortunately, that is exactly how the virtio driver is coded so far: queue
allocation is done at queue_setup stage (when rte_eth_tx/rx_queue_setup is
invoked). This is wrong, because queue_setup can be invoked several times.
For example,
$ start_testpmd.sh ... --txq=1 --rxq=1 ...
> port stop 0
> port config all txq 1 # just trigger the queue_setup callback again
> port config all rxq 1
> port start 0
The right way to do is allocate the queues in the init stage, so that the
vring info could be persistent with the vhost-user backend.
Besides that, we should allocate max_queue pairs the device supports, but
not nr queue pairs firstly configured, to make following case work.
$ start_testpmd.sh ... --txq=1 --rxq=1 ...
> port stop 0
> port config all txq 2
> port config all rxq 2
> port start 0
Since the allocation is switched to init stage, the free should also
moved from the rx/tx_queue_release to dev close stage. That leading we
could do nothing an empty rx/tx_queue_release() implementation.
Signed-off-by: Yuanhan Liu <yuanhan.liu@linux.intel.com>
2016-11-05 09:40:59 +00:00
|
|
|
rte_free(hw->vqs);
|
2017-02-20 14:04:46 +00:00
|
|
|
hw->vqs = NULL;
|
2013-09-18 10:00:00 +00:00
|
|
|
}
|
|
|
|
|
net/virtio: allocate queue at init stage
Queue allocation should be done once, since the queue related info (such
as vring addreess) will only be informed to the vhost-user backend once
without virtio device reset.
That means, if you allocate queues again after the vhost-user negotiation,
the vhost-user backend will not be informed any more. Leading to a state
that the vring info mismatches between virtio PMD driver and vhost-backend:
the driver switches to the new address has just been allocated, while the
vhost-backend still sticks to the old address has been assigned in the init
stage.
Unfortunately, that is exactly how the virtio driver is coded so far: queue
allocation is done at queue_setup stage (when rte_eth_tx/rx_queue_setup is
invoked). This is wrong, because queue_setup can be invoked several times.
For example,
$ start_testpmd.sh ... --txq=1 --rxq=1 ...
> port stop 0
> port config all txq 1 # just trigger the queue_setup callback again
> port config all rxq 1
> port start 0
The right way to do is allocate the queues in the init stage, so that the
vring info could be persistent with the vhost-user backend.
Besides that, we should allocate max_queue pairs the device supports, but
not nr queue pairs firstly configured, to make following case work.
$ start_testpmd.sh ... --txq=1 --rxq=1 ...
> port stop 0
> port config all txq 2
> port config all rxq 2
> port start 0
Since the allocation is switched to init stage, the free should also
moved from the rx/tx_queue_release to dev close stage. That leading we
could do nothing an empty rx/tx_queue_release() implementation.
Signed-off-by: Yuanhan Liu <yuanhan.liu@linux.intel.com>
2016-11-05 09:40:59 +00:00
|
|
|
static int
|
|
|
|
virtio_alloc_queues(struct rte_eth_dev *dev)
|
2015-07-15 13:51:03 +00:00
|
|
|
{
|
net/virtio: allocate queue at init stage
Queue allocation should be done once, since the queue related info (such
as vring addreess) will only be informed to the vhost-user backend once
without virtio device reset.
That means, if you allocate queues again after the vhost-user negotiation,
the vhost-user backend will not be informed any more. Leading to a state
that the vring info mismatches between virtio PMD driver and vhost-backend:
the driver switches to the new address has just been allocated, while the
vhost-backend still sticks to the old address has been assigned in the init
stage.
Unfortunately, that is exactly how the virtio driver is coded so far: queue
allocation is done at queue_setup stage (when rte_eth_tx/rx_queue_setup is
invoked). This is wrong, because queue_setup can be invoked several times.
For example,
$ start_testpmd.sh ... --txq=1 --rxq=1 ...
> port stop 0
> port config all txq 1 # just trigger the queue_setup callback again
> port config all rxq 1
> port start 0
The right way to do is allocate the queues in the init stage, so that the
vring info could be persistent with the vhost-user backend.
Besides that, we should allocate max_queue pairs the device supports, but
not nr queue pairs firstly configured, to make following case work.
$ start_testpmd.sh ... --txq=1 --rxq=1 ...
> port stop 0
> port config all txq 2
> port config all rxq 2
> port start 0
Since the allocation is switched to init stage, the free should also
moved from the rx/tx_queue_release to dev close stage. That leading we
could do nothing an empty rx/tx_queue_release() implementation.
Signed-off-by: Yuanhan Liu <yuanhan.liu@linux.intel.com>
2016-11-05 09:40:59 +00:00
|
|
|
struct virtio_hw *hw = dev->data->dev_private;
|
|
|
|
uint16_t nr_vq = virtio_get_nr_vq(hw);
|
|
|
|
uint16_t i;
|
|
|
|
int ret;
|
2015-07-15 13:51:03 +00:00
|
|
|
|
net/virtio: allocate queue at init stage
Queue allocation should be done once, since the queue related info (such
as vring addreess) will only be informed to the vhost-user backend once
without virtio device reset.
That means, if you allocate queues again after the vhost-user negotiation,
the vhost-user backend will not be informed any more. Leading to a state
that the vring info mismatches between virtio PMD driver and vhost-backend:
the driver switches to the new address has just been allocated, while the
vhost-backend still sticks to the old address has been assigned in the init
stage.
Unfortunately, that is exactly how the virtio driver is coded so far: queue
allocation is done at queue_setup stage (when rte_eth_tx/rx_queue_setup is
invoked). This is wrong, because queue_setup can be invoked several times.
For example,
$ start_testpmd.sh ... --txq=1 --rxq=1 ...
> port stop 0
> port config all txq 1 # just trigger the queue_setup callback again
> port config all rxq 1
> port start 0
The right way to do is allocate the queues in the init stage, so that the
vring info could be persistent with the vhost-user backend.
Besides that, we should allocate max_queue pairs the device supports, but
not nr queue pairs firstly configured, to make following case work.
$ start_testpmd.sh ... --txq=1 --rxq=1 ...
> port stop 0
> port config all txq 2
> port config all rxq 2
> port start 0
Since the allocation is switched to init stage, the free should also
moved from the rx/tx_queue_release to dev close stage. That leading we
could do nothing an empty rx/tx_queue_release() implementation.
Signed-off-by: Yuanhan Liu <yuanhan.liu@linux.intel.com>
2016-11-05 09:40:59 +00:00
|
|
|
hw->vqs = rte_zmalloc(NULL, sizeof(struct virtqueue *) * nr_vq, 0);
|
|
|
|
if (!hw->vqs) {
|
|
|
|
PMD_INIT_LOG(ERR, "failed to allocate vqs");
|
|
|
|
return -ENOMEM;
|
|
|
|
}
|
2015-07-15 13:51:03 +00:00
|
|
|
|
net/virtio: allocate queue at init stage
Queue allocation should be done once, since the queue related info (such
as vring addreess) will only be informed to the vhost-user backend once
without virtio device reset.
That means, if you allocate queues again after the vhost-user negotiation,
the vhost-user backend will not be informed any more. Leading to a state
that the vring info mismatches between virtio PMD driver and vhost-backend:
the driver switches to the new address has just been allocated, while the
vhost-backend still sticks to the old address has been assigned in the init
stage.
Unfortunately, that is exactly how the virtio driver is coded so far: queue
allocation is done at queue_setup stage (when rte_eth_tx/rx_queue_setup is
invoked). This is wrong, because queue_setup can be invoked several times.
For example,
$ start_testpmd.sh ... --txq=1 --rxq=1 ...
> port stop 0
> port config all txq 1 # just trigger the queue_setup callback again
> port config all rxq 1
> port start 0
The right way to do is allocate the queues in the init stage, so that the
vring info could be persistent with the vhost-user backend.
Besides that, we should allocate max_queue pairs the device supports, but
not nr queue pairs firstly configured, to make following case work.
$ start_testpmd.sh ... --txq=1 --rxq=1 ...
> port stop 0
> port config all txq 2
> port config all rxq 2
> port start 0
Since the allocation is switched to init stage, the free should also
moved from the rx/tx_queue_release to dev close stage. That leading we
could do nothing an empty rx/tx_queue_release() implementation.
Signed-off-by: Yuanhan Liu <yuanhan.liu@linux.intel.com>
2016-11-05 09:40:59 +00:00
|
|
|
for (i = 0; i < nr_vq; i++) {
|
|
|
|
ret = virtio_init_queue(dev, i);
|
|
|
|
if (ret < 0) {
|
|
|
|
virtio_free_queues(hw);
|
|
|
|
return ret;
|
|
|
|
}
|
|
|
|
}
|
2015-07-15 13:51:03 +00:00
|
|
|
|
net/virtio: allocate queue at init stage
Queue allocation should be done once, since the queue related info (such
as vring addreess) will only be informed to the vhost-user backend once
without virtio device reset.
That means, if you allocate queues again after the vhost-user negotiation,
the vhost-user backend will not be informed any more. Leading to a state
that the vring info mismatches between virtio PMD driver and vhost-backend:
the driver switches to the new address has just been allocated, while the
vhost-backend still sticks to the old address has been assigned in the init
stage.
Unfortunately, that is exactly how the virtio driver is coded so far: queue
allocation is done at queue_setup stage (when rte_eth_tx/rx_queue_setup is
invoked). This is wrong, because queue_setup can be invoked several times.
For example,
$ start_testpmd.sh ... --txq=1 --rxq=1 ...
> port stop 0
> port config all txq 1 # just trigger the queue_setup callback again
> port config all rxq 1
> port start 0
The right way to do is allocate the queues in the init stage, so that the
vring info could be persistent with the vhost-user backend.
Besides that, we should allocate max_queue pairs the device supports, but
not nr queue pairs firstly configured, to make following case work.
$ start_testpmd.sh ... --txq=1 --rxq=1 ...
> port stop 0
> port config all txq 2
> port config all rxq 2
> port start 0
Since the allocation is switched to init stage, the free should also
moved from the rx/tx_queue_release to dev close stage. That leading we
could do nothing an empty rx/tx_queue_release() implementation.
Signed-off-by: Yuanhan Liu <yuanhan.liu@linux.intel.com>
2016-11-05 09:40:59 +00:00
|
|
|
return 0;
|
2015-07-15 13:51:03 +00:00
|
|
|
}
|
|
|
|
|
2017-01-17 07:10:28 +00:00
|
|
|
static void virtio_queues_unbind_intr(struct rte_eth_dev *dev);
|
|
|
|
|
2014-02-12 16:44:44 +00:00
|
|
|
static void
|
|
|
|
virtio_dev_close(struct rte_eth_dev *dev)
|
|
|
|
{
|
2015-02-09 01:13:58 +00:00
|
|
|
struct virtio_hw *hw = dev->data->dev_private;
|
2017-01-17 07:10:28 +00:00
|
|
|
struct rte_intr_conf *intr_conf = &dev->data->dev_conf.intr_conf;
|
2015-02-09 01:13:58 +00:00
|
|
|
|
2014-02-12 16:44:44 +00:00
|
|
|
PMD_INIT_LOG(DEBUG, "virtio_dev_close");
|
|
|
|
|
2015-02-09 01:13:58 +00:00
|
|
|
/* reset the NIC */
|
2016-05-09 16:35:57 +00:00
|
|
|
if (dev->data->dev_flags & RTE_ETH_DEV_INTR_LSC)
|
2017-01-17 07:10:22 +00:00
|
|
|
VTPCI_OPS(hw)->set_config_irq(hw, VIRTIO_MSI_NO_VECTOR);
|
2017-01-17 07:10:28 +00:00
|
|
|
if (intr_conf->rxq)
|
|
|
|
virtio_queues_unbind_intr(dev);
|
|
|
|
|
|
|
|
if (intr_conf->lsc || intr_conf->rxq) {
|
|
|
|
rte_intr_disable(dev->intr_handle);
|
|
|
|
rte_intr_efd_disable(dev->intr_handle);
|
|
|
|
rte_free(dev->intr_handle->intr_vec);
|
|
|
|
dev->intr_handle->intr_vec = NULL;
|
|
|
|
}
|
|
|
|
|
2015-02-09 01:13:58 +00:00
|
|
|
vtpci_reset(hw);
|
|
|
|
virtio_dev_free_mbufs(dev);
|
net/virtio: allocate queue at init stage
Queue allocation should be done once, since the queue related info (such
as vring addreess) will only be informed to the vhost-user backend once
without virtio device reset.
That means, if you allocate queues again after the vhost-user negotiation,
the vhost-user backend will not be informed any more. Leading to a state
that the vring info mismatches between virtio PMD driver and vhost-backend:
the driver switches to the new address has just been allocated, while the
vhost-backend still sticks to the old address has been assigned in the init
stage.
Unfortunately, that is exactly how the virtio driver is coded so far: queue
allocation is done at queue_setup stage (when rte_eth_tx/rx_queue_setup is
invoked). This is wrong, because queue_setup can be invoked several times.
For example,
$ start_testpmd.sh ... --txq=1 --rxq=1 ...
> port stop 0
> port config all txq 1 # just trigger the queue_setup callback again
> port config all rxq 1
> port start 0
The right way to do is allocate the queues in the init stage, so that the
vring info could be persistent with the vhost-user backend.
Besides that, we should allocate max_queue pairs the device supports, but
not nr queue pairs firstly configured, to make following case work.
$ start_testpmd.sh ... --txq=1 --rxq=1 ...
> port stop 0
> port config all txq 2
> port config all rxq 2
> port start 0
Since the allocation is switched to init stage, the free should also
moved from the rx/tx_queue_release to dev close stage. That leading we
could do nothing an empty rx/tx_queue_release() implementation.
Signed-off-by: Yuanhan Liu <yuanhan.liu@linux.intel.com>
2016-11-05 09:40:59 +00:00
|
|
|
virtio_free_queues(hw);
|
2014-02-12 16:44:44 +00:00
|
|
|
}
|
|
|
|
|
2014-11-08 04:26:15 +00:00
|
|
|
static void
|
|
|
|
virtio_dev_promiscuous_enable(struct rte_eth_dev *dev)
|
|
|
|
{
|
2015-02-09 01:13:56 +00:00
|
|
|
struct virtio_hw *hw = dev->data->dev_private;
|
2014-11-08 04:26:15 +00:00
|
|
|
struct virtio_pmd_ctrl ctrl;
|
|
|
|
int dlen[1];
|
|
|
|
int ret;
|
|
|
|
|
2015-06-11 15:53:24 +00:00
|
|
|
if (!vtpci_with_feature(hw, VIRTIO_NET_F_CTRL_RX)) {
|
2017-01-27 15:16:32 +00:00
|
|
|
PMD_INIT_LOG(INFO, "host does not support rx control");
|
2015-06-11 15:53:24 +00:00
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
2014-11-08 04:26:15 +00:00
|
|
|
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");
|
|
|
|
}
|
|
|
|
|
|
|
|
static void
|
|
|
|
virtio_dev_promiscuous_disable(struct rte_eth_dev *dev)
|
|
|
|
{
|
2015-02-09 01:13:56 +00:00
|
|
|
struct virtio_hw *hw = dev->data->dev_private;
|
2014-11-08 04:26:15 +00:00
|
|
|
struct virtio_pmd_ctrl ctrl;
|
|
|
|
int dlen[1];
|
|
|
|
int ret;
|
|
|
|
|
2015-06-11 15:53:24 +00:00
|
|
|
if (!vtpci_with_feature(hw, VIRTIO_NET_F_CTRL_RX)) {
|
2017-01-27 15:16:32 +00:00
|
|
|
PMD_INIT_LOG(INFO, "host does not support rx control");
|
2015-06-11 15:53:24 +00:00
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
2014-11-08 04:26:15 +00:00
|
|
|
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");
|
|
|
|
}
|
|
|
|
|
|
|
|
static void
|
|
|
|
virtio_dev_allmulticast_enable(struct rte_eth_dev *dev)
|
|
|
|
{
|
2015-02-09 01:13:56 +00:00
|
|
|
struct virtio_hw *hw = dev->data->dev_private;
|
2014-11-08 04:26:15 +00:00
|
|
|
struct virtio_pmd_ctrl ctrl;
|
|
|
|
int dlen[1];
|
|
|
|
int ret;
|
|
|
|
|
2015-06-11 15:53:24 +00:00
|
|
|
if (!vtpci_with_feature(hw, VIRTIO_NET_F_CTRL_RX)) {
|
2017-01-27 15:16:32 +00:00
|
|
|
PMD_INIT_LOG(INFO, "host does not support rx control");
|
2015-06-11 15:53:24 +00:00
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
2014-11-08 04:26:15 +00:00
|
|
|
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");
|
|
|
|
}
|
|
|
|
|
|
|
|
static void
|
|
|
|
virtio_dev_allmulticast_disable(struct rte_eth_dev *dev)
|
|
|
|
{
|
2015-02-09 01:13:56 +00:00
|
|
|
struct virtio_hw *hw = dev->data->dev_private;
|
2014-11-08 04:26:15 +00:00
|
|
|
struct virtio_pmd_ctrl ctrl;
|
|
|
|
int dlen[1];
|
|
|
|
int ret;
|
|
|
|
|
2015-06-11 15:53:24 +00:00
|
|
|
if (!vtpci_with_feature(hw, VIRTIO_NET_F_CTRL_RX)) {
|
2017-01-27 15:16:32 +00:00
|
|
|
PMD_INIT_LOG(INFO, "host does not support rx control");
|
2015-06-11 15:53:24 +00:00
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
2014-11-08 04:26:15 +00:00
|
|
|
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");
|
|
|
|
}
|
|
|
|
|
2016-10-09 03:38:26 +00:00
|
|
|
#define VLAN_TAG_LEN 4 /* 802.3ac tag (not DMA'd) */
|
|
|
|
static int
|
|
|
|
virtio_mtu_set(struct rte_eth_dev *dev, uint16_t mtu)
|
|
|
|
{
|
|
|
|
struct virtio_hw *hw = dev->data->dev_private;
|
|
|
|
uint32_t ether_hdr_len = ETHER_HDR_LEN + VLAN_TAG_LEN +
|
|
|
|
hw->vtnet_hdr_size;
|
|
|
|
uint32_t frame_size = mtu + ether_hdr_len;
|
2017-03-12 16:34:04 +00:00
|
|
|
uint32_t max_frame_size = hw->max_mtu + ether_hdr_len;
|
2016-10-09 03:38:26 +00:00
|
|
|
|
2017-03-12 16:34:04 +00:00
|
|
|
max_frame_size = RTE_MIN(max_frame_size, VIRTIO_MAX_RX_PKTLEN);
|
|
|
|
|
|
|
|
if (mtu < ETHER_MIN_MTU || frame_size > max_frame_size) {
|
2017-01-27 15:16:32 +00:00
|
|
|
PMD_INIT_LOG(ERR, "MTU should be between %d and %d",
|
2017-03-12 16:34:04 +00:00
|
|
|
ETHER_MIN_MTU, max_frame_size - ether_hdr_len);
|
2016-10-09 03:38:26 +00:00
|
|
|
return -EINVAL;
|
|
|
|
}
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
2017-01-17 07:10:25 +00:00
|
|
|
static int
|
|
|
|
virtio_dev_rx_queue_intr_enable(struct rte_eth_dev *dev, uint16_t queue_id)
|
|
|
|
{
|
|
|
|
struct virtnet_rx *rxvq = dev->data->rx_queues[queue_id];
|
|
|
|
struct virtqueue *vq = rxvq->vq;
|
|
|
|
|
|
|
|
virtqueue_enable_intr(vq);
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
static int
|
|
|
|
virtio_dev_rx_queue_intr_disable(struct rte_eth_dev *dev, uint16_t queue_id)
|
|
|
|
{
|
|
|
|
struct virtnet_rx *rxvq = dev->data->rx_queues[queue_id];
|
|
|
|
struct virtqueue *vq = rxvq->vq;
|
|
|
|
|
|
|
|
virtqueue_disable_intr(vq);
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
2013-09-18 10:00:00 +00:00
|
|
|
/*
|
|
|
|
* dev_ops for virtio, bare necessities for basic operation
|
|
|
|
*/
|
2015-04-07 21:21:03 +00:00
|
|
|
static const struct eth_dev_ops virtio_eth_dev_ops = {
|
2014-05-29 07:18:19 +00:00
|
|
|
.dev_configure = virtio_dev_configure,
|
|
|
|
.dev_start = virtio_dev_start,
|
|
|
|
.dev_stop = virtio_dev_stop,
|
|
|
|
.dev_close = virtio_dev_close,
|
2014-11-08 04:26:15 +00:00
|
|
|
.promiscuous_enable = virtio_dev_promiscuous_enable,
|
|
|
|
.promiscuous_disable = virtio_dev_promiscuous_disable,
|
|
|
|
.allmulticast_enable = virtio_dev_allmulticast_enable,
|
|
|
|
.allmulticast_disable = virtio_dev_allmulticast_disable,
|
2016-10-09 03:38:26 +00:00
|
|
|
.mtu_set = virtio_mtu_set,
|
2014-05-29 07:18:19 +00:00
|
|
|
.dev_infos_get = virtio_dev_info_get,
|
|
|
|
.stats_get = virtio_dev_stats_get,
|
2015-11-02 10:19:00 +00:00
|
|
|
.xstats_get = virtio_dev_xstats_get,
|
2016-06-15 15:25:32 +00:00
|
|
|
.xstats_get_names = virtio_dev_xstats_get_names,
|
2014-05-29 07:18:19 +00:00
|
|
|
.stats_reset = virtio_dev_stats_reset,
|
2015-11-02 10:19:00 +00:00
|
|
|
.xstats_reset = virtio_dev_stats_reset,
|
2014-05-29 07:18:19 +00:00
|
|
|
.link_update = virtio_dev_link_update,
|
|
|
|
.rx_queue_setup = virtio_dev_rx_queue_setup,
|
2017-01-17 07:10:25 +00:00
|
|
|
.rx_queue_intr_enable = virtio_dev_rx_queue_intr_enable,
|
|
|
|
.rx_queue_intr_disable = virtio_dev_rx_queue_intr_disable,
|
net/virtio: allocate queue at init stage
Queue allocation should be done once, since the queue related info (such
as vring addreess) will only be informed to the vhost-user backend once
without virtio device reset.
That means, if you allocate queues again after the vhost-user negotiation,
the vhost-user backend will not be informed any more. Leading to a state
that the vring info mismatches between virtio PMD driver and vhost-backend:
the driver switches to the new address has just been allocated, while the
vhost-backend still sticks to the old address has been assigned in the init
stage.
Unfortunately, that is exactly how the virtio driver is coded so far: queue
allocation is done at queue_setup stage (when rte_eth_tx/rx_queue_setup is
invoked). This is wrong, because queue_setup can be invoked several times.
For example,
$ start_testpmd.sh ... --txq=1 --rxq=1 ...
> port stop 0
> port config all txq 1 # just trigger the queue_setup callback again
> port config all rxq 1
> port start 0
The right way to do is allocate the queues in the init stage, so that the
vring info could be persistent with the vhost-user backend.
Besides that, we should allocate max_queue pairs the device supports, but
not nr queue pairs firstly configured, to make following case work.
$ start_testpmd.sh ... --txq=1 --rxq=1 ...
> port stop 0
> port config all txq 2
> port config all rxq 2
> port start 0
Since the allocation is switched to init stage, the free should also
moved from the rx/tx_queue_release to dev close stage. That leading we
could do nothing an empty rx/tx_queue_release() implementation.
Signed-off-by: Yuanhan Liu <yuanhan.liu@linux.intel.com>
2016-11-05 09:40:59 +00:00
|
|
|
.rx_queue_release = virtio_dev_queue_release,
|
2017-01-17 07:10:23 +00:00
|
|
|
.rx_descriptor_done = virtio_dev_rx_queue_done,
|
2014-05-29 07:18:19 +00:00
|
|
|
.tx_queue_setup = virtio_dev_tx_queue_setup,
|
net/virtio: allocate queue at init stage
Queue allocation should be done once, since the queue related info (such
as vring addreess) will only be informed to the vhost-user backend once
without virtio device reset.
That means, if you allocate queues again after the vhost-user negotiation,
the vhost-user backend will not be informed any more. Leading to a state
that the vring info mismatches between virtio PMD driver and vhost-backend:
the driver switches to the new address has just been allocated, while the
vhost-backend still sticks to the old address has been assigned in the init
stage.
Unfortunately, that is exactly how the virtio driver is coded so far: queue
allocation is done at queue_setup stage (when rte_eth_tx/rx_queue_setup is
invoked). This is wrong, because queue_setup can be invoked several times.
For example,
$ start_testpmd.sh ... --txq=1 --rxq=1 ...
> port stop 0
> port config all txq 1 # just trigger the queue_setup callback again
> port config all rxq 1
> port start 0
The right way to do is allocate the queues in the init stage, so that the
vring info could be persistent with the vhost-user backend.
Besides that, we should allocate max_queue pairs the device supports, but
not nr queue pairs firstly configured, to make following case work.
$ start_testpmd.sh ... --txq=1 --rxq=1 ...
> port stop 0
> port config all txq 2
> port config all rxq 2
> port start 0
Since the allocation is switched to init stage, the free should also
moved from the rx/tx_queue_release to dev close stage. That leading we
could do nothing an empty rx/tx_queue_release() implementation.
Signed-off-by: Yuanhan Liu <yuanhan.liu@linux.intel.com>
2016-11-05 09:40:59 +00:00
|
|
|
.tx_queue_release = virtio_dev_queue_release,
|
2014-05-29 07:18:20 +00:00
|
|
|
/* collect stats per queue */
|
2014-06-13 01:32:40 +00:00
|
|
|
.queue_stats_mapping_set = virtio_dev_queue_stats_mapping_set,
|
2015-02-09 01:14:02 +00:00
|
|
|
.vlan_filter_set = virtio_vlan_filter_set,
|
2015-02-09 01:14:03 +00:00
|
|
|
.mac_addr_add = virtio_mac_addr_add,
|
|
|
|
.mac_addr_remove = virtio_mac_addr_remove,
|
2015-02-09 01:14:04 +00:00
|
|
|
.mac_addr_set = virtio_mac_addr_set,
|
2013-09-18 10:00:00 +00:00
|
|
|
};
|
|
|
|
|
|
|
|
static inline int
|
|
|
|
virtio_dev_atomic_read_link_status(struct rte_eth_dev *dev,
|
|
|
|
struct rte_eth_link *link)
|
|
|
|
{
|
|
|
|
struct rte_eth_link *dst = link;
|
|
|
|
struct rte_eth_link *src = &(dev->data->dev_link);
|
|
|
|
|
|
|
|
if (rte_atomic64_cmpset((uint64_t *)dst, *(uint64_t *)dst,
|
|
|
|
*(uint64_t *)src) == 0)
|
2014-06-13 01:32:40 +00:00
|
|
|
return -1;
|
2013-09-18 10:00:00 +00:00
|
|
|
|
2014-06-13 01:32:40 +00:00
|
|
|
return 0;
|
2013-09-18 10:00:00 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Atomically writes the link status information into global
|
|
|
|
* structure rte_eth_dev.
|
|
|
|
*
|
|
|
|
* @param dev
|
|
|
|
* - Pointer to the structure rte_eth_dev to read from.
|
|
|
|
* - Pointer to the buffer to be saved with the link status.
|
|
|
|
*
|
|
|
|
* @return
|
|
|
|
* - On success, zero.
|
|
|
|
* - On failure, negative value.
|
|
|
|
*/
|
|
|
|
static inline int
|
|
|
|
virtio_dev_atomic_write_link_status(struct rte_eth_dev *dev,
|
|
|
|
struct rte_eth_link *link)
|
|
|
|
{
|
|
|
|
struct rte_eth_link *dst = &(dev->data->dev_link);
|
|
|
|
struct rte_eth_link *src = link;
|
|
|
|
|
|
|
|
if (rte_atomic64_cmpset((uint64_t *)dst, *(uint64_t *)dst,
|
|
|
|
*(uint64_t *)src) == 0)
|
2014-06-13 01:32:40 +00:00
|
|
|
return -1;
|
2013-09-18 10:00:00 +00:00
|
|
|
|
2014-06-13 01:32:40 +00:00
|
|
|
return 0;
|
2013-09-18 10:00:00 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
static void
|
2015-11-02 10:19:00 +00:00
|
|
|
virtio_update_stats(struct rte_eth_dev *dev, struct rte_eth_stats *stats)
|
2013-09-18 10:00:00 +00:00
|
|
|
{
|
2014-06-14 01:06:18 +00:00
|
|
|
unsigned i;
|
|
|
|
|
|
|
|
for (i = 0; i < dev->data->nb_tx_queues; i++) {
|
2016-06-01 16:12:13 +00:00
|
|
|
const struct virtnet_tx *txvq = dev->data->tx_queues[i];
|
2014-06-14 01:06:18 +00:00
|
|
|
if (txvq == NULL)
|
|
|
|
continue;
|
|
|
|
|
2016-06-01 16:12:13 +00:00
|
|
|
stats->opackets += txvq->stats.packets;
|
|
|
|
stats->obytes += txvq->stats.bytes;
|
|
|
|
stats->oerrors += txvq->stats.errors;
|
2014-06-14 01:06:18 +00:00
|
|
|
|
|
|
|
if (i < RTE_ETHDEV_QUEUE_STAT_CNTRS) {
|
2016-06-01 16:12:13 +00:00
|
|
|
stats->q_opackets[i] = txvq->stats.packets;
|
|
|
|
stats->q_obytes[i] = txvq->stats.bytes;
|
2014-06-14 01:06:18 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
for (i = 0; i < dev->data->nb_rx_queues; i++) {
|
2016-06-01 16:12:13 +00:00
|
|
|
const struct virtnet_rx *rxvq = dev->data->rx_queues[i];
|
2014-06-14 01:06:18 +00:00
|
|
|
if (rxvq == NULL)
|
|
|
|
continue;
|
|
|
|
|
2016-06-01 16:12:13 +00:00
|
|
|
stats->ipackets += rxvq->stats.packets;
|
|
|
|
stats->ibytes += rxvq->stats.bytes;
|
|
|
|
stats->ierrors += rxvq->stats.errors;
|
2014-06-14 01:06:18 +00:00
|
|
|
|
|
|
|
if (i < RTE_ETHDEV_QUEUE_STAT_CNTRS) {
|
2016-06-01 16:12:13 +00:00
|
|
|
stats->q_ipackets[i] = rxvq->stats.packets;
|
|
|
|
stats->q_ibytes[i] = rxvq->stats.bytes;
|
2014-06-14 01:06:18 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
stats->rx_nombuf = dev->data->rx_mbuf_alloc_failed;
|
2013-09-18 10:00:00 +00:00
|
|
|
}
|
|
|
|
|
2016-06-15 15:25:32 +00:00
|
|
|
static int virtio_dev_xstats_get_names(struct rte_eth_dev *dev,
|
|
|
|
struct rte_eth_xstat_name *xstats_names,
|
|
|
|
__rte_unused unsigned limit)
|
|
|
|
{
|
|
|
|
unsigned i;
|
|
|
|
unsigned count = 0;
|
|
|
|
unsigned t;
|
|
|
|
|
2016-06-01 16:12:13 +00:00
|
|
|
unsigned nstats = dev->data->nb_tx_queues * VIRTIO_NB_TXQ_XSTATS +
|
|
|
|
dev->data->nb_rx_queues * VIRTIO_NB_RXQ_XSTATS;
|
2016-06-15 15:25:32 +00:00
|
|
|
|
2016-06-20 10:43:32 +00:00
|
|
|
if (xstats_names != NULL) {
|
2016-06-15 15:25:32 +00:00
|
|
|
/* Note: limit checked in rte_eth_xstats_names() */
|
|
|
|
|
|
|
|
for (i = 0; i < dev->data->nb_rx_queues; i++) {
|
|
|
|
struct virtqueue *rxvq = dev->data->rx_queues[i];
|
|
|
|
if (rxvq == NULL)
|
|
|
|
continue;
|
2016-06-01 16:12:13 +00:00
|
|
|
for (t = 0; t < VIRTIO_NB_RXQ_XSTATS; t++) {
|
2016-06-15 15:25:32 +00:00
|
|
|
snprintf(xstats_names[count].name,
|
|
|
|
sizeof(xstats_names[count].name),
|
|
|
|
"rx_q%u_%s", i,
|
2016-06-01 16:12:13 +00:00
|
|
|
rte_virtio_rxq_stat_strings[t].name);
|
2016-06-15 15:25:32 +00:00
|
|
|
count++;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
for (i = 0; i < dev->data->nb_tx_queues; i++) {
|
|
|
|
struct virtqueue *txvq = dev->data->tx_queues[i];
|
|
|
|
if (txvq == NULL)
|
|
|
|
continue;
|
2016-06-01 16:12:13 +00:00
|
|
|
for (t = 0; t < VIRTIO_NB_TXQ_XSTATS; t++) {
|
2016-06-15 15:25:32 +00:00
|
|
|
snprintf(xstats_names[count].name,
|
|
|
|
sizeof(xstats_names[count].name),
|
|
|
|
"tx_q%u_%s", i,
|
2016-06-01 16:12:13 +00:00
|
|
|
rte_virtio_txq_stat_strings[t].name);
|
2016-06-15 15:25:32 +00:00
|
|
|
count++;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
return count;
|
|
|
|
}
|
|
|
|
return nstats;
|
|
|
|
}
|
|
|
|
|
2015-11-02 10:19:00 +00:00
|
|
|
static int
|
2016-06-15 15:25:33 +00:00
|
|
|
virtio_dev_xstats_get(struct rte_eth_dev *dev, struct rte_eth_xstat *xstats,
|
2015-11-02 10:19:00 +00:00
|
|
|
unsigned n)
|
|
|
|
{
|
|
|
|
unsigned i;
|
|
|
|
unsigned count = 0;
|
|
|
|
|
2016-06-01 16:12:13 +00:00
|
|
|
unsigned nstats = dev->data->nb_tx_queues * VIRTIO_NB_TXQ_XSTATS +
|
|
|
|
dev->data->nb_rx_queues * VIRTIO_NB_RXQ_XSTATS;
|
2015-11-02 10:19:00 +00:00
|
|
|
|
|
|
|
if (n < nstats)
|
|
|
|
return nstats;
|
|
|
|
|
|
|
|
for (i = 0; i < dev->data->nb_rx_queues; i++) {
|
2016-06-01 16:12:13 +00:00
|
|
|
struct virtnet_rx *rxvq = dev->data->rx_queues[i];
|
2015-11-02 10:19:00 +00:00
|
|
|
|
|
|
|
if (rxvq == NULL)
|
|
|
|
continue;
|
|
|
|
|
|
|
|
unsigned t;
|
|
|
|
|
2016-06-01 16:12:13 +00:00
|
|
|
for (t = 0; t < VIRTIO_NB_RXQ_XSTATS; t++) {
|
2015-11-02 10:19:00 +00:00
|
|
|
xstats[count].value = *(uint64_t *)(((char *)rxvq) +
|
2016-06-01 16:12:13 +00:00
|
|
|
rte_virtio_rxq_stat_strings[t].offset);
|
ethdev: fix extended statistics name index
The function rte_eth_xstats_get() return an array of tuples (id,
value). The value is the statistic counter, while the id references a
name in the array returned by rte_eth_xstats_get_name().
Today, each 'id' returned by rte_eth_xstats_get() is equal to the index
in the returned array, making this value useless. It also prevents a
driver from having different indexes for names and value, like in the
example below:
rte_eth_xstats_get_name() returns:
0: "rx0_stat"
1: "rx1_stat"
2: ...
7: "rx7_stat"
8: "tx0_stat"
9: "tx1_stat"
...
15: "tx7_stat"
rte_eth_xstats_get() returns:
0: id=0, val=<stat> ("rx0_stat")
1: id=1, val=<stat> ("rx1_stat")
2: id=8, val=<stat> ("tx0_stat")
3: id=9, val=<stat> ("tx1_stat")
This patch fixes the drivers to set the 'id' in their ethdev->xstats_get()
(except e1000 which was already doing it), and fixes ethdev by not setting
the 'id' field to the index of the table for pmd-specific stats: instead,
they should just be shifted by the max number of generic statistics.
Fixes: bd6aa172cf35 ("ethdev: fetch extended statistics with integer ids")
Signed-off-by: Olivier Matz <olivier.matz@6wind.com>
Acked-by: Remy Horton <remy.horton@intel.com>
2016-12-16 09:44:13 +00:00
|
|
|
xstats[count].id = count;
|
2015-11-02 10:19:00 +00:00
|
|
|
count++;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
for (i = 0; i < dev->data->nb_tx_queues; i++) {
|
2016-06-01 16:12:13 +00:00
|
|
|
struct virtnet_tx *txvq = dev->data->tx_queues[i];
|
2015-11-02 10:19:00 +00:00
|
|
|
|
|
|
|
if (txvq == NULL)
|
|
|
|
continue;
|
|
|
|
|
|
|
|
unsigned t;
|
|
|
|
|
2016-06-01 16:12:13 +00:00
|
|
|
for (t = 0; t < VIRTIO_NB_TXQ_XSTATS; t++) {
|
2015-11-02 10:19:00 +00:00
|
|
|
xstats[count].value = *(uint64_t *)(((char *)txvq) +
|
2016-06-01 16:12:13 +00:00
|
|
|
rte_virtio_txq_stat_strings[t].offset);
|
ethdev: fix extended statistics name index
The function rte_eth_xstats_get() return an array of tuples (id,
value). The value is the statistic counter, while the id references a
name in the array returned by rte_eth_xstats_get_name().
Today, each 'id' returned by rte_eth_xstats_get() is equal to the index
in the returned array, making this value useless. It also prevents a
driver from having different indexes for names and value, like in the
example below:
rte_eth_xstats_get_name() returns:
0: "rx0_stat"
1: "rx1_stat"
2: ...
7: "rx7_stat"
8: "tx0_stat"
9: "tx1_stat"
...
15: "tx7_stat"
rte_eth_xstats_get() returns:
0: id=0, val=<stat> ("rx0_stat")
1: id=1, val=<stat> ("rx1_stat")
2: id=8, val=<stat> ("tx0_stat")
3: id=9, val=<stat> ("tx1_stat")
This patch fixes the drivers to set the 'id' in their ethdev->xstats_get()
(except e1000 which was already doing it), and fixes ethdev by not setting
the 'id' field to the index of the table for pmd-specific stats: instead,
they should just be shifted by the max number of generic statistics.
Fixes: bd6aa172cf35 ("ethdev: fetch extended statistics with integer ids")
Signed-off-by: Olivier Matz <olivier.matz@6wind.com>
Acked-by: Remy Horton <remy.horton@intel.com>
2016-12-16 09:44:13 +00:00
|
|
|
xstats[count].id = count;
|
2015-11-02 10:19:00 +00:00
|
|
|
count++;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
return count;
|
|
|
|
}
|
|
|
|
|
2017-10-10 20:20:18 +00:00
|
|
|
static int
|
2015-11-02 10:19:00 +00:00
|
|
|
virtio_dev_stats_get(struct rte_eth_dev *dev, struct rte_eth_stats *stats)
|
|
|
|
{
|
|
|
|
virtio_update_stats(dev, stats);
|
2017-10-10 20:20:18 +00:00
|
|
|
|
|
|
|
return 0;
|
2015-11-02 10:19:00 +00:00
|
|
|
}
|
|
|
|
|
2013-09-18 10:00:00 +00:00
|
|
|
static void
|
|
|
|
virtio_dev_stats_reset(struct rte_eth_dev *dev)
|
|
|
|
{
|
2014-06-14 01:06:18 +00:00
|
|
|
unsigned int i;
|
|
|
|
|
|
|
|
for (i = 0; i < dev->data->nb_tx_queues; i++) {
|
2016-06-01 16:12:13 +00:00
|
|
|
struct virtnet_tx *txvq = dev->data->tx_queues[i];
|
2014-06-14 01:06:18 +00:00
|
|
|
if (txvq == NULL)
|
|
|
|
continue;
|
|
|
|
|
2016-06-01 16:12:13 +00:00
|
|
|
txvq->stats.packets = 0;
|
|
|
|
txvq->stats.bytes = 0;
|
|
|
|
txvq->stats.errors = 0;
|
|
|
|
txvq->stats.multicast = 0;
|
|
|
|
txvq->stats.broadcast = 0;
|
|
|
|
memset(txvq->stats.size_bins, 0,
|
|
|
|
sizeof(txvq->stats.size_bins[0]) * 8);
|
2014-06-14 01:06:18 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
for (i = 0; i < dev->data->nb_rx_queues; i++) {
|
2016-06-01 16:12:13 +00:00
|
|
|
struct virtnet_rx *rxvq = dev->data->rx_queues[i];
|
2014-06-14 01:06:18 +00:00
|
|
|
if (rxvq == NULL)
|
|
|
|
continue;
|
|
|
|
|
2016-06-01 16:12:13 +00:00
|
|
|
rxvq->stats.packets = 0;
|
|
|
|
rxvq->stats.bytes = 0;
|
|
|
|
rxvq->stats.errors = 0;
|
|
|
|
rxvq->stats.multicast = 0;
|
|
|
|
rxvq->stats.broadcast = 0;
|
|
|
|
memset(rxvq->stats.size_bins, 0,
|
|
|
|
sizeof(rxvq->stats.size_bins[0]) * 8);
|
2014-06-14 01:06:18 +00:00
|
|
|
}
|
2013-09-18 10:00:00 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
static void
|
|
|
|
virtio_set_hwaddr(struct virtio_hw *hw)
|
|
|
|
{
|
|
|
|
vtpci_write_dev_config(hw,
|
|
|
|
offsetof(struct virtio_net_config, mac),
|
|
|
|
&hw->mac_addr, ETHER_ADDR_LEN);
|
|
|
|
}
|
|
|
|
|
|
|
|
static void
|
|
|
|
virtio_get_hwaddr(struct virtio_hw *hw)
|
|
|
|
{
|
|
|
|
if (vtpci_with_feature(hw, VIRTIO_NET_F_MAC)) {
|
|
|
|
vtpci_read_dev_config(hw,
|
|
|
|
offsetof(struct virtio_net_config, mac),
|
|
|
|
&hw->mac_addr, ETHER_ADDR_LEN);
|
|
|
|
} else {
|
|
|
|
eth_random_addr(&hw->mac_addr[0]);
|
|
|
|
virtio_set_hwaddr(hw);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2017-05-05 00:40:00 +00:00
|
|
|
static int
|
2015-02-09 01:14:03 +00:00
|
|
|
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];
|
|
|
|
|
2015-06-11 15:53:25 +00:00
|
|
|
if (!vtpci_with_feature(hw, VIRTIO_NET_F_CTRL_MAC_ADDR)) {
|
2016-04-19 05:22:37 +00:00
|
|
|
PMD_DRV_LOG(INFO, "host does not support mac table");
|
2017-05-05 00:40:00 +00:00
|
|
|
return -1;
|
2015-06-11 15:53:25 +00:00
|
|
|
}
|
|
|
|
|
2015-02-09 01:14:03 +00:00
|
|
|
ctrl.hdr.class = VIRTIO_NET_CTRL_MAC;
|
|
|
|
ctrl.hdr.cmd = VIRTIO_NET_CTRL_MAC_TABLE_SET;
|
|
|
|
|
|
|
|
len[0] = uc->entries * ETHER_ADDR_LEN + sizeof(uc->entries);
|
|
|
|
memcpy(ctrl.data, uc, len[0]);
|
|
|
|
|
|
|
|
len[1] = mc->entries * ETHER_ADDR_LEN + sizeof(mc->entries);
|
|
|
|
memcpy(ctrl.data + len[0], mc, len[1]);
|
|
|
|
|
|
|
|
err = virtio_send_command(hw->cvq, &ctrl, len, 2);
|
|
|
|
if (err != 0)
|
|
|
|
PMD_DRV_LOG(NOTICE, "mac table set failed: %d", err);
|
2017-05-05 00:40:00 +00:00
|
|
|
return err;
|
2015-02-09 01:14:03 +00:00
|
|
|
}
|
|
|
|
|
2017-05-05 00:40:00 +00:00
|
|
|
static int
|
2015-02-09 01:14:03 +00:00
|
|
|
virtio_mac_addr_add(struct rte_eth_dev *dev, struct ether_addr *mac_addr,
|
|
|
|
uint32_t index, uint32_t vmdq __rte_unused)
|
|
|
|
{
|
|
|
|
struct virtio_hw *hw = dev->data->dev_private;
|
|
|
|
const struct ether_addr *addrs = dev->data->mac_addrs;
|
|
|
|
unsigned int i;
|
|
|
|
struct virtio_net_ctrl_mac *uc, *mc;
|
|
|
|
|
|
|
|
if (index >= VIRTIO_MAX_MAC_ADDRS) {
|
|
|
|
PMD_DRV_LOG(ERR, "mac address index %u out of range", index);
|
2017-05-05 00:40:00 +00:00
|
|
|
return -EINVAL;
|
2015-02-09 01:14:03 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
uc = alloca(VIRTIO_MAX_MAC_ADDRS * ETHER_ADDR_LEN + sizeof(uc->entries));
|
|
|
|
uc->entries = 0;
|
|
|
|
mc = alloca(VIRTIO_MAX_MAC_ADDRS * ETHER_ADDR_LEN + sizeof(mc->entries));
|
|
|
|
mc->entries = 0;
|
|
|
|
|
|
|
|
for (i = 0; i < VIRTIO_MAX_MAC_ADDRS; i++) {
|
|
|
|
const struct ether_addr *addr
|
|
|
|
= (i == index) ? mac_addr : addrs + i;
|
|
|
|
struct virtio_net_ctrl_mac *tbl
|
|
|
|
= is_multicast_ether_addr(addr) ? mc : uc;
|
|
|
|
|
|
|
|
memcpy(&tbl->macs[tbl->entries++], addr, ETHER_ADDR_LEN);
|
|
|
|
}
|
|
|
|
|
2017-05-05 00:40:00 +00:00
|
|
|
return virtio_mac_table_set(hw, uc, mc);
|
2015-02-09 01:14:03 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
static void
|
|
|
|
virtio_mac_addr_remove(struct rte_eth_dev *dev, uint32_t index)
|
|
|
|
{
|
|
|
|
struct virtio_hw *hw = dev->data->dev_private;
|
|
|
|
struct ether_addr *addrs = dev->data->mac_addrs;
|
|
|
|
struct virtio_net_ctrl_mac *uc, *mc;
|
|
|
|
unsigned int i;
|
|
|
|
|
|
|
|
if (index >= VIRTIO_MAX_MAC_ADDRS) {
|
|
|
|
PMD_DRV_LOG(ERR, "mac address index %u out of range", index);
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
uc = alloca(VIRTIO_MAX_MAC_ADDRS * ETHER_ADDR_LEN + sizeof(uc->entries));
|
|
|
|
uc->entries = 0;
|
|
|
|
mc = alloca(VIRTIO_MAX_MAC_ADDRS * ETHER_ADDR_LEN + sizeof(mc->entries));
|
|
|
|
mc->entries = 0;
|
|
|
|
|
|
|
|
for (i = 0; i < VIRTIO_MAX_MAC_ADDRS; i++) {
|
|
|
|
struct virtio_net_ctrl_mac *tbl;
|
|
|
|
|
|
|
|
if (i == index || is_zero_ether_addr(addrs + i))
|
|
|
|
continue;
|
|
|
|
|
|
|
|
tbl = is_multicast_ether_addr(addrs + i) ? mc : uc;
|
|
|
|
memcpy(&tbl->macs[tbl->entries++], addrs + i, ETHER_ADDR_LEN);
|
|
|
|
}
|
|
|
|
|
|
|
|
virtio_mac_table_set(hw, uc, mc);
|
|
|
|
}
|
|
|
|
|
2015-02-09 01:14:04 +00:00
|
|
|
static void
|
|
|
|
virtio_mac_addr_set(struct rte_eth_dev *dev, struct ether_addr *mac_addr)
|
|
|
|
{
|
|
|
|
struct virtio_hw *hw = dev->data->dev_private;
|
|
|
|
|
|
|
|
memcpy(hw->mac_addr, mac_addr, ETHER_ADDR_LEN);
|
|
|
|
|
|
|
|
/* Use atomic update if available */
|
|
|
|
if (vtpci_with_feature(hw, VIRTIO_NET_F_CTRL_MAC_ADDR)) {
|
|
|
|
struct virtio_pmd_ctrl ctrl;
|
|
|
|
int len = ETHER_ADDR_LEN;
|
|
|
|
|
|
|
|
ctrl.hdr.class = VIRTIO_NET_CTRL_MAC;
|
|
|
|
ctrl.hdr.cmd = VIRTIO_NET_CTRL_MAC_ADDR_SET;
|
|
|
|
|
|
|
|
memcpy(ctrl.data, mac_addr, ETHER_ADDR_LEN);
|
|
|
|
virtio_send_command(hw->cvq, &ctrl, &len, 1);
|
|
|
|
} else if (vtpci_with_feature(hw, VIRTIO_NET_F_MAC))
|
|
|
|
virtio_set_hwaddr(hw);
|
|
|
|
}
|
|
|
|
|
2015-02-09 01:14:02 +00:00
|
|
|
static int
|
|
|
|
virtio_vlan_filter_set(struct rte_eth_dev *dev, uint16_t vlan_id, int on)
|
|
|
|
{
|
|
|
|
struct virtio_hw *hw = dev->data->dev_private;
|
|
|
|
struct virtio_pmd_ctrl ctrl;
|
|
|
|
int len;
|
|
|
|
|
|
|
|
if (!vtpci_with_feature(hw, VIRTIO_NET_F_CTRL_VLAN))
|
|
|
|
return -ENOTSUP;
|
|
|
|
|
|
|
|
ctrl.hdr.class = VIRTIO_NET_CTRL_VLAN;
|
|
|
|
ctrl.hdr.cmd = on ? VIRTIO_NET_CTRL_VLAN_ADD : VIRTIO_NET_CTRL_VLAN_DEL;
|
|
|
|
memcpy(ctrl.data, &vlan_id, sizeof(vlan_id));
|
|
|
|
len = sizeof(vlan_id);
|
|
|
|
|
|
|
|
return virtio_send_command(hw->cvq, &ctrl, &len, 1);
|
|
|
|
}
|
2013-09-18 10:00:00 +00:00
|
|
|
|
virtio: support specification 1.0
Modern (v1.0) virtio pci device defines several pci capabilities.
Each cap has a configure structure corresponding to it, and the
cap.bar and cap.offset fields tell us where to find it.
Firstly, we map the pci resources by rte_eal_pci_map_device().
We then could easily locate a cfg structure by:
cfg_addr = dev->mem_resources[cap.bar].addr + cap.offset;
Therefore, the entrance of enabling modern (v1.0) pci device support
is to iterate the pci capability lists, and to locate some configs
we care; and they are:
- common cfg
For generic virtio and virtqueue configuration, such as setting/getting
features, enabling a specific queue, and so on.
- nofity cfg
Combining with `queue_notify_off' from common cfg, we could use it to
notify a specific virt queue.
- device cfg
Where virtio_net_config structure is located.
- isr cfg
Where to read isr (interrupt status).
If any of above cap is not found, we fallback to the legacy virtio
handling.
If succeed, hw->vtpci_ops is assigned to modern_ops, where all
operations are implemented by reading/writing a (or few) specific
configuration space from above 4 cfg structures. And that's basically
how this patch works.
Besides those changes, virtio 1.0 introduces a new status field:
FEATURES_OK, which is set after features negotiation is done.
Last, set the VIRTIO_F_VERSION_1 feature flag.
Signed-off-by: Yuanhan Liu <yuanhan.liu@linux.intel.com>
Tested-by: Qian Xu <qian.q.xu@intel.com>
Reviewed-by: Tetsuya Mukawa <mukawa@igel.co.jp>
Tested-by: Tetsuya Mukawa <mukawa@igel.co.jp>
Acked-by: Huawei Xie <huawei.xie@intel.com>
2016-02-02 13:48:19 +00:00
|
|
|
static int
|
2016-10-13 14:16:02 +00:00
|
|
|
virtio_negotiate_features(struct virtio_hw *hw, uint64_t req_features)
|
2013-09-18 10:00:00 +00:00
|
|
|
{
|
2016-02-02 13:48:16 +00:00
|
|
|
uint64_t host_features;
|
2013-09-18 10:00:00 +00:00
|
|
|
|
|
|
|
/* Prepare guest_features: feature that driver wants to support */
|
2016-02-02 13:48:16 +00:00
|
|
|
PMD_INIT_LOG(DEBUG, "guest_features before negotiate = %" PRIx64,
|
2016-10-13 14:16:02 +00:00
|
|
|
req_features);
|
2013-09-18 10:00:00 +00:00
|
|
|
|
|
|
|
/* Read device(host) feature bits */
|
net/virtio: store PCI operators pointer locally
We used to store the vtpci_ops at virtio_hw structure. The struct,
however, is stored in shared memory. That means only one value is
allowed. For the multiple process model, however, the address of
vtpci_ops should be different among different processes.
Take virtio PMD as example, the vtpci_ops is set by the primary
process, based on its own process space. If we access that address
from the secondary process, that would be an illegal memory access,
A crash then might happen.
To make the multiple process model work, we need store the vtpci_ops
in local memory but not in a shared memory. This is what the patch
does: a local virtio_hw_internal array of size RTE_MAX_ETHPORTS is
allocated. This new structure is used to store all these kind of
info in a non-shared memory. Current, we have:
- vtpci_ops
- rte_pci_ioport
- virtio pci mapped memory, such as common_cfg.
The later two will be done in coming patches. Later patches would also
set them correctly for secondary process, so that the multiple process
model could work.
Signed-off-by: Yuanhan Liu <yuanhan.liu@linux.intel.com>
2017-01-06 10:16:17 +00:00
|
|
|
host_features = VTPCI_OPS(hw)->get_features(hw);
|
2016-02-02 13:48:16 +00:00
|
|
|
PMD_INIT_LOG(DEBUG, "host_features before negotiate = %" PRIx64,
|
2014-06-14 01:06:25 +00:00
|
|
|
host_features);
|
2013-09-18 10:00:00 +00:00
|
|
|
|
2017-03-12 16:34:04 +00:00
|
|
|
/* If supported, ensure MTU value is valid before acknowledging it. */
|
|
|
|
if (host_features & req_features & (1ULL << VIRTIO_NET_F_MTU)) {
|
|
|
|
struct virtio_net_config config;
|
|
|
|
|
|
|
|
vtpci_read_dev_config(hw,
|
|
|
|
offsetof(struct virtio_net_config, mtu),
|
|
|
|
&config.mtu, sizeof(config.mtu));
|
|
|
|
|
|
|
|
if (config.mtu < ETHER_MIN_MTU)
|
|
|
|
req_features &= ~(1ULL << VIRTIO_NET_F_MTU);
|
|
|
|
}
|
|
|
|
|
2014-05-29 07:18:20 +00:00
|
|
|
/*
|
|
|
|
* Negotiate features: Subset of device feature bits are written back
|
|
|
|
* guest feature bits.
|
|
|
|
*/
|
2016-10-13 14:16:02 +00:00
|
|
|
hw->guest_features = req_features;
|
2014-06-14 01:06:25 +00:00
|
|
|
hw->guest_features = vtpci_negotiate_features(hw, host_features);
|
2016-02-02 13:48:16 +00:00
|
|
|
PMD_INIT_LOG(DEBUG, "features after negotiate = %" PRIx64,
|
2014-05-29 07:18:20 +00:00
|
|
|
hw->guest_features);
|
virtio: support specification 1.0
Modern (v1.0) virtio pci device defines several pci capabilities.
Each cap has a configure structure corresponding to it, and the
cap.bar and cap.offset fields tell us where to find it.
Firstly, we map the pci resources by rte_eal_pci_map_device().
We then could easily locate a cfg structure by:
cfg_addr = dev->mem_resources[cap.bar].addr + cap.offset;
Therefore, the entrance of enabling modern (v1.0) pci device support
is to iterate the pci capability lists, and to locate some configs
we care; and they are:
- common cfg
For generic virtio and virtqueue configuration, such as setting/getting
features, enabling a specific queue, and so on.
- nofity cfg
Combining with `queue_notify_off' from common cfg, we could use it to
notify a specific virt queue.
- device cfg
Where virtio_net_config structure is located.
- isr cfg
Where to read isr (interrupt status).
If any of above cap is not found, we fallback to the legacy virtio
handling.
If succeed, hw->vtpci_ops is assigned to modern_ops, where all
operations are implemented by reading/writing a (or few) specific
configuration space from above 4 cfg structures. And that's basically
how this patch works.
Besides those changes, virtio 1.0 introduces a new status field:
FEATURES_OK, which is set after features negotiation is done.
Last, set the VIRTIO_F_VERSION_1 feature flag.
Signed-off-by: Yuanhan Liu <yuanhan.liu@linux.intel.com>
Tested-by: Qian Xu <qian.q.xu@intel.com>
Reviewed-by: Tetsuya Mukawa <mukawa@igel.co.jp>
Tested-by: Tetsuya Mukawa <mukawa@igel.co.jp>
Acked-by: Huawei Xie <huawei.xie@intel.com>
2016-02-02 13:48:19 +00:00
|
|
|
|
|
|
|
if (hw->modern) {
|
|
|
|
if (!vtpci_with_feature(hw, VIRTIO_F_VERSION_1)) {
|
|
|
|
PMD_INIT_LOG(ERR,
|
|
|
|
"VIRTIO_F_VERSION_1 features is not enabled.");
|
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
vtpci_set_status(hw, VIRTIO_CONFIG_STATUS_FEATURES_OK);
|
|
|
|
if (!(vtpci_get_status(hw) & VIRTIO_CONFIG_STATUS_FEATURES_OK)) {
|
|
|
|
PMD_INIT_LOG(ERR,
|
|
|
|
"failed to set FEATURES_OK status!");
|
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2016-10-13 14:16:02 +00:00
|
|
|
hw->req_guest_features = req_features;
|
|
|
|
|
virtio: support specification 1.0
Modern (v1.0) virtio pci device defines several pci capabilities.
Each cap has a configure structure corresponding to it, and the
cap.bar and cap.offset fields tell us where to find it.
Firstly, we map the pci resources by rte_eal_pci_map_device().
We then could easily locate a cfg structure by:
cfg_addr = dev->mem_resources[cap.bar].addr + cap.offset;
Therefore, the entrance of enabling modern (v1.0) pci device support
is to iterate the pci capability lists, and to locate some configs
we care; and they are:
- common cfg
For generic virtio and virtqueue configuration, such as setting/getting
features, enabling a specific queue, and so on.
- nofity cfg
Combining with `queue_notify_off' from common cfg, we could use it to
notify a specific virt queue.
- device cfg
Where virtio_net_config structure is located.
- isr cfg
Where to read isr (interrupt status).
If any of above cap is not found, we fallback to the legacy virtio
handling.
If succeed, hw->vtpci_ops is assigned to modern_ops, where all
operations are implemented by reading/writing a (or few) specific
configuration space from above 4 cfg structures. And that's basically
how this patch works.
Besides those changes, virtio 1.0 introduces a new status field:
FEATURES_OK, which is set after features negotiation is done.
Last, set the VIRTIO_F_VERSION_1 feature flag.
Signed-off-by: Yuanhan Liu <yuanhan.liu@linux.intel.com>
Tested-by: Qian Xu <qian.q.xu@intel.com>
Reviewed-by: Tetsuya Mukawa <mukawa@igel.co.jp>
Tested-by: Tetsuya Mukawa <mukawa@igel.co.jp>
Acked-by: Huawei Xie <huawei.xie@intel.com>
2016-02-02 13:48:19 +00:00
|
|
|
return 0;
|
2013-09-18 10:00:00 +00:00
|
|
|
}
|
|
|
|
|
2015-02-09 01:13:53 +00:00
|
|
|
/*
|
|
|
|
* Process Virtio Config changed interrupt and call the callback
|
|
|
|
* if link state changed.
|
|
|
|
*/
|
2017-03-31 19:44:58 +00:00
|
|
|
void
|
2017-04-06 12:42:22 +00:00
|
|
|
virtio_interrupt_handler(void *param)
|
2015-02-09 01:13:53 +00:00
|
|
|
{
|
|
|
|
struct rte_eth_dev *dev = param;
|
2015-02-09 01:13:56 +00:00
|
|
|
struct virtio_hw *hw = dev->data->dev_private;
|
2015-02-09 01:13:53 +00:00
|
|
|
uint8_t isr;
|
|
|
|
|
|
|
|
/* Read interrupt status which clears interrupt */
|
|
|
|
isr = vtpci_isr(hw);
|
|
|
|
PMD_DRV_LOG(INFO, "interrupt status = %#x", isr);
|
|
|
|
|
2017-04-06 12:42:22 +00:00
|
|
|
if (rte_intr_enable(dev->intr_handle) < 0)
|
2015-02-09 01:13:53 +00:00
|
|
|
PMD_DRV_LOG(ERR, "interrupt enable failed");
|
|
|
|
|
|
|
|
if (isr & VIRTIO_PCI_ISR_CONFIG) {
|
|
|
|
if (virtio_dev_link_update(dev, 0) == 0)
|
|
|
|
_rte_eth_dev_callback_process(dev,
|
2017-06-15 12:29:50 +00:00
|
|
|
RTE_ETH_EVENT_INTR_LSC,
|
|
|
|
NULL, NULL);
|
2015-02-09 01:13:53 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
}
|
|
|
|
|
2017-09-07 12:13:44 +00:00
|
|
|
/* set rx and tx handlers according to what is supported */
|
2015-03-27 13:23:15 +00:00
|
|
|
static void
|
2017-09-07 12:13:44 +00:00
|
|
|
set_rxtx_funcs(struct rte_eth_dev *eth_dev)
|
2015-03-27 13:23:15 +00:00
|
|
|
{
|
|
|
|
struct virtio_hw *hw = eth_dev->data->dev_private;
|
2017-09-07 12:13:44 +00:00
|
|
|
|
2017-09-07 12:13:46 +00:00
|
|
|
if (hw->use_simple_rx) {
|
2017-09-07 12:13:44 +00:00
|
|
|
PMD_INIT_LOG(INFO, "virtio: using simple Rx path on port %u",
|
|
|
|
eth_dev->data->port_id);
|
|
|
|
eth_dev->rx_pkt_burst = virtio_recv_pkts_vec;
|
|
|
|
} else if (vtpci_with_feature(hw, VIRTIO_NET_F_MRG_RXBUF)) {
|
|
|
|
PMD_INIT_LOG(INFO,
|
|
|
|
"virtio: using mergeable buffer Rx path on port %u",
|
|
|
|
eth_dev->data->port_id);
|
2015-03-27 13:23:15 +00:00
|
|
|
eth_dev->rx_pkt_burst = &virtio_recv_mergeable_pkts;
|
2017-09-07 12:13:44 +00:00
|
|
|
} else {
|
|
|
|
PMD_INIT_LOG(INFO, "virtio: using standard Rx path on port %u",
|
|
|
|
eth_dev->data->port_id);
|
2015-03-27 13:23:15 +00:00
|
|
|
eth_dev->rx_pkt_burst = &virtio_recv_pkts;
|
2017-09-07 12:13:44 +00:00
|
|
|
}
|
|
|
|
|
2017-09-07 12:13:46 +00:00
|
|
|
if (hw->use_simple_tx) {
|
2017-09-07 12:13:44 +00:00
|
|
|
PMD_INIT_LOG(INFO, "virtio: using simple Tx path on port %u",
|
|
|
|
eth_dev->data->port_id);
|
|
|
|
eth_dev->tx_pkt_burst = virtio_xmit_pkts_simple;
|
|
|
|
} else {
|
|
|
|
PMD_INIT_LOG(INFO, "virtio: using standard Tx path on port %u",
|
|
|
|
eth_dev->data->port_id);
|
|
|
|
eth_dev->tx_pkt_burst = virtio_xmit_pkts;
|
|
|
|
}
|
2015-03-27 13:23:15 +00:00
|
|
|
}
|
|
|
|
|
2017-01-17 08:00:03 +00:00
|
|
|
/* Only support 1:1 queue/interrupt mapping so far.
|
|
|
|
* TODO: support n:1 queue/interrupt mapping when there are limited number of
|
|
|
|
* interrupt vectors (<N+1).
|
|
|
|
*/
|
|
|
|
static int
|
|
|
|
virtio_queues_bind_intr(struct rte_eth_dev *dev)
|
|
|
|
{
|
|
|
|
uint32_t i;
|
|
|
|
struct virtio_hw *hw = dev->data->dev_private;
|
|
|
|
|
2017-01-27 15:16:32 +00:00
|
|
|
PMD_INIT_LOG(INFO, "queue/interrupt binding");
|
2017-01-17 08:00:03 +00:00
|
|
|
for (i = 0; i < dev->data->nb_rx_queues; ++i) {
|
|
|
|
dev->intr_handle->intr_vec[i] = i + 1;
|
|
|
|
if (VTPCI_OPS(hw)->set_queue_irq(hw, hw->vqs[i * 2], i + 1) ==
|
|
|
|
VIRTIO_MSI_NO_VECTOR) {
|
|
|
|
PMD_DRV_LOG(ERR, "failed to set queue vector");
|
|
|
|
return -EBUSY;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
2017-01-17 07:10:28 +00:00
|
|
|
static void
|
|
|
|
virtio_queues_unbind_intr(struct rte_eth_dev *dev)
|
|
|
|
{
|
|
|
|
uint32_t i;
|
|
|
|
struct virtio_hw *hw = dev->data->dev_private;
|
|
|
|
|
2017-01-27 15:16:32 +00:00
|
|
|
PMD_INIT_LOG(INFO, "queue/interrupt unbinding");
|
2017-01-17 07:10:28 +00:00
|
|
|
for (i = 0; i < dev->data->nb_rx_queues; ++i)
|
|
|
|
VTPCI_OPS(hw)->set_queue_irq(hw,
|
|
|
|
hw->vqs[i * VTNET_CQ],
|
|
|
|
VIRTIO_MSI_NO_VECTOR);
|
|
|
|
}
|
|
|
|
|
2017-01-17 08:00:03 +00:00
|
|
|
static int
|
|
|
|
virtio_configure_intr(struct rte_eth_dev *dev)
|
|
|
|
{
|
|
|
|
struct virtio_hw *hw = dev->data->dev_private;
|
|
|
|
|
|
|
|
if (!rte_intr_cap_multiple(dev->intr_handle)) {
|
|
|
|
PMD_INIT_LOG(ERR, "Multiple intr vector not supported");
|
|
|
|
return -ENOTSUP;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (rte_intr_efd_enable(dev->intr_handle, dev->data->nb_rx_queues)) {
|
|
|
|
PMD_INIT_LOG(ERR, "Fail to create eventfd");
|
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (!dev->intr_handle->intr_vec) {
|
|
|
|
dev->intr_handle->intr_vec =
|
|
|
|
rte_zmalloc("intr_vec",
|
|
|
|
hw->max_queue_pairs * sizeof(int), 0);
|
|
|
|
if (!dev->intr_handle->intr_vec) {
|
|
|
|
PMD_INIT_LOG(ERR, "Failed to allocate %u rxq vectors",
|
|
|
|
hw->max_queue_pairs);
|
|
|
|
return -ENOMEM;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
/* Re-register callback to update max_intr */
|
|
|
|
rte_intr_callback_unregister(dev->intr_handle,
|
|
|
|
virtio_interrupt_handler,
|
|
|
|
dev);
|
|
|
|
rte_intr_callback_register(dev->intr_handle,
|
|
|
|
virtio_interrupt_handler,
|
|
|
|
dev);
|
|
|
|
|
|
|
|
/* DO NOT try to remove this! This function will enable msix, or QEMU
|
|
|
|
* will encounter SIGSEGV when DRIVER_OK is sent.
|
|
|
|
* And for legacy devices, this should be done before queue/vec binding
|
|
|
|
* to change the config size from 20 to 24, or VIRTIO_MSI_QUEUE_VECTOR
|
|
|
|
* (22) will be ignored.
|
|
|
|
*/
|
|
|
|
if (rte_intr_enable(dev->intr_handle) < 0) {
|
|
|
|
PMD_DRV_LOG(ERR, "interrupt enable failed");
|
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (virtio_queues_bind_intr(dev) < 0) {
|
|
|
|
PMD_INIT_LOG(ERR, "Failed to bind queue/interrupt");
|
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
2016-10-13 14:16:02 +00:00
|
|
|
/* reset device and renegotiate features if needed */
|
2016-10-13 14:16:00 +00:00
|
|
|
static int
|
2016-10-13 14:16:02 +00:00
|
|
|
virtio_init_device(struct rte_eth_dev *eth_dev, uint64_t req_features)
|
2013-09-18 10:00:00 +00:00
|
|
|
{
|
2015-02-09 01:13:56 +00:00
|
|
|
struct virtio_hw *hw = eth_dev->data->dev_private;
|
2014-05-29 07:18:20 +00:00
|
|
|
struct virtio_net_config *config;
|
|
|
|
struct virtio_net_config local_config;
|
2017-01-12 05:37:00 +00:00
|
|
|
struct rte_pci_device *pci_dev = NULL;
|
net/virtio: allocate queue at init stage
Queue allocation should be done once, since the queue related info (such
as vring addreess) will only be informed to the vhost-user backend once
without virtio device reset.
That means, if you allocate queues again after the vhost-user negotiation,
the vhost-user backend will not be informed any more. Leading to a state
that the vring info mismatches between virtio PMD driver and vhost-backend:
the driver switches to the new address has just been allocated, while the
vhost-backend still sticks to the old address has been assigned in the init
stage.
Unfortunately, that is exactly how the virtio driver is coded so far: queue
allocation is done at queue_setup stage (when rte_eth_tx/rx_queue_setup is
invoked). This is wrong, because queue_setup can be invoked several times.
For example,
$ start_testpmd.sh ... --txq=1 --rxq=1 ...
> port stop 0
> port config all txq 1 # just trigger the queue_setup callback again
> port config all rxq 1
> port start 0
The right way to do is allocate the queues in the init stage, so that the
vring info could be persistent with the vhost-user backend.
Besides that, we should allocate max_queue pairs the device supports, but
not nr queue pairs firstly configured, to make following case work.
$ start_testpmd.sh ... --txq=1 --rxq=1 ...
> port stop 0
> port config all txq 2
> port config all rxq 2
> port start 0
Since the allocation is switched to init stage, the free should also
moved from the rx/tx_queue_release to dev close stage. That leading we
could do nothing an empty rx/tx_queue_release() implementation.
Signed-off-by: Yuanhan Liu <yuanhan.liu@linux.intel.com>
2016-11-05 09:40:59 +00:00
|
|
|
int ret;
|
2013-09-18 10:00:00 +00:00
|
|
|
|
|
|
|
/* Reset the device although not necessary at startup */
|
|
|
|
vtpci_reset(hw);
|
|
|
|
|
|
|
|
/* Tell the host we've noticed this device. */
|
|
|
|
vtpci_set_status(hw, VIRTIO_CONFIG_STATUS_ACK);
|
|
|
|
|
|
|
|
/* Tell the host we've known how to drive the device. */
|
|
|
|
vtpci_set_status(hw, VIRTIO_CONFIG_STATUS_DRIVER);
|
2016-10-13 14:16:02 +00:00
|
|
|
if (virtio_negotiate_features(hw, req_features) < 0)
|
virtio: support specification 1.0
Modern (v1.0) virtio pci device defines several pci capabilities.
Each cap has a configure structure corresponding to it, and the
cap.bar and cap.offset fields tell us where to find it.
Firstly, we map the pci resources by rte_eal_pci_map_device().
We then could easily locate a cfg structure by:
cfg_addr = dev->mem_resources[cap.bar].addr + cap.offset;
Therefore, the entrance of enabling modern (v1.0) pci device support
is to iterate the pci capability lists, and to locate some configs
we care; and they are:
- common cfg
For generic virtio and virtqueue configuration, such as setting/getting
features, enabling a specific queue, and so on.
- nofity cfg
Combining with `queue_notify_off' from common cfg, we could use it to
notify a specific virt queue.
- device cfg
Where virtio_net_config structure is located.
- isr cfg
Where to read isr (interrupt status).
If any of above cap is not found, we fallback to the legacy virtio
handling.
If succeed, hw->vtpci_ops is assigned to modern_ops, where all
operations are implemented by reading/writing a (or few) specific
configuration space from above 4 cfg structures. And that's basically
how this patch works.
Besides those changes, virtio 1.0 introduces a new status field:
FEATURES_OK, which is set after features negotiation is done.
Last, set the VIRTIO_F_VERSION_1 feature flag.
Signed-off-by: Yuanhan Liu <yuanhan.liu@linux.intel.com>
Tested-by: Qian Xu <qian.q.xu@intel.com>
Reviewed-by: Tetsuya Mukawa <mukawa@igel.co.jp>
Tested-by: Tetsuya Mukawa <mukawa@igel.co.jp>
Acked-by: Huawei Xie <huawei.xie@intel.com>
2016-02-02 13:48:19 +00:00
|
|
|
return -1;
|
2014-05-29 07:18:19 +00:00
|
|
|
|
2017-04-21 02:28:09 +00:00
|
|
|
if (!hw->virtio_user_dev) {
|
2017-05-15 10:24:03 +00:00
|
|
|
pci_dev = RTE_ETH_DEV_TO_PCI(eth_dev);
|
2017-01-17 07:10:21 +00:00
|
|
|
rte_eth_copy_pci_info(eth_dev, pci_dev);
|
|
|
|
}
|
|
|
|
|
2017-04-27 07:35:37 +00:00
|
|
|
eth_dev->data->dev_flags = RTE_ETH_DEV_DETACHABLE;
|
2017-03-09 20:28:02 +00:00
|
|
|
/* If host does not support both status and MSI-X then disable LSC */
|
|
|
|
if (vtpci_with_feature(hw, VIRTIO_NET_F_STATUS) && hw->use_msix)
|
2016-10-13 14:16:00 +00:00
|
|
|
eth_dev->data->dev_flags |= RTE_ETH_DEV_INTR_LSC;
|
2017-03-09 20:28:02 +00:00
|
|
|
else
|
|
|
|
eth_dev->data->dev_flags &= ~RTE_ETH_DEV_INTR_LSC;
|
2015-08-28 16:23:37 +00:00
|
|
|
|
2013-09-18 10:00:00 +00:00
|
|
|
/* Setting up rx_header size for the device */
|
virtio: support specification 1.0
Modern (v1.0) virtio pci device defines several pci capabilities.
Each cap has a configure structure corresponding to it, and the
cap.bar and cap.offset fields tell us where to find it.
Firstly, we map the pci resources by rte_eal_pci_map_device().
We then could easily locate a cfg structure by:
cfg_addr = dev->mem_resources[cap.bar].addr + cap.offset;
Therefore, the entrance of enabling modern (v1.0) pci device support
is to iterate the pci capability lists, and to locate some configs
we care; and they are:
- common cfg
For generic virtio and virtqueue configuration, such as setting/getting
features, enabling a specific queue, and so on.
- nofity cfg
Combining with `queue_notify_off' from common cfg, we could use it to
notify a specific virt queue.
- device cfg
Where virtio_net_config structure is located.
- isr cfg
Where to read isr (interrupt status).
If any of above cap is not found, we fallback to the legacy virtio
handling.
If succeed, hw->vtpci_ops is assigned to modern_ops, where all
operations are implemented by reading/writing a (or few) specific
configuration space from above 4 cfg structures. And that's basically
how this patch works.
Besides those changes, virtio 1.0 introduces a new status field:
FEATURES_OK, which is set after features negotiation is done.
Last, set the VIRTIO_F_VERSION_1 feature flag.
Signed-off-by: Yuanhan Liu <yuanhan.liu@linux.intel.com>
Tested-by: Qian Xu <qian.q.xu@intel.com>
Reviewed-by: Tetsuya Mukawa <mukawa@igel.co.jp>
Tested-by: Tetsuya Mukawa <mukawa@igel.co.jp>
Acked-by: Huawei Xie <huawei.xie@intel.com>
2016-02-02 13:48:19 +00:00
|
|
|
if (vtpci_with_feature(hw, VIRTIO_NET_F_MRG_RXBUF) ||
|
|
|
|
vtpci_with_feature(hw, VIRTIO_F_VERSION_1))
|
2013-09-18 10:00:00 +00:00
|
|
|
hw->vtnet_hdr_size = sizeof(struct virtio_net_hdr_mrg_rxbuf);
|
2015-03-27 13:23:15 +00:00
|
|
|
else
|
2013-09-18 10:00:00 +00:00
|
|
|
hw->vtnet_hdr_size = sizeof(struct virtio_net_hdr);
|
|
|
|
|
|
|
|
/* Copy the permanent MAC address to: virtio_hw */
|
|
|
|
virtio_get_hwaddr(hw);
|
|
|
|
ether_addr_copy((struct ether_addr *) hw->mac_addr,
|
|
|
|
ð_dev->data->mac_addrs[0]);
|
2014-06-13 01:32:40 +00:00
|
|
|
PMD_INIT_LOG(DEBUG,
|
2014-06-14 01:06:19 +00:00
|
|
|
"PORT MAC: %02X:%02X:%02X:%02X:%02X:%02X",
|
2014-06-13 01:32:40 +00:00
|
|
|
hw->mac_addr[0], hw->mac_addr[1], hw->mac_addr[2],
|
|
|
|
hw->mac_addr[3], hw->mac_addr[4], hw->mac_addr[5]);
|
2013-09-18 10:00:00 +00:00
|
|
|
|
2014-05-29 07:18:20 +00:00
|
|
|
if (vtpci_with_feature(hw, VIRTIO_NET_F_CTRL_VQ)) {
|
|
|
|
config = &local_config;
|
|
|
|
|
2015-10-22 12:35:53 +00:00
|
|
|
vtpci_read_dev_config(hw,
|
|
|
|
offsetof(struct virtio_net_config, mac),
|
|
|
|
&config->mac, sizeof(config->mac));
|
|
|
|
|
2014-05-29 07:18:20 +00:00
|
|
|
if (vtpci_with_feature(hw, VIRTIO_NET_F_STATUS)) {
|
2015-10-22 12:35:53 +00:00
|
|
|
vtpci_read_dev_config(hw,
|
|
|
|
offsetof(struct virtio_net_config, status),
|
|
|
|
&config->status, sizeof(config->status));
|
2014-05-29 07:18:20 +00:00
|
|
|
} else {
|
2014-06-13 01:32:40 +00:00
|
|
|
PMD_INIT_LOG(DEBUG,
|
2014-06-14 01:06:19 +00:00
|
|
|
"VIRTIO_NET_F_STATUS is not supported");
|
2014-05-29 07:18:20 +00:00
|
|
|
config->status = 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (vtpci_with_feature(hw, VIRTIO_NET_F_MQ)) {
|
2015-10-22 12:35:53 +00:00
|
|
|
vtpci_read_dev_config(hw,
|
|
|
|
offsetof(struct virtio_net_config, max_virtqueue_pairs),
|
|
|
|
&config->max_virtqueue_pairs,
|
|
|
|
sizeof(config->max_virtqueue_pairs));
|
2014-05-29 07:18:20 +00:00
|
|
|
} else {
|
2014-06-13 01:32:40 +00:00
|
|
|
PMD_INIT_LOG(DEBUG,
|
2014-06-14 01:06:19 +00:00
|
|
|
"VIRTIO_NET_F_MQ is not supported");
|
2014-05-29 07:18:20 +00:00
|
|
|
config->max_virtqueue_pairs = 1;
|
|
|
|
}
|
2013-09-18 10:00:00 +00:00
|
|
|
|
2016-10-13 14:16:01 +00:00
|
|
|
hw->max_queue_pairs = config->max_virtqueue_pairs;
|
2014-05-29 07:18:20 +00:00
|
|
|
|
2017-03-12 16:34:04 +00:00
|
|
|
if (vtpci_with_feature(hw, VIRTIO_NET_F_MTU)) {
|
|
|
|
vtpci_read_dev_config(hw,
|
|
|
|
offsetof(struct virtio_net_config, mtu),
|
|
|
|
&config->mtu,
|
|
|
|
sizeof(config->mtu));
|
|
|
|
|
|
|
|
/*
|
|
|
|
* MTU value has already been checked at negotiation
|
|
|
|
* time, but check again in case it has changed since
|
|
|
|
* then, which should not happen.
|
|
|
|
*/
|
|
|
|
if (config->mtu < ETHER_MIN_MTU) {
|
|
|
|
PMD_INIT_LOG(ERR, "invalid max MTU value (%u)",
|
|
|
|
config->mtu);
|
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
|
|
|
|
hw->max_mtu = config->mtu;
|
|
|
|
/* Set initial MTU to maximum one supported by vhost */
|
|
|
|
eth_dev->data->mtu = config->mtu;
|
|
|
|
|
|
|
|
} else {
|
|
|
|
hw->max_mtu = VIRTIO_MAX_RX_PKTLEN - ETHER_HDR_LEN -
|
|
|
|
VLAN_TAG_LEN - hw->vtnet_hdr_size;
|
|
|
|
}
|
|
|
|
|
2014-06-14 01:06:19 +00:00
|
|
|
PMD_INIT_LOG(DEBUG, "config->max_virtqueue_pairs=%d",
|
2014-05-29 07:18:20 +00:00
|
|
|
config->max_virtqueue_pairs);
|
2014-06-14 01:06:19 +00:00
|
|
|
PMD_INIT_LOG(DEBUG, "config->status=%d", config->status);
|
2014-05-29 07:18:20 +00:00
|
|
|
PMD_INIT_LOG(DEBUG,
|
2014-06-14 01:06:19 +00:00
|
|
|
"PORT MAC: %02X:%02X:%02X:%02X:%02X:%02X",
|
2014-05-29 07:18:20 +00:00
|
|
|
config->mac[0], config->mac[1],
|
|
|
|
config->mac[2], config->mac[3],
|
|
|
|
config->mac[4], config->mac[5]);
|
|
|
|
} else {
|
2016-10-13 14:16:01 +00:00
|
|
|
PMD_INIT_LOG(DEBUG, "config->max_virtqueue_pairs=1");
|
|
|
|
hw->max_queue_pairs = 1;
|
2014-05-29 07:18:20 +00:00
|
|
|
}
|
|
|
|
|
net/virtio: allocate queue at init stage
Queue allocation should be done once, since the queue related info (such
as vring addreess) will only be informed to the vhost-user backend once
without virtio device reset.
That means, if you allocate queues again after the vhost-user negotiation,
the vhost-user backend will not be informed any more. Leading to a state
that the vring info mismatches between virtio PMD driver and vhost-backend:
the driver switches to the new address has just been allocated, while the
vhost-backend still sticks to the old address has been assigned in the init
stage.
Unfortunately, that is exactly how the virtio driver is coded so far: queue
allocation is done at queue_setup stage (when rte_eth_tx/rx_queue_setup is
invoked). This is wrong, because queue_setup can be invoked several times.
For example,
$ start_testpmd.sh ... --txq=1 --rxq=1 ...
> port stop 0
> port config all txq 1 # just trigger the queue_setup callback again
> port config all rxq 1
> port start 0
The right way to do is allocate the queues in the init stage, so that the
vring info could be persistent with the vhost-user backend.
Besides that, we should allocate max_queue pairs the device supports, but
not nr queue pairs firstly configured, to make following case work.
$ start_testpmd.sh ... --txq=1 --rxq=1 ...
> port stop 0
> port config all txq 2
> port config all rxq 2
> port start 0
Since the allocation is switched to init stage, the free should also
moved from the rx/tx_queue_release to dev close stage. That leading we
could do nothing an empty rx/tx_queue_release() implementation.
Signed-off-by: Yuanhan Liu <yuanhan.liu@linux.intel.com>
2016-11-05 09:40:59 +00:00
|
|
|
ret = virtio_alloc_queues(eth_dev);
|
|
|
|
if (ret < 0)
|
|
|
|
return ret;
|
2017-01-17 08:00:03 +00:00
|
|
|
|
|
|
|
if (eth_dev->data->dev_conf.intr_conf.rxq) {
|
|
|
|
if (virtio_configure_intr(eth_dev) < 0) {
|
|
|
|
PMD_INIT_LOG(ERR, "failed to configure interrupt");
|
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2016-11-05 09:41:02 +00:00
|
|
|
vtpci_reinit_complete(hw);
|
net/virtio: allocate queue at init stage
Queue allocation should be done once, since the queue related info (such
as vring addreess) will only be informed to the vhost-user backend once
without virtio device reset.
That means, if you allocate queues again after the vhost-user negotiation,
the vhost-user backend will not be informed any more. Leading to a state
that the vring info mismatches between virtio PMD driver and vhost-backend:
the driver switches to the new address has just been allocated, while the
vhost-backend still sticks to the old address has been assigned in the init
stage.
Unfortunately, that is exactly how the virtio driver is coded so far: queue
allocation is done at queue_setup stage (when rte_eth_tx/rx_queue_setup is
invoked). This is wrong, because queue_setup can be invoked several times.
For example,
$ start_testpmd.sh ... --txq=1 --rxq=1 ...
> port stop 0
> port config all txq 1 # just trigger the queue_setup callback again
> port config all rxq 1
> port start 0
The right way to do is allocate the queues in the init stage, so that the
vring info could be persistent with the vhost-user backend.
Besides that, we should allocate max_queue pairs the device supports, but
not nr queue pairs firstly configured, to make following case work.
$ start_testpmd.sh ... --txq=1 --rxq=1 ...
> port stop 0
> port config all txq 2
> port config all rxq 2
> port start 0
Since the allocation is switched to init stage, the free should also
moved from the rx/tx_queue_release to dev close stage. That leading we
could do nothing an empty rx/tx_queue_release() implementation.
Signed-off-by: Yuanhan Liu <yuanhan.liu@linux.intel.com>
2016-11-05 09:40:59 +00:00
|
|
|
|
net/virtio-user: add virtual device
Add a new virtual device named virtio-user, which can be used just like
eth_ring, eth_null, etc. To reuse the code of original virtio, we do
some adjustment in virtio_ethdev.c, such as remove key _static_ of
eth_virtio_dev_init() so that it can be reused in virtual device; and
we add some check to make sure it will not crash.
Configured parameters include:
- queues (optional, 1 by default), number of queue pairs, multi-queue
not supported for now.
- cq (optional, 0 by default), not supported for now.
- mac (optional), random value will be given if not specified.
- queue_size (optional, 256 by default), size of virtqueues.
- path (madatory), path of vhost user.
When enable CONFIG_RTE_VIRTIO_USER (enabled by default), the compiled
library can be used in both VM and container environment.
Examples:
path_vhost=<path_to_vhost_user> # use vhost-user as a backend
sudo ./examples/l2fwd/build/l2fwd -c 0x100000 -n 4 \
--socket-mem 0,1024 --no-pci --file-prefix=l2fwd \
--vdev=virtio-user0,mac=00:01:02:03:04:05,path=$path_vhost -- -p 0x1
Known issues:
- Control queue and multi-queue are not supported yet.
- Cannot work with --huge-unlink.
- Cannot work with no-huge.
- Cannot work when there are more than VHOST_MEMORY_MAX_NREGIONS(8)
hugepages.
- Root privilege is a must (mainly becase of sorting hugepages according
to physical address).
- Applications should not use file name like HUGEFILE_FMT ("%smap_%d").
- Cannot work with vhost-net backend.
Signed-off-by: Huawei Xie <huawei.xie@intel.com>
Signed-off-by: Jianfeng Tan <jianfeng.tan@intel.com>
Acked-by: Neil Horman <nhorman@tuxdriver.com>
Acked-by: Yuanhan Liu <yuanhan.liu@linux.intel.com>
2016-06-15 09:03:25 +00:00
|
|
|
if (pci_dev)
|
|
|
|
PMD_INIT_LOG(DEBUG, "port %d vendorID=0x%x deviceID=0x%x",
|
2013-09-18 10:00:00 +00:00
|
|
|
eth_dev->data->port_id, pci_dev->id.vendor_id,
|
|
|
|
pci_dev->id.device_id);
|
2015-02-09 01:13:53 +00:00
|
|
|
|
2016-10-13 14:16:00 +00:00
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
net/virtio: fix multiple process support
The introduce of virtio 1.0 support brings yet another set of ops, badly,
it's not handled correctly, that it breaks the multiple process support.
The issue is the data/function pointer may vary from different processes,
and the old used to do one time set (for primary process only). That
said, the function pointer the secondary process saw is actually from the
primary process space. Accessing it could likely result to a crash.
Kudos to the last patches, we now be able to maintain those info that may
vary among different process locally, meaning every process could have its
own copy for each of them, with the correct value set. And this is what
this patch does:
- remap the PCI (IO port for legacy device and memory map for modern
device)
- set vtpci_ops correctly
After that, multiple process would work like a charm. (At least, it
passed my fuzzy test)
Fixes: b8f04520ad71 ("virtio: use PCI ioport API")
Fixes: d5bbeefca826 ("virtio: introduce PCI implementation structure")
Fixes: 6ba1f63b5ab0 ("virtio: support specification 1.0")
Cc: stable@dpdk.org
Reported-by: Juho Snellman <jsnell@iki.fi>
Reported-by: Yaron Illouz <yaroni@radcom.com>
Signed-off-by: Yuanhan Liu <yuanhan.liu@linux.intel.com>
2017-01-06 10:16:19 +00:00
|
|
|
/*
|
|
|
|
* Remap the PCI device again (IO port map for legacy device and
|
|
|
|
* memory map for modern device), so that the secondary process
|
|
|
|
* could have the PCI initiated correctly.
|
|
|
|
*/
|
|
|
|
static int
|
|
|
|
virtio_remap_pci(struct rte_pci_device *pci_dev, struct virtio_hw *hw)
|
|
|
|
{
|
|
|
|
if (hw->modern) {
|
|
|
|
/*
|
|
|
|
* We don't have to re-parse the PCI config space, since
|
2017-05-04 14:48:59 +00:00
|
|
|
* rte_pci_map_device() makes sure the mapped address
|
net/virtio: fix multiple process support
The introduce of virtio 1.0 support brings yet another set of ops, badly,
it's not handled correctly, that it breaks the multiple process support.
The issue is the data/function pointer may vary from different processes,
and the old used to do one time set (for primary process only). That
said, the function pointer the secondary process saw is actually from the
primary process space. Accessing it could likely result to a crash.
Kudos to the last patches, we now be able to maintain those info that may
vary among different process locally, meaning every process could have its
own copy for each of them, with the correct value set. And this is what
this patch does:
- remap the PCI (IO port for legacy device and memory map for modern
device)
- set vtpci_ops correctly
After that, multiple process would work like a charm. (At least, it
passed my fuzzy test)
Fixes: b8f04520ad71 ("virtio: use PCI ioport API")
Fixes: d5bbeefca826 ("virtio: introduce PCI implementation structure")
Fixes: 6ba1f63b5ab0 ("virtio: support specification 1.0")
Cc: stable@dpdk.org
Reported-by: Juho Snellman <jsnell@iki.fi>
Reported-by: Yaron Illouz <yaroni@radcom.com>
Signed-off-by: Yuanhan Liu <yuanhan.liu@linux.intel.com>
2017-01-06 10:16:19 +00:00
|
|
|
* in secondary process would equal to the one mapped in
|
|
|
|
* the primary process: error will be returned if that
|
|
|
|
* requirement is not met.
|
|
|
|
*
|
|
|
|
* That said, we could simply reuse all cap pointers
|
|
|
|
* (such as dev_cfg, common_cfg, etc.) parsed from the
|
|
|
|
* primary process, which is stored in shared memory.
|
|
|
|
*/
|
2017-05-04 14:48:59 +00:00
|
|
|
if (rte_pci_map_device(pci_dev)) {
|
net/virtio: fix multiple process support
The introduce of virtio 1.0 support brings yet another set of ops, badly,
it's not handled correctly, that it breaks the multiple process support.
The issue is the data/function pointer may vary from different processes,
and the old used to do one time set (for primary process only). That
said, the function pointer the secondary process saw is actually from the
primary process space. Accessing it could likely result to a crash.
Kudos to the last patches, we now be able to maintain those info that may
vary among different process locally, meaning every process could have its
own copy for each of them, with the correct value set. And this is what
this patch does:
- remap the PCI (IO port for legacy device and memory map for modern
device)
- set vtpci_ops correctly
After that, multiple process would work like a charm. (At least, it
passed my fuzzy test)
Fixes: b8f04520ad71 ("virtio: use PCI ioport API")
Fixes: d5bbeefca826 ("virtio: introduce PCI implementation structure")
Fixes: 6ba1f63b5ab0 ("virtio: support specification 1.0")
Cc: stable@dpdk.org
Reported-by: Juho Snellman <jsnell@iki.fi>
Reported-by: Yaron Illouz <yaroni@radcom.com>
Signed-off-by: Yuanhan Liu <yuanhan.liu@linux.intel.com>
2017-01-06 10:16:19 +00:00
|
|
|
PMD_INIT_LOG(DEBUG, "failed to map pci device!");
|
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
} else {
|
2017-05-04 14:48:59 +00:00
|
|
|
if (rte_pci_ioport_map(pci_dev, 0, VTPCI_IO(hw)) < 0)
|
net/virtio: fix multiple process support
The introduce of virtio 1.0 support brings yet another set of ops, badly,
it's not handled correctly, that it breaks the multiple process support.
The issue is the data/function pointer may vary from different processes,
and the old used to do one time set (for primary process only). That
said, the function pointer the secondary process saw is actually from the
primary process space. Accessing it could likely result to a crash.
Kudos to the last patches, we now be able to maintain those info that may
vary among different process locally, meaning every process could have its
own copy for each of them, with the correct value set. And this is what
this patch does:
- remap the PCI (IO port for legacy device and memory map for modern
device)
- set vtpci_ops correctly
After that, multiple process would work like a charm. (At least, it
passed my fuzzy test)
Fixes: b8f04520ad71 ("virtio: use PCI ioport API")
Fixes: d5bbeefca826 ("virtio: introduce PCI implementation structure")
Fixes: 6ba1f63b5ab0 ("virtio: support specification 1.0")
Cc: stable@dpdk.org
Reported-by: Juho Snellman <jsnell@iki.fi>
Reported-by: Yaron Illouz <yaroni@radcom.com>
Signed-off-by: Yuanhan Liu <yuanhan.liu@linux.intel.com>
2017-01-06 10:16:19 +00:00
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
static void
|
|
|
|
virtio_set_vtpci_ops(struct virtio_hw *hw)
|
|
|
|
{
|
2017-01-17 22:13:00 +00:00
|
|
|
#ifdef RTE_VIRTIO_USER
|
net/virtio: fix multiple process support
The introduce of virtio 1.0 support brings yet another set of ops, badly,
it's not handled correctly, that it breaks the multiple process support.
The issue is the data/function pointer may vary from different processes,
and the old used to do one time set (for primary process only). That
said, the function pointer the secondary process saw is actually from the
primary process space. Accessing it could likely result to a crash.
Kudos to the last patches, we now be able to maintain those info that may
vary among different process locally, meaning every process could have its
own copy for each of them, with the correct value set. And this is what
this patch does:
- remap the PCI (IO port for legacy device and memory map for modern
device)
- set vtpci_ops correctly
After that, multiple process would work like a charm. (At least, it
passed my fuzzy test)
Fixes: b8f04520ad71 ("virtio: use PCI ioport API")
Fixes: d5bbeefca826 ("virtio: introduce PCI implementation structure")
Fixes: 6ba1f63b5ab0 ("virtio: support specification 1.0")
Cc: stable@dpdk.org
Reported-by: Juho Snellman <jsnell@iki.fi>
Reported-by: Yaron Illouz <yaroni@radcom.com>
Signed-off-by: Yuanhan Liu <yuanhan.liu@linux.intel.com>
2017-01-06 10:16:19 +00:00
|
|
|
if (hw->virtio_user_dev)
|
|
|
|
VTPCI_OPS(hw) = &virtio_user_ops;
|
2017-01-17 22:13:00 +00:00
|
|
|
else
|
|
|
|
#endif
|
|
|
|
if (hw->modern)
|
net/virtio: fix multiple process support
The introduce of virtio 1.0 support brings yet another set of ops, badly,
it's not handled correctly, that it breaks the multiple process support.
The issue is the data/function pointer may vary from different processes,
and the old used to do one time set (for primary process only). That
said, the function pointer the secondary process saw is actually from the
primary process space. Accessing it could likely result to a crash.
Kudos to the last patches, we now be able to maintain those info that may
vary among different process locally, meaning every process could have its
own copy for each of them, with the correct value set. And this is what
this patch does:
- remap the PCI (IO port for legacy device and memory map for modern
device)
- set vtpci_ops correctly
After that, multiple process would work like a charm. (At least, it
passed my fuzzy test)
Fixes: b8f04520ad71 ("virtio: use PCI ioport API")
Fixes: d5bbeefca826 ("virtio: introduce PCI implementation structure")
Fixes: 6ba1f63b5ab0 ("virtio: support specification 1.0")
Cc: stable@dpdk.org
Reported-by: Juho Snellman <jsnell@iki.fi>
Reported-by: Yaron Illouz <yaroni@radcom.com>
Signed-off-by: Yuanhan Liu <yuanhan.liu@linux.intel.com>
2017-01-06 10:16:19 +00:00
|
|
|
VTPCI_OPS(hw) = &modern_ops;
|
|
|
|
else
|
|
|
|
VTPCI_OPS(hw) = &legacy_ops;
|
|
|
|
}
|
|
|
|
|
2016-10-13 14:16:00 +00:00
|
|
|
/*
|
|
|
|
* This function is based on probe() function in virtio_pci.c
|
|
|
|
* It returns 0 on success.
|
|
|
|
*/
|
|
|
|
int
|
|
|
|
eth_virtio_dev_init(struct rte_eth_dev *eth_dev)
|
|
|
|
{
|
|
|
|
struct virtio_hw *hw = eth_dev->data->dev_private;
|
|
|
|
int ret;
|
|
|
|
|
|
|
|
RTE_BUILD_BUG_ON(RTE_PKTMBUF_HEADROOM < sizeof(struct virtio_net_hdr_mrg_rxbuf));
|
|
|
|
|
|
|
|
eth_dev->dev_ops = &virtio_eth_dev_ops;
|
|
|
|
|
|
|
|
if (rte_eal_process_type() == RTE_PROC_SECONDARY) {
|
net/virtio: fix multiple process support
The introduce of virtio 1.0 support brings yet another set of ops, badly,
it's not handled correctly, that it breaks the multiple process support.
The issue is the data/function pointer may vary from different processes,
and the old used to do one time set (for primary process only). That
said, the function pointer the secondary process saw is actually from the
primary process space. Accessing it could likely result to a crash.
Kudos to the last patches, we now be able to maintain those info that may
vary among different process locally, meaning every process could have its
own copy for each of them, with the correct value set. And this is what
this patch does:
- remap the PCI (IO port for legacy device and memory map for modern
device)
- set vtpci_ops correctly
After that, multiple process would work like a charm. (At least, it
passed my fuzzy test)
Fixes: b8f04520ad71 ("virtio: use PCI ioport API")
Fixes: d5bbeefca826 ("virtio: introduce PCI implementation structure")
Fixes: 6ba1f63b5ab0 ("virtio: support specification 1.0")
Cc: stable@dpdk.org
Reported-by: Juho Snellman <jsnell@iki.fi>
Reported-by: Yaron Illouz <yaroni@radcom.com>
Signed-off-by: Yuanhan Liu <yuanhan.liu@linux.intel.com>
2017-01-06 10:16:19 +00:00
|
|
|
if (!hw->virtio_user_dev) {
|
2017-05-15 10:24:03 +00:00
|
|
|
ret = virtio_remap_pci(RTE_ETH_DEV_TO_PCI(eth_dev), hw);
|
net/virtio: fix multiple process support
The introduce of virtio 1.0 support brings yet another set of ops, badly,
it's not handled correctly, that it breaks the multiple process support.
The issue is the data/function pointer may vary from different processes,
and the old used to do one time set (for primary process only). That
said, the function pointer the secondary process saw is actually from the
primary process space. Accessing it could likely result to a crash.
Kudos to the last patches, we now be able to maintain those info that may
vary among different process locally, meaning every process could have its
own copy for each of them, with the correct value set. And this is what
this patch does:
- remap the PCI (IO port for legacy device and memory map for modern
device)
- set vtpci_ops correctly
After that, multiple process would work like a charm. (At least, it
passed my fuzzy test)
Fixes: b8f04520ad71 ("virtio: use PCI ioport API")
Fixes: d5bbeefca826 ("virtio: introduce PCI implementation structure")
Fixes: 6ba1f63b5ab0 ("virtio: support specification 1.0")
Cc: stable@dpdk.org
Reported-by: Juho Snellman <jsnell@iki.fi>
Reported-by: Yaron Illouz <yaroni@radcom.com>
Signed-off-by: Yuanhan Liu <yuanhan.liu@linux.intel.com>
2017-01-06 10:16:19 +00:00
|
|
|
if (ret)
|
|
|
|
return ret;
|
|
|
|
}
|
|
|
|
|
|
|
|
virtio_set_vtpci_ops(hw);
|
2017-09-07 12:13:44 +00:00
|
|
|
set_rxtx_funcs(eth_dev);
|
|
|
|
|
2016-10-13 14:16:00 +00:00
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
/* Allocate memory for storing MAC addresses */
|
|
|
|
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",
|
|
|
|
VIRTIO_MAX_MAC_ADDRS * ETHER_ADDR_LEN);
|
|
|
|
return -ENOMEM;
|
|
|
|
}
|
|
|
|
|
2017-01-22 08:47:00 +00:00
|
|
|
hw->port_id = eth_dev->data->port_id;
|
2016-12-23 15:58:02 +00:00
|
|
|
/* For virtio_user case the hw->virtio_user_dev is populated by
|
|
|
|
* virtio_user_eth_dev_alloc() before eth_virtio_dev_init() is called.
|
|
|
|
*/
|
|
|
|
if (!hw->virtio_user_dev) {
|
2017-05-15 10:24:03 +00:00
|
|
|
ret = vtpci_init(RTE_ETH_DEV_TO_PCI(eth_dev), hw);
|
2016-10-13 14:16:00 +00:00
|
|
|
if (ret)
|
|
|
|
return ret;
|
|
|
|
}
|
|
|
|
|
2016-10-13 14:16:02 +00:00
|
|
|
/* reset device and negotiate default features */
|
2016-10-13 14:16:08 +00:00
|
|
|
ret = virtio_init_device(eth_dev, VIRTIO_PMD_DEFAULT_GUEST_FEATURES);
|
2016-10-13 14:16:00 +00:00
|
|
|
if (ret < 0)
|
|
|
|
return ret;
|
|
|
|
|
2015-02-09 01:13:53 +00:00
|
|
|
/* Setup interrupt callback */
|
2016-05-09 16:35:57 +00:00
|
|
|
if (eth_dev->data->dev_flags & RTE_ETH_DEV_INTR_LSC)
|
2017-01-12 05:31:57 +00:00
|
|
|
rte_intr_callback_register(eth_dev->intr_handle,
|
2016-10-13 14:16:00 +00:00
|
|
|
virtio_interrupt_handler, eth_dev);
|
2015-02-09 01:13:58 +00:00
|
|
|
|
2014-05-29 07:18:19 +00:00
|
|
|
return 0;
|
2013-09-18 10:00:00 +00:00
|
|
|
}
|
|
|
|
|
2015-07-15 13:51:00 +00:00
|
|
|
static int
|
|
|
|
eth_virtio_dev_uninit(struct rte_eth_dev *eth_dev)
|
|
|
|
{
|
|
|
|
PMD_INIT_FUNC_TRACE();
|
|
|
|
|
|
|
|
if (rte_eal_process_type() == RTE_PROC_SECONDARY)
|
|
|
|
return -EPERM;
|
|
|
|
|
2016-11-05 09:41:03 +00:00
|
|
|
virtio_dev_stop(eth_dev);
|
|
|
|
virtio_dev_close(eth_dev);
|
2015-07-15 13:51:00 +00:00
|
|
|
|
|
|
|
eth_dev->dev_ops = NULL;
|
|
|
|
eth_dev->tx_pkt_burst = NULL;
|
|
|
|
eth_dev->rx_pkt_burst = NULL;
|
|
|
|
|
|
|
|
rte_free(eth_dev->data->mac_addrs);
|
|
|
|
eth_dev->data->mac_addrs = NULL;
|
|
|
|
|
|
|
|
/* reset interrupt callback */
|
2016-05-09 16:35:57 +00:00
|
|
|
if (eth_dev->data->dev_flags & RTE_ETH_DEV_INTR_LSC)
|
2017-01-12 05:31:57 +00:00
|
|
|
rte_intr_callback_unregister(eth_dev->intr_handle,
|
2015-07-15 13:51:00 +00:00
|
|
|
virtio_interrupt_handler,
|
|
|
|
eth_dev);
|
2017-01-12 05:37:00 +00:00
|
|
|
if (eth_dev->device)
|
2017-05-15 10:24:03 +00:00
|
|
|
rte_pci_unmap_device(RTE_ETH_DEV_TO_PCI(eth_dev));
|
2015-07-15 13:51:00 +00:00
|
|
|
|
|
|
|
PMD_INIT_LOG(DEBUG, "dev_uninit completed");
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
2017-04-11 15:44:24 +00:00
|
|
|
static int eth_virtio_pci_probe(struct rte_pci_driver *pci_drv __rte_unused,
|
|
|
|
struct rte_pci_device *pci_dev)
|
|
|
|
{
|
|
|
|
return rte_eth_dev_pci_generic_probe(pci_dev, sizeof(struct virtio_hw),
|
|
|
|
eth_virtio_dev_init);
|
|
|
|
}
|
|
|
|
|
|
|
|
static int eth_virtio_pci_remove(struct rte_pci_device *pci_dev)
|
|
|
|
{
|
|
|
|
return rte_eth_dev_pci_generic_remove(pci_dev, eth_virtio_dev_uninit);
|
|
|
|
}
|
|
|
|
|
|
|
|
static struct rte_pci_driver rte_virtio_pmd = {
|
|
|
|
.driver = {
|
|
|
|
.name = "net_virtio",
|
2013-09-18 10:00:00 +00:00
|
|
|
},
|
2017-04-11 15:44:24 +00:00
|
|
|
.id_table = pci_id_virtio_map,
|
|
|
|
.drv_flags = 0,
|
|
|
|
.probe = eth_virtio_pci_probe,
|
|
|
|
.remove = eth_virtio_pci_remove,
|
2013-09-18 10:00:00 +00:00
|
|
|
};
|
|
|
|
|
2016-09-20 12:41:20 +00:00
|
|
|
RTE_INIT(rte_virtio_pmd_init);
|
|
|
|
static void
|
|
|
|
rte_virtio_pmd_init(void)
|
2013-09-18 10:00:00 +00:00
|
|
|
{
|
2014-08-26 14:11:39 +00:00
|
|
|
if (rte_eal_iopl_init() != 0) {
|
|
|
|
PMD_INIT_LOG(ERR, "IOPL call failed - cannot use virtio PMD");
|
2016-09-20 12:41:20 +00:00
|
|
|
return;
|
2014-08-26 14:11:39 +00:00
|
|
|
}
|
|
|
|
|
2017-05-04 14:48:59 +00:00
|
|
|
rte_pci_register(&rte_virtio_pmd);
|
2013-09-18 10:00:00 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Configure virtio device
|
|
|
|
* It returns 0 on success.
|
|
|
|
*/
|
|
|
|
static int
|
2014-06-14 01:06:22 +00:00
|
|
|
virtio_dev_configure(struct rte_eth_dev *dev)
|
2013-09-18 10:00:00 +00:00
|
|
|
{
|
2014-06-14 01:06:22 +00:00
|
|
|
const struct rte_eth_rxmode *rxmode = &dev->data->dev_conf.rxmode;
|
2015-02-09 01:13:56 +00:00
|
|
|
struct virtio_hw *hw = dev->data->dev_private;
|
2017-09-07 12:13:38 +00:00
|
|
|
uint64_t req_features;
|
2017-07-31 07:56:44 +00:00
|
|
|
int ret;
|
2014-06-14 01:06:22 +00:00
|
|
|
|
|
|
|
PMD_INIT_LOG(DEBUG, "configure");
|
2017-09-07 12:13:38 +00:00
|
|
|
req_features = VIRTIO_PMD_DEFAULT_GUEST_FEATURES;
|
2017-07-07 19:52:49 +00:00
|
|
|
|
2017-07-31 07:56:44 +00:00
|
|
|
if (dev->data->dev_conf.intr_conf.rxq) {
|
|
|
|
ret = virtio_init_device(dev, hw->req_guest_features);
|
|
|
|
if (ret < 0)
|
|
|
|
return ret;
|
|
|
|
}
|
|
|
|
|
2017-09-07 12:13:39 +00:00
|
|
|
/* The name hw_ip_checksum is a bit confusing since it can be
|
|
|
|
* set by the application to request L3 and/or L4 checksums. In
|
|
|
|
* case of virtio, only L4 checksum is supported.
|
|
|
|
*/
|
|
|
|
if (rxmode->hw_ip_checksum)
|
|
|
|
req_features |= (1ULL << VIRTIO_NET_F_GUEST_CSUM);
|
|
|
|
|
2017-09-07 12:13:38 +00:00
|
|
|
if (rxmode->enable_lro)
|
|
|
|
req_features |=
|
|
|
|
(1ULL << VIRTIO_NET_F_GUEST_TSO4) |
|
|
|
|
(1ULL << VIRTIO_NET_F_GUEST_TSO6);
|
2016-10-13 14:16:02 +00:00
|
|
|
|
2017-09-07 12:13:38 +00:00
|
|
|
/* if request features changed, reinit the device */
|
|
|
|
if (req_features != hw->req_guest_features) {
|
|
|
|
ret = virtio_init_device(dev, req_features);
|
|
|
|
if (ret < 0)
|
|
|
|
return ret;
|
|
|
|
}
|
|
|
|
|
2017-09-07 12:13:39 +00:00
|
|
|
if (rxmode->hw_ip_checksum &&
|
|
|
|
!vtpci_with_feature(hw, VIRTIO_NET_F_GUEST_CSUM)) {
|
2017-09-07 12:13:41 +00:00
|
|
|
PMD_DRV_LOG(ERR,
|
2017-09-07 12:13:39 +00:00
|
|
|
"rx checksum not available on this host");
|
|
|
|
return -ENOTSUP;
|
|
|
|
}
|
|
|
|
|
2017-09-07 12:13:38 +00:00
|
|
|
if (rxmode->enable_lro &&
|
|
|
|
(!vtpci_with_feature(hw, VIRTIO_NET_F_GUEST_TSO4) ||
|
|
|
|
!vtpci_with_feature(hw, VIRTIO_NET_F_GUEST_TSO4))) {
|
2017-09-07 12:13:41 +00:00
|
|
|
PMD_DRV_LOG(ERR,
|
2017-09-07 12:13:38 +00:00
|
|
|
"Large Receive Offload not available on this host");
|
2016-10-13 14:16:10 +00:00
|
|
|
return -ENOTSUP;
|
|
|
|
}
|
|
|
|
|
net/virtio: allocate queue at init stage
Queue allocation should be done once, since the queue related info (such
as vring addreess) will only be informed to the vhost-user backend once
without virtio device reset.
That means, if you allocate queues again after the vhost-user negotiation,
the vhost-user backend will not be informed any more. Leading to a state
that the vring info mismatches between virtio PMD driver and vhost-backend:
the driver switches to the new address has just been allocated, while the
vhost-backend still sticks to the old address has been assigned in the init
stage.
Unfortunately, that is exactly how the virtio driver is coded so far: queue
allocation is done at queue_setup stage (when rte_eth_tx/rx_queue_setup is
invoked). This is wrong, because queue_setup can be invoked several times.
For example,
$ start_testpmd.sh ... --txq=1 --rxq=1 ...
> port stop 0
> port config all txq 1 # just trigger the queue_setup callback again
> port config all rxq 1
> port start 0
The right way to do is allocate the queues in the init stage, so that the
vring info could be persistent with the vhost-user backend.
Besides that, we should allocate max_queue pairs the device supports, but
not nr queue pairs firstly configured, to make following case work.
$ start_testpmd.sh ... --txq=1 --rxq=1 ...
> port stop 0
> port config all txq 2
> port config all rxq 2
> port start 0
Since the allocation is switched to init stage, the free should also
moved from the rx/tx_queue_release to dev close stage. That leading we
could do nothing an empty rx/tx_queue_release() implementation.
Signed-off-by: Yuanhan Liu <yuanhan.liu@linux.intel.com>
2016-11-05 09:40:59 +00:00
|
|
|
/* start control queue */
|
|
|
|
if (vtpci_with_feature(hw, VIRTIO_NET_F_CTRL_VQ))
|
2016-10-13 14:16:01 +00:00
|
|
|
virtio_dev_cq_start(dev);
|
|
|
|
|
2015-02-09 01:13:55 +00:00
|
|
|
hw->vlan_strip = rxmode->hw_vlan_strip;
|
|
|
|
|
2015-02-09 01:14:02 +00:00
|
|
|
if (rxmode->hw_vlan_filter
|
|
|
|
&& !vtpci_with_feature(hw, VIRTIO_NET_F_CTRL_VLAN)) {
|
2017-09-07 12:13:41 +00:00
|
|
|
PMD_DRV_LOG(ERR,
|
2015-02-09 01:14:02 +00:00
|
|
|
"vlan filtering not available on this host");
|
|
|
|
return -ENOTSUP;
|
|
|
|
}
|
|
|
|
|
2016-05-09 16:35:57 +00:00
|
|
|
if (dev->data->dev_flags & RTE_ETH_DEV_INTR_LSC)
|
2017-01-17 07:10:22 +00:00
|
|
|
/* Enable vector (0) for Link State Intrerrupt */
|
|
|
|
if (VTPCI_OPS(hw)->set_config_irq(hw, 0) ==
|
|
|
|
VIRTIO_MSI_NO_VECTOR) {
|
2015-02-09 01:14:06 +00:00
|
|
|
PMD_DRV_LOG(ERR, "failed to set config vector");
|
|
|
|
return -EBUSY;
|
|
|
|
}
|
2015-02-09 01:13:53 +00:00
|
|
|
|
2017-09-07 12:13:46 +00:00
|
|
|
hw->use_simple_rx = 1;
|
|
|
|
hw->use_simple_tx = 1;
|
2017-09-07 12:13:44 +00:00
|
|
|
|
2017-09-07 12:13:45 +00:00
|
|
|
#if defined RTE_ARCH_ARM64 || defined CONFIG_RTE_ARCH_ARM
|
2017-09-07 12:13:46 +00:00
|
|
|
if (!rte_cpu_get_flag_enabled(RTE_CPUFLAG_NEON)) {
|
|
|
|
hw->use_simple_rx = 0;
|
|
|
|
hw->use_simple_tx = 0;
|
|
|
|
}
|
2017-09-07 12:13:44 +00:00
|
|
|
#endif
|
2017-09-07 12:13:46 +00:00
|
|
|
if (vtpci_with_feature(hw, VIRTIO_NET_F_MRG_RXBUF)) {
|
|
|
|
hw->use_simple_rx = 0;
|
|
|
|
hw->use_simple_tx = 0;
|
|
|
|
}
|
2017-09-07 12:13:44 +00:00
|
|
|
|
2017-09-07 12:13:47 +00:00
|
|
|
if (rxmode->hw_ip_checksum)
|
|
|
|
hw->use_simple_rx = 0;
|
|
|
|
|
2015-02-09 01:13:58 +00:00
|
|
|
return 0;
|
2013-09-18 10:00:00 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
static int
|
|
|
|
virtio_dev_start(struct rte_eth_dev *dev)
|
|
|
|
{
|
2014-05-29 07:18:20 +00:00
|
|
|
uint16_t nb_queues, i;
|
2016-06-01 16:12:13 +00:00
|
|
|
struct virtnet_rx *rxvq;
|
|
|
|
struct virtnet_tx *txvq __rte_unused;
|
2016-11-07 09:25:06 +00:00
|
|
|
struct virtio_hw *hw = dev->data->dev_private;
|
2017-09-07 12:13:43 +00:00
|
|
|
int ret;
|
|
|
|
|
|
|
|
/* Finish the initialization of the queues */
|
|
|
|
for (i = 0; i < dev->data->nb_rx_queues; i++) {
|
|
|
|
ret = virtio_dev_rx_queue_setup_finish(dev, i);
|
|
|
|
if (ret < 0)
|
|
|
|
return ret;
|
|
|
|
}
|
|
|
|
for (i = 0; i < dev->data->nb_tx_queues; i++) {
|
|
|
|
ret = virtio_dev_tx_queue_setup_finish(dev, i);
|
|
|
|
if (ret < 0)
|
|
|
|
return ret;
|
|
|
|
}
|
2013-09-18 10:00:00 +00:00
|
|
|
|
2015-02-09 01:13:53 +00:00
|
|
|
/* check if lsc interrupt feature is enabled */
|
2015-08-28 16:23:37 +00:00
|
|
|
if (dev->data->dev_conf.intr_conf.lsc) {
|
2016-05-09 16:35:57 +00:00
|
|
|
if (!(dev->data->dev_flags & RTE_ETH_DEV_INTR_LSC)) {
|
2015-02-09 01:13:53 +00:00
|
|
|
PMD_DRV_LOG(ERR, "link status not supported by host");
|
|
|
|
return -ENOTSUP;
|
|
|
|
}
|
2017-01-17 07:10:27 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
/* Enable uio/vfio intr/eventfd mapping: althrough we already did that
|
|
|
|
* in device configure, but it could be unmapped when device is
|
|
|
|
* stopped.
|
|
|
|
*/
|
|
|
|
if (dev->data->dev_conf.intr_conf.lsc ||
|
|
|
|
dev->data->dev_conf.intr_conf.rxq) {
|
|
|
|
rte_intr_disable(dev->intr_handle);
|
2015-02-09 01:13:53 +00:00
|
|
|
|
2017-01-12 05:31:57 +00:00
|
|
|
if (rte_intr_enable(dev->intr_handle) < 0) {
|
2015-02-09 01:13:53 +00:00
|
|
|
PMD_DRV_LOG(ERR, "interrupt enable failed");
|
|
|
|
return -EIO;
|
|
|
|
}
|
2013-09-18 10:00:00 +00:00
|
|
|
}
|
2015-02-09 01:13:53 +00:00
|
|
|
|
2013-09-18 10:00:00 +00:00
|
|
|
/*Notify the backend
|
|
|
|
*Otherwise the tap backend might already stop its queue due to fullness.
|
|
|
|
*vhost backend will have no chance to be waked up
|
|
|
|
*/
|
2016-11-05 09:41:04 +00:00
|
|
|
nb_queues = RTE_MAX(dev->data->nb_rx_queues, dev->data->nb_tx_queues);
|
2016-11-07 09:25:06 +00:00
|
|
|
if (hw->max_queue_pairs > 1) {
|
2014-05-29 07:18:20 +00:00
|
|
|
if (virtio_set_multiple_queues(dev, nb_queues) != 0)
|
|
|
|
return -EINVAL;
|
|
|
|
}
|
|
|
|
|
2014-06-14 01:06:19 +00:00
|
|
|
PMD_INIT_LOG(DEBUG, "nb_queues=%d", nb_queues);
|
2014-05-29 07:18:20 +00:00
|
|
|
|
2016-11-05 09:41:04 +00:00
|
|
|
for (i = 0; i < dev->data->nb_rx_queues; i++) {
|
2016-06-01 16:12:13 +00:00
|
|
|
rxvq = dev->data->rx_queues[i];
|
|
|
|
virtqueue_notify(rxvq->vq);
|
|
|
|
}
|
2014-05-29 07:18:20 +00:00
|
|
|
|
2017-08-01 16:17:36 +00:00
|
|
|
for (i = 0; i < dev->data->nb_tx_queues; i++) {
|
|
|
|
txvq = dev->data->tx_queues[i];
|
|
|
|
virtqueue_notify(txvq->vq);
|
|
|
|
}
|
|
|
|
|
2014-06-14 01:06:19 +00:00
|
|
|
PMD_INIT_LOG(DEBUG, "Notified backend at initialization");
|
2014-05-29 07:18:20 +00:00
|
|
|
|
2016-06-01 16:12:13 +00:00
|
|
|
for (i = 0; i < dev->data->nb_rx_queues; i++) {
|
|
|
|
rxvq = dev->data->rx_queues[i];
|
|
|
|
VIRTQUEUE_DUMP(rxvq->vq);
|
|
|
|
}
|
2014-05-29 07:18:20 +00:00
|
|
|
|
2016-06-01 16:12:13 +00:00
|
|
|
for (i = 0; i < dev->data->nb_tx_queues; i++) {
|
|
|
|
txvq = dev->data->tx_queues[i];
|
|
|
|
VIRTQUEUE_DUMP(txvq->vq);
|
|
|
|
}
|
2017-04-27 07:35:39 +00:00
|
|
|
|
2017-09-07 12:13:44 +00:00
|
|
|
set_rxtx_funcs(dev);
|
2017-04-14 06:36:45 +00:00
|
|
|
hw->started = 1;
|
2014-05-29 07:18:20 +00:00
|
|
|
|
2017-04-27 07:35:39 +00:00
|
|
|
/* Initialize Link state */
|
|
|
|
virtio_dev_link_update(dev, 0);
|
|
|
|
|
2014-05-29 07:18:20 +00:00
|
|
|
return 0;
|
2013-09-18 10:00:00 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
static void virtio_dev_free_mbufs(struct rte_eth_dev *dev)
|
|
|
|
{
|
2014-05-29 07:18:20 +00:00
|
|
|
struct rte_mbuf *buf;
|
|
|
|
int i, mbuf_num = 0;
|
2014-06-13 01:32:40 +00:00
|
|
|
|
2014-05-29 07:18:20 +00:00
|
|
|
for (i = 0; i < dev->data->nb_rx_queues; i++) {
|
2016-06-01 16:12:13 +00:00
|
|
|
struct virtnet_rx *rxvq = dev->data->rx_queues[i];
|
|
|
|
|
2014-06-13 01:32:40 +00:00
|
|
|
PMD_INIT_LOG(DEBUG,
|
2014-06-14 01:06:19 +00:00
|
|
|
"Before freeing rxq[%d] used and unused buf", i);
|
2016-06-01 16:12:13 +00:00
|
|
|
VIRTQUEUE_DUMP(rxvq->vq);
|
2014-05-29 07:18:20 +00:00
|
|
|
|
2016-06-01 16:12:13 +00:00
|
|
|
PMD_INIT_LOG(DEBUG, "rx_queues[%d]=%p", i, rxvq);
|
|
|
|
while ((buf = virtqueue_detatch_unused(rxvq->vq)) != NULL) {
|
2014-08-14 08:54:35 +00:00
|
|
|
rte_pktmbuf_free(buf);
|
2014-05-29 07:18:20 +00:00
|
|
|
mbuf_num++;
|
|
|
|
}
|
|
|
|
|
2014-06-14 01:06:19 +00:00
|
|
|
PMD_INIT_LOG(DEBUG, "free %d mbufs", mbuf_num);
|
2014-06-13 01:32:40 +00:00
|
|
|
PMD_INIT_LOG(DEBUG,
|
2014-06-14 01:06:19 +00:00
|
|
|
"After freeing rxq[%d] used and unused buf", i);
|
2016-06-01 16:12:13 +00:00
|
|
|
VIRTQUEUE_DUMP(rxvq->vq);
|
2013-09-18 10:00:00 +00:00
|
|
|
}
|
2014-05-29 07:18:20 +00:00
|
|
|
|
|
|
|
for (i = 0; i < dev->data->nb_tx_queues; i++) {
|
2016-06-01 16:12:13 +00:00
|
|
|
struct virtnet_tx *txvq = dev->data->tx_queues[i];
|
|
|
|
|
2014-06-13 01:32:40 +00:00
|
|
|
PMD_INIT_LOG(DEBUG,
|
2014-06-14 01:06:19 +00:00
|
|
|
"Before freeing txq[%d] used and unused bufs",
|
2014-06-13 01:32:40 +00:00
|
|
|
i);
|
2016-06-01 16:12:13 +00:00
|
|
|
VIRTQUEUE_DUMP(txvq->vq);
|
2014-05-29 07:18:20 +00:00
|
|
|
|
|
|
|
mbuf_num = 0;
|
2016-06-01 16:12:13 +00:00
|
|
|
while ((buf = virtqueue_detatch_unused(txvq->vq)) != NULL) {
|
2014-08-14 08:54:35 +00:00
|
|
|
rte_pktmbuf_free(buf);
|
2014-05-29 07:18:20 +00:00
|
|
|
mbuf_num++;
|
|
|
|
}
|
|
|
|
|
2014-06-14 01:06:19 +00:00
|
|
|
PMD_INIT_LOG(DEBUG, "free %d mbufs", mbuf_num);
|
|
|
|
PMD_INIT_LOG(DEBUG,
|
|
|
|
"After freeing txq[%d] used and unused buf", i);
|
2016-06-01 16:12:13 +00:00
|
|
|
VIRTQUEUE_DUMP(txvq->vq);
|
2013-09-18 10:00:00 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
2015-02-09 01:13:58 +00:00
|
|
|
* Stop device: disable interrupt and mark link down
|
2013-09-18 10:00:00 +00:00
|
|
|
*/
|
|
|
|
static void
|
|
|
|
virtio_dev_stop(struct rte_eth_dev *dev)
|
|
|
|
{
|
2017-04-14 06:36:45 +00:00
|
|
|
struct virtio_hw *hw = dev->data->dev_private;
|
2015-02-09 01:13:58 +00:00
|
|
|
struct rte_eth_link link;
|
2017-01-17 07:10:27 +00:00
|
|
|
struct rte_intr_conf *intr_conf = &dev->data->dev_conf.intr_conf;
|
2013-09-18 10:00:00 +00:00
|
|
|
|
2015-02-09 01:13:58 +00:00
|
|
|
PMD_INIT_LOG(DEBUG, "stop");
|
|
|
|
|
2017-01-17 07:10:27 +00:00
|
|
|
if (intr_conf->lsc || intr_conf->rxq)
|
2017-01-12 05:31:57 +00:00
|
|
|
rte_intr_disable(dev->intr_handle);
|
2015-02-09 01:13:58 +00:00
|
|
|
|
2017-04-14 06:36:45 +00:00
|
|
|
hw->started = 0;
|
2015-02-09 01:13:58 +00:00
|
|
|
memset(&link, 0, sizeof(link));
|
|
|
|
virtio_dev_atomic_write_link_status(dev, &link);
|
2013-09-18 10:00:00 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
static int
|
|
|
|
virtio_dev_link_update(struct rte_eth_dev *dev, __rte_unused int wait_to_complete)
|
|
|
|
{
|
|
|
|
struct rte_eth_link link, old;
|
|
|
|
uint16_t status;
|
2015-02-09 01:13:56 +00:00
|
|
|
struct virtio_hw *hw = dev->data->dev_private;
|
2013-09-18 10:00:00 +00:00
|
|
|
memset(&link, 0, sizeof(link));
|
|
|
|
virtio_dev_atomic_read_link_status(dev, &link);
|
|
|
|
old = link;
|
2016-03-31 22:12:25 +00:00
|
|
|
link.link_duplex = ETH_LINK_FULL_DUPLEX;
|
2014-06-13 01:32:40 +00:00
|
|
|
link.link_speed = SPEED_10G;
|
2015-02-09 01:13:53 +00:00
|
|
|
|
2017-04-14 06:36:45 +00:00
|
|
|
if (hw->started == 0) {
|
|
|
|
link.link_status = ETH_LINK_DOWN;
|
|
|
|
} else if (vtpci_with_feature(hw, VIRTIO_NET_F_STATUS)) {
|
2014-06-14 01:06:19 +00:00
|
|
|
PMD_INIT_LOG(DEBUG, "Get link status from hw");
|
2013-09-18 10:00:00 +00:00
|
|
|
vtpci_read_dev_config(hw,
|
|
|
|
offsetof(struct virtio_net_config, status),
|
|
|
|
&status, sizeof(status));
|
2014-06-13 01:32:40 +00:00
|
|
|
if ((status & VIRTIO_NET_S_LINK_UP) == 0) {
|
2016-03-31 22:12:24 +00:00
|
|
|
link.link_status = ETH_LINK_DOWN;
|
2014-06-14 01:06:19 +00:00
|
|
|
PMD_INIT_LOG(DEBUG, "Port %d is down",
|
2014-06-13 01:32:40 +00:00
|
|
|
dev->data->port_id);
|
2013-09-18 10:00:00 +00:00
|
|
|
} else {
|
2016-03-31 22:12:24 +00:00
|
|
|
link.link_status = ETH_LINK_UP;
|
2014-06-14 01:06:19 +00:00
|
|
|
PMD_INIT_LOG(DEBUG, "Port %d is up",
|
2014-06-13 01:32:40 +00:00
|
|
|
dev->data->port_id);
|
2013-09-18 10:00:00 +00:00
|
|
|
}
|
|
|
|
} else {
|
2016-03-31 22:12:24 +00:00
|
|
|
link.link_status = ETH_LINK_UP;
|
2013-09-18 10:00:00 +00:00
|
|
|
}
|
|
|
|
virtio_dev_atomic_write_link_status(dev, &link);
|
2015-02-09 01:13:53 +00:00
|
|
|
|
|
|
|
return (old.link_status == link.link_status) ? -1 : 0;
|
2013-09-18 10:00:00 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
static void
|
|
|
|
virtio_dev_info_get(struct rte_eth_dev *dev, struct rte_eth_dev_info *dev_info)
|
|
|
|
{
|
2017-01-17 10:35:53 +00:00
|
|
|
uint64_t tso_mask, host_features;
|
2015-02-09 01:13:56 +00:00
|
|
|
struct virtio_hw *hw = dev->data->dev_private;
|
2014-06-13 01:32:40 +00:00
|
|
|
|
2017-02-02 12:05:07 +00:00
|
|
|
dev_info->speed_capa = ETH_LINK_SPEED_10G; /* fake value */
|
|
|
|
|
2017-05-15 10:24:03 +00:00
|
|
|
dev_info->pci_dev = dev->device ? RTE_ETH_DEV_TO_PCI(dev) : NULL;
|
2016-10-13 14:16:01 +00:00
|
|
|
dev_info->max_rx_queues =
|
|
|
|
RTE_MIN(hw->max_queue_pairs, VIRTIO_MAX_RX_QUEUES);
|
|
|
|
dev_info->max_tx_queues =
|
|
|
|
RTE_MIN(hw->max_queue_pairs, VIRTIO_MAX_TX_QUEUES);
|
2013-09-18 10:00:00 +00:00
|
|
|
dev_info->min_rx_bufsize = VIRTIO_MIN_RX_BUFSIZE;
|
|
|
|
dev_info->max_rx_pktlen = VIRTIO_MAX_RX_PKTLEN;
|
|
|
|
dev_info->max_mac_addrs = VIRTIO_MAX_MAC_ADDRS;
|
2015-02-23 07:51:25 +00:00
|
|
|
dev_info->default_txconf = (struct rte_eth_txconf) {
|
|
|
|
.txq_flags = ETH_TXQ_FLAGS_NOOFFLOADS
|
|
|
|
};
|
2016-10-13 14:16:09 +00:00
|
|
|
|
2017-01-17 10:35:53 +00:00
|
|
|
host_features = VTPCI_OPS(hw)->get_features(hw);
|
|
|
|
dev_info->rx_offload_capa = 0;
|
|
|
|
if (host_features & (1ULL << VIRTIO_NET_F_GUEST_CSUM)) {
|
|
|
|
dev_info->rx_offload_capa |=
|
|
|
|
DEV_RX_OFFLOAD_TCP_CKSUM |
|
|
|
|
DEV_RX_OFFLOAD_UDP_CKSUM;
|
|
|
|
}
|
|
|
|
tso_mask = (1ULL << VIRTIO_NET_F_GUEST_TSO4) |
|
|
|
|
(1ULL << VIRTIO_NET_F_GUEST_TSO6);
|
2017-09-07 12:13:38 +00:00
|
|
|
if ((host_features & tso_mask) == tso_mask)
|
|
|
|
dev_info->rx_offload_capa |= DEV_RX_OFFLOAD_TCP_LRO;
|
2017-01-17 10:35:53 +00:00
|
|
|
|
|
|
|
dev_info->tx_offload_capa = 0;
|
2016-10-13 14:16:09 +00:00
|
|
|
if (hw->guest_features & (1ULL << VIRTIO_NET_F_CSUM)) {
|
|
|
|
dev_info->tx_offload_capa |=
|
|
|
|
DEV_TX_OFFLOAD_UDP_CKSUM |
|
|
|
|
DEV_TX_OFFLOAD_TCP_CKSUM;
|
|
|
|
}
|
2016-10-13 14:16:11 +00:00
|
|
|
tso_mask = (1ULL << VIRTIO_NET_F_HOST_TSO4) |
|
|
|
|
(1ULL << VIRTIO_NET_F_HOST_TSO6);
|
|
|
|
if ((hw->guest_features & tso_mask) == tso_mask)
|
|
|
|
dev_info->tx_offload_capa |= DEV_TX_OFFLOAD_TCP_TSO;
|
2013-09-18 10:00:00 +00:00
|
|
|
}
|
2014-04-21 14:59:37 +00:00
|
|
|
|
2014-05-29 07:18:20 +00:00
|
|
|
/*
|
|
|
|
* It enables testpmd to collect per queue stats.
|
|
|
|
*/
|
|
|
|
static int
|
|
|
|
virtio_dev_queue_stats_mapping_set(__rte_unused struct rte_eth_dev *eth_dev,
|
|
|
|
__rte_unused uint16_t queue_id, __rte_unused uint8_t stat_idx,
|
|
|
|
__rte_unused uint8_t is_rx)
|
|
|
|
{
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
2016-10-10 05:43:15 +00:00
|
|
|
RTE_PMD_EXPORT_NAME(net_virtio, __COUNTER__);
|
|
|
|
RTE_PMD_REGISTER_PCI_TABLE(net_virtio, pci_id_virtio_map);
|
2017-05-20 13:12:37 +00:00
|
|
|
RTE_PMD_REGISTER_KMOD_DEP(net_virtio, "* igb_uio | uio_pci_generic | vfio-pci");
|