2
0
mirror of https://github.com/openvswitch/ovs synced 2025-09-03 07:45:30 +00:00

datapath-windows: Implement vport dump Netlink command.

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 now.
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@cloudbasesolutions.com>
Acked-by: Alin Gabriel Serdean <aserdean@cloudbasesolutions.com>
Acked-by: Nithin Raju <nithin@vmware.com>
Signed-off-by: Ben Pfaff <blp@nicira.com>
This commit is contained in:
Samuel Ghinet
2014-09-25 21:21:33 +00:00
committed by Ben Pfaff
parent e00afcf642
commit 17c6a05f08
2 changed files with 276 additions and 8 deletions

View File

@@ -99,7 +99,8 @@ static NetlinkCmdHandler OvsGetPidCmdHandler,
OvsGetDpCmdHandler,
OvsPendEventCmdHandler,
OvsSubscribeEventCmdHandler,
OvsSetDpCmdHandler;
OvsSetDpCmdHandler,
OvsGetVportCmdHandler;
static NTSTATUS HandleGetDpTransaction(POVS_USER_PARAMS_CONTEXT usrParamsCtx,
UINT32 *replyLen);
@@ -180,14 +181,22 @@ NETLINK_FAMILY nlPacketFamilyOps = {
};
/* Netlink vport family. */
/* XXX: Add commands here. */
NETLINK_CMD nlVportFamilyCmdOps[] = {
{ .cmd = OVS_VPORT_CMD_GET,
.handler = OvsGetVportCmdHandler,
.supportedDevOp = OVS_WRITE_DEV_OP | OVS_READ_DEV_OP |
OVS_TRANSACTION_DEV_OP,
.validateDpIndex = TRUE
}
};
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. */
@@ -691,10 +700,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;
@@ -1179,6 +1189,257 @@ OvsSetupDumpStart(POVS_USER_PARAMS_CONTEXT usrParamsCtx)
return InitUserDumpState(instance, msgIn);
}
static VOID
BuildMsgOut(POVS_MESSAGE msgIn, POVS_MESSAGE msgOut, UINT16 type,
UINT32 length, UINT16 flags)
{
msgOut->nlMsg.nlmsgType = type;
msgOut->nlMsg.nlmsgFlags = flags;
msgOut->nlMsg.nlmsgSeq = msgIn->nlMsg.nlmsgSeq;
msgOut->nlMsg.nlmsgPid = msgIn->nlMsg.nlmsgPid;
msgOut->nlMsg.nlmsgLen = length;
msgOut->genlMsg.cmd = msgIn->genlMsg.cmd;
msgOut->genlMsg.version = nlDatapathFamilyOps.version;
msgOut->genlMsg.reserved = 0;
}
static VOID
BuildReplyMsgFromMsgIn(POVS_MESSAGE msgIn, POVS_MESSAGE msgOut, UINT16 flags)
{
BuildMsgOut(msgIn, msgOut, msgIn->nlMsg.nlmsgType, sizeof(OVS_MESSAGE),
flags);
}
static NTSTATUS
OvsCreateMsgFromVport(POVS_VPORT_ENTRY vport,
POVS_MESSAGE msgIn,
PVOID outBuffer,
UINT32 outBufLen,
int dpIfIndex)
{
NL_BUFFER nlBuffer;
OVS_VPORT_FULL_STATS vportStats;
BOOLEAN ok;
OVS_MESSAGE msgOut;
PNL_MSG_HDR nlMsg;
NlBufInit(&nlBuffer, outBuffer, outBufLen);
BuildReplyMsgFromMsgIn(msgIn, &msgOut, NLM_F_MULTI);
msgOut.ovsHdr.dp_ifindex = dpIfIndex;
ok = NlMsgPutHead(&nlBuffer, (PCHAR)&msgOut, sizeof msgOut);
if (!ok) {
return STATUS_INSUFFICIENT_RESOURCES;
}
ok = NlMsgPutTailU32(&nlBuffer, OVS_VPORT_ATTR_PORT_NO, vport->portNo);
if (!ok) {
return STATUS_INSUFFICIENT_RESOURCES;
}
ok = NlMsgPutTailU32(&nlBuffer, OVS_VPORT_ATTR_TYPE, vport->ovsType);
if (!ok) {
return STATUS_INSUFFICIENT_RESOURCES;
}
ok = NlMsgPutTailString(&nlBuffer, OVS_VPORT_ATTR_NAME, vport->ovsName);
if (!ok) {
return STATUS_INSUFFICIENT_RESOURCES;
}
/*
* 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.
*/
ok = NlMsgPutTailU32(&nlBuffer, OVS_VPORT_ATTR_UPCALL_PID,
vport->upcallPid);
if (!ok) {
return STATUS_INSUFFICIENT_RESOURCES;
}
/*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;
ok = NlMsgPutTailUnspec(&nlBuffer, OVS_VPORT_ATTR_STATS,
(PCHAR)&vportStats,
sizeof(OVS_VPORT_FULL_STATS));
if (!ok) {
return STATUS_INSUFFICIENT_RESOURCES;
}
/*
* XXX: when vxlan udp dest port becomes configurable, we will also need
* to add vport options
*/
nlMsg = (PNL_MSG_HDR)NlBufAt(&nlBuffer, 0, 0);
nlMsg->nlmsgLen = NlBufSize(&nlBuffer);
return STATUS_SUCCESS;
}
static NTSTATUS
OvsGetVportDumpNext(POVS_USER_PARAMS_CONTEXT usrParamsCtx,
UINT32 *replyLen)
{
POVS_MESSAGE msgIn;
POVS_OPEN_INSTANCE instance =
(POVS_OPEN_INSTANCE)usrParamsCtx->ovsInstance;
LOCK_STATE_EX lockState;
UINT32 i = OVS_MAX_VPORT_ARRAY_SIZE;
/*
* 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(usrParamsCtx->outputBuffer != NULL);
msgIn = instance->dumpState.ovsMsg;
OvsAcquireCtrlLock();
if (!gOvsSwitchContext) {
/* Treat this as a dump done. */
OvsReleaseCtrlLock();
*replyLen = 0;
FreeUserDumpState(instance);
return STATUS_SUCCESS;
}
/*
* 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;
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);
if (vport->portNo != 0) {
OvsCreateMsgFromVport(vport, msgIn,
usrParamsCtx->outputBuffer,
usrParamsCtx->outputLength,
gOvsSwitchContext->dpNo);
++outIndex;
break;
} else {
vport = NULL;
}
}
++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();
/* if i < OVS_MAX_VPORT_ARRAY_SIZE => vport was found */
if (i < OVS_MAX_VPORT_ARRAY_SIZE) {
POVS_MESSAGE msgOut = (POVS_MESSAGE)usrParamsCtx->outputBuffer;
*replyLen = msgOut->nlMsg.nlmsgLen;
} else {
/*
* if i >= OVS_MAX_VPORT_ARRAY_SIZE => vport was not found =>
* it's dump done
*/
*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)
{
switch (usrParamsCtx->devOp)
{
case OVS_WRITE_DEV_OP:
*replyLen = 0;
return OvsSetupDumpStart(usrParamsCtx);
case OVS_READ_DEV_OP:
return OvsGetVportDumpNext(usrParamsCtx, replyLen);
default:
return STATUS_INVALID_DEVICE_REQUEST;
}
}
/*
* --------------------------------------------------------------------------
* Utility function to map the output buffer in an IRP. The buffer is assumed

View File

@@ -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
@@ -87,6 +93,7 @@ typedef struct _OVS_VPORT_ENTRY {
NDIS_VM_NAME vmName;
GUID netCfgInstanceId;
BOOLEAN isExternal;
UINT32 upcallPid; /* netlink upcall port id */
} OVS_VPORT_ENTRY, *POVS_VPORT_ENTRY;
struct _OVS_SWITCH_CONTEXT;