+
+ return 0;
+}
+
+static int
+__fs_xstats_count(struct rte_eth_dev *dev)
+{
+ struct sub_device *sdev;
+ int count = 0;
+ uint8_t i;
+ int ret;
+
+ FOREACH_SUBDEV_STATE(sdev, i, dev, DEV_ACTIVE) {
+ ret = rte_eth_xstats_get_names(PORT_ID(sdev), NULL, 0);
+ if (ret < 0)
+ return ret;
+ count += ret;
+ }
+
+ return count;
+}
+
+static int
+__fs_xstats_get_names(struct rte_eth_dev *dev,
+ struct rte_eth_xstat_name *xstats_names,
+ unsigned int limit)
+{
+ struct sub_device *sdev;
+ unsigned int count = 0;
+ uint8_t i;
+
+ /* Caller only cares about count */
+ if (!xstats_names)
+ return __fs_xstats_count(dev);
+
+ FOREACH_SUBDEV_STATE(sdev, i, dev, DEV_ACTIVE) {
+ struct rte_eth_xstat_name *sub_names = xstats_names + count;
+ int j, r;
+
+ if (count >= limit)
+ break;
+
+ r = rte_eth_xstats_get_names(PORT_ID(sdev),
+ sub_names, limit - count);
+ if (r < 0)
+ return r;
+
+ /* add subN_ prefix to names */
+ for (j = 0; j < r; j++) {
+ char *xname = sub_names[j].name;
+ char tmp[RTE_ETH_XSTATS_NAME_SIZE];
+
+ if ((xname[0] == 't' || xname[0] == 'r') &&
+ xname[1] == 'x' && xname[2] == '_')
+ snprintf(tmp, sizeof(tmp), "%.3ssub%u_%s",
+ xname, i, xname + 3);
+ else
+ snprintf(tmp, sizeof(tmp), "sub%u_%s",
+ i, xname);
+
+ strlcpy(xname, tmp, RTE_ETH_XSTATS_NAME_SIZE);
+ }
+ count += r;
+ }
+ return count;
+}
+
+static int
+fs_xstats_get_names(struct rte_eth_dev *dev,
+ struct rte_eth_xstat_name *xstats_names,
+ unsigned int limit)
+{
+ int ret;
+
+ fs_lock(dev, 0);
+ ret = __fs_xstats_get_names(dev, xstats_names, limit);
+ fs_unlock(dev, 0);
+ return ret;
+}
+
+static int
+__fs_xstats_get(struct rte_eth_dev *dev,
+ struct rte_eth_xstat *xstats,
+ unsigned int n)
+{
+ unsigned int count = 0;
+ struct sub_device *sdev;
+ uint8_t i;
+ int j, ret;
+
+ ret = __fs_xstats_count(dev);
+ /*
+ * if error
+ * or caller did not give enough space
+ * or just querying
+ */
+ if (ret < 0 || ret > (int)n || xstats == NULL)
+ return ret;
+
+ FOREACH_SUBDEV_STATE(sdev, i, dev, DEV_ACTIVE) {
+ ret = rte_eth_xstats_get(PORT_ID(sdev), xstats, n);
+ if (ret < 0)
+ return ret;
+
+ if (ret > (int)n)
+ return n + count;
+
+ /* add offset to id's from sub-device */
+ for (j = 0; j < ret; j++)
+ xstats[j].id += count;
+
+ xstats += ret;
+ n -= ret;
+ count += ret;
+ }
+
+ return count;
+}
+
+static int
+fs_xstats_get(struct rte_eth_dev *dev,
+ struct rte_eth_xstat *xstats,
+ unsigned int n)
+{
+ int ret;
+
+ fs_lock(dev, 0);
+ ret = __fs_xstats_get(dev, xstats, n);
+ fs_unlock(dev, 0);
+
+ return ret;
+}
+
+
+static int
+fs_xstats_reset(struct rte_eth_dev *dev)
+{
+ struct sub_device *sdev;
+ uint8_t i;
+ int r = 0;
+
+ fs_lock(dev, 0);
+ FOREACH_SUBDEV_STATE(sdev, i, dev, DEV_ACTIVE) {
+ r = rte_eth_xstats_reset(PORT_ID(sdev));
+ if (r < 0)
+ break;
+ }
+ fs_unlock(dev, 0);
+
+ return r;
+}
+
+static void
+fs_dev_merge_desc_lim(struct rte_eth_desc_lim *to,
+ const struct rte_eth_desc_lim *from)
+{
+ to->nb_max = RTE_MIN(to->nb_max, from->nb_max);
+ to->nb_min = RTE_MAX(to->nb_min, from->nb_min);
+ to->nb_align = RTE_MAX(to->nb_align, from->nb_align);
+
+ to->nb_seg_max = RTE_MIN(to->nb_seg_max, from->nb_seg_max);
+ to->nb_mtu_seg_max = RTE_MIN(to->nb_mtu_seg_max, from->nb_mtu_seg_max);
+}
+
+/*
+ * Merge the information from sub-devices.
+ *
+ * The reported values must be the common subset of all sub devices
+ */
+static void
+fs_dev_merge_info(struct rte_eth_dev_info *info,
+ const struct rte_eth_dev_info *sinfo)
+{
+ info->min_mtu = RTE_MAX(info->min_mtu, sinfo->min_mtu);
+ info->max_mtu = RTE_MIN(info->max_mtu, sinfo->max_mtu);
+ info->max_rx_pktlen = RTE_MIN(info->max_rx_pktlen, sinfo->max_rx_pktlen);
+ info->max_rx_queues = RTE_MIN(info->max_rx_queues, sinfo->max_rx_queues);
+ info->max_tx_queues = RTE_MIN(info->max_tx_queues, sinfo->max_tx_queues);
+ info->max_mac_addrs = RTE_MIN(info->max_mac_addrs, sinfo->max_mac_addrs);
+ info->max_hash_mac_addrs = RTE_MIN(info->max_hash_mac_addrs,
+ sinfo->max_hash_mac_addrs);
+ info->max_vmdq_pools = RTE_MIN(info->max_vmdq_pools, sinfo->max_vmdq_pools);
+ info->max_vfs = RTE_MIN(info->max_vfs, sinfo->max_vfs);
+
+ fs_dev_merge_desc_lim(&info->rx_desc_lim, &sinfo->rx_desc_lim);
+ fs_dev_merge_desc_lim(&info->tx_desc_lim, &sinfo->tx_desc_lim);
+
+ info->rx_offload_capa &= sinfo->rx_offload_capa;
+ info->tx_offload_capa &= sinfo->tx_offload_capa;
+ info->rx_queue_offload_capa &= sinfo->rx_queue_offload_capa;
+ info->tx_queue_offload_capa &= sinfo->tx_queue_offload_capa;
+ info->flow_type_rss_offloads &= sinfo->flow_type_rss_offloads;
+
+ /*
+ * RETA size is a GCD of RETA sizes indicated by sub-devices.
+ * Each of these sizes is a power of 2, so use the lower one.
+ */
+ info->reta_size = RTE_MIN(info->reta_size, sinfo->reta_size);
+
+ info->hash_key_size = RTE_MIN(info->hash_key_size,
+ sinfo->hash_key_size);