+/**
+ * DPDK callback to retrieve names of extended device statistics
+ *
+ * @param dev
+ * Pointer to Ethernet device structure.
+ * @param[out] xstats_names
+ * Buffer to insert names into.
+ * @param n
+ * Number of names.
+ *
+ * @return
+ * Number of xstats names.
+ */
+static int ena_xstats_get_names(struct rte_eth_dev *dev,
+ struct rte_eth_xstat_name *xstats_names,
+ unsigned int n)
+{
+ unsigned int xstats_count = ena_xstats_calc_num(dev);
+ unsigned int stat, i, count = 0;
+
+ if (n < xstats_count || !xstats_names)
+ return xstats_count;
+
+ for (stat = 0; stat < ENA_STATS_ARRAY_GLOBAL; stat++, count++)
+ strcpy(xstats_names[count].name,
+ ena_stats_global_strings[stat].name);
+
+ for (stat = 0; stat < ENA_STATS_ARRAY_RX; stat++)
+ for (i = 0; i < dev->data->nb_rx_queues; i++, count++)
+ snprintf(xstats_names[count].name,
+ sizeof(xstats_names[count].name),
+ "rx_q%d_%s", i,
+ ena_stats_rx_strings[stat].name);
+
+ for (stat = 0; stat < ENA_STATS_ARRAY_TX; stat++)
+ for (i = 0; i < dev->data->nb_tx_queues; i++, count++)
+ snprintf(xstats_names[count].name,
+ sizeof(xstats_names[count].name),
+ "tx_q%d_%s", i,
+ ena_stats_tx_strings[stat].name);
+
+ return xstats_count;
+}
+
+/**
+ * DPDK callback to get extended device statistics.
+ *
+ * @param dev
+ * Pointer to Ethernet device structure.
+ * @param[out] stats
+ * Stats table output buffer.
+ * @param n
+ * The size of the stats table.
+ *
+ * @return
+ * Number of xstats on success, negative on failure.
+ */
+static int ena_xstats_get(struct rte_eth_dev *dev,
+ struct rte_eth_xstat *xstats,
+ unsigned int n)
+{
+ struct ena_adapter *adapter = dev->data->dev_private;
+ unsigned int xstats_count = ena_xstats_calc_num(dev);
+ unsigned int stat, i, count = 0;
+ int stat_offset;
+ void *stats_begin;
+
+ if (n < xstats_count)
+ return xstats_count;
+
+ if (!xstats)
+ return 0;
+
+ for (stat = 0; stat < ENA_STATS_ARRAY_GLOBAL; stat++, count++) {
+ stat_offset = ena_stats_rx_strings[stat].stat_offset;
+ stats_begin = &adapter->dev_stats;
+
+ xstats[count].id = count;
+ xstats[count].value = *((uint64_t *)
+ ((char *)stats_begin + stat_offset));
+ }
+
+ for (stat = 0; stat < ENA_STATS_ARRAY_RX; stat++) {
+ for (i = 0; i < dev->data->nb_rx_queues; i++, count++) {
+ stat_offset = ena_stats_rx_strings[stat].stat_offset;
+ stats_begin = &adapter->rx_ring[i].rx_stats;
+
+ xstats[count].id = count;
+ xstats[count].value = *((uint64_t *)
+ ((char *)stats_begin + stat_offset));
+ }
+ }
+
+ for (stat = 0; stat < ENA_STATS_ARRAY_TX; stat++) {
+ for (i = 0; i < dev->data->nb_tx_queues; i++, count++) {
+ stat_offset = ena_stats_tx_strings[stat].stat_offset;
+ stats_begin = &adapter->tx_ring[i].rx_stats;
+
+ xstats[count].id = count;
+ xstats[count].value = *((uint64_t *)
+ ((char *)stats_begin + stat_offset));
+ }
+ }
+
+ return count;
+}
+
+static int ena_xstats_get_by_id(struct rte_eth_dev *dev,
+ const uint64_t *ids,
+ uint64_t *values,
+ unsigned int n)
+{
+ struct ena_adapter *adapter = dev->data->dev_private;
+ uint64_t id;
+ uint64_t rx_entries, tx_entries;
+ unsigned int i;
+ int qid;
+ int valid = 0;
+ for (i = 0; i < n; ++i) {
+ id = ids[i];
+ /* Check if id belongs to global statistics */
+ if (id < ENA_STATS_ARRAY_GLOBAL) {
+ values[i] = *((uint64_t *)&adapter->dev_stats + id);
+ ++valid;
+ continue;
+ }
+
+ /* Check if id belongs to rx queue statistics */
+ id -= ENA_STATS_ARRAY_GLOBAL;
+ rx_entries = ENA_STATS_ARRAY_RX * dev->data->nb_rx_queues;
+ if (id < rx_entries) {
+ qid = id % dev->data->nb_rx_queues;
+ id /= dev->data->nb_rx_queues;
+ values[i] = *((uint64_t *)
+ &adapter->rx_ring[qid].rx_stats + id);
+ ++valid;
+ continue;
+ }
+ /* Check if id belongs to rx queue statistics */
+ id -= rx_entries;
+ tx_entries = ENA_STATS_ARRAY_TX * dev->data->nb_tx_queues;
+ if (id < tx_entries) {
+ qid = id % dev->data->nb_tx_queues;
+ id /= dev->data->nb_tx_queues;
+ values[i] = *((uint64_t *)
+ &adapter->tx_ring[qid].tx_stats + id);
+ ++valid;
+ continue;
+ }
+ }
+
+ return valid;
+}
+
+static int ena_process_bool_devarg(const char *key,
+ const char *value,
+ void *opaque)
+{
+ struct ena_adapter *adapter = opaque;
+ bool bool_value;
+
+ /* Parse the value. */
+ if (strcmp(value, "1") == 0) {
+ bool_value = true;
+ } else if (strcmp(value, "0") == 0) {
+ bool_value = false;
+ } else {
+ PMD_INIT_LOG(ERR,
+ "Invalid value: '%s' for key '%s'. Accepted: '0' or '1'\n",
+ value, key);
+ return -EINVAL;
+ }
+
+ /* Now, assign it to the proper adapter field. */
+ if (strcmp(key, ENA_DEVARG_LARGE_LLQ_HDR))
+ adapter->use_large_llq_hdr = bool_value;
+
+ return 0;
+}
+
+static int ena_parse_devargs(struct ena_adapter *adapter,
+ struct rte_devargs *devargs)
+{
+ static const char * const allowed_args[] = {
+ ENA_DEVARG_LARGE_LLQ_HDR,
+ };
+ struct rte_kvargs *kvlist;
+ int rc;
+
+ if (devargs == NULL)
+ return 0;
+
+ kvlist = rte_kvargs_parse(devargs->args, allowed_args);
+ if (kvlist == NULL) {
+ PMD_INIT_LOG(ERR, "Invalid device arguments: %s\n",
+ devargs->args);
+ return -EINVAL;
+ }
+
+ rc = rte_kvargs_process(kvlist, ENA_DEVARG_LARGE_LLQ_HDR,
+ ena_process_bool_devarg, adapter);
+
+ rte_kvargs_free(kvlist);
+
+ return rc;
+}
+
+/*********************************************************************
+ * PMD configuration
+ *********************************************************************/
+static int eth_ena_pci_probe(struct rte_pci_driver *pci_drv __rte_unused,
+ struct rte_pci_device *pci_dev)
+{
+ return rte_eth_dev_pci_generic_probe(pci_dev,
+ sizeof(struct ena_adapter), eth_ena_dev_init);
+}
+
+static int eth_ena_pci_remove(struct rte_pci_device *pci_dev)
+{
+ return rte_eth_dev_pci_generic_remove(pci_dev, eth_ena_dev_uninit);
+}
+
+static struct rte_pci_driver rte_ena_pmd = {
+ .id_table = pci_id_ena_map,
+ .drv_flags = RTE_PCI_DRV_NEED_MAPPING | RTE_PCI_DRV_INTR_LSC |
+ RTE_PCI_DRV_WC_ACTIVATE,
+ .probe = eth_ena_pci_probe,
+ .remove = eth_ena_pci_remove,