net/sfc: set up and release Tx queues
[dpdk.git] / drivers / net / sfc / sfc_ethdev.c
index ab4bd4d..4a4c4dd 100644 (file)
 #include <rte_ethdev.h>
 #include <rte_pci.h>
 
+#include "efx.h"
+
 #include "sfc.h"
 #include "sfc_debug.h"
 #include "sfc_log.h"
 #include "sfc_kvargs.h"
+#include "sfc_ev.h"
+#include "sfc_rx.h"
+#include "sfc_tx.h"
 
 
 static void
@@ -45,10 +50,290 @@ sfc_dev_infos_get(struct rte_eth_dev *dev, struct rte_eth_dev_info *dev_info)
        sfc_log_init(sa, "entry");
 
        dev_info->pci_dev = RTE_DEV_TO_PCI(dev->device);
+       dev_info->max_rx_pktlen = EFX_MAC_PDU_MAX;
+
+       dev_info->max_rx_queues = sa->rxq_max;
+       dev_info->max_tx_queues = sa->txq_max;
+
+       /* By default packets are dropped if no descriptors are available */
+       dev_info->default_rxconf.rx_drop_en = 1;
+
+       dev_info->tx_offload_capa =
+               DEV_TX_OFFLOAD_IPV4_CKSUM |
+               DEV_TX_OFFLOAD_UDP_CKSUM |
+               DEV_TX_OFFLOAD_TCP_CKSUM;
+
+       dev_info->default_txconf.txq_flags = ETH_TXQ_FLAGS_NOVLANOFFL |
+                                            ETH_TXQ_FLAGS_NOXSUMSCTP;
+
+       dev_info->rx_desc_lim.nb_max = EFX_RXQ_MAXNDESCS;
+       dev_info->rx_desc_lim.nb_min = EFX_RXQ_MINNDESCS;
+       /* The RXQ hardware requires that the descriptor count is a power
+        * of 2, but rx_desc_lim cannot properly describe that constraint.
+        */
+       dev_info->rx_desc_lim.nb_align = EFX_RXQ_MINNDESCS;
+
+       dev_info->tx_desc_lim.nb_max = sa->txq_max_entries;
+       dev_info->tx_desc_lim.nb_min = EFX_TXQ_MINNDESCS;
+       /*
+        * The TXQ hardware requires that the descriptor count is a power
+        * of 2, but tx_desc_lim cannot properly describe that constraint
+        */
+       dev_info->tx_desc_lim.nb_align = EFX_TXQ_MINNDESCS;
+}
+
+static int
+sfc_dev_configure(struct rte_eth_dev *dev)
+{
+       struct rte_eth_dev_data *dev_data = dev->data;
+       struct sfc_adapter *sa = dev_data->dev_private;
+       int rc;
+
+       sfc_log_init(sa, "entry n_rxq=%u n_txq=%u",
+                    dev_data->nb_rx_queues, dev_data->nb_tx_queues);
+
+       sfc_adapter_lock(sa);
+       switch (sa->state) {
+       case SFC_ADAPTER_CONFIGURED:
+               sfc_close(sa);
+               SFC_ASSERT(sa->state == SFC_ADAPTER_INITIALIZED);
+               /* FALLTHROUGH */
+       case SFC_ADAPTER_INITIALIZED:
+               rc = sfc_configure(sa);
+               break;
+       default:
+               sfc_err(sa, "unexpected adapter state %u to configure",
+                       sa->state);
+               rc = EINVAL;
+               break;
+       }
+       sfc_adapter_unlock(sa);
+
+       sfc_log_init(sa, "done %d", rc);
+       SFC_ASSERT(rc >= 0);
+       return -rc;
+}
+
+static int
+sfc_dev_start(struct rte_eth_dev *dev)
+{
+       struct sfc_adapter *sa = dev->data->dev_private;
+       int rc;
+
+       sfc_log_init(sa, "entry");
+
+       sfc_adapter_lock(sa);
+       rc = sfc_start(sa);
+       sfc_adapter_unlock(sa);
+
+       sfc_log_init(sa, "done %d", rc);
+       SFC_ASSERT(rc >= 0);
+       return -rc;
+}
+
+static int
+sfc_dev_link_update(struct rte_eth_dev *dev, int wait_to_complete)
+{
+       struct sfc_adapter *sa = dev->data->dev_private;
+       struct rte_eth_link *dev_link = &dev->data->dev_link;
+       struct rte_eth_link old_link;
+       struct rte_eth_link current_link;
+
+       sfc_log_init(sa, "entry");
+
+       if (sa->state != SFC_ADAPTER_STARTED)
+               return 0;
+
+retry:
+       EFX_STATIC_ASSERT(sizeof(*dev_link) == sizeof(rte_atomic64_t));
+       *(int64_t *)&old_link = rte_atomic64_read((rte_atomic64_t *)dev_link);
+
+       if (wait_to_complete) {
+               efx_link_mode_t link_mode;
+
+               efx_port_poll(sa->nic, &link_mode);
+               sfc_port_link_mode_to_info(link_mode, &current_link);
+
+               if (!rte_atomic64_cmpset((volatile uint64_t *)dev_link,
+                                        *(uint64_t *)&old_link,
+                                        *(uint64_t *)&current_link))
+                       goto retry;
+       } else {
+               sfc_ev_mgmt_qpoll(sa);
+               *(int64_t *)&current_link =
+                       rte_atomic64_read((rte_atomic64_t *)dev_link);
+       }
+
+       if (old_link.link_status != current_link.link_status)
+               sfc_info(sa, "Link status is %s",
+                        current_link.link_status ? "UP" : "DOWN");
+
+       return old_link.link_status == current_link.link_status ? 0 : -1;
+}
+
+static void
+sfc_dev_stop(struct rte_eth_dev *dev)
+{
+       struct sfc_adapter *sa = dev->data->dev_private;
+
+       sfc_log_init(sa, "entry");
+
+       sfc_adapter_lock(sa);
+       sfc_stop(sa);
+       sfc_adapter_unlock(sa);
+
+       sfc_log_init(sa, "done");
+}
+
+static void
+sfc_dev_close(struct rte_eth_dev *dev)
+{
+       struct sfc_adapter *sa = dev->data->dev_private;
+
+       sfc_log_init(sa, "entry");
+
+       sfc_adapter_lock(sa);
+       switch (sa->state) {
+       case SFC_ADAPTER_STARTED:
+               sfc_stop(sa);
+               SFC_ASSERT(sa->state == SFC_ADAPTER_CONFIGURED);
+               /* FALLTHROUGH */
+       case SFC_ADAPTER_CONFIGURED:
+               sfc_close(sa);
+               SFC_ASSERT(sa->state == SFC_ADAPTER_INITIALIZED);
+               /* FALLTHROUGH */
+       case SFC_ADAPTER_INITIALIZED:
+               break;
+       default:
+               sfc_err(sa, "unexpected adapter state %u on close", sa->state);
+               break;
+       }
+       sfc_adapter_unlock(sa);
+
+       sfc_log_init(sa, "done");
+}
+
+static int
+sfc_rx_queue_setup(struct rte_eth_dev *dev, uint16_t rx_queue_id,
+                  uint16_t nb_rx_desc, unsigned int socket_id,
+                  const struct rte_eth_rxconf *rx_conf,
+                  struct rte_mempool *mb_pool)
+{
+       struct sfc_adapter *sa = dev->data->dev_private;
+       int rc;
+
+       sfc_log_init(sa, "RxQ=%u nb_rx_desc=%u socket_id=%u",
+                    rx_queue_id, nb_rx_desc, socket_id);
+
+       sfc_adapter_lock(sa);
+
+       rc = sfc_rx_qinit(sa, rx_queue_id, nb_rx_desc, socket_id,
+                         rx_conf, mb_pool);
+       if (rc != 0)
+               goto fail_rx_qinit;
+
+       dev->data->rx_queues[rx_queue_id] = sa->rxq_info[rx_queue_id].rxq;
+
+       sfc_adapter_unlock(sa);
+
+       return 0;
+
+fail_rx_qinit:
+       sfc_adapter_unlock(sa);
+       SFC_ASSERT(rc > 0);
+       return -rc;
+}
+
+static void
+sfc_rx_queue_release(void *queue)
+{
+       struct sfc_rxq *rxq = queue;
+       struct sfc_adapter *sa;
+       unsigned int sw_index;
+
+       if (rxq == NULL)
+               return;
+
+       sa = rxq->evq->sa;
+       sfc_adapter_lock(sa);
+
+       sw_index = sfc_rxq_sw_index(rxq);
+
+       sfc_log_init(sa, "RxQ=%u", sw_index);
+
+       sa->eth_dev->data->rx_queues[sw_index] = NULL;
+
+       sfc_rx_qfini(sa, sw_index);
+
+       sfc_adapter_unlock(sa);
+}
+
+static int
+sfc_tx_queue_setup(struct rte_eth_dev *dev, uint16_t tx_queue_id,
+                  uint16_t nb_tx_desc, unsigned int socket_id,
+                  const struct rte_eth_txconf *tx_conf)
+{
+       struct sfc_adapter *sa = dev->data->dev_private;
+       int rc;
+
+       sfc_log_init(sa, "TxQ = %u, nb_tx_desc = %u, socket_id = %u",
+                    tx_queue_id, nb_tx_desc, socket_id);
+
+       sfc_adapter_lock(sa);
+
+       rc = sfc_tx_qinit(sa, tx_queue_id, nb_tx_desc, socket_id, tx_conf);
+       if (rc != 0)
+               goto fail_tx_qinit;
+
+       dev->data->tx_queues[tx_queue_id] = sa->txq_info[tx_queue_id].txq;
+
+       sfc_adapter_unlock(sa);
+       return 0;
+
+fail_tx_qinit:
+       sfc_adapter_unlock(sa);
+       SFC_ASSERT(rc > 0);
+       return -rc;
+}
+
+static void
+sfc_tx_queue_release(void *queue)
+{
+       struct sfc_txq *txq = queue;
+       unsigned int sw_index;
+       struct sfc_adapter *sa;
+
+       if (txq == NULL)
+               return;
+
+       sw_index = sfc_txq_sw_index(txq);
+
+       SFC_ASSERT(txq->evq != NULL);
+       sa = txq->evq->sa;
+
+       sfc_log_init(sa, "TxQ = %u", sw_index);
+
+       sfc_adapter_lock(sa);
+
+       SFC_ASSERT(sw_index < sa->eth_dev->data->nb_tx_queues);
+       sa->eth_dev->data->tx_queues[sw_index] = NULL;
+
+       sfc_tx_qfini(sa, sw_index);
+
+       sfc_adapter_unlock(sa);
 }
 
 static const struct eth_dev_ops sfc_eth_dev_ops = {
+       .dev_configure                  = sfc_dev_configure,
+       .dev_start                      = sfc_dev_start,
+       .dev_stop                       = sfc_dev_stop,
+       .dev_close                      = sfc_dev_close,
+       .link_update                    = sfc_dev_link_update,
        .dev_infos_get                  = sfc_dev_infos_get,
+       .rx_queue_setup                 = sfc_rx_queue_setup,
+       .rx_queue_release               = sfc_rx_queue_release,
+       .tx_queue_setup                 = sfc_tx_queue_setup,
+       .tx_queue_release               = sfc_tx_queue_release,
 };
 
 static int
@@ -57,6 +342,8 @@ sfc_eth_dev_init(struct rte_eth_dev *dev)
        struct sfc_adapter *sa = dev->data->dev_private;
        struct rte_pci_device *pci_dev = SFC_DEV_TO_PCI(dev);
        int rc;
+       const efx_nic_cfg_t *encp;
+       const struct ether_addr *from;
 
        /* Required for logging */
        sa->eth_dev = dev;
@@ -75,11 +362,44 @@ sfc_eth_dev_init(struct rte_eth_dev *dev)
 
        sfc_log_init(sa, "entry");
 
+       dev->data->mac_addrs = rte_zmalloc("sfc", ETHER_ADDR_LEN, 0);
+       if (dev->data->mac_addrs == NULL) {
+               rc = ENOMEM;
+               goto fail_mac_addrs;
+       }
+
+       sfc_adapter_lock_init(sa);
+       sfc_adapter_lock(sa);
+
+       sfc_log_init(sa, "attaching");
+       rc = sfc_attach(sa);
+       if (rc != 0)
+               goto fail_attach;
+
+       encp = efx_nic_cfg_get(sa->nic);
+
+       /*
+        * The arguments are really reverse order in comparison to
+        * Linux kernel. Copy from NIC config to Ethernet device data.
+        */
+       from = (const struct ether_addr *)(encp->enc_mac_addr);
+       ether_addr_copy(from, &dev->data->mac_addrs[0]);
+
        dev->dev_ops = &sfc_eth_dev_ops;
+       dev->rx_pkt_burst = &sfc_recv_pkts;
+
+       sfc_adapter_unlock(sa);
 
        sfc_log_init(sa, "done");
        return 0;
 
+fail_attach:
+       sfc_adapter_unlock(sa);
+       sfc_adapter_lock_fini(sa);
+       rte_free(dev->data->mac_addrs);
+       dev->data->mac_addrs = NULL;
+
+fail_mac_addrs:
 fail_kvarg_debug_init:
        sfc_kvargs_cleanup(sa);
 
@@ -96,10 +416,21 @@ sfc_eth_dev_uninit(struct rte_eth_dev *dev)
 
        sfc_log_init(sa, "entry");
 
+       sfc_adapter_lock(sa);
+
+       sfc_detach(sa);
+
+       rte_free(dev->data->mac_addrs);
+       dev->data->mac_addrs = NULL;
+
        dev->dev_ops = NULL;
+       dev->rx_pkt_burst = NULL;
 
        sfc_kvargs_cleanup(sa);
 
+       sfc_adapter_unlock(sa);
+       sfc_adapter_lock_fini(sa);
+
        sfc_log_init(sa, "done");
 
        /* Required for logging, so cleanup last */
@@ -108,13 +439,17 @@ sfc_eth_dev_uninit(struct rte_eth_dev *dev)
 }
 
 static const struct rte_pci_id pci_id_sfc_efx_map[] = {
+       { RTE_PCI_DEVICE(EFX_PCI_VENID_SFC, EFX_PCI_DEVID_FARMINGDALE) },
+       { RTE_PCI_DEVICE(EFX_PCI_VENID_SFC, EFX_PCI_DEVID_GREENPORT) },
+       { RTE_PCI_DEVICE(EFX_PCI_VENID_SFC, EFX_PCI_DEVID_MEDFORD) },
        { .vendor_id = 0 /* sentinel */ }
 };
 
 static struct eth_driver sfc_efx_pmd = {
        .pci_drv = {
                .id_table = pci_id_sfc_efx_map,
-               .drv_flags = 0,
+               .drv_flags =
+                       RTE_PCI_DRV_NEED_MAPPING,
                .probe = rte_eth_dev_pci_probe,
                .remove = rte_eth_dev_pci_remove,
        },