+/* QoS Functions */
+
+/*
+ * Initialize QoS configuration operations.
+ */
+static void
+qos_conf_init(struct qos_conf *conf, const struct dpdk_qos_ops *ops)
+{
+ conf->ops = ops;
+}
+
+/*
+ * Search existing QoS operations in qos_ops and compare each set of
+ * operations qos_name to name. Return a dpdk_qos_ops pointer to a match,
+ * else return NULL
+ */
+static const struct dpdk_qos_ops *
+qos_lookup_name(const char *name)
+{
+ const struct dpdk_qos_ops *const *opsp;
+
+ for (opsp = qos_confs; *opsp != NULL; opsp++) {
+ const struct dpdk_qos_ops *ops = *opsp;
+ if (!strcmp(name, ops->qos_name)) {
+ return ops;
+ }
+ }
+ return NULL;
+}
+
+/*
+ * Call qos_destruct to clean up items associated with the netdevs
+ * qos_conf. Set netdevs qos_conf to NULL.
+ */
+static void
+qos_delete_conf(struct netdev *netdev_)
+{
+ struct netdev_dpdk *netdev = netdev_dpdk_cast(netdev_);
+
+ rte_spinlock_lock(&netdev->qos_lock);
+ if (netdev->qos_conf) {
+ if (netdev->qos_conf->ops->qos_destruct) {
+ netdev->qos_conf->ops->qos_destruct(netdev_, netdev->qos_conf);
+ }
+ netdev->qos_conf = NULL;
+ }
+ rte_spinlock_unlock(&netdev->qos_lock);
+}
+
+static int
+netdev_dpdk_get_qos_types(const struct netdev *netdev OVS_UNUSED,
+ struct sset *types)
+{
+ const struct dpdk_qos_ops *const *opsp;
+
+ for (opsp = qos_confs; *opsp != NULL; opsp++) {
+ const struct dpdk_qos_ops *ops = *opsp;
+ if (ops->qos_construct && ops->qos_name[0] != '\0') {
+ sset_add(types, ops->qos_name);
+ }
+ }
+ return 0;
+}
+
+static int
+netdev_dpdk_get_qos(const struct netdev *netdev_,
+ const char **typep, struct smap *details)
+{
+ struct netdev_dpdk *netdev = netdev_dpdk_cast(netdev_);
+ int error = 0;
+
+ ovs_mutex_lock(&netdev->mutex);
+ if(netdev->qos_conf) {
+ *typep = netdev->qos_conf->ops->qos_name;
+ error = (netdev->qos_conf->ops->qos_get
+ ? netdev->qos_conf->ops->qos_get(netdev_, details): 0);
+ }
+ ovs_mutex_unlock(&netdev->mutex);
+
+ return error;
+}
+
+static int
+netdev_dpdk_set_qos(struct netdev *netdev_,
+ const char *type, const struct smap *details)
+{
+ struct netdev_dpdk *netdev = netdev_dpdk_cast(netdev_);
+ const struct dpdk_qos_ops *new_ops = NULL;
+ int error = 0;
+
+ /* If type is empty or unsupported then the current QoS configuration
+ * for the dpdk-netdev can be destroyed */
+ new_ops = qos_lookup_name(type);
+
+ if (type[0] == '\0' || !new_ops || !new_ops->qos_construct) {
+ qos_delete_conf(netdev_);
+ return EOPNOTSUPP;
+ }
+
+ ovs_mutex_lock(&netdev->mutex);
+
+ if (netdev->qos_conf) {
+ if (new_ops == netdev->qos_conf->ops) {
+ error = new_ops->qos_set ? new_ops->qos_set(netdev_, details) : 0;
+ } else {
+ /* Delete existing QoS configuration. */
+ qos_delete_conf(netdev_);
+ ovs_assert(netdev->qos_conf == NULL);
+
+ /* Install new QoS configuration. */
+ error = new_ops->qos_construct(netdev_, details);
+ ovs_assert((error == 0) == (netdev->qos_conf != NULL));
+ }
+ } else {
+ error = new_ops->qos_construct(netdev_, details);
+ ovs_assert((error == 0) == (netdev->qos_conf != NULL));
+ }
+
+ ovs_mutex_unlock(&netdev->mutex);
+ return error;
+}
+
+/* egress-policer details */
+
+struct egress_policer {
+ struct qos_conf qos_conf;
+ struct rte_meter_srtcm_params app_srtcm_params;
+ struct rte_meter_srtcm egress_meter;
+};
+
+static struct egress_policer *
+egress_policer_get__(const struct netdev *netdev_)
+{
+ struct netdev_dpdk *netdev = netdev_dpdk_cast(netdev_);
+ return CONTAINER_OF(netdev->qos_conf, struct egress_policer, qos_conf);
+}
+
+static int
+egress_policer_qos_construct(struct netdev *netdev_,
+ const struct smap *details)
+{
+ struct netdev_dpdk *netdev = netdev_dpdk_cast(netdev_);
+ struct egress_policer *policer;
+ const char *cir_s;
+ const char *cbs_s;
+ int err = 0;
+
+ rte_spinlock_lock(&netdev->qos_lock);
+ policer = xmalloc(sizeof *policer);
+ qos_conf_init(&policer->qos_conf, &egress_policer_ops);
+ netdev->qos_conf = &policer->qos_conf;
+ cir_s = smap_get(details, "cir");
+ cbs_s = smap_get(details, "cbs");
+ policer->app_srtcm_params.cir = cir_s ? strtoull(cir_s, NULL, 10) : 0;
+ policer->app_srtcm_params.cbs = cbs_s ? strtoull(cbs_s, NULL, 10) : 0;
+ policer->app_srtcm_params.ebs = 0;
+ err = rte_meter_srtcm_config(&policer->egress_meter,
+ &policer->app_srtcm_params);
+ rte_spinlock_unlock(&netdev->qos_lock);
+
+ return err;
+}
+
+static void
+egress_policer_qos_destruct(struct netdev *netdev_ OVS_UNUSED,
+ struct qos_conf *conf)
+{
+ struct egress_policer *policer = CONTAINER_OF(conf, struct egress_policer,
+ qos_conf);
+ free(policer);
+}
+
+static int
+egress_policer_qos_get(const struct netdev *netdev, struct smap *details)
+{
+ struct egress_policer *policer = egress_policer_get__(netdev);
+ smap_add_format(details, "cir", "%llu",
+ 1ULL * policer->app_srtcm_params.cir);
+ smap_add_format(details, "cbs", "%llu",
+ 1ULL * policer->app_srtcm_params.cbs);
+ return 0;
+}
+
+static int
+egress_policer_qos_set(struct netdev *netdev_, const struct smap *details)
+{
+ struct egress_policer *policer;
+ const char *cir_s;
+ const char *cbs_s;
+ int err = 0;
+
+ policer = egress_policer_get__(netdev_);
+ cir_s = smap_get(details, "cir");
+ cbs_s = smap_get(details, "cbs");
+ policer->app_srtcm_params.cir = cir_s ? strtoull(cir_s, NULL, 10) : 0;
+ policer->app_srtcm_params.cbs = cbs_s ? strtoull(cbs_s, NULL, 10) : 0;
+ policer->app_srtcm_params.ebs = 0;
+ err = rte_meter_srtcm_config(&policer->egress_meter,
+ &policer->app_srtcm_params);
+
+ return err;
+}
+
+static inline bool
+egress_policer_pkt_handle__(struct rte_meter_srtcm *meter,
+ struct rte_mbuf *pkt, uint64_t time)
+{
+ uint32_t pkt_len = rte_pktmbuf_pkt_len(pkt) - sizeof(struct ether_hdr);
+
+ return rte_meter_srtcm_color_blind_check(meter, time, pkt_len) ==
+ e_RTE_METER_GREEN;
+}
+
+static int
+egress_policer_run(struct netdev *netdev_, struct rte_mbuf **pkts,
+ int pkt_cnt)
+{
+ int i = 0;
+ int cnt = 0;
+ struct egress_policer *policer = egress_policer_get__(netdev_);
+ struct rte_mbuf *pkt = NULL;
+ uint64_t current_time = rte_rdtsc();
+
+ for(i = 0; i < pkt_cnt; i++) {
+ pkt = pkts[i];
+ /* Handle current packet */
+ if (egress_policer_pkt_handle__(&policer->egress_meter, pkt,
+ current_time)) {
+ if (cnt != i) {
+ pkts[cnt] = pkt;
+ }
+ cnt++;
+ } else {
+ rte_pktmbuf_free(pkt);
+ }
+ }
+
+ return cnt;
+}
+
+static const struct dpdk_qos_ops egress_policer_ops = {
+ "egress-policer", /* qos_name */
+ egress_policer_qos_construct,
+ egress_policer_qos_destruct,
+ egress_policer_qos_get,
+ egress_policer_qos_set,
+ egress_policer_run
+};
+
+#define NETDEV_DPDK_CLASS(NAME, INIT, CONSTRUCT, DESTRUCT, MULTIQ, SEND, \
+ GET_CARRIER, GET_STATS, GET_FEATURES, GET_STATUS, RXQ_RECV) \