diff options
Diffstat (limited to 'drivers/net/netvsc')
-rw-r--r-- | drivers/net/netvsc/Makefile | 1 | ||||
-rw-r--r-- | drivers/net/netvsc/hn_ethdev.c | 221 | ||||
-rw-r--r-- | drivers/net/netvsc/hn_nvs.c | 24 | ||||
-rw-r--r-- | drivers/net/netvsc/hn_nvs.h | 9 | ||||
-rw-r--r-- | drivers/net/netvsc/hn_rndis.c | 47 | ||||
-rw-r--r-- | drivers/net/netvsc/hn_rndis.h | 3 | ||||
-rw-r--r-- | drivers/net/netvsc/hn_rxtx.c | 212 | ||||
-rw-r--r-- | drivers/net/netvsc/hn_var.h | 66 | ||||
-rw-r--r-- | drivers/net/netvsc/hn_vf.c | 549 | ||||
-rw-r--r-- | drivers/net/netvsc/meson.build | 2 |
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' ] |