+ sas->rxq_info[rx_queue_id].deferred_started = B_FALSE;
+
+ sfc_adapter_unlock(sa);
+
+ return 0;
+}
+
+static int
+sfc_tx_queue_start(struct rte_eth_dev *dev, uint16_t tx_queue_id)
+{
+ struct sfc_adapter_shared *sas = sfc_adapter_shared_by_eth_dev(dev);
+ struct sfc_adapter *sa = sfc_adapter_by_eth_dev(dev);
+ int rc;
+
+ sfc_log_init(sa, "TxQ = %u", tx_queue_id);
+
+ sfc_adapter_lock(sa);
+
+ rc = EINVAL;
+ if (sa->state != SFC_ADAPTER_STARTED)
+ goto fail_not_started;
+
+ if (sas->txq_info[tx_queue_id].state != SFC_TXQ_INITIALIZED)
+ goto fail_not_setup;
+
+ rc = sfc_tx_qstart(sa, tx_queue_id);
+ if (rc != 0)
+ goto fail_tx_qstart;
+
+ sas->txq_info[tx_queue_id].deferred_started = B_TRUE;
+
+ sfc_adapter_unlock(sa);
+ return 0;
+
+fail_tx_qstart:
+
+fail_not_setup:
+fail_not_started:
+ sfc_adapter_unlock(sa);
+ SFC_ASSERT(rc > 0);
+ return -rc;
+}
+
+static int
+sfc_tx_queue_stop(struct rte_eth_dev *dev, uint16_t tx_queue_id)
+{
+ struct sfc_adapter_shared *sas = sfc_adapter_shared_by_eth_dev(dev);
+ struct sfc_adapter *sa = sfc_adapter_by_eth_dev(dev);
+
+ sfc_log_init(sa, "TxQ = %u", tx_queue_id);
+
+ sfc_adapter_lock(sa);
+
+ sfc_tx_qstop(sa, tx_queue_id);
+
+ sas->txq_info[tx_queue_id].deferred_started = B_FALSE;
+
+ sfc_adapter_unlock(sa);
+ return 0;
+}
+
+static efx_tunnel_protocol_t
+sfc_tunnel_rte_type_to_efx_udp_proto(enum rte_eth_tunnel_type rte_type)
+{
+ switch (rte_type) {
+ case RTE_TUNNEL_TYPE_VXLAN:
+ return EFX_TUNNEL_PROTOCOL_VXLAN;
+ case RTE_TUNNEL_TYPE_GENEVE:
+ return EFX_TUNNEL_PROTOCOL_GENEVE;
+ default:
+ return EFX_TUNNEL_NPROTOS;
+ }
+}
+
+enum sfc_udp_tunnel_op_e {
+ SFC_UDP_TUNNEL_ADD_PORT,
+ SFC_UDP_TUNNEL_DEL_PORT,
+};
+
+static int
+sfc_dev_udp_tunnel_op(struct rte_eth_dev *dev,
+ struct rte_eth_udp_tunnel *tunnel_udp,
+ enum sfc_udp_tunnel_op_e op)
+{
+ struct sfc_adapter *sa = sfc_adapter_by_eth_dev(dev);
+ efx_tunnel_protocol_t tunnel_proto;
+ int rc;
+
+ sfc_log_init(sa, "%s udp_port=%u prot_type=%u",
+ (op == SFC_UDP_TUNNEL_ADD_PORT) ? "add" :
+ (op == SFC_UDP_TUNNEL_DEL_PORT) ? "delete" : "unknown",
+ tunnel_udp->udp_port, tunnel_udp->prot_type);
+
+ tunnel_proto =
+ sfc_tunnel_rte_type_to_efx_udp_proto(tunnel_udp->prot_type);
+ if (tunnel_proto >= EFX_TUNNEL_NPROTOS) {
+ rc = ENOTSUP;
+ goto fail_bad_proto;
+ }
+
+ sfc_adapter_lock(sa);
+
+ switch (op) {
+ case SFC_UDP_TUNNEL_ADD_PORT:
+ rc = efx_tunnel_config_udp_add(sa->nic,
+ tunnel_udp->udp_port,
+ tunnel_proto);
+ break;
+ case SFC_UDP_TUNNEL_DEL_PORT:
+ rc = efx_tunnel_config_udp_remove(sa->nic,
+ tunnel_udp->udp_port,
+ tunnel_proto);
+ break;
+ default:
+ rc = EINVAL;
+ goto fail_bad_op;
+ }
+
+ if (rc != 0)
+ goto fail_op;
+
+ if (sa->state == SFC_ADAPTER_STARTED) {
+ rc = efx_tunnel_reconfigure(sa->nic);
+ if (rc == EAGAIN) {
+ /*
+ * Configuration is accepted by FW and MC reboot
+ * is initiated to apply the changes. MC reboot
+ * will be handled in a usual way (MC reboot
+ * event on management event queue and adapter
+ * restart).
+ */
+ rc = 0;
+ } else if (rc != 0) {
+ goto fail_reconfigure;
+ }
+ }
+
+ sfc_adapter_unlock(sa);
+ return 0;
+
+fail_reconfigure:
+ /* Remove/restore entry since the change makes the trouble */
+ switch (op) {
+ case SFC_UDP_TUNNEL_ADD_PORT:
+ (void)efx_tunnel_config_udp_remove(sa->nic,
+ tunnel_udp->udp_port,
+ tunnel_proto);
+ break;
+ case SFC_UDP_TUNNEL_DEL_PORT:
+ (void)efx_tunnel_config_udp_add(sa->nic,
+ tunnel_udp->udp_port,
+ tunnel_proto);
+ break;
+ }
+
+fail_op:
+fail_bad_op:
+ sfc_adapter_unlock(sa);
+
+fail_bad_proto:
+ SFC_ASSERT(rc > 0);
+ return -rc;
+}
+
+static int
+sfc_dev_udp_tunnel_port_add(struct rte_eth_dev *dev,
+ struct rte_eth_udp_tunnel *tunnel_udp)
+{
+ return sfc_dev_udp_tunnel_op(dev, tunnel_udp, SFC_UDP_TUNNEL_ADD_PORT);
+}
+
+static int
+sfc_dev_udp_tunnel_port_del(struct rte_eth_dev *dev,
+ struct rte_eth_udp_tunnel *tunnel_udp)
+{
+ return sfc_dev_udp_tunnel_op(dev, tunnel_udp, SFC_UDP_TUNNEL_DEL_PORT);
+}
+
+/*
+ * The function is used by the secondary process as well. It must not
+ * use any process-local pointers from the adapter data.
+ */
+static int
+sfc_dev_rss_hash_conf_get(struct rte_eth_dev *dev,
+ struct rte_eth_rss_conf *rss_conf)
+{
+ struct sfc_adapter_shared *sas = sfc_adapter_shared_by_eth_dev(dev);
+ struct sfc_rss *rss = &sas->rss;
+
+ if (rss->context_type != EFX_RX_SCALE_EXCLUSIVE)
+ return -ENOTSUP;
+
+ /*
+ * Mapping of hash configuration between RTE and EFX is not one-to-one,
+ * hence, conversion is done here to derive a correct set of ETH_RSS
+ * flags which corresponds to the active EFX configuration stored
+ * locally in 'sfc_adapter' and kept up-to-date
+ */
+ rss_conf->rss_hf = sfc_rx_hf_efx_to_rte(rss, rss->hash_types);
+ rss_conf->rss_key_len = EFX_RSS_KEY_SIZE;
+ if (rss_conf->rss_key != NULL)
+ rte_memcpy(rss_conf->rss_key, rss->key, EFX_RSS_KEY_SIZE);
+
+ return 0;
+}
+
+static int
+sfc_dev_rss_hash_update(struct rte_eth_dev *dev,
+ struct rte_eth_rss_conf *rss_conf)
+{
+ struct sfc_adapter *sa = sfc_adapter_by_eth_dev(dev);
+ struct sfc_rss *rss = &sfc_sa2shared(sa)->rss;
+ unsigned int efx_hash_types;
+ uint32_t contexts[] = {EFX_RSS_CONTEXT_DEFAULT, rss->dummy_rss_context};
+ unsigned int n_contexts;
+ unsigned int mode_i = 0;
+ unsigned int key_i = 0;
+ unsigned int i = 0;
+ int rc = 0;
+
+ n_contexts = rss->dummy_rss_context == EFX_RSS_CONTEXT_DEFAULT ? 1 : 2;
+
+ if (sfc_sa2shared(sa)->isolated)
+ return -ENOTSUP;
+
+ if (rss->context_type != EFX_RX_SCALE_EXCLUSIVE) {
+ sfc_err(sa, "RSS is not available");
+ return -ENOTSUP;
+ }
+
+ if (rss->channels == 0) {
+ sfc_err(sa, "RSS is not configured");
+ return -EINVAL;
+ }
+
+ if ((rss_conf->rss_key != NULL) &&
+ (rss_conf->rss_key_len != sizeof(rss->key))) {
+ sfc_err(sa, "RSS key size is wrong (should be %zu)",
+ sizeof(rss->key));
+ return -EINVAL;
+ }
+
+ sfc_adapter_lock(sa);
+
+ rc = sfc_rx_hf_rte_to_efx(sa, rss_conf->rss_hf, &efx_hash_types);
+ if (rc != 0)
+ goto fail_rx_hf_rte_to_efx;
+
+ for (mode_i = 0; mode_i < n_contexts; mode_i++) {
+ rc = efx_rx_scale_mode_set(sa->nic, contexts[mode_i],
+ rss->hash_alg, efx_hash_types,
+ B_TRUE);
+ if (rc != 0)
+ goto fail_scale_mode_set;
+ }
+
+ if (rss_conf->rss_key != NULL) {
+ if (sa->state == SFC_ADAPTER_STARTED) {
+ for (key_i = 0; key_i < n_contexts; key_i++) {
+ rc = efx_rx_scale_key_set(sa->nic,
+ contexts[key_i],
+ rss_conf->rss_key,
+ sizeof(rss->key));
+ if (rc != 0)
+ goto fail_scale_key_set;
+ }
+ }
+
+ rte_memcpy(rss->key, rss_conf->rss_key, sizeof(rss->key));
+ }
+
+ rss->hash_types = efx_hash_types;
+
+ sfc_adapter_unlock(sa);
+
+ return 0;
+
+fail_scale_key_set:
+ for (i = 0; i < key_i; i++) {
+ if (efx_rx_scale_key_set(sa->nic, contexts[i], rss->key,
+ sizeof(rss->key)) != 0)
+ sfc_err(sa, "failed to restore RSS key");
+ }
+
+fail_scale_mode_set:
+ for (i = 0; i < mode_i; i++) {
+ if (efx_rx_scale_mode_set(sa->nic, contexts[i],
+ EFX_RX_HASHALG_TOEPLITZ,
+ rss->hash_types, B_TRUE) != 0)
+ sfc_err(sa, "failed to restore RSS mode");
+ }
+
+fail_rx_hf_rte_to_efx:
+ sfc_adapter_unlock(sa);
+ return -rc;
+}
+
+/*
+ * The function is used by the secondary process as well. It must not
+ * use any process-local pointers from the adapter data.
+ */
+static int
+sfc_dev_rss_reta_query(struct rte_eth_dev *dev,
+ struct rte_eth_rss_reta_entry64 *reta_conf,
+ uint16_t reta_size)
+{
+ struct sfc_adapter_shared *sas = sfc_adapter_shared_by_eth_dev(dev);
+ struct sfc_rss *rss = &sas->rss;
+ int entry;
+
+ if (rss->context_type != EFX_RX_SCALE_EXCLUSIVE || sas->isolated)
+ return -ENOTSUP;
+
+ if (rss->channels == 0)
+ return -EINVAL;
+
+ if (reta_size != EFX_RSS_TBL_SIZE)
+ return -EINVAL;
+
+ for (entry = 0; entry < reta_size; entry++) {
+ int grp = entry / RTE_RETA_GROUP_SIZE;
+ int grp_idx = entry % RTE_RETA_GROUP_SIZE;
+
+ if ((reta_conf[grp].mask >> grp_idx) & 1)
+ reta_conf[grp].reta[grp_idx] = rss->tbl[entry];
+ }
+
+ return 0;
+}
+
+static int
+sfc_dev_rss_reta_update(struct rte_eth_dev *dev,
+ struct rte_eth_rss_reta_entry64 *reta_conf,
+ uint16_t reta_size)
+{
+ struct sfc_adapter *sa = sfc_adapter_by_eth_dev(dev);
+ struct sfc_rss *rss = &sfc_sa2shared(sa)->rss;
+ unsigned int *rss_tbl_new;
+ uint16_t entry;
+ int rc = 0;
+
+
+ if (sfc_sa2shared(sa)->isolated)
+ return -ENOTSUP;
+
+ if (rss->context_type != EFX_RX_SCALE_EXCLUSIVE) {
+ sfc_err(sa, "RSS is not available");
+ return -ENOTSUP;
+ }
+
+ if (rss->channels == 0) {
+ sfc_err(sa, "RSS is not configured");
+ return -EINVAL;
+ }
+
+ if (reta_size != EFX_RSS_TBL_SIZE) {
+ sfc_err(sa, "RETA size is wrong (should be %u)",
+ EFX_RSS_TBL_SIZE);
+ return -EINVAL;
+ }
+
+ rss_tbl_new = rte_zmalloc("rss_tbl_new", sizeof(rss->tbl), 0);
+ if (rss_tbl_new == NULL)
+ return -ENOMEM;
+
+ sfc_adapter_lock(sa);
+
+ rte_memcpy(rss_tbl_new, rss->tbl, sizeof(rss->tbl));
+
+ for (entry = 0; entry < reta_size; entry++) {
+ int grp_idx = entry % RTE_RETA_GROUP_SIZE;
+ struct rte_eth_rss_reta_entry64 *grp;