Hi Alin,

I have sent all the latest patch to patchlist, hope you noticed it.

Regards
Dejing

From: Alin-Gabriel Serdean <aserd...@ovn.org>
Date: Tuesday, August 16, 2022 at 6:19 PM
To: Dejing Liu <ldej...@vmware.com>, 'Alin-Gabriel Serdean' <aserd...@ovn.org>, 
d...@openvswitch.org <d...@openvswitch.org>
Cc: Frank Guo <fra...@vmware.com>, Wilson Peng <pweis...@vmware.com>, Lina Li 
<lin...@vmware.com>
Subject: RE: [ovs-dev] [PATCH v1 1/1] datapath-windows: support meter action 
initial version

⚠ External Email
Dejing,

Looking forward to it.

Alin.

From: Dejing Liu <ldej...@vmware.com>
Sent: Tuesday, August 16, 2022 1:00 PM
To: Alin-Gabriel Serdean <aserd...@ovn.org>; d...@openvswitch.org
Cc: Frank Guo <fra...@vmware.com>; Wilson Peng <pweis...@vmware.com>; Lina Li 
<lin...@vmware.com>
Subject: Re: [ovs-dev] [PATCH v1 1/1] datapath-windows: support meter action 
initial version

Hi Alin,

I have made some modification according to your suggestion and new patch will 
come out soon.

Regards
Dejing

From: Alin-Gabriel Serdean <aserd...@ovn.org<mailto:aserd...@ovn.org>>
Date: Tuesday, August 9, 2022 at 9:22 PM
To: d...@openvswitch.org<mailto:d...@openvswitch.org> 
<d...@openvswitch.org<mailto:d...@openvswitch.org>>
Cc: Dejing Liu <ldej...@vmware.com<mailto:ldej...@vmware.com>>
Subject: [ovs-dev] [PATCH v1 1/1] datapath-windows: support meter action 
initial version
Again patch looks good I only have one question. Please see bellow.

> This patch implemented meter action, currently, meter only support drop
> method and only support one band. The overall implementation is, when a
> packet comes in, it will first lookup meter according to the meter id,
> then get the band->rates and delta time since last access the same meter
> from the meter struct. Add the multiply result(band->rates * delta_time)
> to bucket, finally bucket minus the packet size, if the result larger
> than zero, allow the packet go through, otherwise deny the packet go
> through.
>
> Test case:
>     1. Setting the size meter size 3M, then the bandwidth was limit
>        around 3M;
>         ovs-ofctl -O OpenFlow13 add-meter br-test meter=2,kbps,\
>                      band=type=drop,rate=3000
>         ovs-ofctl add-flow br-test "table=0,priority=1,ip \
>                      actions=meter:2,normal" -O OpenFlow13
>     2. Setting the meter size 8M, then the bandwidth was limit
>        around 8M;
>        ovs-ofctl -O OpenFlow13 add-meter br-test meter=2,\
>                       kbps,band=type=drop,rate=8000
>        ovs-ofctl add-flow br-test "table=0,priority=1,ip\
>                       actions=meter:2,normal" -O OpenFlow13
>
> Signed-off-by: ldejing <ldej...@vmware.com<mailto:ldej...@vmware.com>>
> ---
>  datapath-windows/automake.mk                 |   2 +
>  datapath-windows/include/OvsDpInterfaceExt.h |   3 +
>  datapath-windows/ovsext/Actions.c            |  10 +
>  datapath-windows/ovsext/Datapath.c           |  46 +-
>  datapath-windows/ovsext/Meter.c              | 538 +++++++++++++++++++
>  datapath-windows/ovsext/Meter.h              |  68 +++
>  datapath-windows/ovsext/Switch.c             |   8 +
>  datapath-windows/ovsext/Util.h               |   1 +
>  datapath-windows/ovsext/ovsext.vcxproj       |   2 +
>  lib/netlink-socket.c                         |   5 +
>  10 files changed, 682 insertions(+), 1 deletion(-)
>  create mode 100644 datapath-windows/ovsext/Meter.c
>  create mode 100644 datapath-windows/ovsext/Meter.h
>
> diff --git a/datapath-windows/automake.mk b/datapath-windows/automake.mk
> index 60b3d6033..2d9f9a40d 100644
> --- a/datapath-windows/automake.mk
> +++ b/datapath-windows/automake.mk
> @@ -42,6 +42,8 @@ EXTRA_DIST += \
>        datapath-windows/ovsext/Jhash.c \
>        datapath-windows/ovsext/Jhash.h \
>        datapath-windows/ovsext/Mpls.h \
> +     datapath-windows/ovsext/Meter.c \
> +     datapath-windows/ovsext/Meter.h \
>        datapath-windows/ovsext/NetProto.h \
>        datapath-windows/ovsext/Netlink/Netlink.c \
>        datapath-windows/ovsext/Netlink/Netlink.h \
> diff --git a/datapath-windows/include/OvsDpInterfaceExt.h 
> b/datapath-windows/include/OvsDpInterfaceExt.h
> index 5fd8000d1..045e4cbd6 100644
> --- a/datapath-windows/include/OvsDpInterfaceExt.h
> +++ b/datapath-windows/include/OvsDpInterfaceExt.h
> @@ -74,6 +74,9 @@
>  #define OVS_WIN_NL_CT_FAMILY_ID              (NLMSG_MIN_TYPE + 7)
>  #define OVS_WIN_NL_CTLIMIT_FAMILY_ID         (NLMSG_MIN_TYPE + 8)
>
> +/* Meter Family */
> +#define OVS_WIN_NL_METER_FAMILY_ID            (NLMSG_MIN_TYPE + 9)
> +
>  #define OVS_WIN_NL_INVALID_MCGRP_ID          0
>  #define OVS_WIN_NL_MCGRP_START_ID            100
>  #define OVS_WIN_NL_VPORT_MCGRP_ID            (OVS_WIN_NL_MCGRP_START_ID + 1)
> diff --git a/datapath-windows/ovsext/Actions.c 
> b/datapath-windows/ovsext/Actions.c
> index 20de4db4c..63ee574c5 100644
> --- a/datapath-windows/ovsext/Actions.c
> +++ b/datapath-windows/ovsext/Actions.c
> @@ -23,6 +23,7 @@
>  #include "Flow.h"
>  #include "Gre.h"
>  #include "Jhash.h"
> +#include "Meter.h"
>  #include "Mpls.h"
>  #include "NetProto.h"
>  #include "Offload.h"
> @@ -2502,6 +2503,15 @@ OvsDoExecuteActions(POVS_SWITCH_CONTEXT switchContext,
>              }
>              break;
>          }
> +        case OVS_ACTION_ATTR_METER: {
> +            if (OvsMeterExecute(&ovsFwdCtx, NlAttrGetU32(a))) {
> +                OVS_LOG_INFO("Drop packet");
> +                dropReason = L"Ovs-meter exceed max rate";
> +                goto dropit;
> +            }
> +
> +            break;
> +        }
>          case OVS_ACTION_ATTR_SAMPLE:
>          {
>              if (ovsFwdCtx.destPortsSizeOut > 0 || ovsFwdCtx.tunnelTxNic != 
> NULL
> diff --git a/datapath-windows/ovsext/Datapath.c 
> b/datapath-windows/ovsext/Datapath.c
> index fa994840a..37db5bd17 100644
> --- a/datapath-windows/ovsext/Datapath.c
> +++ b/datapath-windows/ovsext/Datapath.c
> @@ -100,7 +100,11 @@ NetlinkCmdHandler        OvsGetNetdevCmdHandler,
>                           OvsReadPacketCmdHandler,
>                           OvsCtDeleteCmdHandler,
>                           OvsCtDumpCmdHandler,
> -                         OvsCtLimitHandler;
> +                         OvsCtLimitHandler,
> +                         OvsMeterFeatureProbe,
> +                         OvsNewMeterCmdHandler,
> +                         OvsMeterDestroy,
> +                         OvsMeterGet;
>
>  static NTSTATUS HandleGetDpTransaction(POVS_USER_PARAMS_CONTEXT usrParamsCtx,
>                                         UINT32 *replyLen);
> @@ -307,6 +311,43 @@ NETLINK_FAMILY nlCtFamilyOps = {
>      .opsCount = ARRAY_SIZE(nlCtFamilyCmdOps)
>  };
>
> +/* Netlink Meter family */
> +NETLINK_CMD nlMeterFamilyCmdOps[] = {
> +    {  .cmd             = OVS_METER_CMD_FEATURES,
> +       .handler         = OvsMeterFeatureProbe,
> +       .supportedDevOp  = OVS_TRANSACTION_DEV_OP |
> +                             OVS_WRITE_DEV_OP | OVS_READ_DEV_OP,
> +       .validateDpIndex = FALSE
> +    },
> +    { .cmd              = OVS_METER_CMD_SET,
> +      .handler          = OvsNewMeterCmdHandler,
> +      .supportedDevOp   = OVS_TRANSACTION_DEV_OP |
> +                          OVS_WRITE_DEV_OP | OVS_READ_DEV_OP,
> +      .validateDpIndex  = FALSE
> +    },
> +    { .cmd              = OVS_METER_CMD_GET,
> +      .handler          = OvsMeterGet,
> +      .supportedDevOp   = OVS_TRANSACTION_DEV_OP |
> +                          OVS_WRITE_DEV_OP | OVS_READ_DEV_OP,
> +      .validateDpIndex  = FALSE
> +    },
> +    { .cmd              = OVS_METER_CMD_DEL,
> +      .handler          = OvsMeterDestroy,
> +      .supportedDevOp   = OVS_TRANSACTION_DEV_OP |
> +                          OVS_WRITE_DEV_OP | OVS_READ_DEV_OP,
> +      .validateDpIndex  = FALSE
> +    },
> +};
> +
> +NETLINK_FAMILY nlMeterFamilyOps = {
> +    .name     = OVS_METER_FAMILY,
> +    .id       = OVS_WIN_NL_METER_FAMILY_ID,
> +    .version  = OVS_METER_VERSION,
> +    .maxAttr  = __OVS_METER_ATTR_MAX,
> +    .cmds     = nlMeterFamilyCmdOps,
> +    .opsCount = ARRAY_SIZE(nlMeterFamilyCmdOps)
> +};
> +
>  /* Netlink netdev family. */
>  NETLINK_CMD nlNetdevFamilyCmdOps[] = {
>      { .cmd = OVS_WIN_NETDEV_CMD_GET,
> @@ -952,6 +993,9 @@ OvsDeviceControl(PDEVICE_OBJECT deviceObject,
>      case NFNL_TYPE_CT_DEL:
>          nlFamilyOps = &nlCtFamilyOps;
>          break;
> +    case OVS_WIN_NL_METER_FAMILY_ID:
> +        nlFamilyOps = &nlMeterFamilyOps;
> +        break;
>      case OVS_WIN_NL_CTRL_FAMILY_ID:
>          nlFamilyOps = &nlControlFamilyOps;
>          break;
> diff --git a/datapath-windows/ovsext/Meter.c b/datapath-windows/ovsext/Meter.c
> new file mode 100644
> index 000000000..62f5f36c9
> --- /dev/null
> +++ b/datapath-windows/ovsext/Meter.c
> @@ -0,0 +1,538 @@
> +/*
> + * Copyright (c) 2022 VMware, Inc.
> + *
> + * Licensed under the Apache License, Version 2.0 (the "License");
> + * you may not use this file except in compliance with the License.
> + * You may obtain a copy of the License at:
> + *
> + *     
> https://nam04.safelinks.protection.outlook.com/?url=http%3A%2F%2Fwww.apache.org%2Flicenses%2FLICENSE-2.0&amp;data=05%7C01%7Cldejing%40vmware.com%7Cf3637760b1f648439dcb08da7a0a3a46%7Cb39138ca3cee4b4aa4d6cd83d9dd62f0%7C0%7C0%7C637956481621529703%7CUnknown%7CTWFpbGZsb3d8eyJWIjoiMC4wLjAwMDAiLCJQIjoiV2luMzIiLCJBTiI6Ik1haWwiLCJXVCI6Mn0%3D%7C3000%7C%7C%7C&amp;sdata=HBLCMJ8wxRzzMsHhdim8OPYjsYQ7c5X58qarQJlEoQk%3D&amp;reserved=0<https://nam04.safelinks.protection.outlook.com/?url=http%3A%2F%2Fwww.apache.org%2Flicenses%2FLICENSE-2.0&data=05%7C01%7Cldejing%40vmware.com%7C1cc952b1c18148f0405408da7f70d971%7Cb39138ca3cee4b4aa4d6cd83d9dd62f0%7C0%7C0%7C637962419936897924%7CUnknown%7CTWFpbGZsb3d8eyJWIjoiMC4wLjAwMDAiLCJQIjoiV2luMzIiLCJBTiI6Ik1haWwiLCJXVCI6Mn0%3D%7C3000%7C%7C%7C&sdata=Emq9OMXvvmtYQwWixb2ssGqVJF2QKhj81kUv3LyfExo%3D&reserved=0>
> + *
> + * Unless required by applicable law or agreed to in writing, software
> + * distributed under the License is distributed on an "AS IS" BASIS,
> + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
> + * See the License for the specific language governing permissions and
> + * limitations under the License.
> + */
> +
> +#include "Meter.h"
> +#include "precomp.h"
> +#include "Switch.h"
> +#include "User.h"
> +#include "Datapath.h"
> +#include "Event.h"
> +#include "NetProto.h"
> +#include "Flow.h"
> +#include "PacketParser.h"
> +#include "util.h"
> +
> +static PNDIS_RW_LOCK_EX meterGlobalTableLock;
> +PLIST_ENTRY meterGlobalTable;
> +
> +const NL_POLICY nlMeterPolicy[OVS_METER_ATTR_MAX + 1] = {
> +        [OVS_METER_ATTR_ID] = { .type = NL_A_U32, },
> +        [OVS_METER_ATTR_KBPS] = { .type = NL_A_FLAG, .optional = TRUE },
> +        [OVS_METER_ATTR_STATS] = { .minLen = sizeof(struct ovs_flow_stats),
> +                                   .maxLen = sizeof(struct ovs_flow_stats),
> +                                   .optional = TRUE },
> +        [OVS_METER_ATTR_BANDS] = { .type = NL_A_NESTED, .optional = TRUE },
> +        [OVS_METER_ATTR_USED] = { .type = NL_A_U64, .optional = TRUE },
> +        [OVS_METER_ATTR_CLEAR] = { .type = NL_A_FLAG, .optional = TRUE },
> +        [OVS_METER_ATTR_MAX_METERS] = { .type = NL_A_U32, .optional = TRUE },
> +        [OVS_METER_ATTR_MAX_BANDS] = { .type = NL_A_U32,  .optional = TRUE },
> +};
> +
> +const NL_POLICY bandPolicy[OVS_BAND_ATTR_MAX + 1] = {
> +        [OVS_BAND_ATTR_TYPE] = { .type = NL_A_U32, .optional = FALSE },
> +        [OVS_BAND_ATTR_RATE] = { .type = NL_A_U32, .optional = TRUE },
> +        [OVS_BAND_ATTR_BURST] = { .type = NL_A_U32,  .optional = TRUE },
> +        [OVS_BAND_ATTR_STATS] = { .minLen = sizeof(struct ovs_flow_stats),
> +                                  .maxLen = sizeof(struct ovs_flow_stats),
> +                                  .optional = TRUE },
> +};
> +
> +NTSTATUS
> +OvsInitMeter(POVS_SWITCH_CONTEXT context)
> +{
> +    UINT32 maxEntry = METER_HASH_BUCKET_MAX;
> +
> +    meterGlobalTableLock = NdisAllocateRWLock(context->NdisFilterHandle);
> +    if (meterGlobalTableLock == NULL) {
> +        return STATUS_INSUFFICIENT_RESOURCES;
> +    }
> +
> +    meterGlobalTable = OvsAllocateMemoryWithTag(sizeof(LIST_ENTRY) * 
> maxEntry,
> +                                                OVS_METER_TAG);
> +    if (!meterGlobalTable) {
> +        NdisFreeRWLock(meterGlobalTableLock);
> +        return NDIS_STATUS_RESOURCES;
> +    }
> +
> +    for (UINT32 index = 0; index < maxEntry; index++) {
> +        InitializeListHead(&meterGlobalTable[index]);
> +    }
> +
> +    return NDIS_STATUS_SUCCESS;
> +}
> +
> +NDIS_STATUS
> +FillBandIntoMeter(PNL_ATTR meterAttrs[], DpMeter *meter, PNL_MSG_HDR 
> nlMsgHdr)
> +{
> +    PNL_ATTR a = NULL;
> +    INT rem = 0;
> +    UINT32 bandMaxDelta = 0;
> +    UINT32 attrOffset = 0;
> +    DpMeterBand *band = NULL;
> +    PNL_ATTR bandAttrs[OVS_BAND_ATTR_MAX + 1];
> +    UINT16 nBands = 0;
> +
> +    band = meter->bands;
> +    NL_ATTR_FOR_EACH(a, rem, NlAttrData(meterAttrs[OVS_METER_ATTR_BANDS]),
> +                     NlAttrGetSize(meterAttrs[OVS_METER_ATTR_BANDS])) {
> +        RtlZeroMemory(bandAttrs, sizeof(bandAttrs));
> +        attrOffset = (UINT32)((PCHAR)NlAttrData(a) - (PCHAR)nlMsgHdr);
> +        if (!NlAttrParse(nlMsgHdr,
> +                         attrOffset,
> +                         NlAttrGetSize(a),
> +                         bandPolicy, ARRAY_SIZE(bandPolicy),
> +                         bandAttrs, ARRAY_SIZE(bandAttrs))) {
> +            return STATUS_INVALID_PARAMETER;
> +        }
> +
> +        if (bandAttrs[OVS_BAND_ATTR_TYPE]) {
> +            band->type = NlAttrGetU32(bandAttrs[OVS_BAND_ATTR_TYPE]);
> +        }
> +
> +        if (bandAttrs[OVS_BAND_ATTR_RATE]) {
> +            band->rate = NlAttrGetU32(bandAttrs[OVS_BAND_ATTR_RATE]);
> +        }
> +
> +        if (bandAttrs[OVS_BAND_ATTR_BURST]) {
> +            band->burst_size = NlAttrGetU32(bandAttrs[OVS_BAND_ATTR_BURST]);
> +        }
> +
> +        band->bucket = (band->burst_size + band->rate) * 1000;
> +        bandMaxDelta = (UINT32)((band->bucket / band->rate)  / 10);
> +        if (bandMaxDelta > meter->maxDelta) {
> +            meter->maxDelta = bandMaxDelta;
> +        }
> +
> +        nBands++;
> +        band++;
> +    }
> +
> +    meter->nBands = nBands;
> +    return NDIS_STATUS_SUCCESS;
> +}
> +
> +NDIS_STATUS
> +OvsNewMeterCmdHandler(POVS_USER_PARAMS_CONTEXT usrParamsCtx,
> +                      UINT32 *replyLen)
> +{
> +    DpMeter *meter = NULL;
> +    POVS_MESSAGE msgIn = (POVS_MESSAGE)usrParamsCtx->inputBuffer;
> +    PNL_MSG_HDR nlMsgHdr = &(msgIn->nlMsg);
> +    PGENL_MSG_HDR genlMsgHdr = &(msgIn->genlMsg);
> +    POVS_HDR ovsHdr = &(msgIn->ovsHdr);
> +    LOCK_STATE_EX lockState;
> +    PNL_ATTR meterAttrs[ARRAY_SIZE(nlMeterPolicy)];
> +    ASSERT(usrParamsCtx->inputBuffer != NULL);
> +    PNL_MSG_HDR nlMsgOutHdr = NULL;
> +    NL_BUFFER nlBuf;
> +    NL_ERROR nlError = NL_ERROR_SUCCESS;
> +
> +    if (!NlAttrParse((PNL_MSG_HDR)msgIn,
> +                     NLMSG_HDRLEN + GENL_HDRLEN + OVS_HDRLEN,
> +                     NlMsgAttrsLen((PNL_MSG_HDR)msgIn),
> +                     nlMeterPolicy, ARRAY_SIZE(nlMeterPolicy),
> +                     meterAttrs, ARRAY_SIZE(meterAttrs))) {
> +        nlError = NL_ERROR_NOMSG;
> +        goto Done;
> +    }
> +
> +    meter = OvsAllocateMemoryWithTag(sizeof(*meter), OVS_METER_TAG);
> +    if (!meter) {
> +        nlError = NL_ERROR_NOMEM;
> +        goto Done;
> +    }
> +
> +    RtlZeroMemory(meter, sizeof(*meter));
> +    meter->id = NlAttrGetU32(meterAttrs[OVS_METER_ATTR_ID]);
> +    meter->kbps = meterAttrs[OVS_METER_ATTR_KBPS] ? 1 : 0;
> +    meter->keepStatus = meterAttrs[OVS_METER_ATTR_CLEAR] ? 1 : 0;
> +    if (meter->keepStatus && meterAttrs[OVS_METER_ATTR_STATS]) {
> +        meter->stats = *(struct ovs_flow_stats *)NlAttrData(
> +                        meterAttrs[OVS_METER_ATTR_STATS]);
> +    }
> +
> +    if (FillBandIntoMeter(meterAttrs, meter, nlMsgHdr) != 
> NDIS_STATUS_SUCCESS) {
> +        nlError = NL_ERROR_NOMSG;
> +        OvsFreeMemoryWithTag(meter, OVS_METER_TAG);
> +        goto Done;
> +    }
> +
> +    NdisAcquireRWLockWrite(meterGlobalTableLock, &lockState, 0);
> +    InsertHeadList(&meterGlobalTable[meter->id & (METER_HASH_BUCKET_MAX - 
> 1)],
> +                   &(meter->link));
> +    NdisReleaseRWLock(meterGlobalTableLock, &lockState);
> +
> +    NlBufInit(&nlBuf, usrParamsCtx->outputBuffer, 
> usrParamsCtx->outputLength);
> +    nlMsgOutHdr = (PNL_MSG_HDR)(NlBufAt(&nlBuf, 0, 0));
> +    if (!NlFillOvsMsg(&nlBuf, nlMsgHdr->nlmsgType, 0,
> +                     nlMsgHdr->nlmsgSeq, nlMsgHdr->nlmsgPid,
> +                     genlMsgHdr->cmd, OVS_METER_CMD_GET,
> +                     ovsHdr->dp_ifindex)) {
> +        nlError = NL_ERROR_NOMSG;
> +        goto Done;
> +    }
> +
> +    if (!buildOvsMeterReplyMsg(&nlBuf, meter)) {
> +        nlError = NL_ERROR_NOMEM;
> +        goto Done;
> +    }
> +
> +    NlMsgSetSize(nlMsgOutHdr, NlBufSize(&nlBuf));
> +    NlMsgAlignSize(nlMsgOutHdr);
> +    *replyLen += NlMsgSize(nlMsgOutHdr);
> +
> +Done:
> +    if (nlError != NL_ERROR_SUCCESS) {
> +        POVS_MESSAGE_ERROR msgError = (POVS_MESSAGE_ERROR)
> +                usrParamsCtx->outputBuffer;
> +
> +        ASSERT(msgError);
> +        NlBuildErrorMsg(msgIn, msgError, nlError, replyLen);
> +        ASSERT(*replyLen != 0);
> +    }
> +
> +    return NDIS_STATUS_SUCCESS;
> +}
> +
> +NDIS_STATUS OvsMeterFeatureProbe(POVS_USER_PARAMS_CONTEXT usrParamsCtx,
> +                                 UINT32 *replyLen)
> +{
> +    POVS_MESSAGE msgIn = (POVS_MESSAGE)usrParamsCtx->inputBuffer;
> +    PNL_MSG_HDR nlMsgHdr = &(msgIn->nlMsg);
> +    POVS_HDR ovsHdr = &(msgIn->ovsHdr);
> +    PNL_MSG_HDR nlMsgOutHdr = NULL;
> +    BOOLEAN ok = FALSE;
> +    PGENL_MSG_HDR genlMsgHdr = &(msgIn->genlMsg);
> +    NL_BUFFER nlBuf;
> +    NL_ERROR nlError = NL_ERROR_SUCCESS;
> +    UINT32 bandsOffset = 0;
> +    UINT32 bandAttrOffset = 0;
> +
> +    NlBufInit(&nlBuf, usrParamsCtx->outputBuffer, 
> usrParamsCtx->outputLength);
> +    nlMsgOutHdr = (PNL_MSG_HDR)(NlBufAt(&nlBuf, 0, 0));
> +    ok = NlFillOvsMsg(&nlBuf, nlMsgHdr->nlmsgType, 0,
> +                      nlMsgHdr->nlmsgSeq, nlMsgHdr->nlmsgPid,
> +                      genlMsgHdr->cmd, OVS_METER_CMD_FEATURES,
> +                      ovsHdr->dp_ifindex);
> +    if (!ok) {
> +        nlError = NL_ERROR_NOMSG;
> +        goto Done;
> +    }
> +
> +    if (!NlMsgPutTailU32(&nlBuf, OVS_METER_ATTR_MAX_METERS, UINT32_MAX)) {
> +        nlError = NL_ERROR_NOMSG;
> +        goto Done;
> +    }
> +
> +    if (!NlMsgPutTailU32(&nlBuf, OVS_METER_ATTR_MAX_BANDS, OVS_MAX_BANDS)) {
> +        nlError = NL_ERROR_NOMSG;
> +        goto Done;
> +    }
> +
> +    bandsOffset = NlMsgStartNested(&nlBuf, OVS_METER_ATTR_BANDS);
> +    bandAttrOffset = NlMsgStartNested(&nlBuf, OVS_METER_ATTR_UNSPEC);
> +    if (!NlMsgPutTailU32(&nlBuf, OVS_BAND_ATTR_TYPE,
> +                         OVS_METER_BAND_TYPE_DROP)) {
> +        nlError = NL_ERROR_NOMSG;
> +        goto Done;
> +
> +    }
> +    NlMsgEndNested(&nlBuf, bandAttrOffset);
> +    NlMsgEndNested(&nlBuf, bandsOffset);
> +
> +    NlMsgSetSize(nlMsgOutHdr, NlBufSize(&nlBuf));
> +    NlMsgAlignSize(nlMsgOutHdr);
> +    *replyLen += NlMsgSize(nlMsgOutHdr);
> +
> +Done:
> +    if (nlError != NL_ERROR_SUCCESS) {
> +        POVS_MESSAGE_ERROR msgError = (POVS_MESSAGE_ERROR)
> +                usrParamsCtx->outputBuffer;
> +
> +        ASSERT(msgError);
> +        NlBuildErrorMsg(msgIn, msgError, nlError, replyLen);
> +        ASSERT(*replyLen != 0);
> +    }
> +
> +    return STATUS_SUCCESS;
> +}
> +
> +BOOLEAN
> +buildOvsMeterReplyMsg(NL_BUFFER *nlBuf, DpMeter *dpMeter)
> +{
> +    BOOLEAN ok = FALSE;
> +    UINT32 bandAttrOffset;
> +    UINT32 bandsOffset;
> +
> +    /* Add meter element. */
> +    ok = NlMsgPutTailU32(nlBuf, OVS_METER_ATTR_ID, dpMeter->id);
> +    if (!ok) {
> +        OVS_LOG_ERROR("Could not add meter id %d.", dpMeter->id);
> +        return ok;
> +    }
> +
> +    ok = NlMsgPutTailUnspec(nlBuf, OVS_METER_ATTR_STATS,
> +                            (PCHAR)&(dpMeter->stats),
> +                            sizeof(dpMeter->stats));
> +    if (!ok) {
> +        OVS_LOG_ERROR("Could not add ovs meter stats.");
> +        return ok;
> +    }
> +
> +    bandsOffset = NlMsgStartNested(nlBuf, OVS_METER_ATTR_BANDS);
> +    for (int index = 0; index < dpMeter->nBands; index++) {
> +        bandAttrOffset = NlMsgStartNested(nlBuf, OVS_BAND_ATTR_UNSPEC);
> +        ok = NlMsgPutTailUnspec(nlBuf, OVS_BAND_ATTR_STATS,
> +                                (PCHAR)&(dpMeter->bands[index].stats),
> +                                sizeof(dpMeter->bands[index].stats));
> +        NlMsgEndNested(nlBuf, bandAttrOffset);
> +        if (!ok) {
> +            break;
> +        }
> +    }
> +
> +    NlMsgEndNested(nlBuf, bandsOffset);
> +    return ok;
> +}
> +
> +NDIS_STATUS
> +OvsMeterGet(POVS_USER_PARAMS_CONTEXT usrParamsCtx,
> +            UINT32 *replyLen)
> +{
> +    POVS_MESSAGE msgIn = (POVS_MESSAGE)usrParamsCtx->inputBuffer;
> +    PNL_MSG_HDR nlMsgHdr = &(msgIn->nlMsg);
> +    POVS_HDR ovsHdr = &(msgIn->ovsHdr);
> +    PNL_MSG_HDR nlMsgOutHdr = NULL;
> +    PNL_ATTR meterAttrs[ARRAY_SIZE(nlMeterPolicy)];
> +    UINT32 meterId = 0;
> +    DpMeter *dpMeter = NULL;
> +    BOOLEAN ok = FALSE;
> +    PGENL_MSG_HDR genlMsgHdr = &(msgIn->genlMsg);
> +    NL_BUFFER nlBuf;
> +    NL_ERROR nlError = NL_ERROR_SUCCESS;
> +    LOCK_STATE_EX lockState;
> +
> +    if (!NlAttrParse((PNL_MSG_HDR)msgIn,
> +                     NLMSG_HDRLEN + GENL_HDRLEN + OVS_HDRLEN,
> +                     NlMsgAttrsLen((PNL_MSG_HDR)msgIn),
> +                     nlMeterPolicy, ARRAY_SIZE(nlMeterPolicy),
> +                     meterAttrs, ARRAY_SIZE(meterAttrs))) {
> +        nlError = NL_ERROR_NOMSG;
> +        goto Done;
> +    }
> +
> +    NlBufInit(&nlBuf, usrParamsCtx->outputBuffer, 
> usrParamsCtx->outputLength);
> +    meterId = NlAttrGetU32(meterAttrs[OVS_METER_ATTR_ID]);
> +
> +    /* Reply message header */
> +    nlMsgOutHdr = (PNL_MSG_HDR)(NlBufAt(&nlBuf, 0, 0));
> +    ok = NlFillOvsMsg(&nlBuf, nlMsgHdr->nlmsgType, 0,
> +                      nlMsgHdr->nlmsgSeq, nlMsgHdr->nlmsgPid,
> +                      genlMsgHdr->cmd, OVS_METER_CMD_GET,
> +                      ovsHdr->dp_ifindex);
> +    if (!ok) {
> +        nlError = NL_ERROR_NOMSG;
> +        goto Done;
> +    }
> +
> +    NdisAcquireRWLockRead(meterGlobalTableLock, &lockState, 0);
> +    dpMeter = OvsMeterLookup(meterId);
> +    if (!dpMeter) {
> +        OVS_LOG_WARN("Has not find %d associated meter", meterId);
> +        nlError = NL_ERROR_EXIST;
> +        NdisReleaseRWLock(meterGlobalTableLock, &lockState);
> +        goto Done;
> +    }
> +
> +    if (!buildOvsMeterReplyMsg(&nlBuf, dpMeter)) {
> +        nlError = NL_ERROR_NOMEM;
> +        OVS_LOG_ERROR("Could not build ovs meter reply msg.");
> +        NdisReleaseRWLock(meterGlobalTableLock, &lockState);
> +        goto Done;
> +    }
> +
> +    NdisReleaseRWLock(meterGlobalTableLock, &lockState);
> +    NlMsgSetSize(nlMsgOutHdr, NlBufSize(&nlBuf));
> +    NlMsgAlignSize(nlMsgOutHdr);
> +    *replyLen += NlMsgSize(nlMsgOutHdr);
> +
> +Done:
> +    if (nlError != NL_ERROR_SUCCESS) {
> +        POVS_MESSAGE_ERROR msgError = (POVS_MESSAGE_ERROR)
> +                usrParamsCtx->outputBuffer;
> +
> +        ASSERT(msgError);
> +        NlBuildErrorMsg(msgIn, msgError, nlError, replyLen);
> +        ASSERT(*replyLen != 0);
> +    }
> +
> +    return NDIS_STATUS_SUCCESS;
> +}
> +
> +NDIS_STATUS
> +OvsMeterDestroy(POVS_USER_PARAMS_CONTEXT usrParamsCtx,
> +                UINT32 *replyLen)
> +{
> +    POVS_MESSAGE msgIn = (POVS_MESSAGE)usrParamsCtx->inputBuffer;
> +    PNL_MSG_HDR nlMsgHdr = &(msgIn->nlMsg);
> +    POVS_HDR ovsHdr = &(msgIn->ovsHdr);
> +    PNL_MSG_HDR nlMsgOutHdr = NULL;
> +    PNL_ATTR meterAttrs[ARRAY_SIZE(nlMeterPolicy)];
> +    PGENL_MSG_HDR genlMsgHdr = &(msgIn->genlMsg);
> +    LOCK_STATE_EX lockState;
> +    UINT32 meterId = 0;
> +    BOOLEAN ok;
> +    NL_BUFFER nlBuf;
> +    NL_ERROR nlError = NL_ERROR_SUCCESS;
> +
> +    if (!NlAttrParse((PNL_MSG_HDR)msgIn,
> +                     NLMSG_HDRLEN + GENL_HDRLEN + OVS_HDRLEN,
> +                     NlMsgAttrsLen((PNL_MSG_HDR)msgIn),
> +                     nlMeterPolicy, ARRAY_SIZE(nlMeterPolicy),
> +            meterAttrs, ARRAY_SIZE(meterAttrs))) {
> +        return STATUS_INVALID_PARAMETER;
> +    }
> +
> +    NlBufInit(&nlBuf, usrParamsCtx->outputBuffer, 
> usrParamsCtx->outputLength);
> +
> +    meterId = NlAttrGetU32(meterAttrs[OVS_METER_ATTR_ID]);
> +    nlMsgOutHdr = (PNL_MSG_HDR)(NlBufAt(&nlBuf, 0, 0));
> +    ok = NlFillOvsMsg(&nlBuf, nlMsgHdr->nlmsgType, 0,
> +                      nlMsgHdr->nlmsgSeq, nlMsgHdr->nlmsgPid,
> +                      genlMsgHdr->cmd, OVS_METER_CMD_DEL,
> +                      ovsHdr->dp_ifindex);
> +    if (!ok) {
> +        nlError = NL_ERROR_NOMEM;
> +        goto Done;
> +    }
> +
> +    NdisAcquireRWLockWrite(meterGlobalTableLock, &lockState, 0);
> +    PLIST_ENTRY head = &meterGlobalTable[meterId & (METER_HASH_BUCKET_MAX - 
> 1)];
> +    PLIST_ENTRY link, next;
> +    DpMeter *entry = NULL;
> +
> +    LIST_FORALL_SAFE(head, link, next) {
> +        entry = CONTAINING_RECORD(link, DpMeter, link);
> +        if (entry->id == meterId) {
> +            if (!buildOvsMeterReplyMsg(&nlBuf, entry)) {
> +                nlError = NL_ERROR_NOMEM;
> +                NdisReleaseRWLock(meterGlobalTableLock, &lockState);
> +                goto Done;
> +            }
> +            RemoveEntryList(&entry->link);
> +            OvsFreeMemoryWithTag(entry, OVS_METER_TAG);
> +            break;
> +        }
> +    }
> +
> +    NdisReleaseRWLock(meterGlobalTableLock, &lockState);
> +    NlMsgSetSize(nlMsgOutHdr, NlBufSize(&nlBuf));
> +    NlMsgAlignSize(nlMsgOutHdr);
> +    *replyLen += NlMsgSize(nlMsgOutHdr);
> +
> +Done:
> +    if (nlError != NL_ERROR_SUCCESS) {
> +        POVS_MESSAGE_ERROR msgError = (POVS_MESSAGE_ERROR)
> +                usrParamsCtx->outputBuffer;
> +
> +        ASSERT(msgError);
> +        NlBuildErrorMsg(msgIn, msgError, nlError, replyLen);
> +        ASSERT(*replyLen != 0);
> +    }
> +
> +    return NDIS_STATUS_SUCCESS;
> +}
> +
> +
> +
> +DpMeter*
> +OvsMeterLookup(UINT32 meterId)
> +{
> +    PLIST_ENTRY head = &meterGlobalTable[meterId & (METER_HASH_BUCKET_MAX - 
> 1)];
> +    PLIST_ENTRY link, next;
> +    DpMeter *entry = NULL;
> +
> +    LIST_FORALL_SAFE(head, link, next) {
> +        entry = CONTAINING_RECORD(link, DpMeter, link);
> +        if (entry->id == meterId) {
> +            return entry;
> +        }
> +    }
> +
> +    return NULL;
> +}
> +
> +BOOLEAN
> +OvsMeterExecute(OvsForwardingContext *fwdCtx, UINT32 meterId)
> +{
> +    DpMeter *dpMeter;
> +    DpMeterBand *band;
> +    UINT32 longDeltaMs;
> +    UINT32 deltaMs;
> +    UINT64 currentTime;
> +    LOCK_STATE_EX lockState;
> +    UINT32 cost;
> +    UINT32 bandExceededRate = 0;
> +    INT32 bandExceedIndex = -1;
> +    UINT64 maxBucketSize = 0;
> +
> +    NdisAcquireRWLockRead(meterGlobalTableLock, &lockState, 0);
> +    dpMeter = OvsMeterLookup(meterId);
> +    if (!dpMeter) {
> +        OVS_LOG_ERROR("Not found meter id %d associated meter.", meterId);
> +        NdisReleaseRWLock(meterGlobalTableLock, &lockState);
> +        return FALSE;
> +    }
> +
> +    NdisGetCurrentSystemTime((LARGE_INTEGER *)&currentTime);
> +    longDeltaMs = (UINT32)((currentTime - dpMeter->used) / 10000);

[AS] Why 10000 here? Can you please add a comment for it?
Because “currentTime” here represent count of 100-nanosecond intervals, thus 
divide 10000 to convert the unit to ms.




> +    deltaMs = longDeltaMs > dpMeter->maxDelta ? dpMeter->maxDelta :
> +                longDeltaMs;
> +    dpMeter->used = currentTime;
> +    dpMeter->stats.n_packets += 1;
> +    dpMeter->stats.n_bytes += OvsPacketLenNBL(fwdCtx->curNbl);
> +    cost = dpMeter->kbps ? OvsPacketLenNBL(fwdCtx->curNbl) * 8 : 1000;
> +    for (int index = 0; index < dpMeter->nBands; index++) {
> +        band = &(dpMeter->bands[index]);
> +        maxBucketSize = (band->burst_size + band->rate) * 1000LL;
> +        band->bucket += deltaMs * band->rate;
> +        if (band->bucket > maxBucketSize) {
> +            band->bucket = maxBucketSize;
> +        }
> +
> +        if (band->bucket >= cost) {
> +            band->bucket -= cost;
> +            band->stats.n_packets += 1;
> +            band->stats.n_bytes += OvsPacketLenNBL(fwdCtx->curNbl);
> +        } else if (band->rate > bandExceededRate) {
> +            bandExceededRate = band->rate;
> +            bandExceedIndex = index;
> +        }
> +    }
> +
> +    if (bandExceedIndex >= 0) {
> +        band = &(dpMeter->bands[bandExceedIndex]);
> +        band->stats.n_packets += 1;
> +        band->stats.n_bytes += OvsPacketLenNBL(fwdCtx->curNbl);
> +        if (band->type == OVS_METER_BAND_TYPE_DROP) {
> +            NdisReleaseRWLock(meterGlobalTableLock, &lockState);
> +            return TRUE;
> +        }
> +    }
> +
> +    NdisReleaseRWLock(meterGlobalTableLock, &lockState);
> +    return FALSE;
> +}
> \ No newline at end of file

[AS] Nit. Please add new line.
Will fix it.


> diff --git a/datapath-windows/ovsext/Meter.h b/datapath-windows/ovsext/Meter.h
> new file mode 100644
> index 000000000..bc94489a5
> --- /dev/null
> +++ b/datapath-windows/ovsext/Meter.h
> @@ -0,0 +1,68 @@
> +/*
> + * Copyright (c) 2022 VMware, Inc.
> + *
> + * Licensed under the Apache License, Version 2.0 (the "License");
> + * you may not use this file except in compliance with the License.
> + * You may obtain a copy of the License at:
> + *
> + *     
> https://nam04.safelinks.protection.outlook.com/?url=http%3A%2F%2Fwww.apache.org%2Flicenses%2FLICENSE-2.0&amp;data=05%7C01%7Cldejing%40vmware.com%7Cf3637760b1f648439dcb08da7a0a3a46%7Cb39138ca3cee4b4aa4d6cd83d9dd62f0%7C0%7C0%7C637956481621685933%7CUnknown%7CTWFpbGZsb3d8eyJWIjoiMC4wLjAwMDAiLCJQIjoiV2luMzIiLCJBTiI6Ik1haWwiLCJXVCI6Mn0%3D%7C3000%7C%7C%7C&amp;sdata=X1o%2BHH8z7OH95mtHRLPW0ZilompVkeMxx1hSemmumbs%3D&amp;reserved=0<https://nam04.safelinks.protection.outlook.com/?url=http%3A%2F%2Fwww.apache.org%2Flicenses%2FLICENSE-2.0&data=05%7C01%7Cldejing%40vmware.com%7C1cc952b1c18148f0405408da7f70d971%7Cb39138ca3cee4b4aa4d6cd83d9dd62f0%7C0%7C0%7C637962419936897924%7CUnknown%7CTWFpbGZsb3d8eyJWIjoiMC4wLjAwMDAiLCJQIjoiV2luMzIiLCJBTiI6Ik1haWwiLCJXVCI6Mn0%3D%7C3000%7C%7C%7C&sdata=Emq9OMXvvmtYQwWixb2ssGqVJF2QKhj81kUv3LyfExo%3D&reserved=0>
> + *
> + * Unless required by applicable law or agreed to in writing, software
> + * distributed under the License is distributed on an "AS IS" BASIS,
> + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
> + * See the License for the specific language governing permissions and
> + * limitations under the License.
> + */
> +
> +#ifndef OVS_METER_H
> +#define OVS_METER_H
> +
> +#include "precomp.h"
> +#include "Switch.h"
> +#include "User.h"
> +#include "Datapath.h"
> +#include "Event.h"
> +#include "NetProto.h"
> +#include "Netlink/Netlink.h"
> +#include "Flow.h"
> +
> +#define OVS_MAX_BANDS 1
> +#define OVS_MAX_METERS 32
> +#define METER_HASH_BUCKET_MAX 1024
> +
> +typedef struct _DpMeterBand {
> +    UINT32 type;
> +    UINT32 rate;
> +    UINT32 burst_size;
> +    UINT64 bucket;
> +    struct ovs_flow_stats stats;
> +} DpMeterBand;
> +
> +typedef struct _DpMeter {
> +    LIST_ENTRY link;
> +    UINT32 id;
> +    UINT16 kbps:1;
> +    UINT16 keepStatus:1;
> +    UINT16 nBands;
> +    UINT32 maxDelta;
> +    UINT64 used;
> +    struct ovs_flow_stats stats;
> +    DpMeterBand  bands[OVS_MAX_BANDS];
> +} DpMeter;
> +
> +NTSTATUS OvsInitMeter(POVS_SWITCH_CONTEXT context);
> +NDIS_STATUS OvsNewMeterCmdHandler(POVS_USER_PARAMS_CONTEXT usrParamsCtx,
> +                                  UINT32 *replyLen);
> +NDIS_STATUS OvsMeterFeatureProbe(POVS_USER_PARAMS_CONTEXT usrParamsCtx,
> +UINT32 *replyLen);
> +
> +NDIS_STATUS OvsMeterDestroy(POVS_USER_PARAMS_CONTEXT usrParamsCtx,
> +                                   UINT32 *replyLen);
> +DpMeter* OvsMeterLookup(UINT32 meterId);
> +BOOLEAN
> +OvsMeterExecute(OvsForwardingContext *fwdCtx, UINT32 meterId);
> +BOOLEAN
> +buildOvsMeterReplyMsg(NL_BUFFER *nlBuf, DpMeter *dpMeter);
> +
> +
> +#endif //OVS_METER_H
> diff --git a/datapath-windows/ovsext/Switch.c 
> b/datapath-windows/ovsext/Switch.c
> index 1ac4fa77c..088aee21a 100644
> --- a/datapath-windows/ovsext/Switch.c
> +++ b/datapath-windows/ovsext/Switch.c
> @@ -24,6 +24,7 @@
>  #include "Switch.h"
>  #include "Vport.h"
>  #include "Event.h"
> +#include "Meter.h"
>  #include "Flow.h"
>  #include "IpHelper.h"
>  #include "Oid.h"
> @@ -239,6 +240,13 @@ OvsCreateSwitch(NDIS_HANDLE ndisFilterHandle,
>          goto create_switch_done;
>      }
>
> +    status = OvsInitMeter(switchContext);
> +    if (status != STATUS_SUCCESS) {
> +        OvsUninitSwitchContext(switchContext);
> +        OVS_LOG_ERROR("Exit: Failed to initialize Ovs meter.");
> +        goto create_switch_done;
> +    }
> +
>      *switchContextOut = switchContext;
>
>  create_switch_done:
> diff --git a/datapath-windows/ovsext/Util.h b/datapath-windows/ovsext/Util.h
> index f63a885a9..16903ed46 100644
> --- a/datapath-windows/ovsext/Util.h
> +++ b/datapath-windows/ovsext/Util.h
> @@ -38,6 +38,7 @@
>  #define OVS_TUNFLT_POOL_TAG             'WSVO'
>  #define OVS_RECIRC_POOL_TAG             'CSVO'
>  #define OVS_CT_POOL_TAG                 'CTVO'
> +#define OVS_METER_TAG                   'MEVO'
>  #define OVS_GENEVE_POOL_TAG             'GNVO'
>  #define OVS_IPFRAG_POOL_TAG             'FGVO'
>
> diff --git a/datapath-windows/ovsext/ovsext.vcxproj 
> b/datapath-windows/ovsext/ovsext.vcxproj
> index 7a2cbd2de..6e54b71f9 100644
> --- a/datapath-windows/ovsext/ovsext.vcxproj
> +++ b/datapath-windows/ovsext/ovsext.vcxproj
> @@ -164,6 +164,7 @@
>      <ClInclude Include="IpFragment.h" />
>      <ClInclude Include="IpHelper.h" />
>      <ClInclude Include="Jhash.h" />
> +    <ClInclude Include="Meter.h" />
>      <ClInclude Include="Mpls.h" />
>      <ClInclude Include="Netlink/Netlink.h" />
>      <ClInclude Include="Netlink/NetlinkBuf.h" />
> @@ -408,6 +409,7 @@
>      <ClCompile Include="IpFragment.c" />
>      <ClCompile Include="IpHelper.c" />
>      <ClCompile Include="Jhash.c" />
> +    <ClCompile Include="Meter.c" />
>      <ClCompile Include="Netlink/Netlink.c" />
>      <ClCompile Include="Netlink/NetlinkBuf.c" />
>      <ClCompile Include="Datapath.c" />
> diff --git a/lib/netlink-socket.c b/lib/netlink-socket.c
> index 93c1fa561..80da20d9f 100644
> --- a/lib/netlink-socket.c
> +++ b/lib/netlink-socket.c
> @@ -1574,6 +1574,11 @@ do_lookup_genl_family(const char *name, struct nlattr 
> **attrs,
>          family_name = OVS_FLOW_FAMILY;
>          family_version = OVS_FLOW_VERSION;
>          family_attrmax = OVS_FLOW_ATTR_MAX;
> +    } else if (!strcmp(name, OVS_METER_FAMILY)) {
> +        family_id =  OVS_WIN_NL_METER_FAMILY_ID;
> +        family_name = OVS_METER_FAMILY;
> +        family_version = OVS_METER_VERSION;
> +        family_attrmax = __OVS_METER_ATTR_MAX;
>      } else if (!strcmp(name, OVS_WIN_NETDEV_FAMILY)) {
>          family_id = OVS_WIN_NL_NETDEV_FAMILY_ID;
>          family_name = OVS_WIN_NETDEV_FAMILY;

________________________________

⚠ External Email: This email originated from outside of the organization. Do 
not click links or open attachments unless you recognize the sender.
_______________________________________________
dev mailing list
d...@openvswitch.org
https://mail.openvswitch.org/mailman/listinfo/ovs-dev

Reply via email to