[2/6] net/nfp: add meter profile options

Message ID 20221207021748.40143-3-chaoyong.he@corigine.com (mailing list archive)
State Accepted, archived
Delegated to: Ferruh Yigit
Headers
Series add offload support of meter for NFP cards |

Checks

Context Check Description
ci/checkpatch success coding style OK

Commit Message

Chaoyong He Dec. 7, 2022, 2:17 a.m. UTC
  From: Jin Liu <jin.liu@corigine.com>

Add support for meter options, finish DPDK meter architecture
on NFP PMD.

Get meter capabilities function, the meter capabilities indicate
that what meter options NFP card support.

Add support for meter profile functions, include add a new meter
profile and delete a exist profile, the profile include the mode
and specific value of the speed limit, add meter profile to both
driver and firmware.

Signed-off-by: Jin Liu <jin.liu@corigine.com>
Signed-off-by: Peng Zhang <peng.zhang@corigine.com>
Reviewed-by: Chaoyong He <chaoyong.he@corigine.com>
Reviewed-by: Niklas Söderlund <niklas.soderlund@corigine.com>
---
 drivers/net/nfp/flower/nfp_flower.c           |  12 +-
 drivers/net/nfp/flower/nfp_flower.h           |   1 +
 drivers/net/nfp/flower/nfp_flower_cmsg.c      |  58 +++
 drivers/net/nfp/flower/nfp_flower_cmsg.h      |   5 +
 .../net/nfp/flower/nfp_flower_representor.c   |   1 +
 drivers/net/nfp/meson.build                   |   1 +
 drivers/net/nfp/nfp_mtr.c                     | 429 ++++++++++++++++++
 drivers/net/nfp/nfp_mtr.h                     |  94 ++++
 8 files changed, 600 insertions(+), 1 deletion(-)
 create mode 100644 drivers/net/nfp/nfp_mtr.c
 create mode 100644 drivers/net/nfp/nfp_mtr.h
  

Patch

diff --git a/drivers/net/nfp/flower/nfp_flower.c b/drivers/net/nfp/flower/nfp_flower.c
index 5353daa47d..7b46dc0f6a 100644
--- a/drivers/net/nfp/flower/nfp_flower.c
+++ b/drivers/net/nfp/flower/nfp_flower.c
@@ -194,6 +194,8 @@  nfp_flower_pf_close(struct rte_eth_dev *dev)
 	pf_dev = hw->pf_dev;
 	app_fw_flower = NFP_PRIV_TO_APP_FW_FLOWER(pf_dev->app_fw_priv);
 
+	nfp_mtr_priv_uninit(pf_dev);
+
 	/*
 	 * We assume that the DPDK application is stopping all the
 	 * threads/queues before calling the device close function.
@@ -1097,13 +1099,19 @@  nfp_init_app_fw_flower(struct nfp_pf_dev *pf_dev)
 		goto app_cleanup;
 	}
 
+	ret = nfp_mtr_priv_init(pf_dev);
+	if (ret != 0) {
+		PMD_INIT_LOG(ERR, "Error initializing metering private data");
+		goto flow_priv_cleanup;
+	}
+
 	/* Allocate memory for the PF AND ctrl vNIC here (hence the * 2) */
 	pf_hw = rte_zmalloc_socket("nfp_pf_vnic", 2 * sizeof(struct nfp_net_adapter),
 			RTE_CACHE_LINE_SIZE, numa_node);
 	if (pf_hw == NULL) {
 		PMD_INIT_LOG(ERR, "Could not malloc nfp pf vnic");
 		ret = -ENOMEM;
-		goto flow_priv_cleanup;
+		goto mtr_priv_cleanup;
 	}
 
 	/* Map the PF ctrl bar */
@@ -1193,6 +1201,8 @@  nfp_init_app_fw_flower(struct nfp_pf_dev *pf_dev)
 	nfp_cpp_area_free(pf_dev->ctrl_area);
 vnic_cleanup:
 	rte_free(pf_hw);
+mtr_priv_cleanup:
+	nfp_mtr_priv_uninit(pf_dev);
 flow_priv_cleanup:
 	nfp_flow_priv_uninit(pf_dev);
 app_cleanup:
diff --git a/drivers/net/nfp/flower/nfp_flower.h b/drivers/net/nfp/flower/nfp_flower.h
index c05a761a95..0ce5f7bd9e 100644
--- a/drivers/net/nfp/flower/nfp_flower.h
+++ b/drivers/net/nfp/flower/nfp_flower.h
@@ -76,6 +76,7 @@  struct nfp_app_fw_flower {
 	uint64_t ext_features;
 
 	struct nfp_flow_priv *flow_priv;
+	struct nfp_mtr_priv *mtr_priv;
 };
 
 static inline bool
diff --git a/drivers/net/nfp/flower/nfp_flower_cmsg.c b/drivers/net/nfp/flower/nfp_flower_cmsg.c
index f04b9bd921..eaddd6c52c 100644
--- a/drivers/net/nfp/flower/nfp_flower_cmsg.c
+++ b/drivers/net/nfp/flower/nfp_flower_cmsg.c
@@ -467,3 +467,61 @@  nfp_flower_cmsg_tun_mac_rule(struct nfp_app_fw_flower *app_fw_flower,
 
 	return 0;
 }
+
+int
+nfp_flower_cmsg_qos_add(struct nfp_app_fw_flower *app_fw_flower,
+		struct nfp_profile_conf *conf)
+{
+	char *msg;
+	uint16_t cnt;
+	uint32_t len;
+	struct rte_mbuf *mbuf;
+
+	mbuf = rte_pktmbuf_alloc(app_fw_flower->ctrl_pktmbuf_pool);
+	if (mbuf == NULL) {
+		PMD_DRV_LOG(DEBUG, "Failed to alloc mbuf for qos add");
+		return -ENOMEM;
+	}
+
+	len = sizeof(struct nfp_profile_conf);
+	msg = nfp_flower_cmsg_init(mbuf, NFP_FLOWER_CMSG_TYPE_QOS_MOD, len);
+	rte_memcpy(msg, conf, len);
+
+	cnt = nfp_flower_ctrl_vnic_xmit(app_fw_flower, mbuf);
+	if (cnt == 0) {
+		PMD_DRV_LOG(ERR, "Send cmsg through ctrl vnic failed.");
+		rte_pktmbuf_free(mbuf);
+		return -EIO;
+	}
+
+	return 0;
+}
+
+int
+nfp_flower_cmsg_qos_delete(struct nfp_app_fw_flower *app_fw_flower,
+		struct nfp_profile_conf *conf)
+{
+	char *msg;
+	uint16_t cnt;
+	uint32_t len;
+	struct rte_mbuf *mbuf;
+
+	mbuf = rte_pktmbuf_alloc(app_fw_flower->ctrl_pktmbuf_pool);
+	if (mbuf == NULL) {
+		PMD_DRV_LOG(DEBUG, "Failed to alloc mbuf for qos delete");
+		return -ENOMEM;
+	}
+
+	len = sizeof(struct nfp_profile_conf);
+	msg = nfp_flower_cmsg_init(mbuf, NFP_FLOWER_CMSG_TYPE_QOS_DEL, len);
+	rte_memcpy(msg, conf, len);
+
+	cnt = nfp_flower_ctrl_vnic_xmit(app_fw_flower, mbuf);
+	if (cnt == 0) {
+		PMD_DRV_LOG(ERR, "Send cmsg through ctrl vnic failed.");
+		rte_pktmbuf_free(mbuf);
+		return -EIO;
+	}
+
+	return 0;
+}
diff --git a/drivers/net/nfp/flower/nfp_flower_cmsg.h b/drivers/net/nfp/flower/nfp_flower_cmsg.h
index bdfde70fbe..1894c454d3 100644
--- a/drivers/net/nfp/flower/nfp_flower_cmsg.h
+++ b/drivers/net/nfp/flower/nfp_flower_cmsg.h
@@ -9,6 +9,7 @@ 
 #include <rte_byteorder.h>
 #include <rte_ether.h>
 
+#include "../nfp_mtr.h"
 #include "../nfp_flow.h"
 
 struct nfp_flower_cmsg_hdr {
@@ -923,5 +924,9 @@  int nfp_flower_cmsg_tun_mac_rule(struct nfp_app_fw_flower *app_fw_flower,
 		struct rte_ether_addr *mac,
 		uint16_t mac_idx,
 		bool is_del);
+int nfp_flower_cmsg_qos_add(struct nfp_app_fw_flower *app_fw_flower,
+		struct nfp_profile_conf *conf);
+int nfp_flower_cmsg_qos_delete(struct nfp_app_fw_flower *app_fw_flower,
+		struct nfp_profile_conf *conf);
 
 #endif /* _NFP_CMSG_H_ */
diff --git a/drivers/net/nfp/flower/nfp_flower_representor.c b/drivers/net/nfp/flower/nfp_flower_representor.c
index 76be22efa2..362c67f7b5 100644
--- a/drivers/net/nfp/flower/nfp_flower_representor.c
+++ b/drivers/net/nfp/flower/nfp_flower_representor.c
@@ -553,6 +553,7 @@  static const struct eth_dev_ops nfp_flower_repr_dev_ops = {
 	.mac_addr_set         = nfp_flower_repr_mac_addr_set,
 
 	.flow_ops_get         = nfp_net_flow_ops_get,
+	.mtr_ops_get          = nfp_net_mtr_ops_get,
 };
 
 static uint32_t
diff --git a/drivers/net/nfp/meson.build b/drivers/net/nfp/meson.build
index 7416fd3706..fc8fd906bc 100644
--- a/drivers/net/nfp/meson.build
+++ b/drivers/net/nfp/meson.build
@@ -28,6 +28,7 @@  sources = files(
         'nfp_ethdev_vf.c',
         'nfp_ethdev.c',
         'nfp_flow.c',
+        'nfp_mtr.c',
 )
 
 deps += ['hash']
diff --git a/drivers/net/nfp/nfp_mtr.c b/drivers/net/nfp/nfp_mtr.c
new file mode 100644
index 0000000000..50efb56879
--- /dev/null
+++ b/drivers/net/nfp/nfp_mtr.c
@@ -0,0 +1,429 @@ 
+/* SPDX-License-Identifier: BSD-3-Clause
+ * Copyright(c) 2022 Corigine, Inc.
+ * All rights reserved.
+ */
+
+#include <rte_mtr_driver.h>
+#include <bus_pci_driver.h>
+#include <rte_malloc.h>
+
+#include "nfp_common.h"
+#include "nfp_mtr.h"
+#include "nfp_logs.h"
+#include "flower/nfp_flower.h"
+#include "flower/nfp_flower_cmsg.h"
+#include "flower/nfp_flower_representor.h"
+
+#define NFP_FL_QOS_PPS          RTE_BIT32(15)
+#define NFP_FL_QOS_METER        RTE_BIT32(10)
+#define NFP_FL_QOS_RFC2697      RTE_BIT32(0)
+
+/**
+ * Callback to get MTR capabilities.
+ *
+ * @param[in] dev
+ *   Pointer to the device (unused).
+ * @param[out] cap
+ *   Pointer to the meter object capabilities.
+ * @param[out] error
+ *   Pointer to the error (unused).
+ *
+ * @returns
+ *   0 on success, a negative value otherwise and rte_errno is set.
+ */
+static int
+nfp_mtr_cap_get(struct rte_eth_dev *dev __rte_unused,
+		struct rte_mtr_capabilities *cap,
+		struct rte_mtr_error *error)
+{
+	if (cap == NULL) {
+		return -rte_mtr_error_set(error, EINVAL,
+				RTE_MTR_ERROR_TYPE_UNSPECIFIED,
+				NULL, "NULL pointer for capabilitie argument");
+	}
+
+	memset(cap, 0, sizeof(struct rte_mtr_capabilities));
+
+	cap->n_max                               = NFP_MAX_MTR_CNT;
+	cap->n_shared_max                        = NFP_MAX_MTR_CNT;
+	cap->identical                           = 1;
+	cap->shared_identical                    = 1;
+	cap->chaining_n_mtrs_per_flow_max        = 1;
+	cap->meter_srtcm_rfc2697_n_max           = NFP_MAX_MTR_CNT;
+	cap->meter_trtcm_rfc2698_n_max           = NFP_MAX_MTR_CNT;
+	cap->meter_rate_max                      = UINT64_MAX;
+	cap->meter_policy_n_max                  = NFP_MAX_POLICY_CNT;
+	cap->srtcm_rfc2697_byte_mode_supported   = 1;
+	cap->srtcm_rfc2697_packet_mode_supported = 1;
+	cap->trtcm_rfc2698_byte_mode_supported   = 1;
+	cap->trtcm_rfc2698_packet_mode_supported = 1;
+	cap->stats_mask = RTE_MTR_STATS_N_PKTS_GREEN |
+			RTE_MTR_STATS_N_PKTS_DROPPED |
+			RTE_MTR_STATS_N_BYTES_GREEN |
+			RTE_MTR_STATS_N_BYTES_DROPPED;
+
+	return 0;
+}
+
+static int
+nfp_mtr_profile_validate(uint32_t mtr_profile_id,
+		struct rte_mtr_meter_profile *profile,
+		struct rte_mtr_error *error)
+{
+	/* Profile must not be NULL. */
+	if (profile == NULL) {
+		return -rte_mtr_error_set(error, EINVAL,
+				RTE_MTR_ERROR_TYPE_METER_PROFILE,
+				NULL, "Meter profile is null");
+	}
+
+	/* Meter profile ID must be valid. */
+	if (mtr_profile_id >= NFP_MAX_PROFILE_CNT) {
+		return -rte_mtr_error_set(error, EINVAL,
+				RTE_MTR_ERROR_TYPE_METER_PROFILE_ID,
+				NULL, "Meter profile id not valid");
+	}
+
+	switch (profile->alg) {
+	case RTE_MTR_SRTCM_RFC2697:
+	case RTE_MTR_TRTCM_RFC2698:
+		return 0;
+	case RTE_MTR_TRTCM_RFC4115:
+		return -rte_mtr_error_set(error, ENOTSUP,
+				RTE_MTR_ERROR_TYPE_METER_PROFILE,
+				NULL, "Unsupported metering algorithm");
+	default:
+		return -rte_mtr_error_set(error, ENOTSUP,
+				RTE_MTR_ERROR_TYPE_METER_PROFILE,
+				NULL, "Unknown metering algorithm");
+	}
+}
+
+static void
+nfp_mtr_profile_config_2698(uint32_t mtr_profile_id,
+		struct rte_mtr_meter_profile *profile,
+		struct nfp_profile_conf *conf)
+{
+	if (profile->packet_mode != 0)
+		conf->head.flags_opts |= rte_cpu_to_be_32(NFP_FL_QOS_PPS);
+
+	conf->head.flags_opts |= rte_cpu_to_be_32(NFP_FL_QOS_METER);
+	conf->head.profile_id = rte_cpu_to_be_32(mtr_profile_id);
+
+	conf->bkt_tkn_c = rte_cpu_to_be_32(profile->trtcm_rfc2698.cbs);
+	conf->bkt_tkn_p = rte_cpu_to_be_32(profile->trtcm_rfc2698.pbs);
+	conf->cbs = rte_cpu_to_be_32(profile->trtcm_rfc2698.cbs);
+	conf->pbs = rte_cpu_to_be_32(profile->trtcm_rfc2698.pbs);
+	conf->cir = rte_cpu_to_be_32(profile->trtcm_rfc2698.cir);
+	conf->pir = rte_cpu_to_be_32(profile->trtcm_rfc2698.pir);
+}
+
+static void
+nfp_mtr_profile_config_2697(uint32_t mtr_profile_id,
+		struct rte_mtr_meter_profile *profile,
+		struct nfp_profile_conf *conf)
+{
+	if (profile->packet_mode != 0)
+		conf->head.flags_opts |= rte_cpu_to_be_32(NFP_FL_QOS_PPS);
+
+	conf->head.flags_opts |= rte_cpu_to_be_32(NFP_FL_QOS_RFC2697);
+	conf->head.flags_opts |= rte_cpu_to_be_32(NFP_FL_QOS_METER);
+	conf->head.profile_id = rte_cpu_to_be_32(mtr_profile_id);
+
+	conf->bkt_tkn_c = rte_cpu_to_be_32(profile->srtcm_rfc2697.cbs);
+	conf->bkt_tkn_p = rte_cpu_to_be_32(profile->srtcm_rfc2697.ebs);
+	conf->cbs = rte_cpu_to_be_32(profile->srtcm_rfc2697.cbs);
+	conf->pbs = rte_cpu_to_be_32(profile->srtcm_rfc2697.ebs);
+	conf->cir = rte_cpu_to_be_32(profile->srtcm_rfc2697.cir);
+	conf->pir = rte_cpu_to_be_32(profile->srtcm_rfc2697.cir);
+}
+
+static int
+nfp_mtr_profile_conf_mod(uint32_t mtr_profile_id,
+		struct rte_mtr_meter_profile *profile,
+		struct nfp_profile_conf *conf)
+{
+	switch (profile->alg) {
+	case RTE_MTR_SRTCM_RFC2697:
+		nfp_mtr_profile_config_2697(mtr_profile_id, profile, conf);
+		return 0;
+	case RTE_MTR_TRTCM_RFC2698:
+		nfp_mtr_profile_config_2698(mtr_profile_id, profile, conf);
+		return 0;
+	case RTE_MTR_TRTCM_RFC4115:
+		return -ENOTSUP;
+	default:
+		return -EINVAL;
+	}
+}
+
+static int
+nfp_mtr_profile_conf_insert(uint32_t mtr_profile_id,
+		struct rte_mtr_meter_profile *profile,
+		struct nfp_mtr_profile *mtr_profile)
+{
+	mtr_profile->profile_id = mtr_profile_id;
+	mtr_profile->in_use = false;
+
+	return nfp_mtr_profile_conf_mod(mtr_profile_id, profile,
+			&mtr_profile->conf);
+}
+
+static struct nfp_mtr_profile *
+nfp_mtr_profile_search(struct nfp_mtr_priv *priv, uint32_t mtr_profile_id)
+{
+	struct nfp_mtr_profile *mtr_profile;
+
+	LIST_FOREACH(mtr_profile, &priv->profiles, next)
+		if (mtr_profile->profile_id == mtr_profile_id)
+			break;
+
+	return mtr_profile;
+}
+
+static int
+nfp_mtr_profile_insert(struct nfp_app_fw_flower *app_fw_flower,
+		struct rte_mtr_meter_profile *profile,
+		uint32_t mtr_profile_id,
+		struct rte_mtr_error *error)
+{
+	int ret;
+	struct nfp_mtr_priv *priv;
+	struct nfp_mtr_profile *mtr_profile;
+
+	priv = app_fw_flower->mtr_priv;
+
+	/* Meter profile memory allocation. */
+	mtr_profile = rte_zmalloc(NULL, sizeof(struct nfp_mtr_profile), 0);
+	if (mtr_profile == NULL) {
+		return -rte_mtr_error_set(error, ENOMEM,
+				RTE_MTR_ERROR_TYPE_UNSPECIFIED,
+				NULL, "Meter profile alloc failed");
+	}
+
+	ret = nfp_mtr_profile_conf_insert(mtr_profile_id,
+			profile, mtr_profile);
+	if (ret != 0) {
+		rte_mtr_error_set(error, EINVAL,
+				RTE_MTR_ERROR_TYPE_UNSPECIFIED,
+				NULL, "Insert profile config failed");
+		goto free_profile;
+	}
+
+	ret = nfp_flower_cmsg_qos_add(app_fw_flower, &mtr_profile->conf);
+	if (ret != 0) {
+		rte_mtr_error_set(error, EINVAL,
+				RTE_MTR_ERROR_TYPE_UNSPECIFIED,
+				NULL, "Add meter to firmware failed");
+		goto free_profile;
+	}
+
+	/* Insert profile into profile list */
+	LIST_INSERT_HEAD(&priv->profiles, mtr_profile, next);
+
+	return 0;
+
+free_profile:
+	rte_free(mtr_profile);
+
+	return ret;
+}
+
+static int
+nfp_mtr_profile_mod(struct nfp_app_fw_flower *app_fw_flower,
+		struct rte_mtr_meter_profile *profile,
+		struct nfp_mtr_profile *mtr_profile,
+		struct rte_mtr_error *error)
+{
+	int ret;
+	struct nfp_profile_conf old_conf;
+
+	/* Get the old profile config */
+	rte_memcpy(&old_conf, &mtr_profile->conf, sizeof(old_conf));
+
+	ret = nfp_mtr_profile_conf_mod(mtr_profile->profile_id,
+			profile, &mtr_profile->conf);
+	if (ret != 0) {
+		rte_mtr_error_set(error, EINVAL,
+				RTE_MTR_ERROR_TYPE_UNSPECIFIED,
+				NULL, "Mod profile config failed");
+		goto rollback;
+	}
+
+	ret = nfp_flower_cmsg_qos_add(app_fw_flower, &mtr_profile->conf);
+	if (ret != 0) {
+		rte_mtr_error_set(error, EINVAL,
+				RTE_MTR_ERROR_TYPE_UNSPECIFIED,
+				NULL, "Mod meter to firmware failed");
+		goto rollback;
+	}
+
+	return 0;
+
+rollback:
+	rte_memcpy(&mtr_profile->conf, &old_conf, sizeof(old_conf));
+
+	return ret;
+}
+
+/**
+ * Callback to add MTR profile.
+ *
+ * @param[in] dev
+ *   Pointer to Ethernet device.
+ * @param[in] mtr_profile_id
+ *   Meter profile id.
+ * @param[in] profile
+ *   Pointer to meter profile detail.
+ * @param[out] error
+ *   Pointer to the error structure.
+ *
+ * @return
+ *   0 on success, a negative value otherwise and rte_errno is set.
+ */
+static int
+nfp_mtr_profile_add(struct rte_eth_dev *dev,
+		uint32_t mtr_profile_id,
+		struct rte_mtr_meter_profile *profile,
+		struct rte_mtr_error *error)
+{
+	int ret;
+	struct nfp_mtr_priv *priv;
+	struct nfp_mtr_profile *mtr_profile;
+	struct nfp_app_fw_flower *app_fw_flower;
+	struct nfp_flower_representor *representor;
+
+	representor = dev->data->dev_private;
+	app_fw_flower = representor->app_fw_flower;
+	priv = app_fw_flower->mtr_priv;
+
+	/* Check input params */
+	ret = nfp_mtr_profile_validate(mtr_profile_id, profile, error);
+	if (ret != 0)
+		return ret;
+
+	/* Check if mtr profile id exist */
+	mtr_profile = nfp_mtr_profile_search(priv, mtr_profile_id);
+	if (mtr_profile == NULL) {
+		ret = nfp_mtr_profile_insert(app_fw_flower,
+				profile, mtr_profile_id, error);
+	} else {
+		ret = nfp_mtr_profile_mod(app_fw_flower,
+				profile, mtr_profile, error);
+	}
+
+	return ret;
+}
+
+/**
+ * Callback to delete MTR profile.
+ *
+ * @param[in] dev
+ *   Pointer to Ethernet device.
+ * @param[in] mtr_profile_id
+ *   Meter profile id.
+ * @param[out] error
+ *   Pointer to the error structure.
+ *
+ * @return
+ *   0 on success, a negative value otherwise and rte_errno is set.
+ */
+static int
+nfp_mtr_profile_delete(struct rte_eth_dev *dev,
+		uint32_t mtr_profile_id,
+		struct rte_mtr_error *error)
+{
+	int ret;
+	struct nfp_mtr_priv *priv;
+	struct nfp_mtr_profile *mtr_profile;
+	struct nfp_app_fw_flower *app_fw_flower;
+	struct nfp_flower_representor *representor;
+
+	representor = dev->data->dev_private;
+	app_fw_flower = representor->app_fw_flower;
+	priv = app_fw_flower->mtr_priv;
+
+	/* Check if mtr profile id exist */
+	mtr_profile = nfp_mtr_profile_search(priv, mtr_profile_id);
+	if (mtr_profile == NULL) {
+		return -rte_mtr_error_set(error, EINVAL,
+				RTE_MTR_ERROR_TYPE_METER_PROFILE_ID,
+				NULL, "Request meter profile not exist");
+	}
+
+	if (mtr_profile->in_use) {
+		return -rte_mtr_error_set(error, EINVAL,
+				RTE_MTR_ERROR_TYPE_METER_PROFILE,
+				NULL, "Request meter profile is been used");
+	}
+
+	ret = nfp_flower_cmsg_qos_delete(app_fw_flower, &mtr_profile->conf);
+	if (ret != 0) {
+		return -rte_mtr_error_set(error, EINVAL,
+				RTE_MTR_ERROR_TYPE_UNSPECIFIED,
+				NULL, "Delete meter from firmware failed");
+	}
+
+	/* Remove profile from profile list */
+	LIST_REMOVE(mtr_profile, next);
+	rte_free(mtr_profile);
+
+	return 0;
+}
+
+static const struct rte_mtr_ops nfp_mtr_ops = {
+	.capabilities_get      = nfp_mtr_cap_get,
+	.meter_profile_add     = nfp_mtr_profile_add,
+	.meter_profile_delete  = nfp_mtr_profile_delete,
+};
+
+int
+nfp_net_mtr_ops_get(struct rte_eth_dev *dev, void *arg)
+{
+	if ((dev->data->dev_flags & RTE_ETH_DEV_REPRESENTOR) == 0) {
+		PMD_DRV_LOG(ERR, "Port is not a representor");
+		return -EINVAL;
+	}
+
+	*(const struct rte_mtr_ops **)arg = &nfp_mtr_ops;
+
+	return 0;
+}
+
+int
+nfp_mtr_priv_init(struct nfp_pf_dev *pf_dev)
+{
+	struct nfp_mtr_priv *priv;
+	struct nfp_app_fw_flower *app_fw_flower;
+
+	priv = rte_zmalloc("nfp_app_mtr_priv", sizeof(struct nfp_mtr_priv), 0);
+	if (priv == NULL) {
+		PMD_INIT_LOG(ERR, "nfp app mtr priv creation failed");
+		return -ENOMEM;
+	}
+
+	app_fw_flower = NFP_PRIV_TO_APP_FW_FLOWER(pf_dev->app_fw_priv);
+	app_fw_flower->mtr_priv = priv;
+
+	LIST_INIT(&priv->profiles);
+
+	return 0;
+}
+
+void
+nfp_mtr_priv_uninit(struct nfp_pf_dev *pf_dev)
+{
+	struct nfp_mtr_priv *priv;
+	struct nfp_mtr_profile *mtr_profile;
+	struct nfp_app_fw_flower *app_fw_flower;
+
+	app_fw_flower = NFP_PRIV_TO_APP_FW_FLOWER(pf_dev->app_fw_priv);
+	priv = app_fw_flower->mtr_priv;
+
+	LIST_FOREACH(mtr_profile, &priv->profiles, next) {
+		LIST_REMOVE(mtr_profile, next);
+		rte_free(mtr_profile);
+	}
+
+	rte_free(priv);
+}
diff --git a/drivers/net/nfp/nfp_mtr.h b/drivers/net/nfp/nfp_mtr.h
new file mode 100644
index 0000000000..05919c2bde
--- /dev/null
+++ b/drivers/net/nfp/nfp_mtr.h
@@ -0,0 +1,94 @@ 
+/* SPDX-License-Identifier: BSD-3-Clause
+ * Copyright(c) 2022 Corigine, Inc.
+ * All rights reserved.
+ */
+
+#ifndef __NFP_MTR_H__
+#define __NFP_MTR_H__
+
+/**
+ * The max meter count is determined by firmware.
+ * The max count is 65536 defined by OF_METER_COUNT.
+ */
+#define NFP_MAX_MTR_CNT                65536
+#define NFP_MAX_POLICY_CNT             NFP_MAX_MTR_CNT
+#define NFP_MAX_PROFILE_CNT            NFP_MAX_MTR_CNT
+
+/**
+ * See RFC 2698 for more details.
+ * Word[0](Flag options):
+ * [15] p(pps) 1 for pps, 0 for bps
+ *
+ * Meter control message
+ *  1 0 9 8 7 6 5 4 3 2 1 0 9 8 7 6 5 4 3 2 1 0 9 8 7 6 5 4 3 2 1 0
+ * +-------------------------------+-+---+-----+-+---------+-+---+-+
+ * |            Reserved           |p| Y |TYPE |E|  TSHFV  |P| PC|R|
+ * +-------------------------------+-+---+-----+-+---------+-+---+-+
+ * |                           Profile ID                          |
+ * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+ * |                        Token Bucket Peak                      |
+ * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+ * |                     Token Bucket Committed                    |
+ * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+ * |                         Peak Burst Size                       |
+ * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+ * |                      Committed Burst Size                     |
+ * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+ * |                      Peak Information Rate                    |
+ * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+ * |                    Committed Information Rate                 |
+ * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+ */
+struct nfp_cfg_head {
+	rte_be32_t flags_opts;
+	rte_be32_t profile_id;
+};
+
+/**
+ * Struct nfp_profile_conf - profile config, offload to NIC
+ * @head:        config head information
+ * @bkt_tkn_p:   token bucket peak
+ * @bkt_tkn_c:   token bucket committed
+ * @pbs:         peak burst size
+ * @cbs:         committed burst size
+ * @pir:         peak information rate
+ * @cir:         committed information rate
+ */
+struct nfp_profile_conf {
+	struct nfp_cfg_head head;
+	rte_be32_t bkt_tkn_p;
+	rte_be32_t bkt_tkn_c;
+	rte_be32_t pbs;
+	rte_be32_t cbs;
+	rte_be32_t pir;
+	rte_be32_t cir;
+};
+
+/**
+ * Struct nfp_mtr_profile - meter profile, stored in driver
+ * Can only be used by one meter
+ * @next:        next meter profile object
+ * @profile_id:  meter profile id
+ * @conf:        meter profile config
+ * @in_use:      if profile is been used by meter
+ */
+struct nfp_mtr_profile {
+	LIST_ENTRY(nfp_mtr_profile) next;
+	uint32_t profile_id;
+	struct nfp_profile_conf conf;
+	bool in_use;
+};
+
+/**
+ * Struct nfp_mtr_priv - meter private data
+ * @profiles:        the head node of profile list
+ */
+struct nfp_mtr_priv {
+	LIST_HEAD(, nfp_mtr_profile) profiles;
+};
+
+int nfp_net_mtr_ops_get(struct rte_eth_dev *dev, void *arg);
+int nfp_mtr_priv_init(struct nfp_pf_dev *pf_dev);
+void nfp_mtr_priv_uninit(struct nfp_pf_dev *pf_dev);
+
+#endif /* __NFP_MTR_H__ */