aboutsummaryrefslogtreecommitdiffstats
path: root/drivers/net/netvsc
diff options
context:
space:
mode:
Diffstat (limited to 'drivers/net/netvsc')
-rw-r--r--drivers/net/netvsc/Makefile1
-rw-r--r--drivers/net/netvsc/hn_ethdev.c221
-rw-r--r--drivers/net/netvsc/hn_nvs.c24
-rw-r--r--drivers/net/netvsc/hn_nvs.h9
-rw-r--r--drivers/net/netvsc/hn_rndis.c47
-rw-r--r--drivers/net/netvsc/hn_rndis.h3
-rw-r--r--drivers/net/netvsc/hn_rxtx.c212
-rw-r--r--drivers/net/netvsc/hn_var.h66
-rw-r--r--drivers/net/netvsc/hn_vf.c549
-rw-r--r--drivers/net/netvsc/meson.build2
10 files changed, 999 insertions, 135 deletions
diff --git a/drivers/net/netvsc/Makefile b/drivers/net/netvsc/Makefile
index 3c713af3..71482591 100644
--- a/drivers/net/netvsc/Makefile
+++ b/drivers/net/netvsc/Makefile
@@ -15,6 +15,7 @@ SRCS-$(CONFIG_RTE_LIBRTE_NETVSC_PMD) += hn_ethdev.c
SRCS-$(CONFIG_RTE_LIBRTE_NETVSC_PMD) += hn_rxtx.c
SRCS-$(CONFIG_RTE_LIBRTE_NETVSC_PMD) += hn_rndis.c
SRCS-$(CONFIG_RTE_LIBRTE_NETVSC_PMD) += hn_nvs.c
+SRCS-$(CONFIG_RTE_LIBRTE_NETVSC_PMD) += hn_vf.c
LDLIBS += -lrte_eal -lrte_mbuf -lrte_mempool -lrte_ring
LDLIBS += -lrte_ethdev -lrte_net -lrte_kvargs
diff --git a/drivers/net/netvsc/hn_ethdev.c b/drivers/net/netvsc/hn_ethdev.c
index 78b842ba..aa38ee7a 100644
--- a/drivers/net/netvsc/hn_ethdev.c
+++ b/drivers/net/netvsc/hn_ethdev.c
@@ -14,7 +14,9 @@
#include <rte_memcpy.h>
#include <rte_string_fns.h>
#include <rte_memzone.h>
+#include <rte_devargs.h>
#include <rte_malloc.h>
+#include <rte_kvargs.h>
#include <rte_atomic.h>
#include <rte_branch_prediction.h>
#include <rte_ether.h>
@@ -40,8 +42,7 @@
DEV_TX_OFFLOAD_VLAN_INSERT)
#define HN_RX_OFFLOAD_CAPS (DEV_RX_OFFLOAD_CHECKSUM | \
- DEV_RX_OFFLOAD_VLAN_STRIP | \
- DEV_RX_OFFLOAD_CRC_STRIP)
+ DEV_RX_OFFLOAD_VLAN_STRIP)
int hn_logtype_init;
int hn_logtype_driver;
@@ -55,7 +56,7 @@ static const struct hn_xstats_name_off hn_stat_strings[] = {
{ "good_packets", offsetof(struct hn_stats, packets) },
{ "good_bytes", offsetof(struct hn_stats, bytes) },
{ "errors", offsetof(struct hn_stats, errors) },
- { "allocation_failed", offsetof(struct hn_stats, nomemory) },
+ { "ring full", offsetof(struct hn_stats, ring_full) },
{ "multicast_packets", offsetof(struct hn_stats, multicast) },
{ "broadcast_packets", offsetof(struct hn_stats, broadcast) },
{ "undersize_packets", offsetof(struct hn_stats, size_bins[0]) },
@@ -105,6 +106,10 @@ eth_dev_vmbus_allocate(struct rte_vmbus_device *dev, size_t private_data_size)
}
eth_dev->device = &dev->device;
+
+ /* interrupt is simulated */
+ dev->intr_handle.type = RTE_INTR_HANDLE_EXT;
+ eth_dev->data->dev_flags |= RTE_ETH_DEV_INTR_LSC;
eth_dev->intr_handle = &dev->intr_handle;
return eth_dev;
@@ -113,22 +118,66 @@ eth_dev_vmbus_allocate(struct rte_vmbus_device *dev, size_t private_data_size)
static void
eth_dev_vmbus_release(struct rte_eth_dev *eth_dev)
{
+ /* mac_addrs must not be freed alone because part of dev_private */
+ eth_dev->data->mac_addrs = NULL;
/* free ether device */
rte_eth_dev_release_port(eth_dev);
- if (rte_eal_process_type() == RTE_PROC_PRIMARY)
- rte_free(eth_dev->data->dev_private);
+ eth_dev->device = NULL;
+ eth_dev->intr_handle = NULL;
+}
- eth_dev->data->dev_private = NULL;
+/* handle "latency=X" from devargs */
+static int hn_set_latency(const char *key, const char *value, void *opaque)
+{
+ struct hn_data *hv = opaque;
+ char *endp = NULL;
+ unsigned long lat;
- /*
- * Secondary process will check the name to attach.
- * Clear this field to avoid attaching a released ports.
- */
- eth_dev->data->name[0] = '\0';
+ errno = 0;
+ lat = strtoul(value, &endp, 0);
- eth_dev->device = NULL;
- eth_dev->intr_handle = NULL;
+ if (*value == '\0' || *endp != '\0') {
+ PMD_DRV_LOG(ERR, "invalid parameter %s=%s", key, value);
+ return -EINVAL;
+ }
+
+ PMD_DRV_LOG(DEBUG, "set latency %lu usec", lat);
+
+ hv->latency = lat * 1000; /* usec to nsec */
+ return 0;
+}
+
+/* Parse device arguments */
+static int hn_parse_args(const struct rte_eth_dev *dev)
+{
+ struct hn_data *hv = dev->data->dev_private;
+ struct rte_devargs *devargs = dev->device->devargs;
+ static const char * const valid_keys[] = {
+ "latency",
+ NULL
+ };
+ struct rte_kvargs *kvlist;
+ int ret;
+
+ if (!devargs)
+ return 0;
+
+ PMD_INIT_LOG(DEBUG, "device args %s %s",
+ devargs->name, devargs->args);
+
+ kvlist = rte_kvargs_parse(devargs->args, valid_keys);
+ if (!kvlist) {
+ PMD_DRV_LOG(NOTICE, "invalid parameters");
+ return -EINVAL;
+ }
+
+ ret = rte_kvargs_process(kvlist, "latency", hn_set_latency, hv);
+ if (ret)
+ PMD_DRV_LOG(ERR, "Unable to process latency arg\n");
+
+ rte_kvargs_free(kvlist);
+ return ret;
}
/* Update link status.
@@ -136,9 +185,9 @@ eth_dev_vmbus_release(struct rte_eth_dev *eth_dev)
* means block this call until link is up.
* which is not worth supporting.
*/
-static int
+int
hn_dev_link_update(struct rte_eth_dev *dev,
- __rte_unused int wait_to_complete)
+ int wait_to_complete)
{
struct hn_data *hv = dev->data->dev_private;
struct rte_eth_link link, old;
@@ -152,6 +201,8 @@ hn_dev_link_update(struct rte_eth_dev *dev,
hn_rndis_get_linkspeed(hv);
+ hn_vf_link_update(dev, wait_to_complete);
+
link = (struct rte_eth_link) {
.link_duplex = ETH_LINK_FULL_DUPLEX,
.link_autoneg = ETH_LINK_SPEED_FIXED,
@@ -190,6 +241,7 @@ static void hn_dev_info_get(struct rte_eth_dev *dev,
dev_info->max_tx_queues = hv->max_queues;
hn_rndis_get_offload(hv, dev_info);
+ hn_vf_info_get(hv, dev_info);
}
static void
@@ -198,6 +250,7 @@ hn_dev_promiscuous_enable(struct rte_eth_dev *dev)
struct hn_data *hv = dev->data->dev_private;
hn_rndis_set_rxfilter(hv, NDIS_PACKET_TYPE_PROMISCUOUS);
+ hn_vf_promiscuous_enable(dev);
}
static void
@@ -210,6 +263,7 @@ hn_dev_promiscuous_disable(struct rte_eth_dev *dev)
if (dev->data->all_multicast)
filter |= NDIS_PACKET_TYPE_ALL_MULTICAST;
hn_rndis_set_rxfilter(hv, filter);
+ hn_vf_promiscuous_disable(dev);
}
static void
@@ -220,6 +274,7 @@ hn_dev_allmulticast_enable(struct rte_eth_dev *dev)
hn_rndis_set_rxfilter(hv, NDIS_PACKET_TYPE_DIRECTED |
NDIS_PACKET_TYPE_ALL_MULTICAST |
NDIS_PACKET_TYPE_BROADCAST);
+ hn_vf_allmulticast_enable(dev);
}
static void
@@ -229,6 +284,16 @@ hn_dev_allmulticast_disable(struct rte_eth_dev *dev)
hn_rndis_set_rxfilter(hv, NDIS_PACKET_TYPE_DIRECTED |
NDIS_PACKET_TYPE_BROADCAST);
+ hn_vf_allmulticast_disable(dev);
+}
+
+static int
+hn_dev_mc_addr_list(struct rte_eth_dev *dev,
+ struct ether_addr *mc_addr_set,
+ uint32_t nb_mc_addr)
+{
+ /* No filtering on the synthetic path, but can do it on VF */
+ return hn_vf_mc_addr_list(dev, mc_addr_set, nb_mc_addr);
}
/* Setup shared rx/tx queue data */
@@ -264,6 +329,8 @@ static int hn_subchan_configure(struct hn_data *hv,
return err;
}
+ rte_vmbus_set_latency(hv->vmbus, new_sc, hv->latency);
+
retry = 0;
chn_index = rte_vmbus_sub_channel_index(new_sc);
if (chn_index == 0 || chn_index > hv->max_queues) {
@@ -338,7 +405,7 @@ static int hn_dev_configure(struct rte_eth_dev *dev)
}
}
- return 0;
+ return hn_vf_configure(dev, dev_conf);
}
static int hn_dev_stats_get(struct rte_eth_dev *dev,
@@ -346,6 +413,8 @@ static int hn_dev_stats_get(struct rte_eth_dev *dev,
{
unsigned int i;
+ hn_vf_stats_get(dev, stats);
+
for (i = 0; i < dev->data->nb_tx_queues; i++) {
const struct hn_tx_queue *txq = dev->data->tx_queues[i];
@@ -354,7 +423,7 @@ static int hn_dev_stats_get(struct rte_eth_dev *dev,
stats->opackets += txq->stats.packets;
stats->obytes += txq->stats.bytes;
- stats->oerrors += txq->stats.errors + txq->stats.nomemory;
+ stats->oerrors += txq->stats.errors;
if (i < RTE_ETHDEV_QUEUE_STAT_CNTRS) {
stats->q_opackets[i] = txq->stats.packets;
@@ -371,7 +440,7 @@ static int hn_dev_stats_get(struct rte_eth_dev *dev,
stats->ipackets += rxq->stats.packets;
stats->ibytes += rxq->stats.bytes;
stats->ierrors += rxq->stats.errors;
- stats->imissed += rxq->ring_full;
+ stats->imissed += rxq->stats.ring_full;
if (i < RTE_ETHDEV_QUEUE_STAT_CNTRS) {
stats->q_ipackets[i] = rxq->stats.packets;
@@ -405,22 +474,41 @@ hn_dev_stats_reset(struct rte_eth_dev *dev)
continue;
memset(&rxq->stats, 0, sizeof(struct hn_stats));
- rxq->ring_full = 0;
}
}
+static void
+hn_dev_xstats_reset(struct rte_eth_dev *dev)
+{
+ hn_dev_stats_reset(dev);
+ hn_vf_xstats_reset(dev);
+}
+
+static int
+hn_dev_xstats_count(struct rte_eth_dev *dev)
+{
+ int ret, count;
+
+ count = dev->data->nb_tx_queues * RTE_DIM(hn_stat_strings);
+ count += dev->data->nb_rx_queues * RTE_DIM(hn_stat_strings);
+
+ ret = hn_vf_xstats_get_names(dev, NULL, 0);
+ if (ret < 0)
+ return ret;
+
+ return count + ret;
+}
+
static int
hn_dev_xstats_get_names(struct rte_eth_dev *dev,
struct rte_eth_xstat_name *xstats_names,
- __rte_unused unsigned int limit)
+ unsigned int limit)
{
unsigned int i, t, count = 0;
-
- PMD_INIT_FUNC_TRACE();
+ int ret;
if (!xstats_names)
- return dev->data->nb_tx_queues * RTE_DIM(hn_stat_strings)
- + dev->data->nb_rx_queues * RTE_DIM(hn_stat_strings);
+ return hn_dev_xstats_count(dev);
/* Note: limit checked in rte_eth_xstats_names() */
for (i = 0; i < dev->data->nb_tx_queues; i++) {
@@ -429,6 +517,9 @@ hn_dev_xstats_get_names(struct rte_eth_dev *dev,
if (!txq)
continue;
+ if (count >= limit)
+ break;
+
for (t = 0; t < RTE_DIM(hn_stat_strings); t++)
snprintf(xstats_names[count++].name,
RTE_ETH_XSTATS_NAME_SIZE,
@@ -441,6 +532,9 @@ hn_dev_xstats_get_names(struct rte_eth_dev *dev,
if (!rxq)
continue;
+ if (count >= limit)
+ break;
+
for (t = 0; t < RTE_DIM(hn_stat_strings); t++)
snprintf(xstats_names[count++].name,
RTE_ETH_XSTATS_NAME_SIZE,
@@ -448,7 +542,12 @@ hn_dev_xstats_get_names(struct rte_eth_dev *dev,
hn_stat_strings[t].name);
}
- return count;
+ ret = hn_vf_xstats_get_names(dev, xstats_names + count,
+ limit - count);
+ if (ret < 0)
+ return ret;
+
+ return count + ret;
}
static int
@@ -457,11 +556,9 @@ hn_dev_xstats_get(struct rte_eth_dev *dev,
unsigned int n)
{
unsigned int i, t, count = 0;
-
- const unsigned int nstats =
- dev->data->nb_tx_queues * RTE_DIM(hn_stat_strings)
- + dev->data->nb_rx_queues * RTE_DIM(hn_stat_strings);
+ const unsigned int nstats = hn_dev_xstats_count(dev);
const char *stats;
+ int ret;
PMD_INIT_FUNC_TRACE();
@@ -492,26 +589,33 @@ hn_dev_xstats_get(struct rte_eth_dev *dev,
(stats + hn_stat_strings[t].offset);
}
- return count;
+ ret = hn_vf_xstats_get(dev, xstats + count, n - count);
+ if (ret < 0)
+ return ret;
+
+ return count + ret;
}
static int
hn_dev_start(struct rte_eth_dev *dev)
{
struct hn_data *hv = dev->data->dev_private;
+ int error;
PMD_INIT_FUNC_TRACE();
- /* check if lsc interrupt feature is enabled */
- if (dev->data->dev_conf.intr_conf.lsc) {
- PMD_DRV_LOG(ERR, "link status not supported yet");
- return -ENOTSUP;
- }
+ error = hn_rndis_set_rxfilter(hv,
+ NDIS_PACKET_TYPE_BROADCAST |
+ NDIS_PACKET_TYPE_ALL_MULTICAST |
+ NDIS_PACKET_TYPE_DIRECTED);
+ if (error)
+ return error;
+
+ error = hn_vf_start(dev);
+ if (error)
+ hn_rndis_set_rxfilter(hv, 0);
- return hn_rndis_set_rxfilter(hv,
- NDIS_PACKET_TYPE_BROADCAST |
- NDIS_PACKET_TYPE_ALL_MULTICAST |
- NDIS_PACKET_TYPE_DIRECTED);
+ return error;
}
static void
@@ -522,12 +626,15 @@ hn_dev_stop(struct rte_eth_dev *dev)
PMD_INIT_FUNC_TRACE();
hn_rndis_set_rxfilter(hv, 0);
+ hn_vf_stop(dev);
}
static void
hn_dev_close(struct rte_eth_dev *dev __rte_unused)
{
PMD_INIT_LOG(DEBUG, "close");
+
+ hn_vf_close(dev);
}
static const struct eth_dev_ops hn_eth_dev_ops = {
@@ -536,22 +643,23 @@ static const struct eth_dev_ops hn_eth_dev_ops = {
.dev_stop = hn_dev_stop,
.dev_close = hn_dev_close,
.dev_infos_get = hn_dev_info_get,
- .txq_info_get = hn_dev_tx_queue_info,
- .rxq_info_get = hn_dev_rx_queue_info,
+ .dev_supported_ptypes_get = hn_vf_supported_ptypes,
.promiscuous_enable = hn_dev_promiscuous_enable,
.promiscuous_disable = hn_dev_promiscuous_disable,
.allmulticast_enable = hn_dev_allmulticast_enable,
.allmulticast_disable = hn_dev_allmulticast_disable,
+ .set_mc_addr_list = hn_dev_mc_addr_list,
.tx_queue_setup = hn_dev_tx_queue_setup,
.tx_queue_release = hn_dev_tx_queue_release,
+ .tx_done_cleanup = hn_dev_tx_done_cleanup,
.rx_queue_setup = hn_dev_rx_queue_setup,
.rx_queue_release = hn_dev_rx_queue_release,
.link_update = hn_dev_link_update,
.stats_get = hn_dev_stats_get,
+ .stats_reset = hn_dev_stats_reset,
.xstats_get = hn_dev_xstats_get,
.xstats_get_names = hn_dev_xstats_get_names,
- .stats_reset = hn_dev_stats_reset,
- .xstats_reset = hn_dev_stats_reset,
+ .xstats_reset = hn_dev_xstats_reset,
};
/*
@@ -623,12 +731,27 @@ eth_hn_dev_init(struct rte_eth_dev *eth_dev)
hv->rxbuf_res = &vmbus->resource[HV_RECV_BUF_MAP];
hv->chim_res = &vmbus->resource[HV_SEND_BUF_MAP];
hv->port_id = eth_dev->data->port_id;
+ hv->latency = HN_CHAN_LATENCY_NS;
+
+ err = hn_parse_args(eth_dev);
+ if (err)
+ return err;
+
+ strlcpy(hv->owner.name, eth_dev->device->name,
+ RTE_ETH_MAX_OWNER_NAME_LEN);
+ err = rte_eth_dev_owner_new(&hv->owner.id);
+ if (err) {
+ PMD_INIT_LOG(ERR, "Can not get owner id");
+ return err;
+ }
/* Initialize primary channel input for control operations */
err = rte_vmbus_chan_open(vmbus, &hv->channels[0]);
if (err)
return err;
+ rte_vmbus_set_latency(hv->vmbus, hv->channels[0], hv->latency);
+
hv->primary = hn_rx_queue_alloc(hv, 0,
eth_dev->device->numa_node);
@@ -657,6 +780,15 @@ eth_hn_dev_init(struct rte_eth_dev *eth_dev)
hv->max_queues = RTE_MIN(rxr_cnt, (unsigned int)max_chan);
+ /* If VF was reported but not added, do it now */
+ if (hv->vf_present && !hv->vf_dev) {
+ PMD_INIT_LOG(DEBUG, "Adding VF device");
+
+ err = hn_vf_add(eth_dev, hv);
+ if (err)
+ goto failed;
+ }
+
return 0;
failed:
@@ -686,8 +818,7 @@ eth_hn_dev_uninit(struct rte_eth_dev *eth_dev)
hn_detach(hv);
rte_vmbus_chan_close(hv->primary->chan);
rte_free(hv->primary);
-
- eth_dev->data->mac_addrs = NULL;
+ rte_eth_dev_owner_delete(hv->owner.id);
return 0;
}
diff --git a/drivers/net/netvsc/hn_nvs.c b/drivers/net/netvsc/hn_nvs.c
index 77d3b839..9690c5f8 100644
--- a/drivers/net/netvsc/hn_nvs.c
+++ b/drivers/net/netvsc/hn_nvs.c
@@ -279,14 +279,13 @@ hn_nvs_conn_chim(struct hn_data *hv)
NVS_TYPE_CHIM_CONNRESP);
if (error) {
PMD_DRV_LOG(ERR, "exec nvs chim conn failed");
- goto cleanup;
+ return error;
}
if (resp.status != NVS_STATUS_OK) {
PMD_DRV_LOG(ERR, "nvs chim conn failed: %x",
resp.status);
- error = -EIO;
- goto cleanup;
+ return -EIO;
}
sectsz = resp.sectsz;
@@ -295,7 +294,8 @@ hn_nvs_conn_chim(struct hn_data *hv)
PMD_DRV_LOG(NOTICE,
"invalid chimney sending buffer section size: %u",
sectsz);
- return 0;
+ error = -EINVAL;
+ goto cleanup;
}
hv->chim_szmax = sectsz;
@@ -304,11 +304,6 @@ hn_nvs_conn_chim(struct hn_data *hv)
PMD_DRV_LOG(INFO, "send buffer %lu section size:%u, count:%u",
len, hv->chim_szmax, hv->chim_cnt);
- if (len % hv->chim_szmax != 0) {
- PMD_DRV_LOG(NOTICE,
- "chimney sending sections are not properly aligned");
- }
-
/* Done! */
return 0;
@@ -537,10 +532,19 @@ void
hn_nvs_set_datapath(struct hn_data *hv, uint32_t path)
{
struct hn_nvs_datapath dp;
+ int error;
+
+ PMD_DRV_LOG(DEBUG, "set datapath %s",
+ path ? "VF" : "Synthetic");
memset(&dp, 0, sizeof(dp));
dp.type = NVS_TYPE_SET_DATAPATH;
dp.active_path = path;
- hn_nvs_req_send(hv, &dp, sizeof(dp));
+ error = hn_nvs_req_send(hv, &dp, sizeof(dp));
+ if (error) {
+ PMD_DRV_LOG(ERR,
+ "send set datapath failed: %d",
+ error);
+ }
}
diff --git a/drivers/net/netvsc/hn_nvs.h b/drivers/net/netvsc/hn_nvs.h
index 984a9c11..2563fd8d 100644
--- a/drivers/net/netvsc/hn_nvs.h
+++ b/drivers/net/netvsc/hn_nvs.h
@@ -105,6 +105,12 @@ struct hn_nvs_ndis_init {
uint8_t rsvd[28];
} __rte_packed;
+struct hn_nvs_vf_association {
+ uint32_t type; /* NVS_TYPE_VFASSOC_NOTE */
+ uint32_t allocated;
+ uint32_t serial;
+} __rte_packed;
+
#define NVS_DATAPATH_SYNTHETIC 0
#define NVS_DATAPATH_VF 1
@@ -207,6 +213,9 @@ void hn_nvs_detach(struct hn_data *hv);
void hn_nvs_ack_rxbuf(struct vmbus_channel *chan, uint64_t tid);
int hn_nvs_alloc_subchans(struct hn_data *hv, uint32_t *nsubch);
void hn_nvs_set_datapath(struct hn_data *hv, uint32_t path);
+void hn_nvs_handle_vfassoc(struct rte_eth_dev *dev,
+ const struct vmbus_chanpkt_hdr *hdr,
+ const void *data);
static inline int
hn_nvs_send(struct vmbus_channel *chan, uint16_t flags,
diff --git a/drivers/net/netvsc/hn_rndis.c b/drivers/net/netvsc/hn_rndis.c
index bde33969..0134ecb6 100644
--- a/drivers/net/netvsc/hn_rndis.c
+++ b/drivers/net/netvsc/hn_rndis.c
@@ -11,6 +11,7 @@
#include <errno.h>
#include <unistd.h>
+#include <rte_ethdev_driver.h>
#include <rte_ethdev.h>
#include <rte_string_fns.h>
#include <rte_memzone.h>
@@ -281,7 +282,7 @@ static int hn_nvs_send_rndis_ctrl(struct vmbus_channel *chan,
&nvs_rndis, sizeof(nvs_rndis), 0U, NULL);
}
-void hn_rndis_link_status(struct hn_data *hv __rte_unused, const void *msg)
+void hn_rndis_link_status(struct rte_eth_dev *dev, const void *msg)
{
const struct rndis_status_msg *indicate = msg;
@@ -290,15 +291,19 @@ void hn_rndis_link_status(struct hn_data *hv __rte_unused, const void *msg)
PMD_DRV_LOG(DEBUG, "link status %#x", indicate->status);
switch (indicate->status) {
- case RNDIS_STATUS_LINK_SPEED_CHANGE:
case RNDIS_STATUS_NETWORK_CHANGE:
case RNDIS_STATUS_TASK_OFFLOAD_CURRENT_CONFIG:
/* ignore not in DPDK API */
break;
+ case RNDIS_STATUS_LINK_SPEED_CHANGE:
case RNDIS_STATUS_MEDIA_CONNECT:
case RNDIS_STATUS_MEDIA_DISCONNECT:
- /* TODO handle as LSC interrupt */
+ if (dev->data->dev_conf.intr_conf.lsc &&
+ hn_dev_link_update(dev, 0) == 0)
+ _rte_eth_dev_callback_process(dev,
+ RTE_ETH_EVENT_INTR_LSC,
+ NULL);
break;
default:
PMD_DRV_LOG(NOTICE, "unknown RNDIS indication: %#x",
@@ -382,7 +387,7 @@ static int hn_rndis_exec1(struct hn_data *hv,
if (comp) {
/* Poll primary channel until response received */
while (hv->rndis_pending == rid)
- hn_process_events(hv, 0);
+ hn_process_events(hv, 0, 1);
memcpy(comp, hv->rndis_resp, comp_len);
}
@@ -892,8 +897,7 @@ int hn_rndis_get_offload(struct hn_data *hv,
== HN_NDIS_LSOV2_CAP_IP6)
dev_info->tx_offload_capa |= DEV_TX_OFFLOAD_TCP_TSO;
- dev_info->rx_offload_capa = DEV_RX_OFFLOAD_VLAN_STRIP |
- DEV_RX_OFFLOAD_CRC_STRIP;
+ dev_info->rx_offload_capa = DEV_RX_OFFLOAD_VLAN_STRIP;
if (hwcaps.ndis_csum.ndis_ip4_rxcsum & NDIS_RXCSUM_CAP_IP4)
dev_info->rx_offload_capa |= DEV_RX_OFFLOAD_IPV4_CKSUM;
@@ -909,6 +913,37 @@ int hn_rndis_get_offload(struct hn_data *hv,
return 0;
}
+uint32_t
+hn_rndis_get_ptypes(struct hn_data *hv)
+{
+ struct ndis_offload hwcaps;
+ uint32_t ptypes;
+ int error;
+
+ memset(&hwcaps, 0, sizeof(hwcaps));
+
+ error = hn_rndis_query_hwcaps(hv, &hwcaps);
+ if (error) {
+ PMD_DRV_LOG(ERR, "hwcaps query failed: %d", error);
+ return RTE_PTYPE_L2_ETHER;
+ }
+
+ ptypes = RTE_PTYPE_L2_ETHER;
+
+ if (hwcaps.ndis_csum.ndis_ip4_rxcsum & NDIS_RXCSUM_CAP_IP4)
+ ptypes |= RTE_PTYPE_L3_IPV4;
+
+ if ((hwcaps.ndis_csum.ndis_ip4_rxcsum & NDIS_RXCSUM_CAP_TCP4) ||
+ (hwcaps.ndis_csum.ndis_ip6_rxcsum & NDIS_RXCSUM_CAP_TCP6))
+ ptypes |= RTE_PTYPE_L4_TCP;
+
+ if ((hwcaps.ndis_csum.ndis_ip4_rxcsum & NDIS_RXCSUM_CAP_UDP4) ||
+ (hwcaps.ndis_csum.ndis_ip6_rxcsum & NDIS_RXCSUM_CAP_UDP6))
+ ptypes |= RTE_PTYPE_L4_UDP;
+
+ return ptypes;
+}
+
int
hn_rndis_set_rxfilter(struct hn_data *hv, uint32_t filter)
{
diff --git a/drivers/net/netvsc/hn_rndis.h b/drivers/net/netvsc/hn_rndis.h
index 89e2e6ba..319b497a 100644
--- a/drivers/net/netvsc/hn_rndis.h
+++ b/drivers/net/netvsc/hn_rndis.h
@@ -6,7 +6,7 @@ struct hn_data;
void hn_rndis_receive_response(struct hn_data *hv,
const void *data, uint32_t len);
-void hn_rndis_link_status(struct hn_data *hv, const void *data);
+void hn_rndis_link_status(struct rte_eth_dev *dev, const void *msg);
int hn_rndis_attach(struct hn_data *hv);
void hn_rndis_detach(struct hn_data *hv);
int hn_rndis_get_eaddr(struct hn_data *hv, uint8_t *eaddr);
@@ -24,6 +24,7 @@ int hn_rndis_query_rsscaps(struct hn_data *hv,
unsigned int *rxr_cnt0);
int hn_rndis_conf_rss(struct hn_data *hv,
const struct rte_eth_rss_conf *rss_conf);
+uint32_t hn_rndis_get_ptypes(struct hn_data *hv);
#ifdef RTE_LIBRTE_NETVSC_DEBUG_DUMP
void hn_rndis_dump(const void *buf);
diff --git a/drivers/net/netvsc/hn_rxtx.c b/drivers/net/netvsc/hn_rxtx.c
index 02ef27e3..f4a36641 100644
--- a/drivers/net/netvsc/hn_rxtx.c
+++ b/drivers/net/netvsc/hn_rxtx.c
@@ -10,6 +10,7 @@
#include <errno.h>
#include <unistd.h>
#include <strings.h>
+#include <malloc.h>
#include <rte_ethdev.h>
#include <rte_memcpy.h>
@@ -216,6 +217,7 @@ hn_dev_tx_queue_setup(struct rte_eth_dev *dev,
struct hn_data *hv = dev->data->dev_private;
struct hn_tx_queue *txq;
uint32_t tx_free_thresh;
+ int err;
PMD_INIT_FUNC_TRACE();
@@ -245,8 +247,14 @@ hn_dev_tx_queue_setup(struct rte_eth_dev *dev,
hn_reset_txagg(txq);
- dev->data->tx_queues[queue_idx] = txq;
+ err = hn_vf_tx_queue_setup(dev, queue_idx, nb_desc,
+ socket_id, tx_conf);
+ if (err) {
+ rte_free(txq);
+ return err;
+ }
+ dev->data->tx_queues[queue_idx] = txq;
return 0;
}
@@ -269,17 +277,6 @@ hn_dev_tx_queue_release(void *arg)
rte_free(txq);
}
-void
-hn_dev_tx_queue_info(struct rte_eth_dev *dev, uint16_t queue_idx,
- struct rte_eth_txq_info *qinfo)
-{
- struct hn_data *hv = dev->data->dev_private;
- struct hn_tx_queue *txq = dev->data->rx_queues[queue_idx];
-
- qinfo->conf.tx_free_thresh = txq->free_thresh;
- qinfo->nb_desc = hv->tx_pool->size;
-}
-
static void
hn_nvs_send_completed(struct rte_eth_dev *dev, uint16_t queue_id,
unsigned long xactid, const struct hn_nvs_rndis_ack *ack)
@@ -533,7 +530,7 @@ static void hn_rxpkt(struct hn_rx_queue *rxq, struct hn_rx_bufinfo *rxb,
hn_update_packet_stats(&rxq->stats, m);
if (unlikely(rte_ring_sp_enqueue(rxq->rx_ring, m) != 0)) {
- ++rxq->ring_full;
+ ++rxq->stats.ring_full;
rte_pktmbuf_free(m);
}
}
@@ -600,7 +597,7 @@ error:
}
static void
-hn_rndis_receive(const struct rte_eth_dev *dev, struct hn_rx_queue *rxq,
+hn_rndis_receive(struct rte_eth_dev *dev, struct hn_rx_queue *rxq,
struct hn_rx_bufinfo *rxb, void *buf, uint32_t len)
{
const struct rndis_msghdr *hdr = buf;
@@ -612,7 +609,7 @@ hn_rndis_receive(const struct rte_eth_dev *dev, struct hn_rx_queue *rxq,
break;
case RNDIS_INDICATE_STATUS_MSG:
- hn_rndis_link_status(rxq->hv, buf);
+ hn_rndis_link_status(dev, buf);
break;
case RNDIS_INITIALIZE_CMPLT:
@@ -712,22 +709,59 @@ hn_nvs_handle_rxbuf(struct rte_eth_dev *dev,
hn_rx_buf_release(rxb);
}
+/*
+ * Called when NVS inband events are received.
+ * Send up a two part message with port_id and the NVS message
+ * to the pipe to the netvsc-vf-event control thread.
+ */
+static void hn_nvs_handle_notify(struct rte_eth_dev *dev,
+ const struct vmbus_chanpkt_hdr *pkt,
+ const void *data)
+{
+ const struct hn_nvs_hdr *hdr = data;
+
+ switch (hdr->type) {
+ case NVS_TYPE_TXTBL_NOTE:
+ /* Transmit indirection table has locking problems
+ * in DPDK and therefore not implemented
+ */
+ PMD_DRV_LOG(DEBUG, "host notify of transmit indirection table");
+ break;
+
+ case NVS_TYPE_VFASSOC_NOTE:
+ hn_nvs_handle_vfassoc(dev, pkt, data);
+ break;
+
+ default:
+ PMD_DRV_LOG(INFO,
+ "got notify, nvs type %u", hdr->type);
+ }
+}
+
struct hn_rx_queue *hn_rx_queue_alloc(struct hn_data *hv,
uint16_t queue_id,
unsigned int socket_id)
{
struct hn_rx_queue *rxq;
- rxq = rte_zmalloc_socket("HN_RXQ",
- sizeof(*rxq) + HN_RXQ_EVENT_DEFAULT,
+ rxq = rte_zmalloc_socket("HN_RXQ", sizeof(*rxq),
RTE_CACHE_LINE_SIZE, socket_id);
- if (rxq) {
- rxq->hv = hv;
- rxq->chan = hv->channels[queue_id];
- rte_spinlock_init(&rxq->ring_lock);
- rxq->port_id = hv->port_id;
- rxq->queue_id = queue_id;
+ if (!rxq)
+ return NULL;
+
+ rxq->hv = hv;
+ rxq->chan = hv->channels[queue_id];
+ rte_spinlock_init(&rxq->ring_lock);
+ rxq->port_id = hv->port_id;
+ rxq->queue_id = queue_id;
+ rxq->event_sz = HN_RXQ_EVENT_DEFAULT;
+ rxq->event_buf = rte_malloc_socket("HN_EVENTS", HN_RXQ_EVENT_DEFAULT,
+ RTE_CACHE_LINE_SIZE, socket_id);
+ if (!rxq->event_buf) {
+ rte_free(rxq);
+ return NULL;
}
+
return rxq;
}
@@ -735,13 +769,14 @@ int
hn_dev_rx_queue_setup(struct rte_eth_dev *dev,
uint16_t queue_idx, uint16_t nb_desc,
unsigned int socket_id,
- const struct rte_eth_rxconf *rx_conf __rte_unused,
+ const struct rte_eth_rxconf *rx_conf,
struct rte_mempool *mp)
{
struct hn_data *hv = dev->data->dev_private;
char ring_name[RTE_RING_NAMESIZE];
struct hn_rx_queue *rxq;
unsigned int count;
+ int error = -ENOMEM;
PMD_INIT_FUNC_TRACE();
@@ -771,6 +806,11 @@ hn_dev_rx_queue_setup(struct rte_eth_dev *dev,
if (!rxq->rx_ring)
goto fail;
+ error = hn_vf_rx_queue_setup(dev, queue_idx, nb_desc,
+ socket_id, rx_conf, mp);
+ if (error)
+ goto fail;
+
dev->data->rx_queues[queue_idx] = rxq;
return 0;
@@ -778,7 +818,7 @@ fail:
rte_ring_free(rxq->rx_ring);
rte_free(rxq->event_buf);
rte_free(rxq);
- return -ENOMEM;
+ return error;
}
void
@@ -795,77 +835,79 @@ hn_dev_rx_queue_release(void *arg)
rxq->rx_ring = NULL;
rxq->mb_pool = NULL;
+ hn_vf_rx_queue_release(rxq->hv, rxq->queue_id);
+
+ /* Keep primary queue to allow for control operations */
if (rxq != rxq->hv->primary) {
rte_free(rxq->event_buf);
rte_free(rxq);
}
}
-void
-hn_dev_rx_queue_info(struct rte_eth_dev *dev, uint16_t queue_idx,
- struct rte_eth_rxq_info *qinfo)
-{
- struct hn_rx_queue *rxq = dev->data->rx_queues[queue_idx];
-
- qinfo->mp = rxq->mb_pool;
- qinfo->scattered_rx = 1;
- qinfo->nb_desc = rte_ring_get_capacity(rxq->rx_ring);
-}
-
-static void
-hn_nvs_handle_notify(const struct vmbus_chanpkt_hdr *pkthdr,
- const void *data)
+int
+hn_dev_tx_done_cleanup(void *arg, uint32_t free_cnt)
{
- const struct hn_nvs_hdr *hdr = data;
-
- if (unlikely(vmbus_chanpkt_datalen(pkthdr) < sizeof(*hdr))) {
- PMD_DRV_LOG(ERR, "invalid nvs notify");
- return;
- }
+ struct hn_tx_queue *txq = arg;
- PMD_DRV_LOG(INFO,
- "got notify, nvs type %u", hdr->type);
+ return hn_process_events(txq->hv, txq->queue_id, free_cnt);
}
/*
* Process pending events on the channel.
* Called from both Rx queue poll and Tx cleanup
*/
-void hn_process_events(struct hn_data *hv, uint16_t queue_id)
+uint32_t hn_process_events(struct hn_data *hv, uint16_t queue_id,
+ uint32_t tx_limit)
{
struct rte_eth_dev *dev = &rte_eth_devices[hv->port_id];
struct hn_rx_queue *rxq;
uint32_t bytes_read = 0;
+ uint32_t tx_done = 0;
int ret = 0;
rxq = queue_id == 0 ? hv->primary : dev->data->rx_queues[queue_id];
/* If no pending data then nothing to do */
if (rte_vmbus_chan_rx_empty(rxq->chan))
- return;
+ return 0;
/*
* Since channel is shared between Rx and TX queue need to have a lock
* since DPDK does not force same CPU to be used for Rx/Tx.
*/
if (unlikely(!rte_spinlock_trylock(&rxq->ring_lock)))
- return;
+ return 0;
for (;;) {
const struct vmbus_chanpkt_hdr *pkt;
- uint32_t len = HN_RXQ_EVENT_DEFAULT;
+ uint32_t len = rxq->event_sz;
const void *data;
+retry:
ret = rte_vmbus_chan_recv_raw(rxq->chan, rxq->event_buf, &len);
if (ret == -EAGAIN)
break; /* ring is empty */
- else if (ret == -ENOBUFS)
- rte_exit(EXIT_FAILURE, "event buffer not big enough (%u < %u)",
- HN_RXQ_EVENT_DEFAULT, len);
- else if (ret <= 0)
+ if (unlikely(ret == -ENOBUFS)) {
+ /* event buffer not large enough to read ring */
+
+ PMD_DRV_LOG(DEBUG,
+ "event buffer expansion (need %u)", len);
+ rxq->event_sz = len + len / 4;
+ rxq->event_buf = rte_realloc(rxq->event_buf, rxq->event_sz,
+ RTE_CACHE_LINE_SIZE);
+ if (rxq->event_buf)
+ goto retry;
+ /* out of memory, no more events now */
+ rxq->event_sz = 0;
+ break;
+ }
+
+ if (unlikely(ret <= 0)) {
+ /* This indicates a failure to communicate (or worse) */
rte_exit(EXIT_FAILURE,
"vmbus ring buffer error: %d", ret);
+ }
bytes_read += ret;
pkt = (const struct vmbus_chanpkt_hdr *)rxq->event_buf;
@@ -873,6 +915,7 @@ void hn_process_events(struct hn_data *hv, uint16_t queue_id)
switch (pkt->type) {
case VMBUS_CHANPKT_TYPE_COMP:
+ ++tx_done;
hn_nvs_handle_comp(dev, queue_id, pkt, data);
break;
@@ -881,7 +924,7 @@ void hn_process_events(struct hn_data *hv, uint16_t queue_id)
break;
case VMBUS_CHANPKT_TYPE_INBAND:
- hn_nvs_handle_notify(pkt, data);
+ hn_nvs_handle_notify(dev, pkt, data);
break;
default:
@@ -889,6 +932,9 @@ void hn_process_events(struct hn_data *hv, uint16_t queue_id)
break;
}
+ if (tx_limit && tx_done >= tx_limit)
+ break;
+
if (rxq->rx_ring && rte_ring_full(rxq->rx_ring))
break;
}
@@ -897,6 +943,8 @@ void hn_process_events(struct hn_data *hv, uint16_t queue_id)
rte_vmbus_chan_signal_read(rxq->chan, bytes_read);
rte_spinlock_unlock(&rxq->ring_lock);
+
+ return tx_done;
}
static void hn_append_to_chim(struct hn_tx_queue *txq,
@@ -967,7 +1015,7 @@ static struct hn_txdesc *hn_new_txd(struct hn_data *hv,
struct hn_txdesc *txd;
if (rte_mempool_get(hv->tx_pool, (void **)&txd)) {
- ++txq->stats.nomemory;
+ ++txq->stats.ring_full;
PMD_TX_LOG(DEBUG, "tx pool exhausted!");
return NULL;
}
@@ -1235,7 +1283,9 @@ uint16_t
hn_xmit_pkts(void *ptxq, struct rte_mbuf **tx_pkts, uint16_t nb_pkts)
{
struct hn_tx_queue *txq = ptxq;
+ uint16_t queue_id = txq->queue_id;
struct hn_data *hv = txq->hv;
+ struct rte_eth_dev *vf_dev;
bool need_sig = false;
uint16_t nb_tx;
int ret;
@@ -1243,8 +1293,17 @@ hn_xmit_pkts(void *ptxq, struct rte_mbuf **tx_pkts, uint16_t nb_pkts)
if (unlikely(hv->closed))
return 0;
+ /* Transmit over VF if present and up */
+ vf_dev = hv->vf_dev;
+ rte_compiler_barrier();
+ if (vf_dev && vf_dev->data->dev_started) {
+ void *sub_q = vf_dev->data->tx_queues[queue_id];
+
+ return (*vf_dev->tx_pkt_burst)(sub_q, tx_pkts, nb_pkts);
+ }
+
if (rte_mempool_avail_count(hv->tx_pool) <= txq->free_thresh)
- hn_process_events(hv, txq->queue_id);
+ hn_process_events(hv, txq->queue_id, 0);
for (nb_tx = 0; nb_tx < nb_pkts; nb_tx++) {
struct rte_mbuf *m = tx_pkts[nb_tx];
@@ -1264,7 +1323,7 @@ hn_xmit_pkts(void *ptxq, struct rte_mbuf **tx_pkts, uint16_t nb_pkts)
if (unlikely(!pkt))
break;
- hn_encap(pkt, txq->queue_id, m);
+ hn_encap(pkt, queue_id, m);
hn_append_to_chim(txq, pkt, m);
rte_pktmbuf_free(m);
@@ -1291,7 +1350,7 @@ hn_xmit_pkts(void *ptxq, struct rte_mbuf **tx_pkts, uint16_t nb_pkts)
txd->data_size += m->pkt_len;
++txd->packets;
- hn_encap(pkt, txq->queue_id, m);
+ hn_encap(pkt, queue_id, m);
ret = hn_xmit_sg(txq, txd, m, &need_sig);
if (unlikely(ret != 0)) {
@@ -1320,15 +1379,36 @@ hn_recv_pkts(void *prxq, struct rte_mbuf **rx_pkts, uint16_t nb_pkts)
{
struct hn_rx_queue *rxq = prxq;
struct hn_data *hv = rxq->hv;
+ struct rte_eth_dev *vf_dev;
+ uint16_t nb_rcv;
if (unlikely(hv->closed))
return 0;
- /* If ring is empty then process more */
- if (rte_ring_count(rxq->rx_ring) < nb_pkts)
- hn_process_events(hv, rxq->queue_id);
+ vf_dev = hv->vf_dev;
+ rte_compiler_barrier();
+
+ if (vf_dev && vf_dev->data->dev_started) {
+ /* Normally, with SR-IOV the ring buffer will be empty */
+ hn_process_events(hv, rxq->queue_id, 0);
+
+ /* Get mbufs some bufs off of staging ring */
+ nb_rcv = rte_ring_sc_dequeue_burst(rxq->rx_ring,
+ (void **)rx_pkts,
+ nb_pkts / 2, NULL);
+ /* And rest off of VF */
+ nb_rcv += rte_eth_rx_burst(vf_dev->data->port_id,
+ rxq->queue_id,
+ rx_pkts + nb_rcv, nb_pkts - nb_rcv);
+ } else {
+ /* If receive ring is not full then get more */
+ if (rte_ring_count(rxq->rx_ring) < nb_pkts)
+ hn_process_events(hv, rxq->queue_id, 0);
+
+ nb_rcv = rte_ring_sc_dequeue_burst(rxq->rx_ring,
+ (void **)rx_pkts,
+ nb_pkts, NULL);
+ }
- /* Get mbufs off staging ring */
- return rte_ring_sc_dequeue_burst(rxq->rx_ring, (void **)rx_pkts,
- nb_pkts, NULL);
+ return nb_rcv;
}
diff --git a/drivers/net/netvsc/hn_var.h b/drivers/net/netvsc/hn_var.h
index f7ff8585..e1072c7c 100644
--- a/drivers/net/netvsc/hn_var.h
+++ b/drivers/net/netvsc/hn_var.h
@@ -20,6 +20,9 @@
/* Retry interval */
#define HN_CHAN_INTERVAL_US 100
+/* Host monitor interval */
+#define HN_CHAN_LATENCY_NS 50000
+
/* Buffers need to be aligned */
#ifndef PAGE_SIZE
#define PAGE_SIZE 4096
@@ -36,7 +39,7 @@ struct hn_stats {
uint64_t packets;
uint64_t bytes;
uint64_t errors;
- uint64_t nomemory;
+ uint64_t ring_full;
uint64_t multicast;
uint64_t broadcast;
/* Size bins in array as RFC 2819, undersized [0], 64 [1], etc */
@@ -75,9 +78,8 @@ struct hn_rx_queue {
uint16_t port_id;
uint16_t queue_id;
struct hn_stats stats;
- uint64_t ring_full;
- uint8_t event_buf[];
+ void *event_buf;
};
@@ -92,8 +94,11 @@ struct hn_rx_bufinfo {
struct hn_data {
struct rte_vmbus_device *vmbus;
struct hn_rx_queue *primary;
+ struct rte_eth_dev *vf_dev; /* Subordinate device */
+ rte_spinlock_t vf_lock;
uint16_t port_id;
bool closed;
+ bool vf_present;
uint32_t link_status;
uint32_t link_speed;
@@ -110,6 +115,7 @@ struct hn_data {
uint32_t chim_szmax; /* Max size per buffer */
uint32_t chim_cnt; /* Max packets per buffer */
+ uint32_t latency;
uint32_t nvs_ver;
uint32_t ndis_ver;
uint32_t rndis_agg_size;
@@ -121,6 +127,10 @@ struct hn_data {
uint8_t rndis_resp[256];
struct ether_addr mac_addr;
+
+ struct rte_eth_dev_owner owner;
+ struct rte_intr_handle vf_intr;
+
struct vmbus_channel *channels[HN_MAX_CHANNELS];
};
@@ -130,7 +140,8 @@ hn_primary_chan(const struct hn_data *hv)
return hv->channels[0];
}
-void hn_process_events(struct hn_data *hv, uint16_t queue_id);
+uint32_t hn_process_events(struct hn_data *hv, uint16_t queue_id,
+ uint32_t tx_limit);
uint16_t hn_xmit_pkts(void *tx_queue, struct rte_mbuf **tx_pkts,
uint16_t nb_pkts);
@@ -138,12 +149,14 @@ uint16_t hn_recv_pkts(void *rx_queue, struct rte_mbuf **rx_pkts,
uint16_t nb_pkts);
int hn_tx_pool_init(struct rte_eth_dev *dev);
+int hn_dev_link_update(struct rte_eth_dev *dev, int wait);
int hn_dev_tx_queue_setup(struct rte_eth_dev *dev, uint16_t queue_idx,
uint16_t nb_desc, unsigned int socket_id,
const struct rte_eth_txconf *tx_conf);
void hn_dev_tx_queue_release(void *arg);
void hn_dev_tx_queue_info(struct rte_eth_dev *dev, uint16_t queue_idx,
struct rte_eth_txq_info *qinfo);
+int hn_dev_tx_done_cleanup(void *arg, uint32_t free_cnt);
struct hn_rx_queue *hn_rx_queue_alloc(struct hn_data *hv,
uint16_t queue_id,
@@ -154,5 +167,46 @@ int hn_dev_rx_queue_setup(struct rte_eth_dev *dev,
const struct rte_eth_rxconf *rx_conf,
struct rte_mempool *mp);
void hn_dev_rx_queue_release(void *arg);
-void hn_dev_rx_queue_info(struct rte_eth_dev *dev, uint16_t queue_idx,
- struct rte_eth_rxq_info *qinfo);
+
+void hn_vf_info_get(struct hn_data *hv,
+ struct rte_eth_dev_info *info);
+int hn_vf_add(struct rte_eth_dev *dev, struct hn_data *hv);
+int hn_vf_configure(struct rte_eth_dev *dev,
+ const struct rte_eth_conf *dev_conf);
+const uint32_t *hn_vf_supported_ptypes(struct rte_eth_dev *dev);
+int hn_vf_start(struct rte_eth_dev *dev);
+void hn_vf_reset(struct rte_eth_dev *dev);
+void hn_vf_stop(struct rte_eth_dev *dev);
+void hn_vf_close(struct rte_eth_dev *dev);
+
+void hn_vf_allmulticast_enable(struct rte_eth_dev *dev);
+void hn_vf_allmulticast_disable(struct rte_eth_dev *dev);
+void hn_vf_promiscuous_enable(struct rte_eth_dev *dev);
+void hn_vf_promiscuous_disable(struct rte_eth_dev *dev);
+int hn_vf_mc_addr_list(struct rte_eth_dev *dev,
+ struct ether_addr *mc_addr_set,
+ uint32_t nb_mc_addr);
+
+int hn_vf_link_update(struct rte_eth_dev *dev,
+ int wait_to_complete);
+int hn_vf_tx_queue_setup(struct rte_eth_dev *dev,
+ uint16_t queue_idx, uint16_t nb_desc,
+ unsigned int socket_id,
+ const struct rte_eth_txconf *tx_conf);
+void hn_vf_tx_queue_release(struct hn_data *hv, uint16_t queue_id);
+int hn_vf_rx_queue_setup(struct rte_eth_dev *dev,
+ uint16_t queue_idx, uint16_t nb_desc,
+ unsigned int socket_id,
+ const struct rte_eth_rxconf *rx_conf,
+ struct rte_mempool *mp);
+void hn_vf_rx_queue_release(struct hn_data *hv, uint16_t queue_id);
+
+int hn_vf_stats_get(struct rte_eth_dev *dev, struct rte_eth_stats *stats);
+void hn_vf_stats_reset(struct rte_eth_dev *dev);
+int hn_vf_xstats_get_names(struct rte_eth_dev *dev,
+ struct rte_eth_xstat_name *xstats_names,
+ unsigned int size);
+int hn_vf_xstats_get(struct rte_eth_dev *dev,
+ struct rte_eth_xstat *xstats,
+ unsigned int n);
+void hn_vf_xstats_reset(struct rte_eth_dev *dev);
diff --git a/drivers/net/netvsc/hn_vf.c b/drivers/net/netvsc/hn_vf.c
new file mode 100644
index 00000000..7a84ad8c
--- /dev/null
+++ b/drivers/net/netvsc/hn_vf.c
@@ -0,0 +1,549 @@
+/* SPDX-License-Identifier: BSD-3-Clause
+ * Copyright (c) 2018 Microsoft Corp.
+ * All rights reserved.
+ */
+
+#include <stdio.h>
+#include <stdint.h>
+#include <string.h>
+#include <stdbool.h>
+#include <errno.h>
+#include <unistd.h>
+#include <dirent.h>
+#include <sys/types.h>
+#include <sys/fcntl.h>
+#include <sys/uio.h>
+
+#include <rte_ether.h>
+#include <rte_ethdev.h>
+#include <rte_ethdev_driver.h>
+#include <rte_lcore.h>
+#include <rte_memory.h>
+#include <rte_bus_vmbus.h>
+#include <rte_pci.h>
+#include <rte_bus_pci.h>
+#include <rte_log.h>
+#include <rte_string_fns.h>
+
+#include "hn_logs.h"
+#include "hn_var.h"
+#include "hn_nvs.h"
+
+/* Search for VF with matching MAC address, return port id */
+static int hn_vf_match(const struct rte_eth_dev *dev)
+{
+ const struct ether_addr *mac = dev->data->mac_addrs;
+ char buf[32];
+ int i;
+
+ ether_format_addr(buf, sizeof(buf), mac);
+ RTE_ETH_FOREACH_DEV(i) {
+ const struct rte_eth_dev *vf_dev = &rte_eth_devices[i];
+ const struct ether_addr *vf_mac = vf_dev->data->mac_addrs;
+
+ if (vf_dev == dev)
+ continue;
+
+ ether_format_addr(buf, sizeof(buf), vf_mac);
+ if (is_same_ether_addr(mac, vf_mac))
+ return i;
+ }
+ return -ENOENT;
+}
+
+/*
+ * Attach new PCI VF device and return the port_id
+ */
+static int hn_vf_attach(struct hn_data *hv, uint16_t port_id,
+ struct rte_eth_dev **vf_dev)
+{
+ struct rte_eth_dev_owner owner = { .id = RTE_ETH_DEV_NO_OWNER };
+ int ret;
+
+ ret = rte_eth_dev_owner_get(port_id, &owner);
+ if (ret < 0) {
+ PMD_DRV_LOG(ERR, "Can not find owner for port %d", port_id);
+ return ret;
+ }
+
+ if (owner.id != RTE_ETH_DEV_NO_OWNER) {
+ PMD_DRV_LOG(ERR, "Port %u already owned by other device %s",
+ port_id, owner.name);
+ return -EBUSY;
+ }
+
+ ret = rte_eth_dev_owner_set(port_id, &hv->owner);
+ if (ret < 0) {
+ PMD_DRV_LOG(ERR, "Can set owner for port %d", port_id);
+ return ret;
+ }
+
+ PMD_DRV_LOG(DEBUG, "Attach VF device %u", port_id);
+ rte_smp_wmb();
+ *vf_dev = &rte_eth_devices[port_id];
+ return 0;
+}
+
+/* Add new VF device to synthetic device */
+int hn_vf_add(struct rte_eth_dev *dev, struct hn_data *hv)
+{
+ int port, err;
+
+ port = hn_vf_match(dev);
+ if (port < 0) {
+ PMD_DRV_LOG(NOTICE, "No matching MAC found");
+ return port;
+ }
+
+ rte_spinlock_lock(&hv->vf_lock);
+ if (hv->vf_dev) {
+ PMD_DRV_LOG(ERR, "VF already attached");
+ err = -EBUSY;
+ } else {
+ err = hn_vf_attach(hv, port, &hv->vf_dev);
+ }
+
+ if (err == 0) {
+ dev->data->dev_flags |= RTE_ETH_DEV_INTR_LSC;
+ hv->vf_intr = (struct rte_intr_handle) {
+ .fd = -1,
+ .type = RTE_INTR_HANDLE_EXT,
+ };
+ dev->intr_handle = &hv->vf_intr;
+ hn_nvs_set_datapath(hv, NVS_DATAPATH_VF);
+ }
+ rte_spinlock_unlock(&hv->vf_lock);
+
+ return err;
+}
+
+/* Remove new VF device */
+static void hn_vf_remove(struct hn_data *hv)
+{
+ struct rte_eth_dev *vf_dev;
+
+ rte_spinlock_lock(&hv->vf_lock);
+ vf_dev = hv->vf_dev;
+ if (!vf_dev) {
+ PMD_DRV_LOG(ERR, "VF path not active");
+ rte_spinlock_unlock(&hv->vf_lock);
+ return;
+ }
+
+ /* Stop incoming packets from arriving on VF */
+ hn_nvs_set_datapath(hv, NVS_DATAPATH_SYNTHETIC);
+ hv->vf_dev = NULL;
+
+ /* Give back ownership */
+ rte_eth_dev_owner_unset(vf_dev->data->port_id, hv->owner.id);
+ rte_spinlock_unlock(&hv->vf_lock);
+}
+
+/* Handle VF association message from host */
+void
+hn_nvs_handle_vfassoc(struct rte_eth_dev *dev,
+ const struct vmbus_chanpkt_hdr *hdr,
+ const void *data)
+{
+ struct hn_data *hv = dev->data->dev_private;
+ const struct hn_nvs_vf_association *vf_assoc = data;
+
+ if (unlikely(vmbus_chanpkt_datalen(hdr) < sizeof(*vf_assoc))) {
+ PMD_DRV_LOG(ERR, "invalid vf association NVS");
+ return;
+ }
+
+ PMD_DRV_LOG(DEBUG, "VF serial %u %s port %u",
+ vf_assoc->serial,
+ vf_assoc->allocated ? "add to" : "remove from",
+ dev->data->port_id);
+
+ hv->vf_present = vf_assoc->allocated;
+
+ if (dev->state != RTE_ETH_DEV_ATTACHED)
+ return;
+
+ if (vf_assoc->allocated)
+ hn_vf_add(dev, hv);
+ else
+ hn_vf_remove(hv);
+}
+
+/*
+ * Merge the info from the VF and synthetic path.
+ * use the default config of the VF
+ * and the minimum number of queues and buffer sizes.
+ */
+static void hn_vf_info_merge(struct rte_eth_dev *vf_dev,
+ struct rte_eth_dev_info *info)
+{
+ struct rte_eth_dev_info vf_info;
+
+ rte_eth_dev_info_get(vf_dev->data->port_id, &vf_info);
+
+ info->speed_capa = vf_info.speed_capa;
+ info->default_rxportconf = vf_info.default_rxportconf;
+ info->default_txportconf = vf_info.default_txportconf;
+
+ info->max_rx_queues = RTE_MIN(vf_info.max_rx_queues,
+ info->max_rx_queues);
+ info->rx_offload_capa &= vf_info.rx_offload_capa;
+ info->rx_queue_offload_capa &= vf_info.rx_queue_offload_capa;
+ info->flow_type_rss_offloads &= vf_info.flow_type_rss_offloads;
+
+ info->max_tx_queues = RTE_MIN(vf_info.max_tx_queues,
+ info->max_tx_queues);
+ info->tx_offload_capa &= vf_info.tx_offload_capa;
+ info->tx_queue_offload_capa &= vf_info.tx_queue_offload_capa;
+
+ info->min_rx_bufsize = RTE_MAX(vf_info.min_rx_bufsize,
+ info->min_rx_bufsize);
+ info->max_rx_pktlen = RTE_MAX(vf_info.max_rx_pktlen,
+ info->max_rx_pktlen);
+}
+
+void hn_vf_info_get(struct hn_data *hv, struct rte_eth_dev_info *info)
+{
+ struct rte_eth_dev *vf_dev;
+
+ rte_spinlock_lock(&hv->vf_lock);
+ vf_dev = hv->vf_dev;
+ if (vf_dev)
+ hn_vf_info_merge(vf_dev, info);
+ rte_spinlock_unlock(&hv->vf_lock);
+}
+
+int hn_vf_link_update(struct rte_eth_dev *dev,
+ int wait_to_complete)
+{
+ struct hn_data *hv = dev->data->dev_private;
+ struct rte_eth_dev *vf_dev;
+ int ret = 0;
+
+ rte_spinlock_lock(&hv->vf_lock);
+ vf_dev = hv->vf_dev;
+ if (vf_dev && vf_dev->dev_ops->link_update)
+ ret = (*vf_dev->dev_ops->link_update)(dev, wait_to_complete);
+ rte_spinlock_unlock(&hv->vf_lock);
+
+ return ret;
+}
+
+/* called when VF has link state interrupts enabled */
+static int hn_vf_lsc_event(uint16_t port_id __rte_unused,
+ enum rte_eth_event_type event,
+ void *cb_arg, void *out __rte_unused)
+{
+ struct rte_eth_dev *dev = cb_arg;
+
+ if (event != RTE_ETH_EVENT_INTR_LSC)
+ return 0;
+
+ /* if link state has changed pass on */
+ if (hn_dev_link_update(dev, 0) == 0)
+ return 0; /* no change */
+
+ return _rte_eth_dev_callback_process(dev,
+ RTE_ETH_EVENT_INTR_LSC,
+ NULL);
+}
+
+static int _hn_vf_configure(struct rte_eth_dev *dev,
+ struct rte_eth_dev *vf_dev,
+ const struct rte_eth_conf *dev_conf)
+{
+ struct rte_eth_conf vf_conf = *dev_conf;
+ uint16_t vf_port = vf_dev->data->port_id;
+ int ret;
+
+ if (dev_conf->intr_conf.lsc &&
+ (vf_dev->data->dev_flags & RTE_ETH_DEV_INTR_LSC)) {
+ PMD_DRV_LOG(DEBUG, "enabling LSC for VF %u",
+ vf_port);
+ vf_conf.intr_conf.lsc = 1;
+ } else {
+ PMD_DRV_LOG(DEBUG, "disabling LSC for VF %u",
+ vf_port);
+ vf_conf.intr_conf.lsc = 0;
+ }
+
+ ret = rte_eth_dev_configure(vf_port,
+ dev->data->nb_rx_queues,
+ dev->data->nb_tx_queues,
+ &vf_conf);
+ if (ret) {
+ PMD_DRV_LOG(ERR,
+ "VF configuration failed: %d", ret);
+ } else if (vf_conf.intr_conf.lsc) {
+ ret = rte_eth_dev_callback_register(vf_port,
+ RTE_ETH_DEV_INTR_LSC,
+ hn_vf_lsc_event, dev);
+ if (ret)
+ PMD_DRV_LOG(ERR,
+ "Failed to register LSC callback for VF %u",
+ vf_port);
+ }
+ return ret;
+}
+
+/*
+ * Configure VF if present.
+ * Force VF to have same number of queues as synthetic device
+ */
+int hn_vf_configure(struct rte_eth_dev *dev,
+ const struct rte_eth_conf *dev_conf)
+{
+ struct hn_data *hv = dev->data->dev_private;
+ struct rte_eth_dev *vf_dev;
+ int ret = 0;
+
+ rte_spinlock_lock(&hv->vf_lock);
+ vf_dev = hv->vf_dev;
+ if (vf_dev)
+ ret = _hn_vf_configure(dev, vf_dev, dev_conf);
+ rte_spinlock_unlock(&hv->vf_lock);
+ return ret;
+}
+
+const uint32_t *hn_vf_supported_ptypes(struct rte_eth_dev *dev)
+{
+ struct hn_data *hv = dev->data->dev_private;
+ struct rte_eth_dev *vf_dev;
+ const uint32_t *ptypes = NULL;
+
+ rte_spinlock_lock(&hv->vf_lock);
+ vf_dev = hv->vf_dev;
+ if (vf_dev && vf_dev->dev_ops->dev_supported_ptypes_get)
+ ptypes = (*vf_dev->dev_ops->dev_supported_ptypes_get)(vf_dev);
+ rte_spinlock_unlock(&hv->vf_lock);
+
+ return ptypes;
+}
+
+int hn_vf_start(struct rte_eth_dev *dev)
+{
+ struct hn_data *hv = dev->data->dev_private;
+ struct rte_eth_dev *vf_dev;
+ int ret = 0;
+
+ rte_spinlock_lock(&hv->vf_lock);
+ vf_dev = hv->vf_dev;
+ if (vf_dev)
+ ret = rte_eth_dev_start(vf_dev->data->port_id);
+ rte_spinlock_unlock(&hv->vf_lock);
+ return ret;
+}
+
+void hn_vf_stop(struct rte_eth_dev *dev)
+{
+ struct hn_data *hv = dev->data->dev_private;
+ struct rte_eth_dev *vf_dev;
+
+ rte_spinlock_lock(&hv->vf_lock);
+ vf_dev = hv->vf_dev;
+ if (vf_dev)
+ rte_eth_dev_stop(vf_dev->data->port_id);
+ rte_spinlock_unlock(&hv->vf_lock);
+}
+
+/* If VF is present, then cascade configuration down */
+#define VF_ETHDEV_FUNC(dev, func) \
+ { \
+ struct hn_data *hv = (dev)->data->dev_private; \
+ struct rte_eth_dev *vf_dev; \
+ rte_spinlock_lock(&hv->vf_lock); \
+ vf_dev = hv->vf_dev; \
+ if (vf_dev) \
+ func(vf_dev->data->port_id); \
+ rte_spinlock_unlock(&hv->vf_lock); \
+ }
+
+void hn_vf_reset(struct rte_eth_dev *dev)
+{
+ VF_ETHDEV_FUNC(dev, rte_eth_dev_reset);
+}
+
+void hn_vf_close(struct rte_eth_dev *dev)
+{
+ VF_ETHDEV_FUNC(dev, rte_eth_dev_close);
+}
+
+void hn_vf_stats_reset(struct rte_eth_dev *dev)
+{
+ VF_ETHDEV_FUNC(dev, rte_eth_stats_reset);
+}
+
+void hn_vf_allmulticast_enable(struct rte_eth_dev *dev)
+{
+ VF_ETHDEV_FUNC(dev, rte_eth_allmulticast_enable);
+}
+
+void hn_vf_allmulticast_disable(struct rte_eth_dev *dev)
+{
+ VF_ETHDEV_FUNC(dev, rte_eth_allmulticast_disable);
+}
+
+void hn_vf_promiscuous_enable(struct rte_eth_dev *dev)
+{
+ VF_ETHDEV_FUNC(dev, rte_eth_promiscuous_enable);
+}
+
+void hn_vf_promiscuous_disable(struct rte_eth_dev *dev)
+{
+ VF_ETHDEV_FUNC(dev, rte_eth_promiscuous_disable);
+}
+
+int hn_vf_mc_addr_list(struct rte_eth_dev *dev,
+ struct ether_addr *mc_addr_set,
+ uint32_t nb_mc_addr)
+{
+ struct hn_data *hv = dev->data->dev_private;
+ struct rte_eth_dev *vf_dev;
+ int ret = 0;
+
+ rte_spinlock_lock(&hv->vf_lock);
+ vf_dev = hv->vf_dev;
+ if (vf_dev)
+ ret = rte_eth_dev_set_mc_addr_list(vf_dev->data->port_id,
+ mc_addr_set, nb_mc_addr);
+ rte_spinlock_unlock(&hv->vf_lock);
+ return ret;
+}
+
+int hn_vf_tx_queue_setup(struct rte_eth_dev *dev,
+ uint16_t queue_idx, uint16_t nb_desc,
+ unsigned int socket_id,
+ const struct rte_eth_txconf *tx_conf)
+{
+ struct hn_data *hv = dev->data->dev_private;
+ struct rte_eth_dev *vf_dev;
+ int ret = 0;
+
+ rte_spinlock_lock(&hv->vf_lock);
+ vf_dev = hv->vf_dev;
+ if (vf_dev)
+ ret = rte_eth_tx_queue_setup(vf_dev->data->port_id,
+ queue_idx, nb_desc,
+ socket_id, tx_conf);
+ rte_spinlock_unlock(&hv->vf_lock);
+ return ret;
+}
+
+void hn_vf_tx_queue_release(struct hn_data *hv, uint16_t queue_id)
+{
+ struct rte_eth_dev *vf_dev;
+
+ rte_spinlock_lock(&hv->vf_lock);
+ vf_dev = hv->vf_dev;
+ if (vf_dev && vf_dev->dev_ops->tx_queue_release) {
+ void *subq = vf_dev->data->tx_queues[queue_id];
+
+ (*vf_dev->dev_ops->tx_queue_release)(subq);
+ }
+
+ rte_spinlock_unlock(&hv->vf_lock);
+}
+
+int hn_vf_rx_queue_setup(struct rte_eth_dev *dev,
+ uint16_t queue_idx, uint16_t nb_desc,
+ unsigned int socket_id,
+ const struct rte_eth_rxconf *rx_conf,
+ struct rte_mempool *mp)
+{
+ struct hn_data *hv = dev->data->dev_private;
+ struct rte_eth_dev *vf_dev;
+ int ret = 0;
+
+ rte_spinlock_lock(&hv->vf_lock);
+ vf_dev = hv->vf_dev;
+ if (vf_dev)
+ ret = rte_eth_rx_queue_setup(vf_dev->data->port_id,
+ queue_idx, nb_desc,
+ socket_id, rx_conf, mp);
+ rte_spinlock_unlock(&hv->vf_lock);
+ return ret;
+}
+
+void hn_vf_rx_queue_release(struct hn_data *hv, uint16_t queue_id)
+{
+ struct rte_eth_dev *vf_dev;
+
+ rte_spinlock_lock(&hv->vf_lock);
+ vf_dev = hv->vf_dev;
+ if (vf_dev && vf_dev->dev_ops->rx_queue_release) {
+ void *subq = vf_dev->data->rx_queues[queue_id];
+
+ (*vf_dev->dev_ops->rx_queue_release)(subq);
+ }
+ rte_spinlock_unlock(&hv->vf_lock);
+}
+
+int hn_vf_stats_get(struct rte_eth_dev *dev,
+ struct rte_eth_stats *stats)
+{
+ struct hn_data *hv = dev->data->dev_private;
+ struct rte_eth_dev *vf_dev;
+ int ret = 0;
+
+ rte_spinlock_lock(&hv->vf_lock);
+ vf_dev = hv->vf_dev;
+ if (vf_dev)
+ ret = rte_eth_stats_get(vf_dev->data->port_id, stats);
+ rte_spinlock_unlock(&hv->vf_lock);
+ return ret;
+}
+
+int hn_vf_xstats_get_names(struct rte_eth_dev *dev,
+ struct rte_eth_xstat_name *names,
+ unsigned int n)
+{
+ struct hn_data *hv = dev->data->dev_private;
+ struct rte_eth_dev *vf_dev;
+ int i, count = 0;
+ char tmp[RTE_ETH_XSTATS_NAME_SIZE];
+
+ rte_spinlock_lock(&hv->vf_lock);
+ vf_dev = hv->vf_dev;
+ if (vf_dev && vf_dev->dev_ops->xstats_get_names)
+ count = vf_dev->dev_ops->xstats_get_names(vf_dev, names, n);
+ rte_spinlock_unlock(&hv->vf_lock);
+
+ /* add vf_ prefix to xstat names */
+ if (names) {
+ for (i = 0; i < count; i++) {
+ snprintf(tmp, sizeof(tmp), "vf_%s", names[i].name);
+ strlcpy(names[i].name, tmp, sizeof(names[i].name));
+ }
+ }
+
+ return count;
+}
+
+int hn_vf_xstats_get(struct rte_eth_dev *dev,
+ struct rte_eth_xstat *xstats,
+ unsigned int n)
+{
+ struct hn_data *hv = dev->data->dev_private;
+ struct rte_eth_dev *vf_dev;
+ int count = 0;
+
+ rte_spinlock_lock(&hv->vf_lock);
+ vf_dev = hv->vf_dev;
+ if (vf_dev && vf_dev->dev_ops->xstats_get)
+ count = vf_dev->dev_ops->xstats_get(vf_dev, xstats, n);
+ rte_spinlock_unlock(&hv->vf_lock);
+
+ return count;
+}
+
+void hn_vf_xstats_reset(struct rte_eth_dev *dev)
+{
+ struct hn_data *hv = dev->data->dev_private;
+ struct rte_eth_dev *vf_dev;
+
+ rte_spinlock_lock(&hv->vf_lock);
+ vf_dev = hv->vf_dev;
+ if (vf_dev && vf_dev->dev_ops->xstats_reset)
+ vf_dev->dev_ops->xstats_reset(vf_dev);
+ rte_spinlock_unlock(&hv->vf_lock);
+}
diff --git a/drivers/net/netvsc/meson.build b/drivers/net/netvsc/meson.build
index a717cdd4..c8426971 100644
--- a/drivers/net/netvsc/meson.build
+++ b/drivers/net/netvsc/meson.build
@@ -3,7 +3,7 @@
build = dpdk_conf.has('RTE_LIBRTE_VMBUS_BUS')
version = 2
-sources = files('hn_ethdev.c', 'hn_rxtx.c', 'hn_rndis.c', 'hn_nvs.c')
+sources = files('hn_ethdev.c', 'hn_rxtx.c', 'hn_rndis.c', 'hn_nvs.c', 'hn_vf.c')
deps += ['bus_vmbus' ]