diff mbox series

[ovs-dev,v2,1/2] * netdev-dpdk: Add support for userspace port-based egress packet-per-second policing.

Message ID SY4PR01MB8438B2D9D73A46F65A469FFECD949@SY4PR01MB8438.ausprd01.prod.outlook.com
State Changes Requested
Headers show
Series netdev-dpdk: Add support for userspace port-based packet-per-second policing. | expand

Checks

Context Check Description
ovsrobot/apply-robot warning apply and check: warning
ovsrobot/github-robot-_Build_and_Test success github build: passed

Commit Message

miter April 9, 2023, 11:15 a.m. UTC
From: Lin Huang <linhuang@ruijie.com.cn>

OvS has supported packet-per-second policer which can be set at ingress and egress
side in kernel datapath. But the userspace datapath dosen't support for ingress and
egress packet-per-second policing now.

So, this patch add support for userspace egress pps policing by using native ovs
token bucket library. Token bucket is accumulated by 'rate' tokens per millisecond
and store maxiumim tokens at 'burst' bucket size. One token in the bucket means
one packet (1 kpkts * millisecond) which will drop or pass by policer.

This patch add new configuration option 'kpkts_rate' and 'kpkts_burst' for
egress-policer QoS type which now supports setting packet-per-second limits in
addition to the previously configurable byte rate settings.

Examples:
$ovs-vsctl set port vhost-user0 qos=@newqos --
           --id=@newqos create qos type=egress-policer \
           other-config:cir=123000 other-config:cbs=123000
           other-config:kpkts_rate=123 other-config:kpkts_burst=123

Add some unit tests for egress packet-per-second policing.

Signed-off-by: Lin Huang <linhuang@ruijie.com.cn>
---
 NEWS                 |   7 +++
 lib/netdev-dpdk.c    |  90 +++++++++++++++++++++++++----
 tests/system-dpdk.at | 134 +++++++++++++++++++++++++++++++++++++++++++
 vswitchd/vswitch.xml |  10 ++++
 4 files changed, 229 insertions(+), 12 deletions(-)

Comments

Simon Horman April 20, 2023, 8:52 a.m. UTC | #1
Hi Lin Huang,

thanks for your patch.

On Sun, Apr 09, 2023 at 07:15:08PM +0800, miterv@outlook.com wrote:
> From: Lin Huang <linhuang@ruijie.com.cn>
> 
> OvS has supported packet-per-second policer which can be set at ingress and egress
> side in kernel datapath. But the userspace datapath dosen't support for ingress and
> egress packet-per-second policing now.
> 
> So, this patch add support for userspace egress pps policing by using native ovs
> token bucket library. Token bucket is accumulated by 'rate' tokens per millisecond
> and store maxiumim tokens at 'burst' bucket size. One token in the bucket means
> one packet (1 kpkts * millisecond) which will drop or pass by policer.
> 
> This patch add new configuration option 'kpkts_rate' and 'kpkts_burst' for
> egress-policer QoS type which now supports setting packet-per-second limits in
> addition to the previously configurable byte rate settings.

nit: Please keep the width of the patch description narrower
(I think 76 characters wide or less).

> Examples:
> $ovs-vsctl set port vhost-user0 qos=@newqos --
>            --id=@newqos create qos type=egress-policer \
>            other-config:cir=123000 other-config:cbs=123000
>            other-config:kpkts_rate=123 other-config:kpkts_burst=123
> 
> Add some unit tests for egress packet-per-second policing.

...

> @@ -4757,6 +4787,10 @@ netdev_dpdk_queue_dump_done(const struct netdev *netdev OVS_UNUSED,
>  
>  struct egress_policer {
>      struct qos_conf qos_conf;
> +    enum policer_type type;
> +    uint32_t kpkts_rate;
> +    uint32_t kpkts_burst;
> +    struct token_bucket egress_tb;
>      struct rte_meter_srtcm_params app_srtcm_params;
>      struct rte_meter_srtcm egress_meter;
>      struct rte_meter_srtcm_profile egress_prof;
> @@ -4776,10 +4810,11 @@ static int
>  egress_policer_qos_construct(const struct smap *details,
>                               struct qos_conf **conf)
>  {
> +    uint32_t kpkts_burst, kpkts_rate;
>      struct egress_policer *policer;
>      int err = 0;
>  
> -    policer = xmalloc(sizeof *policer);
> +    policer = xcalloc(1, sizeof *policer);

I'm not sure of the motivation for this change.
But if it is to zero the allocation then perhaps xzalloc() is more appropriate.

>      qos_conf_init(&policer->qos_conf, &egress_policer_ops);
>      egress_policer_details_to_param(details, &policer->app_srtcm_params);
>      err = rte_meter_srtcm_profile_config(&policer->egress_prof,
> @@ -4787,15 +4822,32 @@ egress_policer_qos_construct(const struct smap *details,
>      if (!err) {
>          err = rte_meter_srtcm_config(&policer->egress_meter,
>                                       &policer->egress_prof);
> +        if (!err) {
> +            policer->type |= POLICER_BPS;
> +        } else {
> +            VLOG_ERR("Could not create rte meter for egress policer");

I have a few questions about this:

1. Prior to this patch the error message above was logged if
   either call to rte_meter_srtcm_config failed. Now it
   is only logged if the first call succeeds and the second
   one fails. Is that intended?

2. Prior to this patch the err, returned by a failed called to
   rte_meter_srtcm_config was returned. Now EINVAL is returned
   if either call to rte_meter_srtcm_config() fails, is that intended?

> +        }
>      }
>  
> -    if (!err) {
> -        *conf = &policer->qos_conf;
> -    } else {
> -        VLOG_ERR("Could not create rte meter for egress policer");
> +    kpkts_rate  = smap_get_uint(details, "kpkts_rate", 0);
> +    kpkts_burst = smap_get_uint(details, "kpkts_burst", 0);
> +    if (kpkts_rate && kpkts_burst) {

3. Here we configure pps if both parameters are non zero.
   But above bps is configured (using rte_meter_srtcm_config())
   unconditionally. This asymmetry strikes me as odd.

> +        /* Rate in kilo-packets/second, bucket 1000 packets. */
> +        /* msec * kilo-packets/sec = 1 packets. */
> +        token_bucket_set(&policer->egress_tb, kpkts_rate, kpkts_burst * 1000);
> +        policer->kpkts_burst = kpkts_burst;
> +        policer->kpkts_rate = kpkts_rate;
> +        policer->type |= POLICER_PKTPS;
> +    }
> +
> +    if (!policer->type) {
> +        /* both bps and kpkts contrsruct failed.*/

4. If a) BPS configuration fails and b) PPS is not configured
   then this condition is reached, which is treated as an error.
   But is b) an error? And if b) is true does it make a) not an error?

   It seems to me that there are four non error states.

   a) Neither BPS nor PPS are configured
   b) Only BPS is configured (we detect errors instantiating it)
   c) Only PPS is configured.
   d) Both BPS and PPS are configured.

   I assume that egress_policer_qos_construct is called for b, c and d.
   But is it also called for a?

   How do we detect the absence of BPS vs errors configuring BPS?
   Likewise, how do we detect errors configuring PPS, or is that
   not a concern?

>          free(policer);
>          *conf = NULL;
> -        err = -err;
> +        err = EINVAL;
> +    } else {
> +        err = 0;
> +        *conf = &policer->qos_conf;
>      }

...

> diff --git a/vswitchd/vswitch.xml b/vswitchd/vswitch.xml
> index edb5eafa0..93c387e2a 100644
> --- a/vswitchd/vswitch.xml
> +++ b/vswitchd/vswitch.xml
> @@ -4894,6 +4894,16 @@ ovs-vsctl add-port br0 p0 -- set Interface p0 type=patch options:peer=p1 \
>          bytes/tokens of the packet. If there are not enough tokens in the cbs
>          bucket the packet might be dropped.
>        </column>
> +      <column name="other_config" key="kpkts_rate"
> +              type='{"type": "integer", "minInteger": 0, "maxInteger": 4294967}'>
> +        The Packets Per Second (pps) represents the packet per second rate at

nit: s/pps/PPS/

> +        which the token bucket will be updated.
> +      </column>
> +      <column name="other_config" key="kpkts_burst"
> +              type='{"type": "integer", "minInteger": 0, "maxInteger": 4294967}'>
> +        The Packets Per Second Burst Size is measured in count and represents a
> +        token bucket.
> +      </column>
>      </group>
>  
>      <group title="Configuration for linux-sfq">
> -- 
> 2.31.1
>
miter April 30, 2023, 2:58 p.m. UTC | #2
Hi Simon,

Thanks for your reviewing.

Sorry for the late reply.

I will update a new patch after some modification.

On 4/20/2023 4:52 PM, Simon Horman wrote:
> Hi Lin Huang,
>
> thanks for your patch.
>
> On Sun, Apr 09, 2023 at 07:15:08PM +0800,miterv@outlook.com  wrote:
>> From: Lin Huang<linhuang@ruijie.com.cn>
>>
>> OvS has supported packet-per-second policer which can be set at ingress and egress
>> side in kernel datapath. But the userspace datapath dosen't support for ingress and
>> egress packet-per-second policing now.
>>
>> So, this patch add support for userspace egress pps policing by using native ovs
>> token bucket library. Token bucket is accumulated by 'rate' tokens per millisecond
>> and store maxiumim tokens at 'burst' bucket size. One token in the bucket means
>> one packet (1 kpkts * millisecond) which will drop or pass by policer.
>>
>> This patch add new configuration option 'kpkts_rate' and 'kpkts_burst' for
>> egress-policer QoS type which now supports setting packet-per-second limits in
>> addition to the previously configurable byte rate settings.
> nit: Please keep the width of the patch description narrower
> (I think 76 characters wide or less).
Applied, thanks.
>> Examples:
>> $ovs-vsctl set port vhost-user0 qos=@newqos --
>>             --id=@newqos create qos type=egress-policer \
>>             other-config:cir=123000 other-config:cbs=123000
>>             other-config:kpkts_rate=123 other-config:kpkts_burst=123
>>
>> Add some unit tests for egress packet-per-second policing.
> ...
>
>> @@ -4757,6 +4787,10 @@ netdev_dpdk_queue_dump_done(const struct netdev *netdev OVS_UNUSED,
>>   
>>   struct egress_policer {
>>       struct qos_conf qos_conf;
>> +    enum policer_type type;
>> +    uint32_t kpkts_rate;
>> +    uint32_t kpkts_burst;
>> +    struct token_bucket egress_tb;
>>       struct rte_meter_srtcm_params app_srtcm_params;
>>       struct rte_meter_srtcm egress_meter;
>>       struct rte_meter_srtcm_profile egress_prof;
>> @@ -4776,10 +4810,11 @@ static int
>>   egress_policer_qos_construct(const struct smap *details,
>>                                struct qos_conf **conf)
>>   {
>> +    uint32_t kpkts_burst, kpkts_rate;
>>       struct egress_policer *policer;
>>       int err = 0;
>>   
>> -    policer = xmalloc(sizeof *policer);
>> +    policer = xcalloc(1, sizeof *policer);
> I'm not sure of the motivation for this change.
> But if it is to zero the allocation then perhaps xzalloc() is more appropriate.
Applied, thanks.
>>       qos_conf_init(&policer->qos_conf, &egress_policer_ops);
>>       egress_policer_details_to_param(details, &policer->app_srtcm_params);
>>       err = rte_meter_srtcm_profile_config(&policer->egress_prof,
>> @@ -4787,15 +4822,32 @@ egress_policer_qos_construct(const struct smap *details,
>>       if (!err) {
>>           err = rte_meter_srtcm_config(&policer->egress_meter,
>>                                        &policer->egress_prof);
>> +        if (!err) {
>> +            policer->type |= POLICER_BPS;
>> +        } else {
>> +            VLOG_ERR("Could not create rte meter for egress policer");
> I have a few questions about this:
>
> 1. Prior to this patch the error message above was logged if
>     either call to rte_meter_srtcm_config failed. Now it
>     is only logged if the first call succeeds and the second
>     one fails. Is that intended?
>
> 2. Prior to this patch the err, returned by a failed called to
>     rte_meter_srtcm_config was returned. Now EINVAL is returned
>     if either call to rte_meter_srtcm_config() fails, is that intended?

Emmm. It seems to be a problem.

>> +        }
>>       }
>>   
>> -    if (!err) {
>> -        *conf = &policer->qos_conf;
>> -    } else {
>> -        VLOG_ERR("Could not create rte meter for egress policer");
>> +    kpkts_rate  = smap_get_uint(details, "kpkts_rate", 0);
>> +    kpkts_burst = smap_get_uint(details, "kpkts_burst", 0);
>> +    if (kpkts_rate && kpkts_burst) {
> 3. Here we configure pps if both parameters are non zero.
>     But above bps is configured (using rte_meter_srtcm_config())
>     unconditionally. This asymmetry strikes me as odd.

I just want to keep the bps code it is.

If kpkts_rate is zero, there is no need to alloc and set configure to 
tocken bucket.

You are right. There are reasons to keep both of the same.

>> +        /* Rate in kilo-packets/second, bucket 1000 packets. */
>> +        /* msec * kilo-packets/sec = 1 packets. */
>> +        token_bucket_set(&policer->egress_tb, kpkts_rate, kpkts_burst * 1000);
>> +        policer->kpkts_burst = kpkts_burst;
>> +        policer->kpkts_rate = kpkts_rate;
>> +        policer->type |= POLICER_PKTPS;
>> +    }
>> +
>> +    if (!policer->type) {
>> +        /* both bps and kpkts contrsruct failed.*/
> 4. If a) BPS configuration fails and b) PPS is not configured
>     then this condition is reached, which is treated as an error.
>     But is b) an error? And if b) is true does it make a) not an error?
Token_bucket_setket() is a process never ruten error code.
>     It seems to me that there are four non error states.
>
>     a) Neither BPS nor PPS are configured
>     b) Only BPS is configured (we detect errors instantiating it)
>     c) Only PPS is configured.
>     d) Both BPS and PPS are configured.
>
>     I assume that egress_policer_qos_construct is called for b, c and d.
>     But is it also called for a?

egress_policer_qos_construct() called when BPS and PPS parameter are 
different to the old one.

So, when neither BPS nor PPS are configured, 
egress_policer_qos_construct() will not be called.

>     How do we detect the absence of BPS vs errors configuring BPS?
>     Likewise, how do we detect errors configuring PPS, or is that
>     not a concern?

It's a not a concen.

Setting parameters to PPS policer will never return a error code, unless 
policer pointer is NULL.

So we only need to check the bps error.

>>           free(policer);
>>           *conf = NULL;
>> -        err = -err;
>> +        err = EINVAL;
>> +    } else {
>> +        err = 0;
>> +        *conf = &policer->qos_conf;
>>       }
> ...
>
>> diff --git a/vswitchd/vswitch.xml b/vswitchd/vswitch.xml
>> index edb5eafa0..93c387e2a 100644
>> --- a/vswitchd/vswitch.xml
>> +++ b/vswitchd/vswitch.xml
>> @@ -4894,6 +4894,16 @@ ovs-vsctl add-port br0 p0 -- set Interface p0 type=patch options:peer=p1 \
>>           bytes/tokens of the packet. If there are not enough tokens in the cbs
>>           bucket the packet might be dropped.
>>         </column>
>> +      <column name="other_config" key="kpkts_rate"
>> +              type='{"type": "integer", "minInteger": 0, "maxInteger": 4294967}'>
>> +        The Packets Per Second (pps) represents the packet per second rate at
> nit: s/pps/PPS/
Applied, thanks.
>> +        which the token bucket will be updated.
>> +      </column>
>> +      <column name="other_config" key="kpkts_burst"
>> +              type='{"type": "integer", "minInteger": 0, "maxInteger": 4294967}'>
>> +        The Packets Per Second Burst Size is measured in count and represents a
>> +        token bucket.
>> +      </column>
>>       </group>
>>   
>>       <group title="Configuration for linux-sfq">
>> -- 
>> 2.31.1
>>
diff mbox series

Patch

diff --git a/NEWS b/NEWS
index b6418c36e..c41c6adf6 100644
--- a/NEWS
+++ b/NEWS
@@ -23,6 +23,13 @@  Post-v3.1.0
        process extra privileges when mapping physical interconnect memory.
    - SRv6 Tunnel Protocol
      * Added support for userspace datapath (only).
+   - Userspace datapath:
+     * Added new configuration options 'kpkts_rate' and 'kpkts_burst' for
+      'egress-policer' to support packet-per-second policing.
+       Examples:
+       ovs-vsctl set port vhost-user0 qos=@newqos -- \
+          --id=@newqos create qos type=egress-policer \
+          other-config:kpkts_rate=123 other-config:kpkts_burst=123
 
 
 v3.1.0 - 16 Feb 2023
diff --git a/lib/netdev-dpdk.c b/lib/netdev-dpdk.c
index fb0dd43f7..2fd8bc68d 100644
--- a/lib/netdev-dpdk.c
+++ b/lib/netdev-dpdk.c
@@ -19,6 +19,7 @@ 
 
 #include <errno.h>
 #include <signal.h>
+#include <stdint.h>
 #include <stdlib.h>
 #include <string.h>
 #include <unistd.h>
@@ -59,6 +60,7 @@ 
 #include "openvswitch/ofp-parse.h"
 #include "openvswitch/ofp-print.h"
 #include "openvswitch/shash.h"
+#include "openvswitch/token-bucket.h"
 #include "openvswitch/vlog.h"
 #include "ovs-numa.h"
 #include "ovs-rcu.h"
@@ -400,6 +402,11 @@  struct dpdk_tx_queue {
     );
 };
 
+enum policer_type {
+    POLICER_BPS   = 1 << 0,   /* Rate value in bytes/sec. */
+    POLICER_PKTPS = 1 << 1,   /* Rate value in packet/sec. */
+};
+
 struct ingress_policer {
     struct rte_meter_srtcm_params app_srtcm_params;
     struct rte_meter_srtcm in_policer;
@@ -2335,6 +2342,29 @@  srtcm_policer_run_single_packet(struct rte_meter_srtcm *meter,
     return cnt;
 }
 
+static int
+pkts_policer_run_single_packet(struct token_bucket *tb, struct rte_mbuf **pkts,
+                               int pkt_cnt, bool should_steal)
+{
+    struct rte_mbuf *pkt;
+    int cnt = 0;
+
+    for (int i = 0; i < pkt_cnt; i++) {
+        pkt = pkts[i];
+        /* Handle current packet. */
+        if (token_bucket_withdraw(tb, 1)) {
+            if (cnt != i) {
+                pkts[cnt] = pkt;
+            }
+            cnt++;
+        } else if (should_steal) {
+            rte_pktmbuf_free(pkt);
+        }
+    }
+
+    return cnt;
+}
+
 static int
 ingress_policer_run(struct ingress_policer *policer, struct rte_mbuf **pkts,
                     int pkt_cnt, bool should_steal)
@@ -4757,6 +4787,10 @@  netdev_dpdk_queue_dump_done(const struct netdev *netdev OVS_UNUSED,
 
 struct egress_policer {
     struct qos_conf qos_conf;
+    enum policer_type type;
+    uint32_t kpkts_rate;
+    uint32_t kpkts_burst;
+    struct token_bucket egress_tb;
     struct rte_meter_srtcm_params app_srtcm_params;
     struct rte_meter_srtcm egress_meter;
     struct rte_meter_srtcm_profile egress_prof;
@@ -4776,10 +4810,11 @@  static int
 egress_policer_qos_construct(const struct smap *details,
                              struct qos_conf **conf)
 {
+    uint32_t kpkts_burst, kpkts_rate;
     struct egress_policer *policer;
     int err = 0;
 
-    policer = xmalloc(sizeof *policer);
+    policer = xcalloc(1, sizeof *policer);
     qos_conf_init(&policer->qos_conf, &egress_policer_ops);
     egress_policer_details_to_param(details, &policer->app_srtcm_params);
     err = rte_meter_srtcm_profile_config(&policer->egress_prof,
@@ -4787,15 +4822,32 @@  egress_policer_qos_construct(const struct smap *details,
     if (!err) {
         err = rte_meter_srtcm_config(&policer->egress_meter,
                                      &policer->egress_prof);
+        if (!err) {
+            policer->type |= POLICER_BPS;
+        } else {
+            VLOG_ERR("Could not create rte meter for egress policer");
+        }
     }
 
-    if (!err) {
-        *conf = &policer->qos_conf;
-    } else {
-        VLOG_ERR("Could not create rte meter for egress policer");
+    kpkts_rate  = smap_get_uint(details, "kpkts_rate", 0);
+    kpkts_burst = smap_get_uint(details, "kpkts_burst", 0);
+    if (kpkts_rate && kpkts_burst) {
+        /* Rate in kilo-packets/second, bucket 1000 packets. */
+        /* msec * kilo-packets/sec = 1 packets. */
+        token_bucket_set(&policer->egress_tb, kpkts_rate, kpkts_burst * 1000);
+        policer->kpkts_burst = kpkts_burst;
+        policer->kpkts_rate = kpkts_rate;
+        policer->type |= POLICER_PKTPS;
+    }
+
+    if (!policer->type) {
+        /* both bps and kpkts contrsruct failed.*/
         free(policer);
         *conf = NULL;
-        err = -err;
+        err = EINVAL;
+    } else {
+        err = 0;
+        *conf = &policer->qos_conf;
     }
 
     return err;
@@ -4817,6 +4869,8 @@  egress_policer_qos_get(const struct qos_conf *conf, struct smap *details)
 
     smap_add_format(details, "cir", "%"PRIu64, policer->app_srtcm_params.cir);
     smap_add_format(details, "cbs", "%"PRIu64, policer->app_srtcm_params.cbs);
+    smap_add_format(details, "kpkts_rate", "%"PRIu32, policer->kpkts_rate);
+    smap_add_format(details, "kpkts_burst", "%"PRIu32, policer->kpkts_burst);
 
     return 0;
 }
@@ -4828,25 +4882,37 @@  egress_policer_qos_is_equal(const struct qos_conf *conf,
     struct egress_policer *policer =
         CONTAINER_OF(conf, struct egress_policer, qos_conf);
     struct rte_meter_srtcm_params params;
+    uint32_t kpkts_burst, kpkts_rate;
 
     egress_policer_details_to_param(details, &params);
 
-    return !memcmp(&params, &policer->app_srtcm_params, sizeof params);
+    kpkts_rate  = smap_get_uint(details, "kpkts_rate", 0);
+    kpkts_burst = smap_get_uint(details, "kpkts_burst", 0);
+
+    return (!memcmp(&params, &policer->app_srtcm_params, sizeof params))
+            && (policer->kpkts_rate == kpkts_rate
+                && policer->kpkts_burst == kpkts_burst);
 }
 
 static int
 egress_policer_run(struct qos_conf *conf, struct rte_mbuf **pkts, int pkt_cnt,
                    bool should_steal)
 {
-    int cnt = 0;
     struct egress_policer *policer =
         CONTAINER_OF(conf, struct egress_policer, qos_conf);
 
-    cnt = srtcm_policer_run_single_packet(&policer->egress_meter,
-                                          &policer->egress_prof, pkts,
-                                          pkt_cnt, should_steal);
+    if (policer->type & POLICER_BPS) {
+        pkt_cnt = srtcm_policer_run_single_packet(&policer->egress_meter,
+                                                  &policer->egress_prof, pkts,
+                                                  pkt_cnt, should_steal);
+    }
 
-    return cnt;
+    if (policer->type & POLICER_PKTPS) {
+        pkt_cnt = pkts_policer_run_single_packet(&policer->egress_tb, pkts,
+                                                 pkt_cnt, should_steal);
+    }
+
+    return pkt_cnt;
 }
 
 static const struct dpdk_qos_ops egress_policer_ops = {
diff --git a/tests/system-dpdk.at b/tests/system-dpdk.at
index cb6c6d590..ab527d406 100644
--- a/tests/system-dpdk.at
+++ b/tests/system-dpdk.at
@@ -570,6 +570,140 @@  dnl --------------------------------------------------------------------------
 
 
 
+dnl --------------------------------------------------------------------------
+dnl QoS (kpkts) create delete phy port
+AT_SETUP([OVS-DPDK - QoS (kpkts) create delete phy port])
+AT_KEYWORDS([dpdk])
+
+OVS_DPDK_PRE_PHY_SKIP()
+OVS_DPDK_START()
+
+dnl Add userspace bridge and attach it to OVS and add egress policer
+AT_CHECK([ovs-vsctl add-br br10 -- set bridge br10 datapath_type=netdev])
+AT_CHECK([ovs-vsctl add-port br10 phy0 -- set Interface phy0 type=dpdk options:dpdk-devargs=$(cat PCI_ADDR)], [], [stdout], [stderr])
+OVS_WAIT_UNTIL([ovs-vsctl set port phy0 qos=@newqos -- --id=@newqos create qos type=egress-policer other-config:kpkts_rate=123 other-config:kpkts_burst=246])
+AT_CHECK([ovs-appctl -t ovs-vswitchd qos/show phy0], [], [stdout])
+sleep 2
+
+dnl Remove egress policer
+AT_CHECK([ovs-vsctl destroy QoS phy0 -- clear Port phy0 qos])
+
+dnl Check egress policer was removed correctly
+AT_CHECK([ovs-appctl -t ovs-vswitchd qos/show phy0], [], [stdout])
+AT_CHECK([grep -E 'QoS not configured on phy0' stdout], [], [stdout])
+
+dnl Clean up
+AT_CHECK([ovs-vsctl del-port br10 phy0], [], [stdout], [stderr])
+OVS_VSWITCHD_STOP("[SYSTEM_DPDK_ALLOWED_LOGS]")
+AT_CLEANUP
+dnl --------------------------------------------------------------------------
+
+
+
+dnl --------------------------------------------------------------------------
+dnl QoS (kpkts) create delete vport port
+AT_SETUP([OVS-DPDK - QoS (kpkts) create delete vport port])
+AT_KEYWORDS([dpdk])
+
+OVS_DPDK_PRE_CHECK()
+OVS_DPDK_START()
+
+dnl Add userspace bridge and attach it to OVS and add egress policer
+AT_CHECK([ovs-vsctl add-br br10 -- set bridge br10 datapath_type=netdev])
+AT_CHECK([ovs-vsctl add-port br10 dpdkvhostuserclient0 -- set Interface dpdkvhostuserclient0 type=dpdkvhostuserclient options:vhost-server-path=$OVS_RUNDIR/dpdkvhostclient0], [], [stdout], [stderr])
+OVS_WAIT_UNTIL([ovs-vsctl set port dpdkvhostuserclient0 qos=@newqos -- --id=@newqos create qos type=egress-policer other-config:kpkts_rate=123 \
+                  other-config:kpkts_burst=456])
+AT_CHECK([ovs-appctl -t ovs-vswitchd qos/show dpdkvhostuserclient0], [], [stdout])
+sleep 2
+
+dnl Parse log file
+AT_CHECK([grep "VHOST_CONFIG: ($OVS_RUNDIR/dpdkvhostclient0) vhost-user client: socket created" ovs-vswitchd.log], [], [stdout])
+AT_CHECK([grep "vHost User device 'dpdkvhostuserclient0' created in 'client' mode, using client socket" ovs-vswitchd.log], [], [stdout])
+AT_CHECK([grep "VHOST_CONFIG: ($OVS_RUNDIR/dpdkvhostclient0) reconnecting..." ovs-vswitchd.log], [], [stdout])
+
+dnl Remove egress policer
+AT_CHECK([ovs-vsctl destroy QoS dpdkvhostuserclient0 -- clear Port dpdkvhostuserclient0 qos])
+
+dnl Check egress policer was removed correctly
+AT_CHECK([ovs-appctl -t ovs-vswitchd qos/show dpdkvhostuserclient0], [], [stdout])
+AT_CHECK([grep -E 'QoS not configured on dpdkvhostuserclient0' stdout], [], [stdout])
+
+dnl Clean up
+AT_CHECK([ovs-vsctl del-port br10 dpdkvhostuserclient0], [], [stdout], [stderr])
+OVS_VSWITCHD_STOP("m4_join([], [SYSTEM_DPDK_ALLOWED_LOGS], [
+\@VHOST_CONFIG: ($OVS_RUNDIR/dpdkvhostclient0) failed to connect: No such file or directory@d
+\@Failed to set QoS type egress-policer on port dpdkvhostuserclient0@d
+])")
+AT_CLEANUP
+dnl --------------------------------------------------------------------------
+
+
+
+dnl --------------------------------------------------------------------------
+dnl QoS (kpkts) no rate
+AT_SETUP([OVS-DPDK - QoS (kpkts) no rate])
+AT_KEYWORDS([dpdk])
+
+OVS_DPDK_PRE_CHECK()
+OVS_DPDK_START()
+
+dnl Add userspace bridge and attach it to OVS and add egress policer
+AT_CHECK([ovs-vsctl add-br br10 -- set bridge br10 datapath_type=netdev])
+AT_CHECK([ovs-vsctl add-port br10 dpdkvhostuserclient0 -- set Interface dpdkvhostuserclient0 type=dpdkvhostuserclient options:vhost-server-path=$OVS_RUNDIR/dpdkvhostclient0], [], [stdout], [stderr])
+OVS_WAIT_UNTIL([ovs-vsctl set port dpdkvhostuserclient0 qos=@newqos -- --id=@newqos create qos type=egress-policer other-config:kpkts_burst=123])
+sleep 2
+
+dnl Parse log file
+AT_CHECK([grep "VHOST_CONFIG: ($OVS_RUNDIR/dpdkvhostclient0) vhost-user client: socket created" ovs-vswitchd.log], [], [stdout])
+AT_CHECK([grep "vHost User device 'dpdkvhostuserclient0' created in 'client' mode, using client socket" ovs-vswitchd.log], [], [stdout])
+AT_CHECK([grep "VHOST_CONFIG: ($OVS_RUNDIR/dpdkvhostclient0) reconnecting..." ovs-vswitchd.log], [], [stdout])
+
+dnl Check egress policer was not created
+AT_CHECK([ovs-appctl -t ovs-vswitchd qos/show dpdkvhostuserclient0], [], [stdout])
+AT_CHECK([grep -E 'QoS not configured on dpdkvhostuserclient0' stdout], [], [stdout])
+
+dnl Clean up
+AT_CHECK([ovs-vsctl del-port br10 dpdkvhostuserclient0], [], [stdout], [stderr])
+OVS_VSWITCHD_STOP("m4_join([], [SYSTEM_DPDK_ALLOWED_LOGS], [
+\@VHOST_CONFIG: ($OVS_RUNDIR/dpdkvhostclient0) failed to connect: No such file or directory@d
+\@Could not create rte meter for egress policer@d
+\@Failed to set QoS type egress-policer on port dpdkvhostuserclient0@d
+])")
+AT_CLEANUP
+dnl --------------------------------------------------------------------------
+
+
+
+dnl --------------------------------------------------------------------------
+dnl QoS (kpkts) no burst
+AT_SETUP([OVS-DPDK - QoS (kpkts) no burst])
+AT_KEYWORDS([dpdk])
+
+OVS_DPDK_PRE_CHECK()
+OVS_DPDK_START()
+
+dnl Add userspace bridge and attach it to OVS and add egress policer
+AT_CHECK([ovs-vsctl add-br br10 -- set bridge br10 datapath_type=netdev])
+AT_CHECK([ovs-vsctl add-port br10 dpdkvhostuserclient0 -- set Interface dpdkvhostuserclient0 type=dpdkvhostuserclient options:vhost-server-path=$OVS_RUNDIR/dpdkvhostclient0], [], [stdout], [stderr])
+OVS_WAIT_UNTIL([ovs-vsctl set port dpdkvhostuserclient0 qos=@newqos -- --id=@newqos create qos type=egress-policer other-config:kpkts_rate=123])
+sleep 2
+
+dnl Parse log file
+AT_CHECK([grep "VHOST_CONFIG: ($OVS_RUNDIR/dpdkvhostclient0) vhost-user client: socket created" ovs-vswitchd.log], [], [stdout])
+AT_CHECK([grep "vHost User device 'dpdkvhostuserclient0' created in 'client' mode, using client socket" ovs-vswitchd.log], [], [stdout])
+AT_CHECK([grep "VHOST_CONFIG: ($OVS_RUNDIR/dpdkvhostclient0) reconnecting..." ovs-vswitchd.log], [], [stdout])
+
+dnl Clean up
+AT_CHECK([ovs-vsctl del-port br10 dpdkvhostuserclient0], [], [stdout], [stderr])
+OVS_VSWITCHD_STOP("m4_join([], [SYSTEM_DPDK_ALLOWED_LOGS], [
+\@VHOST_CONFIG: ($OVS_RUNDIR/dpdkvhostclient0) failed to connect: No such file or directory@d
+\@Could not create rte meter for egress policer@d
+\@Failed to set QoS type egress-policer on port dpdkvhostuserclient0@d
+])")
+AT_CLEANUP
+dnl --------------------------------------------------------------------------
+
+
 
 dnl --------------------------------------------------------------------------
 dnl MTU increase phy port
diff --git a/vswitchd/vswitch.xml b/vswitchd/vswitch.xml
index edb5eafa0..93c387e2a 100644
--- a/vswitchd/vswitch.xml
+++ b/vswitchd/vswitch.xml
@@ -4894,6 +4894,16 @@  ovs-vsctl add-port br0 p0 -- set Interface p0 type=patch options:peer=p1 \
         bytes/tokens of the packet. If there are not enough tokens in the cbs
         bucket the packet might be dropped.
       </column>
+      <column name="other_config" key="kpkts_rate"
+              type='{"type": "integer", "minInteger": 0, "maxInteger": 4294967}'>
+        The Packets Per Second (pps) represents the packet per second rate at
+        which the token bucket will be updated.
+      </column>
+      <column name="other_config" key="kpkts_burst"
+              type='{"type": "integer", "minInteger": 0, "maxInteger": 4294967}'>
+        The Packets Per Second Burst Size is measured in count and represents a
+        token bucket.
+      </column>
     </group>
 
     <group title="Configuration for linux-sfq">