Open vSwitch CI 3f9b5c
diff --git a/.ci/linux-prepare.sh b/.ci/linux-prepare.sh
Open vSwitch CI ee63f1
index c55125cf78..360c0a68ea 100755
Open vSwitch CI 3f9b5c
--- a/.ci/linux-prepare.sh
Open vSwitch CI 3f9b5c
+++ b/.ci/linux-prepare.sh
Open vSwitch CI ee63f1
@@ -21,8 +21,7 @@ make -j4 HAVE_LLVM= HAVE_SQLITE= install
Open vSwitch CI ee63f1
 cd ..
Open vSwitch CI 3f9b5c
 
Open vSwitch CI 3f9b5c
 pip3 install --disable-pip-version-check --user \
Open vSwitch CI ee63f1
-    flake8 hacking sphinx pyOpenSSL wheel setuptools
Open vSwitch CI 3f9b5c
-pip3 install --user --upgrade docutils
Open vSwitch CI ee63f1
+    flake8 hacking sphinx wheel setuptools
Open vSwitch CI 3f9b5c
 pip3 install --user  'meson==0.47.1'
Open vSwitch CI 3f9b5c
 
Open vSwitch CI 3f9b5c
 if [ "$M32" ]; then
Open vSwitch CI 3f9b5c
diff --git a/.cirrus.yml b/.cirrus.yml
Open vSwitch CI ee63f1
index 358f2ba256..a7ae793bc4 100644
Open vSwitch CI 3f9b5c
--- a/.cirrus.yml
Open vSwitch CI 3f9b5c
+++ b/.cirrus.yml
Open vSwitch CI ee63f1
@@ -5,11 +5,11 @@ freebsd_build_task:
Open vSwitch CI 3f9b5c
       image_family: freebsd-12-2-snap
Open vSwitch CI 3f9b5c
       image_family: freebsd-11-4-snap
Open vSwitch CI 3f9b5c
     cpu: 4
Open vSwitch CI 3f9b5c
-    memory: 8G
Open vSwitch CI 3f9b5c
+    memory: 4G
Open vSwitch CI 3f9b5c
 
Open vSwitch CI 3f9b5c
   env:
Open vSwitch CI 3f9b5c
     DEPENDENCIES: automake libtool gmake gcc wget openssl python3
Open vSwitch CI ee63f1
-    PY_DEPS:      sphinx|openssl
Open vSwitch CI ee63f1
+    PY_DEPS:      sphinx
Open vSwitch CI ee63f1
     matrix:
Open vSwitch CI ee63f1
       COMPILER: gcc
Open vSwitch CI ee63f1
       COMPILER: clang
Open vSwitch CI 3f9b5c
diff --git a/.github/workflows/build-and-test.yml b/.github/workflows/build-and-test.yml
Open vSwitch CI 3f9b5c
index e2350c6d9d..7434ad18ec 100644
Open vSwitch CI 3f9b5c
--- a/.github/workflows/build-and-test.yml
Open vSwitch CI 3f9b5c
+++ b/.github/workflows/build-and-test.yml
Open vSwitch CI 3f9b5c
@@ -127,7 +127,7 @@ jobs:
Open vSwitch CI 3f9b5c
     - name: set up python
Open vSwitch CI 3f9b5c
       uses: actions/setup-python@v2
Open vSwitch CI 3f9b5c
       with:
Open vSwitch CI 3f9b5c
-        python-version: '3.x'
Open vSwitch CI 3f9b5c
+        python-version: '3.9'
Open vSwitch CI 3f9b5c
 
Open vSwitch CI 3f9b5c
     - name: create ci signature file for the dpdk cache key
Open vSwitch CI 3f9b5c
       if:   matrix.dpdk != '' || matrix.dpdk_shared != ''
Open vSwitch CI 3f9b5c
@@ -215,7 +215,7 @@ jobs:
Open vSwitch CI 3f9b5c
     - name: set up python
Open vSwitch CI 3f9b5c
       uses: actions/setup-python@v2
Open vSwitch CI 3f9b5c
       with:
Open vSwitch CI 3f9b5c
-        python-version: '3.x'
Open vSwitch CI 3f9b5c
+        python-version: '3.9'
Open vSwitch CI 3f9b5c
     - name: install dependencies
Open vSwitch CI 3f9b5c
       run:  brew install automake libtool
Open vSwitch CI 3f9b5c
     - name: prepare
Open vSwitch CI ee63f1
diff --git a/.travis.yml b/.travis.yml
Open vSwitch CI ee63f1
index 51d0511080..c7aeede06e 100644
Open vSwitch CI ee63f1
--- a/.travis.yml
Open vSwitch CI ee63f1
+++ b/.travis.yml
Open vSwitch CI ee63f1
@@ -17,7 +17,6 @@ addons:
Open vSwitch CI ee63f1
       - libjemalloc-dev
Open vSwitch CI ee63f1
       - libnuma-dev
Open vSwitch CI ee63f1
       - libpcap-dev
Open vSwitch CI ee63f1
-      - python3-openssl
Open vSwitch CI ee63f1
       - python3-pip
Open vSwitch CI ee63f1
       - python3-sphinx
Open vSwitch CI ee63f1
       - libelf-dev
110336
diff --git a/NEWS b/NEWS
Open vSwitch CI ee63f1
index 559a51ba3f..139c24a4f8 100644
110336
--- a/NEWS
110336
+++ b/NEWS
Open vSwitch CI ee63f1
@@ -1,3 +1,13 @@
Open vSwitch CI 3f9b5c
+v2.16.2 - xx xxx xxxx
110336
+---------------------
Open vSwitch CI ee63f1
+   - Python:
Open vSwitch CI ee63f1
+     * For SSL support, the use of the pyOpenSSL library has been replaced
Open vSwitch CI ee63f1
+       with the native 'ssl' module.
110336
+
Open vSwitch CI 3f9b5c
+v2.16.1 - 21 Oct 2021
Open vSwitch CI 3f9b5c
+---------------------
Open vSwitch CI 3f9b5c
+   - Bug fixes
Open vSwitch CI 3f9b5c
+
110336
 v2.16.0 - 16 Aug 2021
110336
 ---------------------
110336
    - Removed support for 1024-bit Diffie-Hellman key exchange, which is now
110336
diff --git a/configure.ac b/configure.ac
Open vSwitch CI 3f9b5c
index 16b32be965..64c26828f2 100644
110336
--- a/configure.ac
110336
+++ b/configure.ac
110336
@@ -13,7 +13,7 @@
110336
 # limitations under the License.
110336
 
110336
 AC_PREREQ(2.63)
110336
-AC_INIT(openvswitch, 2.16.0, bugs@openvswitch.org)
Open vSwitch CI 3f9b5c
+AC_INIT(openvswitch, 2.16.2, bugs@openvswitch.org)
110336
 AC_CONFIG_SRCDIR([datapath/datapath.c])
110336
 AC_CONFIG_MACRO_DIR([m4])
110336
 AC_CONFIG_AUX_DIR([build-aux])
Open vSwitch CI 3f9b5c
diff --git a/datapath-windows/ovsext/Actions.c b/datapath-windows/ovsext/Actions.c
Open vSwitch CI 3f9b5c
index e130c2f966..0c18c62548 100644
Open vSwitch CI 3f9b5c
--- a/datapath-windows/ovsext/Actions.c
Open vSwitch CI 3f9b5c
+++ b/datapath-windows/ovsext/Actions.c
Open vSwitch CI 3f9b5c
@@ -1112,9 +1112,9 @@ OvsPopFieldInPacketBuf(OvsForwardingContext *ovsFwdCtx,
Open vSwitch CI 3f9b5c
      * should split the function and refactor. */
Open vSwitch CI 3f9b5c
     if (!bufferData) {
Open vSwitch CI 3f9b5c
         EthHdr *ethHdr = (EthHdr *)bufferStart;
Open vSwitch CI 3f9b5c
-        /* If the frame is not VLAN make it a no op */
Open vSwitch CI 3f9b5c
         if (ethHdr->Type != ETH_TYPE_802_1PQ_NBO) {
Open vSwitch CI 3f9b5c
-            return NDIS_STATUS_SUCCESS;
Open vSwitch CI 3f9b5c
+            OVS_LOG_ERROR("Invalid ethHdr type %u, nbl %p", ethHdr->Type, ovsFwdCtx->curNbl);
Open vSwitch CI 3f9b5c
+            return NDIS_STATUS_INVALID_PACKET;
Open vSwitch CI 3f9b5c
         }
Open vSwitch CI 3f9b5c
     }
Open vSwitch CI 3f9b5c
     RtlMoveMemory(bufferStart + shiftLength, bufferStart, shiftOffset);
Open vSwitch CI 3f9b5c
@@ -1137,6 +1137,9 @@ OvsPopFieldInPacketBuf(OvsForwardingContext *ovsFwdCtx,
Open vSwitch CI 3f9b5c
 static __inline NDIS_STATUS
Open vSwitch CI 3f9b5c
 OvsPopVlanInPktBuf(OvsForwardingContext *ovsFwdCtx)
Open vSwitch CI 3f9b5c
 {
Open vSwitch CI 3f9b5c
+    NDIS_STATUS status;
Open vSwitch CI 3f9b5c
+    OVS_PACKET_HDR_INFO* layers = &ovsFwdCtx->layers;
Open vSwitch CI 3f9b5c
+
Open vSwitch CI 3f9b5c
     /*
Open vSwitch CI 3f9b5c
      * Declare a dummy vlanTag structure since we need to compute the size
Open vSwitch CI 3f9b5c
      * of shiftLength. The NDIS one is a unionized structure.
Open vSwitch CI 3f9b5c
@@ -1145,7 +1148,15 @@ OvsPopVlanInPktBuf(OvsForwardingContext *ovsFwdCtx)
Open vSwitch CI 3f9b5c
     UINT32 shiftLength = sizeof(vlanTag.TagHeader);
Open vSwitch CI 3f9b5c
     UINT32 shiftOffset = sizeof(DL_EUI48) + sizeof(DL_EUI48);
Open vSwitch CI 3f9b5c
 
Open vSwitch CI 3f9b5c
-    return OvsPopFieldInPacketBuf(ovsFwdCtx, shiftOffset, shiftLength, NULL);
Open vSwitch CI 3f9b5c
+    status = OvsPopFieldInPacketBuf(ovsFwdCtx, shiftOffset, shiftLength,
Open vSwitch CI 3f9b5c
+                                    NULL);
Open vSwitch CI 3f9b5c
+
Open vSwitch CI 3f9b5c
+    if (status == NDIS_STATUS_SUCCESS) {
Open vSwitch CI 3f9b5c
+        layers->l3Offset -= (UINT16) shiftLength;
Open vSwitch CI 3f9b5c
+        layers->l4Offset -= (UINT16) shiftLength;
Open vSwitch CI 3f9b5c
+    }
Open vSwitch CI 3f9b5c
+
Open vSwitch CI 3f9b5c
+    return status;
Open vSwitch CI 3f9b5c
 }
Open vSwitch CI 3f9b5c
 
Open vSwitch CI 3f9b5c
 
Open vSwitch CI 3f9b5c
@@ -1516,6 +1527,7 @@ OvsUpdateAddressAndPort(OvsForwardingContext *ovsFwdCtx,
Open vSwitch CI 3f9b5c
 
Open vSwitch CI 3f9b5c
     csumInfo.Value = NET_BUFFER_LIST_INFO(ovsFwdCtx->curNbl,
Open vSwitch CI 3f9b5c
                                           TcpIpChecksumNetBufferListInfo);
Open vSwitch CI 3f9b5c
+
Open vSwitch CI 3f9b5c
     /*
Open vSwitch CI 3f9b5c
      * Adjust the IP header inline as dictated by the action, and also update
Open vSwitch CI 3f9b5c
      * the IP and the TCP checksum for the data modified.
Open vSwitch CI 3f9b5c
@@ -1524,6 +1536,7 @@ OvsUpdateAddressAndPort(OvsForwardingContext *ovsFwdCtx,
Open vSwitch CI 3f9b5c
      * ChecksumUpdate32(). Ignoring this for now, since for the most common
Open vSwitch CI 3f9b5c
      * case, we only update the TTL.
Open vSwitch CI 3f9b5c
      */
Open vSwitch CI 3f9b5c
+     /*Only tx direction the checksum value will be reset to be PseudoChecksum*/
Open vSwitch CI 3f9b5c
 
Open vSwitch CI 3f9b5c
     if (isSource) {
Open vSwitch CI 3f9b5c
         addrField = &ipHdr->saddr;
Open vSwitch CI 3f9b5c
@@ -1540,7 +1553,7 @@ OvsUpdateAddressAndPort(OvsForwardingContext *ovsFwdCtx,
Open vSwitch CI 3f9b5c
                         ((BOOLEAN)csumInfo.Receive.UdpChecksumSucceeded ||
Open vSwitch CI 3f9b5c
                          (BOOLEAN)csumInfo.Receive.UdpChecksumFailed);
Open vSwitch CI 3f9b5c
         }
Open vSwitch CI 3f9b5c
-        if (l4Offload) {
Open vSwitch CI 3f9b5c
+        if (isTx && l4Offload) {
Open vSwitch CI 3f9b5c
             *checkField = IPPseudoChecksum(&newAddr, &ipHdr->daddr,
Open vSwitch CI 3f9b5c
                 tcpHdr ? IPPROTO_TCP : IPPROTO_UDP,
Open vSwitch CI 3f9b5c
                 ntohs(ipHdr->tot_len) - ipHdr->ihl * 4);
Open vSwitch CI 3f9b5c
@@ -1561,7 +1574,7 @@ OvsUpdateAddressAndPort(OvsForwardingContext *ovsFwdCtx,
Open vSwitch CI 3f9b5c
                          (BOOLEAN)csumInfo.Receive.UdpChecksumFailed);
Open vSwitch CI 3f9b5c
         }
Open vSwitch CI 3f9b5c
 
Open vSwitch CI 3f9b5c
-       if (l4Offload) {
Open vSwitch CI 3f9b5c
+       if (isTx && l4Offload) {
Open vSwitch CI 3f9b5c
             *checkField = IPPseudoChecksum(&ipHdr->saddr, &newAddr,
Open vSwitch CI 3f9b5c
                 tcpHdr ? IPPROTO_TCP : IPPROTO_UDP,
Open vSwitch CI 3f9b5c
                 ntohs(ipHdr->tot_len) - ipHdr->ihl * 4);
Open vSwitch CI 3f9b5c
@@ -1570,7 +1583,7 @@ OvsUpdateAddressAndPort(OvsForwardingContext *ovsFwdCtx,
Open vSwitch CI 3f9b5c
 
Open vSwitch CI 3f9b5c
     if (*addrField != newAddr) {
Open vSwitch CI 3f9b5c
         UINT32 oldAddr = *addrField;
Open vSwitch CI 3f9b5c
-        if (checkField && *checkField != 0 && !l4Offload) {
Open vSwitch CI 3f9b5c
+        if ((checkField && *checkField != 0) && (!l4Offload || !isTx)) {
Open vSwitch CI 3f9b5c
             /* Recompute total checksum. */
Open vSwitch CI 3f9b5c
             *checkField = ChecksumUpdate32(*checkField, oldAddr,
Open vSwitch CI 3f9b5c
                                             newAddr);
Open vSwitch CI 3f9b5c
@@ -1579,11 +1592,12 @@ OvsUpdateAddressAndPort(OvsForwardingContext *ovsFwdCtx,
Open vSwitch CI 3f9b5c
             ipHdr->check = ChecksumUpdate32(ipHdr->check, oldAddr,
Open vSwitch CI 3f9b5c
                                             newAddr);
Open vSwitch CI 3f9b5c
         }
Open vSwitch CI 3f9b5c
+
Open vSwitch CI 3f9b5c
         *addrField = newAddr;
Open vSwitch CI 3f9b5c
     }
Open vSwitch CI 3f9b5c
 
Open vSwitch CI 3f9b5c
     if (portField && *portField != newPort) {
Open vSwitch CI 3f9b5c
-        if (checkField && !l4Offload) {
Open vSwitch CI 3f9b5c
+        if ((checkField) && (!l4Offload || !isTx)) {
Open vSwitch CI 3f9b5c
             /* Recompute total checksum. */
Open vSwitch CI 3f9b5c
             *checkField = ChecksumUpdate16(*checkField, *portField,
Open vSwitch CI 3f9b5c
                                            newPort);
Open vSwitch CI 3f9b5c
@@ -1792,9 +1806,11 @@ OvsExecuteRecirc(OvsForwardingContext *ovsFwdCtx,
Open vSwitch CI 3f9b5c
     }
Open vSwitch CI 3f9b5c
 
Open vSwitch CI 3f9b5c
     if (newNbl) {
Open vSwitch CI 3f9b5c
-        deferredAction = OvsAddDeferredActions(newNbl, key, NULL);
Open vSwitch CI 3f9b5c
+        deferredAction = OvsAddDeferredActions(newNbl, key, &(ovsFwdCtx->layers),
Open vSwitch CI 3f9b5c
+                                               NULL);
Open vSwitch CI 3f9b5c
     } else {
Open vSwitch CI 3f9b5c
-        deferredAction = OvsAddDeferredActions(ovsFwdCtx->curNbl, key, NULL);
Open vSwitch CI 3f9b5c
+        deferredAction = OvsAddDeferredActions(ovsFwdCtx->curNbl, key,
Open vSwitch CI 3f9b5c
+                                              &(ovsFwdCtx->layers), NULL);
Open vSwitch CI 3f9b5c
     }
Open vSwitch CI 3f9b5c
 
Open vSwitch CI 3f9b5c
     if (deferredAction) {
Open vSwitch CI 3f9b5c
@@ -1964,7 +1980,7 @@ OvsExecuteSampleAction(OvsForwardingContext *ovsFwdCtx,
Open vSwitch CI 3f9b5c
         return STATUS_SUCCESS;
Open vSwitch CI 3f9b5c
     }
Open vSwitch CI 3f9b5c
 
Open vSwitch CI 3f9b5c
-    if (!OvsAddDeferredActions(newNbl, key, a)) {
Open vSwitch CI 3f9b5c
+    if (!OvsAddDeferredActions(newNbl, key, &(ovsFwdCtx->layers), a)) {
Open vSwitch CI 3f9b5c
         OVS_LOG_INFO(
Open vSwitch CI 3f9b5c
             "Deferred actions limit reached, dropping sample action.");
Open vSwitch CI 3f9b5c
         OvsCompleteNBL(ovsFwdCtx->switchContext, newNbl, TRUE);
Open vSwitch CI 3f9b5c
@@ -2100,6 +2116,7 @@ OvsDoExecuteActions(POVS_SWITCH_CONTEXT switchContext,
Open vSwitch CI 3f9b5c
                  */
Open vSwitch CI 3f9b5c
                 status = OvsPopVlanInPktBuf(&ovsFwdCtx);
Open vSwitch CI 3f9b5c
                 if (status != NDIS_STATUS_SUCCESS) {
Open vSwitch CI 3f9b5c
+                    OVS_LOG_ERROR("OVS-pop vlan action failed status = %lu", status);
Open vSwitch CI 3f9b5c
                     dropReason = L"OVS-pop vlan action failed";
Open vSwitch CI 3f9b5c
                     goto dropit;
Open vSwitch CI 3f9b5c
                 }
Open vSwitch CI 3f9b5c
@@ -2349,7 +2366,7 @@ OvsActionsExecute(POVS_SWITCH_CONTEXT switchContext,
Open vSwitch CI 3f9b5c
 
Open vSwitch CI 3f9b5c
     if (status == STATUS_SUCCESS) {
Open vSwitch CI 3f9b5c
         status = OvsProcessDeferredActions(switchContext, completionList,
Open vSwitch CI 3f9b5c
-                                           portNo, sendFlags, layers);
Open vSwitch CI 3f9b5c
+                                           portNo, sendFlags, NULL);
Open vSwitch CI 3f9b5c
     }
Open vSwitch CI 3f9b5c
 
Open vSwitch CI 3f9b5c
     return status;
Open vSwitch CI 550c65
diff --git a/datapath-windows/ovsext/Conntrack.c b/datapath-windows/ovsext/Conntrack.c
Open vSwitch CI 550c65
index 2610d626a0..fd6f3bae04 100644
Open vSwitch CI 550c65
--- a/datapath-windows/ovsext/Conntrack.c
Open vSwitch CI 550c65
+++ b/datapath-windows/ovsext/Conntrack.c
Open vSwitch CI 550c65
@@ -493,15 +493,32 @@ static __inline NDIS_STATUS
Open vSwitch CI 550c65
 OvsDetectCtPacket(OvsForwardingContext *fwdCtx,
Open vSwitch CI 550c65
                   OvsFlowKey *key)
Open vSwitch CI 550c65
 {
Open vSwitch CI 550c65
+    NDIS_STATUS status = NDIS_STATUS_SUCCESS;
Open vSwitch CI 550c65
+    OvsFlowKey  newFlowKey = { 0 };
Open vSwitch CI 550c65
+
Open vSwitch CI 550c65
     switch (ntohs(key->l2.dlType)) {
Open vSwitch CI 550c65
     case ETH_TYPE_IPV4:
Open vSwitch CI 550c65
         if (key->ipKey.nwFrag != OVS_FRAG_TYPE_NONE) {
Open vSwitch CI 550c65
-            return OvsProcessIpv4Fragment(fwdCtx->switchContext,
Open vSwitch CI 550c65
+            status = OvsProcessIpv4Fragment(fwdCtx->switchContext,
Open vSwitch CI 550c65
                                           &fwdCtx->curNbl,
Open vSwitch CI 550c65
                                           fwdCtx->completionList,
Open vSwitch CI 550c65
                                           fwdCtx->fwdDetail->SourcePortId,
Open vSwitch CI 550c65
                                           &fwdCtx->layers,
Open vSwitch CI 550c65
                                           key->tunKey.tunnelId);
Open vSwitch CI 550c65
+            if (status == NDIS_STATUS_SUCCESS) {
Open vSwitch CI 550c65
+                 /* After the Ipv4 Fragment is reassembled, update flow key as
Open vSwitch CI 550c65
+                   L3 and L4 headers are not correct */
Open vSwitch CI 550c65
+                 status =
Open vSwitch CI 550c65
+                      OvsExtractFlow(fwdCtx->curNbl, fwdCtx->srcVportNo,
Open vSwitch CI 550c65
+                                     &newFlowKey, &fwdCtx->layers,
Open vSwitch CI 550c65
+                                     fwdCtx->tunKey.dst != 0 ? &fwdCtx->tunKey : NULL);
Open vSwitch CI 550c65
+                if (status != NDIS_STATUS_SUCCESS) {
Open vSwitch CI 550c65
+                     OVS_LOG_ERROR("Extract flow failed Nbl %p", fwdCtx->curNbl);
Open vSwitch CI 550c65
+                     return status;
Open vSwitch CI 550c65
+                 }
Open vSwitch CI 550c65
+                *key = newFlowKey;
Open vSwitch CI 550c65
+            }
Open vSwitch CI 550c65
+            return status;
Open vSwitch CI 550c65
         }
Open vSwitch CI 550c65
         if (key->ipKey.nwProto == IPPROTO_TCP
Open vSwitch CI 550c65
             || key->ipKey.nwProto == IPPROTO_UDP
Open vSwitch CI 3f9b5c
diff --git a/datapath-windows/ovsext/Recirc.c b/datapath-windows/ovsext/Recirc.c
Open vSwitch CI 3f9b5c
index 2febf060dd..a32b75352b 100644
Open vSwitch CI 3f9b5c
--- a/datapath-windows/ovsext/Recirc.c
Open vSwitch CI 3f9b5c
+++ b/datapath-windows/ovsext/Recirc.c
Open vSwitch CI 3f9b5c
@@ -277,16 +277,23 @@ OvsDeferredActionsQueuePush(POVS_DEFERRED_ACTION_QUEUE queue)
Open vSwitch CI 3f9b5c
 POVS_DEFERRED_ACTION
Open vSwitch CI 3f9b5c
 OvsAddDeferredActions(PNET_BUFFER_LIST nbl,
Open vSwitch CI 3f9b5c
                       OvsFlowKey *key,
Open vSwitch CI 3f9b5c
+                      POVS_PACKET_HDR_INFO layers,
Open vSwitch CI 3f9b5c
                       const PNL_ATTR actions)
Open vSwitch CI 3f9b5c
 {
Open vSwitch CI 3f9b5c
     POVS_DEFERRED_ACTION_QUEUE queue = OvsDeferredActionsQueueGet();
Open vSwitch CI 3f9b5c
     POVS_DEFERRED_ACTION deferredAction = NULL;
Open vSwitch CI 3f9b5c
+    OVS_PACKET_HDR_INFO layersInit = { 0 };
Open vSwitch CI 3f9b5c
 
Open vSwitch CI 3f9b5c
     deferredAction = OvsDeferredActionsQueuePush(queue);
Open vSwitch CI 3f9b5c
     if (deferredAction) {
Open vSwitch CI 3f9b5c
         deferredAction->nbl = nbl;
Open vSwitch CI 3f9b5c
         deferredAction->actions = actions;
Open vSwitch CI 3f9b5c
         deferredAction->key = *key;
Open vSwitch CI 3f9b5c
+        if (layers) {
Open vSwitch CI 3f9b5c
+            deferredAction->layers = *layers;
Open vSwitch CI 3f9b5c
+        } else {
Open vSwitch CI 3f9b5c
+            deferredAction->layers = layersInit;
Open vSwitch CI 3f9b5c
+        }
Open vSwitch CI 3f9b5c
     }
Open vSwitch CI 3f9b5c
 
Open vSwitch CI 3f9b5c
     return deferredAction;
Open vSwitch CI 3f9b5c
@@ -309,9 +316,16 @@ OvsProcessDeferredActions(POVS_SWITCH_CONTEXT switchContext,
Open vSwitch CI 3f9b5c
     NDIS_STATUS status = NDIS_STATUS_SUCCESS;
Open vSwitch CI 3f9b5c
     POVS_DEFERRED_ACTION_QUEUE queue = OvsDeferredActionsQueueGet();
Open vSwitch CI 3f9b5c
     POVS_DEFERRED_ACTION deferredAction = NULL;
Open vSwitch CI 3f9b5c
+    POVS_PACKET_HDR_INFO layersDeferred = NULL;
Open vSwitch CI 3f9b5c
 
Open vSwitch CI 3f9b5c
     /* Process all deferred actions. */
Open vSwitch CI 3f9b5c
     while ((deferredAction = OvsDeferredActionsQueuePop(queue)) != NULL) {
Open vSwitch CI 3f9b5c
+        if (layers) {
Open vSwitch CI 3f9b5c
+            layersDeferred = layers;
Open vSwitch CI 3f9b5c
+         } else {
Open vSwitch CI 3f9b5c
+            layersDeferred = &(deferredAction->layers);
Open vSwitch CI 3f9b5c
+         }
Open vSwitch CI 3f9b5c
+
Open vSwitch CI 3f9b5c
         if (deferredAction->actions) {
Open vSwitch CI 3f9b5c
             status = OvsDoExecuteActions(switchContext,
Open vSwitch CI 3f9b5c
                                          completionList,
Open vSwitch CI 3f9b5c
@@ -319,7 +333,7 @@ OvsProcessDeferredActions(POVS_SWITCH_CONTEXT switchContext,
Open vSwitch CI 3f9b5c
                                          portNo,
Open vSwitch CI 3f9b5c
                                          sendFlags,
Open vSwitch CI 3f9b5c
                                          &deferredAction->key, NULL,
Open vSwitch CI 3f9b5c
-                                         layers, deferredAction->actions,
Open vSwitch CI 3f9b5c
+                                         layersDeferred, deferredAction->actions,
Open vSwitch CI 3f9b5c
                                          NlAttrGetSize(deferredAction->actions));
Open vSwitch CI 3f9b5c
         } else {
Open vSwitch CI 3f9b5c
             status = OvsDoRecirc(switchContext,
Open vSwitch CI 3f9b5c
@@ -327,7 +341,7 @@ OvsProcessDeferredActions(POVS_SWITCH_CONTEXT switchContext,
Open vSwitch CI 3f9b5c
                                  deferredAction->nbl,
Open vSwitch CI 3f9b5c
                                  &deferredAction->key,
Open vSwitch CI 3f9b5c
                                  portNo,
Open vSwitch CI 3f9b5c
-                                 layers);
Open vSwitch CI 3f9b5c
+                                 layersDeferred);
Open vSwitch CI 3f9b5c
         }
Open vSwitch CI 3f9b5c
     }
Open vSwitch CI 3f9b5c
 
Open vSwitch CI 3f9b5c
diff --git a/datapath-windows/ovsext/Recirc.h b/datapath-windows/ovsext/Recirc.h
Open vSwitch CI 3f9b5c
index 2b314ce274..74130a4600 100644
Open vSwitch CI 3f9b5c
--- a/datapath-windows/ovsext/Recirc.h
Open vSwitch CI 3f9b5c
+++ b/datapath-windows/ovsext/Recirc.h
Open vSwitch CI 3f9b5c
@@ -18,6 +18,7 @@
Open vSwitch CI 3f9b5c
 #define __RECIRC_H_ 1
Open vSwitch CI 3f9b5c
 
Open vSwitch CI 3f9b5c
 #include "Actions.h"
Open vSwitch CI 3f9b5c
+#include "NetProto.h"
Open vSwitch CI 3f9b5c
 
Open vSwitch CI 3f9b5c
 #define DEFERRED_ACTION_QUEUE_SIZE          10
Open vSwitch CI 3f9b5c
 #define DEFERRED_ACTION_EXEC_LEVEL           4
Open vSwitch CI 3f9b5c
@@ -26,6 +27,7 @@ typedef struct _OVS_DEFERRED_ACTION {
Open vSwitch CI 3f9b5c
     PNET_BUFFER_LIST    nbl;
Open vSwitch CI 3f9b5c
     PNL_ATTR            actions;
Open vSwitch CI 3f9b5c
     OvsFlowKey          key;
Open vSwitch CI 3f9b5c
+    OVS_PACKET_HDR_INFO layers;
Open vSwitch CI 3f9b5c
 } OVS_DEFERRED_ACTION, *POVS_DEFERRED_ACTION;
Open vSwitch CI 3f9b5c
 
Open vSwitch CI 3f9b5c
 /*
Open vSwitch CI 3f9b5c
@@ -52,6 +54,7 @@ OvsProcessDeferredActions(POVS_SWITCH_CONTEXT switchContext,
Open vSwitch CI 3f9b5c
 POVS_DEFERRED_ACTION
Open vSwitch CI 3f9b5c
 OvsAddDeferredActions(PNET_BUFFER_LIST packet,
Open vSwitch CI 3f9b5c
                       OvsFlowKey *key,
Open vSwitch CI 3f9b5c
+                      POVS_PACKET_HDR_INFO layers,
Open vSwitch CI 3f9b5c
                       const PNL_ATTR actions);
Open vSwitch CI 3f9b5c
 
Open vSwitch CI 3f9b5c
 /*
110336
diff --git a/debian/changelog b/debian/changelog
Open vSwitch CI 3f9b5c
index 239d210b96..a0838ea3d7 100644
110336
--- a/debian/changelog
110336
+++ b/debian/changelog
Open vSwitch CI 3f9b5c
@@ -1,3 +1,15 @@
Open vSwitch CI 3f9b5c
+openvswitch (2.16.2-1) unstable; urgency=low
Open vSwitch CI 3f9b5c
+   [ Open vSwitch team ]
Open vSwitch CI 3f9b5c
+   * New upstream version
Open vSwitch CI 3f9b5c
+
Open vSwitch CI 3f9b5c
+ -- Open vSwitch team <dev@openvswitch.org>  Thu, 21 Oct 2021 23:58:12 +0200
Open vSwitch CI 3f9b5c
+
110336
+openvswitch (2.16.1-1) unstable; urgency=low
110336
+   [ Open vSwitch team ]
110336
+   * New upstream version
110336
+
Open vSwitch CI 3f9b5c
+ -- Open vSwitch team <dev@openvswitch.org>  Thu, 21 Oct 2021 23:58:12 +0200
110336
+
110336
 openvswitch (2.16.0-1) unstable; urgency=low
110336
 
110336
    * New upstream version
110336
diff --git a/include/openvswitch/json.h b/include/openvswitch/json.h
110336
index 73b562e03d..0831a9cee1 100644
110336
--- a/include/openvswitch/json.h
110336
+++ b/include/openvswitch/json.h
110336
@@ -50,7 +50,9 @@ enum json_type {
110336
     JSON_INTEGER,               /* 123. */
110336
     JSON_REAL,                  /* 123.456. */
110336
     JSON_STRING,                /* "..." */
110336
-    JSON_N_TYPES
110336
+    JSON_N_TYPES,
110336
+    JSON_SERIALIZED_OBJECT,     /* Internal type to hold serialized version of
110336
+                                 * data of other types. */
110336
 };
110336
 
110336
 const char *json_type_to_string(enum json_type);
110336
@@ -70,7 +72,7 @@ struct json {
110336
         struct json_array array;
110336
         long long int integer;
110336
         double real;
110336
-        char *string;
110336
+        char *string; /* JSON_STRING or JSON_SERIALIZED_OBJECT. */
110336
     };
110336
 };
110336
 
110336
@@ -78,6 +80,7 @@ struct json *json_null_create(void);
110336
 struct json *json_boolean_create(bool);
110336
 struct json *json_string_create(const char *);
110336
 struct json *json_string_create_nocopy(char *);
110336
+struct json *json_serialized_object_create(const struct json *);
110336
 struct json *json_integer_create(long long int);
110336
 struct json *json_real_create(double);
110336
 
110336
@@ -99,6 +102,7 @@ void json_object_put_format(struct json *,
110336
     OVS_PRINTF_FORMAT(3, 4);
110336
 
110336
 const char *json_string(const struct json *);
110336
+const char *json_serialized_object(const struct json *);
110336
 struct json_array *json_array(const struct json *);
110336
 struct shash *json_object(const struct json *);
110336
 bool json_boolean(const struct json *);
110336
@@ -125,6 +129,7 @@ struct json *json_parser_finish(struct json_parser *);
110336
 void json_parser_abort(struct json_parser *);
110336
 
110336
 struct json *json_from_string(const char *string);
110336
+struct json *json_from_serialized_object(const struct json *);
110336
 struct json *json_from_file(const char *file_name);
110336
 struct json *json_from_stream(FILE *stream);
110336
 
Open vSwitch CI 3f9b5c
diff --git a/include/openvswitch/meta-flow.h b/include/openvswitch/meta-flow.h
Open vSwitch CI 3f9b5c
index 95e52e3587..045dce8f5f 100644
Open vSwitch CI 3f9b5c
--- a/include/openvswitch/meta-flow.h
Open vSwitch CI 3f9b5c
+++ b/include/openvswitch/meta-flow.h
Open vSwitch CI 3f9b5c
@@ -2305,6 +2305,7 @@ void mf_set_flow_value_masked(const struct mf_field *,
Open vSwitch CI 3f9b5c
                               const union mf_value *mask,
Open vSwitch CI 3f9b5c
                               struct flow *);
Open vSwitch CI 3f9b5c
 bool mf_is_tun_metadata(const struct mf_field *);
Open vSwitch CI 3f9b5c
+bool mf_is_frozen_metadata(const struct mf_field *);
Open vSwitch CI 3f9b5c
 bool mf_is_pipeline_field(const struct mf_field *);
Open vSwitch CI 3f9b5c
 bool mf_is_set(const struct mf_field *, const struct flow *);
Open vSwitch CI 3f9b5c
 void mf_mask_field(const struct mf_field *, struct flow_wildcards *);
Open vSwitch CI 3f9b5c
diff --git a/lib/db-ctl-base.c b/lib/db-ctl-base.c
Open vSwitch CI 3f9b5c
index 77cc76a9f6..7074561588 100644
Open vSwitch CI 3f9b5c
--- a/lib/db-ctl-base.c
Open vSwitch CI 3f9b5c
+++ b/lib/db-ctl-base.c
Open vSwitch CI 3f9b5c
@@ -247,15 +247,15 @@ record_id_equals(const union ovsdb_atom *name, enum ovsdb_atomic_type type,
Open vSwitch CI 3f9b5c
                  const char *record_id)
Open vSwitch CI 3f9b5c
 {
Open vSwitch CI 3f9b5c
     if (type == OVSDB_TYPE_STRING) {
Open vSwitch CI 3f9b5c
-        if (!strcmp(name->string, record_id)) {
Open vSwitch CI 3f9b5c
+        if (!strcmp(name->s->string, record_id)) {
Open vSwitch CI 3f9b5c
             return true;
Open vSwitch CI 3f9b5c
         }
Open vSwitch CI 3f9b5c
 
Open vSwitch CI 3f9b5c
         struct uuid uuid;
Open vSwitch CI 3f9b5c
         size_t len = strlen(record_id);
Open vSwitch CI 3f9b5c
         if (len >= 4
Open vSwitch CI 3f9b5c
-            && uuid_from_string(&uuid, name->string)
Open vSwitch CI 3f9b5c
-            && !strncmp(name->string, record_id, len)) {
Open vSwitch CI 3f9b5c
+            && uuid_from_string(&uuid, name->s->string)
Open vSwitch CI 3f9b5c
+            && !strncmp(name->s->string, record_id, len)) {
Open vSwitch CI 3f9b5c
             return true;
Open vSwitch CI 3f9b5c
         }
Open vSwitch CI 3f9b5c
 
Open vSwitch CI 3f9b5c
@@ -314,15 +314,19 @@ get_row_by_id(struct ctl_context *ctx,
Open vSwitch CI 3f9b5c
             row, id->name_column, key, value);
Open vSwitch CI 3f9b5c
 
Open vSwitch CI 3f9b5c
         /* Extract the name from the column. */
Open vSwitch CI 3f9b5c
-        const union ovsdb_atom *name;
Open vSwitch CI 3f9b5c
+        const union ovsdb_atom *name = NULL;
Open vSwitch CI 3f9b5c
         if (!id->key) {
Open vSwitch CI 3f9b5c
             name = datum->n == 1 ? &datum->keys[0] : NULL;
Open vSwitch CI 3f9b5c
         } else {
Open vSwitch CI 3f9b5c
-            const union ovsdb_atom key_atom
Open vSwitch CI 3f9b5c
-                = { .string = CONST_CAST(char *, id->key) };
Open vSwitch CI 3f9b5c
-            unsigned int i = ovsdb_datum_find_key(datum, &key_atom,
Open vSwitch CI 3f9b5c
-                                                  OVSDB_TYPE_STRING);
Open vSwitch CI 3f9b5c
-            name = i == UINT_MAX ? NULL : &datum->values[i];
Open vSwitch CI 3f9b5c
+            union ovsdb_atom key_atom = {
Open vSwitch CI 3f9b5c
+                .s = ovsdb_atom_string_create(CONST_CAST(char *, id->key)) };
Open vSwitch CI 3f9b5c
+            unsigned int i;
Open vSwitch CI 3f9b5c
+
Open vSwitch CI 3f9b5c
+            if (ovsdb_datum_find_key(datum, &key_atom,
Open vSwitch CI 3f9b5c
+                                     OVSDB_TYPE_STRING, &i)) {
Open vSwitch CI 3f9b5c
+                name = &datum->values[i];
Open vSwitch CI 3f9b5c
+            }
Open vSwitch CI 3f9b5c
+            ovsdb_atom_destroy(&key_atom, OVSDB_TYPE_STRING);
Open vSwitch CI 3f9b5c
         }
Open vSwitch CI 3f9b5c
         if (!name) {
Open vSwitch CI 3f9b5c
             continue;
Open vSwitch CI 3f9b5c
@@ -819,14 +823,14 @@ check_condition(const struct ovsdb_idl_table_class *table,
Open vSwitch CI 3f9b5c
             goto out;
Open vSwitch CI 3f9b5c
         }
Open vSwitch CI 3f9b5c
 
Open vSwitch CI 3f9b5c
-        idx = ovsdb_datum_find_key(have_datum,
Open vSwitch CI 3f9b5c
-                                   &want_key, column->type.key.type);
Open vSwitch CI 3f9b5c
-        if (idx == UINT_MAX && !is_set_operator(operator)) {
Open vSwitch CI 3f9b5c
+        bool found = ovsdb_datum_find_key(have_datum, &want_key,
Open vSwitch CI 3f9b5c
+                                          column->type.key.type, &idx);
Open vSwitch CI 3f9b5c
+        if (!found && !is_set_operator(operator)) {
Open vSwitch CI 3f9b5c
             retval = false;
Open vSwitch CI 3f9b5c
         } else {
Open vSwitch CI 3f9b5c
             struct ovsdb_datum a;
Open vSwitch CI 3f9b5c
 
Open vSwitch CI 3f9b5c
-            if (idx != UINT_MAX) {
Open vSwitch CI 3f9b5c
+            if (found) {
Open vSwitch CI 3f9b5c
                 a.n = 1;
Open vSwitch CI 3f9b5c
                 a.keys = &have_datum->values[idx];
Open vSwitch CI 3f9b5c
                 a.values = NULL;
Open vSwitch CI 3f9b5c
@@ -992,9 +996,8 @@ cmd_get(struct ctl_context *ctx)
Open vSwitch CI 3f9b5c
                 return;
Open vSwitch CI 3f9b5c
             }
Open vSwitch CI 3f9b5c
 
Open vSwitch CI 3f9b5c
-            idx = ovsdb_datum_find_key(datum, &key,
Open vSwitch CI 3f9b5c
-                                       column->type.key.type);
Open vSwitch CI 3f9b5c
-            if (idx == UINT_MAX) {
Open vSwitch CI 3f9b5c
+            if (!ovsdb_datum_find_key(datum, &key,
Open vSwitch CI 3f9b5c
+                                      column->type.key.type, &idx)) {
Open vSwitch CI 3f9b5c
                 if (must_exist) {
Open vSwitch CI 3f9b5c
                     ctl_error(
Open vSwitch CI 3f9b5c
                         ctx, "no key \"%s\" in %s record \"%s\" column %s",
Open vSwitch CI 3f9b5c
@@ -1375,7 +1378,7 @@ set_column(const struct ovsdb_idl_table_class *table,
Open vSwitch CI 3f9b5c
         ovsdb_atom_destroy(&value, column->type.value.type);
Open vSwitch CI 3f9b5c
 
Open vSwitch CI 3f9b5c
         ovsdb_datum_union(&datum, ovsdb_idl_read(row, column),
Open vSwitch CI 3f9b5c
-                          &column->type, false);
Open vSwitch CI 3f9b5c
+                          &column->type);
Open vSwitch CI 3f9b5c
         ovsdb_idl_txn_verify(row, column);
Open vSwitch CI 3f9b5c
         ovsdb_idl_txn_write(row, column, &datum);
Open vSwitch CI 3f9b5c
     } else {
Open vSwitch CI 3f9b5c
@@ -1514,7 +1517,7 @@ cmd_add(struct ctl_context *ctx)
Open vSwitch CI 3f9b5c
             ovsdb_datum_destroy(&old, &column->type);
Open vSwitch CI 3f9b5c
             return;
Open vSwitch CI 3f9b5c
         }
Open vSwitch CI 3f9b5c
-        ovsdb_datum_union(&old, &add, type, false);
Open vSwitch CI 3f9b5c
+        ovsdb_datum_union(&old, &add, type);
Open vSwitch CI 3f9b5c
         ovsdb_datum_destroy(&add, type);
Open vSwitch CI 3f9b5c
     }
Open vSwitch CI 3f9b5c
     if (old.n > type->n_max) {
110336
diff --git a/lib/dp-packet.h b/lib/dp-packet.h
110336
index 08d93c2779..3dc582fbfd 100644
110336
--- a/lib/dp-packet.h
110336
+++ b/lib/dp-packet.h
110336
@@ -199,6 +199,7 @@ struct dp_packet *dp_packet_clone_data_with_headroom(const void *, size_t,
110336
 void dp_packet_resize(struct dp_packet *b, size_t new_headroom,
110336
                       size_t new_tailroom);
110336
 static inline void dp_packet_delete(struct dp_packet *);
110336
+static inline void dp_packet_swap(struct dp_packet *, struct dp_packet *);
110336
 
110336
 static inline void *dp_packet_at(const struct dp_packet *, size_t offset,
110336
                                  size_t size);
110336
@@ -256,6 +257,18 @@ dp_packet_delete(struct dp_packet *b)
110336
     }
110336
 }
110336
 
110336
+/* Swaps content of two packets. */
110336
+static inline void
110336
+dp_packet_swap(struct dp_packet *a, struct dp_packet *b)
110336
+{
110336
+    ovs_assert(a->source == DPBUF_MALLOC || a->source == DPBUF_STUB);
110336
+    ovs_assert(b->source == DPBUF_MALLOC || b->source == DPBUF_STUB);
110336
+    struct dp_packet c = *a;
110336
+
110336
+    *a = *b;
110336
+    *b = c;
110336
+}
110336
+
110336
 /* If 'b' contains at least 'offset + size' bytes of data, returns a pointer to
110336
  * byte 'offset'.  Otherwise, returns a null pointer. */
110336
 static inline void *
Open vSwitch CI 3f9b5c
diff --git a/lib/dpdk-stub.c b/lib/dpdk-stub.c
Open vSwitch CI 3f9b5c
index b7d577870d..fe24f9abdf 100644
Open vSwitch CI 3f9b5c
--- a/lib/dpdk-stub.c
Open vSwitch CI 3f9b5c
+++ b/lib/dpdk-stub.c
Open vSwitch CI 3f9b5c
@@ -83,7 +83,7 @@ bool
Open vSwitch CI 3f9b5c
 dpdk_get_cpu_has_isa(const char *arch OVS_UNUSED,
Open vSwitch CI 3f9b5c
                      const char *feature OVS_UNUSED)
Open vSwitch CI 3f9b5c
 {
Open vSwitch CI 3f9b5c
-    VLOG_ERR_ONCE("DPDK not supported in this version of Open vSwitch, "
Open vSwitch CI 3f9b5c
+    VLOG_DBG_ONCE("DPDK not supported in this version of Open vSwitch, "
Open vSwitch CI 3f9b5c
                   "cannot use CPU flag based optimizations");
Open vSwitch CI 3f9b5c
     return false;
Open vSwitch CI 3f9b5c
 }
Open vSwitch CI 3f9b5c
diff --git a/lib/dpif-netdev-private-dfc.h b/lib/dpif-netdev-private-dfc.h
Open vSwitch CI 3f9b5c
index 92092ebec9..3dfc91f0fe 100644
Open vSwitch CI 3f9b5c
--- a/lib/dpif-netdev-private-dfc.h
Open vSwitch CI 3f9b5c
+++ b/lib/dpif-netdev-private-dfc.h
Open vSwitch CI 3f9b5c
@@ -59,7 +59,8 @@ extern "C" {
Open vSwitch CI 3f9b5c
  * Thread-safety
Open vSwitch CI 3f9b5c
  * =============
Open vSwitch CI 3f9b5c
  *
Open vSwitch CI 3f9b5c
- * Each pmd_thread has its own private exact match cache.
Open vSwitch CI 3f9b5c
+ * Each pmd_thread has its own private exact match cache and signature match
Open vSwitch CI 3f9b5c
+ * cache.
Open vSwitch CI 3f9b5c
  * If dp_netdev_input is not called from a pmd thread, a mutex is used.
Open vSwitch CI 3f9b5c
  */
Open vSwitch CI 3f9b5c
 
Open vSwitch CI 3f9b5c
diff --git a/lib/dpif-netdev-private-thread.h b/lib/dpif-netdev-private-thread.h
Open vSwitch CI 3f9b5c
index a782d9678a..ac4885538c 100644
Open vSwitch CI 3f9b5c
--- a/lib/dpif-netdev-private-thread.h
Open vSwitch CI 3f9b5c
+++ b/lib/dpif-netdev-private-thread.h
Open vSwitch CI 3f9b5c
@@ -78,10 +78,10 @@ struct dp_netdev_pmd_thread {
Open vSwitch CI 3f9b5c
     struct ovs_refcount ref_cnt;    /* Every reference must be refcount'ed. */
Open vSwitch CI 3f9b5c
     struct cmap_node node;          /* In 'dp->poll_threads'. */
Open vSwitch CI 3f9b5c
 
Open vSwitch CI 3f9b5c
-    /* Per thread exact-match cache.  Note, the instance for cpu core
Open vSwitch CI 3f9b5c
-     * NON_PMD_CORE_ID can be accessed by multiple threads, and thusly
Open vSwitch CI 3f9b5c
-     * need to be protected by 'non_pmd_mutex'.  Every other instance
Open vSwitch CI 3f9b5c
-     * will only be accessed by its own pmd thread. */
Open vSwitch CI 3f9b5c
+    /* Per thread exact match cache and signature match cache.  Note, the
Open vSwitch CI 3f9b5c
+     * instance for cpu core NON_PMD_CORE_ID can be accessed by multiple
Open vSwitch CI 3f9b5c
+     * threads, and thusly need to be protected by 'non_pmd_mutex'.  Every
Open vSwitch CI 3f9b5c
+     * other instance will only be accessed by its own pmd thread. */
Open vSwitch CI 3f9b5c
     OVS_ALIGNED_VAR(CACHE_LINE_SIZE) struct dfc_cache flow_cache;
Open vSwitch CI 3f9b5c
 
Open vSwitch CI 3f9b5c
     /* Flow-Table and classifiers
110336
diff --git a/lib/dpif-netdev.c b/lib/dpif-netdev.c
Open vSwitch CI 3f9b5c
index bddce75b63..d6bee2a5a9 100644
110336
--- a/lib/dpif-netdev.c
110336
+++ b/lib/dpif-netdev.c
110336
@@ -4061,7 +4061,10 @@ dpif_netdev_execute(struct dpif *dpif, struct dpif_execute *execute)
110336
                                flow_hash_5tuple(execute->flow, 0));
110336
     }
110336
 
110336
-    dp_packet_batch_init_packet(&pp, execute->packet);
110336
+    /* Making a copy because the packet might be stolen during the execution
110336
+     * and caller might still need it.  */
110336
+    struct dp_packet *packet_clone = dp_packet_clone(execute->packet);
110336
+    dp_packet_batch_init_packet(&pp, packet_clone);
110336
     dp_netdev_execute_actions(pmd, &pp, false, execute->flow,
110336
                               execute->actions, execute->actions_len);
110336
     dp_netdev_pmd_flush_output_packets(pmd, true);
Open vSwitch CI 3f9b5c
@@ -4071,6 +4074,24 @@ dpif_netdev_execute(struct dpif *dpif, struct dpif_execute *execute)
110336
         dp_netdev_pmd_unref(pmd);
110336
     }
110336
 
Open vSwitch CI 3f9b5c
+    if (dp_packet_batch_size(&pp) == 1) {
110336
+        /* Packet wasn't dropped during the execution.  Swapping content with
110336
+         * the original packet, because the caller might expect actions to
Open vSwitch CI 3f9b5c
+         * modify it.  Uisng the packet from a batch instead of 'packet_clone'
Open vSwitch CI 3f9b5c
+         * because it maybe stolen and replaced by other packet, e.g. by
Open vSwitch CI 3f9b5c
+         * the fragmentation engine. */
Open vSwitch CI 3f9b5c
+        dp_packet_swap(execute->packet, pp.packets[0]);
Open vSwitch CI 3f9b5c
+        dp_packet_delete_batch(&pp, true);
Open vSwitch CI 3f9b5c
+    } else if (dp_packet_batch_size(&pp)) {
Open vSwitch CI 3f9b5c
+        /* FIXME: We have more packets than expected.  Likely, we got IP
Open vSwitch CI 3f9b5c
+         * fragments of the reassembled packet.  Dropping them here as we have
Open vSwitch CI 3f9b5c
+         * no way to get them to the caller.  It might be that all the required
Open vSwitch CI 3f9b5c
+         * actions with them are already executed, but it also might not be a
Open vSwitch CI 3f9b5c
+         * case, e.g. if dpif_netdev_execute() called to execute a single
Open vSwitch CI 3f9b5c
+         * tunnel push. */
110336
+        dp_packet_delete_batch(&pp, true);
110336
+    }
110336
+
110336
     return 0;
110336
 }
110336
 
Open vSwitch CI 3f9b5c
diff --git a/lib/dpif-netlink.c b/lib/dpif-netlink.c
Open vSwitch CI 3f9b5c
index 34fc042373..5f4b60c5a6 100644
Open vSwitch CI 3f9b5c
--- a/lib/dpif-netlink.c
Open vSwitch CI 3f9b5c
+++ b/lib/dpif-netlink.c
Open vSwitch CI 3f9b5c
@@ -84,6 +84,8 @@ enum { MAX_PORTS = USHRT_MAX };
Open vSwitch CI 3f9b5c
 #define EPOLLEXCLUSIVE (1u << 28)
Open vSwitch CI 3f9b5c
 #endif
Open vSwitch CI 3f9b5c
 
Open vSwitch CI 3f9b5c
+#define OVS_DP_F_UNSUPPORTED (1 << 31);
Open vSwitch CI 3f9b5c
+
Open vSwitch CI 3f9b5c
 /* This PID is not used by the kernel datapath when using dispatch per CPU,
Open vSwitch CI 3f9b5c
  * but it is required to be set (not zero). */
Open vSwitch CI 3f9b5c
 #define DPIF_NETLINK_PER_CPU_PID UINT32_MAX
Open vSwitch CI 3f9b5c
@@ -382,36 +384,62 @@ dpif_netlink_open(const struct dpif_class *class OVS_UNUSED, const char *name,
Open vSwitch CI 3f9b5c
         dp_request.cmd = OVS_DP_CMD_SET;
Open vSwitch CI 3f9b5c
     }
Open vSwitch CI 3f9b5c
 
Open vSwitch CI 3f9b5c
-    /* The Open vSwitch kernel module has two modes for dispatching upcalls:
Open vSwitch CI 3f9b5c
-     * per-vport and per-cpu.
Open vSwitch CI 3f9b5c
-     *
Open vSwitch CI 3f9b5c
-     * When dispatching upcalls per-vport, the kernel will
Open vSwitch CI 3f9b5c
-     * send the upcall via a Netlink socket that has been selected based on the
Open vSwitch CI 3f9b5c
-     * vport that received the packet that is causing the upcall.
Open vSwitch CI 3f9b5c
-     *
Open vSwitch CI 3f9b5c
-     * When dispatching upcall per-cpu, the kernel will send the upcall via
Open vSwitch CI 3f9b5c
-     * a Netlink socket that has been selected based on the cpu that received
Open vSwitch CI 3f9b5c
-     * the packet that is causing the upcall.
Open vSwitch CI 3f9b5c
-     *
Open vSwitch CI 3f9b5c
-     * First we test to see if the kernel module supports per-cpu dispatching
Open vSwitch CI 3f9b5c
-     * (the preferred method). If it does not support per-cpu dispatching, we
Open vSwitch CI 3f9b5c
-     * fall back to the per-vport dispatch mode.
Open vSwitch CI 3f9b5c
+    /* Some older kernels will not reject unknown features. This will cause
Open vSwitch CI 3f9b5c
+     * 'ovs-vswitchd' to incorrectly assume a feature is supported. In order to
Open vSwitch CI 3f9b5c
+     * test for that, we attempt to set a feature that we know is not supported
Open vSwitch CI 3f9b5c
+     * by any kernel. If this feature is not rejected, we can assume we are
Open vSwitch CI 3f9b5c
+     * running on one of these older kernels.
Open vSwitch CI 3f9b5c
      */
Open vSwitch CI 3f9b5c
     dp_request.user_features |= OVS_DP_F_UNALIGNED;
Open vSwitch CI 3f9b5c
-    dp_request.user_features &= ~OVS_DP_F_VPORT_PIDS;
Open vSwitch CI 3f9b5c
-    dp_request.user_features |= OVS_DP_F_DISPATCH_UPCALL_PER_CPU;
Open vSwitch CI 3f9b5c
+    dp_request.user_features |= OVS_DP_F_VPORT_PIDS;
Open vSwitch CI 3f9b5c
+    dp_request.user_features |= OVS_DP_F_UNSUPPORTED;
Open vSwitch CI 3f9b5c
     error = dpif_netlink_dp_transact(&dp_request, &dp, &buf;;
Open vSwitch CI 3f9b5c
     if (error) {
Open vSwitch CI 3f9b5c
-        dp_request.user_features &= ~OVS_DP_F_DISPATCH_UPCALL_PER_CPU;
Open vSwitch CI 3f9b5c
+        /* The Open vSwitch kernel module has two modes for dispatching
Open vSwitch CI 3f9b5c
+         * upcalls: per-vport and per-cpu.
Open vSwitch CI 3f9b5c
+         *
Open vSwitch CI 3f9b5c
+         * When dispatching upcalls per-vport, the kernel will
Open vSwitch CI 3f9b5c
+         * send the upcall via a Netlink socket that has been selected based on
Open vSwitch CI 3f9b5c
+         * the vport that received the packet that is causing the upcall.
Open vSwitch CI 3f9b5c
+         *
Open vSwitch CI 3f9b5c
+         * When dispatching upcall per-cpu, the kernel will send the upcall via
Open vSwitch CI 3f9b5c
+         * a Netlink socket that has been selected based on the cpu that
Open vSwitch CI 3f9b5c
+         * received the packet that is causing the upcall.
Open vSwitch CI 3f9b5c
+         *
Open vSwitch CI 3f9b5c
+         * First we test to see if the kernel module supports per-cpu
Open vSwitch CI 3f9b5c
+         * dispatching (the preferred method). If it does not support per-cpu
Open vSwitch CI 3f9b5c
+         * dispatching, we fall back to the per-vport dispatch mode.
Open vSwitch CI 3f9b5c
+         */
Open vSwitch CI 3f9b5c
+        dp_request.user_features &= ~OVS_DP_F_UNSUPPORTED;
Open vSwitch CI 3f9b5c
+        dp_request.user_features |= OVS_DP_F_UNALIGNED;
Open vSwitch CI 3f9b5c
+        dp_request.user_features &= ~OVS_DP_F_VPORT_PIDS;
Open vSwitch CI 3f9b5c
+        dp_request.user_features |= OVS_DP_F_DISPATCH_UPCALL_PER_CPU;
Open vSwitch CI 3f9b5c
+        error = dpif_netlink_dp_transact(&dp_request, &dp, &buf;;
Open vSwitch CI 3f9b5c
+        if (error) {
Open vSwitch CI 3f9b5c
+            dp_request.user_features &= ~OVS_DP_F_DISPATCH_UPCALL_PER_CPU;
Open vSwitch CI 3f9b5c
+            dp_request.user_features |= OVS_DP_F_VPORT_PIDS;
Open vSwitch CI 3f9b5c
+            error = dpif_netlink_dp_transact(&dp_request, &dp, &buf;;
Open vSwitch CI 3f9b5c
+        }
Open vSwitch CI 3f9b5c
+        if (error) {
Open vSwitch CI 3f9b5c
+            return error;
Open vSwitch CI 3f9b5c
+        }
Open vSwitch CI 3f9b5c
+
Open vSwitch CI 3f9b5c
+        error = open_dpif(&dp, dpifp);
Open vSwitch CI 3f9b5c
+        dpif_netlink_set_features(*dpifp, OVS_DP_F_TC_RECIRC_SHARING);
Open vSwitch CI 3f9b5c
+    } else {
Open vSwitch CI 3f9b5c
+        VLOG_INFO("Kernel does not correctly support feature negotiation. "
Open vSwitch CI 3f9b5c
+                  "Using standard features.");
Open vSwitch CI 3f9b5c
+        dp_request.cmd = OVS_DP_CMD_SET;
Open vSwitch CI 3f9b5c
+        dp_request.user_features = 0;
Open vSwitch CI 3f9b5c
+        dp_request.user_features |= OVS_DP_F_UNALIGNED;
Open vSwitch CI 3f9b5c
         dp_request.user_features |= OVS_DP_F_VPORT_PIDS;
Open vSwitch CI 3f9b5c
         error = dpif_netlink_dp_transact(&dp_request, &dp, &buf;;
Open vSwitch CI 3f9b5c
-    }
Open vSwitch CI 3f9b5c
-    if (error) {
Open vSwitch CI 3f9b5c
-        return error;
Open vSwitch CI 3f9b5c
+        if (error) {
Open vSwitch CI 3f9b5c
+            return error;
Open vSwitch CI 3f9b5c
+        }
Open vSwitch CI 3f9b5c
+        error = open_dpif(&dp, dpifp);
Open vSwitch CI 3f9b5c
     }
Open vSwitch CI 3f9b5c
 
Open vSwitch CI 3f9b5c
-    error = open_dpif(&dp, dpifp);
Open vSwitch CI 3f9b5c
-    dpif_netlink_set_features(*dpifp, OVS_DP_F_TC_RECIRC_SHARING);
Open vSwitch CI 3f9b5c
     ofpbuf_delete(buf);
Open vSwitch CI 3f9b5c
 
Open vSwitch CI 3f9b5c
     if (create) {
110336
diff --git a/lib/ipf.c b/lib/ipf.c
Open vSwitch CI 3f9b5c
index d9f781147a..507db2aea2 100644
110336
--- a/lib/ipf.c
110336
+++ b/lib/ipf.c
Open vSwitch CI 3f9b5c
@@ -943,6 +943,8 @@ ipf_extract_frags_from_batch(struct ipf *ipf, struct dp_packet_batch *pb,
Open vSwitch CI 3f9b5c
             ovs_mutex_lock(&ipf->ipf_lock);
Open vSwitch CI 3f9b5c
             if (!ipf_handle_frag(ipf, pkt, dl_type, zone, now, hash_basis)) {
Open vSwitch CI 3f9b5c
                 dp_packet_batch_refill(pb, pkt, pb_idx);
Open vSwitch CI 3f9b5c
+            } else {
Open vSwitch CI 3f9b5c
+                dp_packet_delete(pkt);
Open vSwitch CI 3f9b5c
             }
Open vSwitch CI 3f9b5c
             ovs_mutex_unlock(&ipf->ipf_lock);
Open vSwitch CI 3f9b5c
         } else {
Open vSwitch CI 3f9b5c
@@ -1152,52 +1154,56 @@ ipf_post_execute_reass_pkts(struct ipf *ipf,
110336
          * NETDEV_MAX_BURST. */
110336
         DP_PACKET_BATCH_REFILL_FOR_EACH (pb_idx, pb_cnt, pkt, pb) {
110336
             if (rp && pkt == rp->list->reass_execute_ctx) {
110336
+                const struct ipf_frag *frag_0 = &rp->list->frag_list[0];
110336
+                void *l4_frag = dp_packet_l4(frag_0->pkt);
110336
+                void *l4_reass = dp_packet_l4(pkt);
110336
+                memcpy(l4_frag, l4_reass, dp_packet_l4_size(frag_0->pkt));
110336
+
110336
                 for (int i = 0; i <= rp->list->last_inuse_idx; i++) {
110336
-                    rp->list->frag_list[i].pkt->md.ct_label = pkt->md.ct_label;
110336
-                    rp->list->frag_list[i].pkt->md.ct_mark = pkt->md.ct_mark;
110336
-                    rp->list->frag_list[i].pkt->md.ct_state = pkt->md.ct_state;
110336
-                    rp->list->frag_list[i].pkt->md.ct_zone = pkt->md.ct_zone;
110336
-                    rp->list->frag_list[i].pkt->md.ct_orig_tuple_ipv6 =
110336
+                    const struct ipf_frag *frag_i = &rp->list->frag_list[i];
110336
+
110336
+                    frag_i->pkt->md.ct_label = pkt->md.ct_label;
110336
+                    frag_i->pkt->md.ct_mark = pkt->md.ct_mark;
110336
+                    frag_i->pkt->md.ct_state = pkt->md.ct_state;
110336
+                    frag_i->pkt->md.ct_zone = pkt->md.ct_zone;
110336
+                    frag_i->pkt->md.ct_orig_tuple_ipv6 =
110336
                         pkt->md.ct_orig_tuple_ipv6;
110336
                     if (pkt->md.ct_orig_tuple_ipv6) {
110336
-                        rp->list->frag_list[i].pkt->md.ct_orig_tuple.ipv6 =
110336
+                        frag_i->pkt->md.ct_orig_tuple.ipv6 =
110336
                             pkt->md.ct_orig_tuple.ipv6;
110336
                     } else {
110336
-                        rp->list->frag_list[i].pkt->md.ct_orig_tuple.ipv4  =
110336
+                        frag_i->pkt->md.ct_orig_tuple.ipv4 =
110336
                             pkt->md.ct_orig_tuple.ipv4;
110336
                     }
110336
-                }
110336
-
110336
-                const struct ipf_frag *frag_0 = &rp->list->frag_list[0];
110336
-                void *l4_frag = dp_packet_l4(frag_0->pkt);
110336
-                void *l4_reass = dp_packet_l4(pkt);
110336
-                memcpy(l4_frag, l4_reass, dp_packet_l4_size(frag_0->pkt));
110336
-
110336
-                if (v6) {
110336
-                    struct ovs_16aligned_ip6_hdr *l3_frag
110336
-                        = dp_packet_l3(frag_0->pkt);
110336
-                    struct ovs_16aligned_ip6_hdr *l3_reass = dp_packet_l3(pkt);
110336
-                    l3_frag->ip6_src = l3_reass->ip6_src;
110336
-                    l3_frag->ip6_dst = l3_reass->ip6_dst;
110336
-                } else {
110336
-                    struct ip_header *l3_frag = dp_packet_l3(frag_0->pkt);
110336
-                    struct ip_header *l3_reass = dp_packet_l3(pkt);
110336
-                    if (!dp_packet_hwol_is_ipv4(frag_0->pkt)) {
110336
-                        ovs_be32 reass_ip =
110336
-                            get_16aligned_be32(&l3_reass->ip_src);
110336
-                        ovs_be32 frag_ip =
110336
-                            get_16aligned_be32(&l3_frag->ip_src);
110336
-
110336
-                        l3_frag->ip_csum = recalc_csum32(l3_frag->ip_csum,
110336
-                                                         frag_ip, reass_ip);
110336
-                        reass_ip = get_16aligned_be32(&l3_reass->ip_dst);
110336
-                        frag_ip = get_16aligned_be32(&l3_frag->ip_dst);
110336
-                        l3_frag->ip_csum = recalc_csum32(l3_frag->ip_csum,
110336
-                                                         frag_ip, reass_ip);
110336
+                    if (v6) {
110336
+                        struct ovs_16aligned_ip6_hdr *l3_frag
110336
+                            = dp_packet_l3(frag_i->pkt);
110336
+                        struct ovs_16aligned_ip6_hdr *l3_reass
110336
+                            = dp_packet_l3(pkt);
110336
+                        l3_frag->ip6_src = l3_reass->ip6_src;
110336
+                        l3_frag->ip6_dst = l3_reass->ip6_dst;
110336
+                    } else {
110336
+                        struct ip_header *l3_frag = dp_packet_l3(frag_i->pkt);
110336
+                        struct ip_header *l3_reass = dp_packet_l3(pkt);
110336
+                        if (!dp_packet_hwol_is_ipv4(frag_i->pkt)) {
110336
+                            ovs_be32 reass_ip =
110336
+                                get_16aligned_be32(&l3_reass->ip_src);
110336
+                            ovs_be32 frag_ip =
110336
+                                get_16aligned_be32(&l3_frag->ip_src);
110336
+
110336
+                            l3_frag->ip_csum = recalc_csum32(l3_frag->ip_csum,
110336
+                                                             frag_ip,
110336
+                                                             reass_ip);
110336
+                            reass_ip = get_16aligned_be32(&l3_reass->ip_dst);
110336
+                            frag_ip = get_16aligned_be32(&l3_frag->ip_dst);
110336
+                            l3_frag->ip_csum = recalc_csum32(l3_frag->ip_csum,
110336
+                                                             frag_ip,
110336
+                                                             reass_ip);
110336
+                        }
110336
+
110336
+                        l3_frag->ip_src = l3_reass->ip_src;
110336
+                        l3_frag->ip_dst = l3_reass->ip_dst;
110336
                     }
110336
-
110336
-                    l3_frag->ip_src = l3_reass->ip_src;
110336
-                    l3_frag->ip_dst = l3_reass->ip_dst;
110336
                 }
110336
 
110336
                 ipf_completed_list_add(&ipf->frag_complete_list, rp->list);
110336
diff --git a/lib/json.c b/lib/json.c
110336
index 32d25003b8..0baf7c622c 100644
110336
--- a/lib/json.c
110336
+++ b/lib/json.c
110336
@@ -146,6 +146,7 @@ json_type_to_string(enum json_type type)
110336
     case JSON_STRING:
110336
         return "string";
110336
 
110336
+    case JSON_SERIALIZED_OBJECT:
110336
     case JSON_N_TYPES:
110336
     default:
110336
         return "<invalid>";
110336
@@ -180,6 +181,14 @@ json_string_create(const char *s)
110336
     return json_string_create_nocopy(xstrdup(s));
110336
 }
110336
 
110336
+struct json *
110336
+json_serialized_object_create(const struct json *src)
110336
+{
110336
+    struct json *json = json_create(JSON_SERIALIZED_OBJECT);
110336
+    json->string = json_to_string(src, JSSF_SORT);
110336
+    return json;
110336
+}
110336
+
110336
 struct json *
110336
 json_array_create_empty(void)
110336
 {
110336
@@ -309,6 +318,13 @@ json_string(const struct json *json)
110336
     return json->string;
110336
 }
110336
 
110336
+const char *
110336
+json_serialized_object(const struct json *json)
110336
+{
110336
+    ovs_assert(json->type == JSON_SERIALIZED_OBJECT);
110336
+    return json->string;
110336
+}
110336
+
110336
 struct json_array *
110336
 json_array(const struct json *json)
110336
 {
110336
@@ -362,6 +378,7 @@ json_destroy(struct json *json)
110336
             break;
110336
 
110336
         case JSON_STRING:
110336
+        case JSON_SERIALIZED_OBJECT:
110336
             free(json->string);
110336
             break;
110336
 
110336
@@ -422,6 +439,9 @@ json_deep_clone(const struct json *json)
110336
     case JSON_STRING:
110336
         return json_string_create(json->string);
110336
 
110336
+    case JSON_SERIALIZED_OBJECT:
110336
+        return json_serialized_object_create(json);
110336
+
110336
     case JSON_NULL:
110336
     case JSON_FALSE:
110336
     case JSON_TRUE:
110336
@@ -521,6 +541,7 @@ json_hash(const struct json *json, size_t basis)
110336
         return json_hash_array(&json->array, basis);
110336
 
110336
     case JSON_STRING:
110336
+    case JSON_SERIALIZED_OBJECT:
110336
         return hash_string(json->string, basis);
110336
 
110336
     case JSON_NULL:
110336
@@ -596,6 +617,7 @@ json_equal(const struct json *a, const struct json *b)
110336
         return json_equal_array(&a->array, &b->array);
110336
 
110336
     case JSON_STRING:
110336
+    case JSON_SERIALIZED_OBJECT:
110336
         return !strcmp(a->string, b->string);
110336
 
110336
     case JSON_NULL:
110336
@@ -1072,6 +1094,14 @@ json_from_string(const char *string)
110336
     return json_parser_finish(p);
110336
 }
110336
 
110336
+/* Parses data of JSON_SERIALIZED_OBJECT to the real JSON. */
110336
+struct json *
110336
+json_from_serialized_object(const struct json *json)
110336
+{
110336
+    ovs_assert(json->type == JSON_SERIALIZED_OBJECT);
110336
+    return json_from_string(json->string);
110336
+}
110336
+
110336
 /* Reads the file named 'file_name', parses its contents as a JSON object or
110336
  * array, and returns a newly allocated 'struct json'.  The caller must free
110336
  * the returned structure with json_destroy() when it is no longer needed.
110336
@@ -1563,6 +1593,10 @@ json_serialize(const struct json *json, struct json_serializer *s)
110336
         json_serialize_string(json->string, ds);
110336
         break;
110336
 
110336
+    case JSON_SERIALIZED_OBJECT:
110336
+        ds_put_cstr(ds, json->string);
110336
+        break;
110336
+
110336
     case JSON_N_TYPES:
110336
     default:
110336
         OVS_NOT_REACHED();
110336
@@ -1696,14 +1730,30 @@ json_serialize_string(const char *string, struct ds *ds)
110336
 {
110336
     uint8_t c;
110336
     uint8_t c2;
110336
+    size_t count;
110336
     const char *escape;
110336
+    const char *start;
110336
 
110336
     ds_put_char(ds, '"');
110336
+    count = 0;
110336
+    start = string;
110336
     while ((c = *string++) != '\0') {
110336
-        escape = chars_escaping[c];
110336
-        while ((c2 = *escape++) != '\0') {
110336
-            ds_put_char(ds, c2);
110336
+        if (c >= ' ' && c != '"' && c != '\\') {
110336
+            count++;
110336
+        } else {
110336
+            if (count) {
110336
+                ds_put_buffer(ds, start, count);
110336
+                count = 0;
110336
+            }
110336
+            start = string;
110336
+            escape = chars_escaping[c];
110336
+            while ((c2 = *escape++) != '\0') {
110336
+                ds_put_char(ds, c2);
110336
+            }
110336
         }
110336
     }
110336
+    if (count) {
110336
+        ds_put_buffer(ds, start, count);
110336
+    }
110336
     ds_put_char(ds, '"');
110336
 }
Open vSwitch CI 3f9b5c
diff --git a/lib/meta-flow.c b/lib/meta-flow.c
Open vSwitch CI 3f9b5c
index c808d205d5..e03cd8d0c5 100644
Open vSwitch CI 3f9b5c
--- a/lib/meta-flow.c
Open vSwitch CI 3f9b5c
+++ b/lib/meta-flow.c
Open vSwitch CI 3f9b5c
@@ -1788,6 +1788,19 @@ mf_is_tun_metadata(const struct mf_field *mf)
Open vSwitch CI 3f9b5c
            mf->id < MFF_TUN_METADATA0 + TUN_METADATA_NUM_OPTS;
Open vSwitch CI 3f9b5c
 }
Open vSwitch CI 3f9b5c
 
Open vSwitch CI 3f9b5c
+bool
Open vSwitch CI 3f9b5c
+mf_is_frozen_metadata(const struct mf_field *mf)
Open vSwitch CI 3f9b5c
+{
Open vSwitch CI 3f9b5c
+    if (mf->id >= MFF_TUN_ID && mf->id <= MFF_IN_PORT_OXM) {
Open vSwitch CI 3f9b5c
+        return true;
Open vSwitch CI 3f9b5c
+    }
Open vSwitch CI 3f9b5c
+
Open vSwitch CI 3f9b5c
+    if (mf->id >= MFF_REG0 && mf->id < MFF_ETH_SRC) {
Open vSwitch CI 3f9b5c
+        return true;
Open vSwitch CI 3f9b5c
+    }
Open vSwitch CI 3f9b5c
+    return false;
Open vSwitch CI 3f9b5c
+}
Open vSwitch CI 3f9b5c
+
Open vSwitch CI 3f9b5c
 bool
Open vSwitch CI 3f9b5c
 mf_is_pipeline_field(const struct mf_field *mf)
Open vSwitch CI 3f9b5c
 {
110336
diff --git a/lib/netdev-dpdk.c b/lib/netdev-dpdk.c
110336
index 45a96b9be2..ca92c947a2 100644
110336
--- a/lib/netdev-dpdk.c
110336
+++ b/lib/netdev-dpdk.c
110336
@@ -961,14 +961,6 @@ dpdk_eth_dev_port_config(struct netdev_dpdk *dev, int n_rxq, int n_txq)
110336
 
110336
     rte_eth_dev_info_get(dev->port_id, &info;;
110336
 
110336
-    /* As of DPDK 19.11, it is not allowed to set a mq_mode for
110336
-     * virtio PMD driver. */
110336
-    if (!strcmp(info.driver_name, "net_virtio")) {
110336
-        conf.rxmode.mq_mode = ETH_MQ_RX_NONE;
110336
-    } else {
110336
-        conf.rxmode.mq_mode = ETH_MQ_RX_RSS;
110336
-    }
110336
-
110336
     /* As of DPDK 17.11.1 a few PMDs require to explicitly enable
110336
      * scatter to support jumbo RX.
110336
      * Setting scatter for the device is done after checking for
110336
@@ -1000,6 +992,11 @@ dpdk_eth_dev_port_config(struct netdev_dpdk *dev, int n_rxq, int n_txq)
110336
     /* Limit configured rss hash functions to only those supported
110336
      * by the eth device. */
110336
     conf.rx_adv_conf.rss_conf.rss_hf &= info.flow_type_rss_offloads;
110336
+    if (conf.rx_adv_conf.rss_conf.rss_hf == 0) {
110336
+        conf.rxmode.mq_mode = ETH_MQ_RX_NONE;
110336
+    } else {
110336
+        conf.rxmode.mq_mode = ETH_MQ_RX_RSS;
110336
+    }
110336
 
110336
     /* A device may report more queues than it makes available (this has
110336
      * been observed for Intel xl710, which reserves some of them for
110336
diff --git a/lib/netdev-linux.c b/lib/netdev-linux.c
110336
index 60dd138914..97bd21be4a 100644
110336
--- a/lib/netdev-linux.c
110336
+++ b/lib/netdev-linux.c
110336
@@ -627,6 +627,7 @@ netdev_linux_notify_sock(void)
110336
         if (!error) {
110336
             size_t i;
110336
 
110336
+            nl_sock_listen_all_nsid(sock, true);
110336
             for (i = 0; i < ARRAY_SIZE(mcgroups); i++) {
110336
                 error = nl_sock_join_mcgroup(sock, mcgroups[i]);
110336
                 if (error) {
110336
@@ -636,7 +637,6 @@ netdev_linux_notify_sock(void)
110336
                 }
110336
             }
110336
         }
110336
-        nl_sock_listen_all_nsid(sock, true);
110336
         ovsthread_once_done(&once);
110336
     }
110336
 
110336
diff --git a/lib/odp-util.c b/lib/odp-util.c
110336
index 7729a90608..fbdfc7ad83 100644
110336
--- a/lib/odp-util.c
110336
+++ b/lib/odp-util.c
110336
@@ -2941,7 +2941,7 @@ odp_nsh_key_from_attr__(const struct nlattr *attr, bool is_mask,
110336
             const struct ovs_nsh_key_md1 *md1 = nl_attr_get(a);
110336
             has_md1 = true;
110336
             memcpy(nsh->context, md1->context, sizeof md1->context);
110336
-            if (len == 2 * sizeof(*md1)) {
110336
+            if (nsh_mask && (len == 2 * sizeof *md1)) {
110336
                 const struct ovs_nsh_key_md1 *md1_mask = md1 + 1;
110336
                 memcpy(nsh_mask->context, md1_mask->context,
110336
                        sizeof(*md1_mask));
110336
@@ -4618,7 +4618,7 @@ odp_flow_format(const struct nlattr *key, size_t key_len,
110336
             }
110336
             ds_put_char(ds, ')');
110336
         }
110336
-        if (!has_ethtype_key) {
110336
+        if (!has_ethtype_key && mask) {
110336
             const struct nlattr *ma = nl_attr_find__(mask, mask_len,
110336
                                                      OVS_KEY_ATTR_ETHERTYPE);
110336
             if (ma) {
Open vSwitch CI 3f9b5c
diff --git a/lib/ovsdb-cs.c b/lib/ovsdb-cs.c
Open vSwitch CI 3f9b5c
index 659d49dbf7..fcb6fe1b34 100644
Open vSwitch CI 3f9b5c
--- a/lib/ovsdb-cs.c
Open vSwitch CI 3f9b5c
+++ b/lib/ovsdb-cs.c
Open vSwitch CI 3f9b5c
@@ -1833,7 +1833,7 @@ server_column_get_string(const struct server_row *row,
Open vSwitch CI 3f9b5c
 {
Open vSwitch CI 3f9b5c
     ovs_assert(server_columns[index].type.key.type == OVSDB_TYPE_STRING);
Open vSwitch CI 3f9b5c
     const struct ovsdb_datum *d = &row->data[index];
Open vSwitch CI 3f9b5c
-    return d->n == 1 ? d->keys[0].string : default_value;
Open vSwitch CI 3f9b5c
+    return d->n == 1 ? d->keys[0].s->string : default_value;
Open vSwitch CI 3f9b5c
 }
Open vSwitch CI 3f9b5c
 
Open vSwitch CI 3f9b5c
 static bool
Open vSwitch CI 3f9b5c
diff --git a/lib/ovsdb-data.c b/lib/ovsdb-data.c
Open vSwitch CI 3f9b5c
index c145f5ad97..6654ed6deb 100644
Open vSwitch CI 3f9b5c
--- a/lib/ovsdb-data.c
Open vSwitch CI 3f9b5c
+++ b/lib/ovsdb-data.c
Open vSwitch CI 3f9b5c
@@ -74,7 +74,7 @@ ovsdb_atom_init_default(union ovsdb_atom *atom, enum ovsdb_atomic_type type)
Open vSwitch CI 3f9b5c
         break;
Open vSwitch CI 3f9b5c
 
Open vSwitch CI 3f9b5c
     case OVSDB_TYPE_STRING:
Open vSwitch CI 3f9b5c
-        atom->string = xmemdup("", 1);
Open vSwitch CI 3f9b5c
+        atom->s = ovsdb_atom_string_create_nocopy(xmemdup("", 1));
Open vSwitch CI 3f9b5c
         break;
Open vSwitch CI 3f9b5c
 
Open vSwitch CI 3f9b5c
     case OVSDB_TYPE_UUID:
Open vSwitch CI 3f9b5c
@@ -136,7 +136,7 @@ ovsdb_atom_is_default(const union ovsdb_atom *atom,
Open vSwitch CI 3f9b5c
         return atom->boolean == false;
Open vSwitch CI 3f9b5c
 
Open vSwitch CI 3f9b5c
     case OVSDB_TYPE_STRING:
Open vSwitch CI 3f9b5c
-        return atom->string[0] == '\0';
Open vSwitch CI 3f9b5c
+        return atom->s->string[0] == '\0';
Open vSwitch CI 3f9b5c
 
Open vSwitch CI 3f9b5c
     case OVSDB_TYPE_UUID:
Open vSwitch CI 3f9b5c
         return uuid_is_zero(&atom->uuid);
Open vSwitch CI 3f9b5c
@@ -172,7 +172,8 @@ ovsdb_atom_clone(union ovsdb_atom *new, const union ovsdb_atom *old,
Open vSwitch CI 3f9b5c
         break;
Open vSwitch CI 3f9b5c
 
Open vSwitch CI 3f9b5c
     case OVSDB_TYPE_STRING:
Open vSwitch CI 3f9b5c
-        new->string = xstrdup(old->string);
Open vSwitch CI 3f9b5c
+        new->s = old->s;
Open vSwitch CI 3f9b5c
+        new->s->n_refs++;
Open vSwitch CI 3f9b5c
         break;
Open vSwitch CI 3f9b5c
 
Open vSwitch CI 3f9b5c
     case OVSDB_TYPE_UUID:
Open vSwitch CI 3f9b5c
@@ -214,7 +215,7 @@ ovsdb_atom_hash(const union ovsdb_atom *atom, enum ovsdb_atomic_type type,
Open vSwitch CI 3f9b5c
         return hash_boolean(atom->boolean, basis);
Open vSwitch CI 3f9b5c
 
Open vSwitch CI 3f9b5c
     case OVSDB_TYPE_STRING:
Open vSwitch CI 3f9b5c
-        return hash_string(atom->string, basis);
Open vSwitch CI 3f9b5c
+        return hash_string(atom->s->string, basis);
Open vSwitch CI 3f9b5c
 
Open vSwitch CI 3f9b5c
     case OVSDB_TYPE_UUID:
Open vSwitch CI 3f9b5c
         return hash_int(uuid_hash(&atom->uuid), basis);
Open vSwitch CI 3f9b5c
@@ -246,7 +247,7 @@ ovsdb_atom_compare_3way(const union ovsdb_atom *a,
Open vSwitch CI 3f9b5c
         return a->boolean - b->boolean;
Open vSwitch CI 3f9b5c
 
Open vSwitch CI 3f9b5c
     case OVSDB_TYPE_STRING:
Open vSwitch CI 3f9b5c
-        return strcmp(a->string, b->string);
Open vSwitch CI 3f9b5c
+        return a->s == b->s ? 0 : strcmp(a->s->string, b->s->string);
Open vSwitch CI 3f9b5c
 
Open vSwitch CI 3f9b5c
     case OVSDB_TYPE_UUID:
Open vSwitch CI 3f9b5c
         return uuid_compare_3way(&a->uuid, &b->uuid);
Open vSwitch CI 3f9b5c
@@ -404,7 +405,7 @@ ovsdb_atom_from_json__(union ovsdb_atom *atom,
Open vSwitch CI 3f9b5c
 
Open vSwitch CI 3f9b5c
     case OVSDB_TYPE_STRING:
Open vSwitch CI 3f9b5c
         if (json->type == JSON_STRING) {
Open vSwitch CI 3f9b5c
-            atom->string = xstrdup(json->string);
Open vSwitch CI 3f9b5c
+            atom->s = ovsdb_atom_string_create(json->string);
Open vSwitch CI 3f9b5c
             return NULL;
Open vSwitch CI 3f9b5c
         }
Open vSwitch CI 3f9b5c
         break;
Open vSwitch CI 3f9b5c
@@ -473,7 +474,7 @@ ovsdb_atom_to_json(const union ovsdb_atom *atom, enum ovsdb_atomic_type type)
Open vSwitch CI 3f9b5c
         return json_boolean_create(atom->boolean);
Open vSwitch CI 3f9b5c
 
Open vSwitch CI 3f9b5c
     case OVSDB_TYPE_STRING:
Open vSwitch CI 3f9b5c
-        return json_string_create(atom->string);
Open vSwitch CI 3f9b5c
+        return json_string_create(atom->s->string);
Open vSwitch CI 3f9b5c
 
Open vSwitch CI 3f9b5c
     case OVSDB_TYPE_UUID:
Open vSwitch CI 3f9b5c
         return wrap_json("uuid", json_string_create_nocopy(
Open vSwitch CI 3f9b5c
@@ -551,14 +552,18 @@ ovsdb_atom_from_string__(union ovsdb_atom *atom,
Open vSwitch CI 3f9b5c
             if (s_len < 2 || s[s_len - 1] != '"') {
Open vSwitch CI 3f9b5c
                 return xasprintf("%s: missing quote at end of "
Open vSwitch CI 3f9b5c
                                  "quoted string", s);
Open vSwitch CI 3f9b5c
-            } else if (!json_string_unescape(s + 1, s_len - 2,
Open vSwitch CI 3f9b5c
-                                             &atom->string)) {
Open vSwitch CI 3f9b5c
-                char *error = xasprintf("%s: %s", s, atom->string);
Open vSwitch CI 3f9b5c
-                free(atom->string);
Open vSwitch CI 3f9b5c
-                return error;
Open vSwitch CI 3f9b5c
+            } else {
Open vSwitch CI 3f9b5c
+                char *res;
Open vSwitch CI 3f9b5c
+                if (json_string_unescape(s + 1, s_len - 2, &res)) {
Open vSwitch CI 3f9b5c
+                    atom->s = ovsdb_atom_string_create_nocopy(res);
Open vSwitch CI 3f9b5c
+                } else {
Open vSwitch CI 3f9b5c
+                    char *error = xasprintf("%s: %s", s, res);
Open vSwitch CI 3f9b5c
+                    free(res);
Open vSwitch CI 3f9b5c
+                    return error;
Open vSwitch CI 3f9b5c
+                }
Open vSwitch CI 3f9b5c
             }
Open vSwitch CI 3f9b5c
         } else {
Open vSwitch CI 3f9b5c
-            atom->string = xstrdup(s);
Open vSwitch CI 3f9b5c
+            atom->s = ovsdb_atom_string_create(s);
Open vSwitch CI 3f9b5c
         }
Open vSwitch CI 3f9b5c
         break;
Open vSwitch CI 3f9b5c
 
Open vSwitch CI 3f9b5c
@@ -721,14 +726,14 @@ ovsdb_atom_to_string(const union ovsdb_atom *atom, enum ovsdb_atomic_type type,
Open vSwitch CI 3f9b5c
         break;
Open vSwitch CI 3f9b5c
 
Open vSwitch CI 3f9b5c
     case OVSDB_TYPE_STRING:
Open vSwitch CI 3f9b5c
-        if (string_needs_quotes(atom->string)) {
Open vSwitch CI 3f9b5c
+        if (string_needs_quotes(atom->s->string)) {
Open vSwitch CI 3f9b5c
             struct json json;
Open vSwitch CI 3f9b5c
 
Open vSwitch CI 3f9b5c
             json.type = JSON_STRING;
Open vSwitch CI 3f9b5c
-            json.string = atom->string;
Open vSwitch CI 3f9b5c
+            json.string = atom->s->string;
Open vSwitch CI 3f9b5c
             json_to_ds(&json, 0, out);
Open vSwitch CI 3f9b5c
         } else {
Open vSwitch CI 3f9b5c
-            ds_put_cstr(out, atom->string);
Open vSwitch CI 3f9b5c
+            ds_put_cstr(out, atom->s->string);
Open vSwitch CI 3f9b5c
         }
Open vSwitch CI 3f9b5c
         break;
Open vSwitch CI 3f9b5c
 
Open vSwitch CI 3f9b5c
@@ -750,7 +755,7 @@ ovsdb_atom_to_bare(const union ovsdb_atom *atom, enum ovsdb_atomic_type type,
Open vSwitch CI 3f9b5c
                    struct ds *out)
Open vSwitch CI 3f9b5c
 {
Open vSwitch CI 3f9b5c
     if (type == OVSDB_TYPE_STRING) {
Open vSwitch CI 3f9b5c
-        ds_put_cstr(out, atom->string);
Open vSwitch CI 3f9b5c
+        ds_put_cstr(out, atom->s->string);
Open vSwitch CI 3f9b5c
     } else {
Open vSwitch CI 3f9b5c
         ovsdb_atom_to_string(atom, type, out);
Open vSwitch CI 3f9b5c
     }
Open vSwitch CI 3f9b5c
@@ -799,7 +804,7 @@ ovsdb_atom_check_constraints(const union ovsdb_atom *atom,
Open vSwitch CI 3f9b5c
                              const struct ovsdb_base_type *base)
Open vSwitch CI 3f9b5c
 {
Open vSwitch CI 3f9b5c
     if (base->enum_
Open vSwitch CI 3f9b5c
-        && ovsdb_datum_find_key(base->enum_, atom, base->type) == UINT_MAX) {
Open vSwitch CI 3f9b5c
+        && !ovsdb_datum_find_key(base->enum_, atom, base->type, NULL)) {
Open vSwitch CI 3f9b5c
         struct ovsdb_error *error;
Open vSwitch CI 3f9b5c
         struct ds actual = DS_EMPTY_INITIALIZER;
Open vSwitch CI 3f9b5c
         struct ds valid = DS_EMPTY_INITIALIZER;
Open vSwitch CI 3f9b5c
@@ -877,7 +882,7 @@ ovsdb_atom_check_constraints(const union ovsdb_atom *atom,
Open vSwitch CI 3f9b5c
         return NULL;
Open vSwitch CI 3f9b5c
 
Open vSwitch CI 3f9b5c
     case OVSDB_TYPE_STRING:
Open vSwitch CI 3f9b5c
-        return check_string_constraints(atom->string, &base->string);
Open vSwitch CI 3f9b5c
+        return check_string_constraints(atom->s->string, &base->string);
Open vSwitch CI 3f9b5c
 
Open vSwitch CI 3f9b5c
     case OVSDB_TYPE_UUID:
Open vSwitch CI 3f9b5c
         return NULL;
Open vSwitch CI 3f9b5c
@@ -1691,8 +1696,8 @@ ovsdb_datum_from_smap(struct ovsdb_datum *datum, const struct smap *smap)
Open vSwitch CI 3f9b5c
     struct smap_node *node;
Open vSwitch CI 3f9b5c
     size_t i = 0;
Open vSwitch CI 3f9b5c
     SMAP_FOR_EACH (node, smap) {
Open vSwitch CI 3f9b5c
-        datum->keys[i].string = xstrdup(node->key);
Open vSwitch CI 3f9b5c
-        datum->values[i].string = xstrdup(node->value);
Open vSwitch CI 3f9b5c
+        datum->keys[i].s = ovsdb_atom_string_create(node->key);
Open vSwitch CI 3f9b5c
+        datum->values[i].s = ovsdb_atom_string_create(node->value);
Open vSwitch CI 3f9b5c
         i++;
Open vSwitch CI 3f9b5c
     }
Open vSwitch CI 3f9b5c
     ovs_assert(i == datum->n);
Open vSwitch CI 3f9b5c
@@ -1784,14 +1789,16 @@ ovsdb_datum_compare_3way(const struct ovsdb_datum *a,
Open vSwitch CI 3f9b5c
                                        a->n));
Open vSwitch CI 3f9b5c
 }
Open vSwitch CI 3f9b5c
 
Open vSwitch CI 3f9b5c
-/* If 'key' is one of the keys in 'datum', returns its index within 'datum',
Open vSwitch CI 3f9b5c
- * otherwise UINT_MAX.  'key.type' must be the type of the atoms stored in the
Open vSwitch CI 3f9b5c
- * 'keys' array in 'datum'.
Open vSwitch CI 3f9b5c
+/* If 'key' is one of the keys in 'datum', returns 'true' and sets '*pos' to
Open vSwitch CI 3f9b5c
+ * its index within 'datum', otherwise returns 'false' and sets '*pos' to the
Open vSwitch CI 3f9b5c
+ * index where 'key' should have been.  'key.type' must be the type of the
Open vSwitch CI 3f9b5c
+ * atoms stored in the 'keys' array in 'datum'.
Open vSwitch CI 3f9b5c
  */
Open vSwitch CI 3f9b5c
-unsigned int
Open vSwitch CI 3f9b5c
+bool
Open vSwitch CI 3f9b5c
 ovsdb_datum_find_key(const struct ovsdb_datum *datum,
Open vSwitch CI 3f9b5c
                      const union ovsdb_atom *key,
Open vSwitch CI 3f9b5c
-                     enum ovsdb_atomic_type key_type)
Open vSwitch CI 3f9b5c
+                     enum ovsdb_atomic_type key_type,
Open vSwitch CI 3f9b5c
+                     unsigned int *pos)
Open vSwitch CI 3f9b5c
 {
Open vSwitch CI 3f9b5c
     unsigned int low = 0;
Open vSwitch CI 3f9b5c
     unsigned int high = datum->n;
Open vSwitch CI 3f9b5c
@@ -1803,10 +1810,16 @@ ovsdb_datum_find_key(const struct ovsdb_datum *datum,
Open vSwitch CI 3f9b5c
         } else if (cmp > 0) {
Open vSwitch CI 3f9b5c
             low = idx + 1;
Open vSwitch CI 3f9b5c
         } else {
Open vSwitch CI 3f9b5c
-            return idx;
Open vSwitch CI 3f9b5c
+            if (pos) {
Open vSwitch CI 3f9b5c
+                *pos = idx;
Open vSwitch CI 3f9b5c
+            }
Open vSwitch CI 3f9b5c
+            return true;
Open vSwitch CI 3f9b5c
         }
Open vSwitch CI 3f9b5c
     }
Open vSwitch CI 3f9b5c
-    return UINT_MAX;
Open vSwitch CI 3f9b5c
+    if (pos) {
Open vSwitch CI 3f9b5c
+        *pos = low;
Open vSwitch CI 3f9b5c
+    }
Open vSwitch CI 3f9b5c
+    return false;
Open vSwitch CI 3f9b5c
 }
Open vSwitch CI 3f9b5c
 
Open vSwitch CI 3f9b5c
 /* If 'key' and 'value' is one of the key-value pairs in 'datum', returns its
Open vSwitch CI 3f9b5c
@@ -1821,10 +1834,11 @@ ovsdb_datum_find_key_value(const struct ovsdb_datum *datum,
Open vSwitch CI 3f9b5c
                            const union ovsdb_atom *value,
Open vSwitch CI 3f9b5c
                            enum ovsdb_atomic_type value_type)
Open vSwitch CI 3f9b5c
 {
Open vSwitch CI 3f9b5c
-    unsigned int idx = ovsdb_datum_find_key(datum, key, key_type);
Open vSwitch CI 3f9b5c
-    if (idx != UINT_MAX
Open vSwitch CI 3f9b5c
-        && value_type != OVSDB_TYPE_VOID
Open vSwitch CI 3f9b5c
-        && !ovsdb_atom_equals(&datum->values[idx], value, value_type)) {
Open vSwitch CI 3f9b5c
+    unsigned int idx;
Open vSwitch CI 3f9b5c
+
Open vSwitch CI 3f9b5c
+    if (!ovsdb_datum_find_key(datum, key, key_type, &idx)
Open vSwitch CI 3f9b5c
+        || (value_type != OVSDB_TYPE_VOID
Open vSwitch CI 3f9b5c
+            && !ovsdb_atom_equals(&datum->values[idx], value, value_type))) {
Open vSwitch CI 3f9b5c
         idx = UINT_MAX;
Open vSwitch CI 3f9b5c
     }
Open vSwitch CI 3f9b5c
     return idx;
Open vSwitch CI 3f9b5c
@@ -1948,38 +1962,68 @@ ovsdb_datum_add_unsafe(struct ovsdb_datum *datum,
Open vSwitch CI 3f9b5c
     }
Open vSwitch CI 3f9b5c
 }
Open vSwitch CI 3f9b5c
 
Open vSwitch CI 3f9b5c
+/* Adds 'n' atoms starting from index 'start_idx' from 'src' to the end of
Open vSwitch CI 3f9b5c
+ * 'dst'.  'dst' should have enough memory allocated to hold the additional
Open vSwitch CI 3f9b5c
+ * 'n' atoms.  Atoms are not cloned, i.e. 'dst' will reference the same data.
Open vSwitch CI 3f9b5c
+ * Caller also should take care of the result being sorted. */
Open vSwitch CI 3f9b5c
+static void
Open vSwitch CI 3f9b5c
+ovsdb_datum_push_unsafe(struct ovsdb_datum *dst,
Open vSwitch CI 3f9b5c
+                        const struct ovsdb_datum *src,
Open vSwitch CI 3f9b5c
+                        unsigned int start_idx, unsigned int n,
Open vSwitch CI 3f9b5c
+                        const struct ovsdb_type *type)
Open vSwitch CI 3f9b5c
+{
Open vSwitch CI 3f9b5c
+    memcpy(&dst->keys[dst->n], &src->keys[start_idx], n * sizeof src->keys[0]);
Open vSwitch CI 3f9b5c
+    if (type->value.type != OVSDB_TYPE_VOID) {
Open vSwitch CI 3f9b5c
+        memcpy(&dst->values[dst->n], &src->values[start_idx],
Open vSwitch CI 3f9b5c
+               n * sizeof src->values[0]);
Open vSwitch CI 3f9b5c
+    }
Open vSwitch CI 3f9b5c
+    dst->n += n;
Open vSwitch CI 3f9b5c
+}
Open vSwitch CI 3f9b5c
+
Open vSwitch CI 3f9b5c
 void
Open vSwitch CI 3f9b5c
 ovsdb_datum_union(struct ovsdb_datum *a, const struct ovsdb_datum *b,
Open vSwitch CI 3f9b5c
-                  const struct ovsdb_type *type, bool replace)
Open vSwitch CI 3f9b5c
+                  const struct ovsdb_type *type)
Open vSwitch CI 3f9b5c
 {
Open vSwitch CI 3f9b5c
-    unsigned int n;
Open vSwitch CI 3f9b5c
-    size_t bi;
Open vSwitch CI 3f9b5c
+    struct ovsdb_datum result;
Open vSwitch CI 3f9b5c
+    unsigned int copied, pos;
Open vSwitch CI 3f9b5c
 
Open vSwitch CI 3f9b5c
-    n = a->n;
Open vSwitch CI 3f9b5c
-    for (bi = 0; bi < b->n; bi++) {
Open vSwitch CI 3f9b5c
-        unsigned int ai;
Open vSwitch CI 3f9b5c
+    ovsdb_datum_init_empty(&result);
Open vSwitch CI 3f9b5c
 
Open vSwitch CI 3f9b5c
-        ai = ovsdb_datum_find_key(a, &b->keys[bi], type->key.type);
Open vSwitch CI 3f9b5c
-        if (ai == UINT_MAX) {
Open vSwitch CI 3f9b5c
-            if (n == a->n) {
Open vSwitch CI 3f9b5c
-                ovsdb_datum_reallocate(a, type, a->n + (b->n - bi));
Open vSwitch CI 3f9b5c
-            }
Open vSwitch CI 3f9b5c
-            ovsdb_atom_clone(&a->keys[n], &b->keys[bi], type->key.type);
Open vSwitch CI 3f9b5c
-            if (type->value.type != OVSDB_TYPE_VOID) {
Open vSwitch CI 3f9b5c
-                ovsdb_atom_clone(&a->values[n], &b->values[bi],
Open vSwitch CI 3f9b5c
-                                 type->value.type);
Open vSwitch CI 3f9b5c
-            }
Open vSwitch CI 3f9b5c
-            n++;
Open vSwitch CI 3f9b5c
-        } else if (replace && type->value.type != OVSDB_TYPE_VOID) {
Open vSwitch CI 3f9b5c
-            ovsdb_atom_destroy(&a->values[ai], type->value.type);
Open vSwitch CI 3f9b5c
-            ovsdb_atom_clone(&a->values[ai], &b->values[bi],
Open vSwitch CI 3f9b5c
+    copied = 0;
Open vSwitch CI 3f9b5c
+    for (size_t bi = 0; bi < b->n; bi++) {
Open vSwitch CI 3f9b5c
+        if (ovsdb_datum_find_key(a, &b->keys[bi], type->key.type, &pos)) {
Open vSwitch CI 3f9b5c
+            /* Atom with the same key already exists. */
Open vSwitch CI 3f9b5c
+            continue;
Open vSwitch CI 3f9b5c
+        }
Open vSwitch CI 3f9b5c
+        if (!result.keys) {
Open vSwitch CI 3f9b5c
+            ovsdb_datum_reallocate(&result, type, a->n + (b->n - bi));
Open vSwitch CI 3f9b5c
+        }
Open vSwitch CI 3f9b5c
+        if (pos > copied) {
Open vSwitch CI 3f9b5c
+            /* Need to copy some atoms from 'a' first. */
Open vSwitch CI 3f9b5c
+            ovsdb_datum_push_unsafe(&result, a, copied, pos - copied, type);
Open vSwitch CI 3f9b5c
+            copied = pos;
Open vSwitch CI 3f9b5c
+        }
Open vSwitch CI 3f9b5c
+        /* Inserting new atom from 'b'. */
Open vSwitch CI 3f9b5c
+        ovsdb_atom_clone(&result.keys[result.n], &b->keys[bi], type->key.type);
Open vSwitch CI 3f9b5c
+        if (type->value.type != OVSDB_TYPE_VOID) {
Open vSwitch CI 3f9b5c
+            ovsdb_atom_clone(&result.values[result.n], &b->values[bi],
Open vSwitch CI 3f9b5c
                              type->value.type);
Open vSwitch CI 3f9b5c
         }
Open vSwitch CI 3f9b5c
+        result.n++;
Open vSwitch CI 3f9b5c
     }
Open vSwitch CI 3f9b5c
-    if (n != a->n) {
Open vSwitch CI 3f9b5c
-        a->n = n;
Open vSwitch CI 3f9b5c
-        ovs_assert(!ovsdb_datum_sort(a, type->key.type));
Open vSwitch CI 3f9b5c
+    if (!result.keys) {
Open vSwitch CI 3f9b5c
+        /* 'a' doesn't need to be changed. */
Open vSwitch CI 3f9b5c
+        return;
Open vSwitch CI 3f9b5c
+    }
Open vSwitch CI 3f9b5c
+    if (a->n > copied) {
Open vSwitch CI 3f9b5c
+        /* Copying remaining atoms. */
Open vSwitch CI 3f9b5c
+        ovsdb_datum_push_unsafe(&result, a, copied, a->n - copied, type);
Open vSwitch CI 3f9b5c
     }
Open vSwitch CI 3f9b5c
+    /* All atoms are copied now. */
Open vSwitch CI 3f9b5c
+    a->n = 0;
Open vSwitch CI 3f9b5c
+
Open vSwitch CI 3f9b5c
+    ovsdb_datum_swap(&result, a);
Open vSwitch CI 3f9b5c
+    ovsdb_datum_destroy(&result, type);
Open vSwitch CI 3f9b5c
 }
Open vSwitch CI 3f9b5c
 
Open vSwitch CI 3f9b5c
 void
Open vSwitch CI 3f9b5c
@@ -1987,26 +2031,55 @@ ovsdb_datum_subtract(struct ovsdb_datum *a, const struct ovsdb_type *a_type,
Open vSwitch CI 3f9b5c
                      const struct ovsdb_datum *b,
Open vSwitch CI 3f9b5c
                      const struct ovsdb_type *b_type)
Open vSwitch CI 3f9b5c
 {
Open vSwitch CI 3f9b5c
-    bool changed = false;
Open vSwitch CI 3f9b5c
-    size_t i;
Open vSwitch CI 3f9b5c
+    unsigned int *idx, ai;
Open vSwitch CI 3f9b5c
+    size_t n_idx;
Open vSwitch CI 3f9b5c
 
Open vSwitch CI 3f9b5c
     ovs_assert(a_type->key.type == b_type->key.type);
Open vSwitch CI 3f9b5c
     ovs_assert(a_type->value.type == b_type->value.type
Open vSwitch CI 3f9b5c
                || b_type->value.type == OVSDB_TYPE_VOID);
Open vSwitch CI 3f9b5c
 
Open vSwitch CI 3f9b5c
-    /* XXX The big-O of this could easily be improved. */
Open vSwitch CI 3f9b5c
-    for (i = 0; i < a->n; ) {
Open vSwitch CI 3f9b5c
-        unsigned int idx = ovsdb_datum_find(a, i, b, b_type);
Open vSwitch CI 3f9b5c
-        if (idx != UINT_MAX) {
Open vSwitch CI 3f9b5c
-            changed = true;
Open vSwitch CI 3f9b5c
-            ovsdb_datum_remove_unsafe(a, i, a_type);
Open vSwitch CI 3f9b5c
-        } else {
Open vSwitch CI 3f9b5c
-            i++;
Open vSwitch CI 3f9b5c
+    idx = xmalloc(b->n * sizeof *idx);
Open vSwitch CI 3f9b5c
+    n_idx = 0;
Open vSwitch CI 3f9b5c
+    for (size_t bi = 0; bi < b->n; bi++) {
Open vSwitch CI 3f9b5c
+        ai = ovsdb_datum_find(b, bi, a, b_type);
Open vSwitch CI 3f9b5c
+        if (ai == UINT_MAX) {
Open vSwitch CI 3f9b5c
+            /* No such atom in 'a'. */
Open vSwitch CI 3f9b5c
+            continue;
Open vSwitch CI 3f9b5c
         }
Open vSwitch CI 3f9b5c
+        /* Not destroying right away since ovsdb_datum_find() will use them. */
Open vSwitch CI 3f9b5c
+        idx[n_idx++] = ai;
Open vSwitch CI 3f9b5c
     }
Open vSwitch CI 3f9b5c
-    if (changed) {
Open vSwitch CI 3f9b5c
-        ovsdb_datum_sort_assert(a, a_type->key.type);
Open vSwitch CI 3f9b5c
+    if (!n_idx) {
Open vSwitch CI 3f9b5c
+        free(idx);
Open vSwitch CI 3f9b5c
+        return;
Open vSwitch CI 3f9b5c
+    }
Open vSwitch CI 3f9b5c
+
Open vSwitch CI 3f9b5c
+    struct ovsdb_datum result;
Open vSwitch CI 3f9b5c
+
Open vSwitch CI 3f9b5c
+    ovsdb_datum_init_empty(&result);
Open vSwitch CI 3f9b5c
+    ovsdb_datum_reallocate(&result, a_type, a->n - n_idx);
Open vSwitch CI 3f9b5c
+
Open vSwitch CI 3f9b5c
+    unsigned int start_idx = 0;
Open vSwitch CI 3f9b5c
+    for (size_t i = 0; i < n_idx; i++) {
Open vSwitch CI 3f9b5c
+        ai = idx[i];
Open vSwitch CI 3f9b5c
+
Open vSwitch CI 3f9b5c
+        /* Destroying atom. */
Open vSwitch CI 3f9b5c
+        ovsdb_atom_destroy(&a->keys[ai], a_type->key.type);
Open vSwitch CI 3f9b5c
+        if (a_type->value.type != OVSDB_TYPE_VOID) {
Open vSwitch CI 3f9b5c
+            ovsdb_atom_destroy(&a->values[ai], a_type->value.type);
Open vSwitch CI 3f9b5c
+        }
Open vSwitch CI 3f9b5c
+
Open vSwitch CI 3f9b5c
+        /* Copy non-removed atoms from 'a' to result. */
Open vSwitch CI 3f9b5c
+        ovsdb_datum_push_unsafe(&result, a, start_idx, ai - start_idx, a_type);
Open vSwitch CI 3f9b5c
+        start_idx = idx[i] + 1;
Open vSwitch CI 3f9b5c
     }
Open vSwitch CI 3f9b5c
+    /* Copying remaining atoms. */
Open vSwitch CI 3f9b5c
+    ovsdb_datum_push_unsafe(&result, a, start_idx, a->n - start_idx, a_type);
Open vSwitch CI 3f9b5c
+    a->n = 0;
Open vSwitch CI 3f9b5c
+
Open vSwitch CI 3f9b5c
+    ovsdb_datum_swap(&result, a);
Open vSwitch CI 3f9b5c
+    ovsdb_datum_destroy(&result, a_type);
Open vSwitch CI 3f9b5c
+    free(idx);
Open vSwitch CI 3f9b5c
 }
Open vSwitch CI 3f9b5c
 
Open vSwitch CI 3f9b5c
 struct ovsdb_symbol_table *
Open vSwitch CI 3f9b5c
@@ -2067,6 +2140,64 @@ ovsdb_symbol_table_insert(struct ovsdb_symbol_table *symtab,
Open vSwitch CI 3f9b5c
 
Open vSwitch CI 3f9b5c
 /* APIs for Generating and apply diffs.  */
Open vSwitch CI 3f9b5c
 
Open vSwitch CI 3f9b5c
+/* Find what needs to be added to and removed from 'old' to construct 'new'.
Open vSwitch CI 3f9b5c
+ *
Open vSwitch CI 3f9b5c
+ * The 'added' and 'removed' datums are always safe; the orders of keys are
Open vSwitch CI 3f9b5c
+ * maintained since they are added in order.   */
Open vSwitch CI 3f9b5c
+void
Open vSwitch CI 3f9b5c
+ovsdb_datum_added_removed(struct ovsdb_datum *added,
Open vSwitch CI 3f9b5c
+                          struct ovsdb_datum *removed,
Open vSwitch CI 3f9b5c
+                          const struct ovsdb_datum *old,
Open vSwitch CI 3f9b5c
+                          const struct ovsdb_datum *new,
Open vSwitch CI 3f9b5c
+                          const struct ovsdb_type *type)
Open vSwitch CI 3f9b5c
+{
Open vSwitch CI 3f9b5c
+    size_t oi, ni;
Open vSwitch CI 3f9b5c
+
Open vSwitch CI 3f9b5c
+    ovsdb_datum_init_empty(added);
Open vSwitch CI 3f9b5c
+    ovsdb_datum_init_empty(removed);
Open vSwitch CI 3f9b5c
+    if (!ovsdb_type_is_composite(type)) {
Open vSwitch CI 3f9b5c
+        ovsdb_datum_clone(removed, old, type);
Open vSwitch CI 3f9b5c
+        ovsdb_datum_clone(added, new, type);
Open vSwitch CI 3f9b5c
+        return;
Open vSwitch CI 3f9b5c
+    }
Open vSwitch CI 3f9b5c
+
Open vSwitch CI 3f9b5c
+    /* Generate the diff in O(n) time. */
Open vSwitch CI 3f9b5c
+    for (oi = ni = 0; oi < old->n && ni < new->n;) {
Open vSwitch CI 3f9b5c
+        int c = ovsdb_atom_compare_3way(&old->keys[oi], &new->keys[ni],
Open vSwitch CI 3f9b5c
+                                        type->key.type);
Open vSwitch CI 3f9b5c
+        if (c < 0) {
Open vSwitch CI 3f9b5c
+            ovsdb_datum_add_unsafe(removed, &old->keys[oi], &old->values[oi],
Open vSwitch CI 3f9b5c
+                                   type, NULL);
Open vSwitch CI 3f9b5c
+            oi++;
Open vSwitch CI 3f9b5c
+        } else if (c > 0) {
Open vSwitch CI 3f9b5c
+            ovsdb_datum_add_unsafe(added, &new->keys[ni], &new->values[ni],
Open vSwitch CI 3f9b5c
+                                   type, NULL);
Open vSwitch CI 3f9b5c
+            ni++;
Open vSwitch CI 3f9b5c
+        } else {
Open vSwitch CI 3f9b5c
+            if (type->value.type != OVSDB_TYPE_VOID &&
Open vSwitch CI 3f9b5c
+                ovsdb_atom_compare_3way(&old->values[oi], &new->values[ni],
Open vSwitch CI 3f9b5c
+                                        type->value.type)) {
Open vSwitch CI 3f9b5c
+                ovsdb_datum_add_unsafe(removed, &old->keys[oi],
Open vSwitch CI 3f9b5c
+                                       &old->values[oi], type, NULL);
Open vSwitch CI 3f9b5c
+                ovsdb_datum_add_unsafe(added, &new->keys[ni], &new->values[ni],
Open vSwitch CI 3f9b5c
+                                       type, NULL);
Open vSwitch CI 3f9b5c
+            }
Open vSwitch CI 3f9b5c
+            oi++; ni++;
Open vSwitch CI 3f9b5c
+        }
Open vSwitch CI 3f9b5c
+    }
Open vSwitch CI 3f9b5c
+
Open vSwitch CI 3f9b5c
+    for (; oi < old->n; oi++) {
Open vSwitch CI 3f9b5c
+        ovsdb_datum_add_unsafe(removed, &old->keys[oi], &old->values[oi],
Open vSwitch CI 3f9b5c
+                               type, NULL);
Open vSwitch CI 3f9b5c
+    }
Open vSwitch CI 3f9b5c
+
Open vSwitch CI 3f9b5c
+    for (; ni < new->n; ni++) {
Open vSwitch CI 3f9b5c
+        ovsdb_datum_add_unsafe(added, &new->keys[ni], &new->values[ni],
Open vSwitch CI 3f9b5c
+                               type, NULL);
Open vSwitch CI 3f9b5c
+    }
Open vSwitch CI 3f9b5c
+}
Open vSwitch CI 3f9b5c
+
Open vSwitch CI 3f9b5c
+
Open vSwitch CI 3f9b5c
 /* Generate a difference ovsdb_dataum between 'old' and 'new'.
Open vSwitch CI 3f9b5c
  * 'new' can be regenerated by applying the difference to the 'old'.
Open vSwitch CI 3f9b5c
  *
Open vSwitch CI 3f9b5c
@@ -2127,6 +2258,106 @@ ovsdb_datum_diff(struct ovsdb_datum *diff,
Open vSwitch CI 3f9b5c
     }
Open vSwitch CI 3f9b5c
 }
Open vSwitch CI 3f9b5c
 
Open vSwitch CI 3f9b5c
+/* Apply 'diff' to 'a'.
Open vSwitch CI 3f9b5c
+ *
Open vSwitch CI 3f9b5c
+ * Return NULL if the 'a' is successfully updated, otherwise, return
Open vSwitch CI 3f9b5c
+ * ovsdb_error. */
Open vSwitch CI 3f9b5c
+struct ovsdb_error *
Open vSwitch CI 3f9b5c
+ovsdb_datum_apply_diff_in_place(struct ovsdb_datum *a,
Open vSwitch CI 3f9b5c
+                                const struct ovsdb_datum *diff,
Open vSwitch CI 3f9b5c
+                                const struct ovsdb_type *type)
Open vSwitch CI 3f9b5c
+{
Open vSwitch CI 3f9b5c
+    struct ovsdb_error *error = NULL;
Open vSwitch CI 3f9b5c
+    struct ovsdb_datum result;
Open vSwitch CI 3f9b5c
+    size_t i, new_size;
Open vSwitch CI 3f9b5c
+    unsigned int *idx, pos;
Open vSwitch CI 3f9b5c
+    enum {
Open vSwitch CI 3f9b5c
+        DIFF_OP_ADD,
Open vSwitch CI 3f9b5c
+        DIFF_OP_REMOVE,
Open vSwitch CI 3f9b5c
+        DIFF_OP_UPDATE,
Open vSwitch CI 3f9b5c
+    } *operation;
Open vSwitch CI 3f9b5c
+
Open vSwitch CI 3f9b5c
+    if (!ovsdb_type_is_composite(type)) {
Open vSwitch CI 3f9b5c
+        ovsdb_datum_destroy(a, type);
Open vSwitch CI 3f9b5c
+        ovsdb_datum_clone(a, diff, type);
Open vSwitch CI 3f9b5c
+        return NULL;
Open vSwitch CI 3f9b5c
+    }
Open vSwitch CI 3f9b5c
+
Open vSwitch CI 3f9b5c
+    operation = xmalloc(diff->n * sizeof *operation);
Open vSwitch CI 3f9b5c
+    idx = xmalloc(diff->n * sizeof *idx);
Open vSwitch CI 3f9b5c
+    new_size = a->n;
Open vSwitch CI 3f9b5c
+    for (i = 0; i < diff->n; i++) {
Open vSwitch CI 3f9b5c
+        if (!ovsdb_datum_find_key(a, &diff->keys[i], type->key.type, &pos)) {
Open vSwitch CI 3f9b5c
+            operation[i] = DIFF_OP_ADD;
Open vSwitch CI 3f9b5c
+            new_size++;
Open vSwitch CI 3f9b5c
+        } else if (type->value.type != OVSDB_TYPE_VOID
Open vSwitch CI 3f9b5c
+                   && !ovsdb_atom_equals(&diff->values[i], &a->values[pos],
Open vSwitch CI 3f9b5c
+                                         type->value.type)) {
Open vSwitch CI 3f9b5c
+            operation[i] = DIFF_OP_UPDATE;
Open vSwitch CI 3f9b5c
+        } else {
Open vSwitch CI 3f9b5c
+            operation[i] = DIFF_OP_REMOVE;
Open vSwitch CI 3f9b5c
+            new_size--;
Open vSwitch CI 3f9b5c
+        }
Open vSwitch CI 3f9b5c
+        idx[i] = pos;
Open vSwitch CI 3f9b5c
+    }
Open vSwitch CI 3f9b5c
+
Open vSwitch CI 3f9b5c
+    /* Make sure member size of 'new' conforms to type. */
Open vSwitch CI 3f9b5c
+    if (new_size < type->n_min || new_size > type->n_max) {
Open vSwitch CI 3f9b5c
+        error = ovsdb_error(NULL, "Datum crated by diff has size error");
Open vSwitch CI 3f9b5c
+        goto exit;
Open vSwitch CI 3f9b5c
+    }
Open vSwitch CI 3f9b5c
+
Open vSwitch CI 3f9b5c
+    ovsdb_datum_init_empty(&result);
Open vSwitch CI 3f9b5c
+    ovsdb_datum_reallocate(&result, type, new_size);
Open vSwitch CI 3f9b5c
+
Open vSwitch CI 3f9b5c
+    unsigned int copied = 0;
Open vSwitch CI 3f9b5c
+    for (i = 0; i < diff->n; i++) {
Open vSwitch CI 3f9b5c
+        pos = idx[i];
Open vSwitch CI 3f9b5c
+
Open vSwitch CI 3f9b5c
+        if (copied < pos) {
Open vSwitch CI 3f9b5c
+            /* Copying all atoms that should go before the current one. */
Open vSwitch CI 3f9b5c
+            ovsdb_datum_push_unsafe(&result, a, copied, pos - copied, type);
Open vSwitch CI 3f9b5c
+            copied = pos;
Open vSwitch CI 3f9b5c
+        }
Open vSwitch CI 3f9b5c
+
Open vSwitch CI 3f9b5c
+        switch (operation[i]) {
Open vSwitch CI 3f9b5c
+        case DIFF_OP_UPDATE:
Open vSwitch CI 3f9b5c
+        case DIFF_OP_ADD:
Open vSwitch CI 3f9b5c
+            /* Inserting new atom from 'diff'. */
Open vSwitch CI 3f9b5c
+            ovsdb_atom_clone(&result.keys[result.n],
Open vSwitch CI 3f9b5c
+                             &diff->keys[i], type->key.type);
Open vSwitch CI 3f9b5c
+            if (type->value.type != OVSDB_TYPE_VOID) {
Open vSwitch CI 3f9b5c
+                ovsdb_atom_clone(&result.values[result.n],
Open vSwitch CI 3f9b5c
+                                 &diff->values[i], type->value.type);
Open vSwitch CI 3f9b5c
+            }
Open vSwitch CI 3f9b5c
+            result.n++;
Open vSwitch CI 3f9b5c
+            if (operation[i] != DIFF_OP_UPDATE) {
Open vSwitch CI 3f9b5c
+                break;
Open vSwitch CI 3f9b5c
+            }
Open vSwitch CI 3f9b5c
+            /* fall through */
Open vSwitch CI 3f9b5c
+
Open vSwitch CI 3f9b5c
+        case DIFF_OP_REMOVE:
Open vSwitch CI 3f9b5c
+            /* Destroying atom. */
Open vSwitch CI 3f9b5c
+            ovsdb_atom_destroy(&a->keys[pos], type->key.type);
Open vSwitch CI 3f9b5c
+            if (type->value.type != OVSDB_TYPE_VOID) {
Open vSwitch CI 3f9b5c
+                ovsdb_atom_destroy(&a->values[pos], type->value.type);
Open vSwitch CI 3f9b5c
+            }
Open vSwitch CI 3f9b5c
+            copied++; /* Skipping removed atom. */
Open vSwitch CI 3f9b5c
+            break;
Open vSwitch CI 3f9b5c
+        }
Open vSwitch CI 3f9b5c
+    }
Open vSwitch CI 3f9b5c
+    /* Copying remaining atoms. */
Open vSwitch CI 3f9b5c
+    ovsdb_datum_push_unsafe(&result, a, copied, a->n - copied, type);
Open vSwitch CI 3f9b5c
+    a->n = 0;
Open vSwitch CI 3f9b5c
+
Open vSwitch CI 3f9b5c
+    ovsdb_datum_swap(&result, a);
Open vSwitch CI 3f9b5c
+    ovsdb_datum_destroy(&result, type);
Open vSwitch CI 3f9b5c
+exit:
Open vSwitch CI 3f9b5c
+    free(operation);
Open vSwitch CI 3f9b5c
+    free(idx);
Open vSwitch CI 3f9b5c
+    return error;
Open vSwitch CI 3f9b5c
+}
Open vSwitch CI 3f9b5c
+
Open vSwitch CI 3f9b5c
 /* Apply 'diff' to 'old' to regenerate 'new'.
Open vSwitch CI 3f9b5c
  *
Open vSwitch CI 3f9b5c
  * Return NULL if the 'new' is successfully generated, otherwise, return
Open vSwitch CI 3f9b5c
diff --git a/lib/ovsdb-data.h b/lib/ovsdb-data.h
Open vSwitch CI 3f9b5c
index c5a80ee39f..f66ed3472c 100644
Open vSwitch CI 3f9b5c
--- a/lib/ovsdb-data.h
Open vSwitch CI 3f9b5c
+++ b/lib/ovsdb-data.h
Open vSwitch CI 3f9b5c
@@ -20,6 +20,7 @@
Open vSwitch CI 3f9b5c
 #include "compiler.h"
Open vSwitch CI 3f9b5c
 #include "ovsdb-types.h"
Open vSwitch CI 3f9b5c
 #include "openvswitch/shash.h"
Open vSwitch CI 3f9b5c
+#include "util.h"
Open vSwitch CI 3f9b5c
 
Open vSwitch CI 3f9b5c
 #ifdef  __cplusplus
Open vSwitch CI 3f9b5c
 extern "C" {
Open vSwitch CI 3f9b5c
@@ -31,12 +32,33 @@ struct ds;
Open vSwitch CI 3f9b5c
 struct ovsdb_symbol_table;
Open vSwitch CI 3f9b5c
 struct smap;
Open vSwitch CI 3f9b5c
 
Open vSwitch CI 3f9b5c
+struct ovsdb_atom_string {
Open vSwitch CI 3f9b5c
+    char *string;
Open vSwitch CI 3f9b5c
+    size_t n_refs;
Open vSwitch CI 3f9b5c
+};
Open vSwitch CI 3f9b5c
+
Open vSwitch CI 3f9b5c
+static inline struct ovsdb_atom_string *
Open vSwitch CI 3f9b5c
+ovsdb_atom_string_create_nocopy(char *str)
Open vSwitch CI 3f9b5c
+{
Open vSwitch CI 3f9b5c
+    struct ovsdb_atom_string *s = xzalloc(sizeof *s);
Open vSwitch CI 3f9b5c
+
Open vSwitch CI 3f9b5c
+    s->string = str;
Open vSwitch CI 3f9b5c
+    s->n_refs = 1;
Open vSwitch CI 3f9b5c
+    return s;
Open vSwitch CI 3f9b5c
+}
Open vSwitch CI 3f9b5c
+
Open vSwitch CI 3f9b5c
+static inline struct ovsdb_atom_string *
Open vSwitch CI 3f9b5c
+ovsdb_atom_string_create(const char *str)
Open vSwitch CI 3f9b5c
+{
Open vSwitch CI 3f9b5c
+    return ovsdb_atom_string_create_nocopy(xstrdup(str));
Open vSwitch CI 3f9b5c
+}
Open vSwitch CI 3f9b5c
+
Open vSwitch CI 3f9b5c
 /* One value of an atomic type (given by enum ovs_atomic_type). */
Open vSwitch CI 3f9b5c
 union ovsdb_atom {
Open vSwitch CI 3f9b5c
     int64_t integer;
Open vSwitch CI 3f9b5c
     double real;
Open vSwitch CI 3f9b5c
     bool boolean;
Open vSwitch CI 3f9b5c
-    char *string;
Open vSwitch CI 3f9b5c
+    struct ovsdb_atom_string *s;
Open vSwitch CI 3f9b5c
     struct uuid uuid;
Open vSwitch CI 3f9b5c
 };
Open vSwitch CI 3f9b5c
 
Open vSwitch CI 3f9b5c
@@ -66,8 +88,9 @@ ovsdb_atom_needs_destruction(enum ovsdb_atomic_type type)
Open vSwitch CI 3f9b5c
 static inline void
Open vSwitch CI 3f9b5c
 ovsdb_atom_destroy(union ovsdb_atom *atom, enum ovsdb_atomic_type type)
Open vSwitch CI 3f9b5c
 {
Open vSwitch CI 3f9b5c
-    if (type == OVSDB_TYPE_STRING) {
Open vSwitch CI 3f9b5c
-        free(atom->string);
Open vSwitch CI 3f9b5c
+    if (type == OVSDB_TYPE_STRING && !--atom->s->n_refs) {
Open vSwitch CI 3f9b5c
+        free(atom->s->string);
Open vSwitch CI 3f9b5c
+        free(atom->s);
Open vSwitch CI 3f9b5c
     }
Open vSwitch CI 3f9b5c
 }
Open vSwitch CI 3f9b5c
 
Open vSwitch CI 3f9b5c
@@ -209,9 +232,10 @@ bool ovsdb_datum_equals(const struct ovsdb_datum *,
Open vSwitch CI 3f9b5c
                         const struct ovsdb_type *);
Open vSwitch CI 3f9b5c
 
Open vSwitch CI 3f9b5c
 /* Search. */
Open vSwitch CI 3f9b5c
-unsigned int ovsdb_datum_find_key(const struct ovsdb_datum *,
Open vSwitch CI 3f9b5c
-                                  const union ovsdb_atom *key,
Open vSwitch CI 3f9b5c
-                                  enum ovsdb_atomic_type key_type);
Open vSwitch CI 3f9b5c
+bool ovsdb_datum_find_key(const struct ovsdb_datum *,
Open vSwitch CI 3f9b5c
+                          const union ovsdb_atom *key,
Open vSwitch CI 3f9b5c
+                          enum ovsdb_atomic_type key_type,
Open vSwitch CI 3f9b5c
+                          unsigned int *pos);
Open vSwitch CI 3f9b5c
 unsigned int ovsdb_datum_find_key_value(const struct ovsdb_datum *,
Open vSwitch CI 3f9b5c
                                         const union ovsdb_atom *key,
Open vSwitch CI 3f9b5c
                                         enum ovsdb_atomic_type key_type,
Open vSwitch CI 3f9b5c
@@ -227,14 +251,19 @@ bool ovsdb_datum_excludes_all(const struct ovsdb_datum *,
Open vSwitch CI 3f9b5c
                               const struct ovsdb_type *);
Open vSwitch CI 3f9b5c
 void ovsdb_datum_union(struct ovsdb_datum *,
Open vSwitch CI 3f9b5c
                        const struct ovsdb_datum *,
Open vSwitch CI 3f9b5c
-                       const struct ovsdb_type *,
Open vSwitch CI 3f9b5c
-                       bool replace);
Open vSwitch CI 3f9b5c
+                       const struct ovsdb_type *);
Open vSwitch CI 3f9b5c
 void ovsdb_datum_subtract(struct ovsdb_datum *a,
Open vSwitch CI 3f9b5c
                           const struct ovsdb_type *a_type,
Open vSwitch CI 3f9b5c
                           const struct ovsdb_datum *b,
Open vSwitch CI 3f9b5c
                           const struct ovsdb_type *b_type);
Open vSwitch CI 3f9b5c
 
Open vSwitch CI 3f9b5c
 /* Generate and apply diffs */
Open vSwitch CI 3f9b5c
+void ovsdb_datum_added_removed(struct ovsdb_datum *added,
Open vSwitch CI 3f9b5c
+                               struct ovsdb_datum *removed,
Open vSwitch CI 3f9b5c
+                               const struct ovsdb_datum *old,
Open vSwitch CI 3f9b5c
+                               const struct ovsdb_datum *new,
Open vSwitch CI 3f9b5c
+                               const struct ovsdb_type *type);
Open vSwitch CI 3f9b5c
+
Open vSwitch CI 3f9b5c
 void ovsdb_datum_diff(struct ovsdb_datum *diff,
Open vSwitch CI 3f9b5c
                       const struct ovsdb_datum *old_datum,
Open vSwitch CI 3f9b5c
                       const struct ovsdb_datum *new_datum,
Open vSwitch CI 3f9b5c
@@ -246,6 +275,12 @@ struct ovsdb_error *ovsdb_datum_apply_diff(struct ovsdb_datum *new_datum,
Open vSwitch CI 3f9b5c
                                            const struct ovsdb_type *type)
Open vSwitch CI 3f9b5c
 OVS_WARN_UNUSED_RESULT;
Open vSwitch CI 3f9b5c
 
Open vSwitch CI 3f9b5c
+struct ovsdb_error * ovsdb_datum_apply_diff_in_place(
Open vSwitch CI 3f9b5c
+                struct ovsdb_datum *a,
Open vSwitch CI 3f9b5c
+                const struct ovsdb_datum *diff,
Open vSwitch CI 3f9b5c
+                const struct ovsdb_type *type)
Open vSwitch CI 3f9b5c
+OVS_WARN_UNUSED_RESULT;
Open vSwitch CI 3f9b5c
+
Open vSwitch CI 3f9b5c
 /* Raw operations that may not maintain the invariants. */
Open vSwitch CI 3f9b5c
 void ovsdb_datum_remove_unsafe(struct ovsdb_datum *, size_t idx,
Open vSwitch CI 3f9b5c
                                const struct ovsdb_type *);
Open vSwitch CI 3f9b5c
diff --git a/lib/ovsdb-idl.c b/lib/ovsdb-idl.c
Open vSwitch CI 3f9b5c
index 2198c69c60..198ba5a828 100644
Open vSwitch CI 3f9b5c
--- a/lib/ovsdb-idl.c
Open vSwitch CI 3f9b5c
+++ b/lib/ovsdb-idl.c
Open vSwitch CI 3f9b5c
@@ -1898,8 +1898,7 @@ ovsdb_idl_index_destroy_row(const struct ovsdb_idl_row *row_)
Open vSwitch CI 3f9b5c
     BITMAP_FOR_EACH_1 (i, class->n_columns, row->written) {
Open vSwitch CI 3f9b5c
         c = &class->columns[i];
Open vSwitch CI 3f9b5c
         (c->unparse) (row);
Open vSwitch CI 3f9b5c
-        free(row->new_datum[i].values);
Open vSwitch CI 3f9b5c
-        free(row->new_datum[i].keys);
Open vSwitch CI 3f9b5c
+        ovsdb_datum_destroy(&row->new_datum[i], &c->type);
Open vSwitch CI 3f9b5c
     }
Open vSwitch CI 3f9b5c
     free(row->new_datum);
Open vSwitch CI 3f9b5c
     free(row->written);
Open vSwitch CI 3f9b5c
@@ -2787,9 +2786,8 @@ ovsdb_idl_txn_extract_mutations(struct ovsdb_idl_row *row,
Open vSwitch CI 3f9b5c
                     struct ovsdb_datum *new_datum;
Open vSwitch CI 3f9b5c
                     unsigned int pos;
Open vSwitch CI 3f9b5c
                     new_datum = map_op_datum(map_op);
Open vSwitch CI 3f9b5c
-                    pos = ovsdb_datum_find_key(old_datum,
Open vSwitch CI 3f9b5c
-                                               &new_datum->keys[0],
Open vSwitch CI 3f9b5c
-                                               key_type);
Open vSwitch CI 3f9b5c
+                    ovsdb_datum_find_key(old_datum, &new_datum->keys[0],
Open vSwitch CI 3f9b5c
+                                         key_type, &pos;;
Open vSwitch CI 3f9b5c
                     if (ovsdb_atom_equals(&new_datum->values[0],
Open vSwitch CI 3f9b5c
                                           &old_datum->values[pos],
Open vSwitch CI 3f9b5c
                                           value_type)) {
Open vSwitch CI 3f9b5c
@@ -2798,11 +2796,9 @@ ovsdb_idl_txn_extract_mutations(struct ovsdb_idl_row *row,
Open vSwitch CI 3f9b5c
                     }
Open vSwitch CI 3f9b5c
                 } else if (map_op_type(map_op) == MAP_OP_DELETE){
Open vSwitch CI 3f9b5c
                     /* Verify that there is a key to delete. */
Open vSwitch CI 3f9b5c
-                    unsigned int pos;
Open vSwitch CI 3f9b5c
-                    pos = ovsdb_datum_find_key(old_datum,
Open vSwitch CI 3f9b5c
-                                               &map_op_datum(map_op)->keys[0],
Open vSwitch CI 3f9b5c
-                                               key_type);
Open vSwitch CI 3f9b5c
-                    if (pos == UINT_MAX) {
Open vSwitch CI 3f9b5c
+                    if (!ovsdb_datum_find_key(old_datum,
Open vSwitch CI 3f9b5c
+                                              &map_op_datum(map_op)->keys[0],
Open vSwitch CI 3f9b5c
+                                              key_type, NULL)) {
Open vSwitch CI 3f9b5c
                         /* No key to delete.  Move on to next update. */
Open vSwitch CI 3f9b5c
                         VLOG_WARN("Trying to delete a key that doesn't "
Open vSwitch CI 3f9b5c
                                   "exist in the map.");
Open vSwitch CI 3f9b5c
@@ -2897,11 +2893,9 @@ ovsdb_idl_txn_extract_mutations(struct ovsdb_idl_row *row,
Open vSwitch CI 3f9b5c
                     any_ins = true;
Open vSwitch CI 3f9b5c
                 } else { /* SETP_OP_DELETE */
Open vSwitch CI 3f9b5c
                     /* Verify that there is a key to delete. */
Open vSwitch CI 3f9b5c
-                    unsigned int pos;
Open vSwitch CI 3f9b5c
-                    pos = ovsdb_datum_find_key(old_datum,
Open vSwitch CI 3f9b5c
-                                               &set_op_datum(set_op)->keys[0],
Open vSwitch CI 3f9b5c
-                                               key_type);
Open vSwitch CI 3f9b5c
-                    if (pos == UINT_MAX) {
Open vSwitch CI 3f9b5c
+                    if (!ovsdb_datum_find_key(old_datum,
Open vSwitch CI 3f9b5c
+                                              &set_op_datum(set_op)->keys[0],
Open vSwitch CI 3f9b5c
+                                              key_type, NULL)) {
Open vSwitch CI 3f9b5c
                         /* No key to delete.  Move on to next update. */
Open vSwitch CI 3f9b5c
                         VLOG_WARN("Trying to delete a key that doesn't "
Open vSwitch CI 3f9b5c
                                   "exist in the set.");
Open vSwitch CI 3f9b5c
@@ -4066,7 +4060,6 @@ ovsdb_idl_txn_write_partial_map(const struct ovsdb_idl_row *row_,
Open vSwitch CI 3f9b5c
     struct ovsdb_idl_row *row = CONST_CAST(struct ovsdb_idl_row *, row_);
Open vSwitch CI 3f9b5c
     enum ovsdb_atomic_type key_type;
Open vSwitch CI 3f9b5c
     enum map_op_type op_type;
Open vSwitch CI 3f9b5c
-    unsigned int pos;
Open vSwitch CI 3f9b5c
     const struct ovsdb_datum *old_datum;
Open vSwitch CI 3f9b5c
 
Open vSwitch CI 3f9b5c
     if (!is_valid_partial_update(row, column, datum)) {
Open vSwitch CI 3f9b5c
@@ -4078,8 +4071,11 @@ ovsdb_idl_txn_write_partial_map(const struct ovsdb_idl_row *row_,
Open vSwitch CI 3f9b5c
     /* Find out if this is an insert or an update. */
Open vSwitch CI 3f9b5c
     key_type = column->type.key.type;
Open vSwitch CI 3f9b5c
     old_datum = ovsdb_idl_read(row, column);
Open vSwitch CI 3f9b5c
-    pos = ovsdb_datum_find_key(old_datum, &datum->keys[0], key_type);
Open vSwitch CI 3f9b5c
-    op_type = pos == UINT_MAX ? MAP_OP_INSERT : MAP_OP_UPDATE;
Open vSwitch CI 3f9b5c
+    if (ovsdb_datum_find_key(old_datum, &datum->keys[0], key_type, NULL)) {
Open vSwitch CI 3f9b5c
+        op_type = MAP_OP_UPDATE;
Open vSwitch CI 3f9b5c
+    } else {
Open vSwitch CI 3f9b5c
+        op_type = MAP_OP_INSERT;
Open vSwitch CI 3f9b5c
+    }
Open vSwitch CI 3f9b5c
 
Open vSwitch CI 3f9b5c
     ovsdb_idl_txn_add_map_op(row, column, datum, op_type);
Open vSwitch CI 3f9b5c
 }
110336
diff --git a/lib/pcap-file.c b/lib/pcap-file.c
110336
index b30a11c24b..41835f6f4d 100644
110336
--- a/lib/pcap-file.c
110336
+++ b/lib/pcap-file.c
110336
@@ -89,6 +89,7 @@ ovs_pcap_open(const char *file_name, const char *mode)
110336
                    : mode[0] == 'w' ? "writing"
110336
                    : "appending"),
110336
                   ovs_strerror(errno));
110336
+        free(p_file);
110336
         return NULL;
110336
     }
110336
 
Open vSwitch CI 3f9b5c
diff --git a/ofproto/ofproto-dpif-xlate.c b/ofproto/ofproto-dpif-xlate.c
Open vSwitch CI 3f9b5c
index a426fcfeb6..1bab22aa5e 100644
Open vSwitch CI 3f9b5c
--- a/ofproto/ofproto-dpif-xlate.c
Open vSwitch CI 3f9b5c
+++ b/ofproto/ofproto-dpif-xlate.c
Open vSwitch CI 3f9b5c
@@ -6177,11 +6177,32 @@ static void
Open vSwitch CI 3f9b5c
 compose_conntrack_action(struct xlate_ctx *ctx, struct ofpact_conntrack *ofc,
Open vSwitch CI 3f9b5c
                          bool is_last_action)
Open vSwitch CI 3f9b5c
 {
Open vSwitch CI 3f9b5c
-    ovs_u128 old_ct_label_mask = ctx->wc->masks.ct_label;
Open vSwitch CI 3f9b5c
-    uint32_t old_ct_mark_mask = ctx->wc->masks.ct_mark;
Open vSwitch CI 3f9b5c
-    size_t ct_offset;
Open vSwitch CI 3f9b5c
     uint16_t zone;
Open vSwitch CI 3f9b5c
+    if (ofc->zone_src.field) {
Open vSwitch CI 3f9b5c
+        union mf_subvalue value;
Open vSwitch CI 3f9b5c
+        memset(&value, 0xff, sizeof(value));
Open vSwitch CI 3f9b5c
+
Open vSwitch CI 3f9b5c
+        zone = mf_get_subfield(&ofc->zone_src, &ctx->xin->flow);
Open vSwitch CI 3f9b5c
+        if (ctx->xin->frozen_state) {
Open vSwitch CI 3f9b5c
+            /* If the upcall is a resume of a recirculation, we only need to
Open vSwitch CI 3f9b5c
+             * unwildcard the fields that are not in the frozen_metadata, as
Open vSwitch CI 3f9b5c
+             * when the rules update, OVS will generate a new recirc_id,
Open vSwitch CI 3f9b5c
+             * which will invalidate the megaflow with old the recirc_id.
Open vSwitch CI 3f9b5c
+             */
Open vSwitch CI 3f9b5c
+            if (!mf_is_frozen_metadata(ofc->zone_src.field)) {
Open vSwitch CI 3f9b5c
+                mf_write_subfield_flow(&ofc->zone_src, &value,
Open vSwitch CI 3f9b5c
+                                       &ctx->wc->masks);
Open vSwitch CI 3f9b5c
+            }
Open vSwitch CI 3f9b5c
+        } else {
Open vSwitch CI 3f9b5c
+            mf_write_subfield_flow(&ofc->zone_src, &value, &ctx->wc->masks);
Open vSwitch CI 3f9b5c
+        }
Open vSwitch CI 3f9b5c
+    } else {
Open vSwitch CI 3f9b5c
+        zone = ofc->zone_imm;
Open vSwitch CI 3f9b5c
+    }
Open vSwitch CI 3f9b5c
 
Open vSwitch CI 3f9b5c
+    size_t ct_offset;
Open vSwitch CI 3f9b5c
+    ovs_u128 old_ct_label_mask = ctx->wc->masks.ct_label;
Open vSwitch CI 3f9b5c
+    uint32_t old_ct_mark_mask = ctx->wc->masks.ct_mark;
Open vSwitch CI 3f9b5c
     /* Ensure that any prior actions are applied before composing the new
Open vSwitch CI 3f9b5c
      * conntrack action. */
Open vSwitch CI 3f9b5c
     xlate_commit_actions(ctx);
Open vSwitch CI 3f9b5c
@@ -6193,11 +6214,6 @@ compose_conntrack_action(struct xlate_ctx *ctx, struct ofpact_conntrack *ofc,
Open vSwitch CI 3f9b5c
     do_xlate_actions(ofc->actions, ofpact_ct_get_action_len(ofc), ctx,
Open vSwitch CI 3f9b5c
                      is_last_action, false);
Open vSwitch CI 3f9b5c
 
Open vSwitch CI 3f9b5c
-    if (ofc->zone_src.field) {
Open vSwitch CI 3f9b5c
-        zone = mf_get_subfield(&ofc->zone_src, &ctx->xin->flow);
Open vSwitch CI 3f9b5c
-    } else {
Open vSwitch CI 3f9b5c
-        zone = ofc->zone_imm;
Open vSwitch CI 3f9b5c
-    }
Open vSwitch CI 3f9b5c
 
Open vSwitch CI 3f9b5c
     ct_offset = nl_msg_start_nested(ctx->odp_actions, OVS_ACTION_ATTR_CT);
Open vSwitch CI 3f9b5c
     if (ofc->flags & NX_CT_F_COMMIT) {
Open vSwitch CI 3f9b5c
diff --git a/ovsdb/file.c b/ovsdb/file.c
Open vSwitch CI 3f9b5c
index 59220824fa..9f44007d97 100644
Open vSwitch CI 3f9b5c
--- a/ovsdb/file.c
Open vSwitch CI 3f9b5c
+++ b/ovsdb/file.c
Open vSwitch CI 3f9b5c
@@ -113,19 +113,17 @@ ovsdb_file_update_row_from_json(struct ovsdb_row *row, bool converting,
Open vSwitch CI 3f9b5c
         if (row_contains_diff
Open vSwitch CI 3f9b5c
             && !ovsdb_datum_is_default(&row->fields[column->index],
Open vSwitch CI 3f9b5c
                                        &column->type)) {
Open vSwitch CI 3f9b5c
-            struct ovsdb_datum new_datum;
Open vSwitch CI 3f9b5c
-
Open vSwitch CI 3f9b5c
-            error = ovsdb_datum_apply_diff(&new_datum,
Open vSwitch CI 3f9b5c
+            error = ovsdb_datum_apply_diff_in_place(
Open vSwitch CI 3f9b5c
                                            &row->fields[column->index],
Open vSwitch CI 3f9b5c
                                            &datum, &column->type);
Open vSwitch CI 3f9b5c
             ovsdb_datum_destroy(&datum, &column->type);
Open vSwitch CI 3f9b5c
             if (error) {
Open vSwitch CI 3f9b5c
                 return error;
Open vSwitch CI 3f9b5c
             }
Open vSwitch CI 3f9b5c
-            ovsdb_datum_swap(&datum, &new_datum);
Open vSwitch CI 3f9b5c
+        } else {
Open vSwitch CI 3f9b5c
+            ovsdb_datum_swap(&row->fields[column->index], &datum);
Open vSwitch CI 3f9b5c
+            ovsdb_datum_destroy(&datum, &column->type);
Open vSwitch CI 3f9b5c
         }
Open vSwitch CI 3f9b5c
-        ovsdb_datum_swap(&row->fields[column->index], &datum);
Open vSwitch CI 3f9b5c
-        ovsdb_datum_destroy(&datum, &column->type);
Open vSwitch CI 3f9b5c
     }
Open vSwitch CI 3f9b5c
 
Open vSwitch CI 3f9b5c
     return NULL;
110336
diff --git a/ovsdb/monitor.c b/ovsdb/monitor.c
110336
index 532dedcb64..ab814cf20e 100644
110336
--- a/ovsdb/monitor.c
110336
+++ b/ovsdb/monitor.c
110336
@@ -1231,6 +1231,15 @@ ovsdb_monitor_get_update(
110336
                                             condition,
110336
                                             ovsdb_monitor_compose_row_update2);
110336
                 if (!condition || !condition->conditional) {
110336
+                    if (json) {
110336
+                        struct json *json_serialized;
110336
+
110336
+                        /* Pre-serializing the object to avoid doing this
110336
+                         * for every client. */
110336
+                        json_serialized = json_serialized_object_create(json);
110336
+                        json_destroy(json);
110336
+                        json = json_serialized;
110336
+                    }
110336
                     ovsdb_monitor_json_cache_insert(dbmon, version, mcs,
110336
                                                     json);
110336
                 }
Open vSwitch CI 3f9b5c
diff --git a/ovsdb/mutation.c b/ovsdb/mutation.c
Open vSwitch CI 3f9b5c
index 56edc5f000..03d1c3499e 100644
Open vSwitch CI 3f9b5c
--- a/ovsdb/mutation.c
Open vSwitch CI 3f9b5c
+++ b/ovsdb/mutation.c
Open vSwitch CI 3f9b5c
@@ -383,7 +383,7 @@ ovsdb_mutation_set_execute(struct ovsdb_row *row,
Open vSwitch CI 3f9b5c
             break;
Open vSwitch CI 3f9b5c
 
Open vSwitch CI 3f9b5c
         case OVSDB_M_INSERT:
Open vSwitch CI 3f9b5c
-            ovsdb_datum_union(dst, arg, dst_type, false);
Open vSwitch CI 3f9b5c
+            ovsdb_datum_union(dst, arg, dst_type);
Open vSwitch CI 3f9b5c
             error = ovsdb_mutation_check_count(dst, dst_type);
Open vSwitch CI 3f9b5c
             break;
Open vSwitch CI 3f9b5c
 
Open vSwitch CI 3f9b5c
diff --git a/ovsdb/ovsdb-idlc.in b/ovsdb/ovsdb-idlc.in
Open vSwitch CI 3f9b5c
index 61cded16d3..78ebf2dc14 100755
Open vSwitch CI 3f9b5c
--- a/ovsdb/ovsdb-idlc.in
Open vSwitch CI 3f9b5c
+++ b/ovsdb/ovsdb-idlc.in
Open vSwitch CI 3f9b5c
@@ -551,20 +551,20 @@ static void
Open vSwitch CI 3f9b5c
                 print("    smap_init(&row->%s);" % columnName)
Open vSwitch CI 3f9b5c
                 print("    for (size_t i = 0; i < datum->n; i++) {")
Open vSwitch CI 3f9b5c
                 print("        smap_add(&row->%s," % columnName)
Open vSwitch CI 3f9b5c
-                print("                 datum->keys[i].string,")
Open vSwitch CI 3f9b5c
-                print("                 datum->values[i].string);")
Open vSwitch CI 3f9b5c
+                print("                 datum->keys[i].s->string,")
Open vSwitch CI 3f9b5c
+                print("                 datum->values[i].s->string);")
Open vSwitch CI 3f9b5c
                 print("    }")
Open vSwitch CI 3f9b5c
             elif (type.n_min == 1 and type.n_max == 1) or type.is_optional_pointer():
Open vSwitch CI 3f9b5c
                 print("")
Open vSwitch CI 3f9b5c
                 print("    if (datum->n >= 1) {")
Open vSwitch CI 3f9b5c
                 if not type.key.ref_table:
Open vSwitch CI 3f9b5c
-                    print("        %s = datum->keys[0].%s;" % (keyVar, type.key.type.to_string()))
Open vSwitch CI 3f9b5c
+                    print("        %s = datum->keys[0].%s;" % (keyVar, type.key.type.to_rvalue_string()))
Open vSwitch CI 3f9b5c
                 else:
Open vSwitch CI 3f9b5c
                     print("        %s = %s%s_cast(ovsdb_idl_get_row_arc(row_, &%stable_%s, &datum->keys[0].uuid));" % (keyVar, prefix, type.key.ref_table.name.lower(), prefix, type.key.ref_table.name.lower()))
Open vSwitch CI 3f9b5c
 
Open vSwitch CI 3f9b5c
                 if valueVar:
Open vSwitch CI 3f9b5c
                     if not type.value.ref_table:
Open vSwitch CI 3f9b5c
-                        print("        %s = datum->values[0].%s;" % (valueVar, type.value.type.to_string()))
Open vSwitch CI 3f9b5c
+                        print("        %s = datum->values[0].%s;" % (valueVar, type.value.type.to_rvalue_string()))
Open vSwitch CI 3f9b5c
                     else:
Open vSwitch CI 3f9b5c
                         print("        %s = %s%s_cast(ovsdb_idl_get_row_arc(row_, &%stable_%s, &datum->values[0].uuid));" % (valueVar, prefix, type.value.ref_table.name.lower(), prefix, type.value.ref_table.name.lower()))
Open vSwitch CI 3f9b5c
                 print("    } else {")
Open vSwitch CI 3f9b5c
@@ -592,7 +592,7 @@ static void
Open vSwitch CI 3f9b5c
 """ % (prefix, type.key.ref_table.name.lower(), prefix, type.key.ref_table.name.lower(), prefix, type.key.ref_table.name.lower()))
Open vSwitch CI 3f9b5c
                     keySrc = "keyRow"
Open vSwitch CI 3f9b5c
                 else:
Open vSwitch CI 3f9b5c
-                    keySrc = "datum->keys[i].%s" % type.key.type.to_string()
Open vSwitch CI 3f9b5c
+                    keySrc = "datum->keys[i].%s" % type.key.type.to_rvalue_string()
Open vSwitch CI 3f9b5c
                 if type.value and type.value.ref_table:
Open vSwitch CI 3f9b5c
                     print("""\
Open vSwitch CI 3f9b5c
         struct %s%s *valueRow = %s%s_cast(ovsdb_idl_get_row_arc(row_, &%stable_%s, &datum->values[i].uuid));
Open vSwitch CI 3f9b5c
@@ -602,7 +602,7 @@ static void
Open vSwitch CI 3f9b5c
 """ % (prefix, type.value.ref_table.name.lower(), prefix, type.value.ref_table.name.lower(), prefix, type.value.ref_table.name.lower()))
Open vSwitch CI 3f9b5c
                     valueSrc = "valueRow"
Open vSwitch CI 3f9b5c
                 elif valueVar:
Open vSwitch CI 3f9b5c
-                    valueSrc = "datum->values[i].%s" % type.value.type.to_string()
Open vSwitch CI 3f9b5c
+                    valueSrc = "datum->values[i].%s" % type.value.type.to_rvalue_string()
Open vSwitch CI 3f9b5c
                 print("        if (!row->n_%s) {" % (columnName))
Open vSwitch CI 3f9b5c
 
Open vSwitch CI 3f9b5c
                 print("            %s = xmalloc(%s * sizeof *%s);" % (
Open vSwitch CI 3f9b5c
@@ -910,45 +910,45 @@ void
Open vSwitch CI 3f9b5c
         'args': ', '.join(['%(type)s%(name)s'
Open vSwitch CI 3f9b5c
                            % m for m in members])})
Open vSwitch CI 3f9b5c
             if type.n_min == 1 and type.n_max == 1:
Open vSwitch CI 3f9b5c
-                print("    union ovsdb_atom key;")
Open vSwitch CI 3f9b5c
+                print("    union ovsdb_atom *key = xmalloc(sizeof *key);")
Open vSwitch CI 3f9b5c
                 if type.value:
Open vSwitch CI 3f9b5c
-                    print("    union ovsdb_atom value;")
Open vSwitch CI 3f9b5c
+                    print("    union ovsdb_atom *value = xmalloc(sizeof *value);")
Open vSwitch CI 3f9b5c
                 print("")
Open vSwitch CI 3f9b5c
                 print("    datum.n = 1;")
Open vSwitch CI 3f9b5c
-                print("    datum.keys = &ke;;")
Open vSwitch CI 3f9b5c
-                print("    " + type.key.assign_c_value_casting_away_const("key.%s" % type.key.type.to_string(), keyVar))
Open vSwitch CI 3f9b5c
+                print("    datum.keys = key;")
Open vSwitch CI 3f9b5c
+                print("    " + type.key.copyCValue("key->%s" % type.key.type.to_lvalue_string(), keyVar))
Open vSwitch CI 3f9b5c
                 if type.value:
Open vSwitch CI 3f9b5c
-                    print("    datum.values = &value;")
Open vSwitch CI 3f9b5c
-                    print("    "+ type.value.assign_c_value_casting_away_const("value.%s" % type.value.type.to_string(), valueVar))
Open vSwitch CI 3f9b5c
+                    print("    datum.values = value;")
Open vSwitch CI 3f9b5c
+                    print("    " + type.value.copyCValue("value->%s" % type.value.type.to_lvalue_string(), valueVar))
Open vSwitch CI 3f9b5c
                 else:
Open vSwitch CI 3f9b5c
                     print("    datum.values = NULL;")
Open vSwitch CI 3f9b5c
-                txn_write_func = "ovsdb_idl_txn_write_clone"
Open vSwitch CI 3f9b5c
+                txn_write_func = "ovsdb_idl_txn_write"
Open vSwitch CI 3f9b5c
             elif type.is_optional_pointer():
Open vSwitch CI 3f9b5c
-                print("    union ovsdb_atom key;")
Open vSwitch CI 3f9b5c
                 print("")
Open vSwitch CI 3f9b5c
                 print("    if (%s) {" % keyVar)
Open vSwitch CI 3f9b5c
+                print("        union ovsdb_atom *key = xmalloc(sizeof *key);")
Open vSwitch CI 3f9b5c
                 print("        datum.n = 1;")
Open vSwitch CI 3f9b5c
-                print("        datum.keys = &ke;;")
Open vSwitch CI 3f9b5c
-                print("        " + type.key.assign_c_value_casting_away_const("key.%s" % type.key.type.to_string(), keyVar))
Open vSwitch CI 3f9b5c
+                print("        datum.keys = key;")
Open vSwitch CI 3f9b5c
+                print("        " + type.key.copyCValue("key->%s" % type.key.type.to_lvalue_string(), keyVar))
Open vSwitch CI 3f9b5c
                 print("    } else {")
Open vSwitch CI 3f9b5c
                 print("        datum.n = 0;")
Open vSwitch CI 3f9b5c
                 print("        datum.keys = NULL;")
Open vSwitch CI 3f9b5c
                 print("    }")
Open vSwitch CI 3f9b5c
                 print("    datum.values = NULL;")
Open vSwitch CI 3f9b5c
-                txn_write_func = "ovsdb_idl_txn_write_clone"
Open vSwitch CI 3f9b5c
+                txn_write_func = "ovsdb_idl_txn_write"
Open vSwitch CI 3f9b5c
             elif type.n_max == 1:
Open vSwitch CI 3f9b5c
-                print("    union ovsdb_atom key;")
Open vSwitch CI 3f9b5c
                 print("")
Open vSwitch CI 3f9b5c
                 print("    if (%s) {" % nVar)
Open vSwitch CI 3f9b5c
+                print("        union ovsdb_atom *key = xmalloc(sizeof *key);")
Open vSwitch CI 3f9b5c
                 print("        datum.n = 1;")
Open vSwitch CI 3f9b5c
-                print("        datum.keys = &ke;;")
Open vSwitch CI 3f9b5c
-                print("        " + type.key.assign_c_value_casting_away_const("key.%s" % type.key.type.to_string(), "*" + keyVar))
Open vSwitch CI 3f9b5c
+                print("        datum.keys = key;")
Open vSwitch CI 3f9b5c
+                print("        " + type.key.copyCValue("key->%s" % type.key.type.to_lvalue_string(), "*" + keyVar))
Open vSwitch CI 3f9b5c
                 print("    } else {")
Open vSwitch CI 3f9b5c
                 print("        datum.n = 0;")
Open vSwitch CI 3f9b5c
                 print("        datum.keys = NULL;")
Open vSwitch CI 3f9b5c
                 print("    }")
Open vSwitch CI 3f9b5c
                 print("    datum.values = NULL;")
Open vSwitch CI 3f9b5c
-                txn_write_func = "ovsdb_idl_txn_write_clone"
Open vSwitch CI 3f9b5c
+                txn_write_func = "ovsdb_idl_txn_write"
Open vSwitch CI 3f9b5c
             else:
Open vSwitch CI 3f9b5c
                 print("")
Open vSwitch CI 3f9b5c
                 print("    datum.n = %s;" % nVar)
Open vSwitch CI 3f9b5c
@@ -958,9 +958,9 @@ void
Open vSwitch CI 3f9b5c
                 else:
Open vSwitch CI 3f9b5c
                     print("    datum.values = NULL;")
Open vSwitch CI 3f9b5c
                 print("    for (size_t i = 0; i < %s; i++) {" % nVar)
Open vSwitch CI 3f9b5c
-                print("        " + type.key.copyCValue("datum.keys[i].%s" % type.key.type.to_string(), "%s[i]" % keyVar))
Open vSwitch CI 3f9b5c
+                print("        " + type.key.copyCValue("datum.keys[i].%s" % type.key.type.to_lvalue_string(), "%s[i]" % keyVar))
Open vSwitch CI 3f9b5c
                 if type.value:
Open vSwitch CI 3f9b5c
-                    print("        " + type.value.copyCValue("datum.values[i].%s" % type.value.type.to_string(), "%s[i]" % valueVar))
Open vSwitch CI 3f9b5c
+                    print("        " + type.value.copyCValue("datum.values[i].%s" % type.value.type.to_lvalue_string(), "%s[i]" % valueVar))
Open vSwitch CI 3f9b5c
                 print("    }")
Open vSwitch CI 3f9b5c
                 if type.value:
Open vSwitch CI 3f9b5c
                     valueType = type.value.toAtomicType()
Open vSwitch CI 3f9b5c
@@ -996,9 +996,8 @@ void
Open vSwitch CI 3f9b5c
 ''' % {'s': structName, 'c': columnName,'coltype':column.type.key.to_const_c_type(prefix),
Open vSwitch CI 3f9b5c
         'valtype':column.type.value.to_const_c_type(prefix), 'S': structName.upper(),
Open vSwitch CI 3f9b5c
         'C': columnName.upper(), 't': tableName})
Open vSwitch CI 3f9b5c
-
Open vSwitch CI 3f9b5c
-                print("    "+ type.key.copyCValue("datum->keys[0].%s" % type.key.type.to_string(), "new_key"))
Open vSwitch CI 3f9b5c
-                print("    "+ type.value.copyCValue("datum->values[0].%s" % type.value.type.to_string(), "new_value"))
Open vSwitch CI 3f9b5c
+                print("    " + type.key.copyCValue("datum->keys[0].%s" % type.key.type.to_lvalue_string(), "new_key"))
Open vSwitch CI 3f9b5c
+                print("    " + type.value.copyCValue("datum->values[0].%s" % type.value.type.to_lvalue_string(), "new_value"))
Open vSwitch CI 3f9b5c
                 print('''
Open vSwitch CI 3f9b5c
     ovsdb_idl_txn_write_partial_map(&row->header_,
Open vSwitch CI 3f9b5c
                                     &%(s)s_col_%(c)s,
Open vSwitch CI 3f9b5c
@@ -1022,8 +1021,7 @@ void
Open vSwitch CI 3f9b5c
 ''' % {'s': structName, 'c': columnName,'coltype':column.type.key.to_const_c_type(prefix),
Open vSwitch CI 3f9b5c
         'valtype':column.type.value.to_const_c_type(prefix), 'S': structName.upper(),
Open vSwitch CI 3f9b5c
         'C': columnName.upper(), 't': tableName})
Open vSwitch CI 3f9b5c
-
Open vSwitch CI 3f9b5c
-                print("    "+ type.key.copyCValue("datum->keys[0].%s" % type.key.type.to_string(), "delete_key"))
Open vSwitch CI 3f9b5c
+                print("    " + type.key.copyCValue("datum->keys[0].%s" % type.key.type.to_lvalue_string(), "delete_key"))
Open vSwitch CI 3f9b5c
                 print('''
Open vSwitch CI 3f9b5c
     ovsdb_idl_txn_delete_partial_map(&row->header_,
Open vSwitch CI 3f9b5c
                                     &%(s)s_col_%(c)s,
Open vSwitch CI 3f9b5c
@@ -1049,8 +1047,7 @@ void
Open vSwitch CI 3f9b5c
     datum->values = NULL;
Open vSwitch CI 3f9b5c
 ''' % {'s': structName, 'c': columnName,
Open vSwitch CI 3f9b5c
         'valtype':column.type.key.to_const_c_type(prefix), 't': tableName})
Open vSwitch CI 3f9b5c
-
Open vSwitch CI 3f9b5c
-                print("    "+ type.key.copyCValue("datum->keys[0].%s" % type.key.type.to_string(), "new_value"))
Open vSwitch CI 3f9b5c
+                print("    " + type.key.copyCValue("datum->keys[0].%s" % type.key.type.to_lvalue_string(), "new_value"))
Open vSwitch CI 3f9b5c
                 print('''
Open vSwitch CI 3f9b5c
     ovsdb_idl_txn_write_partial_set(&row->header_,
Open vSwitch CI 3f9b5c
                                     &%(s)s_col_%(c)s,
Open vSwitch CI 3f9b5c
@@ -1074,8 +1071,7 @@ void
Open vSwitch CI 3f9b5c
 ''' % {'s': structName, 'c': columnName,'coltype':column.type.key.to_const_c_type(prefix),
Open vSwitch CI 3f9b5c
         'valtype':column.type.key.to_const_c_type(prefix), 'S': structName.upper(),
Open vSwitch CI 3f9b5c
         'C': columnName.upper(), 't': tableName})
Open vSwitch CI 3f9b5c
-
Open vSwitch CI 3f9b5c
-                print("    "+ type.key.copyCValue("datum->keys[0].%s" % type.key.type.to_string(), "delete_value"))
Open vSwitch CI 3f9b5c
+                print("    " + type.key.copyCValue("datum->keys[0].%s" % type.key.type.to_lvalue_string(), "delete_value"))
Open vSwitch CI 3f9b5c
                 print('''
Open vSwitch CI 3f9b5c
     ovsdb_idl_txn_delete_partial_set(&row->header_,
Open vSwitch CI 3f9b5c
                                     &%(s)s_col_%(c)s,
Open vSwitch CI 3f9b5c
@@ -1143,37 +1139,36 @@ void
Open vSwitch CI 3f9b5c
             print("    struct ovsdb_datum datum;")
Open vSwitch CI 3f9b5c
             free = []
Open vSwitch CI 3f9b5c
             if type.n_min == 1 and type.n_max == 1:
Open vSwitch CI 3f9b5c
-                print("    union ovsdb_atom key;")
Open vSwitch CI 3f9b5c
+                print("    union ovsdb_atom *key = xmalloc(sizeof *key);")
Open vSwitch CI 3f9b5c
                 if type.value:
Open vSwitch CI 3f9b5c
-                    print("    union ovsdb_atom value;")
Open vSwitch CI 3f9b5c
+                    print("    union ovsdb_atom *value = xmalloc(sizeof *value);")
Open vSwitch CI 3f9b5c
                 print("")
Open vSwitch CI 3f9b5c
                 print("    datum.n = 1;")
Open vSwitch CI 3f9b5c
-                print("    datum.keys = &ke;;")
Open vSwitch CI 3f9b5c
-                print("    " + type.key.assign_c_value_casting_away_const("key.%s" % type.key.type.to_string(), keyVar, refTable=False))
Open vSwitch CI 3f9b5c
+                print("    datum.keys = key;")
Open vSwitch CI 3f9b5c
+                print("    " + type.key.copyCValue("key->%s" % type.key.type.to_lvalue_string(), keyVar, refTable=False))
Open vSwitch CI 3f9b5c
                 if type.value:
Open vSwitch CI 3f9b5c
-                    print("    datum.values = &value;")
Open vSwitch CI 3f9b5c
-                    print("    "+ type.value.assign_c_value_casting_away_const("value.%s" % type.value.type.to_string(), valueVar, refTable=False))
Open vSwitch CI 3f9b5c
+                    print("    " + type.value.copyCValue("value.%s" % type.value.type.to_lvalue_string(), valueVar, refTable=False))
Open vSwitch CI 3f9b5c
                 else:
Open vSwitch CI 3f9b5c
                     print("    datum.values = NULL;")
Open vSwitch CI 3f9b5c
             elif type.is_optional_pointer():
Open vSwitch CI 3f9b5c
-                print("    union ovsdb_atom key;")
Open vSwitch CI 3f9b5c
                 print("")
Open vSwitch CI 3f9b5c
                 print("    if (%s) {" % keyVar)
Open vSwitch CI 3f9b5c
+                print("        union ovsdb_atom *key = xmalloc(sizeof *key);")
Open vSwitch CI 3f9b5c
                 print("        datum.n = 1;")
Open vSwitch CI 3f9b5c
-                print("        datum.keys = &ke;;")
Open vSwitch CI 3f9b5c
-                print("        " + type.key.assign_c_value_casting_away_const("key.%s" % type.key.type.to_string(), keyVar, refTable=False))
Open vSwitch CI 3f9b5c
+                print("        datum.keys = key;")
Open vSwitch CI 3f9b5c
+                print("        " + type.key.copyCValue("key->%s" % type.key.type.to_lvalue_string(), keyVar, refTable=False))
Open vSwitch CI 3f9b5c
                 print("    } else {")
Open vSwitch CI 3f9b5c
                 print("        datum.n = 0;")
Open vSwitch CI 3f9b5c
                 print("        datum.keys = NULL;")
Open vSwitch CI 3f9b5c
                 print("    }")
Open vSwitch CI 3f9b5c
                 print("    datum.values = NULL;")
Open vSwitch CI 3f9b5c
             elif type.n_max == 1:
Open vSwitch CI 3f9b5c
-                print("    union ovsdb_atom key;")
Open vSwitch CI 3f9b5c
                 print("")
Open vSwitch CI 3f9b5c
                 print("    if (%s) {" % nVar)
Open vSwitch CI 3f9b5c
+                print("        union ovsdb_atom *key = xmalloc(sizeof *key);")
Open vSwitch CI 3f9b5c
                 print("        datum.n = 1;")
Open vSwitch CI 3f9b5c
-                print("        datum.keys = &ke;;")
Open vSwitch CI 3f9b5c
-                print("        " + type.key.assign_c_value_casting_away_const("key.%s" % type.key.type.to_string(), "*" + keyVar, refTable=False))
Open vSwitch CI 3f9b5c
+                print("        datum.keys = key;")
Open vSwitch CI 3f9b5c
+                print("        " + type.key.copyCValue("key->%s" % type.key.type.to_lvalue_string(), "*" + keyVar, refTable=False))
Open vSwitch CI 3f9b5c
                 print("    } else {")
Open vSwitch CI 3f9b5c
                 print("        datum.n = 0;")
Open vSwitch CI 3f9b5c
                 print("        datum.keys = NULL;")
Open vSwitch CI 3f9b5c
@@ -1182,16 +1177,14 @@ void
Open vSwitch CI 3f9b5c
             else:
Open vSwitch CI 3f9b5c
                 print("    datum.n = %s;" % nVar)
Open vSwitch CI 3f9b5c
                 print("    datum.keys = %s ? xmalloc(%s * sizeof *datum.keys) : NULL;" % (nVar, nVar))
Open vSwitch CI 3f9b5c
-                free += ['datum.keys']
Open vSwitch CI 3f9b5c
                 if type.value:
Open vSwitch CI 3f9b5c
                     print("    datum.values = xmalloc(%s * sizeof *datum.values);" % nVar)
Open vSwitch CI 3f9b5c
-                    free += ['datum.values']
Open vSwitch CI 3f9b5c
                 else:
Open vSwitch CI 3f9b5c
                     print("    datum.values = NULL;")
Open vSwitch CI 3f9b5c
                 print("    for (size_t i = 0; i < %s; i++) {" % nVar)
Open vSwitch CI 3f9b5c
-                print("        " + type.key.assign_c_value_casting_away_const("datum.keys[i].%s" % type.key.type.to_string(), "%s[i]" % keyVar, refTable=False))
Open vSwitch CI 3f9b5c
+                print("        " + type.key.copyCValue("datum.keys[i].%s" % type.key.type.to_lvalue_string(), "%s[i]" % keyVar, refTable=False))
Open vSwitch CI 3f9b5c
                 if type.value:
Open vSwitch CI 3f9b5c
-                    print("        " + type.value.assign_c_value_casting_away_const("datum.values[i].%s" % type.value.type.to_string(), "%s[i]" % valueVar, refTable=False))
Open vSwitch CI 3f9b5c
+                    print("        " + type.value.copyCValue("datum.values[i].%s" % type.value.type.to_lvalue_string(), "%s[i]" % valueVar, refTable=False))
Open vSwitch CI 3f9b5c
                 print("    }")
Open vSwitch CI 3f9b5c
                 if type.value:
Open vSwitch CI 3f9b5c
                     valueType = type.value.toAtomicType()
Open vSwitch CI 3f9b5c
@@ -1211,8 +1204,8 @@ void
Open vSwitch CI 3f9b5c
        's': structName,
Open vSwitch CI 3f9b5c
        'S': structName.upper(),
Open vSwitch CI 3f9b5c
        'c': columnName})
Open vSwitch CI 3f9b5c
-            for var in free:
Open vSwitch CI 3f9b5c
-                print("    free(%s);" % var)
Open vSwitch CI 3f9b5c
+            print("    ovsdb_datum_destroy(&datum, &%(s)s_col_%(c)s.type);" \
Open vSwitch CI 3f9b5c
+                  % {'s': structName, 'c': columnName})
Open vSwitch CI 3f9b5c
             print("}")
Open vSwitch CI 3f9b5c
 
Open vSwitch CI 3f9b5c
 # Index table related functions
Open vSwitch CI 3f9b5c
@@ -1309,8 +1302,8 @@ struct %(s)s *
Open vSwitch CI 3f9b5c
 
Open vSwitch CI 3f9b5c
         i = 0;
Open vSwitch CI 3f9b5c
         SMAP_FOR_EACH (node, %(c)s) {
Open vSwitch CI 3f9b5c
-            datum->keys[i].string = node->key;
Open vSwitch CI 3f9b5c
-            datum->values[i].string = node->value;
Open vSwitch CI 3f9b5c
+            datum->keys[i].s = ovsdb_atom_string_create(node->key);
Open vSwitch CI 3f9b5c
+            datum->values[i].s = ovsdb_atom_string_create(node->value);
Open vSwitch CI 3f9b5c
             i++;
Open vSwitch CI 3f9b5c
         }
Open vSwitch CI 3f9b5c
         ovsdb_datum_sort_unique(datum, OVSDB_TYPE_STRING, OVSDB_TYPE_STRING);
Open vSwitch CI 3f9b5c
@@ -1359,10 +1352,10 @@ struct %(s)s *
Open vSwitch CI 3f9b5c
                 print()
Open vSwitch CI 3f9b5c
                 print("    datum.n = 1;")
Open vSwitch CI 3f9b5c
                 print("    datum.keys = key;")
Open vSwitch CI 3f9b5c
-                print("    " + type.key.assign_c_value_casting_away_const("key->%s" % type.key.type.to_string(), keyVar))
Open vSwitch CI 3f9b5c
+                print("    " + type.key.copyCValue("key->%s" % type.key.type.to_lvalue_string(), keyVar))
Open vSwitch CI 3f9b5c
                 if type.value:
Open vSwitch CI 3f9b5c
                     print("    datum.values = value;")
Open vSwitch CI 3f9b5c
-                    print("    "+ type.value.assign_c_value_casting_away_const("value->%s" % type.value.type.to_string(), valueVar))
Open vSwitch CI 3f9b5c
+                    print("    " + type.value.copyCValue("value->%s" % type.value.type.to_lvalue_string(), valueVar))
Open vSwitch CI 3f9b5c
                 else:
Open vSwitch CI 3f9b5c
                     print("    datum.values = NULL;")
Open vSwitch CI 3f9b5c
                 txn_write_func = "ovsdb_idl_index_write"
Open vSwitch CI 3f9b5c
@@ -1373,7 +1366,7 @@ struct %(s)s *
Open vSwitch CI 3f9b5c
                 print("        key = xmalloc(sizeof (union ovsdb_atom));")
Open vSwitch CI 3f9b5c
                 print("        datum.n = 1;")
Open vSwitch CI 3f9b5c
                 print("        datum.keys = key;")
Open vSwitch CI 3f9b5c
-                print("        " + type.key.assign_c_value_casting_away_const("key->%s" % type.key.type.to_string(), keyVar))
Open vSwitch CI 3f9b5c
+                print("        " + type.key.copyCValue("key->%s" % type.key.type.to_lvalue_string(), keyVar))
Open vSwitch CI 3f9b5c
                 print("    } else {")
Open vSwitch CI 3f9b5c
                 print("        datum.n = 0;")
Open vSwitch CI 3f9b5c
                 print("        datum.keys = NULL;")
Open vSwitch CI 3f9b5c
@@ -1387,7 +1380,7 @@ struct %(s)s *
Open vSwitch CI 3f9b5c
                 print("        key = xmalloc(sizeof(union ovsdb_atom));")
Open vSwitch CI 3f9b5c
                 print("        datum.n = 1;")
Open vSwitch CI 3f9b5c
                 print("        datum.keys = key;")
Open vSwitch CI 3f9b5c
-                print("        " + type.key.assign_c_value_casting_away_const("key->%s" % type.key.type.to_string(), "*" + keyVar))
Open vSwitch CI 3f9b5c
+                print("        " + type.key.copyCValue("key->%s" % type.key.type.to_lvalue_string(), "*" + keyVar))
Open vSwitch CI 3f9b5c
                 print("    } else {")
Open vSwitch CI 3f9b5c
                 print("        datum.n = 0;")
Open vSwitch CI 3f9b5c
                 print("        datum.keys = NULL;")
Open vSwitch CI 3f9b5c
@@ -1404,9 +1397,9 @@ struct %(s)s *
Open vSwitch CI 3f9b5c
                 else:
Open vSwitch CI 3f9b5c
                     print("    datum.values = NULL;")
Open vSwitch CI 3f9b5c
                 print("    for (i = 0; i < %s; i++) {" % nVar)
Open vSwitch CI 3f9b5c
-                print("        " + type.key.copyCValue("datum.keys[i].%s" % type.key.type.to_string(), "%s[i]" % keyVar))
Open vSwitch CI 3f9b5c
+                print("        " + type.key.copyCValue("datum.keys[i].%s" % type.key.type.to_lvalue_string(), "%s[i]" % keyVar))
Open vSwitch CI 3f9b5c
                 if type.value:
Open vSwitch CI 3f9b5c
-                    print("        " + type.value.copyCValue("datum.values[i].%s" % type.value.type.to_string(), "%s[i]" % valueVar))
Open vSwitch CI 3f9b5c
+                    print("        " + type.value.copyCValue("datum.values[i].%s" % type.value.type.to_lvalue_string(), "%s[i]" % valueVar))
Open vSwitch CI 3f9b5c
                 print("    }")
Open vSwitch CI 3f9b5c
                 if type.value:
Open vSwitch CI 3f9b5c
                     valueType = type.value.toAtomicType()
Open vSwitch CI 3f9b5c
diff --git a/ovsdb/ovsdb-server.c b/ovsdb/ovsdb-server.c
Open vSwitch CI 3f9b5c
index 0b3d2bb714..b34d97e291 100644
Open vSwitch CI 3f9b5c
--- a/ovsdb/ovsdb-server.c
Open vSwitch CI 3f9b5c
+++ b/ovsdb/ovsdb-server.c
Open vSwitch CI 3f9b5c
@@ -904,8 +904,8 @@ query_db_string(const struct shash *all_dbs, const char *name,
Open vSwitch CI 3f9b5c
 
Open vSwitch CI 3f9b5c
             datum = &row->fields[column->index];
Open vSwitch CI 3f9b5c
             for (i = 0; i < datum->n; i++) {
Open vSwitch CI 3f9b5c
-                if (datum->keys[i].string[0]) {
Open vSwitch CI 3f9b5c
-                    return datum->keys[i].string;
Open vSwitch CI 3f9b5c
+                if (datum->keys[i].s->string[0]) {
Open vSwitch CI 3f9b5c
+                    return datum->keys[i].s->string;
Open vSwitch CI 3f9b5c
                 }
Open vSwitch CI 3f9b5c
             }
Open vSwitch CI 3f9b5c
         }
Open vSwitch CI 3f9b5c
@@ -1018,7 +1018,7 @@ query_db_remotes(const char *name, const struct shash *all_dbs,
Open vSwitch CI 3f9b5c
 
Open vSwitch CI 3f9b5c
             datum = &row->fields[column->index];
Open vSwitch CI 3f9b5c
             for (i = 0; i < datum->n; i++) {
Open vSwitch CI 3f9b5c
-                add_remote(remotes, datum->keys[i].string);
Open vSwitch CI 3f9b5c
+                add_remote(remotes, datum->keys[i].s->string);
Open vSwitch CI 3f9b5c
             }
Open vSwitch CI 3f9b5c
         }
Open vSwitch CI 3f9b5c
     } else if (column->type.key.type == OVSDB_TYPE_UUID
110336
diff --git a/ovsdb/ovsdb-tool.c b/ovsdb/ovsdb-tool.c
110336
index 05a0223e71..d4a9e34cc4 100644
110336
--- a/ovsdb/ovsdb-tool.c
110336
+++ b/ovsdb/ovsdb-tool.c
110336
@@ -919,7 +919,8 @@ print_raft_header(const struct raft_header *h,
110336
         if (!uuid_is_zero(&h->snap.eid)) {
110336
             printf(" prev_eid: %04x\n", uuid_prefix(&h->snap.eid, 4));
110336
         }
110336
-        print_data("prev_", h->snap.data, schemap, names);
110336
+        print_data("prev_", raft_entry_get_parsed_data(&h->snap),
110336
+                            schemap, names);
110336
     }
110336
 }
110336
 
110336
@@ -973,11 +974,13 @@ raft_header_to_standalone_log(const struct raft_header *h,
110336
                               struct ovsdb_log *db_log_data)
110336
 {
110336
     if (h->snap_index) {
110336
-        if (!h->snap.data || json_array(h->snap.data)->n != 2) {
110336
+        const struct json *data = raft_entry_get_parsed_data(&h->snap);
110336
+
110336
+        if (!data || json_array(data)->n != 2) {
110336
             ovs_fatal(0, "Incorrect raft header data array length");
110336
         }
110336
 
110336
-        struct json_array *pa = json_array(h->snap.data);
110336
+        struct json_array *pa = json_array(data);
110336
         struct json *schema_json = pa->elems[0];
110336
         struct ovsdb_error *error = NULL;
110336
 
110336
@@ -1373,7 +1376,7 @@ do_check_cluster(struct ovs_cmdl_context *ctx)
110336
                 }
110336
                 struct raft_entry *e = &s->entries[log_idx];
110336
                 e->term = r->term;
110336
-                e->data = r->entry.data;
110336
+                raft_entry_set_parsed_data_nocopy(e, r->entry.data);
110336
                 e->eid = r->entry.eid;
110336
                 e->servers = r->entry.servers;
110336
                 break;
Open vSwitch CI 3f9b5c
diff --git a/ovsdb/ovsdb-util.c b/ovsdb/ovsdb-util.c
Open vSwitch CI 3f9b5c
index c4075cdae3..6d7be066b6 100644
Open vSwitch CI 3f9b5c
--- a/ovsdb/ovsdb-util.c
Open vSwitch CI 3f9b5c
+++ b/ovsdb/ovsdb-util.c
Open vSwitch CI 3f9b5c
@@ -111,13 +111,13 @@ ovsdb_util_read_map_string_column(const struct ovsdb_row *row,
Open vSwitch CI 3f9b5c
 
Open vSwitch CI 3f9b5c
     for (i = 0; i < datum->n; i++) {
Open vSwitch CI 3f9b5c
         atom_key = &datum->keys[i];
Open vSwitch CI 3f9b5c
-        if (!strcmp(atom_key->string, key)) {
Open vSwitch CI 3f9b5c
+        if (!strcmp(atom_key->s->string, key)) {
Open vSwitch CI 3f9b5c
             atom_value = &datum->values[i];
Open vSwitch CI 3f9b5c
             break;
Open vSwitch CI 3f9b5c
         }
Open vSwitch CI 3f9b5c
     }
Open vSwitch CI 3f9b5c
 
Open vSwitch CI 3f9b5c
-    return atom_value ? atom_value->string : NULL;
Open vSwitch CI 3f9b5c
+    return atom_value ? atom_value->s->string : NULL;
Open vSwitch CI 3f9b5c
 }
Open vSwitch CI 3f9b5c
 
Open vSwitch CI 3f9b5c
 /* Read string-uuid key-values from a map.  Returns the row associated with
Open vSwitch CI 3f9b5c
@@ -143,7 +143,7 @@ ovsdb_util_read_map_string_uuid_column(const struct ovsdb_row *row,
Open vSwitch CI 3f9b5c
     const struct ovsdb_datum *datum = &row->fields[column->index];
Open vSwitch CI 3f9b5c
     for (size_t i = 0; i < datum->n; i++) {
Open vSwitch CI 3f9b5c
         union ovsdb_atom *atom_key = &datum->keys[i];
Open vSwitch CI 3f9b5c
-        if (!strcmp(atom_key->string, key)) {
Open vSwitch CI 3f9b5c
+        if (!strcmp(atom_key->s->string, key)) {
Open vSwitch CI 3f9b5c
             const union ovsdb_atom *atom_value = &datum->values[i];
Open vSwitch CI 3f9b5c
             return ovsdb_table_get_row(ref_table, &atom_value->uuid);
Open vSwitch CI 3f9b5c
         }
Open vSwitch CI 3f9b5c
@@ -181,7 +181,7 @@ ovsdb_util_read_string_column(const struct ovsdb_row *row,
Open vSwitch CI 3f9b5c
     const union ovsdb_atom *atom;
Open vSwitch CI 3f9b5c
 
Open vSwitch CI 3f9b5c
     atom = ovsdb_util_read_column(row, column_name, OVSDB_TYPE_STRING);
Open vSwitch CI 3f9b5c
-    *stringp = atom ? atom->string : NULL;
Open vSwitch CI 3f9b5c
+    *stringp = atom ? atom->s->string : NULL;
Open vSwitch CI 3f9b5c
     return atom != NULL;
Open vSwitch CI 3f9b5c
 }
Open vSwitch CI 3f9b5c
 
Open vSwitch CI 3f9b5c
@@ -269,8 +269,10 @@ ovsdb_util_write_string_column(struct ovsdb_row *row, const char *column_name,
Open vSwitch CI 3f9b5c
                                const char *string)
Open vSwitch CI 3f9b5c
 {
Open vSwitch CI 3f9b5c
     if (string) {
Open vSwitch CI 3f9b5c
-        const union ovsdb_atom atom = { .string = CONST_CAST(char *, string) };
Open vSwitch CI 3f9b5c
+        union ovsdb_atom atom = {
Open vSwitch CI 3f9b5c
+            .s = ovsdb_atom_string_create(CONST_CAST(char *, string)) };
Open vSwitch CI 3f9b5c
         ovsdb_util_write_singleton(row, column_name, &atom, OVSDB_TYPE_STRING);
Open vSwitch CI 3f9b5c
+        ovsdb_atom_destroy(&atom, OVSDB_TYPE_STRING);
Open vSwitch CI 3f9b5c
     } else {
Open vSwitch CI 3f9b5c
         ovsdb_util_clear_column(row, column_name);
Open vSwitch CI 3f9b5c
     }
Open vSwitch CI 3f9b5c
@@ -305,8 +307,8 @@ ovsdb_util_write_string_string_column(struct ovsdb_row *row,
Open vSwitch CI 3f9b5c
     datum->values = xmalloc(n * sizeof *datum->values);
Open vSwitch CI 3f9b5c
 
Open vSwitch CI 3f9b5c
     for (i = 0; i < n; ++i) {
Open vSwitch CI 3f9b5c
-        datum->keys[i].string = keys[i];
Open vSwitch CI 3f9b5c
-        datum->values[i].string = values[i];
Open vSwitch CI 3f9b5c
+        datum->keys[i].s = ovsdb_atom_string_create_nocopy(keys[i]);
Open vSwitch CI 3f9b5c
+        datum->values[i].s = ovsdb_atom_string_create_nocopy(values[i]);
Open vSwitch CI 3f9b5c
     }
Open vSwitch CI 3f9b5c
 
Open vSwitch CI 3f9b5c
     /* Sort and check constraints. */
Open vSwitch CI 483c2c
diff --git a/ovsdb/ovsdb.c b/ovsdb/ovsdb.c
Open vSwitch CI 483c2c
index 126d16a2f5..e6d866182c 100644
Open vSwitch CI 483c2c
--- a/ovsdb/ovsdb.c
Open vSwitch CI 483c2c
+++ b/ovsdb/ovsdb.c
Open vSwitch CI 483c2c
@@ -422,6 +422,8 @@ ovsdb_create(struct ovsdb_schema *schema, struct ovsdb_storage *storage)
Open vSwitch CI 483c2c
     ovs_list_init(&db->triggers);
Open vSwitch CI 483c2c
     db->run_triggers_now = db->run_triggers = false;
Open vSwitch CI 483c2c
 
Open vSwitch CI 483c2c
+    db->n_atoms = 0;
Open vSwitch CI 483c2c
+
Open vSwitch CI 483c2c
     db->is_relay = false;
Open vSwitch CI 483c2c
     ovs_list_init(&db->txn_forward_new);
Open vSwitch CI 483c2c
     hmap_init(&db->txn_forward_sent);
Open vSwitch CI 483c2c
@@ -518,6 +520,9 @@ ovsdb_get_memory_usage(const struct ovsdb *db, struct simap *usage)
Open vSwitch CI 483c2c
     }
Open vSwitch CI 483c2c
 
Open vSwitch CI 483c2c
     simap_increase(usage, "cells", cells);
Open vSwitch CI 483c2c
+    simap_increase(usage, "atoms", db->n_atoms);
Open vSwitch CI 483c2c
+    simap_increase(usage, "txn-history", db->n_txn_history);
Open vSwitch CI 483c2c
+    simap_increase(usage, "txn-history-atoms", db->n_txn_history_atoms);
Open vSwitch CI 483c2c
 
Open vSwitch CI 483c2c
     if (db->storage) {
Open vSwitch CI 483c2c
         ovsdb_storage_get_memory_usage(db->storage, usage);
Open vSwitch CI 483c2c
diff --git a/ovsdb/ovsdb.h b/ovsdb/ovsdb.h
Open vSwitch CI 483c2c
index 4a7bd0f0ec..ec2d235ec2 100644
Open vSwitch CI 483c2c
--- a/ovsdb/ovsdb.h
Open vSwitch CI 483c2c
+++ b/ovsdb/ovsdb.h
Open vSwitch CI 483c2c
@@ -90,8 +90,11 @@ struct ovsdb {
Open vSwitch CI 483c2c
     /* History trasanctions for incremental monitor transfer. */
Open vSwitch CI 483c2c
     bool need_txn_history;     /* Need to maintain history of transactions. */
Open vSwitch CI 483c2c
     unsigned int n_txn_history; /* Current number of history transactions. */
Open vSwitch CI 483c2c
+    unsigned int n_txn_history_atoms; /* Total number of atoms in history. */
Open vSwitch CI 483c2c
     struct ovs_list txn_history; /* Contains "struct ovsdb_txn_history_node. */
Open vSwitch CI 483c2c
 
Open vSwitch CI 483c2c
+    size_t n_atoms;  /* Total number of ovsdb atoms in the database. */
Open vSwitch CI 483c2c
+
Open vSwitch CI 483c2c
     /* Relay mode. */
Open vSwitch CI 483c2c
     bool is_relay;  /* True, if database is in relay mode. */
Open vSwitch CI 483c2c
     /* List that holds transactions waiting to be forwarded to the server. */
110336
diff --git a/ovsdb/raft-private.c b/ovsdb/raft-private.c
110336
index 26d39a087f..30760233ee 100644
110336
--- a/ovsdb/raft-private.c
110336
+++ b/ovsdb/raft-private.c
110336
@@ -18,11 +18,14 @@
110336
 
110336
 #include "raft-private.h"
110336
 
110336
+#include "coverage.h"
110336
 #include "openvswitch/dynamic-string.h"
110336
 #include "ovsdb-error.h"
110336
 #include "ovsdb-parser.h"
110336
 #include "socket-util.h"
110336
 #include "sset.h"
110336
+
110336
+COVERAGE_DEFINE(raft_entry_serialize);
110336
 
110336
 /* Addresses of Raft servers. */
110336
 
110336
@@ -281,7 +284,8 @@ void
110336
 raft_entry_clone(struct raft_entry *dst, const struct raft_entry *src)
110336
 {
110336
     dst->term = src->term;
110336
-    dst->data = json_nullable_clone(src->data);
110336
+    dst->data.full_json = json_nullable_clone(src->data.full_json);
110336
+    dst->data.serialized = json_nullable_clone(src->data.serialized);
110336
     dst->eid = src->eid;
110336
     dst->servers = json_nullable_clone(src->servers);
110336
     dst->election_timer = src->election_timer;
110336
@@ -291,7 +295,8 @@ void
110336
 raft_entry_uninit(struct raft_entry *e)
110336
 {
110336
     if (e) {
110336
-        json_destroy(e->data);
110336
+        json_destroy(e->data.full_json);
110336
+        json_destroy(e->data.serialized);
110336
         json_destroy(e->servers);
110336
     }
110336
 }
110336
@@ -301,8 +306,9 @@ raft_entry_to_json(const struct raft_entry *e)
110336
 {
110336
     struct json *json = json_object_create();
110336
     raft_put_uint64(json, "term", e->term);
110336
-    if (e->data) {
110336
-        json_object_put(json, "data", json_clone(e->data));
110336
+    if (raft_entry_has_data(e)) {
110336
+        json_object_put(json, "data",
110336
+                        json_clone(raft_entry_get_serialized_data(e)));
110336
         json_object_put_format(json, "eid", UUID_FMT, UUID_ARGS(&e->eid));
110336
     }
110336
     if (e->servers) {
110336
@@ -323,9 +329,10 @@ raft_entry_from_json(struct json *json, struct raft_entry *e)
110336
     struct ovsdb_parser p;
110336
     ovsdb_parser_init(&p, json, "raft log entry");
110336
     e->term = raft_parse_required_uint64(&p, "term");
110336
-    e->data = json_nullable_clone(
110336
+    raft_entry_set_parsed_data(e,
110336
         ovsdb_parser_member(&p, "data", OP_OBJECT | OP_ARRAY | OP_OPTIONAL));
110336
-    e->eid = e->data ? raft_parse_required_uuid(&p, "eid") : UUID_ZERO;
110336
+    e->eid = raft_entry_has_data(e)
110336
+             ? raft_parse_required_uuid(&p, "eid") : UUID_ZERO;
110336
     e->servers = json_nullable_clone(
110336
         ovsdb_parser_member(&p, "servers", OP_OBJECT | OP_OPTIONAL));
110336
     if (e->servers) {
110336
@@ -344,9 +351,72 @@ bool
110336
 raft_entry_equals(const struct raft_entry *a, const struct raft_entry *b)
110336
 {
110336
     return (a->term == b->term
110336
-            && json_equal(a->data, b->data)
110336
             && uuid_equals(&a->eid, &b->eid)
110336
-            && json_equal(a->servers, b->servers));
110336
+            && json_equal(a->servers, b->servers)
110336
+            && json_equal(raft_entry_get_parsed_data(a),
110336
+                          raft_entry_get_parsed_data(b)));
110336
+}
110336
+
110336
+bool
110336
+raft_entry_has_data(const struct raft_entry *e)
110336
+{
110336
+    return e->data.full_json || e->data.serialized;
110336
+}
110336
+
110336
+static void
110336
+raft_entry_data_serialize(struct raft_entry *e)
110336
+{
110336
+    if (!raft_entry_has_data(e) || e->data.serialized) {
110336
+        return;
110336
+    }
110336
+    COVERAGE_INC(raft_entry_serialize);
110336
+    e->data.serialized = json_serialized_object_create(e->data.full_json);
110336
+}
110336
+
110336
+void
110336
+raft_entry_set_parsed_data_nocopy(struct raft_entry *e, struct json *json)
110336
+{
110336
+    ovs_assert(!json || json->type != JSON_SERIALIZED_OBJECT);
110336
+    e->data.full_json = json;
110336
+    e->data.serialized = NULL;
110336
+}
110336
+
110336
+void
110336
+raft_entry_set_parsed_data(struct raft_entry *e, const struct json *json)
110336
+{
110336
+    raft_entry_set_parsed_data_nocopy(e, json_nullable_clone(json));
110336
+}
110336
+
110336
+/* Returns a pointer to the fully parsed json object of the data.
110336
+ * Caller takes the ownership of the result.
110336
+ *
110336
+ * Entry will no longer contain a fully parsed json object.
110336
+ * Subsequent calls for the same raft entry will return NULL. */
110336
+struct json * OVS_WARN_UNUSED_RESULT
110336
+raft_entry_steal_parsed_data(struct raft_entry *e)
110336
+{
110336
+    /* Ensure that serialized version exists. */
110336
+    raft_entry_data_serialize(e);
110336
+
110336
+    struct json *json = e->data.full_json;
110336
+    e->data.full_json = NULL;
110336
+
110336
+    return json;
110336
+}
110336
+
110336
+/* Returns a pointer to the fully parsed json object of the data, if any. */
110336
+const struct json *
110336
+raft_entry_get_parsed_data(const struct raft_entry *e)
110336
+{
110336
+    return e->data.full_json;
110336
+}
110336
+
110336
+/* Returns a pointer to the JSON_SERIALIZED_OBJECT of the data. */
110336
+const struct json *
110336
+raft_entry_get_serialized_data(const struct raft_entry *e)
110336
+{
110336
+    raft_entry_data_serialize(CONST_CAST(struct raft_entry *, e));
110336
+    return e->data.serialized;
110336
 }
110336
 
110336
 void
110336
@@ -402,8 +472,8 @@ raft_header_from_json__(struct raft_header *h, struct ovsdb_parser *p)
110336
          * present, all of them must be. */
110336
         h->snap_index = raft_parse_optional_uint64(p, "prev_index");
110336
         if (h->snap_index) {
110336
-            h->snap.data = json_nullable_clone(
110336
-                ovsdb_parser_member(p, "prev_data", OP_ANY));
110336
+            raft_entry_set_parsed_data(
110336
+                &h->snap, ovsdb_parser_member(p, "prev_data", OP_ANY));
110336
             h->snap.eid = raft_parse_required_uuid(p, "prev_eid");
110336
             h->snap.term = raft_parse_required_uint64(p, "prev_term");
110336
             h->snap.election_timer = raft_parse_optional_uint64(
110336
@@ -455,8 +525,9 @@ raft_header_to_json(const struct raft_header *h)
110336
     if (h->snap_index) {
110336
         raft_put_uint64(json, "prev_index", h->snap_index);
110336
         raft_put_uint64(json, "prev_term", h->snap.term);
110336
-        if (h->snap.data) {
110336
-            json_object_put(json, "prev_data", json_clone(h->snap.data));
110336
+        if (raft_entry_has_data(&h->snap)) {
110336
+            json_object_put(json, "prev_data",
110336
+                json_clone(raft_entry_get_serialized_data(&h->snap)));
110336
         }
110336
         json_object_put_format(json, "prev_eid",
110336
                                UUID_FMT, UUID_ARGS(&h->snap.eid));
110336
diff --git a/ovsdb/raft-private.h b/ovsdb/raft-private.h
110336
index a69e37e5c2..48c6df511f 100644
110336
--- a/ovsdb/raft-private.h
110336
+++ b/ovsdb/raft-private.h
110336
@@ -118,7 +118,10 @@ void raft_servers_format(const struct hmap *servers, struct ds *ds);
110336
  * entry.  */
110336
 struct raft_entry {
110336
     uint64_t term;
110336
-    struct json *data;
110336
+    struct {
110336
+        struct json *full_json;   /* Fully parsed JSON object. */
110336
+        struct json *serialized;  /* JSON_SERIALIZED_OBJECT version of data. */
110336
+    } data;
110336
     struct uuid eid;
110336
     struct json *servers;
110336
     uint64_t election_timer;
110336
@@ -130,6 +133,13 @@ struct json *raft_entry_to_json(const struct raft_entry *);
110336
 struct ovsdb_error *raft_entry_from_json(struct json *, struct raft_entry *)
110336
     OVS_WARN_UNUSED_RESULT;
110336
 bool raft_entry_equals(const struct raft_entry *, const struct raft_entry *);
110336
+bool raft_entry_has_data(const struct raft_entry *);
110336
+void raft_entry_set_parsed_data(struct raft_entry *, const struct json *);
110336
+void raft_entry_set_parsed_data_nocopy(struct raft_entry *, struct json *);
110336
+struct json *raft_entry_steal_parsed_data(struct raft_entry *)
110336
+    OVS_WARN_UNUSED_RESULT;
110336
+const struct json *raft_entry_get_parsed_data(const struct raft_entry *);
110336
+const struct json *raft_entry_get_serialized_data(const struct raft_entry *);
110336
 
110336
 /* On disk data serialization and deserialization. */
110336
 
110336
diff --git a/ovsdb/raft.c b/ovsdb/raft.c
110336
index 2fb5156519..ce40c5bc07 100644
110336
--- a/ovsdb/raft.c
110336
+++ b/ovsdb/raft.c
110336
@@ -494,11 +494,11 @@ raft_create_cluster(const char *file_name, const char *name,
110336
         .snap_index = index++,
110336
         .snap = {
110336
             .term = term,
110336
-            .data = json_nullable_clone(data),
110336
             .eid = uuid_random(),
110336
             .servers = json_object_create(),
110336
         },
110336
     };
110336
+    raft_entry_set_parsed_data(&h.snap, data);
110336
     shash_add_nocopy(json_object(h.snap.servers),
110336
                      xasprintf(UUID_FMT, UUID_ARGS(&h.sid)),
110336
                      json_string_create(local_address));
110336
@@ -727,10 +727,10 @@ raft_add_entry(struct raft *raft,
110336
     uint64_t index = raft->log_end++;
110336
     struct raft_entry *entry = &raft->entries[index - raft->log_start];
110336
     entry->term = term;
110336
-    entry->data = data;
110336
     entry->eid = eid ? *eid : UUID_ZERO;
110336
     entry->servers = servers;
110336
     entry->election_timer = election_timer;
110336
+    raft_entry_set_parsed_data_nocopy(entry, data);
110336
     return index;
110336
 }
110336
 
110336
@@ -741,13 +741,16 @@ raft_write_entry(struct raft *raft, uint64_t term, struct json *data,
110336
                  const struct uuid *eid, struct json *servers,
110336
                  uint64_t election_timer)
110336
 {
110336
+    uint64_t index = raft_add_entry(raft, term, data, eid, servers,
110336
+                                    election_timer);
110336
+    const struct json *entry_data = raft_entry_get_serialized_data(
110336
+                                      &raft->entries[index - raft->log_start]);
110336
     struct raft_record r = {
110336
         .type = RAFT_REC_ENTRY,
110336
         .term = term,
110336
         .entry = {
110336
-            .index = raft_add_entry(raft, term, data, eid, servers,
110336
-                                    election_timer),
110336
-            .data = data,
110336
+            .index = index,
110336
+            .data = CONST_CAST(struct json *, entry_data),
110336
             .servers = servers,
110336
             .election_timer = election_timer,
110336
             .eid = eid ? *eid : UUID_ZERO,
110336
@@ -2161,7 +2164,7 @@ raft_get_eid(const struct raft *raft, uint64_t index)
110336
 {
110336
     for (; index >= raft->log_start; index--) {
110336
         const struct raft_entry *e = raft_get_entry(raft, index);
110336
-        if (e->data) {
110336
+        if (raft_entry_has_data(e)) {
110336
             return &e->eid;
110336
         }
110336
     }
110336
@@ -2826,8 +2829,8 @@ raft_truncate(struct raft *raft, uint64_t new_end)
110336
     return servers_changed;
110336
 }
110336
 
110336
-static const struct json *
110336
-raft_peek_next_entry(struct raft *raft, struct uuid *eid)
110336
+static const struct raft_entry *
110336
+raft_peek_next_entry(struct raft *raft)
110336
 {
110336
     /* Invariant: log_start - 2 <= last_applied <= commit_index < log_end. */
110336
     ovs_assert(raft->log_start <= raft->last_applied + 2);
110336
@@ -2839,32 +2842,20 @@ raft_peek_next_entry(struct raft *raft, struct uuid *eid)
110336
     }
110336
 
110336
     if (raft->log_start == raft->last_applied + 2) {
110336
-        *eid = raft->snap.eid;
110336
-        return raft->snap.data;
110336
+        return &raft->snap;
110336
     }
110336
 
110336
     while (raft->last_applied < raft->commit_index) {
110336
         const struct raft_entry *e = raft_get_entry(raft,
110336
                                                     raft->last_applied + 1);
110336
-        if (e->data) {
110336
-            *eid = e->eid;
110336
-            return e->data;
110336
+        if (raft_entry_has_data(e)) {
110336
+            return e;
110336
         }
110336
         raft->last_applied++;
110336
     }
110336
     return NULL;
110336
 }
110336
 
110336
-static const struct json *
110336
-raft_get_next_entry(struct raft *raft, struct uuid *eid)
110336
-{
110336
-    const struct json *data = raft_peek_next_entry(raft, eid);
110336
-    if (data) {
110336
-        raft->last_applied++;
110336
-    }
110336
-    return data;
110336
-}
110336
-
110336
 /* Updates commit index in raft log. If commit index is already up-to-date
110336
  * it does nothing and return false, otherwise, returns true. */
110336
 static bool
110336
@@ -2878,7 +2869,7 @@ raft_update_commit_index(struct raft *raft, uint64_t new_commit_index)
110336
         while (raft->commit_index < new_commit_index) {
110336
             uint64_t index = ++raft->commit_index;
110336
             const struct raft_entry *e = raft_get_entry(raft, index);
110336
-            if (e->data) {
110336
+            if (raft_entry_has_data(e)) {
110336
                 struct raft_command *cmd
110336
                     = raft_find_command_by_eid(raft, &e->eid);
110336
                 if (cmd) {
110336
@@ -3059,7 +3050,9 @@ raft_handle_append_entries(struct raft *raft,
110336
     for (; i < n_entries; i++) {
110336
         const struct raft_entry *e = &entries[i];
110336
         error = raft_write_entry(raft, e->term,
110336
-                                 json_nullable_clone(e->data), &e->eid,
110336
+                                 json_nullable_clone(
110336
+                                    raft_entry_get_parsed_data(e)),
110336
+                                 &e->eid,
110336
                                  json_nullable_clone(e->servers),
110336
                                  e->election_timer);
110336
         if (error) {
110336
@@ -3314,20 +3307,29 @@ bool
110336
 raft_has_next_entry(const struct raft *raft_)
110336
 {
110336
     struct raft *raft = CONST_CAST(struct raft *, raft_);
110336
-    struct uuid eid;
110336
-    return raft_peek_next_entry(raft, &eid) != NULL;
110336
+    return raft_peek_next_entry(raft) != NULL;
110336
 }
110336
 
110336
 /* Returns the next log entry or snapshot from 'raft', or NULL if there are
110336
- * none left to read.  Stores the entry ID of the log entry in '*eid'.  Stores
110336
- * true in '*is_snapshot' if the returned data is a snapshot, false if it is a
110336
- * log entry. */
110336
-const struct json *
110336
-raft_next_entry(struct raft *raft, struct uuid *eid, bool *is_snapshot)
110336
+ * none left to read.  Stores the entry ID of the log entry in '*eid'.
110336
+ *
110336
+ * The caller takes ownership of the result. */
110336
+struct json * OVS_WARN_UNUSED_RESULT
110336
+raft_next_entry(struct raft *raft, struct uuid *eid)
110336
 {
110336
-    const struct json *data = raft_get_next_entry(raft, eid);
110336
-    *is_snapshot = data == raft->snap.data;
110336
-    return data;
110336
+    const struct raft_entry *e = raft_peek_next_entry(raft);
110336
+
110336
+    if (!e) {
110336
+        return NULL;
110336
+    }
110336
+
110336
+    raft->last_applied++;
110336
+    *eid = e->eid;
110336
+
110336
+    /* DB will only read each entry once, so we don't need to store the fully
110336
+     * parsed json object any longer.  The serialized version is sufficient
110336
+     * for sending to other cluster members or writing to the log. */
110336
+    return raft_entry_steal_parsed_data(CONST_CAST(struct raft_entry *, e));
110336
 }
110336
 
110336
 /* Returns the log index of the last-read snapshot or log entry. */
110336
@@ -3420,6 +3422,7 @@ raft_send_install_snapshot_request(struct raft *raft,
110336
                                    const struct raft_server *s,
110336
                                    const char *comment)
110336
 {
110336
+    const struct json *data = raft_entry_get_serialized_data(&raft->snap);
110336
     union raft_rpc rpc = {
110336
         .install_snapshot_request = {
110336
             .common = {
110336
@@ -3432,7 +3435,7 @@ raft_send_install_snapshot_request(struct raft *raft,
110336
             .last_term = raft->snap.term,
110336
             .last_servers = raft->snap.servers,
110336
             .last_eid = raft->snap.eid,
110336
-            .data = raft->snap.data,
110336
+            .data = CONST_CAST(struct json *, data),
110336
             .election_timer = raft->election_timer, /* use latest value */
110336
         }
110336
     };
110336
@@ -3980,6 +3983,10 @@ raft_write_snapshot(struct raft *raft, struct ovsdb_log *log,
110336
                     uint64_t new_log_start,
110336
                     const struct raft_entry *new_snapshot)
110336
 {
110336
+    /* Ensure that new snapshot contains serialized data object, so it will
110336
+     * not be allocated while serializing the on-stack raft header object. */
110336
+    ovs_assert(raft_entry_get_serialized_data(new_snapshot));
110336
+
110336
     struct raft_header h = {
110336
         .sid = raft->sid,
110336
         .cid = raft->cid,
110336
@@ -3998,12 +4005,13 @@ raft_write_snapshot(struct raft *raft, struct ovsdb_log *log,
110336
     /* Write log records. */
110336
     for (uint64_t index = new_log_start; index < raft->log_end; index++) {
110336
         const struct raft_entry *e = &raft->entries[index - raft->log_start];
110336
+        const struct json *log_data = raft_entry_get_serialized_data(e);
110336
         struct raft_record r = {
110336
             .type = RAFT_REC_ENTRY,
110336
             .term = e->term,
110336
             .entry = {
110336
                 .index = index,
110336
-                .data = e->data,
110336
+                .data = CONST_CAST(struct json *, log_data),
110336
                 .servers = e->servers,
110336
                 .election_timer = e->election_timer,
110336
                 .eid = e->eid,
110336
@@ -4093,19 +4101,21 @@ raft_handle_install_snapshot_request__(
110336
 
110336
     /* Case 3: The new snapshot starts past the end of our current log, so
110336
      * discard all of our current log. */
110336
-    const struct raft_entry new_snapshot = {
110336
+    struct raft_entry new_snapshot = {
110336
         .term = rq->last_term,
110336
-        .data = rq->data,
110336
         .eid = rq->last_eid,
110336
-        .servers = rq->last_servers,
110336
+        .servers = json_clone(rq->last_servers),
110336
         .election_timer = rq->election_timer,
110336
     };
110336
+    raft_entry_set_parsed_data(&new_snapshot, rq->data);
110336
+
110336
     struct ovsdb_error *error = raft_save_snapshot(raft, new_log_start,
110336
                                                    &new_snapshot);
110336
     if (error) {
110336
         char *error_s = ovsdb_error_to_string_free(error);
110336
         VLOG_WARN("could not save snapshot: %s", error_s);
110336
         free(error_s);
110336
+        raft_entry_uninit(&new_snapshot);
110336
         return false;
110336
     }
110336
 
110336
@@ -4120,7 +4130,7 @@ raft_handle_install_snapshot_request__(
110336
     }
110336
 
110336
     raft_entry_uninit(&raft->snap);
110336
-    raft_entry_clone(&raft->snap, &new_snapshot);
110336
+    raft->snap = new_snapshot;
110336
 
110336
     raft_get_servers_from_log(raft, VLL_INFO);
110336
     raft_get_election_timer_from_log(raft);
110336
@@ -4265,11 +4275,12 @@ raft_store_snapshot(struct raft *raft, const struct json *new_snapshot_data)
110336
     uint64_t new_log_start = raft->last_applied + 1;
110336
     struct raft_entry new_snapshot = {
110336
         .term = raft_get_term(raft, new_log_start - 1),
110336
-        .data = json_clone(new_snapshot_data),
110336
         .eid = *raft_get_eid(raft, new_log_start - 1),
110336
         .servers = json_clone(raft_servers_for_index(raft, new_log_start - 1)),
110336
         .election_timer = raft->election_timer,
110336
     };
110336
+    raft_entry_set_parsed_data(&new_snapshot, new_snapshot_data);
110336
+
110336
     struct ovsdb_error *error = raft_save_snapshot(raft, new_log_start,
110336
                                                    &new_snapshot);
110336
     if (error) {
110336
@@ -4286,6 +4297,9 @@ raft_store_snapshot(struct raft *raft, const struct json *new_snapshot_data)
110336
     memmove(&raft->entries[0], &raft->entries[new_log_start - raft->log_start],
110336
             (raft->log_end - new_log_start) * sizeof *raft->entries);
110336
     raft->log_start = new_log_start;
110336
+    /* It's a snapshot of the current database state, ovsdb-server will not
110336
+     * read it back.  Destroying the parsed json object to not waste memory. */
110336
+    json_destroy(raft_entry_steal_parsed_data(&raft->snap));
110336
     return NULL;
110336
 }
110336
 
110336
diff --git a/ovsdb/raft.h b/ovsdb/raft.h
110336
index 3545c41c2c..599bc0ae86 100644
110336
--- a/ovsdb/raft.h
110336
+++ b/ovsdb/raft.h
110336
@@ -132,8 +132,8 @@ bool raft_left(const struct raft *);
110336
 bool raft_failed(const struct raft *);
110336
 
110336
 /* Reading snapshots and log entries. */
110336
-const struct json *raft_next_entry(struct raft *, struct uuid *eid,
110336
-                                   bool *is_snapshot);
110336
+struct json *raft_next_entry(struct raft *, struct uuid *eid)
110336
+    OVS_WARN_UNUSED_RESULT;
110336
 bool raft_has_next_entry(const struct raft *);
110336
 
110336
 uint64_t raft_get_applied_index(const struct raft *);
Open vSwitch CI 3f9b5c
diff --git a/ovsdb/rbac.c b/ovsdb/rbac.c
Open vSwitch CI 3f9b5c
index 2986027c90..ff411675f0 100644
Open vSwitch CI 3f9b5c
--- a/ovsdb/rbac.c
Open vSwitch CI 3f9b5c
+++ b/ovsdb/rbac.c
Open vSwitch CI 3f9b5c
@@ -53,8 +53,8 @@ ovsdb_find_row_by_string_key(const struct ovsdb_table *table,
Open vSwitch CI 3f9b5c
         HMAP_FOR_EACH (row, hmap_node, &table->rows) {
Open vSwitch CI 3f9b5c
             const struct ovsdb_datum *datum = &row->fields[column->index];
Open vSwitch CI 3f9b5c
             for (size_t i = 0; i < datum->n; i++) {
Open vSwitch CI 3f9b5c
-                if (datum->keys[i].string[0] &&
Open vSwitch CI 3f9b5c
-                    !strcmp(key, datum->keys[i].string)) {
Open vSwitch CI 3f9b5c
+                if (datum->keys[i].s->string[0] &&
Open vSwitch CI 3f9b5c
+                    !strcmp(key, datum->keys[i].s->string)) {
Open vSwitch CI 3f9b5c
                     return row;
Open vSwitch CI 3f9b5c
                 }
Open vSwitch CI 3f9b5c
             }
Open vSwitch CI 3f9b5c
@@ -113,7 +113,7 @@ ovsdb_rbac_authorized(const struct ovsdb_row *perms,
Open vSwitch CI 3f9b5c
     }
Open vSwitch CI 3f9b5c
 
Open vSwitch CI 3f9b5c
     for (i = 0; i < datum->n; i++) {
Open vSwitch CI 3f9b5c
-        const char *name = datum->keys[i].string;
Open vSwitch CI 3f9b5c
+        const char *name = datum->keys[i].s->string;
Open vSwitch CI 3f9b5c
         const char *value = NULL;
Open vSwitch CI 3f9b5c
         bool is_map;
Open vSwitch CI 3f9b5c
 
Open vSwitch CI 3f9b5c
@@ -271,7 +271,7 @@ rbac_column_modification_permitted(const struct ovsdb_column *column,
Open vSwitch CI 3f9b5c
     size_t i;
Open vSwitch CI 3f9b5c
 
Open vSwitch CI 3f9b5c
     for (i = 0; i < modifiable->n; i++) {
Open vSwitch CI 3f9b5c
-        char *name = modifiable->keys[i].string;
Open vSwitch CI 3f9b5c
+        char *name = modifiable->keys[i].s->string;
Open vSwitch CI 3f9b5c
 
Open vSwitch CI 3f9b5c
         if (!strcmp(name, column->name)) {
Open vSwitch CI 3f9b5c
             return true;
Open vSwitch CI 483c2c
diff --git a/ovsdb/row.c b/ovsdb/row.c
Open vSwitch CI 483c2c
index 65a0546211..e83c60a218 100644
Open vSwitch CI 483c2c
--- a/ovsdb/row.c
Open vSwitch CI 483c2c
+++ b/ovsdb/row.c
Open vSwitch CI 483c2c
@@ -38,8 +38,7 @@ allocate_row(const struct ovsdb_table *table)
Open vSwitch CI 483c2c
     struct ovsdb_row *row = xmalloc(row_size);
Open vSwitch CI 483c2c
     row->table = CONST_CAST(struct ovsdb_table *, table);
Open vSwitch CI 483c2c
     row->txn_row = NULL;
Open vSwitch CI 483c2c
-    ovs_list_init(&row->src_refs);
Open vSwitch CI 483c2c
-    ovs_list_init(&row->dst_refs);
Open vSwitch CI 483c2c
+    hmap_init(&row->dst_refs);
Open vSwitch CI 483c2c
     row->n_refs = 0;
Open vSwitch CI 483c2c
     return row;
Open vSwitch CI 483c2c
 }
Open vSwitch CI 483c2c
@@ -61,6 +60,78 @@ ovsdb_row_create(const struct ovsdb_table *table)
Open vSwitch CI 483c2c
     return row;
Open vSwitch CI 483c2c
 }
Open vSwitch CI 483c2c
 
Open vSwitch CI 483c2c
+static struct ovsdb_weak_ref *
Open vSwitch CI 483c2c
+ovsdb_weak_ref_clone(struct ovsdb_weak_ref *src)
Open vSwitch CI 483c2c
+{
Open vSwitch CI 483c2c
+    struct ovsdb_weak_ref *weak = xzalloc(sizeof *weak);
Open vSwitch CI 483c2c
+
Open vSwitch CI 483c2c
+    hmap_node_nullify(&weak->dst_node);
Open vSwitch CI 483c2c
+    ovs_list_init(&weak->src_node);
Open vSwitch CI 483c2c
+    weak->src_table = src->src_table;
Open vSwitch CI 483c2c
+    weak->src = src->src;
Open vSwitch CI 483c2c
+    weak->dst_table = src->dst_table;
Open vSwitch CI 483c2c
+    weak->dst = src->dst;
Open vSwitch CI 483c2c
+    ovsdb_atom_clone(&weak->key, &src->key, src->type.key.type);
Open vSwitch CI 483c2c
+    if (src->type.value.type != OVSDB_TYPE_VOID) {
Open vSwitch CI 483c2c
+        ovsdb_atom_clone(&weak->value, &src->value, src->type.value.type);
Open vSwitch CI 483c2c
+    }
Open vSwitch CI 483c2c
+    ovsdb_type_clone(&weak->type, &src->type);
Open vSwitch CI 483c2c
+    weak->column_idx = src->column_idx;
Open vSwitch CI 483c2c
+    weak->by_key = src->by_key;
Open vSwitch CI 483c2c
+    return weak;
Open vSwitch CI 483c2c
+}
Open vSwitch CI 483c2c
+
Open vSwitch CI 483c2c
+uint32_t
Open vSwitch CI 483c2c
+ovsdb_weak_ref_hash(const struct ovsdb_weak_ref *weak)
Open vSwitch CI 483c2c
+{
Open vSwitch CI 483c2c
+    return uuid_hash(&weak->src);
Open vSwitch CI 483c2c
+}
Open vSwitch CI 483c2c
+
Open vSwitch CI 483c2c
+static bool
Open vSwitch CI 483c2c
+ovsdb_weak_ref_equals(const struct ovsdb_weak_ref *a,
Open vSwitch CI 483c2c
+                      const struct ovsdb_weak_ref *b)
Open vSwitch CI 483c2c
+{
Open vSwitch CI 483c2c
+    if (a == b) {
Open vSwitch CI 483c2c
+        return true;
Open vSwitch CI 483c2c
+    }
Open vSwitch CI 483c2c
+    return a->src_table == b->src_table
Open vSwitch CI 483c2c
+           && a->dst_table == b->dst_table
Open vSwitch CI 483c2c
+           && uuid_equals(&a->src, &b->src)
Open vSwitch CI 483c2c
+           && uuid_equals(&a->dst, &b->dst)
Open vSwitch CI 483c2c
+           && a->column_idx == b->column_idx
Open vSwitch CI 483c2c
+           && a->by_key == b->by_key
Open vSwitch CI 483c2c
+           && ovsdb_atom_equals(&a->key, &b->key, a->type.key.type);
Open vSwitch CI 483c2c
+}
Open vSwitch CI 483c2c
+
Open vSwitch CI 483c2c
+struct ovsdb_weak_ref *
Open vSwitch CI 483c2c
+ovsdb_row_find_weak_ref(const struct ovsdb_row *row,
Open vSwitch CI 483c2c
+                        const struct ovsdb_weak_ref *ref)
Open vSwitch CI 483c2c
+{
Open vSwitch CI 483c2c
+    struct ovsdb_weak_ref *weak;
Open vSwitch CI 483c2c
+    HMAP_FOR_EACH_WITH_HASH (weak, dst_node,
Open vSwitch CI 483c2c
+                             ovsdb_weak_ref_hash(ref), &row->dst_refs) {
Open vSwitch CI 483c2c
+        if (ovsdb_weak_ref_equals(weak, ref)) {
Open vSwitch CI 483c2c
+            return weak;
Open vSwitch CI 483c2c
+        }
Open vSwitch CI 483c2c
+    }
Open vSwitch CI 483c2c
+    return NULL;
Open vSwitch CI 483c2c
+}
Open vSwitch CI 483c2c
+
Open vSwitch CI 483c2c
+void
Open vSwitch CI 483c2c
+ovsdb_weak_ref_destroy(struct ovsdb_weak_ref *weak)
Open vSwitch CI 483c2c
+{
Open vSwitch CI 483c2c
+    if (!weak) {
Open vSwitch CI 483c2c
+        return;
Open vSwitch CI 483c2c
+    }
Open vSwitch CI 483c2c
+    ovs_assert(ovs_list_is_empty(&weak->src_node));
Open vSwitch CI 483c2c
+    ovsdb_atom_destroy(&weak->key, weak->type.key.type);
Open vSwitch CI 483c2c
+    if (weak->type.value.type != OVSDB_TYPE_VOID) {
Open vSwitch CI 483c2c
+        ovsdb_atom_destroy(&weak->value, weak->type.value.type);
Open vSwitch CI 483c2c
+    }
Open vSwitch CI 483c2c
+    ovsdb_type_destroy(&weak->type);
Open vSwitch CI 483c2c
+    free(weak);
Open vSwitch CI 483c2c
+}
Open vSwitch CI 483c2c
+
Open vSwitch CI 483c2c
 struct ovsdb_row *
Open vSwitch CI 483c2c
 ovsdb_row_clone(const struct ovsdb_row *old)
Open vSwitch CI 483c2c
 {
Open vSwitch CI 483c2c
@@ -75,6 +146,13 @@ ovsdb_row_clone(const struct ovsdb_row *old)
Open vSwitch CI 483c2c
                           &old->fields[column->index],
Open vSwitch CI 483c2c
                           &column->type);
Open vSwitch CI 483c2c
     }
Open vSwitch CI 483c2c
+
Open vSwitch CI 483c2c
+    struct ovsdb_weak_ref *weak, *clone;
Open vSwitch CI 483c2c
+    HMAP_FOR_EACH (weak, dst_node, &old->dst_refs) {
Open vSwitch CI 483c2c
+        clone = ovsdb_weak_ref_clone(weak);
Open vSwitch CI 483c2c
+        hmap_insert(&new->dst_refs, &clone->dst_node,
Open vSwitch CI 483c2c
+                    ovsdb_weak_ref_hash(clone));
Open vSwitch CI 483c2c
+    }
Open vSwitch CI 483c2c
     return new;
Open vSwitch CI 483c2c
 }
Open vSwitch CI 483c2c
 
Open vSwitch CI 483c2c
@@ -85,20 +163,13 @@ ovsdb_row_destroy(struct ovsdb_row *row)
Open vSwitch CI 483c2c
 {
Open vSwitch CI 483c2c
     if (row) {
Open vSwitch CI 483c2c
         const struct ovsdb_table *table = row->table;
Open vSwitch CI 483c2c
-        struct ovsdb_weak_ref *weak, *next;
Open vSwitch CI 483c2c
+        struct ovsdb_weak_ref *weak;
Open vSwitch CI 483c2c
         const struct shash_node *node;
Open vSwitch CI 483c2c
 
Open vSwitch CI 483c2c
-        LIST_FOR_EACH_SAFE (weak, next, dst_node, &row->dst_refs) {
Open vSwitch CI 483c2c
-            ovs_list_remove(&weak->src_node);
Open vSwitch CI 483c2c
-            ovs_list_remove(&weak->dst_node);
Open vSwitch CI 483c2c
-            free(weak);
Open vSwitch CI 483c2c
-        }
Open vSwitch CI 483c2c
-
Open vSwitch CI 483c2c
-        LIST_FOR_EACH_SAFE (weak, next, src_node, &row->src_refs) {
Open vSwitch CI 483c2c
-            ovs_list_remove(&weak->src_node);
Open vSwitch CI 483c2c
-            ovs_list_remove(&weak->dst_node);
Open vSwitch CI 483c2c
-            free(weak);
Open vSwitch CI 483c2c
+        HMAP_FOR_EACH_POP (weak, dst_node, &row->dst_refs) {
Open vSwitch CI 483c2c
+            ovsdb_weak_ref_destroy(weak);
Open vSwitch CI 483c2c
         }
Open vSwitch CI 483c2c
+        hmap_destroy(&row->dst_refs);
Open vSwitch CI 483c2c
 
Open vSwitch CI 483c2c
         SHASH_FOR_EACH (node, &table->schema->columns) {
Open vSwitch CI 483c2c
             const struct ovsdb_column *column = node->data;
Open vSwitch CI 483c2c
diff --git a/ovsdb/row.h b/ovsdb/row.h
Open vSwitch CI 483c2c
index 394ac8eb49..fe04555d0c 100644
Open vSwitch CI 483c2c
--- a/ovsdb/row.h
Open vSwitch CI 483c2c
+++ b/ovsdb/row.h
Open vSwitch CI 483c2c
@@ -36,11 +36,28 @@ struct ovsdb_column_set;
Open vSwitch CI 483c2c
  * ovsdb_weak_ref" structures are created for them.
Open vSwitch CI 483c2c
  */
Open vSwitch CI 483c2c
 struct ovsdb_weak_ref {
Open vSwitch CI 483c2c
-    struct ovs_list src_node;      /* In src->src_refs list. */
Open vSwitch CI 483c2c
-    struct ovs_list dst_node;      /* In destination row's dst_refs list. */
Open vSwitch CI 483c2c
-    struct ovsdb_row *src;         /* Source row. */
Open vSwitch CI 483c2c
-    struct ovsdb_table *dst_table; /* Destination table. */
Open vSwitch CI 483c2c
+    struct hmap_node dst_node;     /* In ovsdb_row's 'dst_refs' hmap. */
Open vSwitch CI 483c2c
+    struct ovs_list src_node;      /* In txn_row's 'deleted/added_refs'. */
Open vSwitch CI 483c2c
+
Open vSwitch CI 483c2c
+    struct ovsdb_table *src_table; /* Source row table. */
Open vSwitch CI 483c2c
+    struct uuid src;               /* Source row uuid. */
Open vSwitch CI 483c2c
+
Open vSwitch CI 483c2c
+    struct ovsdb_table *dst_table; /* Destination row table. */
Open vSwitch CI 483c2c
     struct uuid dst;               /* Destination row uuid. */
Open vSwitch CI 483c2c
+
Open vSwitch CI 483c2c
+    /* Source row's key-value pair that created this reference.
Open vSwitch CI 483c2c
+     * This information is needed in order to find and delete the reference
Open vSwitch CI 483c2c
+     * from the source row.  We need both key and value in order to avoid
Open vSwitch CI 483c2c
+     * accidential deletion of an updated data, i.e. if value in datum got
Open vSwitch CI 483c2c
+     * updated and the reference was created by the old value.
Open vSwitch CI 483c2c
+     * Storing column index in order to remove references from the correct
Open vSwitch CI 483c2c
+     * column.   'by_key' flag allows to distinguish 2 references in a corner
Open vSwitch CI 483c2c
+     * case where key and value are the same. */
Open vSwitch CI 483c2c
+    union ovsdb_atom key;
Open vSwitch CI 483c2c
+    union ovsdb_atom value;
Open vSwitch CI 483c2c
+    struct ovsdb_type type;        /* Datum type of the key-value pair. */
Open vSwitch CI 483c2c
+    unsigned int column_idx;       /* Row column index for this pair. */
Open vSwitch CI 483c2c
+    bool by_key;                   /* 'true' if reference is a 'key'. */
Open vSwitch CI 483c2c
 };
Open vSwitch CI 483c2c
 
Open vSwitch CI 483c2c
 /* A row in a database table. */
Open vSwitch CI 483c2c
@@ -50,8 +67,7 @@ struct ovsdb_row {
Open vSwitch CI 483c2c
     struct ovsdb_txn_row *txn_row; /* Transaction that row is in, if any. */
Open vSwitch CI 483c2c
 
Open vSwitch CI 483c2c
     /* Weak references.  Updated and checked only at transaction commit. */
Open vSwitch CI 483c2c
-    struct ovs_list src_refs;   /* Weak references from this row. */
Open vSwitch CI 483c2c
-    struct ovs_list dst_refs;   /* Weak references to this row. */
Open vSwitch CI 483c2c
+    struct hmap dst_refs;          /* Weak references to this row. */
Open vSwitch CI 483c2c
 
Open vSwitch CI 483c2c
     /* Number of strong refs to this row from other rows, in this table or
Open vSwitch CI 483c2c
      * other tables, through 'uuid' columns that have a 'refTable' constraint
Open vSwitch CI 483c2c
@@ -69,6 +85,12 @@ struct ovsdb_row {
Open vSwitch CI 483c2c
      * index 'i' is contained in hmap table->indexes[i].  */
Open vSwitch CI 483c2c
 };
Open vSwitch CI 483c2c
 
Open vSwitch CI 483c2c
+uint32_t ovsdb_weak_ref_hash(const struct ovsdb_weak_ref *);
Open vSwitch CI 483c2c
+struct ovsdb_weak_ref * ovsdb_row_find_weak_ref(const struct ovsdb_row *,
Open vSwitch CI 483c2c
+                                                const struct ovsdb_weak_ref *);
Open vSwitch CI 483c2c
+void ovsdb_weak_ref_destroy(struct ovsdb_weak_ref *);
Open vSwitch CI 483c2c
+
Open vSwitch CI 483c2c
+
Open vSwitch CI 483c2c
 struct ovsdb_row *ovsdb_row_create(const struct ovsdb_table *);
Open vSwitch CI 483c2c
 struct ovsdb_row *ovsdb_row_clone(const struct ovsdb_row *);
Open vSwitch CI 483c2c
 void ovsdb_row_destroy(struct ovsdb_row *);
110336
diff --git a/ovsdb/storage.c b/ovsdb/storage.c
110336
index d727b1eacd..9e32efe582 100644
110336
--- a/ovsdb/storage.c
110336
+++ b/ovsdb/storage.c
110336
@@ -268,9 +268,7 @@ ovsdb_storage_read(struct ovsdb_storage *storage,
110336
     struct json *schema_json = NULL;
110336
     struct json *txn_json = NULL;
110336
     if (storage->raft) {
110336
-        bool is_snapshot;
110336
-        json = json_nullable_clone(
110336
-            raft_next_entry(storage->raft, txnid, &is_snapshot));
110336
+        json = raft_next_entry(storage->raft, txnid);
110336
         if (!json) {
110336
             return NULL;
110336
         } else if (json->type != JSON_ARRAY || json->array.n != 2) {
Open vSwitch CI 3f9b5c
diff --git a/ovsdb/transaction.c b/ovsdb/transaction.c
Open vSwitch CI 483c2c
index 8ffefcf7c9..88e0528002 100644
Open vSwitch CI 3f9b5c
--- a/ovsdb/transaction.c
Open vSwitch CI 3f9b5c
+++ b/ovsdb/transaction.c
Open vSwitch CI 483c2c
@@ -41,6 +41,9 @@ struct ovsdb_txn {
Open vSwitch CI 483c2c
     struct ovs_list txn_tables; /* Contains "struct ovsdb_txn_table"s. */
Open vSwitch CI 483c2c
     struct ds comment;
Open vSwitch CI 483c2c
     struct uuid txnid; /* For clustered mode only. It is the eid. */
Open vSwitch CI 483c2c
+    size_t n_atoms;    /* Number of atoms in all transaction rows. */
Open vSwitch CI 483c2c
+    ssize_t n_atoms_diff;  /* Difference between number of added and
Open vSwitch CI 483c2c
+                            * removed atoms. */
Open vSwitch CI 483c2c
 };
Open vSwitch CI 483c2c
 
Open vSwitch CI 483c2c
 /* A table modified by a transaction. */
Open vSwitch CI 483c2c
@@ -86,6 +89,10 @@ struct ovsdb_txn_row {
Open vSwitch CI 483c2c
     struct uuid uuid;
Open vSwitch CI 483c2c
     struct ovsdb_table *table;
Open vSwitch CI 483c2c
 
Open vSwitch CI 483c2c
+    /* Weak refs that needs to be added/deleted to/from destination rows. */
Open vSwitch CI 483c2c
+    struct ovs_list added_refs;
Open vSwitch CI 483c2c
+    struct ovs_list deleted_refs;
Open vSwitch CI 483c2c
+
Open vSwitch CI 483c2c
     /* Used by for_each_txn_row(). */
Open vSwitch CI 483c2c
     unsigned int serial;        /* Serial number of in-progress commit. */
Open vSwitch CI 483c2c
 
Open vSwitch CI 483c2c
@@ -151,6 +158,23 @@ ovsdb_txn_row_abort(struct ovsdb_txn *txn OVS_UNUSED,
Open vSwitch CI 483c2c
     } else {
Open vSwitch CI 483c2c
         hmap_replace(&new->table->rows, &new->hmap_node, &old->hmap_node);
Open vSwitch CI 483c2c
     }
Open vSwitch CI 483c2c
+
Open vSwitch CI 483c2c
+    struct ovsdb_weak_ref *weak, *next;
Open vSwitch CI 483c2c
+    LIST_FOR_EACH_SAFE (weak, next, src_node, &txn_row->deleted_refs) {
Open vSwitch CI 483c2c
+        ovs_list_remove(&weak->src_node);
Open vSwitch CI 483c2c
+        ovs_list_init(&weak->src_node);
Open vSwitch CI 483c2c
+        if (hmap_node_is_null(&weak->dst_node)) {
Open vSwitch CI 483c2c
+            ovsdb_weak_ref_destroy(weak);
Open vSwitch CI 483c2c
+        }
Open vSwitch CI 483c2c
+    }
Open vSwitch CI 483c2c
+    LIST_FOR_EACH_SAFE (weak, next, src_node, &txn_row->added_refs) {
Open vSwitch CI 483c2c
+        ovs_list_remove(&weak->src_node);
Open vSwitch CI 483c2c
+        ovs_list_init(&weak->src_node);
Open vSwitch CI 483c2c
+        if (hmap_node_is_null(&weak->dst_node)) {
Open vSwitch CI 483c2c
+            ovsdb_weak_ref_destroy(weak);
Open vSwitch CI 483c2c
+        }
Open vSwitch CI 483c2c
+    }
Open vSwitch CI 483c2c
+
Open vSwitch CI 483c2c
     ovsdb_row_destroy(new);
Open vSwitch CI 483c2c
     free(txn_row);
Open vSwitch CI 483c2c
 
Open vSwitch CI 483c2c
@@ -266,9 +290,9 @@ ovsdb_txn_adjust_atom_refs(struct ovsdb_txn *txn, const struct ovsdb_row *r,
Open vSwitch CI 3f9b5c
 
Open vSwitch CI 3f9b5c
 static struct ovsdb_error * OVS_WARN_UNUSED_RESULT
Open vSwitch CI 3f9b5c
 ovsdb_txn_adjust_row_refs(struct ovsdb_txn *txn, const struct ovsdb_row *r,
Open vSwitch CI 3f9b5c
-                          const struct ovsdb_column *column, int delta)
Open vSwitch CI 3f9b5c
+                          const struct ovsdb_column *column,
Open vSwitch CI 3f9b5c
+                          const struct ovsdb_datum *field, int delta)
Open vSwitch CI 3f9b5c
 {
Open vSwitch CI 3f9b5c
-    const struct ovsdb_datum *field = &r->fields[column->index];
Open vSwitch CI 3f9b5c
     struct ovsdb_error *error;
Open vSwitch CI 3f9b5c
 
Open vSwitch CI 3f9b5c
     error = ovsdb_txn_adjust_atom_refs(txn, r, column, &column->type.key,
Open vSwitch CI 483c2c
@@ -291,14 +315,39 @@ update_row_ref_count(struct ovsdb_txn *txn, struct ovsdb_txn_row *r)
Open vSwitch CI 3f9b5c
         struct ovsdb_error *error;
Open vSwitch CI 3f9b5c
 
Open vSwitch CI 3f9b5c
         if (bitmap_is_set(r->changed, column->index)) {
Open vSwitch CI 3f9b5c
-            if (r->old) {
Open vSwitch CI 3f9b5c
-                error = ovsdb_txn_adjust_row_refs(txn, r->old, column, -1);
Open vSwitch CI 3f9b5c
+            if (r->old && !r->new) {
Open vSwitch CI 3f9b5c
+                error = ovsdb_txn_adjust_row_refs(
Open vSwitch CI 3f9b5c
+                            txn, r->old, column,
Open vSwitch CI 3f9b5c
+                            &r->old->fields[column->index], -1);
Open vSwitch CI 3f9b5c
                 if (error) {
Open vSwitch CI 3f9b5c
                     return OVSDB_WRAP_BUG("error decreasing refcount", error);
Open vSwitch CI 3f9b5c
                 }
Open vSwitch CI 3f9b5c
-            }
Open vSwitch CI 3f9b5c
-            if (r->new) {
Open vSwitch CI 3f9b5c
-                error = ovsdb_txn_adjust_row_refs(txn, r->new, column, 1);
Open vSwitch CI 3f9b5c
+            } else if (!r->old && r->new) {
Open vSwitch CI 3f9b5c
+                error = ovsdb_txn_adjust_row_refs(
Open vSwitch CI 3f9b5c
+                            txn, r->new, column,
Open vSwitch CI 3f9b5c
+                            &r->new->fields[column->index], 1);
Open vSwitch CI 3f9b5c
+                if (error) {
Open vSwitch CI 3f9b5c
+                    return error;
Open vSwitch CI 3f9b5c
+                }
Open vSwitch CI 3f9b5c
+            } else if (r->old && r->new) {
Open vSwitch CI 3f9b5c
+                struct ovsdb_datum added, removed;
Open vSwitch CI 3f9b5c
+
Open vSwitch CI 3f9b5c
+                ovsdb_datum_added_removed(&added, &removed,
Open vSwitch CI 3f9b5c
+                                          &r->old->fields[column->index],
Open vSwitch CI 3f9b5c
+                                          &r->new->fields[column->index],
Open vSwitch CI 3f9b5c
+                                          &column->type);
Open vSwitch CI 3f9b5c
+
Open vSwitch CI 3f9b5c
+                error = ovsdb_txn_adjust_row_refs(
Open vSwitch CI 3f9b5c
+                            txn, r->old, column, &removed, -1);
Open vSwitch CI 3f9b5c
+                ovsdb_datum_destroy(&removed, &column->type);
Open vSwitch CI 3f9b5c
+                if (error) {
Open vSwitch CI 3f9b5c
+                    ovsdb_datum_destroy(&added, &column->type);
Open vSwitch CI 3f9b5c
+                    return OVSDB_WRAP_BUG("error decreasing refcount", error);
Open vSwitch CI 3f9b5c
+                }
Open vSwitch CI 3f9b5c
+
Open vSwitch CI 3f9b5c
+                error = ovsdb_txn_adjust_row_refs(
Open vSwitch CI 3f9b5c
+                            txn, r->new, column, &added, 1);
Open vSwitch CI 3f9b5c
+                ovsdb_datum_destroy(&added, &column->type);
Open vSwitch CI 3f9b5c
                 if (error) {
Open vSwitch CI 3f9b5c
                     return error;
Open vSwitch CI 3f9b5c
                 }
Open vSwitch CI 483c2c
@@ -459,93 +508,125 @@ static struct ovsdb_error *
Open vSwitch CI 483c2c
 ovsdb_txn_update_weak_refs(struct ovsdb_txn *txn OVS_UNUSED,
Open vSwitch CI 483c2c
                            struct ovsdb_txn_row *txn_row)
Open vSwitch CI 483c2c
 {
Open vSwitch CI 483c2c
-    struct ovsdb_weak_ref *weak, *next;
Open vSwitch CI 483c2c
+    struct ovsdb_weak_ref *weak, *next, *dst_weak;
Open vSwitch CI 483c2c
+    struct ovsdb_row *dst_row;
Open vSwitch CI 483c2c
 
Open vSwitch CI 483c2c
-    /* Remove the weak references originating in the old version of the row. */
Open vSwitch CI 483c2c
-    if (txn_row->old) {
Open vSwitch CI 483c2c
-        LIST_FOR_EACH_SAFE (weak, next, src_node, &txn_row->old->src_refs) {
Open vSwitch CI 483c2c
-            ovs_list_remove(&weak->src_node);
Open vSwitch CI 483c2c
-            ovs_list_remove(&weak->dst_node);
Open vSwitch CI 483c2c
-            free(weak);
Open vSwitch CI 483c2c
+    /* Find and clean up deleted references from destination rows. */
Open vSwitch CI 483c2c
+    LIST_FOR_EACH_SAFE (weak, next, src_node, &txn_row->deleted_refs) {
Open vSwitch CI 483c2c
+        dst_row = CONST_CAST(struct ovsdb_row *,
Open vSwitch CI 483c2c
+                    ovsdb_table_get_row(weak->dst_table, &weak->dst));
Open vSwitch CI 483c2c
+        if (dst_row) {
Open vSwitch CI 483c2c
+            dst_weak = ovsdb_row_find_weak_ref(dst_row, weak);
Open vSwitch CI 483c2c
+            hmap_remove(&dst_row->dst_refs, &dst_weak->dst_node);
Open vSwitch CI 483c2c
+            ovs_assert(ovs_list_is_empty(&dst_weak->src_node));
Open vSwitch CI 483c2c
+            ovsdb_weak_ref_destroy(dst_weak);
Open vSwitch CI 483c2c
+        }
Open vSwitch CI 483c2c
+        ovs_list_remove(&weak->src_node);
Open vSwitch CI 483c2c
+        ovs_list_init(&weak->src_node);
Open vSwitch CI 483c2c
+        if (hmap_node_is_null(&weak->dst_node)) {
Open vSwitch CI 483c2c
+            ovsdb_weak_ref_destroy(weak);
Open vSwitch CI 483c2c
         }
Open vSwitch CI 483c2c
     }
Open vSwitch CI 483c2c
 
Open vSwitch CI 483c2c
-    /* Although the originating rows have the responsibility of updating the
Open vSwitch CI 483c2c
-     * weak references in the dst, it is possible that some source rows aren't
Open vSwitch CI 483c2c
-     * part of the transaction.  In that situation this row needs to move the
Open vSwitch CI 483c2c
-     * list of incoming weak references from the old row into the new one.
Open vSwitch CI 483c2c
-     */
Open vSwitch CI 483c2c
-    if (txn_row->old && txn_row->new) {
Open vSwitch CI 483c2c
-        /* Move the incoming weak references from old to new. */
Open vSwitch CI 483c2c
-        ovs_list_push_back_all(&txn_row->new->dst_refs,
Open vSwitch CI 483c2c
-                               &txn_row->old->dst_refs);
Open vSwitch CI 483c2c
-    }
Open vSwitch CI 483c2c
-
Open vSwitch CI 483c2c
-    /* Insert the weak references originating in the new version of the row. */
Open vSwitch CI 483c2c
-    struct ovsdb_row *dst_row;
Open vSwitch CI 483c2c
-    if (txn_row->new) {
Open vSwitch CI 483c2c
-        LIST_FOR_EACH (weak, src_node, &txn_row->new->src_refs) {
Open vSwitch CI 483c2c
-            /* dst_row MUST exist. */
Open vSwitch CI 483c2c
-            dst_row = CONST_CAST(struct ovsdb_row *,
Open vSwitch CI 483c2c
+    /* Insert the weak references added in the new version of the row. */
Open vSwitch CI 483c2c
+    LIST_FOR_EACH_SAFE (weak, next, src_node, &txn_row->added_refs) {
Open vSwitch CI 483c2c
+        dst_row = CONST_CAST(struct ovsdb_row *,
Open vSwitch CI 483c2c
                     ovsdb_table_get_row(weak->dst_table, &weak->dst));
Open vSwitch CI 483c2c
-            ovs_list_insert(&dst_row->dst_refs, &weak->dst_node);
Open vSwitch CI 483c2c
-        }
Open vSwitch CI 483c2c
+
Open vSwitch CI 483c2c
+        ovs_assert(!ovsdb_row_find_weak_ref(dst_row, weak));
Open vSwitch CI 483c2c
+        hmap_insert(&dst_row->dst_refs, &weak->dst_node,
Open vSwitch CI 483c2c
+                    ovsdb_weak_ref_hash(weak));
Open vSwitch CI 483c2c
+        ovs_list_remove(&weak->src_node);
Open vSwitch CI 483c2c
+        ovs_list_init(&weak->src_node);
Open vSwitch CI 483c2c
     }
Open vSwitch CI 483c2c
 
Open vSwitch CI 483c2c
     return NULL;
Open vSwitch CI 483c2c
 }
Open vSwitch CI 483c2c
 
Open vSwitch CI 483c2c
 static void
Open vSwitch CI 483c2c
-add_weak_ref(const struct ovsdb_row *src_, const struct ovsdb_row *dst_)
Open vSwitch CI 483c2c
+add_weak_ref(struct ovsdb_txn_row *txn_row, const struct ovsdb_row *dst_,
Open vSwitch CI 483c2c
+             struct ovs_list *ref_list,
Open vSwitch CI 483c2c
+             const union ovsdb_atom *key, const union ovsdb_atom *value,
Open vSwitch CI 483c2c
+             bool by_key, const struct ovsdb_column *column)
Open vSwitch CI 483c2c
 {
Open vSwitch CI 483c2c
-    struct ovsdb_row *src = CONST_CAST(struct ovsdb_row *, src_);
Open vSwitch CI 483c2c
     struct ovsdb_row *dst = CONST_CAST(struct ovsdb_row *, dst_);
Open vSwitch CI 483c2c
     struct ovsdb_weak_ref *weak;
Open vSwitch CI 483c2c
 
Open vSwitch CI 483c2c
-    if (src == dst) {
Open vSwitch CI 483c2c
+    if (txn_row->new == dst) {
Open vSwitch CI 483c2c
         return;
Open vSwitch CI 483c2c
     }
Open vSwitch CI 483c2c
 
Open vSwitch CI 483c2c
-    if (!ovs_list_is_empty(&dst->dst_refs)) {
Open vSwitch CI 483c2c
-        /* Omit duplicates. */
Open vSwitch CI 483c2c
-        weak = CONTAINER_OF(ovs_list_back(&dst->dst_refs),
Open vSwitch CI 483c2c
-                            struct ovsdb_weak_ref, dst_node);
Open vSwitch CI 483c2c
-        if (weak->src == src) {
Open vSwitch CI 483c2c
-            return;
Open vSwitch CI 483c2c
-        }
Open vSwitch CI 483c2c
-    }
Open vSwitch CI 483c2c
-
Open vSwitch CI 483c2c
-    weak = xmalloc(sizeof *weak);
Open vSwitch CI 483c2c
-    weak->src = src;
Open vSwitch CI 483c2c
+    weak = xzalloc(sizeof *weak);
Open vSwitch CI 483c2c
+    weak->src_table = txn_row->new->table;
Open vSwitch CI 483c2c
+    weak->src = *ovsdb_row_get_uuid(txn_row->new);
Open vSwitch CI 483c2c
     weak->dst_table = dst->table;
Open vSwitch CI 483c2c
     weak->dst = *ovsdb_row_get_uuid(dst);
Open vSwitch CI 483c2c
-    /* The dst_refs list is updated at commit time. */
Open vSwitch CI 483c2c
-    ovs_list_init(&weak->dst_node);
Open vSwitch CI 483c2c
-    ovs_list_push_back(&src->src_refs, &weak->src_node);
Open vSwitch CI 483c2c
+    ovsdb_type_clone(&weak->type, &column->type);
Open vSwitch CI 483c2c
+    ovsdb_atom_clone(&weak->key, key, column->type.key.type);
Open vSwitch CI 483c2c
+    if (column->type.value.type != OVSDB_TYPE_VOID) {
Open vSwitch CI 483c2c
+        ovsdb_atom_clone(&weak->value, value, column->type.value.type);
Open vSwitch CI 483c2c
+    }
Open vSwitch CI 483c2c
+    weak->by_key = by_key;
Open vSwitch CI 483c2c
+    weak->column_idx = column->index;
Open vSwitch CI 483c2c
+    hmap_node_nullify(&weak->dst_node);
Open vSwitch CI 483c2c
+    ovs_list_push_back(ref_list, &weak->src_node);
Open vSwitch CI 483c2c
+}
Open vSwitch CI 483c2c
+
Open vSwitch CI 483c2c
+static void
Open vSwitch CI 483c2c
+find_and_add_weak_ref(struct ovsdb_txn_row *txn_row,
Open vSwitch CI 483c2c
+                      const union ovsdb_atom *key,
Open vSwitch CI 483c2c
+                      const union ovsdb_atom *value,
Open vSwitch CI 483c2c
+                      const struct ovsdb_column *column,
Open vSwitch CI 483c2c
+                      bool by_key, struct ovs_list *ref_list,
Open vSwitch CI 483c2c
+                      struct ovsdb_datum *not_found, bool *zero)
Open vSwitch CI 483c2c
+{
Open vSwitch CI 483c2c
+    const struct ovsdb_row *row = by_key
Open vSwitch CI 483c2c
+        ? ovsdb_table_get_row(column->type.key.uuid.refTable, &key->uuid)
Open vSwitch CI 483c2c
+        : ovsdb_table_get_row(column->type.value.uuid.refTable, &value->uuid);
Open vSwitch CI 483c2c
+
Open vSwitch CI 483c2c
+    if (row) {
Open vSwitch CI 483c2c
+        add_weak_ref(txn_row, row, ref_list, key, value, by_key, column);
Open vSwitch CI 483c2c
+    } else if (not_found) {
Open vSwitch CI 483c2c
+        if (uuid_is_zero(by_key ? &key->uuid : &value->uuid)) {
Open vSwitch CI 483c2c
+            *zero = true;
Open vSwitch CI 483c2c
+        }
Open vSwitch CI 483c2c
+        ovsdb_datum_add_unsafe(not_found, key, value, &column->type, NULL);
Open vSwitch CI 483c2c
+    }
Open vSwitch CI 483c2c
 }
Open vSwitch CI 483c2c
 
Open vSwitch CI 483c2c
 static struct ovsdb_error * OVS_WARN_UNUSED_RESULT
Open vSwitch CI 483c2c
 assess_weak_refs(struct ovsdb_txn *txn, struct ovsdb_txn_row *txn_row)
Open vSwitch CI 483c2c
 {
Open vSwitch CI 483c2c
+    struct ovsdb_weak_ref *weak, *next;
Open vSwitch CI 483c2c
     struct ovsdb_table *table;
Open vSwitch CI 483c2c
     struct shash_node *node;
Open vSwitch CI 483c2c
 
Open vSwitch CI 483c2c
     if (txn_row->old && !txn_row->new) {
Open vSwitch CI 483c2c
         /* Mark rows that have weak references to 'txn_row' as modified, so
Open vSwitch CI 483c2c
-         * that their weak references will get reassessed. */
Open vSwitch CI 483c2c
-        struct ovsdb_weak_ref *weak, *next;
Open vSwitch CI 483c2c
-
Open vSwitch CI 483c2c
-        LIST_FOR_EACH_SAFE (weak, next, dst_node, &txn_row->old->dst_refs) {
Open vSwitch CI 483c2c
-            if (!weak->src->txn_row) {
Open vSwitch CI 483c2c
-                ovsdb_txn_row_modify(txn, weak->src);
Open vSwitch CI 483c2c
+         * that their weak references will get reassessed.  Adding all weak
Open vSwitch CI 483c2c
+         * refs to 'deleted_ref' lists of their source rows, so they will be
Open vSwitch CI 483c2c
+         * cleaned up from datums and deleted on commit. */
Open vSwitch CI 483c2c
+
Open vSwitch CI 483c2c
+        HMAP_FOR_EACH (weak, dst_node, &txn_row->old->dst_refs) {
Open vSwitch CI 483c2c
+            struct ovsdb_txn_row *src_txn_row;
Open vSwitch CI 483c2c
+
Open vSwitch CI 483c2c
+            src_txn_row = find_or_make_txn_row(txn, weak->src_table,
Open vSwitch CI 483c2c
+                                               &weak->src);
Open vSwitch CI 483c2c
+            if (!src_txn_row) {
Open vSwitch CI 483c2c
+                /* Source row is also removed. */
Open vSwitch CI 483c2c
+                continue;
Open vSwitch CI 483c2c
             }
Open vSwitch CI 483c2c
+            ovs_assert(src_txn_row);
Open vSwitch CI 483c2c
+            ovs_assert(ovs_list_is_empty(&weak->src_node));
Open vSwitch CI 483c2c
+            ovs_list_insert(&src_txn_row->deleted_refs, &weak->src_node);
Open vSwitch CI 483c2c
         }
Open vSwitch CI 483c2c
     }
Open vSwitch CI 483c2c
 
Open vSwitch CI 483c2c
     if (!txn_row->new) {
Open vSwitch CI 483c2c
-        /* We don't have to do anything about references that originate at
Open vSwitch CI 483c2c
-         * 'txn_row', because ovsdb_row_destroy() will remove those weak
Open vSwitch CI 483c2c
-         * references. */
Open vSwitch CI 483c2c
+        /* Since all the atoms will be destroyed by the ovsdb_row_destroy(),
Open vSwitch CI 483c2c
+         * there is no need to check them here.  Source references queued
Open vSwitch CI 483c2c
+         * into 'deleted_ref' while removing other rows will be cleaned up at
Open vSwitch CI 483c2c
+         * commit time. */
Open vSwitch CI 483c2c
         return NULL;
Open vSwitch CI 483c2c
     }
Open vSwitch CI 483c2c
 
Open vSwitch CI 483c2c
@@ -553,50 +634,94 @@ assess_weak_refs(struct ovsdb_txn *txn, struct ovsdb_txn_row *txn_row)
Open vSwitch CI 483c2c
     SHASH_FOR_EACH (node, &table->schema->columns) {
Open vSwitch CI 483c2c
         const struct ovsdb_column *column = node->data;
Open vSwitch CI 483c2c
         struct ovsdb_datum *datum = &txn_row->new->fields[column->index];
Open vSwitch CI 483c2c
+        struct ovsdb_datum added, removed, deleted_refs;
Open vSwitch CI 483c2c
         unsigned int orig_n, i;
Open vSwitch CI 483c2c
         bool zero = false;
Open vSwitch CI 483c2c
 
Open vSwitch CI 483c2c
         orig_n = datum->n;
Open vSwitch CI 483c2c
 
Open vSwitch CI 483c2c
+        /* Collecting all key-value pairs that references deleted rows. */
Open vSwitch CI 483c2c
+        ovsdb_datum_init_empty(&deleted_refs);
Open vSwitch CI 483c2c
+        LIST_FOR_EACH_SAFE (weak, next, src_node, &txn_row->deleted_refs) {
Open vSwitch CI 483c2c
+            if (column->index == weak->column_idx) {
Open vSwitch CI 483c2c
+                ovsdb_datum_add_unsafe(&deleted_refs, &weak->key, &weak->value,
Open vSwitch CI 483c2c
+                                       &column->type, NULL);
Open vSwitch CI 483c2c
+                ovs_list_remove(&weak->src_node);
Open vSwitch CI 483c2c
+                ovs_list_init(&weak->src_node);
Open vSwitch CI 483c2c
+            }
Open vSwitch CI 483c2c
+        }
Open vSwitch CI 483c2c
+        ovsdb_datum_sort_unique(&deleted_refs, column->type.key.type,
Open vSwitch CI 483c2c
+                                               column->type.value.type);
Open vSwitch CI 483c2c
+
Open vSwitch CI 483c2c
+        /* Removing elements that references deleted rows. */
Open vSwitch CI 483c2c
+        ovsdb_datum_subtract(datum, &column->type,
Open vSwitch CI 483c2c
+                             &deleted_refs, &column->type);
Open vSwitch CI 483c2c
+        ovsdb_datum_destroy(&deleted_refs, &column->type);
Open vSwitch CI 483c2c
+
Open vSwitch CI 483c2c
+        /* Generating the difference between old and new data. */
Open vSwitch CI 483c2c
+        if (txn_row->old) {
Open vSwitch CI 483c2c
+            ovsdb_datum_added_removed(&added, &removed,
Open vSwitch CI 483c2c
+                                      &txn_row->old->fields[column->index],
Open vSwitch CI 483c2c
+                                      datum, &column->type);
Open vSwitch CI 483c2c
+        } else {
Open vSwitch CI 483c2c
+            ovsdb_datum_init_empty(&removed);
Open vSwitch CI 483c2c
+            ovsdb_datum_clone(&added, datum, &column->type);
Open vSwitch CI 483c2c
+        }
Open vSwitch CI 483c2c
+
Open vSwitch CI 483c2c
+        /* Checking added data and creating new references. */
Open vSwitch CI 483c2c
+        ovsdb_datum_init_empty(&deleted_refs);
Open vSwitch CI 483c2c
         if (ovsdb_base_type_is_weak_ref(&column->type.key)) {
Open vSwitch CI 483c2c
-            for (i = 0; i < datum->n; ) {
Open vSwitch CI 483c2c
-                const struct ovsdb_row *row;
Open vSwitch CI 483c2c
-
Open vSwitch CI 483c2c
-                row = ovsdb_table_get_row(column->type.key.uuid.refTable,
Open vSwitch CI 483c2c
-                                          &datum->keys[i].uuid);
Open vSwitch CI 483c2c
-                if (row) {
Open vSwitch CI 483c2c
-                    add_weak_ref(txn_row->new, row);
Open vSwitch CI 483c2c
-                    i++;
Open vSwitch CI 483c2c
-                } else {
Open vSwitch CI 483c2c
-                    if (uuid_is_zero(&datum->keys[i].uuid)) {
Open vSwitch CI 483c2c
-                        zero = true;
Open vSwitch CI 483c2c
-                    }
Open vSwitch CI 483c2c
-                    ovsdb_datum_remove_unsafe(datum, i, &column->type);
Open vSwitch CI 483c2c
-                }
Open vSwitch CI 483c2c
+            for (i = 0; i < added.n; i++) {
Open vSwitch CI 483c2c
+                find_and_add_weak_ref(txn_row, &added.keys[i],
Open vSwitch CI 483c2c
+                                      added.values ? &added.values[i] : NULL,
Open vSwitch CI 483c2c
+                                      column, true, &txn_row->added_refs,
Open vSwitch CI 483c2c
+                                      &deleted_refs, &zero);
Open vSwitch CI 483c2c
             }
Open vSwitch CI 483c2c
         }
Open vSwitch CI 483c2c
 
Open vSwitch CI 483c2c
         if (ovsdb_base_type_is_weak_ref(&column->type.value)) {
Open vSwitch CI 483c2c
-            for (i = 0; i < datum->n; ) {
Open vSwitch CI 483c2c
-                const struct ovsdb_row *row;
Open vSwitch CI 483c2c
-
Open vSwitch CI 483c2c
-                row = ovsdb_table_get_row(column->type.value.uuid.refTable,
Open vSwitch CI 483c2c
-                                          &datum->values[i].uuid);
Open vSwitch CI 483c2c
-                if (row) {
Open vSwitch CI 483c2c
-                    add_weak_ref(txn_row->new, row);
Open vSwitch CI 483c2c
-                    i++;
Open vSwitch CI 483c2c
-                } else {
Open vSwitch CI 483c2c
-                    if (uuid_is_zero(&datum->values[i].uuid)) {
Open vSwitch CI 483c2c
-                        zero = true;
Open vSwitch CI 483c2c
-                    }
Open vSwitch CI 483c2c
-                    ovsdb_datum_remove_unsafe(datum, i, &column->type);
Open vSwitch CI 483c2c
-                }
Open vSwitch CI 483c2c
+            for (i = 0; i < added.n; i++) {
Open vSwitch CI 483c2c
+                find_and_add_weak_ref(txn_row, &added.keys[i],
Open vSwitch CI 483c2c
+                                      &added.values[i],
Open vSwitch CI 483c2c
+                                      column, false, &txn_row->added_refs,
Open vSwitch CI 483c2c
+                                      &deleted_refs, &zero);
Open vSwitch CI 483c2c
+            }
Open vSwitch CI 483c2c
+        }
Open vSwitch CI 483c2c
+        if (deleted_refs.n) {
Open vSwitch CI 483c2c
+            /* Removing all the references that doesn't point to valid rows. */
Open vSwitch CI 483c2c
+            ovsdb_datum_sort_unique(&deleted_refs, column->type.key.type,
Open vSwitch CI 483c2c
+                                                   column->type.value.type);
Open vSwitch CI 483c2c
+            ovsdb_datum_subtract(datum, &column->type,
Open vSwitch CI 483c2c
+                                 &deleted_refs, &column->type);
Open vSwitch CI 483c2c
+            ovsdb_datum_destroy(&deleted_refs, &column->type);
Open vSwitch CI 483c2c
+        }
Open vSwitch CI 483c2c
+        ovsdb_datum_destroy(&added, &column->type);
Open vSwitch CI 483c2c
+
Open vSwitch CI 483c2c
+        /* Creating refs that needs to be removed on commit.  This includes
Open vSwitch CI 483c2c
+         * both: the references that got directly removed from the datum and
Open vSwitch CI 483c2c
+         * references removed due to deletion of a referenced row. */
Open vSwitch CI 483c2c
+        if (ovsdb_base_type_is_weak_ref(&column->type.key)) {
Open vSwitch CI 483c2c
+            for (i = 0; i < removed.n; i++) {
Open vSwitch CI 483c2c
+                find_and_add_weak_ref(txn_row, &removed.keys[i],
Open vSwitch CI 483c2c
+                                      removed.values
Open vSwitch CI 483c2c
+                                      ? &removed.values[i] : NULL,
Open vSwitch CI 483c2c
+                                      column, true, &txn_row->deleted_refs,
Open vSwitch CI 483c2c
+                                      NULL, NULL);
Open vSwitch CI 483c2c
             }
Open vSwitch CI 483c2c
         }
Open vSwitch CI 483c2c
 
Open vSwitch CI 483c2c
+        if (ovsdb_base_type_is_weak_ref(&column->type.value)) {
Open vSwitch CI 483c2c
+            for (i = 0; i < removed.n; i++) {
Open vSwitch CI 483c2c
+                find_and_add_weak_ref(txn_row, &removed.keys[i],
Open vSwitch CI 483c2c
+                                      &removed.values[i],
Open vSwitch CI 483c2c
+                                      column, false, &txn_row->deleted_refs,
Open vSwitch CI 483c2c
+                                      NULL, NULL);
Open vSwitch CI 483c2c
+            }
Open vSwitch CI 483c2c
+        }
Open vSwitch CI 483c2c
+        ovsdb_datum_destroy(&removed, &column->type);
Open vSwitch CI 483c2c
+
Open vSwitch CI 483c2c
         if (datum->n != orig_n) {
Open vSwitch CI 483c2c
             bitmap_set1(txn_row->changed, column->index);
Open vSwitch CI 483c2c
-            ovsdb_datum_sort_assert(datum, column->type.key.type);
Open vSwitch CI 483c2c
             if (datum->n < column->type.n_min) {
Open vSwitch CI 483c2c
                 const struct uuid *row_uuid = ovsdb_row_get_uuid(txn_row->new);
Open vSwitch CI 483c2c
                 if (zero && !txn_row->old) {
Open vSwitch CI 483c2c
@@ -817,6 +942,37 @@ check_index_uniqueness(struct ovsdb_txn *txn OVS_UNUSED,
Open vSwitch CI 483c2c
     return NULL;
Open vSwitch CI 483c2c
 }
Open vSwitch CI 483c2c
 
Open vSwitch CI 483c2c
+static struct ovsdb_error * OVS_WARN_UNUSED_RESULT
Open vSwitch CI 483c2c
+count_atoms(struct ovsdb_txn *txn, struct ovsdb_txn_row *txn_row)
Open vSwitch CI 483c2c
+{
Open vSwitch CI 483c2c
+    struct ovsdb_table *table = txn_row->table;
Open vSwitch CI 483c2c
+    ssize_t n_atoms_old = 0, n_atoms_new = 0;
Open vSwitch CI 483c2c
+    struct shash_node *node;
Open vSwitch CI 483c2c
+
Open vSwitch CI 483c2c
+    SHASH_FOR_EACH (node, &table->schema->columns) {
Open vSwitch CI 483c2c
+        const struct ovsdb_column *column = node->data;
Open vSwitch CI 483c2c
+        const struct ovsdb_type *type = &column->type;
Open vSwitch CI 483c2c
+        unsigned int idx = column->index;
Open vSwitch CI 483c2c
+
Open vSwitch CI 483c2c
+        if (txn_row->old) {
Open vSwitch CI 483c2c
+            n_atoms_old += txn_row->old->fields[idx].n;
Open vSwitch CI 483c2c
+            if (type->value.type != OVSDB_TYPE_VOID) {
Open vSwitch CI 483c2c
+                n_atoms_old += txn_row->old->fields[idx].n;
Open vSwitch CI 483c2c
+            }
Open vSwitch CI 483c2c
+        }
Open vSwitch CI 483c2c
+        if (txn_row->new) {
Open vSwitch CI 483c2c
+            n_atoms_new += txn_row->new->fields[idx].n;
Open vSwitch CI 483c2c
+            if (type->value.type != OVSDB_TYPE_VOID) {
Open vSwitch CI 483c2c
+                n_atoms_new += txn_row->new->fields[idx].n;
Open vSwitch CI 483c2c
+            }
Open vSwitch CI 483c2c
+        }
Open vSwitch CI 483c2c
+    }
Open vSwitch CI 483c2c
+
Open vSwitch CI 483c2c
+    txn->n_atoms += n_atoms_old + n_atoms_new;
Open vSwitch CI 483c2c
+    txn->n_atoms_diff += n_atoms_new - n_atoms_old;
Open vSwitch CI 483c2c
+    return NULL;
Open vSwitch CI 483c2c
+}
Open vSwitch CI 483c2c
+
Open vSwitch CI 483c2c
 static struct ovsdb_error * OVS_WARN_UNUSED_RESULT
Open vSwitch CI 483c2c
 update_version(struct ovsdb_txn *txn OVS_UNUSED, struct ovsdb_txn_row *txn_row)
Open vSwitch CI 483c2c
 {
Open vSwitch CI 483c2c
@@ -885,6 +1041,12 @@ ovsdb_txn_precommit(struct ovsdb_txn *txn)
Open vSwitch CI 483c2c
         return error;
Open vSwitch CI 483c2c
     }
Open vSwitch CI 483c2c
 
Open vSwitch CI 483c2c
+    /* Count atoms. */
Open vSwitch CI 483c2c
+    error = for_each_txn_row(txn, count_atoms);
Open vSwitch CI 483c2c
+    if (error) {
Open vSwitch CI 483c2c
+        return OVSDB_WRAP_BUG("can't happen", error);
Open vSwitch CI 483c2c
+    }
Open vSwitch CI 483c2c
+
Open vSwitch CI 483c2c
     /* Update _version for rows that changed.  */
Open vSwitch CI 483c2c
     error = for_each_txn_row(txn, update_version);
Open vSwitch CI 483c2c
     if (error) {
Open vSwitch CI 483c2c
@@ -900,6 +1062,8 @@ ovsdb_txn_clone(const struct ovsdb_txn *txn)
Open vSwitch CI 483c2c
     struct ovsdb_txn *txn_cloned = xzalloc(sizeof *txn_cloned);
Open vSwitch CI 483c2c
     ovs_list_init(&txn_cloned->txn_tables);
Open vSwitch CI 483c2c
     txn_cloned->txnid = txn->txnid;
Open vSwitch CI 483c2c
+    txn_cloned->n_atoms = txn->n_atoms;
Open vSwitch CI 483c2c
+    txn_cloned->n_atoms_diff = txn->n_atoms_diff;
Open vSwitch CI 483c2c
 
Open vSwitch CI 483c2c
     struct ovsdb_txn_table *t;
Open vSwitch CI 483c2c
     LIST_FOR_EACH (t, node, &txn->txn_tables) {
Open vSwitch CI 483c2c
@@ -958,6 +1122,7 @@ ovsdb_txn_add_to_history(struct ovsdb_txn *txn)
Open vSwitch CI 483c2c
         node->txn = ovsdb_txn_clone(txn);
Open vSwitch CI 483c2c
         ovs_list_push_back(&txn->db->txn_history, &node->node);
Open vSwitch CI 483c2c
         txn->db->n_txn_history++;
Open vSwitch CI 483c2c
+        txn->db->n_txn_history_atoms += txn->n_atoms;
Open vSwitch CI 483c2c
     }
Open vSwitch CI 483c2c
 }
Open vSwitch CI 483c2c
 
Open vSwitch CI 483c2c
@@ -968,6 +1133,7 @@ ovsdb_txn_complete(struct ovsdb_txn *txn)
Open vSwitch CI 483c2c
     if (!ovsdb_txn_is_empty(txn)) {
Open vSwitch CI 483c2c
 
Open vSwitch CI 483c2c
         txn->db->run_triggers_now = txn->db->run_triggers = true;
Open vSwitch CI 483c2c
+        txn->db->n_atoms += txn->n_atoms_diff;
Open vSwitch CI 483c2c
         ovsdb_monitors_commit(txn->db, txn);
Open vSwitch CI 483c2c
         ovsdb_error_assert(for_each_txn_row(txn, ovsdb_txn_update_weak_refs));
Open vSwitch CI 483c2c
         ovsdb_error_assert(for_each_txn_row(txn, ovsdb_txn_row_commit));
Open vSwitch CI 483c2c
@@ -1215,6 +1381,9 @@ ovsdb_txn_row_create(struct ovsdb_txn *txn, struct ovsdb_table *table,
Open vSwitch CI 483c2c
     txn_row->n_refs = old ? old->n_refs : 0;
Open vSwitch CI 483c2c
     txn_row->serial = serial - 1;
Open vSwitch CI 483c2c
 
Open vSwitch CI 483c2c
+    ovs_list_init(&txn_row->added_refs);
Open vSwitch CI 483c2c
+    ovs_list_init(&txn_row->deleted_refs);
Open vSwitch CI 483c2c
+
Open vSwitch CI 483c2c
     if (old) {
Open vSwitch CI 483c2c
         old->txn_row = txn_row;
Open vSwitch CI 483c2c
     }
Open vSwitch CI 483c2c
@@ -1423,12 +1592,18 @@ ovsdb_txn_history_run(struct ovsdb *db)
Open vSwitch CI 483c2c
     if (!db->need_txn_history) {
Open vSwitch CI 483c2c
         return;
Open vSwitch CI 483c2c
     }
Open vSwitch CI 483c2c
-    /* Remove old histories to limit the size of the history */
Open vSwitch CI 483c2c
-    while (db->n_txn_history > 100) {
Open vSwitch CI 483c2c
+    /* Remove old histories to limit the size of the history.  Removing until
Open vSwitch CI 483c2c
+     * the number of ovsdb atoms in history becomes less than the number of
Open vSwitch CI 483c2c
+     * atoms in the database, because it will be faster to just get a database
Open vSwitch CI 483c2c
+     * snapshot than re-constructing changes from the history that big. */
Open vSwitch CI 483c2c
+    while (db->n_txn_history &&
Open vSwitch CI 483c2c
+           (db->n_txn_history > 100 ||
Open vSwitch CI 483c2c
+            db->n_txn_history_atoms > db->n_atoms)) {
Open vSwitch CI 483c2c
         struct ovsdb_txn_history_node *txn_h_node = CONTAINER_OF(
Open vSwitch CI 483c2c
                 ovs_list_pop_front(&db->txn_history),
Open vSwitch CI 483c2c
                 struct ovsdb_txn_history_node, node);
Open vSwitch CI 483c2c
 
Open vSwitch CI 483c2c
+        db->n_txn_history_atoms -= txn_h_node->txn->n_atoms;
Open vSwitch CI 483c2c
         ovsdb_txn_destroy_cloned(txn_h_node->txn);
Open vSwitch CI 483c2c
         free(txn_h_node);
Open vSwitch CI 483c2c
         db->n_txn_history--;
Open vSwitch CI 483c2c
@@ -1440,6 +1615,7 @@ ovsdb_txn_history_init(struct ovsdb *db, bool need_txn_history)
Open vSwitch CI 483c2c
 {
Open vSwitch CI 483c2c
     db->need_txn_history = need_txn_history;
Open vSwitch CI 483c2c
     db->n_txn_history = 0;
Open vSwitch CI 483c2c
+    db->n_txn_history_atoms = 0;
Open vSwitch CI 483c2c
     ovs_list_init(&db->txn_history);
Open vSwitch CI 483c2c
 }
Open vSwitch CI 483c2c
 
Open vSwitch CI 483c2c
@@ -1458,4 +1634,5 @@ ovsdb_txn_history_destroy(struct ovsdb *db)
Open vSwitch CI 483c2c
         free(txn_h_node);
Open vSwitch CI 483c2c
     }
Open vSwitch CI 483c2c
     db->n_txn_history = 0;
Open vSwitch CI 483c2c
+    db->n_txn_history_atoms = 0;
Open vSwitch CI 483c2c
 }
Open vSwitch CI 3f9b5c
diff --git a/python/ovs/db/data.py b/python/ovs/db/data.py
Open vSwitch CI 3f9b5c
index 2a2102d6be..99bf80ed62 100644
Open vSwitch CI 3f9b5c
--- a/python/ovs/db/data.py
Open vSwitch CI 3f9b5c
+++ b/python/ovs/db/data.py
Open vSwitch CI 3f9b5c
@@ -204,7 +204,7 @@ class Atom(object):
Open vSwitch CI 3f9b5c
             else:
Open vSwitch CI 3f9b5c
                 return '.boolean = false'
Open vSwitch CI 3f9b5c
         elif self.type == ovs.db.types.StringType:
Open vSwitch CI 3f9b5c
-            return '.string = "%s"' % escapeCString(self.value)
Open vSwitch CI 3f9b5c
+            return '.s = %s' % escapeCString(self.value)
Open vSwitch CI 3f9b5c
         elif self.type == ovs.db.types.UuidType:
Open vSwitch CI 3f9b5c
             return '.uuid = %s' % ovs.ovsuuid.to_c_assignment(self.value)
Open vSwitch CI 3f9b5c
 
Open vSwitch CI 3f9b5c
@@ -563,16 +563,41 @@ class Datum(object):
Open vSwitch CI 3f9b5c
         if n == 0:
Open vSwitch CI 3f9b5c
             return ["static struct ovsdb_datum %s = { .n = 0 };"]
Open vSwitch CI 3f9b5c
 
Open vSwitch CI 3f9b5c
-        s = ["static union ovsdb_atom %s_keys[%d] = {" % (name, n)]
Open vSwitch CI 3f9b5c
-        for key in sorted(self.values):
Open vSwitch CI 3f9b5c
-            s += ["    { %s }," % key.cInitAtom(key)]
Open vSwitch CI 3f9b5c
-        s += ["};"]
Open vSwitch CI 3f9b5c
+        s = []
Open vSwitch CI 3f9b5c
+        if self.type.key.type == ovs.db.types.StringType:
Open vSwitch CI 3f9b5c
+            s += ["static struct ovsdb_atom_string %s_key_strings[%d] = {"
Open vSwitch CI 3f9b5c
+                  % (name, n)]
Open vSwitch CI 3f9b5c
+            for key in sorted(self.values):
Open vSwitch CI 3f9b5c
+                s += ['    { .string = "%s", .n_refs = 2 },'
Open vSwitch CI 3f9b5c
+                      % escapeCString(key.value)]
Open vSwitch CI 3f9b5c
+            s += ["};"]
Open vSwitch CI 3f9b5c
+            s += ["static union ovsdb_atom %s_keys[%d] = {" % (name, n)]
Open vSwitch CI 3f9b5c
+            for i in range(n):
Open vSwitch CI 3f9b5c
+                s += ["    { .s = &%s_key_strings[%d] }," % (name, i)]
Open vSwitch CI 3f9b5c
+            s += ["};"]
Open vSwitch CI 3f9b5c
+        else:
Open vSwitch CI 3f9b5c
+            s = ["static union ovsdb_atom %s_keys[%d] = {" % (name, n)]
Open vSwitch CI 3f9b5c
+            for key in sorted(self.values):
Open vSwitch CI 3f9b5c
+                s += ["    { %s }," % key.cInitAtom(key)]
Open vSwitch CI 3f9b5c
+            s += ["};"]
Open vSwitch CI 3f9b5c
 
Open vSwitch CI 3f9b5c
         if self.type.value:
Open vSwitch CI 3f9b5c
-            s = ["static union ovsdb_atom %s_values[%d] = {" % (name, n)]
Open vSwitch CI 3f9b5c
-            for k, v in sorted(self.values.items()):
Open vSwitch CI 3f9b5c
-                s += ["    { %s }," % v.cInitAtom(v)]
Open vSwitch CI 3f9b5c
-            s += ["};"]
Open vSwitch CI 3f9b5c
+            if self.type.value.type == ovs.db.types.StringType:
Open vSwitch CI 3f9b5c
+                s += ["static struct ovsdb_atom_string %s_val_strings[%d] = {"
Open vSwitch CI 3f9b5c
+                      % (name, n)]
Open vSwitch CI 3f9b5c
+                for k, v in sorted(self.values):
Open vSwitch CI 3f9b5c
+                    s += ['    { .string = "%s", .n_refs = 2 },'
Open vSwitch CI 3f9b5c
+                          % escapeCString(v.value)]
Open vSwitch CI 3f9b5c
+                s += ["};"]
Open vSwitch CI 3f9b5c
+                s += ["static union ovsdb_atom %s_values[%d] = {" % (name, n)]
Open vSwitch CI 3f9b5c
+                for i in range(n):
Open vSwitch CI 3f9b5c
+                    s += ["    { .s = &%s_val_strings[%d] }," % (name, i)]
Open vSwitch CI 3f9b5c
+                s += ["};"]
Open vSwitch CI 3f9b5c
+            else:
Open vSwitch CI 3f9b5c
+                s = ["static union ovsdb_atom %s_values[%d] = {" % (name, n)]
Open vSwitch CI 3f9b5c
+                for k, v in sorted(self.values.items()):
Open vSwitch CI 3f9b5c
+                    s += ["    { %s }," % v.cInitAtom(v)]
Open vSwitch CI 3f9b5c
+                s += ["};"]
Open vSwitch CI 3f9b5c
 
Open vSwitch CI 3f9b5c
         s += ["static struct ovsdb_datum %s = {" % name]
Open vSwitch CI 3f9b5c
         s += ["    .n = %d," % n]
Open vSwitch CI 3f9b5c
diff --git a/python/ovs/db/idl.py b/python/ovs/db/idl.py
Open vSwitch CI 3f9b5c
index ecae5e1432..87ee06cdef 100644
Open vSwitch CI 3f9b5c
--- a/python/ovs/db/idl.py
Open vSwitch CI 3f9b5c
+++ b/python/ovs/db/idl.py
Open vSwitch CI 3f9b5c
@@ -1505,6 +1505,11 @@ class Transaction(object):
Open vSwitch CI 3f9b5c
         if self != self.idl.txn:
Open vSwitch CI 3f9b5c
             return self._status
Open vSwitch CI 3f9b5c
 
Open vSwitch CI 3f9b5c
+        if self.idl.state != Idl.IDL_S_MONITORING:
Open vSwitch CI 3f9b5c
+            self._status = Transaction.TRY_AGAIN
Open vSwitch CI 3f9b5c
+            self.__disassemble()
Open vSwitch CI 3f9b5c
+            return self._status
Open vSwitch CI 3f9b5c
+
Open vSwitch CI 3f9b5c
         # If we need a lock but don't have it, give up quickly.
Open vSwitch CI 3f9b5c
         if self.idl.lock_name and not self.idl.has_lock:
Open vSwitch CI 3f9b5c
             self._status = Transaction.NOT_LOCKED
Open vSwitch CI 3f9b5c
diff --git a/python/ovs/db/types.py b/python/ovs/db/types.py
Open vSwitch CI 3f9b5c
index 626ae8fc44..3318a3b6f8 100644
Open vSwitch CI 3f9b5c
--- a/python/ovs/db/types.py
Open vSwitch CI 3f9b5c
+++ b/python/ovs/db/types.py
Open vSwitch CI 3f9b5c
@@ -48,6 +48,16 @@ class AtomicType(object):
Open vSwitch CI 3f9b5c
     def to_string(self):
Open vSwitch CI 3f9b5c
         return self.name
Open vSwitch CI 3f9b5c
 
Open vSwitch CI 3f9b5c
+    def to_rvalue_string(self):
Open vSwitch CI 3f9b5c
+        if self == StringType:
Open vSwitch CI 3f9b5c
+            return 's->' + self.name
Open vSwitch CI 3f9b5c
+        return self.name
Open vSwitch CI 3f9b5c
+
Open vSwitch CI 3f9b5c
+    def to_lvalue_string(self):
Open vSwitch CI 3f9b5c
+        if self == StringType:
Open vSwitch CI 3f9b5c
+            return 's'
Open vSwitch CI 3f9b5c
+        return self.name
Open vSwitch CI 3f9b5c
+
Open vSwitch CI 3f9b5c
     def to_json(self):
Open vSwitch CI 3f9b5c
         return self.name
Open vSwitch CI 3f9b5c
 
Open vSwitch CI 3f9b5c
@@ -373,18 +383,7 @@ class BaseType(object):
Open vSwitch CI 3f9b5c
                 return "%(dst)s = *%(src)s;" % args
Open vSwitch CI 3f9b5c
             return ("%(dst)s = %(src)s->header_.uuid;") % args
Open vSwitch CI 3f9b5c
         elif self.type == StringType:
Open vSwitch CI 3f9b5c
-            return "%(dst)s = xstrdup(%(src)s);" % args
Open vSwitch CI 3f9b5c
-        else:
Open vSwitch CI 3f9b5c
-            return "%(dst)s = %(src)s;" % args
Open vSwitch CI 3f9b5c
-
Open vSwitch CI 3f9b5c
-    def assign_c_value_casting_away_const(self, dst, src, refTable=True):
Open vSwitch CI 3f9b5c
-        args = {'dst': dst, 'src': src}
Open vSwitch CI 3f9b5c
-        if self.ref_table_name:
Open vSwitch CI 3f9b5c
-            if not refTable:
Open vSwitch CI 3f9b5c
-                return "%(dst)s = *%(src)s;" % args
Open vSwitch CI 3f9b5c
-            return ("%(dst)s = %(src)s->header_.uuid;") % args
Open vSwitch CI 3f9b5c
-        elif self.type == StringType:
Open vSwitch CI 3f9b5c
-            return "%(dst)s = CONST_CAST(char *, %(src)s);" % args
Open vSwitch CI 3f9b5c
+            return "%(dst)s = ovsdb_atom_string_create(%(src)s);" % args
Open vSwitch CI 3f9b5c
         else:
Open vSwitch CI 3f9b5c
             return "%(dst)s = %(src)s;" % args
Open vSwitch CI 3f9b5c
 
Open vSwitch CI ee63f1
diff --git a/python/ovs/poller.py b/python/ovs/poller.py
Open vSwitch CI ee63f1
index 3624ec8655..157719c3a4 100644
Open vSwitch CI ee63f1
--- a/python/ovs/poller.py
Open vSwitch CI ee63f1
+++ b/python/ovs/poller.py
Open vSwitch CI ee63f1
@@ -26,9 +26,9 @@ if sys.platform == "win32":
Open vSwitch CI ee63f1
     import ovs.winutils as winutils
Open vSwitch CI ee63f1
 
Open vSwitch CI ee63f1
 try:
Open vSwitch CI ee63f1
-    from OpenSSL import SSL
Open vSwitch CI ee63f1
+    import ssl
Open vSwitch CI ee63f1
 except ImportError:
Open vSwitch CI ee63f1
-    SSL = None
Open vSwitch CI ee63f1
+    ssl = None
Open vSwitch CI ee63f1
 
Open vSwitch CI ee63f1
 try:
Open vSwitch CI ee63f1
     from eventlet import patcher as eventlet_patcher
Open vSwitch CI ee63f1
@@ -73,7 +73,7 @@ class _SelectSelect(object):
Open vSwitch CI ee63f1
     def register(self, fd, events):
Open vSwitch CI ee63f1
         if isinstance(fd, socket.socket):
Open vSwitch CI ee63f1
             fd = fd.fileno()
Open vSwitch CI ee63f1
-        if SSL and isinstance(fd, SSL.Connection):
Open vSwitch CI ee63f1
+        if ssl and isinstance(fd, ssl.SSLSocket):
Open vSwitch CI ee63f1
             fd = fd.fileno()
Open vSwitch CI ee63f1
 
Open vSwitch CI ee63f1
         if sys.platform != 'win32':
Open vSwitch CI ee63f1
diff --git a/python/ovs/socket_util.py b/python/ovs/socket_util.py
Open vSwitch CI ee63f1
index 3faa64e9d7..651012bf06 100644
Open vSwitch CI ee63f1
--- a/python/ovs/socket_util.py
Open vSwitch CI ee63f1
+++ b/python/ovs/socket_util.py
Open vSwitch CI ee63f1
@@ -222,8 +222,7 @@ def inet_parse_active(target, default_port):
Open vSwitch CI ee63f1
     return (host_name, port)
Open vSwitch CI ee63f1
 
Open vSwitch CI ee63f1
 
Open vSwitch CI ee63f1
-def inet_open_active(style, target, default_port, dscp):
Open vSwitch CI ee63f1
-    address = inet_parse_active(target, default_port)
Open vSwitch CI ee63f1
+def inet_create_socket_active(style, address):
Open vSwitch CI ee63f1
     try:
Open vSwitch CI ee63f1
         is_addr_inet = is_valid_ipv4_address(address[0])
Open vSwitch CI ee63f1
         if is_addr_inet:
Open vSwitch CI ee63f1
@@ -235,23 +234,32 @@ def inet_open_active(style, target, default_port, dscp):
Open vSwitch CI ee63f1
     except socket.error as e:
Open vSwitch CI ee63f1
         return get_exception_errno(e), None
Open vSwitch CI ee63f1
 
Open vSwitch CI ee63f1
+    return family, sock
Open vSwitch CI ee63f1
+
Open vSwitch CI ee63f1
+
Open vSwitch CI ee63f1
+def inet_connect_active(sock, address, family, dscp):
Open vSwitch CI ee63f1
     try:
Open vSwitch CI ee63f1
         set_nonblocking(sock)
Open vSwitch CI ee63f1
         set_dscp(sock, family, dscp)
Open vSwitch CI ee63f1
-        try:
Open vSwitch CI ee63f1
-            sock.connect(address)
Open vSwitch CI ee63f1
-        except socket.error as e:
Open vSwitch CI ee63f1
-            error = get_exception_errno(e)
Open vSwitch CI ee63f1
-            if sys.platform == 'win32' and error == errno.WSAEWOULDBLOCK:
Open vSwitch CI ee63f1
-                # WSAEWOULDBLOCK would be the equivalent on Windows
Open vSwitch CI ee63f1
-                # for EINPROGRESS on Unix.
Open vSwitch CI ee63f1
-                error = errno.EINPROGRESS
Open vSwitch CI ee63f1
-            if error != errno.EINPROGRESS:
Open vSwitch CI ee63f1
-                raise
Open vSwitch CI ee63f1
-        return 0, sock
Open vSwitch CI ee63f1
+        error = sock.connect_ex(address)
Open vSwitch CI ee63f1
+        if error not in (0, errno.EINPROGRESS, errno.EWOULDBLOCK):
Open vSwitch CI ee63f1
+            sock.close()
Open vSwitch CI ee63f1
+            return error
Open vSwitch CI ee63f1
+        return 0
Open vSwitch CI ee63f1
     except socket.error as e:
Open vSwitch CI ee63f1
         sock.close()
Open vSwitch CI ee63f1
-        return get_exception_errno(e), None
Open vSwitch CI ee63f1
+        return get_exception_errno(e)
Open vSwitch CI ee63f1
+
Open vSwitch CI ee63f1
+
Open vSwitch CI ee63f1
+def inet_open_active(style, target, default_port, dscp):
Open vSwitch CI ee63f1
+    address = inet_parse_active(target, default_port)
Open vSwitch CI ee63f1
+    family, sock = inet_create_socket_active(style, address)
Open vSwitch CI ee63f1
+    if sock is None:
Open vSwitch CI ee63f1
+        return family, sock
Open vSwitch CI ee63f1
+    error = inet_connect_active(sock, address, family, dscp)
Open vSwitch CI ee63f1
+    if error:
Open vSwitch CI ee63f1
+        return error, None
Open vSwitch CI ee63f1
+    return 0, sock
Open vSwitch CI ee63f1
 
Open vSwitch CI ee63f1
 
Open vSwitch CI ee63f1
 def get_exception_errno(e):
Open vSwitch CI ee63f1
diff --git a/python/ovs/stream.py b/python/ovs/stream.py
Open vSwitch CI ee63f1
index f5a520862c..ac5b0fd0c6 100644
Open vSwitch CI ee63f1
--- a/python/ovs/stream.py
Open vSwitch CI ee63f1
+++ b/python/ovs/stream.py
Open vSwitch CI ee63f1
@@ -22,9 +22,9 @@ import ovs.socket_util
Open vSwitch CI ee63f1
 import ovs.vlog
Open vSwitch CI ee63f1
 
Open vSwitch CI ee63f1
 try:
Open vSwitch CI ee63f1
-    from OpenSSL import SSL
Open vSwitch CI ee63f1
+    import ssl
Open vSwitch CI ee63f1
 except ImportError:
Open vSwitch CI ee63f1
-    SSL = None
Open vSwitch CI ee63f1
+    ssl = None
Open vSwitch CI ee63f1
 
Open vSwitch CI ee63f1
 if sys.platform == 'win32':
Open vSwitch CI ee63f1
     import ovs.winutils as winutils
Open vSwitch CI ee63f1
@@ -322,6 +322,12 @@ class Stream(object):
Open vSwitch CI ee63f1
         The recv function will not block waiting for data to arrive.  If no
Open vSwitch CI ee63f1
         data have been received, it returns (errno.EAGAIN, "") immediately."""
Open vSwitch CI ee63f1
 
Open vSwitch CI ee63f1
+        try:
Open vSwitch CI ee63f1
+            return self._recv(n)
Open vSwitch CI ee63f1
+        except socket.error as e:
Open vSwitch CI ee63f1
+            return (ovs.socket_util.get_exception_errno(e), "")
Open vSwitch CI ee63f1
+
Open vSwitch CI ee63f1
+    def _recv(self, n):
Open vSwitch CI ee63f1
         retval = self.connect()
Open vSwitch CI ee63f1
         if retval != 0:
Open vSwitch CI ee63f1
             return (retval, "")
Open vSwitch CI ee63f1
@@ -331,10 +337,7 @@ class Stream(object):
Open vSwitch CI ee63f1
         if sys.platform == 'win32' and self.socket is None:
Open vSwitch CI ee63f1
             return self.__recv_windows(n)
Open vSwitch CI ee63f1
 
Open vSwitch CI ee63f1
-        try:
Open vSwitch CI ee63f1
-            return (0, self.socket.recv(n))
Open vSwitch CI ee63f1
-        except socket.error as e:
Open vSwitch CI ee63f1
-            return (ovs.socket_util.get_exception_errno(e), "")
Open vSwitch CI ee63f1
+        return (0, self.socket.recv(n))
Open vSwitch CI ee63f1
 
Open vSwitch CI ee63f1
     def __recv_windows(self, n):
Open vSwitch CI ee63f1
         if self._read_pending:
Open vSwitch CI ee63f1
@@ -396,6 +399,12 @@ class Stream(object):
Open vSwitch CI ee63f1
         Will not block.  If no bytes can be immediately accepted for
Open vSwitch CI ee63f1
         transmission, returns -errno.EAGAIN immediately."""
Open vSwitch CI ee63f1
 
Open vSwitch CI ee63f1
+        try:
Open vSwitch CI ee63f1
+            return self._send(buf)
Open vSwitch CI ee63f1
+        except socket.error as e:
Open vSwitch CI ee63f1
+            return -ovs.socket_util.get_exception_errno(e)
Open vSwitch CI ee63f1
+
Open vSwitch CI ee63f1
+    def _send(self, buf):
Open vSwitch CI ee63f1
         retval = self.connect()
Open vSwitch CI ee63f1
         if retval != 0:
Open vSwitch CI ee63f1
             return -retval
Open vSwitch CI ee63f1
@@ -409,10 +418,7 @@ class Stream(object):
Open vSwitch CI ee63f1
         if sys.platform == 'win32' and self.socket is None:
Open vSwitch CI ee63f1
             return self.__send_windows(buf)
Open vSwitch CI ee63f1
 
Open vSwitch CI ee63f1
-        try:
Open vSwitch CI ee63f1
-            return self.socket.send(buf)
Open vSwitch CI ee63f1
-        except socket.error as e:
Open vSwitch CI ee63f1
-            return -ovs.socket_util.get_exception_errno(e)
Open vSwitch CI ee63f1
+        return self.socket.send(buf)
Open vSwitch CI ee63f1
 
Open vSwitch CI ee63f1
     def __send_windows(self, buf):
Open vSwitch CI ee63f1
         if self._write_pending:
Open vSwitch CI ee63f1
@@ -769,35 +775,42 @@ class SSLStream(Stream):
Open vSwitch CI ee63f1
     def check_connection_completion(sock):
Open vSwitch CI ee63f1
         try:
Open vSwitch CI ee63f1
             return Stream.check_connection_completion(sock)
Open vSwitch CI ee63f1
-        except SSL.SysCallError as e:
Open vSwitch CI ee63f1
+        except ssl.SSLSyscallError as e:
Open vSwitch CI ee63f1
             return ovs.socket_util.get_exception_errno(e)
Open vSwitch CI ee63f1
 
Open vSwitch CI ee63f1
     @staticmethod
Open vSwitch CI ee63f1
     def needs_probes():
Open vSwitch CI ee63f1
         return True
Open vSwitch CI ee63f1
 
Open vSwitch CI ee63f1
-    @staticmethod
Open vSwitch CI ee63f1
-    def verify_cb(conn, cert, errnum, depth, ok):
Open vSwitch CI ee63f1
-        return ok
Open vSwitch CI ee63f1
-
Open vSwitch CI ee63f1
     @staticmethod
Open vSwitch CI ee63f1
     def _open(suffix, dscp):
Open vSwitch CI ee63f1
-        error, sock = TCPStream._open(suffix, dscp)
Open vSwitch CI ee63f1
-        if error:
Open vSwitch CI ee63f1
-            return error, None
Open vSwitch CI ee63f1
+        address = ovs.socket_util.inet_parse_active(suffix, 0)
Open vSwitch CI ee63f1
+        family, sock = ovs.socket_util.inet_create_socket_active(
Open vSwitch CI ee63f1
+                socket.SOCK_STREAM, address)
Open vSwitch CI ee63f1
+        if sock is None:
Open vSwitch CI ee63f1
+            return family, sock
Open vSwitch CI ee63f1
 
Open vSwitch CI ee63f1
         # Create an SSL context
Open vSwitch CI ee63f1
-        ctx = SSL.Context(SSL.SSLv23_METHOD)
Open vSwitch CI ee63f1
-        ctx.set_verify(SSL.VERIFY_PEER, SSLStream.verify_cb)
Open vSwitch CI ee63f1
-        ctx.set_options(SSL.OP_NO_SSLv2 | SSL.OP_NO_SSLv3)
Open vSwitch CI ee63f1
+        ctx = ssl.SSLContext(ssl.PROTOCOL_SSLv23)
Open vSwitch CI ee63f1
+        ctx.verify_mode = ssl.CERT_REQUIRED
Open vSwitch CI ee63f1
+        ctx.options |= ssl.OP_NO_SSLv2
Open vSwitch CI ee63f1
+        ctx.options |= ssl.OP_NO_SSLv3
Open vSwitch CI ee63f1
         # If the client has not set the SSL configuration files
Open vSwitch CI ee63f1
         # exception would be raised.
Open vSwitch CI ee63f1
-        ctx.use_privatekey_file(Stream._SSL_private_key_file)
Open vSwitch CI ee63f1
-        ctx.use_certificate_file(Stream._SSL_certificate_file)
Open vSwitch CI ee63f1
         ctx.load_verify_locations(Stream._SSL_ca_cert_file)
Open vSwitch CI ee63f1
+        ctx.load_cert_chain(Stream._SSL_certificate_file,
Open vSwitch CI ee63f1
+                            Stream._SSL_private_key_file)
Open vSwitch CI ee63f1
+        ssl_sock = ctx.wrap_socket(sock, do_handshake_on_connect=False)
Open vSwitch CI ee63f1
 
Open vSwitch CI ee63f1
-        ssl_sock = SSL.Connection(ctx, sock)
Open vSwitch CI ee63f1
-        ssl_sock.set_connect_state()
Open vSwitch CI ee63f1
+        # Connect
Open vSwitch CI ee63f1
+        error = ovs.socket_util.inet_connect_active(ssl_sock, address, family,
Open vSwitch CI ee63f1
+                                                    dscp)
Open vSwitch CI ee63f1
+        if not error:
Open vSwitch CI ee63f1
+            try:
Open vSwitch CI ee63f1
+                ssl_sock.setsockopt(socket.IPPROTO_TCP, socket.TCP_NODELAY, 1)
Open vSwitch CI ee63f1
+            except socket.error as e:
Open vSwitch CI ee63f1
+                ssl_sock.close()
Open vSwitch CI ee63f1
+                return ovs.socket_util.get_exception_errno(e), None
Open vSwitch CI ee63f1
         return error, ssl_sock
Open vSwitch CI ee63f1
 
Open vSwitch CI ee63f1
     def connect(self):
Open vSwitch CI ee63f1
@@ -809,40 +822,44 @@ class SSLStream(Stream):
Open vSwitch CI ee63f1
         # TCP Connection is successful. Now do the SSL handshake
Open vSwitch CI ee63f1
         try:
Open vSwitch CI ee63f1
             self.socket.do_handshake()
Open vSwitch CI ee63f1
-        except SSL.WantReadError:
Open vSwitch CI ee63f1
+        except ssl.SSLWantReadError:
Open vSwitch CI ee63f1
             return errno.EAGAIN
Open vSwitch CI ee63f1
-        except SSL.SysCallError as e:
Open vSwitch CI ee63f1
+        except ssl.SSLSyscallError as e:
Open vSwitch CI ee63f1
             return ovs.socket_util.get_exception_errno(e)
Open vSwitch CI ee63f1
 
Open vSwitch CI ee63f1
         return 0
Open vSwitch CI ee63f1
 
Open vSwitch CI ee63f1
     def recv(self, n):
Open vSwitch CI ee63f1
         try:
Open vSwitch CI ee63f1
-            return super(SSLStream, self).recv(n)
Open vSwitch CI ee63f1
-        except SSL.WantReadError:
Open vSwitch CI ee63f1
+            return super(SSLStream, self)._recv(n)
Open vSwitch CI ee63f1
+        except ssl.SSLWantReadError:
Open vSwitch CI ee63f1
             return (errno.EAGAIN, "")
Open vSwitch CI ee63f1
-        except SSL.SysCallError as e:
Open vSwitch CI ee63f1
+        except ssl.SSLSyscallError as e:
Open vSwitch CI ee63f1
             return (ovs.socket_util.get_exception_errno(e), "")
Open vSwitch CI ee63f1
-        except SSL.ZeroReturnError:
Open vSwitch CI ee63f1
+        except ssl.SSLZeroReturnError:
Open vSwitch CI ee63f1
             return (0, "")
Open vSwitch CI ee63f1
+        except socket.error as e:
Open vSwitch CI ee63f1
+            return (ovs.socket_util.get_exception_errno(e), "")
Open vSwitch CI ee63f1
 
Open vSwitch CI ee63f1
     def send(self, buf):
Open vSwitch CI ee63f1
         try:
Open vSwitch CI ee63f1
-            return super(SSLStream, self).send(buf)
Open vSwitch CI ee63f1
-        except SSL.WantWriteError:
Open vSwitch CI ee63f1
+            return super(SSLStream, self)._send(buf)
Open vSwitch CI ee63f1
+        except ssl.SSLWantWriteError:
Open vSwitch CI ee63f1
             return -errno.EAGAIN
Open vSwitch CI ee63f1
-        except SSL.SysCallError as e:
Open vSwitch CI ee63f1
+        except ssl.SSLSyscallError as e:
Open vSwitch CI ee63f1
+            return -ovs.socket_util.get_exception_errno(e)
Open vSwitch CI ee63f1
+        except socket.error as e:
Open vSwitch CI ee63f1
             return -ovs.socket_util.get_exception_errno(e)
Open vSwitch CI ee63f1
 
Open vSwitch CI ee63f1
     def close(self):
Open vSwitch CI ee63f1
         if self.socket:
Open vSwitch CI ee63f1
             try:
Open vSwitch CI ee63f1
-                self.socket.shutdown()
Open vSwitch CI ee63f1
-            except SSL.Error:
Open vSwitch CI ee63f1
+                self.socket.shutdown(socket.SHUT_RDWR)
Open vSwitch CI ee63f1
+            except socket.error:
Open vSwitch CI ee63f1
                 pass
Open vSwitch CI ee63f1
         return super(SSLStream, self).close()
Open vSwitch CI ee63f1
 
Open vSwitch CI ee63f1
 
Open vSwitch CI ee63f1
-if SSL:
Open vSwitch CI ee63f1
+if ssl:
Open vSwitch CI ee63f1
     # Register SSL only if the OpenSSL module is available
Open vSwitch CI ee63f1
     Stream.register_method("ssl", SSLStream)
110336
diff --git a/tests/ofproto-dpif.at b/tests/ofproto-dpif.at
110336
index 956a69e1fa..1dad6f62c6 100644
110336
--- a/tests/ofproto-dpif.at
110336
+++ b/tests/ofproto-dpif.at
110336
@@ -9695,6 +9695,26 @@ OFPST_TABLE reply (OF1.3) (xid=0x2):
110336
 OVS_VSWITCHD_STOP
110336
 AT_CLEANUP
110336
 
110336
+AT_SETUP([ofproto-dpif packet-out table meter drop])
110336
+OVS_VSWITCHD_START
110336
+add_of_ports br0 1 2
110336
+
110336
+AT_CHECK([ovs-ofctl -O OpenFlow13 add-meter br0 'meter=1 pktps bands=type=drop rate=1'])
110336
+AT_CHECK([ovs-ofctl -O OpenFlow13 add-flow br0 'in_port=1 action=meter:1,output:2'])
110336
+
110336
+ovs-ofctl -O OpenFlow13 packet-out br0 "in_port=1 packet=50540000000a50540000000908004500001c000000000011a4cd0a0101010a0101020001000400080000 actions=resubmit(,0)"
110336
+ovs-ofctl -O OpenFlow13 packet-out br0 "in_port=1 packet=50540000000a50540000000908004500001c000000000011a4cd0a0101010a0101020001000400080000 actions=resubmit(,0)"
110336
+
110336
+# Check that vswitchd hasn't crashed by dumping the meter added above
110336
+AT_CHECK([ovs-ofctl -O OpenFlow13 dump-meters br0 | ofctl_strip], [0], [dnl
110336
+OFPST_METER_CONFIG reply (OF1.3):
110336
+meter=1 pktps bands=
110336
+type=drop rate=1
110336
+])
110336
+
110336
+OVS_VSWITCHD_STOP
110336
+AT_CLEANUP
110336
+
110336
 AT_SETUP([ofproto-dpif - ICMPv6])
110336
 OVS_VSWITCHD_START
110336
 add_of_ports br0 1
Open vSwitch CI 3f9b5c
diff --git a/tests/ovsdb-data.at b/tests/ovsdb-data.at
Open vSwitch CI 3f9b5c
index 8cd2a26cb3..25c6acdac6 100644
Open vSwitch CI 3f9b5c
--- a/tests/ovsdb-data.at
Open vSwitch CI 3f9b5c
+++ b/tests/ovsdb-data.at
Open vSwitch CI 3f9b5c
@@ -846,18 +846,21 @@ OVSDB_CHECK_POSITIVE([generate and apply diff -- integer],
Open vSwitch CI 3f9b5c
   [[diff-data '["integer"]' '[0]' '[2]']],
Open vSwitch CI 3f9b5c
   [[diff: 2
Open vSwitch CI 3f9b5c
 apply diff: 2
Open vSwitch CI 3f9b5c
+apply diff in place: 2
Open vSwitch CI 3f9b5c
 OK]])
Open vSwitch CI 3f9b5c
 
Open vSwitch CI 3f9b5c
 OVSDB_CHECK_POSITIVE([generate and apply diff -- boolean],
Open vSwitch CI 3f9b5c
   [[diff-data '["boolean"]' '[true]' '[false]']],
Open vSwitch CI 3f9b5c
   [[diff: false
Open vSwitch CI 3f9b5c
 apply diff: false
Open vSwitch CI 3f9b5c
+apply diff in place: false
Open vSwitch CI 3f9b5c
 OK]])
Open vSwitch CI 3f9b5c
 
Open vSwitch CI 3f9b5c
 OVSDB_CHECK_POSITIVE([generate and apply diff -- string],
Open vSwitch CI 3f9b5c
   [[diff-data '["string"]' '["AAA"]' '["BBB"]']],
Open vSwitch CI 3f9b5c
   [[diff: "BBB"
Open vSwitch CI 3f9b5c
 apply diff: "BBB"
Open vSwitch CI 3f9b5c
+apply diff in place: "BBB"
Open vSwitch CI 3f9b5c
 OK]])
Open vSwitch CI 3f9b5c
 
Open vSwitch CI 3f9b5c
 dnl Test set modifications.
Open vSwitch CI 3f9b5c
@@ -870,15 +873,19 @@ OVSDB_CHECK_POSITIVE([generate and apply diff -- set],
Open vSwitch CI 3f9b5c
   ]],
Open vSwitch CI 3f9b5c
   [[diff: ["set",[0,2]]
Open vSwitch CI 3f9b5c
 apply diff: ["set",[1,2]]
Open vSwitch CI 3f9b5c
+apply diff in place: ["set",[1,2]]
Open vSwitch CI 3f9b5c
 OK
Open vSwitch CI 3f9b5c
 diff: 0
Open vSwitch CI 3f9b5c
 apply diff: 1
Open vSwitch CI 3f9b5c
+apply diff in place: 1
Open vSwitch CI 3f9b5c
 OK
Open vSwitch CI 3f9b5c
 diff: ["set",[0,1]]
Open vSwitch CI 3f9b5c
 apply diff: ["set",[0,1]]
Open vSwitch CI 3f9b5c
+apply diff in place: ["set",[0,1]]
Open vSwitch CI 3f9b5c
 OK
Open vSwitch CI 3f9b5c
 diff: ["set",[0,1]]
Open vSwitch CI 3f9b5c
 apply diff: ["set",[]]
Open vSwitch CI 3f9b5c
+apply diff in place: ["set",[]]
Open vSwitch CI 3f9b5c
 OK]])
Open vSwitch CI 3f9b5c
 
Open vSwitch CI 3f9b5c
 dnl Test set modifications causes data to violate set size constrain.
Open vSwitch CI 3f9b5c
@@ -898,18 +905,23 @@ OVSDB_CHECK_POSITIVE([generate and apply diff -- map],
Open vSwitch CI 3f9b5c
   ]],
Open vSwitch CI 3f9b5c
   [[diff: ["map",[["2 gills","1 chopin"],["2 pints","1 quart"]]]
Open vSwitch CI 3f9b5c
 apply diff: ["map",[["2 pints","1 quart"]]]
Open vSwitch CI 3f9b5c
+apply diff in place: ["map",[["2 pints","1 quart"]]]
Open vSwitch CI 3f9b5c
 OK
Open vSwitch CI 3f9b5c
 diff: ["map",[]]
Open vSwitch CI 3f9b5c
 apply diff: ["map",[["2 gills","1 chopin"]]]
Open vSwitch CI 3f9b5c
+apply diff in place: ["map",[["2 gills","1 chopin"]]]
Open vSwitch CI 3f9b5c
 OK
Open vSwitch CI 3f9b5c
 diff: ["map",[["2 gills","1 chopin"]]]
Open vSwitch CI 3f9b5c
 apply diff: ["map",[]]
Open vSwitch CI 3f9b5c
+apply diff in place: ["map",[]]
Open vSwitch CI 3f9b5c
 OK
Open vSwitch CI 3f9b5c
 diff: ["map",[["2 pints","1 quart"]]]
Open vSwitch CI 3f9b5c
 apply diff: ["map",[["2 pints","1 quart"]]]
Open vSwitch CI 3f9b5c
+apply diff in place: ["map",[["2 pints","1 quart"]]]
Open vSwitch CI 3f9b5c
 OK
Open vSwitch CI 3f9b5c
 diff: ["map",[["2 gills","1 gallon"]]]
Open vSwitch CI 3f9b5c
 apply diff: ["map",[["2 gills","1 gallon"]]]
Open vSwitch CI 3f9b5c
+apply diff in place: ["map",[["2 gills","1 gallon"]]]
Open vSwitch CI 3f9b5c
 OK]])
Open vSwitch CI 3f9b5c
 
Open vSwitch CI 3f9b5c
 OVSDB_CHECK_NEGATIVE([generate and apply diff with map -- size error],
Open vSwitch CI ee63f1
diff --git a/tests/ovsdb-idl.at b/tests/ovsdb-idl.at
Open vSwitch CI ee63f1
index 1386f13770..cd28a587c6 100644
Open vSwitch CI ee63f1
--- a/tests/ovsdb-idl.at
Open vSwitch CI ee63f1
+++ b/tests/ovsdb-idl.at
Open vSwitch CI ee63f1
@@ -225,7 +225,7 @@ m4_define([OVSDB_CHECK_IDL_TCP6_MULTIPLE_REMOTES_PY],
Open vSwitch CI ee63f1
 m4_define([OVSDB_CHECK_IDL_SSL_PY],
Open vSwitch CI ee63f1
   [AT_SETUP([$1 - Python3 - SSL])
Open vSwitch CI ee63f1
    AT_SKIP_IF([test "$HAVE_OPENSSL" = no])
Open vSwitch CI ee63f1
-   $PYTHON3 -c "import OpenSSL.SSL"
Open vSwitch CI ee63f1
+   $PYTHON3 -c "import ssl"
Open vSwitch CI ee63f1
    SSL_PRESENT=$?
Open vSwitch CI ee63f1
    AT_SKIP_IF([test $SSL_PRESENT != 0])
Open vSwitch CI ee63f1
    AT_KEYWORDS([ovsdb server idl positive Python with ssl socket $5])
Open vSwitch CI 483c2c
diff --git a/tests/ovsdb-server.at b/tests/ovsdb-server.at
Open vSwitch CI 483c2c
index ac243d6a79..2b742f78b0 100644
Open vSwitch CI 483c2c
--- a/tests/ovsdb-server.at
Open vSwitch CI 483c2c
+++ b/tests/ovsdb-server.at
Open vSwitch CI 483c2c
@@ -1228,6 +1228,69 @@ AT_CHECK([test $logged_updates -lt $logged_nonblock_updates])
Open vSwitch CI 483c2c
 AT_CHECK_UNQUOTED([ovs-vsctl get open_vswitch . system_version], [0],
Open vSwitch CI 483c2c
   [xyzzy$counter
Open vSwitch CI 483c2c
 ])
Open vSwitch CI 483c2c
+OVS_APP_EXIT_AND_WAIT([ovsdb-server])
Open vSwitch CI 483c2c
+AT_CLEANUP
Open vSwitch CI 483c2c
+
Open vSwitch CI 483c2c
+AT_SETUP([ovsdb-server transaction history size])
Open vSwitch CI 483c2c
+on_exit 'kill `cat *.pid`'
Open vSwitch CI 483c2c
+
Open vSwitch CI 483c2c
+dnl Start an ovsdb-server with the clustered vswitchd schema.
Open vSwitch CI 483c2c
+AT_CHECK([ovsdb-tool create-cluster db dnl
Open vSwitch CI 483c2c
+            $abs_top_srcdir/vswitchd/vswitch.ovsschema unix:s1.raft],
Open vSwitch CI 483c2c
+         [0], [ignore], [ignore])
Open vSwitch CI 483c2c
+AT_CHECK([ovsdb-server --detach --no-chdir --pidfile dnl
Open vSwitch CI 483c2c
+            --log-file --remote=punix:db.sock db],
Open vSwitch CI 483c2c
+         [0], [ignore], [ignore])
Open vSwitch CI 483c2c
+AT_CHECK([ovs-vsctl --no-wait init])
Open vSwitch CI 483c2c
+
Open vSwitch CI 483c2c
+dnl Create a bridge with N ports per transaction.  Increase N every 4
Open vSwitch CI 483c2c
+dnl iterations.  And then remove the bridges.  By increasing the size of
Open vSwitch CI 483c2c
+dnl transactions, ensuring that they take up a significant percentage of
Open vSwitch CI 483c2c
+dnl the total database size, so the transaction history will not be able
Open vSwitch CI 483c2c
+dnl to hold all of them.
Open vSwitch CI 483c2c
+dnl
Open vSwitch CI 483c2c
+dnl The test verifies that the number of atoms in the transaction history
Open vSwitch CI 483c2c
+dnl is always less than the number of atoms in the database.
Open vSwitch CI 483c2c
+get_n_atoms () {
Open vSwitch CI 483c2c
+    n=$(ovs-appctl -t ovsdb-server memory/show dnl
Open vSwitch CI 483c2c
+            | tr ' ' '\n' | grep atoms | grep "^$1:" | cut -d ':' -f 2)
Open vSwitch CI 483c2c
+    if test X"$n" == "X"; then
Open vSwitch CI 483c2c
+        n=0
Open vSwitch CI 483c2c
+    fi
Open vSwitch CI 483c2c
+    echo $n
Open vSwitch CI 483c2c
+}
Open vSwitch CI 483c2c
+
Open vSwitch CI 483c2c
+check_atoms () {
Open vSwitch CI 483c2c
+    n_db_atoms=$(get_n_atoms atoms)
Open vSwitch CI 483c2c
+    n_txn_history_atoms=$(get_n_atoms txn-history-atoms)
Open vSwitch CI 483c2c
+    echo "n_db_atoms:          $n_db_atoms"
Open vSwitch CI 483c2c
+    echo "n_txn_history_atoms: $n_txn_history_atoms"
Open vSwitch CI 483c2c
+    AT_CHECK([test $n_txn_history_atoms -le $n_db_atoms])
Open vSwitch CI 483c2c
+}
Open vSwitch CI 483c2c
+
Open vSwitch CI 483c2c
+add_ports () {
Open vSwitch CI 483c2c
+    for j in $(seq 1 $2); do
Open vSwitch CI 483c2c
+        printf " -- add-port br$1 p$1-%d" $j
Open vSwitch CI 483c2c
+    done
Open vSwitch CI 483c2c
+}
Open vSwitch CI 483c2c
+
Open vSwitch CI 483c2c
+initial_db_atoms=$(get_n_atoms atoms)
Open vSwitch CI 483c2c
+
Open vSwitch CI 483c2c
+for i in $(seq 1 100); do
Open vSwitch CI 483c2c
+    cmd=$(add_ports $i $(($i / 4 + 1)))
Open vSwitch CI 483c2c
+    AT_CHECK([ovs-vsctl --no-wait add-br br$i $cmd])
Open vSwitch CI 483c2c
+    check_atoms
Open vSwitch CI 483c2c
+done
Open vSwitch CI 483c2c
+
Open vSwitch CI 483c2c
+for i in $(seq 1 100); do
Open vSwitch CI 483c2c
+    AT_CHECK([ovs-vsctl --no-wait del-br br$i])
Open vSwitch CI 483c2c
+    check_atoms
Open vSwitch CI 483c2c
+done
Open vSwitch CI 483c2c
+
Open vSwitch CI 483c2c
+dnl After removing all the bridges, the number of atoms in the database
Open vSwitch CI 483c2c
+dnl should return to its initial value.
Open vSwitch CI 483c2c
+AT_CHECK([test $(get_n_atoms atoms) -eq $initial_db_atoms])
Open vSwitch CI 483c2c
+
Open vSwitch CI 483c2c
 OVS_APP_EXIT_AND_WAIT([ovsdb-server])
Open vSwitch CI 483c2c
 AT_CLEANUP
Open vSwitch CI 483c2c
 
110336
diff --git a/tests/system-traffic.at b/tests/system-traffic.at
Open vSwitch CI 3f9b5c
index f400cfabc9..092de308be 100644
110336
--- a/tests/system-traffic.at
110336
+++ b/tests/system-traffic.at
Open vSwitch CI 3f9b5c
@@ -1981,6 +1981,111 @@ tcp,orig=(src=10.1.1.3,dst=10.1.1.4,sport=<cleared>,dport=<cleared>),reply=(src=
Open vSwitch CI 3f9b5c
 OVS_TRAFFIC_VSWITCHD_STOP
Open vSwitch CI 3f9b5c
 AT_CLEANUP
Open vSwitch CI 3f9b5c
 
Open vSwitch CI 3f9b5c
+AT_SETUP([conntrack - zones from other field])
Open vSwitch CI 3f9b5c
+CHECK_CONNTRACK()
Open vSwitch CI 3f9b5c
+OVS_TRAFFIC_VSWITCHD_START()
Open vSwitch CI 3f9b5c
+
Open vSwitch CI 3f9b5c
+ADD_NAMESPACES(at_ns0, at_ns1)
Open vSwitch CI 3f9b5c
+
Open vSwitch CI 3f9b5c
+ADD_VETH(p0, at_ns0, br0, "10.1.1.1/24")
Open vSwitch CI 3f9b5c
+ADD_VETH(p1, at_ns1, br0, "10.1.1.2/24")
Open vSwitch CI 3f9b5c
+
Open vSwitch CI 3f9b5c
+dnl Allow any traffic from ns0->ns1. Only allow nd, return traffic from ns1->ns0.
Open vSwitch CI 3f9b5c
+AT_DATA([flows.txt], [dnl
Open vSwitch CI 3f9b5c
+priority=1,action=drop
Open vSwitch CI 3f9b5c
+priority=10,arp,action=normal
Open vSwitch CI 3f9b5c
+priority=10,icmp,action=normal
Open vSwitch CI 3f9b5c
+priority=100,in_port=1,tcp,ct_state=-trk,action=ct(zone=5,table=0)
Open vSwitch CI 3f9b5c
+priority=100,in_port=1,tcp,ct_state=+trk,action=ct(commit,zone=NXM_NX_CT_ZONE[]),2
Open vSwitch CI 3f9b5c
+priority=100,in_port=2,ct_state=-trk,tcp,action=ct(table=0,zone=5)
Open vSwitch CI 3f9b5c
+priority=100,in_port=2,ct_state=+trk,ct_zone=5,tcp,action=1
Open vSwitch CI 3f9b5c
+])
Open vSwitch CI 3f9b5c
+
Open vSwitch CI 3f9b5c
+AT_CHECK([ovs-ofctl --bundle add-flows br0 flows.txt])
Open vSwitch CI 3f9b5c
+
Open vSwitch CI 3f9b5c
+OVS_START_L7([at_ns1], [http])
Open vSwitch CI 3f9b5c
+
Open vSwitch CI 3f9b5c
+dnl HTTP requests from p0->p1 should work fine.
Open vSwitch CI 3f9b5c
+NS_CHECK_EXEC([at_ns0], [wget 10.1.1.2 -t 3 -T 1 --retry-connrefused -v -o wget0.log])
Open vSwitch CI 3f9b5c
+
Open vSwitch CI 3f9b5c
+AT_CHECK([ovs-appctl dpctl/dump-conntrack | FORMAT_CT(10.1.1.2)], [0], [dnl
Open vSwitch CI 3f9b5c
+tcp,dnl
Open vSwitch CI 3f9b5c
+orig=(src=10.1.1.1,dst=10.1.1.2,sport=<cleared>,dport=<cleared>),dnl
Open vSwitch CI 3f9b5c
+reply=(src=10.1.1.2,dst=10.1.1.1,sport=<cleared>,dport=<cleared>),dnl
Open vSwitch CI 3f9b5c
+zone=5,protoinfo=(state=<cleared>)
Open vSwitch CI 3f9b5c
+])
Open vSwitch CI 3f9b5c
+
Open vSwitch CI 3f9b5c
+dnl This is to test when the zoneid is set by a field variable like
Open vSwitch CI 3f9b5c
+dnl NXM_NX_CT_ZONE, the OVS xlate should generate a megaflow with a form of
Open vSwitch CI 3f9b5c
+dnl "ct_zone(5), ...  actions: ct(commit, zone=5)".  The match "ct_zone(5)"
Open vSwitch CI 3f9b5c
+dnl is needed as if we changes the zoneid into 15 in the following, the old
Open vSwitch CI 3f9b5c
+dnl "ct_zone(5), ... actions: ct(commit, zone=5)" megaflow will not get hit,
Open vSwitch CI 3f9b5c
+dnl and OVS will generate a new megaflow with the match "ct_zone(0xf)".
Open vSwitch CI 3f9b5c
+dnl This will make sure that the new packets are committing to zoneid 15
Open vSwitch CI 3f9b5c
+dnl rather than old 5.
Open vSwitch CI 3f9b5c
+AT_CHECK([ovs-appctl dpctl/dump-flows --names filter=in_port=ovs-p0 dnl
Open vSwitch CI 3f9b5c
+              | grep "+trk" | grep -q "ct_zone(0x5)" ], [0], [])
Open vSwitch CI 3f9b5c
+
Open vSwitch CI 3f9b5c
+AT_CHECK([ovs-ofctl mod-flows br0 dnl
Open vSwitch CI 3f9b5c
+            'priority=100,ct_state=-trk,tcp,in_port="ovs-p0" actions=ct(table=0,zone=15)'])
Open vSwitch CI 3f9b5c
+
Open vSwitch CI 3f9b5c
+NS_CHECK_EXEC([at_ns0], [wget 10.1.1.2 -t 3 -T 1 --retry-connrefused -v -o wget0.log])
Open vSwitch CI 3f9b5c
+
Open vSwitch CI 3f9b5c
+AT_CHECK([ovs-appctl dpctl/dump-flows --names filter=in_port=ovs-p0 dnl
Open vSwitch CI 3f9b5c
+              | grep "+trk" | grep -q "ct_zone(0xf)" ], [0], [])
Open vSwitch CI 3f9b5c
+
Open vSwitch CI 3f9b5c
+OVS_TRAFFIC_VSWITCHD_STOP
Open vSwitch CI 3f9b5c
+AT_CLEANUP
Open vSwitch CI 3f9b5c
+
Open vSwitch CI 3f9b5c
+AT_SETUP([conntrack - zones from other field, more tests])
Open vSwitch CI 3f9b5c
+CHECK_CONNTRACK()
Open vSwitch CI 3f9b5c
+OVS_TRAFFIC_VSWITCHD_START()
Open vSwitch CI 3f9b5c
+
Open vSwitch CI 3f9b5c
+ADD_NAMESPACES(at_ns0, at_ns1)
Open vSwitch CI 3f9b5c
+
Open vSwitch CI 3f9b5c
+ADD_VETH(p0, at_ns0, br0, "10.1.1.1/24")
Open vSwitch CI 3f9b5c
+ADD_VETH(p1, at_ns1, br0, "10.1.1.2/24")
Open vSwitch CI 3f9b5c
+
Open vSwitch CI 3f9b5c
+dnl Allow any traffic from ns0->ns1. Only allow nd, return traffic from ns1->ns0.
Open vSwitch CI 3f9b5c
+AT_DATA([flows.txt], [dnl
Open vSwitch CI 3f9b5c
+priority=1,action=drop
Open vSwitch CI 3f9b5c
+priority=10,arp,action=normal
Open vSwitch CI 3f9b5c
+priority=10,icmp,action=normal
Open vSwitch CI 3f9b5c
+priority=100,in_port=1,tcp,ct_state=-trk,action=ct(zone=5,table=0,commit,exec(load:0xffff0005->NXM_NX_CT_LABEL[[0..31]]))
Open vSwitch CI 3f9b5c
+priority=100,in_port=1,tcp,ct_state=+trk,action=ct(commit,zone=NXM_NX_CT_LABEL[[0..15]]),2
Open vSwitch CI 3f9b5c
+priority=100,in_port=2,ct_state=-trk,tcp,action=ct(table=0,zone=5)
Open vSwitch CI 3f9b5c
+priority=100,in_port=2,ct_state=+trk,ct_zone=5,tcp,action=1
Open vSwitch CI 3f9b5c
+])
Open vSwitch CI 3f9b5c
+
Open vSwitch CI 3f9b5c
+AT_CHECK([ovs-ofctl --bundle add-flows br0 flows.txt])
Open vSwitch CI 3f9b5c
+
Open vSwitch CI 3f9b5c
+OVS_START_L7([at_ns1], [http])
Open vSwitch CI 3f9b5c
+
Open vSwitch CI 3f9b5c
+dnl HTTP requests from p0->p1 should work fine.
Open vSwitch CI 3f9b5c
+NS_CHECK_EXEC([at_ns0], [wget 10.1.1.2 -t 3 -T 1 --retry-connrefused -v -o wget0.log])
Open vSwitch CI 3f9b5c
+
Open vSwitch CI 3f9b5c
+AT_CHECK([ovs-appctl dpctl/dump-conntrack | FORMAT_CT(10.1.1.2)], [0], [dnl
Open vSwitch CI 3f9b5c
+tcp,dnl
Open vSwitch CI 3f9b5c
+orig=(src=10.1.1.1,dst=10.1.1.2,sport=<cleared>,dport=<cleared>),dnl
Open vSwitch CI 3f9b5c
+reply=(src=10.1.1.2,dst=10.1.1.1,sport=<cleared>,dport=<cleared>),dnl
Open vSwitch CI 3f9b5c
+zone=5,labels=0xffff0005,protoinfo=(state=<cleared>)
Open vSwitch CI 3f9b5c
+])
Open vSwitch CI 3f9b5c
+
Open vSwitch CI 3f9b5c
+AT_CHECK([ovs-appctl dpctl/dump-flows --names filter=in_port=ovs-p0 dnl
Open vSwitch CI 3f9b5c
+              | grep "+trk" | sed 's/0xffff0005\/0xffff/0x5\/0xffff/' dnl
Open vSwitch CI 3f9b5c
+              | grep -q "ct_label(0x5/0xffff)" ], [0], [])
Open vSwitch CI 3f9b5c
+
Open vSwitch CI 3f9b5c
+AT_CHECK([ovs-ofctl mod-flows br0 'priority=100,ct_state=-trk,tcp,in_port="ovs-p0" actions=ct(table=0,zone=15,commit,exec(load:0xffff000f->NXM_NX_CT_LABEL[[0..31]]))'])
Open vSwitch CI 3f9b5c
+
Open vSwitch CI 3f9b5c
+NS_CHECK_EXEC([at_ns0], [wget 10.1.1.2 -t 3 -T 1 --retry-connrefused -v -o wget0.log])
Open vSwitch CI 3f9b5c
+
Open vSwitch CI 3f9b5c
+AT_CHECK([ovs-appctl dpctl/dump-flows --names filter=in_port=ovs-p0 dnl
Open vSwitch CI 3f9b5c
+              | grep "+trk" | sed 's/0xffff000f\/0xffff/0xf\/0xffff/' dnl
Open vSwitch CI 3f9b5c
+              | grep -q "ct_label(0xf/0xffff)" ], [0], [])
Open vSwitch CI 3f9b5c
+
Open vSwitch CI 3f9b5c
+OVS_TRAFFIC_VSWITCHD_STOP
Open vSwitch CI 3f9b5c
+AT_CLEANUP
Open vSwitch CI 3f9b5c
+
Open vSwitch CI 3f9b5c
 AT_SETUP([conntrack - multiple bridges])
Open vSwitch CI 3f9b5c
 CHECK_CONNTRACK()
Open vSwitch CI 3f9b5c
 OVS_TRAFFIC_VSWITCHD_START(
Open vSwitch CI 3f9b5c
@@ -3305,6 +3410,46 @@ NS_CHECK_EXEC([at_ns0], [ping6 -s 3200 -q -c 3 -i 0.3 -w 2 fc00::2 | FORMAT_PING
110336
 OVS_TRAFFIC_VSWITCHD_STOP
110336
 AT_CLEANUP
110336
 
110336
+AT_SETUP([conntrack - IPv4 Fragmentation + NAT])
110336
+AT_SKIP_IF([test $HAVE_TCPDUMP = no])
110336
+CHECK_CONNTRACK()
110336
+
110336
+OVS_TRAFFIC_VSWITCHD_START(
110336
+   [set-fail-mode br0 secure -- ])
110336
+
110336
+ADD_NAMESPACES(at_ns0, at_ns1)
110336
+
110336
+ADD_VETH(p0, at_ns0, br0, "10.2.1.1/24")
110336
+ADD_VETH(p1, at_ns1, br0, "10.2.1.2/24")
110336
+
110336
+dnl Create a dummy route for NAT
110336
+NS_CHECK_EXEC([at_ns1], [ip addr add 10.1.1.2/32 dev lo])
110336
+NS_CHECK_EXEC([at_ns0], [ip route add 10.1.1.0/24 via 10.2.1.2])
110336
+NS_CHECK_EXEC([at_ns1], [ip route add 10.1.1.0/24 via 10.2.1.1])
110336
+
110336
+dnl Solely for debugging when things go wrong
110336
+NS_EXEC([at_ns0], [tcpdump -l -n -xx -U -i p0 -w p0.pcap >tcpdump.out 2>/dev/null &])
110336
+NS_EXEC([at_ns1], [tcpdump -l -n -xx -U -i p1 -w p1.pcap >tcpdump.out 2>/dev/null &])
110336
+
110336
+AT_DATA([flows.txt], [dnl
110336
+table=0,arp,actions=normal
110336
+table=0,ct_state=-trk,ip,in_port=ovs-p0, actions=ct(table=1, nat)
110336
+table=0,ct_state=-trk,ip,in_port=ovs-p1, actions=ct(table=1, nat)
110336
+table=1,ct_state=+trk+new,ip,in_port=ovs-p0, actions=ct(commit, nat(src=10.1.1.1)),ovs-p1
110336
+table=1,ct_state=+trk+est,ip,in_port=ovs-p0, actions=ovs-p1
110336
+table=1,ct_state=+trk+est,ip,in_port=ovs-p1, actions=ovs-p0
110336
+])
110336
+
110336
+AT_CHECK([ovs-ofctl add-flows br0 flows.txt])
110336
+
110336
+dnl Check connectivity
110336
+NS_CHECK_EXEC([at_ns0], [ping -c 1 10.1.1.2 -M dont -s 4500 | FORMAT_PING], [0], [dnl
110336
+1 packets transmitted, 1 received, 0% packet loss, time 0ms
110336
+])
110336
+
110336
+OVS_TRAFFIC_VSWITCHD_STOP
110336
+AT_CLEANUP
110336
+
110336
 AT_SETUP([conntrack - resubmit to ct multiple times])
110336
 CHECK_CONNTRACK()
110336
 
110336
diff --git a/tests/test-json.c b/tests/test-json.c
110336
index a7ee595e0b..072a537252 100644
110336
--- a/tests/test-json.c
110336
+++ b/tests/test-json.c
110336
@@ -22,6 +22,8 @@
110336
 #include <getopt.h>
110336
 #include <stdio.h>
110336
 #include "ovstest.h"
110336
+#include "random.h"
110336
+#include "timeval.h"
110336
 #include "util.h"
110336
 
110336
 /* --pretty: If set, the JSON output is pretty-printed, instead of printed as
110336
@@ -157,3 +159,69 @@ test_json_main(int argc, char *argv[])
110336
 }
110336
 
110336
 OVSTEST_REGISTER("test-json", test_json_main);
110336
+
110336
+static void
110336
+json_string_benchmark_main(int argc OVS_UNUSED, char *argv[] OVS_UNUSED)
110336
+{
110336
+    struct {
110336
+        int n;
110336
+        int quote_probablility;
110336
+        int special_probability;
110336
+        int iter;
110336
+    } configs[] = {
110336
+        { 100000,     0, 0, 1000, },
110336
+        { 100000,     2, 1, 1000, },
110336
+        { 100000,    10, 1, 1000, },
110336
+        { 10000000,   0, 0, 100,  },
110336
+        { 10000000,   2, 1, 100,  },
110336
+        { 10000000,  10, 1, 100,  },
110336
+        { 100000000,  0, 0, 10.   },
110336
+        { 100000000,  2, 1, 10,   },
110336
+        { 100000000, 10, 1, 10,   },
110336
+    };
110336
+
110336
+    printf("  SIZE      Q  S            TIME\n");
110336
+    printf("--------------------------------------\n");
110336
+
110336
+    for (int i = 0; i < ARRAY_SIZE(configs); i++) {
110336
+        int iter = configs[i].iter;
110336
+        int n = configs[i].n;
110336
+        char *str = xzalloc(n);
110336
+
110336
+        for (int j = 0; j < n - 1; j++) {
110336
+            int r = random_range(100);
110336
+
110336
+            if (r < configs[i].special_probability) {
110336
+                str[j] = random_range(' ' - 1) + 1;
110336
+            } else if (r < (configs[i].special_probability
110336
+                            + configs[i].quote_probablility)) {
110336
+                str[j] = '"';
110336
+            } else {
110336
+                str[j] = random_range(256 - ' ') + ' ';
110336
+            }
110336
+        }
110336
+
110336
+        printf("%-11d %-2d %-2d: ", n, configs[i].quote_probablility,
110336
+                                       configs[i].special_probability);
110336
+        fflush(stdout);
110336
+
110336
+        struct json *json = json_string_create_nocopy(str);
110336
+        uint64_t start = time_msec();
110336
+
110336
+        char **res = xzalloc(iter * sizeof *res);
110336
+        for (int j = 0; j < iter; j++) {
110336
+            res[j] = json_to_string(json, 0);
110336
+        }
110336
+
110336
+        printf("%16.3lf ms\n", (double) (time_msec() - start) / iter);
110336
+        json_destroy(json);
110336
+        for (int j = 0; j < iter; j++) {
110336
+            free(res[j]);
110336
+        }
110336
+        free(res);
110336
+    }
110336
+
110336
+    exit(0);
110336
+}
110336
+
110336
+OVSTEST_REGISTER("json-string-benchmark", json_string_benchmark_main);
Open vSwitch CI 3f9b5c
diff --git a/tests/test-ovsdb.c b/tests/test-ovsdb.c
Open vSwitch CI 3f9b5c
index daa55dab7b..637271619f 100644
Open vSwitch CI 3f9b5c
--- a/tests/test-ovsdb.c
Open vSwitch CI 3f9b5c
+++ b/tests/test-ovsdb.c
Open vSwitch CI 3f9b5c
@@ -512,6 +512,18 @@ do_diff_data(struct ovs_cmdl_context *ctx)
Open vSwitch CI 3f9b5c
             ovs_fatal(0, "failed to apply diff");
Open vSwitch CI 3f9b5c
         }
Open vSwitch CI 3f9b5c
 
Open vSwitch CI 3f9b5c
+        /* Apply diff to 'old' in place. */
Open vSwitch CI 3f9b5c
+        error = ovsdb_datum_apply_diff_in_place(&old, &diff, &type);
Open vSwitch CI 3f9b5c
+        if (error) {
Open vSwitch CI 3f9b5c
+            char *string = ovsdb_error_to_string_free(error);
Open vSwitch CI 3f9b5c
+            ovs_fatal(0, "%s", string);
Open vSwitch CI 3f9b5c
+        }
Open vSwitch CI 3f9b5c
+
Open vSwitch CI 3f9b5c
+        /* Test to make sure 'old' equals 'new' now.  */
Open vSwitch CI 3f9b5c
+        if (!ovsdb_datum_equals(&new, &old, &type)) {
Open vSwitch CI 3f9b5c
+            ovs_fatal(0, "failed to apply diff in place");
Open vSwitch CI 3f9b5c
+        }
Open vSwitch CI 3f9b5c
+
Open vSwitch CI 3f9b5c
         /* Print diff */
Open vSwitch CI 3f9b5c
         json = ovsdb_datum_to_json(&diff, &type);
Open vSwitch CI 3f9b5c
         printf ("diff: ");
Open vSwitch CI 3f9b5c
@@ -522,6 +534,11 @@ do_diff_data(struct ovs_cmdl_context *ctx)
Open vSwitch CI 3f9b5c
         printf ("apply diff: ");
Open vSwitch CI 3f9b5c
         print_and_free_json(json);
Open vSwitch CI 3f9b5c
 
Open vSwitch CI 3f9b5c
+        /* Print updated 'old' */
Open vSwitch CI 3f9b5c
+        json = ovsdb_datum_to_json(&old, &type);
Open vSwitch CI 3f9b5c
+        printf ("apply diff in place: ");
Open vSwitch CI 3f9b5c
+        print_and_free_json(json);
Open vSwitch CI 3f9b5c
+
Open vSwitch CI 3f9b5c
         ovsdb_datum_destroy(&new, &type);
Open vSwitch CI 3f9b5c
         ovsdb_datum_destroy(&old, &type);
Open vSwitch CI 3f9b5c
         ovsdb_datum_destroy(&diff, &type);
Open vSwitch CI 3f9b5c
@@ -2727,13 +2744,15 @@ print_idl_row_simple2(const struct idltest_simple2 *s, int step)
Open vSwitch CI 3f9b5c
     printf("%03d: name=%s smap=[",
Open vSwitch CI 3f9b5c
            step, s->name);
Open vSwitch CI 3f9b5c
     for (i = 0; i < smap->n; i++) {
Open vSwitch CI 3f9b5c
-        printf("[%s : %s]%s", smap->keys[i].string, smap->values[i].string,
Open vSwitch CI 3f9b5c
-                i < smap->n-1? ",": "");
Open vSwitch CI 3f9b5c
+        printf("[%s : %s]%s",
Open vSwitch CI 3f9b5c
+               smap->keys[i].s->string, smap->values[i].s->string,
Open vSwitch CI 3f9b5c
+               i < smap->n - 1 ? "," : "");
Open vSwitch CI 3f9b5c
     }
Open vSwitch CI 3f9b5c
     printf("] imap=[");
Open vSwitch CI 3f9b5c
     for (i = 0; i < imap->n; i++) {
Open vSwitch CI 3f9b5c
-        printf("[%"PRId64" : %s]%s", imap->keys[i].integer, imap->values[i].string,
Open vSwitch CI 3f9b5c
-                i < imap->n-1? ",":"");
Open vSwitch CI 3f9b5c
+        printf("[%"PRId64" : %s]%s",
Open vSwitch CI 3f9b5c
+               imap->keys[i].integer, imap->values[i].s->string,
Open vSwitch CI 3f9b5c
+               i < imap->n - 1 ? "," : "");
Open vSwitch CI 3f9b5c
     }
Open vSwitch CI 3f9b5c
     printf("]\n");
Open vSwitch CI 3f9b5c
 }
Open vSwitch CI 3f9b5c
@@ -2802,8 +2821,8 @@ do_idl_partial_update_map_column(struct ovs_cmdl_context *ctx)
Open vSwitch CI 3f9b5c
     myTxn = ovsdb_idl_txn_create(idl);
Open vSwitch CI 3f9b5c
     smap = idltest_simple2_get_smap(myRow, OVSDB_TYPE_STRING,
Open vSwitch CI 3f9b5c
                                     OVSDB_TYPE_STRING);
Open vSwitch CI 3f9b5c
-    strcpy(key_to_delete, smap->keys[0].string);
Open vSwitch CI 3f9b5c
-    idltest_simple2_update_smap_delkey(myRow, smap->keys[0].string);
Open vSwitch CI 3f9b5c
+    ovs_strlcpy(key_to_delete, smap->keys[0].s->string, sizeof key_to_delete);
Open vSwitch CI 3f9b5c
+    idltest_simple2_update_smap_delkey(myRow, smap->keys[0].s->string);
Open vSwitch CI 3f9b5c
     ovsdb_idl_txn_commit_block(myTxn);
Open vSwitch CI 3f9b5c
     ovsdb_idl_txn_destroy(myTxn);
Open vSwitch CI 3f9b5c
     ovsdb_idl_get_initial_snapshot(idl);
110336
diff --git a/tests/tunnel-push-pop.at b/tests/tunnel-push-pop.at
Open vSwitch CI 3f9b5c
index 48c5de9d19..6364653975 100644
110336
--- a/tests/tunnel-push-pop.at
110336
+++ b/tests/tunnel-push-pop.at
Open vSwitch CI 3f9b5c
@@ -595,6 +595,64 @@ OVS_WAIT_UNTIL([test `ovs-pcap p0.pcap | grep 50540000000a5054000000091235 | wc
110336
 OVS_VSWITCHD_STOP
110336
 AT_CLEANUP
110336
 
110336
+AT_SETUP([tunnel_push_pop - packet_out debug_slow])
110336
+
110336
+OVS_VSWITCHD_START(
110336
+    [add-port br0 p0 dnl
110336
+     -- set Interface p0 type=dummy ofport_request=1 dnl
110336
+                         other-config:hwaddr=aa:55:aa:55:00:00])
110336
+AT_CHECK([ovs-appctl vlog/set dpif_netdev:dbg])
110336
+AT_CHECK([ovs-vsctl add-br int-br -- set bridge int-br datapath_type=dummy])
110336
+AT_CHECK([ovs-vsctl add-port int-br t2 dnl
110336
+          -- set Interface t2 type=geneve options:remote_ip=1.1.2.92 dnl
110336
+                              options:key=123 ofport_request=2])
110336
+
110336
+dnl First setup dummy interface IP address, then add the route
110336
+dnl so that tnl-port table can get valid IP address for the device.
110336
+AT_CHECK([ovs-appctl netdev-dummy/ip4addr br0 1.1.2.88/24], [0], [OK
110336
+])
110336
+AT_CHECK([ovs-appctl ovs/route/add 1.1.2.92/24 br0], [0], [OK
110336
+])
110336
+AT_CHECK([ovs-ofctl add-flow br0 action=normal])
110336
+
110336
+dnl This ARP reply from p0 has two effects:
110336
+dnl 1. The ARP cache will learn that 1.1.2.92 is at f8:bc:12:44:34:b6.
110336
+dnl 2. The br0 mac learning will learn that f8:bc:12:44:34:b6 is on p0.
110336
+AT_CHECK([
110336
+  ovs-appctl netdev-dummy/receive p0 dnl
110336
+      'recirc_id(0),in_port(2),dnl
110336
+       eth(src=f8:bc:12:44:34:b6,dst=ff:ff:ff:ff:ff:ff),eth_type(0x0806),dnl
110336
+       arp(sip=1.1.2.92,tip=1.1.2.88,op=2,sha=f8:bc:12:44:34:b6,tha=00:00:00:00:00:00)'
110336
+])
110336
+
110336
+AT_CHECK([ovs-vsctl -- set Interface p0 options:tx_pcap=p0.pcap])
110336
+
110336
+packet=50540000000a505400000009123
Open vSwitch CI 3f9b5c
+dnl Source port is based on a packet hash, so it may differ depending on the
Open vSwitch CI 3f9b5c
+dnl compiler flags and CPU type.  Masked with '....'.
Open vSwitch CI 3f9b5c
+encap=f8bc124434b6aa55aa5500000800450000320000400040113406010102580101025c....17c1001e00000000655800007b00
110336
+
110336
+dnl Output to tunnel from a int-br internal port.
110336
+dnl Checking that the packet arrived and it was correctly encapsulated.
110336
+AT_CHECK([ovs-ofctl add-flow int-br "in_port=LOCAL,actions=debug_slow,output:2"])
110336
+AT_CHECK([ovs-appctl netdev-dummy/receive int-br "${packet}4"])
Open vSwitch CI 3f9b5c
+OVS_WAIT_UNTIL([test `ovs-pcap p0.pcap | egrep "${encap}${packet}4" | wc -l` -ge 1])
110336
+dnl Sending again to exercise the non-miss upcall path.
110336
+AT_CHECK([ovs-appctl netdev-dummy/receive int-br "${packet}4"])
Open vSwitch CI 3f9b5c
+OVS_WAIT_UNTIL([test `ovs-pcap p0.pcap | egrep "${encap}${packet}4" | wc -l` -ge 2])
110336
+
110336
+dnl Output to tunnel from the controller.
110336
+AT_CHECK([ovs-ofctl -O OpenFlow13 packet-out int-br CONTROLLER "debug_slow,output:2" "${packet}5"])
Open vSwitch CI 3f9b5c
+OVS_WAIT_UNTIL([test `ovs-pcap p0.pcap | egrep "${encap}${packet}5" | wc -l` -ge 1])
110336
+
110336
+dnl Datapath actions should not have tunnel push action.
110336
+AT_CHECK([ovs-appctl dpctl/dump-flows | grep -q tnl_push], [1])
110336
+dnl There should be slow_path action instead.
110336
+AT_CHECK([ovs-appctl dpctl/dump-flows | grep -q 'slow_path(action)'], [0])
110336
+
110336
+OVS_VSWITCHD_STOP
110336
+AT_CLEANUP
110336
+
110336
 AT_SETUP([tunnel_push_pop - underlay bridge match])
110336
 
110336
 OVS_VSWITCHD_START([add-port br0 p0 -- set Interface p0 type=dummy ofport_request=1 other-config:hwaddr=aa:55:aa:55:00:00])
Open vSwitch CI 3f9b5c
diff --git a/utilities/ovs-ctl.in b/utilities/ovs-ctl.in
Open vSwitch CI 3f9b5c
index 71800795c0..e6e07f4763 100644
Open vSwitch CI 3f9b5c
--- a/utilities/ovs-ctl.in
Open vSwitch CI 3f9b5c
+++ b/utilities/ovs-ctl.in
Open vSwitch CI 3f9b5c
@@ -421,7 +421,9 @@ Less important options for "start", "restart" and "force-reload-kmod":
Open vSwitch CI 3f9b5c
   --no-force-corefiles           do not force on core dumps for OVS daemons
Open vSwitch CI 3f9b5c
   --no-mlockall                  do not lock all of ovs-vswitchd into memory
Open vSwitch CI 3f9b5c
   --ovsdb-server-priority=NICE   set ovsdb-server's niceness (default: $OVSDB_SERVER_PRIORITY)
Open vSwitch CI 3f9b5c
+  --ovsdb-server-options=OPTIONS additional options for ovsdb-server (example: '-vconsole:dbg -vfile:dbg')
Open vSwitch CI 3f9b5c
   --ovs-vswitchd-priority=NICE   set ovs-vswitchd's niceness (default: $OVS_VSWITCHD_PRIORITY)
Open vSwitch CI 3f9b5c
+  --ovs-vswitchd-options=OPTIONS additional options for ovs-vswitchd (example: '-vconsole:dbg -vfile:dbg')
Open vSwitch CI 3f9b5c
   --no-full-hostname             set short hostname instead of full hostname
Open vSwitch CI 3f9b5c
   --no-record-hostname           do not attempt to determine/record system
Open vSwitch CI 3f9b5c
                                  hostname as part of start command
Open vSwitch CI 3f9b5c
diff --git a/vswitchd/bridge.c b/vswitchd/bridge.c
Open vSwitch CI 3f9b5c
index cb7c5cb769..c790a56adf 100644
Open vSwitch CI 3f9b5c
--- a/vswitchd/bridge.c
Open vSwitch CI 3f9b5c
+++ b/vswitchd/bridge.c
Open vSwitch CI 3f9b5c
@@ -4229,7 +4229,7 @@ bridge_configure_aa(struct bridge *br)
Open vSwitch CI 3f9b5c
         union ovsdb_atom atom;
Open vSwitch CI 3f9b5c
 
Open vSwitch CI 3f9b5c
         atom.integer = m->isid;
Open vSwitch CI 3f9b5c
-        if (ovsdb_datum_find_key(mc, &atom, OVSDB_TYPE_INTEGER) == UINT_MAX) {
Open vSwitch CI 3f9b5c
+        if (!ovsdb_datum_find_key(mc, &atom, OVSDB_TYPE_INTEGER, NULL)) {
Open vSwitch CI 3f9b5c
             VLOG_INFO("Deleting isid=%"PRIu32", vlan=%"PRIu16,
Open vSwitch CI 3f9b5c
                       m->isid, m->vlan);
Open vSwitch CI 3f9b5c
             bridge_aa_mapping_destroy(m);
Open vSwitch CI 3f9b5c
@@ -4826,7 +4826,7 @@ queue_ids_include(const struct ovsdb_datum *queues, int64_t target)
Open vSwitch CI 3f9b5c
     union ovsdb_atom atom;
Open vSwitch CI 3f9b5c
 
Open vSwitch CI 3f9b5c
     atom.integer = target;
Open vSwitch CI 3f9b5c
-    return ovsdb_datum_find_key(queues, &atom, OVSDB_TYPE_INTEGER) != UINT_MAX;
Open vSwitch CI 3f9b5c
+    return ovsdb_datum_find_key(queues, &atom, OVSDB_TYPE_INTEGER, NULL);
Open vSwitch CI 3f9b5c
 }
Open vSwitch CI 3f9b5c
 
Open vSwitch CI 3f9b5c
 static void
Open vSwitch CI 3f9b5c
@@ -5020,7 +5020,7 @@ bridge_configure_mirrors(struct bridge *br)
Open vSwitch CI 3f9b5c
         union ovsdb_atom atom;
Open vSwitch CI 3f9b5c
 
Open vSwitch CI 3f9b5c
         atom.uuid = m->uuid;
Open vSwitch CI 3f9b5c
-        if (ovsdb_datum_find_key(mc, &atom, OVSDB_TYPE_UUID) == UINT_MAX) {
Open vSwitch CI 3f9b5c
+        if (!ovsdb_datum_find_key(mc, &atom, OVSDB_TYPE_UUID, NULL)) {
Open vSwitch CI 3f9b5c
             mirror_destroy(m);
Open vSwitch CI 3f9b5c
         }
Open vSwitch CI 3f9b5c
     }