+int
+rte_eth_stats_get(uint16_t port_id, struct rte_eth_stats *stats)
+{
+ struct rte_eth_dev *dev;
+
+ RTE_ETH_VALID_PORTID_OR_ERR_RET(port_id, -EINVAL);
+
+ dev = &rte_eth_devices[port_id];
+ memset(stats, 0, sizeof(*stats));
+
+ RTE_FUNC_PTR_OR_ERR_RET(*dev->dev_ops->stats_get, -ENOTSUP);
+ stats->rx_nombuf = dev->data->rx_mbuf_alloc_failed;
+ return eth_err(port_id, (*dev->dev_ops->stats_get)(dev, stats));
+}
+
+int
+rte_eth_stats_reset(uint16_t port_id)
+{
+ struct rte_eth_dev *dev;
+
+ RTE_ETH_VALID_PORTID_OR_ERR_RET(port_id, -ENODEV);
+ dev = &rte_eth_devices[port_id];
+
+ RTE_FUNC_PTR_OR_ERR_RET(*dev->dev_ops->stats_reset, -ENOTSUP);
+ (*dev->dev_ops->stats_reset)(dev);
+ dev->data->rx_mbuf_alloc_failed = 0;
+
+ return 0;
+}
+
+static inline int
+get_xstats_basic_count(struct rte_eth_dev *dev)
+{
+ uint16_t nb_rxqs, nb_txqs;
+ int count;
+
+ nb_rxqs = RTE_MIN(dev->data->nb_rx_queues, RTE_ETHDEV_QUEUE_STAT_CNTRS);
+ nb_txqs = RTE_MIN(dev->data->nb_tx_queues, RTE_ETHDEV_QUEUE_STAT_CNTRS);
+
+ count = RTE_NB_STATS;
+ count += nb_rxqs * RTE_NB_RXQ_STATS;
+ count += nb_txqs * RTE_NB_TXQ_STATS;
+
+ return count;
+}
+
+static int
+get_xstats_count(uint16_t port_id)
+{
+ struct rte_eth_dev *dev;
+ int count;
+
+ RTE_ETH_VALID_PORTID_OR_ERR_RET(port_id, -EINVAL);
+ dev = &rte_eth_devices[port_id];
+ if (dev->dev_ops->xstats_get_names_by_id != NULL) {
+ count = (*dev->dev_ops->xstats_get_names_by_id)(dev, NULL,
+ NULL, 0);
+ if (count < 0)
+ return eth_err(port_id, count);
+ }
+ if (dev->dev_ops->xstats_get_names != NULL) {
+ count = (*dev->dev_ops->xstats_get_names)(dev, NULL, 0);
+ if (count < 0)
+ return eth_err(port_id, count);
+ } else
+ count = 0;
+
+
+ count += get_xstats_basic_count(dev);
+
+ return count;
+}
+
+int
+rte_eth_xstats_get_id_by_name(uint16_t port_id, const char *xstat_name,
+ uint64_t *id)
+{
+ int cnt_xstats, idx_xstat;
+
+ RTE_ETH_VALID_PORTID_OR_ERR_RET(port_id, -ENODEV);
+
+ if (!id) {
+ RTE_PMD_DEBUG_TRACE("Error: id pointer is NULL\n");
+ return -ENOMEM;
+ }
+
+ if (!xstat_name) {
+ RTE_PMD_DEBUG_TRACE("Error: xstat_name pointer is NULL\n");
+ return -ENOMEM;
+ }
+
+ /* Get count */
+ cnt_xstats = rte_eth_xstats_get_names_by_id(port_id, NULL, 0, NULL);
+ if (cnt_xstats < 0) {
+ RTE_PMD_DEBUG_TRACE("Error: Cannot get count of xstats\n");
+ return -ENODEV;
+ }
+
+ /* Get id-name lookup table */
+ struct rte_eth_xstat_name xstats_names[cnt_xstats];
+
+ if (cnt_xstats != rte_eth_xstats_get_names_by_id(
+ port_id, xstats_names, cnt_xstats, NULL)) {
+ RTE_PMD_DEBUG_TRACE("Error: Cannot get xstats lookup\n");
+ return -1;
+ }
+
+ for (idx_xstat = 0; idx_xstat < cnt_xstats; idx_xstat++) {
+ if (!strcmp(xstats_names[idx_xstat].name, xstat_name)) {
+ *id = idx_xstat;
+ return 0;
+ };
+ }
+
+ return -EINVAL;
+}
+
+/* retrieve basic stats names */
+static int
+rte_eth_basic_stats_get_names(struct rte_eth_dev *dev,
+ struct rte_eth_xstat_name *xstats_names)
+{
+ int cnt_used_entries = 0;
+ uint32_t idx, id_queue;
+ uint16_t num_q;
+
+ for (idx = 0; idx < RTE_NB_STATS; idx++) {
+ snprintf(xstats_names[cnt_used_entries].name,
+ sizeof(xstats_names[0].name),
+ "%s", rte_stats_strings[idx].name);
+ cnt_used_entries++;
+ }
+ num_q = RTE_MIN(dev->data->nb_rx_queues, RTE_ETHDEV_QUEUE_STAT_CNTRS);
+ for (id_queue = 0; id_queue < num_q; id_queue++) {
+ for (idx = 0; idx < RTE_NB_RXQ_STATS; idx++) {
+ snprintf(xstats_names[cnt_used_entries].name,
+ sizeof(xstats_names[0].name),
+ "rx_q%u%s",
+ id_queue, rte_rxq_stats_strings[idx].name);
+ cnt_used_entries++;
+ }
+
+ }
+ num_q = RTE_MIN(dev->data->nb_tx_queues, RTE_ETHDEV_QUEUE_STAT_CNTRS);
+ for (id_queue = 0; id_queue < num_q; id_queue++) {
+ for (idx = 0; idx < RTE_NB_TXQ_STATS; idx++) {
+ snprintf(xstats_names[cnt_used_entries].name,
+ sizeof(xstats_names[0].name),
+ "tx_q%u%s",
+ id_queue, rte_txq_stats_strings[idx].name);
+ cnt_used_entries++;
+ }
+ }
+ return cnt_used_entries;
+}
+
+/* retrieve ethdev extended statistics names */
+int
+rte_eth_xstats_get_names_by_id(uint16_t port_id,
+ struct rte_eth_xstat_name *xstats_names, unsigned int size,
+ uint64_t *ids)
+{
+ struct rte_eth_xstat_name *xstats_names_copy;
+ unsigned int no_basic_stat_requested = 1;
+ unsigned int no_ext_stat_requested = 1;
+ unsigned int expected_entries;
+ unsigned int basic_count;
+ struct rte_eth_dev *dev;
+ unsigned int i;
+ int ret;
+
+ RTE_ETH_VALID_PORTID_OR_ERR_RET(port_id, -ENODEV);
+ dev = &rte_eth_devices[port_id];
+
+ basic_count = get_xstats_basic_count(dev);
+ ret = get_xstats_count(port_id);
+ if (ret < 0)
+ return ret;
+ expected_entries = (unsigned int)ret;
+
+ /* Return max number of stats if no ids given */
+ if (!ids) {
+ if (!xstats_names)
+ return expected_entries;
+ else if (xstats_names && size < expected_entries)
+ return expected_entries;
+ }
+
+ if (ids && !xstats_names)
+ return -EINVAL;
+
+ if (ids && dev->dev_ops->xstats_get_names_by_id != NULL && size > 0) {
+ uint64_t ids_copy[size];
+
+ for (i = 0; i < size; i++) {
+ if (ids[i] < basic_count) {
+ no_basic_stat_requested = 0;
+ break;
+ }
+
+ /*
+ * Convert ids to xstats ids that PMD knows.
+ * ids known by user are basic + extended stats.
+ */
+ ids_copy[i] = ids[i] - basic_count;
+ }
+
+ if (no_basic_stat_requested)
+ return (*dev->dev_ops->xstats_get_names_by_id)(dev,
+ xstats_names, ids_copy, size);
+ }
+
+ /* Retrieve all stats */
+ if (!ids) {
+ int num_stats = rte_eth_xstats_get_names(port_id, xstats_names,
+ expected_entries);
+ if (num_stats < 0 || num_stats > (int)expected_entries)
+ return num_stats;
+ else
+ return expected_entries;
+ }
+
+ xstats_names_copy = calloc(expected_entries,
+ sizeof(struct rte_eth_xstat_name));
+
+ if (!xstats_names_copy) {
+ RTE_PMD_DEBUG_TRACE("ERROR: can't allocate memory");
+ return -ENOMEM;
+ }
+
+ if (ids) {
+ for (i = 0; i < size; i++) {
+ if (ids[i] >= basic_count) {
+ no_ext_stat_requested = 0;
+ break;
+ }
+ }
+ }
+
+ /* Fill xstats_names_copy structure */
+ if (ids && no_ext_stat_requested) {
+ rte_eth_basic_stats_get_names(dev, xstats_names_copy);
+ } else {
+ ret = rte_eth_xstats_get_names(port_id, xstats_names_copy,
+ expected_entries);
+ if (ret < 0) {
+ free(xstats_names_copy);
+ return ret;
+ }
+ }
+
+ /* Filter stats */
+ for (i = 0; i < size; i++) {
+ if (ids[i] >= expected_entries) {
+ RTE_PMD_DEBUG_TRACE("ERROR: id value isn't valid\n");
+ free(xstats_names_copy);
+ return -1;
+ }
+ xstats_names[i] = xstats_names_copy[ids[i]];
+ }
+
+ free(xstats_names_copy);
+ return size;
+}
+
+int
+rte_eth_xstats_get_names(uint16_t port_id,
+ struct rte_eth_xstat_name *xstats_names,
+ unsigned int size)
+{
+ struct rte_eth_dev *dev;
+ int cnt_used_entries;
+ int cnt_expected_entries;
+ int cnt_driver_entries;
+
+ cnt_expected_entries = get_xstats_count(port_id);
+ if (xstats_names == NULL || cnt_expected_entries < 0 ||
+ (int)size < cnt_expected_entries)
+ return cnt_expected_entries;
+
+ /* port_id checked in get_xstats_count() */
+ dev = &rte_eth_devices[port_id];
+
+ cnt_used_entries = rte_eth_basic_stats_get_names(
+ dev, xstats_names);
+
+ if (dev->dev_ops->xstats_get_names != NULL) {
+ /* If there are any driver-specific xstats, append them
+ * to end of list.
+ */
+ cnt_driver_entries = (*dev->dev_ops->xstats_get_names)(
+ dev,
+ xstats_names + cnt_used_entries,
+ size - cnt_used_entries);
+ if (cnt_driver_entries < 0)
+ return eth_err(port_id, cnt_driver_entries);
+ cnt_used_entries += cnt_driver_entries;
+ }
+
+ return cnt_used_entries;
+}
+
+
+static int
+rte_eth_basic_stats_get(uint16_t port_id, struct rte_eth_xstat *xstats)
+{
+ struct rte_eth_dev *dev;
+ struct rte_eth_stats eth_stats;
+ unsigned int count = 0, i, q;
+ uint64_t val, *stats_ptr;
+ uint16_t nb_rxqs, nb_txqs;
+ int ret;
+
+ ret = rte_eth_stats_get(port_id, ð_stats);
+ if (ret < 0)
+ return ret;
+
+ dev = &rte_eth_devices[port_id];
+
+ nb_rxqs = RTE_MIN(dev->data->nb_rx_queues, RTE_ETHDEV_QUEUE_STAT_CNTRS);
+ nb_txqs = RTE_MIN(dev->data->nb_tx_queues, RTE_ETHDEV_QUEUE_STAT_CNTRS);
+
+ /* global stats */
+ for (i = 0; i < RTE_NB_STATS; i++) {
+ stats_ptr = RTE_PTR_ADD(ð_stats,
+ rte_stats_strings[i].offset);
+ val = *stats_ptr;
+ xstats[count++].value = val;
+ }
+
+ /* per-rxq stats */
+ for (q = 0; q < nb_rxqs; q++) {
+ for (i = 0; i < RTE_NB_RXQ_STATS; i++) {
+ stats_ptr = RTE_PTR_ADD(ð_stats,
+ rte_rxq_stats_strings[i].offset +
+ q * sizeof(uint64_t));
+ val = *stats_ptr;
+ xstats[count++].value = val;
+ }
+ }
+
+ /* per-txq stats */
+ for (q = 0; q < nb_txqs; q++) {
+ for (i = 0; i < RTE_NB_TXQ_STATS; i++) {
+ stats_ptr = RTE_PTR_ADD(ð_stats,
+ rte_txq_stats_strings[i].offset +
+ q * sizeof(uint64_t));
+ val = *stats_ptr;
+ xstats[count++].value = val;
+ }
+ }
+ return count;
+}
+
+/* retrieve ethdev extended statistics */
+int
+rte_eth_xstats_get_by_id(uint16_t port_id, const uint64_t *ids,
+ uint64_t *values, unsigned int size)