diff --git a/SOURCES/openvswitch-2.16.0.patch b/SOURCES/openvswitch-2.16.0.patch index 7b9c0be..75d73e4 100644 --- a/SOURCES/openvswitch-2.16.0.patch +++ b/SOURCES/openvswitch-2.16.0.patch @@ -1,16 +1,67 @@ +diff --git a/.ci/linux-prepare.sh b/.ci/linux-prepare.sh +index c55125cf78..e4d2a187e0 100755 +--- a/.ci/linux-prepare.sh ++++ b/.ci/linux-prepare.sh +@@ -22,7 +22,6 @@ cd .. + + pip3 install --disable-pip-version-check --user \ + flake8 hacking sphinx pyOpenSSL wheel setuptools +-pip3 install --user --upgrade docutils + pip3 install --user 'meson==0.47.1' + + if [ "$M32" ]; then +diff --git a/.cirrus.yml b/.cirrus.yml +index 358f2ba256..480fea2421 100644 +--- a/.cirrus.yml ++++ b/.cirrus.yml +@@ -5,7 +5,7 @@ freebsd_build_task: + image_family: freebsd-12-2-snap + image_family: freebsd-11-4-snap + cpu: 4 +- memory: 8G ++ memory: 4G + + env: + DEPENDENCIES: automake libtool gmake gcc wget openssl python3 +diff --git a/.github/workflows/build-and-test.yml b/.github/workflows/build-and-test.yml +index e2350c6d9d..7434ad18ec 100644 +--- a/.github/workflows/build-and-test.yml ++++ b/.github/workflows/build-and-test.yml +@@ -127,7 +127,7 @@ jobs: + - name: set up python + uses: actions/setup-python@v2 + with: +- python-version: '3.x' ++ python-version: '3.9' + + - name: create ci signature file for the dpdk cache key + if: matrix.dpdk != '' || matrix.dpdk_shared != '' +@@ -215,7 +215,7 @@ jobs: + - name: set up python + uses: actions/setup-python@v2 + with: +- python-version: '3.x' ++ python-version: '3.9' + - name: install dependencies + run: brew install automake libtool + - name: prepare diff --git a/NEWS b/NEWS -index 559a51ba3f..f2497d5cec 100644 +index 559a51ba3f..7a6de3da82 100644 --- a/NEWS +++ b/NEWS -@@ -1,3 +1,6 @@ -+v2.16.1 - xx xxx xxxx +@@ -1,3 +1,10 @@ ++v2.16.2 - xx xxx xxxx +--------------------- + ++v2.16.1 - 21 Oct 2021 ++--------------------- ++ - Bug fixes ++ v2.16.0 - 16 Aug 2021 --------------------- - Removed support for 1024-bit Diffie-Hellman key exchange, which is now diff --git a/configure.ac b/configure.ac -index 16b32be965..4def2ebd0a 100644 +index 16b32be965..64c26828f2 100644 --- a/configure.ac +++ b/configure.ac @@ -13,7 +13,7 @@ @@ -18,20 +69,257 @@ index 16b32be965..4def2ebd0a 100644 AC_PREREQ(2.63) -AC_INIT(openvswitch, 2.16.0, bugs@openvswitch.org) -+AC_INIT(openvswitch, 2.16.1, bugs@openvswitch.org) ++AC_INIT(openvswitch, 2.16.2, bugs@openvswitch.org) AC_CONFIG_SRCDIR([datapath/datapath.c]) AC_CONFIG_MACRO_DIR([m4]) AC_CONFIG_AUX_DIR([build-aux]) +diff --git a/datapath-windows/ovsext/Actions.c b/datapath-windows/ovsext/Actions.c +index e130c2f966..0c18c62548 100644 +--- a/datapath-windows/ovsext/Actions.c ++++ b/datapath-windows/ovsext/Actions.c +@@ -1112,9 +1112,9 @@ OvsPopFieldInPacketBuf(OvsForwardingContext *ovsFwdCtx, + * should split the function and refactor. */ + if (!bufferData) { + EthHdr *ethHdr = (EthHdr *)bufferStart; +- /* If the frame is not VLAN make it a no op */ + if (ethHdr->Type != ETH_TYPE_802_1PQ_NBO) { +- return NDIS_STATUS_SUCCESS; ++ OVS_LOG_ERROR("Invalid ethHdr type %u, nbl %p", ethHdr->Type, ovsFwdCtx->curNbl); ++ return NDIS_STATUS_INVALID_PACKET; + } + } + RtlMoveMemory(bufferStart + shiftLength, bufferStart, shiftOffset); +@@ -1137,6 +1137,9 @@ OvsPopFieldInPacketBuf(OvsForwardingContext *ovsFwdCtx, + static __inline NDIS_STATUS + OvsPopVlanInPktBuf(OvsForwardingContext *ovsFwdCtx) + { ++ NDIS_STATUS status; ++ OVS_PACKET_HDR_INFO* layers = &ovsFwdCtx->layers; ++ + /* + * Declare a dummy vlanTag structure since we need to compute the size + * of shiftLength. The NDIS one is a unionized structure. +@@ -1145,7 +1148,15 @@ OvsPopVlanInPktBuf(OvsForwardingContext *ovsFwdCtx) + UINT32 shiftLength = sizeof(vlanTag.TagHeader); + UINT32 shiftOffset = sizeof(DL_EUI48) + sizeof(DL_EUI48); + +- return OvsPopFieldInPacketBuf(ovsFwdCtx, shiftOffset, shiftLength, NULL); ++ status = OvsPopFieldInPacketBuf(ovsFwdCtx, shiftOffset, shiftLength, ++ NULL); ++ ++ if (status == NDIS_STATUS_SUCCESS) { ++ layers->l3Offset -= (UINT16) shiftLength; ++ layers->l4Offset -= (UINT16) shiftLength; ++ } ++ ++ return status; + } + + +@@ -1516,6 +1527,7 @@ OvsUpdateAddressAndPort(OvsForwardingContext *ovsFwdCtx, + + csumInfo.Value = NET_BUFFER_LIST_INFO(ovsFwdCtx->curNbl, + TcpIpChecksumNetBufferListInfo); ++ + /* + * Adjust the IP header inline as dictated by the action, and also update + * the IP and the TCP checksum for the data modified. +@@ -1524,6 +1536,7 @@ OvsUpdateAddressAndPort(OvsForwardingContext *ovsFwdCtx, + * ChecksumUpdate32(). Ignoring this for now, since for the most common + * case, we only update the TTL. + */ ++ /*Only tx direction the checksum value will be reset to be PseudoChecksum*/ + + if (isSource) { + addrField = &ipHdr->saddr; +@@ -1540,7 +1553,7 @@ OvsUpdateAddressAndPort(OvsForwardingContext *ovsFwdCtx, + ((BOOLEAN)csumInfo.Receive.UdpChecksumSucceeded || + (BOOLEAN)csumInfo.Receive.UdpChecksumFailed); + } +- if (l4Offload) { ++ if (isTx && l4Offload) { + *checkField = IPPseudoChecksum(&newAddr, &ipHdr->daddr, + tcpHdr ? IPPROTO_TCP : IPPROTO_UDP, + ntohs(ipHdr->tot_len) - ipHdr->ihl * 4); +@@ -1561,7 +1574,7 @@ OvsUpdateAddressAndPort(OvsForwardingContext *ovsFwdCtx, + (BOOLEAN)csumInfo.Receive.UdpChecksumFailed); + } + +- if (l4Offload) { ++ if (isTx && l4Offload) { + *checkField = IPPseudoChecksum(&ipHdr->saddr, &newAddr, + tcpHdr ? IPPROTO_TCP : IPPROTO_UDP, + ntohs(ipHdr->tot_len) - ipHdr->ihl * 4); +@@ -1570,7 +1583,7 @@ OvsUpdateAddressAndPort(OvsForwardingContext *ovsFwdCtx, + + if (*addrField != newAddr) { + UINT32 oldAddr = *addrField; +- if (checkField && *checkField != 0 && !l4Offload) { ++ if ((checkField && *checkField != 0) && (!l4Offload || !isTx)) { + /* Recompute total checksum. */ + *checkField = ChecksumUpdate32(*checkField, oldAddr, + newAddr); +@@ -1579,11 +1592,12 @@ OvsUpdateAddressAndPort(OvsForwardingContext *ovsFwdCtx, + ipHdr->check = ChecksumUpdate32(ipHdr->check, oldAddr, + newAddr); + } ++ + *addrField = newAddr; + } + + if (portField && *portField != newPort) { +- if (checkField && !l4Offload) { ++ if ((checkField) && (!l4Offload || !isTx)) { + /* Recompute total checksum. */ + *checkField = ChecksumUpdate16(*checkField, *portField, + newPort); +@@ -1792,9 +1806,11 @@ OvsExecuteRecirc(OvsForwardingContext *ovsFwdCtx, + } + + if (newNbl) { +- deferredAction = OvsAddDeferredActions(newNbl, key, NULL); ++ deferredAction = OvsAddDeferredActions(newNbl, key, &(ovsFwdCtx->layers), ++ NULL); + } else { +- deferredAction = OvsAddDeferredActions(ovsFwdCtx->curNbl, key, NULL); ++ deferredAction = OvsAddDeferredActions(ovsFwdCtx->curNbl, key, ++ &(ovsFwdCtx->layers), NULL); + } + + if (deferredAction) { +@@ -1964,7 +1980,7 @@ OvsExecuteSampleAction(OvsForwardingContext *ovsFwdCtx, + return STATUS_SUCCESS; + } + +- if (!OvsAddDeferredActions(newNbl, key, a)) { ++ if (!OvsAddDeferredActions(newNbl, key, &(ovsFwdCtx->layers), a)) { + OVS_LOG_INFO( + "Deferred actions limit reached, dropping sample action."); + OvsCompleteNBL(ovsFwdCtx->switchContext, newNbl, TRUE); +@@ -2100,6 +2116,7 @@ OvsDoExecuteActions(POVS_SWITCH_CONTEXT switchContext, + */ + status = OvsPopVlanInPktBuf(&ovsFwdCtx); + if (status != NDIS_STATUS_SUCCESS) { ++ OVS_LOG_ERROR("OVS-pop vlan action failed status = %lu", status); + dropReason = L"OVS-pop vlan action failed"; + goto dropit; + } +@@ -2349,7 +2366,7 @@ OvsActionsExecute(POVS_SWITCH_CONTEXT switchContext, + + if (status == STATUS_SUCCESS) { + status = OvsProcessDeferredActions(switchContext, completionList, +- portNo, sendFlags, layers); ++ portNo, sendFlags, NULL); + } + + return status; +diff --git a/datapath-windows/ovsext/Recirc.c b/datapath-windows/ovsext/Recirc.c +index 2febf060dd..a32b75352b 100644 +--- a/datapath-windows/ovsext/Recirc.c ++++ b/datapath-windows/ovsext/Recirc.c +@@ -277,16 +277,23 @@ OvsDeferredActionsQueuePush(POVS_DEFERRED_ACTION_QUEUE queue) + POVS_DEFERRED_ACTION + OvsAddDeferredActions(PNET_BUFFER_LIST nbl, + OvsFlowKey *key, ++ POVS_PACKET_HDR_INFO layers, + const PNL_ATTR actions) + { + POVS_DEFERRED_ACTION_QUEUE queue = OvsDeferredActionsQueueGet(); + POVS_DEFERRED_ACTION deferredAction = NULL; ++ OVS_PACKET_HDR_INFO layersInit = { 0 }; + + deferredAction = OvsDeferredActionsQueuePush(queue); + if (deferredAction) { + deferredAction->nbl = nbl; + deferredAction->actions = actions; + deferredAction->key = *key; ++ if (layers) { ++ deferredAction->layers = *layers; ++ } else { ++ deferredAction->layers = layersInit; ++ } + } + + return deferredAction; +@@ -309,9 +316,16 @@ OvsProcessDeferredActions(POVS_SWITCH_CONTEXT switchContext, + NDIS_STATUS status = NDIS_STATUS_SUCCESS; + POVS_DEFERRED_ACTION_QUEUE queue = OvsDeferredActionsQueueGet(); + POVS_DEFERRED_ACTION deferredAction = NULL; ++ POVS_PACKET_HDR_INFO layersDeferred = NULL; + + /* Process all deferred actions. */ + while ((deferredAction = OvsDeferredActionsQueuePop(queue)) != NULL) { ++ if (layers) { ++ layersDeferred = layers; ++ } else { ++ layersDeferred = &(deferredAction->layers); ++ } ++ + if (deferredAction->actions) { + status = OvsDoExecuteActions(switchContext, + completionList, +@@ -319,7 +333,7 @@ OvsProcessDeferredActions(POVS_SWITCH_CONTEXT switchContext, + portNo, + sendFlags, + &deferredAction->key, NULL, +- layers, deferredAction->actions, ++ layersDeferred, deferredAction->actions, + NlAttrGetSize(deferredAction->actions)); + } else { + status = OvsDoRecirc(switchContext, +@@ -327,7 +341,7 @@ OvsProcessDeferredActions(POVS_SWITCH_CONTEXT switchContext, + deferredAction->nbl, + &deferredAction->key, + portNo, +- layers); ++ layersDeferred); + } + } + +diff --git a/datapath-windows/ovsext/Recirc.h b/datapath-windows/ovsext/Recirc.h +index 2b314ce274..74130a4600 100644 +--- a/datapath-windows/ovsext/Recirc.h ++++ b/datapath-windows/ovsext/Recirc.h +@@ -18,6 +18,7 @@ + #define __RECIRC_H_ 1 + + #include "Actions.h" ++#include "NetProto.h" + + #define DEFERRED_ACTION_QUEUE_SIZE 10 + #define DEFERRED_ACTION_EXEC_LEVEL 4 +@@ -26,6 +27,7 @@ typedef struct _OVS_DEFERRED_ACTION { + PNET_BUFFER_LIST nbl; + PNL_ATTR actions; + OvsFlowKey key; ++ OVS_PACKET_HDR_INFO layers; + } OVS_DEFERRED_ACTION, *POVS_DEFERRED_ACTION; + + /* +@@ -52,6 +54,7 @@ OvsProcessDeferredActions(POVS_SWITCH_CONTEXT switchContext, + POVS_DEFERRED_ACTION + OvsAddDeferredActions(PNET_BUFFER_LIST packet, + OvsFlowKey *key, ++ POVS_PACKET_HDR_INFO layers, + const PNL_ATTR actions); + + /* diff --git a/debian/changelog b/debian/changelog -index 239d210b96..0f521be4d8 100644 +index 239d210b96..a0838ea3d7 100644 --- a/debian/changelog +++ b/debian/changelog -@@ -1,3 +1,9 @@ +@@ -1,3 +1,15 @@ ++openvswitch (2.16.2-1) unstable; urgency=low ++ [ Open vSwitch team ] ++ * New upstream version ++ ++ -- Open vSwitch team Thu, 21 Oct 2021 23:58:12 +0200 ++ +openvswitch (2.16.1-1) unstable; urgency=low + [ Open vSwitch team ] + * New upstream version + -+ -- Open vSwitch team Mon, 16 Aug 2021 22:08:13 +0200 ++ -- Open vSwitch team Thu, 21 Oct 2021 23:58:12 +0200 + openvswitch (2.16.0-1) unstable; urgency=low @@ -84,6 +372,116 @@ index 73b562e03d..0831a9cee1 100644 struct json *json_from_file(const char *file_name); struct json *json_from_stream(FILE *stream); +diff --git a/include/openvswitch/meta-flow.h b/include/openvswitch/meta-flow.h +index 95e52e3587..045dce8f5f 100644 +--- a/include/openvswitch/meta-flow.h ++++ b/include/openvswitch/meta-flow.h +@@ -2305,6 +2305,7 @@ void mf_set_flow_value_masked(const struct mf_field *, + const union mf_value *mask, + struct flow *); + bool mf_is_tun_metadata(const struct mf_field *); ++bool mf_is_frozen_metadata(const struct mf_field *); + bool mf_is_pipeline_field(const struct mf_field *); + bool mf_is_set(const struct mf_field *, const struct flow *); + void mf_mask_field(const struct mf_field *, struct flow_wildcards *); +diff --git a/lib/db-ctl-base.c b/lib/db-ctl-base.c +index 77cc76a9f6..7074561588 100644 +--- a/lib/db-ctl-base.c ++++ b/lib/db-ctl-base.c +@@ -247,15 +247,15 @@ record_id_equals(const union ovsdb_atom *name, enum ovsdb_atomic_type type, + const char *record_id) + { + if (type == OVSDB_TYPE_STRING) { +- if (!strcmp(name->string, record_id)) { ++ if (!strcmp(name->s->string, record_id)) { + return true; + } + + struct uuid uuid; + size_t len = strlen(record_id); + if (len >= 4 +- && uuid_from_string(&uuid, name->string) +- && !strncmp(name->string, record_id, len)) { ++ && uuid_from_string(&uuid, name->s->string) ++ && !strncmp(name->s->string, record_id, len)) { + return true; + } + +@@ -314,15 +314,19 @@ get_row_by_id(struct ctl_context *ctx, + row, id->name_column, key, value); + + /* Extract the name from the column. */ +- const union ovsdb_atom *name; ++ const union ovsdb_atom *name = NULL; + if (!id->key) { + name = datum->n == 1 ? &datum->keys[0] : NULL; + } else { +- const union ovsdb_atom key_atom +- = { .string = CONST_CAST(char *, id->key) }; +- unsigned int i = ovsdb_datum_find_key(datum, &key_atom, +- OVSDB_TYPE_STRING); +- name = i == UINT_MAX ? NULL : &datum->values[i]; ++ union ovsdb_atom key_atom = { ++ .s = ovsdb_atom_string_create(CONST_CAST(char *, id->key)) }; ++ unsigned int i; ++ ++ if (ovsdb_datum_find_key(datum, &key_atom, ++ OVSDB_TYPE_STRING, &i)) { ++ name = &datum->values[i]; ++ } ++ ovsdb_atom_destroy(&key_atom, OVSDB_TYPE_STRING); + } + if (!name) { + continue; +@@ -819,14 +823,14 @@ check_condition(const struct ovsdb_idl_table_class *table, + goto out; + } + +- idx = ovsdb_datum_find_key(have_datum, +- &want_key, column->type.key.type); +- if (idx == UINT_MAX && !is_set_operator(operator)) { ++ bool found = ovsdb_datum_find_key(have_datum, &want_key, ++ column->type.key.type, &idx); ++ if (!found && !is_set_operator(operator)) { + retval = false; + } else { + struct ovsdb_datum a; + +- if (idx != UINT_MAX) { ++ if (found) { + a.n = 1; + a.keys = &have_datum->values[idx]; + a.values = NULL; +@@ -992,9 +996,8 @@ cmd_get(struct ctl_context *ctx) + return; + } + +- idx = ovsdb_datum_find_key(datum, &key, +- column->type.key.type); +- if (idx == UINT_MAX) { ++ if (!ovsdb_datum_find_key(datum, &key, ++ column->type.key.type, &idx)) { + if (must_exist) { + ctl_error( + ctx, "no key \"%s\" in %s record \"%s\" column %s", +@@ -1375,7 +1378,7 @@ set_column(const struct ovsdb_idl_table_class *table, + ovsdb_atom_destroy(&value, column->type.value.type); + + ovsdb_datum_union(&datum, ovsdb_idl_read(row, column), +- &column->type, false); ++ &column->type); + ovsdb_idl_txn_verify(row, column); + ovsdb_idl_txn_write(row, column, &datum); + } else { +@@ -1514,7 +1517,7 @@ cmd_add(struct ctl_context *ctx) + ovsdb_datum_destroy(&old, &column->type); + return; + } +- ovsdb_datum_union(&old, &add, type, false); ++ ovsdb_datum_union(&old, &add, type); + ovsdb_datum_destroy(&add, type); + } + if (old.n > type->n_max) { diff --git a/lib/dp-packet.h b/lib/dp-packet.h index 08d93c2779..3dc582fbfd 100644 --- a/lib/dp-packet.h @@ -115,8 +513,54 @@ index 08d93c2779..3dc582fbfd 100644 /* If 'b' contains at least 'offset + size' bytes of data, returns a pointer to * byte 'offset'. Otherwise, returns a null pointer. */ static inline void * +diff --git a/lib/dpdk-stub.c b/lib/dpdk-stub.c +index b7d577870d..fe24f9abdf 100644 +--- a/lib/dpdk-stub.c ++++ b/lib/dpdk-stub.c +@@ -83,7 +83,7 @@ bool + dpdk_get_cpu_has_isa(const char *arch OVS_UNUSED, + const char *feature OVS_UNUSED) + { +- VLOG_ERR_ONCE("DPDK not supported in this version of Open vSwitch, " ++ VLOG_DBG_ONCE("DPDK not supported in this version of Open vSwitch, " + "cannot use CPU flag based optimizations"); + return false; + } +diff --git a/lib/dpif-netdev-private-dfc.h b/lib/dpif-netdev-private-dfc.h +index 92092ebec9..3dfc91f0fe 100644 +--- a/lib/dpif-netdev-private-dfc.h ++++ b/lib/dpif-netdev-private-dfc.h +@@ -59,7 +59,8 @@ extern "C" { + * Thread-safety + * ============= + * +- * Each pmd_thread has its own private exact match cache. ++ * Each pmd_thread has its own private exact match cache and signature match ++ * cache. + * If dp_netdev_input is not called from a pmd thread, a mutex is used. + */ + +diff --git a/lib/dpif-netdev-private-thread.h b/lib/dpif-netdev-private-thread.h +index a782d9678a..ac4885538c 100644 +--- a/lib/dpif-netdev-private-thread.h ++++ b/lib/dpif-netdev-private-thread.h +@@ -78,10 +78,10 @@ struct dp_netdev_pmd_thread { + struct ovs_refcount ref_cnt; /* Every reference must be refcount'ed. */ + struct cmap_node node; /* In 'dp->poll_threads'. */ + +- /* Per thread exact-match cache. Note, the instance for cpu core +- * NON_PMD_CORE_ID can be accessed by multiple threads, and thusly +- * need to be protected by 'non_pmd_mutex'. Every other instance +- * will only be accessed by its own pmd thread. */ ++ /* Per thread exact match cache and signature match cache. Note, the ++ * instance for cpu core NON_PMD_CORE_ID can be accessed by multiple ++ * threads, and thusly need to be protected by 'non_pmd_mutex'. Every ++ * other instance will only be accessed by its own pmd thread. */ + OVS_ALIGNED_VAR(CACHE_LINE_SIZE) struct dfc_cache flow_cache; + + /* Flow-Table and classifiers diff --git a/lib/dpif-netdev.c b/lib/dpif-netdev.c -index bddce75b63..f9782b596f 100644 +index bddce75b63..d6bee2a5a9 100644 --- a/lib/dpif-netdev.c +++ b/lib/dpif-netdev.c @@ -4061,7 +4061,10 @@ dpif_netdev_execute(struct dpif *dpif, struct dpif_execute *execute) @@ -131,26 +575,143 @@ index bddce75b63..f9782b596f 100644 dp_netdev_execute_actions(pmd, &pp, false, execute->flow, execute->actions, execute->actions_len); dp_netdev_pmd_flush_output_packets(pmd, true); -@@ -4071,6 +4074,14 @@ dpif_netdev_execute(struct dpif *dpif, struct dpif_execute *execute) +@@ -4071,6 +4074,24 @@ dpif_netdev_execute(struct dpif *dpif, struct dpif_execute *execute) dp_netdev_pmd_unref(pmd); } -+ if (dp_packet_batch_size(&pp)) { ++ if (dp_packet_batch_size(&pp) == 1) { + /* Packet wasn't dropped during the execution. Swapping content with + * the original packet, because the caller might expect actions to -+ * modify it. */ -+ dp_packet_swap(execute->packet, packet_clone); ++ * modify it. Uisng the packet from a batch instead of 'packet_clone' ++ * because it maybe stolen and replaced by other packet, e.g. by ++ * the fragmentation engine. */ ++ dp_packet_swap(execute->packet, pp.packets[0]); ++ dp_packet_delete_batch(&pp, true); ++ } else if (dp_packet_batch_size(&pp)) { ++ /* FIXME: We have more packets than expected. Likely, we got IP ++ * fragments of the reassembled packet. Dropping them here as we have ++ * no way to get them to the caller. It might be that all the required ++ * actions with them are already executed, but it also might not be a ++ * case, e.g. if dpif_netdev_execute() called to execute a single ++ * tunnel push. */ + dp_packet_delete_batch(&pp, true); + } + return 0; } +diff --git a/lib/dpif-netlink.c b/lib/dpif-netlink.c +index 34fc042373..5f4b60c5a6 100644 +--- a/lib/dpif-netlink.c ++++ b/lib/dpif-netlink.c +@@ -84,6 +84,8 @@ enum { MAX_PORTS = USHRT_MAX }; + #define EPOLLEXCLUSIVE (1u << 28) + #endif + ++#define OVS_DP_F_UNSUPPORTED (1 << 31); ++ + /* This PID is not used by the kernel datapath when using dispatch per CPU, + * but it is required to be set (not zero). */ + #define DPIF_NETLINK_PER_CPU_PID UINT32_MAX +@@ -382,36 +384,62 @@ dpif_netlink_open(const struct dpif_class *class OVS_UNUSED, const char *name, + dp_request.cmd = OVS_DP_CMD_SET; + } + +- /* The Open vSwitch kernel module has two modes for dispatching upcalls: +- * per-vport and per-cpu. +- * +- * When dispatching upcalls per-vport, the kernel will +- * send the upcall via a Netlink socket that has been selected based on the +- * vport that received the packet that is causing the upcall. +- * +- * When dispatching upcall per-cpu, the kernel will send the upcall via +- * a Netlink socket that has been selected based on the cpu that received +- * the packet that is causing the upcall. +- * +- * First we test to see if the kernel module supports per-cpu dispatching +- * (the preferred method). If it does not support per-cpu dispatching, we +- * fall back to the per-vport dispatch mode. ++ /* Some older kernels will not reject unknown features. This will cause ++ * 'ovs-vswitchd' to incorrectly assume a feature is supported. In order to ++ * test for that, we attempt to set a feature that we know is not supported ++ * by any kernel. If this feature is not rejected, we can assume we are ++ * running on one of these older kernels. + */ + dp_request.user_features |= OVS_DP_F_UNALIGNED; +- dp_request.user_features &= ~OVS_DP_F_VPORT_PIDS; +- dp_request.user_features |= OVS_DP_F_DISPATCH_UPCALL_PER_CPU; ++ dp_request.user_features |= OVS_DP_F_VPORT_PIDS; ++ dp_request.user_features |= OVS_DP_F_UNSUPPORTED; + error = dpif_netlink_dp_transact(&dp_request, &dp, &buf); + if (error) { +- dp_request.user_features &= ~OVS_DP_F_DISPATCH_UPCALL_PER_CPU; ++ /* The Open vSwitch kernel module has two modes for dispatching ++ * upcalls: per-vport and per-cpu. ++ * ++ * When dispatching upcalls per-vport, the kernel will ++ * send the upcall via a Netlink socket that has been selected based on ++ * the vport that received the packet that is causing the upcall. ++ * ++ * When dispatching upcall per-cpu, the kernel will send the upcall via ++ * a Netlink socket that has been selected based on the cpu that ++ * received the packet that is causing the upcall. ++ * ++ * First we test to see if the kernel module supports per-cpu ++ * dispatching (the preferred method). If it does not support per-cpu ++ * dispatching, we fall back to the per-vport dispatch mode. ++ */ ++ dp_request.user_features &= ~OVS_DP_F_UNSUPPORTED; ++ dp_request.user_features |= OVS_DP_F_UNALIGNED; ++ dp_request.user_features &= ~OVS_DP_F_VPORT_PIDS; ++ dp_request.user_features |= OVS_DP_F_DISPATCH_UPCALL_PER_CPU; ++ error = dpif_netlink_dp_transact(&dp_request, &dp, &buf); ++ if (error) { ++ dp_request.user_features &= ~OVS_DP_F_DISPATCH_UPCALL_PER_CPU; ++ dp_request.user_features |= OVS_DP_F_VPORT_PIDS; ++ error = dpif_netlink_dp_transact(&dp_request, &dp, &buf); ++ } ++ if (error) { ++ return error; ++ } ++ ++ error = open_dpif(&dp, dpifp); ++ dpif_netlink_set_features(*dpifp, OVS_DP_F_TC_RECIRC_SHARING); ++ } else { ++ VLOG_INFO("Kernel does not correctly support feature negotiation. " ++ "Using standard features."); ++ dp_request.cmd = OVS_DP_CMD_SET; ++ dp_request.user_features = 0; ++ dp_request.user_features |= OVS_DP_F_UNALIGNED; + dp_request.user_features |= OVS_DP_F_VPORT_PIDS; + error = dpif_netlink_dp_transact(&dp_request, &dp, &buf); +- } +- if (error) { +- return error; ++ if (error) { ++ return error; ++ } ++ error = open_dpif(&dp, dpifp); + } + +- error = open_dpif(&dp, dpifp); +- dpif_netlink_set_features(*dpifp, OVS_DP_F_TC_RECIRC_SHARING); + ofpbuf_delete(buf); + + if (create) { diff --git a/lib/ipf.c b/lib/ipf.c -index d9f781147a..665f40fefe 100644 +index d9f781147a..507db2aea2 100644 --- a/lib/ipf.c +++ b/lib/ipf.c -@@ -1152,52 +1152,56 @@ ipf_post_execute_reass_pkts(struct ipf *ipf, +@@ -943,6 +943,8 @@ ipf_extract_frags_from_batch(struct ipf *ipf, struct dp_packet_batch *pb, + ovs_mutex_lock(&ipf->ipf_lock); + if (!ipf_handle_frag(ipf, pkt, dl_type, zone, now, hash_basis)) { + dp_packet_batch_refill(pb, pkt, pb_idx); ++ } else { ++ dp_packet_delete(pkt); + } + ovs_mutex_unlock(&ipf->ipf_lock); + } else { +@@ -1152,52 +1154,56 @@ ipf_post_execute_reass_pkts(struct ipf *ipf, * NETDEV_MAX_BURST. */ DP_PACKET_BATCH_REFILL_FOR_EACH (pb_idx, pb_cnt, pkt, pb) { if (rp && pkt == rp->list->reass_execute_ctx) { @@ -380,6 +941,30 @@ index 32d25003b8..0baf7c622c 100644 + } ds_put_char(ds, '"'); } +diff --git a/lib/meta-flow.c b/lib/meta-flow.c +index c808d205d5..e03cd8d0c5 100644 +--- a/lib/meta-flow.c ++++ b/lib/meta-flow.c +@@ -1788,6 +1788,19 @@ mf_is_tun_metadata(const struct mf_field *mf) + mf->id < MFF_TUN_METADATA0 + TUN_METADATA_NUM_OPTS; + } + ++bool ++mf_is_frozen_metadata(const struct mf_field *mf) ++{ ++ if (mf->id >= MFF_TUN_ID && mf->id <= MFF_IN_PORT_OXM) { ++ return true; ++ } ++ ++ if (mf->id >= MFF_REG0 && mf->id < MFF_ETH_SRC) { ++ return true; ++ } ++ return false; ++} ++ + bool + mf_is_pipeline_field(const struct mf_field *mf) + { diff --git a/lib/netdev-dpdk.c b/lib/netdev-dpdk.c index 45a96b9be2..ca92c947a2 100644 --- a/lib/netdev-dpdk.c @@ -453,6 +1038,743 @@ index 7729a90608..fbdfc7ad83 100644 const struct nlattr *ma = nl_attr_find__(mask, mask_len, OVS_KEY_ATTR_ETHERTYPE); if (ma) { +diff --git a/lib/ovsdb-cs.c b/lib/ovsdb-cs.c +index 659d49dbf7..fcb6fe1b34 100644 +--- a/lib/ovsdb-cs.c ++++ b/lib/ovsdb-cs.c +@@ -1833,7 +1833,7 @@ server_column_get_string(const struct server_row *row, + { + ovs_assert(server_columns[index].type.key.type == OVSDB_TYPE_STRING); + const struct ovsdb_datum *d = &row->data[index]; +- return d->n == 1 ? d->keys[0].string : default_value; ++ return d->n == 1 ? d->keys[0].s->string : default_value; + } + + static bool +diff --git a/lib/ovsdb-data.c b/lib/ovsdb-data.c +index c145f5ad97..6654ed6deb 100644 +--- a/lib/ovsdb-data.c ++++ b/lib/ovsdb-data.c +@@ -74,7 +74,7 @@ ovsdb_atom_init_default(union ovsdb_atom *atom, enum ovsdb_atomic_type type) + break; + + case OVSDB_TYPE_STRING: +- atom->string = xmemdup("", 1); ++ atom->s = ovsdb_atom_string_create_nocopy(xmemdup("", 1)); + break; + + case OVSDB_TYPE_UUID: +@@ -136,7 +136,7 @@ ovsdb_atom_is_default(const union ovsdb_atom *atom, + return atom->boolean == false; + + case OVSDB_TYPE_STRING: +- return atom->string[0] == '\0'; ++ return atom->s->string[0] == '\0'; + + case OVSDB_TYPE_UUID: + return uuid_is_zero(&atom->uuid); +@@ -172,7 +172,8 @@ ovsdb_atom_clone(union ovsdb_atom *new, const union ovsdb_atom *old, + break; + + case OVSDB_TYPE_STRING: +- new->string = xstrdup(old->string); ++ new->s = old->s; ++ new->s->n_refs++; + break; + + case OVSDB_TYPE_UUID: +@@ -214,7 +215,7 @@ ovsdb_atom_hash(const union ovsdb_atom *atom, enum ovsdb_atomic_type type, + return hash_boolean(atom->boolean, basis); + + case OVSDB_TYPE_STRING: +- return hash_string(atom->string, basis); ++ return hash_string(atom->s->string, basis); + + case OVSDB_TYPE_UUID: + return hash_int(uuid_hash(&atom->uuid), basis); +@@ -246,7 +247,7 @@ ovsdb_atom_compare_3way(const union ovsdb_atom *a, + return a->boolean - b->boolean; + + case OVSDB_TYPE_STRING: +- return strcmp(a->string, b->string); ++ return a->s == b->s ? 0 : strcmp(a->s->string, b->s->string); + + case OVSDB_TYPE_UUID: + return uuid_compare_3way(&a->uuid, &b->uuid); +@@ -404,7 +405,7 @@ ovsdb_atom_from_json__(union ovsdb_atom *atom, + + case OVSDB_TYPE_STRING: + if (json->type == JSON_STRING) { +- atom->string = xstrdup(json->string); ++ atom->s = ovsdb_atom_string_create(json->string); + return NULL; + } + break; +@@ -473,7 +474,7 @@ ovsdb_atom_to_json(const union ovsdb_atom *atom, enum ovsdb_atomic_type type) + return json_boolean_create(atom->boolean); + + case OVSDB_TYPE_STRING: +- return json_string_create(atom->string); ++ return json_string_create(atom->s->string); + + case OVSDB_TYPE_UUID: + return wrap_json("uuid", json_string_create_nocopy( +@@ -551,14 +552,18 @@ ovsdb_atom_from_string__(union ovsdb_atom *atom, + if (s_len < 2 || s[s_len - 1] != '"') { + return xasprintf("%s: missing quote at end of " + "quoted string", s); +- } else if (!json_string_unescape(s + 1, s_len - 2, +- &atom->string)) { +- char *error = xasprintf("%s: %s", s, atom->string); +- free(atom->string); +- return error; ++ } else { ++ char *res; ++ if (json_string_unescape(s + 1, s_len - 2, &res)) { ++ atom->s = ovsdb_atom_string_create_nocopy(res); ++ } else { ++ char *error = xasprintf("%s: %s", s, res); ++ free(res); ++ return error; ++ } + } + } else { +- atom->string = xstrdup(s); ++ atom->s = ovsdb_atom_string_create(s); + } + break; + +@@ -721,14 +726,14 @@ ovsdb_atom_to_string(const union ovsdb_atom *atom, enum ovsdb_atomic_type type, + break; + + case OVSDB_TYPE_STRING: +- if (string_needs_quotes(atom->string)) { ++ if (string_needs_quotes(atom->s->string)) { + struct json json; + + json.type = JSON_STRING; +- json.string = atom->string; ++ json.string = atom->s->string; + json_to_ds(&json, 0, out); + } else { +- ds_put_cstr(out, atom->string); ++ ds_put_cstr(out, atom->s->string); + } + break; + +@@ -750,7 +755,7 @@ ovsdb_atom_to_bare(const union ovsdb_atom *atom, enum ovsdb_atomic_type type, + struct ds *out) + { + if (type == OVSDB_TYPE_STRING) { +- ds_put_cstr(out, atom->string); ++ ds_put_cstr(out, atom->s->string); + } else { + ovsdb_atom_to_string(atom, type, out); + } +@@ -799,7 +804,7 @@ ovsdb_atom_check_constraints(const union ovsdb_atom *atom, + const struct ovsdb_base_type *base) + { + if (base->enum_ +- && ovsdb_datum_find_key(base->enum_, atom, base->type) == UINT_MAX) { ++ && !ovsdb_datum_find_key(base->enum_, atom, base->type, NULL)) { + struct ovsdb_error *error; + struct ds actual = DS_EMPTY_INITIALIZER; + struct ds valid = DS_EMPTY_INITIALIZER; +@@ -877,7 +882,7 @@ ovsdb_atom_check_constraints(const union ovsdb_atom *atom, + return NULL; + + case OVSDB_TYPE_STRING: +- return check_string_constraints(atom->string, &base->string); ++ return check_string_constraints(atom->s->string, &base->string); + + case OVSDB_TYPE_UUID: + return NULL; +@@ -1691,8 +1696,8 @@ ovsdb_datum_from_smap(struct ovsdb_datum *datum, const struct smap *smap) + struct smap_node *node; + size_t i = 0; + SMAP_FOR_EACH (node, smap) { +- datum->keys[i].string = xstrdup(node->key); +- datum->values[i].string = xstrdup(node->value); ++ datum->keys[i].s = ovsdb_atom_string_create(node->key); ++ datum->values[i].s = ovsdb_atom_string_create(node->value); + i++; + } + ovs_assert(i == datum->n); +@@ -1784,14 +1789,16 @@ ovsdb_datum_compare_3way(const struct ovsdb_datum *a, + a->n)); + } + +-/* If 'key' is one of the keys in 'datum', returns its index within 'datum', +- * otherwise UINT_MAX. 'key.type' must be the type of the atoms stored in the +- * 'keys' array in 'datum'. ++/* If 'key' is one of the keys in 'datum', returns 'true' and sets '*pos' to ++ * its index within 'datum', otherwise returns 'false' and sets '*pos' to the ++ * index where 'key' should have been. 'key.type' must be the type of the ++ * atoms stored in the 'keys' array in 'datum'. + */ +-unsigned int ++bool + ovsdb_datum_find_key(const struct ovsdb_datum *datum, + const union ovsdb_atom *key, +- enum ovsdb_atomic_type key_type) ++ enum ovsdb_atomic_type key_type, ++ unsigned int *pos) + { + unsigned int low = 0; + unsigned int high = datum->n; +@@ -1803,10 +1810,16 @@ ovsdb_datum_find_key(const struct ovsdb_datum *datum, + } else if (cmp > 0) { + low = idx + 1; + } else { +- return idx; ++ if (pos) { ++ *pos = idx; ++ } ++ return true; + } + } +- return UINT_MAX; ++ if (pos) { ++ *pos = low; ++ } ++ return false; + } + + /* If 'key' and 'value' is one of the key-value pairs in 'datum', returns its +@@ -1821,10 +1834,11 @@ ovsdb_datum_find_key_value(const struct ovsdb_datum *datum, + const union ovsdb_atom *value, + enum ovsdb_atomic_type value_type) + { +- unsigned int idx = ovsdb_datum_find_key(datum, key, key_type); +- if (idx != UINT_MAX +- && value_type != OVSDB_TYPE_VOID +- && !ovsdb_atom_equals(&datum->values[idx], value, value_type)) { ++ unsigned int idx; ++ ++ if (!ovsdb_datum_find_key(datum, key, key_type, &idx) ++ || (value_type != OVSDB_TYPE_VOID ++ && !ovsdb_atom_equals(&datum->values[idx], value, value_type))) { + idx = UINT_MAX; + } + return idx; +@@ -1948,38 +1962,68 @@ ovsdb_datum_add_unsafe(struct ovsdb_datum *datum, + } + } + ++/* Adds 'n' atoms starting from index 'start_idx' from 'src' to the end of ++ * 'dst'. 'dst' should have enough memory allocated to hold the additional ++ * 'n' atoms. Atoms are not cloned, i.e. 'dst' will reference the same data. ++ * Caller also should take care of the result being sorted. */ ++static void ++ovsdb_datum_push_unsafe(struct ovsdb_datum *dst, ++ const struct ovsdb_datum *src, ++ unsigned int start_idx, unsigned int n, ++ const struct ovsdb_type *type) ++{ ++ memcpy(&dst->keys[dst->n], &src->keys[start_idx], n * sizeof src->keys[0]); ++ if (type->value.type != OVSDB_TYPE_VOID) { ++ memcpy(&dst->values[dst->n], &src->values[start_idx], ++ n * sizeof src->values[0]); ++ } ++ dst->n += n; ++} ++ + void + ovsdb_datum_union(struct ovsdb_datum *a, const struct ovsdb_datum *b, +- const struct ovsdb_type *type, bool replace) ++ const struct ovsdb_type *type) + { +- unsigned int n; +- size_t bi; ++ struct ovsdb_datum result; ++ unsigned int copied, pos; + +- n = a->n; +- for (bi = 0; bi < b->n; bi++) { +- unsigned int ai; ++ ovsdb_datum_init_empty(&result); + +- ai = ovsdb_datum_find_key(a, &b->keys[bi], type->key.type); +- if (ai == UINT_MAX) { +- if (n == a->n) { +- ovsdb_datum_reallocate(a, type, a->n + (b->n - bi)); +- } +- ovsdb_atom_clone(&a->keys[n], &b->keys[bi], type->key.type); +- if (type->value.type != OVSDB_TYPE_VOID) { +- ovsdb_atom_clone(&a->values[n], &b->values[bi], +- type->value.type); +- } +- n++; +- } else if (replace && type->value.type != OVSDB_TYPE_VOID) { +- ovsdb_atom_destroy(&a->values[ai], type->value.type); +- ovsdb_atom_clone(&a->values[ai], &b->values[bi], ++ copied = 0; ++ for (size_t bi = 0; bi < b->n; bi++) { ++ if (ovsdb_datum_find_key(a, &b->keys[bi], type->key.type, &pos)) { ++ /* Atom with the same key already exists. */ ++ continue; ++ } ++ if (!result.keys) { ++ ovsdb_datum_reallocate(&result, type, a->n + (b->n - bi)); ++ } ++ if (pos > copied) { ++ /* Need to copy some atoms from 'a' first. */ ++ ovsdb_datum_push_unsafe(&result, a, copied, pos - copied, type); ++ copied = pos; ++ } ++ /* Inserting new atom from 'b'. */ ++ ovsdb_atom_clone(&result.keys[result.n], &b->keys[bi], type->key.type); ++ if (type->value.type != OVSDB_TYPE_VOID) { ++ ovsdb_atom_clone(&result.values[result.n], &b->values[bi], + type->value.type); + } ++ result.n++; + } +- if (n != a->n) { +- a->n = n; +- ovs_assert(!ovsdb_datum_sort(a, type->key.type)); ++ if (!result.keys) { ++ /* 'a' doesn't need to be changed. */ ++ return; ++ } ++ if (a->n > copied) { ++ /* Copying remaining atoms. */ ++ ovsdb_datum_push_unsafe(&result, a, copied, a->n - copied, type); + } ++ /* All atoms are copied now. */ ++ a->n = 0; ++ ++ ovsdb_datum_swap(&result, a); ++ ovsdb_datum_destroy(&result, type); + } + + void +@@ -1987,26 +2031,55 @@ ovsdb_datum_subtract(struct ovsdb_datum *a, const struct ovsdb_type *a_type, + const struct ovsdb_datum *b, + const struct ovsdb_type *b_type) + { +- bool changed = false; +- size_t i; ++ unsigned int *idx, ai; ++ size_t n_idx; + + ovs_assert(a_type->key.type == b_type->key.type); + ovs_assert(a_type->value.type == b_type->value.type + || b_type->value.type == OVSDB_TYPE_VOID); + +- /* XXX The big-O of this could easily be improved. */ +- for (i = 0; i < a->n; ) { +- unsigned int idx = ovsdb_datum_find(a, i, b, b_type); +- if (idx != UINT_MAX) { +- changed = true; +- ovsdb_datum_remove_unsafe(a, i, a_type); +- } else { +- i++; ++ idx = xmalloc(b->n * sizeof *idx); ++ n_idx = 0; ++ for (size_t bi = 0; bi < b->n; bi++) { ++ ai = ovsdb_datum_find(b, bi, a, b_type); ++ if (ai == UINT_MAX) { ++ /* No such atom in 'a'. */ ++ continue; + } ++ /* Not destroying right away since ovsdb_datum_find() will use them. */ ++ idx[n_idx++] = ai; + } +- if (changed) { +- ovsdb_datum_sort_assert(a, a_type->key.type); ++ if (!n_idx) { ++ free(idx); ++ return; ++ } ++ ++ struct ovsdb_datum result; ++ ++ ovsdb_datum_init_empty(&result); ++ ovsdb_datum_reallocate(&result, a_type, a->n - n_idx); ++ ++ unsigned int start_idx = 0; ++ for (size_t i = 0; i < n_idx; i++) { ++ ai = idx[i]; ++ ++ /* Destroying atom. */ ++ ovsdb_atom_destroy(&a->keys[ai], a_type->key.type); ++ if (a_type->value.type != OVSDB_TYPE_VOID) { ++ ovsdb_atom_destroy(&a->values[ai], a_type->value.type); ++ } ++ ++ /* Copy non-removed atoms from 'a' to result. */ ++ ovsdb_datum_push_unsafe(&result, a, start_idx, ai - start_idx, a_type); ++ start_idx = idx[i] + 1; + } ++ /* Copying remaining atoms. */ ++ ovsdb_datum_push_unsafe(&result, a, start_idx, a->n - start_idx, a_type); ++ a->n = 0; ++ ++ ovsdb_datum_swap(&result, a); ++ ovsdb_datum_destroy(&result, a_type); ++ free(idx); + } + + struct ovsdb_symbol_table * +@@ -2067,6 +2140,64 @@ ovsdb_symbol_table_insert(struct ovsdb_symbol_table *symtab, + + /* APIs for Generating and apply diffs. */ + ++/* Find what needs to be added to and removed from 'old' to construct 'new'. ++ * ++ * The 'added' and 'removed' datums are always safe; the orders of keys are ++ * maintained since they are added in order. */ ++void ++ovsdb_datum_added_removed(struct ovsdb_datum *added, ++ struct ovsdb_datum *removed, ++ const struct ovsdb_datum *old, ++ const struct ovsdb_datum *new, ++ const struct ovsdb_type *type) ++{ ++ size_t oi, ni; ++ ++ ovsdb_datum_init_empty(added); ++ ovsdb_datum_init_empty(removed); ++ if (!ovsdb_type_is_composite(type)) { ++ ovsdb_datum_clone(removed, old, type); ++ ovsdb_datum_clone(added, new, type); ++ return; ++ } ++ ++ /* Generate the diff in O(n) time. */ ++ for (oi = ni = 0; oi < old->n && ni < new->n;) { ++ int c = ovsdb_atom_compare_3way(&old->keys[oi], &new->keys[ni], ++ type->key.type); ++ if (c < 0) { ++ ovsdb_datum_add_unsafe(removed, &old->keys[oi], &old->values[oi], ++ type, NULL); ++ oi++; ++ } else if (c > 0) { ++ ovsdb_datum_add_unsafe(added, &new->keys[ni], &new->values[ni], ++ type, NULL); ++ ni++; ++ } else { ++ if (type->value.type != OVSDB_TYPE_VOID && ++ ovsdb_atom_compare_3way(&old->values[oi], &new->values[ni], ++ type->value.type)) { ++ ovsdb_datum_add_unsafe(removed, &old->keys[oi], ++ &old->values[oi], type, NULL); ++ ovsdb_datum_add_unsafe(added, &new->keys[ni], &new->values[ni], ++ type, NULL); ++ } ++ oi++; ni++; ++ } ++ } ++ ++ for (; oi < old->n; oi++) { ++ ovsdb_datum_add_unsafe(removed, &old->keys[oi], &old->values[oi], ++ type, NULL); ++ } ++ ++ for (; ni < new->n; ni++) { ++ ovsdb_datum_add_unsafe(added, &new->keys[ni], &new->values[ni], ++ type, NULL); ++ } ++} ++ ++ + /* Generate a difference ovsdb_dataum between 'old' and 'new'. + * 'new' can be regenerated by applying the difference to the 'old'. + * +@@ -2127,6 +2258,106 @@ ovsdb_datum_diff(struct ovsdb_datum *diff, + } + } + ++/* Apply 'diff' to 'a'. ++ * ++ * Return NULL if the 'a' is successfully updated, otherwise, return ++ * ovsdb_error. */ ++struct ovsdb_error * ++ovsdb_datum_apply_diff_in_place(struct ovsdb_datum *a, ++ const struct ovsdb_datum *diff, ++ const struct ovsdb_type *type) ++{ ++ struct ovsdb_error *error = NULL; ++ struct ovsdb_datum result; ++ size_t i, new_size; ++ unsigned int *idx, pos; ++ enum { ++ DIFF_OP_ADD, ++ DIFF_OP_REMOVE, ++ DIFF_OP_UPDATE, ++ } *operation; ++ ++ if (!ovsdb_type_is_composite(type)) { ++ ovsdb_datum_destroy(a, type); ++ ovsdb_datum_clone(a, diff, type); ++ return NULL; ++ } ++ ++ operation = xmalloc(diff->n * sizeof *operation); ++ idx = xmalloc(diff->n * sizeof *idx); ++ new_size = a->n; ++ for (i = 0; i < diff->n; i++) { ++ if (!ovsdb_datum_find_key(a, &diff->keys[i], type->key.type, &pos)) { ++ operation[i] = DIFF_OP_ADD; ++ new_size++; ++ } else if (type->value.type != OVSDB_TYPE_VOID ++ && !ovsdb_atom_equals(&diff->values[i], &a->values[pos], ++ type->value.type)) { ++ operation[i] = DIFF_OP_UPDATE; ++ } else { ++ operation[i] = DIFF_OP_REMOVE; ++ new_size--; ++ } ++ idx[i] = pos; ++ } ++ ++ /* Make sure member size of 'new' conforms to type. */ ++ if (new_size < type->n_min || new_size > type->n_max) { ++ error = ovsdb_error(NULL, "Datum crated by diff has size error"); ++ goto exit; ++ } ++ ++ ovsdb_datum_init_empty(&result); ++ ovsdb_datum_reallocate(&result, type, new_size); ++ ++ unsigned int copied = 0; ++ for (i = 0; i < diff->n; i++) { ++ pos = idx[i]; ++ ++ if (copied < pos) { ++ /* Copying all atoms that should go before the current one. */ ++ ovsdb_datum_push_unsafe(&result, a, copied, pos - copied, type); ++ copied = pos; ++ } ++ ++ switch (operation[i]) { ++ case DIFF_OP_UPDATE: ++ case DIFF_OP_ADD: ++ /* Inserting new atom from 'diff'. */ ++ ovsdb_atom_clone(&result.keys[result.n], ++ &diff->keys[i], type->key.type); ++ if (type->value.type != OVSDB_TYPE_VOID) { ++ ovsdb_atom_clone(&result.values[result.n], ++ &diff->values[i], type->value.type); ++ } ++ result.n++; ++ if (operation[i] != DIFF_OP_UPDATE) { ++ break; ++ } ++ /* fall through */ ++ ++ case DIFF_OP_REMOVE: ++ /* Destroying atom. */ ++ ovsdb_atom_destroy(&a->keys[pos], type->key.type); ++ if (type->value.type != OVSDB_TYPE_VOID) { ++ ovsdb_atom_destroy(&a->values[pos], type->value.type); ++ } ++ copied++; /* Skipping removed atom. */ ++ break; ++ } ++ } ++ /* Copying remaining atoms. */ ++ ovsdb_datum_push_unsafe(&result, a, copied, a->n - copied, type); ++ a->n = 0; ++ ++ ovsdb_datum_swap(&result, a); ++ ovsdb_datum_destroy(&result, type); ++exit: ++ free(operation); ++ free(idx); ++ return error; ++} ++ + /* Apply 'diff' to 'old' to regenerate 'new'. + * + * Return NULL if the 'new' is successfully generated, otherwise, return +diff --git a/lib/ovsdb-data.h b/lib/ovsdb-data.h +index c5a80ee39f..f66ed3472c 100644 +--- a/lib/ovsdb-data.h ++++ b/lib/ovsdb-data.h +@@ -20,6 +20,7 @@ + #include "compiler.h" + #include "ovsdb-types.h" + #include "openvswitch/shash.h" ++#include "util.h" + + #ifdef __cplusplus + extern "C" { +@@ -31,12 +32,33 @@ struct ds; + struct ovsdb_symbol_table; + struct smap; + ++struct ovsdb_atom_string { ++ char *string; ++ size_t n_refs; ++}; ++ ++static inline struct ovsdb_atom_string * ++ovsdb_atom_string_create_nocopy(char *str) ++{ ++ struct ovsdb_atom_string *s = xzalloc(sizeof *s); ++ ++ s->string = str; ++ s->n_refs = 1; ++ return s; ++} ++ ++static inline struct ovsdb_atom_string * ++ovsdb_atom_string_create(const char *str) ++{ ++ return ovsdb_atom_string_create_nocopy(xstrdup(str)); ++} ++ + /* One value of an atomic type (given by enum ovs_atomic_type). */ + union ovsdb_atom { + int64_t integer; + double real; + bool boolean; +- char *string; ++ struct ovsdb_atom_string *s; + struct uuid uuid; + }; + +@@ -66,8 +88,9 @@ ovsdb_atom_needs_destruction(enum ovsdb_atomic_type type) + static inline void + ovsdb_atom_destroy(union ovsdb_atom *atom, enum ovsdb_atomic_type type) + { +- if (type == OVSDB_TYPE_STRING) { +- free(atom->string); ++ if (type == OVSDB_TYPE_STRING && !--atom->s->n_refs) { ++ free(atom->s->string); ++ free(atom->s); + } + } + +@@ -209,9 +232,10 @@ bool ovsdb_datum_equals(const struct ovsdb_datum *, + const struct ovsdb_type *); + + /* Search. */ +-unsigned int ovsdb_datum_find_key(const struct ovsdb_datum *, +- const union ovsdb_atom *key, +- enum ovsdb_atomic_type key_type); ++bool ovsdb_datum_find_key(const struct ovsdb_datum *, ++ const union ovsdb_atom *key, ++ enum ovsdb_atomic_type key_type, ++ unsigned int *pos); + unsigned int ovsdb_datum_find_key_value(const struct ovsdb_datum *, + const union ovsdb_atom *key, + enum ovsdb_atomic_type key_type, +@@ -227,14 +251,19 @@ bool ovsdb_datum_excludes_all(const struct ovsdb_datum *, + const struct ovsdb_type *); + void ovsdb_datum_union(struct ovsdb_datum *, + const struct ovsdb_datum *, +- const struct ovsdb_type *, +- bool replace); ++ const struct ovsdb_type *); + void ovsdb_datum_subtract(struct ovsdb_datum *a, + const struct ovsdb_type *a_type, + const struct ovsdb_datum *b, + const struct ovsdb_type *b_type); + + /* Generate and apply diffs */ ++void ovsdb_datum_added_removed(struct ovsdb_datum *added, ++ struct ovsdb_datum *removed, ++ const struct ovsdb_datum *old, ++ const struct ovsdb_datum *new, ++ const struct ovsdb_type *type); ++ + void ovsdb_datum_diff(struct ovsdb_datum *diff, + const struct ovsdb_datum *old_datum, + const struct ovsdb_datum *new_datum, +@@ -246,6 +275,12 @@ struct ovsdb_error *ovsdb_datum_apply_diff(struct ovsdb_datum *new_datum, + const struct ovsdb_type *type) + OVS_WARN_UNUSED_RESULT; + ++struct ovsdb_error * ovsdb_datum_apply_diff_in_place( ++ struct ovsdb_datum *a, ++ const struct ovsdb_datum *diff, ++ const struct ovsdb_type *type) ++OVS_WARN_UNUSED_RESULT; ++ + /* Raw operations that may not maintain the invariants. */ + void ovsdb_datum_remove_unsafe(struct ovsdb_datum *, size_t idx, + const struct ovsdb_type *); +diff --git a/lib/ovsdb-idl.c b/lib/ovsdb-idl.c +index 2198c69c60..198ba5a828 100644 +--- a/lib/ovsdb-idl.c ++++ b/lib/ovsdb-idl.c +@@ -1898,8 +1898,7 @@ ovsdb_idl_index_destroy_row(const struct ovsdb_idl_row *row_) + BITMAP_FOR_EACH_1 (i, class->n_columns, row->written) { + c = &class->columns[i]; + (c->unparse) (row); +- free(row->new_datum[i].values); +- free(row->new_datum[i].keys); ++ ovsdb_datum_destroy(&row->new_datum[i], &c->type); + } + free(row->new_datum); + free(row->written); +@@ -2787,9 +2786,8 @@ ovsdb_idl_txn_extract_mutations(struct ovsdb_idl_row *row, + struct ovsdb_datum *new_datum; + unsigned int pos; + new_datum = map_op_datum(map_op); +- pos = ovsdb_datum_find_key(old_datum, +- &new_datum->keys[0], +- key_type); ++ ovsdb_datum_find_key(old_datum, &new_datum->keys[0], ++ key_type, &pos); + if (ovsdb_atom_equals(&new_datum->values[0], + &old_datum->values[pos], + value_type)) { +@@ -2798,11 +2796,9 @@ ovsdb_idl_txn_extract_mutations(struct ovsdb_idl_row *row, + } + } else if (map_op_type(map_op) == MAP_OP_DELETE){ + /* Verify that there is a key to delete. */ +- unsigned int pos; +- pos = ovsdb_datum_find_key(old_datum, +- &map_op_datum(map_op)->keys[0], +- key_type); +- if (pos == UINT_MAX) { ++ if (!ovsdb_datum_find_key(old_datum, ++ &map_op_datum(map_op)->keys[0], ++ key_type, NULL)) { + /* No key to delete. Move on to next update. */ + VLOG_WARN("Trying to delete a key that doesn't " + "exist in the map."); +@@ -2897,11 +2893,9 @@ ovsdb_idl_txn_extract_mutations(struct ovsdb_idl_row *row, + any_ins = true; + } else { /* SETP_OP_DELETE */ + /* Verify that there is a key to delete. */ +- unsigned int pos; +- pos = ovsdb_datum_find_key(old_datum, +- &set_op_datum(set_op)->keys[0], +- key_type); +- if (pos == UINT_MAX) { ++ if (!ovsdb_datum_find_key(old_datum, ++ &set_op_datum(set_op)->keys[0], ++ key_type, NULL)) { + /* No key to delete. Move on to next update. */ + VLOG_WARN("Trying to delete a key that doesn't " + "exist in the set."); +@@ -4066,7 +4060,6 @@ ovsdb_idl_txn_write_partial_map(const struct ovsdb_idl_row *row_, + struct ovsdb_idl_row *row = CONST_CAST(struct ovsdb_idl_row *, row_); + enum ovsdb_atomic_type key_type; + enum map_op_type op_type; +- unsigned int pos; + const struct ovsdb_datum *old_datum; + + if (!is_valid_partial_update(row, column, datum)) { +@@ -4078,8 +4071,11 @@ ovsdb_idl_txn_write_partial_map(const struct ovsdb_idl_row *row_, + /* Find out if this is an insert or an update. */ + key_type = column->type.key.type; + old_datum = ovsdb_idl_read(row, column); +- pos = ovsdb_datum_find_key(old_datum, &datum->keys[0], key_type); +- op_type = pos == UINT_MAX ? MAP_OP_INSERT : MAP_OP_UPDATE; ++ if (ovsdb_datum_find_key(old_datum, &datum->keys[0], key_type, NULL)) { ++ op_type = MAP_OP_UPDATE; ++ } else { ++ op_type = MAP_OP_INSERT; ++ } + + ovsdb_idl_txn_add_map_op(row, column, datum, op_type); + } diff --git a/lib/pcap-file.c b/lib/pcap-file.c index b30a11c24b..41835f6f4d 100644 --- a/lib/pcap-file.c @@ -465,6 +1787,86 @@ index b30a11c24b..41835f6f4d 100644 return NULL; } +diff --git a/ofproto/ofproto-dpif-xlate.c b/ofproto/ofproto-dpif-xlate.c +index a426fcfeb6..1bab22aa5e 100644 +--- a/ofproto/ofproto-dpif-xlate.c ++++ b/ofproto/ofproto-dpif-xlate.c +@@ -6177,11 +6177,32 @@ static void + compose_conntrack_action(struct xlate_ctx *ctx, struct ofpact_conntrack *ofc, + bool is_last_action) + { +- ovs_u128 old_ct_label_mask = ctx->wc->masks.ct_label; +- uint32_t old_ct_mark_mask = ctx->wc->masks.ct_mark; +- size_t ct_offset; + uint16_t zone; ++ if (ofc->zone_src.field) { ++ union mf_subvalue value; ++ memset(&value, 0xff, sizeof(value)); ++ ++ zone = mf_get_subfield(&ofc->zone_src, &ctx->xin->flow); ++ if (ctx->xin->frozen_state) { ++ /* If the upcall is a resume of a recirculation, we only need to ++ * unwildcard the fields that are not in the frozen_metadata, as ++ * when the rules update, OVS will generate a new recirc_id, ++ * which will invalidate the megaflow with old the recirc_id. ++ */ ++ if (!mf_is_frozen_metadata(ofc->zone_src.field)) { ++ mf_write_subfield_flow(&ofc->zone_src, &value, ++ &ctx->wc->masks); ++ } ++ } else { ++ mf_write_subfield_flow(&ofc->zone_src, &value, &ctx->wc->masks); ++ } ++ } else { ++ zone = ofc->zone_imm; ++ } + ++ size_t ct_offset; ++ ovs_u128 old_ct_label_mask = ctx->wc->masks.ct_label; ++ uint32_t old_ct_mark_mask = ctx->wc->masks.ct_mark; + /* Ensure that any prior actions are applied before composing the new + * conntrack action. */ + xlate_commit_actions(ctx); +@@ -6193,11 +6214,6 @@ compose_conntrack_action(struct xlate_ctx *ctx, struct ofpact_conntrack *ofc, + do_xlate_actions(ofc->actions, ofpact_ct_get_action_len(ofc), ctx, + is_last_action, false); + +- if (ofc->zone_src.field) { +- zone = mf_get_subfield(&ofc->zone_src, &ctx->xin->flow); +- } else { +- zone = ofc->zone_imm; +- } + + ct_offset = nl_msg_start_nested(ctx->odp_actions, OVS_ACTION_ATTR_CT); + if (ofc->flags & NX_CT_F_COMMIT) { +diff --git a/ovsdb/file.c b/ovsdb/file.c +index 59220824fa..9f44007d97 100644 +--- a/ovsdb/file.c ++++ b/ovsdb/file.c +@@ -113,19 +113,17 @@ ovsdb_file_update_row_from_json(struct ovsdb_row *row, bool converting, + if (row_contains_diff + && !ovsdb_datum_is_default(&row->fields[column->index], + &column->type)) { +- struct ovsdb_datum new_datum; +- +- error = ovsdb_datum_apply_diff(&new_datum, ++ error = ovsdb_datum_apply_diff_in_place( + &row->fields[column->index], + &datum, &column->type); + ovsdb_datum_destroy(&datum, &column->type); + if (error) { + return error; + } +- ovsdb_datum_swap(&datum, &new_datum); ++ } else { ++ ovsdb_datum_swap(&row->fields[column->index], &datum); ++ ovsdb_datum_destroy(&datum, &column->type); + } +- ovsdb_datum_swap(&row->fields[column->index], &datum); +- ovsdb_datum_destroy(&datum, &column->type); + } + + return NULL; diff --git a/ovsdb/monitor.c b/ovsdb/monitor.c index 532dedcb64..ab814cf20e 100644 --- a/ovsdb/monitor.c @@ -485,6 +1887,338 @@ index 532dedcb64..ab814cf20e 100644 ovsdb_monitor_json_cache_insert(dbmon, version, mcs, json); } +diff --git a/ovsdb/mutation.c b/ovsdb/mutation.c +index 56edc5f000..03d1c3499e 100644 +--- a/ovsdb/mutation.c ++++ b/ovsdb/mutation.c +@@ -383,7 +383,7 @@ ovsdb_mutation_set_execute(struct ovsdb_row *row, + break; + + case OVSDB_M_INSERT: +- ovsdb_datum_union(dst, arg, dst_type, false); ++ ovsdb_datum_union(dst, arg, dst_type); + error = ovsdb_mutation_check_count(dst, dst_type); + break; + +diff --git a/ovsdb/ovsdb-idlc.in b/ovsdb/ovsdb-idlc.in +index 61cded16d3..78ebf2dc14 100755 +--- a/ovsdb/ovsdb-idlc.in ++++ b/ovsdb/ovsdb-idlc.in +@@ -551,20 +551,20 @@ static void + print(" smap_init(&row->%s);" % columnName) + print(" for (size_t i = 0; i < datum->n; i++) {") + print(" smap_add(&row->%s," % columnName) +- print(" datum->keys[i].string,") +- print(" datum->values[i].string);") ++ print(" datum->keys[i].s->string,") ++ print(" datum->values[i].s->string);") + print(" }") + elif (type.n_min == 1 and type.n_max == 1) or type.is_optional_pointer(): + print("") + print(" if (datum->n >= 1) {") + if not type.key.ref_table: +- print(" %s = datum->keys[0].%s;" % (keyVar, type.key.type.to_string())) ++ print(" %s = datum->keys[0].%s;" % (keyVar, type.key.type.to_rvalue_string())) + else: + 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())) + + if valueVar: + if not type.value.ref_table: +- print(" %s = datum->values[0].%s;" % (valueVar, type.value.type.to_string())) ++ print(" %s = datum->values[0].%s;" % (valueVar, type.value.type.to_rvalue_string())) + else: + 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())) + print(" } else {") +@@ -592,7 +592,7 @@ static void + """ % (prefix, type.key.ref_table.name.lower(), prefix, type.key.ref_table.name.lower(), prefix, type.key.ref_table.name.lower())) + keySrc = "keyRow" + else: +- keySrc = "datum->keys[i].%s" % type.key.type.to_string() ++ keySrc = "datum->keys[i].%s" % type.key.type.to_rvalue_string() + if type.value and type.value.ref_table: + print("""\ + struct %s%s *valueRow = %s%s_cast(ovsdb_idl_get_row_arc(row_, &%stable_%s, &datum->values[i].uuid)); +@@ -602,7 +602,7 @@ static void + """ % (prefix, type.value.ref_table.name.lower(), prefix, type.value.ref_table.name.lower(), prefix, type.value.ref_table.name.lower())) + valueSrc = "valueRow" + elif valueVar: +- valueSrc = "datum->values[i].%s" % type.value.type.to_string() ++ valueSrc = "datum->values[i].%s" % type.value.type.to_rvalue_string() + print(" if (!row->n_%s) {" % (columnName)) + + print(" %s = xmalloc(%s * sizeof *%s);" % ( +@@ -910,45 +910,45 @@ void + 'args': ', '.join(['%(type)s%(name)s' + % m for m in members])}) + if type.n_min == 1 and type.n_max == 1: +- print(" union ovsdb_atom key;") ++ print(" union ovsdb_atom *key = xmalloc(sizeof *key);") + if type.value: +- print(" union ovsdb_atom value;") ++ print(" union ovsdb_atom *value = xmalloc(sizeof *value);") + print("") + print(" datum.n = 1;") +- print(" datum.keys = &key;") +- print(" " + type.key.assign_c_value_casting_away_const("key.%s" % type.key.type.to_string(), keyVar)) ++ print(" datum.keys = key;") ++ print(" " + type.key.copyCValue("key->%s" % type.key.type.to_lvalue_string(), keyVar)) + if type.value: +- print(" datum.values = &value;") +- print(" "+ type.value.assign_c_value_casting_away_const("value.%s" % type.value.type.to_string(), valueVar)) ++ print(" datum.values = value;") ++ print(" " + type.value.copyCValue("value->%s" % type.value.type.to_lvalue_string(), valueVar)) + else: + print(" datum.values = NULL;") +- txn_write_func = "ovsdb_idl_txn_write_clone" ++ txn_write_func = "ovsdb_idl_txn_write" + elif type.is_optional_pointer(): +- print(" union ovsdb_atom key;") + print("") + print(" if (%s) {" % keyVar) ++ print(" union ovsdb_atom *key = xmalloc(sizeof *key);") + print(" datum.n = 1;") +- print(" datum.keys = &key;") +- print(" " + type.key.assign_c_value_casting_away_const("key.%s" % type.key.type.to_string(), keyVar)) ++ print(" datum.keys = key;") ++ print(" " + type.key.copyCValue("key->%s" % type.key.type.to_lvalue_string(), keyVar)) + print(" } else {") + print(" datum.n = 0;") + print(" datum.keys = NULL;") + print(" }") + print(" datum.values = NULL;") +- txn_write_func = "ovsdb_idl_txn_write_clone" ++ txn_write_func = "ovsdb_idl_txn_write" + elif type.n_max == 1: +- print(" union ovsdb_atom key;") + print("") + print(" if (%s) {" % nVar) ++ print(" union ovsdb_atom *key = xmalloc(sizeof *key);") + print(" datum.n = 1;") +- print(" datum.keys = &key;") +- print(" " + type.key.assign_c_value_casting_away_const("key.%s" % type.key.type.to_string(), "*" + keyVar)) ++ print(" datum.keys = key;") ++ print(" " + type.key.copyCValue("key->%s" % type.key.type.to_lvalue_string(), "*" + keyVar)) + print(" } else {") + print(" datum.n = 0;") + print(" datum.keys = NULL;") + print(" }") + print(" datum.values = NULL;") +- txn_write_func = "ovsdb_idl_txn_write_clone" ++ txn_write_func = "ovsdb_idl_txn_write" + else: + print("") + print(" datum.n = %s;" % nVar) +@@ -958,9 +958,9 @@ void + else: + print(" datum.values = NULL;") + print(" for (size_t i = 0; i < %s; i++) {" % nVar) +- print(" " + type.key.copyCValue("datum.keys[i].%s" % type.key.type.to_string(), "%s[i]" % keyVar)) ++ print(" " + type.key.copyCValue("datum.keys[i].%s" % type.key.type.to_lvalue_string(), "%s[i]" % keyVar)) + if type.value: +- print(" " + type.value.copyCValue("datum.values[i].%s" % type.value.type.to_string(), "%s[i]" % valueVar)) ++ print(" " + type.value.copyCValue("datum.values[i].%s" % type.value.type.to_lvalue_string(), "%s[i]" % valueVar)) + print(" }") + if type.value: + valueType = type.value.toAtomicType() +@@ -996,9 +996,8 @@ void + ''' % {'s': structName, 'c': columnName,'coltype':column.type.key.to_const_c_type(prefix), + 'valtype':column.type.value.to_const_c_type(prefix), 'S': structName.upper(), + 'C': columnName.upper(), 't': tableName}) +- +- print(" "+ type.key.copyCValue("datum->keys[0].%s" % type.key.type.to_string(), "new_key")) +- print(" "+ type.value.copyCValue("datum->values[0].%s" % type.value.type.to_string(), "new_value")) ++ print(" " + type.key.copyCValue("datum->keys[0].%s" % type.key.type.to_lvalue_string(), "new_key")) ++ print(" " + type.value.copyCValue("datum->values[0].%s" % type.value.type.to_lvalue_string(), "new_value")) + print(''' + ovsdb_idl_txn_write_partial_map(&row->header_, + &%(s)s_col_%(c)s, +@@ -1022,8 +1021,7 @@ void + ''' % {'s': structName, 'c': columnName,'coltype':column.type.key.to_const_c_type(prefix), + 'valtype':column.type.value.to_const_c_type(prefix), 'S': structName.upper(), + 'C': columnName.upper(), 't': tableName}) +- +- print(" "+ type.key.copyCValue("datum->keys[0].%s" % type.key.type.to_string(), "delete_key")) ++ print(" " + type.key.copyCValue("datum->keys[0].%s" % type.key.type.to_lvalue_string(), "delete_key")) + print(''' + ovsdb_idl_txn_delete_partial_map(&row->header_, + &%(s)s_col_%(c)s, +@@ -1049,8 +1047,7 @@ void + datum->values = NULL; + ''' % {'s': structName, 'c': columnName, + 'valtype':column.type.key.to_const_c_type(prefix), 't': tableName}) +- +- print(" "+ type.key.copyCValue("datum->keys[0].%s" % type.key.type.to_string(), "new_value")) ++ print(" " + type.key.copyCValue("datum->keys[0].%s" % type.key.type.to_lvalue_string(), "new_value")) + print(''' + ovsdb_idl_txn_write_partial_set(&row->header_, + &%(s)s_col_%(c)s, +@@ -1074,8 +1071,7 @@ void + ''' % {'s': structName, 'c': columnName,'coltype':column.type.key.to_const_c_type(prefix), + 'valtype':column.type.key.to_const_c_type(prefix), 'S': structName.upper(), + 'C': columnName.upper(), 't': tableName}) +- +- print(" "+ type.key.copyCValue("datum->keys[0].%s" % type.key.type.to_string(), "delete_value")) ++ print(" " + type.key.copyCValue("datum->keys[0].%s" % type.key.type.to_lvalue_string(), "delete_value")) + print(''' + ovsdb_idl_txn_delete_partial_set(&row->header_, + &%(s)s_col_%(c)s, +@@ -1143,37 +1139,36 @@ void + print(" struct ovsdb_datum datum;") + free = [] + if type.n_min == 1 and type.n_max == 1: +- print(" union ovsdb_atom key;") ++ print(" union ovsdb_atom *key = xmalloc(sizeof *key);") + if type.value: +- print(" union ovsdb_atom value;") ++ print(" union ovsdb_atom *value = xmalloc(sizeof *value);") + print("") + print(" datum.n = 1;") +- print(" datum.keys = &key;") +- print(" " + type.key.assign_c_value_casting_away_const("key.%s" % type.key.type.to_string(), keyVar, refTable=False)) ++ print(" datum.keys = key;") ++ print(" " + type.key.copyCValue("key->%s" % type.key.type.to_lvalue_string(), keyVar, refTable=False)) + if type.value: +- print(" datum.values = &value;") +- print(" "+ type.value.assign_c_value_casting_away_const("value.%s" % type.value.type.to_string(), valueVar, refTable=False)) ++ print(" " + type.value.copyCValue("value.%s" % type.value.type.to_lvalue_string(), valueVar, refTable=False)) + else: + print(" datum.values = NULL;") + elif type.is_optional_pointer(): +- print(" union ovsdb_atom key;") + print("") + print(" if (%s) {" % keyVar) ++ print(" union ovsdb_atom *key = xmalloc(sizeof *key);") + print(" datum.n = 1;") +- print(" datum.keys = &key;") +- print(" " + type.key.assign_c_value_casting_away_const("key.%s" % type.key.type.to_string(), keyVar, refTable=False)) ++ print(" datum.keys = key;") ++ print(" " + type.key.copyCValue("key->%s" % type.key.type.to_lvalue_string(), keyVar, refTable=False)) + print(" } else {") + print(" datum.n = 0;") + print(" datum.keys = NULL;") + print(" }") + print(" datum.values = NULL;") + elif type.n_max == 1: +- print(" union ovsdb_atom key;") + print("") + print(" if (%s) {" % nVar) ++ print(" union ovsdb_atom *key = xmalloc(sizeof *key);") + print(" datum.n = 1;") +- print(" datum.keys = &key;") +- print(" " + type.key.assign_c_value_casting_away_const("key.%s" % type.key.type.to_string(), "*" + keyVar, refTable=False)) ++ print(" datum.keys = key;") ++ print(" " + type.key.copyCValue("key->%s" % type.key.type.to_lvalue_string(), "*" + keyVar, refTable=False)) + print(" } else {") + print(" datum.n = 0;") + print(" datum.keys = NULL;") +@@ -1182,16 +1177,14 @@ void + else: + print(" datum.n = %s;" % nVar) + print(" datum.keys = %s ? xmalloc(%s * sizeof *datum.keys) : NULL;" % (nVar, nVar)) +- free += ['datum.keys'] + if type.value: + print(" datum.values = xmalloc(%s * sizeof *datum.values);" % nVar) +- free += ['datum.values'] + else: + print(" datum.values = NULL;") + print(" for (size_t i = 0; i < %s; i++) {" % nVar) +- print(" " + type.key.assign_c_value_casting_away_const("datum.keys[i].%s" % type.key.type.to_string(), "%s[i]" % keyVar, refTable=False)) ++ print(" " + type.key.copyCValue("datum.keys[i].%s" % type.key.type.to_lvalue_string(), "%s[i]" % keyVar, refTable=False)) + if type.value: +- print(" " + type.value.assign_c_value_casting_away_const("datum.values[i].%s" % type.value.type.to_string(), "%s[i]" % valueVar, refTable=False)) ++ print(" " + type.value.copyCValue("datum.values[i].%s" % type.value.type.to_lvalue_string(), "%s[i]" % valueVar, refTable=False)) + print(" }") + if type.value: + valueType = type.value.toAtomicType() +@@ -1211,8 +1204,8 @@ void + 's': structName, + 'S': structName.upper(), + 'c': columnName}) +- for var in free: +- print(" free(%s);" % var) ++ print(" ovsdb_datum_destroy(&datum, &%(s)s_col_%(c)s.type);" \ ++ % {'s': structName, 'c': columnName}) + print("}") + + # Index table related functions +@@ -1309,8 +1302,8 @@ struct %(s)s * + + i = 0; + SMAP_FOR_EACH (node, %(c)s) { +- datum->keys[i].string = node->key; +- datum->values[i].string = node->value; ++ datum->keys[i].s = ovsdb_atom_string_create(node->key); ++ datum->values[i].s = ovsdb_atom_string_create(node->value); + i++; + } + ovsdb_datum_sort_unique(datum, OVSDB_TYPE_STRING, OVSDB_TYPE_STRING); +@@ -1359,10 +1352,10 @@ struct %(s)s * + print() + print(" datum.n = 1;") + print(" datum.keys = key;") +- print(" " + type.key.assign_c_value_casting_away_const("key->%s" % type.key.type.to_string(), keyVar)) ++ print(" " + type.key.copyCValue("key->%s" % type.key.type.to_lvalue_string(), keyVar)) + if type.value: + print(" datum.values = value;") +- print(" "+ type.value.assign_c_value_casting_away_const("value->%s" % type.value.type.to_string(), valueVar)) ++ print(" " + type.value.copyCValue("value->%s" % type.value.type.to_lvalue_string(), valueVar)) + else: + print(" datum.values = NULL;") + txn_write_func = "ovsdb_idl_index_write" +@@ -1373,7 +1366,7 @@ struct %(s)s * + print(" key = xmalloc(sizeof (union ovsdb_atom));") + print(" datum.n = 1;") + print(" datum.keys = key;") +- print(" " + type.key.assign_c_value_casting_away_const("key->%s" % type.key.type.to_string(), keyVar)) ++ print(" " + type.key.copyCValue("key->%s" % type.key.type.to_lvalue_string(), keyVar)) + print(" } else {") + print(" datum.n = 0;") + print(" datum.keys = NULL;") +@@ -1387,7 +1380,7 @@ struct %(s)s * + print(" key = xmalloc(sizeof(union ovsdb_atom));") + print(" datum.n = 1;") + print(" datum.keys = key;") +- print(" " + type.key.assign_c_value_casting_away_const("key->%s" % type.key.type.to_string(), "*" + keyVar)) ++ print(" " + type.key.copyCValue("key->%s" % type.key.type.to_lvalue_string(), "*" + keyVar)) + print(" } else {") + print(" datum.n = 0;") + print(" datum.keys = NULL;") +@@ -1404,9 +1397,9 @@ struct %(s)s * + else: + print(" datum.values = NULL;") + print(" for (i = 0; i < %s; i++) {" % nVar) +- print(" " + type.key.copyCValue("datum.keys[i].%s" % type.key.type.to_string(), "%s[i]" % keyVar)) ++ print(" " + type.key.copyCValue("datum.keys[i].%s" % type.key.type.to_lvalue_string(), "%s[i]" % keyVar)) + if type.value: +- print(" " + type.value.copyCValue("datum.values[i].%s" % type.value.type.to_string(), "%s[i]" % valueVar)) ++ print(" " + type.value.copyCValue("datum.values[i].%s" % type.value.type.to_lvalue_string(), "%s[i]" % valueVar)) + print(" }") + if type.value: + valueType = type.value.toAtomicType() +diff --git a/ovsdb/ovsdb-server.c b/ovsdb/ovsdb-server.c +index 0b3d2bb714..b34d97e291 100644 +--- a/ovsdb/ovsdb-server.c ++++ b/ovsdb/ovsdb-server.c +@@ -904,8 +904,8 @@ query_db_string(const struct shash *all_dbs, const char *name, + + datum = &row->fields[column->index]; + for (i = 0; i < datum->n; i++) { +- if (datum->keys[i].string[0]) { +- return datum->keys[i].string; ++ if (datum->keys[i].s->string[0]) { ++ return datum->keys[i].s->string; + } + } + } +@@ -1018,7 +1018,7 @@ query_db_remotes(const char *name, const struct shash *all_dbs, + + datum = &row->fields[column->index]; + for (i = 0; i < datum->n; i++) { +- add_remote(remotes, datum->keys[i].string); ++ add_remote(remotes, datum->keys[i].s->string); + } + } + } else if (column->type.key.type == OVSDB_TYPE_UUID diff --git a/ovsdb/ovsdb-tool.c b/ovsdb/ovsdb-tool.c index 05a0223e71..d4a9e34cc4 100644 --- a/ovsdb/ovsdb-tool.c @@ -524,6 +2258,67 @@ index 05a0223e71..d4a9e34cc4 100644 e->eid = r->entry.eid; e->servers = r->entry.servers; break; +diff --git a/ovsdb/ovsdb-util.c b/ovsdb/ovsdb-util.c +index c4075cdae3..6d7be066b6 100644 +--- a/ovsdb/ovsdb-util.c ++++ b/ovsdb/ovsdb-util.c +@@ -111,13 +111,13 @@ ovsdb_util_read_map_string_column(const struct ovsdb_row *row, + + for (i = 0; i < datum->n; i++) { + atom_key = &datum->keys[i]; +- if (!strcmp(atom_key->string, key)) { ++ if (!strcmp(atom_key->s->string, key)) { + atom_value = &datum->values[i]; + break; + } + } + +- return atom_value ? atom_value->string : NULL; ++ return atom_value ? atom_value->s->string : NULL; + } + + /* Read string-uuid key-values from a map. Returns the row associated with +@@ -143,7 +143,7 @@ ovsdb_util_read_map_string_uuid_column(const struct ovsdb_row *row, + const struct ovsdb_datum *datum = &row->fields[column->index]; + for (size_t i = 0; i < datum->n; i++) { + union ovsdb_atom *atom_key = &datum->keys[i]; +- if (!strcmp(atom_key->string, key)) { ++ if (!strcmp(atom_key->s->string, key)) { + const union ovsdb_atom *atom_value = &datum->values[i]; + return ovsdb_table_get_row(ref_table, &atom_value->uuid); + } +@@ -181,7 +181,7 @@ ovsdb_util_read_string_column(const struct ovsdb_row *row, + const union ovsdb_atom *atom; + + atom = ovsdb_util_read_column(row, column_name, OVSDB_TYPE_STRING); +- *stringp = atom ? atom->string : NULL; ++ *stringp = atom ? atom->s->string : NULL; + return atom != NULL; + } + +@@ -269,8 +269,10 @@ ovsdb_util_write_string_column(struct ovsdb_row *row, const char *column_name, + const char *string) + { + if (string) { +- const union ovsdb_atom atom = { .string = CONST_CAST(char *, string) }; ++ union ovsdb_atom atom = { ++ .s = ovsdb_atom_string_create(CONST_CAST(char *, string)) }; + ovsdb_util_write_singleton(row, column_name, &atom, OVSDB_TYPE_STRING); ++ ovsdb_atom_destroy(&atom, OVSDB_TYPE_STRING); + } else { + ovsdb_util_clear_column(row, column_name); + } +@@ -305,8 +307,8 @@ ovsdb_util_write_string_string_column(struct ovsdb_row *row, + datum->values = xmalloc(n * sizeof *datum->values); + + for (i = 0; i < n; ++i) { +- datum->keys[i].string = keys[i]; +- datum->values[i].string = values[i]; ++ datum->keys[i].s = ovsdb_atom_string_create_nocopy(keys[i]); ++ datum->values[i].s = ovsdb_atom_string_create_nocopy(values[i]); + } + + /* Sort and check constraints. */ diff --git a/ovsdb/raft-private.c b/ovsdb/raft-private.c index 26d39a087f..30760233ee 100644 --- a/ovsdb/raft-private.c @@ -997,6 +2792,39 @@ index 3545c41c2c..599bc0ae86 100644 bool raft_has_next_entry(const struct raft *); uint64_t raft_get_applied_index(const struct raft *); +diff --git a/ovsdb/rbac.c b/ovsdb/rbac.c +index 2986027c90..ff411675f0 100644 +--- a/ovsdb/rbac.c ++++ b/ovsdb/rbac.c +@@ -53,8 +53,8 @@ ovsdb_find_row_by_string_key(const struct ovsdb_table *table, + HMAP_FOR_EACH (row, hmap_node, &table->rows) { + const struct ovsdb_datum *datum = &row->fields[column->index]; + for (size_t i = 0; i < datum->n; i++) { +- if (datum->keys[i].string[0] && +- !strcmp(key, datum->keys[i].string)) { ++ if (datum->keys[i].s->string[0] && ++ !strcmp(key, datum->keys[i].s->string)) { + return row; + } + } +@@ -113,7 +113,7 @@ ovsdb_rbac_authorized(const struct ovsdb_row *perms, + } + + for (i = 0; i < datum->n; i++) { +- const char *name = datum->keys[i].string; ++ const char *name = datum->keys[i].s->string; + const char *value = NULL; + bool is_map; + +@@ -271,7 +271,7 @@ rbac_column_modification_permitted(const struct ovsdb_column *column, + size_t i; + + for (i = 0; i < modifiable->n; i++) { +- char *name = modifiable->keys[i].string; ++ char *name = modifiable->keys[i].s->string; + + if (!strcmp(name, column->name)) { + return true; diff --git a/ovsdb/storage.c b/ovsdb/storage.c index d727b1eacd..9e32efe582 100644 --- a/ovsdb/storage.c @@ -1012,6 +2840,187 @@ index d727b1eacd..9e32efe582 100644 if (!json) { return NULL; } else if (json->type != JSON_ARRAY || json->array.n != 2) { +diff --git a/ovsdb/transaction.c b/ovsdb/transaction.c +index 8ffefcf7c9..dcccc61c05 100644 +--- a/ovsdb/transaction.c ++++ b/ovsdb/transaction.c +@@ -266,9 +266,9 @@ ovsdb_txn_adjust_atom_refs(struct ovsdb_txn *txn, const struct ovsdb_row *r, + + static struct ovsdb_error * OVS_WARN_UNUSED_RESULT + ovsdb_txn_adjust_row_refs(struct ovsdb_txn *txn, const struct ovsdb_row *r, +- const struct ovsdb_column *column, int delta) ++ const struct ovsdb_column *column, ++ const struct ovsdb_datum *field, int delta) + { +- const struct ovsdb_datum *field = &r->fields[column->index]; + struct ovsdb_error *error; + + error = ovsdb_txn_adjust_atom_refs(txn, r, column, &column->type.key, +@@ -291,14 +291,39 @@ update_row_ref_count(struct ovsdb_txn *txn, struct ovsdb_txn_row *r) + struct ovsdb_error *error; + + if (bitmap_is_set(r->changed, column->index)) { +- if (r->old) { +- error = ovsdb_txn_adjust_row_refs(txn, r->old, column, -1); ++ if (r->old && !r->new) { ++ error = ovsdb_txn_adjust_row_refs( ++ txn, r->old, column, ++ &r->old->fields[column->index], -1); + if (error) { + return OVSDB_WRAP_BUG("error decreasing refcount", error); + } +- } +- if (r->new) { +- error = ovsdb_txn_adjust_row_refs(txn, r->new, column, 1); ++ } else if (!r->old && r->new) { ++ error = ovsdb_txn_adjust_row_refs( ++ txn, r->new, column, ++ &r->new->fields[column->index], 1); ++ if (error) { ++ return error; ++ } ++ } else if (r->old && r->new) { ++ struct ovsdb_datum added, removed; ++ ++ ovsdb_datum_added_removed(&added, &removed, ++ &r->old->fields[column->index], ++ &r->new->fields[column->index], ++ &column->type); ++ ++ error = ovsdb_txn_adjust_row_refs( ++ txn, r->old, column, &removed, -1); ++ ovsdb_datum_destroy(&removed, &column->type); ++ if (error) { ++ ovsdb_datum_destroy(&added, &column->type); ++ return OVSDB_WRAP_BUG("error decreasing refcount", error); ++ } ++ ++ error = ovsdb_txn_adjust_row_refs( ++ txn, r->new, column, &added, 1); ++ ovsdb_datum_destroy(&added, &column->type); + if (error) { + return error; + } +diff --git a/python/ovs/db/data.py b/python/ovs/db/data.py +index 2a2102d6be..99bf80ed62 100644 +--- a/python/ovs/db/data.py ++++ b/python/ovs/db/data.py +@@ -204,7 +204,7 @@ class Atom(object): + else: + return '.boolean = false' + elif self.type == ovs.db.types.StringType: +- return '.string = "%s"' % escapeCString(self.value) ++ return '.s = %s' % escapeCString(self.value) + elif self.type == ovs.db.types.UuidType: + return '.uuid = %s' % ovs.ovsuuid.to_c_assignment(self.value) + +@@ -563,16 +563,41 @@ class Datum(object): + if n == 0: + return ["static struct ovsdb_datum %s = { .n = 0 };"] + +- s = ["static union ovsdb_atom %s_keys[%d] = {" % (name, n)] +- for key in sorted(self.values): +- s += [" { %s }," % key.cInitAtom(key)] +- s += ["};"] ++ s = [] ++ if self.type.key.type == ovs.db.types.StringType: ++ s += ["static struct ovsdb_atom_string %s_key_strings[%d] = {" ++ % (name, n)] ++ for key in sorted(self.values): ++ s += [' { .string = "%s", .n_refs = 2 },' ++ % escapeCString(key.value)] ++ s += ["};"] ++ s += ["static union ovsdb_atom %s_keys[%d] = {" % (name, n)] ++ for i in range(n): ++ s += [" { .s = &%s_key_strings[%d] }," % (name, i)] ++ s += ["};"] ++ else: ++ s = ["static union ovsdb_atom %s_keys[%d] = {" % (name, n)] ++ for key in sorted(self.values): ++ s += [" { %s }," % key.cInitAtom(key)] ++ s += ["};"] + + if self.type.value: +- s = ["static union ovsdb_atom %s_values[%d] = {" % (name, n)] +- for k, v in sorted(self.values.items()): +- s += [" { %s }," % v.cInitAtom(v)] +- s += ["};"] ++ if self.type.value.type == ovs.db.types.StringType: ++ s += ["static struct ovsdb_atom_string %s_val_strings[%d] = {" ++ % (name, n)] ++ for k, v in sorted(self.values): ++ s += [' { .string = "%s", .n_refs = 2 },' ++ % escapeCString(v.value)] ++ s += ["};"] ++ s += ["static union ovsdb_atom %s_values[%d] = {" % (name, n)] ++ for i in range(n): ++ s += [" { .s = &%s_val_strings[%d] }," % (name, i)] ++ s += ["};"] ++ else: ++ s = ["static union ovsdb_atom %s_values[%d] = {" % (name, n)] ++ for k, v in sorted(self.values.items()): ++ s += [" { %s }," % v.cInitAtom(v)] ++ s += ["};"] + + s += ["static struct ovsdb_datum %s = {" % name] + s += [" .n = %d," % n] +diff --git a/python/ovs/db/idl.py b/python/ovs/db/idl.py +index ecae5e1432..87ee06cdef 100644 +--- a/python/ovs/db/idl.py ++++ b/python/ovs/db/idl.py +@@ -1505,6 +1505,11 @@ class Transaction(object): + if self != self.idl.txn: + return self._status + ++ if self.idl.state != Idl.IDL_S_MONITORING: ++ self._status = Transaction.TRY_AGAIN ++ self.__disassemble() ++ return self._status ++ + # If we need a lock but don't have it, give up quickly. + if self.idl.lock_name and not self.idl.has_lock: + self._status = Transaction.NOT_LOCKED +diff --git a/python/ovs/db/types.py b/python/ovs/db/types.py +index 626ae8fc44..3318a3b6f8 100644 +--- a/python/ovs/db/types.py ++++ b/python/ovs/db/types.py +@@ -48,6 +48,16 @@ class AtomicType(object): + def to_string(self): + return self.name + ++ def to_rvalue_string(self): ++ if self == StringType: ++ return 's->' + self.name ++ return self.name ++ ++ def to_lvalue_string(self): ++ if self == StringType: ++ return 's' ++ return self.name ++ + def to_json(self): + return self.name + +@@ -373,18 +383,7 @@ class BaseType(object): + return "%(dst)s = *%(src)s;" % args + return ("%(dst)s = %(src)s->header_.uuid;") % args + elif self.type == StringType: +- return "%(dst)s = xstrdup(%(src)s);" % args +- else: +- return "%(dst)s = %(src)s;" % args +- +- def assign_c_value_casting_away_const(self, dst, src, refTable=True): +- args = {'dst': dst, 'src': src} +- if self.ref_table_name: +- if not refTable: +- return "%(dst)s = *%(src)s;" % args +- return ("%(dst)s = %(src)s->header_.uuid;") % args +- elif self.type == StringType: +- return "%(dst)s = CONST_CAST(char *, %(src)s);" % args ++ return "%(dst)s = ovsdb_atom_string_create(%(src)s);" % args + else: + return "%(dst)s = %(src)s;" % args + diff --git a/tests/ofproto-dpif.at b/tests/ofproto-dpif.at index 956a69e1fa..1dad6f62c6 100644 --- a/tests/ofproto-dpif.at @@ -1043,11 +3052,193 @@ index 956a69e1fa..1dad6f62c6 100644 AT_SETUP([ofproto-dpif - ICMPv6]) OVS_VSWITCHD_START add_of_ports br0 1 +diff --git a/tests/ovsdb-data.at b/tests/ovsdb-data.at +index 8cd2a26cb3..25c6acdac6 100644 +--- a/tests/ovsdb-data.at ++++ b/tests/ovsdb-data.at +@@ -846,18 +846,21 @@ OVSDB_CHECK_POSITIVE([generate and apply diff -- integer], + [[diff-data '["integer"]' '[0]' '[2]']], + [[diff: 2 + apply diff: 2 ++apply diff in place: 2 + OK]]) + + OVSDB_CHECK_POSITIVE([generate and apply diff -- boolean], + [[diff-data '["boolean"]' '[true]' '[false]']], + [[diff: false + apply diff: false ++apply diff in place: false + OK]]) + + OVSDB_CHECK_POSITIVE([generate and apply diff -- string], + [[diff-data '["string"]' '["AAA"]' '["BBB"]']], + [[diff: "BBB" + apply diff: "BBB" ++apply diff in place: "BBB" + OK]]) + + dnl Test set modifications. +@@ -870,15 +873,19 @@ OVSDB_CHECK_POSITIVE([generate and apply diff -- set], + ]], + [[diff: ["set",[0,2]] + apply diff: ["set",[1,2]] ++apply diff in place: ["set",[1,2]] + OK + diff: 0 + apply diff: 1 ++apply diff in place: 1 + OK + diff: ["set",[0,1]] + apply diff: ["set",[0,1]] ++apply diff in place: ["set",[0,1]] + OK + diff: ["set",[0,1]] + apply diff: ["set",[]] ++apply diff in place: ["set",[]] + OK]]) + + dnl Test set modifications causes data to violate set size constrain. +@@ -898,18 +905,23 @@ OVSDB_CHECK_POSITIVE([generate and apply diff -- map], + ]], + [[diff: ["map",[["2 gills","1 chopin"],["2 pints","1 quart"]]] + apply diff: ["map",[["2 pints","1 quart"]]] ++apply diff in place: ["map",[["2 pints","1 quart"]]] + OK + diff: ["map",[]] + apply diff: ["map",[["2 gills","1 chopin"]]] ++apply diff in place: ["map",[["2 gills","1 chopin"]]] + OK + diff: ["map",[["2 gills","1 chopin"]]] + apply diff: ["map",[]] ++apply diff in place: ["map",[]] + OK + diff: ["map",[["2 pints","1 quart"]]] + apply diff: ["map",[["2 pints","1 quart"]]] ++apply diff in place: ["map",[["2 pints","1 quart"]]] + OK + diff: ["map",[["2 gills","1 gallon"]]] + apply diff: ["map",[["2 gills","1 gallon"]]] ++apply diff in place: ["map",[["2 gills","1 gallon"]]] + OK]]) + + OVSDB_CHECK_NEGATIVE([generate and apply diff with map -- size error], diff --git a/tests/system-traffic.at b/tests/system-traffic.at -index f400cfabc9..c4442c183f 100644 +index f400cfabc9..092de308be 100644 --- a/tests/system-traffic.at +++ b/tests/system-traffic.at -@@ -3305,6 +3305,46 @@ NS_CHECK_EXEC([at_ns0], [ping6 -s 3200 -q -c 3 -i 0.3 -w 2 fc00::2 | FORMAT_PING +@@ -1981,6 +1981,111 @@ tcp,orig=(src=10.1.1.3,dst=10.1.1.4,sport=,dport=),reply=(src= + OVS_TRAFFIC_VSWITCHD_STOP + AT_CLEANUP + ++AT_SETUP([conntrack - zones from other field]) ++CHECK_CONNTRACK() ++OVS_TRAFFIC_VSWITCHD_START() ++ ++ADD_NAMESPACES(at_ns0, at_ns1) ++ ++ADD_VETH(p0, at_ns0, br0, "10.1.1.1/24") ++ADD_VETH(p1, at_ns1, br0, "10.1.1.2/24") ++ ++dnl Allow any traffic from ns0->ns1. Only allow nd, return traffic from ns1->ns0. ++AT_DATA([flows.txt], [dnl ++priority=1,action=drop ++priority=10,arp,action=normal ++priority=10,icmp,action=normal ++priority=100,in_port=1,tcp,ct_state=-trk,action=ct(zone=5,table=0) ++priority=100,in_port=1,tcp,ct_state=+trk,action=ct(commit,zone=NXM_NX_CT_ZONE[]),2 ++priority=100,in_port=2,ct_state=-trk,tcp,action=ct(table=0,zone=5) ++priority=100,in_port=2,ct_state=+trk,ct_zone=5,tcp,action=1 ++]) ++ ++AT_CHECK([ovs-ofctl --bundle add-flows br0 flows.txt]) ++ ++OVS_START_L7([at_ns1], [http]) ++ ++dnl HTTP requests from p0->p1 should work fine. ++NS_CHECK_EXEC([at_ns0], [wget 10.1.1.2 -t 3 -T 1 --retry-connrefused -v -o wget0.log]) ++ ++AT_CHECK([ovs-appctl dpctl/dump-conntrack | FORMAT_CT(10.1.1.2)], [0], [dnl ++tcp,dnl ++orig=(src=10.1.1.1,dst=10.1.1.2,sport=,dport=),dnl ++reply=(src=10.1.1.2,dst=10.1.1.1,sport=,dport=),dnl ++zone=5,protoinfo=(state=) ++]) ++ ++dnl This is to test when the zoneid is set by a field variable like ++dnl NXM_NX_CT_ZONE, the OVS xlate should generate a megaflow with a form of ++dnl "ct_zone(5), ... actions: ct(commit, zone=5)". The match "ct_zone(5)" ++dnl is needed as if we changes the zoneid into 15 in the following, the old ++dnl "ct_zone(5), ... actions: ct(commit, zone=5)" megaflow will not get hit, ++dnl and OVS will generate a new megaflow with the match "ct_zone(0xf)". ++dnl This will make sure that the new packets are committing to zoneid 15 ++dnl rather than old 5. ++AT_CHECK([ovs-appctl dpctl/dump-flows --names filter=in_port=ovs-p0 dnl ++ | grep "+trk" | grep -q "ct_zone(0x5)" ], [0], []) ++ ++AT_CHECK([ovs-ofctl mod-flows br0 dnl ++ 'priority=100,ct_state=-trk,tcp,in_port="ovs-p0" actions=ct(table=0,zone=15)']) ++ ++NS_CHECK_EXEC([at_ns0], [wget 10.1.1.2 -t 3 -T 1 --retry-connrefused -v -o wget0.log]) ++ ++AT_CHECK([ovs-appctl dpctl/dump-flows --names filter=in_port=ovs-p0 dnl ++ | grep "+trk" | grep -q "ct_zone(0xf)" ], [0], []) ++ ++OVS_TRAFFIC_VSWITCHD_STOP ++AT_CLEANUP ++ ++AT_SETUP([conntrack - zones from other field, more tests]) ++CHECK_CONNTRACK() ++OVS_TRAFFIC_VSWITCHD_START() ++ ++ADD_NAMESPACES(at_ns0, at_ns1) ++ ++ADD_VETH(p0, at_ns0, br0, "10.1.1.1/24") ++ADD_VETH(p1, at_ns1, br0, "10.1.1.2/24") ++ ++dnl Allow any traffic from ns0->ns1. Only allow nd, return traffic from ns1->ns0. ++AT_DATA([flows.txt], [dnl ++priority=1,action=drop ++priority=10,arp,action=normal ++priority=10,icmp,action=normal ++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]])) ++priority=100,in_port=1,tcp,ct_state=+trk,action=ct(commit,zone=NXM_NX_CT_LABEL[[0..15]]),2 ++priority=100,in_port=2,ct_state=-trk,tcp,action=ct(table=0,zone=5) ++priority=100,in_port=2,ct_state=+trk,ct_zone=5,tcp,action=1 ++]) ++ ++AT_CHECK([ovs-ofctl --bundle add-flows br0 flows.txt]) ++ ++OVS_START_L7([at_ns1], [http]) ++ ++dnl HTTP requests from p0->p1 should work fine. ++NS_CHECK_EXEC([at_ns0], [wget 10.1.1.2 -t 3 -T 1 --retry-connrefused -v -o wget0.log]) ++ ++AT_CHECK([ovs-appctl dpctl/dump-conntrack | FORMAT_CT(10.1.1.2)], [0], [dnl ++tcp,dnl ++orig=(src=10.1.1.1,dst=10.1.1.2,sport=,dport=),dnl ++reply=(src=10.1.1.2,dst=10.1.1.1,sport=,dport=),dnl ++zone=5,labels=0xffff0005,protoinfo=(state=) ++]) ++ ++AT_CHECK([ovs-appctl dpctl/dump-flows --names filter=in_port=ovs-p0 dnl ++ | grep "+trk" | sed 's/0xffff0005\/0xffff/0x5\/0xffff/' dnl ++ | grep -q "ct_label(0x5/0xffff)" ], [0], []) ++ ++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]]))']) ++ ++NS_CHECK_EXEC([at_ns0], [wget 10.1.1.2 -t 3 -T 1 --retry-connrefused -v -o wget0.log]) ++ ++AT_CHECK([ovs-appctl dpctl/dump-flows --names filter=in_port=ovs-p0 dnl ++ | grep "+trk" | sed 's/0xffff000f\/0xffff/0xf\/0xffff/' dnl ++ | grep -q "ct_label(0xf/0xffff)" ], [0], []) ++ ++OVS_TRAFFIC_VSWITCHD_STOP ++AT_CLEANUP ++ + AT_SETUP([conntrack - multiple bridges]) + CHECK_CONNTRACK() + OVS_TRAFFIC_VSWITCHD_START( +@@ -3305,6 +3410,46 @@ NS_CHECK_EXEC([at_ns0], [ping6 -s 3200 -q -c 3 -i 0.3 -w 2 fc00::2 | FORMAT_PING OVS_TRAFFIC_VSWITCHD_STOP AT_CLEANUP @@ -1177,11 +3368,77 @@ index a7ee595e0b..072a537252 100644 +} + +OVSTEST_REGISTER("json-string-benchmark", json_string_benchmark_main); +diff --git a/tests/test-ovsdb.c b/tests/test-ovsdb.c +index daa55dab7b..637271619f 100644 +--- a/tests/test-ovsdb.c ++++ b/tests/test-ovsdb.c +@@ -512,6 +512,18 @@ do_diff_data(struct ovs_cmdl_context *ctx) + ovs_fatal(0, "failed to apply diff"); + } + ++ /* Apply diff to 'old' in place. */ ++ error = ovsdb_datum_apply_diff_in_place(&old, &diff, &type); ++ if (error) { ++ char *string = ovsdb_error_to_string_free(error); ++ ovs_fatal(0, "%s", string); ++ } ++ ++ /* Test to make sure 'old' equals 'new' now. */ ++ if (!ovsdb_datum_equals(&new, &old, &type)) { ++ ovs_fatal(0, "failed to apply diff in place"); ++ } ++ + /* Print diff */ + json = ovsdb_datum_to_json(&diff, &type); + printf ("diff: "); +@@ -522,6 +534,11 @@ do_diff_data(struct ovs_cmdl_context *ctx) + printf ("apply diff: "); + print_and_free_json(json); + ++ /* Print updated 'old' */ ++ json = ovsdb_datum_to_json(&old, &type); ++ printf ("apply diff in place: "); ++ print_and_free_json(json); ++ + ovsdb_datum_destroy(&new, &type); + ovsdb_datum_destroy(&old, &type); + ovsdb_datum_destroy(&diff, &type); +@@ -2727,13 +2744,15 @@ print_idl_row_simple2(const struct idltest_simple2 *s, int step) + printf("%03d: name=%s smap=[", + step, s->name); + for (i = 0; i < smap->n; i++) { +- printf("[%s : %s]%s", smap->keys[i].string, smap->values[i].string, +- i < smap->n-1? ",": ""); ++ printf("[%s : %s]%s", ++ smap->keys[i].s->string, smap->values[i].s->string, ++ i < smap->n - 1 ? "," : ""); + } + printf("] imap=["); + for (i = 0; i < imap->n; i++) { +- printf("[%"PRId64" : %s]%s", imap->keys[i].integer, imap->values[i].string, +- i < imap->n-1? ",":""); ++ printf("[%"PRId64" : %s]%s", ++ imap->keys[i].integer, imap->values[i].s->string, ++ i < imap->n - 1 ? "," : ""); + } + printf("]\n"); + } +@@ -2802,8 +2821,8 @@ do_idl_partial_update_map_column(struct ovs_cmdl_context *ctx) + myTxn = ovsdb_idl_txn_create(idl); + smap = idltest_simple2_get_smap(myRow, OVSDB_TYPE_STRING, + OVSDB_TYPE_STRING); +- strcpy(key_to_delete, smap->keys[0].string); +- idltest_simple2_update_smap_delkey(myRow, smap->keys[0].string); ++ ovs_strlcpy(key_to_delete, smap->keys[0].s->string, sizeof key_to_delete); ++ idltest_simple2_update_smap_delkey(myRow, smap->keys[0].s->string); + ovsdb_idl_txn_commit_block(myTxn); + ovsdb_idl_txn_destroy(myTxn); + ovsdb_idl_get_initial_snapshot(idl); diff --git a/tests/tunnel-push-pop.at b/tests/tunnel-push-pop.at -index 48c5de9d19..12fc1ef910 100644 +index 48c5de9d19..6364653975 100644 --- a/tests/tunnel-push-pop.at +++ b/tests/tunnel-push-pop.at -@@ -595,6 +595,62 @@ OVS_WAIT_UNTIL([test `ovs-pcap p0.pcap | grep 50540000000a5054000000091235 | wc +@@ -595,6 +595,64 @@ OVS_WAIT_UNTIL([test `ovs-pcap p0.pcap | grep 50540000000a5054000000091235 | wc OVS_VSWITCHD_STOP AT_CLEANUP @@ -1218,20 +3475,22 @@ index 48c5de9d19..12fc1ef910 100644 +AT_CHECK([ovs-vsctl -- set Interface p0 options:tx_pcap=p0.pcap]) + +packet=50540000000a505400000009123 -+encap=f8bc124434b6aa55aa5500000800450000320000400040113406010102580101025c83a917c1001e00000000655800007b00 ++dnl Source port is based on a packet hash, so it may differ depending on the ++dnl compiler flags and CPU type. Masked with '....'. ++encap=f8bc124434b6aa55aa5500000800450000320000400040113406010102580101025c....17c1001e00000000655800007b00 + +dnl Output to tunnel from a int-br internal port. +dnl Checking that the packet arrived and it was correctly encapsulated. +AT_CHECK([ovs-ofctl add-flow int-br "in_port=LOCAL,actions=debug_slow,output:2"]) +AT_CHECK([ovs-appctl netdev-dummy/receive int-br "${packet}4"]) -+OVS_WAIT_UNTIL([test `ovs-pcap p0.pcap | grep "${encap}${packet}4" | wc -l` -ge 1]) ++OVS_WAIT_UNTIL([test `ovs-pcap p0.pcap | egrep "${encap}${packet}4" | wc -l` -ge 1]) +dnl Sending again to exercise the non-miss upcall path. +AT_CHECK([ovs-appctl netdev-dummy/receive int-br "${packet}4"]) -+OVS_WAIT_UNTIL([test `ovs-pcap p0.pcap | grep "${encap}${packet}4" | wc -l` -ge 2]) ++OVS_WAIT_UNTIL([test `ovs-pcap p0.pcap | egrep "${encap}${packet}4" | wc -l` -ge 2]) + +dnl Output to tunnel from the controller. +AT_CHECK([ovs-ofctl -O OpenFlow13 packet-out int-br CONTROLLER "debug_slow,output:2" "${packet}5"]) -+OVS_WAIT_UNTIL([test `ovs-pcap p0.pcap | grep "${encap}${packet}5" | wc -l` -ge 1]) ++OVS_WAIT_UNTIL([test `ovs-pcap p0.pcap | egrep "${encap}${packet}5" | wc -l` -ge 1]) + +dnl Datapath actions should not have tunnel push action. +AT_CHECK([ovs-appctl dpctl/dump-flows | grep -q tnl_push], [1]) @@ -1244,3 +3503,48 @@ index 48c5de9d19..12fc1ef910 100644 AT_SETUP([tunnel_push_pop - underlay bridge match]) OVS_VSWITCHD_START([add-port br0 p0 -- set Interface p0 type=dummy ofport_request=1 other-config:hwaddr=aa:55:aa:55:00:00]) +diff --git a/utilities/ovs-ctl.in b/utilities/ovs-ctl.in +index 71800795c0..e6e07f4763 100644 +--- a/utilities/ovs-ctl.in ++++ b/utilities/ovs-ctl.in +@@ -421,7 +421,9 @@ Less important options for "start", "restart" and "force-reload-kmod": + --no-force-corefiles do not force on core dumps for OVS daemons + --no-mlockall do not lock all of ovs-vswitchd into memory + --ovsdb-server-priority=NICE set ovsdb-server's niceness (default: $OVSDB_SERVER_PRIORITY) ++ --ovsdb-server-options=OPTIONS additional options for ovsdb-server (example: '-vconsole:dbg -vfile:dbg') + --ovs-vswitchd-priority=NICE set ovs-vswitchd's niceness (default: $OVS_VSWITCHD_PRIORITY) ++ --ovs-vswitchd-options=OPTIONS additional options for ovs-vswitchd (example: '-vconsole:dbg -vfile:dbg') + --no-full-hostname set short hostname instead of full hostname + --no-record-hostname do not attempt to determine/record system + hostname as part of start command +diff --git a/vswitchd/bridge.c b/vswitchd/bridge.c +index cb7c5cb769..c790a56adf 100644 +--- a/vswitchd/bridge.c ++++ b/vswitchd/bridge.c +@@ -4229,7 +4229,7 @@ bridge_configure_aa(struct bridge *br) + union ovsdb_atom atom; + + atom.integer = m->isid; +- if (ovsdb_datum_find_key(mc, &atom, OVSDB_TYPE_INTEGER) == UINT_MAX) { ++ if (!ovsdb_datum_find_key(mc, &atom, OVSDB_TYPE_INTEGER, NULL)) { + VLOG_INFO("Deleting isid=%"PRIu32", vlan=%"PRIu16, + m->isid, m->vlan); + bridge_aa_mapping_destroy(m); +@@ -4826,7 +4826,7 @@ queue_ids_include(const struct ovsdb_datum *queues, int64_t target) + union ovsdb_atom atom; + + atom.integer = target; +- return ovsdb_datum_find_key(queues, &atom, OVSDB_TYPE_INTEGER) != UINT_MAX; ++ return ovsdb_datum_find_key(queues, &atom, OVSDB_TYPE_INTEGER, NULL); + } + + static void +@@ -5020,7 +5020,7 @@ bridge_configure_mirrors(struct bridge *br) + union ovsdb_atom atom; + + atom.uuid = m->uuid; +- if (ovsdb_datum_find_key(mc, &atom, OVSDB_TYPE_UUID) == UINT_MAX) { ++ if (!ovsdb_datum_find_key(mc, &atom, OVSDB_TYPE_UUID, NULL)) { + mirror_destroy(m); + } + } diff --git a/SPECS/openvswitch2.16.spec b/SPECS/openvswitch2.16.spec index 863f8fb..7b89691 100644 --- a/SPECS/openvswitch2.16.spec +++ b/SPECS/openvswitch2.16.spec @@ -57,7 +57,7 @@ Summary: Open vSwitch Group: System Environment/Daemons daemon/database/utilities URL: http://www.openvswitch.org/ Version: 2.16.0 -Release: 8%{?dist} +Release: 25%{?dist} # Nearly all of openvswitch is ASL 2.0. The bugtool is LGPLv2+, and the # lib/sflow*.[ch] files are SISSL @@ -676,9 +676,11 @@ exit 0 %doc LICENSE NOTICE README.rst NEWS rhel/README.RHEL.rst %ifarch %{dpdkarches} %doc %{dpdkdir}/README.DPDK-PMDS +%attr(750,openvswitch,hugetlbfs) %verify(not owner group) /var/log/openvswitch +%else +%attr(750,openvswitch,openvswitch) %verify(not owner group) /var/log/openvswitch %endif /var/lib/openvswitch -%attr(750,openvswitch,openvswitch) %verify(not owner group) /var/log/openvswitch %ghost %attr(755,root,root) %verify(not owner group) %{_rundir}/openvswitch %{_datadir}/openvswitch/bugtool-plugins/ %{_datadir}/openvswitch/scripts/ovs-bugtool-* @@ -698,6 +700,321 @@ exit 0 %endif %changelog +* Thu Oct 28 2021 Open vSwitch CI - 2.16.0-25 +- Merging upstream branch-2.16 [RH git: f5366890c5] + Commit list: + c221c8e613 datapath-windows:Reset PseudoChecksum value only for TX direction offload case + + +* Wed Oct 27 2021 Open vSwitch CI - 2.16.0-24 +- Merging upstream branch-2.16 [RH git: 4682b76694] + Commit list: + b79f0369f2 ci: Make linux-prepare trust system installs. + + +* Mon Oct 25 2021 Open vSwitch CI - 2.16.0-23 +- Merging upstream branch-2.16 [RH git: cce913794e] + Commit list: + 2a4c87f300 Prepare for 2.16.2. + aaa1439b8e Set release date for 2.16.1. + + +* Thu Oct 21 2021 Open vSwitch CI - 2.16.0-22 +- Merging upstream branch-2.16 [RH git: 29f01c4fdb] + Commit list: + 108176ab5a github: Stick to python 3.9. + + +* Tue Oct 19 2021 Open vSwitch CI - 2.16.0-21 +- Merging upstream branch-2.16 [RH git: 2546fa9646] + Commit list: + 5c5e34603b datapath-windows: add layers when adding the deferred actions + + +* Thu Oct 14 2021 Open vSwitch CI - 2.16.0-20 +- Merging upstream branch-2.16 [RH git: d572c95f69] + Commit list: + 458a4f75f3 ofproto-dpif-xlate: Fix zone set from non-frozen-metadata fields. + + +* Wed Oct 13 2021 Open vSwitch CI - 2.16.0-19 +- Merging upstream branch-2.16 [RH git: 557ca689f7] + Commit list: + 6d8190584a dpif-netdev: Fix use-after-free on PACKET_OUT of IP fragments. + 44a66cc1d0 tunnel-push-pop.at: Mask source port in tunnel header. + + +* Tue Oct 12 2021 Open vSwitch CI - 2.16.0-18 +- Merging upstream branch-2.16 [RH git: a6c4770398] + Commit list: + 27a5848a33 ovs-ctl: Add missing description for --ovs-vswitchd-options and --ovsdb-server-options to usage(). + 0300d0c0c2 dpdk-stub: Change the ERR log to DBG. + cdd6dd821d dpif-netlink: Fix feature negotiation for older kernels. + c2682c42cb dpif-netdev: Fix pmd thread comments to include SMC. + 9377f4a465 python: idl: Avoid sending transactions when the DB is not synced up. + + +* Tue Oct 12 2021 Open vSwitch CI - 2.16.0-17 +- Merging upstream branch-2.16 [RH git: c1145b5236] + Commit list: + 0fd17fbb09 ipf: release unhandled packets from the batch + + +* Thu Sep 30 2021 Open vSwitch CI - 2.16.0-16 +- Merging upstream branch-2.16 [RH git: 5c05133179] + Commit list: + 3f692fba98 datapath-windows:adjust Offset when processing packet in POP_VLAN action + + +* Wed Sep 29 2021 Dumitru Ceara - 2.16.0-15 +- ovsdb-data: Deduplicate string atoms. [RH git: 24e7d1140e] (#2006839) + commit 429b114c5aadee24ccfb16ad7d824f45cdcea75a + Author: Ilya Maximets + Date: Wed Sep 22 09:28:50 2021 +0200 + + ovsdb-server spends a lot of time cloning atoms for various reasons, + e.g. to create a diff of two rows or to clone a row to the transaction. + All atoms, except for strings, contains a simple value that could be + copied in efficient way, but duplicating strings every time has a + significant performance impact. + + Introducing a new reference-counted structure 'ovsdb_atom_string' + that allows to not copy strings every time, but just increase a + reference counter. + + This change allows to increase transaction throughput in benchmarks + up to 2x for standalone databases and 3x for clustered databases, i.e. + number of transactions that ovsdb-server can handle per second. + It also noticeably reduces memory consumption of ovsdb-server. + + Next step will be to consolidate this structure with json strings, + so we will not need to duplicate strings while converting database + objects to json and back. + + Signed-off-by: Ilya Maximets + Acked-by: Dumitru Ceara + Acked-by: Mark D. Gray + + Reported-at: https://bugzilla.redhat.com/show_bug.cgi?id=2006839 + Signed-off-by: Dumitru Ceara + + +* Wed Sep 29 2021 Dumitru Ceara - 2.16.0-14 +- ovsdb-data: Add function to apply diff in-place. [RH git: df0e4bda98] (#2006851) + commit 32b51326ef9c307b4acd0bacafb0218dd1372f3d + Author: Ilya Maximets + Date: Thu Sep 23 01:47:24 2021 +0200 + + ovsdb_datum_apply_diff() is heavily used in ovsdb transactions, but + it's linear in terms of number of comparisons. And it also clones + all the atoms along the way. In most cases size of a diff is much + smaller than the size of the original datum, this allows to perform + the same operation in-place with only O(diff->n * log2(old->n)) + comparisons and O(old->n + diff->n) memory copies with memcpy. + Using this function while applying diffs read from the storage gives + a significant performance boost and allows to execute much more + transactions per second. + + Signed-off-by: Ilya Maximets + Acked-by: Mark D. Gray + + Reported-at: https://bugzilla.redhat.com/show_bug.cgi?id=2006851 + Signed-off-by: Dumitru Ceara + + +* Wed Sep 29 2021 Dumitru Ceara - 2.16.0-13 +- ovsdb-data: Optimize subtraction of sets. [RH git: 5bace82405] (#2005483) + commit bb12b63176389e516ddfefce20dfa165f24430fb + Author: Ilya Maximets + Date: Thu Sep 23 01:47:23 2021 +0200 + + Current algorithm for ovsdb_datum_subtract looks like this: + + for-each atom in a: + if atom in b: + swap(atom, ) + destroy(atom) + quicksort(a) + + Complexity: + + Na * log2(Nb) + (Na - Nb) * log2(Na - Nb) + Search Comparisons for quicksort + + It's not optimal, especially because Nb << Na in a vast majority of + cases. + + Reversing the search phase to look up atoms from 'b' in 'a', and + closing gaps from deleted elements in 'a' by plain memory copy to + avoid quicksort. + + Resulted complexity: + + Nb * log2(Na) + (Na - Nb) + Search Memory copies + + Subtraction is heavily used while executing database transactions. + For example, to remove one port from a logical switch in OVN. + Complexity of such operation if original logical switch had 100 ports + goes down from + + 100 * log2(1) = 100 comparisons for search and + 99 * log2(99) = 656 comparisons for quicksort + ------------------------------ + 756 comparisons in total + to only + + 1 * log2(100) = 7 comparisons for search + + memory copy of 99 * sizeof (union ovsdb_atom) bytes. + + We could use memmove to close the gaps after removing atoms, but + it will lead to 2 memory copies inside the call, while we can perform + only one to the temporary 'result' and swap pointers. + + Performance in cases, where sizes of 'a' and 'b' are comparable, + should not change. Cases with Nb >> Na should not happen in practice. + + All in all, this change allows ovsdb-server to perform several times + more transactions, that removes elements from sets, per second. + + Signed-off-by: Ilya Maximets + Acked-by: Han Zhou + Acked-by: Mark D. Gray + + Reported-at: https://bugzilla.redhat.com/show_bug.cgi?id=2005483 + Signed-off-by: Dumitru Ceara + + +* Wed Sep 29 2021 Dumitru Ceara - 2.16.0-12 +- ovsdb-data: Optimize union of sets. [RH git: e2a4c7d794] (#2005483) + commit 51946d22274cd591dc061358fb507056fbd91420 + Author: Ilya Maximets + Date: Thu Sep 23 01:47:22 2021 +0200 + + Current algorithm of ovsdb_datum_union looks like this: + + for-each atom in b: + if not bin_search(a, atom): + push(a, clone(atom)) + quicksort(a) + + So, the complexity looks like this: + + Nb * log2(Na) + Nb + (Na + Nb) * log2(Na + Nb) + Comparisons clones Comparisons for quicksort + for search + + ovsdb_datum_union() is heavily used in database transactions while + new element is added to a set. For example, if new logical switch + port is added to a logical switch in OVN. This is a very common + use case where CMS adds one new port to an existing switch that + already has, let's say, 100 ports. For this case ovsdb-server will + have to perform: + + 1 * log2(100) + 1 clone + 101 * log2(101) + Comparisons Comparisons for + for search quicksort. + ~7 1 ~707 + Roughly 714 comparisons of atoms and 1 clone. + + Since binary search can give us position, where new atom should go + (it's the 'low' index after the search completion) for free, the + logic can be re-worked like this: + + copied = 0 + for-each atom in b: + desired_position = bin_search(a, atom) + push(result, a[ copied : desired_position - 1 ]) + copied = desired_position + push(result, clone(atom)) + push(result, a[ copied : Na ]) + swap(a, result) + + Complexity of this schema: + + Nb * log2(Na) + Nb + Na + Comparisons clones memory copy on push + for search + + 'swap' is just a swap of a few pointers. 'push' is not a 'clone', + but a simple memory copy of 'union ovsdb_atom'. + + In general, this schema substitutes complexity of a quicksort + with complexity of a memory copy of Na atom structures, where we're + not even copying strings that these atoms are pointing to. + + Complexity in the example above goes down from 714 comparisons + to 7 comparisons and memcpy of 100 * sizeof (union ovsdb_atom) bytes. + + General complexity of a memory copy should always be lower than + complexity of a quicksort, especially because these copies usually + performed in bulk, so this new schema should work faster for any input. + + All in all, this change allows to execute several times more + transactions per second for transactions that adds new entries to sets. + + Alternatively, union can be implemented as a linear merge of two + sorted arrays, but this will result in O(Na) comparisons, which + is more than Nb * log2(Na) in common case, since Na is usually + far bigger than Nb. Linear merge will also mean per-atom memory + copies instead of copying in bulk. + + 'replace' functionality of ovsdb_datum_union() had no users, so it + just removed. But it can easily be added back if needed in the future. + + Signed-off-by: Ilya Maximets + Acked-by: Han Zhou + Acked-by: Mark D. Gray + + Reported-at: https://bugzilla.redhat.com/show_bug.cgi?id=2005483 + Signed-off-by: Dumitru Ceara + + +* Wed Sep 29 2021 Dumitru Ceara - 2.16.0-11 +- ovsdb: transaction: Use diffs for strong reference counting. [RH git: 85da133eaa] (#2003203) + commit b2712d026eae2d9a5150c2805310eaf506e1f162 + Author: Ilya Maximets + Date: Tue Sep 14 00:19:57 2021 +0200 + + Currently, even if one reference added to the set of strong references + or removed from it, ovsdb-server will walk through the whole set and + re-count references to other rows. These referenced rows will also be + added to the transaction in order to re-count their references. + + For example, every time Logical Switch Port added to a Logical Switch, + OVN Northbound database server will walk through all ports of this + Logical Switch, clone their rows, and re-count references. This is + not very efficient. Instead, it can only increase reference counters + for added references and reduce for removed ones. In many cases this + will be only one row affected in the Logical_Switch_Port table. + + Introducing new function that generates a diff of two datum objects, + but stores added and removed atoms separately, so they can be used + to increase or decrease row reference counters accordingly. + + This change allows to perform several times more transactions that + adds or removes strong references to/from sets per second, because + ovsdb-server no longer clones and re-counts rows that are irrelevant + to current transaction. + + Acked-by: Dumitru Ceara + Signed-off-by: Ilya Maximets + + Reported-at: https://bugzilla.redhat.com/show_bug.cgi?id=2003203 + Signed-off-by: Dumitru Ceara + + +* Mon Sep 27 2021 Open vSwitch CI - 2.16.0-10 +- Merging upstream branch-2.16 [RH git: 2114714012] + Commit list: + 547371ecdb cirrus: Reduce memory requirements for FreeBSD VMs. + + +* Thu Sep 23 2021 Timothy Redaelli - 2.16.0-9 +- redhat: use hugetlbfs group for /var/log/openvswitch when dpdk is enabled [RH git: 4e5928b671] (#2004543) + Resolves: #2004543 + + * Thu Sep 16 2021 Open vSwitch CI - 2.16.0-8 - Merging upstream branch-2.16 [RH git: 7332b410fc] Commit list: