diff mbox series

[ovs-dev,v7,3/4] netdev-dpdk: Add support for egress packet-per-second policing.

Message ID SY4PR01MB8438A3F17ABF417EC30E6663CD07A@SY4PR01MB8438.ausprd01.prod.outlook.com
State Superseded
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 fail github build: failed

Commit Message

miter July 29, 2023, 2:49 p.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 doesn'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 maximum 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 a new egress Qos type called 'kpkts-policer'.
the policer police the kilo-packet per second at which the token bucket
be updated by 'kpkts_rate'. and the policer's burst size is defined by
'kpkts_burst'.

Examples:
$ovs-vsctl set port vhost-user0 qos=@newqos --
           --id=@newqos create qos type=kpkts-policer \
           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>
---
 Documentation/topics/dpdk/qos.rst |  21 +++
 NEWS                              |   1 +
 lib/netdev-dpdk.c                 | 159 +++++++++++++++++++
 tests/system-dpdk.at              | 255 ++++++++++++++++++++++++++++++
 vswitchd/vswitch.xml              |  32 ++++
 5 files changed, 468 insertions(+)

Comments

Aaron Conole July 31, 2023, 8:10 p.m. UTC | #1
miterv@outlook.com writes:

> 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 doesn'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 maximum 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 a new egress Qos type called 'kpkts-policer'.
> the policer police the kilo-packet per second at which the token bucket
> be updated by 'kpkts_rate'. and the policer's burst size is defined by
> 'kpkts_burst'.
>
> Examples:
> $ovs-vsctl set port vhost-user0 qos=@newqos --
>            --id=@newqos create qos type=kpkts-policer \
>            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>
> ---
>  Documentation/topics/dpdk/qos.rst |  21 +++
>  NEWS                              |   1 +
>  lib/netdev-dpdk.c                 | 159 +++++++++++++++++++
>  tests/system-dpdk.at              | 255 ++++++++++++++++++++++++++++++
>  vswitchd/vswitch.xml              |  32 ++++
>  5 files changed, 468 insertions(+)
>
> diff --git a/Documentation/topics/dpdk/qos.rst b/Documentation/topics/dpdk/qos.rst
> index a98ec672f..6a4408127 100644
> --- a/Documentation/topics/dpdk/qos.rst
> +++ b/Documentation/topics/dpdk/qos.rst
> @@ -36,6 +36,9 @@ QoS (Egress Policing)
>  Single Queue Policer
>  ~~~~~~~~~~~~~~~~~~~~
>  
> +Bytes Per Second Policer
> ++++++++++++++++++++
> +
>  Assuming you have a :doc:`vhost-user port <vhost-user>` transmitting traffic
>  consisting of packets of size 64 bytes, the following command would limit the
>  egress transmission rate of the port to ~1,000,000 packets per second::
> @@ -52,6 +55,24 @@ To clear the QoS configuration from the port and ovsdb, run::
>  
>      $ ovs-vsctl destroy QoS vhost-user0 -- clear Port vhost-user0 qos
>  
> +Packets Per Second Policer
> ++++++++++++++++++++
> +
> +Assuming you have a :doc:`vhost-user port <vhost-user>` transmitting traffic,
> +the following command would limit the egress transmission rate of the port to
> +~1,000,000 packets per second::
> +
> +       ovs-vsctl set port vhost-user0 qos=@newqos -- \
> +          --id=@newqos create qos type=kpkts-policer \
> +          other-config:kpkts_rate=1000 other-config:kpkts_burst=1000
> +
> +To examine the QoS configuration of the port, run::
> +
> +    $ ovs-appctl -t ovs-vswitchd qos/show vhost-user0
> +
> +To clear the QoS configuration from the port and ovsdb, run::
> +
> +    $ ovs-vsctl destroy QoS vhost-user0 -- clear Port vhost-user0 qos
>  
>  Multi Queue Policer
>  ~~~~~~~~~~~~~~~~~~~
> diff --git a/NEWS b/NEWS
> index 7a852427e..3d1ab282e 100644
> --- a/NEWS
> +++ b/NEWS
> @@ -63,6 +63,7 @@ v3.2.0 - xx xxx xxxx
>       * 'ovs-appctl dpif-netdev/pmd-sleep-show' command was added to get the
>         max sleep configuration of PMD thread cores.
>       * Removed experimental tag from PMD load based sleeping.
> +     * Added new Qos type 'pkts-policer' to support kilo packet-per-second policing.
>     - Linux TC offload:
>       * Add support for offloading VXLAN tunnels with the GBP extensions.
>     - Python
> diff --git a/lib/netdev-dpdk.c b/lib/netdev-dpdk.c
> index 7e3020a7d..e6b8922aa 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"
> @@ -91,6 +93,8 @@ static bool per_port_memory = false; /* Status of per port memory support */
>  #define OVS_CACHE_LINE_SIZE CACHE_LINE_SIZE
>  #define OVS_VPORT_DPDK "ovs_dpdk"
>  
> +#define MAX_KPKTS_PARAMETER 4294967U  /* UINT32_MAX / 1000 */
> +
>  /*
>   * need to reserve tons of extra space in the mbufs so we can align the
>   * DMA addresses to 4KB.
> @@ -346,6 +350,7 @@ struct dpdk_qos_ops {
>  /* dpdk_qos_ops for each type of user space QoS implementation. */
>  static const struct dpdk_qos_ops egress_policer_ops;
>  static const struct dpdk_qos_ops trtcm_policer_ops;
> +static const struct dpdk_qos_ops kpkts_policer_ops;
>  
>  /*
>   * Array of dpdk_qos_ops, contains pointer to all supported QoS
> @@ -354,6 +359,7 @@ static const struct dpdk_qos_ops trtcm_policer_ops;
>  static const struct dpdk_qos_ops *const qos_confs[] = {
>      &egress_policer_ops,
>      &trtcm_policer_ops,
> +    &kpkts_policer_ops,
>      NULL
>  };
>  
> @@ -5572,6 +5578,159 @@ static const struct dpdk_qos_ops trtcm_policer_ops = {
>      .qos_queue_dump_state_init = trtcm_policer_qos_queue_dump_state_init
>  };
>  
> +/* kpkts-policer details */
> +struct kpkts_policer {
> +    struct qos_conf qos_conf;
> +    struct token_bucket tb;
> +    uint32_t kpkts_rate;
> +    uint32_t kpkts_burst;
> +};
> +
> +static int
> +kpkts_policer_run_single_packet(struct token_bucket *tb, struct rte_mbuf **pkts,
> +                                int pkt_cnt, bool should_steal)
> +{
> +    struct rte_mbuf *batch[NETDEV_MAX_BURST] = {0};
> +    long long int now = time_msec();
> +    struct rte_mbuf *pkt = NULL;
> +    int i = 0, n = 0;
> +    int cnt = 0;
> +
> +    for (i = 0; i < pkt_cnt; i++) {
> +        pkt = pkts[i];
> +        /* Handle current packet. */
> +        if (token_bucket_withdraw(tb, 1, now)) {
> +            /* Count passed packets. */
> +            if (cnt != i) {
> +                pkts[cnt] = pkt;
> +            }
> +            cnt++;
> +        } else {
> +            /* Count dropped packets. */
> +            batch[n++] = pkt;
> +        }
> +    }
> +
> +    if (should_steal && n) {
> +        rte_pktmbuf_free_bulk(batch, n);
> +    }
> +
> +    return cnt;
> +}
> +
> +static int
> +kpkts_policer_profile_config(struct token_bucket *tb,
> +                             uint32_t kpkts_rate, uint32_t kpkts_burst)
> +{
> +    if (kpkts_rate > MAX_KPKTS_PARAMETER ||
> +        kpkts_burst > MAX_KPKTS_PARAMETER) {
> +        return EINVAL;
> +    }
> +
> +   /* Rate in kilo-packets/second, bucket 1000 packets.
> +    * msec * kilo-packets/sec = 1 packets. */
> +    if (kpkts_rate) {
> +        /* Parameters between (1 ~ MAX_KPKTS_PARAMETER). */
> +        token_bucket_init(tb, kpkts_rate, kpkts_burst * 1000);
> +    } else {
> +        /* Zero means not to police the traffic. */
> +        return EINVAL;
> +    }
> +
> +    return 0;
> +}
> +
> +static int
> +kpkts_policer_qos_construct(const struct smap *details, struct qos_conf **conf)
> +{
> +    uint32_t kpkts_rate, kpkts_burst;
> +    struct kpkts_policer *policer;
> +    int err;
> +
> +    policer = xmalloc(sizeof *policer);
> +    kpkts_rate  = smap_get_uint(details, "kpkts_rate", 0);
> +    kpkts_burst = smap_get_uint(details, "kpkts_burst", 0);
> +
> +    /*
> +     * Force to 0 if no rate specified,
> +     * default to rate if burst is 0,
> +     * else stick with user-specified value.
> +     */
> +    kpkts_burst = (!kpkts_rate ? 0 : !kpkts_burst ? kpkts_rate : kpkts_burst);
> +
> +    qos_conf_init(&policer->qos_conf, &kpkts_policer_ops);
> +    err = kpkts_policer_profile_config(&policer->tb, kpkts_rate, kpkts_burst);
> +    if (err) {
> +        VLOG_DBG("Could not create token bucket for egress policer");

I didn't do an in-depth look at this yet, just noticed this needs free
here for policer object.

> +        return err;
> +    }
> +
> +    policer->kpkts_rate = kpkts_rate;
> +    policer->kpkts_burst = kpkts_burst;
> +
> +    *conf = &policer->qos_conf;
> +
> +    return err;
> +}
> +
> +static void
> +kpkts_policer_qos_destruct(struct qos_conf *conf)
> +{
> +    struct kpkts_policer *policer = CONTAINER_OF(conf, struct kpkts_policer,
> +                                                 qos_conf);
> +
> +    free(policer);
> +}
> +
> +static int
> +kpkts_policer_qos_get(const struct qos_conf *conf, struct smap *details)
> +{
> +    struct kpkts_policer *policer = CONTAINER_OF(conf, struct kpkts_policer,
> +                                                 qos_conf);
> +
> +    smap_add_format(details, "kpkts_rate", "%"PRIu32, policer->kpkts_rate);
> +    smap_add_format(details, "kpkts_burst", "%"PRIu32, policer->kpkts_burst);
> +
> +    return 0;
> +}
> +
> +static bool
> +kpkts_pkts_policer_qos_is_equal(const struct qos_conf *conf,
> +                                const struct smap *details)
> +{
> +    uint32_t rate, burst;
> +    struct kpkts_policer *policer = CONTAINER_OF(conf, struct kpkts_policer,
> +                                                 qos_conf);
> +
> +    rate  = smap_get_uint(details, "pkts_rate", 0);
> +    burst = smap_get_uint(details, "pkts_burst", 0);
> +
> +    return (policer->tb.rate == rate && policer->tb.burst == burst);
> +}
> +
> +static int
> +kpkts_policer_run(struct qos_conf *conf, struct rte_mbuf **pkts, int pkt_cnt,
> +                  bool should_steal)
> +{
> +    int cnt;
> +    struct kpkts_policer *policer = CONTAINER_OF(conf, struct kpkts_policer,
> +                                                 qos_conf);
> +
> +    cnt = kpkts_policer_run_single_packet(&policer->tb, pkts, pkt_cnt,
> +                                          should_steal);
> +
> +    return cnt;
> +}
> +
> +static const struct dpdk_qos_ops kpkts_policer_ops = {
> +    .qos_name = "kpkts-policer",
> +    .qos_construct = kpkts_policer_qos_construct,
> +    .qos_destruct = kpkts_policer_qos_destruct,
> +    .qos_get = kpkts_policer_qos_get,
> +    .qos_is_equal = kpkts_pkts_policer_qos_is_equal,
> +    .qos_run = kpkts_policer_run
> +};
> +
>  static int
>  dpdk_rx_steer_add_flow(struct netdev_dpdk *dev,
>                        const struct rte_flow_item items[],
> diff --git a/tests/system-dpdk.at b/tests/system-dpdk.at
> index 0f58e8574..8b80a31e6 100644
> --- a/tests/system-dpdk.at
> +++ b/tests/system-dpdk.at
> @@ -570,6 +570,261 @@ 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-appctl vlog/set netdev_dpdk:dbg])
> +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=kpkts-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 kpkts-policer on port dpdkvhostuserclient0: Invalid argument@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-appctl vlog/set netdev_dpdk:dbg])
> +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=kpkts-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
> +\@Failed to set QoS type kpkts-policer on port dpdkvhostuserclient0: Invalid argument@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-appctl vlog/set netdev_dpdk:dbg])
> +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=kpkts-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 Check egress policer was created
> +AT_CHECK([ovs-appctl -t ovs-vswitchd qos/show dpdkvhostuserclient0], [], [stdout])
> +AT_CHECK([grep -E 'kpkts_rate: 123' stdout], [], [stdout])
> +
> +dnl Check egress policer was deleted
> +QOS_UUID=`ovs-vsctl get port dpdkvhostuserclient0 qos`
> +AT_CHECK([ovs-vsctl set qos $QOS_UUID other_config:kpkts_rate=0])
> +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 kpkts-policer on port dpdkvhostuserclient0: Invalid argument@d
> +])")
> +AT_CLEANUP
> +dnl --------------------------------------------------------------------------
> +
> +
> +
> +dnl --------------------------------------------------------------------------
> +dnl QoS (kpkts) max rate
> +AT_SETUP([OVS-DPDK - QoS (kpkts) max 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-appctl vlog/set netdev_dpdk:dbg])
> +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=kpkts-policer other-config:kpkts_rate=42949671])
> +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
> +\@Failed to set QoS type kpkts-policer on port dpdkvhostuserclient0: Invalid argument@d
> +])")
> +AT_CLEANUP
> +dnl --------------------------------------------------------------------------
> +
> +
> +dnl --------------------------------------------------------------------------
> +dnl QoS (kpkts) max burst
> +AT_SETUP([OVS-DPDK - QoS (kpkts) max 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-appctl vlog/set netdev_dpdk:dbg])
> +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=kpkts-policer other-config:kpkts_burst=42949671])
> +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
> +\@Failed to set QoS type kpkts-policer on port dpdkvhostuserclient0: Invalid argument@d
> +])")
> +AT_CLEANUP
> +dnl --------------------------------------------------------------------------
> +
> +
> +dnl --------------------------------------------------------------------------
> +dnl QoS (kpkts) testpmd flowgen test
> +AT_SETUP([OVS-DPDK - QoS (kpkts) police])
> +AT_KEYWORDS([dpdk])
> +
> +OVS_DPDK_PRE_CHECK()
> +AT_SKIP_IF([! which dpdk-testpmd >/dev/null 2>/dev/null])
> +OVS_DPDK_START([--no-pci])
> +
> +dnl Find number of sockets
> +AT_CHECK([lscpu], [], [stdout])
> +AT_CHECK([cat stdout | grep "NUMA node(s)" | awk '{c=1; while (c++<$(3)) {printf "512,"}; print "512"}' > NUMA_NODE])
> +
> +dnl Add userspace bridge and attach it to OVS
> +AT_CHECK([ovs-vsctl add-br br10 -- set bridge br10 datapath_type=netdev])
> +
> +dnl Parse log file
> +AT_CHECK([ovs-vsctl add-port br10 dpdkvhostuser0 -- set Interface dpdkvhostuser0 type=dpdkvhostuser], [], [stdout], [stderr])
> +AT_CHECK([ovs-vsctl add-port br10 dpdkvhostuser1 -- set Interface dpdkvhostuser1 type=dpdkvhostuser], [], [stdout], [stderr])
> +AT_CHECK([ovs-vsctl show], [], [stdout])
> +
> +dnl Parse log file
> +AT_CHECK([grep "VHOST_CONFIG: ($OVS_RUNDIR/dpdkvhostuser0) vhost-user server: socket created" ovs-vswitchd.log], [], [stdout])
> +AT_CHECK([grep "Socket $OVS_RUNDIR/dpdkvhostuser0 created for vhost-user port dpdkvhostuser0" ovs-vswitchd.log], [], [stdout])
> +AT_CHECK([grep "VHOST_CONFIG: ($OVS_RUNDIR/dpdkvhostuser0) binding succeeded" ovs-vswitchd.log], [], [stdout])
> +
> +dnl Parse log file
> +AT_CHECK([grep "VHOST_CONFIG: ($OVS_RUNDIR/dpdkvhostuser1) vhost-user server: socket created" ovs-vswitchd.log], [], [stdout])
> +AT_CHECK([grep "Socket $OVS_RUNDIR/dpdkvhostuser1 created for vhost-user port dpdkvhostuser1" ovs-vswitchd.log], [], [stdout])
> +AT_CHECK([grep "VHOST_CONFIG: ($OVS_RUNDIR/dpdkvhostuser1) binding succeeded" ovs-vswitchd.log], [], [stdout])
> +
> +dnl Configure the same QoS for both ports.
> +AT_CHECK([ovs-vsctl set port dpdkvhostuser0 qos=@newqos -- --id=@newqos create qos type=kpkts-policer other-config:kpkts_rate=1 other-config:kpkts_burst=1], [0], [ignore])
> +
> +dnl add flows, only send packets from dpdkvhostuser1 to dpdkvhostuser0.
> +AT_DATA([flows.txt], [dnl
> +priority=100,in_port=dpdkvhostuser1,ip,actions=dpdkvhostuser0
> +])
> +
> +AT_CHECK([ovs-ofctl del-flows br10])
> +AT_CHECK([ovs-ofctl add-flows br10 flows.txt])
> +
> +dnl Execute testpmd in background
> +on_exit "pkill -f -x -9 'tail -f /dev/null'"
> +tail -f /dev/null | dpdk-testpmd --socket-mem="$(cat NUMA_NODE)" --no-pci\
> +           --vdev="net_virtio_user0,path=$OVS_RUNDIR/dpdkvhostuser0" \
> +           --vdev="net_virtio_user1,path=$OVS_RUNDIR/dpdkvhostuser1" \
> +           --single-file-segments -- --forward-mode=flowgen -a > $OVS_RUNDIR/testpmd-dpdkvhostuser.log 2>&1 &
> +
> +dnl sent packet 10 second.
> +AT_CHECK([sleep 10])
> +
> +dnl Clean up the testpmd now
> +pkill -f -x -9 'tail -f /dev/null'
> +
> +dnl ---------------------- Forward statistics for port 0  ----------------------
> +dnl RX-packets: 9911           RX-dropped: 0             RX-total: 9911
> +dnl TX-packets: 15937632       TX-dropped: 226661984     TX-total: 242599616
> +dnl ----------------------------------------------------------------------------
> +port0_rx_packets=`cat testpmd-dpdkvhostuser.log | grep "Forward statistics for port 0" -A 1 | grep "RX-packets:" | awk '{print $2}'`
> +echo "port0_rx_packets=$port0_rx_packets"
> +
> +AT_CHECK([test $port0_rx_packets -lt 10500])
> +AT_CHECK([test $port0_rx_packets -gt 9500])
> +
> +OVS_VSWITCHD_STOP("m4_join([], [SYSTEM_DPDK_ALLOWED_LOGS], [
> +\@dpdkvhostuser ports are considered deprecated;  please migrate to dpdkvhostuserclient ports.@d
> +])")
> +AT_CLEANUP
> +dnl --------------------------------------------------------------------------
> +
> +
>  
>  dnl --------------------------------------------------------------------------
>  dnl MTU increase phy port
> diff --git a/vswitchd/vswitch.xml b/vswitchd/vswitch.xml
> index cfcde34ff..19fc13873 100644
> --- a/vswitchd/vswitch.xml
> +++ b/vswitchd/vswitch.xml
> @@ -4874,6 +4874,19 @@ ovs-vsctl add-port br0 p0 -- set Interface p0 type=patch options:peer=p1 \
>            created with the same <code>other_config</code> values as the
>            physical port.
>          </dd>
> +        <dt><code>kpkts-policer</code></dt>
> +        <dd>
> +          A DPDK egress packet per second policer algorithm using the ovs
> +          token bucket library. The token bucket library provides an
> +          implementation which allows the policing of packets traffic. The
> +          implementation in OVS essentially creates a single token bucket used
> +          to police traffic. It should be noted that when the token bucket is
> +          configured as part of QoS there will be a performance overhead as the
> +          token bucket itself will consume CPU cycles in order to police
> +          traffic. These CPU cycles ordinarily are used for packet proccessing.
> +          As such the drop in performance will be noticed in terms of overall
> +          aggregate traffic throughput.
> +        </dd>
>        </dl>
>      </column>
>  
> @@ -4966,6 +4979,25 @@ ovs-vsctl add-port br0 p0 -- set Interface p0 type=patch options:peer=p1 \
>        </column>
>      </group>
>  
> +    <group title="Configuration for kpkts-policer QoS">
> +      <p>
> +        <ref table="QoS"/> <ref table="QoS" column="type"/>
> +        <code>kpkts-policer</code> provides egress pkts policing for userspace
> +        port types with DPDK.
> +
> +        It has the following key-value pairs defined.
> +      </p>
> +
> +      <column name="other_config" key="kpkts_rate" type='{"type": "integer"}'>
> +        The Kilo Packets Per Second (kpps) 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"}'>
> +        The Packets Per Second Burst Size is measured in count and represents a
> +        token bucket.
> +      </column>
> +    </group>
> +
>      <group title="Configuration for linux-sfq">
>        <p>
>          The <code>linux-sfq</code> QoS supports the following key-value pairs:
miter Aug. 1, 2023, 2:55 p.m. UTC | #2
Hi Aaron,

Thanks for reviewing my code.

I will update a new patch later.

On 8/1/2023 4:10 AM, Aaron Conole wrote:
> miterv@outlook.com writes:
>
>> 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 doesn'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 maximum 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 a new egress Qos type called 'kpkts-policer'.
>> the policer police the kilo-packet per second at which the token bucket
>> be updated by 'kpkts_rate'. and the policer's burst size is defined by
>> 'kpkts_burst'.
>>
>> Examples:
>> $ovs-vsctl set port vhost-user0 qos=@newqos --
>>             --id=@newqos create qos type=kpkts-policer \
>>             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>
>> ---
>>   Documentation/topics/dpdk/qos.rst |  21 +++
>>   NEWS                              |   1 +
>>   lib/netdev-dpdk.c                 | 159 +++++++++++++++++++
>>   tests/system-dpdk.at              | 255 ++++++++++++++++++++++++++++++
>>   vswitchd/vswitch.xml              |  32 ++++
>>   5 files changed, 468 insertions(+)
>>
>> diff --git a/Documentation/topics/dpdk/qos.rst b/Documentation/topics/dpdk/qos.rst
>> index a98ec672f..6a4408127 100644
>> --- a/Documentation/topics/dpdk/qos.rst
>> +++ b/Documentation/topics/dpdk/qos.rst
>> @@ -36,6 +36,9 @@ QoS (Egress Policing)
>>   Single Queue Policer
>>   ~~~~~~~~~~~~~~~~~~~~
>>   
>> +Bytes Per Second Policer
>> ++++++++++++++++++++
>> +
>>   Assuming you have a :doc:`vhost-user port <vhost-user>` transmitting traffic
>>   consisting of packets of size 64 bytes, the following command would limit the
>>   egress transmission rate of the port to ~1,000,000 packets per second::
>> @@ -52,6 +55,24 @@ To clear the QoS configuration from the port and ovsdb, run::
>>   
>>       $ ovs-vsctl destroy QoS vhost-user0 -- clear Port vhost-user0 qos
>>   
>> +Packets Per Second Policer
>> ++++++++++++++++++++
>> +
>> +Assuming you have a :doc:`vhost-user port <vhost-user>` transmitting traffic,
>> +the following command would limit the egress transmission rate of the port to
>> +~1,000,000 packets per second::
>> +
>> +       ovs-vsctl set port vhost-user0 qos=@newqos -- \
>> +          --id=@newqos create qos type=kpkts-policer \
>> +          other-config:kpkts_rate=1000 other-config:kpkts_burst=1000
>> +
>> +To examine the QoS configuration of the port, run::
>> +
>> +    $ ovs-appctl -t ovs-vswitchd qos/show vhost-user0
>> +
>> +To clear the QoS configuration from the port and ovsdb, run::
>> +
>> +    $ ovs-vsctl destroy QoS vhost-user0 -- clear Port vhost-user0 qos
>>   
>>   Multi Queue Policer
>>   ~~~~~~~~~~~~~~~~~~~
>> diff --git a/NEWS b/NEWS
>> index 7a852427e..3d1ab282e 100644
>> --- a/NEWS
>> +++ b/NEWS
>> @@ -63,6 +63,7 @@ v3.2.0 - xx xxx xxxx
>>        * 'ovs-appctl dpif-netdev/pmd-sleep-show' command was added to get the
>>          max sleep configuration of PMD thread cores.
>>        * Removed experimental tag from PMD load based sleeping.
>> +     * Added new Qos type 'pkts-policer' to support kilo packet-per-second policing.
>>      - Linux TC offload:
>>        * Add support for offloading VXLAN tunnels with the GBP extensions.
>>      - Python
>> diff --git a/lib/netdev-dpdk.c b/lib/netdev-dpdk.c
>> index 7e3020a7d..e6b8922aa 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"
>> @@ -91,6 +93,8 @@ static bool per_port_memory = false; /* Status of per port memory support */
>>   #define OVS_CACHE_LINE_SIZE CACHE_LINE_SIZE
>>   #define OVS_VPORT_DPDK "ovs_dpdk"
>>   
>> +#define MAX_KPKTS_PARAMETER 4294967U  /* UINT32_MAX / 1000 */
>> +
>>   /*
>>    * need to reserve tons of extra space in the mbufs so we can align the
>>    * DMA addresses to 4KB.
>> @@ -346,6 +350,7 @@ struct dpdk_qos_ops {
>>   /* dpdk_qos_ops for each type of user space QoS implementation. */
>>   static const struct dpdk_qos_ops egress_policer_ops;
>>   static const struct dpdk_qos_ops trtcm_policer_ops;
>> +static const struct dpdk_qos_ops kpkts_policer_ops;
>>   
>>   /*
>>    * Array of dpdk_qos_ops, contains pointer to all supported QoS
>> @@ -354,6 +359,7 @@ static const struct dpdk_qos_ops trtcm_policer_ops;
>>   static const struct dpdk_qos_ops *const qos_confs[] = {
>>       &egress_policer_ops,
>>       &trtcm_policer_ops,
>> +    &kpkts_policer_ops,
>>       NULL
>>   };
>>   
>> @@ -5572,6 +5578,159 @@ static const struct dpdk_qos_ops trtcm_policer_ops = {
>>       .qos_queue_dump_state_init = trtcm_policer_qos_queue_dump_state_init
>>   };
>>   
>> +/* kpkts-policer details */
>> +struct kpkts_policer {
>> +    struct qos_conf qos_conf;
>> +    struct token_bucket tb;
>> +    uint32_t kpkts_rate;
>> +    uint32_t kpkts_burst;
>> +};
>> +
>> +static int
>> +kpkts_policer_run_single_packet(struct token_bucket *tb, struct rte_mbuf **pkts,
>> +                                int pkt_cnt, bool should_steal)
>> +{
>> +    struct rte_mbuf *batch[NETDEV_MAX_BURST] = {0};
>> +    long long int now = time_msec();
>> +    struct rte_mbuf *pkt = NULL;
>> +    int i = 0, n = 0;
>> +    int cnt = 0;
>> +
>> +    for (i = 0; i < pkt_cnt; i++) {
>> +        pkt = pkts[i];
>> +        /* Handle current packet. */
>> +        if (token_bucket_withdraw(tb, 1, now)) {
>> +            /* Count passed packets. */
>> +            if (cnt != i) {
>> +                pkts[cnt] = pkt;
>> +            }
>> +            cnt++;
>> +        } else {
>> +            /* Count dropped packets. */
>> +            batch[n++] = pkt;
>> +        }
>> +    }
>> +
>> +    if (should_steal && n) {
>> +        rte_pktmbuf_free_bulk(batch, n);
>> +    }
>> +
>> +    return cnt;
>> +}
>> +
>> +static int
>> +kpkts_policer_profile_config(struct token_bucket *tb,
>> +                             uint32_t kpkts_rate, uint32_t kpkts_burst)
>> +{
>> +    if (kpkts_rate > MAX_KPKTS_PARAMETER ||
>> +        kpkts_burst > MAX_KPKTS_PARAMETER) {
>> +        return EINVAL;
>> +    }
>> +
>> +   /* Rate in kilo-packets/second, bucket 1000 packets.
>> +    * msec * kilo-packets/sec = 1 packets. */
>> +    if (kpkts_rate) {
>> +        /* Parameters between (1 ~ MAX_KPKTS_PARAMETER). */
>> +        token_bucket_init(tb, kpkts_rate, kpkts_burst * 1000);
>> +    } else {
>> +        /* Zero means not to police the traffic. */
>> +        return EINVAL;
>> +    }
>> +
>> +    return 0;
>> +}
>> +
>> +static int
>> +kpkts_policer_qos_construct(const struct smap *details, struct qos_conf **conf)
>> +{
>> +    uint32_t kpkts_rate, kpkts_burst;
>> +    struct kpkts_policer *policer;
>> +    int err;
>> +
>> +    policer = xmalloc(sizeof *policer);
>> +    kpkts_rate  = smap_get_uint(details, "kpkts_rate", 0);
>> +    kpkts_burst = smap_get_uint(details, "kpkts_burst", 0);
>> +
>> +    /*
>> +     * Force to 0 if no rate specified,
>> +     * default to rate if burst is 0,
>> +     * else stick with user-specified value.
>> +     */
>> +    kpkts_burst = (!kpkts_rate ? 0 : !kpkts_burst ? kpkts_rate : kpkts_burst);
>> +
>> +    qos_conf_init(&policer->qos_conf, &kpkts_policer_ops);
>> +    err = kpkts_policer_profile_config(&policer->tb, kpkts_rate, kpkts_burst);
>> +    if (err) {
>> +        VLOG_DBG("Could not create token bucket for egress policer");
> I didn't do an in-depth look at this yet, just noticed this needs free
> here for policer object.
>
>> +        return err;
>> +    }
>> +
>> +    policer->kpkts_rate = kpkts_rate;
>> +    policer->kpkts_burst = kpkts_burst;
>> +
>> +    *conf = &policer->qos_conf;
>> +
>> +    return err;
>> +}
>> +
>> +static void
>> +kpkts_policer_qos_destruct(struct qos_conf *conf)
>> +{
>> +    struct kpkts_policer *policer = CONTAINER_OF(conf, struct kpkts_policer,
>> +                                                 qos_conf);
>> +
>> +    free(policer);
>> +}
>> +
>> +static int
>> +kpkts_policer_qos_get(const struct qos_conf *conf, struct smap *details)
>> +{
>> +    struct kpkts_policer *policer = CONTAINER_OF(conf, struct kpkts_policer,
>> +                                                 qos_conf);
>> +
>> +    smap_add_format(details, "kpkts_rate", "%"PRIu32, policer->kpkts_rate);
>> +    smap_add_format(details, "kpkts_burst", "%"PRIu32, policer->kpkts_burst);
>> +
>> +    return 0;
>> +}
>> +
>> +static bool
>> +kpkts_pkts_policer_qos_is_equal(const struct qos_conf *conf,
>> +                                const struct smap *details)
>> +{
>> +    uint32_t rate, burst;
>> +    struct kpkts_policer *policer = CONTAINER_OF(conf, struct kpkts_policer,
>> +                                                 qos_conf);
>> +
>> +    rate  = smap_get_uint(details, "pkts_rate", 0);
>> +    burst = smap_get_uint(details, "pkts_burst", 0);
>> +
>> +    return (policer->tb.rate == rate && policer->tb.burst == burst);
>> +}
>> +
>> +static int
>> +kpkts_policer_run(struct qos_conf *conf, struct rte_mbuf **pkts, int pkt_cnt,
>> +                  bool should_steal)
>> +{
>> +    int cnt;
>> +    struct kpkts_policer *policer = CONTAINER_OF(conf, struct kpkts_policer,
>> +                                                 qos_conf);
>> +
>> +    cnt = kpkts_policer_run_single_packet(&policer->tb, pkts, pkt_cnt,
>> +                                          should_steal);
>> +
>> +    return cnt;
>> +}
>> +
>> +static const struct dpdk_qos_ops kpkts_policer_ops = {
>> +    .qos_name = "kpkts-policer",
>> +    .qos_construct = kpkts_policer_qos_construct,
>> +    .qos_destruct = kpkts_policer_qos_destruct,
>> +    .qos_get = kpkts_policer_qos_get,
>> +    .qos_is_equal = kpkts_pkts_policer_qos_is_equal,
>> +    .qos_run = kpkts_policer_run
>> +};
>> +
>>   static int
>>   dpdk_rx_steer_add_flow(struct netdev_dpdk *dev,
>>                         const struct rte_flow_item items[],
>> diff --git a/tests/system-dpdk.at b/tests/system-dpdk.at
>> index 0f58e8574..8b80a31e6 100644
>> --- a/tests/system-dpdk.at
>> +++ b/tests/system-dpdk.at
>> @@ -570,6 +570,261 @@ 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-appctl vlog/set netdev_dpdk:dbg])
>> +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=kpkts-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 kpkts-policer on port dpdkvhostuserclient0: Invalid argument@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-appctl vlog/set netdev_dpdk:dbg])
>> +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=kpkts-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
>> +\@Failed to set QoS type kpkts-policer on port dpdkvhostuserclient0: Invalid argument@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-appctl vlog/set netdev_dpdk:dbg])
>> +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=kpkts-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 Check egress policer was created
>> +AT_CHECK([ovs-appctl -t ovs-vswitchd qos/show dpdkvhostuserclient0], [], [stdout])
>> +AT_CHECK([grep -E 'kpkts_rate: 123' stdout], [], [stdout])
>> +
>> +dnl Check egress policer was deleted
>> +QOS_UUID=`ovs-vsctl get port dpdkvhostuserclient0 qos`
>> +AT_CHECK([ovs-vsctl set qos $QOS_UUID other_config:kpkts_rate=0])
>> +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 kpkts-policer on port dpdkvhostuserclient0: Invalid argument@d
>> +])")
>> +AT_CLEANUP
>> +dnl --------------------------------------------------------------------------
>> +
>> +
>> +
>> +dnl --------------------------------------------------------------------------
>> +dnl QoS (kpkts) max rate
>> +AT_SETUP([OVS-DPDK - QoS (kpkts) max 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-appctl vlog/set netdev_dpdk:dbg])
>> +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=kpkts-policer other-config:kpkts_rate=42949671])
>> +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
>> +\@Failed to set QoS type kpkts-policer on port dpdkvhostuserclient0: Invalid argument@d
>> +])")
>> +AT_CLEANUP
>> +dnl --------------------------------------------------------------------------
>> +
>> +
>> +dnl --------------------------------------------------------------------------
>> +dnl QoS (kpkts) max burst
>> +AT_SETUP([OVS-DPDK - QoS (kpkts) max 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-appctl vlog/set netdev_dpdk:dbg])
>> +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=kpkts-policer other-config:kpkts_burst=42949671])
>> +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
>> +\@Failed to set QoS type kpkts-policer on port dpdkvhostuserclient0: Invalid argument@d
>> +])")
>> +AT_CLEANUP
>> +dnl --------------------------------------------------------------------------
>> +
>> +
>> +dnl --------------------------------------------------------------------------
>> +dnl QoS (kpkts) testpmd flowgen test
>> +AT_SETUP([OVS-DPDK - QoS (kpkts) police])
>> +AT_KEYWORDS([dpdk])
>> +
>> +OVS_DPDK_PRE_CHECK()
>> +AT_SKIP_IF([! which dpdk-testpmd >/dev/null 2>/dev/null])
>> +OVS_DPDK_START([--no-pci])
>> +
>> +dnl Find number of sockets
>> +AT_CHECK([lscpu], [], [stdout])
>> +AT_CHECK([cat stdout | grep "NUMA node(s)" | awk '{c=1; while (c++<$(3)) {printf "512,"}; print "512"}' > NUMA_NODE])
>> +
>> +dnl Add userspace bridge and attach it to OVS
>> +AT_CHECK([ovs-vsctl add-br br10 -- set bridge br10 datapath_type=netdev])
>> +
>> +dnl Parse log file
>> +AT_CHECK([ovs-vsctl add-port br10 dpdkvhostuser0 -- set Interface dpdkvhostuser0 type=dpdkvhostuser], [], [stdout], [stderr])
>> +AT_CHECK([ovs-vsctl add-port br10 dpdkvhostuser1 -- set Interface dpdkvhostuser1 type=dpdkvhostuser], [], [stdout], [stderr])
>> +AT_CHECK([ovs-vsctl show], [], [stdout])
>> +
>> +dnl Parse log file
>> +AT_CHECK([grep "VHOST_CONFIG: ($OVS_RUNDIR/dpdkvhostuser0) vhost-user server: socket created" ovs-vswitchd.log], [], [stdout])
>> +AT_CHECK([grep "Socket $OVS_RUNDIR/dpdkvhostuser0 created for vhost-user port dpdkvhostuser0" ovs-vswitchd.log], [], [stdout])
>> +AT_CHECK([grep "VHOST_CONFIG: ($OVS_RUNDIR/dpdkvhostuser0) binding succeeded" ovs-vswitchd.log], [], [stdout])
>> +
>> +dnl Parse log file
>> +AT_CHECK([grep "VHOST_CONFIG: ($OVS_RUNDIR/dpdkvhostuser1) vhost-user server: socket created" ovs-vswitchd.log], [], [stdout])
>> +AT_CHECK([grep "Socket $OVS_RUNDIR/dpdkvhostuser1 created for vhost-user port dpdkvhostuser1" ovs-vswitchd.log], [], [stdout])
>> +AT_CHECK([grep "VHOST_CONFIG: ($OVS_RUNDIR/dpdkvhostuser1) binding succeeded" ovs-vswitchd.log], [], [stdout])
>> +
>> +dnl Configure the same QoS for both ports.
>> +AT_CHECK([ovs-vsctl set port dpdkvhostuser0 qos=@newqos -- --id=@newqos create qos type=kpkts-policer other-config:kpkts_rate=1 other-config:kpkts_burst=1], [0], [ignore])
>> +
>> +dnl add flows, only send packets from dpdkvhostuser1 to dpdkvhostuser0.
>> +AT_DATA([flows.txt], [dnl
>> +priority=100,in_port=dpdkvhostuser1,ip,actions=dpdkvhostuser0
>> +])
>> +
>> +AT_CHECK([ovs-ofctl del-flows br10])
>> +AT_CHECK([ovs-ofctl add-flows br10 flows.txt])
>> +
>> +dnl Execute testpmd in background
>> +on_exit "pkill -f -x -9 'tail -f /dev/null'"
>> +tail -f /dev/null | dpdk-testpmd --socket-mem="$(cat NUMA_NODE)" --no-pci\
>> +           --vdev="net_virtio_user0,path=$OVS_RUNDIR/dpdkvhostuser0" \
>> +           --vdev="net_virtio_user1,path=$OVS_RUNDIR/dpdkvhostuser1" \
>> +           --single-file-segments -- --forward-mode=flowgen -a > $OVS_RUNDIR/testpmd-dpdkvhostuser.log 2>&1 &
>> +
>> +dnl sent packet 10 second.
>> +AT_CHECK([sleep 10])
>> +
>> +dnl Clean up the testpmd now
>> +pkill -f -x -9 'tail -f /dev/null'
>> +
>> +dnl ---------------------- Forward statistics for port 0  ----------------------
>> +dnl RX-packets: 9911           RX-dropped: 0             RX-total: 9911
>> +dnl TX-packets: 15937632       TX-dropped: 226661984     TX-total: 242599616
>> +dnl ----------------------------------------------------------------------------
>> +port0_rx_packets=`cat testpmd-dpdkvhostuser.log | grep "Forward statistics for port 0" -A 1 | grep "RX-packets:" | awk '{print $2}'`
>> +echo "port0_rx_packets=$port0_rx_packets"
>> +
>> +AT_CHECK([test $port0_rx_packets -lt 10500])
>> +AT_CHECK([test $port0_rx_packets -gt 9500])
>> +
>> +OVS_VSWITCHD_STOP("m4_join([], [SYSTEM_DPDK_ALLOWED_LOGS], [
>> +\@dpdkvhostuser ports are considered deprecated;  please migrate to dpdkvhostuserclient ports.@d
>> +])")
>> +AT_CLEANUP
>> +dnl --------------------------------------------------------------------------
>> +
>> +
>>   
>>   dnl --------------------------------------------------------------------------
>>   dnl MTU increase phy port
>> diff --git a/vswitchd/vswitch.xml b/vswitchd/vswitch.xml
>> index cfcde34ff..19fc13873 100644
>> --- a/vswitchd/vswitch.xml
>> +++ b/vswitchd/vswitch.xml
>> @@ -4874,6 +4874,19 @@ ovs-vsctl add-port br0 p0 -- set Interface p0 type=patch options:peer=p1 \
>>             created with the same <code>other_config</code> values as the
>>             physical port.
>>           </dd>
>> +        <dt><code>kpkts-policer</code></dt>
>> +        <dd>
>> +          A DPDK egress packet per second policer algorithm using the ovs
>> +          token bucket library. The token bucket library provides an
>> +          implementation which allows the policing of packets traffic. The
>> +          implementation in OVS essentially creates a single token bucket used
>> +          to police traffic. It should be noted that when the token bucket is
>> +          configured as part of QoS there will be a performance overhead as the
>> +          token bucket itself will consume CPU cycles in order to police
>> +          traffic. These CPU cycles ordinarily are used for packet proccessing.
>> +          As such the drop in performance will be noticed in terms of overall
>> +          aggregate traffic throughput.
>> +        </dd>
>>         </dl>
>>       </column>
>>   
>> @@ -4966,6 +4979,25 @@ ovs-vsctl add-port br0 p0 -- set Interface p0 type=patch options:peer=p1 \
>>         </column>
>>       </group>
>>   
>> +    <group title="Configuration for kpkts-policer QoS">
>> +      <p>
>> +        <ref table="QoS"/> <ref table="QoS" column="type"/>
>> +        <code>kpkts-policer</code> provides egress pkts policing for userspace
>> +        port types with DPDK.
>> +
>> +        It has the following key-value pairs defined.
>> +      </p>
>> +
>> +      <column name="other_config" key="kpkts_rate" type='{"type": "integer"}'>
>> +        The Kilo Packets Per Second (kpps) 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"}'>
>> +        The Packets Per Second Burst Size is measured in count and represents a
>> +        token bucket.
>> +      </column>
>> +    </group>
>> +
>>       <group title="Configuration for linux-sfq">
>>         <p>
>>           The <code>linux-sfq</code> QoS supports the following key-value pairs:
diff mbox series

Patch

diff --git a/Documentation/topics/dpdk/qos.rst b/Documentation/topics/dpdk/qos.rst
index a98ec672f..6a4408127 100644
--- a/Documentation/topics/dpdk/qos.rst
+++ b/Documentation/topics/dpdk/qos.rst
@@ -36,6 +36,9 @@  QoS (Egress Policing)
 Single Queue Policer
 ~~~~~~~~~~~~~~~~~~~~
 
+Bytes Per Second Policer
++++++++++++++++++++
+
 Assuming you have a :doc:`vhost-user port <vhost-user>` transmitting traffic
 consisting of packets of size 64 bytes, the following command would limit the
 egress transmission rate of the port to ~1,000,000 packets per second::
@@ -52,6 +55,24 @@  To clear the QoS configuration from the port and ovsdb, run::
 
     $ ovs-vsctl destroy QoS vhost-user0 -- clear Port vhost-user0 qos
 
+Packets Per Second Policer
++++++++++++++++++++
+
+Assuming you have a :doc:`vhost-user port <vhost-user>` transmitting traffic,
+the following command would limit the egress transmission rate of the port to
+~1,000,000 packets per second::
+
+       ovs-vsctl set port vhost-user0 qos=@newqos -- \
+          --id=@newqos create qos type=kpkts-policer \
+          other-config:kpkts_rate=1000 other-config:kpkts_burst=1000
+
+To examine the QoS configuration of the port, run::
+
+    $ ovs-appctl -t ovs-vswitchd qos/show vhost-user0
+
+To clear the QoS configuration from the port and ovsdb, run::
+
+    $ ovs-vsctl destroy QoS vhost-user0 -- clear Port vhost-user0 qos
 
 Multi Queue Policer
 ~~~~~~~~~~~~~~~~~~~
diff --git a/NEWS b/NEWS
index 7a852427e..3d1ab282e 100644
--- a/NEWS
+++ b/NEWS
@@ -63,6 +63,7 @@  v3.2.0 - xx xxx xxxx
      * 'ovs-appctl dpif-netdev/pmd-sleep-show' command was added to get the
        max sleep configuration of PMD thread cores.
      * Removed experimental tag from PMD load based sleeping.
+     * Added new Qos type 'pkts-policer' to support kilo packet-per-second policing.
    - Linux TC offload:
      * Add support for offloading VXLAN tunnels with the GBP extensions.
    - Python
diff --git a/lib/netdev-dpdk.c b/lib/netdev-dpdk.c
index 7e3020a7d..e6b8922aa 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"
@@ -91,6 +93,8 @@  static bool per_port_memory = false; /* Status of per port memory support */
 #define OVS_CACHE_LINE_SIZE CACHE_LINE_SIZE
 #define OVS_VPORT_DPDK "ovs_dpdk"
 
+#define MAX_KPKTS_PARAMETER 4294967U  /* UINT32_MAX / 1000 */
+
 /*
  * need to reserve tons of extra space in the mbufs so we can align the
  * DMA addresses to 4KB.
@@ -346,6 +350,7 @@  struct dpdk_qos_ops {
 /* dpdk_qos_ops for each type of user space QoS implementation. */
 static const struct dpdk_qos_ops egress_policer_ops;
 static const struct dpdk_qos_ops trtcm_policer_ops;
+static const struct dpdk_qos_ops kpkts_policer_ops;
 
 /*
  * Array of dpdk_qos_ops, contains pointer to all supported QoS
@@ -354,6 +359,7 @@  static const struct dpdk_qos_ops trtcm_policer_ops;
 static const struct dpdk_qos_ops *const qos_confs[] = {
     &egress_policer_ops,
     &trtcm_policer_ops,
+    &kpkts_policer_ops,
     NULL
 };
 
@@ -5572,6 +5578,159 @@  static const struct dpdk_qos_ops trtcm_policer_ops = {
     .qos_queue_dump_state_init = trtcm_policer_qos_queue_dump_state_init
 };
 
+/* kpkts-policer details */
+struct kpkts_policer {
+    struct qos_conf qos_conf;
+    struct token_bucket tb;
+    uint32_t kpkts_rate;
+    uint32_t kpkts_burst;
+};
+
+static int
+kpkts_policer_run_single_packet(struct token_bucket *tb, struct rte_mbuf **pkts,
+                                int pkt_cnt, bool should_steal)
+{
+    struct rte_mbuf *batch[NETDEV_MAX_BURST] = {0};
+    long long int now = time_msec();
+    struct rte_mbuf *pkt = NULL;
+    int i = 0, n = 0;
+    int cnt = 0;
+
+    for (i = 0; i < pkt_cnt; i++) {
+        pkt = pkts[i];
+        /* Handle current packet. */
+        if (token_bucket_withdraw(tb, 1, now)) {
+            /* Count passed packets. */
+            if (cnt != i) {
+                pkts[cnt] = pkt;
+            }
+            cnt++;
+        } else {
+            /* Count dropped packets. */
+            batch[n++] = pkt;
+        }
+    }
+
+    if (should_steal && n) {
+        rte_pktmbuf_free_bulk(batch, n);
+    }
+
+    return cnt;
+}
+
+static int
+kpkts_policer_profile_config(struct token_bucket *tb,
+                             uint32_t kpkts_rate, uint32_t kpkts_burst)
+{
+    if (kpkts_rate > MAX_KPKTS_PARAMETER ||
+        kpkts_burst > MAX_KPKTS_PARAMETER) {
+        return EINVAL;
+    }
+
+   /* Rate in kilo-packets/second, bucket 1000 packets.
+    * msec * kilo-packets/sec = 1 packets. */
+    if (kpkts_rate) {
+        /* Parameters between (1 ~ MAX_KPKTS_PARAMETER). */
+        token_bucket_init(tb, kpkts_rate, kpkts_burst * 1000);
+    } else {
+        /* Zero means not to police the traffic. */
+        return EINVAL;
+    }
+
+    return 0;
+}
+
+static int
+kpkts_policer_qos_construct(const struct smap *details, struct qos_conf **conf)
+{
+    uint32_t kpkts_rate, kpkts_burst;
+    struct kpkts_policer *policer;
+    int err;
+
+    policer = xmalloc(sizeof *policer);
+    kpkts_rate  = smap_get_uint(details, "kpkts_rate", 0);
+    kpkts_burst = smap_get_uint(details, "kpkts_burst", 0);
+
+    /*
+     * Force to 0 if no rate specified,
+     * default to rate if burst is 0,
+     * else stick with user-specified value.
+     */
+    kpkts_burst = (!kpkts_rate ? 0 : !kpkts_burst ? kpkts_rate : kpkts_burst);
+
+    qos_conf_init(&policer->qos_conf, &kpkts_policer_ops);
+    err = kpkts_policer_profile_config(&policer->tb, kpkts_rate, kpkts_burst);
+    if (err) {
+        VLOG_DBG("Could not create token bucket for egress policer");
+        return err;
+    }
+
+    policer->kpkts_rate = kpkts_rate;
+    policer->kpkts_burst = kpkts_burst;
+
+    *conf = &policer->qos_conf;
+
+    return err;
+}
+
+static void
+kpkts_policer_qos_destruct(struct qos_conf *conf)
+{
+    struct kpkts_policer *policer = CONTAINER_OF(conf, struct kpkts_policer,
+                                                 qos_conf);
+
+    free(policer);
+}
+
+static int
+kpkts_policer_qos_get(const struct qos_conf *conf, struct smap *details)
+{
+    struct kpkts_policer *policer = CONTAINER_OF(conf, struct kpkts_policer,
+                                                 qos_conf);
+
+    smap_add_format(details, "kpkts_rate", "%"PRIu32, policer->kpkts_rate);
+    smap_add_format(details, "kpkts_burst", "%"PRIu32, policer->kpkts_burst);
+
+    return 0;
+}
+
+static bool
+kpkts_pkts_policer_qos_is_equal(const struct qos_conf *conf,
+                                const struct smap *details)
+{
+    uint32_t rate, burst;
+    struct kpkts_policer *policer = CONTAINER_OF(conf, struct kpkts_policer,
+                                                 qos_conf);
+
+    rate  = smap_get_uint(details, "pkts_rate", 0);
+    burst = smap_get_uint(details, "pkts_burst", 0);
+
+    return (policer->tb.rate == rate && policer->tb.burst == burst);
+}
+
+static int
+kpkts_policer_run(struct qos_conf *conf, struct rte_mbuf **pkts, int pkt_cnt,
+                  bool should_steal)
+{
+    int cnt;
+    struct kpkts_policer *policer = CONTAINER_OF(conf, struct kpkts_policer,
+                                                 qos_conf);
+
+    cnt = kpkts_policer_run_single_packet(&policer->tb, pkts, pkt_cnt,
+                                          should_steal);
+
+    return cnt;
+}
+
+static const struct dpdk_qos_ops kpkts_policer_ops = {
+    .qos_name = "kpkts-policer",
+    .qos_construct = kpkts_policer_qos_construct,
+    .qos_destruct = kpkts_policer_qos_destruct,
+    .qos_get = kpkts_policer_qos_get,
+    .qos_is_equal = kpkts_pkts_policer_qos_is_equal,
+    .qos_run = kpkts_policer_run
+};
+
 static int
 dpdk_rx_steer_add_flow(struct netdev_dpdk *dev,
                       const struct rte_flow_item items[],
diff --git a/tests/system-dpdk.at b/tests/system-dpdk.at
index 0f58e8574..8b80a31e6 100644
--- a/tests/system-dpdk.at
+++ b/tests/system-dpdk.at
@@ -570,6 +570,261 @@  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-appctl vlog/set netdev_dpdk:dbg])
+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=kpkts-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 kpkts-policer on port dpdkvhostuserclient0: Invalid argument@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-appctl vlog/set netdev_dpdk:dbg])
+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=kpkts-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
+\@Failed to set QoS type kpkts-policer on port dpdkvhostuserclient0: Invalid argument@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-appctl vlog/set netdev_dpdk:dbg])
+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=kpkts-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 Check egress policer was created
+AT_CHECK([ovs-appctl -t ovs-vswitchd qos/show dpdkvhostuserclient0], [], [stdout])
+AT_CHECK([grep -E 'kpkts_rate: 123' stdout], [], [stdout])
+
+dnl Check egress policer was deleted
+QOS_UUID=`ovs-vsctl get port dpdkvhostuserclient0 qos`
+AT_CHECK([ovs-vsctl set qos $QOS_UUID other_config:kpkts_rate=0])
+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 kpkts-policer on port dpdkvhostuserclient0: Invalid argument@d
+])")
+AT_CLEANUP
+dnl --------------------------------------------------------------------------
+
+
+
+dnl --------------------------------------------------------------------------
+dnl QoS (kpkts) max rate
+AT_SETUP([OVS-DPDK - QoS (kpkts) max 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-appctl vlog/set netdev_dpdk:dbg])
+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=kpkts-policer other-config:kpkts_rate=42949671])
+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
+\@Failed to set QoS type kpkts-policer on port dpdkvhostuserclient0: Invalid argument@d
+])")
+AT_CLEANUP
+dnl --------------------------------------------------------------------------
+
+
+dnl --------------------------------------------------------------------------
+dnl QoS (kpkts) max burst
+AT_SETUP([OVS-DPDK - QoS (kpkts) max 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-appctl vlog/set netdev_dpdk:dbg])
+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=kpkts-policer other-config:kpkts_burst=42949671])
+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
+\@Failed to set QoS type kpkts-policer on port dpdkvhostuserclient0: Invalid argument@d
+])")
+AT_CLEANUP
+dnl --------------------------------------------------------------------------
+
+
+dnl --------------------------------------------------------------------------
+dnl QoS (kpkts) testpmd flowgen test
+AT_SETUP([OVS-DPDK - QoS (kpkts) police])
+AT_KEYWORDS([dpdk])
+
+OVS_DPDK_PRE_CHECK()
+AT_SKIP_IF([! which dpdk-testpmd >/dev/null 2>/dev/null])
+OVS_DPDK_START([--no-pci])
+
+dnl Find number of sockets
+AT_CHECK([lscpu], [], [stdout])
+AT_CHECK([cat stdout | grep "NUMA node(s)" | awk '{c=1; while (c++<$(3)) {printf "512,"}; print "512"}' > NUMA_NODE])
+
+dnl Add userspace bridge and attach it to OVS
+AT_CHECK([ovs-vsctl add-br br10 -- set bridge br10 datapath_type=netdev])
+
+dnl Parse log file
+AT_CHECK([ovs-vsctl add-port br10 dpdkvhostuser0 -- set Interface dpdkvhostuser0 type=dpdkvhostuser], [], [stdout], [stderr])
+AT_CHECK([ovs-vsctl add-port br10 dpdkvhostuser1 -- set Interface dpdkvhostuser1 type=dpdkvhostuser], [], [stdout], [stderr])
+AT_CHECK([ovs-vsctl show], [], [stdout])
+
+dnl Parse log file
+AT_CHECK([grep "VHOST_CONFIG: ($OVS_RUNDIR/dpdkvhostuser0) vhost-user server: socket created" ovs-vswitchd.log], [], [stdout])
+AT_CHECK([grep "Socket $OVS_RUNDIR/dpdkvhostuser0 created for vhost-user port dpdkvhostuser0" ovs-vswitchd.log], [], [stdout])
+AT_CHECK([grep "VHOST_CONFIG: ($OVS_RUNDIR/dpdkvhostuser0) binding succeeded" ovs-vswitchd.log], [], [stdout])
+
+dnl Parse log file
+AT_CHECK([grep "VHOST_CONFIG: ($OVS_RUNDIR/dpdkvhostuser1) vhost-user server: socket created" ovs-vswitchd.log], [], [stdout])
+AT_CHECK([grep "Socket $OVS_RUNDIR/dpdkvhostuser1 created for vhost-user port dpdkvhostuser1" ovs-vswitchd.log], [], [stdout])
+AT_CHECK([grep "VHOST_CONFIG: ($OVS_RUNDIR/dpdkvhostuser1) binding succeeded" ovs-vswitchd.log], [], [stdout])
+
+dnl Configure the same QoS for both ports.
+AT_CHECK([ovs-vsctl set port dpdkvhostuser0 qos=@newqos -- --id=@newqos create qos type=kpkts-policer other-config:kpkts_rate=1 other-config:kpkts_burst=1], [0], [ignore])
+
+dnl add flows, only send packets from dpdkvhostuser1 to dpdkvhostuser0.
+AT_DATA([flows.txt], [dnl
+priority=100,in_port=dpdkvhostuser1,ip,actions=dpdkvhostuser0
+])
+
+AT_CHECK([ovs-ofctl del-flows br10])
+AT_CHECK([ovs-ofctl add-flows br10 flows.txt])
+
+dnl Execute testpmd in background
+on_exit "pkill -f -x -9 'tail -f /dev/null'"
+tail -f /dev/null | dpdk-testpmd --socket-mem="$(cat NUMA_NODE)" --no-pci\
+           --vdev="net_virtio_user0,path=$OVS_RUNDIR/dpdkvhostuser0" \
+           --vdev="net_virtio_user1,path=$OVS_RUNDIR/dpdkvhostuser1" \
+           --single-file-segments -- --forward-mode=flowgen -a > $OVS_RUNDIR/testpmd-dpdkvhostuser.log 2>&1 &
+
+dnl sent packet 10 second.
+AT_CHECK([sleep 10])
+
+dnl Clean up the testpmd now
+pkill -f -x -9 'tail -f /dev/null'
+
+dnl ---------------------- Forward statistics for port 0  ----------------------
+dnl RX-packets: 9911           RX-dropped: 0             RX-total: 9911
+dnl TX-packets: 15937632       TX-dropped: 226661984     TX-total: 242599616
+dnl ----------------------------------------------------------------------------
+port0_rx_packets=`cat testpmd-dpdkvhostuser.log | grep "Forward statistics for port 0" -A 1 | grep "RX-packets:" | awk '{print $2}'`
+echo "port0_rx_packets=$port0_rx_packets"
+
+AT_CHECK([test $port0_rx_packets -lt 10500])
+AT_CHECK([test $port0_rx_packets -gt 9500])
+
+OVS_VSWITCHD_STOP("m4_join([], [SYSTEM_DPDK_ALLOWED_LOGS], [
+\@dpdkvhostuser ports are considered deprecated;  please migrate to dpdkvhostuserclient ports.@d
+])")
+AT_CLEANUP
+dnl --------------------------------------------------------------------------
+
+
 
 dnl --------------------------------------------------------------------------
 dnl MTU increase phy port
diff --git a/vswitchd/vswitch.xml b/vswitchd/vswitch.xml
index cfcde34ff..19fc13873 100644
--- a/vswitchd/vswitch.xml
+++ b/vswitchd/vswitch.xml
@@ -4874,6 +4874,19 @@  ovs-vsctl add-port br0 p0 -- set Interface p0 type=patch options:peer=p1 \
           created with the same <code>other_config</code> values as the
           physical port.
         </dd>
+        <dt><code>kpkts-policer</code></dt>
+        <dd>
+          A DPDK egress packet per second policer algorithm using the ovs
+          token bucket library. The token bucket library provides an
+          implementation which allows the policing of packets traffic. The
+          implementation in OVS essentially creates a single token bucket used
+          to police traffic. It should be noted that when the token bucket is
+          configured as part of QoS there will be a performance overhead as the
+          token bucket itself will consume CPU cycles in order to police
+          traffic. These CPU cycles ordinarily are used for packet proccessing.
+          As such the drop in performance will be noticed in terms of overall
+          aggregate traffic throughput.
+        </dd>
       </dl>
     </column>
 
@@ -4966,6 +4979,25 @@  ovs-vsctl add-port br0 p0 -- set Interface p0 type=patch options:peer=p1 \
       </column>
     </group>
 
+    <group title="Configuration for kpkts-policer QoS">
+      <p>
+        <ref table="QoS"/> <ref table="QoS" column="type"/>
+        <code>kpkts-policer</code> provides egress pkts policing for userspace
+        port types with DPDK.
+
+        It has the following key-value pairs defined.
+      </p>
+
+      <column name="other_config" key="kpkts_rate" type='{"type": "integer"}'>
+        The Kilo Packets Per Second (kpps) 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"}'>
+        The Packets Per Second Burst Size is measured in count and represents a
+        token bucket.
+      </column>
+    </group>
+
     <group title="Configuration for linux-sfq">
       <p>
         The <code>linux-sfq</code> QoS supports the following key-value pairs: