+ .fw_version_get = sfc_fw_version_get,
+ .xstats_get_by_id = sfc_xstats_get_by_id,
+ .xstats_get_names_by_id = sfc_xstats_get_names_by_id,
+ .pool_ops_supported = sfc_pool_ops_supported,
+};
+
+/**
+ * Duplicate a string in potentially shared memory required for
+ * multi-process support.
+ *
+ * strdup() allocates from process-local heap/memory.
+ */
+static char *
+sfc_strdup(const char *str)
+{
+ size_t size;
+ char *copy;
+
+ if (str == NULL)
+ return NULL;
+
+ size = strlen(str) + 1;
+ copy = rte_malloc(__func__, size, 0);
+ if (copy != NULL)
+ rte_memcpy(copy, str, size);
+
+ return copy;
+}
+
+static int
+sfc_eth_dev_set_ops(struct rte_eth_dev *dev)
+{
+ struct sfc_adapter *sa = sfc_adapter_by_eth_dev(dev);
+ struct sfc_adapter_shared *sas = sfc_adapter_shared_by_eth_dev(dev);
+ const struct sfc_dp_rx *dp_rx;
+ const struct sfc_dp_tx *dp_tx;
+ const efx_nic_cfg_t *encp;
+ unsigned int avail_caps = 0;
+ const char *rx_name = NULL;
+ const char *tx_name = NULL;
+ int rc;
+
+ switch (sa->family) {
+ case EFX_FAMILY_HUNTINGTON:
+ case EFX_FAMILY_MEDFORD:
+ case EFX_FAMILY_MEDFORD2:
+ avail_caps |= SFC_DP_HW_FW_CAP_EF10;
+ avail_caps |= SFC_DP_HW_FW_CAP_RX_EFX;
+ avail_caps |= SFC_DP_HW_FW_CAP_TX_EFX;
+ break;
+ case EFX_FAMILY_RIVERHEAD:
+ avail_caps |= SFC_DP_HW_FW_CAP_EF100;
+ break;
+ default:
+ break;
+ }
+
+ encp = efx_nic_cfg_get(sa->nic);
+ if (encp->enc_rx_es_super_buffer_supported)
+ avail_caps |= SFC_DP_HW_FW_CAP_RX_ES_SUPER_BUFFER;
+
+ rc = sfc_kvargs_process(sa, SFC_KVARG_RX_DATAPATH,
+ sfc_kvarg_string_handler, &rx_name);
+ if (rc != 0)
+ goto fail_kvarg_rx_datapath;
+
+ if (rx_name != NULL) {
+ dp_rx = sfc_dp_find_rx_by_name(&sfc_dp_head, rx_name);
+ if (dp_rx == NULL) {
+ sfc_err(sa, "Rx datapath %s not found", rx_name);
+ rc = ENOENT;
+ goto fail_dp_rx;
+ }
+ if (!sfc_dp_match_hw_fw_caps(&dp_rx->dp, avail_caps)) {
+ sfc_err(sa,
+ "Insufficient Hw/FW capabilities to use Rx datapath %s",
+ rx_name);
+ rc = EINVAL;
+ goto fail_dp_rx_caps;
+ }
+ } else {
+ dp_rx = sfc_dp_find_rx_by_caps(&sfc_dp_head, avail_caps);
+ if (dp_rx == NULL) {
+ sfc_err(sa, "Rx datapath by caps %#x not found",
+ avail_caps);
+ rc = ENOENT;
+ goto fail_dp_rx;
+ }
+ }
+
+ sas->dp_rx_name = sfc_strdup(dp_rx->dp.name);
+ if (sas->dp_rx_name == NULL) {
+ rc = ENOMEM;
+ goto fail_dp_rx_name;
+ }
+
+ sfc_notice(sa, "use %s Rx datapath", sas->dp_rx_name);
+
+ rc = sfc_kvargs_process(sa, SFC_KVARG_TX_DATAPATH,
+ sfc_kvarg_string_handler, &tx_name);
+ if (rc != 0)
+ goto fail_kvarg_tx_datapath;
+
+ if (tx_name != NULL) {
+ dp_tx = sfc_dp_find_tx_by_name(&sfc_dp_head, tx_name);
+ if (dp_tx == NULL) {
+ sfc_err(sa, "Tx datapath %s not found", tx_name);
+ rc = ENOENT;
+ goto fail_dp_tx;
+ }
+ if (!sfc_dp_match_hw_fw_caps(&dp_tx->dp, avail_caps)) {
+ sfc_err(sa,
+ "Insufficient Hw/FW capabilities to use Tx datapath %s",
+ tx_name);
+ rc = EINVAL;
+ goto fail_dp_tx_caps;
+ }
+ } else {
+ dp_tx = sfc_dp_find_tx_by_caps(&sfc_dp_head, avail_caps);
+ if (dp_tx == NULL) {
+ sfc_err(sa, "Tx datapath by caps %#x not found",
+ avail_caps);
+ rc = ENOENT;
+ goto fail_dp_tx;
+ }
+ }
+
+ sas->dp_tx_name = sfc_strdup(dp_tx->dp.name);
+ if (sas->dp_tx_name == NULL) {
+ rc = ENOMEM;
+ goto fail_dp_tx_name;
+ }
+
+ sfc_notice(sa, "use %s Tx datapath", sas->dp_tx_name);
+
+ sa->priv.dp_rx = dp_rx;
+ sa->priv.dp_tx = dp_tx;
+
+ dev->rx_pkt_burst = dp_rx->pkt_burst;
+ dev->tx_pkt_prepare = dp_tx->pkt_prepare;
+ dev->tx_pkt_burst = dp_tx->pkt_burst;
+
+ dev->rx_queue_count = sfc_rx_queue_count;
+ dev->rx_descriptor_done = sfc_rx_descriptor_done;
+ dev->rx_descriptor_status = sfc_rx_descriptor_status;
+ dev->tx_descriptor_status = sfc_tx_descriptor_status;
+ dev->dev_ops = &sfc_eth_dev_ops;
+
+ return 0;
+
+fail_dp_tx_name:
+fail_dp_tx_caps:
+fail_dp_tx:
+fail_kvarg_tx_datapath:
+ rte_free(sas->dp_rx_name);
+ sas->dp_rx_name = NULL;
+
+fail_dp_rx_name:
+fail_dp_rx_caps:
+fail_dp_rx:
+fail_kvarg_rx_datapath:
+ return rc;
+}
+
+static void
+sfc_eth_dev_clear_ops(struct rte_eth_dev *dev)
+{
+ struct sfc_adapter *sa = sfc_adapter_by_eth_dev(dev);
+ struct sfc_adapter_shared *sas = sfc_adapter_shared_by_eth_dev(dev);
+
+ dev->dev_ops = NULL;
+ dev->tx_pkt_prepare = NULL;
+ dev->rx_pkt_burst = NULL;
+ dev->tx_pkt_burst = NULL;
+
+ rte_free(sas->dp_tx_name);
+ sas->dp_tx_name = NULL;
+ sa->priv.dp_tx = NULL;
+
+ rte_free(sas->dp_rx_name);
+ sas->dp_rx_name = NULL;
+ sa->priv.dp_rx = NULL;
+}
+
+static const struct eth_dev_ops sfc_eth_dev_secondary_ops = {
+ .dev_supported_ptypes_get = sfc_dev_supported_ptypes_get,
+ .reta_query = sfc_dev_rss_reta_query,
+ .rss_hash_conf_get = sfc_dev_rss_hash_conf_get,
+ .rxq_info_get = sfc_rx_queue_info_get,
+ .txq_info_get = sfc_tx_queue_info_get,