[ovs-dev] [PATCH v2] datapath-windows: Netlink command: vport dump

Samuel Ghinet sghinet at cloudbasesolutions.com
Wed Sep 17 13:08:35 UTC 2014


Functionality for vport dump.
Later, when we will add more netlink dump commands, some common code will need
to be split to functions.

Notes:
a) the current implementation of vport assumes the datapath feature
"multiple upcall pids" is not used. A single upcall pid is used instead.
c) the vxlan destination udp port is currently a constant. When it will become
configurable, the vport options netlink attribute will become relevant.

Signed-off-by: Samuel Ghinet <sghinet at cloudbasesolutions.com>
---
 datapath-windows/ovsext/Datapath.c | 222 ++++++++++++++++++++++++++++++++++++-
 datapath-windows/ovsext/Vport.h    |  11 +-
 2 files changed, 227 insertions(+), 6 deletions(-)

diff --git a/datapath-windows/ovsext/Datapath.c b/datapath-windows/ovsext/Datapath.c
index d90ae23..5e9aadf 100644
--- a/datapath-windows/ovsext/Datapath.c
+++ b/datapath-windows/ovsext/Datapath.c
@@ -101,6 +101,9 @@ static NTSTATUS OvsGetPidCmdHandler(POVS_USER_PARAMS_CONTEXT usrParamsCtx,
 static NTSTATUS OvsGetDpCmdHandler(POVS_USER_PARAMS_CONTEXT usrParamsCtx,
                                    UINT32 *replyLen);
 
+static NTSTATUS OvsGetVportCmdHandler(POVS_USER_PARAMS_CONTEXT usrParamsCtx,
+                                      UINT32 *replyLen);
+
 /*
  * The various netlink families, along with the supported commands. Most of
  * these families and commands are part of the openvswitch specification for a
@@ -156,14 +159,20 @@ NETLINK_FAMILY nlPacketFamilyOps = {
 };
 
 /* Netlink vport family. */
+NETLINK_CMD nlVportFamilyCmdOps[] = {
+    { OVS_VPORT_CMD_GET, OvsGetVportCmdHandler,
+    OVS_WRITE_DEV_OP | OVS_READ_DEV_OP, FALSE }
+};
+
+/* Netlink vport family. */
 /* XXX: Add commands here. */
 NETLINK_FAMILY nlVportFamilyOps = {
     .name     = OVS_VPORT_FAMILY,
     .id       = OVS_WIN_NL_VPORT_FAMILY_ID,
     .version  = OVS_VPORT_VERSION,
     .maxAttr  = OVS_VPORT_ATTR_MAX,
-    .cmds     = NULL, /* XXX: placeholder. */
-    .opsCount = 0
+    .cmds     = nlVportFamilyCmdOps,
+    .opsCount = ARRAY_SIZE(nlVportFamilyCmdOps)
 };
 
 /* Netlink flow family. */
@@ -668,10 +677,11 @@ OvsDeviceControl(PDEVICE_OBJECT deviceObject,
         break;
     case OVS_WIN_NL_PACKET_FAMILY_ID:
     case OVS_WIN_NL_FLOW_FAMILY_ID:
-    case OVS_WIN_NL_VPORT_FAMILY_ID:
         status = STATUS_NOT_IMPLEMENTED;
         goto done;
-
+    case OVS_WIN_NL_VPORT_FAMILY_ID:
+        nlFamilyOps = &nlVportFamilyOps;
+        break;
     default:
         status = STATUS_INVALID_PARAMETER;
         goto done;
@@ -966,6 +976,210 @@ OvsSetupDumpStart(POVS_USER_PARAMS_CONTEXT usrParamsCtx)
 }
 
 /*
+* XXX: When "dump done" and "error" netlink types will be implemented, we will
+* need a few more BuildMsgOut functions, which would ultimately call a generic
+* BuildMsgOutFromMsgIn.
+*/
+
+static VOID
+BuildMsgOutFromMsgIn(POVS_MESSAGE msgIn, POVS_MESSAGE msgOut, UINT16 flags)
+{
+    msgOut->nlMsg.nlmsgType = msgIn->nlMsg.nlmsgType;
+    msgOut->nlMsg.nlmsgFlags = flags;
+    msgOut->nlMsg.nlmsgSeq = msgIn->nlMsg.nlmsgSeq;
+    msgOut->nlMsg.nlmsgPid = msgIn->nlMsg.nlmsgPid;
+    msgOut->nlMsg.nlmsgLen = sizeof *msgOut;
+
+    msgOut->genlMsg.cmd = msgIn->genlMsg.cmd;
+    msgOut->genlMsg.version = nlDatapathFamilyOps.version;
+    msgOut->genlMsg.reserved = 0;
+}
+
+static NTSTATUS
+OvsCreateMsgFromVport(POVS_VPORT_ENTRY vport,
+                      POVS_MESSAGE msgOut,
+                      UINT32 maxPayloadSize)
+{
+    NL_BUFFER nlBuffer;
+    OVS_VPORT_FULL_STATS vportStats;
+
+    NlBufInit(&nlBuffer, (PCHAR)msgOut + sizeof (*msgOut), maxPayloadSize);
+
+    NlMsgPutTailU32(&nlBuffer, OVS_VPORT_ATTR_PORT_NO, vport->portNo);
+    NlMsgPutTailU32(&nlBuffer, OVS_VPORT_ATTR_TYPE, vport->ovsType);
+
+    NlMsgPutTailString(&nlBuffer, OVS_VPORT_ATTR_NAME, vport->ovsName);
+
+    /*
+    * XXX: when we implement OVS_DP_ATTR_USER_FEATURES in datapath,
+    * we'll need to check the OVS_DP_F_VPORT_PIDS flag: if it is set,
+    * it means we have an array of pids, instead of a single pid.
+    * ATM we assume we have one pid only.
+    */
+
+    NlMsgPutTailU32(&nlBuffer, OVS_VPORT_ATTR_UPCALL_PID, vport->upcallPid);
+
+    /*stats*/
+    vportStats.rxPackets = vport->stats.rxPackets;
+    vportStats.rxBytes = vport->stats.rxBytes;
+    vportStats.txPackets = vport->stats.txPackets;
+    vportStats.txBytes = vport->stats.txBytes;
+    vportStats.rxErrors = vport->errStats.rxErrors;
+    vportStats.txErrors = vport->errStats.txErrors;
+    vportStats.rxDropped = vport->errStats.rxDropped;
+    vportStats.txDropped = vport->errStats.txDropped;
+
+    NlMsgPutTailUnspec(&nlBuffer, OVS_VPORT_ATTR_STATS, (PCHAR)&vportStats,
+              sizeof(OVS_VPORT_FULL_STATS));
+
+    /*
+    * XXX: when vxlan udp dest port becomes configurable, we will also need
+    * to add vport options
+    */
+
+    msgOut->nlMsg.nlmsgLen = NlBufSize(&nlBuffer) + sizeof(OVS_MESSAGE);
+
+    return STATUS_SUCCESS;
+}
+
+static NTSTATUS
+OvsGetVportDumpNext(POVS_USER_PARAMS_CONTEXT usrParamsCtx,
+                    UINT32 *replyLen)
+{
+    POVS_MESSAGE msgIn;
+    POVS_MESSAGE msgOut = (POVS_MESSAGE)usrParamsCtx->outputBuffer;
+    POVS_OPEN_INSTANCE instance =
+        (POVS_OPEN_INSTANCE)usrParamsCtx->ovsInstance;
+    LOCK_STATE_EX lockState;
+    UINT32 i = 0;
+
+    /*
+    * XXX: this function shares some code with other dump command(s).
+    * In the future, we will need to refactor the dump functions
+    */
+
+    ASSERT(usrParamsCtx->devOp == OVS_READ_DEV_OP);
+
+    if (instance->dumpState.ovsMsg == NULL) {
+        ASSERT(FALSE);
+        return STATUS_INVALID_DEVICE_STATE;
+    }
+
+    /* Output buffer has been validated while validating read dev op. */
+    ASSERT(msgOut != NULL && usrParamsCtx->outputLength >= sizeof *msgOut);
+
+    msgIn = instance->dumpState.ovsMsg;
+    BuildMsgOutFromMsgIn(msgIn, msgOut, NLM_F_MULTI);
+
+    OvsAcquireCtrlLock();
+    if (!gOvsSwitchContext) {
+        /* Treat this as a dump done. */
+        OvsReleaseCtrlLock();
+        *replyLen = 0;
+        FreeUserDumpState(instance);
+        return STATUS_SUCCESS;
+    }
+
+    msgOut->ovsHdr.dp_ifindex = gOvsSwitchContext->dpNo;
+
+    /*
+    * XXX: when we implement OVS_DP_ATTR_USER_FEATURES in datapath,
+    * we'll need to check the OVS_DP_F_VPORT_PIDS flag: if it is set,
+    * it means we have an array of pids, instead of a single pid.
+    * ATM we assume we have one pid only.
+    */
+
+    NdisAcquireRWLockRead(gOvsSwitchContext->dispatchLock, &lockState, 0);
+
+    if (gOvsSwitchContext->numVports > 0) {
+        /* inBucket: the bucket, used for lookup */
+        UINT32 inBucket = instance->dumpState.index[0];
+        /* inIndex: index within the given bucket, used for lookup */
+        UINT32 inIndex = instance->dumpState.index[1];
+        /* the bucket to be used for the next dump operation */
+        UINT32 outBucket = 0;
+        /* the index within the outBucket to be used for the next dump */
+        UINT32 outIndex = 0;
+        UINT32 maxSize = usrParamsCtx->outputLength;
+
+        for (i = inBucket; i < OVS_MAX_VPORT_ARRAY_SIZE; i++) {
+            PLIST_ENTRY head, link;
+            head = &(gOvsSwitchContext->portHashArray[i]);
+            POVS_VPORT_ENTRY vport = NULL;
+
+            outIndex = 0;
+            LIST_FORALL(head, link) {
+
+                /*
+                * if one or more dumps were previously done on this same bucket,
+                * inIndex will be > 0, so we'll need to reply with the
+                * inIndex + 1 vport from the bucket.
+                */
+                if (outIndex >= inIndex) {
+                    vport = CONTAINING_RECORD(link, OVS_VPORT_ENTRY, portLink);
+                    OvsCreateMsgFromVport(vport, msgOut, maxSize);
+                    ++outIndex;
+                    break;
+                }
+
+                ++outIndex;
+            }
+
+            if (vport) {
+                break;
+            }
+
+            /*
+            * if no vport was found above, check the next bucket, beginning
+            * with the first (i.e. index 0) elem from within that bucket
+            */
+            inIndex = 0;
+        }
+
+        outBucket = i;
+
+        /* XXX: what about NLMSG_DONE (as msg type)? */
+        instance->dumpState.index[0] = outBucket;
+        instance->dumpState.index[1] = outIndex;
+    }
+
+    NdisReleaseRWLock(gOvsSwitchContext->dispatchLock, &lockState);
+
+    OvsReleaseCtrlLock();
+
+    *replyLen = msgOut->nlMsg.nlmsgLen;
+
+    /* if i is OVS_MAX_VPORT_ARRAY_SIZE => no vport was found => dump done*/
+    if (i == OVS_MAX_VPORT_ARRAY_SIZE) {
+        *replyLen = 0;
+        /* Free up the dump state, since there's no more data to continue. */
+        FreeUserDumpState(instance);
+    }
+
+    return STATUS_SUCCESS;
+}
+
+/*
+* --------------------------------------------------------------------------
+*  Handler for the get vport command. The function handles the initial call to
+*  setup the dump state, as well as subsequent calls to continue dumping data.
+* --------------------------------------------------------------------------
+*/
+static NTSTATUS
+OvsGetVportCmdHandler(POVS_USER_PARAMS_CONTEXT usrParamsCtx,
+                      UINT32 *replyLen)
+{
+    if (usrParamsCtx->devOp == OVS_WRITE_DEV_OP) {
+        *replyLen = 0;
+        OvsSetupDumpStart(usrParamsCtx);
+    } else {
+        return OvsGetVportDumpNext(usrParamsCtx, replyLen);
+    }
+
+    return STATUS_SUCCESS;
+}
+
+/*
  * --------------------------------------------------------------------------
  *  Utility function to map the output buffer in an IRP. The buffer is assumed
  *  to have been passed down using METHOD_OUT_DIRECT (Direct I/O).
diff --git a/datapath-windows/ovsext/Vport.h b/datapath-windows/ovsext/Vport.h
index 80bdea8..0305d35 100644
--- a/datapath-windows/ovsext/Vport.h
+++ b/datapath-windows/ovsext/Vport.h
@@ -39,10 +39,10 @@ typedef enum {
 } OVS_VPORT_STATE;
 
 typedef struct _OVS_VPORT_STATS {
-    UINT64 rxBytes;
     UINT64 rxPackets;
-    UINT64 txBytes;
     UINT64 txPackets;
+    UINT64 rxBytes;
+    UINT64 txBytes;
 } OVS_VPORT_STATS;
 
 typedef struct _OVS_VPORT_ERR_STATS {
@@ -51,6 +51,12 @@ typedef struct _OVS_VPORT_ERR_STATS {
     UINT64  rxDropped;
     UINT64  txDropped;
 } OVS_VPORT_ERR_STATS;
+
+/* used for vport netlink commands. */
+typedef struct _OVS_VPORT_FULL_STATS {
+    OVS_VPORT_STATS;
+    OVS_VPORT_ERR_STATS;
+}OVS_VPORT_FULL_STATS;
 /*
  * Each internal, external adapter or vritual adapter has
  * one vport entry. In addition, we have one vport for each
@@ -86,6 +92,7 @@ typedef struct _OVS_VPORT_ENTRY {
     NDIS_SWITCH_NIC_NAME   nicName;
     NDIS_VM_NAME           vmName;
     GUID                   netCfgInstanceId;
+    UINT32                 upcallPid; /* netlink upcall port id */
 } OVS_VPORT_ENTRY, *POVS_VPORT_ENTRY;
 
 struct _OVS_SWITCH_CONTEXT;
-- 
1.8.3.msysgit.0




More information about the dev mailing list