+/**
+ * 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,
+};
+
+static int
+sfc_eth_dev_secondary_init(struct rte_eth_dev *dev, uint32_t logtype_main)
+{
+ struct sfc_adapter_shared *sas = sfc_adapter_shared_by_eth_dev(dev);
+ struct sfc_adapter_priv *sap;
+ const struct sfc_dp_rx *dp_rx;
+ const struct sfc_dp_tx *dp_tx;
+ int rc;
+
+ /*
+ * Allocate process private data from heap, since it should not
+ * be located in shared memory allocated using rte_malloc() API.
+ */
+ sap = calloc(1, sizeof(*sap));
+ if (sap == NULL) {
+ rc = ENOMEM;
+ goto fail_alloc_priv;
+ }
+
+ sap->logtype_main = logtype_main;
+
+ dp_rx = sfc_dp_find_rx_by_name(&sfc_dp_head, sas->dp_rx_name);
+ if (dp_rx == NULL) {
+ SFC_LOG(sas, RTE_LOG_ERR, logtype_main,
+ "cannot find %s Rx datapath", sas->dp_rx_name);
+ rc = ENOENT;
+ goto fail_dp_rx;
+ }
+ if (~dp_rx->features & SFC_DP_RX_FEAT_MULTI_PROCESS) {
+ SFC_LOG(sas, RTE_LOG_ERR, logtype_main,
+ "%s Rx datapath does not support multi-process",
+ sas->dp_rx_name);
+ rc = EINVAL;
+ goto fail_dp_rx_multi_process;
+ }
+
+ dp_tx = sfc_dp_find_tx_by_name(&sfc_dp_head, sas->dp_tx_name);
+ if (dp_tx == NULL) {
+ SFC_LOG(sas, RTE_LOG_ERR, logtype_main,
+ "cannot find %s Tx datapath", sas->dp_tx_name);
+ rc = ENOENT;
+ goto fail_dp_tx;
+ }
+ if (~dp_tx->features & SFC_DP_TX_FEAT_MULTI_PROCESS) {
+ SFC_LOG(sas, RTE_LOG_ERR, logtype_main,
+ "%s Tx datapath does not support multi-process",
+ sas->dp_tx_name);
+ rc = EINVAL;
+ goto fail_dp_tx_multi_process;
+ }
+
+ sap->dp_rx = dp_rx;
+ sap->dp_tx = dp_tx;
+
+ dev->process_private = sap;
+ 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_secondary_ops;
+
+ return 0;
+
+fail_dp_tx_multi_process:
+fail_dp_tx:
+fail_dp_rx_multi_process:
+fail_dp_rx:
+ free(sap);
+
+fail_alloc_priv:
+ return rc;
+}
+
+static void
+sfc_register_dp(void)
+{
+ /* Register once */
+ if (TAILQ_EMPTY(&sfc_dp_head)) {
+ /* Prefer EF10 datapath */
+ sfc_dp_register(&sfc_dp_head, &sfc_ef100_rx.dp);
+ sfc_dp_register(&sfc_dp_head, &sfc_ef10_essb_rx.dp);
+ sfc_dp_register(&sfc_dp_head, &sfc_ef10_rx.dp);
+ sfc_dp_register(&sfc_dp_head, &sfc_efx_rx.dp);
+
+ sfc_dp_register(&sfc_dp_head, &sfc_ef100_tx.dp);
+ sfc_dp_register(&sfc_dp_head, &sfc_ef10_tx.dp);
+ sfc_dp_register(&sfc_dp_head, &sfc_efx_tx.dp);
+ sfc_dp_register(&sfc_dp_head, &sfc_ef10_simple_tx.dp);
+ }
+}
+